From 75553ebd316378991761bceec56ca47290797355 Mon Sep 17 00:00:00 2001 From: Your Name <> Date: Thu, 16 Oct 2025 19:38:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20cppcheck=20=E6=BA=90?= =?UTF-8?q?=E7=A0=81=E6=96=87=E4=BB=B6=E5=A4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cppcheck-2.14.0/.clang-tidy | 79 + cppcheck-2.14.0/.codacy.yml | 8 + cppcheck-2.14.0/.gitattributes | 19 + .../.github/workflows/CI-cygwin.yml | 56 + .../.github/workflows/CI-mingw.yml | 71 + .../.github/workflows/CI-unixish-docker.yml | 158 + .../.github/workflows/CI-unixish.yml | 552 + .../.github/workflows/CI-windows.yml | 227 + cppcheck-2.14.0/.github/workflows/asan.yml | 140 + .../.github/workflows/buildman.yml | 60 + cppcheck-2.14.0/.github/workflows/cifuzz.yml | 34 + .../.github/workflows/clang-tidy.yml | 73 + .../.github/workflows/codeql-analysis.yml | 56 + .../.github/workflows/coverage.yml | 70 + .../.github/workflows/coverity.yml | 39 + .../.github/workflows/cppcheck-premium.yml | 44 + cppcheck-2.14.0/.github/workflows/format.yml | 47 + cppcheck-2.14.0/.github/workflows/iwyu.yml | 187 + .../.github/workflows/release-windows.yml | 168 + .../.github/workflows/scriptcheck.yml | 200 + .../.github/workflows/selfcheck.yml | 135 + cppcheck-2.14.0/.github/workflows/tsan.yml | 142 + cppcheck-2.14.0/.github/workflows/ubsan.yml | 136 + .../.github/workflows/valgrind.yml | 62 + cppcheck-2.14.0/.gitignore | 136 + cppcheck-2.14.0/.mailmap | 49 + cppcheck-2.14.0/.selfcheck_suppressions | 28 + .../.selfcheck_unused_suppressions | 15 + cppcheck-2.14.0/.travis.yml | 43 + cppcheck-2.14.0/.uncrustify.cfg | 3128 +++ cppcheck-2.14.0/AUTHORS | 408 + cppcheck-2.14.0/CMakeLists.txt | 105 + cppcheck-2.14.0/COPYING | 674 + cppcheck-2.14.0/Makefile | 902 + cppcheck-2.14.0/addons/README.md | 67 + cppcheck-2.14.0/addons/ROS_naming.json | 24 + cppcheck-2.14.0/addons/__init__.py | 0 cppcheck-2.14.0/addons/cppcheck.py | 39 + cppcheck-2.14.0/addons/cppcheckdata.doxyfile | 1792 ++ cppcheck-2.14.0/addons/cppcheckdata.py | 1708 ++ .../addons/doc/img/cppcheck-gui-addons.png | Bin 0 -> 23645 bytes cppcheck-2.14.0/addons/doc/y2038.txt | 151 + cppcheck-2.14.0/addons/findcasts.py | 32 + cppcheck-2.14.0/addons/misc.py | 163 + cppcheck-2.14.0/addons/misra.py | 5000 ++++ cppcheck-2.14.0/addons/misra_9.py | 563 + cppcheck-2.14.0/addons/naming.py | 91 + cppcheck-2.14.0/addons/namingng.config.json | 19 + cppcheck-2.14.0/addons/namingng.json | 6 + cppcheck-2.14.0/addons/namingng.py | 419 + cppcheck-2.14.0/addons/runaddon.py | 12 + cppcheck-2.14.0/addons/test/__init__.py | 0 cppcheck-2.14.0/addons/test/misc-test.cpp | 30 + cppcheck-2.14.0/addons/test/misra/config1.c | 10 + cppcheck-2.14.0/addons/test/misra/crash1.c | 8 + cppcheck-2.14.0/addons/test/misra/crash10.c | 12 + cppcheck-2.14.0/addons/test/misra/crash2.c | 9 + cppcheck-2.14.0/addons/test/misra/crash3.c | 30 + cppcheck-2.14.0/addons/test/misra/crash4.c | 11 + cppcheck-2.14.0/addons/test/misra/crash5.c | 23 + cppcheck-2.14.0/addons/test/misra/crash6.c | 17 + cppcheck-2.14.0/addons/test/misra/crash7.c | 8 + cppcheck-2.14.0/addons/test/misra/crash8.c | 10 + cppcheck-2.14.0/addons/test/misra/crash9.c | 3 + .../misra/crash_misra9_parseInitializer.c | 18 + .../addons/test/misra/misra-ctu-1-test.c | 52 + .../addons/test/misra/misra-ctu-2-test.c | 59 + .../addons/test/misra/misra-ctu-test.h | 23 + .../test/misra/misra-suppressions1-test.c | 37 + .../test/misra/misra-suppressions2-test.c | 20 + .../addons/test/misra/misra-test-avr8.c | 29 + .../addons/test/misra/misra-test-c11.c | 25 + .../addons/test/misra/misra-test.c | 2075 ++ .../addons/test/misra/misra-test.cpp | 25 + .../addons/test/misra/misra-test.h | 7 + .../misra/misra2012_rules_dummy_ascii.txt | 5 + .../test/misra/misra2012_rules_dummy_utf8.txt | 5 + .../misra2012_rules_dummy_windows1250.txt | 5 + .../addons/test/misra/misra_rules_dummy.txt | 22 + .../test/misra/misra_rules_empty_lines.txt | 21 + .../test/misra/misra_rules_multiple_lines.txt | 24 + .../test/misra/misra_rules_structure.txt | 20 + .../addons/test/misra/suppressions.txt | 6 + cppcheck-2.14.0/addons/test/misra_test.py | 177 + cppcheck-2.14.0/addons/test/naming_test.c | 5 + cppcheck-2.14.0/addons/test/naming_test.cpp | 11 + .../test/path1/misra-suppressions1-test.c | 33 + .../test/path1/misra-suppressions2-test.c | 14 + .../addons/test/threadsafety/MT-Unsafe.cpp | 67 + .../addons/test/threadsafety/local_static.cpp | 7 + .../test/threadsafety/local_static_const.cpp | 7 + cppcheck-2.14.0/addons/test/util.py | 47 + cppcheck-2.14.0/addons/test/y2038/y2038-inc.h | 31 + .../test/y2038/y2038-test-1-bad-time-bits.c | 18 + .../test/y2038/y2038-test-2-no-time-bits.c | 16 + .../y2038/y2038-test-3-no-use-time-bits.c | 16 + .../addons/test/y2038/y2038-test-4-good.c | 15 + .../y2038/y2038-test-5-good-no-time-used.c | 14 + cppcheck-2.14.0/addons/test/y2038_test.py | 139 + cppcheck-2.14.0/addons/threadsafety.py | 350 + cppcheck-2.14.0/addons/y2038.py | 242 + cppcheck-2.14.0/build-pcre.txt | 76 + cppcheck-2.14.0/cfg/avr.cfg | 402 + cppcheck-2.14.0/cfg/bento4.cfg | 142 + cppcheck-2.14.0/cfg/boost.cfg | 1283 + cppcheck-2.14.0/cfg/bsd.cfg | 567 + cppcheck-2.14.0/cfg/cairo.cfg | 89 + cppcheck-2.14.0/cfg/cppcheck-cfg.rng | 730 + cppcheck-2.14.0/cfg/cppcheck-lib.cfg | 72 + cppcheck-2.14.0/cfg/cppunit.cfg | 48 + cppcheck-2.14.0/cfg/dpdk.cfg | 11 + cppcheck-2.14.0/cfg/embedded_sql.cfg | 4 + cppcheck-2.14.0/cfg/emscripten.cfg | 14 + cppcheck-2.14.0/cfg/ginac.cfg | 12848 ++++++++++ cppcheck-2.14.0/cfg/gnu.cfg | 1690 ++ cppcheck-2.14.0/cfg/googletest.cfg | 60 + cppcheck-2.14.0/cfg/gtk.cfg | 21360 ++++++++++++++++ cppcheck-2.14.0/cfg/icu.cfg | 221 + cppcheck-2.14.0/cfg/kde.cfg | 51 + cppcheck-2.14.0/cfg/libcerror.cfg | 203 + cppcheck-2.14.0/cfg/libcurl.cfg | 487 + cppcheck-2.14.0/cfg/libsigc++.cfg | 36 + cppcheck-2.14.0/cfg/lua.cfg | 316 + cppcheck-2.14.0/cfg/mfc.cfg | 271 + cppcheck-2.14.0/cfg/microsoft_atl.cfg | 40 + cppcheck-2.14.0/cfg/microsoft_sal.cfg | 490 + cppcheck-2.14.0/cfg/microsoft_unittest.cfg | 20 + cppcheck-2.14.0/cfg/motif.cfg | 318 + cppcheck-2.14.0/cfg/nspr.cfg | 38 + cppcheck-2.14.0/cfg/ntl.cfg | 6907 +++++ cppcheck-2.14.0/cfg/opencv2.cfg | 132 + cppcheck-2.14.0/cfg/opengl.cfg | 580 + cppcheck-2.14.0/cfg/openmp.cfg | 157 + cppcheck-2.14.0/cfg/openssl.cfg | 174 + cppcheck-2.14.0/cfg/pcre.cfg | 165 + cppcheck-2.14.0/cfg/posix.cfg | 6438 +++++ cppcheck-2.14.0/cfg/python.cfg | 674 + cppcheck-2.14.0/cfg/qt.cfg | 5442 ++++ cppcheck-2.14.0/cfg/ruby.cfg | 142 + cppcheck-2.14.0/cfg/sdl.cfg | 354 + cppcheck-2.14.0/cfg/sfml.cfg | 301 + cppcheck-2.14.0/cfg/sqlite3.cfg | 1527 ++ cppcheck-2.14.0/cfg/std.cfg | 9060 +++++++ cppcheck-2.14.0/cfg/tinyxml2.cfg | 37 + cppcheck-2.14.0/cfg/vcl.cfg | 4 + cppcheck-2.14.0/cfg/windows.cfg | 17158 +++++++++++++ cppcheck-2.14.0/cfg/wxsqlite3.cfg | 1955 ++ cppcheck-2.14.0/cfg/wxsvg.cfg | 16905 ++++++++++++ cppcheck-2.14.0/cfg/wxwidgets.cfg | 17229 +++++++++++++ cppcheck-2.14.0/cfg/zephyr.cfg | 113 + cppcheck-2.14.0/cfg/zlib.cfg | 1450 ++ cppcheck-2.14.0/clang-tidy.md | 198 + cppcheck-2.14.0/cli/CMakeLists.txt | 86 + cppcheck-2.14.0/cli/cli.vcxproj | 260 + cppcheck-2.14.0/cli/cli.vcxproj.filters | 88 + cppcheck-2.14.0/cli/cmdlinelogger.h | 37 + cppcheck-2.14.0/cli/cmdlineparser.cpp | 1903 ++ cppcheck-2.14.0/cli/cmdlineparser.h | 172 + cppcheck-2.14.0/cli/cppcheckexecutor.cpp | 534 + cppcheck-2.14.0/cli/cppcheckexecutor.h | 121 + cppcheck-2.14.0/cli/cppcheckexecutorseh.cpp | 267 + cppcheck-2.14.0/cli/cppcheckexecutorseh.h | 33 + cppcheck-2.14.0/cli/executor.cpp | 72 + cppcheck-2.14.0/cli/executor.h | 84 + cppcheck-2.14.0/cli/filelister.cpp | 255 + cppcheck-2.14.0/cli/filelister.h | 81 + cppcheck-2.14.0/cli/main.cpp | 103 + cppcheck-2.14.0/cli/precompiled.h | 25 + cppcheck-2.14.0/cli/processexecutor.cpp | 408 + cppcheck-2.14.0/cli/processexecutor.h | 75 + cppcheck-2.14.0/cli/signalhandler.cpp | 333 + cppcheck-2.14.0/cli/signalhandler.h | 37 + cppcheck-2.14.0/cli/singleexecutor.cpp | 79 + cppcheck-2.14.0/cli/singleexecutor.h | 48 + cppcheck-2.14.0/cli/stacktrace.cpp | 102 + cppcheck-2.14.0/cli/stacktrace.h | 44 + cppcheck-2.14.0/cli/threadexecutor.cpp | 203 + cppcheck-2.14.0/cli/threadexecutor.h | 57 + cppcheck-2.14.0/cli/version.rc | 34 + cppcheck-2.14.0/cmake/buildFiles.cmake | 2 + cppcheck-2.14.0/cmake/clang_tidy.cmake | 37 + .../cmake/cmake_uninstall.cmake.in | 16 + cppcheck-2.14.0/cmake/compilerCheck.cmake | 13 + .../cmake/compilerDefinitions.cmake | 65 + cppcheck-2.14.0/cmake/compileroptions.cmake | 231 + cppcheck-2.14.0/cmake/cxx11.cmake | 9 + .../cmake/dynamic_analyzer_options.cmake | 39 + cppcheck-2.14.0/cmake/findDependencies.cmake | 89 + cppcheck-2.14.0/cmake/options.cmake | 95 + cppcheck-2.14.0/cmake/printInfo.cmake | 96 + cppcheck-2.14.0/cmake/qtCompat.cmake | 43 + cppcheck-2.14.0/cmake/versions.cmake | 7 + cppcheck-2.14.0/codecov.yml | 5 + cppcheck-2.14.0/console_common.pri | 23 + cppcheck-2.14.0/cppcheck-errors.rng | 95 + cppcheck-2.14.0/cppcheck.cppcheck | 22 + cppcheck-2.14.0/cppcheck.sln | 63 + cppcheck-2.14.0/cppcheckpremium-suppressions | 228 + cppcheck-2.14.0/createrelease | 165 + cppcheck-2.14.0/democlient/build.sh | 25 + cppcheck-2.14.0/democlient/democlient.cpp | 129 + cppcheck-2.14.0/doxyfile | 1906 ++ cppcheck-2.14.0/externals/.clang-tidy | 5 + cppcheck-2.14.0/externals/externals.pri | 11 + cppcheck-2.14.0/externals/picojson/LICENSE | 25 + cppcheck-2.14.0/externals/picojson/picojson.h | 1200 + .../externals/simplecpp/CMakeLists.txt | 11 + cppcheck-2.14.0/externals/simplecpp/LICENSE | 14 + .../externals/simplecpp/simplecpp.cpp | 3761 +++ .../externals/simplecpp/simplecpp.h | 376 + .../externals/tinyxml2/CMakeLists.txt | 21 + cppcheck-2.14.0/externals/tinyxml2/LICENSE | 18 + .../externals/tinyxml2/tinyxml2.cpp | 3031 +++ cppcheck-2.14.0/externals/tinyxml2/tinyxml2.h | 2384 ++ cppcheck-2.14.0/generate_coverage_report | 20 + cppcheck-2.14.0/gui/.clang-tidy | 5 + cppcheck-2.14.0/gui/CMakeLists.txt | 86 + cppcheck-2.14.0/gui/about.ui | 172 + cppcheck-2.14.0/gui/aboutdialog.cpp | 47 + cppcheck-2.14.0/gui/aboutdialog.h | 51 + cppcheck-2.14.0/gui/application.cpp | 28 + cppcheck-2.14.0/gui/application.h | 114 + cppcheck-2.14.0/gui/applicationdialog.cpp | 99 + cppcheck-2.14.0/gui/applicationdialog.h | 82 + cppcheck-2.14.0/gui/applicationdialog.ui | 200 + cppcheck-2.14.0/gui/applicationlist.cpp | 266 + cppcheck-2.14.0/gui/applicationlist.h | 139 + cppcheck-2.14.0/gui/checkstatistics.cpp | 117 + cppcheck-2.14.0/gui/checkstatistics.h | 102 + cppcheck-2.14.0/gui/checkthread.cpp | 468 + cppcheck-2.14.0/gui/checkthread.h | 150 + cppcheck-2.14.0/gui/codeeditor.cpp | 462 + cppcheck-2.14.0/gui/codeeditor.h | 165 + cppcheck-2.14.0/gui/codeeditorstyle.cpp | 232 + cppcheck-2.14.0/gui/codeeditorstyle.h | 128 + cppcheck-2.14.0/gui/codeeditstylecontrols.cpp | 126 + cppcheck-2.14.0/gui/codeeditstylecontrols.h | 76 + cppcheck-2.14.0/gui/codeeditstyledialog.cpp | 357 + cppcheck-2.14.0/gui/codeeditstyledialog.h | 105 + cppcheck-2.14.0/gui/common.cpp | 87 + cppcheck-2.14.0/gui/common.h | 155 + .../gui/compliancereportdialog.cpp | 236 + cppcheck-2.14.0/gui/compliancereportdialog.h | 52 + cppcheck-2.14.0/gui/compliancereportdialog.ui | 104 + cppcheck-2.14.0/gui/cppcheck-gui.desktop | 8 + cppcheck-2.14.0/gui/cppcheck-gui.png | Bin 0 -> 2543 bytes cppcheck-2.14.0/gui/cppcheck-gui.rc | 5 + cppcheck-2.14.0/gui/cppcheck-gui.svg | 100 + cppcheck-2.14.0/gui/cppcheck.ico | Bin 0 -> 25214 bytes cppcheck-2.14.0/gui/cppcheck_de.ts | 3042 +++ cppcheck-2.14.0/gui/cppcheck_es.ts | 3008 +++ cppcheck-2.14.0/gui/cppcheck_fi.ts | 2997 +++ cppcheck-2.14.0/gui/cppcheck_fr.ts | 2981 +++ cppcheck-2.14.0/gui/cppcheck_it.ts | 3020 +++ cppcheck-2.14.0/gui/cppcheck_ja.ts | 3069 +++ cppcheck-2.14.0/gui/cppcheck_ko.ts | 2997 +++ cppcheck-2.14.0/gui/cppcheck_nl.ts | 3022 +++ cppcheck-2.14.0/gui/cppcheck_ru.ts | 3055 +++ cppcheck-2.14.0/gui/cppcheck_sr.ts | 2991 +++ cppcheck-2.14.0/gui/cppcheck_sv.ts | 3059 +++ cppcheck-2.14.0/gui/cppcheck_zh_CN.ts | 3063 +++ cppcheck-2.14.0/gui/cppcheck_zh_TW.ts | 2984 +++ cppcheck-2.14.0/gui/cppchecklibrarydata.cpp | 944 + cppcheck-2.14.0/gui/cppchecklibrarydata.h | 261 + cppcheck-2.14.0/gui/csvreport.cpp | 72 + cppcheck-2.14.0/gui/csvreport.h | 73 + cppcheck-2.14.0/gui/erroritem.cpp | 99 + cppcheck-2.14.0/gui/erroritem.h | 128 + cppcheck-2.14.0/gui/filelist.cpp | 137 + cppcheck-2.14.0/gui/filelist.h | 101 + cppcheck-2.14.0/gui/fileview.ui | 71 + cppcheck-2.14.0/gui/fileviewdialog.cpp | 82 + cppcheck-2.14.0/gui/fileviewdialog.h | 64 + cppcheck-2.14.0/gui/gui.cppcheck | 14 + cppcheck-2.14.0/gui/gui.pro | 224 + cppcheck-2.14.0/gui/gui.qrc | 33 + .../gui/help/images/index-mainwindow.png | Bin 0 -> 201009 bytes .../gui/help/images/severities-error.png | Bin 0 -> 809 bytes .../help/images/severities-information.png | Bin 0 -> 1006 bytes .../help/images/severities-performance.png | Bin 0 -> 847 bytes .../help/images/severities-portability.png | Bin 0 -> 1288 bytes .../gui/help/images/severities-style.png | Bin 0 -> 1215 bytes .../gui/help/images/severities-warning.png | Bin 0 -> 805 bytes .../gui/help/images/walkthrough-analysis.png | Bin 0 -> 142639 bytes .../images/walkthrough-import-project.png | Bin 0 -> 12437 bytes .../gui/help/images/walkthrough-library.png | Bin 0 -> 4743 bytes .../help/images/walkthrough-new-project.png | Bin 0 -> 10712 bytes .../images/walkthrough-toolbar-severities.png | Bin 0 -> 6693 bytes .../help/images/walkthrough-warning-menu.png | Bin 0 -> 9096 bytes cppcheck-2.14.0/gui/help/index.html | 21 + .../gui/help/investigating-warnings.html | 22 + cppcheck-2.14.0/gui/help/manual.html | 947 + cppcheck-2.14.0/gui/help/online-help.qhcp | 14 + cppcheck-2.14.0/gui/help/online-help.qhp | 50 + cppcheck-2.14.0/gui/help/preferences.html | 74 + .../gui/help/projectfiledialog.html | 179 + cppcheck-2.14.0/gui/help/severities.html | 47 + .../gui/help/standalone-analysis.html | 17 + cppcheck-2.14.0/gui/help/tagging.html | 22 + cppcheck-2.14.0/gui/help/walkthrough.html | 69 + cppcheck-2.14.0/gui/helpdialog.cpp | 120 + cppcheck-2.14.0/gui/helpdialog.h | 60 + cppcheck-2.14.0/gui/helpdialog.ui | 67 + .../gui/images/applications-development.png | Bin 0 -> 1215 bytes .../gui/images/applications-system.png | Bin 0 -> 1288 bytes cppcheck-2.14.0/gui/images/dialog-error.png | Bin 0 -> 809 bytes .../gui/images/dialog-information.png | Bin 0 -> 1006 bytes cppcheck-2.14.0/gui/images/dialog-warning.png | Bin 0 -> 805 bytes cppcheck-2.14.0/gui/images/edit-clear.png | Bin 0 -> 1131 bytes cppcheck-2.14.0/gui/images/go-down.png | Bin 0 -> 750 bytes cppcheck-2.14.0/gui/images/go-home.png | Bin 0 -> 726 bytes cppcheck-2.14.0/gui/images/go-next.png | Bin 0 -> 819 bytes cppcheck-2.14.0/gui/images/go-previous.png | Bin 0 -> 849 bytes cppcheck-2.14.0/gui/images/help-browser.png | Bin 0 -> 1218 bytes cppcheck-2.14.0/gui/images/llvm-dragon.png | Bin 0 -> 9482 bytes cppcheck-2.14.0/gui/images/llvm-dragon.svg | 1 + cppcheck-2.14.0/gui/images/media-floppy.png | Bin 0 -> 671 bytes cppcheck-2.14.0/gui/images/openproject.png | Bin 0 -> 1021 bytes .../gui/images/preferences-system.png | Bin 0 -> 796 bytes cppcheck-2.14.0/gui/images/process-stop.png | Bin 0 -> 1120 bytes cppcheck-2.14.0/gui/images/scratchpad.png | Bin 0 -> 265 bytes cppcheck-2.14.0/gui/images/showerrors.png | Bin 0 -> 809 bytes .../gui/images/showperformance.png | Bin 0 -> 847 bytes .../gui/images/showstylewarnings.png | Bin 0 -> 1006 bytes cppcheck-2.14.0/gui/images/showwarnings.png | Bin 0 -> 805 bytes cppcheck-2.14.0/gui/images/text-x-generic.png | Bin 0 -> 399 bytes .../gui/images/utilities-system-monitor.png | Bin 0 -> 847 bytes cppcheck-2.14.0/gui/images/verify.svg | 74 + cppcheck-2.14.0/gui/images/view-recheck.png | Bin 0 -> 1253 bytes cppcheck-2.14.0/gui/images/view-refresh.png | Bin 0 -> 610 bytes .../gui/libraryaddfunctiondialog.cpp | 54 + .../gui/libraryaddfunctiondialog.h | 51 + .../gui/libraryaddfunctiondialog.ui | 120 + cppcheck-2.14.0/gui/librarydialog.cpp | 367 + cppcheck-2.14.0/gui/librarydialog.h | 66 + cppcheck-2.14.0/gui/librarydialog.ui | 539 + cppcheck-2.14.0/gui/libraryeditargdialog.cpp | 126 + cppcheck-2.14.0/gui/libraryeditargdialog.h | 54 + cppcheck-2.14.0/gui/libraryeditargdialog.ui | 401 + cppcheck-2.14.0/gui/main.cpp | 146 + cppcheck-2.14.0/gui/mainwindow.cpp | 2168 ++ cppcheck-2.14.0/gui/mainwindow.h | 485 + cppcheck-2.14.0/gui/mainwindow.ui | 936 + cppcheck-2.14.0/gui/newsuppressiondialog.cpp | 85 + cppcheck-2.14.0/gui/newsuppressiondialog.h | 59 + cppcheck-2.14.0/gui/newsuppressiondialog.ui | 121 + cppcheck-2.14.0/gui/platforms.cpp | 61 + cppcheck-2.14.0/gui/platforms.h | 59 + cppcheck-2.14.0/gui/precompiled.h | 33 + cppcheck-2.14.0/gui/precompiled_qmake.h | 23 + cppcheck-2.14.0/gui/printablereport.cpp | 59 + cppcheck-2.14.0/gui/printablereport.h | 75 + cppcheck-2.14.0/gui/projectfile.cpp | 1154 + cppcheck-2.14.0/gui/projectfile.h | 648 + cppcheck-2.14.0/gui/projectfile.txt | 48 + cppcheck-2.14.0/gui/projectfile.ui | 1022 + cppcheck-2.14.0/gui/projectfiledialog.cpp | 931 + cppcheck-2.14.0/gui/projectfiledialog.h | 338 + cppcheck-2.14.0/gui/readme.txt | 70 + cppcheck-2.14.0/gui/report.cpp | 63 + cppcheck-2.14.0/gui/report.h | 98 + cppcheck-2.14.0/gui/resultstree.cpp | 1463 ++ cppcheck-2.14.0/gui/resultstree.h | 528 + cppcheck-2.14.0/gui/resultsview.cpp | 585 + cppcheck-2.14.0/gui/resultsview.h | 396 + cppcheck-2.14.0/gui/resultsview.ui | 189 + cppcheck-2.14.0/gui/scratchpad.cpp | 55 + cppcheck-2.14.0/gui/scratchpad.h | 61 + cppcheck-2.14.0/gui/scratchpad.ui | 128 + cppcheck-2.14.0/gui/settings.ui | 591 + cppcheck-2.14.0/gui/settingsdialog.cpp | 420 + cppcheck-2.14.0/gui/settingsdialog.h | 248 + cppcheck-2.14.0/gui/showtypes.cpp | 130 + cppcheck-2.14.0/gui/showtypes.h | 125 + cppcheck-2.14.0/gui/statsdialog.cpp | 458 + cppcheck-2.14.0/gui/statsdialog.h | 81 + cppcheck-2.14.0/gui/statsdialog.ui | 519 + cppcheck-2.14.0/gui/test/CMakeLists.txt | 6 + cppcheck-2.14.0/gui/test/common.pri | 12 + .../test/cppchecklibrarydata/CMakeLists.txt | 22 + .../cppchecklibrarydata.pro | 23 + .../files/container_unhandled_element.cfg | 11 + .../files/container_valid.cfg | 9 + .../files/define_valid.cfg | 5 + .../files/mandatory_attribute_missing.cfg | 4 + .../markup_mandatory_attribute_missing.cfg | 6 + .../files/markup_unhandled_element.cfg | 36 + .../files/markup_valid.cfg | 35 + .../memory_resource_unhandled_element.cfg | 12 + .../files/memory_resource_valid.cfg | 15 + .../files/platform_type_unhandled_element.cfg | 8 + .../files/platform_type_valid.cfg | 19 + .../files/podtype_valid.cfg | 5 + ...reflection_mandatory_attribute_missing.cfg | 6 + .../files/reflection_unhandled_element.cfg | 7 + .../files/reflection_valid.cfg | 8 + .../files/smartptr_valid.cfg | 8 + .../files/typechecks_valid.cfg | 15 + .../files/undefine_valid.cfg | 5 + .../files/unhandled_element.cfg | 5 + .../files/xml_reader_error.cfg | 5 + .../test/cppchecklibrarydata/resources.qrc | 24 + .../testcppchecklibrarydata.cpp | 635 + .../testcppchecklibrarydata.h | 56 + cppcheck-2.14.0/gui/test/data/files/bar1 | 1 + cppcheck-2.14.0/gui/test/data/files/bar1.foo | 1 + .../gui/test/data/files/dir1/dir11/foo11.cpp | 1 + .../gui/test/data/files/dir1/foo1.cpp | 1 + .../gui/test/data/files/dir2/foo1.cpp | 1 + cppcheck-2.14.0/gui/test/data/files/foo1.cpp | 1 + cppcheck-2.14.0/gui/test/data/files/foo2.cxx | 1 + cppcheck-2.14.0/gui/test/data/files/foo3.cc | 1 + cppcheck-2.14.0/gui/test/data/files/foo4.c | 1 + cppcheck-2.14.0/gui/test/data/files/foo5.c++ | 1 + cppcheck-2.14.0/gui/test/data/files/foo6.txx | 1 + cppcheck-2.14.0/gui/test/data/files/foo7.tpp | 1 + cppcheck-2.14.0/gui/test/data/files/foo8.ipp | 1 + cppcheck-2.14.0/gui/test/data/files/foo9.ixx | 1 + .../test/data/projectfiles/simple.cppcheck | 18 + .../data/projectfiles/simple_ignore.cppcheck | 18 + .../data/projectfiles/simple_noroot.cppcheck | 17 + .../gui/test/data/xmlfiles/xmlreport_v2.xml | 25 + .../gui/test/filelist/CMakeLists.txt | 25 + .../gui/test/filelist/filelist.pro | 29 + .../gui/test/filelist/testfilelist.cpp | 187 + .../gui/test/filelist/testfilelist.h | 37 + .../gui/test/projectfile/CMakeLists.txt | 20 + .../gui/test/projectfile/projectfile.pro | 22 + .../gui/test/projectfile/testprojectfile.cpp | 173 + .../gui/test/projectfile/testprojectfile.h | 36 + cppcheck-2.14.0/gui/test/readme.txt | 27 + cppcheck-2.14.0/gui/test/test.pro | 9 + .../test/translationhandler/CMakeLists.txt | 21 + .../testtranslationhandler.cpp | 43 + .../testtranslationhandler.h | 27 + .../translationhandler/translationhandler.pro | 22 + .../gui/test/xmlreportv2/CMakeLists.txt | 37 + .../gui/test/xmlreportv2/testxmlreportv2.cpp | 56 + .../gui/test/xmlreportv2/testxmlreportv2.h | 27 + .../gui/test/xmlreportv2/xmlreportv2.pro | 28 + cppcheck-2.14.0/gui/threadhandler.cpp | 301 + cppcheck-2.14.0/gui/threadhandler.h | 273 + cppcheck-2.14.0/gui/threadresult.cpp | 127 + cppcheck-2.14.0/gui/threadresult.h | 166 + cppcheck-2.14.0/gui/translationhandler.cpp | 187 + cppcheck-2.14.0/gui/translationhandler.h | 141 + cppcheck-2.14.0/gui/txtreport.cpp | 85 + cppcheck-2.14.0/gui/txtreport.h | 74 + cppcheck-2.14.0/gui/xmlreport.cpp | 98 + cppcheck-2.14.0/gui/xmlreport.h | 68 + cppcheck-2.14.0/gui/xmlreportv2.cpp | 291 + cppcheck-2.14.0/gui/xmlreportv2.h | 99 + cppcheck-2.14.0/htmlreport/README.txt | 12 + cppcheck-2.14.0/htmlreport/check.sh | 80 + .../htmlreport/cppcheck-htmlreport | 986 + cppcheck-2.14.0/htmlreport/example.cc | 7 + cppcheck-2.14.0/htmlreport/example.xml | 8 + cppcheck-2.14.0/htmlreport/requirements.txt | 1 + cppcheck-2.14.0/htmlreport/setup.py | 21 + cppcheck-2.14.0/htmlreport/test_htmlreport.py | 115 + .../htmlreport/test_suppressions.txt | 3 + cppcheck-2.14.0/htmlreport/tox.ini | 12 + cppcheck-2.14.0/lib/CMakeLists.txt | 63 + cppcheck-2.14.0/lib/addoninfo.cpp | 171 + cppcheck-2.14.0/lib/addoninfo.h | 38 + cppcheck-2.14.0/lib/analyzer.h | 197 + cppcheck-2.14.0/lib/analyzerinfo.cpp | 163 + cppcheck-2.14.0/lib/analyzerinfo.h | 71 + cppcheck-2.14.0/lib/astutils.cpp | 3590 +++ cppcheck-2.14.0/lib/astutils.h | 461 + cppcheck-2.14.0/lib/calculate.h | 127 + cppcheck-2.14.0/lib/check.cpp | 133 + cppcheck-2.14.0/lib/check.h | 175 + cppcheck-2.14.0/lib/check64bit.cpp | 163 + cppcheck-2.14.0/lib/check64bit.h | 89 + cppcheck-2.14.0/lib/checkassert.cpp | 159 + cppcheck-2.14.0/lib/checkassert.h | 81 + cppcheck-2.14.0/lib/checkautovariables.cpp | 789 + cppcheck-2.14.0/lib/checkautovariables.h | 131 + cppcheck-2.14.0/lib/checkbool.cpp | 519 + cppcheck-2.14.0/lib/checkbool.h | 145 + cppcheck-2.14.0/lib/checkboost.cpp | 66 + cppcheck-2.14.0/lib/checkboost.h | 80 + cppcheck-2.14.0/lib/checkbufferoverrun.cpp | 1210 + cppcheck-2.14.0/lib/checkbufferoverrun.h | 159 + cppcheck-2.14.0/lib/checkclass.cpp | 3681 +++ cppcheck-2.14.0/lib/checkclass.h | 418 + cppcheck-2.14.0/lib/checkcondition.cpp | 2045 ++ cppcheck-2.14.0/lib/checkcondition.h | 223 + cppcheck-2.14.0/lib/checkers.cpp | 983 + cppcheck-2.14.0/lib/checkers.h | 48 + cppcheck-2.14.0/lib/checkersreport.cpp | 266 + cppcheck-2.14.0/lib/checkersreport.h | 48 + cppcheck-2.14.0/lib/checkexceptionsafety.cpp | 411 + cppcheck-2.14.0/lib/checkexceptionsafety.h | 134 + cppcheck-2.14.0/lib/checkfunctions.cpp | 830 + cppcheck-2.14.0/lib/checkfunctions.h | 169 + cppcheck-2.14.0/lib/checkinternal.cpp | 400 + cppcheck-2.14.0/lib/checkinternal.h | 122 + cppcheck-2.14.0/lib/checkio.cpp | 2024 ++ cppcheck-2.14.0/lib/checkio.h | 184 + cppcheck-2.14.0/lib/checkleakautovar.cpp | 1215 + cppcheck-2.14.0/lib/checkleakautovar.h | 180 + cppcheck-2.14.0/lib/checkmemoryleak.cpp | 1156 + cppcheck-2.14.0/lib/checkmemoryleak.h | 364 + cppcheck-2.14.0/lib/checknullpointer.cpp | 650 + cppcheck-2.14.0/lib/checknullpointer.h | 149 + cppcheck-2.14.0/lib/checkother.cpp | 4017 +++ cppcheck-2.14.0/lib/checkother.h | 432 + cppcheck-2.14.0/lib/checkpostfixoperator.cpp | 89 + cppcheck-2.14.0/lib/checkpostfixoperator.h | 83 + cppcheck-2.14.0/lib/checksizeof.cpp | 503 + cppcheck-2.14.0/lib/checksizeof.h | 138 + cppcheck-2.14.0/lib/checkstl.cpp | 3285 +++ cppcheck-2.14.0/lib/checkstl.h | 313 + cppcheck-2.14.0/lib/checkstring.cpp | 473 + cppcheck-2.14.0/lib/checkstring.h | 129 + cppcheck-2.14.0/lib/checktype.cpp | 510 + cppcheck-2.14.0/lib/checktype.h | 120 + cppcheck-2.14.0/lib/checkuninitvar.cpp | 1762 ++ cppcheck-2.14.0/lib/checkuninitvar.h | 155 + cppcheck-2.14.0/lib/checkunusedfunctions.cpp | 476 + cppcheck-2.14.0/lib/checkunusedfunctions.h | 94 + cppcheck-2.14.0/lib/checkunusedvar.cpp | 1747 ++ cppcheck-2.14.0/lib/checkunusedvar.h | 119 + cppcheck-2.14.0/lib/checkvaarg.cpp | 182 + cppcheck-2.14.0/lib/checkvaarg.h | 91 + cppcheck-2.14.0/lib/clangimport.cpp | 1639 ++ cppcheck-2.14.0/lib/clangimport.h | 35 + cppcheck-2.14.0/lib/color.cpp | 65 + cppcheck-2.14.0/lib/color.h | 43 + cppcheck-2.14.0/lib/config.h | 204 + cppcheck-2.14.0/lib/cppcheck.cpp | 1900 ++ cppcheck-2.14.0/lib/cppcheck.h | 260 + cppcheck-2.14.0/lib/cppcheck.natvis | 71 + cppcheck-2.14.0/lib/cppcheck.vcxproj | 389 + cppcheck-2.14.0/lib/cppcheck.vcxproj.filters | 431 + cppcheck-2.14.0/lib/ctu.cpp | 592 + cppcheck-2.14.0/lib/ctu.h | 156 + cppcheck-2.14.0/lib/errorlogger.cpp | 937 + cppcheck-2.14.0/lib/errorlogger.h | 283 + cppcheck-2.14.0/lib/errortypes.cpp | 99 + cppcheck-2.14.0/lib/errortypes.h | 134 + cppcheck-2.14.0/lib/filesettings.h | 49 + cppcheck-2.14.0/lib/findtoken.h | 221 + cppcheck-2.14.0/lib/forwardanalyzer.cpp | 925 + cppcheck-2.14.0/lib/forwardanalyzer.h | 39 + cppcheck-2.14.0/lib/fwdanalysis.cpp | 564 + cppcheck-2.14.0/lib/fwdanalysis.h | 91 + cppcheck-2.14.0/lib/importproject.cpp | 1357 + cppcheck-2.14.0/lib/importproject.h | 180 + cppcheck-2.14.0/lib/infer.cpp | 388 + cppcheck-2.14.0/lib/infer.h | 59 + cppcheck-2.14.0/lib/json.h | 37 + cppcheck-2.14.0/lib/keywords.cpp | 227 + cppcheck-2.14.0/lib/keywords.h | 37 + cppcheck-2.14.0/lib/lib.pri | 145 + cppcheck-2.14.0/lib/library.cpp | 1793 ++ cppcheck-2.14.0/lib/library.h | 615 + cppcheck-2.14.0/lib/matchcompiler.h | 74 + cppcheck-2.14.0/lib/mathlib.cpp | 1363 + cppcheck-2.14.0/lib/mathlib.h | 152 + cppcheck-2.14.0/lib/path.cpp | 345 + cppcheck-2.14.0/lib/path.h | 224 + cppcheck-2.14.0/lib/pathanalysis.cpp | 195 + cppcheck-2.14.0/lib/pathanalysis.h | 82 + cppcheck-2.14.0/lib/pathmatch.cpp | 85 + cppcheck-2.14.0/lib/pathmatch.h | 68 + cppcheck-2.14.0/lib/pcrerules.pri | 13 + cppcheck-2.14.0/lib/platform.cpp | 446 + cppcheck-2.14.0/lib/platform.h | 195 + cppcheck-2.14.0/lib/precompiled.h | 32 + cppcheck-2.14.0/lib/preprocessor.cpp | 1014 + cppcheck-2.14.0/lib/preprocessor.h | 158 + cppcheck-2.14.0/lib/programmemory.cpp | 1798 ++ cppcheck-2.14.0/lib/programmemory.h | 218 + cppcheck-2.14.0/lib/reverseanalyzer.cpp | 404 + cppcheck-2.14.0/lib/reverseanalyzer.h | 32 + cppcheck-2.14.0/lib/settings.cpp | 624 + cppcheck-2.14.0/lib/settings.h | 484 + cppcheck-2.14.0/lib/smallvector.h | 71 + cppcheck-2.14.0/lib/sourcelocation.h | 98 + cppcheck-2.14.0/lib/standards.h | 138 + cppcheck-2.14.0/lib/summaries.cpp | 196 + cppcheck-2.14.0/lib/summaries.h | 38 + cppcheck-2.14.0/lib/suppressions.cpp | 579 + cppcheck-2.14.0/lib/suppressions.h | 271 + cppcheck-2.14.0/lib/symboldatabase.cpp | 8226 ++++++ cppcheck-2.14.0/lib/symboldatabase.h | 1483 ++ cppcheck-2.14.0/lib/templatesimplifier.cpp | 4033 +++ cppcheck-2.14.0/lib/templatesimplifier.h | 516 + cppcheck-2.14.0/lib/timer.cpp | 144 + cppcheck-2.14.0/lib/timer.h | 89 + cppcheck-2.14.0/lib/token.cpp | 2760 ++ cppcheck-2.14.0/lib/token.h | 1493 ++ cppcheck-2.14.0/lib/tokenize.cpp | 10708 ++++++++ cppcheck-2.14.0/lib/tokenize.h | 688 + cppcheck-2.14.0/lib/tokenlist.cpp | 2189 ++ cppcheck-2.14.0/lib/tokenlist.h | 230 + cppcheck-2.14.0/lib/tokenrange.h | 83 + cppcheck-2.14.0/lib/utils.cpp | 131 + cppcheck-2.14.0/lib/utils.h | 370 + cppcheck-2.14.0/lib/valueflow.cpp | 9751 +++++++ cppcheck-2.14.0/lib/valueflow.h | 137 + cppcheck-2.14.0/lib/valueptr.h | 106 + cppcheck-2.14.0/lib/version.h | 23 + cppcheck-2.14.0/lib/version.rc | 34 + cppcheck-2.14.0/lib/vfvalue.cpp | 195 + cppcheck-2.14.0/lib/vfvalue.h | 416 + cppcheck-2.14.0/lib/xml.h | 34 + cppcheck-2.14.0/man/CMakeLists.txt | 7 + cppcheck-2.14.0/man/build-html.sh | 3 + cppcheck-2.14.0/man/build-pdf.sh | 32 + cppcheck-2.14.0/man/buildman.sh | 10 + cppcheck-2.14.0/man/cppcheck-design.docbook | 190 + cppcheck-2.14.0/man/cppcheck.1.xml | 673 + .../man/images/gui-newproject-addons.png | Bin 0 -> 20633 bytes .../images/gui-newproject-pathsanddefines.png | Bin 0 -> 36460 bytes .../man/images/gui-newproject-project.png | Bin 0 -> 33686 bytes cppcheck-2.14.0/man/images/gui-newproject.png | Bin 0 -> 35440 bytes cppcheck-2.14.0/man/images/gui-results.png | Bin 0 -> 105686 bytes cppcheck-2.14.0/man/manual-ja.docbook | 1895 ++ cppcheck-2.14.0/man/manual-premium.md | 1152 + cppcheck-2.14.0/man/manual-style.tex | 32 + cppcheck-2.14.0/man/manual.css | 86 + cppcheck-2.14.0/man/manual.md | 1155 + cppcheck-2.14.0/man/reference-cfg-format.md | 613 + cppcheck-2.14.0/man/writing-addons.md | 408 + cppcheck-2.14.0/man/writing-rules-1.docbook | 133 + cppcheck-2.14.0/man/writing-rules-2.docbook | 339 + cppcheck-2.14.0/man/writing-rules-3.docbook | 229 + cppcheck-2.14.0/naming.json | 9 + cppcheck-2.14.0/oss-fuzz/Makefile | 342 + cppcheck-2.14.0/oss-fuzz/main.cpp | 83 + cppcheck-2.14.0/oss-fuzz/translate.cpp | 46 + cppcheck-2.14.0/oss-fuzz/type2.cpp | 231 + cppcheck-2.14.0/oss-fuzz/type2.h | 25 + cppcheck-2.14.0/philosophy.md | 64 + cppcheck-2.14.0/platforms/aix_ppc64.xml | 18 + cppcheck-2.14.0/platforms/arm32-wchar_t2.xml | 18 + cppcheck-2.14.0/platforms/arm32-wchar_t4.xml | 18 + cppcheck-2.14.0/platforms/arm64-wchar_t2.xml | 18 + cppcheck-2.14.0/platforms/arm64-wchar_t4.xml | 18 + cppcheck-2.14.0/platforms/avr8.xml | 18 + .../platforms/cppcheck-platforms.rng | 48 + cppcheck-2.14.0/platforms/cray_sv1.xml | 18 + cppcheck-2.14.0/platforms/elbrus-e1cp.xml | 18 + cppcheck-2.14.0/platforms/mips32.xml | 18 + .../platforms/msp430_eabi_large_datamodel.xml | 18 + cppcheck-2.14.0/platforms/pic16.xml | 18 + cppcheck-2.14.0/platforms/pic8-enhanced.xml | 18 + cppcheck-2.14.0/platforms/pic8.xml | 18 + cppcheck-2.14.0/platforms/unix32-unsigned.xml | 18 + cppcheck-2.14.0/platforms/unix64-unsigned.xml | 18 + cppcheck-2.14.0/pylintrc_travis | 23 + cppcheck-2.14.0/readme.md | 275 + cppcheck-2.14.0/readme.txt | 123 + cppcheck-2.14.0/readmeja.md | 140 + cppcheck-2.14.0/releasenotes.txt | 34 + cppcheck-2.14.0/requirements.txt | 1 + cppcheck-2.14.0/rules/empty-catch-block.xml | 9 + cppcheck-2.14.0/rules/error-reporting.xml | 10 + cppcheck-2.14.0/rules/show-all-defines.rule | 11 + cppcheck-2.14.0/rules/stl.xml | 11 + cppcheck-2.14.0/rules/strlen-empty-str.xml | 9 + cppcheck-2.14.0/rules/suggest_nullptr.xml | 10 + cppcheck-2.14.0/rules/token-matching.xml | 43 + cppcheck-2.14.0/rules/unused-deref.xml | 9 + cppcheck-2.14.0/runformat | 54 + .../samples/AssignmentAddressToInteger/bad.c | 12 + .../samples/AssignmentAddressToInteger/good.c | 11 + .../AssignmentAddressToInteger/out.txt | 3 + .../samples/arrayIndexOutOfBounds_1/bad.c | 9 + .../samples/arrayIndexOutOfBounds_1/good.c | 9 + .../samples/arrayIndexOutOfBounds_1/out.txt | 3 + .../samples/arrayIndexOutOfBounds_2/bad.c | 8 + .../samples/arrayIndexOutOfBounds_2/good.c | 8 + .../samples/arrayIndexOutOfBounds_2/out.txt | 9 + cppcheck-2.14.0/samples/autoVariables/bad.c | 12 + cppcheck-2.14.0/samples/autoVariables/good.c | 13 + cppcheck-2.14.0/samples/autoVariables/out.txt | 3 + .../samples/bufferAccessOutOfBounds/bad.c | 7 + .../samples/bufferAccessOutOfBounds/good.c | 7 + .../samples/bufferAccessOutOfBounds/out.txt | 3 + .../samples/invalidContainer/bad.cpp | 14 + .../samples/invalidContainer/good.cpp | 16 + .../samples/invalidContainer/out.txt | 24 + cppcheck-2.14.0/samples/memleak/bad.c | 9 + cppcheck-2.14.0/samples/memleak/good.c | 10 + cppcheck-2.14.0/samples/memleak/out.txt | 3 + cppcheck-2.14.0/samples/resourceLeak/bad.c | 9 + cppcheck-2.14.0/samples/resourceLeak/good.c | 9 + cppcheck-2.14.0/samples/resourceLeak/out.txt | 3 + cppcheck-2.14.0/samples/syntaxError/bad.c | 6 + cppcheck-2.14.0/samples/syntaxError/good.c | 6 + cppcheck-2.14.0/samples/syntaxError/out.txt | 3 + cppcheck-2.14.0/snap/gui/cppcheck-gui.desktop | 9 + cppcheck-2.14.0/snap/gui/cppcheck-gui.png | Bin 0 -> 2543 bytes cppcheck-2.14.0/snap/snapcraft.yaml | 50 + cppcheck-2.14.0/test/CMakeLists.txt | 195 + cppcheck-2.14.0/test/cfg/boost.cpp | 107 + cppcheck-2.14.0/test/cfg/bsd.c | 175 + cppcheck-2.14.0/test/cfg/cairo.c | 26 + cppcheck-2.14.0/test/cfg/cppunit.cpp | 55 + cppcheck-2.14.0/test/cfg/gnu.c | 475 + cppcheck-2.14.0/test/cfg/googletest.cpp | 85 + cppcheck-2.14.0/test/cfg/gtk.c | 456 + cppcheck-2.14.0/test/cfg/kde.cpp | 32 + cppcheck-2.14.0/test/cfg/libcurl.c | 90 + cppcheck-2.14.0/test/cfg/libsigc++.cpp | 28 + cppcheck-2.14.0/test/cfg/lua.c | 31 + cppcheck-2.14.0/test/cfg/mfc.cpp | 26 + cppcheck-2.14.0/test/cfg/opencv2.cpp | 51 + cppcheck-2.14.0/test/cfg/openmp.c | 32 + cppcheck-2.14.0/test/cfg/openssl.c | 68 + cppcheck-2.14.0/test/cfg/posix.c | 1465 ++ cppcheck-2.14.0/test/cfg/python.c | 64 + cppcheck-2.14.0/test/cfg/qt.cpp | 777 + cppcheck-2.14.0/test/cfg/runtests.sh | 593 + cppcheck-2.14.0/test/cfg/sqlite3.c | 57 + cppcheck-2.14.0/test/cfg/std.c | 5038 ++++ cppcheck-2.14.0/test/cfg/std.cpp | 5001 ++++ cppcheck-2.14.0/test/cfg/windows.cpp | 1163 + cppcheck-2.14.0/test/cfg/wxwidgets.cpp | 1289 + .../test/cli/QML-Samples-TableView/README | 2 + .../cli/QML-Samples-TableView/TableColumn.qml | 11 + .../cli/QML-Samples-TableView/TableRow.qml | 25 + .../cli/QML-Samples-TableView/TableView.pro | 27 + .../cli/QML-Samples-TableView/TableView.qml | 30 + .../test/cli/QML-Samples-TableView/main.cpp | 25 + .../test/cli/QML-Samples-TableView/main.qml | 37 + .../test/cli/QML-Samples-TableView/qml.qrc | 8 + .../cli/QML-Samples-TableView/samplemodel.cpp | 66 + .../cli/QML-Samples-TableView/samplemodel.h | 30 + cppcheck-2.14.0/test/cli/clang-import_test.py | 153 + ...h-0f0efd6e66bd115fc0aabbfe6503230b31de5f24 | 1 + ...h-15d71125ba17344f02417a9d46443cdaa30aa17f | 1 + ...h-19219d7e7dfe8202248cd22229cdd9a2fd87a78a | 1 + ...h-20efd8856e04ca1fced9daa93837ae3384bb5e62 | 1 + ...h-2490dbc1880f2d7883c1b634deee8da3805186f8 | 1 + ...h-26edfe9761d3b681c841dfe80398847dee332f83 | 1 + ...h-3997cb1cad0af26035d36ca1a01ece07ca3fd114 | 1 + ...h-3ea64296c8518edb538e0047c3eba0792d5deeba | 1 + ...h-43fe82a87d6a7f34f000cbbc90b63ad1a58e3dcd | 1 + ...h-4d4e80f09d4733a9f724282d60ee4e3c3a143b61 | 1 + ...h-5db88ae1610f5a9782ea78af206f9cd87b6708dd | 1 + ...h-69570c88fc79e9a66ce2f2c729b455eaa237f3d2 | 1 + ...h-6eadb507c9ee93d9500028a9be2d183d1518f26b | 1 + ...h-77a4e4ffd476997f06f67e4a18b5d4eedfa71900 | 1 + ...h-7bac85061edab7fdce2889f02ea3a044242a3920 | 1 + ...h-7ead2ccf9be8b03b2d9c8c82891f58081390a560 | 1 + ...h-82986578453ec2056069c70846571775b10dfbcb | 1 + ...h-8a24e81ac1d7627233a227e6cc156dd20d57b058 | 1 + ...h-9a91d3da4e19336af500b94b2405831ed0fc1585 | 1 + ...h-9ef938bba7d752386e24f2438c73cec66f6b972b | 1 + ...h-d6609399a4398aed92f5ac9e53aa0554d9f8bbd6 | 1 + ...h-e000709d155e9c993795748ba31fddacbd5a86ac | 1 + ...h-e4a26f2d7d0a73836bf086f54e48204d8914b95a | 1 + ...h-f4ec019b9a1f357d036a9bc3c2cb6fb10a0c3ded | 1 + ...k-9543188fae87abc4409106e1918d6424efe0d68a | 1 + ...m-179e5b42a7b0ba41a088e7972bdb13dde18ef4d1 | 1 + ...t-0ee5eed9abd34e9d23640a5b82dd724affd05b79 | 1 + ...t-9598595ae3c480b58773e85cd4e4d52b1dc37038 | 1 + ...t-a0b9848dd6e98677a0a96c5fc50ad571ed5a7092 | 2 + cppcheck-2.14.0/test/cli/fuzz_test.py | 34 + .../test/cli/helloworld/helloworld.cppcheck | 6 + .../test/cli/helloworld/helloworld.sln | 31 + .../test/cli/helloworld/helloworld.vcxproj | 123 + cppcheck-2.14.0/test/cli/helloworld/main.c | 11 + cppcheck-2.14.0/test/cli/helloworld_test.py | 230 + .../test/cli/inline-suppress_test.py | 284 + .../test/cli/more-projects_test.py | 639 + cppcheck-2.14.0/test/cli/other_test.py | 1360 + cppcheck-2.14.0/test/cli/performance_test.py | 221 + cppcheck-2.14.0/test/cli/premium_test.py | 51 + .../proj-inline-suppress-unusedFunction/A.cpp | 7 + .../proj-inline-suppress-unusedFunction/B.cpp | 6 + .../proj-inline-suppress-unusedFunction/B.hpp | 6 + .../test/cli/proj-inline-suppress/1.c | 1 + .../test/cli/proj-inline-suppress/1.h | 5 + .../test/cli/proj-inline-suppress/2.c | 3 + .../test/cli/proj-inline-suppress/3.cpp | 6 + .../test/cli/proj-inline-suppress/4.c | 6 + .../cli/proj-inline-suppress/template.cpp | 15 + .../test/cli/proj-suppress-syntaxError/1.c | 8 + .../test/cli/proj-suppress-syntaxError/2.c | 1 + .../test/cli/proj-suppress-syntaxError/3.c | 2 + cppcheck-2.14.0/test/cli/proj2/a/a.c | 5 + cppcheck-2.14.0/test/cli/proj2/b/b.c | 2 + cppcheck-2.14.0/test/cli/proj2/proj2.cppcheck | 6 + cppcheck-2.14.0/test/cli/proj2/proj2.sln | 31 + cppcheck-2.14.0/test/cli/proj2/proj2.vcxproj | 124 + cppcheck-2.14.0/test/cli/proj2_test.py | 155 + cppcheck-2.14.0/test/cli/project_test.py | 151 + cppcheck-2.14.0/test/cli/qml_test.py | 65 + cppcheck-2.14.0/test/cli/readme.txt | 17 + cppcheck-2.14.0/test/cli/samples_test.py | 45 + .../test/cli/suppress-syntaxError_test.py | 29 + cppcheck-2.14.0/test/cli/testutils.py | 169 + cppcheck-2.14.0/test/cli/trac5704/trac5704a.c | 11 + cppcheck-2.14.0/test/cli/trac5704/trac5704b.c | 11 + cppcheck-2.14.0/test/cli/unusedFunction/1.c | 7 + cppcheck-2.14.0/test/cli/unusedFunction/2.c | 7 + cppcheck-2.14.0/test/cli/unusedFunction/3.c | 3 + cppcheck-2.14.0/test/cli/unusedFunction/3.h | 3 + cppcheck-2.14.0/test/cli/unusedFunction/4.c | 7 + .../unusedFunction/unusedFunction.cppcheck | 9 + .../test/cli/unused_function_test.py | 214 + cppcheck-2.14.0/test/fixture.cpp | 492 + cppcheck-2.14.0/test/fixture.h | 315 + cppcheck-2.14.0/test/helpers.cpp | 183 + cppcheck-2.14.0/test/helpers.h | 221 + cppcheck-2.14.0/test/main.cpp | 44 + cppcheck-2.14.0/test/options.cpp | 67 + cppcheck-2.14.0/test/options.h | 58 + cppcheck-2.14.0/test/precompiled.h | 32 + cppcheck-2.14.0/test/redirect.h | 132 + .../test/scripts/testrunner-single.sh | 19 + cppcheck-2.14.0/test/signal/CMakeLists.txt | 24 + .../test/signal/test-signalhandler.cpp | 78 + .../test/signal/test-signalhandler.py | 84 + .../test/signal/test-stacktrace.cpp | 48 + .../test/signal/test-stacktrace.py | 38 + cppcheck-2.14.0/test/test64bit.cpp | 303 + .../test/testanalyzerinformation.cpp | 46 + cppcheck-2.14.0/test/testassert.cpp | 250 + cppcheck-2.14.0/test/testastutils.cpp | 492 + cppcheck-2.14.0/test/testautovariables.cpp | 4558 ++++ cppcheck-2.14.0/test/testbool.cpp | 1386 + cppcheck-2.14.0/test/testboost.cpp | 98 + cppcheck-2.14.0/test/testbufferoverrun.cpp | 5641 ++++ cppcheck-2.14.0/test/testcharvar.cpp | 196 + cppcheck-2.14.0/test/testcheck.cpp | 58 + cppcheck-2.14.0/test/testclangimport.cpp | 1370 + cppcheck-2.14.0/test/testclass.cpp | 8970 +++++++ cppcheck-2.14.0/test/testcmdlineparser.cpp | 2749 ++ cppcheck-2.14.0/test/testcolor.cpp | 38 + cppcheck-2.14.0/test/testcondition.cpp | 5990 +++++ cppcheck-2.14.0/test/testconstructors.cpp | 4502 ++++ cppcheck-2.14.0/test/testcppcheck.cpp | 230 + cppcheck-2.14.0/test/testerrorlogger.cpp | 531 + cppcheck-2.14.0/test/testexceptionsafety.cpp | 451 + cppcheck-2.14.0/test/testfilelister.cpp | 126 + cppcheck-2.14.0/test/testfunctions.cpp | 2164 ++ cppcheck-2.14.0/test/testgarbage.cpp | 1877 ++ cppcheck-2.14.0/test/testimportproject.cpp | 415 + .../test/testincompletestatement.cpp | 785 + cppcheck-2.14.0/test/testinternal.cpp | 527 + cppcheck-2.14.0/test/testio.cpp | 4919 ++++ cppcheck-2.14.0/test/testleakautovar.cpp | 3316 +++ cppcheck-2.14.0/test/testlibrary.cpp | 1077 + cppcheck-2.14.0/test/testmathlib.cpp | 1498 ++ cppcheck-2.14.0/test/testmemleak.cpp | 2875 +++ cppcheck-2.14.0/test/testnullpointer.cpp | 4595 ++++ cppcheck-2.14.0/test/testoptions.cpp | 140 + cppcheck-2.14.0/test/testother.cpp | 12064 +++++++++ cppcheck-2.14.0/test/testpath.cpp | 386 + cppcheck-2.14.0/test/testpathmatch.cpp | 202 + cppcheck-2.14.0/test/testplatform.cpp | 437 + cppcheck-2.14.0/test/testpostfixoperator.cpp | 382 + cppcheck-2.14.0/test/testpreprocessor.cpp | 2484 ++ cppcheck-2.14.0/test/testprocessexecutor.cpp | 384 + cppcheck-2.14.0/test/testrunner.vcxproj | 335 + .../test/testrunner.vcxproj.filters | 310 + cppcheck-2.14.0/test/testsettings.cpp | 280 + cppcheck-2.14.0/test/testsimplifytemplate.cpp | 6460 +++++ cppcheck-2.14.0/test/testsimplifytokens.cpp | 2609 ++ cppcheck-2.14.0/test/testsimplifytypedef.cpp | 4246 +++ cppcheck-2.14.0/test/testsimplifyusing.cpp | 1495 ++ cppcheck-2.14.0/test/testsingleexecutor.cpp | 393 + cppcheck-2.14.0/test/testsizeof.cpp | 936 + cppcheck-2.14.0/test/teststl.cpp | 6856 +++++ cppcheck-2.14.0/test/teststring.cpp | 829 + cppcheck-2.14.0/test/testsummaries.cpp | 58 + cppcheck-2.14.0/test/testsuppressions.cpp | 1530 ++ cppcheck-2.14.0/test/testsymboldatabase.cpp | 10577 ++++++++ cppcheck-2.14.0/test/testthreadexecutor.cpp | 382 + cppcheck-2.14.0/test/testtimer.cpp | 45 + cppcheck-2.14.0/test/testtoken.cpp | 1199 + cppcheck-2.14.0/test/testtokenize.cpp | 8046 ++++++ cppcheck-2.14.0/test/testtokenlist.cpp | 149 + cppcheck-2.14.0/test/testtokenrange.cpp | 131 + cppcheck-2.14.0/test/testtype.cpp | 517 + cppcheck-2.14.0/test/testuninitvar.cpp | 7877 ++++++ cppcheck-2.14.0/test/testunusedfunctions.cpp | 692 + cppcheck-2.14.0/test/testunusedprivfunc.cpp | 881 + cppcheck-2.14.0/test/testunusedvar.cpp | 7110 +++++ cppcheck-2.14.0/test/testutils.cpp | 362 + cppcheck-2.14.0/test/testvaarg.cpp | 359 + cppcheck-2.14.0/test/testvalueflow.cpp | 8504 ++++++ cppcheck-2.14.0/test/testvarid.cpp | 4088 +++ cppcheck-2.14.0/tools/CMakeLists.txt | 1 + cppcheck-2.14.0/tools/MT-Unsafe.py | 200 + cppcheck-2.14.0/tools/bisect/README.md | 164 + cppcheck-2.14.0/tools/bisect/bisect.sh | 99 + cppcheck-2.14.0/tools/bisect/bisect_common.py | 51 + cppcheck-2.14.0/tools/bisect/bisect_hang.py | 79 + cppcheck-2.14.0/tools/bisect/bisect_res.py | 67 + cppcheck-2.14.0/tools/ci.py | 90 + .../tools/compare-normal-exhaustive.py | 213 + cppcheck-2.14.0/tools/compare.cs | 331 + cppcheck-2.14.0/tools/compare_ast_symdb.py | 89 + cppcheck-2.14.0/tools/creduce.py | 107 + cppcheck-2.14.0/tools/daca2-download.py | 176 + cppcheck-2.14.0/tools/daca2-getpackages.py | 122 + .../tools/defines/create_platform_cfg.sh | 49 + cppcheck-2.14.0/tools/defines/defines.sh | 15 + cppcheck-2.14.0/tools/defines/float.c | 43 + cppcheck-2.14.0/tools/defines/limits.c | 32 + cppcheck-2.14.0/tools/defines/readme.md | 22 + cppcheck-2.14.0/tools/defines/run_cppcheck.sh | 9 + cppcheck-2.14.0/tools/defines/stdint.c | 44 + cppcheck-2.14.0/tools/dmake/CMakeLists.txt | 32 + cppcheck-2.14.0/tools/dmake/dmake.cpp | 878 + cppcheck-2.14.0/tools/dmake/dmake.vcxproj | 113 + cppcheck-2.14.0/tools/donate-cpu-server.py | 1510 ++ cppcheck-2.14.0/tools/donate-cpu.py | 312 + cppcheck-2.14.0/tools/donate_cpu_lib.py | 778 + cppcheck-2.14.0/tools/donate_cpu_lib_test.py | 67 + .../tools/donate_cpu_server_test.py | 17 + .../tools/extract_and_run_more_tests.sh | 8 + cppcheck-2.14.0/tools/extracttests.py | 393 + .../tools/generate_and_run_more_tests.sh | 21 + cppcheck-2.14.0/tools/get_checkers.py | 300 + cppcheck-2.14.0/tools/git-pre-commit-cppcheck | 48 + cppcheck-2.14.0/tools/listErrorsWithoutCWE.py | 20 + cppcheck-2.14.0/tools/matchcompiler.py | 797 + cppcheck-2.14.0/tools/parse-glibc.py | 126 + cppcheck-2.14.0/tools/readme.md | 80 + cppcheck-2.14.0/tools/reduce.py | 353 + cppcheck-2.14.0/tools/reduce_test.py | 142 + cppcheck-2.14.0/tools/run-coverity.sh | 24 + cppcheck-2.14.0/tools/run_more_tests.sh | 111 + cppcheck-2.14.0/tools/test-my-pr.py | 229 + .../tools/test/run_donate_cpu_client_tests.sh | 49 + .../start_donate_cpu_client_productive.sh | 17 + .../start_donate_cpu_client_test_local.sh | 9 + .../start_donate_cpu_server_test_local.sh | 31 + cppcheck-2.14.0/tools/test_matchcompiler.py | 200 + cppcheck-2.14.0/tools/testrunnerify_code.sh | 3 + cppcheck-2.14.0/tools/trac-keywords.py | 37 + cppcheck-2.14.0/tools/triage/.clang-tidy | 5 + cppcheck-2.14.0/tools/triage/.gitignore | 6 + cppcheck-2.14.0/tools/triage/CMakeLists.txt | 39 + cppcheck-2.14.0/tools/triage/main.cpp | 30 + cppcheck-2.14.0/tools/triage/mainwindow.cpp | 425 + cppcheck-2.14.0/tools/triage/mainwindow.h | 75 + cppcheck-2.14.0/tools/triage/mainwindow.ui | 493 + cppcheck-2.14.0/tools/triage/readme.txt | 20 + cppcheck-2.14.0/tools/triage/triage.pro | 24 + cppcheck-2.14.0/tools/triage_py/README.md | 40 + .../tools/triage_py/triage_version.py | 276 + cppcheck-2.14.0/valgrind/testrunner.supp | 16 + cppcheck-2.14.0/webreport.sh | 21 + cppcheck-2.14.0/win_installer/GPLv3.txt | 619 + cppcheck-2.14.0/win_installer/config.wxi | 19 + cppcheck-2.14.0/win_installer/cppcheck.sln | 22 + .../win_installer/cppcheck.wixproj | 45 + cppcheck-2.14.0/win_installer/cppcheck.wxs | 288 + .../win_installer/images/banner.jpg | Bin 0 -> 2533 bytes .../win_installer/images/dialog.jpg | Bin 0 -> 6133 bytes cppcheck-2.14.0/win_installer/productInfo.wxi | 23 + cppcheck-2.14.0/win_installer/readme.txt | 45 + cppcheck.tar.gz | Bin 0 -> 3716204 bytes 结果.txt | 1567 ++ 965 files changed, 548299 insertions(+) create mode 100644 cppcheck-2.14.0/.clang-tidy create mode 100644 cppcheck-2.14.0/.codacy.yml create mode 100644 cppcheck-2.14.0/.gitattributes create mode 100644 cppcheck-2.14.0/.github/workflows/CI-cygwin.yml create mode 100644 cppcheck-2.14.0/.github/workflows/CI-mingw.yml create mode 100644 cppcheck-2.14.0/.github/workflows/CI-unixish-docker.yml create mode 100644 cppcheck-2.14.0/.github/workflows/CI-unixish.yml create mode 100644 cppcheck-2.14.0/.github/workflows/CI-windows.yml create mode 100644 cppcheck-2.14.0/.github/workflows/asan.yml create mode 100644 cppcheck-2.14.0/.github/workflows/buildman.yml create mode 100644 cppcheck-2.14.0/.github/workflows/cifuzz.yml create mode 100644 cppcheck-2.14.0/.github/workflows/clang-tidy.yml create mode 100644 cppcheck-2.14.0/.github/workflows/codeql-analysis.yml create mode 100644 cppcheck-2.14.0/.github/workflows/coverage.yml create mode 100644 cppcheck-2.14.0/.github/workflows/coverity.yml create mode 100644 cppcheck-2.14.0/.github/workflows/cppcheck-premium.yml create mode 100644 cppcheck-2.14.0/.github/workflows/format.yml create mode 100644 cppcheck-2.14.0/.github/workflows/iwyu.yml create mode 100644 cppcheck-2.14.0/.github/workflows/release-windows.yml create mode 100644 cppcheck-2.14.0/.github/workflows/scriptcheck.yml create mode 100644 cppcheck-2.14.0/.github/workflows/selfcheck.yml create mode 100644 cppcheck-2.14.0/.github/workflows/tsan.yml create mode 100644 cppcheck-2.14.0/.github/workflows/ubsan.yml create mode 100644 cppcheck-2.14.0/.github/workflows/valgrind.yml create mode 100644 cppcheck-2.14.0/.gitignore create mode 100644 cppcheck-2.14.0/.mailmap create mode 100644 cppcheck-2.14.0/.selfcheck_suppressions create mode 100644 cppcheck-2.14.0/.selfcheck_unused_suppressions create mode 100644 cppcheck-2.14.0/.travis.yml create mode 100644 cppcheck-2.14.0/.uncrustify.cfg create mode 100644 cppcheck-2.14.0/AUTHORS create mode 100644 cppcheck-2.14.0/CMakeLists.txt create mode 100644 cppcheck-2.14.0/COPYING create mode 100644 cppcheck-2.14.0/Makefile create mode 100644 cppcheck-2.14.0/addons/README.md create mode 100644 cppcheck-2.14.0/addons/ROS_naming.json create mode 100644 cppcheck-2.14.0/addons/__init__.py create mode 100644 cppcheck-2.14.0/addons/cppcheck.py create mode 100644 cppcheck-2.14.0/addons/cppcheckdata.doxyfile create mode 100644 cppcheck-2.14.0/addons/cppcheckdata.py create mode 100644 cppcheck-2.14.0/addons/doc/img/cppcheck-gui-addons.png create mode 100644 cppcheck-2.14.0/addons/doc/y2038.txt create mode 100644 cppcheck-2.14.0/addons/findcasts.py create mode 100644 cppcheck-2.14.0/addons/misc.py create mode 100644 cppcheck-2.14.0/addons/misra.py create mode 100644 cppcheck-2.14.0/addons/misra_9.py create mode 100644 cppcheck-2.14.0/addons/naming.py create mode 100644 cppcheck-2.14.0/addons/namingng.config.json create mode 100644 cppcheck-2.14.0/addons/namingng.json create mode 100644 cppcheck-2.14.0/addons/namingng.py create mode 100644 cppcheck-2.14.0/addons/runaddon.py create mode 100644 cppcheck-2.14.0/addons/test/__init__.py create mode 100644 cppcheck-2.14.0/addons/test/misc-test.cpp create mode 100644 cppcheck-2.14.0/addons/test/misra/config1.c create mode 100644 cppcheck-2.14.0/addons/test/misra/crash1.c create mode 100644 cppcheck-2.14.0/addons/test/misra/crash10.c create mode 100644 cppcheck-2.14.0/addons/test/misra/crash2.c create mode 100644 cppcheck-2.14.0/addons/test/misra/crash3.c create mode 100644 cppcheck-2.14.0/addons/test/misra/crash4.c create mode 100644 cppcheck-2.14.0/addons/test/misra/crash5.c create mode 100644 cppcheck-2.14.0/addons/test/misra/crash6.c create mode 100644 cppcheck-2.14.0/addons/test/misra/crash7.c create mode 100644 cppcheck-2.14.0/addons/test/misra/crash8.c create mode 100644 cppcheck-2.14.0/addons/test/misra/crash9.c create mode 100644 cppcheck-2.14.0/addons/test/misra/crash_misra9_parseInitializer.c create mode 100644 cppcheck-2.14.0/addons/test/misra/misra-ctu-1-test.c create mode 100644 cppcheck-2.14.0/addons/test/misra/misra-ctu-2-test.c create mode 100644 cppcheck-2.14.0/addons/test/misra/misra-ctu-test.h create mode 100644 cppcheck-2.14.0/addons/test/misra/misra-suppressions1-test.c create mode 100644 cppcheck-2.14.0/addons/test/misra/misra-suppressions2-test.c create mode 100644 cppcheck-2.14.0/addons/test/misra/misra-test-avr8.c create mode 100644 cppcheck-2.14.0/addons/test/misra/misra-test-c11.c create mode 100644 cppcheck-2.14.0/addons/test/misra/misra-test.c create mode 100644 cppcheck-2.14.0/addons/test/misra/misra-test.cpp create mode 100644 cppcheck-2.14.0/addons/test/misra/misra-test.h create mode 100644 cppcheck-2.14.0/addons/test/misra/misra2012_rules_dummy_ascii.txt create mode 100644 cppcheck-2.14.0/addons/test/misra/misra2012_rules_dummy_utf8.txt create mode 100644 cppcheck-2.14.0/addons/test/misra/misra2012_rules_dummy_windows1250.txt create mode 100644 cppcheck-2.14.0/addons/test/misra/misra_rules_dummy.txt create mode 100644 cppcheck-2.14.0/addons/test/misra/misra_rules_empty_lines.txt create mode 100644 cppcheck-2.14.0/addons/test/misra/misra_rules_multiple_lines.txt create mode 100644 cppcheck-2.14.0/addons/test/misra/misra_rules_structure.txt create mode 100644 cppcheck-2.14.0/addons/test/misra/suppressions.txt create mode 100644 cppcheck-2.14.0/addons/test/misra_test.py create mode 100644 cppcheck-2.14.0/addons/test/naming_test.c create mode 100644 cppcheck-2.14.0/addons/test/naming_test.cpp create mode 100644 cppcheck-2.14.0/addons/test/path1/misra-suppressions1-test.c create mode 100644 cppcheck-2.14.0/addons/test/path1/misra-suppressions2-test.c create mode 100644 cppcheck-2.14.0/addons/test/threadsafety/MT-Unsafe.cpp create mode 100644 cppcheck-2.14.0/addons/test/threadsafety/local_static.cpp create mode 100644 cppcheck-2.14.0/addons/test/threadsafety/local_static_const.cpp create mode 100644 cppcheck-2.14.0/addons/test/util.py create mode 100644 cppcheck-2.14.0/addons/test/y2038/y2038-inc.h create mode 100644 cppcheck-2.14.0/addons/test/y2038/y2038-test-1-bad-time-bits.c create mode 100644 cppcheck-2.14.0/addons/test/y2038/y2038-test-2-no-time-bits.c create mode 100644 cppcheck-2.14.0/addons/test/y2038/y2038-test-3-no-use-time-bits.c create mode 100644 cppcheck-2.14.0/addons/test/y2038/y2038-test-4-good.c create mode 100644 cppcheck-2.14.0/addons/test/y2038/y2038-test-5-good-no-time-used.c create mode 100644 cppcheck-2.14.0/addons/test/y2038_test.py create mode 100644 cppcheck-2.14.0/addons/threadsafety.py create mode 100644 cppcheck-2.14.0/addons/y2038.py create mode 100644 cppcheck-2.14.0/build-pcre.txt create mode 100644 cppcheck-2.14.0/cfg/avr.cfg create mode 100644 cppcheck-2.14.0/cfg/bento4.cfg create mode 100644 cppcheck-2.14.0/cfg/boost.cfg create mode 100644 cppcheck-2.14.0/cfg/bsd.cfg create mode 100644 cppcheck-2.14.0/cfg/cairo.cfg create mode 100644 cppcheck-2.14.0/cfg/cppcheck-cfg.rng create mode 100644 cppcheck-2.14.0/cfg/cppcheck-lib.cfg create mode 100644 cppcheck-2.14.0/cfg/cppunit.cfg create mode 100644 cppcheck-2.14.0/cfg/dpdk.cfg create mode 100644 cppcheck-2.14.0/cfg/embedded_sql.cfg create mode 100644 cppcheck-2.14.0/cfg/emscripten.cfg create mode 100644 cppcheck-2.14.0/cfg/ginac.cfg create mode 100644 cppcheck-2.14.0/cfg/gnu.cfg create mode 100644 cppcheck-2.14.0/cfg/googletest.cfg create mode 100644 cppcheck-2.14.0/cfg/gtk.cfg create mode 100644 cppcheck-2.14.0/cfg/icu.cfg create mode 100644 cppcheck-2.14.0/cfg/kde.cfg create mode 100644 cppcheck-2.14.0/cfg/libcerror.cfg create mode 100644 cppcheck-2.14.0/cfg/libcurl.cfg create mode 100644 cppcheck-2.14.0/cfg/libsigc++.cfg create mode 100644 cppcheck-2.14.0/cfg/lua.cfg create mode 100644 cppcheck-2.14.0/cfg/mfc.cfg create mode 100644 cppcheck-2.14.0/cfg/microsoft_atl.cfg create mode 100644 cppcheck-2.14.0/cfg/microsoft_sal.cfg create mode 100644 cppcheck-2.14.0/cfg/microsoft_unittest.cfg create mode 100644 cppcheck-2.14.0/cfg/motif.cfg create mode 100644 cppcheck-2.14.0/cfg/nspr.cfg create mode 100644 cppcheck-2.14.0/cfg/ntl.cfg create mode 100644 cppcheck-2.14.0/cfg/opencv2.cfg create mode 100644 cppcheck-2.14.0/cfg/opengl.cfg create mode 100644 cppcheck-2.14.0/cfg/openmp.cfg create mode 100644 cppcheck-2.14.0/cfg/openssl.cfg create mode 100644 cppcheck-2.14.0/cfg/pcre.cfg create mode 100644 cppcheck-2.14.0/cfg/posix.cfg create mode 100644 cppcheck-2.14.0/cfg/python.cfg create mode 100644 cppcheck-2.14.0/cfg/qt.cfg create mode 100644 cppcheck-2.14.0/cfg/ruby.cfg create mode 100644 cppcheck-2.14.0/cfg/sdl.cfg create mode 100644 cppcheck-2.14.0/cfg/sfml.cfg create mode 100644 cppcheck-2.14.0/cfg/sqlite3.cfg create mode 100644 cppcheck-2.14.0/cfg/std.cfg create mode 100644 cppcheck-2.14.0/cfg/tinyxml2.cfg create mode 100644 cppcheck-2.14.0/cfg/vcl.cfg create mode 100644 cppcheck-2.14.0/cfg/windows.cfg create mode 100644 cppcheck-2.14.0/cfg/wxsqlite3.cfg create mode 100644 cppcheck-2.14.0/cfg/wxsvg.cfg create mode 100644 cppcheck-2.14.0/cfg/wxwidgets.cfg create mode 100644 cppcheck-2.14.0/cfg/zephyr.cfg create mode 100644 cppcheck-2.14.0/cfg/zlib.cfg create mode 100644 cppcheck-2.14.0/clang-tidy.md create mode 100644 cppcheck-2.14.0/cli/CMakeLists.txt create mode 100644 cppcheck-2.14.0/cli/cli.vcxproj create mode 100644 cppcheck-2.14.0/cli/cli.vcxproj.filters create mode 100644 cppcheck-2.14.0/cli/cmdlinelogger.h create mode 100644 cppcheck-2.14.0/cli/cmdlineparser.cpp create mode 100644 cppcheck-2.14.0/cli/cmdlineparser.h create mode 100644 cppcheck-2.14.0/cli/cppcheckexecutor.cpp create mode 100644 cppcheck-2.14.0/cli/cppcheckexecutor.h create mode 100644 cppcheck-2.14.0/cli/cppcheckexecutorseh.cpp create mode 100644 cppcheck-2.14.0/cli/cppcheckexecutorseh.h create mode 100644 cppcheck-2.14.0/cli/executor.cpp create mode 100644 cppcheck-2.14.0/cli/executor.h create mode 100644 cppcheck-2.14.0/cli/filelister.cpp create mode 100644 cppcheck-2.14.0/cli/filelister.h create mode 100644 cppcheck-2.14.0/cli/main.cpp create mode 100644 cppcheck-2.14.0/cli/precompiled.h create mode 100644 cppcheck-2.14.0/cli/processexecutor.cpp create mode 100644 cppcheck-2.14.0/cli/processexecutor.h create mode 100644 cppcheck-2.14.0/cli/signalhandler.cpp create mode 100644 cppcheck-2.14.0/cli/signalhandler.h create mode 100644 cppcheck-2.14.0/cli/singleexecutor.cpp create mode 100644 cppcheck-2.14.0/cli/singleexecutor.h create mode 100644 cppcheck-2.14.0/cli/stacktrace.cpp create mode 100644 cppcheck-2.14.0/cli/stacktrace.h create mode 100644 cppcheck-2.14.0/cli/threadexecutor.cpp create mode 100644 cppcheck-2.14.0/cli/threadexecutor.h create mode 100644 cppcheck-2.14.0/cli/version.rc create mode 100644 cppcheck-2.14.0/cmake/buildFiles.cmake create mode 100644 cppcheck-2.14.0/cmake/clang_tidy.cmake create mode 100644 cppcheck-2.14.0/cmake/cmake_uninstall.cmake.in create mode 100644 cppcheck-2.14.0/cmake/compilerCheck.cmake create mode 100644 cppcheck-2.14.0/cmake/compilerDefinitions.cmake create mode 100644 cppcheck-2.14.0/cmake/compileroptions.cmake create mode 100644 cppcheck-2.14.0/cmake/cxx11.cmake create mode 100644 cppcheck-2.14.0/cmake/dynamic_analyzer_options.cmake create mode 100644 cppcheck-2.14.0/cmake/findDependencies.cmake create mode 100644 cppcheck-2.14.0/cmake/options.cmake create mode 100644 cppcheck-2.14.0/cmake/printInfo.cmake create mode 100644 cppcheck-2.14.0/cmake/qtCompat.cmake create mode 100644 cppcheck-2.14.0/cmake/versions.cmake create mode 100644 cppcheck-2.14.0/codecov.yml create mode 100644 cppcheck-2.14.0/console_common.pri create mode 100644 cppcheck-2.14.0/cppcheck-errors.rng create mode 100644 cppcheck-2.14.0/cppcheck.cppcheck create mode 100644 cppcheck-2.14.0/cppcheck.sln create mode 100644 cppcheck-2.14.0/cppcheckpremium-suppressions create mode 100644 cppcheck-2.14.0/createrelease create mode 100644 cppcheck-2.14.0/democlient/build.sh create mode 100644 cppcheck-2.14.0/democlient/democlient.cpp create mode 100644 cppcheck-2.14.0/doxyfile create mode 100644 cppcheck-2.14.0/externals/.clang-tidy create mode 100644 cppcheck-2.14.0/externals/externals.pri create mode 100644 cppcheck-2.14.0/externals/picojson/LICENSE create mode 100644 cppcheck-2.14.0/externals/picojson/picojson.h create mode 100644 cppcheck-2.14.0/externals/simplecpp/CMakeLists.txt create mode 100644 cppcheck-2.14.0/externals/simplecpp/LICENSE create mode 100644 cppcheck-2.14.0/externals/simplecpp/simplecpp.cpp create mode 100644 cppcheck-2.14.0/externals/simplecpp/simplecpp.h create mode 100644 cppcheck-2.14.0/externals/tinyxml2/CMakeLists.txt create mode 100644 cppcheck-2.14.0/externals/tinyxml2/LICENSE create mode 100644 cppcheck-2.14.0/externals/tinyxml2/tinyxml2.cpp create mode 100644 cppcheck-2.14.0/externals/tinyxml2/tinyxml2.h create mode 100644 cppcheck-2.14.0/generate_coverage_report create mode 100644 cppcheck-2.14.0/gui/.clang-tidy create mode 100644 cppcheck-2.14.0/gui/CMakeLists.txt create mode 100644 cppcheck-2.14.0/gui/about.ui create mode 100644 cppcheck-2.14.0/gui/aboutdialog.cpp create mode 100644 cppcheck-2.14.0/gui/aboutdialog.h create mode 100644 cppcheck-2.14.0/gui/application.cpp create mode 100644 cppcheck-2.14.0/gui/application.h create mode 100644 cppcheck-2.14.0/gui/applicationdialog.cpp create mode 100644 cppcheck-2.14.0/gui/applicationdialog.h create mode 100644 cppcheck-2.14.0/gui/applicationdialog.ui create mode 100644 cppcheck-2.14.0/gui/applicationlist.cpp create mode 100644 cppcheck-2.14.0/gui/applicationlist.h create mode 100644 cppcheck-2.14.0/gui/checkstatistics.cpp create mode 100644 cppcheck-2.14.0/gui/checkstatistics.h create mode 100644 cppcheck-2.14.0/gui/checkthread.cpp create mode 100644 cppcheck-2.14.0/gui/checkthread.h create mode 100644 cppcheck-2.14.0/gui/codeeditor.cpp create mode 100644 cppcheck-2.14.0/gui/codeeditor.h create mode 100644 cppcheck-2.14.0/gui/codeeditorstyle.cpp create mode 100644 cppcheck-2.14.0/gui/codeeditorstyle.h create mode 100644 cppcheck-2.14.0/gui/codeeditstylecontrols.cpp create mode 100644 cppcheck-2.14.0/gui/codeeditstylecontrols.h create mode 100644 cppcheck-2.14.0/gui/codeeditstyledialog.cpp create mode 100644 cppcheck-2.14.0/gui/codeeditstyledialog.h create mode 100644 cppcheck-2.14.0/gui/common.cpp create mode 100644 cppcheck-2.14.0/gui/common.h create mode 100644 cppcheck-2.14.0/gui/compliancereportdialog.cpp create mode 100644 cppcheck-2.14.0/gui/compliancereportdialog.h create mode 100644 cppcheck-2.14.0/gui/compliancereportdialog.ui create mode 100644 cppcheck-2.14.0/gui/cppcheck-gui.desktop create mode 100644 cppcheck-2.14.0/gui/cppcheck-gui.png create mode 100644 cppcheck-2.14.0/gui/cppcheck-gui.rc create mode 100644 cppcheck-2.14.0/gui/cppcheck-gui.svg create mode 100644 cppcheck-2.14.0/gui/cppcheck.ico create mode 100644 cppcheck-2.14.0/gui/cppcheck_de.ts create mode 100644 cppcheck-2.14.0/gui/cppcheck_es.ts create mode 100644 cppcheck-2.14.0/gui/cppcheck_fi.ts create mode 100644 cppcheck-2.14.0/gui/cppcheck_fr.ts create mode 100644 cppcheck-2.14.0/gui/cppcheck_it.ts create mode 100644 cppcheck-2.14.0/gui/cppcheck_ja.ts create mode 100644 cppcheck-2.14.0/gui/cppcheck_ko.ts create mode 100644 cppcheck-2.14.0/gui/cppcheck_nl.ts create mode 100644 cppcheck-2.14.0/gui/cppcheck_ru.ts create mode 100644 cppcheck-2.14.0/gui/cppcheck_sr.ts create mode 100644 cppcheck-2.14.0/gui/cppcheck_sv.ts create mode 100644 cppcheck-2.14.0/gui/cppcheck_zh_CN.ts create mode 100644 cppcheck-2.14.0/gui/cppcheck_zh_TW.ts create mode 100644 cppcheck-2.14.0/gui/cppchecklibrarydata.cpp create mode 100644 cppcheck-2.14.0/gui/cppchecklibrarydata.h create mode 100644 cppcheck-2.14.0/gui/csvreport.cpp create mode 100644 cppcheck-2.14.0/gui/csvreport.h create mode 100644 cppcheck-2.14.0/gui/erroritem.cpp create mode 100644 cppcheck-2.14.0/gui/erroritem.h create mode 100644 cppcheck-2.14.0/gui/filelist.cpp create mode 100644 cppcheck-2.14.0/gui/filelist.h create mode 100644 cppcheck-2.14.0/gui/fileview.ui create mode 100644 cppcheck-2.14.0/gui/fileviewdialog.cpp create mode 100644 cppcheck-2.14.0/gui/fileviewdialog.h create mode 100644 cppcheck-2.14.0/gui/gui.cppcheck create mode 100644 cppcheck-2.14.0/gui/gui.pro create mode 100644 cppcheck-2.14.0/gui/gui.qrc create mode 100644 cppcheck-2.14.0/gui/help/images/index-mainwindow.png create mode 100644 cppcheck-2.14.0/gui/help/images/severities-error.png create mode 100644 cppcheck-2.14.0/gui/help/images/severities-information.png create mode 100644 cppcheck-2.14.0/gui/help/images/severities-performance.png create mode 100644 cppcheck-2.14.0/gui/help/images/severities-portability.png create mode 100644 cppcheck-2.14.0/gui/help/images/severities-style.png create mode 100644 cppcheck-2.14.0/gui/help/images/severities-warning.png create mode 100644 cppcheck-2.14.0/gui/help/images/walkthrough-analysis.png create mode 100644 cppcheck-2.14.0/gui/help/images/walkthrough-import-project.png create mode 100644 cppcheck-2.14.0/gui/help/images/walkthrough-library.png create mode 100644 cppcheck-2.14.0/gui/help/images/walkthrough-new-project.png create mode 100644 cppcheck-2.14.0/gui/help/images/walkthrough-toolbar-severities.png create mode 100644 cppcheck-2.14.0/gui/help/images/walkthrough-warning-menu.png create mode 100644 cppcheck-2.14.0/gui/help/index.html create mode 100644 cppcheck-2.14.0/gui/help/investigating-warnings.html create mode 100644 cppcheck-2.14.0/gui/help/manual.html create mode 100644 cppcheck-2.14.0/gui/help/online-help.qhcp create mode 100644 cppcheck-2.14.0/gui/help/online-help.qhp create mode 100644 cppcheck-2.14.0/gui/help/preferences.html create mode 100644 cppcheck-2.14.0/gui/help/projectfiledialog.html create mode 100644 cppcheck-2.14.0/gui/help/severities.html create mode 100644 cppcheck-2.14.0/gui/help/standalone-analysis.html create mode 100644 cppcheck-2.14.0/gui/help/tagging.html create mode 100644 cppcheck-2.14.0/gui/help/walkthrough.html create mode 100644 cppcheck-2.14.0/gui/helpdialog.cpp create mode 100644 cppcheck-2.14.0/gui/helpdialog.h create mode 100644 cppcheck-2.14.0/gui/helpdialog.ui create mode 100644 cppcheck-2.14.0/gui/images/applications-development.png create mode 100644 cppcheck-2.14.0/gui/images/applications-system.png create mode 100644 cppcheck-2.14.0/gui/images/dialog-error.png create mode 100644 cppcheck-2.14.0/gui/images/dialog-information.png create mode 100644 cppcheck-2.14.0/gui/images/dialog-warning.png create mode 100644 cppcheck-2.14.0/gui/images/edit-clear.png create mode 100644 cppcheck-2.14.0/gui/images/go-down.png create mode 100644 cppcheck-2.14.0/gui/images/go-home.png create mode 100644 cppcheck-2.14.0/gui/images/go-next.png create mode 100644 cppcheck-2.14.0/gui/images/go-previous.png create mode 100644 cppcheck-2.14.0/gui/images/help-browser.png create mode 100644 cppcheck-2.14.0/gui/images/llvm-dragon.png create mode 100644 cppcheck-2.14.0/gui/images/llvm-dragon.svg create mode 100644 cppcheck-2.14.0/gui/images/media-floppy.png create mode 100644 cppcheck-2.14.0/gui/images/openproject.png create mode 100644 cppcheck-2.14.0/gui/images/preferences-system.png create mode 100644 cppcheck-2.14.0/gui/images/process-stop.png create mode 100644 cppcheck-2.14.0/gui/images/scratchpad.png create mode 100644 cppcheck-2.14.0/gui/images/showerrors.png create mode 100644 cppcheck-2.14.0/gui/images/showperformance.png create mode 100644 cppcheck-2.14.0/gui/images/showstylewarnings.png create mode 100644 cppcheck-2.14.0/gui/images/showwarnings.png create mode 100644 cppcheck-2.14.0/gui/images/text-x-generic.png create mode 100644 cppcheck-2.14.0/gui/images/utilities-system-monitor.png create mode 100644 cppcheck-2.14.0/gui/images/verify.svg create mode 100644 cppcheck-2.14.0/gui/images/view-recheck.png create mode 100644 cppcheck-2.14.0/gui/images/view-refresh.png create mode 100644 cppcheck-2.14.0/gui/libraryaddfunctiondialog.cpp create mode 100644 cppcheck-2.14.0/gui/libraryaddfunctiondialog.h create mode 100644 cppcheck-2.14.0/gui/libraryaddfunctiondialog.ui create mode 100644 cppcheck-2.14.0/gui/librarydialog.cpp create mode 100644 cppcheck-2.14.0/gui/librarydialog.h create mode 100644 cppcheck-2.14.0/gui/librarydialog.ui create mode 100644 cppcheck-2.14.0/gui/libraryeditargdialog.cpp create mode 100644 cppcheck-2.14.0/gui/libraryeditargdialog.h create mode 100644 cppcheck-2.14.0/gui/libraryeditargdialog.ui create mode 100644 cppcheck-2.14.0/gui/main.cpp create mode 100644 cppcheck-2.14.0/gui/mainwindow.cpp create mode 100644 cppcheck-2.14.0/gui/mainwindow.h create mode 100644 cppcheck-2.14.0/gui/mainwindow.ui create mode 100644 cppcheck-2.14.0/gui/newsuppressiondialog.cpp create mode 100644 cppcheck-2.14.0/gui/newsuppressiondialog.h create mode 100644 cppcheck-2.14.0/gui/newsuppressiondialog.ui create mode 100644 cppcheck-2.14.0/gui/platforms.cpp create mode 100644 cppcheck-2.14.0/gui/platforms.h create mode 100644 cppcheck-2.14.0/gui/precompiled.h create mode 100644 cppcheck-2.14.0/gui/precompiled_qmake.h create mode 100644 cppcheck-2.14.0/gui/printablereport.cpp create mode 100644 cppcheck-2.14.0/gui/printablereport.h create mode 100644 cppcheck-2.14.0/gui/projectfile.cpp create mode 100644 cppcheck-2.14.0/gui/projectfile.h create mode 100644 cppcheck-2.14.0/gui/projectfile.txt create mode 100644 cppcheck-2.14.0/gui/projectfile.ui create mode 100644 cppcheck-2.14.0/gui/projectfiledialog.cpp create mode 100644 cppcheck-2.14.0/gui/projectfiledialog.h create mode 100644 cppcheck-2.14.0/gui/readme.txt create mode 100644 cppcheck-2.14.0/gui/report.cpp create mode 100644 cppcheck-2.14.0/gui/report.h create mode 100644 cppcheck-2.14.0/gui/resultstree.cpp create mode 100644 cppcheck-2.14.0/gui/resultstree.h create mode 100644 cppcheck-2.14.0/gui/resultsview.cpp create mode 100644 cppcheck-2.14.0/gui/resultsview.h create mode 100644 cppcheck-2.14.0/gui/resultsview.ui create mode 100644 cppcheck-2.14.0/gui/scratchpad.cpp create mode 100644 cppcheck-2.14.0/gui/scratchpad.h create mode 100644 cppcheck-2.14.0/gui/scratchpad.ui create mode 100644 cppcheck-2.14.0/gui/settings.ui create mode 100644 cppcheck-2.14.0/gui/settingsdialog.cpp create mode 100644 cppcheck-2.14.0/gui/settingsdialog.h create mode 100644 cppcheck-2.14.0/gui/showtypes.cpp create mode 100644 cppcheck-2.14.0/gui/showtypes.h create mode 100644 cppcheck-2.14.0/gui/statsdialog.cpp create mode 100644 cppcheck-2.14.0/gui/statsdialog.h create mode 100644 cppcheck-2.14.0/gui/statsdialog.ui create mode 100644 cppcheck-2.14.0/gui/test/CMakeLists.txt create mode 100644 cppcheck-2.14.0/gui/test/common.pri create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/CMakeLists.txt create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/cppchecklibrarydata.pro create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/container_unhandled_element.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/container_valid.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/define_valid.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/mandatory_attribute_missing.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/markup_mandatory_attribute_missing.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/markup_unhandled_element.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/markup_valid.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/memory_resource_unhandled_element.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/memory_resource_valid.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/platform_type_unhandled_element.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/platform_type_valid.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/podtype_valid.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/reflection_mandatory_attribute_missing.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/reflection_unhandled_element.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/reflection_valid.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/smartptr_valid.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/typechecks_valid.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/undefine_valid.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/unhandled_element.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/xml_reader_error.cfg create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/resources.qrc create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp create mode 100644 cppcheck-2.14.0/gui/test/cppchecklibrarydata/testcppchecklibrarydata.h create mode 100644 cppcheck-2.14.0/gui/test/data/files/bar1 create mode 100644 cppcheck-2.14.0/gui/test/data/files/bar1.foo create mode 100644 cppcheck-2.14.0/gui/test/data/files/dir1/dir11/foo11.cpp create mode 100644 cppcheck-2.14.0/gui/test/data/files/dir1/foo1.cpp create mode 100644 cppcheck-2.14.0/gui/test/data/files/dir2/foo1.cpp create mode 100644 cppcheck-2.14.0/gui/test/data/files/foo1.cpp create mode 100644 cppcheck-2.14.0/gui/test/data/files/foo2.cxx create mode 100644 cppcheck-2.14.0/gui/test/data/files/foo3.cc create mode 100644 cppcheck-2.14.0/gui/test/data/files/foo4.c create mode 100644 cppcheck-2.14.0/gui/test/data/files/foo5.c++ create mode 100644 cppcheck-2.14.0/gui/test/data/files/foo6.txx create mode 100644 cppcheck-2.14.0/gui/test/data/files/foo7.tpp create mode 100644 cppcheck-2.14.0/gui/test/data/files/foo8.ipp create mode 100644 cppcheck-2.14.0/gui/test/data/files/foo9.ixx create mode 100644 cppcheck-2.14.0/gui/test/data/projectfiles/simple.cppcheck create mode 100644 cppcheck-2.14.0/gui/test/data/projectfiles/simple_ignore.cppcheck create mode 100644 cppcheck-2.14.0/gui/test/data/projectfiles/simple_noroot.cppcheck create mode 100644 cppcheck-2.14.0/gui/test/data/xmlfiles/xmlreport_v2.xml create mode 100644 cppcheck-2.14.0/gui/test/filelist/CMakeLists.txt create mode 100644 cppcheck-2.14.0/gui/test/filelist/filelist.pro create mode 100644 cppcheck-2.14.0/gui/test/filelist/testfilelist.cpp create mode 100644 cppcheck-2.14.0/gui/test/filelist/testfilelist.h create mode 100644 cppcheck-2.14.0/gui/test/projectfile/CMakeLists.txt create mode 100644 cppcheck-2.14.0/gui/test/projectfile/projectfile.pro create mode 100644 cppcheck-2.14.0/gui/test/projectfile/testprojectfile.cpp create mode 100644 cppcheck-2.14.0/gui/test/projectfile/testprojectfile.h create mode 100644 cppcheck-2.14.0/gui/test/readme.txt create mode 100644 cppcheck-2.14.0/gui/test/test.pro create mode 100644 cppcheck-2.14.0/gui/test/translationhandler/CMakeLists.txt create mode 100644 cppcheck-2.14.0/gui/test/translationhandler/testtranslationhandler.cpp create mode 100644 cppcheck-2.14.0/gui/test/translationhandler/testtranslationhandler.h create mode 100644 cppcheck-2.14.0/gui/test/translationhandler/translationhandler.pro create mode 100644 cppcheck-2.14.0/gui/test/xmlreportv2/CMakeLists.txt create mode 100644 cppcheck-2.14.0/gui/test/xmlreportv2/testxmlreportv2.cpp create mode 100644 cppcheck-2.14.0/gui/test/xmlreportv2/testxmlreportv2.h create mode 100644 cppcheck-2.14.0/gui/test/xmlreportv2/xmlreportv2.pro create mode 100644 cppcheck-2.14.0/gui/threadhandler.cpp create mode 100644 cppcheck-2.14.0/gui/threadhandler.h create mode 100644 cppcheck-2.14.0/gui/threadresult.cpp create mode 100644 cppcheck-2.14.0/gui/threadresult.h create mode 100644 cppcheck-2.14.0/gui/translationhandler.cpp create mode 100644 cppcheck-2.14.0/gui/translationhandler.h create mode 100644 cppcheck-2.14.0/gui/txtreport.cpp create mode 100644 cppcheck-2.14.0/gui/txtreport.h create mode 100644 cppcheck-2.14.0/gui/xmlreport.cpp create mode 100644 cppcheck-2.14.0/gui/xmlreport.h create mode 100644 cppcheck-2.14.0/gui/xmlreportv2.cpp create mode 100644 cppcheck-2.14.0/gui/xmlreportv2.h create mode 100644 cppcheck-2.14.0/htmlreport/README.txt create mode 100644 cppcheck-2.14.0/htmlreport/check.sh create mode 100644 cppcheck-2.14.0/htmlreport/cppcheck-htmlreport create mode 100644 cppcheck-2.14.0/htmlreport/example.cc create mode 100644 cppcheck-2.14.0/htmlreport/example.xml create mode 100644 cppcheck-2.14.0/htmlreport/requirements.txt create mode 100644 cppcheck-2.14.0/htmlreport/setup.py create mode 100644 cppcheck-2.14.0/htmlreport/test_htmlreport.py create mode 100644 cppcheck-2.14.0/htmlreport/test_suppressions.txt create mode 100644 cppcheck-2.14.0/htmlreport/tox.ini create mode 100644 cppcheck-2.14.0/lib/CMakeLists.txt create mode 100644 cppcheck-2.14.0/lib/addoninfo.cpp create mode 100644 cppcheck-2.14.0/lib/addoninfo.h create mode 100644 cppcheck-2.14.0/lib/analyzer.h create mode 100644 cppcheck-2.14.0/lib/analyzerinfo.cpp create mode 100644 cppcheck-2.14.0/lib/analyzerinfo.h create mode 100644 cppcheck-2.14.0/lib/astutils.cpp create mode 100644 cppcheck-2.14.0/lib/astutils.h create mode 100644 cppcheck-2.14.0/lib/calculate.h create mode 100644 cppcheck-2.14.0/lib/check.cpp create mode 100644 cppcheck-2.14.0/lib/check.h create mode 100644 cppcheck-2.14.0/lib/check64bit.cpp create mode 100644 cppcheck-2.14.0/lib/check64bit.h create mode 100644 cppcheck-2.14.0/lib/checkassert.cpp create mode 100644 cppcheck-2.14.0/lib/checkassert.h create mode 100644 cppcheck-2.14.0/lib/checkautovariables.cpp create mode 100644 cppcheck-2.14.0/lib/checkautovariables.h create mode 100644 cppcheck-2.14.0/lib/checkbool.cpp create mode 100644 cppcheck-2.14.0/lib/checkbool.h create mode 100644 cppcheck-2.14.0/lib/checkboost.cpp create mode 100644 cppcheck-2.14.0/lib/checkboost.h create mode 100644 cppcheck-2.14.0/lib/checkbufferoverrun.cpp create mode 100644 cppcheck-2.14.0/lib/checkbufferoverrun.h create mode 100644 cppcheck-2.14.0/lib/checkclass.cpp create mode 100644 cppcheck-2.14.0/lib/checkclass.h create mode 100644 cppcheck-2.14.0/lib/checkcondition.cpp create mode 100644 cppcheck-2.14.0/lib/checkcondition.h create mode 100644 cppcheck-2.14.0/lib/checkers.cpp create mode 100644 cppcheck-2.14.0/lib/checkers.h create mode 100644 cppcheck-2.14.0/lib/checkersreport.cpp create mode 100644 cppcheck-2.14.0/lib/checkersreport.h create mode 100644 cppcheck-2.14.0/lib/checkexceptionsafety.cpp create mode 100644 cppcheck-2.14.0/lib/checkexceptionsafety.h create mode 100644 cppcheck-2.14.0/lib/checkfunctions.cpp create mode 100644 cppcheck-2.14.0/lib/checkfunctions.h create mode 100644 cppcheck-2.14.0/lib/checkinternal.cpp create mode 100644 cppcheck-2.14.0/lib/checkinternal.h create mode 100644 cppcheck-2.14.0/lib/checkio.cpp create mode 100644 cppcheck-2.14.0/lib/checkio.h create mode 100644 cppcheck-2.14.0/lib/checkleakautovar.cpp create mode 100644 cppcheck-2.14.0/lib/checkleakautovar.h create mode 100644 cppcheck-2.14.0/lib/checkmemoryleak.cpp create mode 100644 cppcheck-2.14.0/lib/checkmemoryleak.h create mode 100644 cppcheck-2.14.0/lib/checknullpointer.cpp create mode 100644 cppcheck-2.14.0/lib/checknullpointer.h create mode 100644 cppcheck-2.14.0/lib/checkother.cpp create mode 100644 cppcheck-2.14.0/lib/checkother.h create mode 100644 cppcheck-2.14.0/lib/checkpostfixoperator.cpp create mode 100644 cppcheck-2.14.0/lib/checkpostfixoperator.h create mode 100644 cppcheck-2.14.0/lib/checksizeof.cpp create mode 100644 cppcheck-2.14.0/lib/checksizeof.h create mode 100644 cppcheck-2.14.0/lib/checkstl.cpp create mode 100644 cppcheck-2.14.0/lib/checkstl.h create mode 100644 cppcheck-2.14.0/lib/checkstring.cpp create mode 100644 cppcheck-2.14.0/lib/checkstring.h create mode 100644 cppcheck-2.14.0/lib/checktype.cpp create mode 100644 cppcheck-2.14.0/lib/checktype.h create mode 100644 cppcheck-2.14.0/lib/checkuninitvar.cpp create mode 100644 cppcheck-2.14.0/lib/checkuninitvar.h create mode 100644 cppcheck-2.14.0/lib/checkunusedfunctions.cpp create mode 100644 cppcheck-2.14.0/lib/checkunusedfunctions.h create mode 100644 cppcheck-2.14.0/lib/checkunusedvar.cpp create mode 100644 cppcheck-2.14.0/lib/checkunusedvar.h create mode 100644 cppcheck-2.14.0/lib/checkvaarg.cpp create mode 100644 cppcheck-2.14.0/lib/checkvaarg.h create mode 100644 cppcheck-2.14.0/lib/clangimport.cpp create mode 100644 cppcheck-2.14.0/lib/clangimport.h create mode 100644 cppcheck-2.14.0/lib/color.cpp create mode 100644 cppcheck-2.14.0/lib/color.h create mode 100644 cppcheck-2.14.0/lib/config.h create mode 100644 cppcheck-2.14.0/lib/cppcheck.cpp create mode 100644 cppcheck-2.14.0/lib/cppcheck.h create mode 100644 cppcheck-2.14.0/lib/cppcheck.natvis create mode 100644 cppcheck-2.14.0/lib/cppcheck.vcxproj create mode 100644 cppcheck-2.14.0/lib/cppcheck.vcxproj.filters create mode 100644 cppcheck-2.14.0/lib/ctu.cpp create mode 100644 cppcheck-2.14.0/lib/ctu.h create mode 100644 cppcheck-2.14.0/lib/errorlogger.cpp create mode 100644 cppcheck-2.14.0/lib/errorlogger.h create mode 100644 cppcheck-2.14.0/lib/errortypes.cpp create mode 100644 cppcheck-2.14.0/lib/errortypes.h create mode 100644 cppcheck-2.14.0/lib/filesettings.h create mode 100644 cppcheck-2.14.0/lib/findtoken.h create mode 100644 cppcheck-2.14.0/lib/forwardanalyzer.cpp create mode 100644 cppcheck-2.14.0/lib/forwardanalyzer.h create mode 100644 cppcheck-2.14.0/lib/fwdanalysis.cpp create mode 100644 cppcheck-2.14.0/lib/fwdanalysis.h create mode 100644 cppcheck-2.14.0/lib/importproject.cpp create mode 100644 cppcheck-2.14.0/lib/importproject.h create mode 100644 cppcheck-2.14.0/lib/infer.cpp create mode 100644 cppcheck-2.14.0/lib/infer.h create mode 100644 cppcheck-2.14.0/lib/json.h create mode 100644 cppcheck-2.14.0/lib/keywords.cpp create mode 100644 cppcheck-2.14.0/lib/keywords.h create mode 100644 cppcheck-2.14.0/lib/lib.pri create mode 100644 cppcheck-2.14.0/lib/library.cpp create mode 100644 cppcheck-2.14.0/lib/library.h create mode 100644 cppcheck-2.14.0/lib/matchcompiler.h create mode 100644 cppcheck-2.14.0/lib/mathlib.cpp create mode 100644 cppcheck-2.14.0/lib/mathlib.h create mode 100644 cppcheck-2.14.0/lib/path.cpp create mode 100644 cppcheck-2.14.0/lib/path.h create mode 100644 cppcheck-2.14.0/lib/pathanalysis.cpp create mode 100644 cppcheck-2.14.0/lib/pathanalysis.h create mode 100644 cppcheck-2.14.0/lib/pathmatch.cpp create mode 100644 cppcheck-2.14.0/lib/pathmatch.h create mode 100644 cppcheck-2.14.0/lib/pcrerules.pri create mode 100644 cppcheck-2.14.0/lib/platform.cpp create mode 100644 cppcheck-2.14.0/lib/platform.h create mode 100644 cppcheck-2.14.0/lib/precompiled.h create mode 100644 cppcheck-2.14.0/lib/preprocessor.cpp create mode 100644 cppcheck-2.14.0/lib/preprocessor.h create mode 100644 cppcheck-2.14.0/lib/programmemory.cpp create mode 100644 cppcheck-2.14.0/lib/programmemory.h create mode 100644 cppcheck-2.14.0/lib/reverseanalyzer.cpp create mode 100644 cppcheck-2.14.0/lib/reverseanalyzer.h create mode 100644 cppcheck-2.14.0/lib/settings.cpp create mode 100644 cppcheck-2.14.0/lib/settings.h create mode 100644 cppcheck-2.14.0/lib/smallvector.h create mode 100644 cppcheck-2.14.0/lib/sourcelocation.h create mode 100644 cppcheck-2.14.0/lib/standards.h create mode 100644 cppcheck-2.14.0/lib/summaries.cpp create mode 100644 cppcheck-2.14.0/lib/summaries.h create mode 100644 cppcheck-2.14.0/lib/suppressions.cpp create mode 100644 cppcheck-2.14.0/lib/suppressions.h create mode 100644 cppcheck-2.14.0/lib/symboldatabase.cpp create mode 100644 cppcheck-2.14.0/lib/symboldatabase.h create mode 100644 cppcheck-2.14.0/lib/templatesimplifier.cpp create mode 100644 cppcheck-2.14.0/lib/templatesimplifier.h create mode 100644 cppcheck-2.14.0/lib/timer.cpp create mode 100644 cppcheck-2.14.0/lib/timer.h create mode 100644 cppcheck-2.14.0/lib/token.cpp create mode 100644 cppcheck-2.14.0/lib/token.h create mode 100644 cppcheck-2.14.0/lib/tokenize.cpp create mode 100644 cppcheck-2.14.0/lib/tokenize.h create mode 100644 cppcheck-2.14.0/lib/tokenlist.cpp create mode 100644 cppcheck-2.14.0/lib/tokenlist.h create mode 100644 cppcheck-2.14.0/lib/tokenrange.h create mode 100644 cppcheck-2.14.0/lib/utils.cpp create mode 100644 cppcheck-2.14.0/lib/utils.h create mode 100644 cppcheck-2.14.0/lib/valueflow.cpp create mode 100644 cppcheck-2.14.0/lib/valueflow.h create mode 100644 cppcheck-2.14.0/lib/valueptr.h create mode 100644 cppcheck-2.14.0/lib/version.h create mode 100644 cppcheck-2.14.0/lib/version.rc create mode 100644 cppcheck-2.14.0/lib/vfvalue.cpp create mode 100644 cppcheck-2.14.0/lib/vfvalue.h create mode 100644 cppcheck-2.14.0/lib/xml.h create mode 100644 cppcheck-2.14.0/man/CMakeLists.txt create mode 100644 cppcheck-2.14.0/man/build-html.sh create mode 100644 cppcheck-2.14.0/man/build-pdf.sh create mode 100644 cppcheck-2.14.0/man/buildman.sh create mode 100644 cppcheck-2.14.0/man/cppcheck-design.docbook create mode 100644 cppcheck-2.14.0/man/cppcheck.1.xml create mode 100644 cppcheck-2.14.0/man/images/gui-newproject-addons.png create mode 100644 cppcheck-2.14.0/man/images/gui-newproject-pathsanddefines.png create mode 100644 cppcheck-2.14.0/man/images/gui-newproject-project.png create mode 100644 cppcheck-2.14.0/man/images/gui-newproject.png create mode 100644 cppcheck-2.14.0/man/images/gui-results.png create mode 100644 cppcheck-2.14.0/man/manual-ja.docbook create mode 100644 cppcheck-2.14.0/man/manual-premium.md create mode 100644 cppcheck-2.14.0/man/manual-style.tex create mode 100644 cppcheck-2.14.0/man/manual.css create mode 100644 cppcheck-2.14.0/man/manual.md create mode 100644 cppcheck-2.14.0/man/reference-cfg-format.md create mode 100644 cppcheck-2.14.0/man/writing-addons.md create mode 100644 cppcheck-2.14.0/man/writing-rules-1.docbook create mode 100644 cppcheck-2.14.0/man/writing-rules-2.docbook create mode 100644 cppcheck-2.14.0/man/writing-rules-3.docbook create mode 100644 cppcheck-2.14.0/naming.json create mode 100644 cppcheck-2.14.0/oss-fuzz/Makefile create mode 100644 cppcheck-2.14.0/oss-fuzz/main.cpp create mode 100644 cppcheck-2.14.0/oss-fuzz/translate.cpp create mode 100644 cppcheck-2.14.0/oss-fuzz/type2.cpp create mode 100644 cppcheck-2.14.0/oss-fuzz/type2.h create mode 100644 cppcheck-2.14.0/philosophy.md create mode 100644 cppcheck-2.14.0/platforms/aix_ppc64.xml create mode 100644 cppcheck-2.14.0/platforms/arm32-wchar_t2.xml create mode 100644 cppcheck-2.14.0/platforms/arm32-wchar_t4.xml create mode 100644 cppcheck-2.14.0/platforms/arm64-wchar_t2.xml create mode 100644 cppcheck-2.14.0/platforms/arm64-wchar_t4.xml create mode 100644 cppcheck-2.14.0/platforms/avr8.xml create mode 100644 cppcheck-2.14.0/platforms/cppcheck-platforms.rng create mode 100644 cppcheck-2.14.0/platforms/cray_sv1.xml create mode 100644 cppcheck-2.14.0/platforms/elbrus-e1cp.xml create mode 100644 cppcheck-2.14.0/platforms/mips32.xml create mode 100644 cppcheck-2.14.0/platforms/msp430_eabi_large_datamodel.xml create mode 100644 cppcheck-2.14.0/platforms/pic16.xml create mode 100644 cppcheck-2.14.0/platforms/pic8-enhanced.xml create mode 100644 cppcheck-2.14.0/platforms/pic8.xml create mode 100644 cppcheck-2.14.0/platforms/unix32-unsigned.xml create mode 100644 cppcheck-2.14.0/platforms/unix64-unsigned.xml create mode 100644 cppcheck-2.14.0/pylintrc_travis create mode 100644 cppcheck-2.14.0/readme.md create mode 100644 cppcheck-2.14.0/readme.txt create mode 100644 cppcheck-2.14.0/readmeja.md create mode 100644 cppcheck-2.14.0/releasenotes.txt create mode 100644 cppcheck-2.14.0/requirements.txt create mode 100644 cppcheck-2.14.0/rules/empty-catch-block.xml create mode 100644 cppcheck-2.14.0/rules/error-reporting.xml create mode 100644 cppcheck-2.14.0/rules/show-all-defines.rule create mode 100644 cppcheck-2.14.0/rules/stl.xml create mode 100644 cppcheck-2.14.0/rules/strlen-empty-str.xml create mode 100644 cppcheck-2.14.0/rules/suggest_nullptr.xml create mode 100644 cppcheck-2.14.0/rules/token-matching.xml create mode 100644 cppcheck-2.14.0/rules/unused-deref.xml create mode 100644 cppcheck-2.14.0/runformat create mode 100644 cppcheck-2.14.0/samples/AssignmentAddressToInteger/bad.c create mode 100644 cppcheck-2.14.0/samples/AssignmentAddressToInteger/good.c create mode 100644 cppcheck-2.14.0/samples/AssignmentAddressToInteger/out.txt create mode 100644 cppcheck-2.14.0/samples/arrayIndexOutOfBounds_1/bad.c create mode 100644 cppcheck-2.14.0/samples/arrayIndexOutOfBounds_1/good.c create mode 100644 cppcheck-2.14.0/samples/arrayIndexOutOfBounds_1/out.txt create mode 100644 cppcheck-2.14.0/samples/arrayIndexOutOfBounds_2/bad.c create mode 100644 cppcheck-2.14.0/samples/arrayIndexOutOfBounds_2/good.c create mode 100644 cppcheck-2.14.0/samples/arrayIndexOutOfBounds_2/out.txt create mode 100644 cppcheck-2.14.0/samples/autoVariables/bad.c create mode 100644 cppcheck-2.14.0/samples/autoVariables/good.c create mode 100644 cppcheck-2.14.0/samples/autoVariables/out.txt create mode 100644 cppcheck-2.14.0/samples/bufferAccessOutOfBounds/bad.c create mode 100644 cppcheck-2.14.0/samples/bufferAccessOutOfBounds/good.c create mode 100644 cppcheck-2.14.0/samples/bufferAccessOutOfBounds/out.txt create mode 100644 cppcheck-2.14.0/samples/invalidContainer/bad.cpp create mode 100644 cppcheck-2.14.0/samples/invalidContainer/good.cpp create mode 100644 cppcheck-2.14.0/samples/invalidContainer/out.txt create mode 100644 cppcheck-2.14.0/samples/memleak/bad.c create mode 100644 cppcheck-2.14.0/samples/memleak/good.c create mode 100644 cppcheck-2.14.0/samples/memleak/out.txt create mode 100644 cppcheck-2.14.0/samples/resourceLeak/bad.c create mode 100644 cppcheck-2.14.0/samples/resourceLeak/good.c create mode 100644 cppcheck-2.14.0/samples/resourceLeak/out.txt create mode 100644 cppcheck-2.14.0/samples/syntaxError/bad.c create mode 100644 cppcheck-2.14.0/samples/syntaxError/good.c create mode 100644 cppcheck-2.14.0/samples/syntaxError/out.txt create mode 100644 cppcheck-2.14.0/snap/gui/cppcheck-gui.desktop create mode 100644 cppcheck-2.14.0/snap/gui/cppcheck-gui.png create mode 100644 cppcheck-2.14.0/snap/snapcraft.yaml create mode 100644 cppcheck-2.14.0/test/CMakeLists.txt create mode 100644 cppcheck-2.14.0/test/cfg/boost.cpp create mode 100644 cppcheck-2.14.0/test/cfg/bsd.c create mode 100644 cppcheck-2.14.0/test/cfg/cairo.c create mode 100644 cppcheck-2.14.0/test/cfg/cppunit.cpp create mode 100644 cppcheck-2.14.0/test/cfg/gnu.c create mode 100644 cppcheck-2.14.0/test/cfg/googletest.cpp create mode 100644 cppcheck-2.14.0/test/cfg/gtk.c create mode 100644 cppcheck-2.14.0/test/cfg/kde.cpp create mode 100644 cppcheck-2.14.0/test/cfg/libcurl.c create mode 100644 cppcheck-2.14.0/test/cfg/libsigc++.cpp create mode 100644 cppcheck-2.14.0/test/cfg/lua.c create mode 100644 cppcheck-2.14.0/test/cfg/mfc.cpp create mode 100644 cppcheck-2.14.0/test/cfg/opencv2.cpp create mode 100644 cppcheck-2.14.0/test/cfg/openmp.c create mode 100644 cppcheck-2.14.0/test/cfg/openssl.c create mode 100644 cppcheck-2.14.0/test/cfg/posix.c create mode 100644 cppcheck-2.14.0/test/cfg/python.c create mode 100644 cppcheck-2.14.0/test/cfg/qt.cpp create mode 100644 cppcheck-2.14.0/test/cfg/runtests.sh create mode 100644 cppcheck-2.14.0/test/cfg/sqlite3.c create mode 100644 cppcheck-2.14.0/test/cfg/std.c create mode 100644 cppcheck-2.14.0/test/cfg/std.cpp create mode 100644 cppcheck-2.14.0/test/cfg/windows.cpp create mode 100644 cppcheck-2.14.0/test/cfg/wxwidgets.cpp create mode 100644 cppcheck-2.14.0/test/cli/QML-Samples-TableView/README create mode 100644 cppcheck-2.14.0/test/cli/QML-Samples-TableView/TableColumn.qml create mode 100644 cppcheck-2.14.0/test/cli/QML-Samples-TableView/TableRow.qml create mode 100644 cppcheck-2.14.0/test/cli/QML-Samples-TableView/TableView.pro create mode 100644 cppcheck-2.14.0/test/cli/QML-Samples-TableView/TableView.qml create mode 100644 cppcheck-2.14.0/test/cli/QML-Samples-TableView/main.cpp create mode 100644 cppcheck-2.14.0/test/cli/QML-Samples-TableView/main.qml create mode 100644 cppcheck-2.14.0/test/cli/QML-Samples-TableView/qml.qrc create mode 100644 cppcheck-2.14.0/test/cli/QML-Samples-TableView/samplemodel.cpp create mode 100644 cppcheck-2.14.0/test/cli/QML-Samples-TableView/samplemodel.h create mode 100644 cppcheck-2.14.0/test/cli/clang-import_test.py create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-0f0efd6e66bd115fc0aabbfe6503230b31de5f24 create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-15d71125ba17344f02417a9d46443cdaa30aa17f create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-19219d7e7dfe8202248cd22229cdd9a2fd87a78a create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-20efd8856e04ca1fced9daa93837ae3384bb5e62 create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-2490dbc1880f2d7883c1b634deee8da3805186f8 create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-26edfe9761d3b681c841dfe80398847dee332f83 create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-3997cb1cad0af26035d36ca1a01ece07ca3fd114 create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-3ea64296c8518edb538e0047c3eba0792d5deeba create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-43fe82a87d6a7f34f000cbbc90b63ad1a58e3dcd create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-4d4e80f09d4733a9f724282d60ee4e3c3a143b61 create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-5db88ae1610f5a9782ea78af206f9cd87b6708dd create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-69570c88fc79e9a66ce2f2c729b455eaa237f3d2 create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-6eadb507c9ee93d9500028a9be2d183d1518f26b create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-77a4e4ffd476997f06f67e4a18b5d4eedfa71900 create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-7bac85061edab7fdce2889f02ea3a044242a3920 create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-7ead2ccf9be8b03b2d9c8c82891f58081390a560 create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-82986578453ec2056069c70846571775b10dfbcb create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-8a24e81ac1d7627233a227e6cc156dd20d57b058 create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-9a91d3da4e19336af500b94b2405831ed0fc1585 create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-9ef938bba7d752386e24f2438c73cec66f6b972b create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-d6609399a4398aed92f5ac9e53aa0554d9f8bbd6 create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-e000709d155e9c993795748ba31fddacbd5a86ac create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-e4a26f2d7d0a73836bf086f54e48204d8914b95a create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/crash-f4ec019b9a1f357d036a9bc3c2cb6fb10a0c3ded create mode 100644 cppcheck-2.14.0/test/cli/fuzz-crash/leak-9543188fae87abc4409106e1918d6424efe0d68a create mode 100644 cppcheck-2.14.0/test/cli/fuzz-timeout/minimized-from-179e5b42a7b0ba41a088e7972bdb13dde18ef4d1 create mode 100644 cppcheck-2.14.0/test/cli/fuzz-timeout/timeout-0ee5eed9abd34e9d23640a5b82dd724affd05b79 create mode 100644 cppcheck-2.14.0/test/cli/fuzz-timeout/timeout-9598595ae3c480b58773e85cd4e4d52b1dc37038 create mode 100644 cppcheck-2.14.0/test/cli/fuzz-timeout/timeout-a0b9848dd6e98677a0a96c5fc50ad571ed5a7092 create mode 100644 cppcheck-2.14.0/test/cli/fuzz_test.py create mode 100644 cppcheck-2.14.0/test/cli/helloworld/helloworld.cppcheck create mode 100644 cppcheck-2.14.0/test/cli/helloworld/helloworld.sln create mode 100644 cppcheck-2.14.0/test/cli/helloworld/helloworld.vcxproj create mode 100644 cppcheck-2.14.0/test/cli/helloworld/main.c create mode 100644 cppcheck-2.14.0/test/cli/helloworld_test.py create mode 100644 cppcheck-2.14.0/test/cli/inline-suppress_test.py create mode 100644 cppcheck-2.14.0/test/cli/more-projects_test.py create mode 100644 cppcheck-2.14.0/test/cli/other_test.py create mode 100644 cppcheck-2.14.0/test/cli/performance_test.py create mode 100644 cppcheck-2.14.0/test/cli/premium_test.py create mode 100644 cppcheck-2.14.0/test/cli/proj-inline-suppress-unusedFunction/A.cpp create mode 100644 cppcheck-2.14.0/test/cli/proj-inline-suppress-unusedFunction/B.cpp create mode 100644 cppcheck-2.14.0/test/cli/proj-inline-suppress-unusedFunction/B.hpp create mode 100644 cppcheck-2.14.0/test/cli/proj-inline-suppress/1.c create mode 100644 cppcheck-2.14.0/test/cli/proj-inline-suppress/1.h create mode 100644 cppcheck-2.14.0/test/cli/proj-inline-suppress/2.c create mode 100644 cppcheck-2.14.0/test/cli/proj-inline-suppress/3.cpp create mode 100644 cppcheck-2.14.0/test/cli/proj-inline-suppress/4.c create mode 100644 cppcheck-2.14.0/test/cli/proj-inline-suppress/template.cpp create mode 100644 cppcheck-2.14.0/test/cli/proj-suppress-syntaxError/1.c create mode 100644 cppcheck-2.14.0/test/cli/proj-suppress-syntaxError/2.c create mode 100644 cppcheck-2.14.0/test/cli/proj-suppress-syntaxError/3.c create mode 100644 cppcheck-2.14.0/test/cli/proj2/a/a.c create mode 100644 cppcheck-2.14.0/test/cli/proj2/b/b.c create mode 100644 cppcheck-2.14.0/test/cli/proj2/proj2.cppcheck create mode 100644 cppcheck-2.14.0/test/cli/proj2/proj2.sln create mode 100644 cppcheck-2.14.0/test/cli/proj2/proj2.vcxproj create mode 100644 cppcheck-2.14.0/test/cli/proj2_test.py create mode 100644 cppcheck-2.14.0/test/cli/project_test.py create mode 100644 cppcheck-2.14.0/test/cli/qml_test.py create mode 100644 cppcheck-2.14.0/test/cli/readme.txt create mode 100644 cppcheck-2.14.0/test/cli/samples_test.py create mode 100644 cppcheck-2.14.0/test/cli/suppress-syntaxError_test.py create mode 100644 cppcheck-2.14.0/test/cli/testutils.py create mode 100644 cppcheck-2.14.0/test/cli/trac5704/trac5704a.c create mode 100644 cppcheck-2.14.0/test/cli/trac5704/trac5704b.c create mode 100644 cppcheck-2.14.0/test/cli/unusedFunction/1.c create mode 100644 cppcheck-2.14.0/test/cli/unusedFunction/2.c create mode 100644 cppcheck-2.14.0/test/cli/unusedFunction/3.c create mode 100644 cppcheck-2.14.0/test/cli/unusedFunction/3.h create mode 100644 cppcheck-2.14.0/test/cli/unusedFunction/4.c create mode 100644 cppcheck-2.14.0/test/cli/unusedFunction/unusedFunction.cppcheck create mode 100644 cppcheck-2.14.0/test/cli/unused_function_test.py create mode 100644 cppcheck-2.14.0/test/fixture.cpp create mode 100644 cppcheck-2.14.0/test/fixture.h create mode 100644 cppcheck-2.14.0/test/helpers.cpp create mode 100644 cppcheck-2.14.0/test/helpers.h create mode 100644 cppcheck-2.14.0/test/main.cpp create mode 100644 cppcheck-2.14.0/test/options.cpp create mode 100644 cppcheck-2.14.0/test/options.h create mode 100644 cppcheck-2.14.0/test/precompiled.h create mode 100644 cppcheck-2.14.0/test/redirect.h create mode 100644 cppcheck-2.14.0/test/scripts/testrunner-single.sh create mode 100644 cppcheck-2.14.0/test/signal/CMakeLists.txt create mode 100644 cppcheck-2.14.0/test/signal/test-signalhandler.cpp create mode 100644 cppcheck-2.14.0/test/signal/test-signalhandler.py create mode 100644 cppcheck-2.14.0/test/signal/test-stacktrace.cpp create mode 100644 cppcheck-2.14.0/test/signal/test-stacktrace.py create mode 100644 cppcheck-2.14.0/test/test64bit.cpp create mode 100644 cppcheck-2.14.0/test/testanalyzerinformation.cpp create mode 100644 cppcheck-2.14.0/test/testassert.cpp create mode 100644 cppcheck-2.14.0/test/testastutils.cpp create mode 100644 cppcheck-2.14.0/test/testautovariables.cpp create mode 100644 cppcheck-2.14.0/test/testbool.cpp create mode 100644 cppcheck-2.14.0/test/testboost.cpp create mode 100644 cppcheck-2.14.0/test/testbufferoverrun.cpp create mode 100644 cppcheck-2.14.0/test/testcharvar.cpp create mode 100644 cppcheck-2.14.0/test/testcheck.cpp create mode 100644 cppcheck-2.14.0/test/testclangimport.cpp create mode 100644 cppcheck-2.14.0/test/testclass.cpp create mode 100644 cppcheck-2.14.0/test/testcmdlineparser.cpp create mode 100644 cppcheck-2.14.0/test/testcolor.cpp create mode 100644 cppcheck-2.14.0/test/testcondition.cpp create mode 100644 cppcheck-2.14.0/test/testconstructors.cpp create mode 100644 cppcheck-2.14.0/test/testcppcheck.cpp create mode 100644 cppcheck-2.14.0/test/testerrorlogger.cpp create mode 100644 cppcheck-2.14.0/test/testexceptionsafety.cpp create mode 100644 cppcheck-2.14.0/test/testfilelister.cpp create mode 100644 cppcheck-2.14.0/test/testfunctions.cpp create mode 100644 cppcheck-2.14.0/test/testgarbage.cpp create mode 100644 cppcheck-2.14.0/test/testimportproject.cpp create mode 100644 cppcheck-2.14.0/test/testincompletestatement.cpp create mode 100644 cppcheck-2.14.0/test/testinternal.cpp create mode 100644 cppcheck-2.14.0/test/testio.cpp create mode 100644 cppcheck-2.14.0/test/testleakautovar.cpp create mode 100644 cppcheck-2.14.0/test/testlibrary.cpp create mode 100644 cppcheck-2.14.0/test/testmathlib.cpp create mode 100644 cppcheck-2.14.0/test/testmemleak.cpp create mode 100644 cppcheck-2.14.0/test/testnullpointer.cpp create mode 100644 cppcheck-2.14.0/test/testoptions.cpp create mode 100644 cppcheck-2.14.0/test/testother.cpp create mode 100644 cppcheck-2.14.0/test/testpath.cpp create mode 100644 cppcheck-2.14.0/test/testpathmatch.cpp create mode 100644 cppcheck-2.14.0/test/testplatform.cpp create mode 100644 cppcheck-2.14.0/test/testpostfixoperator.cpp create mode 100644 cppcheck-2.14.0/test/testpreprocessor.cpp create mode 100644 cppcheck-2.14.0/test/testprocessexecutor.cpp create mode 100644 cppcheck-2.14.0/test/testrunner.vcxproj create mode 100644 cppcheck-2.14.0/test/testrunner.vcxproj.filters create mode 100644 cppcheck-2.14.0/test/testsettings.cpp create mode 100644 cppcheck-2.14.0/test/testsimplifytemplate.cpp create mode 100644 cppcheck-2.14.0/test/testsimplifytokens.cpp create mode 100644 cppcheck-2.14.0/test/testsimplifytypedef.cpp create mode 100644 cppcheck-2.14.0/test/testsimplifyusing.cpp create mode 100644 cppcheck-2.14.0/test/testsingleexecutor.cpp create mode 100644 cppcheck-2.14.0/test/testsizeof.cpp create mode 100644 cppcheck-2.14.0/test/teststl.cpp create mode 100644 cppcheck-2.14.0/test/teststring.cpp create mode 100644 cppcheck-2.14.0/test/testsummaries.cpp create mode 100644 cppcheck-2.14.0/test/testsuppressions.cpp create mode 100644 cppcheck-2.14.0/test/testsymboldatabase.cpp create mode 100644 cppcheck-2.14.0/test/testthreadexecutor.cpp create mode 100644 cppcheck-2.14.0/test/testtimer.cpp create mode 100644 cppcheck-2.14.0/test/testtoken.cpp create mode 100644 cppcheck-2.14.0/test/testtokenize.cpp create mode 100644 cppcheck-2.14.0/test/testtokenlist.cpp create mode 100644 cppcheck-2.14.0/test/testtokenrange.cpp create mode 100644 cppcheck-2.14.0/test/testtype.cpp create mode 100644 cppcheck-2.14.0/test/testuninitvar.cpp create mode 100644 cppcheck-2.14.0/test/testunusedfunctions.cpp create mode 100644 cppcheck-2.14.0/test/testunusedprivfunc.cpp create mode 100644 cppcheck-2.14.0/test/testunusedvar.cpp create mode 100644 cppcheck-2.14.0/test/testutils.cpp create mode 100644 cppcheck-2.14.0/test/testvaarg.cpp create mode 100644 cppcheck-2.14.0/test/testvalueflow.cpp create mode 100644 cppcheck-2.14.0/test/testvarid.cpp create mode 100644 cppcheck-2.14.0/tools/CMakeLists.txt create mode 100644 cppcheck-2.14.0/tools/MT-Unsafe.py create mode 100644 cppcheck-2.14.0/tools/bisect/README.md create mode 100644 cppcheck-2.14.0/tools/bisect/bisect.sh create mode 100644 cppcheck-2.14.0/tools/bisect/bisect_common.py create mode 100644 cppcheck-2.14.0/tools/bisect/bisect_hang.py create mode 100644 cppcheck-2.14.0/tools/bisect/bisect_res.py create mode 100644 cppcheck-2.14.0/tools/ci.py create mode 100644 cppcheck-2.14.0/tools/compare-normal-exhaustive.py create mode 100644 cppcheck-2.14.0/tools/compare.cs create mode 100644 cppcheck-2.14.0/tools/compare_ast_symdb.py create mode 100644 cppcheck-2.14.0/tools/creduce.py create mode 100644 cppcheck-2.14.0/tools/daca2-download.py create mode 100644 cppcheck-2.14.0/tools/daca2-getpackages.py create mode 100644 cppcheck-2.14.0/tools/defines/create_platform_cfg.sh create mode 100644 cppcheck-2.14.0/tools/defines/defines.sh create mode 100644 cppcheck-2.14.0/tools/defines/float.c create mode 100644 cppcheck-2.14.0/tools/defines/limits.c create mode 100644 cppcheck-2.14.0/tools/defines/readme.md create mode 100644 cppcheck-2.14.0/tools/defines/run_cppcheck.sh create mode 100644 cppcheck-2.14.0/tools/defines/stdint.c create mode 100644 cppcheck-2.14.0/tools/dmake/CMakeLists.txt create mode 100644 cppcheck-2.14.0/tools/dmake/dmake.cpp create mode 100644 cppcheck-2.14.0/tools/dmake/dmake.vcxproj create mode 100644 cppcheck-2.14.0/tools/donate-cpu-server.py create mode 100644 cppcheck-2.14.0/tools/donate-cpu.py create mode 100644 cppcheck-2.14.0/tools/donate_cpu_lib.py create mode 100644 cppcheck-2.14.0/tools/donate_cpu_lib_test.py create mode 100644 cppcheck-2.14.0/tools/donate_cpu_server_test.py create mode 100644 cppcheck-2.14.0/tools/extract_and_run_more_tests.sh create mode 100644 cppcheck-2.14.0/tools/extracttests.py create mode 100644 cppcheck-2.14.0/tools/generate_and_run_more_tests.sh create mode 100644 cppcheck-2.14.0/tools/get_checkers.py create mode 100644 cppcheck-2.14.0/tools/git-pre-commit-cppcheck create mode 100644 cppcheck-2.14.0/tools/listErrorsWithoutCWE.py create mode 100644 cppcheck-2.14.0/tools/matchcompiler.py create mode 100644 cppcheck-2.14.0/tools/parse-glibc.py create mode 100644 cppcheck-2.14.0/tools/readme.md create mode 100644 cppcheck-2.14.0/tools/reduce.py create mode 100644 cppcheck-2.14.0/tools/reduce_test.py create mode 100644 cppcheck-2.14.0/tools/run-coverity.sh create mode 100644 cppcheck-2.14.0/tools/run_more_tests.sh create mode 100644 cppcheck-2.14.0/tools/test-my-pr.py create mode 100644 cppcheck-2.14.0/tools/test/run_donate_cpu_client_tests.sh create mode 100644 cppcheck-2.14.0/tools/test/start_donate_cpu_client_productive.sh create mode 100644 cppcheck-2.14.0/tools/test/start_donate_cpu_client_test_local.sh create mode 100644 cppcheck-2.14.0/tools/test/start_donate_cpu_server_test_local.sh create mode 100644 cppcheck-2.14.0/tools/test_matchcompiler.py create mode 100644 cppcheck-2.14.0/tools/testrunnerify_code.sh create mode 100644 cppcheck-2.14.0/tools/trac-keywords.py create mode 100644 cppcheck-2.14.0/tools/triage/.clang-tidy create mode 100644 cppcheck-2.14.0/tools/triage/.gitignore create mode 100644 cppcheck-2.14.0/tools/triage/CMakeLists.txt create mode 100644 cppcheck-2.14.0/tools/triage/main.cpp create mode 100644 cppcheck-2.14.0/tools/triage/mainwindow.cpp create mode 100644 cppcheck-2.14.0/tools/triage/mainwindow.h create mode 100644 cppcheck-2.14.0/tools/triage/mainwindow.ui create mode 100644 cppcheck-2.14.0/tools/triage/readme.txt create mode 100644 cppcheck-2.14.0/tools/triage/triage.pro create mode 100644 cppcheck-2.14.0/tools/triage_py/README.md create mode 100644 cppcheck-2.14.0/tools/triage_py/triage_version.py create mode 100644 cppcheck-2.14.0/valgrind/testrunner.supp create mode 100644 cppcheck-2.14.0/webreport.sh create mode 100644 cppcheck-2.14.0/win_installer/GPLv3.txt create mode 100644 cppcheck-2.14.0/win_installer/config.wxi create mode 100644 cppcheck-2.14.0/win_installer/cppcheck.sln create mode 100644 cppcheck-2.14.0/win_installer/cppcheck.wixproj create mode 100644 cppcheck-2.14.0/win_installer/cppcheck.wxs create mode 100644 cppcheck-2.14.0/win_installer/images/banner.jpg create mode 100644 cppcheck-2.14.0/win_installer/images/dialog.jpg create mode 100644 cppcheck-2.14.0/win_installer/productInfo.wxi create mode 100644 cppcheck-2.14.0/win_installer/readme.txt create mode 100644 cppcheck.tar.gz create mode 100644 结果.txt diff --git a/cppcheck-2.14.0/.clang-tidy b/cppcheck-2.14.0/.clang-tidy new file mode 100644 index 00000000..0b130f31 --- /dev/null +++ b/cppcheck-2.14.0/.clang-tidy @@ -0,0 +1,79 @@ +--- +Checks: > + *, + -abseil-*, + -altera-*, + -android-*, + -boost-*, + -cert-*, + -cppcoreguidelines-*, + -darwin-*, + -fuchsia-*, + -google-*, + -hicpp-*, + -linuxkernel-*, + -llvm-*, + -llvmlibc-*, + -mpi-*, + -objc-*, + -openmp-*, + -zircon-*, + cert-err34-c, + google-explicit-constructor, + cppcoreguidelines-rvalue-reference-param-not-moved, + -bugprone-assignment-in-if-condition, + -bugprone-branch-clone, + -bugprone-easily-swappable-parameters, + -bugprone-empty-catch, + -bugprone-macro-parentheses, + -bugprone-narrowing-conversions, + -bugprone-signed-char-misuse, + -bugprone-switch-missing-default-case, + -bugprone-unchecked-optional-access, + -clang-analyzer-*, + -concurrency-mt-unsafe, + -misc-const-correctness, + -misc-no-recursion, + -misc-non-private-member-variables-in-classes, + -misc-throw-by-value-catch-by-reference, + -misc-use-anonymous-namespace, + -modernize-avoid-c-arrays, + -modernize-deprecated-ios-base-aliases, + -misc-include-cleaner, + -misc-unused-using-decls, + -modernize-loop-convert, + -modernize-macro-to-enum, + -modernize-raw-string-literal, + -modernize-replace-auto-ptr, + -modernize-return-braced-init-list, + -modernize-type-traits, + -modernize-use-auto, + -modernize-use-nodiscard, + -modernize-use-trailing-return-type, + -performance-avoid-endl, + -performance-enum-size, + -performance-inefficient-string-concatenation, + -performance-no-automatic-move, + -performance-noexcept-swap, + -portability-simd-intrinsics, + -portability-std-allocator-const, + -readability-avoid-const-params-in-decls, + -readability-avoid-nested-conditional-operator, + -readability-braces-around-statements, + -readability-container-data-pointer, + -readability-function-cognitive-complexity, + -readability-function-size, + -readability-identifier-length, + -readability-identifier-naming, + -readability-implicit-bool-conversion, + -readability-isolate-declaration, + -readability-magic-numbers, + -readability-suspicious-call-argument, + -readability-uppercase-literal-suffix +WarningsAsErrors: '*' +HeaderFilterRegex: '(cli|gui|lib|oss-fuzz|test|triage)\/[a-z]+\.h' +CheckOptions: + - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic + value: '1' + - key: readability-simplify-boolean-expr.SimplifyDeMorgan + value: '0' diff --git a/cppcheck-2.14.0/.codacy.yml b/cppcheck-2.14.0/.codacy.yml new file mode 100644 index 00000000..dcfec8e5 --- /dev/null +++ b/cppcheck-2.14.0/.codacy.yml @@ -0,0 +1,8 @@ +exclude_paths: + - addons/test/** + - addons/y2038/test/*.c + - htmlreport/example.cc + - samples/**/bad.c + - samples/**/bad.cpp + - test/cfg/*.c + - test/cfg/*.cpp diff --git a/cppcheck-2.14.0/.gitattributes b/cppcheck-2.14.0/.gitattributes new file mode 100644 index 00000000..ed782473 --- /dev/null +++ b/cppcheck-2.14.0/.gitattributes @@ -0,0 +1,19 @@ +## standard default enconding +* text=auto + +## UNIX specific files +*.sh text eol=lf + +## Windows specific files +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf +*.vcxproj text eol=crlf +*.vcxproj.filters text eol=crlf +*.sln text eol=crlf +*.wixproj text eol=crlf +*.wxi text eol=crlf +*.wxs text eol=crlf + +## Binary resources +*.pdf binary \ No newline at end of file diff --git a/cppcheck-2.14.0/.github/workflows/CI-cygwin.yml b/cppcheck-2.14.0/.github/workflows/CI-cygwin.yml new file mode 100644 index 00000000..f864be5b --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/CI-cygwin.yml @@ -0,0 +1,56 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: CI-cygwin + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + +defaults: + run: + shell: cmd + +jobs: + build_cygwin: + strategy: + matrix: + os: [windows-2022] + arch: [x64] + include: + - platform: 'x86_64' + packages: | + gcc-g++ + python3 + fail-fast: false + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v3 + + - name: Set up Cygwin + uses: cygwin/cygwin-install-action@master + with: + platform: ${{ matrix.arch }} + packages: ${{ matrix.packages }} + + # Cygwin will always link the binaries even if they already exist. The linking is also extremely slow. So just run the "check" target which includes all the binaries. + - name: Build all and run test + run: | + C:\cygwin\bin\bash.exe -l -c cd %GITHUB_WORKSPACE% && make VERBOSE=1 -j2 check + + - name: Extra test for misra + run: | + cd %GITHUB_WORKSPACE%\addons\test + ..\..\cppcheck.exe --dump -DDUMMY --suppress=uninitvar --inline-suppr misra\misra-test.c --std=c89 --platform=unix64 + python3 ..\misra.py -verify misra\misra-test.c.dump + ..\..\cppcheck.exe --addon=misra --enable=style --inline-suppr --enable=information --error-exitcode=1 misra\misra-ctu-1-test.c misra\misra-ctu-2-test.c + diff --git a/cppcheck-2.14.0/.github/workflows/CI-mingw.yml b/cppcheck-2.14.0/.github/workflows/CI-mingw.yml new file mode 100644 index 00000000..04145d72 --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/CI-mingw.yml @@ -0,0 +1,71 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: CI-mingw + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + +defaults: + run: + shell: msys2 {0} + +jobs: + build_mingw: + strategy: + matrix: + # the MinGW installation in windows-2019 is supposed to be 8.1 but it is 12.2 + # the MinGW installation in windows-2022 is not including all necessary packages by default, so just use the older image instead - package versions are he same + os: [windows-2019] + fail-fast: false + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v3 + + - name: Set up MSYS2 + uses: msys2/setup-msys2@v2 + with: + release: false # use pre-installed + install: >- + mingw-w64-x86_64-lld + mingw-w64-x86_64-ccache + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} + + # TODO: bail out on warning + - name: Build cppcheck + run: | + export PATH="/mingw64/lib/ccache/bin:$PATH" + # set RDYNAMIC to work around broken MinGW detection + make VERBOSE=1 RDYNAMIC=-lshlwapi -j2 cppcheck + env: + LDFLAGS: -fuse-ld=lld # use lld for faster linking + + - name: Build test + run: | + export PATH="/mingw64/lib/ccache/bin:$PATH" + # set RDYNAMIC to work around broken MinGW detection + make VERBOSE=1 RDYNAMIC=-lshlwapi -j2 testrunner + env: + LDFLAGS: -fuse-ld=lld # use lld for faster linking + + - name: Run test + run: | + export PATH="/mingw64/lib/ccache/bin:$PATH" + # set RDYNAMIC to work around broken MinGW detection + make VERBOSE=1 RDYNAMIC=-lshlwapi -j2 check + env: + LDFLAGS: -fuse-ld=lld # use lld for faster linking diff --git a/cppcheck-2.14.0/.github/workflows/CI-unixish-docker.yml b/cppcheck-2.14.0/.github/workflows/CI-unixish-docker.yml new file mode 100644 index 00000000..82439c9b --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/CI-unixish-docker.yml @@ -0,0 +1,158 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: CI-unixish-docker + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + +jobs: + build_cmake: + + strategy: + matrix: + image: ["ubuntu:16.04", "ubuntu:18.04", "ubuntu:23.10"] + include: + - build_gui: false + - image: "ubuntu:23.10" + build_gui: true + fail-fast: false # Prefer quick result + + runs-on: ubuntu-22.04 + + # TODO: is this actually applied to the guest? + env: + # TODO: figure out why there are cache misses with PCH enabled + CCACHE_SLOPPINESS: pch_defines,time_macros + + container: + image: ${{ matrix.image }} + + steps: + - uses: actions/checkout@v3 + + - name: Install missing software on ubuntu + if: contains(matrix.image, 'ubuntu') + run: | + apt-get update + apt-get install -y cmake g++ make libxml2-utils libpcre3-dev + + - name: Install missing software (gui) on latest ubuntu + if: matrix.build_gui + run: | + apt-get install -y qt6-base-dev qt6-charts-dev qt6-tools-dev + + # needs to be called after the package installation since + # - it doesn't call "apt-get update" + # - it doesn't support centos + # + # needs to be to fixated on 1.2.11 so it works with older images - see https://github.com/hendrikmuhs/ccache-action/issues/178 + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ matrix.image }} + + # tests require CMake 3.9 - ccache available + - name: CMake build (no tests) + if: matrix.image == 'ubuntu:16.04' + run: | + mkdir cmake.output + cd cmake.output + cmake -G "Unix Makefiles" -DHAVE_RULES=On -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache .. + cmake --build . -- -j$(nproc) + + - name: CMake build + if: ${{ !matrix.build_gui && matrix.image != 'ubuntu:16.04' }} + run: | + mkdir cmake.output + cd cmake.output + cmake -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache .. + cmake --build . -- -j$(nproc) + + - name: CMake build (with GUI) + if: matrix.build_gui + run: | + cmake -S . -B cmake.output -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DUSE_QT6=On -DWITH_QCHART=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + cmake --build cmake.output -- -j$(nproc) + + - name: Run CMake test + if: matrix.image != 'ubuntu:16.04' + run: | + cmake --build cmake.output --target check -- -j$(nproc) + + build_make: + + strategy: + matrix: + image: ["ubuntu:16.04", "ubuntu:18.04", "ubuntu:23.10"] + fail-fast: false # Prefer quick result + + runs-on: ubuntu-22.04 + + container: + image: ${{ matrix.image }} + + steps: + - uses: actions/checkout@v3 + + - name: Install missing software on ubuntu + if: contains(matrix.image, 'ubuntu') + run: | + apt-get update + apt-get install -y g++ make python3 libxml2-utils libpcre3-dev + + # needs to be called after the package installation since + # - it doesn't call "apt-get update" + # - it doesn't support centos + # + # needs to be to fixated on 1.2.11 so it works with older images - see https://github.com/hendrikmuhs/ccache-action/issues/178 + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ matrix.image }} + + - name: Build cppcheck + run: | + # "/usr/lib64" for centos / "/usr/lib" for ubuntu + export PATH="/usr/lib64/ccache:/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + make -j$(nproc) HAVE_RULES=yes CXXFLAGS="-w" + + - name: Build test + run: | + # "/usr/lib64" for centos / "/usr/lib" for ubuntu + export PATH="/usr/lib64/ccache:/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + make -j$(nproc) testrunner HAVE_RULES=yes CXXFLAGS="-w" + + - name: Run test + run: | + make -j$(nproc) check HAVE_RULES=yes + + # requires python3 + - name: Run extra tests + run: | + tools/generate_and_run_more_tests.sh + + # requires which + - name: Validate + run: | + make -j$(nproc) checkCWEEntries validateXML + + - name: Test addons + run: | + ./cppcheck --addon=threadsafety addons/test/threadsafety + ./cppcheck --addon=threadsafety --std=c++03 addons/test/threadsafety + + - name: Generate Qt help file on ubuntu 18.04 + if: false # matrix.os == 'ubuntu-18.04' + run: | + pushd gui/help + qcollectiongenerator online-help.qhcp -o online-help.qhc + diff --git a/cppcheck-2.14.0/.github/workflows/CI-unixish.yml b/cppcheck-2.14.0/.github/workflows/CI-unixish.yml new file mode 100644 index 00000000..2f7dd0de --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/CI-unixish.yml @@ -0,0 +1,552 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: CI-unixish + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + +jobs: + build_cmake_tinyxml2: + + strategy: + matrix: + os: [ubuntu-20.04, ubuntu-22.04, macos-12] + include: + - use_qt6: On + - os: ubuntu-20.04 + use_qt6: Off + fail-fast: false # Prefer quick result + + runs-on: ${{ matrix.os }} + + env: + # TODO: figure out why there are cache misses with PCH enabled + CCACHE_SLOPPINESS: pch_defines,time_macros + + steps: + - uses: actions/checkout@v3 + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} + + - name: Install missing software on ubuntu + if: contains(matrix.os, 'ubuntu') && matrix.use_qt6 == 'Off' + run: | + sudo apt-get update + sudo apt-get install libxml2-utils libtinyxml2-dev qtbase5-dev qttools5-dev libqt5charts5-dev qtchooser + + - name: Install missing software on ubuntu + if: contains(matrix.os, 'ubuntu') && matrix.use_qt6 == 'On' + run: | + sudo apt-get update + # qt6-tools-dev-tools for lprodump + # qt6-l10n-tools for lupdate + sudo apt-get install libxml2-utils libtinyxml2-dev qt6-base-dev libqt6charts6-dev qt6-tools-dev qt6-tools-dev-tools qt6-l10n-tools libglx-dev libgl1-mesa-dev + + # coreutils contains "nproc" + - name: Install missing software on macos + if: contains(matrix.os, 'macos') + run: | + # pcre was removed from runner images in November 2022 + brew install coreutils qt@6 tinyxml2 pcre + + - name: CMake build on ubuntu (with GUI / system tinyxml2) + if: contains(matrix.os, 'ubuntu') + run: | + cmake -S . -B cmake.output.tinyxml2 -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DUSE_QT6=${{ matrix.use_qt6 }} -DWITH_QCHART=On -DUSE_BUNDLED_TINYXML2=Off -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + cmake --build cmake.output.tinyxml2 -- -j$(nproc) + + - name: CMake build on macos (with GUI / system tinyxml2) + if: contains(matrix.os, 'macos') + run: | + cmake -S . -B cmake.output.tinyxml2 -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DUSE_QT6=On -DWITH_QCHART=On -DUSE_BUNDLED_TINYXML2=Off -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DQt6_DIR=$(brew --prefix qt@6)/lib/cmake/Qt6 + cmake --build cmake.output.tinyxml2 -- -j$(nproc) + + - name: Run CMake test (system tinyxml2) + run: | + cmake --build cmake.output.tinyxml2 --target check -- -j$(nproc) + + build_cmake: + + strategy: + matrix: + os: [ubuntu-20.04, ubuntu-22.04, macos-12] + include: + - use_qt6: On + - os: ubuntu-20.04 + use_qt6: Off + fail-fast: false # Prefer quick result + + runs-on: ${{ matrix.os }} + + env: + # TODO: figure out why there are cache misses with PCH enabled + CCACHE_SLOPPINESS: pch_defines,time_macros + + steps: + - uses: actions/checkout@v3 + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} + + - name: Install missing software on ubuntu + if: contains(matrix.os, 'ubuntu') && matrix.use_qt6 == 'Off' + run: | + sudo apt-get update + sudo apt-get install libxml2-utils qtbase5-dev qttools5-dev libqt5charts5-dev qtchooser + + # TODO: move latest compiler to separate step + # TODO: bail out on warnings with latest GCC + - name: Set up GCC + uses: egor-tensin/setup-gcc@v1 + if: matrix.os == 'ubuntu-22.04' + with: + version: 13 + platform: x64 + + - name: Select compiler + if: matrix.os == 'ubuntu-22.04' + run: | + echo "CXX=g++-13" >> $GITHUB_ENV + + - name: Install missing software on ubuntu + if: contains(matrix.os, 'ubuntu') && matrix.use_qt6 == 'On' + run: | + sudo apt-get update + # qt6-tools-dev-tools for lprodump + # qt6-l10n-tools for lupdate + sudo apt-get install libxml2-utils qt6-base-dev libqt6charts6-dev qt6-tools-dev qt6-tools-dev-tools qt6-l10n-tools libglx-dev libgl1-mesa-dev + + # coreutils contains "nproc" + - name: Install missing software on macos + if: contains(matrix.os, 'macos') + run: | + # pcre was removed from runner images in November 2022 + brew install coreutils qt@6 pcre + + - name: CMake build on ubuntu (with GUI) + if: contains(matrix.os, 'ubuntu') + run: | + cmake -S . -B cmake.output -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DUSE_QT6=${{ matrix.use_qt6 }} -DWITH_QCHART=On -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + cmake --build cmake.output -- -j$(nproc) + + - name: CMake build on macos (with GUI) + if: contains(matrix.os, 'macos') + run: | + cmake -S . -B cmake.output -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DUSE_QT6=On -DWITH_QCHART=On -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DQt6_DIR=$(brew --prefix qt@6)/lib/cmake/Qt6 + cmake --build cmake.output -- -j$(nproc) + + - name: Run CMake test + run: | + cmake --build cmake.output --target check -- -j$(nproc) + + - name: Run CTest + run: | + pushd cmake.output + ctest --output-on-failure -j$(nproc) + + build_uchar: + + strategy: + matrix: + os: [ubuntu-20.04, ubuntu-22.04, macos-12] + fail-fast: false # Prefer quick result + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v3 + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} + + # coreutils contains "nproc" + - name: Install missing software on macos + if: contains(matrix.os, 'macos') + run: | + brew install coreutils + + - name: Build with Unsigned char + run: | + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + make -j$(nproc) CXXFLAGS=-funsigned-char testrunner + + - name: Test with Unsigned char + run: | + ./testrunner TestSymbolDatabase + + build_mathlib: + + strategy: + matrix: + os: [ubuntu-20.04, ubuntu-22.04, macos-12] + fail-fast: false # Prefer quick result + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v3 + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} + + # coreutils contains "nproc" + - name: Install missing software on macos + if: contains(matrix.os, 'macos') + run: | + brew install coreutils + + - name: Build with TEST_MATHLIB_VALUE + run: | + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + make -j$(nproc) CPPFLAGS=-DTEST_MATHLIB_VALUE all + + - name: Test with TEST_MATHLIB_VALUE + run: | + make -j$(nproc) CPPFLAGS=-DTEST_MATHLIB_VALUE check + + check_nonneg: + + strategy: + matrix: + os: [ubuntu-20.04, ubuntu-22.04, macos-12] + fail-fast: false # Prefer quick result + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v3 + + # coreutils contains "g++" (default is "c++") and "nproc" + - name: Install missing software on macos + if: contains(matrix.os, 'macos') + run: | + brew install coreutils + + - name: Check syntax with NONNEG + run: | + ls lib/*.cpp | xargs -n 1 -P $(nproc) g++ -fsyntax-only -std=c++0x -Ilib -Iexternals -Iexternals/picojson -Iexternals/simplecpp -Iexternals/tinyxml2 -DNONNEG + + build_qmake: + + strategy: + matrix: + # no longer build with qmake on MacOS as brew might lack pre-built Qt5 packages causing the step to run for hours + os: [ubuntu-20.04, ubuntu-22.04] + fail-fast: false # Prefer quick result + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v3 + + - name: Install missing software on ubuntu + if: contains(matrix.os, 'ubuntu') + run: | + sudo apt-get update + sudo apt-get install qtbase5-dev qttools5-dev libqt5charts5-dev qtchooser + + # coreutils contains "nproc" + - name: Install missing software on macos + if: contains(matrix.os, 'macos') + run: | + brew install coreutils qt@5 + # expose qmake + brew link qt@5 --force + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} + + - name: Build GUI + run: | + export PATH="$(brew --prefix)/opt/ccache/libexec:/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + pushd gui + qmake CONFIG+=debug CONFIG+=ccache HAVE_QCHART=yes + make -j$(nproc) + + # TODO: binaries are in a different location on macos + - name: Build and Run GUI tests + if: contains(matrix.os, 'ubuntu') + run: | + export PATH="$(brew --prefix)/opt/ccache/libexec:/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + pushd gui/test/cppchecklibrarydata + qmake CONFIG+=debug CONFIG+=ccache + make -j$(nproc) + ./test-cppchecklibrarydata + popd + pushd gui/test/filelist + qmake CONFIG+=debug CONFIG+=ccache + make -j$(nproc) + ./test-filelist + popd + pushd gui/test/projectfile + qmake CONFIG+=debug CONFIG+=ccache + make -j$(nproc) + ./test-projectfile + popd + pushd gui/test/translationhandler + qmake CONFIG+=debug CONFIG+=ccache + make -j$(nproc) + # TODO: requires X session because of QApplication dependency in translationhandler.cpp + #./test-translationhandler + popd + pushd gui/test/xmlreportv2 + qmake CONFIG+=debug CONFIG+=ccache + make -j$(nproc) + ./test-xmlreportv2 + + - name: Generate Qt help file + run: | + pushd gui/help + qhelpgenerator online-help.qhcp -o online-help.qhc + + - name: Build triage + run: | + export PATH="$(brew --prefix)/opt/ccache/libexec:/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + pushd tools/triage + qmake CONFIG+=debug CONFIG+=ccache + make -j$(nproc) + + build: + + strategy: + matrix: + os: [ubuntu-20.04, ubuntu-22.04, macos-12] + fail-fast: false # Prefer quick result + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v3 + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} + + - name: Install missing software on ubuntu + if: contains(matrix.os, 'ubuntu') + run: | + sudo apt-get update + sudo apt-get install libxml2-utils + + # packages for strict cfg checks + - name: Install missing software on ubuntu 22.04 (cfg) + if: matrix.os == 'ubuntu-22.04' + run: | + sudo apt-get install libcairo2-dev libcurl4-openssl-dev liblua5.3-dev libssl-dev libsqlite3-dev libcppunit-dev libsigc++-2.0-dev libgtk-3-dev libboost-all-dev libwxgtk3.0-gtk3-dev xmlstarlet qtbase5-dev + + # coreutils contains "nproc" + - name: Install missing software on macos + if: contains(matrix.os, 'macos') + run: | + # pcre was removed from runner images in November 2022 + brew install coreutils python3 pcre gnu-sed + + - name: Install missing Python packages + run: | + python3 -m pip install pip --upgrade + python3 -m pip install pytest + python3 -m pip install pytest-timeout + + - name: Build cppcheck + run: | + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + make -j$(nproc) HAVE_RULES=yes + + - name: Build test + run: | + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + make -j$(nproc) testrunner HAVE_RULES=yes + + - name: Run test + run: | + make -j$(nproc) check HAVE_RULES=yes + + # requires "gnu-sed" installed on macos + - name: Run extra tests + run: | + tools/generate_and_run_more_tests.sh + + # do not use pushd in this step since we go below the working directory + - name: Run test/cli + run: | + cd test/cli + python3 -m pytest -Werror --strict-markers -vv + cd ../../.. + ln -s cppcheck 'cpp check' + cd 'cpp check/test/cli' + python3 -m pytest -Werror --strict-markers -vv + + # do not use pushd in this step since we go below the working directory + - name: Run test/cli (-j2) + run: | + cd test/cli + python3 -m pytest -Werror --strict-markers -vv + env: + TEST_CPPCHECK_INJECT_J: 2 + + # do not use pushd in this step since we go below the working directory + - name: Run test/cli (--clang) + if: false + run: | + cd test/cli + python3 -m pytest -Werror --strict-markers -vv + env: + TEST_CPPCHECK_INJECT_CLANG: clang + + - name: Run cfg tests + if: matrix.os != 'ubuntu-22.04' + run: | + make -j$(nproc) checkcfg + + - name: Run cfg tests (strict) + if: matrix.os == 'ubuntu-22.04' + run: | + make -j$(nproc) checkcfg + env: + STRICT: 1 + + - name: Run --dump test + run: | + ./cppcheck test/testpreprocessor.cpp --dump + xmllint --noout test/testpreprocessor.cpp.dump + + - name: Validate + run: | + make -j$(nproc) checkCWEEntries validateXML + + - name: Test Signalhandler + run: | + cmake -S . -B cmake.output.signal -G "Unix Makefiles" -DBUILD_TESTS=On + cmake --build cmake.output.signal --target test-signalhandler -- -j$(nproc) + cp cmake.output.signal/bin/test-s* . + python3 -m pytest -Werror --strict-markers -vv test/signal/test-signalhandler.py + + # no unix backtrace support on MacOs + - name: Test Stacktrace + if: contains(matrix.os, 'ubuntu') + run: | + cmake -S . -B cmake.output.signal -G "Unix Makefiles" -DBUILD_TESTS=On + cmake --build cmake.output.signal --target test-stacktrace -- -j$(nproc) + cp cmake.output.signal/bin/test-s* . + python3 -m pytest -Werror --strict-markers -vv test/signal/test-stacktrace.py + + # TODO: move to scriptcheck.yml so these are tested with all Python versions? + - name: Test addons + run: | + ./cppcheck --error-exitcode=1 --inline-suppr --addon=threadsafety addons/test/threadsafety + ./cppcheck --error-exitcode=1 --inline-suppr --addon=threadsafety --std=c++03 addons/test/threadsafety + ./cppcheck --error-exitcode=1 --inline-suppr --addon=misra addons/test/misra/crash*.c + ./cppcheck --error-exitcode=1 --inline-suppr --addon=misra --enable=information addons/test/misra/config*.c + + ./cppcheck --addon=misra --enable=style --inline-suppr --enable=information --error-exitcode=1 addons/test/misra/misra-ctu-*-test.c + pushd addons/test + # We'll force C89 standard to enable an additional verification for + # rules 5.4 and 5.5 which have standard-dependent options. + ../../cppcheck --dump -DDUMMY --suppress=uninitvar --inline-suppr misra/misra-test.c --std=c89 --platform=unix64 + python3 ../misra.py -verify misra/misra-test.c.dump + # Test slight MISRA differences in C11 standard + ../../cppcheck --dump -DDUMMY --suppress=uninitvar --inline-suppr misra/misra-test-c11.c --std=c11 --platform=unix64 + python3 ../misra.py -verify misra/misra-test-c11.c.dump + # TODO: do we need to verify something here? + ../../cppcheck --dump -DDUMMY --suppress=uninitvar --suppress=uninitStructMember --std=c89 misra/misra-test.h + ../../cppcheck --dump misra/misra-test.cpp + python3 ../misra.py -verify misra/misra-test.cpp.dump + python3 ../misra.py --rule-texts=misra/misra2012_rules_dummy_ascii.txt -verify misra/misra-test.cpp.dump + python3 ../misra.py --rule-texts=misra/misra2012_rules_dummy_utf8.txt -verify misra/misra-test.cpp.dump + python3 ../misra.py --rule-texts=misra/misra2012_rules_dummy_windows1250.txt -verify misra/misra-test.cpp.dump + ../../cppcheck --addon=misra --enable=style --platform=avr8 --error-exitcode=1 misra/misra-test-avr8.c + ../../cppcheck --dump misc-test.cpp + python3 ../misc.py -verify misc-test.cpp.dump + ../../cppcheck --dump naming_test.c + python3 ../naming.py --var='[a-z].*' --function='[a-z].*' naming_test.c.dump + ../../cppcheck --dump naming_test.cpp + python3 ../naming.py --var='[a-z].*' --function='[a-z].*' naming_test.cpp.dump + + - name: Build democlient + if: matrix.os == 'ubuntu-22.04' + run: | + warnings="-pedantic -Wall -Wextra -Wcast-qual -Wno-deprecated-declarations -Wfloat-equal -Wmissing-declarations -Wmissing-format-attribute -Wno-long-long -Wpacked -Wredundant-decls -Wundef -Wno-shadow -Wno-missing-field-initializers -Wno-missing-braces -Wno-sign-compare -Wno-multichar" + g++ $warnings -c -Ilib -Iexternals/tinyxml2 democlient/democlient.cpp + + selfcheck: + needs: build # wait for all tests to be successful first + + runs-on: ubuntu-22.04 # run on the latest image only + + steps: + - uses: actions/checkout@v3 + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} + + - name: Install missing software on ubuntu + run: | + sudo apt-get update + sudo apt-get install qtbase5-dev qttools5-dev libqt5charts5-dev libboost-container-dev + + - name: Self check (build) + run: | + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + # compile with verification and ast matchers + make -j$(nproc) -s CPPFLAGS="-DCHECK_INTERNAL" CXXFLAGS="-g -O2 -w -DHAVE_BOOST" MATCHCOMPILER=yes VERIFY=1 + + # TODO: update to Qt6 + - name: CMake + run: | + cmake -S . -B cmake.output -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DWITH_QCHART=On -DUSE_MATCHCOMPILER=Verify -DENABLE_CHECK_INTERNAL=On -DCPPCHK_GLIBCXX_DEBUG=Off -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On -DDISABLE_DMAKE=On + + - name: Generate dependencies + run: | + # make sure auto-generated GUI files exist + make -C cmake.output autogen + make -C cmake.output gui-build-deps triage-build-ui-deps + + - name: Self check + run: | + selfcheck_options="-q -j$(nproc) --std=c++11 --template=selfcheck --showtime=top5_summary -D__GNUC__ --error-exitcode=1 --inline-suppr --suppressions-list=.selfcheck_suppressions --library=gnu --inconclusive --enable=style,performance,portability,warning,missingInclude,internal --exception-handling --debug-warnings --check-level=exhaustive" + cppcheck_options="-D__CPPCHECK__ -DCHECK_INTERNAL -DHAVE_RULES --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2" + ec=0 + + # TODO: add --check-config + + # early exit + if [ $ec -eq 1 ]; then + exit $ec + fi + + # self check simplecpp + ./cppcheck $selfcheck_options externals/simplecpp || ec=1 + # self check lib/cli + mkdir b1 + ./cppcheck $selfcheck_options $cppcheck_options --cppcheck-build-dir=b1 --addon=naming.json cli lib || ec=1 + # check gui with qt settings + mkdir b2 + ./cppcheck $selfcheck_options $cppcheck_options --cppcheck-build-dir=b2 -DQT_VERSION=0x050000 -DQ_MOC_OUTPUT_REVISION=67 -DQT_CHARTS_LIB --library=qt --addon=naming.json -Icmake.output/gui -Igui gui/*.cpp cmake.output/gui || ec=1 + # self check test and tools + ./cppcheck $selfcheck_options $cppcheck_options -Icli test/*.cpp tools/*.cpp || ec=1 + # triage + ./cppcheck $selfcheck_options $cppcheck_options -DQ_MOC_OUTPUT_REVISION=67 -DQT_CHARTS_LIB --library=qt -Icmake.output/tools/triage -Igui tools/triage/*.cpp cmake.output/tools/triage || ec=1 + exit $ec diff --git a/cppcheck-2.14.0/.github/workflows/CI-windows.yml b/cppcheck-2.14.0/.github/workflows/CI-windows.yml new file mode 100644 index 00000000..19bcaf00 --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/CI-windows.yml @@ -0,0 +1,227 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: CI-windows + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + +defaults: + run: + shell: cmd + +# TODO: choose/add a step to bail out on compiler warnings (maybe even the release build) + +jobs: + + build_qt: + strategy: + matrix: + os: [windows-2019, windows-2022] + qt_ver: [5.15.2, 6.7.0] + fail-fast: false + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v3 + + - name: Set up Visual Studio environment + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + + - name: Install Qt ${{ matrix.qt_ver }} + uses: jurplel/install-qt-action@v3 + with: + version: ${{ matrix.qt_ver }} + modules: 'qtcharts' + cache: true + + - name: Build GUI release (qmake) + if: startsWith(matrix.qt_ver, '5') + run: | + cd gui || exit /b !errorlevel! + qmake HAVE_QCHART=yes || exit /b !errorlevel! + nmake release || exit /b !errorlevel! + env: + CL: /MP + + - name: Deploy GUI + if: startsWith(matrix.qt_ver, '5') + run: | + windeployqt Build\gui || exit /b !errorlevel! + del Build\gui\cppcheck-gui.ilk || exit /b !errorlevel! + del Build\gui\cppcheck-gui.pdb || exit /b !errorlevel! + + - name: Build GUI release (CMake) + if: startsWith(matrix.qt_ver, '6') + run: | + cmake -S . -B build -DBUILD_GUI=On -DUSE_QT6=On -DWITH_QCHART=On || exit /b !errorlevel! + cmake --build build --target cppcheck-gui || exit /b !errorlevel! + + # TODO: deploy with CMake/Qt6 + + build: + strategy: + matrix: + os: [windows-2019, windows-2022] + config: [debug, release] + fail-fast: false + + runs-on: ${{ matrix.os }} + + env: + # see https://www.pcre.org/original/changelog.txt + PCRE_VERSION: 8.45 + + steps: + - uses: actions/checkout@v3 + + - name: Set up Python 3.12 + if: matrix.config == 'release' + uses: actions/setup-python@v4 + with: + python-version: '3.12' + check-latest: true + + - name: Set up Visual Studio environment + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + + - name: Cache PCRE + id: cache-pcre + uses: actions/cache@v3 + with: + path: | + externals\pcre.h + externals\pcre.lib + externals\pcre64.lib + key: pcre-${{ env.PCRE_VERSION }}-x64-bin-win + + - name: Download PCRE + if: steps.cache-pcre.outputs.cache-hit != 'true' + run: | + curl -fsSL https://github.com/pfultz2/pcre/archive/refs/tags/%PCRE_VERSION%.zip -o pcre-%PCRE_VERSION%.zip || exit /b !errorlevel! + + - name: Install PCRE + if: steps.cache-pcre.outputs.cache-hit != 'true' + run: | + 7z x pcre-%PCRE_VERSION%.zip || exit /b !errorlevel! + cd pcre-%PCRE_VERSION% || exit /b !errorlevel! + cmake . -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DPCRE_BUILD_PCRECPP=Off -DPCRE_BUILD_TESTS=Off -DPCRE_BUILD_PCREGREP=Off || exit /b !errorlevel! + nmake || exit /b !errorlevel! + copy pcre.h ..\externals || exit /b !errorlevel! + copy pcre.lib ..\externals\pcre64.lib || exit /b !errorlevel! + env: + CL: /MP + + - name: Install missing Python packages + if: matrix.config == 'release' + run: | + python -m pip install pip --upgrade || exit /b !errorlevel! + python -m pip install pytest || exit /b !errorlevel! + python -m pip install pytest-custom_exit_code || exit /b !errorlevel! + python -m pip install pytest-timeout || exit /b !errorlevel! + + - name: Run CMake + if: false # TODO: enable + run: | + cmake -S . -B build -DBUILD_TESTS=On || exit /b !errorlevel! + + - name: Build CLI debug configuration using MSBuild + if: matrix.config == 'debug' + run: | + :: cmake --build build --target check --config Debug || exit /b !errorlevel! + msbuild -m cppcheck.sln /p:Configuration=Debug-PCRE;Platform=x64 -maxcpucount || exit /b !errorlevel! + + - name: Run Debug test + if: matrix.config == 'debug' + run: .\bin\debug\testrunner.exe || exit /b !errorlevel! + + - name: Build CLI release configuration using MSBuild + if: matrix.config == 'release' + run: | + :: cmake --build build --target check --config Release || exit /b !errorlevel! + msbuild -m cppcheck.sln /p:Configuration=Release-PCRE;Platform=x64 -maxcpucount || exit /b !errorlevel! + + - name: Run Release test + if: matrix.config == 'release' + run: .\bin\testrunner.exe || exit /b !errorlevel! + + - name: Prepare test/cli + if: matrix.config == 'release' + run: | + :: since FILESDIR is not set copy the binary to the root so the addons are found + :: copy .\build\bin\Release\cppcheck.exe .\cppcheck.exe || exit /b !errorlevel! + copy .\bin\cppcheck.exe .\cppcheck.exe || exit /b !errorlevel! + copy .\bin\cppcheck-core.dll .\cppcheck-core.dll || exit /b !errorlevel! + + - name: Run test/cli + if: matrix.config == 'release' + run: | + cd test/cli || exit /b !errorlevel! + python -m pytest -Werror --strict-markers -vv || exit /b !errorlevel! + + - name: Run test/cli (-j2) + if: matrix.config == 'release' + run: | + cd test/cli || exit /b !errorlevel! + python -m pytest -Werror --strict-markers -vv || exit /b !errorlevel! + env: + TEST_CPPCHECK_INJECT_J: 2 + + # TODO: install clang + - name: Run test/cli (--clang) + if: false # matrix.config == 'release' + run: | + cd test/cli || exit /b !errorlevel! + python -m pytest -Werror --strict-markers -vv || exit /b !errorlevel! + env: + TEST_CPPCHECK_INJECT_CLANG: clang + + - name: Test addons + if: matrix.config == 'release' + run: | + .\cppcheck --addon=threadsafety addons\test\threadsafety || exit /b !errorlevel! + .\cppcheck --addon=threadsafety --std=c++03 addons\test\threadsafety || exit /b !errorlevel! + .\cppcheck --addon=misra --enable=style --inline-suppr --enable=information --error-exitcode=1 addons\test\misra\misra-ctu-*-test.c || exit /b !errorlevel! + cd addons\test + rem We'll force C89 standard to enable an additional verification for + rem rules 5.4 and 5.5 which have standard-dependent options. + ..\..\cppcheck --dump -DDUMMY --suppress=uninitvar --inline-suppr misra\misra-test.c --std=c89 --platform=unix64 || exit /b !errorlevel! + python3 ..\misra.py -verify misra\misra-test.c.dump || exit /b !errorlevel! + rem Test slight MISRA differences in C11 standard + ..\..\cppcheck --dump -DDUMMY --suppress=uninitvar --inline-suppr misra\misra-test-c11.c --std=c11 --platform=unix64 || exit /b !errorlevel! + python3 ..\misra.py -verify misra\misra-test-c11.c.dump || exit /b !errorlevel! + rem TODO: do we need to verify something here? + ..\..\cppcheck --dump -DDUMMY --suppress=uninitvar --suppress=uninitStructMember --std=c89 misra\misra-test.h || exit /b !errorlevel! + ..\..\cppcheck --dump misra\misra-test.cpp || exit /b !errorlevel! + python3 ..\misra.py -verify misra\misra-test.cpp.dump || exit /b !errorlevel! + python3 ..\misra.py --rule-texts=misra\misra2012_rules_dummy_ascii.txt -verify misra\misra-test.cpp.dump || exit /b !errorlevel! + python3 ..\misra.py --rule-texts=misra\misra2012_rules_dummy_utf8.txt -verify misra\misra-test.cpp.dump || exit /b !errorlevel! + python3 ..\misra.py --rule-texts=misra\misra2012_rules_dummy_windows1250.txt -verify misra\misra-test.cpp.dump || exit /b !errorlevel! + ..\..\cppcheck --addon=misra --enable=style --platform=avr8 --error-exitcode=1 misra\misra-test-avr8.c || exit /b !errorlevel! + ..\..\cppcheck --dump misc-test.cpp || exit /b !errorlevel! + python3 ..\misc.py -verify misc-test.cpp.dump || exit /b !errorlevel! + ..\..\cppcheck --dump naming_test.c || exit /b !errorlevel! + rem TODO: fix this - does not fail on Linux + rem python3 ..\naming.py --var='[a-z].*' --function='[a-z].*' naming_test.c.dump || exit /b !errorlevel! + ..\..\cppcheck --dump naming_test.cpp || exit /b !errorlevel! + python3 ..\naming.py --var='[a-z].*' --function='[a-z].*' naming_test.cpp.dump || exit /b !errorlevel! + + - name: Check Windows test syntax + if: matrix.config == 'debug' + run: | + cd test\cfg + cl.exe windows.cpp -DUNICODE=1 -D_UNICODE=1 /Zs || exit /b !errorlevel! + cl.exe mfc.cpp /EHsc /Zs || exit /b !errorlevel! diff --git a/cppcheck-2.14.0/.github/workflows/asan.yml b/cppcheck-2.14.0/.github/workflows/asan.yml new file mode 100644 index 00000000..e223952f --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/asan.yml @@ -0,0 +1,140 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: address sanitizer + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-22.04 + + env: + QT_VERSION: 5.15.2 + ASAN_OPTIONS: detect_stack_use_after_return=1 + # TODO: figure out why there are cache misses with PCH enabled + CCACHE_SLOPPINESS: pch_defines,time_macros + + steps: + - uses: actions/checkout@v3 + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} + + - name: Set up Python 3.12 + uses: actions/setup-python@v4 + with: + python-version: '3.12' + check-latest: true + + - name: Install missing software on ubuntu + run: | + sudo apt-get update + sudo apt-get install -y cmake make libpcre3-dev libboost-container-dev libxml2-utils + + - name: Install clang + run: | + sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14 + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 18 + + - name: Install Qt ${{ env.QT_VERSION }} + if: false + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + modules: 'qtcharts' + cache: true + + - name: Install missing Python packages + run: | + python3 -m pip install pip --upgrade + python3 -m pip install pytest + python3 -m pip install pytest-timeout + + # TODO: disable all warnings + - name: CMake + run: | + cmake -S . -B cmake.output -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=Off -DWITH_QCHART=Off -DUSE_MATCHCOMPILER=Verify -DANALYZE_ADDRESS=On -DENABLE_CHECK_INTERNAL=On -DUSE_BOOST=On -DCPPCHK_GLIBCXX_DEBUG=Off -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=Off -DDISABLE_DMAKE=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + env: + CC: clang-18 + CXX: clang++-18 + + - name: Build cppcheck + run: | + cmake --build cmake.output --target cppcheck -- -j $(nproc) + + - name: Build test + run: | + cmake --build cmake.output --target testrunner -- -j $(nproc) + + - name: Run tests + run: ./cmake.output/bin/testrunner + + - name: Run cfg tests + run: | + cmake --build cmake.output --target checkcfg -- -j $(nproc) + + # TODO: we should use CTest instead to parallelize tests but the start-up overhead will slow things down + - name: Run CTest + if: false + run: | + ctest --test-dir cmake.output --output-on-failure -j$(nproc) + + - name: Run test/cli + run: | + pwd=$(pwd) + cd test/cli + TEST_CPPCHECK_EXE_LOOKUP_PATH="$pwd/cmake.output" python3 -m pytest -Werror --strict-markers -vv + + - name: Run test/cli (-j2) + run: | + pwd=$(pwd) + cd test/cli + TEST_CPPCHECK_EXE_LOOKUP_PATH="$pwd/cmake.output" python3 -m pytest -Werror --strict-markers -vv + env: + TEST_CPPCHECK_INJECT_J: 2 + + - name: Run test/cli (--clang) + if: false + run: | + pwd=$(pwd) + cd test/cli + TEST_CPPCHECK_EXE_LOOKUP_PATH="$pwd/cmake.output" python3 -m pytest -Werror --strict-markers -vv + env: + TEST_CPPCHECK_INJECT_CLANG: clang + + - name: Generate dependencies + if: false + run: | + # make sure auto-generated GUI files exist + make -C cmake.output autogen + make -C cmake.output gui-build-deps triage-build-ui-deps + + # TODO: this is currently way too slow (~60 minutes) to enable it + # TODO: only fail the step on sanitizer issues - since we use processes it will only fail the underlying process which will result in an cppcheckError + - name: Self check + if: false + run: | + selfcheck_options="-q -j$(nproc) --std=c++11 --template=selfcheck --showtime=top5_summary -D__GNUC__ --error-exitcode=1 --inline-suppr --suppressions-list=.selfcheck_suppressions --library=gnu --inconclusive --enable=style,performance,portability,warning,missingInclude,internal --exception-handling --debug-warnings --check-level=exhaustive" + cppcheck_options="-D__CPPCHECK__ -DCHECK_INTERNAL -DHAVE_RULES --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2" + ec=0 + ./cmake.output/bin/cppcheck $selfcheck_options externals/simplecpp || ec=1 + ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options --addon=naming.json cli lib || ec=1 + ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -DQT_VERSION=0x060000 -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB -DQT_MOC_HAS_STRINGDATA --library=qt --addon=naming.json -Icmake.output/gui -Igui gui/*.cpp cmake.output/gui/*.cpp || ec=1 + ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -Icli test/*.cpp tools/*.cpp || ec=1 + ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB -DQT_MOC_HAS_STRINGDATA --library=qt -Icmake.output/tools/triage -Igui tools/triage/*.cpp cmake.output/tools/triage/*.cpp || ec=1 + exit $ec diff --git a/cppcheck-2.14.0/.github/workflows/buildman.yml b/cppcheck-2.14.0/.github/workflows/buildman.yml new file mode 100644 index 00000000..1d680471 --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/buildman.yml @@ -0,0 +1,60 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: Build manual + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + +jobs: + convert_via_pandoc: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + + - run: | + mkdir output + + - uses: docker://pandoc/latex:2.9 + with: + args: --output=output/manual.html man/manual.md + + - uses: docker://pandoc/latex:2.9 + with: + args: --output=output/manual.pdf man/manual.md + + - uses: docker://pandoc/latex:2.9 + with: + args: --output=output/manual-premium.pdf man/manual-premium.md + + - uses: actions/upload-artifact@v3 + with: + name: output + path: output + + manpage: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + + - name: Install missing software on ubuntu + run: | + sudo apt-get update + sudo apt-get install -y xsltproc docbook-xsl + + - name: build manpage + run: | + make man + + - uses: actions/upload-artifact@v3 + with: + name: cppcheck.1 + path: cppcheck.1 diff --git a/cppcheck-2.14.0/.github/workflows/cifuzz.yml b/cppcheck-2.14.0/.github/workflows/cifuzz.yml new file mode 100644 index 00000000..cfc3bc12 --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/cifuzz.yml @@ -0,0 +1,34 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: CIFuzz + +on: [pull_request] + +permissions: + contents: read + +jobs: + Fuzzing: + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'danmar' }} + steps: + - name: Build Fuzzers + id: build + uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master + with: + oss-fuzz-project-name: 'cppcheck' + dry-run: false + language: c++ + - name: Run Fuzzers + uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master + with: + oss-fuzz-project-name: 'cppcheck' + fuzz-seconds: 300 + dry-run: false + language: c++ + - name: Upload Crash + uses: actions/upload-artifact@v3 + if: failure() && steps.build.outcome == 'success' + with: + name: artifacts + path: ./out/artifacts diff --git a/cppcheck-2.14.0/.github/workflows/clang-tidy.yml b/cppcheck-2.14.0/.github/workflows/clang-tidy.yml new file mode 100644 index 00000000..29cf0ecb --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/clang-tidy.yml @@ -0,0 +1,73 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: clang-tidy + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-22.04 + + env: + QT_VERSION: 6.7.0 + + steps: + - uses: actions/checkout@v3 + + - name: Install missing software + run: | + sudo apt-get update + sudo apt-get install -y cmake make + sudo apt-get install -y libpcre3-dev + sudo apt-get install -y libffi7 # work around missing dependency for Qt install step + + - name: Install clang + run: | + sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14 + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 18 + sudo apt-get install -y clang-tidy-18 + + - name: Install Qt ${{ env.QT_VERSION }} + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + modules: 'qtcharts' + cache: true + + - name: Verify clang-tidy configuration + run: | + clang-tidy-18 --verify-config + + - name: Prepare CMake + run: | + cmake -S . -B cmake.output -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DUSE_QT6=On -DWITH_QCHART=On -DENABLE_CHECK_INTERNAL=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On -DCPPCHK_GLIBCXX_DEBUG=Off + env: + CC: clang-18 + CXX: clang++-18 + + - name: Prepare CMake dependencies + run: | + # make sure the precompiled headers exist + make -C cmake.output/cli cmake_pch.hxx.pch + make -C cmake.output/gui cmake_pch.hxx.pch + make -C cmake.output/lib cmake_pch.hxx.pch + make -C cmake.output/test cmake_pch.hxx.pch + # make sure the auto-generated GUI sources exist + make -C cmake.output autogen + + - name: Clang-Tidy + run: | + cmake --build cmake.output --target run-clang-tidy 2> /dev/null diff --git a/cppcheck-2.14.0/.github/workflows/codeql-analysis.yml b/cppcheck-2.14.0/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..d553598c --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/codeql-analysis.yml @@ -0,0 +1,56 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: "CodeQL" + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + security-events: write + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-22.04 + + strategy: + fail-fast: false + matrix: + # Override automatic language detection by changing the below list + # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] + language: ['cpp', 'python'] + # Learn more... + # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Install missing software on ubuntu + run: | + sudo apt-get update + sudo apt-get install libxml2-utils + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + setup-python-dependencies: false + + - run: | + make -j$(nproc) HAVE_RULES=yes cppcheck + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/cppcheck-2.14.0/.github/workflows/coverage.yml b/cppcheck-2.14.0/.github/workflows/coverage.yml new file mode 100644 index 00000000..5a6d1696 --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/coverage.yml @@ -0,0 +1,70 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: Coverage + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-22.04 + # FIXME: disabled because the tokenless upload suddenly started to permanently fail + if: false # ${{ github.repository_owner == 'danmar' }} + + steps: + - uses: actions/checkout@v3 + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ runner.os }} + + - name: Install missing software on ubuntu + run: | + sudo apt-get update + sudo apt-get install libxml2-utils lcov + + - name: Install missing Python packages on ubuntu + run: | + python -m pip install pip --upgrade + python -m pip install lcov_cobertura + + - name: Compile instrumented + run: | + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + make -j$(nproc) all CXXFLAGS="-g -fprofile-arcs -ftest-coverage" HAVE_RULES=yes + + - name: Run instrumented tests + run: | + ./testrunner + test/cfg/runtests.sh + + - name: Generate coverage report + run: | + gcov lib/*.cpp -o lib/ + lcov --directory ./ --capture --output-file lcov_tmp.info -b ./ + lcov --extract lcov_tmp.info "$(pwd)/*" --output-file lcov.info + genhtml lcov.info -o coverage_report --frame --legend --demangle-cpp + + - uses: actions/upload-artifact@v3 + with: + name: Coverage results + path: coverage_report + + - uses: codecov/codecov-action@v3 + with: + # token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos + # file: ./coverage.xml # optional + flags: unittests # optional + name: ${{ github.repository }} # optional + fail_ci_if_error: true # optional (default = false): diff --git a/cppcheck-2.14.0/.github/workflows/coverity.yml b/cppcheck-2.14.0/.github/workflows/coverity.yml new file mode 100644 index 00000000..148fc154 --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/coverity.yml @@ -0,0 +1,39 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: Coverity + +on: + schedule: + - cron: "0 0 * * *" + +permissions: + contents: read + +jobs: + scan: + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'danmar' }} + steps: + - uses: actions/checkout@v4 + - name: Install missing software on ubuntu + run: | + sudo apt-get update + sudo apt-get install qtbase5-dev qttools5-dev libqt5charts5-dev libboost-container-dev + - name: Download Coverity build tool + run: | + wget -c -N https://scan.coverity.com/download/linux64 --post-data "token=${{ secrets.COVERITY_SCAN_TOKEN }}&project=cppcheck" -O coverity_tool.tar.gz + mkdir coverity_tool + tar xzf coverity_tool.tar.gz --strip 1 -C coverity_tool + - name: Build with Coverity build tool + run: | + export PATH=`pwd`/coverity_tool/bin:$PATH + cov-build --dir cov-int make CPPCHK_GLIBCXX_DEBUG= + - name: Submit build result to Coverity Scan + run: | + tar czvf cov.tar.gz cov-int + curl --form token=${{ secrets.COVERITY_SCAN_TOKEN }} \ + --form email=daniel.marjamaki@gmail.com \ + --form file=@cov.tar.gz \ + --form version="Commit $GITHUB_SHA" \ + --form description="Development" \ + https://scan.coverity.com/builds?project=cppcheck diff --git a/cppcheck-2.14.0/.github/workflows/cppcheck-premium.yml b/cppcheck-2.14.0/.github/workflows/cppcheck-premium.yml new file mode 100644 index 00000000..a9b40184 --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/cppcheck-premium.yml @@ -0,0 +1,44 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: cppcheck-premium + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + +jobs: + + build: + runs-on: ubuntu-22.04 # run on the latest image only + + env: + PREMIUM_VERSION: 24.2.0 + + steps: + - uses: actions/checkout@v3 + + - name: Download cppcheckpremium + run: | + wget https://files.cppchecksolutions.com/${{ env.PREMIUM_VERSION }}/ubuntu-22.04/cppcheckpremium-${{ env.PREMIUM_VERSION }}-amd64.tar.gz + tar xzf cppcheckpremium-${{ env.PREMIUM_VERSION }}-amd64.tar.gz + + - name: Generate a license file + run: | + echo cppcheck > cppcheck.lic + echo 241231 >> cppcheck.lic + echo 80000 >> cppcheck.lic + echo 53b72a908d7aeeee >> cppcheck.lic + echo path:lib >> cppcheck.lic + + - name: Check + run: | + cppcheckpremium-${{ env.PREMIUM_VERSION }}/premiumaddon --check-loc-license cppcheck.lic > cppcheck-premium-loc + cppcheckpremium-${{ env.PREMIUM_VERSION }}/cppcheck -j$(nproc) -D__GNUC__ -D__CPPCHECK__ --suppressions-list=cppcheckpremium-suppressions --platform=unix64 --enable=style --premium=misra-c++-2008 --premium=cert-c++-2016 --inline-suppr --error-exitcode=1 lib diff --git a/cppcheck-2.14.0/.github/workflows/format.yml b/cppcheck-2.14.0/.github/workflows/format.yml new file mode 100644 index 00000000..3ae9ceb2 --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/format.yml @@ -0,0 +1,47 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: format + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v3 + + - name: Cache uncrustify + uses: actions/cache@v3 + id: cache-uncrustify + with: + path: | + ~/uncrustify + key: ${{ runner.os }}-uncrustify + + - name: build uncrustify + if: steps.cache-uncrustify.outputs.cache-hit != 'true' + run: | + wget https://github.com/uncrustify/uncrustify/archive/refs/tags/uncrustify-0.72.0.tar.gz + tar xzvf uncrustify-0.72.0.tar.gz && cd uncrustify-uncrustify-0.72.0 + cmake -S . -B build -DCMAKE_BUILD_TYPE=Release + cmake --build build -- -j$(nproc) -s + mkdir ~/uncrustify + cd build && cp uncrustify ~/uncrustify/ + + - name: Uncrustify check + run: | + ~/uncrustify/uncrustify -c .uncrustify.cfg -l CPP --no-backup --replace */*.cpp */*.h + git diff + git diff | diff - /dev/null &> /dev/null diff --git a/cppcheck-2.14.0/.github/workflows/iwyu.yml b/cppcheck-2.14.0/.github/workflows/iwyu.yml new file mode 100644 index 00000000..738d0d14 --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/iwyu.yml @@ -0,0 +1,187 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: include-what-you-use + +on: + schedule: + - cron: '0 0 * * 0' + workflow_dispatch: + +permissions: + contents: read + +jobs: + iwyu: + + strategy: + matrix: + image: ["archlinux:latest"] # "opensuse/tumbleweed:latest" / "fedora:latest" / "debian:unstable" / "archlinux:latest" + + runs-on: ubuntu-22.04 + if: ${{ github.repository_owner == 'danmar' }} + + container: + image: ${{ matrix.image }} + + env: + QT_VERSION: 6.7.0 + + steps: + - uses: actions/checkout@v3 + + - name: Install missing software on debian/ubuntu + if: contains(matrix.image, 'debian') + run: | + apt-get update + apt-get install -y cmake clang make libpcre3-dev + apt-get install -y libgl-dev # fixes missing dependency for Qt in CMake + apt-get install -y iwyu + + - name: Install missing software on archlinux + if: contains(matrix.image, 'archlinux') + run: | + set -x + pacman -Sy + pacman -S cmake make clang pcre --noconfirm + pacman -S libglvnd --noconfirm # fixes missing dependency for Qt in CMake + pacman-key --init + pacman-key --recv-key 3056513887B78AEB --keyserver keyserver.ubuntu.com + pacman-key --lsign-key 3056513887B78AEB + pacman -U 'https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-keyring.pkg.tar.zst' 'https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-mirrorlist.pkg.tar.zst' --noconfirm + echo "[chaotic-aur]" >> /etc/pacman.conf + echo "Include = /etc/pacman.d/chaotic-mirrorlist" >> /etc/pacman.conf + pacman -Sy + pacman -S include-what-you-use --noconfirm + ln -s iwyu-tool /usr/sbin/iwyu_tool + + - name: Install missing software on Fedora + if: contains(matrix.image, 'fedora') + run: | + dnf install -y cmake clang pcre-devel + dnf install -y libglvnd-devel # fixes missing dependency for Qt in CMake + dnf install -y iwyu + ln -s iwyu_tool.py /usr/bin/iwyu_tool + + - name: Install missing software on OpenSUSE + if: contains(matrix.image, 'opensuse') + run: | + zypper install -y cmake clang pcre-devel + zypper install -y include-what-you-use-tools + # fixes error during Qt installation + # /__w/cppcheck/Qt/6.7.0/gcc_64/bin/qmake: error while loading shared libraries: libgthread-2.0.so.0: cannot open shared object file: No such file or directory + zypper install -y libgthread-2_0-0 + ln -s iwyu_tool.py /usr/bin/iwyu_tool + + # Fails on OpenSUSE: + # Warning: Failed to restore: Tar failed with error: Unable to locate executable file: tar. Please verify either the file path exists or the file can be found within a directory specified by the PATH environment variable. Also check the file mode to verify the file is executable. + # Also the shell is broken afterwards: + # OCI runtime exec failed: exec failed: unable to start container process: exec: "sh": executable file not found in $PATH: unknown + - name: Install Qt ${{ env.QT_VERSION }} + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + modules: 'qtcharts' + install-deps: false + cache: true + + - name: Prepare CMake + run: | + cmake -S . -B cmake.output -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DUSE_QT6=On -DWITH_QCHART=On -DENABLE_CHECK_INTERNAL=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCPPCHK_GLIBCXX_DEBUG=Off -DUSE_MATCHCOMPILER=Off -DEXTERNALS_AS_SYSTEM=On + env: + CC: clang + CXX: clang++ + + # Fails on Debian: + # /__w/cppcheck/Qt/6.7.0/gcc_64/libexec/rcc: error while loading shared libraries: libglib-2.0.so.0: cannot open shared object file: No such file or directory + - name: Prepare CMake dependencies + run: | + # make sure the precompiled headers exist + #make -C cmake.output/cli cmake_pch.hxx.pch + #make -C cmake.output/gui cmake_pch.hxx.pch + #make -C cmake.output/lib cmake_pch.hxx.pch + #make -C cmake.output/test cmake_pch.hxx.pch + # make sure the auto-generated GUI sources exist + make -C cmake.output autogen + # make sure the auto-generated GUI dependencies exist + make -C cmake.output gui-build-deps + make -C cmake.output triage-build-ui-deps + + - name: iwyu_tool + run: | + PWD=$(pwd) + # -isystem/usr/lib/clang/17/include + iwyu_tool -p cmake.output -j $(nproc) -- -w -Xiwyu --max_line_length=1024 -Xiwyu --comment_style=long -Xiwyu --quoted_includes_first -Xiwyu --update_comments > iwyu.log + + - uses: actions/upload-artifact@v3 + if: success() || failure() + with: + name: Compilation Database + path: ./cmake.output/compile_commands.json + + - uses: actions/upload-artifact@v3 + if: success() || failure() + with: + name: Logs (include-what-you-use) + path: ./*.log + + clang-include-cleaner: + + runs-on: ubuntu-22.04 + if: ${{ github.repository_owner == 'danmar' }} + + env: + QT_VERSION: 6.7.0 + + steps: + - uses: actions/checkout@v3 + + - name: Install missing software + run: | + sudo apt-get update + sudo apt-get install -y cmake make libpcre3-dev + sudo apt-get install -y libgl-dev # missing dependency for using Qt in CMake + + - name: Install clang + run: | + sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14 + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 18 + sudo apt-get install -y clang-tools-18 + + - name: Install Qt ${{ env.QT_VERSION }} + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + modules: 'qtcharts' + install-deps: false + cache: true + + - name: Prepare CMake + run: | + cmake -S . -B cmake.output -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DUSE_QT6=On -DWITH_QCHART=On -DENABLE_CHECK_INTERNAL=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCPPCHK_GLIBCXX_DEBUG=Off -DUSE_MATCHCOMPILER=Off -DEXTERNALS_AS_SYSTEM=On + env: + CC: clang-18 + CXX: clang++-18 + + - name: Prepare CMake dependencies + run: | + # make sure the precompiled headers exist + #make -C cmake.output/cli cmake_pch.hxx.pch + #make -C cmake.output/gui cmake_pch.hxx.pch + #make -C cmake.output/lib cmake_pch.hxx.pch + #make -C cmake.output/test cmake_pch.hxx.pch + # make sure the auto-generated GUI sources exist + make -C cmake.output autogen + # make sure the auto-generated GUI dependencies exist + make -C cmake.output gui-build-deps + + - name: clang-include-cleaner + run: | + # TODO: run multi-threaded + find $PWD/cli $PWD/lib $PWD/test $PWD/gui -maxdepth 1 -name "*.cpp" | xargs -t -n 1 clang-include-cleaner-18 --print=changes --extra-arg=-w -p cmake.output > clang-include-cleaner.log 2>&1 + + - uses: actions/upload-artifact@v3 + with: + name: Logs (clang-include-cleaner) + path: ./*.log diff --git a/cppcheck-2.14.0/.github/workflows/release-windows.yml b/cppcheck-2.14.0/.github/workflows/release-windows.yml new file mode 100644 index 00000000..ca4bbac6 --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/release-windows.yml @@ -0,0 +1,168 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: release-windows + +on: + push: + tags: + - '2.*' + schedule: + - cron: '0 0 * * *' + workflow_dispatch: + +permissions: + contents: read + +defaults: + run: + shell: cmd + +jobs: + + build: + + runs-on: windows-2022 + if: ${{ github.repository_owner == 'danmar' }} + + env: + # see https://www.pcre.org/original/changelog.txt + PCRE_VERSION: 8.45 + QT_VERSION: 5.15.2 + + steps: + - uses: actions/checkout@v3 + + - name: Set up Visual Studio environment + uses: ilammy/msvc-dev-cmd@v1 + + - name: Cache PCRE + id: cache-pcre + uses: actions/cache@v3 + with: + path: | + externals\pcre.h + externals\pcre64.lib + key: pcre-${{ env.PCRE_VERSION }}-bin-x64-win-release-job + + - name: Download PCRE + if: steps.cache-pcre.outputs.cache-hit != 'true' + run: | + curl -fsSL https://github.com/pfultz2/pcre/archive/refs/tags/%PCRE_VERSION%.zip -o pcre-%PCRE_VERSION%.zip || exit /b !errorlevel! + + - name: Install PCRE + if: steps.cache-pcre.outputs.cache-hit != 'true' + run: | + 7z x pcre-%PCRE_VERSION%.zip || exit /b !errorlevel! + cd pcre-%PCRE_VERSION% || exit /b !errorlevel! + cmake . -G "Visual Studio 17 2022" -A x64 -DPCRE_BUILD_PCRECPP=OFF -DPCRE_BUILD_PCREGREP=OFF -DPCRE_BUILD_TESTS=OFF || exit /b !errorlevel! + msbuild -m PCRE.sln -p:Configuration=Release -p:Platform=x64 || exit /b !errorlevel! + copy pcre.h ..\externals || exit /b !errorlevel! + copy Release\pcre.lib ..\externals\pcre64.lib || exit /b !errorlevel! + + # available modules: https://github.com/miurahr/aqtinstall/blob/master/docs/getting_started.rst#installing-modules + # available tools: https://github.com/miurahr/aqtinstall/blob/master/docs/getting_started.rst#installing-tools + - name: Install Qt ${{ env.QT_VERSION }} + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + modules: 'qtcharts' + tools: 'tools_opensslv3_x64' + cache: true + + - name: Create .qm + run: | + cd gui || exit /b !errorlevel! + lupdate gui.pro -no-obsolete || exit /b !errorlevel! + lrelease gui.pro -removeidentical || exit /b !errorlevel! + + - name: Matchcompiler + run: python tools\matchcompiler.py --write-dir lib || exit /b !errorlevel! + + - name: Build x64 release GUI + run: | + cd gui || exit /b !errorlevel! + qmake HAVE_QCHART=yes || exit /b !errorlevel! + nmake release || exit /b !errorlevel! + env: + CL: /MP + + - name: Deploy app + run: | + windeployqt Build\gui || exit /b !errorlevel! + del Build\gui\cppcheck-gui.ilk || exit /b !errorlevel! + del Build\gui\cppcheck-gui.pdb || exit /b !errorlevel! + + # TODO: build with boost enabled + - name: Build CLI x64 release configuration using MSBuild + run: msbuild -m cppcheck.sln -t:cli -p:Configuration=Release-PCRE -p:Platform=x64 || exit /b !errorlevel! + + - name: Compile misra.py executable + run: | + pip install -U pyinstaller || exit /b !errorlevel! + cd addons || exit /b !errorlevel! + pyinstaller --hidden-import xml --hidden-import xml.etree --hidden-import xml.etree.ElementTree misra.py || exit /b !errorlevel! + del *.spec || exit /b !errorlevel! + + - name: Collect files + run: | + move Build\gui win_installer\files || exit /b !errorlevel! + mkdir win_installer\files\addons || exit /b !errorlevel! + copy addons\*.* win_installer\files\addons || exit /b !errorlevel! + copy addons\dist\misra\*.* win_installer\files\addons || exit /b !errorlevel! + mkdir win_installer\files\cfg || exit /b !errorlevel! + copy cfg\*.cfg win_installer\files\cfg || exit /b !errorlevel! + :: "platforms" is a folder used by Qt as well so it already exists + :: mkdir win_installer\files\platforms || exit /b !errorlevel! + copy platforms\*.xml win_installer\files\platforms || exit /b !errorlevel! + copy bin\cppcheck.exe win_installer\files || exit /b !errorlevel! + copy bin\cppcheck-core.dll win_installer\files || exit /b !errorlevel! + mkdir win_installer\files\help || exit /b !errorlevel! + xcopy /s gui\help win_installer\files\help || exit /b !errorlevel! + del win_installer\files\translations\*.qm || exit /b !errorlevel! + move gui\*.qm win_installer\files\translations || exit /b !errorlevel! + :: copy libcrypto-3-x64.dll and libssl-3-x64.dll + copy %RUNNER_WORKSPACE%\Qt\Tools\OpenSSLv3\Win_x64\bin\lib*.dll win_installer\files || exit /b !errorlevel! + + - name: Build Installer + run: | + cd win_installer || exit /b !errorlevel! + REM Read ProductVersion + for /f "tokens=4 delims= " %%a in ('find "ProductVersion" productInfo.wxi') do set PRODUCTVER=%%a + REM Remove double quotes + set PRODUCTVER=%PRODUCTVER:"=% + echo ProductVersion="%PRODUCTVER%" || exit /b !errorlevel! + msbuild -m cppcheck.wixproj -p:Platform=x64,ProductVersion=%PRODUCTVER%.${{ github.run_number }} || exit /b !errorlevel! + + - uses: actions/upload-artifact@v3 + with: + name: installer + path: win_installer/Build/ + + - uses: actions/upload-artifact@v3 + with: + name: deploy + path: win_installer\files + + - name: Clean up deploy + run: | + del win_installer\files\addons\*.dll || exit /b !errorlevel! + del win_installer\files\addons\*.pyd || exit /b !errorlevel! + del win_installer\files\addons\base_library.zip || exit /b !errorlevel! + rmdir /s /q win_installer\files\bearer || exit /b !errorlevel! + rmdir /s /q win_installer\files\help || exit /b !errorlevel! + rmdir /s /q win_installer\files\iconengines || exit /b !errorlevel! + rmdir /s /q win_installer\files\imageformats || exit /b !errorlevel! + rmdir /s /q win_installer\files\printsupport || exit /b !errorlevel! + rmdir /s /q win_installer\files\sqldrivers || exit /b !errorlevel! + ren win_installer\files\translations lang || exit /b !errorlevel! + del win_installer\files\d3dcompiler_47.dll || exit /b !errorlevel! + del win_installer\files\libEGL.dll || exit /b !errorlevel! + del win_installer\files\libGLESv2.dll || exit /b !errorlevel! + del win_installer\files\opengl32sw.dll || exit /b !errorlevel! + del win_installer\files\Qt5Svg.dll || exit /b !errorlevel! + del win_installer\files\vc_redist.x64.exe || exit /b !errorlevel! + + - uses: actions/upload-artifact@v3 + with: + name: portable + path: win_installer\files diff --git a/cppcheck-2.14.0/.github/workflows/scriptcheck.yml b/cppcheck-2.14.0/.github/workflows/scriptcheck.yml new file mode 100644 index 00000000..5f702f2f --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/scriptcheck.yml @@ -0,0 +1,200 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: scriptcheck + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + +jobs: + build: + + # 'ubuntu-22.04' removes Python 2.7, 3.6 and 3.6 so keep the previous LTS version + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v3 + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ runner.os }} + + - name: Cache Cppcheck + uses: actions/cache@v3 + with: + path: cppcheck + key: ${{ runner.os }}-scriptcheck-cppcheck-${{ github.sha }} + + - name: build cppcheck + run: | + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + make -j$(nproc) -s CXXFLAGS="-w" + strip -s ./cppcheck + + scriptcheck: + + needs: build + # 'ubuntu-22.04' removes Python 2.7, 3.5 and 3.6 so keep the previous LTS version + # 'ubutunu-20.04' no longer works on 2.7 - TODO: re-added in a different way or remove support for it? + runs-on: ubuntu-20.04 + strategy: + matrix: + python-version: [3.5, 3.6, 3.7, 3.8, 3.9, '3.10', '3.11', '3.12'] + include: + - python-version: '3.12' + python-latest: true + + fail-fast: false + + steps: + - uses: actions/checkout@v3 + + - name: Restore Cppcheck + uses: actions/cache@v3 + with: + path: cppcheck + key: ${{ runner.os }}-scriptcheck-cppcheck-${{ github.sha }} + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + check-latest: true + + - name: Install missing software on ubuntu + run: | + sudo apt-get update + sudo apt-get install tidy libxml2-utils + + - name: Install missing software on ubuntu (Python 2) + if: matrix.python-version == '2.7' + run: | + python -m pip install pip --upgrade + python -m pip install pathlib + python -m pip install pytest + python -m pip install pygments + + - name: Install missing software on ubuntu (Python 3) + if: matrix.python-version != '2.7' + run: | + # shellcheck cannot be installed via pip + # ERROR: Could not find a version that satisfies the requirement shellcheck (from versions: none) + # ERROR: No matching distribution found for shellcheck + sudo apt-get install shellcheck + python -m pip install pip --upgrade + python -m pip install natsort + python -m pip install pexpect + python -m pip install pylint + python -m pip install unittest2 + python -m pip install pytest + python -m pip install pygments + python -m pip install requests + python -m pip install psutil + + - name: run Shellcheck + if: matrix.python-latest + run: | + find . -name "*.sh" | xargs shellcheck --exclude SC2002,SC2013,SC2034,SC2035,SC2043,SC2046,SC2086,SC2089,SC2090,SC2129,SC2211,SC2231 + + - name: run pylint + if: matrix.python-latest + run: | + echo "FIXME pylint is disabled for now because it fails to import files:" + echo "FIXME addons/runaddon.py:1:0: E0401: Unable to import 'cppcheckdata' (import-error)" + echo "FIXME addons/runaddon.py:1:0: E0401: Unable to import 'cppcheck' (import-error)" + # pylint --rcfile=pylintrc_travis --jobs $(nproc) addons/*.py htmlreport/cppcheck-htmlreport htmlreport/*.py tools/*.py + + - name: check .json files + if: matrix.python-latest + run: | + find . -name '*.json' | xargs -n 1 python -m json.tool > /dev/null + + - name: Validate + if: matrix.python-latest + run: | + make -j$(nproc) validateCFG validatePlatforms validateRules + + - name: check python syntax + if: matrix.python-version != '2.7' + run: | + python -m py_compile addons/*.py + python -m py_compile htmlreport/cppcheck-htmlreport + python -m py_compile htmlreport/*.py + python -m py_compile tools/*.py + + - name: compile addons + run: | + python -m compileall ./addons + + - name: test matchcompiler + run: | + python tools/test_matchcompiler.py + + # we cannot specify -Werror since xml/etree/ElementTree.py in Python 3.9/3.10 contains an unclosed file + - name: test addons + if: matrix.python-version == '3.9' || matrix.python-version == '3.10' + run: | + python -m pytest --strict-markers -vv addons/test + env: + PYTHONPATH: ./addons + + - name: test addons + if: matrix.python-version != '3.9' && matrix.python-version != '3.10' + run: | + python -m pytest -Werror --strict-markers -vv addons/test + env: + PYTHONPATH: ./addons + + - name: test htmlreport + run: | + htmlreport/test_htmlreport.py + cd htmlreport + ./check.sh + + - name: test reduce + run: | + python -m pytest -Werror --strict-markers -vv tools/reduce_test.py + env: + PYTHONPATH: ./tools + + - name: test donate_cpu_lib + if: matrix.python-version != '2.7' + run: | + python -m pytest -Werror --strict-markers -vv tools/donate_cpu_lib_test.py + env: + PYTHONPATH: ./tools + + - name: test donate_cpu_server + if: matrix.python-version != '2.7' + run: | + python -m pytest -Werror --strict-markers -vv tools/donate_cpu_server_test.py + env: + PYTHONPATH: ./tools + + dmake: + strategy: + matrix: + os: [ubuntu-22.04, macos-12, windows-2022] + fail-fast: false + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v3 + + - name: run dmake + run: | + make -j2 CXXFLAGS="-w" run-dmake + + - name: check diff + run: | + git diff --exit-code diff --git a/cppcheck-2.14.0/.github/workflows/selfcheck.yml b/cppcheck-2.14.0/.github/workflows/selfcheck.yml new file mode 100644 index 00000000..f76cb978 --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/selfcheck.yml @@ -0,0 +1,135 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: selfcheck + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-22.04 + + env: + QT_VERSION: 6.7.0 + + steps: + - uses: actions/checkout@v3 + + - name: Install missing software + run: | + sudo apt-get update + sudo apt-get install libboost-container-dev + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ runner.os }} + + - name: Install missing software + run: | + sudo apt-get update + sudo apt-get install clang-14 valgrind + + - name: Install Qt ${{ env.QT_VERSION }} + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + modules: 'qtcharts' + cache: true + + # TODO: cache this - perform same build as for the other self check + - name: Self check (build) + run: | + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + # valgrind cannot handle DWARF 5 yet so force version 4 + # work around performance regression with -inline-deferral + make -j$(nproc) -s CXXFLAGS="-O2 -w -DHAVE_BOOST -gdwarf-4 -mllvm -inline-deferral" MATCHCOMPILER=yes + env: + CC: clang-14 + CXX: clang++-14 + + - name: CMake + run: | + cmake -S . -B cmake.output -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=ON -DUSE_QT6=On -DWITH_QCHART=ON -DENABLE_CHECK_INTERNAL=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On + + - name: Generate dependencies + run: | + # make sure the precompiled headers exist + make -C cmake.output lib/CMakeFiles/cppcheck-core.dir/cmake_pch.hxx.cxx + make -C cmake.output test/CMakeFiles/testrunner.dir/cmake_pch.hxx.cxx + # make sure auto-generated GUI files exist + make -C cmake.output autogen + make -C cmake.output gui-build-deps + + # TODO: find a way to report unmatched suppressions without need to add information checks + - name: Self check (unusedFunction) + if: false # TODO: fails with preprocessorErrorDirective - see #10667 + run: | + ./cppcheck -q --template=selfcheck --error-exitcode=1 --library=cppcheck-lib --library=qt -D__CPPCHECK__ -D__GNUC__ -DQT_VERSION=0x060000 -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB -DQT_MOC_HAS_STRINGDATA --enable=unusedFunction --exception-handling -rp=. --project=cmake.output/compile_commands.json --suppressions-list=.selfcheck_unused_suppressions --inline-suppr + env: + DISABLE_VALUEFLOW: 1 + UNUSEDFUNCTION_ONLY: 1 + + # the following steps are duplicated from above since setting up the build node in a parallel step takes longer than the actual steps + - name: CMake (no test) + run: | + cmake -S . -B cmake.output.notest -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=Off -DBUILD_GUI=ON -DUSE_QT6=On -DWITH_QCHART=ON -DENABLE_CHECK_INTERNAL=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On + + - name: Generate dependencies (no test) + run: | + # make sure the precompiled headers exist + make -C cmake.output.notest lib/CMakeFiles/cppcheck-core.dir/cmake_pch.hxx.cxx + # make sure auto-generated GUI files exist + make -C cmake.output.notest autogen + make -C cmake.output.notest gui-build-deps + + # TODO: find a way to report unmatched suppressions without need to add information checks + - name: Self check (unusedFunction / no test) + run: | + ./cppcheck -q --template=selfcheck --error-exitcode=1 --library=cppcheck-lib --library=qt -D__CPPCHECK__ -D__GNUC__ -DQT_VERSION=0x060000 -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB -DQT_MOC_HAS_STRINGDATA --enable=unusedFunction --exception-handling -rp=. --project=cmake.output.notest/compile_commands.json --suppressions-list=.selfcheck_unused_suppressions --inline-suppr + env: + DISABLE_VALUEFLOW: 1 + UNUSEDFUNCTION_ONLY: 1 + + - name: Fetch corpus + run: | + wget https://github.com/danmar/cppcheck/archive/refs/tags/2.8.tar.gz + tar xvf 2.8.tar.gz + + - name: CMake (corpus / no test) + run: | + cmake -S cppcheck-2.8 -B cmake.output.corpus -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=Off -DBUILD_GUI=ON -DUSE_QT6=On -DWITH_QCHART=ON -DENABLE_CHECK_INTERNAL=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On + + - name: Generate dependencies (corpus) + run: | + # make sure the precompiled headers exist + make -C cmake.output.notest lib/CMakeFiles/cppcheck-core.dir/cmake_pch.hxx.cxx + # make sure auto-generated GUI files exist + make -C cmake.output.corpus autogen + make -C cmake.output.corpus gui-build-deps + + # TODO: find a way to report unmatched suppressions without need to add information checks + - name: Self check (unusedFunction / corpus / no test / callgrind) + run: | + # TODO: fix -rp so the suppressions actually work + valgrind --tool=callgrind ./cppcheck --template=selfcheck --error-exitcode=0 --library=cppcheck-lib --library=qt -D__GNUC__ -DQT_VERSION=0x060000 -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB -DQT_MOC_HAS_STRINGDATA --enable=unusedFunction --exception-handling -rp=. --project=cmake.output.corpus/compile_commands.json --suppressions-list=.selfcheck_unused_suppressions --inline-suppr 2>callgrind.log || (cat callgrind.log && false) + cat callgrind.log + callgrind_annotate --auto=no > callgrind.annotated.log + head -50 callgrind.annotated.log + env: + DISABLE_VALUEFLOW: 1 + + - uses: actions/upload-artifact@v3 + with: + name: Callgrind Output + path: ./callgrind.* diff --git a/cppcheck-2.14.0/.github/workflows/tsan.yml b/cppcheck-2.14.0/.github/workflows/tsan.yml new file mode 100644 index 00000000..28f508fd --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/tsan.yml @@ -0,0 +1,142 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: thread sanitizer + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-22.04 + + env: + QT_VERSION: 5.15.2 + TSAN_OPTIONS: halt_on_error=1 + # TODO: figure out why there are cache misses with PCH enabled + CCACHE_SLOPPINESS: pch_defines,time_macros + + steps: + - uses: actions/checkout@v3 + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} + + - name: Set up Python 3.12 + uses: actions/setup-python@v4 + with: + python-version: '3.12' + check-latest: true + + - name: Install missing software on ubuntu + run: | + sudo apt-get update + sudo apt-get install -y cmake make libpcre3-dev libboost-container-dev libxml2-utils + + - name: Install clang + run: | + sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14 + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 18 + + - name: Install Qt ${{ env.QT_VERSION }} + if: false + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + modules: 'qtcharts' + cache: true + + - name: Install missing Python packages + run: | + python3 -m pip install pip --upgrade + python3 -m pip install pytest + python3 -m pip install pytest-timeout + + - name: CMake + run: | + cmake -S . -B cmake.output -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=Off -DWITH_QCHART=Off -DUSE_MATCHCOMPILER=Verify -DANALYZE_THREAD=On -DENABLE_CHECK_INTERNAL=On -DUSE_BOOST=On -DCPPCHK_GLIBCXX_DEBUG=Off -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=Off -DDISABLE_DMAKE=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + env: + CC: clang-18 + CXX: clang++-18 + + - name: Build cppcheck + run: | + cmake --build cmake.output --target cppcheck -- -j $(nproc) + + - name: Build test + run: | + cmake --build cmake.output --target testrunner -- -j $(nproc) + + - name: Run tests + run: ./cmake.output/bin/testrunner + + - name: Run cfg tests + run: | + cmake --build cmake.output --target checkcfg -- -j $(nproc) + + # TODO: we should use CTest instead to parallelize tests but the start-up overhead will slow things down + - name: Run CTest + if: false + run: | + ctest --test-dir cmake.output --output-on-failure -j$(nproc) + + - name: Run test/cli + run: | + pwd=$(pwd) + cd test/cli + TEST_CPPCHECK_EXE_LOOKUP_PATH="$pwd/cmake.output" python3 -m pytest -Werror --strict-markers -vv + env: + TEST_CPPCHECK_INJECT_EXECUTOR: thread + + - name: Run test/cli (-j2) + run: | + pwd=$(pwd) + cd test/cli + TEST_CPPCHECK_EXE_LOOKUP_PATH="$pwd/cmake.output" python3 -m pytest -Werror --strict-markers -vv + env: + TEST_CPPCHECK_INJECT_J: 2 + + - name: Run test/cli (--clang) + if: false + run: | + pwd=$(pwd) + cd test/cli + TEST_CPPCHECK_EXE_LOOKUP_PATH="$pwd/cmake.output" python3 -m pytest -Werror --strict-markers -vv + env: + TEST_CPPCHECK_INJECT_CLANG: clang + + - name: Generate dependencies + if: false + run: | + # make sure auto-generated GUI files exist + make -C cmake.output autogen + make -C cmake.output gui-build-deps triage-build-ui-deps + + # TODO: disabled for now as it takes around 40 minutes to finish + # set --error-exitcode=0 so we only fail on sanitizer issues - since it uses threads for execution it will exit the whole process on the first issue + - name: Self check + if: false + run: | + selfcheck_options="-q -j$(nproc) --std=c++11 --template=selfcheck --showtime=top5_summary -D__GNUC__ --error-exitcode=0 --inline-suppr --suppressions-list=.selfcheck_suppressions --library=gnu --inconclusive --enable=style,performance,portability,warning,missingInclude,internal --exception-handling --debug-warnings --check-level=exhaustive" + selfcheck_options="$selfcheck_options --executor=thread" + cppcheck_options="-D__CPPCHECK__ -DCHECK_INTERNAL -DHAVE_RULES --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2" + ec=0 + ./cmake.output/bin/cppcheck $selfcheck_options externals/simplecpp || ec=1 + ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options --addon=naming.json -DCHECK_INTERNAL cli lib || ec=1 + ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -DQT_VERSION=0x060000 -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB -DQT_MOC_HAS_STRINGDATA --library=qt --addon=naming.json -Icmake.output/gui -Igui gui/*.cpp cmake.output/gui/*.cpp || ec=1 + ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -Icli test/*.cpp tools/*.cpp || ec=1 + ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB -DQT_MOC_HAS_STRINGDATA --library=qt -Icmake.output/tools/triage -Igui tools/triage/*.cpp cmake.output/tools/triage/*.cpp || ec=1 + exit $ec diff --git a/cppcheck-2.14.0/.github/workflows/ubsan.yml b/cppcheck-2.14.0/.github/workflows/ubsan.yml new file mode 100644 index 00000000..31701b1b --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/ubsan.yml @@ -0,0 +1,136 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: undefined behaviour sanitizers + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-22.04 + + env: + QT_VERSION: 5.15.2 + UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1:report_error_type=1 + # TODO: figure out why there are cache misses with PCH enabled + CCACHE_SLOPPINESS: pch_defines,time_macros + + steps: + - uses: actions/checkout@v3 + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} + + - name: Set up Python 3.12 + uses: actions/setup-python@v4 + with: + python-version: '3.12' + check-latest: true + + - name: Install missing software on ubuntu + run: | + sudo apt-get update + sudo apt-get install -y cmake make libpcre3-dev libboost-container-dev libxml2-utils + + - name: Install clang + run: | + sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14 + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 18 + + - name: Install Qt ${{ env.QT_VERSION }} + uses: jurplel/install-qt-action@v3 + with: + version: ${{ env.QT_VERSION }} + modules: 'qtcharts' + cache: true + + - name: Install missing Python packages + run: | + python3 -m pip install pip --upgrade + python3 -m pip install pytest + python3 -m pip install pytest-timeout + + # TODO: disable warnings + - name: CMake + run: | + cmake -S . -B cmake.output -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=ON -DWITH_QCHART=ON -DUSE_MATCHCOMPILER=Verify -DANALYZE_UNDEFINED=On -DENABLE_CHECK_INTERNAL=On -DUSE_BOOST=On -DCPPCHK_GLIBCXX_DEBUG=Off -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On -DDISABLE_DMAKE=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache + env: + CC: clang-18 + CXX: clang++-18 + + - name: Build cppcheck + run: | + cmake --build cmake.output --target cppcheck -- -j $(nproc) + + - name: Build test + run: | + cmake --build cmake.output --target testrunner -- -j $(nproc) + + - name: Run tests + run: ./cmake.output/bin/testrunner + + - name: Run cfg tests + run: | + cmake --build cmake.output --target checkcfg -- -j $(nproc) + + # TODO: we should use CTest instead to parallelize tests but the start-up overhead will slow things down + - name: Run CTest + if: false + run: | + ctest --test-dir cmake.output --output-on-failure -j$(nproc) + + - name: Run test/cli + run: | + pwd=$(pwd) + cd test/cli + TEST_CPPCHECK_EXE_LOOKUP_PATH="$pwd/cmake.output" python3 -m pytest -Werror --strict-markers -vv + + - name: Run test/cli (-j2) + run: | + pwd=$(pwd) + cd test/cli + TEST_CPPCHECK_EXE_LOOKUP_PATH="$pwd/cmake.output" python3 -m pytest -Werror --strict-markers -vv + env: + TEST_CPPCHECK_INJECT_J: 2 + + - name: Run test/cli (--clang) + if: false + run: | + pwd=$(pwd) + cd test/cli + TEST_CPPCHECK_EXE_LOOKUP_PATH="$pwd/cmake.output" python3 -m pytest -Werror --strict-markers -vv + env: + TEST_CPPCHECK_INJECT_CLANG: clang + + - name: Generate dependencies + run: | + # make sure auto-generated GUI files exist + make -C cmake.output autogen + make -C cmake.output gui-build-deps triage-build-ui-deps + + # TODO: only fail the step on sanitizer issues - since we use processes it will only fail the underlying process which will result in an cppcheckError + - name: Self check + run: | + selfcheck_options="-q -j$(nproc) --std=c++11 --template=selfcheck --showtime=top5_summary -D__GNUC__ --error-exitcode=1 --inline-suppr --suppressions-list=.selfcheck_suppressions --library=gnu --inconclusive --enable=style,performance,portability,warning,missingInclude,internal --exception-handling --debug-warnings --check-level=exhaustive" + cppcheck_options="-D__CPPCHECK__ -DCHECK_INTERNAL -DHAVE_RULES --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2" + ec=0 + ./cmake.output/bin/cppcheck $selfcheck_options externals/simplecpp || ec=1 + ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options --addon=naming.json cli lib || ec=1 + ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -DQT_VERSION=0x060000 -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB -DQT_MOC_HAS_STRINGDATA --library=qt --addon=naming.json -Icmake.output/gui -Igui gui/*.cpp cmake.output/gui/*.cpp || ec=1 + ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -Icli test/*.cpp tools/*.cpp || ec=1 + ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB -DQT_MOC_HAS_STRINGDATA --library=qt -Icmake.output/tools/triage -Igui tools/triage/*.cpp cmake.output/tools/triage/*.cpp || ec=1 + exit $ec diff --git a/cppcheck-2.14.0/.github/workflows/valgrind.yml b/cppcheck-2.14.0/.github/workflows/valgrind.yml new file mode 100644 index 00000000..7e85e41c --- /dev/null +++ b/cppcheck-2.14.0/.github/workflows/valgrind.yml @@ -0,0 +1,62 @@ +# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions +# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners +name: valgrind + +on: + push: + branches: + - 'main' + - 'releases/**' + tags: + - '2.*' + pull_request: + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-22.04 + + steps: + - uses: actions/checkout@v3 + + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2.11 + with: + key: ${{ github.workflow }}-${{ runner.os }} + + - name: Install missing software + run: | + sudo apt-get update + sudo apt-get install libxml2-utils + sudo apt-get install valgrind + sudo apt-get install libboost-container-dev + sudo apt-get install debuginfod + + - name: Build cppcheck + run: | + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + CXXFLAGS="-O1 -g -w -DHAVE_BOOST" make -j$(nproc) HAVE_RULES=yes MATCHCOMPILER=yes + + - name: Build test + run: | + export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" + CXXFLAGS="-O1 -g -w -DHAVE_BOOST" make -j$(nproc) testrunner HAVE_RULES=yes MATCHCOMPILER=yes + + - name: Run valgrind + run: | + ec=0 + valgrind --error-limit=yes --leak-check=full --num-callers=50 --show-reachable=yes --track-origins=yes --suppressions=valgrind/testrunner.supp --gen-suppressions=all --log-fd=9 --error-exitcode=42 ./testrunner TestGarbage TestOther TestSimplifyTemplate 9>memcheck.log || ec=1 + cat memcheck.log + exit $ec + # TODO: debuginfod.ubuntu.com is currently not responding to any requests causing it to run into a 40(!) minute timeout + #env: + # DEBUGINFOD_URLS: https://debuginfod.ubuntu.com + + - uses: actions/upload-artifact@v3 + if: success() || failure() + with: + name: Logs + path: ./*.log diff --git a/cppcheck-2.14.0/.gitignore b/cppcheck-2.14.0/.gitignore new file mode 100644 index 00000000..8afdc961 --- /dev/null +++ b/cppcheck-2.14.0/.gitignore @@ -0,0 +1,136 @@ +*.bak +*.gcno +*.o +*.pyc +/cppcheck +/cppcheck.exe +cppcheck-core.dll +/dmake +/dmake.exe +reduce +reduce.exe +tags +/testrunner +/testrunner.exe +tools/daca2*.html +tools/errmsg +tools/extracttests + +# dump files generated by Cppcheck +*.*.dump + +# CTU info files generated by Cppcheck +*.*.ctu-info + +# VS generated files +*.aps +*.idb +*.ncb +*.obj +*.opensdf +*.orig +*.pdb +*.sdf +*.suo +*.user +/.vs/ +UpgradeLog*.htm + +# 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/ +/triage/temp + +# Other (generated) GUI files +/gui/*.qm +/gui/cppcheck-gui +/gui/cppcheck-gui.exe +/gui/gui.sln +/gui/gui.vcproj +/gui/help/online-help.qch +/gui/help/online-help.qhc +/gui/Makefile +/gui/Makefile.debug +/gui/Makefile.release +/gui/qrc_gui.cpp +/gui/test/Makefile +/gui/test/*/Makefile +/gui/test/*/*/Makefile +/gui/test/benchmark/simple/benchmark-simple +/gui/test/cppchecklibrarydata/qrc_resources.cpp +/gui/test/cppchecklibrarydata/test-cppchecklibrarydata +/gui/test/filelist/test-filelist +/gui/test/projectfile/test-projectfile +/gui/test/translationhandler/test-translationhandler +/gui/test/xmlreportv2/test-xmlreportv2 + +# Doxygen output folder +doxyoutput/ + +# qmake generated +htmlreport/.tox/ +htmlreport/MANIFEST + +# Backup files and stuff from patches +*.rej +*~ + +# kdevelop 4.x +*.kdev4 + +# Common cmake build directories +build**/ + +# Temporal files +*.swp + +# Snapcraft build +part +prime +parts +stage +*.snap +/snap/.snapcraft + +# Manual folder +/man/manual.log +/man/manual.tex +/man/*.pdf +/man/*.html + +# CLion +/.idea +/.metadata/ +/cmake-build-* +/.run + +# clang tooling temporary files +.clangd/ +.cache/ +compile_commands.json + +# qmake +.qmake.stash + +#vs code +/.vscode + +# fuzzing output +/oss-fuzz/corpus +/oss-fuzz/corpus_ +/oss-fuzz/samples diff --git a/cppcheck-2.14.0/.mailmap b/cppcheck-2.14.0/.mailmap new file mode 100644 index 00000000..6f42f4e4 --- /dev/null +++ b/cppcheck-2.14.0/.mailmap @@ -0,0 +1,49 @@ +Andreas 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 + + diff --git a/cppcheck-2.14.0/.selfcheck_suppressions b/cppcheck-2.14.0/.selfcheck_suppressions new file mode 100644 index 00000000..03e54874 --- /dev/null +++ b/cppcheck-2.14.0/.selfcheck_suppressions @@ -0,0 +1,28 @@ +missingIncludeSystem + +# temporary suppressions - fix the warnings! +simplifyUsing:lib/valueptr.h +varid0:gui/projectfile.cpp +naming-privateMemberVariable:gui/test/cppchecklibrarydata/testcppchecklibrarydata.h +symbolDatabaseWarning:*/moc_*.cpp +simplifyUsing:*/moc_*.cpp + +# warnings in Qt generated code we cannot fix +funcArgNamesDifferent:*/moc_*.cpp +naming-varname:*/ui_*.h +functionStatic:*/ui_fileview.h + +# --debug-warnings suppressions +valueFlowBailout +valueFlowBailoutIncompleteVar +autoNoType + +naming-varname:externals/simplecpp/simplecpp.h +naming-privateMemberVariable:externals/simplecpp/simplecpp.h + +# these warnings need to be addressed upstream +uninitMemberVar:externals/tinyxml2/tinyxml2.h +noExplicitConstructor:externals/tinyxml2/tinyxml2.h +missingOverride:externals/tinyxml2/tinyxml2.h +invalidPrintfArgType_sint:externals/tinyxml2/tinyxml2.h +naming-privateMemberVariable:externals/tinyxml2/tinyxml2.h \ No newline at end of file diff --git a/cppcheck-2.14.0/.selfcheck_unused_suppressions b/cppcheck-2.14.0/.selfcheck_unused_suppressions new file mode 100644 index 00000000..68f296fe --- /dev/null +++ b/cppcheck-2.14.0/.selfcheck_unused_suppressions @@ -0,0 +1,15 @@ +# we are not using all methods of their interfaces +unusedFunction:externals/*/* + +# TODO: fix these +# false positive - # 10660 +unusedFunction:gui/mainwindow.cpp +unusedFunction:gui/resultstree.cpp +unusedFunction:gui/codeeditor.* +# usage is disabled +unusedFunction:lib/symboldatabase.cpp +# false positive - #10661 +unusedFunction:oss-fuzz/main.cpp + +# Q_OBJECT functions which are not called in our code +unusedFunction:cmake.output.notest/gui/cppcheck-gui_autogen/*/moc_aboutdialog.cpp \ No newline at end of file diff --git a/cppcheck-2.14.0/.travis.yml b/cppcheck-2.14.0/.travis.yml new file mode 100644 index 00000000..46b471c0 --- /dev/null +++ b/cppcheck-2.14.0/.travis.yml @@ -0,0 +1,43 @@ +language: cpp +dist: xenial + +compiler: + - gcc + - clang + +env: + global: + - ORIGINAL_CXXFLAGS="-pedantic -Wall -Wextra -Wcast-qual -Wno-deprecated-declarations -Wfloat-equal -Wmissing-declarations -Wmissing-format-attribute -Wno-long-long -Wpacked -Wredundant-decls -Wundef -Wno-shadow -Wno-missing-field-initializers -Wno-missing-braces -Wno-sign-compare -Wno-multichar -D_GLIBCXX_DEBUG -g" +# unfortunately we need this to stay within 50min timelimit given by travis. + - CXXFLAGS="${ORIGINAL_CXXFLAGS} -O2 -march=native -Wstrict-aliasing=2 -Werror=strict-aliasing" + - CPPCHECK=${TRAVIS_BUILD_DIR}/cppcheck + matrix: + - CXXFLAGS="${CXXFLAGS} -DCHECK_INTERNAL" + - CXXFLAGS="${CXXFLAGS} -DCHECK_INTERNAL" MAKEFLAGS="HAVE_RULES=yes" MATCHCOMPILER=yes VERIFY=1 + +before_install: +# install needed deps + - travis_retry sudo apt-get update -qq + - travis_retry sudo apt-get install -qq libxml2-utils libpcre3 gdb unzip wx-common xmlstarlet liblua5.3-dev libcurl3 libcairo2-dev libsigc++-2.0-dev tidy libopencv-dev + +matrix: +# do notify immediately about it when a job of a build fails. + fast_finish: true +# defined extra jobs that run besides what is configured in the build matrix + include: + +# check a lot of stuff that only needs to be checked in a single configuration + - name: "misc" + compiler: clang + script: + - make -j$(nproc) -s +# check if DESTDIR works TODO: actually execute this + - mkdir install_test + - echo $CXXFLAGS + - make -s DESTDIR=install_test FILESDIR=/usr/share/cppcheck install +# rm everything + - git clean -dfx +# check what happens if we want to install it to some other dir, + - echo $CXXFLAGS + - make -s MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck -j$(nproc) + - sudo make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck install diff --git a/cppcheck-2.14.0/.uncrustify.cfg b/cppcheck-2.14.0/.uncrustify.cfg new file mode 100644 index 00000000..074811b0 --- /dev/null +++ b/cppcheck-2.14.0/.uncrustify.cfg @@ -0,0 +1,3128 @@ +# Uncrustify-0.72.0_f + +# +# General options +# + +# The type of line endings. +# +# Default: auto +newlines = auto # lf/crlf/cr/auto + +# The original size of tabs in the input. +# +# Default: 8 +input_tab_size = 4 # unsigned number + +# The size of tabs in the output (only used if align_with_tabs=true). +# +# Default: 8 +output_tab_size = 4 # unsigned number + +# The ASCII value of the string escape char, usually 92 (\) or (Pawn) 94 (^). +# +# Default: 92 +string_escape_char = 92 # unsigned number + +# Alternate string escape char (usually only used for Pawn). +# Only works right before the quote char. +string_escape_char2 = 0 # unsigned number + +# Replace tab characters found in string literals with the escape sequence \t +# instead. +string_replace_tab_chars = false # true/false + +# Allow interpreting '>=' and '>>=' as part of a template in code like +# 'void f(list>=val);'. If true, 'assert(x<0 && y>=3)' will be broken. +# Improvements to template detection may make this option obsolete. +tok_split_gte = false # true/false + +# Disable formatting of NL_CONT ('\\n') ended lines (e.g. multiline macros) +disable_processing_nl_cont = false # true/false + +# Specify the marker used in comments to disable processing of part of the +# file. +# The comment should be used alone in one line. +# +# Default: *INDENT-OFF* +disable_processing_cmt = " *INDENT-OFF*" # string + +# Specify the marker used in comments to (re)enable processing in a file. +# The comment should be used alone in one line. +# +# Default: *INDENT-ON* +enable_processing_cmt = " *INDENT-ON*" # string + +# Enable parsing of digraphs. +enable_digraphs = false # true/false + +# Add or remove the UTF-8 BOM (recommend 'remove'). +utf8_bom = ignore # ignore/add/remove/force + +# If the file contains bytes with values between 128 and 255, but is not +# UTF-8, then output as UTF-8. +utf8_byte = false # true/false + +# Force the output encoding to UTF-8. +utf8_force = false # true/false + +# Add or remove space between 'do' and '{'. +sp_do_brace_open = ignore # ignore/add/remove/force + +# Add or remove space between '}' and 'while'. +sp_brace_close_while = ignore # ignore/add/remove/force + +# Add or remove space between 'while' and '('. +sp_while_paren_open = add # ignore/add/remove/force + +# +# Spacing options +# + +# Add or remove space around non-assignment symbolic operators ('+', '/', '%', +# '<<', and so forth). +sp_arith = ignore # ignore/add/remove/force + +# Add or remove space around arithmetic operators '+' and '-'. +# +# Overrides sp_arith. +sp_arith_additive = ignore # ignore/add/remove/force + +# Add or remove space around assignment operator '=', '+=', etc. +sp_assign = ignore # ignore/add/remove/force + +# Add or remove space around '=' in C++11 lambda capture specifications. +# +# Overrides sp_assign. +sp_cpp_lambda_assign = ignore # ignore/add/remove/force + +# Add or remove space after the capture specification of a C++11 lambda when +# an argument list is present, as in '[] (int x){ ... }'. +sp_cpp_lambda_square_paren = ignore # ignore/add/remove/force + +# Add or remove space after the capture specification of a C++11 lambda with +# no argument list is present, as in '[] { ... }'. +sp_cpp_lambda_square_brace = ignore # ignore/add/remove/force + +# Add or remove space after the argument list of a C++11 lambda, as in +# '[](int x) { ... }'. +sp_cpp_lambda_paren_brace = ignore # ignore/add/remove/force + +# Add or remove space between a lambda body and its call operator of an +# immediately invoked lambda, as in '[]( ... ){ ... } ( ... )'. +sp_cpp_lambda_fparen = ignore # ignore/add/remove/force + +# Add or remove space around assignment operator '=' in a prototype. +# +# If set to ignore, use sp_assign. +sp_assign_default = ignore # ignore/add/remove/force + +# Add or remove space before assignment operator '=', '+=', etc. +# +# Overrides sp_assign. +sp_before_assign = ignore # ignore/add/remove/force + +# Add or remove space after assignment operator '=', '+=', etc. +# +# Overrides sp_assign. +sp_after_assign = ignore # ignore/add/remove/force + +# Add or remove space in 'NS_ENUM ('. +sp_enum_paren = ignore # ignore/add/remove/force + +# Add or remove space around assignment '=' in enum. +sp_enum_assign = ignore # ignore/add/remove/force + +# Add or remove space before assignment '=' in enum. +# +# Overrides sp_enum_assign. +sp_enum_before_assign = ignore # ignore/add/remove/force + +# Add or remove space after assignment '=' in enum. +# +# Overrides sp_enum_assign. +sp_enum_after_assign = ignore # ignore/add/remove/force + +# Add or remove space around assignment ':' in enum. +sp_enum_colon = ignore # ignore/add/remove/force + +# Add or remove space around preprocessor '##' concatenation operator. +# +# Default: add +sp_pp_concat = add # ignore/add/remove/force + +# Add or remove space after preprocessor '#' stringify operator. +# Also affects the '#@' charizing operator. +sp_pp_stringify = ignore # ignore/add/remove/force + +# Add or remove space before preprocessor '#' stringify operator +# as in '#define x(y) L#y'. +sp_before_pp_stringify = ignore # ignore/add/remove/force + +# Add or remove space around boolean operators '&&' and '||'. +sp_bool = force # ignore/add/remove/force + +# Add or remove space around compare operator '<', '>', '==', etc. +sp_compare = ignore # ignore/add/remove/force + +# Add or remove space inside '(' and ')'. +sp_inside_paren = remove # ignore/add/remove/force + +# Add or remove space between nested parentheses, i.e. '((' vs. ') )'. +sp_paren_paren = remove # ignore/add/remove/force + +# Add or remove space between back-to-back parentheses, i.e. ')(' vs. ') ('. +sp_cparen_oparen = ignore # ignore/add/remove/force + +# Whether to balance spaces inside nested parentheses. +sp_balance_nested_parens = false # true/false + +# Add or remove space between ')' and '{'. +sp_paren_brace = force # ignore/add/remove/force + +# Add or remove space between nested braces, i.e. '{{' vs '{ {'. +sp_brace_brace = ignore # ignore/add/remove/force + +# Add or remove space before pointer star '*'. +sp_before_ptr_star = ignore # ignore/add/remove/force + +# Add or remove space before pointer star '*' that isn't followed by a +# variable name. If set to ignore, sp_before_ptr_star is used instead. +sp_before_unnamed_ptr_star = ignore # ignore/add/remove/force + +# Add or remove space between pointer stars '*'. +sp_between_ptr_star = remove # ignore/add/remove/force + +# Add or remove space after pointer star '*', if followed by a word. +# +# Overrides sp_type_func. +sp_after_ptr_star = ignore # ignore/add/remove/force + +# Add or remove space after pointer caret '^', if followed by a word. +sp_after_ptr_block_caret = ignore # ignore/add/remove/force + +# Add or remove space after pointer star '*', if followed by a qualifier. +sp_after_ptr_star_qualifier = ignore # ignore/add/remove/force + +# Add or remove space after a pointer star '*', if followed by a function +# prototype or function definition. +# +# Overrides sp_after_ptr_star and sp_type_func. +sp_after_ptr_star_func = ignore # ignore/add/remove/force + +# Add or remove space after a pointer star '*', if followed by an open +# parenthesis, as in 'void* (*)(). +sp_ptr_star_paren = ignore # ignore/add/remove/force + +# Add or remove space before a pointer star '*', if followed by a function +# prototype or function definition. +sp_before_ptr_star_func = ignore # ignore/add/remove/force + +# Add or remove space before a reference sign '&'. +sp_before_byref = ignore # ignore/add/remove/force + +# Add or remove space before a reference sign '&' that isn't followed by a +# variable name. If set to ignore, sp_before_byref is used instead. +sp_before_unnamed_byref = ignore # ignore/add/remove/force + +# Add or remove space after reference sign '&', if followed by a word. +# +# Overrides sp_type_func. +sp_after_byref = ignore # ignore/add/remove/force + +# Add or remove space after a reference sign '&', if followed by a function +# prototype or function definition. +# +# Overrides sp_after_byref and sp_type_func. +sp_after_byref_func = ignore # ignore/add/remove/force + +# Add or remove space before a reference sign '&', if followed by a function +# prototype or function definition. +sp_before_byref_func = ignore # ignore/add/remove/force + +# Add or remove space between type and word. In cases where total removal of +# whitespace would be a syntax error, a value of 'remove' is treated the same +# as 'force'. +# +# This also affects some other instances of space following a type that are +# not covered by other options; for example, between the return type and +# parenthesis of a function type template argument, between the type and +# parenthesis of an array parameter, or between 'decltype(...)' and the +# following word. +# +# Default: force +sp_after_type = force # ignore/add/remove/force + +# Add or remove space between 'decltype(...)' and word. +# +# Overrides sp_after_type. +sp_after_decltype = ignore # ignore/add/remove/force + +# (D) Add or remove space before the parenthesis in the D constructs +# 'template Foo(' and 'class Foo('. +sp_before_template_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'template' and '<'. +# If set to ignore, sp_before_angle is used. +sp_template_angle = ignore # ignore/add/remove/force + +# Add or remove space before '<'. +sp_before_angle = remove # ignore/add/remove/force + +# Add or remove space inside '<' and '>'. +sp_inside_angle = remove # ignore/add/remove/force + +# Add or remove space inside '<>'. +sp_inside_angle_empty = ignore # ignore/add/remove/force + +# Add or remove space between '>' and ':'. +sp_angle_colon = ignore # ignore/add/remove/force + +# Add or remove space after '>'. +sp_after_angle = add # ignore/add/remove/force + +# Add or remove space between '>' and '(' as found in 'new List(foo);'. +sp_angle_paren = remove # ignore/add/remove/force + +# Add or remove space between '>' and '()' as found in 'new List();'. +sp_angle_paren_empty = ignore # ignore/add/remove/force + +# Add or remove space between '>' and a word as in 'List m;' or +# 'template static ...'. +sp_angle_word = add # ignore/add/remove/force + +# Add or remove space between '>' and '>' in '>>' (template stuff). +# +# Default: add +sp_angle_shift = ignore # ignore/add/remove/force + +# (C++11) Permit removal of the space between '>>' in 'foo >'. Note +# that sp_angle_shift cannot remove the space without this option. +sp_permit_cpp11_shift = true # true/false + +# Add or remove space before '(' of control statements ('if', 'for', 'switch', +# 'while', etc.). +sp_before_sparen = force # ignore/add/remove/force + +# Add or remove space inside '(' and ')' of control statements. +sp_inside_sparen = remove # ignore/add/remove/force + +# Add or remove space after '(' of control statements. +# +# Overrides sp_inside_sparen. +sp_inside_sparen_open = ignore # ignore/add/remove/force + +# Add or remove space before ')' of control statements. +# +# Overrides sp_inside_sparen. +sp_inside_sparen_close = ignore # ignore/add/remove/force + +# Add or remove space after ')' of control statements. +sp_after_sparen = force # ignore/add/remove/force + +# Add or remove space between ')' and '{' of of control statements. +sp_sparen_brace = force # ignore/add/remove/force + +# (D) Add or remove space between 'invariant' and '('. +sp_invariant_paren = ignore # ignore/add/remove/force + +# (D) Add or remove space after the ')' in 'invariant (C) c'. +sp_after_invariant_paren = ignore # ignore/add/remove/force + +# Add or remove space before empty statement ';' on 'if', 'for' and 'while'. +sp_special_semi = ignore # ignore/add/remove/force + +# Add or remove space before ';'. +# +# Default: remove +sp_before_semi = remove # ignore/add/remove/force + +# Add or remove space before ';' in non-empty 'for' statements. +sp_before_semi_for = remove # ignore/add/remove/force + +# Add or remove space before a semicolon of an empty part of a for statement. +sp_before_semi_for_empty = ignore # ignore/add/remove/force + +# Add or remove space after ';', except when followed by a comment. +# +# Default: add +sp_after_semi = add # ignore/add/remove/force + +# Add or remove space after ';' in non-empty 'for' statements. +# +# Default: force +sp_after_semi_for = force # ignore/add/remove/force + +# Add or remove space after the final semicolon of an empty part of a for +# statement, as in 'for ( ; ; )'. +sp_after_semi_for_empty = remove # ignore/add/remove/force + +# Add or remove space before '[' (except '[]'). +sp_before_square = ignore # ignore/add/remove/force + +# Add or remove space before '[' for a variable definition. +# +# Default: remove +sp_before_vardef_square = remove # ignore/add/remove/force + +# Add or remove space before '[' for asm block. +sp_before_square_asm_block = ignore # ignore/add/remove/force + +# Add or remove space before '[]'. +sp_before_squares = remove # ignore/add/remove/force + +# Add or remove space before C++17 structured bindings. +sp_cpp_before_struct_binding = ignore # ignore/add/remove/force + +# Add or remove space inside a non-empty '[' and ']'. +sp_inside_square = remove # ignore/add/remove/force + +# Add or remove space inside '[]'. +sp_inside_square_empty = ignore # ignore/add/remove/force + +# (OC) Add or remove space inside a non-empty Objective-C boxed array '@[' and +# ']'. If set to ignore, sp_inside_square is used. +sp_inside_square_oc_array = ignore # ignore/add/remove/force + +# Add or remove space after ',', i.e. 'a,b' vs. 'a, b'. +sp_after_comma = ignore # ignore/add/remove/force + +# Add or remove space before ','. +# +# Default: remove +sp_before_comma = remove # ignore/add/remove/force + +# (C#) Add or remove space between ',' and ']' in multidimensional array type +# like 'int[,,]'. +sp_after_mdatype_commas = ignore # ignore/add/remove/force + +# (C#) Add or remove space between '[' and ',' in multidimensional array type +# like 'int[,,]'. +sp_before_mdatype_commas = ignore # ignore/add/remove/force + +# (C#) Add or remove space between ',' in multidimensional array type +# like 'int[,,]'. +sp_between_mdatype_commas = ignore # ignore/add/remove/force + +# Add or remove space between an open parenthesis and comma, +# i.e. '(,' vs. '( ,'. +# +# Default: force +sp_paren_comma = force # ignore/add/remove/force + +# Add or remove space before the variadic '...' when preceded by a +# non-punctuator. +sp_before_ellipsis = ignore # ignore/add/remove/force + +# Add or remove space between a type and '...'. +sp_type_ellipsis = ignore # ignore/add/remove/force + +# (D) Add or remove space between a type and '?'. +sp_type_question = ignore # ignore/add/remove/force + +# Add or remove space between ')' and '...'. +sp_paren_ellipsis = ignore # ignore/add/remove/force + +# Add or remove space between ')' and a qualifier such as 'const'. +sp_paren_qualifier = ignore # ignore/add/remove/force + +# Add or remove space between ')' and 'noexcept'. +sp_paren_noexcept = ignore # ignore/add/remove/force + +# Add or remove space after class ':'. +sp_after_class_colon = force # ignore/add/remove/force + +# Add or remove space before class ':'. +sp_before_class_colon = force # ignore/add/remove/force + +# Add or remove space after class constructor ':'. +sp_after_constr_colon = ignore # ignore/add/remove/force + +# Add or remove space before class constructor ':'. +sp_before_constr_colon = ignore # ignore/add/remove/force + +# Add or remove space before case ':'. +# +# Default: remove +sp_before_case_colon = remove # ignore/add/remove/force + +# Add or remove space between 'operator' and operator sign. +sp_after_operator = ignore # ignore/add/remove/force + +# Add or remove space between the operator symbol and the open parenthesis, as +# in 'operator ++('. +sp_after_operator_sym = ignore # ignore/add/remove/force + +# Overrides sp_after_operator_sym when the operator has no arguments, as in +# 'operator *()'. +sp_after_operator_sym_empty = ignore # ignore/add/remove/force + +# Add or remove space after C/D cast, i.e. 'cast(int)a' vs. 'cast(int) a' or +# '(int)a' vs. '(int) a'. +sp_after_cast = ignore # ignore/add/remove/force + +# Add or remove spaces inside cast parentheses. +sp_inside_paren_cast = ignore # ignore/add/remove/force + +# Add or remove space between the type and open parenthesis in a C++ cast, +# i.e. 'int(exp)' vs. 'int (exp)'. +sp_cpp_cast_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'sizeof' and '('. +sp_sizeof_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'sizeof' and '...'. +sp_sizeof_ellipsis = ignore # ignore/add/remove/force + +# Add or remove space between 'sizeof...' and '('. +sp_sizeof_ellipsis_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'decltype' and '('. +sp_decltype_paren = ignore # ignore/add/remove/force + +# (Pawn) Add or remove space after the tag keyword. +sp_after_tag = ignore # ignore/add/remove/force + +# Add or remove space inside enum '{' and '}'. +sp_inside_braces_enum = ignore # ignore/add/remove/force + +# Add or remove space inside struct/union '{' and '}'. +sp_inside_braces_struct = ignore # ignore/add/remove/force + +# (OC) Add or remove space inside Objective-C boxed dictionary '{' and '}' +sp_inside_braces_oc_dict = ignore # ignore/add/remove/force + +# Add or remove space after open brace in an unnamed temporary +# direct-list-initialization. +sp_after_type_brace_init_lst_open = ignore # ignore/add/remove/force + +# Add or remove space before close brace in an unnamed temporary +# direct-list-initialization. +sp_before_type_brace_init_lst_close = ignore # ignore/add/remove/force + +# Add or remove space inside an unnamed temporary direct-list-initialization. +sp_inside_type_brace_init_lst = ignore # ignore/add/remove/force + +# Add or remove space inside '{' and '}'. +sp_inside_braces = ignore # ignore/add/remove/force + +# Add or remove space inside '{}'. +sp_inside_braces_empty = remove # ignore/add/remove/force + +# Add or remove space around trailing return operator '->'. +sp_trailing_return = ignore # ignore/add/remove/force + +# Add or remove space between return type and function name. A minimum of 1 +# is forced except for pointer return types. +sp_type_func = ignore # ignore/add/remove/force + +# Add or remove space between type and open brace of an unnamed temporary +# direct-list-initialization. +sp_type_brace_init_lst = ignore # ignore/add/remove/force + +# Add or remove space between function name and '(' on function declaration. +sp_func_proto_paren = ignore # ignore/add/remove/force + +# Add or remove space between function name and '()' on function declaration +# without parameters. +sp_func_proto_paren_empty = ignore # ignore/add/remove/force + +# Add or remove space between function name and '(' with a typedef specifier. +sp_func_type_paren = ignore # ignore/add/remove/force + +# Add or remove space between alias name and '(' of a non-pointer function type typedef. +sp_func_def_paren = ignore # ignore/add/remove/force + +# Add or remove space between function name and '()' on function definition +# without parameters. +sp_func_def_paren_empty = ignore # ignore/add/remove/force + +# Add or remove space inside empty function '()'. +# Overrides sp_after_angle unless use_sp_after_angle_always is set to true. +sp_inside_fparens = ignore # ignore/add/remove/force + +# Add or remove space inside function '(' and ')'. +sp_inside_fparen = ignore # ignore/add/remove/force + +# Add or remove space inside the first parentheses in a function type, as in +# 'void (*x)(...)'. +sp_inside_tparen = ignore # ignore/add/remove/force + +# Add or remove space between the ')' and '(' in a function type, as in +# 'void (*x)(...)'. +sp_after_tparen_close = ignore # ignore/add/remove/force + +# Add or remove space between ']' and '(' when part of a function call. +sp_square_fparen = ignore # ignore/add/remove/force + +# Add or remove space between ')' and '{' of function. +sp_fparen_brace = ignore # ignore/add/remove/force + +# Add or remove space between ')' and '{' of a function call in object +# initialization. +# +# Overrides sp_fparen_brace. +sp_fparen_brace_initializer = ignore # ignore/add/remove/force + +# (Java) Add or remove space between ')' and '{{' of double brace initializer. +sp_fparen_dbrace = ignore # ignore/add/remove/force + +# Add or remove space between function name and '(' on function calls. +sp_func_call_paren = ignore # ignore/add/remove/force + +# Add or remove space between function name and '()' on function calls without +# parameters. If set to ignore (the default), sp_func_call_paren is used. +sp_func_call_paren_empty = ignore # ignore/add/remove/force + +# Add or remove space between the user function name and '(' on function +# calls. You need to set a keyword to be a user function in the config file, +# like: +# set func_call_user tr _ i18n +sp_func_call_user_paren = ignore # ignore/add/remove/force + +# Add or remove space inside user function '(' and ')'. +sp_func_call_user_inside_fparen = ignore # ignore/add/remove/force + +# Add or remove space between nested parentheses with user functions, +# i.e. '((' vs. '( ('. +sp_func_call_user_paren_paren = ignore # ignore/add/remove/force + +# Add or remove space between a constructor/destructor and the open +# parenthesis. +sp_func_class_paren = ignore # ignore/add/remove/force + +# Add or remove space between a constructor without parameters or destructor +# and '()'. +sp_func_class_paren_empty = ignore # ignore/add/remove/force + +# Add or remove space between 'return' and '('. +sp_return_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'return' and '{'. +sp_return_brace = ignore # ignore/add/remove/force + +# Add or remove space between '__attribute__' and '('. +sp_attribute_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'defined' and '(' in '#if defined (FOO)'. +sp_defined_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'throw' and '(' in 'throw (something)'. +sp_throw_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'throw' and anything other than '(' as in +# '@throw [...];'. +sp_after_throw = ignore # ignore/add/remove/force + +# Add or remove space between 'catch' and '(' in 'catch (something) { }'. +# If set to ignore, sp_before_sparen is used. +sp_catch_paren = ignore # ignore/add/remove/force + +# (OC) Add or remove space between '@catch' and '(' +# in '@catch (something) { }'. If set to ignore, sp_catch_paren is used. +sp_oc_catch_paren = ignore # ignore/add/remove/force + +# (OC) Add or remove space before Objective-C protocol list +# as in '@protocol Protocol' or '@interface MyClass : NSObject'. +sp_before_oc_proto_list = ignore # ignore/add/remove/force + +# (OC) Add or remove space between class name and '(' +# in '@interface className(categoryName):BaseClass' +sp_oc_classname_paren = ignore # ignore/add/remove/force + +# (D) Add or remove space between 'version' and '(' +# in 'version (something) { }'. If set to ignore, sp_before_sparen is used. +sp_version_paren = ignore # ignore/add/remove/force + +# (D) Add or remove space between 'scope' and '(' +# in 'scope (something) { }'. If set to ignore, sp_before_sparen is used. +sp_scope_paren = ignore # ignore/add/remove/force + +# Add or remove space between 'super' and '(' in 'super (something)'. +# +# Default: remove +sp_super_paren = remove # ignore/add/remove/force + +# Add or remove space between 'this' and '(' in 'this (something)'. +# +# Default: remove +sp_this_paren = remove # ignore/add/remove/force + +# Add or remove space between a macro name and its definition. +sp_macro = ignore # ignore/add/remove/force + +# Add or remove space between a macro function ')' and its definition. +sp_macro_func = ignore # ignore/add/remove/force + +# Add or remove space between 'else' and '{' if on the same line. +sp_else_brace = force # ignore/add/remove/force + +# Add or remove space between '}' and 'else' if on the same line. +sp_brace_else = force # ignore/add/remove/force + +# Add or remove space between '}' and the name of a typedef on the same line. +sp_brace_typedef = ignore # ignore/add/remove/force + +# Add or remove space before the '{' of a 'catch' statement, if the '{' and +# 'catch' are on the same line, as in 'catch (decl) {'. +sp_catch_brace = force # ignore/add/remove/force + +# (OC) Add or remove space before the '{' of a '@catch' statement, if the '{' +# and '@catch' are on the same line, as in '@catch (decl) {'. +# If set to ignore, sp_catch_brace is used. +sp_oc_catch_brace = ignore # ignore/add/remove/force + +# Add or remove space between '}' and 'catch' if on the same line. +sp_brace_catch = force # ignore/add/remove/force + +# (OC) Add or remove space between '}' and '@catch' if on the same line. +# If set to ignore, sp_brace_catch is used. +sp_oc_brace_catch = ignore # ignore/add/remove/force + +# Add or remove space between 'finally' and '{' if on the same line. +sp_finally_brace = ignore # ignore/add/remove/force + +# Add or remove space between '}' and 'finally' if on the same line. +sp_brace_finally = ignore # ignore/add/remove/force + +# Add or remove space between 'try' and '{' if on the same line. +sp_try_brace = ignore # ignore/add/remove/force + +# Add or remove space between get/set and '{' if on the same line. +sp_getset_brace = ignore # ignore/add/remove/force + +# Add or remove space between a variable and '{' for C++ uniform +# initialization. +sp_word_brace_init_lst = ignore # ignore/add/remove/force + +# Add or remove space between a variable and '{' for a namespace. +# +# Default: add +sp_word_brace_ns = add # ignore/add/remove/force + +# Add or remove space before the '::' operator. +sp_before_dc = ignore # ignore/add/remove/force + +# Add or remove space after the '::' operator. +sp_after_dc = ignore # ignore/add/remove/force + +# (D) Add or remove around the D named array initializer ':' operator. +sp_d_array_colon = ignore # ignore/add/remove/force + +# Add or remove space after the '!' (not) unary operator. +# +# Default: remove +sp_not = remove # ignore/add/remove/force + +# Add or remove space after the '~' (invert) unary operator. +# +# Default: remove +sp_inv = remove # ignore/add/remove/force + +# Add or remove space after the '&' (address-of) unary operator. This does not +# affect the spacing after a '&' that is part of a type. +# +# Default: remove +sp_addr = remove # ignore/add/remove/force + +# Add or remove space around the '.' or '->' operators. +# +# Default: remove +sp_member = remove # ignore/add/remove/force + +# Add or remove space after the '*' (dereference) unary operator. This does +# not affect the spacing after a '*' that is part of a type. +# +# Default: remove +sp_deref = remove # ignore/add/remove/force + +# Add or remove space after '+' or '-', as in 'x = -5' or 'y = +7'. +# +# Default: remove +sp_sign = remove # ignore/add/remove/force + +# Add or remove space between '++' and '--' the word to which it is being +# applied, as in '(--x)' or 'y++;'. +# +# Default: remove +sp_incdec = remove # ignore/add/remove/force + +# Add or remove space before a backslash-newline at the end of a line. +# +# Default: add +sp_before_nl_cont = add # ignore/add/remove/force + +# (OC) Add or remove space after the scope '+' or '-', as in '-(void) foo;' +# or '+(int) bar;'. +sp_after_oc_scope = ignore # ignore/add/remove/force + +# (OC) Add or remove space after the colon in message specs, +# i.e. '-(int) f:(int) x;' vs. '-(int) f: (int) x;'. +sp_after_oc_colon = ignore # ignore/add/remove/force + +# (OC) Add or remove space before the colon in message specs, +# i.e. '-(int) f: (int) x;' vs. '-(int) f : (int) x;'. +sp_before_oc_colon = ignore # ignore/add/remove/force + +# (OC) Add or remove space after the colon in immutable dictionary expression +# 'NSDictionary *test = @{@"foo" :@"bar"};'. +sp_after_oc_dict_colon = ignore # ignore/add/remove/force + +# (OC) Add or remove space before the colon in immutable dictionary expression +# 'NSDictionary *test = @{@"foo" :@"bar"};'. +sp_before_oc_dict_colon = ignore # ignore/add/remove/force + +# (OC) Add or remove space after the colon in message specs, +# i.e. '[object setValue:1];' vs. '[object setValue: 1];'. +sp_after_send_oc_colon = ignore # ignore/add/remove/force + +# (OC) Add or remove space before the colon in message specs, +# i.e. '[object setValue:1];' vs. '[object setValue :1];'. +sp_before_send_oc_colon = ignore # ignore/add/remove/force + +# (OC) Add or remove space after the (type) in message specs, +# i.e. '-(int)f: (int) x;' vs. '-(int)f: (int)x;'. +sp_after_oc_type = ignore # ignore/add/remove/force + +# (OC) Add or remove space after the first (type) in message specs, +# i.e. '-(int) f:(int)x;' vs. '-(int)f:(int)x;'. +sp_after_oc_return_type = ignore # ignore/add/remove/force + +# (OC) Add or remove space between '@selector' and '(', +# i.e. '@selector(msgName)' vs. '@selector (msgName)'. +# Also applies to '@protocol()' constructs. +sp_after_oc_at_sel = ignore # ignore/add/remove/force + +# (OC) Add or remove space between '@selector(x)' and the following word, +# i.e. '@selector(foo) a:' vs. '@selector(foo)a:'. +sp_after_oc_at_sel_parens = ignore # ignore/add/remove/force + +# (OC) Add or remove space inside '@selector' parentheses, +# i.e. '@selector(foo)' vs. '@selector( foo )'. +# Also applies to '@protocol()' constructs. +sp_inside_oc_at_sel_parens = ignore # ignore/add/remove/force + +# (OC) Add or remove space before a block pointer caret, +# i.e. '^int (int arg){...}' vs. ' ^int (int arg){...}'. +sp_before_oc_block_caret = ignore # ignore/add/remove/force + +# (OC) Add or remove space after a block pointer caret, +# i.e. '^int (int arg){...}' vs. '^ int (int arg){...}'. +sp_after_oc_block_caret = ignore # ignore/add/remove/force + +# (OC) Add or remove space between the receiver and selector in a message, +# as in '[receiver selector ...]'. +sp_after_oc_msg_receiver = ignore # ignore/add/remove/force + +# (OC) Add or remove space after '@property'. +sp_after_oc_property = ignore # ignore/add/remove/force + +# (OC) Add or remove space between '@synchronized' and the open parenthesis, +# i.e. '@synchronized(foo)' vs. '@synchronized (foo)'. +sp_after_oc_synchronized = ignore # ignore/add/remove/force + +# Add or remove space around the ':' in 'b ? t : f'. +sp_cond_colon = ignore # ignore/add/remove/force + +# Add or remove space before the ':' in 'b ? t : f'. +# +# Overrides sp_cond_colon. +sp_cond_colon_before = ignore # ignore/add/remove/force + +# Add or remove space after the ':' in 'b ? t : f'. +# +# Overrides sp_cond_colon. +sp_cond_colon_after = ignore # ignore/add/remove/force + +# Add or remove space around the '?' in 'b ? t : f'. +sp_cond_question = ignore # ignore/add/remove/force + +# Add or remove space before the '?' in 'b ? t : f'. +# +# Overrides sp_cond_question. +sp_cond_question_before = ignore # ignore/add/remove/force + +# Add or remove space after the '?' in 'b ? t : f'. +# +# Overrides sp_cond_question. +sp_cond_question_after = ignore # ignore/add/remove/force + +# In the abbreviated ternary form '(a ?: b)', add or remove space between '?' +# and ':'. +# +# Overrides all other sp_cond_* options. +sp_cond_ternary_short = ignore # ignore/add/remove/force + +# Fix the spacing between 'case' and the label. Only 'ignore' and 'force' make +# sense here. +sp_case_label = ignore # ignore/add/remove/force + +# (D) Add or remove space around the D '..' operator. +sp_range = ignore # ignore/add/remove/force + +# Add or remove space after ':' in a Java/C++11 range-based 'for', +# as in 'for (Type var : expr)'. +sp_after_for_colon = ignore # ignore/add/remove/force + +# Add or remove space before ':' in a Java/C++11 range-based 'for', +# as in 'for (Type var : expr)'. +sp_before_for_colon = ignore # ignore/add/remove/force + +# (D) Add or remove space between 'extern' and '(' as in 'extern (C)'. +sp_extern_paren = ignore # ignore/add/remove/force + +# Add or remove space after the opening of a C++ comment, +# i.e. '// A' vs. '//A'. +sp_cmt_cpp_start = ignore # ignore/add/remove/force + +# If true, space is added with sp_cmt_cpp_start will be added after doxygen +# sequences like '///', '///<', '//!' and '//!<'. +sp_cmt_cpp_doxygen = false # true/false + +# If true, space is added with sp_cmt_cpp_start will be added after Qt +# translator or meta-data comments like '//:', '//=', and '//~'. +sp_cmt_cpp_qttr = false # true/false + +# Add or remove space between #else or #endif and a trailing comment. +sp_endif_cmt = ignore # ignore/add/remove/force + +# Add or remove space after 'new', 'delete' and 'delete[]'. +sp_after_new = ignore # ignore/add/remove/force + +# Add or remove space between 'new' and '(' in 'new()'. +sp_between_new_paren = ignore # ignore/add/remove/force + +# Add or remove space between ')' and type in 'new(foo) BAR'. +sp_after_newop_paren = ignore # ignore/add/remove/force + +# Add or remove space inside parenthesis of the new operator +# as in 'new(foo) BAR'. +sp_inside_newop_paren = ignore # ignore/add/remove/force + +# Add or remove space after the open parenthesis of the new operator, +# as in 'new(foo) BAR'. +# +# Overrides sp_inside_newop_paren. +sp_inside_newop_paren_open = ignore # ignore/add/remove/force + +# Add or remove space before the close parenthesis of the new operator, +# as in 'new(foo) BAR'. +# +# Overrides sp_inside_newop_paren. +sp_inside_newop_paren_close = ignore # ignore/add/remove/force + +# Add or remove space before a trailing or embedded comment. +sp_before_tr_emb_cmt = ignore # ignore/add/remove/force + +# Number of spaces before a trailing or embedded comment. +sp_num_before_tr_emb_cmt = 0 # unsigned number + +# (Java) Add or remove space between an annotation and the open parenthesis. +sp_annotation_paren = ignore # ignore/add/remove/force + +# If true, vbrace tokens are dropped to the previous token and skipped. +sp_skip_vbrace_tokens = false # true/false + +# Add or remove space after 'noexcept'. +sp_after_noexcept = ignore # ignore/add/remove/force + +# Add or remove space after '_'. +sp_vala_after_translation = ignore # ignore/add/remove/force + +# If true, a is inserted after #define. +force_tab_after_define = false # true/false + +# +# Indenting options +# + +# The number of columns to indent per level. Usually 2, 3, 4, or 8. +# +# Default: 8 +indent_columns = 4 # unsigned number + +# The continuation indent. If non-zero, this overrides the indent of '(', '[' +# and '=' continuation indents. Negative values are OK; negative value is +# absolute and not increased for each '(' or '[' level. +# +# For FreeBSD, this is set to 4. +indent_continue = 0 # number + +# The continuation indent, only for class header line(s). If non-zero, this +# overrides the indent of 'class' continuation indents. +indent_continue_class_head = 0 # unsigned number + +# Whether to indent empty lines (i.e. lines which contain only spaces before +# the newline character). +indent_single_newlines = false # true/false + +# The continuation indent for func_*_param if they are true. If non-zero, this +# overrides the indent. +indent_param = 0 # unsigned number + +# How to use tabs when indenting code. +# +# 0: Spaces only +# 1: Indent with tabs to brace level, align with spaces (default) +# 2: Indent and align with tabs, using spaces when not on a tabstop +# +# Default: 1 +indent_with_tabs = 0 # unsigned number + +# Whether to indent comments that are not at a brace level with tabs on a +# tabstop. Requires indent_with_tabs=2. If false, will use spaces. +indent_cmt_with_tabs = false # true/false + +# Whether to indent strings broken by '\' so that they line up. +indent_align_string = false # true/false + +# The number of spaces to indent multi-line XML strings. +# Requires indent_align_string=true. +indent_xml_string = 0 # unsigned number + +# Spaces to indent '{' from level. +indent_brace = 0 # unsigned number + +# Whether braces are indented to the body level. +indent_braces = false # true/false + +# Whether to disable indenting function braces if indent_braces=true. +indent_braces_no_func = false # true/false + +# Whether to disable indenting class braces if indent_braces=true. +indent_braces_no_class = false # true/false + +# Whether to disable indenting struct braces if indent_braces=true. +indent_braces_no_struct = false # true/false + +# Whether to indent based on the size of the brace parent, +# i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc. +indent_brace_parent = false # true/false + +# Whether to indent based on the open parenthesis instead of the open brace +# in '({\n'. +indent_paren_open_brace = false # true/false + +# (C#) Whether to indent the brace of a C# delegate by another level. +indent_cs_delegate_brace = false # true/false + +# (C#) Whether to indent a C# delegate (to handle delegates with no brace) by +# another level. +indent_cs_delegate_body = false # true/false + +# Whether to indent the body of a 'namespace'. +indent_namespace = true # true/false + +# Whether to indent only the first namespace, and not any nested namespaces. +# Requires indent_namespace=true. +indent_namespace_single_indent = false # true/false + +# The number of spaces to indent a namespace block. +# If set to zero, use the value indent_columns +indent_namespace_level = 0 # unsigned number + +# If the body of the namespace is longer than this number, it won't be +# indented. Requires indent_namespace=true. 0 means no limit. +indent_namespace_limit = 0 # unsigned number + +# Whether the 'extern "C"' body is indented. +indent_extern = false # true/false + +# Whether the 'class' body is indented. +indent_class = true # true/false + +# Whether to indent the stuff after a leading base class colon. +indent_class_colon = false # true/false + +# Whether to indent based on a class colon instead of the stuff after the +# colon. Requires indent_class_colon=true. +indent_class_on_colon = false # true/false + +# Whether to indent the stuff after a leading class initializer colon. +indent_constr_colon = false # true/false + +# Virtual indent from the ':' for member initializers. +# +# Default: 2 +indent_ctor_init_leading = 2 # unsigned number + +# Additional indent for constructor initializer list. +# Negative values decrease indent down to the first column. +indent_ctor_init = 0 # number + +# Whether to indent 'if' following 'else' as a new block under the 'else'. +# If false, 'else\nif' is treated as 'else if' for indenting purposes. +indent_else_if = false # true/false + +# Amount to indent variable declarations after a open brace. +# +# <0: Relative +# >=0: Absolute +indent_var_def_blk = 0 # number + +# Whether to indent continued variable declarations instead of aligning. +indent_var_def_cont = false # true/false + +# Whether to indent continued shift expressions ('<<' and '>>') instead of +# aligning. Set align_left_shift=false when enabling this. +indent_shift = false # true/false + +# Whether to force indentation of function definitions to start in column 1. +indent_func_def_force_col1 = false # true/false + +# Whether to indent continued function call parameters one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_call_param = false # true/false + +# Whether to indent continued function definition parameters one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_def_param = false # true/false + +# for function definitions, only if indent_func_def_param is false +# Allows to align params when appropriate and indent them when not +# behave as if it was true if paren position is more than this value +# if paren position is more than the option value +indent_func_def_param_paren_pos_threshold = 0 # unsigned number + +# Whether to indent continued function call prototype one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_proto_param = false # true/false + +# Whether to indent continued function call declaration one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_class_param = false # true/false + +# Whether to indent continued class variable constructors one indent level, +# rather than aligning parameters under the open parenthesis. +indent_func_ctor_var_param = false # true/false + +# Whether to indent continued template parameter list one indent level, +# rather than aligning parameters under the open parenthesis. +indent_template_param = false # true/false + +# Double the indent for indent_func_xxx_param options. +# Use both values of the options indent_columns and indent_param. +indent_func_param_double = false # true/false + +# Indentation column for standalone 'const' qualifier on a function +# prototype. +indent_func_const = 0 # unsigned number + +# Indentation column for standalone 'throw' qualifier on a function +# prototype. +indent_func_throw = 0 # unsigned number + +# How to indent within a macro followed by a brace on the same line +# This allows reducing the indent in macros that have (for example) +# `do { ... } while (0)` blocks bracketing them. +# +# true: add an indent for the brace on the same line as the macro +# false: do not add an indent for the brace on the same line as the macro +# +# Default: true +indent_macro_brace = true # true/false + +# The number of spaces to indent a continued '->' or '.'. +# Usually set to 0, 1, or indent_columns. +indent_member = 0 # unsigned number + +# Whether lines broken at '.' or '->' should be indented by a single indent. +# The indent_member option will not be effective if this is set to true. +indent_member_single = false # true/false + +# Spaces to indent single line ('//') comments on lines before code. +indent_sing_line_comments = 0 # unsigned number + +# When opening a paren for a control statement (if, for, while, etc), increase +# the indent level by this value. Negative values decrease the indent level. +indent_sparen_extra = 0 # number + +# Whether to indent trailing single line ('//') comments relative to the code +# instead of trying to keep the same absolute column. +indent_relative_single_line_comments = false # true/false + +# Spaces to indent 'case' from 'switch'. Usually 0 or indent_columns. +indent_switch_case = 0 # unsigned number + +# indent 'break' with 'case' from 'switch'. +indent_switch_break_with_case = false # true/false + +# Whether to indent preprocessor statements inside of switch statements. +# +# Default: true +indent_switch_pp = true # true/false + +# Spaces to shift the 'case' line, without affecting any other lines. +# Usually 0. +indent_case_shift = 0 # unsigned number + +# Spaces to indent '{' from 'case'. By default, the brace will appear under +# the 'c' in case. Usually set to 0 or indent_columns. Negative values are OK. +indent_case_brace = 0 # number + +# Whether to indent comments found in first column. +indent_col1_comment = false # true/false + +# Whether to indent multi string literal in first column. +indent_col1_multi_string_literal = false # true/false + +# How to indent goto labels. +# +# >0: Absolute column where 1 is the leftmost column +# <=0: Subtract from brace indent +# +# Default: 1 +indent_label = 1 # number + +# How to indent access specifiers that are followed by a +# colon. +# +# >0: Absolute column where 1 is the leftmost column +# <=0: Subtract from brace indent +# +# Default: 1 +indent_access_spec = -4 # number + +# Whether to indent the code after an access specifier by one level. +# If true, this option forces 'indent_access_spec=0'. +indent_access_spec_body = false # true/false + +# If an open parenthesis is followed by a newline, whether to indent the next +# line so that it lines up after the open parenthesis (not recommended). +indent_paren_nl = false # true/false + +# How to indent a close parenthesis after a newline. +# +# 0: Indent to body level (default) +# 1: Align under the open parenthesis +# 2: Indent to the brace level +indent_paren_close = 0 # unsigned number + +# Whether to indent the open parenthesis of a function definition, +# if the parenthesis is on its own line. +indent_paren_after_func_def = false # true/false + +# Whether to indent the open parenthesis of a function declaration, +# if the parenthesis is on its own line. +indent_paren_after_func_decl = false # true/false + +# Whether to indent the open parenthesis of a function call, +# if the parenthesis is on its own line. +indent_paren_after_func_call = false # true/false + +# Whether to indent a comma when inside a parenthesis. +# If true, aligns under the open parenthesis. +indent_comma_paren = false # true/false + +# Whether to indent a Boolean operator when inside a parenthesis. +# If true, aligns under the open parenthesis. +indent_bool_paren = false # true/false + +# Whether to indent a semicolon when inside a for parenthesis. +# If true, aligns under the open for parenthesis. +indent_semicolon_for_paren = false # true/false + +# Whether to align the first expression to following ones +# if indent_bool_paren=true. +indent_first_bool_expr = false # true/false + +# Whether to align the first expression to following ones +# if indent_semicolon_for_paren=true. +indent_first_for_expr = false # true/false + +# If an open square is followed by a newline, whether to indent the next line +# so that it lines up after the open square (not recommended). +indent_square_nl = false # true/false + +# (ESQL/C) Whether to preserve the relative indent of 'EXEC SQL' bodies. +indent_preserve_sql = false # true/false + +# Whether to align continued statements at the '='. If false or if the '=' is +# followed by a newline, the next line is indent one tab. +# +# Default: true +indent_align_assign = true # true/false + +# If true, the indentation of the chunks after a '=' sequence will be set at +# LHS token indentation column before '='. +indent_off_after_assign = false # true/false + +# Whether to align continued statements at the '('. If false or the '(' is +# followed by a newline, the next line indent is one tab. +# +# Default: true +indent_align_paren = true # true/false + +# (OC) Whether to indent Objective-C code inside message selectors. +indent_oc_inside_msg_sel = false # true/false + +# (OC) Whether to indent Objective-C blocks at brace level instead of usual +# rules. +indent_oc_block = false # true/false + +# (OC) Indent for Objective-C blocks in a message relative to the parameter +# name. +# +# =0: Use indent_oc_block rules +# >0: Use specified number of spaces to indent +indent_oc_block_msg = 0 # unsigned number + +# (OC) Minimum indent for subsequent parameters +indent_oc_msg_colon = 0 # unsigned number + +# (OC) Whether to prioritize aligning with initial colon (and stripping spaces +# from lines, if necessary). +# +# Default: true +indent_oc_msg_prioritize_first_colon = true # true/false + +# (OC) Whether to indent blocks the way that Xcode does by default +# (from the keyword if the parameter is on its own line; otherwise, from the +# previous indentation level). Requires indent_oc_block_msg=true. +indent_oc_block_msg_xcode_style = false # true/false + +# (OC) Whether to indent blocks from where the brace is, relative to a +# message keyword. Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_keyword = false # true/false + +# (OC) Whether to indent blocks from where the brace is, relative to a message +# colon. Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_colon = false # true/false + +# (OC) Whether to indent blocks from where the block caret is. +# Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_caret = false # true/false + +# (OC) Whether to indent blocks from where the brace caret is. +# Requires indent_oc_block_msg=true. +indent_oc_block_msg_from_brace = false # true/false + +# When indenting after virtual brace open and newline add further spaces to +# reach this minimum indent. +indent_min_vbrace_open = 0 # unsigned number + +# Whether to add further spaces after regular indent to reach next tabstop +# when indenting after virtual brace open and newline. +indent_vbrace_open_on_tabstop = false # true/false + +# How to indent after a brace followed by another token (not a newline). +# true: indent all contained lines to match the token +# false: indent all contained lines to match the brace +# +# Default: true +indent_token_after_brace = true # true/false + +# Whether to indent the body of a C++11 lambda. +indent_cpp_lambda_body = false # true/false + +# How to indent compound literals that are being returned. +# true: add both the indent from return & the compound literal open brace (ie: +# 2 indent levels) +# false: only indent 1 level, don't add the indent for the open brace, only add +# the indent for the return. +# +# Default: true +indent_compound_literal_return = true # true/false + +# (C#) Whether to indent a 'using' block if no braces are used. +# +# Default: true +indent_using_block = true # true/false + +# How to indent the continuation of ternary operator. +# +# 0: Off (default) +# 1: When the `if_false` is a continuation, indent it under `if_false` +# 2: When the `:` is a continuation, indent it under `?` +indent_ternary_operator = 0 # unsigned number + +# Whether to indent the statments inside ternary operator. +indent_inside_ternary_operator = false # true/false + +# If true, the indentation of the chunks after a `return` sequence will be set at return indentation column. +indent_off_after_return = false # true/false + +# If true, the indentation of the chunks after a `return new` sequence will be set at return indentation column. +indent_off_after_return_new = false # true/false + +# If true, the tokens after return are indented with regular single indentation. By default (false) the indentation is after the return token. +indent_single_after_return = false # true/false + +# Whether to ignore indent and alignment for 'asm' blocks (i.e. assume they +# have their own indentation). +indent_ignore_asm_block = false # true/false + +# Don't indent the close parenthesis of a function definition, +# if the parenthesis is on its own line. +donot_indent_func_def_close_paren = false # true/false + +# +# Newline adding and removing options +# + +# Whether to collapse empty blocks between '{' and '}'. +# If true, overrides nl_inside_empty_func +nl_collapse_empty_body = true # true/false + +# Don't split one-line braced assignments, as in 'foo_t f = { 1, 2 };'. +nl_assign_leave_one_liners = false # true/false + +# Don't split one-line braced statements inside a 'class xx { }' body. +nl_class_leave_one_liners = false # true/false + +# Don't split one-line enums, as in 'enum foo { BAR = 15 };' +nl_enum_leave_one_liners = false # true/false + +# Don't split one-line get or set functions. +nl_getset_leave_one_liners = false # true/false + +# (C#) Don't split one-line property get or set functions. +nl_cs_property_leave_one_liners = false # true/false + +# Don't split one-line function definitions, as in 'int foo() { return 0; }'. +# might modify nl_func_type_name +nl_func_leave_one_liners = false # true/false + +# Don't split one-line C++11 lambdas, as in '[]() { return 0; }'. +nl_cpp_lambda_leave_one_liners = false # true/false + +# Don't split one-line if/else statements, as in 'if(...) b++;'. +nl_if_leave_one_liners = false # true/false + +# Don't split one-line while statements, as in 'while(...) b++;'. +nl_while_leave_one_liners = false # true/false + +# Don't split one-line for statements, as in 'for(...) b++;'. +nl_for_leave_one_liners = false # true/false + +# (OC) Don't split one-line Objective-C messages. +nl_oc_msg_leave_one_liner = false # true/false + +# (OC) Add or remove newline between method declaration and '{'. +nl_oc_mdef_brace = ignore # ignore/add/remove/force + +# (OC) Add or remove newline between Objective-C block signature and '{'. +nl_oc_block_brace = ignore # ignore/add/remove/force + +# (OC) Add or remove blank line before '@interface' statement. +nl_oc_before_interface = ignore # ignore/add/remove/force + +# (OC) Add or remove blank line before '@implementation' statement. +nl_oc_before_implementation = ignore # ignore/add/remove/force + +# (OC) Add or remove blank line before '@end' statement. +nl_oc_before_end = ignore # ignore/add/remove/force + +# (OC) Add or remove newline between '@interface' and '{'. +nl_oc_interface_brace = ignore # ignore/add/remove/force + +# (OC) Add or remove newline between '@implementation' and '{'. +nl_oc_implementation_brace = ignore # ignore/add/remove/force + +# Add or remove newlines at the start of the file. +nl_start_of_file = ignore # ignore/add/remove/force + +# The minimum number of newlines at the start of the file (only used if +# nl_start_of_file is 'add' or 'force'). +nl_start_of_file_min = 0 # unsigned number + +# Add or remove newline at the end of the file. +nl_end_of_file = ignore # ignore/add/remove/force + +# The minimum number of newlines at the end of the file (only used if +# nl_end_of_file is 'add' or 'force'). +nl_end_of_file_min = 0 # unsigned number + +# Add or remove newline between '=' and '{'. +nl_assign_brace = ignore # ignore/add/remove/force + +# (D) Add or remove newline between '=' and '['. +nl_assign_square = ignore # ignore/add/remove/force + +# Add or remove newline between '[]' and '{'. +nl_tsquare_brace = ignore # ignore/add/remove/force + +# (D) Add or remove newline after '= ['. Will also affect the newline before +# the ']'. +nl_after_square_assign = ignore # ignore/add/remove/force + +# Add or remove newline between a function call's ')' and '{', as in +# 'list_for_each(item, &list) { }'. +nl_fcall_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'enum' and '{'. +nl_enum_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'enum' and 'class'. +nl_enum_class = ignore # ignore/add/remove/force + +# Add or remove newline between 'enum class' and the identifier. +nl_enum_class_identifier = ignore # ignore/add/remove/force + +# Add or remove newline between 'enum class' type and ':'. +nl_enum_identifier_colon = ignore # ignore/add/remove/force + +# Add or remove newline between 'enum class identifier :' and type. +nl_enum_colon_type = ignore # ignore/add/remove/force + +# Add or remove newline between 'struct and '{'. +nl_struct_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'union' and '{'. +nl_union_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'if' and '{'. +nl_if_brace = ignore # ignore/add/remove/force + +# Add or remove newline between '}' and 'else'. +nl_brace_else = ignore # ignore/add/remove/force + +# Add or remove newline between 'else if' and '{'. If set to ignore, +# nl_if_brace is used instead. +nl_elseif_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'else' and '{'. +nl_else_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'else' and 'if'. +nl_else_if = ignore # ignore/add/remove/force + +# Add or remove newline before '{' opening brace +nl_before_opening_brace_func_class_def = ignore # ignore/add/remove/force + +# Add or remove newline before 'if'/'else if' closing parenthesis. +nl_before_if_closing_paren = ignore # ignore/add/remove/force + +# Add or remove newline between '}' and 'finally'. +nl_brace_finally = ignore # ignore/add/remove/force + +# Add or remove newline between 'finally' and '{'. +nl_finally_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'try' and '{'. +nl_try_brace = ignore # ignore/add/remove/force + +# Add or remove newline between get/set and '{'. +nl_getset_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'for' and '{'. +nl_for_brace = ignore # ignore/add/remove/force + +# Add or remove newline before the '{' of a 'catch' statement, as in +# 'catch (decl) {'. +nl_catch_brace = ignore # ignore/add/remove/force + +# (OC) Add or remove newline before the '{' of a '@catch' statement, as in +# '@catch (decl) {'. If set to ignore, nl_catch_brace is used. +nl_oc_catch_brace = ignore # ignore/add/remove/force + +# Add or remove newline between '}' and 'catch'. +nl_brace_catch = ignore # ignore/add/remove/force + +# (OC) Add or remove newline between '}' and '@catch'. If set to ignore, +# nl_brace_catch is used. +nl_oc_brace_catch = ignore # ignore/add/remove/force + +# Add or remove newline between '}' and ']'. +nl_brace_square = ignore # ignore/add/remove/force + +# Add or remove newline between '}' and ')' in a function invocation. +nl_brace_fparen = ignore # ignore/add/remove/force + +# Add or remove newline between 'while' and '{'. +nl_while_brace = ignore # ignore/add/remove/force + +# (D) Add or remove newline between 'scope (x)' and '{'. +nl_scope_brace = ignore # ignore/add/remove/force + +# (D) Add or remove newline between 'unittest' and '{'. +nl_unittest_brace = ignore # ignore/add/remove/force + +# (D) Add or remove newline between 'version (x)' and '{'. +nl_version_brace = ignore # ignore/add/remove/force + +# (C#) Add or remove newline between 'using' and '{'. +nl_using_brace = ignore # ignore/add/remove/force + +# Add or remove newline between two open or close braces. Due to general +# newline/brace handling, REMOVE may not work. +nl_brace_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'do' and '{'. +nl_do_brace = ignore # ignore/add/remove/force + +# Add or remove newline between '}' and 'while' of 'do' statement. +nl_brace_while = ignore # ignore/add/remove/force + +# Add or remove newline between 'switch' and '{'. +nl_switch_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'synchronized' and '{'. +nl_synchronized_brace = ignore # ignore/add/remove/force + +# Add a newline between ')' and '{' if the ')' is on a different line than the +# if/for/etc. +# +# Overrides nl_for_brace, nl_if_brace, nl_switch_brace, nl_while_switch and +# nl_catch_brace. +nl_multi_line_cond = false # true/false + +# Add a newline after '(' if an if/for/while/switch condition spans multiple +# lines +nl_multi_line_sparen_open = ignore # ignore/add/remove/force + +# Add a newline before ')' if an if/for/while/switch condition spans multiple +# lines. Overrides nl_before_if_closing_paren if both are specified. +nl_multi_line_sparen_close = ignore # ignore/add/remove/force + +# Force a newline in a define after the macro name for multi-line defines. +nl_multi_line_define = false # true/false + +# Whether to add a newline before 'case', and a blank line before a 'case' +# statement that follows a ';' or '}'. +nl_before_case = false # true/false + +# Whether to add a newline after a 'case' statement. +nl_after_case = false # true/false + +# Add or remove newline between a case ':' and '{'. +# +# Overrides nl_after_case. +nl_case_colon_brace = ignore # ignore/add/remove/force + +# Add or remove newline between ')' and 'throw'. +nl_before_throw = ignore # ignore/add/remove/force + +# Add or remove newline between 'namespace' and '{'. +nl_namespace_brace = ignore # ignore/add/remove/force + +# Add or remove newline after 'template<...>' of a template class. +nl_template_class = ignore # ignore/add/remove/force + +# Add or remove newline after 'template<...>' of a template class declaration. +# +# Overrides nl_template_class. +nl_template_class_decl = ignore # ignore/add/remove/force + +# Add or remove newline after 'template<>' of a specialized class declaration. +# +# Overrides nl_template_class_decl. +nl_template_class_decl_special = ignore # ignore/add/remove/force + +# Add or remove newline after 'template<...>' of a template class definition. +# +# Overrides nl_template_class. +nl_template_class_def = ignore # ignore/add/remove/force + +# Add or remove newline after 'template<>' of a specialized class definition. +# +# Overrides nl_template_class_def. +nl_template_class_def_special = ignore # ignore/add/remove/force + +# Add or remove newline after 'template<...>' of a template function. +nl_template_func = ignore # ignore/add/remove/force + +# Add or remove newline after 'template<...>' of a template function +# declaration. +# +# Overrides nl_template_func. +nl_template_func_decl = ignore # ignore/add/remove/force + +# Add or remove newline after 'template<>' of a specialized function +# declaration. +# +# Overrides nl_template_func_decl. +nl_template_func_decl_special = ignore # ignore/add/remove/force + +# Add or remove newline after 'template<...>' of a template function +# definition. +# +# Overrides nl_template_func. +nl_template_func_def = ignore # ignore/add/remove/force + +# Add or remove newline after 'template<>' of a specialized function +# definition. +# +# Overrides nl_template_func_def. +nl_template_func_def_special = ignore # ignore/add/remove/force + +# Add or remove newline after 'template<...>' of a template variable. +nl_template_var = ignore # ignore/add/remove/force + +# Add or remove newline between 'template<...>' and 'using' of a templated +# type alias. +nl_template_using = ignore # ignore/add/remove/force + +# Add or remove newline between 'class' and '{'. +nl_class_brace = ignore # ignore/add/remove/force + +# Add or remove newline before or after (depending on pos_class_comma, +# may not be IGNORE) each',' in the base class list. +nl_class_init_args = ignore # ignore/add/remove/force + +# Add or remove newline after each ',' in the constructor member +# initialization. Related to nl_constr_colon, pos_constr_colon and +# pos_constr_comma. +nl_constr_init_args = ignore # ignore/add/remove/force + +# Add or remove newline before first element, after comma, and after last +# element, in 'enum'. +nl_enum_own_lines = ignore # ignore/add/remove/force + +# Add or remove newline between return type and function name in a function +# definition. +# might be modified by nl_func_leave_one_liners +nl_func_type_name = ignore # ignore/add/remove/force + +# Add or remove newline between return type and function name inside a class +# definition. If set to ignore, nl_func_type_name or nl_func_proto_type_name +# is used instead. +nl_func_type_name_class = ignore # ignore/add/remove/force + +# Add or remove newline between class specification and '::' +# in 'void A::f() { }'. Only appears in separate member implementation (does +# not appear with in-line implementation). +nl_func_class_scope = ignore # ignore/add/remove/force + +# Add or remove newline between function scope and name, as in +# 'void A :: f() { }'. +nl_func_scope_name = ignore # ignore/add/remove/force + +# Add or remove newline between return type and function name in a prototype. +nl_func_proto_type_name = ignore # ignore/add/remove/force + +# Add or remove newline between a function name and the opening '(' in the +# declaration. +nl_func_paren = ignore # ignore/add/remove/force + +# Overrides nl_func_paren for functions with no parameters. +nl_func_paren_empty = ignore # ignore/add/remove/force + +# Add or remove newline between a function name and the opening '(' in the +# definition. +nl_func_def_paren = ignore # ignore/add/remove/force + +# Overrides nl_func_def_paren for functions with no parameters. +nl_func_def_paren_empty = ignore # ignore/add/remove/force + +# Add or remove newline between a function name and the opening '(' in the +# call. +nl_func_call_paren = ignore # ignore/add/remove/force + +# Overrides nl_func_call_paren for functions with no parameters. +nl_func_call_paren_empty = ignore # ignore/add/remove/force + +# Add or remove newline after '(' in a function declaration. +nl_func_decl_start = ignore # ignore/add/remove/force + +# Add or remove newline after '(' in a function definition. +nl_func_def_start = ignore # ignore/add/remove/force + +# Overrides nl_func_decl_start when there is only one parameter. +nl_func_decl_start_single = ignore # ignore/add/remove/force + +# Overrides nl_func_def_start when there is only one parameter. +nl_func_def_start_single = ignore # ignore/add/remove/force + +# Whether to add a newline after '(' in a function declaration if '(' and ')' +# are in different lines. If false, nl_func_decl_start is used instead. +nl_func_decl_start_multi_line = false # true/false + +# Whether to add a newline after '(' in a function definition if '(' and ')' +# are in different lines. If false, nl_func_def_start is used instead. +nl_func_def_start_multi_line = false # true/false + +# Add or remove newline after each ',' in a function declaration. +nl_func_decl_args = ignore # ignore/add/remove/force + +# Add or remove newline after each ',' in a function definition. +nl_func_def_args = ignore # ignore/add/remove/force + +# Add or remove newline after each ',' in a function call. +nl_func_call_args = ignore # ignore/add/remove/force + +# Whether to add a newline after each ',' in a function declaration if '(' +# and ')' are in different lines. If false, nl_func_decl_args is used instead. +nl_func_decl_args_multi_line = false # true/false + +# Whether to add a newline after each ',' in a function definition if '(' +# and ')' are in different lines. If false, nl_func_def_args is used instead. +nl_func_def_args_multi_line = false # true/false + +# Add or remove newline before the ')' in a function declaration. +nl_func_decl_end = ignore # ignore/add/remove/force + +# Add or remove newline before the ')' in a function definition. +nl_func_def_end = ignore # ignore/add/remove/force + +# Overrides nl_func_decl_end when there is only one parameter. +nl_func_decl_end_single = ignore # ignore/add/remove/force + +# Overrides nl_func_def_end when there is only one parameter. +nl_func_def_end_single = ignore # ignore/add/remove/force + +# Whether to add a newline before ')' in a function declaration if '(' and ')' +# are in different lines. If false, nl_func_decl_end is used instead. +nl_func_decl_end_multi_line = false # true/false + +# Whether to add a newline before ')' in a function definition if '(' and ')' +# are in different lines. If false, nl_func_def_end is used instead. +nl_func_def_end_multi_line = false # true/false + +# Add or remove newline between '()' in a function declaration. +nl_func_decl_empty = ignore # ignore/add/remove/force + +# Add or remove newline between '()' in a function definition. +nl_func_def_empty = ignore # ignore/add/remove/force + +# Add or remove newline between '()' in a function call. +nl_func_call_empty = ignore # ignore/add/remove/force + +# Whether to add a newline after '(' in a function call, +# has preference over nl_func_call_start_multi_line. +nl_func_call_start = ignore # ignore/add/remove/force + +# Whether to add a newline before ')' in a function call. +nl_func_call_end = ignore # ignore/add/remove/force + +# Whether to add a newline after '(' in a function call if '(' and ')' are in +# different lines. +nl_func_call_start_multi_line = false # true/false + +# Whether to add a newline after each ',' in a function call if '(' and ')' +# are in different lines. +nl_func_call_args_multi_line = false # true/false + +# Whether to add a newline before ')' in a function call if '(' and ')' are in +# different lines. +nl_func_call_end_multi_line = false # true/false + +# Whether to respect nl_func_call_XXX option incase of closure args. +nl_func_call_args_multi_line_ignore_closures = false # true/false + +# Whether to add a newline after '<' of a template parameter list. +nl_template_start = false # true/false + +# Whether to add a newline after each ',' in a template parameter list. +nl_template_args = false # true/false + +# Whether to add a newline before '>' of a template parameter list. +nl_template_end = false # true/false + +# (OC) Whether to put each Objective-C message parameter on a separate line. +# See nl_oc_msg_leave_one_liner. +nl_oc_msg_args = false # true/false + +# Add or remove newline between function signature and '{'. +nl_fdef_brace = ignore # ignore/add/remove/force + +# Add or remove newline between function signature and '{', +# if signature ends with ')'. Overrides nl_fdef_brace. +nl_fdef_brace_cond = ignore # ignore/add/remove/force + +# Add or remove newline between C++11 lambda signature and '{'. +nl_cpp_ldef_brace = ignore # ignore/add/remove/force + +# Add or remove newline between 'return' and the return expression. +nl_return_expr = ignore # ignore/add/remove/force + +# Whether to add a newline after semicolons, except in 'for' statements. +nl_after_semicolon = false # true/false + +# (Java) Add or remove newline between the ')' and '{{' of the double brace +# initializer. +nl_paren_dbrace_open = ignore # ignore/add/remove/force + +# Whether to add a newline after the type in an unnamed temporary +# direct-list-initialization. +nl_type_brace_init_lst = ignore # ignore/add/remove/force + +# Whether to add a newline after the open brace in an unnamed temporary +# direct-list-initialization. +nl_type_brace_init_lst_open = ignore # ignore/add/remove/force + +# Whether to add a newline before the close brace in an unnamed temporary +# direct-list-initialization. +nl_type_brace_init_lst_close = ignore # ignore/add/remove/force + +# Whether to add a newline after '{'. This also adds a newline before the +# matching '}'. +nl_after_brace_open = false # true/false + +# Whether to add a newline between the open brace and a trailing single-line +# comment. Requires nl_after_brace_open=true. +nl_after_brace_open_cmt = false # true/false + +# Whether to add a newline after a virtual brace open with a non-empty body. +# These occur in un-braced if/while/do/for statement bodies. +nl_after_vbrace_open = false # true/false + +# Whether to add a newline after a virtual brace open with an empty body. +# These occur in un-braced if/while/do/for statement bodies. +nl_after_vbrace_open_empty = false # true/false + +# Whether to add a newline after '}'. Does not apply if followed by a +# necessary ';'. +nl_after_brace_close = false # true/false + +# Whether to add a newline after a virtual brace close, +# as in 'if (foo) a++; return;'. +nl_after_vbrace_close = false # true/false + +# Add or remove newline between the close brace and identifier, +# as in 'struct { int a; } b;'. Affects enumerations, unions and +# structures. If set to ignore, uses nl_after_brace_close. +nl_brace_struct_var = ignore # ignore/add/remove/force + +# Whether to alter newlines in '#define' macros. +nl_define_macro = false # true/false + +# Whether to alter newlines between consecutive parenthesis closes. The number +# of closing parentheses in a line will depend on respective open parenthesis +# lines. +nl_squeeze_paren_close = false # true/false + +# Whether to remove blanks after '#ifxx' and '#elxx', or before '#elxx' and +# '#endif'. Does not affect top-level #ifdefs. +nl_squeeze_ifdef = false # true/false + +# Makes the nl_squeeze_ifdef option affect the top-level #ifdefs as well. +nl_squeeze_ifdef_top_level = false # true/false + +# Add or remove blank line before 'if'. +nl_before_if = ignore # ignore/add/remove/force + +# Add or remove blank line after 'if' statement. Add/Force work only if the +# next token is not a closing brace. +nl_after_if = ignore # ignore/add/remove/force + +# Add or remove blank line before 'for'. +nl_before_for = ignore # ignore/add/remove/force + +# Add or remove blank line after 'for' statement. +nl_after_for = ignore # ignore/add/remove/force + +# Add or remove blank line before 'while'. +nl_before_while = ignore # ignore/add/remove/force + +# Add or remove blank line after 'while' statement. +nl_after_while = ignore # ignore/add/remove/force + +# Add or remove blank line before 'switch'. +nl_before_switch = ignore # ignore/add/remove/force + +# Add or remove blank line after 'switch' statement. +nl_after_switch = ignore # ignore/add/remove/force + +# Add or remove blank line before 'synchronized'. +nl_before_synchronized = ignore # ignore/add/remove/force + +# Add or remove blank line after 'synchronized' statement. +nl_after_synchronized = ignore # ignore/add/remove/force + +# Add or remove blank line before 'do'. +nl_before_do = ignore # ignore/add/remove/force + +# Add or remove blank line after 'do/while' statement. +nl_after_do = ignore # ignore/add/remove/force + +# Whether to put a blank line before 'return' statements, unless after an open +# brace. +nl_before_return = false # true/false + +# Whether to put a blank line after 'return' statements, unless followed by a +# close brace. +nl_after_return = false # true/false + +# Whether to put a blank line before a member '.' or '->' operators. +nl_before_member = ignore # ignore/add/remove/force + +# (Java) Whether to put a blank line after a member '.' or '->' operators. +nl_after_member = ignore # ignore/add/remove/force + +# Whether to double-space commented-entries in 'struct'/'union'/'enum'. +nl_ds_struct_enum_cmt = false # true/false + +# Whether to force a newline before '}' of a 'struct'/'union'/'enum'. +# (Lower priority than eat_blanks_before_close_brace.) +nl_ds_struct_enum_close_brace = false # true/false + +# Add or remove newline before or after (depending on pos_class_colon) a class +# colon, as in 'class Foo : public Bar'. +nl_class_colon = ignore # ignore/add/remove/force + +# Add or remove newline around a class constructor colon. The exact position +# depends on nl_constr_init_args, pos_constr_colon and pos_constr_comma. +nl_constr_colon = ignore # ignore/add/remove/force + +# Whether to collapse a two-line namespace, like 'namespace foo\n{ decl; }' +# into a single line. If true, prevents other brace newline rules from turning +# such code into four lines. +nl_namespace_two_to_one_liner = false # true/false + +# Whether to remove a newline in simple unbraced if statements, turning them +# into one-liners, as in 'if(b)\n i++;' => 'if(b) i++;'. +nl_create_if_one_liner = false # true/false + +# Whether to remove a newline in simple unbraced for statements, turning them +# into one-liners, as in 'for (...)\n stmt;' => 'for (...) stmt;'. +nl_create_for_one_liner = false # true/false + +# Whether to remove a newline in simple unbraced while statements, turning +# them into one-liners, as in 'while (expr)\n stmt;' => 'while (expr) stmt;'. +nl_create_while_one_liner = false # true/false + +# Whether to collapse a function definition whose body (not counting braces) +# is only one line so that the entire definition (prototype, braces, body) is +# a single line. +nl_create_func_def_one_liner = false # true/false + +# Whether to collapse a function definition whose body (not counting braces) +# is only one line so that the entire definition (prototype, braces, body) is +# a single line. +nl_create_list_one_liner = false # true/false + +# Whether to split one-line simple unbraced if statements into two lines by +# adding a newline, as in 'if(b) i++;'. +nl_split_if_one_liner = false # true/false + +# Whether to split one-line simple unbraced for statements into two lines by +# adding a newline, as in 'for (...) stmt;'. +nl_split_for_one_liner = false # true/false + +# Whether to split one-line simple unbraced while statements into two lines by +# adding a newline, as in 'while (expr) stmt;'. +nl_split_while_one_liner = false # true/false + +# Don't add a newline before a cpp-comment in a parameter list of a function +# call. +donot_add_nl_before_cpp_comment = false # true/false + +# +# Blank line options +# + +# The maximum number of consecutive newlines (3 = 2 blank lines). +nl_max = 0 # unsigned number + +# The maximum number of consecutive newlines in a function. +nl_max_blank_in_func = 0 # unsigned number + +# The number of newlines inside an empty function body. +# This option is overridden by nl_collapse_empty_body=true +nl_inside_empty_func = 0 # unsigned number + +# The number of newlines before a function prototype. +nl_before_func_body_proto = 0 # unsigned number + +# The number of newlines before a multi-line function definition. +nl_before_func_body_def = 0 # unsigned number + +# The number of newlines before a class constructor/destructor prototype. +nl_before_func_class_proto = 0 # unsigned number + +# The number of newlines before a class constructor/destructor definition. +nl_before_func_class_def = 0 # unsigned number + +# The number of newlines after a function prototype. +nl_after_func_proto = 0 # unsigned number + +# The number of newlines after a function prototype, if not followed by +# another function prototype. +nl_after_func_proto_group = 0 # unsigned number + +# The number of newlines after a class constructor/destructor prototype. +nl_after_func_class_proto = 0 # unsigned number + +# The number of newlines after a class constructor/destructor prototype, +# if not followed by another constructor/destructor prototype. +nl_after_func_class_proto_group = 0 # unsigned number + +# Whether one-line method definitions inside a class body should be treated +# as if they were prototypes for the purposes of adding newlines. +# +# Requires nl_class_leave_one_liners=true. Overrides nl_before_func_body_def +# and nl_before_func_class_def for one-liners. +nl_class_leave_one_liner_groups = false # true/false + +# The number of newlines after '}' of a multi-line function body. +nl_after_func_body = 0 # unsigned number + +# The number of newlines after '}' of a multi-line function body in a class +# declaration. Also affects class constructors/destructors. +# +# Overrides nl_after_func_body. +nl_after_func_body_class = 0 # unsigned number + +# The number of newlines after '}' of a single line function body. Also +# affects class constructors/destructors. +# +# Overrides nl_after_func_body and nl_after_func_body_class. +nl_after_func_body_one_liner = 0 # unsigned number + +# The number of blank lines after a block of variable definitions at the top +# of a function body. +# +# 0: No change (default). +nl_func_var_def_blk = 0 # unsigned number + +# The number of newlines before a block of typedefs. If nl_after_access_spec +# is non-zero, that option takes precedence. +# +# 0: No change (default). +nl_typedef_blk_start = 0 # unsigned number + +# The number of newlines after a block of typedefs. +# +# 0: No change (default). +nl_typedef_blk_end = 0 # unsigned number + +# The maximum number of consecutive newlines within a block of typedefs. +# +# 0: No change (default). +nl_typedef_blk_in = 0 # unsigned number + +# The number of newlines before a block of variable definitions not at the top +# of a function body. If nl_after_access_spec is non-zero, that option takes +# precedence. +# +# 0: No change (default). +nl_var_def_blk_start = 0 # unsigned number + +# The number of newlines after a block of variable definitions not at the top +# of a function body. +# +# 0: No change (default). +nl_var_def_blk_end = 0 # unsigned number + +# The maximum number of consecutive newlines within a block of variable +# definitions. +# +# 0: No change (default). +nl_var_def_blk_in = 0 # unsigned number + +# The minimum number of newlines before a multi-line comment. +# Doesn't apply if after a brace open or another multi-line comment. +nl_before_block_comment = 0 # unsigned number + +# The minimum number of newlines before a single-line C comment. +# Doesn't apply if after a brace open or other single-line C comments. +nl_before_c_comment = 0 # unsigned number + +# The minimum number of newlines before a CPP comment. +# Doesn't apply if after a brace open or other CPP comments. +nl_before_cpp_comment = 0 # unsigned number + +# Whether to force a newline after a multi-line comment. +nl_after_multiline_comment = false # true/false + +# Whether to force a newline after a label's colon. +nl_after_label_colon = false # true/false + +# The number of newlines after '}' or ';' of a struct/enum/union definition. +nl_after_struct = 0 # unsigned number + +# The number of newlines before a class definition. +nl_before_class = 0 # unsigned number + +# The number of newlines after '}' or ';' of a class definition. +nl_after_class = 0 # unsigned number + +# The number of newlines before a namespace. +nl_before_namespace = 0 # unsigned number + +# The number of newlines after '{' of a namespace. This also adds newlines +# before the matching '}'. +# +# 0: Apply eat_blanks_after_open_brace or eat_blanks_before_close_brace if +# applicable, otherwise no change. +# +# Overrides eat_blanks_after_open_brace and eat_blanks_before_close_brace. +nl_inside_namespace = 0 # unsigned number + +# The number of newlines after '}' of a namespace. +nl_after_namespace = 0 # unsigned number + +# The number of newlines before an access specifier label. This also includes +# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count +# if after a brace open. +# +# 0: No change (default). +nl_before_access_spec = 0 # unsigned number + +# The number of newlines after an access specifier label. This also includes +# the Qt-specific 'signals:' and 'slots:'. Will not change the newline count +# if after a brace open. +# +# 0: No change (default). +# +# Overrides nl_typedef_blk_start and nl_var_def_blk_start. +nl_after_access_spec = 0 # unsigned number + +# The number of newlines between a function definition and the function +# comment, as in '// comment\n void foo() {...}'. +# +# 0: No change (default). +nl_comment_func_def = 0 # unsigned number + +# The number of newlines after a try-catch-finally block that isn't followed +# by a brace close. +# +# 0: No change (default). +nl_after_try_catch_finally = 0 # unsigned number + +# (C#) The number of newlines before and after a property, indexer or event +# declaration. +# +# 0: No change (default). +nl_around_cs_property = 0 # unsigned number + +# (C#) The number of newlines between the get/set/add/remove handlers. +# +# 0: No change (default). +nl_between_get_set = 0 # unsigned number + +# (C#) Add or remove newline between property and the '{'. +nl_property_brace = ignore # ignore/add/remove/force + +# Whether to remove blank lines after '{'. +eat_blanks_after_open_brace = false # true/false + +# Whether to remove blank lines before '}'. +eat_blanks_before_close_brace = false # true/false + +# How aggressively to remove extra newlines not in preprocessor. +# +# 0: No change (default) +# 1: Remove most newlines not handled by other config +# 2: Remove all newlines and reformat completely by config +nl_remove_extra_newlines = 0 # unsigned number + +# (Java) Add or remove newline after an annotation statement. Only affects +# annotations that are after a newline. +nl_after_annotation = ignore # ignore/add/remove/force + +# (Java) Add or remove newline between two annotations. +nl_between_annotation = ignore # ignore/add/remove/force + +# The number of newlines before a whole-file #ifdef. +# +# 0: No change (default). +nl_before_whole_file_ifdef = 0 # unsigned number + +# The number of newlines after a whole-file #ifdef. +# +# 0: No change (default). +nl_after_whole_file_ifdef = 0 # unsigned number + +# The number of newlines before a whole-file #endif. +# +# 0: No change (default). +nl_before_whole_file_endif = 0 # unsigned number + +# The number of newlines after a whole-file #endif. +# +# 0: No change (default). +nl_after_whole_file_endif = 0 # unsigned number + +# +# Positioning options +# + +# The position of arithmetic operators in wrapped expressions. +pos_arith = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of assignment in wrapped expressions. Do not affect '=' +# followed by '{'. +pos_assign = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of Boolean operators in wrapped expressions. +pos_bool = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of comparison operators in wrapped expressions. +pos_compare = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of conditional operators, as in the '?' and ':' of +# 'expr ? stmt : stmt', in wrapped expressions. +pos_conditional = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in wrapped expressions. +pos_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in enum entries. +pos_enum_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in the base class list if there is more than one +# line. Affects nl_class_init_args. +pos_class_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of the comma in the constructor initialization list. +# Related to nl_constr_colon, nl_constr_init_args and pos_constr_colon. +pos_constr_comma = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of trailing/leading class colon, between class and base class +# list. Affects nl_class_colon. +pos_class_colon = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of colons between constructor and member initialization. +# Related to nl_constr_colon, nl_constr_init_args and pos_constr_comma. +pos_constr_colon = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# The position of shift operators in wrapped expressions. +pos_shift = ignore # ignore/break/force/lead/trail/join/lead_break/lead_force/trail_break/trail_force + +# +# Line splitting options +# + +# Try to limit code width to N columns. +code_width = 0 # unsigned number + +# Whether to fully split long 'for' statements at semi-colons. +ls_for_split_full = false # true/false + +# Whether to fully split long function prototypes/calls at commas. +# The option ls_code_width has priority over the option ls_func_split_full. +ls_func_split_full = false # true/false + +# Whether to split lines as close to code_width as possible and ignore some +# groupings. +# The option ls_code_width has priority over the option ls_func_split_full. +ls_code_width = false # true/false + +# +# Code alignment options (not left column spaces/tabs) +# + +# Whether to keep non-indenting tabs. +align_keep_tabs = false # true/false + +# Whether to use tabs for aligning. +align_with_tabs = false # true/false + +# Whether to bump out to the next tab when aligning. +align_on_tabstop = false # true/false + +# Whether to right-align numbers. +align_number_right = false # true/false + +# Whether to keep whitespace not required for alignment. +align_keep_extra_space = false # true/false + +# Whether to align variable definitions in prototypes and functions. +align_func_params = false # true/false + +# The span for aligning parameter definitions in function on parameter name. +# +# 0: Don't align (default). +align_func_params_span = 0 # unsigned number + +# The threshold for aligning function parameter definitions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_func_params_thresh = 0 # number + +# The gap for aligning function parameter definitions. +align_func_params_gap = 0 # unsigned number + +# The span for aligning constructor value. +# +# 0: Don't align (default). +align_constr_value_span = 0 # unsigned number + +# The threshold for aligning constructor value. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_constr_value_thresh = 0 # number + +# The gap for aligning constructor value. +align_constr_value_gap = 0 # unsigned number + +# Whether to align parameters in single-line functions that have the same +# name. The function names must already be aligned with each other. +align_same_func_call_params = false # true/false + +# The span for aligning function-call parameters for single line functions. +# +# 0: Don't align (default). +align_same_func_call_params_span = 0 # unsigned number + +# The threshold for aligning function-call parameters for single line +# functions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_same_func_call_params_thresh = 0 # number + +# The span for aligning variable definitions. +# +# 0: Don't align (default). +align_var_def_span = 0 # unsigned number + +# How to consider (or treat) the '*' in the alignment of variable definitions. +# +# 0: Part of the type 'void * foo;' (default) +# 1: Part of the variable 'void *foo;' +# 2: Dangling 'void *foo;' +# Dangling: the '*' will not be taken into account when aligning. +align_var_def_star_style = 0 # unsigned number + +# How to consider (or treat) the '&' in the alignment of variable definitions. +# +# 0: Part of the type 'long & foo;' (default) +# 1: Part of the variable 'long &foo;' +# 2: Dangling 'long &foo;' +# Dangling: the '&' will not be taken into account when aligning. +align_var_def_amp_style = 0 # unsigned number + +# The threshold for aligning variable definitions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_var_def_thresh = 0 # number + +# The gap for aligning variable definitions. +align_var_def_gap = 0 # unsigned number + +# Whether to align the colon in struct bit fields. +align_var_def_colon = false # true/false + +# The gap for aligning the colon in struct bit fields. +align_var_def_colon_gap = 0 # unsigned number + +# Whether to align any attribute after the variable name. +align_var_def_attribute = false # true/false + +# Whether to align inline struct/enum/union variable definitions. +align_var_def_inline = false # true/false + +# The span for aligning on '=' in assignments. +# +# 0: Don't align (default). +align_assign_span = 0 # unsigned number + +# The span for aligning on '=' in function prototype modifier. +# +# 0: Don't align (default). +align_assign_func_proto_span = 0 # unsigned number + +# The threshold for aligning on '=' in assignments. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_assign_thresh = 0 # number + +# How to apply align_assign_span to function declaration "assignments", i.e. +# 'virtual void foo() = 0' or '~foo() = {default|delete}'. +# +# 0: Align with other assignments (default) +# 1: Align with each other, ignoring regular assignments +# 2: Don't align +align_assign_decl_func = 0 # unsigned number + +# The span for aligning on '=' in enums. +# +# 0: Don't align (default). +align_enum_equ_span = 0 # unsigned number + +# The threshold for aligning on '=' in enums. +# Use a negative number for absolute thresholds. +# +# 0: no limit (default). +align_enum_equ_thresh = 0 # number + +# The span for aligning class member definitions. +# +# 0: Don't align (default). +align_var_class_span = 0 # unsigned number + +# The threshold for aligning class member definitions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_var_class_thresh = 0 # number + +# The gap for aligning class member definitions. +align_var_class_gap = 0 # unsigned number + +# The span for aligning struct/union member definitions. +# +# 0: Don't align (default). +align_var_struct_span = 0 # unsigned number + +# The threshold for aligning struct/union member definitions. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_var_struct_thresh = 0 # number + +# The gap for aligning struct/union member definitions. +align_var_struct_gap = 0 # unsigned number + +# The span for aligning struct initializer values. +# +# 0: Don't align (default). +align_struct_init_span = 0 # unsigned number + +# The span for aligning single-line typedefs. +# +# 0: Don't align (default). +align_typedef_span = 0 # unsigned number + +# The minimum space between the type and the synonym of a typedef. +align_typedef_gap = 0 # unsigned number + +# How to align typedef'd functions with other typedefs. +# +# 0: Don't mix them at all (default) +# 1: Align the open parenthesis with the types +# 2: Align the function type name with the other type names +align_typedef_func = 0 # unsigned number + +# How to consider (or treat) the '*' in the alignment of typedefs. +# +# 0: Part of the typedef type, 'typedef int * pint;' (default) +# 1: Part of type name: 'typedef int *pint;' +# 2: Dangling: 'typedef int *pint;' +# Dangling: the '*' will not be taken into account when aligning. +align_typedef_star_style = 0 # unsigned number + +# How to consider (or treat) the '&' in the alignment of typedefs. +# +# 0: Part of the typedef type, 'typedef int & intref;' (default) +# 1: Part of type name: 'typedef int &intref;' +# 2: Dangling: 'typedef int &intref;' +# Dangling: the '&' will not be taken into account when aligning. +align_typedef_amp_style = 0 # unsigned number + +# The span for aligning comments that end lines. +# +# 0: Don't align (default). +align_right_cmt_span = 0 # unsigned number + +# Minimum number of columns between preceding text and a trailing comment in +# order for the comment to qualify for being aligned. Must be non-zero to have +# an effect. +align_right_cmt_gap = 0 # unsigned number + +# If aligning comments, whether to mix with comments after '}' and #endif with +# less than three spaces before the comment. +align_right_cmt_mix = false # true/false + +# Whether to only align trailing comments that are at the same brace level. +align_right_cmt_same_level = false # true/false + +# Minimum column at which to align trailing comments. Comments which are +# aligned beyond this column, but which can be aligned in a lesser column, +# may be "pulled in". +# +# 0: Ignore (default). +align_right_cmt_at_col = 0 # unsigned number + +# The span for aligning function prototypes. +# +# 0: Don't align (default). +align_func_proto_span = 0 # unsigned number + +# The threshold for aligning function prototypes. +# Use a negative number for absolute thresholds. +# +# 0: No limit (default). +align_func_proto_thresh = 0 # number + +# Minimum gap between the return type and the function name. +align_func_proto_gap = 0 # unsigned number + +# Whether to align function prototypes on the 'operator' keyword instead of +# what follows. +align_on_operator = false # true/false + +# Whether to mix aligning prototype and variable declarations. If true, +# align_var_def_XXX options are used instead of align_func_proto_XXX options. +align_mix_var_proto = false # true/false + +# Whether to align single-line functions with function prototypes. +# Uses align_func_proto_span. +align_single_line_func = false # true/false + +# Whether to align the open brace of single-line functions. +# Requires align_single_line_func=true. Uses align_func_proto_span. +align_single_line_brace = false # true/false + +# Gap for align_single_line_brace. +align_single_line_brace_gap = 0 # unsigned number + +# (OC) The span for aligning Objective-C message specifications. +# +# 0: Don't align (default). +align_oc_msg_spec_span = 0 # unsigned number + +# Whether to align macros wrapped with a backslash and a newline. This will +# not work right if the macro contains a multi-line comment. +align_nl_cont = false # true/false + +# Whether to align macro functions and variables together. +align_pp_define_together = false # true/false + +# The span for aligning on '#define' bodies. +# +# =0: Don't align (default) +# >0: Number of lines (including comments) between blocks +align_pp_define_span = 0 # unsigned number + +# The minimum space between label and value of a preprocessor define. +align_pp_define_gap = 0 # unsigned number + +# Whether to align lines that start with '<<' with previous '<<'. +# +# Default: true +align_left_shift = true # true/false + +# Whether to align comma-separated statements following '<<' (as used to +# initialize Eigen matrices). +align_eigen_comma_init = false # true/false + +# Whether to align text after 'asm volatile ()' colons. +align_asm_colon = false # true/false + +# (OC) Span for aligning parameters in an Objective-C message call +# on the ':'. +# +# 0: Don't align. +align_oc_msg_colon_span = 0 # unsigned number + +# (OC) Whether to always align with the first parameter, even if it is too +# short. +align_oc_msg_colon_first = false # true/false + +# (OC) Whether to align parameters in an Objective-C '+' or '-' declaration +# on the ':'. +align_oc_decl_colon = false # true/false + +# (OC) Whether to not align parameters in an Objectve-C message call if first +# colon is not on next line of the message call (the same way Xcode does +# aligment) +align_oc_msg_colon_xcode_like = false # true/false + +# +# Comment modification options +# + +# Try to wrap comments at N columns. +cmt_width = 0 # unsigned number + +# How to reflow comments. +# +# 0: No reflowing (apart from the line wrapping due to cmt_width) (default) +# 1: No touching at all +# 2: Full reflow +cmt_reflow_mode = 0 # unsigned number + +# Whether to convert all tabs to spaces in comments. If false, tabs in +# comments are left alone, unless used for indenting. +cmt_convert_tab_to_spaces = true # true/false + +# Whether to apply changes to multi-line comments, including cmt_width, +# keyword substitution and leading chars. +# +# Default: true +cmt_indent_multi = true # true/false + +# Whether to group c-comments that look like they are in a block. +cmt_c_group = false # true/false + +# Whether to put an empty '/*' on the first line of the combined c-comment. +cmt_c_nl_start = false # true/false + +# Whether to add a newline before the closing '*/' of the combined c-comment. +cmt_c_nl_end = false # true/false + +# Whether to change cpp-comments into c-comments. +cmt_cpp_to_c = false # true/false + +# Whether to group cpp-comments that look like they are in a block. Only +# meaningful if cmt_cpp_to_c=true. +cmt_cpp_group = false # true/false + +# Whether to put an empty '/*' on the first line of the combined cpp-comment +# when converting to a c-comment. +# +# Requires cmt_cpp_to_c=true and cmt_cpp_group=true. +cmt_cpp_nl_start = false # true/false + +# Whether to add a newline before the closing '*/' of the combined cpp-comment +# when converting to a c-comment. +# +# Requires cmt_cpp_to_c=true and cmt_cpp_group=true. +cmt_cpp_nl_end = false # true/false + +# Whether to put a star on subsequent comment lines. +cmt_star_cont = false # true/false + +# The number of spaces to insert at the start of subsequent comment lines. +cmt_sp_before_star_cont = 0 # unsigned number + +# The number of spaces to insert after the star on subsequent comment lines. +cmt_sp_after_star_cont = 0 # unsigned number + +# For multi-line comments with a '*' lead, remove leading spaces if the first +# and last lines of the comment are the same length. +# +# Default: true +cmt_multi_check_last = true # true/false + +# For multi-line comments with a '*' lead, remove leading spaces if the first +# and last lines of the comment are the same length AND if the length is +# bigger as the first_len minimum. +# +# Default: 4 +cmt_multi_first_len_minimum = 4 # unsigned number + +# Path to a file that contains text to insert at the beginning of a file if +# the file doesn't start with a C/C++ comment. If the inserted text contains +# '$(filename)', that will be replaced with the current file's name. +cmt_insert_file_header = "" # string + +# Path to a file that contains text to insert at the end of a file if the +# file doesn't end with a C/C++ comment. If the inserted text contains +# '$(filename)', that will be replaced with the current file's name. +cmt_insert_file_footer = "" # string + +# Path to a file that contains text to insert before a function definition if +# the function isn't preceded by a C/C++ comment. If the inserted text +# contains '$(function)', '$(javaparam)' or '$(fclass)', these will be +# replaced with, respectively, the name of the function, the javadoc '@param' +# and '@return' stuff, or the name of the class to which the member function +# belongs. +cmt_insert_func_header = "" # string + +# Path to a file that contains text to insert before a class if the class +# isn't preceded by a C/C++ comment. If the inserted text contains '$(class)', +# that will be replaced with the class name. +cmt_insert_class_header = "" # string + +# Path to a file that contains text to insert before an Objective-C message +# specification, if the method isn't preceded by a C/C++ comment. If the +# inserted text contains '$(message)' or '$(javaparam)', these will be +# replaced with, respectively, the name of the function, or the javadoc +# '@param' and '@return' stuff. +cmt_insert_oc_msg_header = "" # string + +# Whether a comment should be inserted if a preprocessor is encountered when +# stepping backwards from a function name. +# +# Applies to cmt_insert_oc_msg_header, cmt_insert_func_header and +# cmt_insert_class_header. +cmt_insert_before_preproc = false # true/false + +# Whether a comment should be inserted if a function is declared inline to a +# class definition. +# +# Applies to cmt_insert_func_header. +# +# Default: true +cmt_insert_before_inlines = true # true/false + +# Whether a comment should be inserted if the function is a class constructor +# or destructor. +# +# Applies to cmt_insert_func_header. +cmt_insert_before_ctor_dtor = false # true/false + +# +# Code modifying options (non-whitespace) +# + +# Add or remove braces on a single-line 'do' statement. +mod_full_brace_do = ignore # ignore/add/remove/force + +# Add or remove braces on a single-line 'for' statement. +mod_full_brace_for = ignore # ignore/add/remove/force + +# (Pawn) Add or remove braces on a single-line function definition. +mod_full_brace_function = ignore # ignore/add/remove/force + +# Add or remove braces on a single-line 'if' statement. Braces will not be +# removed if the braced statement contains an 'else'. +mod_full_brace_if = ignore # ignore/add/remove/force + +# Whether to enforce that all blocks of an 'if'/'else if'/'else' chain either +# have, or do not have, braces. If true, braces will be added if any block +# needs braces, and will only be removed if they can be removed from all +# blocks. +# +# Overrides mod_full_brace_if. +mod_full_brace_if_chain = false # true/false + +# Whether to add braces to all blocks of an 'if'/'else if'/'else' chain. +# If true, mod_full_brace_if_chain will only remove braces from an 'if' that +# does not have an 'else if' or 'else'. +mod_full_brace_if_chain_only = false # true/false + +# Add or remove braces on single-line 'while' statement. +mod_full_brace_while = ignore # ignore/add/remove/force + +# Add or remove braces on single-line 'using ()' statement. +mod_full_brace_using = ignore # ignore/add/remove/force + +# Don't remove braces around statements that span N newlines +mod_full_brace_nl = 0 # unsigned number + +# Whether to prevent removal of braces from 'if'/'for'/'while'/etc. blocks +# which span multiple lines. +# +# Affects: +# mod_full_brace_for +# mod_full_brace_if +# mod_full_brace_if_chain +# mod_full_brace_if_chain_only +# mod_full_brace_while +# mod_full_brace_using +# +# Does not affect: +# mod_full_brace_do +# mod_full_brace_function +mod_full_brace_nl_block_rem_mlcond = false # true/false + +# Add or remove unnecessary parenthesis on 'return' statement. +mod_paren_on_return = ignore # ignore/add/remove/force + +# (Pawn) Whether to change optional semicolons to real semicolons. +mod_pawn_semicolon = false # true/false + +# Whether to fully parenthesize Boolean expressions in 'while' and 'if' +# statement, as in 'if (a && b > c)' => 'if (a && (b > c))'. +mod_full_paren_if_bool = false # true/false + +# Whether to remove superfluous semicolons. +mod_remove_extra_semicolon = false # true/false + +# If a function body exceeds the specified number of newlines and doesn't have +# a comment after the close brace, a comment will be added. +mod_add_long_function_closebrace_comment = 0 # unsigned number + +# If a namespace body exceeds the specified number of newlines and doesn't +# have a comment after the close brace, a comment will be added. +mod_add_long_namespace_closebrace_comment = 0 # unsigned number + +# If a class body exceeds the specified number of newlines and doesn't have a +# comment after the close brace, a comment will be added. +mod_add_long_class_closebrace_comment = 0 # unsigned number + +# If a switch body exceeds the specified number of newlines and doesn't have a +# comment after the close brace, a comment will be added. +mod_add_long_switch_closebrace_comment = 0 # unsigned number + +# If an #ifdef body exceeds the specified number of newlines and doesn't have +# a comment after the #endif, a comment will be added. +mod_add_long_ifdef_endif_comment = 0 # unsigned number + +# If an #ifdef or #else body exceeds the specified number of newlines and +# doesn't have a comment after the #else, a comment will be added. +mod_add_long_ifdef_else_comment = 0 # unsigned number + +# Whether to take care of the case by the mod_sort_xx options. +mod_sort_case_sensitive = false # true/false + +# Whether to sort consecutive single-line 'import' statements. +mod_sort_import = false # true/false + +# (C#) Whether to sort consecutive single-line 'using' statements. +mod_sort_using = false # true/false + +# Whether to sort consecutive single-line '#include' statements (C/C++) and +# '#import' statements (Objective-C). Be aware that this has the potential to +# break your code if your includes/imports have ordering dependencies. +mod_sort_include = false # true/false + +# Whether to prioritize '#include' and '#import' statements that contain +# filename without extension when sorting is enabled. +mod_sort_incl_import_prioritize_filename = false # true/false + +# Whether to prioritize '#include' and '#import' statements that does not +# contain extensions when sorting is enabled. +mod_sort_incl_import_prioritize_extensionless = false # true/false + +# Whether to prioritize '#include' and '#import' statements that contain +# angle over quotes when sorting is enabled. +mod_sort_incl_import_prioritize_angle_over_quotes = false # true/false + +# Whether to ignore file extension in '#include' and '#import' statements +# for sorting comparison. +mod_sort_incl_import_ignore_extension = false # true/false + +# Whether to group '#include' and '#import' statements when sorting is enabled. +mod_sort_incl_import_grouping_enabled = false # true/false + +# Whether to move a 'break' that appears after a fully braced 'case' before +# the close brace, as in 'case X: { ... } break;' => 'case X: { ... break; }'. +mod_move_case_break = false # true/false + +# Add or remove braces around a fully braced case statement. Will only remove +# braces if there are no variable declarations in the block. +mod_case_brace = ignore # ignore/add/remove/force + +# Whether to remove a void 'return;' that appears as the last statement in a +# function. +mod_remove_empty_return = false # true/false + +# Add or remove the comma after the last value of an enumeration. +mod_enum_last_comma = ignore # ignore/add/remove/force + +# (OC) Whether to organize the properties. If true, properties will be +# rearranged according to the mod_sort_oc_property_*_weight factors. +mod_sort_oc_properties = false # true/false + +# (OC) Weight of a class property modifier. +mod_sort_oc_property_class_weight = 0 # number + +# (OC) Weight of 'atomic' and 'nonatomic'. +mod_sort_oc_property_thread_safe_weight = 0 # number + +# (OC) Weight of 'readwrite' when organizing properties. +mod_sort_oc_property_readwrite_weight = 0 # number + +# (OC) Weight of a reference type specifier ('retain', 'copy', 'assign', +# 'weak', 'strong') when organizing properties. +mod_sort_oc_property_reference_weight = 0 # number + +# (OC) Weight of getter type ('getter=') when organizing properties. +mod_sort_oc_property_getter_weight = 0 # number + +# (OC) Weight of setter type ('setter=') when organizing properties. +mod_sort_oc_property_setter_weight = 0 # number + +# (OC) Weight of nullability type ('nullable', 'nonnull', 'null_unspecified', +# 'null_resettable') when organizing properties. +mod_sort_oc_property_nullability_weight = 0 # number + +# +# Preprocessor options +# + +# Add or remove indentation of preprocessor directives inside #if blocks +# at brace level 0 (file-level). +pp_indent = ignore # ignore/add/remove/force + +# Whether to indent #if/#else/#endif at the brace level. If false, these are +# indented from column 1. +pp_indent_at_level = false # true/false + +# Specifies the number of columns to indent preprocessors per level +# at brace level 0 (file-level). If pp_indent_at_level=false, also specifies +# the number of columns to indent preprocessors per level +# at brace level > 0 (function-level). +# +# Default: 1 +pp_indent_count = 1 # unsigned number + +# Add or remove space after # based on pp_level of #if blocks. +pp_space = ignore # ignore/add/remove/force + +# Sets the number of spaces per level added with pp_space. +pp_space_count = 0 # unsigned number + +# The indent for '#region' and '#endregion' in C# and '#pragma region' in +# C/C++. Negative values decrease indent down to the first column. +pp_indent_region = 0 # number + +# Whether to indent the code between #region and #endregion. +pp_region_indent_code = false # true/false + +# If pp_indent_at_level=true, sets the indent for #if, #else and #endif when +# not at file-level. Negative values decrease indent down to the first column. +# +# =0: Indent preprocessors using output_tab_size +# >0: Column at which all preprocessors will be indented +pp_indent_if = 0 # number + +# Whether to indent the code between #if, #else and #endif. +pp_if_indent_code = false # true/false + +# Whether to indent '#define' at the brace level. If false, these are +# indented from column 1. +pp_define_at_level = false # true/false + +# Whether to ignore the '#define' body while formatting. +pp_ignore_define_body = false # true/false + +# Whether to indent case statements between #if, #else, and #endif. +# Only applies to the indent of the preprocesser that the case statements +# directly inside of. +# +# Default: true +pp_indent_case = true # true/false + +# Whether to indent whole function definitions between #if, #else, and #endif. +# Only applies to the indent of the preprocesser that the function definition +# is directly inside of. +# +# Default: true +pp_indent_func_def = true # true/false + +# Whether to indent extern C blocks between #if, #else, and #endif. +# Only applies to the indent of the preprocesser that the extern block is +# directly inside of. +# +# Default: true +pp_indent_extern = true # true/false + +# Whether to indent braces directly inside #if, #else, and #endif. +# Only applies to the indent of the preprocesser that the braces are directly +# inside of. +# +# Default: true +pp_indent_brace = true # true/false + +# +# Sort includes options +# + +# The regex for include category with priority 0. +include_category_0 = "" # string + +# The regex for include category with priority 1. +include_category_1 = "" # string + +# The regex for include category with priority 2. +include_category_2 = "" # string + +# +# Use or Do not Use options +# + +# true: indent_func_call_param will be used (default) +# false: indent_func_call_param will NOT be used +# +# Default: true +use_indent_func_call_param = true # true/false + +# The value of the indentation for a continuation line is calculated +# differently if the statement is: +# - a declaration: your case with QString fileName ... +# - an assignment: your case with pSettings = new QSettings( ... +# +# At the second case the indentation value might be used twice: +# - at the assignment +# - at the function call (if present) +# +# To prevent the double use of the indentation value, use this option with the +# value 'true'. +# +# true: indent_continue will be used only once +# false: indent_continue will be used every time (default) +use_indent_continue_only_once = false # true/false + +# The value might be used twice: +# - at the assignment +# - at the opening brace +# +# To prevent the double use of the indentation value, use this option with the +# value 'true'. +# +# true: indentation will be used only once +# false: indentation will be used every time (default) +indent_cpp_lambda_only_once = true # true/false + +# Whether sp_after_angle takes precedence over sp_inside_fparen. This was the +# historic behavior, but is probably not the desired behavior, so this is off +# by default. +use_sp_after_angle_always = false # true/false + +# Whether to apply special formatting for Qt SIGNAL/SLOT macros. Essentially, +# this tries to format these so that they match Qt's normalized form (i.e. the +# result of QMetaObject::normalizedSignature), which can slightly improve the +# performance of the QObject::connect call, rather than how they would +# otherwise be formatted. +# +# See options_for_QT.cpp for details. +# +# Default: true +use_options_overriding_for_qt_macros = true # true/false + +# If true: the form feed character is removed from the list +# of whitespace characters. +# See https://en.cppreference.com/w/cpp/string/byte/isspace +use_form_feed_no_more_as_whitespace_character = false # true/false + +# +# Warn levels - 1: error, 2: warning (default), 3: note +# + +# (C#) Warning is given if doing tab-to-\t replacement and we have found one +# in a C# verbatim string literal. +# +# Default: 2 +warn_level_tabs_found_in_verbatim_string_literals = 2 # unsigned number + +# Limit the number of loops. +# Used by uncrustify.cpp to exit from infinite loop. +# 0: no limit. +debug_max_number_of_loops = 0 # number + +# Set the number of the line to protocol; +# Used in the function prot_the_line if the 2. parameter is zero. +# 0: nothing protocol. +debug_line_number_to_protocol = 0 # number + +# Set the number of second(s) before terminating formatting the current file, +# 0: no timeout. +# only for linux +debug_timeout = 0 # number + +# Meaning of the settings: +# Ignore - do not do any changes +# Add - makes sure there is 1 or more space/brace/newline/etc +# Force - makes sure there is exactly 1 space/brace/newline/etc, +# behaves like Add in some contexts +# Remove - removes space/brace/newline/etc +# +# +# - Token(s) can be treated as specific type(s) with the 'set' option: +# `set tokenType tokenString [tokenString...]` +# +# Example: +# `set BOOL __AND__ __OR__` +# +# tokenTypes are defined in src/token_enum.h, use them without the +# 'CT_' prefix: 'CT_BOOL' => 'BOOL' +# +# +# - Token(s) can be treated as type(s) with the 'type' option. +# `type tokenString [tokenString...]` +# +# Example: +# `type int c_uint_8 Rectangle` +# +# This can also be achieved with `set TYPE int c_uint_8 Rectangle` +# +# +# To embed whitespace in tokenStrings use the '\' escape character, or quote +# the tokenStrings. These quotes are supported: "'` +# +# +# - Support for the auto detection of languages through the file ending can be +# added using the 'file_ext' command. +# `file_ext langType langString [langString..]` +# +# Example: +# `file_ext CPP .ch .cxx .cpp.in` +# +# langTypes are defined in uncrusify_types.h in the lang_flag_e enum, use +# them without the 'LANG_' prefix: 'LANG_CPP' => 'CPP' +# +# +# - Custom macro-based indentation can be set up using 'macro-open', +# 'macro-else' and 'macro-close'. +# `(macro-open | macro-else | macro-close) tokenString` +# +# Example: +# `macro-open BEGIN_TEMPLATE_MESSAGE_MAP` +# `macro-open BEGIN_MESSAGE_MAP` +# `macro-close END_MESSAGE_MAP` +# +# +# option(s) with 'not default' value: 24 +# diff --git a/cppcheck-2.14.0/AUTHORS b/cppcheck-2.14.0/AUTHORS new file mode 100644 index 00000000..8865e1e7 --- /dev/null +++ b/cppcheck-2.14.0/AUTHORS @@ -0,0 +1,408 @@ +The cppcheck team, in alphabetical order: + +0x41head +Abhijit Sawant +Abhishek Bharadwaj +Abigail Buccaneer +Adam J Richter +Adrien Chardon +Ahti Legonkov +Akhilesh Nema +Akio Idehara +Albert Aribaud +Aleksandr Pikalev +Aleksey Palazhchenko +Alexander Alekseev +Alexander Festini +Alexander Gushchin +Alexander Mai +Alexander Tkachev +Alexandre Chouvellon +Alexey Eryomenko +Alexey Zhikhartsev +Alfi Maulana +Ali Can Demiralp +Alon Alexander +Alon Liberman +Ameen Ali +Andreas Bacher +Andreas Bießmann +Andreas Grob +Andreas Pokorny +Andreas Rönnquist +Andreas Vollenweider +Andrei Karas +Andrew C Aitchison +Andrew C. Martin +Andrew D. Bancroft +Andy Holmes +Andy Maloney +Andy Mac Gregor +Aneesh Azhakesan S +Ankita Gupta +Anton Lindqvist +Antti Tuppurainen +Anurag Garg +Armin Müller +Arpit Chaudhary +August Sodora +Ayaz Salikhov +Balázs Tóth +Baris Demiray +Bart vdr. Meulen +Bartlomiej Grzeskowiak +bbennetts +Benjamin Bannier +Benjamin Fovet +Benjamin Goose +Benjamin Kramer +Benjamin Woester +Benjamin Wolsey +Ben T +Bernd Buschinski +Bill Egert +Björge Dijkstra +booga +Boris Barbulovski +Boris Egorov +Boussaffa Walid +Bo Rydberg +bzgec +Carl Michael Grüner Monzón +Carl Morgan +Carlo Marcelo Arenas Belón +Carlos Gomes Martinho +Carl-Oskar Larsson +Cary R +Changkyoon Kim +Chris Lalancette +Christian Ehrlicher +Christian Franke +Christoph Grüninger +Christoph Schmidt +Christoph Strehle +Chuck Larson +Cilyan Olowen +Claus Jensby Madsen +Colomban Wendling +Conrado Gouvea +daisuke-chiba +Daniel Friedrich +David Korczynski +Daniel Marjamäki +David Hallas +David Korth +Dávid Slivka +Debrard Sebastien +Deepak Gupta +Degen's Regens +dencat +Diego de las Heras +Dirk Jagdmann +Dirk Mueller +Dmitriy +Dmitry Marakasov +Dmitry-Me +dsamo +Duraffort +Edoardo Prezioso +Eivind Tagseth +Elbert Pol +Emmanuel Blot +Eric Lemanissier +Eric Malenfant +Eric Sesterhenn +Erik Hovland +Erik Lax +Ettl Martin +Even Rouault +Evgeny Mandrikov +Felipe Pena +Felix Geyer +Felix Passenberg +Felix Wolff +Florin Iucha +Francesc Elies +Frank Zingsheim +Frederik Schwarzer +fu7mu4 +Galimov Albert +Garrett Bodily +Gary Leutheuser +gaurav kaushik +Gennady Feldman +Georgi D. Sotirov +Georgy Komarov +Gerbo Engels +Gerhard Zlabinger +Gerik Rhoden +Gianfranco Costamagna +Gianluca Scacco +Gleydson Soares +Goran Džaferi +Graham Whitted +Greg Hewgill +Guillaume A. +Guillaume Chauvel +Guillaume Miossec +Gustav Palmqvist +Günther Makulik +Haowei Hsu +Harald Scheidl +Heiko Bauke +Heiko Eißfeldt +Heinrich Schuchardt +Henrik Nilsson +He Yuqi +Hoang Tuan Su +Igor Rondarev +Igor Zhukov +Ilya Shipitsin +Ivan Maidanski +Iván Matellanes +Ivan Ryabov +Ivar Bonsaksen +Jakub Melka +Jan Egil Ruud +Jan Hellwig +János Maros +Jay Sigbrandt +Jedrzej Klocek +Jens Bäckman +Jens Yllman +Jérémy Lefaure +Jes Ramsing +Jesse Boswell +Jim Kuhn +Jim Zhou +jlguardi +Johan Bertrand +Johan Samuelson +John Marshall +John-Paul Ore +John Smits +Jonathan Clohessy +Jonathan Haehne +Jonathan Neuschäfer +Jonathan Thackray +José Martins +Jose Roquette +Joshua Beck +Joshua Rogers +Julian Santander +Julien Marrec +Julien Peyregne +Jure Menart +Jussi Lehtola +Jørgen Kvalsvik +Kamil Dudka +Kartik Bajaj +Kefu Chai +keinflue +Ken-Patrick Lehrmann +Ketil Skjerve +Kevin Christian +Kevin Kendzia +Kimmo Varis +Kleber Tarcísio +Konrad Grochowski +Konrad Windszus +Kumar Ashwani +Kushal Chandar +Kyle Chisholm +Lars Even Almaas +larudwer +Lau bakman +Lauri Nurmi +Leandro Lisboa Penz +Leila F. Rahman +Lena Herscheid +Leon De Andrade +Lieven de Cock +lioncash +Lionel Gimbert +Lucas Manuel Rodriguez +Luis Díaz Más +Lukas Grützmacher +Lukasz Czajczyk +Łukasz Jankowski +Luxon Jean-Pierre +Maarten van der Schrieck +Maksim Derbasov +Malcolm Parsons +Marc-Antoine Perennou +Marcel Raad +Marco Trevisan +Marek Zmysłowski +Marian Klymov +Mark de Wever +Mark Hermeling +Markus Elfring +Martin Delille +Martin Ettl +Martin Exner +Martin Güthle +Martin Herren +Márton Csordás +Masafumi Koba +Massimo Paladin +Mateusz Michalak +Mateusz Pusz +Mathias De Maré +Mathias Schmid +Matthias Krüger +Matthias Kuhn +Matthias Schmieder +Matt Johnson +Maurice Gilden +Mavik +Michael Drake +Michael Løiten +Miika-Petteri Matikainen +Mika Attila +Mike Tzou +Milhan Kim +Mil Tolstoy +Mischa Aster Alff +Mohit Mate +Monika Lukow +Moritz Barsnick +Moritz Lipp +Moshe Kaplan +ms +Neszt Tibor +Nguyen Duong Tuan +Ni2c2k +Nick Ridgway +Nicolás Alvarez +Nicolas Le Cam +Nilesh Kumar +Ogawa KenIchi +Oleksandr Redko +Oliver Schode +Oliver Stöneberg +Olivier Croquette +Patrick Oppenlander +Paul Aitken +Paul Bersee +Paul Fultz II +Pavel Bibergal +Pavel Pimenov +Pavel Roschin +Pavel Skipenes +Pavel Šimovec +Pavol Misik +Pete Johns +Peter Pentchev +Peter Schops +Philip Chimento +Philipp Kloke +Pierre Schweitzer +Pino Toscano +Pranav Khanna +Radek Jarecki +Rainer Wiesenfarth +Ramzan Bekbulatov +Raphael Geissert +Razvan Ioan Alexe +Reijo Tomperi +Rainer Wiesenfarth +Riccardo Ghetta +Richard A. Smith +Richard Quirk +Rick van der Sluijs +Rikard Falkeborn +rivdsl +Robert Habrich +Robert Morin +Roberto Martelloni +Robert Reif +rofl0r +Roman Zaytsev Borisovich +Ronald Hiemstra +root +Rosen Penev +Rudi Danner +Rudolf Grauberger +Ryan M. Lederman +Ryan Pavlik +Samir Aguiar +Sam Truscott +Samuel Degrande +Samuel Poláček +Sandeep Dutta +Savvas Etairidis +Scott Furry +Sebastian Held +Sebastian Matuschka +Sébastien Debrard +Sergei Chernykh +Sergei Trofimovich +Sergey Burgsdorf +Shane Tapp +Shohei YOSHIDA +Simon Cornell +Simon Kagstrom +Simon Large +Simon Martin +Simon Shanks +Slava Semushin +Stas Cymbalov +Stefan Beller +Stefan Hagen +Stefan Naewe +Stefan van Kessel +Stefan Weil +Stéphane Michel +Steve Browne +Steve Duan +Steve Mokris +Steven Cook +Steven Myint +Susi Lehtola +Swasti Shrivastava +Sylvain Joubert +Tam Do Thanh +Teddy Didé +Thomas Arnhold +Tomasz Edward Posluszny +Thomas Jarosch +Thomas Niederberger +Thomas Otto +Thomas P. K. Healy +Thomas Sondergaard +Thorsten Sick +Tim Blume +Tim Gerundt +tititiou36 +Tobias Weibel +Tomasz Kłoczko +Tom Pollok +Tomo Dote +Toralf Förster +Troshin V.S. +Tyson Nottingham +Valentin Batz +Valerii Lashmanov +Vasily Maslyukov +Veli-Matti Visuri +Vesa Pikki +Ville-Pekka Vahteala +Ville Skyttä +Vincent Le Garrec +Wang Haoyu +WenChung Chiu +Wolfgang Stöggl +x29a +XhmikosR +Xuecheng Zhang +Yichen Yan +Yurii Putin +Zachary Blair +Zhao Qifa +Zhiyuan Zhang +Zhu Lei +Дмитрий Старцев + +GUI graphics courtesy of Tango Desktop Project: +http://tango.freedesktop.org diff --git a/cppcheck-2.14.0/CMakeLists.txt b/cppcheck-2.14.0/CMakeLists.txt new file mode 100644 index 00000000..f67a7691 --- /dev/null +++ b/cppcheck-2.14.0/CMakeLists.txt @@ -0,0 +1,105 @@ +cmake_minimum_required(VERSION 3.5) +if (MSVC) + cmake_minimum_required(VERSION 3.13) +endif() +cmake_policy(SET CMP0048 NEW) # allow VERSION in project() +project(Cppcheck VERSION 2.13.99 LANGUAGES CXX) + +include(cmake/cxx11.cmake) +use_cxx11() +set (CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +include(GNUInstallDirs) + +include(cmake/compilerCheck.cmake) +include(cmake/versions.cmake) +include(cmake/options.cmake) +include(cmake/findDependencies.cmake) +include(cmake/compileroptions.cmake) +include(cmake/compilerDefinitions.cmake) +include(cmake/buildFiles.cmake) +include(cmake/printInfo.cmake) +if(BUILD_GUI) + include(cmake/qtCompat.cmake) +endif() + + +file(GLOB addons "addons/*.py") +file(GLOB cfgs "cfg/*.cfg") +file(GLOB platforms "platforms/*.xml") + +if(LIBXML2_XMLLINT_EXECUTABLE) + add_custom_target(validateCFG DEPENDS validateCFG-cmd) + add_custom_command(OUTPUT validateCFG-cmd + COMMAND ${LIBXML2_XMLLINT_EXECUTABLE} --noout ${CMAKE_SOURCE_DIR}/cfg/cppcheck-cfg.rng) + foreach(cfg ${cfgs}) + add_custom_command(OUTPUT validateCFG-cmd APPEND + COMMAND ${LIBXML2_XMLLINT_EXECUTABLE} --noout --relaxng ${CMAKE_SOURCE_DIR}/cfg/cppcheck-cfg.rng ${cfg}) + endforeach() + # this is a symbolic name for a build rule and not an output file + set_source_files_properties(validateCFG-cmd PROPERTIES SYMBOLIC "true") + + add_custom_target(validatePlatforms ${LIBXML2_XMLLINT_EXECUTABLE} --noout ${CMAKE_SOURCE_DIR}/platforms/cppcheck-platforms.rng) + foreach(platform ${platforms}) + get_filename_component(platformname ${platform} NAME_WE) + add_custom_target(validatePlatforms-${platformname} ${LIBXML2_XMLLINT_EXECUTABLE} --noout --relaxng ${CMAKE_SOURCE_DIR}/platforms/cppcheck-platforms.rng ${platform}) + add_dependencies(validatePlatforms validatePlatforms-${platformname}) + endforeach() + + add_custom_target(errorlist-xml $ --errorlist > ${CMAKE_BINARY_DIR}/errorlist.xml + DEPENDS cppcheck) + + add_custom_target(example-xml $ --xml --enable=all --inconclusive --max-configs=1 ${CMAKE_SOURCE_DIR}/samples 2> ${CMAKE_BINARY_DIR}/example.xml + DEPENDS cppcheck) + + add_custom_target(createXMLExamples DEPENDS errorlist-xml example-xml) + + if(Python_EXECUTABLE) + add_custom_target(checkCWEEntries ${Python_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/listErrorsWithoutCWE.py -F ${CMAKE_BINARY_DIR}/errorlist.xml + DEPENDS errorlist-xml) + endif() + + add_custom_target(validateXML ${LIBXML2_XMLLINT_EXECUTABLE} --noout ${CMAKE_SOURCE_DIR}/cppcheck-errors.rng + COMMAND ${LIBXML2_XMLLINT_EXECUTABLE} --noout --relaxng ${CMAKE_SOURCE_DIR}/cppcheck-errors.rng ${CMAKE_BINARY_DIR}/errorlist.xml + COMMAND ${LIBXML2_XMLLINT_EXECUTABLE} --noout --relaxng ${CMAKE_SOURCE_DIR}/cppcheck-errors.rng ${CMAKE_BINARY_DIR}/example.xml + DEPENDS createXMLExamples + ) + + add_custom_target(validateRules ${LIBXML2_XMLLINT_EXECUTABLE} --noout ${CMAKE_SOURCE_DIR}/rules/*.xml) +endif() + +if(BUILD_TESTS) + enable_testing() +endif() + +add_custom_target(copy_cfg ALL + ${CMAKE_COMMAND} -E copy_directory "${PROJECT_SOURCE_DIR}/cfg" + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/cfg" + COMMENT "Copying cfg files to ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}") + +add_custom_target(copy_addons ALL + ${CMAKE_COMMAND} -E copy_directory "${PROJECT_SOURCE_DIR}/addons" + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/addons" + COMMENT "Copying addons files to ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}") + +add_custom_target(copy_platforms ALL + ${CMAKE_COMMAND} -E copy_directory "${PROJECT_SOURCE_DIR}/platforms" + "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/platforms" + COMMENT "Copying platforms files to ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}") + +if(USE_BUNDLED_TINYXML2) + message(STATUS "Using bundled version of tinyxml2") + add_subdirectory(externals/tinyxml2) +endif() +add_subdirectory(externals/simplecpp) +add_subdirectory(lib) # CppCheck Library +add_subdirectory(cli) # Client application +add_subdirectory(test) # Tests +add_subdirectory(gui) # Graphical application +add_subdirectory(tools/triage) # Triage tool +add_subdirectory(tools) +add_subdirectory(man) + +include(cmake/clang_tidy.cmake) diff --git a/cppcheck-2.14.0/COPYING b/cppcheck-2.14.0/COPYING new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/cppcheck-2.14.0/COPYING @@ -0,0 +1,674 @@ + 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 +. diff --git a/cppcheck-2.14.0/Makefile b/cppcheck-2.14.0/Makefile new file mode 100644 index 00000000..c97ea02f --- /dev/null +++ b/cppcheck-2.14.0/Makefile @@ -0,0 +1,902 @@ +# This file is generated by dmake, do not edit. + +ifndef VERBOSE + VERBOSE= +endif +# To compile with rules, use 'make HAVE_RULES=yes' +ifndef HAVE_RULES + HAVE_RULES= +endif + +ifndef MATCHCOMPILER + MATCHCOMPILER= +endif +# use match compiler +ifeq ($(MATCHCOMPILER),yes) + # Find available Python interpreter + ifeq ($(PYTHON_INTERPRETER),) + PYTHON_INTERPRETER := $(shell which python3) + endif + ifeq ($(PYTHON_INTERPRETER),) + PYTHON_INTERPRETER := $(shell which python) + endif + ifeq ($(PYTHON_INTERPRETER),) + $(error Did not find a Python interpreter) + endif + ifdef VERIFY + matchcompiler_S := $(shell $(PYTHON_INTERPRETER) tools/matchcompiler.py --verify) + else + matchcompiler_S := $(shell $(PYTHON_INTERPRETER) tools/matchcompiler.py) + endif + libcppdir:=build +else ifeq ($(MATCHCOMPILER),) + libcppdir:=lib +else + $(error invalid MATCHCOMPILER value '$(MATCHCOMPILER)') +endif + +ifndef CPPFLAGS + CPPFLAGS= +endif + +ifdef FILESDIR + CPPFLAGS+=-DFILESDIR=\"$(FILESDIR)\" +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 + ifeq ($(VERBOSE),1) + $(info COMSPEC not found) + endif + ifdef ComSpec + ifeq ($(VERBOSE),1) + $(info ComSpec found) + endif + #### ComSpec is defined on some WIN32's. + WINNT=1 + + ifeq ($(VERBOSE),1) + $(info PATH=$(PATH)) + endif + + ifneq (,$(findstring /cygdrive/,$(PATH))) + ifeq ($(VERBOSE),1) + $(info /cygdrive/ found in PATH) + endif + CYGWIN=1 + endif # CYGWIN + endif # ComSpec +endif # COMSPEC + +ifdef WINNT + ifeq ($(VERBOSE),1) + $(info WINNT found) + endif + #### Maybe Windows + ifndef CPPCHK_GLIBCXX_DEBUG + CPPCHK_GLIBCXX_DEBUG= + endif # !CPPCHK_GLIBCXX_DEBUG + + ifeq ($(VERBOSE),1) + $(info MSYSTEM=$(MSYSTEM)) + endif + + ifneq ($(MSYSTEM),MINGW32 MINGW64) + RDYNAMIC= + endif + + LDFLAGS+=-lshlwapi +else # !WINNT + ifeq ($(VERBOSE),1) + $(info WINNT not found) + endif + + uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not') + + ifeq ($(VERBOSE),1) + $(info uname_S=$(uname_S)) + endif + + 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 + + LDFLAGS+=-pthread + +endif # WINNT + +ifdef CYGWIN + ifeq ($(VERBOSE),1) + $(info CYGWIN found) + endif + + # Set the flag to address compile time warnings + # with tinyxml2 and Cygwin. + CPPFLAGS+=-U__STRICT_ANSI__ + + # Increase stack size for Cygwin builds to avoid segmentation fault in limited recursive tests. + CXXFLAGS+=-Wl,--stack,8388608 +endif # CYGWIN + +ifndef CXX + CXX=g++ +endif + +ifeq (clang++, $(findstring clang++,$(CXX))) + CPPCHK_GLIBCXX_DEBUG= +endif +ifndef CXXFLAGS + CXXFLAGS=-std=c++0x -O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-multichar +endif + +ifeq (g++, $(findstring g++,$(CXX))) + override CXXFLAGS += -std=gnu++0x -pipe +else ifeq (clang++, $(findstring clang++,$(CXX))) + override CXXFLAGS += -std=c++0x +else ifeq ($(CXX), c++) + ifeq ($(shell uname -s), Darwin) + override CXXFLAGS += -std=c++0x + endif +endif + +ifeq ($(HAVE_RULES),yes) + PCRE_CONFIG = $(shell which pcre-config) + ifeq ($(PCRE_CONFIG),) + $(error Did not find pcre-config) + endif + override CXXFLAGS += -DHAVE_RULES $(shell $(PCRE_CONFIG) --cflags) + ifdef LIBS + LIBS += $(shell $(PCRE_CONFIG) --libs) + else + LIBS=$(shell $(PCRE_CONFIG) --libs) + endif +else ifneq ($(HAVE_RULES),) + $(error invalid HAVE_RULES value '$(HAVE_RULES)') +endif + +ifndef PREFIX + PREFIX=/usr +endif + +ifndef INCLUDE_FOR_LIB + INCLUDE_FOR_LIB=-Ilib -isystem externals -isystem externals/picojson -isystem externals/simplecpp -isystem externals/tinyxml2 +endif + +ifndef INCLUDE_FOR_CLI + INCLUDE_FOR_CLI=-Ilib -isystem externals/simplecpp -isystem externals/tinyxml2 +endif + +ifndef INCLUDE_FOR_TEST + INCLUDE_FOR_TEST=-Ilib -Icli -isystem externals/simplecpp -isystem externals/tinyxml2 +endif + +BIN=$(DESTDIR)$(PREFIX)/bin + +# For 'make man': sudo apt-get install xsltproc docbook-xsl docbook-xml on Linux +DB2MAN?=/usr/share/sgml/docbook/stylesheet/xsl/nwalsh/manpages/docbook.xsl +XP=xsltproc -''-nonet -''-param man.charmap.use.subset "0" +MAN_SOURCE=man/cppcheck.1.xml + + +###### Object Files + +LIBOBJ = $(libcppdir)/valueflow.o \ + $(libcppdir)/tokenize.o \ + $(libcppdir)/symboldatabase.o \ + $(libcppdir)/addoninfo.o \ + $(libcppdir)/analyzerinfo.o \ + $(libcppdir)/astutils.o \ + $(libcppdir)/check.o \ + $(libcppdir)/check64bit.o \ + $(libcppdir)/checkassert.o \ + $(libcppdir)/checkautovariables.o \ + $(libcppdir)/checkbool.o \ + $(libcppdir)/checkboost.o \ + $(libcppdir)/checkbufferoverrun.o \ + $(libcppdir)/checkclass.o \ + $(libcppdir)/checkcondition.o \ + $(libcppdir)/checkers.o \ + $(libcppdir)/checkersreport.o \ + $(libcppdir)/checkexceptionsafety.o \ + $(libcppdir)/checkfunctions.o \ + $(libcppdir)/checkinternal.o \ + $(libcppdir)/checkio.o \ + $(libcppdir)/checkleakautovar.o \ + $(libcppdir)/checkmemoryleak.o \ + $(libcppdir)/checknullpointer.o \ + $(libcppdir)/checkother.o \ + $(libcppdir)/checkpostfixoperator.o \ + $(libcppdir)/checksizeof.o \ + $(libcppdir)/checkstl.o \ + $(libcppdir)/checkstring.o \ + $(libcppdir)/checktype.o \ + $(libcppdir)/checkuninitvar.o \ + $(libcppdir)/checkunusedfunctions.o \ + $(libcppdir)/checkunusedvar.o \ + $(libcppdir)/checkvaarg.o \ + $(libcppdir)/clangimport.o \ + $(libcppdir)/color.o \ + $(libcppdir)/cppcheck.o \ + $(libcppdir)/ctu.o \ + $(libcppdir)/errorlogger.o \ + $(libcppdir)/errortypes.o \ + $(libcppdir)/forwardanalyzer.o \ + $(libcppdir)/fwdanalysis.o \ + $(libcppdir)/importproject.o \ + $(libcppdir)/infer.o \ + $(libcppdir)/keywords.o \ + $(libcppdir)/library.o \ + $(libcppdir)/mathlib.o \ + $(libcppdir)/path.o \ + $(libcppdir)/pathanalysis.o \ + $(libcppdir)/pathmatch.o \ + $(libcppdir)/platform.o \ + $(libcppdir)/preprocessor.o \ + $(libcppdir)/programmemory.o \ + $(libcppdir)/reverseanalyzer.o \ + $(libcppdir)/settings.o \ + $(libcppdir)/summaries.o \ + $(libcppdir)/suppressions.o \ + $(libcppdir)/templatesimplifier.o \ + $(libcppdir)/timer.o \ + $(libcppdir)/token.o \ + $(libcppdir)/tokenlist.o \ + $(libcppdir)/utils.o \ + $(libcppdir)/vfvalue.o + +EXTOBJ = externals/simplecpp/simplecpp.o \ + externals/tinyxml2/tinyxml2.o + +CLIOBJ = cli/cmdlineparser.o \ + cli/cppcheckexecutor.o \ + cli/cppcheckexecutorseh.o \ + cli/executor.o \ + cli/filelister.o \ + cli/main.o \ + cli/processexecutor.o \ + cli/signalhandler.o \ + cli/singleexecutor.o \ + cli/stacktrace.o \ + cli/threadexecutor.o + +TESTOBJ = test/fixture.o \ + test/helpers.o \ + test/main.o \ + test/options.o \ + test/test64bit.o \ + test/testanalyzerinformation.o \ + test/testassert.o \ + test/testastutils.o \ + test/testautovariables.o \ + test/testbool.o \ + test/testboost.o \ + test/testbufferoverrun.o \ + test/testcharvar.o \ + test/testcheck.o \ + test/testclangimport.o \ + test/testclass.o \ + test/testcmdlineparser.o \ + test/testcolor.o \ + test/testcondition.o \ + test/testconstructors.o \ + test/testcppcheck.o \ + test/testerrorlogger.o \ + test/testexceptionsafety.o \ + test/testfilelister.o \ + test/testfunctions.o \ + test/testgarbage.o \ + test/testimportproject.o \ + test/testincompletestatement.o \ + test/testinternal.o \ + test/testio.o \ + test/testleakautovar.o \ + test/testlibrary.o \ + test/testmathlib.o \ + test/testmemleak.o \ + test/testnullpointer.o \ + test/testoptions.o \ + test/testother.o \ + test/testpath.o \ + test/testpathmatch.o \ + test/testplatform.o \ + test/testpostfixoperator.o \ + test/testpreprocessor.o \ + test/testprocessexecutor.o \ + test/testsettings.o \ + test/testsimplifytemplate.o \ + test/testsimplifytokens.o \ + test/testsimplifytypedef.o \ + test/testsimplifyusing.o \ + test/testsingleexecutor.o \ + test/testsizeof.o \ + test/teststl.o \ + test/teststring.o \ + test/testsummaries.o \ + test/testsuppressions.o \ + test/testsymboldatabase.o \ + test/testthreadexecutor.o \ + test/testtimer.o \ + test/testtoken.o \ + test/testtokenize.o \ + test/testtokenlist.o \ + test/testtokenrange.o \ + test/testtype.o \ + test/testuninitvar.o \ + test/testunusedfunctions.o \ + test/testunusedprivfunc.o \ + test/testunusedvar.o \ + test/testutils.o \ + test/testvaarg.o \ + test/testvalueflow.o \ + test/testvarid.o + +.PHONY: run-dmake tags + + +###### Targets + +cppcheck: $(EXTOBJ) $(LIBOBJ) $(CLIOBJ) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC) + +all: cppcheck testrunner + +testrunner: $(EXTOBJ) $(TESTOBJ) $(LIBOBJ) cli/executor.o cli/processexecutor.o cli/singleexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/cppcheckexecutorseh.o cli/signalhandler.o cli/stacktrace.o cli/filelister.o + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC) + +test: all + ./testrunner + +check: all + ./testrunner -q + +checkcfg: cppcheck validateCFG + ./test/cfg/runtests.sh + +dmake: tools/dmake/dmake.o cli/filelister.o $(libcppdir)/pathmatch.o $(libcppdir)/path.o $(libcppdir)/utils.o externals/simplecpp/simplecpp.o + $(CXX) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) + +run-dmake: dmake + ./dmake --release + +clean: + rm -f build/*.cpp build/*.o lib/*.o cli/*.o test/*.o tools/*.o externals/*/*.o testrunner dmake cppcheck cppcheck.exe cppcheck.1 + +man: man/cppcheck.1 + +man/cppcheck.1: $(MAN_SOURCE) + + $(XP) $(DB2MAN) $(MAN_SOURCE) + +tags: + ctags -R --exclude=doxyoutput --exclude=test/cfg cli externals gui lib test + +install: cppcheck + install -d ${BIN} + install cppcheck ${BIN} + install htmlreport/cppcheck-htmlreport ${BIN} +ifdef FILESDIR + install -d ${DESTDIR}${FILESDIR} + install -d ${DESTDIR}${FILESDIR}/addons + install -m 644 addons/*.py ${DESTDIR}${FILESDIR}/addons + install -d ${DESTDIR}${FILESDIR}/cfg + install -m 644 cfg/*.cfg ${DESTDIR}${FILESDIR}/cfg + install -d ${DESTDIR}${FILESDIR}/platforms + install -m 644 platforms/*.xml ${DESTDIR}${FILESDIR}/platforms +else + $(error FILESDIR must be set!) +endif + +uninstall: + @if test -d ${BIN}; then \ + files="cppcheck cppcheck-htmlreport"; \ + echo '(' cd ${BIN} '&&' rm -f $$files ')'; \ + ( cd ${BIN} && rm -f $$files ); \ + fi +ifdef FILESDIR + @if test -d ${DESTDIR}${FILESDIR}; then \ + echo rm -rf ${DESTDIR}${FILESDIR}; \ + rm -rf ${DESTDIR}${FILESDIR}; \ + fi +endif +ifdef CFGDIR + @if test -d ${DESTDIR}${CFGDIR}; then \ + files="`cd cfg 2>/dev/null && ls`"; \ + if test -n "$$files"; then \ + echo '(' cd ${DESTDIR}${CFGDIR} '&&' rm -f $$files ')'; \ + ( cd ${DESTDIR}${CFGDIR} && rm -f $$files ); \ + fi; \ + fi +endif + +# Validation of library files: +ConfigFiles := $(wildcard cfg/*.cfg) +ConfigFilesCHECKED := $(patsubst %.cfg,%.checked,$(ConfigFiles)) +.PHONY: validateCFG +%.checked:%.cfg + xmllint --noout --relaxng cfg/cppcheck-cfg.rng $< +validateCFG: ${ConfigFilesCHECKED} + xmllint --noout cfg/cppcheck-cfg.rng + +# Validation of platforms files: +PlatformFiles := $(wildcard platforms/*.xml) +PlatformFilesCHECKED := $(patsubst %.xml,%.checked,$(PlatformFiles)) +.PHONY: validatePlatforms +%.checked:%.xml + xmllint --noout --relaxng platforms/cppcheck-platforms.rng $< +validatePlatforms: ${PlatformFilesCHECKED} + xmllint --noout platforms/cppcheck-platforms.rng + +# Validate XML output (to detect regressions) +/tmp/errorlist.xml: cppcheck + ./cppcheck --errorlist >$@ +/tmp/example.xml: cppcheck + ./cppcheck --xml --enable=all --inconclusive --max-configs=1 samples 2>/tmp/example.xml +createXMLExamples:/tmp/errorlist.xml /tmp/example.xml +.PHONY: validateXML +validateXML: createXMLExamples + xmllint --noout cppcheck-errors.rng + xmllint --noout --relaxng cppcheck-errors.rng /tmp/errorlist.xml + xmllint --noout --relaxng cppcheck-errors.rng /tmp/example.xml + +checkCWEEntries: /tmp/errorlist.xml + $(eval PYTHON_INTERPRETER := $(if $(PYTHON_INTERPRETER),$(PYTHON_INTERPRETER),$(shell which python3))) + $(eval PYTHON_INTERPRETER := $(if $(PYTHON_INTERPRETER),$(PYTHON_INTERPRETER),$(shell which python))) + $(eval PYTHON_INTERPRETER := $(if $(PYTHON_INTERPRETER),$(PYTHON_INTERPRETER),$(error Did not find a Python interpreter))) + $(PYTHON_INTERPRETER) tools/listErrorsWithoutCWE.py -F /tmp/errorlist.xml +.PHONY: validateRules +validateRules: + xmllint --noout rules/*.xml + +###### Build + +$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/addoninfo.h lib/analyzer.h lib/astutils.h lib/calculate.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/findtoken.h lib/forwardanalyzer.h lib/infer.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/valueflow.cpp + +$(libcppdir)/tokenize.o: lib/tokenize.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/summaries.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/tokenize.cpp + +$(libcppdir)/symboldatabase.o: lib/symboldatabase.cpp lib/addoninfo.h lib/astutils.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/keywords.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/symboldatabase.cpp + +$(libcppdir)/addoninfo.o: lib/addoninfo.cpp externals/picojson/picojson.h lib/addoninfo.h lib/config.h lib/json.h lib/path.h lib/standards.h lib/utils.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/addoninfo.cpp + +$(libcppdir)/analyzerinfo.o: lib/analyzerinfo.cpp externals/tinyxml2/tinyxml2.h lib/analyzerinfo.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/path.h lib/platform.h lib/standards.h lib/utils.h lib/xml.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/analyzerinfo.cpp + +$(libcppdir)/astutils.o: lib/astutils.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkclass.h lib/config.h lib/errortypes.h lib/findtoken.h lib/infer.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/astutils.cpp + +$(libcppdir)/check.o: lib/check.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/check.cpp + +$(libcppdir)/check64bit.o: lib/check64bit.cpp lib/addoninfo.h lib/check.h lib/check64bit.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/check64bit.cpp + +$(libcppdir)/checkassert.o: lib/checkassert.cpp lib/addoninfo.h lib/check.h lib/checkassert.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkassert.cpp + +$(libcppdir)/checkautovariables.o: lib/checkautovariables.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkautovariables.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkautovariables.cpp + +$(libcppdir)/checkbool.o: lib/checkbool.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkbool.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkbool.cpp + +$(libcppdir)/checkboost.o: lib/checkboost.cpp lib/check.h lib/checkboost.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkboost.cpp + +$(libcppdir)/checkbufferoverrun.o: lib/checkbufferoverrun.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/astutils.h lib/check.h lib/checkbufferoverrun.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h lib/xml.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkbufferoverrun.cpp + +$(libcppdir)/checkclass.o: lib/checkclass.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/astutils.h lib/check.h lib/checkclass.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h lib/xml.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkclass.cpp + +$(libcppdir)/checkcondition.o: lib/checkcondition.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkcondition.h lib/checkother.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkcondition.cpp + +$(libcppdir)/checkers.o: lib/checkers.cpp lib/checkers.h lib/config.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkers.cpp + +$(libcppdir)/checkersreport.o: lib/checkersreport.cpp lib/addoninfo.h lib/checkers.h lib/checkersreport.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkersreport.cpp + +$(libcppdir)/checkexceptionsafety.o: lib/checkexceptionsafety.cpp lib/addoninfo.h lib/check.h lib/checkexceptionsafety.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkexceptionsafety.cpp + +$(libcppdir)/checkfunctions.o: lib/checkfunctions.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkfunctions.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkfunctions.cpp + +$(libcppdir)/checkinternal.o: lib/checkinternal.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkinternal.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkinternal.cpp + +$(libcppdir)/checkio.o: lib/checkio.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkio.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkio.cpp + +$(libcppdir)/checkleakautovar.o: lib/checkleakautovar.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkleakautovar.h lib/checkmemoryleak.h lib/checknullpointer.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkleakautovar.cpp + +$(libcppdir)/checkmemoryleak.o: lib/checkmemoryleak.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkmemoryleak.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkmemoryleak.cpp + +$(libcppdir)/checknullpointer.o: lib/checknullpointer.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checknullpointer.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checknullpointer.cpp + +$(libcppdir)/checkother.o: lib/checkother.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkother.h lib/config.h lib/errortypes.h lib/fwdanalysis.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkother.cpp + +$(libcppdir)/checkpostfixoperator.o: lib/checkpostfixoperator.cpp lib/addoninfo.h lib/check.h lib/checkpostfixoperator.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkpostfixoperator.cpp + +$(libcppdir)/checksizeof.o: lib/checksizeof.cpp lib/addoninfo.h lib/check.h lib/checksizeof.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checksizeof.cpp + +$(libcppdir)/checkstl.o: lib/checkstl.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checknullpointer.h lib/checkstl.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/pathanalysis.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkstl.cpp + +$(libcppdir)/checkstring.o: lib/checkstring.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkstring.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkstring.cpp + +$(libcppdir)/checktype.o: lib/checktype.cpp lib/addoninfo.h lib/check.h lib/checktype.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checktype.cpp + +$(libcppdir)/checkuninitvar.o: lib/checkuninitvar.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checknullpointer.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkuninitvar.cpp + +$(libcppdir)/checkunusedfunctions.o: lib/checkunusedfunctions.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/astutils.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkunusedfunctions.cpp + +$(libcppdir)/checkunusedvar.o: lib/checkunusedvar.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkunusedvar.h lib/config.h lib/errortypes.h lib/fwdanalysis.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkunusedvar.cpp + +$(libcppdir)/checkvaarg.o: lib/checkvaarg.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkvaarg.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkvaarg.cpp + +$(libcppdir)/clangimport.o: lib/clangimport.cpp lib/addoninfo.h lib/clangimport.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/clangimport.cpp + +$(libcppdir)/color.o: lib/color.cpp lib/color.h lib/config.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/color.cpp + +$(libcppdir)/cppcheck.o: lib/cppcheck.cpp externals/picojson/picojson.h externals/simplecpp/simplecpp.h externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/checkunusedfunctions.h lib/clangimport.h lib/color.h lib/config.h lib/cppcheck.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/json.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/version.h lib/vfvalue.h lib/xml.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/cppcheck.cpp + +$(libcppdir)/ctu.o: lib/ctu.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/astutils.h lib/check.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/ctu.cpp + +$(libcppdir)/errorlogger.o: lib/errorlogger.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/errorlogger.cpp + +$(libcppdir)/errortypes.o: lib/errortypes.cpp lib/config.h lib/errortypes.h lib/utils.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/errortypes.cpp + +$(libcppdir)/forwardanalyzer.o: lib/forwardanalyzer.cpp lib/addoninfo.h lib/analyzer.h lib/astutils.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/forwardanalyzer.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueptr.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/forwardanalyzer.cpp + +$(libcppdir)/fwdanalysis.o: lib/fwdanalysis.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/fwdanalysis.h lib/library.h lib/mathlib.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/fwdanalysis.cpp + +$(libcppdir)/importproject.o: lib/importproject.cpp externals/picojson/picojson.h externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/config.h lib/errortypes.h lib/filesettings.h lib/importproject.h lib/json.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/importproject.cpp + +$(libcppdir)/infer.o: lib/infer.cpp lib/calculate.h lib/config.h lib/errortypes.h lib/infer.h lib/mathlib.h lib/valueptr.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/infer.cpp + +$(libcppdir)/keywords.o: lib/keywords.cpp lib/config.h lib/keywords.h lib/standards.h lib/utils.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/keywords.cpp + +$(libcppdir)/library.o: lib/library.cpp externals/tinyxml2/tinyxml2.h lib/astutils.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h lib/xml.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/library.cpp + +$(libcppdir)/mathlib.o: lib/mathlib.cpp externals/simplecpp/simplecpp.h lib/config.h lib/errortypes.h lib/mathlib.h lib/utils.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/mathlib.cpp + +$(libcppdir)/path.o: lib/path.cpp externals/simplecpp/simplecpp.h lib/config.h lib/path.h lib/standards.h lib/utils.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/path.cpp + +$(libcppdir)/pathanalysis.o: lib/pathanalysis.cpp lib/astutils.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/pathanalysis.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/pathanalysis.cpp + +$(libcppdir)/pathmatch.o: lib/pathmatch.cpp lib/config.h lib/path.h lib/pathmatch.h lib/standards.h lib/utils.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/pathmatch.cpp + +$(libcppdir)/platform.o: lib/platform.cpp externals/tinyxml2/tinyxml2.h lib/config.h lib/path.h lib/platform.h lib/standards.h lib/utils.h lib/xml.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/platform.cpp + +$(libcppdir)/preprocessor.o: lib/preprocessor.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/preprocessor.cpp + +$(libcppdir)/programmemory.o: lib/programmemory.cpp lib/addoninfo.h lib/astutils.h lib/calculate.h lib/config.h lib/errortypes.h lib/infer.h lib/library.h lib/mathlib.h lib/platform.h lib/programmemory.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/programmemory.cpp + +$(libcppdir)/reverseanalyzer.o: lib/reverseanalyzer.cpp lib/addoninfo.h lib/analyzer.h lib/astutils.h lib/config.h lib/errortypes.h lib/forwardanalyzer.h lib/library.h lib/mathlib.h lib/platform.h lib/reverseanalyzer.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueptr.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/reverseanalyzer.cpp + +$(libcppdir)/settings.o: lib/settings.cpp externals/picojson/picojson.h lib/addoninfo.h lib/config.h lib/errortypes.h lib/json.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/summaries.h lib/suppressions.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/settings.cpp + +$(libcppdir)/summaries.o: lib/summaries.cpp lib/addoninfo.h lib/analyzerinfo.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/summaries.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/summaries.cpp + +$(libcppdir)/suppressions.o: lib/suppressions.cpp externals/tinyxml2/tinyxml2.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/mathlib.h lib/path.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/suppressions.cpp + +$(libcppdir)/templatesimplifier.o: lib/templatesimplifier.cpp lib/addoninfo.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/templatesimplifier.cpp + +$(libcppdir)/timer.o: lib/timer.cpp lib/config.h lib/timer.h lib/utils.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/timer.cpp + +$(libcppdir)/token.o: lib/token.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/astutils.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/tokenrange.h lib/utils.h lib/valueflow.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/token.cpp + +$(libcppdir)/tokenlist.o: lib/tokenlist.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/astutils.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/keywords.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/tokenlist.cpp + +$(libcppdir)/utils.o: lib/utils.cpp lib/config.h lib/utils.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/utils.cpp + +$(libcppdir)/vfvalue.o: lib/vfvalue.cpp lib/config.h lib/errortypes.h lib/mathlib.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vfvalue.cpp + +cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlinelogger.h cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h lib/xml.h + $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/cmdlineparser.cpp + +cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlinelogger.h cli/cmdlineparser.h cli/cppcheckexecutor.h cli/cppcheckexecutorseh.h cli/executor.h cli/processexecutor.h cli/signalhandler.h cli/singleexecutor.h cli/threadexecutor.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/checkersreport.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h + $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/cppcheckexecutor.cpp + +cli/cppcheckexecutorseh.o: cli/cppcheckexecutorseh.cpp cli/cppcheckexecutor.h cli/cppcheckexecutorseh.h lib/config.h lib/filesettings.h lib/platform.h lib/standards.h lib/utils.h + $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/cppcheckexecutorseh.cpp + +cli/executor.o: cli/executor.cpp cli/executor.h lib/addoninfo.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h + $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/executor.cpp + +cli/filelister.o: cli/filelister.cpp cli/filelister.h lib/config.h lib/path.h lib/pathmatch.h lib/standards.h lib/utils.h + $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/filelister.cpp + +cli/main.o: cli/main.cpp cli/cppcheckexecutor.h lib/config.h lib/errortypes.h lib/filesettings.h lib/platform.h lib/standards.h lib/utils.h + $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/main.cpp + +cli/processexecutor.o: cli/processexecutor.cpp cli/executor.h cli/processexecutor.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h + $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/processexecutor.cpp + +cli/signalhandler.o: cli/signalhandler.cpp cli/signalhandler.h cli/stacktrace.h lib/config.h + $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/signalhandler.cpp + +cli/singleexecutor.o: cli/singleexecutor.cpp cli/executor.h cli/singleexecutor.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h + $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/singleexecutor.cpp + +cli/stacktrace.o: cli/stacktrace.cpp cli/stacktrace.h lib/config.h lib/utils.h + $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/stacktrace.cpp + +cli/threadexecutor.o: cli/threadexecutor.cpp cli/executor.h cli/threadexecutor.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h + $(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/threadexecutor.cpp + +test/fixture.o: test/fixture.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h lib/xml.h test/fixture.h test/options.h test/redirect.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/fixture.cpp + +test/helpers.o: test/helpers.cpp cli/filelister.h externals/simplecpp/simplecpp.h lib/addoninfo.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/helpers.cpp + +test/main.o: test/main.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h test/options.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/main.cpp + +test/options.o: test/options.cpp test/options.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/options.cpp + +test/test64bit.o: test/test64bit.cpp lib/addoninfo.h lib/check.h lib/check64bit.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/test64bit.cpp + +test/testanalyzerinformation.o: test/testanalyzerinformation.cpp lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testanalyzerinformation.cpp + +test/testassert.o: test/testassert.cpp lib/addoninfo.h lib/check.h lib/checkassert.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testassert.cpp + +test/testastutils.o: test/testastutils.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testastutils.cpp + +test/testautovariables.o: test/testautovariables.cpp lib/addoninfo.h lib/check.h lib/checkautovariables.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testautovariables.cpp + +test/testbool.o: test/testbool.cpp lib/addoninfo.h lib/check.h lib/checkbool.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testbool.cpp + +test/testboost.o: test/testboost.cpp lib/addoninfo.h lib/check.h lib/checkboost.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testboost.cpp + +test/testbufferoverrun.o: test/testbufferoverrun.cpp lib/addoninfo.h lib/check.h lib/checkbufferoverrun.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testbufferoverrun.cpp + +test/testcharvar.o: test/testcharvar.cpp lib/addoninfo.h lib/check.h lib/checkother.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testcharvar.cpp + +test/testcheck.o: test/testcheck.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testcheck.cpp + +test/testclangimport.o: test/testclangimport.cpp lib/addoninfo.h lib/check.h lib/clangimport.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testclangimport.cpp + +test/testclass.o: test/testclass.cpp lib/addoninfo.h lib/check.h lib/checkclass.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testclass.cpp + +test/testcmdlineparser.o: test/testcmdlineparser.cpp cli/cmdlinelogger.h cli/cmdlineparser.h cli/cppcheckexecutor.h lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h test/redirect.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testcmdlineparser.cpp + +test/testcolor.o: test/testcolor.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testcolor.cpp + +test/testcondition.o: test/testcondition.cpp lib/addoninfo.h lib/check.h lib/checkcondition.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testcondition.cpp + +test/testconstructors.o: test/testconstructors.cpp lib/addoninfo.h lib/check.h lib/checkclass.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testconstructors.cpp + +test/testcppcheck.o: test/testcppcheck.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testcppcheck.cpp + +test/testerrorlogger.o: test/testerrorlogger.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h lib/xml.h test/fixture.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testerrorlogger.cpp + +test/testexceptionsafety.o: test/testexceptionsafety.cpp lib/addoninfo.h lib/check.h lib/checkexceptionsafety.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testexceptionsafety.cpp + +test/testfilelister.o: test/testfilelister.cpp cli/filelister.h lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testfilelister.cpp + +test/testfunctions.o: test/testfunctions.cpp lib/addoninfo.h lib/check.h lib/checkfunctions.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testfunctions.cpp + +test/testgarbage.o: test/testgarbage.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testgarbage.cpp + +test/testimportproject.o: test/testimportproject.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h test/redirect.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testimportproject.cpp + +test/testincompletestatement.o: test/testincompletestatement.cpp lib/addoninfo.h lib/check.h lib/checkother.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testincompletestatement.cpp + +test/testinternal.o: test/testinternal.cpp lib/addoninfo.h lib/check.h lib/checkinternal.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testinternal.cpp + +test/testio.o: test/testio.cpp lib/addoninfo.h lib/check.h lib/checkio.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testio.cpp + +test/testleakautovar.o: test/testleakautovar.cpp lib/addoninfo.h lib/check.h lib/checkleakautovar.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testleakautovar.cpp + +test/testlibrary.o: test/testlibrary.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testlibrary.cpp + +test/testmathlib.o: test/testmathlib.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testmathlib.cpp + +test/testmemleak.o: test/testmemleak.cpp lib/addoninfo.h lib/check.h lib/checkmemoryleak.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testmemleak.cpp + +test/testnullpointer.o: test/testnullpointer.cpp lib/addoninfo.h lib/check.h lib/checknullpointer.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testnullpointer.cpp + +test/testoptions.o: test/testoptions.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h test/options.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testoptions.cpp + +test/testother.o: test/testother.cpp lib/addoninfo.h lib/check.h lib/checkother.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testother.cpp + +test/testpath.o: test/testpath.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testpath.cpp + +test/testpathmatch.o: test/testpathmatch.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/pathmatch.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testpathmatch.cpp + +test/testplatform.o: test/testplatform.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h lib/xml.h test/fixture.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testplatform.cpp + +test/testpostfixoperator.o: test/testpostfixoperator.cpp lib/addoninfo.h lib/check.h lib/checkpostfixoperator.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testpostfixoperator.cpp + +test/testpreprocessor.o: test/testpreprocessor.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testpreprocessor.cpp + +test/testprocessexecutor.o: test/testprocessexecutor.cpp cli/executor.h cli/processexecutor.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h test/redirect.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testprocessexecutor.cpp + +test/testsettings.o: test/testsettings.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsettings.cpp + +test/testsimplifytemplate.o: test/testsimplifytemplate.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsimplifytemplate.cpp + +test/testsimplifytokens.o: test/testsimplifytokens.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsimplifytokens.cpp + +test/testsimplifytypedef.o: test/testsimplifytypedef.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsimplifytypedef.cpp + +test/testsimplifyusing.o: test/testsimplifyusing.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsimplifyusing.cpp + +test/testsingleexecutor.o: test/testsingleexecutor.cpp cli/executor.h cli/singleexecutor.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h test/redirect.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsingleexecutor.cpp + +test/testsizeof.o: test/testsizeof.cpp lib/addoninfo.h lib/check.h lib/checksizeof.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsizeof.cpp + +test/teststl.o: test/teststl.cpp lib/addoninfo.h lib/check.h lib/checkstl.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/teststl.cpp + +test/teststring.o: test/teststring.cpp lib/addoninfo.h lib/check.h lib/checkstring.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/teststring.cpp + +test/testsummaries.o: test/testsummaries.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/summaries.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsummaries.cpp + +test/testsuppressions.o: test/testsuppressions.cpp cli/cppcheckexecutor.h cli/executor.h cli/processexecutor.h cli/singleexecutor.h cli/threadexecutor.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsuppressions.cpp + +test/testsymboldatabase.o: test/testsymboldatabase.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsymboldatabase.cpp + +test/testthreadexecutor.o: test/testthreadexecutor.cpp cli/executor.h cli/threadexecutor.h lib/addoninfo.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h test/redirect.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testthreadexecutor.cpp + +test/testtimer.o: test/testtimer.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h test/fixture.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testtimer.cpp + +test/testtoken.o: test/testtoken.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testtoken.cpp + +test/testtokenize.o: test/testtokenize.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testtokenize.cpp + +test/testtokenlist.o: test/testtokenlist.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testtokenlist.cpp + +test/testtokenrange.o: test/testtokenrange.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/tokenrange.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testtokenrange.cpp + +test/testtype.o: test/testtype.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/check.h lib/checktype.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testtype.cpp + +test/testuninitvar.o: test/testuninitvar.cpp lib/addoninfo.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testuninitvar.cpp + +test/testunusedfunctions.o: test/testunusedfunctions.cpp lib/addoninfo.h lib/check.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testunusedfunctions.cpp + +test/testunusedprivfunc.o: test/testunusedprivfunc.cpp lib/addoninfo.h lib/check.h lib/checkclass.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testunusedprivfunc.cpp + +test/testunusedvar.o: test/testunusedvar.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/check.h lib/checkunusedvar.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testunusedvar.cpp + +test/testutils.o: test/testutils.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h test/fixture.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testutils.cpp + +test/testvaarg.o: test/testvaarg.cpp lib/addoninfo.h lib/check.h lib/checkvaarg.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testvaarg.cpp + +test/testvalueflow.o: test/testvalueflow.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testvalueflow.cpp + +test/testvarid.o: test/testvarid.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testvarid.cpp + +externals/simplecpp/simplecpp.o: externals/simplecpp/simplecpp.cpp externals/simplecpp/simplecpp.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -w -c -o $@ externals/simplecpp/simplecpp.cpp + +externals/tinyxml2/tinyxml2.o: externals/tinyxml2/tinyxml2.cpp externals/tinyxml2/tinyxml2.h + $(CXX) $(CPPFLAGS) $(CXXFLAGS) -w -c -o $@ externals/tinyxml2/tinyxml2.cpp + +tools/dmake/dmake.o: tools/dmake/dmake.cpp cli/filelister.h lib/config.h lib/pathmatch.h lib/utils.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ tools/dmake/dmake.cpp + diff --git a/cppcheck-2.14.0/addons/README.md b/cppcheck-2.14.0/addons/README.md new file mode 100644 index 00000000..563c0c79 --- /dev/null +++ b/cppcheck-2.14.0/addons/README.md @@ -0,0 +1,67 @@ +# Cppcheck addons + +Addons are scripts that analyses Cppcheck dump files to check compatibility with secure coding standards and to locate various issues. + +## Supported addons + ++ [misra.py](https://github.com/danmar/cppcheck/blob/main/addons/misra.py) + Used to verify compliance with MISRA C 2012 - a proprietary set of guidelines to avoid such questionable code, developed for embedded systems. Since this standard is proprietary, cppcheck does not display error text by specifying only the number of violated rules (for example, [c2012-21.3]). If you want to display full texts for violated rules, you will need to create a text file containing MISRA rules, which you will have to pass when calling the script with `--rule-texts` key. Some examples of rule texts files available in [tests directory](https://github.com/danmar/cppcheck/blob/main/addons/test/misra/). ++ [y2038.py](https://github.com/danmar/cppcheck/blob/main/addons/y2038.py) + Checks Linux system for [year 2038 problem](https://en.wikipedia.org/wiki/Year_2038_problem) safety. This required [modified environment](https://github.com/3adev/y2038). See complete description [here](https://github.com/danmar/cppcheck/blob/main/addons/doc/y2038.txt). ++ [threadsafety.py](https://github.com/danmar/cppcheck/blob/main/addons/threadsafety.py) + Analyse Cppcheck dump files to locate threadsafety issues like static local objects used by multiple threads. ++ [naming.py](https://github.com/danmar/cppcheck/blob/main/addons/naming.py) + Enforces naming conventions across the code. ++ [namingng.py](https://github.com/danmar/cppcheck/blob/main/addons/namingng.py) + Enforces naming conventions across the code. Enhanced version with support for type prefixes in variable and function names. ++ [findcasts.py](https://github.com/danmar/cppcheck/blob/main/addons/findcasts.py) + Locates casts in the code. ++ [misc.py](https://github.com/danmar/cppcheck/blob/main/addons/misc.py) + Performs miscellaneous checks. + +### Other files + +- doc + Additional files for documentation generation. +- tests + Contains various unit tests for the addons. +- cppcheck.py + Internal helper used by Cppcheck binary to run the addons. +- cppcheckdata.doxyfile + Configuration file for documentation generation. +- cppcheckdata.py + Helper class for reading Cppcheck dump files within an addon. +- misra_9.py + Implementation of the MISRA 9.x rules used by `misra` addon. +- namingng.config.json + Example configuration for `namingng` addon. +- namingng.json + Example JSON file that can be used using --addon=namingng.json, referring to namingng.py and namingng.config.json +- ROS_naming.json + Example configuration for the `namingng` addon enforcing the [ROS naming convention for C++ ](http://wiki.ros.org/CppStyleGuide#Files). +- runaddon.py + Internal helper used by Cppcheck binary to run the addons. + +## Usage + +### Command line interface + +```bash +cppcheck --addon=misc src/test.c +``` + +It is also possible to call scripts as follows: +```bash +cppcheck --dump --quiet src/test.c +python misc.py src/test.c.dump +python misra.py --rule-texts=~/misra_rules.txt src/test.c.dump +``` + +This allows you to add additional parameters when calling the script (for example, `--rule-texts` for `misra.py`). The full list of available parameters can be found by calling any script with the `--help` flag. + +### GUI + +When using the graphical interface `cppcheck-gui`, the selection and configuration of addons is carried out on the tab `Addons and tools` in the project settings (`Edit Project File`): + +![Screenshot](https://raw.githubusercontent.com/danmar/cppcheck/main/addons/doc/img/cppcheck-gui-addons.png) + diff --git a/cppcheck-2.14.0/addons/ROS_naming.json b/cppcheck-2.14.0/addons/ROS_naming.json new file mode 100644 index 00000000..0262d69c --- /dev/null +++ b/cppcheck-2.14.0/addons/ROS_naming.json @@ -0,0 +1,24 @@ +{ + "RE_FILE": [".*[A-Z]"], + "RE_NAMESPACE": {".*[A-Z]": [true, "under_scored"], + ".*\\_$": [true, "under_scored"]}, + "RE_FUNCTIONNAME": {".*\\_": [true, "camelCase"], + ".*^[a-z]": [false, "camelCase"]}, + "RE_CLASS_NAME": {".*^[A-Z]": [false, "CamelCase"], + ".*\\_": [true, "CamelCase"]}, + "RE_GLOBAL_VARNAME": {".*^([g]\\_)": [false, "g_under_scored"], + ".*[A-Z]": [true, "g_under_scored"], + ".*\\_$": [true, "g_under_scored"]}, + "RE_VARNAME": {".*^([g]\\_)": [true, "under_scored"], + ".*[A-Z]": [true, "under_scored"], + ".*\\_$": [true, "under_scored"]}, + "RE_PRIVATE_MEMBER_VARIABLE": {".*\\_$": [false, "under_scored_"], + ".*[A-Z]": [true, "under_scored_"]}, + "RE_PUBLIC_MEMBER_VARIABLE": {".*\\_$": [false, "under_scored_"], + ".*[A-Z]": [true, "under_scored_"]}, + "var_prefixes": {"uint32_t": "ui32", + "int*": "intp"}, + "function_prefixes": {"uint16_t": "ui16", + "uint32_t": "ui32"}, + "skip_one_char_variables": false +} \ No newline at end of file diff --git a/cppcheck-2.14.0/addons/__init__.py b/cppcheck-2.14.0/addons/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cppcheck-2.14.0/addons/cppcheck.py b/cppcheck-2.14.0/addons/cppcheck.py new file mode 100644 index 00000000..1649815e --- /dev/null +++ b/cppcheck-2.14.0/addons/cppcheck.py @@ -0,0 +1,39 @@ + +import cppcheckdata, sys, os + +__checkers__ = [] + +def checker(f): + __checkers__.append(f) + return f + + +__errorid__ = '' +__addon_name__ = '' +def reportError(location, severity, message, errorId=None): + cppcheckdata.reportError(location, severity, message, __addon_name__, errorId or __errorid__) + +def runcheckers(): + # If there are no checkers then don't run + if len(__checkers__) == 0: + return + global __addon_name__ + global __errorid__ + addon = sys.argv[0] + parser = cppcheckdata.ArgumentParser() + args = parser.parse_args() + + __addon_name__ = os.path.splitext(os.path.basename(addon))[0] + + for dumpfile in args.dumpfile: + if not args.quiet: + print('Checking %s...' % dumpfile) + + data = cppcheckdata.CppcheckData(dumpfile) + + for cfg in data.iterconfigurations(): + if not args.quiet: + print('Checking %s, config %s...' % (dumpfile, cfg.name)) + for c in __checkers__: + __errorid__ = c.__name__ + c(cfg, data) diff --git a/cppcheck-2.14.0/addons/cppcheckdata.doxyfile b/cppcheck-2.14.0/addons/cppcheckdata.doxyfile new file mode 100644 index 00000000..9fd8eada --- /dev/null +++ b/cppcheck-2.14.0/addons/cppcheckdata.doxyfile @@ -0,0 +1,1792 @@ +# Doxyfile 1.8.1.2 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = "cppcheckdata" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +SYMBOL_CACHE_SIZE = 0 + +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = cppcheckdata.py + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# style sheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = NO + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# manageable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = NO + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = NO + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = NO + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = NO diff --git a/cppcheck-2.14.0/addons/cppcheckdata.py b/cppcheck-2.14.0/addons/cppcheckdata.py new file mode 100644 index 00000000..fcd05c61 --- /dev/null +++ b/cppcheck-2.14.0/addons/cppcheckdata.py @@ -0,0 +1,1708 @@ +""" +cppcheckdata + +This is a Python module that helps you access Cppcheck dump data. + +License: No restrictions, use this as you need. +""" + +import argparse +import json +import os +import sys +import subprocess + +try: + import pathlib +except ImportError: + message = "Failed to load pathlib. Upgrade Python to 3.x or install pathlib with 'pip install pathlib'." + error_id = 'pythonError' + if '--cli' in sys.argv: + msg = { 'file': '', + 'linenr': 0, + 'column': 0, + 'severity': 'error', + 'message': message, + 'addon': 'cppcheckdata', + 'errorId': error_id, + 'extra': ''} + sys.stdout.write(json.dumps(msg) + '\n') + else: + sys.stderr.write('%s [%s]\n' % (message, error_id)) + sys.exit(1) + +from xml.etree import ElementTree +from fnmatch import fnmatch + +EXIT_CODE = 0 + +current_dumpfile_suppressions = [] + +def _load_location(location, element): + """Load location from element/dict""" + location.file = element.get('file') + line = element.get('line') + if line is None: + line = element.get('linenr') + if line is None: + line = '0' + location.linenr = int(line) + location.column = int(element.get('column', '0')) + + +class Location: + """Utility location class""" + file = None + linenr = None + column = None + def __init__(self, element): + _load_location(self, element) + + +class Directive: + """ + Directive class. Contains information about each preprocessor directive in the source code. + + Attributes: + str The directive line, with all C or C++ comments removed + file Name of (possibly included) file where directive is defined + linenr Line number in (possibly included) file where directive is defined + + To iterate through all directives use such code: + @code + data = cppcheckdata.parsedump(...) + for cfg in data.configurations: + for directive in cfg.directives: + print(directive.str) + @endcode + """ + #preprocessor.cpp/Preprocessor::dump + + str = None + file = None + linenr = None + column = None + + def __init__(self, element): + self.str = element.get('str') + _load_location(self, element) + + def __repr__(self): + attrs = ["str", "file", "linenr"] + return "{}({})".format( + "Directive", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + +class MacroUsage: + """ + Tracks preprocessor macro usage + + Attributes: + name Name of the macro + usefile + useline + usecolumn + isKnownValue + """ + #preprocessor.cpp/Preprocessor::dump + + name = None # Macro name + file = None + linenr = None + column = None + usefile = None + uselinenr = None + usecolumn = None + + def __init__(self, element): + self.name = element.get('name') + _load_location(self, element) + self.usefile = element.get('usefile') + self.useline = element.get('useline') + self.usecolumn = element.get('usecolumn') + self.isKnownValue = element.get('is-known-value', 'false') == 'true' + + def __repr__(self): + attrs = ["name", "file", "linenr", "column", "usefile", "useline", "usecolumn", "isKnownValue"] + return "{}({})".format( + "MacroUsage", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + + +class PreprocessorIfCondition: + """ + Information about #if/#elif conditions + + Attributes: + E + result + """ + #preprocessor.cpp/Preprocessor::dump + + file = None + linenr = None + column = None + E = None + result = None + + def __init__(self, element): + _load_location(self, element) + self.E = element.get('E') + self.result = int(element.get('result')) + + def __repr__(self): + attrs = ["file", "linenr", "column", "E", "result"] + return "{}({})".format( + "PreprocessorIfCondition", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + +class ValueType: + """ + ValueType class. Contains (promoted) type information for each node in the AST. + + Attributes: + type nonstd/pod/record/smart-pointer/container/iterator/void/bool/char/short/wchar_t/int/long/long long/unknown int/float/double/long double + sign signed/unsigned + bits + pointer + constness + reference + typeScopeId + originalTypeName bool/const char */long/char */size_t/int/double/std::string/.. + + """ + #symboldatabase.cpp/ValueType::dump + + type = None + sign = None + bits = 0 + constness = 0 + pointer = 0 + typeScopeId = None + typeScope = None + originalTypeName = None + + def __init__(self, element): + self.type = element.get('valueType-type') + self.sign = element.get('valueType-sign') + self.bits = int(element.get('valueType-bits', 0)) + self.pointer = int(element.get('valueType-pointer', 0)) + self.constness = int(element.get('valueType-constness', 0)) + self.reference = element.get('valueType-reference') + self.typeScopeId = element.get('valueType-typeScope') + self.originalTypeName = element.get('valueType-originalTypeName') + #valueType-containerId TODO add + + + def __repr__(self): + attrs = ["type", "sign", "bits", "typeScopeId", "originalTypeName", + "constness", "pointer"] + return "{}({})".format( + "ValueType", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + + + def setId(self, IdMap): + self.typeScope = IdMap[self.typeScopeId] + + def isIntegral(self): + return self.type in {'bool', 'char', 'short', 'int', 'long', 'long long'} + + def isFloat(self): + return self.type in {'float', 'double', 'long double'} + + def isEnum(self): + return self.typeScope and self.typeScope.type == "Enum" + + +class Token: + """ + Token class. Contains information about each token in the source code. + + The CppcheckData.tokenlist is a list of Token items + + C++ class: https://cppcheck.sourceforge.io/devinfo/doxyoutput/classToken.html + + Attributes: + str Token string + next Next token in tokenlist. For last token, next is None. + previous Previous token in tokenlist. For first token, previous is None. + link Linked token in tokenlist. Each '(', '[' and '{' are linked to the + corresponding '}', ']' and ')'. For templates, the '<' is linked to + the corresponding '>'. + scope Scope information for this token. See the Scope class. + type Type information: name/op/number/string/.. + isName Is this token a symbol name + isUnsigned Is this token a unsigned type + isSigned Is this token a signed type + isNumber Is this token a number, for example 123, 12.34 + isInt Is this token a int value such as 1234 + isFloat Is this token a float value such as 12.34 + isString Is this token a string literal such as "hello" + strlen string length for string literal + isChar Is this token a char literal such as 'x' + isBoolean Is this token a boolean + isOp Is this token a operator + isArithmeticalOp Is this token a arithmetic operator + isAssignmentOp Is this token a assignment operator + isComparisonOp Is this token a comparison operator + isLogicalOp Is this token a logical operator: && || + isCast + externLang + isExpandedMacro Is this token a expanded macro token + macroName Macro name that this token is expanded from + isRemovedVoidParameter Has void parameter been removed? + isSplittedVarDeclComma Is this a comma changed to semicolon in a split variable declaration ('int a,b;' => 'int a; int b;') + isSplittedVarDeclEq Is this a '=' changed to semicolon in a split variable declaration ('int a=5;' => 'int a; a=5;') + isImplicitInt Is this token an implicit "int"? + isComplex + isRestrict + isAttributeExport + varId varId for token, each variable has a unique non-zero id + exprId exprId for token, each expression has a unique non-zero id + variable Variable information for this token. See the Variable class. + function If this token points at a function call, this attribute has the Function + information. See the Function class. + values Possible/Known values of token + typeScope type scope (token->type()->classScope) + astParent ast parent + astOperand1 ast operand1 + astOperand2 ast operand2 + orriginalName orriginal name of the token + valueType type information: container/.. + file file name + linenr line number + column column + + To iterate through all tokens use such code: + @code + data = cppcheckdata.parsedump(...) + for cfg in data.configurations: + code = '' + for token in cfg.tokenlist: + code = code + token.str + ' ' + print(code) + @endcode + """ + #tokenize.cpp/Tokenizer::dump + + Id = None + str = None + next = None + previous = None + linkId = None + link = None + scopeId = None + scope = None + isName = False + isNumber = False + isInt = False + isFloat = False + isString = False + strlen = None + isChar = False + isBoolean = False + isOp = False + isArithmeticalOp = False + isAssignmentOp = False + isComparisonOp = False + isLogicalOp = False + isCast = False + isUnsigned = False + isSigned = False + macroName = None + isExpandedMacro = False + isRemovedVoidParameter = False + isSplittedVarDeclComma = False + isSplittedVarDeclEq = False + isImplicitInt = False + isComplex = False + isRestrict = False + isAttributeExport = False + exprId = None + varId = None + variableId = None + variable = None + functionId = None + function = None + valuesId = None + values = None + impossible_values = None + valueType = None + + typeScopeId = None + typeScope = None + type = None + + astParentId = None + astParent = None + astOperand1Id = None + astOperand1 = None + astOperand2Id = None + astOperand2 = None + + file = None + linenr = None + column = None + + def __init__(self, element): + self.Id = element.get('id') + self.str = element.get('str') + self.next = None + self.previous = None + self.scopeId = element.get('scope') + self.scope = None + self.type = element.get('type') + if self.type == 'name': + self.isName = True + if element.get('isUnsigned'): + self.isUnsigned = True + if element.get('isSigned'): + self.isSigned = True + elif self.type == 'number': + self.isNumber = True + if element.get('isInt'): + self.isInt = True + elif element.get('isFloat'): + self.isFloat = True + elif self.type == 'string': + self.isString = True + self.strlen = int(element.get('strlen')) + elif self.type == 'char': + self.isChar = True + elif self.type == 'boolean': + self.isBoolean = True + elif self.type == 'op': + self.isOp = True + if element.get('isArithmeticalOp'): + self.isArithmeticalOp = True + elif element.get('isAssignmentOp'): + self.isAssignmentOp = True + elif element.get('isComparisonOp'): + self.isComparisonOp = True + elif element.get('isLogicalOp'): + self.isLogicalOp = True + if element.get('isCast'): + self.isCast = True + self.externLang = element.get('externLang') + self.macroName = element.get('macroName') + if self.macroName or element.get('isExpandedMacro'): + self.isExpandedMacro = True + if element.get('isRemovedVoidParameter'): + self.isRemovedVoidParameter = True + if element.get('isSplittedVarDeclComma'): + self.isSplittedVarDeclComma = True + if element.get('isSplittedVarDeclEq'): + self.isSplittedVarDeclEq = True + if element.get('isImplicitInt'): + self.isImplicitInt = True + if element.get('isComplex'): + self.isComplex = True + if element.get('isRestrict'): + self.isRestrict = True + if element.get('isAttributeExport'): + self.isAttributeExport = True + self.linkId = element.get('link') + self.link = None + if element.get('varId'): + self.varId = int(element.get('varId')) + if element.get('exprId'): + self.exprId = int(element.get('exprId')) + 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.typeScopeId = element.get('type-scope') + self.typeScope = None + self.astParentId = element.get('astParent') + self.astParent = None + self.astOperand1Id = element.get('astOperand1') + self.astOperand1 = None + self.astOperand2Id = element.get('astOperand2') + self.astOperand2 = None + self.originalName = element.get('originalName') + if element.get('valueType-type'): + self.valueType = ValueType(element) + else: + self.valueType = None + _load_location(self, element) + + def __repr__(self): + attrs = ["Id", "str", "scopeId", "isName", "isUnsigned", "isSigned", + "isNumber", "isInt", "isFloat", "isString", "strlen", + "isChar", "isBoolean", "isOp", "isArithmeticalOp", "isAssignmentOp", + "isComparisonOp", "isLogicalOp", "isCast", "externLang", "isExpandedMacro", + "isRemovedVoidParameter", "isSplittedVarDeclComma", "isSplittedVarDeclEq", + "isImplicitInt", "isComplex", "isRestrict", "isAttributeExport", "linkId", + "varId", "variableId", "functionId", "valuesId", "valueType", + "typeScopeId", "astParentId", "astOperand1Id", "file", + "linenr", "column"] + return "{}({})".format( + "Token", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + + def setId(self, IdMap): + self.scope = IdMap[self.scopeId] + self.link = IdMap[self.linkId] + self.variable = IdMap[self.variableId] + self.function = IdMap[self.functionId] + self.values = [] + self.impossible_values = [] + if IdMap[self.valuesId]: + for v in IdMap[self.valuesId]: + if v.isImpossible(): + self.impossible_values.append(v) + else: + self.values.append(v) + v.setId(IdMap) + self.typeScope = IdMap[self.typeScopeId] + self.astParent = IdMap[self.astParentId] + self.astOperand1 = IdMap[self.astOperand1Id] + self.astOperand2 = IdMap[self.astOperand2Id] + if self.valueType: + self.valueType.setId(IdMap) + + def getValue(self, v): + """ + Get value if it exists + Returns None if it doesn't exist + """ + + if not self.values: + return None + for value in self.values: + if value.intvalue == v: + return value + return None + + def getKnownIntValue(self): + """ + If token has a known int value then return that. + Otherwise returns None + """ + if not self.values: + return None + for value in self.values: + if value.valueKind == 'known': + return value.intvalue + return None + + def isUnaryOp(self, op): + return self.astOperand1 and (self.astOperand2 is None) and self.str == op + + def isBinaryOp(self): + return self.astOperand1 and self.astOperand2 + + def forward(self, end=None): + token = self + while token and token != end: + yield token + token = token.next + + def backward(self, start=None): + token = self + while token and token != start: + yield token + token = token.previous + + def astParents(self): + token = self + while token and token.astParent: + token = token.astParent + yield token + + def astTop(self): + top = None + for parent in self.astParents(): + top = parent + return top + + def tokAt(self, n): + tl = self.forward() + if n < 0: + tl = self.backward() + n = -n + for i, t in enumerate(tl): + if i == n: + return t + + def linkAt(self, n): + token = self.tokAt(n) + if token: + return token.link + return None + +class Scope: + """ + Scope. Information about global scope, function scopes, class scopes, inner scopes, etc. + C++ class: https://cppcheck.sourceforge.io/devinfo/doxyoutput/classScope.html + + Attributes + bodyStart The { Token for this scope + bodyEnd The } Token for this scope + className Name of this scope. + For a function scope, this is the function name; + For a class scope, this is the class name. + function If this scope belongs at a function call, this attribute + has the Function information. See the Function class. + functions if this is a Class type, it may have functions defined + nestedIn + type Type of scope: Function, If/Else/For/While/Switch/Global/Enum/Struct/Namespace/Class/Constructor/Destructor + isExecutable True when the type is: Function/If/Else/For/While/Do/Switch/Try/Catch/Unconditional/Lambda + definedType + """ + #symboldatabase.cpp/SymbolDatabase::printXml + + Id = None + bodyStartId = None + bodyStart = None + bodyEndId = None + bodyEnd = None + className = None + functionId = None + function = None + nestedInId = None + nestedIn = None + nestedList = None + type = None + isExecutable = None + varlistId = None + varlist = None + + def __init__(self, element): + self.Id = element.get('id') + self.className = element.get('className') + self.functionId = element.get('function') + self.function = None + self.functions = [] + self.bodyStartId = element.get('bodyStart') + self.bodyStart = None + self.bodyEndId = element.get('bodyEnd') + self.bodyEnd = None + self.nestedInId = element.get('nestedIn') + self.nestedIn = None + self.nestedList = list() + self.type = element.get('type') + self.definedType = element.get('definedType') + self.isExecutable = (self.type in ('Function', 'If', 'Else', 'For', 'While', 'Do', + 'Switch', 'Try', 'Catch', 'Unconditional', 'Lambda')) + + self.varlistId = list() + self.varlist = list() + + def __repr__(self): + attrs = ["Id", "className", "functionId", "bodyStartId", "bodyEndId", + "nestedInId", "nestedIn", "type", "definedType", "isExecutable", "functions"] + return "{}({})".format( + "Scope", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + + def setId(self, IdMap): + self.bodyStart = IdMap[self.bodyStartId] + self.bodyEnd = IdMap[self.bodyEndId] + self.nestedIn = IdMap[self.nestedInId] + if self.nestedIn: + self.nestedIn.nestedList.append(self) + self.function = IdMap[self.functionId] + for v in self.varlistId: + value = IdMap.get(v) + if value: + self.varlist.append(value) + + +class Function: + """ + Information about a function + C++ class: + https://cppcheck.sourceforge.io/devinfo/doxyoutput/classFunction.html + + Attributes + argument Argument list (dict of argument number and variable) + token Token in function implementation + tokenDef Token in function definition + name + type Constructor/CopyConstructor/MoveConstructor/OperatorEqual/Destructor/Function/Lambda/Unknown + hasVirtualSpecifier Is this function is virtual + isImplicitlyVirtual Is this function is virtual this in the base classes + access Public/Protected/Private + isInlineKeyword Is inline keyword used + isStatic Is this function static + isAttributeNoreturn + overriddenFunction + """ + #symboldatabase.cpp/SymbolDatabase::printXml + + Id = None + argument = None + argumentId = None + token = None + tokenId = None + tokenDef = None + tokenDefId = None + name = None + type = None + access = None + isImplicitlyVirtual = None + hasVirtualSpecifier = None + isInlineKeyword = None + isStatic = None + isAttributeNoreturn = None + overriddenFunction = None + nestedIn = None + + def __init__(self, element, nestedIn): + self.Id = element.get('id') + self.tokenId = element.get('token') + self.tokenDefId = element.get('tokenDef') + self.name = element.get('name') + self.type = element.get('type') + self.hasVirtualSpecifier = element.get('hasVirtualSpecifier', 'false') == 'true' + self.isImplicitlyVirtual = element.get('isImplicitlyVirtual', 'false') == 'true' + self.access = element.get('access') + self.isInlineKeyword = element.get('isInlineKeyword', 'false') == 'true' + self.isStatic = element.get('isStatic', 'false') == 'true' + self.isAttributeNoreturn = element.get('isAttributeNoreturn', 'false') == 'true' + self.overriddenFunction = element.get('overriddenFunction', 'false') == 'true' + self.nestedIn = nestedIn + + self.argument = {} + self.argumentId = {} + + def __repr__(self): + attrs = ["Id", "tokenId", "tokenDefId", "name", "type", "hasVirtualSpecifier", + "isImplicitlyVirtual", "access", "isInlineKeyword", "isStatic", + "isAttributeNoreturn", "overriddenFunction", "nestedIn", "argumentId"] + return "{}({})".format( + "Function", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + + def setId(self, IdMap): + for argnr, argid in self.argumentId.items(): + self.argument[argnr] = IdMap[argid] + self.token = IdMap.get(self.tokenId, None) + self.tokenDef = IdMap[self.tokenDefId] + + +#todo add class Types: + #symboldatabase.cpp/SymbolDatabase::printXml + + +class Variable: + """ + Information about a variable + C++ class: + https://cppcheck.sourceforge.io/devinfo/doxyoutput/classVariable.html + + Attributes: + nameToken Name token in variable declaration + typeStartToken Start token of variable declaration + typeEndToken End token of variable declaration + access Global/Local/Namespace/Public/Protected/Public/Throw/Argument/Unknown + scope Variable scope + constness Variable constness (same encoding as ValueType::constness) + isArgument Is this variable a function argument? + isGlobal Is this variable a global variable? + isLocal Is this variable a local variable? + isArray Is this variable an array? + isClass Is this variable a class or struct? + isConst Is this variable a const variable? + isExtern Is this variable an extern variable? + isPointer Is this variable a pointer + isReference Is this variable a reference + isStatic Is this variable static? + isVolatile Is this variable volatile? + """ + #symboldatabase.cpp/SymbolDatabase::printXml + + Id = None + nameTokenId = None + nameToken = None + typeStartTokenId = None + typeStartToken = None + typeEndTokenId = None + typeEndToken = None + access = None + scopeId = None + scope = None + isArgument = False + isArray = False + isClass = False + isConst = False + isExtern = False + isGlobal = False + isLocal = False + isPointer = False + isReference = False + isStatic = False + isVolatile = False + constness = 0 + + def __init__(self, element): + self.Id = element.get('id') + self.nameTokenId = element.get('nameToken') + self.nameToken = None + self.typeStartTokenId = element.get('typeStartToken') + self.typeStartToken = None + self.typeEndTokenId = element.get('typeEndToken') + self.typeEndToken = None + self.access = element.get('access') + self.isArgument = (self.access and self.access == 'Argument') + self.isGlobal = (self.access and self.access == 'Global') + self.isLocal = (self.access and self.access == 'Local') + self.scopeId = element.get('scope') + self.scope = None + self.constness = int(element.get('constness',0)) + self.isArray = element.get('isArray') == 'true' + self.isClass = element.get('isClass') == 'true' + self.isConst = element.get('isConst') == 'true' + self.isExtern = element.get('isExtern') == 'true' + self.isPointer = element.get('isPointer') == 'true' + self.isReference = element.get('isReference') == 'true' + self.isStatic = element.get('isStatic') == 'true' + self.isVolatile = element.get('isVolatile') == 'true' + + def __repr__(self): + attrs = ["Id", "nameTokenId", "typeStartTokenId", "typeEndTokenId", + "access", "scopeId", "isArgument", "isArray", "isClass", + "isConst", "isGlobal", "isExtern", "isLocal", "isPointer", + "isReference", "isStatic", "isVolatile", "constness"] + return "{}({})".format( + "Variable", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + + def setId(self, IdMap): + self.nameToken = IdMap[self.nameTokenId] + self.typeStartToken = IdMap[self.typeStartTokenId] + self.typeEndToken = IdMap[self.typeEndTokenId] + self.scope = IdMap[self.scopeId] + +class Container: + """ + Container class -- information about containers + + Attributes: + array-like-index-op true/false + stdStringLike true/false + """ + #tokenizer.cpp/tokenizer::dump + Id = None + + def __init__(self, element): + self.Id = element.get('id') + self.arrayLikeIndexOp = element.get('array-like-index-op') == 'true' + self.stdStringLike = element.get('std-string-like') == 'true' + +class TypedefInfo: + """ + TypedefInfo class -- information about typedefs + + Attributes: + name name of the typedef + used 0/1 + """ + #tokenizer.cpp/tokenizer::dump + + name = None + used = None + file = None + linenr = None + column = None + + def __init__(self, element): + self.name = element.get('name') + _load_location(self, element) + self.used = (element.get('used') == '1') + +class Value: + """ + Value class + + Attributes: + intvalue integer value + tokvalue token value + floatvalue float value + movedvalue + uninit + containerSize container size + bufferSize buffer size + lifetimeScope Local/Argument/SubFunction/ThisPointer/ThisValue + lifetimeKind Object/SubObject/Lambda/Iterator/Address + symbolicDelta + condition condition where this Value comes from + bound Upper/Lower/Point + valueKind known/possible/impossible/inconclusive + path 0/1/2/3/.. + """ + #token.cpp/token::printValueFlow + + intvalue = None + tokvalue = None + floatvalue = None + containerSize = None + condition = None + valueKind = None + + def isKnown(self): + return self.valueKind and self.valueKind == 'known' + + def isPossible(self): + return self.valueKind and self.valueKind == 'possible' + + def isImpossible(self): + return self.valueKind and self.valueKind == 'impossible' + + def isInconclusive(self): + return self.valueKind and self.valueKind == 'inconclusive' + + def __init__(self, element): + self.intvalue = element.get('intvalue') + if self.intvalue: + self.intvalue = int(self.intvalue) + self._tokvalueId = element.get('tokvalue') + self.floatvalue = element.get('floatvalue') + self.movedvalue = element.get('movedvalue') + self.uninit = element.get('uninit') + self.bufferSize = element.get('buffer-size') + self.containerSize = element.get('container-size') + self.iteratorStart = element.get('iterator-start') + self.iteratorEnd = element.get('iterator-end') + self._lifetimeId = element.get('lifetime') + self.lifetimeScope = element.get('lifetime-scope') + self.lifetimeKind = element.get('lifetime-kind') + self._symbolicId = element.get('symbolic') + self.symbolicDelta = element.get('symbolic-delta') + self.bound = element.get('bound') + self.condition = element.get('condition-line') + if self.condition: + self.condition = int(self.condition) + if element.get('known'): + self.valueKind = 'known' + elif element.get('possible'): + self.valueKind = 'possible' + elif element.get('impossible'): + self.valueKind = 'impossible' + elif element.get('inconclusive'): + self.valueKind = 'inconclusive' + self.path = element.get('path') + + def setId(self, IdMap): + self.tokvalue = IdMap.get(self._tokvalueId) + self.lifetime = IdMap.get(self._lifetimeId) + self.symbolic = IdMap.get(self._symbolicId) + + def __repr__(self): + attrs = ["intvalue", "tokvalue", "floatvalue", "movedvalue", "uninit", + "bufferSize", "containerSize", "condition", "valueKind"] + return "{}({})".format( + "Value", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + + +class ValueFlow: + """ + ValueFlow::Value class + Each possible value has a ValueFlow::Value item. + Each ValueFlow::Value either has a intvalue or tokvalue + C++ class: + https://cppcheck.sourceforge.io/devinfo/doxyoutput/classValueFlow_1_1Value.html + + Attributes: + values Possible values + """ + + Id = None + values = None + + def __init__(self, element): + self.Id = element.get('id') + self.values = [] + + def __repr__(self): + attrs = ["Id", "values"] + return "{}({})".format( + "ValueFlow", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + + +class Suppression: + """ + Suppression class + This class contains a suppression entry to suppress a warning. + + Attributes + errorId The id string of the error to suppress, can be a wildcard + fileName The name of the file to suppress warnings for, can include wildcards + lineNumber The number of the line to suppress warnings from, can be 0 to represent any line + symbolName The name of the symbol to match warnings for, can include wildcards + lineBegin The first line to suppress warnings from + lineEnd The last line to suppress warnings from + suppressionType The type of suppression which is applied (unique = None (default), file, block, blockBegin, blockEnd, macro) + """ + + errorId = None + fileName = None + lineNumber = None + symbolName = None + lineBegin = None + lineEnd = None + suppressionType = None + + def __init__(self, element): + self.errorId = element.get('errorId') + self.fileName = element.get('fileName') + self.lineNumber = element.get('lineNumber') + self.symbolName = element.get('symbolName') + self.lineBegin = element.get('lineBegin') + self.lineEnd = element.get('lineEnd') + self.suppressionType = element.get('type') + + def __repr__(self): + attrs = ["errorId", "fileName", "lineNumber", "symbolName", "lineBegin", "lineEnd","suppressionType"] + return "{}({})".format( + "Suppression", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + + def isMatch(self, file, line, message, errorId): + # Line Suppression + if ((self.fileName is None or fnmatch(file, self.fileName)) + and (self.suppressionType == None) # Verify use of default suppression type (None = unique) + and (self.lineNumber != None and int(line) == int(self.lineNumber)) + and (self.symbolName is None or fnmatch(message, '*'+self.symbolName+'*')) + and fnmatch(errorId, self.errorId)): + return True + # File Suppression + if ((self.fileName is None or fnmatch(file, self.fileName)) + and (self.suppressionType != None and self.suppressionType == "file") # Verify use of file (global) suppression type + and (self.symbolName is None or fnmatch(message, '*'+self.symbolName+'*')) + and fnmatch(errorId, self.errorId)): + return True + # Block Suppression Mode + if ((self.fileName is None or fnmatch(file, self.fileName)) + and (self.suppressionType != None and self.suppressionType == "block") # Type for Block suppression + and (self.lineBegin != None and int(line) > int(self.lineBegin)) # Code Match is between the Block suppression + and (self.lineEnd != None and int(line) < int(self.lineEnd)) # Code Match is between the Block suppression + and (self.symbolName is None or fnmatch(message, '*'+self.symbolName+'*')) + and fnmatch(errorId, self.errorId)): + return True + # Other Suppression (Globaly set via suppression file or cli command) + if ((self.fileName is None or fnmatch(file, self.fileName)) + and (self.suppressionType is None) + and (self.symbolName is None or fnmatch(message, '*'+self.symbolName+'*')) + and fnmatch(errorId, self.errorId)): + return True + return False + + +class Configuration: + """ + Configuration class + This class contains the directives, tokens, scopes, functions, + variables, value flows, and suppressions for one configuration. + + Attributes: + name Name of the configuration, "" for default + directives List of Directive items + macro_usage List of used macros + preprocessor_if_conditions List of preprocessor if conditions that was evaluated during preprocessing + tokenlist List of Token items + scopes List of Scope items + containers List of Container items + functions List of Function items + variables List of Variable items + valueflow List of ValueFlow values + standards List of Standards values + """ + + name = '' + directives = [] + macro_usage = [] + preprocessor_if_conditions = [] + tokenlist = [] + scopes = [] + containers = [] + functions = [] + variables = [] + typedefInfo = [] + valueflow = [] + standards = None + clang_warnings = [] + + def __init__(self, name): + self.name = name + self.directives = [] + self.macro_usage = [] + self.preprocessor_if_conditions = [] + self.tokenlist = [] + self.scopes = [] + self.containers = [] + self.functions = [] + self.variables = [] + self.typedefInfo = [] + self.valueflow = [] + self.standards = Standards() + self.clang_warnings = [] + + def set_tokens_links(self): + """Set next/previous links between tokens.""" + prev = None + for token in self.tokenlist: + token.previous = prev + if prev: + prev.next = token + prev = token + + def set_id_map(self, arguments): + IdMap = {None: None, '0': None, '00000000': None, '0000000000000000': None, '0x0': None} + for token in self.tokenlist: + IdMap[token.Id] = token + for scope in self.scopes: + IdMap[scope.Id] = scope + for container in self.containers: + IdMap[container.Id] = container + for function in self.functions: + IdMap[function.Id] = function + for variable in self.variables: + IdMap[variable.Id] = variable + for variable in arguments: + IdMap[variable.Id] = variable + for values in self.valueflow: + IdMap[values.Id] = values.values + for token in self.tokenlist: + token.setId(IdMap) + for scope in self.scopes: + scope.setId(IdMap) + #for container in self.containers: + # container.setId(IdMap) + for function in self.functions: + function.setId(IdMap) + for variable in self.variables: + variable.setId(IdMap) + for variable in arguments: + variable.setId(IdMap) + + def setIdMap(self, functions_arguments): + """Set relationships between objects stored in this configuration. + :param functions_arguments: List of Variable objects which are function arguments + """ + self.set_tokens_links() + self.set_id_map(functions_arguments) + + +class Platform: + """ + Platform class + This class contains type sizes + + Attributes: + name Name of the platform: unspecified/native/win32A/win32W/win64/unix32/unix64/platformFile + char_bit CHAR_BIT value + short_bit SHORT_BIT value + int_bit INT_BIT value + long_bit LONG_BIT value + long_long_bit LONG_LONG_BIT value + pointer_bit POINTER_BIT value + """ + + name = '' + char_bit = 0 + short_bit = 0 + int_bit = 0 + long_bit = 0 + long_long_bit = 0 + pointer_bit = 0 + + def __init__(self, platformnode): + self.name = platformnode.get('name') + self.char_bit = int(platformnode.get('char_bit')) + self.short_bit = int(platformnode.get('short_bit')) + self.int_bit = int(platformnode.get('int_bit')) + self.long_bit = int(platformnode.get('long_bit')) + self.long_long_bit = int(platformnode.get('long_long_bit')) + self.pointer_bit = int(platformnode.get('pointer_bit')) + + def __repr__(self): + attrs = ["name", "char_bit", "short_bit", "int_bit", + "long_bit", "long_long_bit", "pointer_bit"] + return "{}({})".format( + "Platform", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + + +class Standards: + """ + Standards class + This class contains versions of standards that were used for the cppcheck + + Attributes: + c C Standard used + cpp C++ Standard used + posix If Posix was used + """ + + c = "" + cpp = "" + posix = False + + def set_c(self, node): + self.c = node.get("version") + + def set_cpp(self, node): + self.cpp = node.get("version") + + def set_posix(self, node): + self.posix = node.get("posix") is not None + + def __repr__(self): + attrs = ["c", "cpp", "posix"] + return "{}({})".format( + "Standards", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + + +class CppcheckData: + """ + Class that makes cppcheck dump data available + Contains a list of Configuration instances + + Attributes: + filename Path to Cppcheck dump file + rawTokens List of rawToken elements + suppressions List of Suppressions + files Source files for elements occurred in this configuration + + To iterate through all configurations use such code: + @code + data = cppcheckdata.parsedump(...) + for cfg in data.configurations: + print('cfg: ' + cfg.name) + @endcode + + To iterate through all tokens in each configuration use such code: + @code + data = cppcheckdata.parsedump(...) + for cfg in data.configurations: + print('cfg: ' + cfg.name) + code = '' + for token in cfg.tokenlist: + code = code + token.str + ' ' + print(' ' + code) + @endcode + + To iterate through all scopes (functions, types, etc) use such code: + @code + data = cppcheckdata.parsedump(...) + for cfg in data.configurations: + print('cfg: ' + cfg.name) + for scope in cfg.scopes: + print(' type:' + scope.type + ' name:' + scope.className) + @endcode + """ + + def __init__(self, filename): + """ + :param filename: Path to Cppcheck dump file + """ + self.filename = filename + self.rawTokens = [] + self.platform = None + self.suppressions = [] + self.files = [] # source files for elements occurred in this configuration + + platform_done = False + rawtokens_done = False + suppressions_done = False + + # Parse general configuration options from node + # We intentionally don't clean node resources here because we + # want to serialize in memory only small part of the XML tree. + for event, node in ElementTree.iterparse(self.filename, events=('start', 'end')): + if platform_done and rawtokens_done and suppressions_done: + break + if node.tag == 'platform' and event == 'start': + self.platform = Platform(node) + platform_done = True + elif node.tag == 'rawtokens' and event == 'end': + for rawtokens_node in node: + if rawtokens_node.tag == 'file': + self.files.append(rawtokens_node.get('name')) + elif rawtokens_node.tag == 'tok': + tok = Token(rawtokens_node) + tok.file = self.files[int(rawtokens_node.get('fileIndex'))] + self.rawTokens.append(tok) + rawtokens_done = True + elif node.tag == 'suppressions' and event == 'end': + for suppressions_node in node: + self.suppressions.append(Suppression(suppressions_node)) + suppressions_done = True + + global current_dumpfile_suppressions + current_dumpfile_suppressions = self.suppressions + + # Set links between rawTokens. + for i in range(len(self.rawTokens)-1): + self.rawTokens[i+1].previous = self.rawTokens[i] + self.rawTokens[i].next = self.rawTokens[i+1] + + @property + def configurations(self): + """ + Return the list of all available Configuration objects. + """ + return list(self.iterconfigurations()) + + def iterconfigurations(self): + """ + Create and return iterator for the available Configuration objects. + The iterator loops over all Configurations in the dump file tree, in document order. + """ + cfg = None + cfg_arguments = [] # function arguments for Configuration node initialization + cfg_function = None + cfg_valueflow = None + + # Iterating in a . + iter_scope_varlist = False + + # Iterating + iter_typedef_info = False + + # Use iterable objects to traverse XML tree for dump files incrementally. + # Iterative approach is required to avoid large memory consumption. + # Calling .clear() is necessary to let the element be garbage collected. + for event, node in ElementTree.iterparse(self.filename, events=('start', 'end')): + # Serialize new configuration node + if node.tag == 'dump': + if event == 'start': + cfg = Configuration(node.get('cfg')) + continue + elif event == 'end': + cfg.setIdMap(cfg_arguments) + yield cfg + cfg = None + cfg_arguments = [] + + elif node.tag == 'clang-warning' and event == 'start': + cfg.clang_warnings.append({'file': node.get('file'), + 'line': int(node.get('line')), + 'column': int(node.get('column')), + 'message': node.get('message')}) + # Parse standards + elif node.tag == "standards" and event == 'start': + continue + elif node.tag == 'c' and event == 'start': + cfg.standards.set_c(node) + elif node.tag == 'cpp' and event == 'start': + cfg.standards.set_cpp(node) + elif node.tag == 'posix' and event == 'start': + cfg.standards.set_posix(node) + + # Parse directives list + elif node.tag == 'directive' and event == 'start': + cfg.directives.append(Directive(node)) + # Parse macro usage + elif node.tag == 'macro' and event == 'start': + cfg.macro_usage.append(MacroUsage(node)) + + # Preprocessor #if/#elif condition + elif node.tag == "if-cond" and event == 'start': + cfg.preprocessor_if_conditions.append(PreprocessorIfCondition(node)) + + # Parse tokens + elif node.tag == 'tokenlist' and event == 'start': + continue + elif node.tag == 'token' and event == 'start': + cfg.tokenlist.append(Token(node)) + + # Parse scopes + elif node.tag == 'scopes' and event == 'start': + continue + elif node.tag == 'scope' and event == 'start': + cfg.scopes.append(Scope(node)) + elif node.tag == 'varlist': + if event == 'start': + iter_scope_varlist = True + elif event == 'end': + iter_scope_varlist = False + + # Parse functions + elif node.tag == 'functionList' and event == 'start': + continue + elif node.tag == 'function': + if event == 'start': + cfg_function = Function(node, cfg.scopes[-1]) + continue + elif event == 'end': + cfg.functions.append(cfg_function) + cfg_function = None + + # Parse function arguments + elif node.tag == 'arg' and event == 'start': + arg_nr = int(node.get('nr')) + arg_variable_id = node.get('variable') + cfg_function.argumentId[arg_nr] = arg_variable_id + + # Parse variables + elif node.tag == 'var' and event == 'start': + if iter_scope_varlist: + cfg.scopes[-1].varlistId.append(node.get('id')) + else: + var = Variable(node) + if var.nameTokenId: + cfg.variables.append(var) + else: + cfg_arguments.append(var) + + # Parse containers + elif node.tag == 'containers' and event == 'start': + continue + elif node.tag == 'container' and event == 'start': + cfg.containers.append(Container(node)) + + # Parse typedef info + elif node.tag == 'typedef-info': + iter_typedef_info = (event == 'start') + elif iter_typedef_info and node.tag == 'info' and event == 'start': + cfg.typedefInfo.append(TypedefInfo(node)) + + # Parse template-token + #elif node.tag == 'TokenAndName' and event == 'start': #todo add processing of containers + # cfg.containers.append(Container(node)) + + # Parse valueflows (list of values) + elif node.tag == 'valueflow' and event == 'start': + continue + elif node.tag == 'values': + if event == 'start': + cfg_valueflow = ValueFlow(node) + continue + elif event == 'end': + cfg.valueflow.append(cfg_valueflow) + cfg_valueflow = None + + # Parse values + elif node.tag == 'value' and event == 'start': + cfg_valueflow.values.append(Value(node)) + + # Remove links to the sibling nodes + node.clear() + + def __repr__(self): + attrs = ["configurations", "platform"] + return "{}({})".format( + "CppcheckData", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + + +# Get function arguments +def getArgumentsRecursive(tok, arguments): + if tok is None: + return + if tok.str == ',': + getArgumentsRecursive(tok.astOperand1, arguments) + getArgumentsRecursive(tok.astOperand2, arguments) + else: + arguments.append(tok) + + +def getArguments(ftok): + if (not ftok.isName) or (ftok.next is None) or ftok.next.str != '(': + return None + args = [] + getArgumentsRecursive(ftok.next.astOperand2, args) + return args + + +def parsedump(filename): + """ + parse a cppcheck dump file + """ + return CppcheckData(filename) + + +def astIsFloat(token): + """ + Check if type of ast node is float/double + """ + + if not token: + return False + if token.str == '.': + return astIsFloat(token.astOperand2) + if token.str in '+-*/%': + return astIsFloat(token.astOperand1) or astIsFloat(token.astOperand2) + if not token.variable: + # float literal? + if token.str[0].isdigit(): + for c in token.str: + if c == 'f' or c == '.' or c == 'E': + return True + return False + typeToken = token.variable.typeStartToken + endToken = token.variable.typeEndToken + while typeToken != endToken: + if typeToken.str == 'float' or typeToken.str == 'double': + return True + typeToken = typeToken.next + if typeToken.str == 'float' or typeToken.str == 'double': + return True + return False + + +class CppCheckFormatter(argparse.HelpFormatter): + """ + Properly formats multiline argument helps + """ + def _split_lines(self, text, width): + # this is the RawTextHelpFormatter._split_lines + if text.startswith('R|'): + return text[2:].splitlines() + return argparse.HelpFormatter._split_lines(self, text, width) + + +def ArgumentParser(): + """ + Returns an argparse argument parser with an already-added + argument definition for -t/--template + """ + parser = argparse.ArgumentParser(formatter_class=CppCheckFormatter) + parser.add_argument('-t', '--template', metavar='', + default='{callstack}: ({severity}) {message}', + help="R|Format the error messages. E.g.\n" + "'{file}:{line},{severity},{id},{message}' or\n" + "'{file}({line}):({severity}) {message}' or\n" + "'{callstack} {message}'\n" + "Pre-defined templates: gcc, vs, edit") + parser.add_argument("dumpfile", nargs='*', + help="Path of dump files from cppcheck.") + parser.add_argument("--cli", + help="Addon is executed from Cppcheck", + action="store_true") + parser.add_argument("--file-list", metavar='', + default=None, + help="file list in a text file") + parser.add_argument("-q", "--quiet", + help='do not print "Checking ..." lines', + action="store_true") + return parser + + +def get_files(args): + """Return dump_files, ctu_info_files""" + all_files = args.dumpfile + if args.file_list: + with open(args.file_list, 'rt') as f: + for line in f.readlines(): + all_files.append(line.rstrip()) + dump_files = [] + ctu_info_files = [] + for f in all_files: + if f.endswith('.ctu-info'): + ctu_info_files.append(f) + else: + dump_files.append(f) + return dump_files, ctu_info_files + +def simpleMatch(token, pattern): + for p in pattern.split(' '): + if not token or token.str != p: + return False + token = token.next + return True + +patterns = { + '%any%': lambda tok: tok, + '%assign%': lambda tok: tok if tok.isAssignmentOp else None, + '%comp%': lambda tok: tok if tok.isComparisonOp else None, + '%name%': lambda tok: tok if tok.isName else None, + '%op%': lambda tok: tok if tok.isOp else None, + '%or%': lambda tok: tok if tok.str == '|' else None, + '%oror%': lambda tok: tok if tok.str == '||' else None, + '%var%': lambda tok: tok if tok.variable else None, + '(*)': lambda tok: tok.link if tok.str == '(' else None, + '[*]': lambda tok: tok.link if tok.str == '[' else None, + '{*}': lambda tok: tok.link if tok.str == '{' else None, + '<*>': lambda tok: tok.link if tok.str == '<' and tok.link else None, +} + +def match_atom(token, p): + if not token: + return None + if not p: + return None + if token.str == p: + return token + if p in ['!', '|', '||', '%', '!=', '*']: + return None + if p in patterns: + return patterns[p](token) + if '|' in p: + for x in p.split('|'): + t = match_atom(token, x) + if t: + return t + elif p.startswith('!!'): + t = match_atom(token, p[2:]) + if not t: + return token + elif p.startswith('**'): + a = p[2:] + t = token + while t: + if match_atom(t, a): + return t + if t.link and t.str in ['(', '[', '<', '{']: + t = t.link + t = t.next + return None + +class MatchResult: + def __init__(self, matches, bindings=None, keys=None): + self.__dict__.update(bindings or {}) + self._matches = matches + self._keys = keys or [] + + def __bool__(self): + return self._matches + + def __nonzero__(self): + return self._matches + + def __getattr__(self, k): + if k in self._keys: + return None + else: + raise AttributeError + +def bind_split(s): + if '@' in s: + p = s.partition('@') + return (p[0], p[2]) + return (s, None) + +def match(token, pattern): + if not pattern: + return MatchResult(False) + end = None + bindings = {} + words = [bind_split(word) for word in pattern.split()] + for p, b in words: + t = match_atom(token, p) + if b: + bindings[b] = token + if not t: + return MatchResult(False, keys=[xx for pp, xx in words]+['end']) + end = t + token = t.next + bindings['end'] = end + return MatchResult(True, bindings=bindings) + +def get_function_call_name_args(token): + """Get function name and arguments for function call + name, args = get_function_call_name_args(tok) + """ + if token is None: + return None, None + if not token.isName or not token.scope.isExecutable: + return None, None + if not simpleMatch(token.next, '('): + return None, None + if token.function: + nametok = token.function.token + if nametok is None: + nametok = token.function.tokenDef + if token in (token.function.token, token.function.tokenDef): + return None, None + name = nametok.str + while nametok.previous and nametok.previous.previous and nametok.previous.str == '::' and nametok.previous.previous.isName: + name = nametok.previous.previous.str + '::' + name + nametok = nametok.previous.previous + scope = token.function.nestedIn + while scope: + if scope.className: + name = scope.className + '::' + name + scope = scope.nestedIn + else: + nametok = token + name = nametok.str + while nametok.previous and nametok.previous.previous and nametok.previous.str == '::' and nametok.previous.previous.isName: + name = nametok.previous.previous.str + '::' + name + nametok = nametok.previous.previous + return name, getArguments(token) + +def is_suppressed(location, message, errorId): + for suppression in current_dumpfile_suppressions: + if suppression.isMatch(location.file, location.linenr, message, errorId): + return True + return False + +def log_checker(message, addon): + if '--cli' in sys.argv: + msg = { 'addon': addon, + 'severity': 'none', + 'message': message, + 'errorId': 'logChecker'} + sys.stdout.write(json.dumps(msg) + '\n') + +def reportError(location, severity, message, addon, errorId, extra='', columnOverride=None): + if '--cli' in sys.argv: + msg = { 'file': location.file, + 'linenr': location.linenr, + 'column': location.column if columnOverride is None else columnOverride, + 'severity': severity, + 'message': message, + 'addon': addon, + 'errorId': errorId, + 'extra': extra} + sys.stdout.write(json.dumps(msg) + '\n') + else: + if is_suppressed(location, message, '%s-%s' % (addon, errorId)): + return + loc = '[%s:%i]' % (location.file, location.linenr) + if len(extra) > 0: + message += ' (' + extra + ')' + sys.stderr.write('%s (%s) %s [%s-%s]\n' % (loc, severity, message, addon, errorId)) + global EXIT_CODE + EXIT_CODE = 1 + +def reportSummary(dumpfile, summary_type, summary_data): + # dumpfile ends with ".dump" + ctu_info_file = dumpfile[:-4] + "ctu-info" + with open(ctu_info_file, 'at') as f: + msg = {'summary': summary_type, 'data': summary_data} + f.write(json.dumps(msg) + '\n') + + +def get_path_premium_addon(): + p = pathlib.Path(sys.argv[0]).parent.parent + + for ext in ('.exe', ''): + p1 = os.path.join(p, 'premiumaddon' + ext) + p2 = os.path.join(p, 'cppcheck' + ext) + if os.path.isfile(p1) and os.path.isfile(p2): + return p1 + return None + + +def cmd_output(cmd): + with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p: + comm = p.communicate() + out = comm[0] + if p.returncode == 1 and len(comm[1]) > 2: + out = comm[1] + return out.decode(encoding='utf-8', errors='ignore') diff --git a/cppcheck-2.14.0/addons/doc/img/cppcheck-gui-addons.png b/cppcheck-2.14.0/addons/doc/img/cppcheck-gui-addons.png new file mode 100644 index 0000000000000000000000000000000000000000..b8a22ba953c7725a5c931146e44d9e43f14fd0ec GIT binary patch literal 23645 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 NJYD@<);T3K0RXq~G|2z} literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/addons/doc/y2038.txt b/cppcheck-2.14.0/addons/doc/y2038.txt new file mode 100644 index 00000000..990b24bd --- /dev/null +++ b/cppcheck-2.14.0/addons/doc/y2038.txt @@ -0,0 +1,151 @@ +README of the Y2038 cppcheck addon +================================== + +Contents + +1. What is Y2038? +2. What is the Y2038 cppcheck addon? +3. How does the Y2038 cppcheck addon work? +4. How to use the Y2038 cppcheck addon + +--- + +1. What is Y2038? + +In a few words: + +In Linux, the current date and time is kept as the number of seconds elapsed +since the Unix epoch, that is, since January 1st, 1970 at 00:00:00 GMT. + +Most of the time, this representation is stored as a 32-bit signed quantity. + +On January 19th, 2038 at 03:14:07 GMT, such 32-bit representations will reach +their maximum positive value. + +What happens then is unpredictable: system time might roll back to December +13th, 1901 at 19:55:13, or it might keep running on until February 7th, 2106 +at 06:28:15 GMT, or the computer may freeze, or just about anything you can +think of, plus a few ones you can't. + +The workaround for this is to switch to a 64-bit signed representation of time +as seconds from the Unix epoch. This representation will work for more than 250 +billion years. + +Working around Y2038 requires fixing the Linux kernel, the C libraries, and +any user code around which uses 32-bit epoch representations. + +There is Y2038-proofing work in progress on the Linux and GNU glibc front. + +2. What is the Y2038 cppcheck addon? + +The Y2038 cppcheck addon is a tool to help detect code which might need fixing +because it is Y2038-unsafe. This may be because it uses types or functions from +GNU libc or from the Linux kernel which are known not to be Y2038-proof. + +3. How does the Y2038 cppcheck addon work? + +The Y2038 cppcheck addon takes XML dumps produced by cppcheck from source code +files and looks for the names of types or functions which are known to be Y2038- +unsafe, and emits diagnostics whenever it finds one. + +Of course, this is of little use if your code uses a Y2038-proof glibc and +correctly configured Y2038-proof time support. + +This is why y2038.py takes into account two preprocessor directives: +_TIME_BITS and __USE_TIME_BITS64. + +_TIME_BITS is defined equal to 64 by user code when it wants 64-bit time +support from the GNU glibc. Code which does not define _TIME_BITS equal to 64 +(or defines it to something else than 64) runs a risk of not being Y2038-proof. + +__USE_TIME_BITS64 is defined by the GNU glibc when it actually provides 64-bit +time support. When this is defined, then all glibc symbols, barring bugs, are +Y2038-proof (but your code might have its own Y2038 bugs, if it handles signed +32-bit Unix epoch values). + +The Y2038 cppcheck performs the following checks: + + 1. Upon meeting a definition for _TIME_BITS, if that definition does not + set it equal to 64, this error diagnostic is emitted: + + Error: _TIME_BITS must be defined equal to 64 + + This case is very unlikely but might result from a typo, so pointing + it out is quite useful. Note that definitions of _TIME_BITS as an + expression evaluating to 64 will be flagged too. + + 2. Upon meeting a definition for _USE_TIME_BITS64, if _TIME_BITS is not + defined equal to 64, this information diagnostic is emitted: + + Warning: _USE_TIME_BITS64 is defined but _TIME_BITS was not + + This reflects the fact that even though the glibc checked default to + 64-bit time support, this was not requested by the user code, and + therefore the user code might fail Y2038 if built against a glibc + which defaults to 32-bit time support. + + 3. Upon meeting a symbol (type or function) which is known to be Y2038- + unsafe, if _USE_TIME_BITS64 is undefined or _TIME_BITS not properly + defined, this warning diagnostic is emitted: + + Warning: is Y2038-unsafe + + This reflects the fact that the user code is referring to a symbol + which, when glibc defaults to 32-bit time support, might fail Y2038. + +General note: y2038.py will handle multiple configurations, and will +emit diagnostics for each configuration in turn. + +4. How to use the Y2038 cppcheck addon + +The Y2038 cppcheck addon is used like any other cppcheck addon: + + cppcheck --dump file1.c [ file2.c [...]]] + y2038.py file1.c [ file2.c [...]]] + +Sample test C file is provided: + + test/y2038-test-1-bad-time-bits.c + test/y2038-test-2-no-time-bits.c + test/y2038-test-3-no-use-time-bits.c + test/y2038-test-4-good.c + +These cover the cases described above. You can run them through cppcheck +and y2038.py to see for yourself how the addon diagnostics look like. If +this README is not outdated (and if it is, feel free to submit a patch), +you can run cppcheck on these files as on any others: + + cppcheck --dump addons/y2038/test/y2038-*.c + y2038.py addons/y2038/test/y2038-*.dump + +If you have not installed cppcheck yet, you will have to run these +commands from the root of the cppcheck repository: + + make + sudo make install + ./cppcheck --dump addons/y2038/test/y2038-*.c + PYTHONPATH=addons python addons/y2038/y2038.py addons/y2038/test/y2038-*.c.dump + +In both cases, y2038.py execution should result in the following: + +Checking addons/y2038/test/y2038-test-1-bad-time-bits.c.dump... +Checking addons/y2038/test/y2038-test-1-bad-time-bits.c.dump, config ""... +Checking addons/y2038/test/y2038-test-2-no-time-bits.c.dump... +Checking addons/y2038/test/y2038-test-2-no-time-bits.c.dump, config ""... +Checking addons/y2038/test/y2038-test-3-no-use-time-bits.c.dump... +Checking addons/y2038/test/y2038-test-3-no-use-time-bits.c.dump, config ""... +Checking addons/y2038/test/y2038-test-4-good.c.dump... +Checking addons/y2038/test/y2038-test-4-good.c.dump, config ""... +# Configuration "": +# Configuration "": +[addons/y2038/test/y2038-test-1-bad-time-bits.c:8]: (error) _TIME_BITS must be defined equal to 64 +[addons/y2038/test/y2038-inc.h:9]: (warning) _USE_TIME_BITS64 is defined but _TIME_BITS was not +[addons/y2038/test/y2038-test-1-bad-time-bits.c:10]: (information) addons/y2038/test/y2038-inc.h was included from here +[addons/y2038/test/y2038-inc.h:9]: (warning) _USE_TIME_BITS64 is defined but _TIME_BITS was not +[addons/y2038/test/y2038-test-2-no-time-bits.c:8]: (information) addons/y2038/test/y2038-inc.h was included from here +[addons/y2038/test/y2038-test-3-no-use-time-bits.c:13]: (warning) timespec is Y2038-unsafe +[addons/y2038/test/y2038-test-3-no-use-time-bits.c:15]: (warning) clock_gettime is Y2038-unsafe + +Note: y2038.py recognizes option --template as cppcheck does, including +pre-defined templates 'gcc', 'vs' and 'edit'. The short form -t is also +recognized. diff --git a/cppcheck-2.14.0/addons/findcasts.py b/cppcheck-2.14.0/addons/findcasts.py new file mode 100644 index 00000000..d4842b22 --- /dev/null +++ b/cppcheck-2.14.0/addons/findcasts.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# +# Locate casts in the code +# + +import cppcheck + +@cppcheck.checker +def cast(cfg, data): + for token in cfg.tokenlist: + if token.str != '(' or not token.astOperand1 or token.astOperand2: + continue + + # Is it a lambda? + if token.astOperand1.str == '{': + continue + + # we probably have a cast.. if there is something inside the parentheses + # there is a cast. Otherwise this is a function call. + typetok = token.next + if not typetok.isName: + continue + + # cast number => skip output + if token.astOperand1.isNumber: + continue + + # void cast => often used to suppress compiler warnings + if typetok.str == 'void': + continue + + cppcheck.reportError(token, 'information', 'found a cast') diff --git a/cppcheck-2.14.0/addons/misc.py b/cppcheck-2.14.0/addons/misc.py new file mode 100644 index 00000000..15a742c2 --- /dev/null +++ b/cppcheck-2.14.0/addons/misc.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +# +# Misc: Uncategorized checks that might be moved to some better addon later +# +# Example usage of this addon (scan a sourcefile main.cpp) +# cppcheck --dump main.cpp +# python misc.py main.cpp.dump + +import cppcheckdata +import sys +import re + +DEBUG = ('-debug' in sys.argv) +VERIFY = ('-verify' in sys.argv) +VERIFY_EXPECTED = [] +VERIFY_ACTUAL = [] + +def reportError(token, severity, msg, id): + if id == 'debug' and DEBUG == False: + return + if VERIFY: + VERIFY_ACTUAL.append(str(token.linenr) + ':' + id) + else: + cppcheckdata.reportError(token, severity, msg, 'misc', id) + +def simpleMatch(token, pattern): + return cppcheckdata.simpleMatch(token, pattern) + +# Get function arguments +def getArgumentsRecursive(tok, arguments): + if tok is None: + return + if tok.str == ',': + getArgumentsRecursive(tok.astOperand1, arguments) + getArgumentsRecursive(tok.astOperand2, arguments) + else: + arguments.append(tok) + +def getArguments(ftok): + arguments = [] + getArgumentsRecursive(ftok.astOperand2, arguments) + return arguments + +def isStringLiteral(tokenString): + return tokenString.startswith('"') + +# check data +def stringConcatInArrayInit(data): + # Get all string macros + stringMacros = [] + for cfg in data.iterconfigurations(): + for directive in cfg.directives: + res = re.match(r'#define[ ]+([A-Za-z0-9_]+)[ ]+".*', directive.str) + if res: + macroName = res.group(1) + if macroName not in stringMacros: + stringMacros.append(macroName) + + # Check code + arrayInit = False + for i in range(len(data.rawTokens)): + if i < 2: + continue + tok1 = data.rawTokens[i-2].str + tok2 = data.rawTokens[i-1].str + tok3 = data.rawTokens[i-0].str + if tok3 == '}': + arrayInit = False + elif tok1 == ']' and tok2 == '=' and tok3 == '{': + arrayInit = True + elif arrayInit and (tok1 in [',', '{']): + isString2 = (isStringLiteral(tok2) or (tok2 in stringMacros)) + isString3 = (isStringLiteral(tok3) or (tok3 in stringMacros)) + if isString2 and isString3: + reportError(data.rawTokens[i], 'style', 'String concatenation in array initialization, missing comma?', 'stringConcatInArrayInit') + + +def implicitlyVirtual(data): + for cfg in data.iterconfigurations(): + for function in cfg.functions: + if function.isImplicitlyVirtual is None: + continue + if not function.isImplicitlyVirtual: + continue + reportError(function.tokenDef, 'style', 'Function \'' + function.name + '\' overrides base class function but is not marked with \'virtual\' keyword.', 'implicitlyVirtual') + +def ellipsisStructArg(data): + for cfg in data.iterconfigurations(): + for tok in cfg.tokenlist: + if tok.str != '(': + continue + if tok.astOperand1 is None or tok.astOperand2 is None: + continue + if tok.astOperand2.str != ',': + continue + if tok.scope.type in ['Global', 'Class']: + continue + if tok.astOperand1.function is None: + continue + for argnr, argvar in tok.astOperand1.function.argument.items(): + if argnr < 1: + continue + if not simpleMatch(argvar.typeStartToken, '...'): + continue + callArgs = getArguments(tok) + for i in range(argnr-1, len(callArgs)): + valueType = callArgs[i].valueType + if valueType is None: + argStart = callArgs[i].previous + while argStart.str != ',': + if argStart.str == ')': + argStart = argStart.link + argStart = argStart.previous + argEnd = callArgs[i] + while argEnd.str != ',' and argEnd.str != ')': + if argEnd.str == '(': + argEnd = argEnd.link + argEnd = argEnd.next + expression = '' + argStart = argStart.next + while argStart != argEnd: + expression = expression + argStart.str + argStart = argStart.next + reportError(tok, 'debug', 'Bailout, unknown argument type for argument \'' + expression + '\'.', 'debug') + continue + if valueType.pointer > 0: + continue + if valueType.type != 'record' and valueType.type != 'container': + continue + reportError(tok, 'style', 'Passing record to ellipsis function \'' + tok.astOperand1.function.name + '\'.', 'ellipsisStructArg') + break + +for arg in sys.argv[1:]: + if arg in ['-debug', '-verify', '--cli']: + continue + + print("Checking %s..." % arg) + data = cppcheckdata.CppcheckData(arg) + + if VERIFY: + VERIFY_ACTUAL = [] + VERIFY_EXPECTED = [] + for tok in data.rawTokens: + if tok.str.startswith('//'): + for word in tok.str[2:].split(' '): + if word in ['stringConcatInArrayInit', 'implicitlyVirtual', 'ellipsisStructArg']: + VERIFY_EXPECTED.append(str(tok.linenr) + ':' + word) + + stringConcatInArrayInit(data) + implicitlyVirtual(data) + ellipsisStructArg(data) + + if VERIFY: + for expected in VERIFY_EXPECTED: + if expected not in VERIFY_ACTUAL: + print('Expected but not seen: ' + expected) + sys.exit(1) + for actual in VERIFY_ACTUAL: + if actual not in VERIFY_EXPECTED: + print('Not expected: ' + actual) + sys.exit(1) + +sys.exit(cppcheckdata.EXIT_CODE) diff --git a/cppcheck-2.14.0/addons/misra.py b/cppcheck-2.14.0/addons/misra.py new file mode 100644 index 00000000..990059c4 --- /dev/null +++ b/cppcheck-2.14.0/addons/misra.py @@ -0,0 +1,5000 @@ +#!/usr/bin/env python3 +# +# MISRA C 2012 checkers +# Partially reused for "MISRA C++ 2008" checking +# +# Example usage of this addon (scan a sourcefile main.cpp) +# cppcheck --dump main.cpp +# python misra.py --rule-texts= main.cpp.dump +# +# Limitations: This addon is released as open source. Rule texts can't be freely +# distributed. https://www.misra.org.uk/forum/viewtopic.php?f=56&t=1189 +# +# The MISRA standard documents may be obtained from https://www.misra.org.uk +# +# Total number of rules: 143 + +from __future__ import print_function + +import cppcheckdata +import itertools +import json +import sys +import re +import os +import argparse +import codecs +import string +import copy + +try: + from itertools import izip as zip +except ImportError: + pass + +import misra_9 + +def grouped(iterable, n): + """s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ...""" + return zip(*[iter(iterable)] * n) + + +INT_TYPES = ['bool', 'char', 'short', 'int', 'long', 'long long'] + + +STDINT_TYPES = ['%s%d_t' % (n, v) for n, v in itertools.product( + ['int', 'uint', 'int_least', 'uint_least', 'int_fast', 'uint_fast'], + [8, 16, 32, 64])] + + +typeBits = { + 'CHAR': None, + 'SHORT': None, + 'INT': None, + 'LONG': None, + 'LONG_LONG': None, + 'POINTER': None +} + + +def isUnsignedType(ty): + return ty == 'unsigned' or ty.startswith('uint') + + +def simpleMatch(token, pattern): + return cppcheckdata.simpleMatch(token, pattern) + + +def rawlink(rawtoken): + if rawtoken.str == '}': + indent = 0 + while rawtoken: + if rawtoken.str == '}': + indent = indent + 1 + elif rawtoken.str == '{': + indent = indent - 1 + if indent == 0: + break + rawtoken = rawtoken.previous + else: + rawtoken = None + return rawtoken + + +# Identifiers described in Section 7 "Library" of C90 Standard +# Based on ISO/IEC9899:1990 Annex D -- Library summary and +# Annex E -- Implementation limits. +C90_STDLIB_IDENTIFIERS = { + # D.1 Errors + 'errno.h': ['EDOM', 'ERANGE', 'errno'], + # D.2 Common definitions + 'stddef.h': ['NULL', 'offsetof', 'ptrdiff_t', 'size_t', 'wchar_t'], + # D.3 Diagnostics + 'assert.h': ['NDEBUG', 'assert'], + # D.4 Character handling + 'ctype.h': [ + 'isalnum', 'isalpha', 'isblank', 'iscntrl', 'isdigit', + 'isgraph', 'islower', 'isprint', 'ispunct', 'isspace', + 'isupper', 'isxdigit', 'tolower', 'toupper', + ], + # D.5 Localization + 'locale.h': [ + 'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MONETARY', + 'LC_NUMERIC', 'LC_TIME', 'NULL', 'lconv', + 'setlocale', 'localeconv', + ], + # D.6 Mathematics + 'math.h': [ + 'HUGE_VAL', 'acos', 'asin' , 'atan2', 'cos', 'sin', 'tan', 'cosh', + 'sinh', 'tanh', 'exp', 'frexp', 'ldexp', 'log', 'loglO', 'modf', + 'pow', 'sqrt', 'ceil', 'fabs', 'floor', 'fmod', + ], + # D.7 Nonlocal jumps + 'setjmp.h': ['jmp_buf', 'setjmp', 'longjmp'], + # D.8 Signal handling + 'signal.h': [ + 'sig_atomic_t', 'SIG_DFL', 'SIG_ERR', 'SIG_IGN', 'SIGABRT', 'SIGFPE', + 'SIGILL', 'SIGINT', 'SIGSEGV', 'SIGTERM', 'signal', 'raise', + ], + # D.9 Variable arguments + 'stdarg.h': ['va_list', 'va_start', 'va_arg', 'va_end'], + # D.10 Input/output + 'stdio.h': [ + '_IOFBF', '_IOLBF', '_IONBF', 'BUFSIZ', 'EOF', 'FILE', 'FILENAME_MAX', + 'FOPEN_MAX', 'fpos_t', 'L_tmpnam', 'NULL', 'SEEK_CUR', 'SEEK_END', + 'SEEK_SET', 'size_t', 'stderr', 'stdin', 'stdout', 'TMP_MAX', + 'remove', 'rename', 'tmpfile', 'tmpnam', 'fclose', 'fflush', 'fopen', + 'freopen', 'setbuf', 'setvbuf', 'fprintf', 'fscanf', 'printf', + 'scanf', 'sprintf', 'sscanf', 'vfprintf', 'vprintf', 'vsprintf', + 'fgetc', 'fgets', 'fputc', 'fputs', 'getc', 'getchar', 'gets', 'putc', + 'putchar', 'puts', 'ungetc', 'fread', 'fwrite', 'fgetpos', 'fseek', + 'fsetpos', 'rewind', 'clearerr', 'feof', 'ferror', 'perror', + ], + # D.11 General utilities + 'stdlib.h': [ + 'EXIT_FAILURE', 'EXIT_SUCCESS', 'MB_CUR_MAX', 'NULL', 'RAND_MAX', + 'div_t', 'ldiv_t', 'wchar_t', 'atof', 'atoi', 'strtod', 'rand', + 'srand', 'calloc', 'free', 'malloc', 'realloc', 'abort', 'atexit', + 'exit', 'getenv', 'system', 'bsearch', 'qsort', 'abs', 'div', 'ldiv', + 'mblen', 'mbtowc', 'wctomb', 'mbstowcs', 'wcstombs', + ], + # D.12 String handling + 'string.h': [ + 'NULL', 'size_t', 'memcpy', 'memmove', 'strcpy', 'strncpy', 'strcat', + 'strncat', 'memcmp', 'strcmp', 'strcoll', 'strncmp', 'strxfrm', + 'memchr', 'strchr', 'strcspn', 'strpbrk', 'strrchr', 'strspn', + 'strstr', 'strtok', 'memset', 'strerror', 'strlen', + ], + # D.13 Date and time + 'time.h': [ + 'CLK_TCK', 'NULL', 'clock_t', 'time_t', 'size_t', 'tm', 'clock', + 'difftime', 'mktime', 'time', 'asctime', 'ctime', 'gmtime', + 'localtime', 'strftime', + ], + # Annex E: Implementation limits + 'limits.h': [ + 'CHAR_BIT', 'SCHAR_MIN', 'SCHAR_MAX', 'UCHAR_MAX', 'CHAR_MIN', + 'CHAR_MAX', 'MB_LEN_MAX', 'SHRT_MIN', 'SHRT_MAX', 'USHRT_MAX', + 'INT_MIN', 'INT_MAX', 'UINT_MAX', 'LONG_MIN', 'LONG_MAX', 'ULONG_MAX', + ], + 'float.h': [ + 'FLT_ROUNDS', 'FLT_RADIX', 'FLT_MANT_DIG', 'DBL_MANT_DIG', + 'LDBL_MANT_DIG', 'DECIMAL_DIG', 'FLT_DIG', 'DBL_DIG', 'LDBL_DIG', + 'DBL_MIN_EXP', 'LDBL_MIN_EXP', 'FLT_MIN_10_EXP', 'DBL_MIN_10_EXP', + 'LDBL_MIN_10_EXP', 'FLT_MAX_EXP', 'DBL_MAX_EXP', 'LDBL_MAX_EXP', + 'FLT_MAX_10_EXP', 'DBL_MAX_10_EXP', 'LDBL_MAX_10_EXP', 'FLT_MAX', + 'DBL_MAX', 'LDBL_MAX', 'FLT_MIN', 'DBL_MIN', 'LDBL_MIN', + 'FLT_EPSILON', 'DBL_EPSILON', 'LDBL_EPSILON' + ], +} + + +# Identifiers described in Section 7 "Library" of C99 Standard +# Based on ISO/IEC 9899 WF14/N1256 Annex B -- Library summary +C99_STDLIB_IDENTIFIERS = { + # B.1 Diagnostics + 'assert.h': C90_STDLIB_IDENTIFIERS['assert.h'], + # B.2 Complex + 'complex.h': [ + 'complex', 'imaginary', 'I', '_Complex_I', '_Imaginary_I', + 'CX_LIMITED_RANGE', + 'cacos', 'cacosf', 'cacosl', + 'casin', 'casinf', 'casinl', + 'catan', 'catanf', 'catanl', + 'ccos', 'ccosf', 'ccosl', + 'csin', 'csinf', 'csinl', + 'ctan', 'ctanf', 'ctanl', + 'cacosh', 'cacoshf', 'cacoshl', + 'casinh', 'casinhf', 'casinhl', + 'catanh', 'catanhf', 'catanhl', + 'ccosh', 'ccoshf', 'ccoshl', + 'csinh', 'csinhf', 'csinhl', + 'ctanh', 'ctanhf', 'ctanhl', + 'cexp', 'cexpf', 'cexpl', + 'clog', 'clogf', 'clogl', + 'cabs', 'cabsf', 'cabsl', + 'cpow', 'cpowf', 'cpowl', + 'csqrt', 'csqrtf', 'csqrtl', + 'carg', 'cargf', 'cargl', + 'cimag', 'cimagf', 'cimagl', + 'conj', 'conjf', 'conjl', + 'cproj', 'cprojf', 'cprojl', + 'creal', 'crealf', 'creall', + ], + # B.3 Character handling + 'ctype.h': C90_STDLIB_IDENTIFIERS['ctype.h'], + # B.4 Errors + 'errno.h': C90_STDLIB_IDENTIFIERS['errno.h'] + ['EILSEQ'], + # B.5 Floating-point environment + 'fenv.h': [ + 'fenv_t', 'FE_OVERFLOW', 'FE_TOWARDZERO', + 'fexcept_t', 'FE_UNDERFLOW', 'FE_UPWARD', + 'FE_DIVBYZERO', 'FE_ALL_EXCEPT', 'FE_DFL_ENV', + 'FE_INEXACT', 'FE_DOWNWARD', + 'FE_INVALID', 'FE_TONEAREST', + 'FENV_ACCESS', + 'feclearexcept', 'fegetexceptflag', 'fegetround', + 'fesetround', 'fegetenv', 'feholdexcept', + 'fesetenv', 'feupdateenv', + ], + # B.6 Characteristics of floating types + 'float.h': C90_STDLIB_IDENTIFIERS['float.h'] + ['FLT_EVAL_METHOD'], + # B.7 Format conversion of integer types + 'inttypes.h': [ + 'imaxdiv_t', 'imaxabs', 'imaxdiv', 'strtoimax', + 'strtoumax', 'wcstoimax', 'wcstoumax', + ], + # B.8 Alternative spellings + 'iso646.h': [ + 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not', 'not_eq', + 'or', 'or_eq', 'xor', 'xor_eq', + ], + # B.9 Size of integer types + 'limits.h': C90_STDLIB_IDENTIFIERS['limits.h'] + + ['LLONG_MIN', 'LLONG_MAX', 'ULLONG_MAX'], + # B.10 Localization + 'locale.h': C90_STDLIB_IDENTIFIERS['locale.h'], + # B.11 Mathematics + 'math.h': C90_STDLIB_IDENTIFIERS['math.h'] + [ + 'float_t', 'double_t', 'HUGE_VAL', 'HUGE_VALF', 'HUGE_VALL', + 'INFINITY', 'NAN', 'FP_INFINITE', 'FP_NAN', 'FP_NORMAL', + 'FP_SUBNORMAL', 'FP_ZERO', 'FP_FAST_FMA', 'FP_FAST_FMAF', + 'FP_FAST_FMAL', 'FP_ILOGB0', 'FP_ILOGBNAN', 'MATH_ERRNO', + 'MATH_ERREXCEPT', 'math_errhandling', 'FP_CONTRACT', 'fpclassify', + 'isfinite', 'isinf', 'isnan', 'isnormal', 'signbit', 'acosf', 'acosl', + 'asinf', 'asinl', 'atanf', 'atanl', 'atan2', 'atan2f', 'atan2l', + 'cosf', 'cosl', 'sinf', 'sinl', 'tanf', 'tanl', 'acosh', 'acoshf', + 'acoshl', 'asinh', 'asinhf', 'asinhl', 'atanh', 'atanhf', 'atanhl', + 'cosh', 'coshf', 'coshl', 'sinh', 'sinhf', 'sinhl', 'tanh', 'tanhf', + 'tanhl', 'expf', 'expl', 'exp2', 'exp2f', 'exp2l', 'expm1', 'expm1f', + 'expm1l', 'frexpf', 'frexpl', 'ilogb', 'ilogbf', 'ilogbl', 'float', + 'ldexpl', 'logf', 'logl', 'log10f', 'log10l', 'log1p', 'log1pf', + 'log1pl', 'log2', 'log2f', 'log2l', 'logb', 'logbf', 'logbl', 'modff', + 'modfl', 'scalbn', 'scalbnf', 'scalbnl', 'scalbln', 'scalblnf', + 'scalblnl', 'hypotl', 'powf', 'powl', 'sqrtf', 'sqrtl', 'erf', 'erff', + 'erfl', 'erfc', 'erfcf', 'erfcl', 'lgamma', 'lgammaf', 'lgammal', + 'tgamma', 'tgammaf', 'tgammal', 'ceilf', 'ceill', 'floorf', 'floorl', + 'nearbyint', 'nearbyintf', 'nearbyintl', 'rint', 'rintf', 'rintl', + 'lrint', 'lrintf', 'lrintl', 'llrint', 'llrintf', 'llrintl', 'round', + 'roundf', 'roundl', 'lround', 'lroundf', 'lroundl', 'llround', + 'llroundf', 'llroundl', 'trunc', 'truncf', 'truncl', 'fmodf', 'fmodl', + 'remainder', 'remainderf', 'remainderl', 'remquo', 'remquof', + 'remquol', 'copysign', 'copysignf', 'copysignl', 'nan', 'nanf', + 'nanl', 'nextafter', 'nextafterf', 'nextafterl', 'nexttoward', + 'nexttowardf', 'nexttowardl', 'fdim', 'fdimf', 'fdiml', 'fmax', + 'fmaxf', 'fmaxl', 'fmin', 'fminf', 'fminl', 'fmal', 'isgreater', + 'isgreaterequal', 'isless', 'islessequal', 'islessgreater', + 'isunordered', + ], + # B.12 Nonlocal jumps + 'setjmp.h': C90_STDLIB_IDENTIFIERS['setjmp.h'], + # B.13 Signal handling + 'signal.h': C90_STDLIB_IDENTIFIERS['signal.h'], + # B.14 Variable arguments + 'stdarg.h': C90_STDLIB_IDENTIFIERS['stdarg.h'] + ['va_copy'], + # B.15 Boolean type and values + 'stdbool.h': ['bool', 'true', 'false', '__bool_true_false_are_defined'], + # B.16 Common definitions + 'stddef.h': C90_STDLIB_IDENTIFIERS['stddef.h'], + # B.17 Integer types + 'stdint.h': [ + 'intptr_t', 'uintptr_t', 'intmax_t', 'uintmax_t', 'INTN_MIN', + 'INTN_MAX', 'UINTN_MAX', 'INT_LEASTN_MIN', 'INT_LEASTN_MAX', + 'UINT_LEASTN_MAX', 'INT_FASTN_MIN', 'INT_FASTN_MAX', 'UINT_FASTN_MAX', + 'INTPTR_MIN', 'INTPTR_MAX', 'UINTPTR_MAX', 'INTMAX_MIN', 'INTMAX_MAX', + 'UINTMAX_MAX', 'PTRDIFF_MIN', 'PTRDIFF_MAX', 'SIG_ATOMIC_MIN', + 'SIG_ATOMIC_MAX', 'SIZE_MAX', 'WCHAR_MIN', 'WCHAR_MAX', 'WINT_MIN', + 'WINT_MAX', 'INTN_C', 'UINTN_C', 'INTMAX_C', 'UINTMAX_C', + ] + STDINT_TYPES, + # B.18 Input/output + 'stdio.h': C90_STDLIB_IDENTIFIERS['stdio.h'] + [ + 'mode', 'restrict', 'snprintf', 'vfscanf', 'vscanf', + 'vsnprintf', 'vsscanf', + ], + # B.19 General utilities + 'stdlib.h': C90_STDLIB_IDENTIFIERS['stdlib.h'] + [ + '_Exit', 'labs', 'llabs', 'lldiv', 'lldiv_t', 'strtof', 'strtol', + 'strtold', 'strtoll', 'strtoul', 'strtoull' + ], + # B.20 String handling + 'string.h': C90_STDLIB_IDENTIFIERS['string.h'], + # B.21 Type-generic math + 'tgmath.h': [ + 'acos', 'asin', 'atan', 'acosh', 'asinh', 'atanh', 'cos', 'sin', 'tan', + 'cosh', 'sinh', 'tanh', 'exp', 'log', 'pow', 'sqrt', 'fabs', 'atan2', + 'cbrt', 'ceil', 'copysign', 'erf', 'erfc', 'exp2', 'expm1', 'fdim', + 'floor', 'fma', 'fmax', 'fmin', 'fmod', 'frexp', 'hypot', 'ilogb', + 'ldexp', 'lgamma', 'llrint', 'llround', 'log10', 'log1p', 'log2', + 'logb', 'lrint', 'lround', 'nearbyint', 'nextafter', 'nexttoward', + 'remainder', 'remquo', 'rint', 'round', 'scalbn', 'scalbln', 'tgamma', + 'trunc', 'carg', 'cimag', 'conj', 'cproj', 'creal', + ], + # B.22 Date and time + 'time.h': C90_STDLIB_IDENTIFIERS['time.h'] + ['CLOCKS_PER_SEC'], + # B.23 Extended multibyte/wide character utilities + 'wchar.h': [ + 'wchar_t', 'size_t', 'mbstate_t', 'wint_t', 'tm', 'NULL', 'WCHAR_MAX', + 'WCHAR_MIN', 'WEOF', 'fwprintf', 'fwscanf', 'swprintf', 'swscanf', + 'vfwprintf', 'vfwscanf', 'vswprintf', 'vswscanf', 'vwprintf', + 'vwscanf', 'wprintf', 'wscanf', 'fgetwc', 'fgetws', 'fputwc', 'fputws', + 'fwide', 'getwc', 'getwchar', 'putwc', 'putwchar', 'ungetwc', 'wcstod', + 'wcstof', 'double', 'int', 'long', 'long', 'long', 'wcscpy', 'wcsncpy', + 'wmemcpy', 'wmemmove', 'wcscat', 'wcsncat', 'wcscmp', 'wcscoll', + 'wcsncmp', 'wcsxfrm', 'wmemcmp', 'wcschr', 'wcscspn', 'wcspbrk', + 'wcsrchr', 'wcsspn', 'wcsstr', 'wcstok', 'wmemchr', 'wcslen', + 'wmemset', 'wcsftime', 'btowc', 'wctob', 'mbsinit', 'mbrlen', + 'mbrtowc', 'wcrtomb', 'mbsrtowcs', 'wcsrtombs', + ], +} + + +def isStdLibId(id_, standard='c99'): + id_lists = [] + if standard == 'c89': + id_lists = C90_STDLIB_IDENTIFIERS.values() + elif standard in ('c99', 'c11'): + id_lists = C99_STDLIB_IDENTIFIERS.values() + for l in id_lists: + if id_ in l: + return True + return False + + +# Reserved keywords defined in ISO/IEC9899:1990 -- ch 6.1.1 +C90_KEYWORDS = { + 'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', + 'double', 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', + 'int', 'long', 'register', 'return', 'short', 'signed', + 'sizeof', 'static', 'struct', 'switch', 'typedef', 'union', 'unsigned', + 'void', 'volatile', 'while' +} + + +# Reserved keywords defined in ISO/IEC 9899 WF14/N1256 -- ch. 6.4.1 +C99_ADDED_KEYWORDS = { + 'inline', 'restrict', '_Bool', '_Complex', '_Imaginary', + 'bool', 'complex', 'imaginary' +} + +C11_ADDED_KEYWORDS = { + '_Alignas', '_Alignof', '_Atomic', '_Generic', '_Noreturn', + '_Statis_assert', '_Thread_local' , + 'alignas', 'alignof', 'noreturn', 'static_assert' +} + +def isKeyword(keyword, standard='c99'): + kw_set = {} + if standard == 'c89': + kw_set = C90_KEYWORDS + elif standard == 'c99': + kw_set = copy.copy(C90_KEYWORDS) + kw_set.update(C99_ADDED_KEYWORDS) + else: + kw_set = copy.copy(C90_KEYWORDS) + kw_set.update(C99_ADDED_KEYWORDS) + kw_set.update(C11_ADDED_KEYWORDS) + return keyword in kw_set + + +def is_source_file(file): + return file.endswith('.c') + + +def is_header(file): + return file.endswith('.h') + + +def is_errno_setting_function(function_name): + return function_name and \ + function_name in ('ftell', 'fgetpos', 'fsetpos', 'fgetwc', 'fputwc' + 'strtoimax', 'strtoumax', 'strtol', 'strtoul', + 'strtoll', 'strtoull', 'strtof', 'strtod', 'strtold' + 'wcstoimax', 'wcstoumax', 'wcstol', 'wcstoul', + 'wcstoll', 'wcstoull', 'wcstof', 'wcstod', 'wcstold' + 'wcrtomb', 'wcsrtombs', 'mbrtowc') + + +def get_type_conversion_to_from(token): + def get_vartok(expr): + while expr: + if isCast(expr): + if expr.astOperand2 is None: + expr = expr.astOperand1 + else: + expr = expr.astOperand2 + elif expr.str in ('.', '::'): + expr = expr.astOperand2 + elif expr.str == '[': + expr = expr.astOperand1 + else: + break + return expr if (expr and expr.variable) else None + + if isCast(token): + vartok = get_vartok(token) + if vartok: + return (token.next, vartok.variable.typeStartToken) + + elif token.str == '=': + lhs = get_vartok(token.astOperand1) + rhs = get_vartok(token.astOperand2) + if lhs and rhs: + return (lhs.variable.typeStartToken, rhs.variable.typeStartToken) + + return None + + +def is_composite_expr(expr, composite_operator=False): + """MISRA C 2012, section 8.10.3""" + if expr is None: + return False + + if not composite_operator: + if expr.str == '?' and simpleMatch(expr.astOperand2, ':'): + colon = expr.astOperand2 + return is_composite_expr(colon.astOperand1,True) or is_composite_expr(colon.astOperand2, True) + if (expr.str in ('+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":", '~')): + return is_composite_expr(expr.astOperand1,True) or is_composite_expr(expr.astOperand2, True) + return False + + # non constant expression? + if expr.isNumber: + return False + if expr.astOperand1 or expr.astOperand2: + return is_composite_expr(expr.astOperand1,True) or is_composite_expr(expr.astOperand2, True) + return True + + +def getEssentialTypeCategory(expr): + if not expr: + return None + if expr.str == ',': + return getEssentialTypeCategory(expr.astOperand2) + if expr.str in ('<', '<=', '==', '!=', '>=', '>', '&&', '||', '!'): + return 'bool' + if expr.str in ('<<', '>>'): + # TODO this is incomplete + return getEssentialTypeCategory(expr.astOperand1) + if len(expr.str) == 1 and expr.str in '+-*/%&|^': + # TODO this is incomplete + e1 = getEssentialTypeCategory(expr.astOperand1) + e2 = getEssentialTypeCategory(expr.astOperand2) + # print('{0}: {1} {2}'.format(expr.str, e1, e2)) + if e1 and e2 and e1 == e2: + return e1 + if expr.valueType: + return expr.valueType.sign + if expr.valueType and expr.valueType.typeScope and expr.valueType.typeScope.className: + return "enum<" + expr.valueType.typeScope.className + ">" + # Unwrap membership, dereferences and array indexing + vartok = expr + while True: + if simpleMatch(vartok, '[') or (vartok and vartok.str == '*' and vartok.astOperand2 is None): + vartok = vartok.astOperand1 + elif simpleMatch(vartok, '.'): + vartok = vartok.astOperand2 + else: + break + if vartok and vartok.variable: + typeToken = vartok.variable.typeStartToken + while typeToken and typeToken.isName: + if typeToken.str == 'char' and not typeToken.isSigned and not typeToken.isUnsigned: + return 'char' + if typeToken.valueType: + if typeToken.valueType.type == 'bool': + return typeToken.valueType.type + if typeToken.valueType.type in ('float', 'double', 'long double'): + return "float" + if typeToken.valueType.sign: + return typeToken.valueType.sign + typeToken = typeToken.next + + # See Appendix D, section D.6, Character constants + if expr.str[0] == "'" and expr.str[-1] == "'": + if len(expr.str) == 3 or (len(expr.str) == 4 and expr.str[1] == '\\'): + return 'char' + return expr.valueType.sign + + if (expr.isCast and expr.str == "("): + castTok = expr.next + while castTok.isName or castTok.str == "*": + if castTok.str == 'char' and not castTok.isSigned and not castTok.isUnsigned: + return 'char' + castTok = castTok.next + + if expr.valueType: + return expr.valueType.sign + return None + + +def getEssentialCategorylist(operand1, operand2): + if not operand1 or not operand2: + return None, None + if (operand1.str in ('++', '--') or + operand2.str in ('++', '--')): + return None, None + if ((operand1.valueType and operand1.valueType.pointer) or + (operand2.valueType and operand2.valueType.pointer)): + return None, None + e1 = getEssentialTypeCategory(operand1) + e2 = getEssentialTypeCategory(operand2) + return e1, e2 + + +def get_essential_type_from_value(value, is_signed): + if value is None: + return None + for t in ('char', 'short', 'int', 'long', 'long long'): + bits = bitsOfEssentialType(t) + if bits >= 64: + continue + if is_signed: + range_min = -(1 << (bits - 1)) + range_max = (1 << (bits - 1)) - 1 + else: + range_min = 0 + range_max = (1 << bits) - 1 + sign = 'signed' if is_signed else 'unsigned' + if is_signed and value < 0 and value >= range_min: + return '%s %s' % (sign, t) + if value >= 0 and value <= range_max: + return '%s %s' % (sign, t) + return None + +def getEssentialType(expr): + if not expr: + return None + + # See Appendix D, section D.6, Character constants + if expr.str[0] == "'" and expr.str[-1] == "'": + if len(expr.str) == 3 or (len(expr.str) == 4 and expr.str[1] == '\\'): + return 'char' + return '%s %s' % (expr.valueType.sign, expr.valueType.type) + + if expr.variable or isCast(expr): + typeToken = expr.variable.typeStartToken if expr.variable else expr.next + while typeToken and typeToken.isName: + if typeToken.str == 'char' and not typeToken.isSigned and not typeToken.isUnsigned: + return 'char' + typeToken = typeToken.next + if expr.valueType: + if expr.valueType.type == 'bool': + return 'bool' + if expr.valueType.isFloat(): + return expr.valueType.type + if expr.valueType.isIntegral(): + if (expr.valueType.sign is None) and expr.valueType.type == 'char': + return 'char' + return '%s %s' % (expr.valueType.sign, expr.valueType.type) + + elif expr.isNumber: + # Appendix D, D.6 The essential type of literal constants + # Integer constants + if expr.valueType.type == 'bool': + return 'bool' + if expr.valueType.isFloat(): + return expr.valueType.type + if expr.valueType.isIntegral(): + if expr.valueType.type != 'int': + return '%s %s' % (expr.valueType.sign, expr.valueType.type) + return get_essential_type_from_value(expr.getKnownIntValue(), expr.valueType.sign == 'signed') + + elif expr.str in ('<', '<=', '>=', '>', '==', '!=', '&&', '||', '!'): + return 'bool' + + elif expr.astOperand1 and expr.astOperand2 and expr.str in ( + '+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":"): + if expr.astOperand1.valueType and expr.astOperand1.valueType.pointer > 0: + return None + if expr.astOperand2.valueType and expr.astOperand2.valueType.pointer > 0: + return None + e1 = getEssentialType(expr.astOperand1) + e2 = getEssentialType(expr.astOperand2) + if e1 is None or e2 is None: + return None + if is_constant_integer_expression(expr): + sign1 = e1.split(' ')[0] + sign2 = e2.split(' ')[0] + if sign1 == sign2 and sign1 in ('signed', 'unsigned'): + e = get_essential_type_from_value(expr.getKnownIntValue(), sign1 == 'signed') + if e: + return e + if bitsOfEssentialType(e2) >= bitsOfEssentialType(e1): + return e2 + else: + return e1 + + elif expr.str == "~": + e1 = getEssentialType(expr.astOperand1) + return e1 + + return None + + +def bitsOfEssentialType(ty): + if ty is None: + return 0 + last_type = ty.split(' ')[-1] + if last_type == 'Boolean': + return 1 + if last_type == 'char': + return typeBits['CHAR'] + if last_type == 'short': + return typeBits['SHORT'] + if last_type == 'int': + return typeBits['INT'] + if ty.endswith('long long'): + return typeBits['LONG_LONG'] + if last_type == 'long': + return typeBits['LONG'] + for sty in STDINT_TYPES: + if ty == sty: + return int(''.join(filter(str.isdigit, sty))) + return 0 + + +def get_function_pointer_type(tok): + ret = '' + par = 0 + while tok and (tok.isName or tok.str == '*'): + ret += ' ' + tok.str + tok = tok.next + if tok is None or tok.str != '(': + return None + tok = tok.link + if not simpleMatch(tok, ') ('): + return None + ret += '(' + tok = tok.next.next + while tok and (tok.str not in '()'): + if tok.varId is None: + ret += ' ' + tok.str + tok = tok.next + if (tok is None) or tok.str != ')': + return None + return ret[1:] + ')' + +def isCast(expr): + if not expr or expr.str != '(' or not expr.astOperand1 or expr.astOperand2: + return False + if simpleMatch(expr, '( )'): + return False + return True + +def is_constant_integer_expression(expr): + if expr is None: + return False + if expr.isInt: + return True + if not expr.isArithmeticalOp: + return False + if expr.astOperand1 and not is_constant_integer_expression(expr.astOperand1): + return False + if expr.astOperand2 and not is_constant_integer_expression(expr.astOperand2): + return False + return True + +def isFunctionCall(expr, std='c99'): + if not expr: + return False + if expr.str != '(' or not expr.astOperand1: + return False + if expr.astOperand1 != expr.previous: + return False + if isKeyword(expr.astOperand1.str, std): + return False + return True + + +def hasExternalLinkage(var): + return var.isGlobal and not var.isStatic + + +def countSideEffects(expr): + if not expr or expr.str in (',', ';'): + return 0 + ret = 0 + if expr.str in ('++', '--', '='): + ret = 1 + return ret + countSideEffects(expr.astOperand1) + countSideEffects(expr.astOperand2) + + +def getForLoopExpressions(forToken): + if not forToken or forToken.str != 'for': + return None + lpar = forToken.next + if not lpar or lpar.str != '(': + return None + if not lpar.astOperand2 or lpar.astOperand2.str != ';': + return None + if not lpar.astOperand2.astOperand2 or lpar.astOperand2.astOperand2.str != ';': + return None + return [lpar.astOperand2.astOperand1, + lpar.astOperand2.astOperand2.astOperand1, + lpar.astOperand2.astOperand2.astOperand2] + + +def get_function_scope(cfg, func): + if func: + for scope in cfg.scopes: + if scope.function == func: + return scope + return None + + +def is_variable_changed(start_token, end_token, var): + """Check if variable is updated between body_start and body_end""" + tok = start_token + while tok != end_token: + if tok.isAssignmentOp: + vartok = tok.astOperand1 + while vartok.astOperand1: + vartok = vartok.astOperand1 + if vartok and vartok.variable == var: + return True + tok = tok.next + return False + +def getForLoopCounterVariables(forToken, cfg): + """ Return a set of Variable objects defined in ``for`` statement and + satisfy requirements to loop counter term from section 8.14 of MISRA + document. + """ + if not forToken or forToken.str != 'for': + return None + tn = forToken.next + if not tn or tn.str != '(': + return None + vars_defined = set() + vars_initialized = set() + vars_exit = set() + vars_modified = set() + cur_clause = 1 + te = tn.link + while tn and tn != te: + if tn.variable: + if cur_clause == 1 and tn.variable.nameToken == tn: + vars_defined.add(tn.variable) + elif cur_clause == 2: + vars_exit.add(tn.variable) + elif cur_clause == 3: + if tn.next and countSideEffectsRecursive(tn.next) > 0: + vars_modified.add(tn.variable) + elif tn.previous and tn.previous.str in ('++', '--'): + tn_ast = tn.astParent + if tn_ast and tn_ast == tn.previous: + vars_modified.add(tn.variable) + elif tn_ast and tn_ast.str == '.' and tn_ast.astOperand2 and tn_ast.astOperand2.variable: + vars_modified.add(tn_ast.astOperand2.variable) + if cur_clause == 1 and tn.isAssignmentOp: + var_token = tn.astOperand1 + while var_token and var_token.str == '.': + var_token = var_token.astOperand2 + if var_token and var_token.variable: + vars_initialized.add(var_token.variable) + if cur_clause == 1 and tn.isName and tn.next.str == '(': + function_args_in_init = getArguments(tn.next) + function_scope = get_function_scope(cfg, tn.function) + for arg_nr in range(len(function_args_in_init)): + init_arg = function_args_in_init[arg_nr] + if init_arg is None or not init_arg.isUnaryOp('&'): + continue + var_token = init_arg.astOperand1 + while var_token and var_token.str == '.': + var_token = var_token.astOperand2 + if var_token is None or var_token.variable is None: + continue + changed = False + if function_scope is None: + changed = True + elif tn.function is None: + changed = True + else: + function_body_start = function_scope.bodyStart + function_body_end = function_scope.bodyEnd + args = tn.function.argument[arg_nr + 1] + if function_scope is None or is_variable_changed(function_body_start, function_body_end, args): + changed = True + if changed: + vars_initialized.add(var_token.variable) + + if tn.str == ';': + cur_clause += 1 + tn = tn.next + return vars_defined | vars_initialized, vars_exit & vars_modified + + +def findCounterTokens(cond): + if not cond: + return [] + if cond.str in ['&&', '||']: + c = findCounterTokens(cond.astOperand1) + c.extend(findCounterTokens(cond.astOperand2)) + return c + ret = [] + if ((cond.isArithmeticalOp and cond.astOperand1 and cond.astOperand2) or + (cond.isComparisonOp and cond.astOperand1 and cond.astOperand2)): + if cond.astOperand1.isName: + ret.append(cond.astOperand1) + if cond.astOperand2.isName: + ret.append(cond.astOperand2) + if cond.astOperand1.isOp: + ret.extend(findCounterTokens(cond.astOperand1)) + if cond.astOperand2.isOp: + ret.extend(findCounterTokens(cond.astOperand2)) + return ret + + +def isFloatCounterInWhileLoop(whileToken): + if not simpleMatch(whileToken, 'while ('): + return False + lpar = whileToken.next + rpar = lpar.link + counterTokens = findCounterTokens(lpar.astOperand2) + whileBodyStart = None + if simpleMatch(rpar, ') {'): + whileBodyStart = rpar.next + elif simpleMatch(whileToken.previous, '} while') and simpleMatch(whileToken.previous.link.previous, 'do {'): + whileBodyStart = whileToken.previous.link + else: + return False + token = whileBodyStart + while token != whileBodyStart.link: + token = token.next + for counterToken in counterTokens: + if not counterToken.valueType or not counterToken.valueType.isFloat(): + continue + if token.isAssignmentOp and token.astOperand1.str == counterToken.str: + return True + if token.str == counterToken.str and token.astParent and token.astParent.str in ('++', '--'): + return True + return False + + +def countSideEffectsRecursive(expr): + if not expr or expr.str == ';': + return 0 + if expr.str == '=' and expr.astOperand1 and expr.astOperand1.str == '[': + prev = expr.astOperand1.previous + if prev and (prev.str == '{' or prev.str == '{'): + return countSideEffectsRecursive(expr.astOperand2) + if expr.str == '=' and expr.astOperand1 and expr.astOperand1.str == '.': + e = expr.astOperand1 + while e and e.str == '.' and e.astOperand2: + e = e.astOperand1 + if e and e.str == '.': + return 0 + if expr.isAssignmentOp or expr.str in {'++', '--'}: + return 1 + # Todo: Check function calls + return countSideEffectsRecursive(expr.astOperand1) + countSideEffectsRecursive(expr.astOperand2) + + +def isBoolExpression(expr): + if not expr: + return False + if expr.valueType and (expr.valueType.type == 'bool' or expr.valueType.bits == 1): + return True + return expr.str in ['!', '==', '!=', '<', '<=', '>', '>=', '&&', '||', '0', '1', 'true', 'false'] + + +def isEnumConstant(expr): + if not expr or not expr.values: + return False + values = expr.values + return len(values) == 1 and values[0].valueKind == 'known' + + +def isConstantExpression(expr): + if expr.isNumber: + return True + if expr.isName and not isEnumConstant(expr): + return False + if simpleMatch(expr.previous, 'sizeof ('): + return True + if expr.astOperand1 and not isConstantExpression(expr.astOperand1): + return False + if expr.astOperand2 and not isConstantExpression(expr.astOperand2): + return False + return True + +def isUnknownConstantExpression(expr): + if expr.isName and not isEnumConstant(expr) and expr.variable is None: + return True + if expr.astOperand1 and isUnknownConstantExpression(expr.astOperand1): + return True + if expr.astOperand2 and isUnknownConstantExpression(expr.astOperand2): + return True + return False + +def isUnsignedInt(expr): + return expr and expr.valueType and expr.valueType.type in ('short', 'int') and expr.valueType.sign == 'unsigned' + + +def getPrecedence(expr): + if not expr: + return 16 + if not expr.astOperand1 or not expr.astOperand2: + return 16 + if expr.str in ('*', '/', '%'): + return 12 + if expr.str in ('+', '-'): + return 11 + if expr.str in ('<<', '>>'): + return 10 + if expr.str in ('<', '>', '<=', '>='): + return 9 + if expr.str in ('==', '!='): + return 8 + if expr.str == '&': + return 7 + if expr.str == '^': + return 6 + if expr.str == '|': + return 5 + if expr.str == '&&': + return 4 + if expr.str == '||': + return 3 + if expr.str in ('?', ':'): + return 2 + if expr.isAssignmentOp: + return 1 + if expr.str == ',': + return 0 + return -1 + + +def findRawLink(token): + tok1 = None + tok2 = None + forward = False + + if token.str in '{([': + tok1 = token.str + tok2 = '})]'['{(['.find(token.str)] + forward = True + elif token.str in '})]': + tok1 = token.str + tok2 = '{(['['})]'.find(token.str)] + forward = False + else: + return None + + # try to find link + indent = 0 + while token: + if token.str == tok1: + indent = indent + 1 + elif token.str == tok2: + if indent <= 1: + return token + indent = indent - 1 + if forward is True: + token = token.next + else: + token = token.previous + + # raw link not found + return None + + +def numberOfParentheses(tok1, tok2): + while tok1 and tok1 != tok2: + if tok1.str == '(' or tok1.str == ')': + return False + tok1 = tok1.next + return tok1 == tok2 + + +def findGotoLabel(gotoToken): + label = gotoToken.next.str + tok = gotoToken.next.next + while tok: + if tok.str == '}' and tok.scope.type == 'Function': + break + if tok.str == label and tok.next.str == ':': + return tok + tok = tok.next + return None + + +def findInclude(directives, header): + for directive in directives: + if directive.str == '#include ' + header: + return directive + return None + + +# Get function arguments +def getArgumentsRecursive(tok, arguments): + if tok is None: + return + if tok.str == ',': + getArgumentsRecursive(tok.astOperand1, arguments) + getArgumentsRecursive(tok.astOperand2, arguments) + else: + arguments.append(tok) + + +def getArguments(ftok): + arguments = [] + getArgumentsRecursive(ftok.astOperand2, arguments) + return arguments + + +def isalnum(c): + return c in string.digits or c in string.ascii_letters + + +def isHexEscapeSequence(symbols): + """Checks that given symbols are valid hex escape sequence. + + hexadecimal-escape-sequence: + \\x hexadecimal-digit + hexadecimal-escape-sequence hexadecimal-digit + + Reference: n1570 6.4.4.4""" + if len(symbols) < 3 or symbols[:2] != '\\x': + return False + return all([s in string.hexdigits for s in symbols[2:]]) + + +def isOctalEscapeSequence(symbols): + r"""Checks that given symbols are valid octal escape sequence: + + octal-escape-sequence: + \ octal-digit + \ octal-digit octal-digit + \ octal-digit octal-digit octal-digit + + Reference: n1570 6.4.4.4""" + if len(symbols) not in range(2, 5) or symbols[0] != '\\': + return False + return all([s in string.octdigits for s in symbols[1:]]) + + +def isSimpleEscapeSequence(symbols): + """Checks that given symbols are simple escape sequence. + Reference: n1570 6.4.4.4""" + if len(symbols) != 2 or symbols[0] != '\\': + return False + return symbols[1] in ("'", '"', '?', '\\', 'a', 'b', 'f', 'n', 'r', 't', 'v') + + +def isTernaryOperator(token): + if not token: + return False + if not token.astOperand2: + return False + return token.str == '?' and token.astOperand2.str == ':' + + +def getTernaryOperandsRecursive(token): + """Returns list of ternary operands including nested ones.""" + if not isTernaryOperator(token): + return [] + result = [] + result += getTernaryOperandsRecursive(token.astOperand2.astOperand1) + if token.astOperand2.astOperand1 and not isTernaryOperator(token.astOperand2.astOperand1): + result += [token.astOperand2.astOperand1] + result += getTernaryOperandsRecursive(token.astOperand2.astOperand2) + if token.astOperand2.astOperand2 and not isTernaryOperator(token.astOperand2.astOperand2): + result += [token.astOperand2.astOperand2] + return result + + +def hasNumericEscapeSequence(symbols): + """Check that given string contains octal or hexadecimal escape sequences.""" + if '\\' not in symbols: + return False + for c, cn in grouped(symbols, 2): + if c == '\\' and cn in ('x' + string.octdigits): + return True + return False + + +def isNoReturnScope(tok): + if tok is None or tok.str != '}': + return False + if tok.previous is None or tok.previous.str != ';': + return False + if simpleMatch(tok.previous.previous, 'break ;'): + return True + prev = tok.previous.previous + while prev and prev.str not in ';{}': + if prev.str in '])': + prev = prev.link + prev = prev.previous + if prev and prev.next.str in ['throw', 'return']: + return True + return False + + +# Return the token which the value is assigned to +def getAssignedVariableToken(vartok): + if not vartok: + return None + parent = vartok.astParent + while parent and parent.isArithmeticalOp: + parent = parent.astParent + if parent and parent.isAssignmentOp: + return parent.astOperand1 + return None + +# If the value is used as a return value, return the function definition +def getFunctionUsingReturnValue(valueToken): + if not valueToken: + return None + if not valueToken.astParent: + return None + operator = valueToken.astParent + if operator.str == 'return': + return operator.scope.function + if operator.isArithmeticalOp: + return getFunctionUsingReturnValue(operator) + return None + +# Return true if the token follows a specific sequence of token str values +def tokenFollowsSequence(token, sequence): + if not token: + return False + for i in reversed(sequence): + prev = token.previous + if not prev: + return False + if prev.str != i: + return False + token = prev + return True + +class Define: + def __init__(self, directive): + self.name = '' + self.args = [] + self.expansionList = '' + + res = re.match(r'#define ([A-Za-z0-9_]+)\(([A-Za-z0-9_, ]+)\)[ ]+(.*)', directive.str) + if res: + self.name = res.group(1) + self.args = res.group(2).strip().split(',') + self.expansionList = res.group(3) + else: + res = re.match(r'#define ([A-Za-z0-9_]+)[ ]+(.*)', directive.str) + if res: + self.name = res.group(1) + self.expansionList = res.group(2) + + def __repr__(self): + attrs = ["name", "args", "expansionList"] + return "{}({})".format( + "Define", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + + +def getAddonRules(): + """Returns dict of MISRA rules handled by this addon.""" + addon_rules = [] + compiled = re.compile(r'.*def[ ]+misra_([0-9]+)_([0-9]+)[(].*') + with open(__file__) as f: + for line in f: + res = compiled.match(line) + if res is None: + continue + addon_rules.append(res.group(1) + '.' + res.group(2)) + return addon_rules + + +def getCppcheckRules(): + """Returns list of rules handled by cppcheck.""" + return ['1.3', # + '2.1', # alwaysFalse, duplicateBreak + '2.2', # alwaysTrue, redundantCondition, redundantAssignment, redundantAssignInSwitch, unreadVariable + '2.6', # unusedLabel + '5.3', # shadowVariable + '8.3', # funcArgNamesDifferent + '8.13', # constPointer + '9.1', # uninitvar + '14.3', # alwaysTrue, alwaysFalse, compareValueOutOfTypeRangeError + '13.2', # unknownEvaluationOrder + '13.6', # sizeofCalculation + '17.4', # missingReturn + '17.5', # argumentSize + '18.1', # pointerOutOfBounds + '18.2', # comparePointers + '18.3', # comparePointers + '18.6', # danglingLifetime + '19.1', # overlappingWriteUnion, overlappingWriteFunction + '20.6', # preprocessorErrorDirective + '21.13', # invalidFunctionArg + '21.17', # bufferAccessOutOfBounds + '21.18', # bufferAccessOutOfBounds + '22.1', # memleak, resourceLeak, memleakOnRealloc, leakReturnValNotUsed, leakNoVarFunctionCall + '22.2', # autovarInvalidDeallocation + '22.3', # incompatibleFileOpen + '22.4', # writeReadOnlyFile + '22.6' # useClosedFile + ] + + +def generateTable(): + # print table + numberOfRules = {} + numberOfRules[1] = 3 + numberOfRules[2] = 7 + numberOfRules[3] = 2 + numberOfRules[4] = 2 + numberOfRules[5] = 9 + numberOfRules[6] = 2 + numberOfRules[7] = 4 + numberOfRules[8] = 14 + numberOfRules[9] = 5 + numberOfRules[10] = 8 + numberOfRules[11] = 9 + numberOfRules[12] = 4 + numberOfRules[13] = 6 + numberOfRules[14] = 4 + numberOfRules[15] = 7 + numberOfRules[16] = 7 + numberOfRules[17] = 8 + numberOfRules[18] = 8 + numberOfRules[19] = 2 + numberOfRules[20] = 14 + numberOfRules[21] = 21 + numberOfRules[22] = 10 + + # Rules that can be checked with compilers: + # compiler = ['1.1', '1.2'] + + addon = getAddonRules() + cppcheck = getCppcheckRules() + for i1 in range(1, 23): + for i2 in range(1, numberOfRules[i1] + 1): + num = str(i1) + '.' + str(i2) + s = '' + if num in addon: + s = 'X (Addon)' + elif num in cppcheck: + s = 'X (Cppcheck)' + num = num + ' ' + print(num[:8] + s) + + +def remove_file_prefix(file_path, prefix): + """ + Remove a file path prefix from a give path. leftover + directory separators at the beginning of a file + after the removal are also stripped. + + Example: + '/remove/this/path/file.c' + with a prefix of: + '/remove/this/path' + becomes: + file.c + """ + result = None + if file_path.startswith(prefix): + result = file_path[len(prefix):] + # Remove any leftover directory separators at the + # beginning + result = result.lstrip('\\/') + else: + result = file_path + return result + + +class Rule(object): + """Class to keep rule text and metadata""" + + MISRA_SEVERITY_LEVELS = ['Required', 'Mandatory', 'Advisory'] + + def __init__(self, num1, num2): + self.num1 = num1 + self.num2 = num2 + self.text = '' + self.misra_severity = '' + + @property + def num(self): + return self.num1 * 100 + self.num2 + + @property + def misra_severity(self): + return self._misra_severity + + @misra_severity.setter + def misra_severity(self, val): + if val in self.MISRA_SEVERITY_LEVELS: + self._misra_severity = val + else: + self._misra_severity = '' + + @property + def cppcheck_severity(self): + return 'style' + + def __repr__(self): + return "%d.%d (%s)" % (self.num1, self.num2, self.misra_severity) + + +class MisraSettings(object): + """Hold settings for misra.py script.""" + + __slots__ = ["verify", "quiet", "show_summary"] + + def __init__(self, args): + """ + :param args: Arguments given by argparse. + """ + self.verify = False + self.quiet = False + self.show_summary = True + + if args.verify: + self.verify = True + if args.cli: + self.quiet = True + self.show_summary = False + if args.quiet: + self.quiet = True + if args.no_summary: + self.show_summary = False + + def __repr__(self): + attrs = ["verify", "quiet", "show_summary", "verify"] + return "{}({})".format( + "MisraSettings", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + + +class MisraChecker: + + def __init__(self, settings, stdversion="c89"): + """ + :param settings: misra.py script settings. + """ + + self.settings = settings + + # Test validation rules lists + self.verify_expected = list() + self.verify_actual = list() + + # List of formatted violation messages + self.violations = dict() + + # if --rule-texts is specified this dictionary + # is loaded with descriptions of each rule + # by rule number (in hundreds). + # ie rule 1.2 becomes 102 + self.ruleTexts = dict() + self.ruleText_filename = None + + # Dictionary of dictionaries for rules to suppress + # Dict1 is keyed by rule number in the hundreds format of + # Major * 100 + minor. ie Rule 5.2 = (5*100) + 2 + # Dict 2 is keyed by filename. An entry of None means suppress globally. + # Each file name entry contains a list of tuples of (lineNumber, symbolName) + # or an item of None which indicates suppress rule for the entire file. + # The line and symbol name tuple may have None as either of its elements but + # should not be None for both. + self.suppressedRules = dict() + + # Prefix to ignore when matching suppression files. + self.filePrefix = None + + # Number of all violations suppressed per rule + self.suppressionStats = dict() + + self.stdversion = stdversion + + self.severity = None + + self.existing_violations = set() + + self._ctu_summary_typedefs = False + self._ctu_summary_tagnames = False + self._ctu_summary_identifiers = False + self._ctu_summary_usage = False + + self.path_premium_addon = None + + def __repr__(self): + attrs = ["settings", "verify_expected", "verify_actual", "violations", + "ruleTexts", "suppressedRules", "filePrefix", + "suppressionStats", "stdversion", "severity"] + return "{}({})".format( + "MisraChecker", + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + + def get_num_significant_naming_chars(self, cfg): + if cfg.standards and cfg.standards.c == "c89": + return 31 + else: + return 63 + + def _save_ctu_summary_typedefs(self, dumpfile, typedef_info): + if self._ctu_summary_typedefs: + return + + self._ctu_summary_typedefs = True + + summary = [] + for ti in typedef_info: + summary.append({ 'name': ti.name, 'file': ti.file, 'line': ti.linenr, 'column': ti.column, 'used': ti.used }) + if len(summary) > 0: + cppcheckdata.reportSummary(dumpfile, 'MisraTypedefInfo', summary) + + def _save_ctu_summary_tagnames(self, dumpfile, cfg): + if self._ctu_summary_tagnames: + return + + self._ctu_summary_tagnames = True + + summary = [] + # structs/enums + for scope in cfg.scopes: + if scope.className is None: + continue + if scope.className.startswith('Anonymous'): + continue + if scope.type not in ('Struct', 'Enum'): + continue + used = False + tok = scope.bodyEnd + while tok: + if tok.str == scope.className: + used = True + break + tok = tok.next + summary.append({'name': scope.className, 'used':used, 'file': scope.bodyStart.file, 'line': scope.bodyStart.linenr, 'column': scope.bodyStart.column}) + if len(summary) > 0: + cppcheckdata.reportSummary(dumpfile, 'MisraTagName', summary) + + def _save_ctu_summary_identifiers(self, dumpfile, cfg): + if self._ctu_summary_identifiers: + return + self._ctu_summary_identifiers = True + + external_identifiers = [] + internal_identifiers = [] + local_identifiers = [] + + def identifier(nameToken): + return {'name':nameToken.str, 'file':nameToken.file, 'line':nameToken.linenr, 'column':nameToken.column} + + names = [] + + for var in cfg.variables: + if var.nameToken is None: + continue + if var.access != 'Global': + if var.nameToken.str in names: + continue + names.append(var.nameToken.str) + local_identifiers.append(identifier(var.nameToken)) + elif var.isStatic: + names.append(var.nameToken.str) + i = identifier(var.nameToken) + i['inlinefunc'] = False + internal_identifiers.append(i) + else: + names.append(var.nameToken.str) + i = identifier(var.nameToken) + i['decl'] = var.isExtern + external_identifiers.append(i) + + for func in cfg.functions: + if func.tokenDef is None: + continue + if func.isStatic: + i = identifier(func.tokenDef) + i['inlinefunc'] = func.isInlineKeyword + internal_identifiers.append(i) + else: + if func.token is None: + i = identifier(func.tokenDef) + else: + i = identifier(func.token) + i['decl'] = func.token is None + external_identifiers.append(i) + + cppcheckdata.reportSummary(dumpfile, 'MisraExternalIdentifiers', external_identifiers) + cppcheckdata.reportSummary(dumpfile, 'MisraInternalIdentifiers', internal_identifiers) + cppcheckdata.reportSummary(dumpfile, 'MisraLocalIdentifiers', local_identifiers) + + def _save_ctu_summary_usage(self, dumpfile, cfg): + if self._ctu_summary_usage: + return + self._ctu_summary_usage = True + + names = [] + for token in cfg.tokenlist: + if not token.isName: + continue + if token.function and token != token.function.tokenDef: + if (not token.function.isStatic) and (token.str not in names): + names.append({'name': token.str, 'file': token.file}) + elif token.variable: + if token == token.variable.nameToken: + continue + if token.variable.access == 'Global' and (not token.variable.isStatic) and (token.str not in names): + names.append({'name': token.str, 'file': token.file}) + + if len(names) > 0: + cppcheckdata.reportSummary(dumpfile, 'MisraUsage', names) + + + def misra_1_2(self, cfg): + # gcc language extensions: https://gcc.gnu.org/onlinedocs/gcc/C-Extensions.html + for token in cfg.tokenlist: + if simpleMatch(token, '? :'): + self.reportError(token, 1, 2) + elif simpleMatch(token, '( {') and simpleMatch(token.next.link.previous, '; } )'): + self.reportError(token, 1, 2) + + + def misra_1_4(self, cfg): + for token in cfg.tokenlist: + if token.str in ('_Atomic', '_Noreturn', '_Generic', '_Thread_local', '_Alignas', '_Alignof'): + self.reportError(token, 1, 4) + if token.str.endswith('_s') and isFunctionCall(token.next): + # See C specification C11 - Annex K, page 578 + if token.str in ('tmpfile_s', 'tmpnam_s', 'fopen_s', 'freopen_s', 'fprintf_s', 'fscanf_s', 'printf_s', 'scanf_s', + 'snprintf_s', 'sprintf_s', 'sscanf_s', 'vfprintf_s', 'vfscanf_s', 'vprintf_s', 'vscanf_s', + 'vsnprintf_s', 'vsprintf_s', 'vsscanf_s', 'gets_s', 'set_constraint_handler_s', 'abort_handler_s', + 'ignore_handler_s', 'getenv_s', 'bsearch_s', 'qsort_s', 'wctomb_s', 'mbstowcs_s', 'wcstombs_s', + 'memcpy_s', 'memmove_s', 'strcpy_s', 'strncpy_s', 'strcat_s', 'strncat_s', 'strtok_s', 'memset_s', + 'strerror_s', 'strerrorlen_s', 'strnlen_s', 'asctime_s', 'ctime_s', 'gmtime_s', 'localtime_s', + 'fwprintf_s', 'fwscanf_s', 'snwprintf_s', 'swprintf_s', 'swscanf_s', 'vfwprintf_s', 'vfwscanf_s', + 'vsnwprintf_s', 'vswprintf_s', 'vswscanf_s', 'vwprintf_s', 'vwscanf_s', 'wprintf_s', 'wscanf_s', + 'wcscpy_s', 'wcsncpy_s', 'wmemcpy_s', 'wmemmove_s', 'wcscat_s', 'wcsncat_s', 'wcstok_s', 'wcsnlen_s', + 'wcrtomb_s', 'mbsrtowcs_s', 'wcsrtombs_s'): + self.reportError(token, 1, 4) + + def misra_2_2(self, cfg): + for token in cfg.tokenlist: + if token.isExpandedMacro: + continue + if (token.str in '+-') and token.astOperand2: + if simpleMatch(token.astOperand1, '0'): + self.reportError(token.astOperand1, 2, 2) + elif simpleMatch(token.astOperand2, '0'): + self.reportError(token.astOperand2, 2, 2) + if token.str == '*' and token.astOperand2: + if simpleMatch(token.astOperand2, '0'): + self.reportError(token.astOperand1, 2, 2) + elif simpleMatch(token.astOperand1, '0'): + self.reportError(token.astOperand2, 2, 2) + elif simpleMatch(token.astOperand1, '1'): + self.reportError(token.astOperand1, 2, 2) + elif simpleMatch(token.astOperand2, '1'): + self.reportError(token.astOperand2, 2, 2) + + def misra_2_3(self, dumpfile, typedefInfo): + self._save_ctu_summary_typedefs(dumpfile, typedefInfo) + + def misra_2_4(self, dumpfile, cfg): + self._save_ctu_summary_tagnames(dumpfile, cfg) + + def misra_2_5(self, dumpfile, cfg): + used_macros = list() + for m in cfg.macro_usage: + used_macros.append(m.name) + summary = [] + for directive in cfg.directives: + res = re.match(r'#define[ \t]+([a-zA-Z_][a-zA-Z_0-9]*).*', directive.str) + if res: + macro_name = res.group(1) + summary.append({'name': macro_name, 'used': (macro_name in used_macros), 'file': directive.file, 'line': directive.linenr, 'column': directive.column}) + if len(summary) > 0: + cppcheckdata.reportSummary(dumpfile, 'MisraMacro', summary) + + def misra_2_7(self, data): + for func in data.functions: + # Skip function with no parameter + if len(func.argument) == 0: + continue + # Setup list of function parameters + func_param_list = list() + for arg in func.argument: + func_arg = func.argument[arg] + if func_arg.typeStartToken and func_arg.typeStartToken.str == '...': + continue + func_param_list.append(func_arg) + # Search for scope of current function + for scope in data.scopes: + if (scope.type == "Function") and (scope.function == func): + # Search function body: remove referenced function parameter from list + token = scope.bodyStart + while token.next is not None and token != scope.bodyEnd and len(func_param_list) > 0: + if token.variable is not None and token.variable in func_param_list: + func_param_list.remove(token.variable) + token = token.next + # Emit a warning for each unused variable, but no more that one warning per line + reported_linenrs = set() + for func_param in func_param_list: + if func_param.nameToken: + linenr = func_param.nameToken + if linenr not in reported_linenrs: + self.reportError(func_param.nameToken, 2, 7) + reported_linenrs.add(linenr) + else: + linenr = func.tokenDef.linenr + if linenr not in reported_linenrs: + self.reportError(func.tokenDef, 2, 7) + reported_linenrs.add(linenr) + + def misra_3_1(self, rawTokens): + for token in rawTokens: + starts_with_double_slash = token.str.startswith('//') + starts_with_block_comment = token.str.startswith("/*") + s = token.str.lstrip('/') + if (starts_with_double_slash or starts_with_block_comment) and "/*" in s: + # Block comment inside of regular comment, violation + self.reportError(token, 3, 1) + elif starts_with_block_comment and "//" in s: + # "//" in block comment, check if it's a uri + while "//" in s: + possible_uri, s = s.split("//", 1) + if not re.search(r"\w+:$", possible_uri): + # Violation if no uri was found + self.reportError(token, 3, 1) + break + + def misra_3_2(self, rawTokens): + for token in rawTokens: + if token.str.startswith('//'): + # Check for comment ends with trigraph which might be replaced + # by a backslash. + if token.str.endswith('??/'): + self.reportError(token, 3, 2) + # Check for comment which has been merged with subsequent line + # because it ends with backslash. + # The last backslash is no more part of the comment token thus + # check if next token exists and compare line numbers. + elif (token.next is not None) and (token.linenr == token.next.linenr): + self.reportError(token, 3, 2) + + def misra_4_1(self, rawTokens): + for token in rawTokens: + if (token.str[0] != '"') and (token.str[0] != '\''): + continue + if len(token.str) < 3: + continue + + delimiter = token.str[0] + symbols = token.str[1:-1] + + # No closing delimiter. This will not compile. + if token.str[-1] != delimiter: + continue + + if len(symbols) < 2: + continue + + if not hasNumericEscapeSequence(symbols): + continue + + # String literals that contains one or more escape sequences. All of them should be + # terminated. + for sequence in ['\\' + t for t in symbols.split('\\')][1:]: + if (isHexEscapeSequence(sequence) or isOctalEscapeSequence(sequence) or + isSimpleEscapeSequence(sequence)): + continue + else: + self.reportError(token, 4, 1) + + def misra_4_2(self, rawTokens): + for token in rawTokens: + if (token.str[0] != '"') or (token.str[-1] != '"'): + continue + # Check for trigraph sequence as defined by ISO/IEC 9899:1999 + for sequence in ['??=', '??(', '??/', '??)', '??\'', '??<', '??!', '??>', '??-']: + if sequence in token.str[1:-1]: + # First trigraph sequence match, report error and leave loop. + self.reportError(token, 4, 2) + break + + def misra_5_1(self, data): + long_vars = {} + num_sign_chars = self.get_num_significant_naming_chars(data) + for var in data.variables: + if var.nameToken is None: + continue + if len(var.nameToken.str) <= num_sign_chars: + continue + if not hasExternalLinkage(var): + continue + long_vars.setdefault(var.nameToken.str[:num_sign_chars], []).append(var.nameToken) + for name_prefix in long_vars: + tokens = long_vars[name_prefix] + if len(tokens) < 2: + continue + for tok in sorted(tokens, key=lambda t: (t.linenr, t.column))[1:]: + self.reportError(tok, 5, 1) + + def misra_5_2(self, data): + scopeVars = {} + num_sign_chars = self.get_num_significant_naming_chars(data) + for var in data.variables: + if var.nameToken is None: + continue + if len(var.nameToken.str) <= num_sign_chars: + continue + if var.nameToken.scope not in scopeVars: + scopeVars.setdefault(var.nameToken.scope, {})["varlist"] = [] + scopeVars.setdefault(var.nameToken.scope, {})["scopelist"] = [] + scopeVars[var.nameToken.scope]["varlist"].append(var) + for scope in data.scopes: + if scope.nestedIn and scope.className: + if scope.nestedIn not in scopeVars: + scopeVars.setdefault(scope.nestedIn, {})["varlist"] = [] + scopeVars.setdefault(scope.nestedIn, {})["scopelist"] = [] + scopeVars[scope.nestedIn]["scopelist"].append(scope) + for scope in scopeVars: + if len(scopeVars[scope]["varlist"]) <= 1: + continue + for i, variable1 in enumerate(scopeVars[scope]["varlist"]): + for variable2 in scopeVars[scope]["varlist"][i + 1:]: + if variable1.isArgument and variable2.isArgument: + continue + if hasExternalLinkage(variable1) or hasExternalLinkage(variable2): + continue + if (variable1.nameToken.str[:num_sign_chars] == variable2.nameToken.str[:num_sign_chars] and + variable1 is not variable2): + if int(variable1.nameToken.linenr) > int(variable2.nameToken.linenr): + self.reportError(variable1.nameToken, 5, 2) + else: + self.reportError(variable2.nameToken, 5, 2) + for innerscope in scopeVars[scope]["scopelist"]: + if variable1.nameToken.str[:num_sign_chars] == innerscope.className[:num_sign_chars]: + if int(variable1.nameToken.linenr) > int(innerscope.bodyStart.linenr): + self.reportError(variable1.nameToken, 5, 2) + else: + self.reportError(innerscope.bodyStart, 5, 2) + if len(scopeVars[scope]["scopelist"]) <= 1: + continue + for i, scopename1 in enumerate(scopeVars[scope]["scopelist"]): + for scopename2 in scopeVars[scope]["scopelist"][i + 1:]: + if scopename1.className[:num_sign_chars] == scopename2.className[:num_sign_chars]: + if int(scopename1.bodyStart.linenr) > int(scopename2.bodyStart.linenr): + self.reportError(scopename1.bodyStart, 5, 2) + else: + self.reportError(scopename2.bodyStart, 5, 2) + + def misra_5_4(self, data): + num_sign_chars = self.get_num_significant_naming_chars(data) + macro = {} + compile_name = re.compile(r'#define ([a-zA-Z0-9_]+)') + compile_param = re.compile(r'#define ([a-zA-Z0-9_]+)[(]([a-zA-Z0-9_, ]+)[)]') + short_names = {} + macro_w_arg = [] + for dir in data.directives: + res1 = compile_name.match(dir.str) + if res1: + if dir not in macro: + macro.setdefault(dir, {})["name"] = [] + macro.setdefault(dir, {})["params"] = [] + full_name = res1.group(1) + macro[dir]["name"] = full_name + short_name = full_name[:num_sign_chars] + if short_name in short_names: + _dir = short_names[short_name] + if full_name != macro[_dir]["name"]: + self.reportError(dir, 5, 4) + else: + short_names[short_name] = dir + res2 = compile_param.match(dir.str) + if res2: + res_gp2 = res2.group(2).split(",") + res_gp2 = [macroname.replace(" ", "") for macroname in res_gp2] + macro[dir]["params"].extend(res_gp2) + macro_w_arg.append(dir) + for mvar in macro_w_arg: + for i, macroparam1 in enumerate(macro[mvar]["params"]): + for j, macroparam2 in enumerate(macro[mvar]["params"]): + if j > i and macroparam1[:num_sign_chars] == macroparam2[:num_sign_chars]: + self.reportError(mvar, 5, 4) + param = macroparam1 + if param[:num_sign_chars] in short_names: + m_var1 = short_names[param[:num_sign_chars]] + if m_var1.linenr > mvar.linenr: + self.reportError(m_var1, 5, 4) + else: + self.reportError(mvar, 5, 4) + + def misra_5_5(self, data): + num_sign_chars = self.get_num_significant_naming_chars(data) + macroNames = {} + compiled = re.compile(r'#define ([A-Za-z0-9_]+)') + for dir in data.directives: + res = compiled.match(dir.str) + if res: + macroNames[res.group(1)[:num_sign_chars]] = dir + for var in data.variables: + if var.nameToken and var.nameToken.str[:num_sign_chars] in macroNames: + self.reportError(var.nameToken, 5, 5) + for scope in data.scopes: + if scope.className and scope.className[:num_sign_chars] in macroNames: + self.reportError(scope.bodyStart, 5, 5) + + + def misra_5_6(self, dumpfile, typedefInfo): + self._save_ctu_summary_typedefs(dumpfile, typedefInfo) + + def misra_5_7(self, dumpfile, cfg): + self._save_ctu_summary_tagnames(dumpfile, cfg) + + def misra_5_8(self, dumpfile, cfg): + self._save_ctu_summary_identifiers(dumpfile, cfg) + + def misra_5_9(self, dumpfile, cfg): + self._save_ctu_summary_identifiers(dumpfile, cfg) + + def misra_6_1(self, data): + # Bitfield type must be bool or explicitly signed/unsigned int + for token in data.tokenlist: + if not token.valueType: + continue + if token.valueType.bits == 0: + continue + if not token.variable: + continue + if not token.scope: + continue + if token.scope.type not in 'Struct': + continue + + if data.standards.c == 'c89': + if token.valueType.type != 'int' and not isUnsignedType(token.variable.typeStartToken.str): + self.reportError(token, 6, 1) + elif data.standards.c in ('c99', 'c11', 'c17', 'c18'): + if token.valueType.type == 'bool': + continue + + isExplicitlySignedOrUnsigned = False + typeToken = token.variable.typeStartToken + while typeToken: + if typeToken.isUnsigned or typeToken.isSigned or isUnsignedType(typeToken.str): + isExplicitlySignedOrUnsigned = True + break + + if typeToken is token.variable.typeEndToken: + break + + typeToken = typeToken.next + + if not isExplicitlySignedOrUnsigned: + self.reportError(token, 6, 1) + + + def misra_6_2(self, data): + # Bitfields of size 1 can not be signed + for token in data.tokenlist: + if not token.valueType: + continue + if not token.scope: + continue + if token.scope.type not in 'Struct': + continue + if token.valueType.bits == 1 and token.valueType.sign == 'signed': + self.reportError(token, 6, 2) + + + def misra_7_1(self, rawTokens): + compiled = re.compile(r'^0[0-7]+$') + for tok in rawTokens: + if compiled.match(tok.str): + self.reportError(tok, 7, 1) + + def misra_7_2(self, data): + for token in data.tokenlist: + if token.isInt and ('U' not in token.str.upper()) and token.valueType and token.valueType.sign == 'unsigned': + self.reportError(token, 7, 2) + + def misra_7_3(self, rawTokens): + compiled = re.compile(r'^[0-9.]+[Uu]*l+[Uu]*$') + for tok in rawTokens: + if compiled.match(tok.str): + self.reportError(tok, 7, 3) + + def misra_7_4(self, data): + # A string literal shall not be assigned to an object unless the object's type + # is constant. + def reportErrorIfVariableIsNotConst(variable, stringLiteral): + if variable.valueType: + if (variable.valueType.constness % 2) != 1: + self.reportError(stringLiteral, 7, 4) + + for token in data.tokenlist: + if token.isString: + # Check normal variable assignment + variable = getAssignedVariableToken(token) + if variable: + reportErrorIfVariableIsNotConst(variable, token) + + # Check use as return value + function = getFunctionUsingReturnValue(token) + if function: + # "Primitive" test since there is no info available on return value type + if not tokenFollowsSequence(function.tokenDef, ['const', 'char', '*']): + self.reportError(token, 7, 4) + + # Check use as function parameter + if isFunctionCall(token) and token.astOperand1 and token.astOperand1.function: + functionDeclaration = token.astOperand1.function + + if functionDeclaration.tokenDef: + if functionDeclaration.tokenDef is token.astOperand1: + # Token is not a function call, but it is the definition of the function + continue + + parametersUsed = getArguments(token) + for i in range(len(parametersUsed)): + usedParameter = parametersUsed[i] + parameterDefinition = functionDeclaration.argument.get(i+1) + + if usedParameter.isString and parameterDefinition and parameterDefinition.nameToken: + reportErrorIfVariableIsNotConst(parameterDefinition.nameToken, usedParameter) + + def misra_8_1(self, cfg): + for token in cfg.tokenlist: + if token.isImplicitInt and not token.isUnsigned and not token.isSigned: + self.reportError(token, 8, 1) + + def misra_8_2(self, data, rawTokens): + def getFollowingRawTokens(rawTokens, token, count): + following =[] + for rawToken in rawTokens: + if (rawToken.file == token.file and + rawToken.linenr == token.linenr and + rawToken.column == token.column): + for _ in range(count): + rawToken = rawToken.next + # Skip comments + while rawToken and (rawToken.str.startswith('/*') or rawToken.str.startswith('//')): + rawToken = rawToken.next + if rawToken is None: + break + following.append(rawToken) + return following + + # Zero arguments should be in form ( void ) + def checkZeroArguments(func, startCall, endCall): + if not startCall.isRemovedVoidParameter and len(func.argument) == 0: + if func.tokenDef.next: + self.reportError(func.tokenDef.next, 8, 2) + else: + self.reportError(func.tokenDef, 8, 2) + + def checkDeclarationArgumentsViolations(func, startCall, endCall): + # Collect the tokens for the arguments in function definition + argNameTokens = set() + for arg in func.argument: + argument = func.argument[arg] + typeStartToken = argument.typeStartToken + if typeStartToken is None: + continue + nameToken = argument.nameToken + if nameToken is None: + continue + argNameTokens.add(nameToken) + + # Check if we have the same number of variables in both the + # declaration and the definition. + # + # TODO: We actually need to check if the names of the arguments are + # the same. But we can't do this because we have no links to + # variables in the arguments in function definition in the dump file. + foundVariables = 0 + while startCall and startCall != endCall: + if startCall.varId: + foundVariables += 1 + startCall = startCall.next + + if len(argNameTokens) != foundVariables: + if func.tokenDef.next: + self.reportError(func.tokenDef.next, 8, 2) + else: + self.reportError(func.tokenDef, 8, 2) + + def checkDefinitionArgumentsViolations(func, startCall, endCall): + for arg in func.argument: + argument = func.argument[arg] + typeStartToken = argument.typeStartToken + if typeStartToken is None: + continue + + # Arguments should have a name unless variable length arg + nameToken = argument.nameToken + if nameToken is None and typeStartToken.str != '...': + self.reportError(typeStartToken, 8, 2) + + # Type declaration on next line (old style declaration list) is not allowed + if typeStartToken.linenr > endCall.linenr: + self.reportError(typeStartToken, 8, 2) + + # Check arguments in function declaration + for func in data.functions: + + # Check arguments in function definition + tokenImpl = func.token + if tokenImpl: + startCall = tokenImpl.next + if startCall is None or startCall.str != '(': + continue + endCall = startCall.link + if endCall is None or endCall.str != ')': + continue + checkZeroArguments(func, startCall, endCall) + checkDefinitionArgumentsViolations(func, startCall, endCall) + + # Check arguments in function declaration + tokenDef = func.tokenDef + if tokenDef: + startCall = func.tokenDef.next + if startCall is None or startCall.str != '(': + continue + endCall = startCall.link + if endCall is None or endCall.str != ')': + continue + checkZeroArguments(func, startCall, endCall) + if tokenImpl: + checkDeclarationArgumentsViolations(func, startCall, endCall) + else: + # When there is no function definition, we should execute + # its checks for the declaration token. The point is that without + # a known definition we have no Function.argument list required + # for declaration check. + checkDefinitionArgumentsViolations(func, startCall, endCall) + + # Check arguments in pointer declarations + for var in data.variables: + if not var.isPointer: + continue + + if var.nameToken is None: + continue + + rawTokensFollowingPtr = getFollowingRawTokens(rawTokens, var.nameToken, 3) + if len(rawTokensFollowingPtr) != 3: + continue + + # Compliant: returnType (*ptrName) ( ArgType ) + # Non-compliant: returnType (*ptrName) ( ) + if (rawTokensFollowingPtr[0].str == ')' and + rawTokensFollowingPtr[1].str == '(' and + rawTokensFollowingPtr[2].str == ')'): + self.reportError(var.nameToken, 8, 2) + + def insert_in_dict(self, dict_name,key, value): + if key not in dict_name: + dict_name[key] = [] + dict_name[key].append(value) + def misra_8_4(self, cfg): + for func in cfg.functions: + if func.isStatic: + continue + if func.token is None: + continue + if not is_source_file(func.token.file): + continue + if func.token != func.tokenDef: + continue + if func.tokenDef.str == 'main': + continue + self.reportError(func.tokenDef, 8, 4) + extern_var_with_def = {} + extern_var_without_def = {} + for var in cfg.variables: + if not var.isGlobal: + continue + if var.isStatic: + continue + if var.nameToken is None: + continue + tok = var.nameToken + if tok.next.str == ";": + if tok.next.isSplittedVarDeclEq or (tok.valueType and tok.valueType.type == "record"): + self.insert_in_dict(extern_var_with_def, tok.str, tok) + else: + self.insert_in_dict(extern_var_without_def, tok.str, tok) + else: + self.insert_in_dict(extern_var_without_def, var.nameToken.str, var.nameToken) + + for var in extern_var_with_def: + if var not in extern_var_without_def: + for t in extern_var_with_def[var]: + self.reportError(t, 8, 4) + + for var_str, var_tok in extern_var_without_def.items(): + warn = True + if var_str not in extern_var_with_def: + for t in var_tok: + if t.variable.isExtern: + warn = False + break + if warn: + for t in var_tok: + self.reportError(t, 8, 4) + + def misra_8_5(self, dumpfile, cfg): + self._save_ctu_summary_identifiers(dumpfile, cfg) + + def misra_8_6(self, dumpfile, cfg): + self._save_ctu_summary_identifiers(dumpfile, cfg) + + def misra_8_7(self, dumpfile, cfg): + self._save_ctu_summary_usage(dumpfile, cfg) + + def misra_8_8(self, cfg): + vars = {} + for var in cfg.variables: + if var.access != 'Global': + continue + if var.nameToken is None: + continue + varname = var.nameToken.str + if varname in vars: + vars[varname].append(var) + else: + vars[varname] = [var] + for varname, varlist in vars.items(): + static_var = None + extern_var = None + for var in varlist: + if var.isStatic: + static_var = var + elif var.isExtern: + extern_var = var + if static_var and extern_var: + self.reportError(extern_var.nameToken, 8, 8) + + def misra_8_9(self, cfg): + variables = {} + for scope in cfg.scopes: + if scope.type != 'Function': + continue + variables_used_in_scope = [] + tok = scope.bodyStart + while tok != scope.bodyEnd: + if tok.variable and tok.variable.access == 'Global' and tok.variable.isStatic: + if tok.variable not in variables_used_in_scope: + variables_used_in_scope.append(tok.variable) + tok = tok.next + for var in variables_used_in_scope: + if var in variables: + variables[var] += 1 + else: + variables[var] = 1 + for var, count in variables.items(): + if count == 1: + self.reportError(var.nameToken, 8, 9) + + + def misra_8_10(self, cfg): + for func in cfg.functions: + if func.isInlineKeyword and not func.isStatic: + self.reportError(func.tokenDef, 8, 10) + + def misra_8_11(self, data): + for var in data.variables: + if var.isExtern and simpleMatch(var.nameToken.next, '[ ]') and var.nameToken.scope.type == 'Global': + self.reportError(var.nameToken, 8, 11) + + def misra_8_12(self, data): + for scope in data.scopes: + if scope.type != 'Enum': + continue + enum_values = [] + implicit_enum_values = [] + e_token = scope.bodyStart.next + while e_token != scope.bodyEnd: + if e_token.str == '(': + e_token = e_token.link + continue + if e_token.previous.str not in ',{': + e_token = e_token.next + continue + if e_token.isName and e_token.values and e_token.valueType and e_token.valueType.typeScope == scope: + token_values = [v.intvalue for v in e_token.values] + enum_values += token_values + if e_token.next.str != "=": + implicit_enum_values += token_values + e_token = e_token.next + for implicit_enum_value in implicit_enum_values: + if enum_values.count(implicit_enum_value) != 1: + self.reportError(scope.bodyStart, 8, 12) + + def misra_8_14(self, rawTokens): + for token in rawTokens: + if token.str == 'restrict': + self.reportError(token, 8, 14) + + def misra_9_2(self, data): + misra_9.misra_9_x(self, data, 902) + + def misra_9_3(self, data): + misra_9.misra_9_x(self, data, 903) + + def misra_9_4(self, data): + misra_9.misra_9_x(self, data, 904) + + def misra_9_5(self, data, rawTokens): + misra_9.misra_9_x(self, data, 905, rawTokens) + #for token in rawTokens: + # if simpleMatch(token, '[ ] = { ['): + # self.reportError(token, 9, 5) + + def misra_10_1(self, data): + for token in data.tokenlist: + if not token.isOp: + continue + + for t1, t2 in itertools.product( + list(getTernaryOperandsRecursive(token.astOperand1) or [token.astOperand1]), + list(getTernaryOperandsRecursive(token.astOperand2) or [token.astOperand2]), + ): + e1 = getEssentialTypeCategory(t1) + e2 = getEssentialTypeCategory(t2) + if not e1 or not e2: + continue + if token.str in ('<<', '>>'): + if not isUnsignedType(e1): + self.reportError(token, 10, 1) + elif not isUnsignedType(e2) and not token.astOperand2.isNumber: + self.reportError(token, 10, 1) + elif token.str in ('~', '&', '|', '^'): + e1_et = getEssentialType(token.astOperand1) + e2_et = getEssentialType(token.astOperand2) + if e1_et == 'char' or e2_et == 'char': + self.reportError(token, 10, 1) + + def misra_10_2(self, data): + def isEssentiallySignedOrUnsigned(op): + e = getEssentialType(op) + return e and (e.split(' ')[0] in ('unsigned', 'signed')) + + def isEssentiallyChar(op): + if op is None: + return False + if op.str == '+': + return isEssentiallyChar(op.astOperand1) or isEssentiallyChar(op.astOperand2) + return op.isChar + + for token in data.tokenlist: + if token.str not in ('+', '-'): + continue + + if (not isEssentiallyChar(token.astOperand1)) and (not isEssentiallyChar(token.astOperand2)): + continue + + if token.str == '+': + if isEssentiallyChar(token.astOperand1) and not isEssentiallySignedOrUnsigned(token.astOperand2): + self.reportError(token, 10, 2) + if isEssentiallyChar(token.astOperand2) and not isEssentiallySignedOrUnsigned(token.astOperand1): + self.reportError(token, 10, 2) + + if token.str == '-': + e1 = getEssentialType(token.astOperand1) + if e1 and e1.split(' ')[-1] != 'char': + self.reportError(token, 10, 2) + if not isEssentiallyChar(token.astOperand2) and not isEssentiallySignedOrUnsigned(token.astOperand2): + self.reportError(token, 10, 2) + + def misra_10_3(self, cfg): + def get_category(essential_type): + if essential_type: + if essential_type in ('bool', 'char'): + return essential_type + if essential_type.split(' ')[-1] in ('float', 'double'): + return 'floating' + if essential_type.split(' ')[0] in ('unsigned', 'signed'): + return essential_type.split(' ')[0] + return None + for tok in cfg.tokenlist: + if tok.isAssignmentOp: + lhs = getEssentialType(tok.astOperand1) + rhs = getEssentialType(tok.astOperand2) + #print(lhs) + #print(rhs) + if lhs is None or rhs is None: + continue + lhs_category = get_category(lhs) + rhs_category = get_category(rhs) + if lhs_category and rhs_category and lhs_category != rhs_category and rhs_category not in ('signed','unsigned'): + self.reportError(tok, 10, 3) + if bitsOfEssentialType(lhs) < bitsOfEssentialType(rhs) and (lhs != "bool" or tok.astOperand2.str not in ('0','1')): + self.reportError(tok, 10, 3) + + def misra_10_4(self, data): + op = {'+', '-', '*', '/', '%', '&', '|', '^', '+=', '-=', ':'} + for token in data.tokenlist: + if token.str not in op and not token.isComparisonOp: + continue + if not token.astOperand1 or not token.astOperand2: + continue + if not token.astOperand1.valueType or not token.astOperand2.valueType: + continue + if ((token.astOperand1.str in op or token.astOperand1.isComparisonOp) and + (token.astOperand2.str in op or token.astOperand2.isComparisonOp)): + e1, e2 = getEssentialCategorylist(token.astOperand1.astOperand2, token.astOperand2.astOperand1) + elif token.astOperand1.str in op or token.astOperand1.isComparisonOp: + e1, e2 = getEssentialCategorylist(token.astOperand1.astOperand2, token.astOperand2) + elif token.astOperand2.str in op or token.astOperand2.isComparisonOp: + e1, e2 = getEssentialCategorylist(token.astOperand1, token.astOperand2.astOperand1) + else: + e1, e2 = getEssentialCategorylist(token.astOperand1, token.astOperand2) + if token.str == "+=" or token.str == "+": + if e1 == "char" and (e2 == "signed" or e2 == "unsigned"): + continue + if e2 == "char" and (e1 == "signed" or e1 == "unsigned"): + continue + if token.str == "-=" or token.str == "-": + if e1 == "char" and (e2 == "signed" or e2 == "unsigned"): + continue + if e1 and e2 and (e1.find('Anonymous') != -1 and (e2 == "signed" or e2 == "unsigned")): + continue + if e1 and e2 and (e2.find('Anonymous') != -1 and (e1 == "signed" or e1 == "unsigned")): + continue + if e1 and e2 and e1 != e2: + self.reportError(token, 10, 4) + + def misra_10_5(self, cfg): + def _get_essential_category(token): + essential_type = getEssentialType(token) + #print(essential_type) + if essential_type: + if essential_type in ('bool', 'char'): + return essential_type + if essential_type.split(' ')[-1] in ('float', 'double'): + return 'floating' + if essential_type.split(' ')[0] in ('unsigned', 'signed'): + return essential_type.split(' ')[0] + return None + for token in cfg.tokenlist: + if not isCast(token): + continue + to_type = _get_essential_category(token) + #print(to_type) + if to_type is None: + continue + from_type = _get_essential_category(token.astOperand1) + #print(from_type) + if from_type is None: + continue + if to_type == from_type: + continue + if to_type == 'bool' or from_type == 'bool': + if token.astOperand1.isInt and token.astOperand1.getKnownIntValue() == 1: + # Exception + continue + self.reportError(token, 10, 5) + continue + if to_type == 'enum': + self.reportError(token, 10, 5) + continue + if from_type == 'float' and to_type == 'char': + self.reportError(token, 10, 5) + continue + if from_type == 'char' and to_type == 'float': + self.reportError(token, 10, 5) + continue + + def misra_10_6(self, data): + for token in data.tokenlist: + if token.str != '=' or not token.astOperand1 or not token.astOperand2: + continue + if not is_composite_expr(token.astOperand2): + continue + vt1 = token.astOperand1.valueType + vt2 = token.astOperand2.valueType + if not vt1 or vt1.pointer > 0: + continue + if not vt2 or vt2.pointer > 0: + continue + try: + if isCast(token.astOperand2): + e = vt2.type + else: + e = getEssentialType(token.astOperand2) + if not e: + continue + if e == "char" and vt1.type == "int": + # When arithmetic operations are performed on char values, they are usually promoted to int + continue + lhsbits = vt1.bits if vt1.bits else bitsOfEssentialType(vt1.type) + if lhsbits > bitsOfEssentialType(e): + self.reportError(token, 10, 6) + except ValueError: + pass + + def misra_10_7(self, cfg): + for token in cfg.tokenlist: + if token.astOperand1 is None or token.astOperand2 is None: + continue + if not token.isArithmeticalOp: + continue + if not is_composite_expr(token): + continue + parent = token.astParent + if parent is None: + continue + if not parent.isArithmeticalOp: + if not parent.isAssignmentOp: + continue + if parent.str == '=': + continue + token_type = getEssentialType(token) + if token_type is None: + continue + sibling = parent.astOperand1 if (token == parent.astOperand2) else parent.astOperand2 + sibling_type = getEssentialType(sibling) + if sibling_type is None: + continue + b1 = bitsOfEssentialType(token_type) + b2 = bitsOfEssentialType(sibling_type) + if b1 > 0 and b1 < b2: + self.reportError(token, 10, 7) + + def misra_10_8(self, data): + for token in data.tokenlist: + if not isCast(token): + continue + if not token.valueType or token.valueType.pointer > 0: + continue + if not token.astOperand1.valueType or token.astOperand1.valueType.pointer > 0: + continue + if not token.astOperand1.astOperand1: + continue + if token.astOperand1.str not in ('+', '-', '*', '/', '%', '&', '|', '^', '>>', "<<", "?", ":", '~'): + continue + if token.astOperand1.str != '~' and not token.astOperand1.astOperand2: + continue + if token.astOperand1.str == '~': + e2 = getEssentialTypeCategory(token.astOperand1.astOperand1) + else: + e2, e3 = getEssentialCategorylist(token.astOperand1.astOperand1, token.astOperand1.astOperand2) + if e2 != e3: + continue + e1 = getEssentialTypeCategory(token) + if e1 != e2: + self.reportError(token, 10, 8) + else: + try: + e = getEssentialType(token.astOperand1) + if not e: + continue + if bitsOfEssentialType(token.valueType.type) > bitsOfEssentialType(e): + self.reportError(token, 10, 8) + except ValueError: + pass + + def misra_11_1(self, data): + for token in data.tokenlist: + to_from = get_type_conversion_to_from(token) + if to_from is None: + continue + from_type = get_function_pointer_type(to_from[1]) + if from_type is None: + continue + to_type = get_function_pointer_type(to_from[0]) + if to_type is None or to_type != from_type: + self.reportError(token, 11, 1) + + def misra_11_2(self, data): + def get_pointer_type(type_token): + while type_token and (type_token.str in ('const', 'struct')): + type_token = type_token.next + if type_token is None: + return None + if not type_token.isName: + return None + return type_token if (type_token.next and type_token.next.str == '*') else None + + incomplete_types = [] + + for token in data.tokenlist: + if token.str == 'struct' and token.next and token.next.next and token.next.isName and token.next.next.str == ';': + incomplete_types.append(token.next.str) + to_from = get_type_conversion_to_from(token) + if to_from is None: + continue + to_pointer_type_token = get_pointer_type(to_from[0]) + if to_pointer_type_token is None: + continue + from_pointer_type_token = get_pointer_type(to_from[1]) + if from_pointer_type_token is None: + continue + if to_pointer_type_token.str == from_pointer_type_token.str: + continue + if from_pointer_type_token.typeScope is None and (from_pointer_type_token.str in incomplete_types): + self.reportError(token, 11, 2) + elif to_pointer_type_token.typeScope is None and (to_pointer_type_token.str in incomplete_types): + self.reportError(token, 11, 2) + + def misra_11_3(self, data): + for token in data.tokenlist: + if not isCast(token): + continue + vt1 = token.valueType + vt2 = token.astOperand1.valueType + if not vt1 or not vt2: + continue + if vt1.type == 'void' or vt2.type == 'void': + continue + if (vt1.pointer > 0 and vt1.type == 'record' and + vt2.pointer > 0 and vt2.type == 'record' and + vt1.typeScopeId != vt2.typeScopeId): + self.reportError(token, 11, 3) + elif (vt1.pointer == vt2.pointer and vt1.pointer > 0 and + vt1.type != vt2.type and vt1.type != 'char'): + self.reportError(token, 11, 3) + + def misra_11_4(self, data): + # Get list of macro definitions + macros = {} + for directive in data.directives: + #define X ((peripheral_t *)0x40000U) + res = re.match(r'#define ([A-Za-z0-9_]+).*', directive.str) + if res: + if res.group(1) in macros: + macros[res.group(1)].append(directive) + else: + macros[res.group(1)] = [directive] + + # If macro definition is non-compliant then warn about the macro definition instead of + # the macro usages. To reduce diagnostics for a non-compliant macro. + bad_macros = [] + for token in data.tokenlist: + if not isCast(token): + continue + vt1 = token.valueType + vt2 = token.astOperand1.valueType + if not vt1 or not vt2: + continue + if vt2.pointer > 0 and vt1.pointer == 0 and (vt1.isIntegral() or vt1.isEnum()) and vt2.type != 'void': + self.reportError(token, 11, 4) + elif vt1.pointer > 0 and vt2.pointer == 0 and (vt2.isIntegral() or vt2.isEnum()) and vt1.type != 'void': + if token.macroName is not None and \ + token.macroName == token.astOperand1.macroName and \ + token.astOperand1.isInt and \ + token.link.previous.str == '*' and \ + token.macroName == token.link.previous.macroName and \ + token.macroName in macros and \ + len(macros[token.macroName]) == 1: + if token.macroName not in bad_macros: + bad_macros.append(token.macroName) + self.reportError(macros[token.macroName][0], 11, 4) + continue + self.reportError(token, 11, 4) + + def misra_11_5(self, data): + for token in data.tokenlist: + if not isCast(token): + if token.astOperand1 and token.astOperand2 and token.str == "=" and token.next.str != "(": + vt1 = token.astOperand1.valueType + vt2 = token.astOperand2.valueType + if not vt1 or not vt2: + continue + if vt1.pointer > 0 and vt1.type != 'void' and vt2.pointer == vt1.pointer and vt2.type == 'void': + self.reportError(token, 11, 5) + continue + if token.astOperand1.astOperand1 and token.astOperand1.astOperand1.str in ( + 'malloc', 'calloc', 'realloc', 'free'): + continue + vt1 = token.valueType + vt2 = token.astOperand1.valueType + if not vt1 or not vt2: + continue + if vt1.pointer > 0 and vt1.type != 'void' and vt2.pointer == vt1.pointer and vt2.type == 'void': + self.reportError(token, 11, 5) + + def misra_11_6(self, data): + for token in data.tokenlist: + if not isCast(token): + continue + vt1 = token.valueType + vt2 = token.astOperand1.valueType + if not vt1 or not vt2: + continue + if vt1.pointer == 1 and vt1.type == 'void' and vt2.pointer == 0 and token.astOperand1.getKnownIntValue() != 0: + self.reportError(token, 11, 6) + elif vt1.pointer == 0 and vt1.type != 'void' and vt2.pointer == 1 and vt2.type == 'void': + self.reportError(token, 11, 6) + + def misra_11_7(self, data): + for token in data.tokenlist: + if not isCast(token): + continue + vt1 = token.valueType + vt2 = token.astOperand1.valueType + if not vt1 or not vt2: + continue + if token.astOperand1.astOperand1: + continue + if (vt2.pointer > 0 and vt1.pointer == 0 and + not vt1.isIntegral() and not vt1.isEnum() and + vt1.type != 'void'): + self.reportError(token, 11, 7) + elif (vt1.pointer > 0 and vt2.pointer == 0 and + not vt2.isIntegral() and not vt2.isEnum() and + vt1.type != 'void'): + self.reportError(token, 11, 7) + + def misra_11_8(self, data): + # TODO: reuse code in CERT-EXP05 + for token in data.tokenlist: + if isCast(token): + # C-style cast + if not token.valueType: + continue + if not token.astOperand1.valueType: + continue + if token.valueType.pointer == 0: + continue + if token.astOperand1.valueType.pointer == 0: + continue + const1 = token.valueType.constness + const2 = token.astOperand1.valueType.constness + if (const1 % 2) < (const2 % 2): + self.reportError(token, 11, 8) + + elif token.str == '(' and token.astOperand1 and token.astOperand2 and token.astOperand1.function: + # Function call + function = token.astOperand1.function + arguments = getArguments(token) + for argnr, argvar in function.argument.items(): + if argnr < 1 or argnr > len(arguments): + continue + if not argvar.isPointer: + continue + argtok = arguments[argnr - 1] + if not argtok.valueType: + continue + if argtok.valueType.pointer == 0: + continue + const1 = argvar.constness + const2 = arguments[argnr - 1].valueType.constness + if (const1 % 2) < (const2 % 2): + self.reportError(token, 11, 8) + + def misra_11_9(self, data): + for token in data.tokenlist: + if token.astOperand1 and token.astOperand2 and token.str in ["=", "==", "!=", "?", ":"]: + vt1 = token.astOperand1.valueType + vt2 = token.astOperand2.valueType + if not vt1 or not vt2: + continue + if vt1.pointer > 0 and vt2.pointer == 0 and token.astOperand2.str == "NULL": + continue + if (token.astOperand2.values and vt1.pointer > 0 and + vt2.pointer == 0 and token.astOperand2.values): + if token.astOperand2.getValue(0): + self.reportError(token, 11, 9) + + def misra_12_1_sizeof(self, rawTokens): + state = 0 + compiled = re.compile(r'^[a-zA-Z_]') + for tok in rawTokens: + if tok.str.startswith('//') or tok.str.startswith('/*'): + continue + if tok.str == 'sizeof': + state = 1 + elif state == 1: + if compiled.match(tok.str): + state = 2 + else: + state = 0 + elif state == 2: + if tok.str in ('+', '-', '*', '/', '%'): + self.reportError(tok, 12, 1) + else: + state = 0 + + def misra_12_1(self, data): + for token in data.tokenlist: + p = getPrecedence(token) + if p < 2 or p > 12: + continue + p1 = getPrecedence(token.astOperand1) + if p < p1 <= 12 and numberOfParentheses(token.astOperand1, token): + self.reportError(token, 12, 1) + continue + p2 = getPrecedence(token.astOperand2) + if p < p2 <= 12 and numberOfParentheses(token, token.astOperand2): + self.reportError(token, 12, 1) + continue + + def misra_12_2(self, data): + for token in data.tokenlist: + if not (token.str in ('<<', '>>')): + continue + if (not token.astOperand2) or (not token.astOperand2.values): + continue + maxval = 0 + for val in token.astOperand2.values: + if val.intvalue and val.intvalue > maxval: + maxval = val.intvalue + if maxval == 0: + continue + sz = bitsOfEssentialType(getEssentialType(token.astOperand1)) + if sz <= 0: + continue + if maxval >= sz: + self.reportError(token, 12, 2) + + def misra_12_3(self, data): + for token in data.tokenlist: + if token.str == ';' and (token.isSplittedVarDeclComma is True): + self.reportError(token, 12, 3) + if token.str == ',' and token.astParent and token.astParent.str == ';': + self.reportError(token, 12, 3) + if token.str == ',' and token.astParent is None: + if token.scope.type in ('Class', 'Struct'): + # Is this initlist.. + tok = token + while tok and tok.str == ',': + tok = tok.next + if tok and tok.next and tok.isName and tok.next.str == '(': + tok = tok.next.link.next + if tok.str == '{': + # This comma is used in initlist, do not warn + continue + prev = token.previous + while prev: + if prev.str == ';': + self.reportError(token, 12, 3) + break + elif prev.str in ')}]': + prev = prev.link + elif prev.str in '({[': + break + prev = prev.previous + + def misra_12_4_check_expr(self, expr): + if not expr.astOperand2 or not expr.astOperand1: + return + if expr.valueType is None: + return + if expr.valueType.sign is None or expr.valueType.sign != 'unsigned': + return + if expr.valueType.pointer > 0: + return + if not expr.valueType.isIntegral(): + return + op1 = expr.astOperand1.getKnownIntValue() + if op1 is None: + return + op2 = expr.astOperand2.getKnownIntValue() + if op2 is None: + return + bits = bitsOfEssentialType('unsigned ' + expr.valueType.type) + if bits <= 0 or bits >= 64: + return + max_value = (1 << bits) - 1 + if not is_constant_integer_expression(expr): + return + if expr.str == '+' and op1 + op2 > max_value: + self.reportError(expr, 12, 4) + elif expr.str == '-' and op1 - op2 < 0: + self.reportError(expr, 12, 4) + elif expr.str == '*' and op1 * op2 > max_value: + self.reportError(expr, 12, 4) + def misra_12_4(self, cfg): + if not cfg.tokenlist: + return + expr = cfg.tokenlist[0] + while expr.next: + expr = expr.next + if expr.str == "?" and expr.astOperand2.str == ":": + known_value = expr.astOperand1.getKnownIntValue() + if known_value == 1: + tok = expr + while tok != expr.astOperand2: + self.misra_12_4_check_expr(tok) + tok = tok.next + expr = tok + while expr.str not in (";", "{", "}"): + expr = expr.next + continue + elif known_value == 0: + expr = expr.astOperand2 + self.misra_12_4_check_expr(expr) + + + def misra_13_1(self, data): + for token in data.tokenlist: + if simpleMatch(token, ") {") and token.next.astParent == token.link: + pass + elif not simpleMatch(token, '= {'): + continue + init = token.next + end = init.link + if not end: + continue # syntax is broken + + tn = init + while tn and tn != end: + if tn.str == '[' and tn.link: + tn = tn.link + if tn and tn.next and tn.next.str == '=': + tn = tn.next.next + continue + else: + break + if tn.str == '.' and tn.next and tn.next.isName: + tn = tn.next + if tn.next and tn.next.str == '=': + tn = tn.next.next + continue + if tn.str in {'++', '--'} or tn.isAssignmentOp: + self.reportError(init, 13, 1) + tn = tn.next + + def misra_13_3(self, data): + for token in data.tokenlist: + if token.str not in ('++', '--'): + continue + astTop = token + while astTop.astParent and astTop.astParent.str not in (',', ';'): + astTop = astTop.astParent + if countSideEffects(astTop) >= 2: + self.reportError(astTop, 13, 3) + + def misra_13_4(self, data): + for token in data.tokenlist: + if token.str != '=': + continue + if not token.astParent: + continue + if (token.astOperand1 is None) or (token.astOperand2 is None): + continue + if token.astOperand1.str == '[' and token.astOperand1.previous.str in ('{', ','): + continue + if not (token.astParent.str in [',', ';', '{']): + self.reportError(token, 13, 4) + + def misra_13_5(self, data): + for token in data.tokenlist: + if token.isLogicalOp and countSideEffectsRecursive(token.astOperand2) > 0: + self.reportError(token, 13, 5) + + def misra_13_6(self, data): + for token in data.tokenlist: + if token.str == 'sizeof' and countSideEffectsRecursive(token.next) > 0: + self.reportError(token, 13, 6) + + def misra_14_1(self, data): + for token in data.tokenlist: + if token.str == 'for': + exprs = getForLoopExpressions(token) + if not exprs: + continue + for counter in findCounterTokens(exprs[1]): + if counter.valueType and counter.valueType.isFloat(): + self.reportError(token, 14, 1) + elif token.str == 'while': + if isFloatCounterInWhileLoop(token): + self.reportError(token, 14, 1) + + def misra_14_2(self, data): + for token in data.tokenlist: + if token.str == 'for': + expressions = getForLoopExpressions(token) + if not expressions: + continue + if expressions[0] and not expressions[0].isAssignmentOp: + if expressions[0].str != "(" or not expressions[0].previous.isName: + self.reportError(token, 14, 2) + if countSideEffectsRecursive(expressions[1]) > 0: + self.reportError(token, 14, 2) + if countSideEffectsRecursive(expressions[2]) > 1: + self.reportError(token, 14, 2) + + counter_vars_first_clause, counter_vars_exit_modified = getForLoopCounterVariables(token, data) + if len(counter_vars_exit_modified) == 0: + # if it's not possible to identify a loop counter, all 3 clauses must be empty + for idx in range(len(expressions)): + if expressions[idx]: + self.reportError(token, 14, 2) + break + elif len(counter_vars_exit_modified) > 1: + # there shall be a single loop counter + self.reportError(token, 14, 2) + else: # len(counter_vars_exit_modified) == 1: + loop_counter = counter_vars_exit_modified.pop() + # if the first clause is not empty, then it shall (declare and) initialize the loop counter + if expressions[0] is not None and loop_counter not in counter_vars_first_clause: + self.reportError(token, 14, 2) + + # Inspect modification of loop counter in loop body + body_scope = token.next.link.next.scope + if not body_scope: + continue + tn = body_scope.bodyStart + while tn and tn != body_scope.bodyEnd: + if tn.variable == loop_counter: + if tn.next: + # TODO: Check modifications in function calls + if countSideEffectsRecursive(tn.next) > 0: + self.reportError(tn, 14, 2) + tn = tn.next + + def misra_14_4(self, data): + for token in data.tokenlist: + if token.str != '(': + continue + if not token.astOperand1 or not (token.astOperand1.str in ['if', 'while']): + continue + if isBoolExpression(token.astOperand2): + continue + if token.astOperand2.valueType: + self.reportError(token, 14, 4) + + def misra_15_1(self, data): + for token in data.tokenlist: + if token.str == "goto": + self.reportError(token, 15, 1) + + def misra_15_2(self, data): + for token in data.tokenlist: + if token.str != 'goto': + continue + if (not token.next) or (not token.next.isName): + continue + if not findGotoLabel(token): + self.reportError(token, 15, 2) + + def misra_15_3(self, data): + for token in data.tokenlist: + if token.str != 'goto': + continue + if (not token.next) or (not token.next.isName): + continue + tok = findGotoLabel(token) + if not tok: + continue + scope = token.scope + while scope and scope != tok.scope: + scope = scope.nestedIn + if not scope: + self.reportError(token, 15, 3) + # Jump crosses from one switch-clause to another is non-compliant + elif scope.type == 'Switch': + # Search for start of a current case block + tcase_start = token + while tcase_start and tcase_start.str not in ('case', 'default'): + tcase_start = tcase_start.previous + # Make sure that goto label doesn't occurs in the other + # switch-clauses + if tcase_start: + t = scope.bodyStart + in_this_case = False + while t and t != scope.bodyEnd: + if t == tcase_start: + in_this_case = True + if in_this_case and t.str not in ('case', 'default'): + in_this_case = False + if t == tok and not in_this_case: + self.reportError(token, 15, 3) + break + t = t.next + + def misra_15_4(self, data): + # Return a list of scopes affected by a break or goto + def getLoopsAffectedByBreak(knownLoops, scope, isGoto): + if scope and scope.type and scope.type not in ['Global', 'Function']: + if not isGoto and scope.type == 'Switch': + return + if scope.type in ['For', 'While', 'Do']: + knownLoops.append(scope) + if not isGoto: + return + getLoopsAffectedByBreak(knownLoops, scope.nestedIn, isGoto) + + loopWithBreaks = {} + for token in data.tokenlist: + if token.str not in ['break', 'goto']: + continue + + affectedLoopScopes = [] + getLoopsAffectedByBreak(affectedLoopScopes, token.scope, token.str == 'goto') + for scope in affectedLoopScopes: + if scope in loopWithBreaks: + loopWithBreaks[scope] += 1 + else: + loopWithBreaks[scope] = 1 + + for scope, breakCount in loopWithBreaks.items(): + if breakCount > 1: + self.reportError(scope.bodyStart, 15, 4) + + def misra_15_5(self, data): + for token in data.tokenlist: + if token.str == 'return' and token.scope.type != 'Function': + self.reportError(token, 15, 5) + + def misra_15_6(self, rawTokens): + state = 0 + indent = 0 + tok1 = None + def tokAt(tok,i): + while i < 0 and tok: + tok = tok.previous + if tok.str.startswith('//') or tok.str.startswith('/*'): + continue + i += 1 + while i > 0 and tok: + tok = tok.next + if tok.str.startswith('//') or tok.str.startswith('/*'): + continue + i -= 1 + return tok + + def strtokens(tok, i1, i2): + tok1 = tokAt(tok, i1) + tok2 = tokAt(tok, i2) + tok = tok1 + s = '' + while tok != tok2: + if tok.str.startswith('//') or tok.str.startswith('/*'): + tok = tok.next + continue + s += ' ' + tok.str + tok = tok.next + s += ' ' + tok.str + return s[1:] + + for token in rawTokens: + if token.str in ['if', 'for', 'while']: + if strtokens(token,-1,0) == '# if': + continue + if strtokens(token,-1,0) == "} while": + # is there a 'do { .. } while'? + start = rawlink(tokAt(token,-1)) + if start and strtokens(start, -1, 0) == 'do {': + continue + if state == 2: + self.reportError(tok1, 15, 6) + state = 1 + indent = 0 + tok1 = token + elif token.str == 'else': + if strtokens(token,-1,0) == '# else': + continue + if strtokens(token,0,1) == 'else if': + continue + if state == 2: + self.reportError(tok1, 15, 6) + state = 2 + indent = 0 + tok1 = token + elif state == 1: + if indent == 0 and token.str != '(': + state = 0 + continue + if token.str == '(': + indent = indent + 1 + elif token.str == ')': + if indent == 0: + state = 0 + elif indent == 1: + state = 2 + indent = indent - 1 + elif state == 2: + if token.str.startswith('//') or token.str.startswith('/*'): + continue + state = 0 + if token.str not in ('{', '#'): + self.reportError(tok1, 15, 6) + + def misra_15_7(self, data): + for scope in data.scopes: + if scope.type != 'Else': + continue + if not simpleMatch(scope.bodyStart, '{ if ('): + continue + if scope.bodyStart.column > 0: + continue + tok = scope.bodyStart.next.next.link + if not simpleMatch(tok, ') {'): + continue + tok = tok.next.link + if not simpleMatch(tok, '} else'): + self.reportError(tok, 15, 7) + + def misra_16_1(self, cfg): + for scope in cfg.scopes: + if scope.type != 'Switch': + continue + in_case_or_default = False + tok = scope.bodyStart.next + while tok != scope.bodyEnd: + if not in_case_or_default: + if tok.str not in ('case', 'default'): + self.reportError(tok, 16, 1) + else: + in_case_or_default = True + else: + if simpleMatch(tok, 'break ;'): + in_case_or_default = False + tok = tok.next + if tok.str == '{': + tok = tok.link + if tok.scope.type == 'Unconditional' and simpleMatch(tok.previous.previous, 'break ;'): + in_case_or_default = False + tok = tok.next + + def misra_16_2(self, data): + for token in data.tokenlist: + if token.str == 'case' and token.scope.type != 'Switch': + self.reportError(token, 16, 2) + + def misra_16_3(self, rawTokens): + STATE_NONE = 0 # default state, not in switch case/default block + STATE_BREAK = 1 # break/comment is seen but not its ';' + STATE_OK = 2 # a case/default is allowed (we have seen 'break;'/'comment'/'{'/attribute) + STATE_SWITCH = 3 # walking through switch statement scope + + define = None + state = STATE_NONE + end_switch_token = None # end '}' for the switch scope + for token in rawTokens: + if simpleMatch(token, '# define'): + define = token + if define: + if token.linenr != define.linenr: + define = None + else: + continue + + # Find switch scope borders + if token.str == 'switch': + state = STATE_SWITCH + if state == STATE_SWITCH: + if token.str == '{': + end_switch_token = findRawLink(token) + else: + continue + + if token.str == 'break' or token.str == 'return' or token.str == 'throw': + state = STATE_BREAK + elif token.str == ';': + if state == STATE_BREAK: + state = STATE_OK + elif token.next and token.next == end_switch_token: + self.reportError(token.next, 16, 3) + else: + state = STATE_NONE + elif token.str.startswith('/*') or token.str.startswith('//'): + if 'fallthrough' in token.str.lower(): + state = STATE_OK + elif simpleMatch(token, '[ [ fallthrough ] ] ;'): + state = STATE_BREAK + elif token.str == '{': + state = STATE_OK + elif token.str == '}' and state == STATE_OK: + # is this {} an unconditional block of code? + prev = findRawLink(token) + if prev: + prev = prev.previous + while prev and prev.str[:2] in ('//', '/*'): + prev = prev.previous + if (prev is None) or (prev.str not in ':;{}'): + state = STATE_NONE + elif token.str == 'case' or token.str == 'default': + if state != STATE_OK: + self.reportError(token, 16, 3) + state = STATE_OK + + def misra_16_4(self, data): + for token in data.tokenlist: + if token.str != 'switch': + continue + if not simpleMatch(token, 'switch ('): + continue + if not simpleMatch(token.next.link, ') {'): + continue + startTok = token.next.link.next + tok = startTok.next + while tok and tok.str != '}': + if tok.str == '{': + tok = tok.link + elif tok.str == 'default': + break + tok = tok.next + if tok and tok.str != 'default': + self.reportError(token, 16, 4) + + def misra_16_5(self, data): + for token in data.tokenlist: + if token.str != 'default': + continue + if token.previous and token.previous.str == '{': + continue + tok2 = token + while tok2: + if tok2.str in ('}', 'case'): + break + if tok2.str == '{': + tok2 = tok2.link + tok2 = tok2.next + if tok2 and tok2.str == 'case': + self.reportError(token, 16, 5) + + def misra_16_6(self, data): + for token in data.tokenlist: + if not (simpleMatch(token, 'switch (') and simpleMatch(token.next.link, ') {')): + continue + tok = token.next.link.next.next + count = 0 + while tok: + if tok.str in ['break', 'return', 'throw']: + count = count + 1 + elif tok.str == '{': + tok = tok.link + if isNoReturnScope(tok): + count = count + 1 + elif tok.str == '}': + break + tok = tok.next + if count < 2: + self.reportError(token, 16, 6) + + def misra_16_7(self, data): + for token in data.tokenlist: + if simpleMatch(token, 'switch (') and isBoolExpression(token.next.astOperand2): + self.reportError(token, 16, 7) + + def misra_17_1(self, data): + for token in data.tokenlist: + if isFunctionCall(token) and token.astOperand1.str in ( + 'va_list', 'va_arg', 'va_start', 'va_end', 'va_copy'): + self.reportError(token, 17, 1) + elif token.str == 'va_list': + self.reportError(token, 17, 1) + + def misra_17_2(self, data): + # find recursions.. + def find_recursive_call(search_for_function, direct_call, calls_map, visited=None): + if visited is None: + visited = set() + if direct_call == search_for_function: + return True + for indirect_call in calls_map.get(direct_call, []): + if indirect_call == search_for_function: + return True + if indirect_call in visited: + # This has already been handled + continue + visited.add(indirect_call) + if find_recursive_call(search_for_function, indirect_call, calls_map, visited): + return True + return False + + # List functions called in each function + function_calls = {} + for scope in data.scopes: + if scope.type != 'Function': + continue + calls = [] + tok = scope.bodyStart + while tok != scope.bodyEnd: + tok = tok.next + if not isFunctionCall(tok, data.standards.c): + continue + f = tok.astOperand1.function + if f is not None and f not in calls: + calls.append(f) + function_calls[scope.function] = calls + + # Report warnings for all recursions.. + for func in function_calls: + for call in function_calls[func]: + if not find_recursive_call(func, call, function_calls): + # Function call is not recursive + continue + # Warn about all functions calls.. + for scope in data.scopes: + if scope.type != 'Function' or scope.function != func: + continue + tok = scope.bodyStart + while tok != scope.bodyEnd: + if tok.function and tok.function == call: + self.reportError(tok, 17, 2) + tok = tok.next + + def misra_17_3(self, cfg): + for w in cfg.clang_warnings: + if w['message'].endswith('[-Wimplicit-function-declaration]'): + self.reportError(cppcheckdata.Location(w), 17, 3) + for token in cfg.tokenlist: + if token.str not in ["while", "if"]: + continue + if token.next.str != "(": + continue + tok = token.next + end_token = token.next.link + while tok != end_token: + if tok.isName and tok.function is None and tok.valueType is None and tok.next.str == "(" and \ + tok.next.valueType is None and not isKeyword(tok.str) and not isStdLibId(tok.str): + self.reportError(tok, 17, 3) + break + tok = tok.next + + def misra_config(self, data): + for var in data.variables: + if not var.isArray or var.nameToken is None or not cppcheckdata.simpleMatch(var.nameToken.next, '['): + continue + tok = var.nameToken.next + while tok.str == '[': + sz = tok.astOperand2 + if sz and sz.getKnownIntValue() is None: + has_var = False + unknown_constant = False + tokens = [sz] + while len(tokens) > 0: + t = tokens[-1] + tokens = tokens[:-1] + if t: + if t.isName and t.getKnownIntValue() is None: + if t.varId or t.variable: + has_var = True + continue + unknown_constant = True + self.report_config_error(tok, 'Unknown constant {}, please review configuration'.format(t.str)) + if t.isArithmeticalOp: + tokens += [t.astOperand1, t.astOperand2] + if not unknown_constant and not has_var: + self.report_config_error(tok, 'Unknown array size, please review configuration') + tok = tok.link.next + + for token in data.tokenlist: + if token.str not in ("while", "if"): + continue + tok = token.next + if token is None or tok.str != "(": + continue + end_token = tok.link + while tok != end_token: + tok = tok.next + if tok.str == 'sizeof' and tok.next.str == '(': + tok = tok.next.link + continue + if tok.str == "(" and tok.isCast: + tok = tok.link + continue + if not tok.isName: + continue + if tok.function or tok.variable or tok.varId or tok.valueType or tok.typeScope: + continue + if tok.next.str == "(" or tok.str in ["EOF"]: + continue + if isKeyword(tok.str) or isStdLibId(tok.str): + continue + if tok.astParent is None: + continue + if tok.astParent.str == "." and tok.astParent.valueType: + continue + self.report_config_error(tok, "Variable '%s' is unknown" % tok.str) + + def misra_17_6(self, rawTokens): + for token in rawTokens: + if simpleMatch(token, '[ static'): + self.reportError(token, 17, 6) + + def misra_17_7(self, data): + for token in data.tokenlist: + if not token.scope.isExecutable: + continue + if token.str != '(' or token.astParent: + continue + if token.astOperand1 is None or not token.astOperand1.isName: + continue + if token.astOperand1.varId and (token.astOperand1.variable is None or get_function_pointer_type(token.astOperand1.variable.typeStartToken) is None): + continue + if token.valueType is None: + continue + if token.valueType.type == 'void' and token.valueType.pointer == 0: + continue + self.reportError(token, 17, 7) + + def misra_17_8(self, data): + for token in data.tokenlist: + if not (token.isAssignmentOp or (token.str in ('++', '--'))): + continue + if not token.astOperand1: + continue + var = token.astOperand1.variable + if var and var.isArgument: + self.reportError(token, 17, 8) + + def misra_18_4(self, data): + for token in data.tokenlist: + if token.str not in ('+', '-', '+=', '-='): + continue + if token.astOperand1 is None or token.astOperand2 is None: + continue + vt1 = token.astOperand1.valueType + vt2 = token.astOperand2.valueType + if vt1 and vt1.pointer > 0: + self.reportError(token, 18, 4) + elif vt2 and vt2.pointer > 0: + self.reportError(token, 18, 4) + + def misra_18_5(self, data): + for var in data.variables: + if not var.isPointer: + continue + typetok = var.nameToken + count = 0 + while typetok: + if typetok.str == '*': + count = count + 1 + elif not typetok.isName: + break + typetok = typetok.previous + if count > 2: + self.reportError(var.nameToken, 18, 5) + + def misra_18_7(self, data): + for scope in data.scopes: + if scope.type != 'Struct': + continue + + token = scope.bodyStart.next + while token != scope.bodyEnd and token is not None: + # Handle nested structures to not duplicate an error. + if token.str == '{': + token = token.link + + # skip function pointer parameter types + if token.astOperand1 is None: + pass + elif cppcheckdata.simpleMatch(token, "[ ]"): + self.reportError(token, 18, 7) + break + token = token.next + + def misra_18_8(self, data): + for var in data.variables: + if not var.isArray or not var.isLocal: + continue + # TODO Array dimensions are not available in dump, must look in tokens + typetok = var.nameToken.next + if not typetok or typetok.str != '[': + continue + # Unknown define or syntax error + if not typetok.astOperand2: + continue + if not isConstantExpression(typetok.astOperand2) and not isUnknownConstantExpression(typetok.astOperand2): + self.reportError(var.nameToken, 18, 8) + + def misra_19_2(self, data): + for token in data.tokenlist: + if token.str == 'union': + self.reportError(token, 19, 2) + + def misra_20_1(self, data): + token_in_file = {} + for token in data.tokenlist: + if token.file not in token_in_file: + token_in_file[token.file] = int(token.linenr) + else: + token_in_file[token.file] = min(token_in_file[token.file], int(token.linenr)) + + for directive in data.directives: + if not directive.str.startswith('#include'): + continue + if directive.file not in token_in_file: + continue + if token_in_file[directive.file] < int(directive.linenr): + self.reportError(directive, 20, 1) + + def misra_20_2(self, data): + for directive in data.directives: + if not directive.str.startswith('#include '): + continue + for pattern in ('\\', '//', '/*', ',', "'"): + if pattern in directive.str: + self.reportError(directive, 20, 2) + break + + def misra_20_3(self, data): + for directive in data.directives: + if not directive.str.startswith('#include '): + continue + + words = directive.str.split(' ') + + # If include directive contains more than two words, here would be + # violation anyway. + if len(words) > 2: + self.reportError(directive, 20, 3) + + # Handle include directives with not quoted argument + elif len(words) > 1: + filename = words[1] + if not ((filename.startswith('"') and + filename.endswith('"')) or + (filename.startswith('<') and + filename.endswith('>'))): + # We are handle only directly included files in the + # following format: #include file.h + # Cases with macro expansion provided by MISRA document are + # skipped because we don't always have access to directive + # definition. + if '.' in filename: + self.reportError(directive, 20, 3) + + def misra_20_4(self, data): + for directive in data.directives: + res = re.search(r'#define ([a-z][a-z0-9_]+)', directive.str) + if res and isKeyword(res.group(1), data.standards.c): + self.reportError(directive, 20, 4) + + def misra_20_5(self, data): + for directive in data.directives: + if directive.str.startswith('#undef '): + self.reportError(directive, 20, 5) + + def misra_20_7(self, data): + def find_string_concat(exp, arg, directive_args): + # Handle concatenation of string literals, e.g.: + # #define MACRO(A, B) (A " " B) + # Addon should not report errors for both macro arguments. + arg_pos = exp.find(arg, 0) + need_check = False + skip_next = False + state_in_string = False + pos_search = arg_pos + 1 + directive_args = [a.strip() for a in directive_args if a != arg] + arg = arg.strip() + while pos_search < len(exp): + if exp[pos_search] == '"': + if state_in_string: + state_in_string = False + else: + state_in_string = True + pos_search += 1 + elif exp[pos_search].isalnum(): + word = "" + while pos_search < len(exp) and exp[pos_search].isalnum(): + word += exp[pos_search] + pos_search += 1 + if word == arg: + pos_search += 1 + elif word in directive_args: + skip_next = True + break + elif exp[pos_search] == ' ': + pos_search += 1 + elif state_in_string: + pos_search += 1 + else: + need_check = True + break + return need_check, skip_next + + for directive in data.directives: + d = Define(directive) + exp = '(' + d.expansionList + ')' + skip_next = False + for arg in d.args: + if skip_next: + _, skip_next = find_string_concat(exp, arg, d.args) + continue + need_check, skip_next = find_string_concat(exp, arg, d.args) + if not need_check: + continue + + pos = 0 + while pos < len(exp): + pos = exp.find(arg, pos) + if pos < 0: + break + # is 'arg' used at position pos + pos1 = pos - 1 + pos2 = pos + len(arg) + pos = pos2 + if pos1 >= 0 and (isalnum(exp[pos1]) or exp[pos1] == '_'): + continue + if pos2 < len(exp) and (isalnum(exp[pos2]) or exp[pos2] == '_'): + continue + + while pos1 >= 0 and exp[pos1] == ' ': + pos1 -= 1 + if exp[pos1] == '#': + continue + if exp[pos1] not in '([,.': + self.reportError(directive, 20, 7) + break + while pos2 < len(exp) and exp[pos2] == ' ': + pos2 += 1 + if pos2 < len(exp) and exp[pos2] not in ')]#,': + self.reportError(directive, 20, 7) + break + + def misra_20_8(self, cfg): + for cond in cfg.preprocessor_if_conditions: + #print(cond) + if cond.result and cond.result not in (0,1): + self.reportError(cond, 20, 8) + + def misra_20_9(self, cfg): + for cond in cfg.preprocessor_if_conditions: + if cond.E is None: + continue + defined = [] + for directive in cfg.directives: + if directive.file == cond.file and directive.linenr == cond.linenr: + for name in re.findall(r'[^_a-zA-Z0-9]defined[ ]*\([ ]*([_a-zA-Z0-9]+)[ ]*\)', directive.str): + defined.append(name) + for name in re.findall(r'[^_a-zA-Z0-9]defined[ ]*([_a-zA-Z0-9]+)', directive.str): + defined.append(name) + break + for s in cond.E.split(' '): + if (s[0] >= 'A' and s[0] <= 'Z') or (s[0] >= 'a' and s[0] <= 'z'): + if isKeyword(s): + continue + if s in defined: + continue + self.reportError(cond, 20, 9) + + def misra_20_10(self, data): + for directive in data.directives: + d = Define(directive) + if d.expansionList.find('#') >= 0: + self.reportError(directive, 20, 10) + + def misra_20_11(self, cfg): + for directive in cfg.directives: + d = Define(directive) + for arg in d.args: + res = re.search(r'[^#]#[ ]*%s[ ]*##' % arg, ' ' + d.expansionList) + if res: + self.reportError(directive, 20, 11) + + def misra_20_12(self, cfg): + def _is_hash_hash_op(expansion_list, arg): + return re.search(r'##[ ]*%s[^a-zA-Z0-9_]' % arg, expansion_list) or \ + re.search(r'[^a-zA-Z0-9_]%s[ ]*##' % arg, expansion_list) + + def _is_other_op(expansion_list, arg): + pos = expansion_list.find(arg) + while pos >= 0: + pos1 = pos - 1 + pos2 = pos + len(arg) + pos = expansion_list.find(arg, pos2) + if isalnum(expansion_list[pos1]) or expansion_list[pos1] == '_': + continue + if isalnum(expansion_list[pos2]) or expansion_list[pos2] == '_': + continue + while expansion_list[pos1] == ' ': + pos1 = pos1 - 1 + if expansion_list[pos1] == '#': + continue + while expansion_list[pos2] == ' ': + pos2 = pos2 + 1 + if expansion_list[pos2] == '#': + continue + return True + return False + + def _is_arg_macro_usage(directive, arg): + for macro_usage in cfg.macro_usage: + if macro_usage.file == directive.file and macro_usage.linenr == directive.linenr: + for macro_usage_arg in cfg.macro_usage: + if macro_usage_arg == macro_usage: + continue + if (macro_usage.usefile == macro_usage_arg.usefile and + macro_usage.uselinenr == macro_usage_arg.uselinenr and + macro_usage.usecolumn == macro_usage_arg.usecolumn): + # TODO: check arg better + return True + return False + + for directive in cfg.directives: + define = Define(directive) + expansion_list = '(%s)' % define.expansionList + for arg in define.args: + if not _is_hash_hash_op(expansion_list, arg): + continue + if not _is_other_op(expansion_list, arg): + continue + if _is_arg_macro_usage(directive, arg): + self.reportError(directive, 20, 12) + break + + def misra_20_13(self, data): + dir_pattern = re.compile(r'#[ ]*([^ (<]*)') + for directive in data.directives: + dir = directive.str + mo = dir_pattern.match(dir) + if mo: + dir = mo.group(1) + if dir not in ['define', 'elif', 'else', 'endif', 'error', 'if', 'ifdef', 'ifndef', 'include', + 'pragma', 'undef', 'warning']: + self.reportError(directive, 20, 13) + + def misra_20_14(self, data): + # stack for #if blocks. contains the #if directive until the corresponding #endif is seen. + # the size increases when there are inner #if directives. + ifStack = [] + for directive in data.directives: + if directive.str.startswith('#if ') or directive.str.startswith('#ifdef ') or directive.str.startswith( + '#ifndef '): + ifStack.append(directive) + elif directive.str == '#else' or directive.str.startswith('#elif '): + if len(ifStack) == 0: + self.reportError(directive, 20, 14) + ifStack.append(directive) + elif directive.file != ifStack[-1].file: + self.reportError(directive, 20, 14) + elif directive.str == '#endif': + if len(ifStack) == 0: + self.reportError(directive, 20, 14) + elif directive.file != ifStack[-1].file: + self.reportError(directive, 20, 14) + ifStack.pop() + + def misra_21_1(self, data): + re_forbidden_macro = re.compile(r'#(?:define|undef) _[_A-Z]+') + re_macro_name = re.compile(r'#(?:define|undef) (.+)[ $]') + + for d in data.directives: + # Search for forbidden identifiers + m = re.search(re_forbidden_macro, d.str) + if m: + self.reportError(d, 21, 1) + continue + + # Search standard library identifiers in macro names + m = re.search(re_macro_name, d.str) + if not m: + continue + name = m.group(1) + if isStdLibId(name, data.standards.c): + self.reportError(d, 21, 1) + + def misra_21_2(self, cfg): + for directive in cfg.directives: + define = Define(directive) + if re.match(r'_+BUILTIN_.*', define.name.upper()): + self.reportError(directive, 21, 2) + for func in cfg.functions: + if isStdLibId(func.name, cfg.standards.c): + tok = func.tokenDef if func.tokenDef else func.token + self.reportError(tok, 21, 2) + + def misra_21_3(self, data): + for token in data.tokenlist: + if isFunctionCall(token) and (token.astOperand1.str in ('malloc', 'calloc', 'realloc', 'free')): + self.reportError(token, 21, 3) + + def misra_21_4(self, data): + directive = findInclude(data.directives, '') + if directive: + self.reportError(directive, 21, 4) + + def misra_21_5(self, data): + directive = findInclude(data.directives, '') + if directive: + self.reportError(directive, 21, 5) + + def misra_21_6(self, data): + dir_stdio = findInclude(data.directives, '') + dir_wchar = findInclude(data.directives, '') + if dir_stdio: + self.reportError(dir_stdio, 21, 6) + if dir_wchar: + self.reportError(dir_wchar, 21, 6) + + def misra_21_7(self, data): + for token in data.tokenlist: + if isFunctionCall(token) and (token.astOperand1.str in ('atof', 'atoi', 'atol', 'atoll')): + self.reportError(token, 21, 7) + + def misra_21_8(self, data): + for token in data.tokenlist: + if isFunctionCall(token) and (token.astOperand1.str in ('abort', 'exit', 'getenv')): + self.reportError(token, 21, 8) + + def misra_21_9(self, data): + for token in data.tokenlist: + if (token.str in ('bsearch', 'qsort')) and token.next and token.next.str == '(': + self.reportError(token, 21, 9) + + def misra_21_10(self, data): + directive = findInclude(data.directives, '') + if directive: + self.reportError(directive, 21, 10) + + for token in data.tokenlist: + if (token.str == 'wcsftime') and token.next and token.next.str == '(': + self.reportError(token, 21, 10) + + def misra_21_11(self, data): + directive = findInclude(data.directives, '') + if directive: + self.reportError(directive, 21, 11) + + def misra_21_12(self, data): + if findInclude(data.directives, ''): + for token in data.tokenlist: + if token.str == 'fexcept_t' and token.isName: + self.reportError(token, 21, 12) + if isFunctionCall(token) and (token.astOperand1.str in ( + 'feclearexcept', + 'fegetexceptflag', + 'feraiseexcept', + 'fesetexceptflag', + 'fetestexcept')): + self.reportError(token, 21, 12) + + def misra_21_14(self, data): + # buffers used in strcpy/strlen/etc function calls + string_buffers = [] + for token in data.tokenlist: + if token.str[0] == 's' and isFunctionCall(token.next): + name, args = cppcheckdata.get_function_call_name_args(token) + if name is None: + continue + def _get_string_buffers(match, args, argnum): + if not match: + return [] + ret = [] + for a in argnum: + if a < len(args): + arg = args[a] + while arg and arg.str in ('.', '::'): + arg = arg.astOperand2 + if arg and arg.varId != 0 and arg.varId not in ret: + ret.append(arg.varId) + return ret + string_buffers += _get_string_buffers(name == 'strcpy', args, [0, 1]) + string_buffers += _get_string_buffers(name == 'strncpy', args, [0, 1]) + string_buffers += _get_string_buffers(name == 'strlen', args, [0]) + string_buffers += _get_string_buffers(name == 'strcmp', args, [0, 1]) + string_buffers += _get_string_buffers(name == 'sprintf', args, [0]) + string_buffers += _get_string_buffers(name == 'snprintf', args, [0, 3]) + + for token in data.tokenlist: + if token.str != 'memcmp': + continue + name, args = cppcheckdata.get_function_call_name_args(token) + if name is None: + continue + if len(args) != 3: + continue + for arg in args[:2]: + if arg.str[-1] == '\"': + self.reportError(arg, 21, 14) + continue + while arg and arg.str in ('.', '::'): + arg = arg.astOperand2 + if arg and arg.varId and arg.varId in string_buffers: + self.reportError(arg, 21, 14) + + def misra_21_15(self, data): + for token in data.tokenlist: + if token.str not in ('memcpy', 'memmove', 'memcmp'): + continue + name, args = cppcheckdata.get_function_call_name_args(token) + if name is None: + continue + if len(args) != 3: + continue + if args[0].valueType is None or args[1].valueType is None: + continue + if args[0].valueType.type == args[1].valueType.type: + continue + if args[0].valueType.type == 'void' or args[1].valueType.type == 'void': + continue + self.reportError(token, 21, 15) + + def misra_21_16(self, cfg): + for token in cfg.tokenlist: + if token.str != 'memcmp': + continue + name, args = cppcheckdata.get_function_call_name_args(token) + if name is None: + continue + if len(args) != 3: + continue + for arg in args[:2]: + if arg.valueType is None: + continue + if arg.valueType.pointer > 1: + continue + if getEssentialTypeCategory(arg) in ('unsigned', 'signed', 'bool'): + continue + if arg.valueType.isEnum(): + continue + self.reportError(token, 21, 16) + + def misra_21_19(self, cfg): + for token in cfg.tokenlist: + if token.str in ('localeconv', 'getenv', 'setlocale', 'strerror') and simpleMatch(token.next, '('): + name, _ = cppcheckdata.get_function_call_name_args(token) + if name is None or name != token.str: + continue + parent = token.next + while simpleMatch(parent.astParent, '+'): + parent = parent.astParent + # x = f() + if simpleMatch(parent.astParent, '=') and parent == parent.astParent.astOperand2: + lhs = parent.astParent.astOperand1 + if lhs and lhs.valueType and lhs.valueType.pointer > 0 and lhs.valueType.constness == 0: + self.reportError(token, 21, 19) + if token.str == '=': + lhs = token.astOperand1 + while simpleMatch(lhs, '*') and lhs.astOperand2 is None: + lhs = lhs.astOperand1 + if not simpleMatch(lhs, '.'): + continue + while simpleMatch(lhs, '.'): + lhs = lhs.astOperand1 + if lhs and lhs.variable and simpleMatch(lhs.variable.typeStartToken, 'lconv'): + self.reportError(token, 21, 19) + + def misra_21_20(self, cfg): + assigned = {} + invalid = [] + for token in cfg.tokenlist: + # No sophisticated data flow analysis, bail out if control flow is "interrupted" + if token.str in ('{', '}', 'break', 'continue', 'return'): + assigned = {} + invalid = [] + continue + + # When pointer is assigned, remove it from 'assigned' and 'invalid' + if token.varId and token.varId > 0 and simpleMatch(token.next, '='): + for name in assigned.keys(): + while token.varId in assigned[name]: + assigned[name].remove(token.varId) + while token.varId in invalid: + invalid.remove(token.varId) + continue + + # Calling dangerous function + if token.str in ('asctime', 'ctime', 'gmtime', 'localtime', 'localeconv', 'getenv', 'setlocale', 'strerror'): + name, args = cppcheckdata.get_function_call_name_args(token) + if name and name == token.str: + # make assigned pointers invalid + for varId in assigned.get(name, ()): + if varId not in invalid: + invalid.append(varId) + + # assign pointer + parent = token.next + while parent.astParent and (parent.astParent.str == '+' or isCast(parent.astParent)): + parent = parent.astParent + if simpleMatch(parent.astParent, '='): + eq = parent.astParent + vartok = eq.previous + if vartok and vartok.varId and vartok.varId > 0: + if name not in assigned: + assigned[name] = [vartok.varId] + elif vartok.varId not in assigned[name]: + assigned[name].append(vartok.varId) + continue + + # taking value of invalid pointer.. + if token.astParent and token.varId: + if token.varId in invalid: + self.reportError(token, 21, 20) + + def misra_21_21(self, cfg): + for token in cfg.tokenlist: + if token.str == 'system': + name, args = cppcheckdata.get_function_call_name_args(token) + if name == 'system' and len(args) == 1: + self.reportError(token, 21, 21) + + def misra_22_5(self, cfg): + for token in cfg.tokenlist: + if token.isUnaryOp("*") or (token.isBinaryOp() and token.str == '.'): + fileptr = token.astOperand1 + if fileptr.variable and cppcheckdata.simpleMatch(fileptr.variable.typeStartToken, 'FILE *'): + self.reportError(token, 22, 5) + + def misra_22_7(self, cfg): + for eofToken in cfg.tokenlist: + if eofToken.str != 'EOF': + continue + if eofToken.astParent is None or not eofToken.astParent.isComparisonOp: + continue + if eofToken.astParent.astOperand1 == eofToken: + eofTokenSibling = eofToken.astParent.astOperand2 + else: + eofTokenSibling = eofToken.astParent.astOperand1 + while isCast(eofTokenSibling) and eofTokenSibling.valueType and eofTokenSibling.valueType.type and eofTokenSibling.valueType.type == 'int': + eofTokenSibling = eofTokenSibling.astOperand2 if eofTokenSibling.astOperand2 else eofTokenSibling.astOperand1 + if eofTokenSibling is not None and eofTokenSibling.valueType and eofTokenSibling.valueType and eofTokenSibling.valueType.type in ('bool', 'char', 'short'): + self.reportError(eofToken, 22, 7) + + def misra_22_8(self, cfg): + is_zero = False + for token in cfg.tokenlist: + if simpleMatch(token, 'errno = 0'): + is_zero = True + if token.str == '(' and not simpleMatch(token.link, ') {'): + name, _ = cppcheckdata.get_function_call_name_args(token.previous) + if name is None: + continue + if is_errno_setting_function(name): + if not is_zero: + self.reportError(token, 22, 8) + else: + is_zero = False + + def misra_22_9(self, cfg): + errno_is_set = False + for token in cfg.tokenlist: + if token.str == '(' and not simpleMatch(token.link, ') {'): + name, args = cppcheckdata.get_function_call_name_args(token.previous) + if name is None: + continue + errno_is_set = is_errno_setting_function(name) + if errno_is_set and token.str in '{};': + errno_is_set = False + tok = token.next + while tok and tok.str not in ('{','}',';','errno'): + tok = tok.next + if tok is None or tok.str != 'errno': + self.reportError(token, 22, 9) + elif (tok.astParent is None) or (not tok.astParent.isComparisonOp): + self.reportError(token, 22, 9) + + def misra_22_10(self, cfg): + last_function_call = None + for token in cfg.tokenlist: + if token.isName and token.next.str == '(' and not simpleMatch(token.next.link, ') {'): + name, args = cppcheckdata.get_function_call_name_args(token) + last_function_call = name + if token.str == '}': + last_function_call = None + if token.str == 'errno' and token.astParent and token.astParent.isComparisonOp: + if last_function_call is None: + self.reportError(token, 22, 10) + elif not is_errno_setting_function(last_function_call): + self.reportError(token, 22, 10) + + + def get_verify_expected(self): + """Return the list of expected violations in the verify test""" + return self.verify_expected + + def get_verify_actual(self): + """Return the list of actual violations in for the verify test""" + return self.verify_actual + + def get_violations(self, violation_type=None): + """Return the list of violations for a normal checker run""" + if violation_type is None: + return self.violations.items() + else: + return self.violations[violation_type] + + def get_violation_types(self): + """Return the list of violations for a normal checker run""" + return self.violations.keys() + + def addSuppressedRule(self, ruleNum, + fileName=None, + lineNumber=None, + symbolName=None): + """ + Add a suppression to the suppressions data structure + + Suppressions are stored in a dictionary of dictionaries that + contains a list of tuples. + + The first dictionary is keyed by the MISRA rule in hundreds + format. The value of that dictionary is a dictionary of filenames. + If the value is None then the rule is assumed to be suppressed for + all files. + If the filename exists then the value of that dictionary contains a list + with the scope of the suppression. If the list contains an item of None + then the rule is assumed to be suppressed for the entire file. Otherwise + the list contains line number, symbol name tuples. + For each tuple either line number or symbol name can can be none. + + """ + normalized_filename = None + + if fileName is not None: + normalized_filename = os.path.expanduser(fileName) + normalized_filename = os.path.normpath(normalized_filename) + + if lineNumber is not None or symbolName is not None: + line_symbol = (lineNumber, symbolName) + else: + line_symbol = None + + # If the rule is not in the dict already then add it + if ruleNum not in self.suppressedRules: + ruleItemList = list() + ruleItemList.append(line_symbol) + + fileDict = dict() + fileDict[normalized_filename] = ruleItemList + + self.suppressedRules[ruleNum] = fileDict + + # Rule is added. Done. + return + + # Rule existed in the dictionary. Check for + # filename entries. + + # Get the dictionary for the rule number + fileDict = self.suppressedRules[ruleNum] + + # If the filename is not in the dict already add it + if normalized_filename not in fileDict: + ruleItemList = list() + ruleItemList.append(line_symbol) + + fileDict[normalized_filename] = ruleItemList + + # Rule is added with a file scope. Done + return + + # Rule has a matching filename. Get the rule item list. + + # Check the lists of rule items + # to see if this (lineNumber, symbolName) combination + # or None already exists. + ruleItemList = fileDict[normalized_filename] + + if line_symbol is None: + # is it already in the list? + if line_symbol not in ruleItemList: + ruleItemList.append(line_symbol) + else: + # Check the list looking for matches + matched = False + for each in ruleItemList: + if each is not None: + if (each[0] == line_symbol[0]) and (each[1] == line_symbol[1]): + matched = True + + # Append the rule item if it was not already found + if not matched: + ruleItemList.append(line_symbol) + + def isRuleSuppressed(self, file_path, linenr, ruleNum): + """ + Check to see if a rule is suppressed. + + :param ruleNum: is the rule number in hundreds format + :param file_path: File path of checked location + :param linenr: Line number of checked location + + If the rule exists in the dict then check for a filename + If the filename is None then rule is suppressed globally + for all files. + If the filename exists then look for list of + line number, symbol name tuples. If the list is None then + the rule is suppressed for the entire file + If the list of tuples exists then search the list looking for + matching line numbers. Symbol names are currently ignored + because they can include regular expressions. + TODO: Support symbol names and expression matching. + + """ + ruleIsSuppressed = False + + # Remove any prefix listed in command arguments from the filename. + filename = None + if file_path is not None: + if self.filePrefix is not None: + filename = remove_file_prefix(file_path, self.filePrefix) + else: + filename = os.path.basename(file_path) + + if ruleNum in self.suppressedRules: + fileDict = self.suppressedRules[ruleNum] + + # a file name entry of None means that the rule is suppressed + # globally + if None in fileDict: + ruleIsSuppressed = True + else: + # Does the filename match one of the names in + # the file list + if filename in fileDict: + # Get the list of ruleItems + ruleItemList = fileDict[filename] + + if None in ruleItemList: + # Entry of None in the ruleItemList means the rule is + # suppressed for all lines in the filename + ruleIsSuppressed = True + else: + # Iterate though the the list of line numbers + # and symbols looking for a match of the line + # number. Matching the symbol is a TODO: + for each in ruleItemList: + if each is not None: + if each[0] == linenr: + ruleIsSuppressed = True + + return ruleIsSuppressed + + def isRuleGloballySuppressed(self, rule_num): + """ + Check to see if a rule is globally suppressed. + :param rule_num: is the rule number in hundreds format + """ + if rule_num not in self.suppressedRules: + return False + return None in self.suppressedRules[rule_num] + + def showSuppressedRules(self): + """ + Print out rules in suppression list sorted by Rule Number + """ + print("Suppressed Rules List:") + outlist = list() + + for ruleNum in self.suppressedRules: + fileDict = self.suppressedRules[ruleNum] + + for fname in fileDict: + ruleItemList = fileDict[fname] + + for item in ruleItemList: + if item is None: + item_str = "None" + else: + item_str = str(item[0]) + + outlist.append("%s: %s: %s (%d locations suppressed)" % ( + float(ruleNum) / 100, fname, item_str, self.suppressionStats.get(ruleNum, 0))) + + for line in sorted(outlist, reverse=True): + print(" %s" % line) + + def setFilePrefix(self, prefix): + """ + Set the file prefix to ignore from files when matching + suppression files + """ + self.filePrefix = prefix + + def setSeverity(self, severity): + """ + Set the severity for all errors. + """ + self.severity = severity + + def setSuppressionList(self, suppressionlist): + num1 = 0 + num2 = 0 + rule_pattern = re.compile(r'([0-9]+).([0-9]+)') + strlist = suppressionlist.split(",") + + # build ignore list + for item in strlist: + res = rule_pattern.match(item) + if res: + num1 = int(res.group(1)) + num2 = int(res.group(2)) + ruleNum = (num1 * 100) + num2 + + self.addSuppressedRule(ruleNum) + + def report_config_error(self, location, errmsg): + errmsg = 'Because of missing configuration, misra checking is incomplete. There can be false negatives! ' + errmsg + cppcheck_severity = 'error' + error_id = 'config' + if self.settings.verify: + self.verify_actual.append('%s:%d %s' % (location.file, location.linenr, error_id)) + else: + cppcheckdata.reportError(location, cppcheck_severity, errmsg, 'misra', error_id) + + def reportError(self, location, num1, num2): + ruleNum = num1 * 100 + num2 + + if self.isRuleGloballySuppressed(ruleNum): + return + + if self.settings.verify: + self.verify_actual.append('%s:%d %d.%d' % (location.file, location.linenr, num1, num2)) + elif self.isRuleSuppressed(location.file, location.linenr, ruleNum): + # Error is suppressed. Ignore + self.suppressionStats.setdefault(ruleNum, 0) + self.suppressionStats[ruleNum] += 1 + return + else: + errorId = 'c2012-' + str(num1) + '.' + str(num2) + misra_severity = 'Undefined' + cppcheck_severity = 'style' + if ruleNum in self.ruleTexts: + errmsg = self.ruleTexts[ruleNum].text + if self.ruleTexts[ruleNum].misra_severity: + misra_severity = self.ruleTexts[ruleNum].misra_severity + cppcheck_severity = self.ruleTexts[ruleNum].cppcheck_severity + elif len(self.ruleTexts) == 0: + if self.ruleText_filename is None: + errmsg = 'misra violation (use --rule-texts= to get proper output)' + else: + errmsg = 'misra violation (rule-texts-file not found: ' + self.ruleText_filename + ')' + else: + errmsg = 'misra violation %s with no text in the supplied rule-texts-file' % (ruleNum) + + if self.severity: + cppcheck_severity = self.severity + + this_violation = '{}-{}-{}-{}'.format(location.file, location.linenr, location.column, ruleNum) + + # If this is new violation then record it and show it. If not then + # skip it since it has already been displayed. + if this_violation not in self.existing_violations: + self.existing_violations.add(this_violation) + cppcheckdata.reportError(location, cppcheck_severity, errmsg, 'misra', errorId, misra_severity) + + if misra_severity not in self.violations: + self.violations[misra_severity] = [] + self.violations[misra_severity].append('misra-' + errorId) + + def loadRuleTexts(self, filename): + num1 = 0 + num2 = 0 + appendixA = False + ruleText = False + expect_more = False + + Rule_pattern = re.compile(r'^Rule ([0-9]+).([0-9]+)') + severity_pattern = re.compile(r'.*[ ]*(Advisory|Required|Mandatory)$') + xA_Z_pattern = re.compile(r'^[#A-Z].*') + a_z_pattern = re.compile(r'^[a-z].*') + # Try to detect the file encoding + file_stream = None + encodings = ['ascii', 'utf-8', 'windows-1250', 'windows-1252'] + for e in encodings: + try: + file_stream = codecs.open(filename, 'r', encoding=e) + file_stream.readlines() + file_stream.seek(0) + except UnicodeDecodeError: + file_stream.close() + file_stream = None + else: + break + if not file_stream: + print('Could not find a suitable codec for "' + filename + '".') + print('If you know the codec please report it to the developers so the list can be enhanced.') + print('Trying with default codec now and ignoring errors if possible ...') + try: + file_stream = open(filename, 'rt', errors='ignore') + except TypeError: + # Python 2 does not support the errors parameter + file_stream = open(filename, 'rt') + + rule = None + have_severity = False + severity_loc = 0 + + for line in file_stream: + + line = line.replace('\r', '').replace('\n', '') + + if not appendixA: + if line.find('Appendix A') >= 0 and line.find('Summary of guidelines') >= 10: + appendixA = True + continue + if line.find('Appendix B') >= 0: + break + if len(line) == 0: + continue + + # Parse rule declaration. + res = Rule_pattern.match(line) + + if res: + have_severity = False + expect_more = False + severity_loc = 0 + num1 = int(res.group(1)) + num2 = int(res.group(2)) + rule = Rule(num1, num2) + + if not have_severity and rule is not None: + res = severity_pattern.match(line) + + if res: + rule.misra_severity = res.group(1) + have_severity = True + else: + severity_loc += 1 + + # Only look for severity on the Rule line + # or the next non-blank line after + # If it's not in either of those locations then + # assume a severity was not provided. + + if severity_loc < 2: + continue + else: + rule.misra_severity = '' + have_severity = True + + if rule is None: + continue + + # Parse continuing of rule text. + if expect_more: + if a_z_pattern.match(line): + self.ruleTexts[rule.num].text += ' ' + line + continue + + expect_more = False + continue + + # Parse beginning of rule text. + if xA_Z_pattern.match(line): + rule.text = line + self.ruleTexts[rule.num] = rule + expect_more = True + + file_stream.close() + + def verifyRuleTexts(self): + """Prints rule numbers without rule text.""" + rule_texts_rules = [] + for rule_num in self.ruleTexts: + rule = self.ruleTexts[rule_num] + rule_texts_rules.append(str(rule.num1) + '.' + str(rule.num2)) + + all_rules = list(getAddonRules() + getCppcheckRules()) + + missing_rules = list(set(all_rules) - set(rule_texts_rules)) + if len(missing_rules) == 0: + print("Rule texts are correct.") + else: + print("Missing rule texts: " + ', '.join(missing_rules)) + + def printStatus(self, *args, **kwargs): + if not self.settings.quiet: + print(*args, **kwargs) + + def executeCheck(self, rule_num, check_function, *args): + """Execute check function for a single MISRA rule. + + :param rule_num: Number of rule in hundreds format + :param check_function: Check function to execute + :param args: Check function arguments + """ + if not self.isRuleGloballySuppressed(rule_num): + misra_cpp = ( + 202, # misra-c2012-2.3 : misra c++2008 0-1-9 + 203, # misra-c2012-2.3 : misra c++2008 0-1-5 + 402, # misra-c2012-4.2 : misra c++2008 2-3-1 + 701, # misra-c2012-7.1 : misra c++2008 2-3-1 + 702, # misra-c2012-7.2 : misra c++2008 2-13-2 + 1203, # misra-c2012-12.3 : misra c++2008 5-14-1 + 1204, # misra-c2012-12.4 : misra c++2008 5-18-1 + 1305, # misra-c2012-13.5 : misra c++2008 5-19-1 + 1702, # misra-c2012-17.2 : misra c++2008 7-5-4 + 1901) # misra-c2012-19.1 : misra c++2008 2-13-3 + + if (not self.is_cpp) or rule_num in misra_cpp: + # log checker + errmsg = 'Misra C: %i.%i' % (rule_num // 100, rule_num % 100) + cppcheckdata.log_checker(errmsg, 'misra') + + check_function(*args) + + def parseDump(self, dumpfile, path_premium_addon=None): + def fillVerifyExpected(verify_expected, tok): + """Add expected suppressions to verify_expected list.""" + rule_re = re.compile(r'[0-9]+\.[0-9]+') + if tok.str.startswith('//') and 'TODO' not in tok.str: + for word in tok.str[2:].split(' '): + if rule_re.match(word) or word == "config": + verify_expected.append('%s:%d %s' % (tok.file, tok.linenr, word)) + + data = cppcheckdata.parsedump(dumpfile) + typeBits['CHAR'] = data.platform.char_bit + typeBits['SHORT'] = data.platform.short_bit + typeBits['INT'] = data.platform.int_bit + typeBits['LONG'] = data.platform.long_bit + typeBits['LONG_LONG'] = data.platform.long_long_bit + typeBits['POINTER'] = data.platform.pointer_bit + + if self.settings.verify: + # Add suppressions from the current file + for tok in data.rawTokens: + fillVerifyExpected(self.verify_expected, tok) + # Add suppressions from the included headers + include_re = re.compile(r'^#include [<"]([a-zA-Z0-9]+[a-zA-Z\-_./\\0-9]*)[">]$') + dump_dir = os.path.dirname(data.filename) + for conf in data.configurations: + for directive in conf.directives: + m = re.match(include_re, directive.str) + if not m: + continue + header_dump_path = os.path.join(dump_dir, m.group(1) + '.dump') + if not os.path.exists(header_dump_path): + continue + header_data = cppcheckdata.parsedump(header_dump_path) + for tok in header_data.rawTokens: + fillVerifyExpected(self.verify_expected, tok) + else: + self.printStatus('Checking ' + dumpfile + '...') + + self.is_cpp = data.files and data.files[0].endswith('.cpp') + + for cfgNumber, cfg in enumerate(data.iterconfigurations()): + if not self.settings.quiet: + self.printStatus('Checking %s, config %s...' % (dumpfile, cfg.name)) + + self.executeCheck(102, self.misra_1_2, cfg) + if not path_premium_addon: + self.executeCheck(104, self.misra_1_4, cfg) + self.executeCheck(202, self.misra_2_2, cfg) + self.executeCheck(203, self.misra_2_3, dumpfile, cfg.typedefInfo) + self.executeCheck(204, self.misra_2_4, dumpfile, cfg) + self.executeCheck(205, self.misra_2_5, dumpfile, cfg) + self.executeCheck(207, self.misra_2_7, cfg) + # data.rawTokens is same for all configurations + if cfgNumber == 0: + self.executeCheck(301, self.misra_3_1, data.rawTokens) + self.executeCheck(302, self.misra_3_2, data.rawTokens) + self.executeCheck(401, self.misra_4_1, data.rawTokens) + self.executeCheck(402, self.misra_4_2, data.rawTokens) + self.executeCheck(501, self.misra_5_1, cfg) + self.executeCheck(502, self.misra_5_2, cfg) + self.executeCheck(504, self.misra_5_4, cfg) + self.executeCheck(505, self.misra_5_5, cfg) + self.executeCheck(506, self.misra_5_6, dumpfile, cfg.typedefInfo) + self.executeCheck(507, self.misra_5_7, dumpfile, cfg) + self.executeCheck(508, self.misra_5_8, dumpfile, cfg) + self.executeCheck(509, self.misra_5_9, dumpfile, cfg) + self.executeCheck(601, self.misra_6_1, cfg) + self.executeCheck(602, self.misra_6_2, cfg) + if cfgNumber == 0: + self.executeCheck(701, self.misra_7_1, data.rawTokens) + self.executeCheck(702, self.misra_7_2, cfg) + if cfgNumber == 0: + self.executeCheck(703, self.misra_7_3, data.rawTokens) + self.executeCheck(704, self.misra_7_4, cfg) + self.executeCheck(801, self.misra_8_1, cfg) + if cfgNumber == 0: + self.executeCheck(802, self.misra_8_2, cfg, data.rawTokens) + self.executeCheck(804, self.misra_8_4, cfg) + self.executeCheck(805, self.misra_8_5, dumpfile, cfg) + self.executeCheck(806, self.misra_8_6, dumpfile, cfg) + self.executeCheck(807, self.misra_8_7, dumpfile, cfg) + self.executeCheck(808, self.misra_8_8, cfg) + self.executeCheck(809, self.misra_8_9, cfg) + self.executeCheck(810, self.misra_8_10, cfg) + self.executeCheck(811, self.misra_8_11, cfg) + self.executeCheck(812, self.misra_8_12, cfg) + if cfgNumber == 0: + self.executeCheck(814, self.misra_8_14, data.rawTokens) + self.executeCheck(902, self.misra_9_2, cfg) + self.executeCheck(903, self.misra_9_3, cfg) + self.executeCheck(904, self.misra_9_4, cfg) + if cfgNumber == 0: + self.executeCheck(905, self.misra_9_5, cfg, data.rawTokens) + if not path_premium_addon: + self.executeCheck(1001, self.misra_10_1, cfg) + self.executeCheck(1002, self.misra_10_2, cfg) + self.executeCheck(1003, self.misra_10_3, cfg) + self.executeCheck(1004, self.misra_10_4, cfg) + self.executeCheck(1005, self.misra_10_5, cfg) + self.executeCheck(1006, self.misra_10_6, cfg) + self.executeCheck(1007, self.misra_10_7, cfg) + self.executeCheck(1008, self.misra_10_8, cfg) + self.executeCheck(1101, self.misra_11_1, cfg) + self.executeCheck(1102, self.misra_11_2, cfg) + self.executeCheck(1103, self.misra_11_3, cfg) + self.executeCheck(1104, self.misra_11_4, cfg) + self.executeCheck(1105, self.misra_11_5, cfg) + self.executeCheck(1106, self.misra_11_6, cfg) + self.executeCheck(1107, self.misra_11_7, cfg) + self.executeCheck(1108, self.misra_11_8, cfg) + self.executeCheck(1109, self.misra_11_9, cfg) + if cfgNumber == 0: + self.executeCheck(1201, self.misra_12_1_sizeof, data.rawTokens) + self.executeCheck(1201, self.misra_12_1, cfg) + self.executeCheck(1202, self.misra_12_2, cfg) + self.executeCheck(1203, self.misra_12_3, cfg) + self.executeCheck(1204, self.misra_12_4, cfg) + self.executeCheck(1301, self.misra_13_1, cfg) + self.executeCheck(1303, self.misra_13_3, cfg) + self.executeCheck(1304, self.misra_13_4, cfg) + self.executeCheck(1305, self.misra_13_5, cfg) + self.executeCheck(1306, self.misra_13_6, cfg) + self.executeCheck(1401, self.misra_14_1, cfg) + self.executeCheck(1402, self.misra_14_2, cfg) + self.executeCheck(1404, self.misra_14_4, cfg) + self.executeCheck(1501, self.misra_15_1, cfg) + self.executeCheck(1502, self.misra_15_2, cfg) + self.executeCheck(1503, self.misra_15_3, cfg) + self.executeCheck(1504, self.misra_15_4, cfg) + self.executeCheck(1505, self.misra_15_5, cfg) + if cfgNumber == 0: + self.executeCheck(1506, self.misra_15_6, data.rawTokens) + self.executeCheck(1507, self.misra_15_7, cfg) + self.executeCheck(1601, self.misra_16_1, cfg) + self.executeCheck(1602, self.misra_16_2, cfg) + if cfgNumber == 0: + self.executeCheck(1603, self.misra_16_3, data.rawTokens) + self.executeCheck(1604, self.misra_16_4, cfg) + self.executeCheck(1605, self.misra_16_5, cfg) + self.executeCheck(1606, self.misra_16_6, cfg) + self.executeCheck(1607, self.misra_16_7, cfg) + self.executeCheck(1701, self.misra_17_1, cfg) + self.executeCheck(1702, self.misra_17_2, cfg) + self.executeCheck(1703, self.misra_17_3, cfg) + self.misra_config(cfg) + if cfgNumber == 0: + self.executeCheck(1706, self.misra_17_6, data.rawTokens) + self.executeCheck(1707, self.misra_17_7, cfg) + self.executeCheck(1708, self.misra_17_8, cfg) + self.executeCheck(1804, self.misra_18_4, cfg) + self.executeCheck(1805, self.misra_18_5, cfg) + self.executeCheck(1807, self.misra_18_7, cfg) + self.executeCheck(1808, self.misra_18_8, cfg) + self.executeCheck(1902, self.misra_19_2, cfg) + self.executeCheck(2001, self.misra_20_1, cfg) + self.executeCheck(2002, self.misra_20_2, cfg) + self.executeCheck(2003, self.misra_20_3, cfg) + self.executeCheck(2004, self.misra_20_4, cfg) + self.executeCheck(2005, self.misra_20_5, cfg) + self.executeCheck(2007, self.misra_20_7, cfg) + self.executeCheck(2008, self.misra_20_8, cfg) + self.executeCheck(2009, self.misra_20_9, cfg) + self.executeCheck(2010, self.misra_20_10, cfg) + self.executeCheck(2011, self.misra_20_11, cfg) + self.executeCheck(2012, self.misra_20_12, cfg) + self.executeCheck(2013, self.misra_20_13, cfg) + self.executeCheck(2014, self.misra_20_14, cfg) + self.executeCheck(2101, self.misra_21_1, cfg) + self.executeCheck(2102, self.misra_21_2, cfg) + self.executeCheck(2103, self.misra_21_3, cfg) + self.executeCheck(2104, self.misra_21_4, cfg) + self.executeCheck(2105, self.misra_21_5, cfg) + self.executeCheck(2106, self.misra_21_6, cfg) + self.executeCheck(2107, self.misra_21_7, cfg) + self.executeCheck(2108, self.misra_21_8, cfg) + self.executeCheck(2109, self.misra_21_9, cfg) + self.executeCheck(2110, self.misra_21_10, cfg) + self.executeCheck(2111, self.misra_21_11, cfg) + self.executeCheck(2112, self.misra_21_12, cfg) + self.executeCheck(2114, self.misra_21_14, cfg) + self.executeCheck(2115, self.misra_21_15, cfg) + self.executeCheck(2116, self.misra_21_16, cfg) + self.executeCheck(2119, self.misra_21_19, cfg) + self.executeCheck(2120, self.misra_21_20, cfg) + self.executeCheck(2121, self.misra_21_21, cfg) + # 22.4 is already covered by Cppcheck writeReadOnlyFile + self.executeCheck(2205, self.misra_22_5, cfg) + self.executeCheck(2207, self.misra_22_7, cfg) + self.executeCheck(2208, self.misra_22_8, cfg) + self.executeCheck(2209, self.misra_22_9, cfg) + self.executeCheck(2210, self.misra_22_10, cfg) + + def read_ctu_info_line(self, line): + if not line.startswith('{'): + return None + try: + ctu_info = json.loads(line) + except json.decoder.JSONDecodeError: + return None + if 'summary' not in ctu_info: + return None + if 'data' not in ctu_info: + return None + return ctu_info + + def analyse_ctu_info(self, ctu_info_files): + all_typedef_info = {} + all_tagname_info = {} + all_macro_info = {} + all_external_identifiers_decl = {} + all_external_identifiers_def = {} + all_internal_identifiers = {} + all_local_identifiers = {} + all_usage_files = {} + + from cppcheckdata import Location + + def is_different_location(loc1, loc2): + return loc1['file'] != loc2['file'] or loc1['line'] != loc2['line'] + + def is_different_file(loc1, loc2): + return loc1['file'] != loc2['file'] + + try: + for filename in ctu_info_files: + for line in open(filename, 'rt'): + s = self.read_ctu_info_line(line) + if s is None: + continue + summary_type = s.get('summary', '') + summary_data = s.get('data', None) + + if summary_type == 'MisraTypedefInfo': + for new_typedef_info in summary_data: + key = new_typedef_info['name'] + existing_typedef_info = all_typedef_info.get(key, None) + if existing_typedef_info: + if is_different_location(existing_typedef_info, new_typedef_info): + self.reportError(Location(existing_typedef_info), 5, 6) + self.reportError(Location(new_typedef_info), 5, 6) + else: + existing_typedef_info['used'] = existing_typedef_info['used'] or new_typedef_info['used'] + else: + all_typedef_info[key] = new_typedef_info + + if summary_type == 'MisraTagName': + for new_tagname_info in summary_data: + key = new_tagname_info['name'] + existing_tagname_info = all_tagname_info.get(key, None) + if existing_tagname_info: + if is_different_location(existing_tagname_info, new_tagname_info): + self.reportError(Location(existing_tagname_info), 5, 7) + self.reportError(Location(new_tagname_info), 5, 7) + else: + existing_tagname_info['used'] = existing_tagname_info['used'] or new_tagname_info['used'] + else: + all_tagname_info[key] = new_tagname_info + + if summary_type == 'MisraMacro': + for new_macro in summary_data: + key = new_macro['name'] + existing_macro = all_macro_info.get(key, None) + if existing_macro: + existing_macro['used'] = existing_macro['used'] or new_macro['used'] + else: + all_macro_info[key] = new_macro + + if summary_type == 'MisraExternalIdentifiers': + for s in sorted(summary_data, key=lambda d: "%s %s %s" %(d['file'],d['line'], d['column'] )): + is_declaration = s['decl'] + if is_declaration: + all_external_identifiers = all_external_identifiers_decl + else: + all_external_identifiers = all_external_identifiers_def + + name = s['name'] + if name in all_external_identifiers: + if is_declaration and is_different_location(s, all_external_identifiers[name]): + self.reportError(Location(s), 8, 5) + self.reportError(Location(all_external_identifiers[name]), 8, 5) + elif is_different_file(s, all_external_identifiers[name]): + self.reportError(Location(s), 8, 6) + self.reportError(Location(all_external_identifiers[name]), 8, 6) + all_external_identifiers[name] = s + + if summary_type == 'MisraInternalIdentifiers': + for s in summary_data: + if s['name'] in all_internal_identifiers: + if not s['inlinefunc'] or s['file'] != all_internal_identifiers[s['name']]['file']: + self.reportError(Location(s), 5, 9) + self.reportError(Location(all_internal_identifiers[s['name']]), 5, 9) + all_internal_identifiers[s['name']] = s + + if summary_type == 'MisraLocalIdentifiers': + for s in summary_data: + all_local_identifiers[s['name']] = s + + if summary_type == 'MisraUsage': + for s in summary_data: + if s['name'] in all_usage_files: + all_usage_files[s['name']].append(s['file']) + else: + all_usage_files[s['name']] = [s['file']] + + except FileNotFoundError: + return + + unused_typedefs = [tdi for tdi in all_typedef_info.values() if not tdi['used']] + for tdi in unused_typedefs: + self.reportError(Location(tdi), 2, 3) + + unused_tags = [tag for tag in all_tagname_info.values() if not tag['used']] + for tag in unused_tags: + self.reportError(Location(tag), 2, 4) + + unused_macros = [m for m in all_macro_info.values() if not m['used']] + for m in unused_macros: + self.reportError(Location(m), 2, 5) + + all_external_identifiers = all_external_identifiers_decl + all_external_identifiers.update(all_external_identifiers_def) + for name, external_identifier in all_external_identifiers.items(): + internal_identifier = all_internal_identifiers.get(name) + if internal_identifier: + self.reportError(Location(internal_identifier), 5, 8) + self.reportError(Location(external_identifier), 5, 8) + + local_identifier = all_local_identifiers.get(name) + if local_identifier: + self.reportError(Location(local_identifier), 5, 8) + self.reportError(Location(external_identifier), 5, 8) + + for name, files in all_usage_files.items(): + #print('%s:%i' % (name, count)) + count = len(files) + if count != 1 or name not in all_external_identifiers_def: + continue + if files[0] != Location(all_external_identifiers_def[name]).file: + continue + if name in all_external_identifiers: + self.reportError(Location(all_external_identifiers[name]), 8, 7) + +RULE_TEXTS_HELP = '''Path to text file of MISRA rules + +If you have the tool 'pdftotext' you might be able +to generate this textfile with such command: + + pdftotext MISRA_C_2012.pdf MISRA_C_2012.txt + +Otherwise you can more or less copy/paste the chapter +Appendix A Summary of guidelines +from the MISRA pdf. You can buy the MISRA pdf from +http://www.misra.org.uk/ + +Format: + +<..arbitrary text..> +Appendix A Summary of guidelines +Rule 1.1 Required +Rule text for 1.1 +continuation of rule text for 1.1 +Rule 1.2 Mandatory +Rule text for 1.2 +continuation of rule text for 1.2 +<...> + +''' + +SUPPRESS_RULES_HELP = '''MISRA rules to suppress (comma-separated) + +For example, if you'd like to suppress rules 15.1, 11.3, +and 20.13, run: + + python misra.py --suppress-rules 15.1,11.3,20.13 ... + +''' + + +def get_args_parser(): + """Generates list of command-line arguments acceptable by misra.py script.""" + parser = cppcheckdata.ArgumentParser() + parser.add_argument("--rule-texts", type=str, help=RULE_TEXTS_HELP) + parser.add_argument("--verify-rule-texts", + help="Verify that all supported rules texts are present in given file and exit.", + action="store_true") + parser.add_argument("--suppress-rules", type=str, help=SUPPRESS_RULES_HELP) + parser.add_argument("--no-summary", help="Hide summary of violations", action="store_true") + parser.add_argument("--show-suppressed-rules", help="Print rule suppression list", action="store_true") + parser.add_argument("-P", "--file-prefix", type=str, help="Prefix to strip when matching suppression file rules") + parser.add_argument("-generate-table", help=argparse.SUPPRESS, action="store_true") + parser.add_argument("-verify", help=argparse.SUPPRESS, action="store_true") + parser.add_argument("--severity", type=str, help="Set a custom severity string, for example 'error' or 'warning'. ") + return parser + + +def main(): + parser = get_args_parser() + args = parser.parse_args() + settings = MisraSettings(args) + checker = MisraChecker(settings) + + checker.path_premium_addon = cppcheckdata.get_path_premium_addon() + + if args.generate_table: + generateTable() + sys.exit(0) + + if args.rule_texts: + filename = os.path.expanduser(args.rule_texts) + filename = os.path.normpath(filename) + checker.ruleText_filename = filename + if os.path.isfile(filename): + checker.loadRuleTexts(filename) + if args.verify_rule_texts: + checker.verifyRuleTexts() + sys.exit(0) + else: + if args.verify_rule_texts: + print('Fatal error: file is not found: ' + filename) + sys.exit(1) + + + if args.verify_rule_texts and not args.rule_texts: + print("Error: Please specify rule texts file with --rule-texts=") + sys.exit(1) + + if args.suppress_rules: + checker.setSuppressionList(args.suppress_rules) + + if args.file_prefix: + checker.setFilePrefix(args.file_prefix) + + dump_files, ctu_info_files = cppcheckdata.get_files(args) + + if (not dump_files) and (not ctu_info_files): + if not args.quiet: + print("No input files.") + sys.exit(0) + + if args.severity: + checker.setSeverity(args.severity) + + for item in dump_files: + checker.parseDump(item,checker.path_premium_addon) + + if settings.verify: + verify_expected = checker.get_verify_expected() + verify_actual = checker.get_verify_actual() + + exitCode = 0 + for expected in verify_expected: + if expected not in verify_actual: + print('Expected but not seen: ' + expected) + exitCode = 1 + for actual in verify_actual: + if actual not in verify_expected: + print('Not expected: ' + actual) + exitCode = 1 + + # Existing behavior of verify mode is to exit + # on the first un-expected output. + # TODO: Is this required? or can it be moved to after + # all input files have been processed + if exitCode != 0: + sys.exit(exitCode) + + checker.analyse_ctu_info(ctu_info_files) + + if settings.verify: + sys.exit(exitCode) + + number_of_violations = len(checker.get_violations()) + if number_of_violations > 0: + if settings.show_summary: + print("\nMISRA rules violations found:\n\t%s\n" % ( + "\n\t".join(["%s: %d" % (viol, len(checker.get_violations(viol))) for viol in + checker.get_violation_types()]))) + + rules_violated = {} + for severity, ids in checker.get_violations(): + for misra_id in ids: + rules_violated[misra_id] = rules_violated.get(misra_id, 0) + 1 + print("MISRA rules violated:") + convert = lambda text: int(text) if text.isdigit() else 0 + misra_sort = lambda key: [convert(c) for c in re.split(r'[\.-]([0-9]*)', key)] + for misra_id in sorted(rules_violated.keys(), key=misra_sort): + res = re.match(r'misra-c2012-([0-9]+)\\.([0-9]+)', misra_id) + if res is None: + num = 0 + else: + num = int(res.group(1)) * 100 + int(res.group(2)) + severity = '-' + if num in checker.ruleTexts: + severity = checker.ruleTexts[num].cppcheck_severity + print("\t%15s (%s): %d" % (misra_id, severity, rules_violated[misra_id])) + + if args.show_suppressed_rules: + checker.showSuppressedRules() + + +if __name__ == '__main__': + main() + sys.exit(cppcheckdata.EXIT_CODE) diff --git a/cppcheck-2.14.0/addons/misra_9.py b/cppcheck-2.14.0/addons/misra_9.py new file mode 100644 index 00000000..806d27d2 --- /dev/null +++ b/cppcheck-2.14.0/addons/misra_9.py @@ -0,0 +1,563 @@ +import cppcheckdata + +# Holds information about an array, struct or union's element definition. +class ElementDef: + def __init__(self, elementType, name, valueType, dimensions = None): + self.elementType = elementType # 'array', 'record' or 'value' + self.name = str(name) + self.valueType = valueType + self.children = [] + self.dimensions = dimensions + self.parent = None + + self.isDesignated = False + self.isPositional = False + self.numInits = 0 + self.childIndex = -1 + + self.flexibleToken = None + self.isFlexible = False + self.structureViolationToken = None + + def __repr__(self): + inits = "" + if self.isPositional: + inits += 'P' + if self.isDesignated: + inits += 'D' + if not (self.isPositional or self.isDesignated) and self.numInits == 0: + inits += '_' + if self.numInits > 1: + inits += str(self.numInits) + + attrs = ["childIndex", "elementType", "valueType"] + return "{}({}, {}, {})".format( + "ElementDef", + self.getLongName(), + inits, + ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) + ) + + @property + def isArray(self): + return self.elementType == 'array' + + @property + def isRecord(self): + return self.elementType == 'record' + + @property + def isValue(self): + return self.elementType == 'value' + + + def getLongName(self): + return self.parent.getLongName() + "." + self.name if self.parent else self.name + + def getInitDump(self): + t = [] + if self.isPositional: + t.append('P') + if self.isDesignated: + t.append('D') + if self.numInits == 0: + t.append('_') + if self.numInits > 1: + t.append(str(self.numInits)) + + myDump = "".join(t) + + if len(self.children): + childDumps = [] + for c in self.children: + childDumps.append(c.getInitDump()) + if self.structureViolationToken is not None: + myDump += "!" + myDump += "{ " + ", ".join(childDumps) + " }" + + return myDump + + def addChild(self, child): + self.children.append(child) + child.parent = self + + def getNextChild(self): + self.childIndex += 1 + return self.getChildByIndex(self.childIndex) + + def getChildByIndex(self, index): + if self.isFlexible: + while len(self.children) <= index: + createChild(self, self.flexibleToken, len(self.children), None) + return self.children[index] if 0 <= index < len(self.children) else None + + def getChildByName(self, name): + for c in self.children: + if c.name == name: + return c + return None + + def getNextValueElement(self, root): + current = self + while current != root: + if not current.parent: + return None + # Get next index of parent + i = current.parent.children.index(current) + 1 + # Next index of parent exists + if i < len(current.parent.children): + current = current.parent.children[i] + return current.getFirstValueElement() + + # Next index of parent doesn't exist. Move up + current = current.parent + return None + + def getFirstValueElement(self): + current = self + + # Move to first child as long as children exists + next_child = current.getChildByIndex(0) + while next_child: + current = next_child + next_child = current.getChildByIndex(0) + return current + + def getLastValueElement(self): + current = self + # Move to last child as long as children exists + while len(current.children) > 0: + current = current.children[-1] + return current + + def getChildByValueElement(self, ed): + potentialChild = ed + while potentialChild and potentialChild not in self.children: + potentialChild = potentialChild.parent + + return self.children[self.children.index(potentialChild)] if potentialChild else None + + def getEffectiveLevel(self): + if self.parent and self.parent.elementType == "array": + return self.parent.getEffectiveLevel() + 1 + else: + return 0 + + def setInitialized(self, designated=False, positional=False): + if designated: + self.isDesignated = True + if positional or not designated: + self.isPositional = True + self.numInits += 1 + + def initializeChildren(self): + for child in self.children: + child.setInitialized(positional=True) + child.initializeChildren() + + def unset(self): + self.isDesignated = False + self.isPositional = False + + # Unset is always recursive + for child in self.children: + child.unset() + + def markStuctureViolation(self, token): + if self.name == '->': + self.children[0].markStuctureViolation(token) + elif not self.structureViolationToken: + self.structureViolationToken = token + + def markAsFlexibleArray(self, token): + self.flexibleToken = token + self.isFlexible = True + + def markAsCurrent(self): + if self.parent: + if self.name == '<-': + self.parent.childIndex = self.parent.children.index(self.children[0]) + else: + self.parent.childIndex = self.parent.children.index(self) + + self.parent.markAsCurrent() + + def isAllChildrenSet(self): + myself = len(self.children) == 0 and (self.isDesignated or self.isPositional) + mychildren = len(self.children) > 0 and all([child.isAllChildrenSet() for child in self.children]) + return myself or mychildren + + def isAllSet(self): + return all([child.isPositional or child.isDesignated for child in self.children]) + + def isOnlyDesignated(self): + return all([not child.isPositional for child in self.children]) + + def isMisra92Compliant(self): + return self.structureViolationToken is None and all([child.isMisra92Compliant() for child in self.children]) + + def isMisra93Compliant(self): + if self.elementType == 'array': + result = self.isAllChildrenSet() or \ + ((self.isAllSet() or + self.isOnlyDesignated()) and + all([not (child.isDesignated or child.isPositional) or child.isMisra93Compliant() for child in self.children])) + return result + elif self.elementType == 'record': + result = all([child.isMisra93Compliant() for child in self.children]) + return result + else: + return True + + def isMisra94Compliant(self): + return self.numInits <= 1 and all([child.isMisra94Compliant() for child in self.children]) + + def isMisra95Compliant(self): + return not self.isFlexible or all([not child.isDesignated for child in self.children]) + +# Parses the initializers and update the ElementDefs status accordingly +class InitializerParser: + def __init__(self): + self.token = None + self.root = None + self.ed = None + self.rootStack = [] + + def parseInitializer(self, root, token): + self.root = root + self.token = token + dummyRoot = ElementDef('array', '->', self.root.valueType) + dummyRoot.children = [self.root] + + self.rootStack = [] + self.root = dummyRoot + self.ed = self.root.getFirstValueElement() + isFirstElement = False + isDesignated = False + + while self.token: + if self.token.str == ',': + self.token = self.token.astOperand1 + isFirstElement = False + + # Designated initializer ( [2]=... or .name=... ) + elif self.token.isAssignmentOp and not self.token.valueType: + self.popFromStackIfExitElement() + + self.ed = getElementByDesignator(self.root, self.token.astOperand1) + if self.ed: + # Update root + self.pushToRootStackAndMarkAsDesignated() + # Make sure ed points to valueElement + self.ed = self.ed.getFirstValueElement() + + self.token = self.token.astOperand2 + isFirstElement = False + isDesignated = True + + elif self.token.isString and self.ed and self.ed.isArray: + self.ed.setInitialized(isDesignated) + if self.token == self.token.astParent.astOperand1 and self.token.astParent.astOperand2: + self.token = self.token.astParent.astOperand2 + self.ed.markAsCurrent() + self.ed = self.root.getNextChild() + else: + self.unwindAndContinue() + continue + + elif self.token.str == '{': + nextChild = self.root.getNextChild() if self.root is not None else None + + if nextChild: + if nextChild.isArray or nextChild.isRecord: + nextChild.unset() + nextChild.setInitialized(isDesignated) + self.ed = nextChild.getFirstValueElement() + isDesignated = False + elif nextChild.valueType is None: + # No type information available - unable to check structure - assume correct initialization + nextChild.setInitialized(isDesignated) + self.unwindAndContinue() + continue + + elif self.token.astOperand1: + # Create dummy nextChild to represent excess levels in initializer + dummyRoot = ElementDef('array', '<-', self.root.valueType) + dummyRoot.parent = self.root + dummyRoot.childIndex = 0 + dummyRoot.children = [nextChild] + nextChild.parent = dummyRoot + + self.root.markStuctureViolation(self.token) + + # Fake dummy as nextChild (of current root) + nextChild = dummyRoot + + if nextChild and self.token.astOperand1: + self.root = nextChild + self.token = self.token.astOperand1 + isFirstElement = True + else: + if self.root: + # {} + if self.root.name == '<-': + self.root.parent.markStuctureViolation(self.token) + else: + self.root.markStuctureViolation(self.token) + self.ed = None + self.unwindAndContinue() + + else: + if self.ed and self.ed.isValue: + if not isDesignated and len(self.rootStack) > 0 and self.rootStack[-1][1] == self.root: + self.rootStack[-1][0].markStuctureViolation(self.token) + if isFirstElement and self.token.str == '0' and self.token.next.str == '}': + # Zero initializer causes recursive initialization + self.root.initializeChildren() + elif self.token.isString and self.ed.valueType and self.ed.valueType.pointer > 0: + if self.ed.valueType.pointer - self.ed.getEffectiveLevel() == 1: + if self.ed.parent != self.root: + self.root.markStuctureViolation(self.token) + self.ed.setInitialized(isDesignated) + elif self.ed.valueType.pointer == self.ed.getEffectiveLevel(): + if(self.root.name != '->' and self.ed.parent.parent != self.root) or (self.root.name == '->' and self.root.children[0] != self.ed.parent): + self.root.markStuctureViolation(self.token) + else: + self.ed.parent.setInitialized(isDesignated) + self.ed.parent.initializeChildren() + + else: + if self.root is not None and self.ed.parent != self.root: + # Check if token is correct value type for self.root.children[?] + child = self.root.getChildByValueElement(self.ed) + if self.token.valueType: + if child.elementType != 'record' or self.token.valueType.type != 'record' or child.valueType.typeScope != self.token.valueType.typeScope: + self.root.markStuctureViolation(self.token) + + self.ed.setInitialized(isDesignated) + + # Mark all elements up to root with positional or designated + # (for complex designators, or missing structure) + parent = self.ed.parent + while parent and parent != self.root: + parent.isDesignated = isDesignated if isDesignated and not parent.isPositional else parent.isDesignated + parent.isPositional = not isDesignated if not isDesignated and not parent.isDesignated else parent.isPositional + parent = parent.parent + isDesignated = False + + if self.token.isString and self.ed.parent.isArray: + self.ed = self.ed.parent + self.unwindAndContinue() + + def pushToRootStackAndMarkAsDesignated(self): + new = self.ed.parent + if new != self.root: + # Mark all elements up to self.root root as designated + parent = new + while parent and parent != self.root: + parent.isDesignated = True + parent = parent.parent + self.rootStack.append((self.root, new)) + new.markAsCurrent() + new.childIndex = new.children.index(self.ed) - 1 + self.root = new + + def popFromStackIfExitElement(self): + if len(self.rootStack) > 0 and self.rootStack[-1][1] == self.root: + old = self.rootStack.pop()[0] + old.markAsCurrent() + self.root = old + + def unwindAndContinue(self): + while self.token: + if self.token.astParent.astOperand1 == self.token and self.token.astParent.astOperand2: + if self.ed: + if self.token.astParent.astOperand2.str == "{" and self.ed.isDesignated: + self.popFromStackIfExitElement() + else: + self.ed.markAsCurrent() + self.ed = self.ed.getNextValueElement(self.root) + + self.token = self.token.astParent.astOperand2 + break + else: + self.token = self.token.astParent + if self.token.str == '{': + if self.root: + self.ed = self.root.getLastValueElement() + self.ed.markAsCurrent() + + # Cleanup if root is dummy node representing excess levels in initializer + if self.root.name == '<-': + self.root.children[0].parent = self.root.parent + + self.root = self.root.parent + + if self.token.astParent is None: + self.token = None + break + +def misra_9_x(self, data, rule, rawTokens = None): + + parser = InitializerParser() + + for variable in data.variables: + if variable.nameToken is None: + continue + + nameToken = variable.nameToken + + # Check if declaration and initialization is + # split into two separate statements in ast. + if nameToken.next and nameToken.next.isSplittedVarDeclEq: + nameToken = nameToken.next.next + + # Find declarations with initializer assignment + eq = nameToken + while not eq.isAssignmentOp and eq.astParent: + eq = eq.astParent + + # We are only looking for initializers + if not eq.isAssignmentOp or eq.astOperand2.isName: + continue + + if variable.isArray or variable.isClass: + ed = getElementDef(nameToken, rawTokens) + # No need to check non-arrays if valueType is missing, + # since we can't say anything useful about the structure + # without it. + if ed.valueType is None and not variable.isArray: + continue + parser.parseInitializer(ed, eq.astOperand2) + # print(rule, nameToken.str + '=', ed.getInitDump()) + if rule == 902 and not ed.isMisra92Compliant(): + self.reportError(nameToken, 9, 2) + if rule == 903 and not ed.isMisra93Compliant(): + # Do not check when variable is pointer type + type_token = variable.nameToken + while type_token and type_token.isName: + type_token = type_token.previous + if type_token and type_token.str == '*': + continue + + self.reportError(nameToken, 9, 3) + if rule == 904 and not ed.isMisra94Compliant(): + self.reportError(nameToken, 9, 4) + if rule == 905 and not ed.isMisra95Compliant(): + self.reportError(nameToken, 9, 5) + +def getElementDef(nameToken, rawTokens = None): + if nameToken.variable.isArray: + ed = ElementDef("array", nameToken.str, nameToken.valueType) + createArrayChildrenDefs(ed, nameToken.astParent, nameToken.variable, rawTokens) + elif nameToken.variable.isClass: + ed = ElementDef("record", nameToken.str, nameToken.valueType) + createRecordChildrenDefs(ed, nameToken.variable) + else: + ed = ElementDef("value", nameToken.str, nameToken.valueType) + return ed + +def createArrayChildrenDefs(ed, token, var, rawTokens = None): + if token and token.str == '[': + if rawTokens is not None: + foundToken = next((rawToken for rawToken in rawTokens + if rawToken.file == token.file + and rawToken.linenr == token.linenr + and rawToken.column == token.column + ), None) + + if foundToken and foundToken.next and foundToken.next.str == ']': + ed.markAsFlexibleArray(token) + + if (token.astOperand2 is not None) and (token.astOperand2.getKnownIntValue() is not None): + for i in range(token.astOperand2.getKnownIntValue()): + createChild(ed, token, i, var) + else: + ed.markAsFlexibleArray(token) + + +def createChild(ed, token, name, var): + if token.astParent and token.astParent.str == '[': + child = ElementDef("array", name, ed.valueType) + createArrayChildrenDefs(child, token.astParent, var) + else: + if ed.valueType and ed.valueType.type == "record": + child = ElementDef("record", name, ed.valueType) + createRecordChildrenDefs(child, var) + else: + child = ElementDef("value", name, ed.valueType) + + ed.addChild(child) + +def createRecordChildrenDefs(ed, var): + valueType = ed.valueType + if not valueType or not valueType.typeScope: + return + if var is None: + return + typeToken = var.typeEndToken + while typeToken and typeToken.isName: + typeToken = typeToken.previous + if typeToken and typeToken.str == '*': + child = ElementDef("pointer", var.nameToken, var.nameToken.valueType) + ed.addChild(child) + return + child_dict = {} + for variable in valueType.typeScope.varlist: + if variable is var: + continue + child = getElementDef(variable.nameToken) + child_dict[variable.nameToken] = child + for scopes in valueType.typeScope.nestedList: + varscope = False + if scopes.nestedIn == valueType.typeScope: + for variable in valueType.typeScope.varlist: + if variable.nameToken and variable.nameToken.valueType and variable.nameToken.valueType.typeScope == scopes: + varscope = True + break + if not varscope: + ed1 = ElementDef("record", scopes.Id, valueType) + for variable in scopes.varlist: + child = getElementDef(variable.nameToken) + ed1.addChild(child) + child_dict[scopes.bodyStart] = ed1 + sorted_keys = sorted(list(child_dict.keys()), key=lambda k: "%s %s %s" % (k.file, k.linenr, k.column)) + for _key in sorted_keys: + ed.addChild(child_dict[_key]) + + +def getElementByDesignator(ed, token): + if not token.str in [ '.', '[' ]: + return None + + while token.str in [ '.', '[' ]: + token = token.astOperand1 + + while ed and not token.isAssignmentOp: + token = token.astParent + + if token.str == '[': + if not ed.isArray: + ed.markStuctureViolation(token) + + chIndex = -1 + if token.astOperand2 is not None: + chIndex = token.astOperand2.getKnownIntValue() + elif token.astOperand1 is not None: + chIndex = token.astOperand1.getKnownIntValue() + + ed = ed.getChildByIndex(chIndex) if chIndex is not None else None + + elif token.str == '.': + if not ed.isRecord: + ed.markStuctureViolation(token) + + name = "" + if token.astOperand2 is not None: + name = token.astOperand2.str + elif token.astOperand1 is not None: + name = token.astOperand1.str + + ed = ed.getChildByName(name) + + return ed diff --git a/cppcheck-2.14.0/addons/naming.py b/cppcheck-2.14.0/addons/naming.py new file mode 100644 index 00000000..948f158b --- /dev/null +++ b/cppcheck-2.14.0/addons/naming.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 +# +# cppcheck addon for naming conventions +# +# Example usage (variable name must start with lowercase, function name must start with uppercase): +# $ cppcheck --dump path-to-src/ +# $ python addons/naming.py --var='[a-z].*' --function='[A-Z].*' path-to-src/*.dump +# + +import cppcheckdata +import sys +import re + + +def validate_regex(expr): + try: + re.compile(expr) + except re.error: + print('Error: "{}" is not a valid regular expression.'.format(expr)) + exit(1) + + +RE_VARNAME = None +RE_CONSTNAME = None +RE_PRIVATE_MEMBER_VARIABLE = None +RE_FUNCTIONNAME = None +for arg in sys.argv[1:]: + if arg[:6] == '--var=': + RE_VARNAME = arg[6:] + validate_regex(RE_VARNAME) + elif arg.startswith('--const='): + RE_CONSTNAME = arg[arg.find('=')+1:] + validate_regex(RE_CONSTNAME) + elif arg.startswith('--private-member-variable='): + RE_PRIVATE_MEMBER_VARIABLE = arg[arg.find('=')+1:] + validate_regex(RE_PRIVATE_MEMBER_VARIABLE) + elif arg[:11] == '--function=': + RE_FUNCTIONNAME = arg[11:] + validate_regex(RE_FUNCTIONNAME) + + +def reportError(token, severity, msg, errorId): + cppcheckdata.reportError(token, severity, msg, 'naming', errorId) + + +for arg in sys.argv[1:]: + if not arg.endswith('.dump'): + continue + print('Checking ' + arg + '...') + data = cppcheckdata.CppcheckData(arg) + + for cfg in data.iterconfigurations(): + print('Checking %s, config %s...' % (arg, cfg.name)) + if RE_VARNAME: + for var in cfg.variables: + if var.access == 'Private': + continue + if var.nameToken and not var.isConst: + res = re.match(RE_VARNAME, var.nameToken.str) + if not res: + reportError(var.typeStartToken, 'style', 'Variable ' + + var.nameToken.str + ' violates naming convention', 'varname') + if RE_CONSTNAME: + for var in cfg.variables: + if var.access == 'Private': + continue + if var.nameToken and var.isConst: + res = re.match(RE_CONSTNAME, var.nameToken.str) + if not res: + reportError(var.typeStartToken, 'style', 'Constant ' + + var.nameToken.str + ' violates naming convention', 'constname') + if RE_PRIVATE_MEMBER_VARIABLE: + for var in cfg.variables: + if (var.access is None) or var.access != 'Private': + continue + res = re.match(RE_PRIVATE_MEMBER_VARIABLE, var.nameToken.str) + if not res: + reportError(var.typeStartToken, 'style', 'Private member variable ' + + var.nameToken.str + ' violates naming convention', 'privateMemberVariable') + if RE_FUNCTIONNAME: + for scope in cfg.scopes: + if scope.type == 'Function': + function = scope.function + if function is not None and function.type in ('Constructor', 'Destructor', 'CopyConstructor', 'MoveConstructor'): + continue + res = re.match(RE_FUNCTIONNAME, scope.className) + if not res: + reportError( + scope.bodyStart, 'style', 'Function ' + scope.className + ' violates naming convention', 'functionName') + +sys.exit(cppcheckdata.EXIT_CODE) diff --git a/cppcheck-2.14.0/addons/namingng.config.json b/cppcheck-2.14.0/addons/namingng.config.json new file mode 100644 index 00000000..0abba4f1 --- /dev/null +++ b/cppcheck-2.14.0/addons/namingng.config.json @@ -0,0 +1,19 @@ +{ + "RE_VARNAME": ["[a-z]*[a-zA-Z0-9_]*\\Z"], + "RE_PRIVATE_MEMBER_VARIABLE": null, + "RE_FUNCTIONNAME": ["[a-z0-9A-Z]*\\Z"], + "include_guard": { + "input": "path", + "prefix": "", + "suffix": "", + "case": "upper", + "max_linenr": 5, + "RE_HEADERFILE": "[^/].*\\.h\\Z", + "required": true + }, + "var_prefixes": {"uint32_t": "ui32", + "int*": "intp"}, + "function_prefixes": {"uint16_t": "ui16", + "uint32_t": "ui32"}, + "skip_one_char_variables": false +} diff --git a/cppcheck-2.14.0/addons/namingng.json b/cppcheck-2.14.0/addons/namingng.json new file mode 100644 index 00000000..83f9a5a5 --- /dev/null +++ b/cppcheck-2.14.0/addons/namingng.json @@ -0,0 +1,6 @@ +{ + "script":"namingng.py", + "args":[ + "--configfile=namingng.config.json" + ] +} diff --git a/cppcheck-2.14.0/addons/namingng.py b/cppcheck-2.14.0/addons/namingng.py new file mode 100644 index 00000000..8cc2b848 --- /dev/null +++ b/cppcheck-2.14.0/addons/namingng.py @@ -0,0 +1,419 @@ +#!/usr/bin/env python3 +# +# cppcheck addon for naming conventions +# An enhanced version. Configuration is taken from a json file +# It supports to check for type-based prefixes in function or variable names. +# Aside from include guard naming, include guard presence can also be tested. +# +# Example usage (variable name must start with lowercase, function name must start with uppercase): +# $ cppcheck --dump path-to-src/ +# $ python namingng.py test.c.dump +# +# JSON format: +# +# { +# "RE_VARNAME": ["[a-z]*[a-zA-Z0-9_]*\\Z"], +# "RE_PRIVATE_MEMBER_VARIABLE": null, +# "RE_FUNCTIONNAME": ["[a-z0-9A-Z]*\\Z"], +# "_comment": "comments can be added to the config with underscore-prefixed keys", +# "include_guard": { +# "input": "path", +# "prefix": "GUARD_", +# "case": "upper", +# "max_linenr": 5, +# "RE_HEADERFILE": "[^/].*\\.h\\Z", +# "required": true +# }, +# "var_prefixes": {"uint32_t": "ui32"}, +# "function_prefixes": {"uint16_t": "ui16", +# "uint32_t": "ui32"} +# } +# +# RE_VARNAME, RE_PRIVATE_MEMBER_VARIABLE and RE_FUNCTIONNAME are regular expressions to cover the basic names +# In var_prefixes and function_prefixes there are the variable-type/prefix pairs + +import cppcheckdata +import sys +import os +import re +import argparse +import json + +# Auxiliary class +class DataStruct: + def __init__(self, file, linenr, string, column=0): + self.file = file + self.linenr = linenr + self.str = string + self.column = column + +def reportNamingError(location,message,errorId='namingConvention',severity='style',extra='',column=None): + cppcheckdata.reportError(location,severity,message,'namingng',errorId,extra,columnOverride=column) + +def configError(error,fatal=True): + print('config error: %s'%error) + if fatal: + sys.exit(1) + +def validateConfigREs(list_or_dict,json_key): + have_error = False + for item in list_or_dict: + try: + re.compile(item) + except re.error as err: + configError("item '%s' of '%s' is not a valid regular expression: %s"%(item,json_key,err),fatal=False) + have_error = True + continue + if not isinstance(list_or_dict,dict): + continue + # item is actually a dict key; check value + value = list_or_dict[item] + if (not isinstance(value,list) or len(value) != 2 + or not isinstance(value[0],bool) or not isinstance(value[1],str)): + configError("item '%s' of '%s' must be an array [bool,string]"%(item,json_key),fatal=False) + have_error = True + + return have_error + +def loadConfig(configfile): + if not os.path.exists(configfile): + configError("cannot find config file '%s'"%configfile) + + try: + with open(configfile) as fh: + data = json.load(fh) + except json.JSONDecodeError as e: + configError("error parsing config file as JSON at line %d: %s"%(e.lineno,e.msg)) + except Exception as e: + configError("error opening config file '%s': %s"%(configfile,e)) + + if not isinstance(data, dict): + configError('config file must contain a JSON object at the top level') + + # All errors are emitted before bailing out, to make the unit test more + # effective. + have_error = False + + # Put config items in a class, so that settings can be accessed using + # config.feature + class Config: + pass + config = Config() + + mapping = { + 'file': ('RE_FILE', (list,)), + 'namespace': ('RE_NAMESPACE', (list,dict)), + 'include_guard': ('include_guard', (dict,)), + 'variable': ('RE_VARNAME', (list,dict)), + 'variable_prefixes': ('var_prefixes', (dict,), {}), + 'private_member': ('RE_PRIVATE_MEMBER_VARIABLE', (list,dict)), + 'public_member': ('RE_PUBLIC_MEMBER_VARIABLE', (list,dict)), + 'global_variable': ('RE_GLOBAL_VARNAME', (list,dict)), + 'function_name': ('RE_FUNCTIONNAME', (list,dict)), + 'function_prefixes': ('function_prefixes', (dict,), {}), + 'class_name': ('RE_CLASS_NAME', (list,dict)), + 'skip_one_char_variables': ('skip_one_char_variables', (bool,)), + } + + # parse defined keys and store as members of config object + for key,opts in mapping.items(): + json_key = opts[0] + req_type = opts[1] + default = None if len(opts)<3 else opts[2] + + value = data.pop(json_key,default) + if value is not None and type(value) not in req_type: + req_typename = ' or '.join([tp.__name__ for tp in req_type]) + got_typename = type(value).__name__ + configError('%s must be %s (not %s), or not set'%(json_key,req_typename,got_typename),fatal=False) + have_error = True + continue + + # type list implies that this is either a list of REs or a dict with RE keys + if list in req_type and value is not None: + re_error = validateConfigREs(value,json_key) + if re_error: + have_error = True + + setattr(config,key,value) + + # check remaining keys, only accept underscore-prefixed comments + for key,value in data.items(): + if key == '' or key[0] != '_': + configError("unknown config key '%s'"%key,fatal=False) + have_error = True + + if have_error: + sys.exit(1) + + return config + + +def evalExpr(conf, exp, mockToken, msgType): + report_as_error = False + msg = msgType + ' ' + mockToken.str + ' violates naming convention' + + if isinstance(conf, dict): + report_as_error = conf[exp][0] + msg += ': ' + conf[exp][1] + + res = re.match(exp,mockToken.str) + if bool(res) == report_as_error: + reportNamingError(mockToken,msg) + +def check_include_guard_name(conf,directive): + parts = directive.str.split() + if len(parts) != 2: + msg = 'syntax error' + reportNamingError(directive,msg,'syntax') + return None,None + guard_name = parts[1] + guard_column = 1+directive.str.find(guard_name) + + filename = directive.file + if conf.include_guard.get('input','path') == 'basename': + filename = os.path.basename(filename) + use_case = conf.include_guard.get('case','upper') + if use_case == 'upper': + filename = filename.upper() + elif use_case == 'lower': + filename = filename.lower() + elif use_case == 'keep': + pass # keep filename case as-is + else: + print("invalid config value for 'case': '%s'"%use_case,file=sys.stderr) + sys.exit(1) + + barename = re.sub('[^A-Za-z0-9]','_',filename).strip('_') + expect_guard_name = conf.include_guard.get('prefix','') + barename + conf.include_guard.get('suffix','') + if expect_guard_name != guard_name: + msg = 'include guard naming violation; %s != %s'%(guard_name,expect_guard_name) + reportNamingError(directive,msg,'includeGuardName',column=guard_column) + + return guard_name,guard_column + +def check_include_guards(conf,cfg,unguarded_include_files): + # Scan for '#ifndef FILE_H' as the first directive, in the first N lines. + # Then test whether the next directive #defines the found name. + # Various tests are done: + # - check include guards for their naming and consistency + # - test whether include guards are in place + max_linenr = conf.include_guard.get('max_linenr', 5) + + def report(directive,msg,errorId,column=0): + reportNamingError(directive,msg,errorId,column=column) + + def report_pending_ifndef(directive,column): + report(directive,'include guard #ifndef is not followed by #define','includeGuardIncomplete',column=column) + + last_fn = None + pending_ifndef = None + phase = 0 + for directive in cfg.directives: + if last_fn != directive.file: + if pending_ifndef: + report_pending_ifndef(pending_ifndef,guard_column) + pending_ifndef = None + last_fn = directive.file + phase = 0 + if phase == -1: + # ignore (the remainder of) this file + continue + if not re.match(include_guard_header_re,directive.file): + phase = -1 + continue + + if directive.linenr > max_linenr: + if phase == 0 and conf.include_guard.get('required',1): + report(directive,'include guard not found before line %d'%max_linenr,'includeGuardMissing') + phase = -1 + continue + + if phase == 0: + # looking for '#ifndef FILE_H' + if not directive.str.startswith('#ifndef'): + if conf.include_guard.get('required',1): + report(directive,'first preprocessor directive should be include guard #ifndef','includeGuardMissing') + phase = -1 + continue + guard_name,guard_column = check_include_guard_name(conf,directive) + if guard_name == None: + phase = -1 + continue + pending_ifndef = directive + phase = 1 + elif phase == 1: + pending_ifndef = None + # looking for '#define FILE_H' + if not directive.str.startswith('#define'): + report(directive,'second preprocessor directive should be include guard #define','includeGuardIncomplete') + phase = -1 + continue + parts = directive.str.split() + if len(parts) == 1: + report(directive,'syntax error','syntax') + phase = -1 + continue + if guard_name != parts[1]: + report(directive,'include guard does not guard; %s != %s'%(guard_name,parts[1]),'includeGuardAwayFromDuty',severity='warning',column=guard_column) + + unguarded_include_files.remove(directive.file) + + phase = -1 + if pending_ifndef: + report_pending_ifndef(pending_ifndef,guard_column) + +def process(dumpfiles, configfile): + conf = loadConfig(configfile) + + if conf.include_guard: + global include_guard_header_re + include_guard_header_re = conf.include_guard.get('RE_HEADERFILE',"[^/].*\\.h\\Z") + + for afile in dumpfiles: + if not afile[-5:] == '.dump': + continue + if not args.cli: + print('Checking ' + afile + '...') + data = cppcheckdata.CppcheckData(afile) + process_data(conf,data) + +def check_file_naming(conf,data): + for source_file in data.files: + basename = os.path.basename(source_file) + good = False + for exp in conf.file: + good |= bool(re.match(exp, source_file)) + good |= bool(re.match(exp, basename)) + if not good: + mockToken = DataStruct(source_file, 0, basename) + reportNamingError(mockToken, 'File name ' + basename + ' violates naming convention') + +def check_namespace_naming(conf,data): + for tk in data.rawTokens: + if tk.str != 'namespace': + continue + mockToken = DataStruct(tk.next.file, tk.next.linenr, tk.next.str, tk.next.column) + for exp in conf.namespace: + evalExpr(conf.namespace, exp, mockToken, 'Namespace') + +def check_variable_naming(conf,cfg): + for var in cfg.variables: + if not var.nameToken: + continue + if var.access in ('Global','Public','Private'): + continue + prev = var.nameToken.previous + varType = prev.str + while "*" in varType and len(varType.replace("*", "")) == 0: + prev = prev.previous + varType = prev.str + varType + + if args.debugprint: + print("Variable Name: " + str(var.nameToken.str)) + print("original Type Name: " + str(var.nameToken.valueType.originalTypeName)) + print("Type Name: " + var.nameToken.valueType.type) + print("Sign: " + str(var.nameToken.valueType.sign)) + print("variable type: " + varType) + print("\n") + print("\t-- {} {}".format(varType, str(var.nameToken.str))) + + if conf.skip_one_char_variables and len(var.nameToken.str) == 1: + continue + if varType in conf.variable_prefixes: + prefix = conf.variable_prefixes[varType] + if not var.nameToken.str.startswith(prefix): + reportNamingError(var.typeStartToken, + 'Variable ' + + var.nameToken.str + + ' violates naming convention', + column=var.nameToken.column) + + mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str, var.nameToken.column) + for exp in conf.variable: + evalExpr(conf.variable, exp, mockToken, 'Variable') + +# Naming check for Global, Private and Public member variables +def check_gpp_naming(conf_list,cfg,access,message): + for var in cfg.variables: + if var.access != access: + continue + mockToken = DataStruct(var.typeStartToken.file, var.typeStartToken.linenr, var.nameToken.str, var.nameToken.column) + for exp in conf_list: + evalExpr(conf_list, exp, mockToken, message) + +def check_function_naming(conf,cfg): + for token in cfg.tokenlist: + if not token.function: + continue + if token.function.type in ('Constructor', 'Destructor', 'CopyConstructor', 'MoveConstructor'): + continue + retval = token.previous.str + prev = token.previous + while "*" in retval and len(retval.replace("*", "")) == 0: + prev = prev.previous + retval = prev.str + retval + if args.debugprint: + print("\t:: {} {}".format(retval, token.function.name)) + + if retval and retval in conf.function_prefixes: + if not token.function.name.startswith(conf.function_prefixes[retval]): + reportNamingError(token, 'Function ' + token.function.name + ' violates naming convention', column=token.column) + mockToken = DataStruct(token.file, token.linenr, token.function.name, token.column) + msgType = 'Function' + for exp in conf.function_name: + evalExpr(conf.function_name, exp, mockToken, msgType) + +def check_class_naming(conf,cfg): + for fnc in cfg.functions: + if fnc.type not in ('Constructor','Destructor'): + continue + mockToken = DataStruct(fnc.tokenDef.file, fnc.tokenDef.linenr, fnc.name, fnc.tokenDef.column) + msgType = 'Class ' + fnc.type + for exp in conf.class_name: + evalExpr(conf.class_name, exp, mockToken, msgType) + +def process_data(conf,data): + if conf.file: + check_file_naming(conf,data) + + if conf.namespace: + check_namespace_naming(conf,data) + + unguarded_include_files = [] + if conf.include_guard and conf.include_guard.get('required',1): + unguarded_include_files = [fn for fn in data.files if re.match(include_guard_header_re,fn)] + + for cfg in data.configurations: + if not args.cli: + print('Checking config %s...' % cfg.name) + if conf.variable: + check_variable_naming(conf,cfg) + if conf.private_member: + check_gpp_naming(conf.private_member,cfg,'Private','Private member variable') + if conf.public_member: + check_gpp_naming(conf.public_member,cfg,'Public','Public member variable') + if conf.global_variable: + check_gpp_naming(conf.global_variable,cfg,'Global','Global variable') + if conf.function_name: + check_function_naming(conf,cfg) + if conf.class_name: + check_class_naming(conf,cfg) + if conf.include_guard: + check_include_guards(conf,cfg,unguarded_include_files) + + for fn in unguarded_include_files: + mockToken = DataStruct(fn,0,os.path.basename(fn)) + reportNamingError(mockToken,'Missing include guard','includeGuardMissing') + +if __name__ == "__main__": + parser = cppcheckdata.ArgumentParser() + parser.add_argument("--debugprint", action="store_true", default=False, + help="Add debug prints") + parser.add_argument("--configfile", type=str, default="namingng.config.json", + help="Naming check config file") + + args = parser.parse_args() + process(args.dumpfile, args.configfile) + + sys.exit(0) diff --git a/cppcheck-2.14.0/addons/runaddon.py b/cppcheck-2.14.0/addons/runaddon.py new file mode 100644 index 00000000..536abe86 --- /dev/null +++ b/cppcheck-2.14.0/addons/runaddon.py @@ -0,0 +1,12 @@ +import cppcheckdata, cppcheck, runpy, sys, os + +if __name__ == '__main__': + addon = sys.argv[1] + __addon_name__ = os.path.splitext(os.path.basename(addon))[0] + sys.argv.pop(0) + + runpy.run_path(addon, run_name='__main__') + + # Run registered checkers + cppcheck.runcheckers() + sys.exit(cppcheckdata.EXIT_CODE) \ No newline at end of file diff --git a/cppcheck-2.14.0/addons/test/__init__.py b/cppcheck-2.14.0/addons/test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cppcheck-2.14.0/addons/test/misc-test.cpp b/cppcheck-2.14.0/addons/test/misc-test.cpp new file mode 100644 index 00000000..df1f8352 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misc-test.cpp @@ -0,0 +1,30 @@ +// To test: +// ~/cppcheck/cppcheck --dump misc-test.cpp && python ../misc.py -verify misc-test.cpp.dump + +#include +#include + +// Warn about string concatenation in array initializers.. +const char *a[] = {"a" "b"}; // stringConcatInArrayInit +const char *b[] = {"a","b" "c"}; // stringConcatInArrayInit +#define MACRO "MACRO" +const char *c[] = { MACRO "text" }; // stringConcatInArrayInit + + +// Function is implicitly virtual +class base { + virtual void dostuff(int); +}; + +class derived : base { + void dostuff(int); // implicitlyVirtual +}; + + +// Pass struct to ellipsis function +struct {int x;int y;} s; +void ellipsis(int x, ...); +void foo(std::vector v) { + ellipsis(321, s); // ellipsisStructArg + ellipsis(321, v[0]); // ellipsisStructArg +} diff --git a/cppcheck-2.14.0/addons/test/misra/config1.c b/cppcheck-2.14.0/addons/test/misra/config1.c new file mode 100644 index 00000000..31aa2a4d --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/config1.c @@ -0,0 +1,10 @@ + +struct S { + uint32_t some[100]; +}; + +void foo( void ) +{ + if (((S *)0x8000)->some[0] != 0U) { } +} + diff --git a/cppcheck-2.14.0/addons/test/misra/crash1.c b/cppcheck-2.14.0/addons/test/misra/crash1.c new file mode 100644 index 00000000..d00a72c3 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/crash1.c @@ -0,0 +1,8 @@ + +struct expression { + int nargs; + struct expression *args[3]; +}; + +struct expression plvar = {0}; + diff --git a/cppcheck-2.14.0/addons/test/misra/crash10.c b/cppcheck-2.14.0/addons/test/misra/crash10.c new file mode 100644 index 00000000..455d86e5 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/crash10.c @@ -0,0 +1,12 @@ +//#12267 + +extern uint32_t end; + +//#define KEEP // if uncomment this then wont crash + +KEEP static const int32_t ptr_to_end = &end; + +void foo(void) +{ + (void)ptr_to_end; +} diff --git a/cppcheck-2.14.0/addons/test/misra/crash2.c b/cppcheck-2.14.0/addons/test/misra/crash2.c new file mode 100644 index 00000000..c736a265 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/crash2.c @@ -0,0 +1,9 @@ + +// #11793 + +typedef struct pfmlib_pmu { + int flags ; + int (*get_event_encoding[10])(void* this, pfmlib_event_desc_t* e); +} pfmlib_pmu_t ; + +pfmlib_pmu_t sparc_ultra3_support = { .flags = 0 }; diff --git a/cppcheck-2.14.0/addons/test/misra/crash3.c b/cppcheck-2.14.0/addons/test/misra/crash3.c new file mode 100644 index 00000000..19583f00 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/crash3.c @@ -0,0 +1,30 @@ + + + + +/* This is the representation of the expressions to determine the + plural form. */ +struct expression +{ + int nargs; /* Number of arguments. */ + union + { + unsigned long int num; /* Number value for `num'. */ + struct expression *args[3]; /* Up to three arguments. */ + } val; +}; + + +struct expression GERMANIC_PLURAL = +{ + .nargs = 2, + .val = + { + .args = + { + [0] = (struct expression *) &plvar, + [1] = (struct expression *) &plone + } + } +}; + diff --git a/cppcheck-2.14.0/addons/test/misra/crash4.c b/cppcheck-2.14.0/addons/test/misra/crash4.c new file mode 100644 index 00000000..78625818 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/crash4.c @@ -0,0 +1,11 @@ + +struct ConDesDesc { + unsigned Order; + unsigned Import; +}; + +// cppcheck-suppress misra-config +static ConDesDesc ConDes[CD_TYPE_COUNT] = { + { 0, 0 }, + { 0, 0 }, +}; diff --git a/cppcheck-2.14.0/addons/test/misra/crash5.c b/cppcheck-2.14.0/addons/test/misra/crash5.c new file mode 100644 index 00000000..86010153 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/crash5.c @@ -0,0 +1,23 @@ + +struct _boardcnf_ch { + // cppcheck-suppress misra-config + uint8_t ddr_density[CS_CNT]; + uint64_t ca_swap; +}; + +struct _boardcnf { + + uint16_t dqdm_dly_r; + // cppcheck-suppress misra-config + struct _boardcnf_ch ch[DRAM_CH_CNT]; +}; + +static const struct _boardcnf boardcnfs[1] = { + { + 0x0a0, + { + { {0x02, 0x02}, 0x00345201 }, + { {0x02, 0x02}, 0x00302154 } + } + }, +}; diff --git a/cppcheck-2.14.0/addons/test/misra/crash6.c b/cppcheck-2.14.0/addons/test/misra/crash6.c new file mode 100644 index 00000000..bf77e5f6 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/crash6.c @@ -0,0 +1,17 @@ + + +typedef struct _tGames +{ + char magicdirname[10]; + unsigned int expectedmask; + unsigned char pictureorder[3]; +} tGames; + +static const tGames games[1]={ + {"Pawn", 1, {0,1,2}} +}; + + + + + diff --git a/cppcheck-2.14.0/addons/test/misra/crash7.c b/cppcheck-2.14.0/addons/test/misra/crash7.c new file mode 100644 index 00000000..210cfeb4 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/crash7.c @@ -0,0 +1,8 @@ + + +static const struct id3_frametype wordlist[] = +{ + {0, "Encryption method registration"}, + {1, "Popularimeter"}, +}; + diff --git a/cppcheck-2.14.0/addons/test/misra/crash8.c b/cppcheck-2.14.0/addons/test/misra/crash8.c new file mode 100644 index 00000000..970d7254 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/crash8.c @@ -0,0 +1,10 @@ + +struct three_d_filter_t { + char name[16]; + double elem[2]; +}; + +static three_d_filter_t base_filters[] = { + {"Identity", { 1.0, 0.0 } }, + {"Echo", { 0.4, 0.0 } } +}; diff --git a/cppcheck-2.14.0/addons/test/misra/crash9.c b/cppcheck-2.14.0/addons/test/misra/crash9.c new file mode 100644 index 00000000..5abf2b8a --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/crash9.c @@ -0,0 +1,3 @@ + +#line 3 "" +typedef int8_t flex_int8_t; diff --git a/cppcheck-2.14.0/addons/test/misra/crash_misra9_parseInitializer.c b/cppcheck-2.14.0/addons/test/misra/crash_misra9_parseInitializer.c new file mode 100644 index 00000000..8e3dcfd5 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/crash_misra9_parseInitializer.c @@ -0,0 +1,18 @@ +union { + struct { + uint8_t a; + uint8_t b; + } a; +} bar; + +struct foo { + uint8_t a; + union bar w; + uint8_t b; +}; + +struct foo asdf = { + 0, + {{0,0}}, + 1 +}; diff --git a/cppcheck-2.14.0/addons/test/misra/misra-ctu-1-test.c b/cppcheck-2.14.0/addons/test/misra/misra-ctu-1-test.c new file mode 100644 index 00000000..45c963de --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra-ctu-1-test.c @@ -0,0 +1,52 @@ +// Test with command: +// ./cppcheck --enable=information --enable=style --addon=misra --inline-suppr addons/test/misra/misra-ctu-*-test.c + +#include "misra-ctu-test.h" + +extern MISRA_2_3_A misra_2_3_a; + +x = MISRA_2_5_OK_1; + +// cppcheck-suppress misra-c2012-2.3 +// cppcheck-suppress misra-c2012-5.6 +typedef int MISRA_5_6_VIOLATION; + +// cppcheck-suppress misra-c2012-5.7 +struct misra_5_7_violation_t { + int x; // cppcheck-suppress unusedStructMember +}; +static misra_5_7_violation_t misra_5_7_use_type_1; + +// #11443 - FP +static struct +{ // no warning + uint16_t x; // cppcheck-suppress unusedStructMember +} misra_5_7_false_positive_1; + +// cppcheck-suppress misra-c2012-8.4 +// cppcheck-suppress misra-c2012-5.8 +int misra_5_8_var1; +// cppcheck-suppress misra-c2012-8.4 +// cppcheck-suppress misra-c2012-5.8 +int misra_5_8_var2; +// cppcheck-suppress misra-c2012-5.8 +static void misra_5_8_f(void) {} + + +// cppcheck-suppress misra-c2012-5.9 +static int misra_5_9_count; +// cppcheck-suppress misra-c2012-5.9 +static void misra_5_8_foo(void) {} + +// cppcheck-suppress misra-c2012-8.5 +extern int misra_8_5; + +// cppcheck-suppress misra-c2012-8.4 +// cppcheck-suppress misra-c2012-8.6 +int32_t misra_8_6 = 1; + +void misra_8_7_external(void) {} + +// #12362 +void misra_8_7_compliant( void ){} +static void misra_8_7_call(void) { misra_8_7_compliant(); } \ No newline at end of file diff --git a/cppcheck-2.14.0/addons/test/misra/misra-ctu-2-test.c b/cppcheck-2.14.0/addons/test/misra/misra-ctu-2-test.c new file mode 100644 index 00000000..e08bd6d0 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra-ctu-2-test.c @@ -0,0 +1,59 @@ +// Test with command: +// ./cppcheck --enable=information --enable=style --addon=misra --inline-suppr addons/test/misra/misra-ctu-*-test.c + +#include "misra-ctu-test.h" + +extern MISRA_2_3_B misra_2_3_b; + +x = MISRA_2_5_OK_2; + +// cppcheck-suppress misra-c2012-5.6 +typedef int MISRA_5_6_VIOLATION; +static MISRA_5_6_VIOLATION misra_5_6_x; + +// cppcheck-suppress misra-c2012-5.7 +struct misra_5_7_violation_t { + int x; // cppcheck-suppress unusedStructMember +}; +static misra_5_7_violation_t misra_5_7_use_type_2; + +// #11443 - FP +static struct +{ // no warning + uint16_t x; // cppcheck-suppress unusedStructMember +} misra_5_7_false_positive_2; + +// cppcheck-suppress misra-c2012-5.8 +static int misra_5_8_var1; +// cppcheck-suppress misra-c2012-8.4 +// cppcheck-suppress misra-c2012-5.8 +void misra_5_8_f(void) { + // cppcheck-suppress [misra-c2012-5.8, unusedVariable] + char misra_5_8_var2; +} + +// cppcheck-suppress misra-c2012-5.9 +static int misra_5_9_count; +// cppcheck-suppress misra-c2012-5.9 +static void misra_5_8_foo(void) {} + +// cppcheck-suppress misra-c2012-8.5 +extern int misra_8_5; + +// cppcheck-suppress misra-c2012-8.4 +// cppcheck-suppress misra-c2012-8.6 +int32_t misra_8_6 = 2; +int32_t misra_8_6_1; +// cppcheck-suppress misra-c2012-8.7 +int32_t misra_8_6_1 = 2; +// cppcheck-suppress misra-c2012-8.4 +// cppcheck-suppress misra-c2012-8.7 +void misra_8_7(void) {} +static void misra_8_7_caller(void) { + misra_8_7(); + misra_8_7_external(); +} + +// #12362 +typedef void(*misra_8_7_func_ptr)( void ); +static const misra_8_7_func_ptr ptrs[] = { misra_8_7_compliant, NULL }; \ No newline at end of file diff --git a/cppcheck-2.14.0/addons/test/misra/misra-ctu-test.h b/cppcheck-2.14.0/addons/test/misra/misra-ctu-test.h new file mode 100644 index 00000000..f9a3f680 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra-ctu-test.h @@ -0,0 +1,23 @@ + + +typedef int MISRA_2_3_A; +typedef int MISRA_2_3_B; +typedef int MISRA_2_3_VIOLATION; // cppcheck-suppress misra-c2012-2.3 + +// cppcheck-suppress misra-c2012-2.4 +struct misra_2_4_violation_t { + int x; +}; + +static inline void misra_5_9_exception(void) {} + +void misra_8_7_external(void); + +#define MISRA_2_5_OK_1 1 +#define MISRA_2_5_OK_2 2 +// cppcheck-suppress misra-c2012-2.5 +#define MISRA_2_5_VIOLATION 0 + +// #12362 +extern void misra_8_7_compliant( void ); + diff --git a/cppcheck-2.14.0/addons/test/misra/misra-suppressions1-test.c b/cppcheck-2.14.0/addons/test/misra/misra-suppressions1-test.c new file mode 100644 index 00000000..403d71f7 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra-suppressions1-test.c @@ -0,0 +1,37 @@ +// To test: +// ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump +// There should be no violations reported + +// This needs to stay at line number 7 to make the test pass +// If it is changed update suppressions.txt with the new line number +// cppcheck-suppress-file misra-c2012-5.2 +#include //21.6 + +extern int misra_5_2_var_hides_var______31x;//8.4 +static int misra_5_2_var_hides_var______31y;//5.2 +static int misra_5_2_function_hides_var_31x; +static void misra_5_2_function_hides_var_31y(void) {}//5.2 +static void foo(void) +{ + int i; + // cppcheck-suppress-begin misra-c2012-16.4 + // cppcheck-suppress misra-c2012-16.6 + switch(misra_5_2_func1()) //16.4 16.6 + { + case 1: + { + do + { + for(i = 0; i < 10; i++) + { + if(misra_5_2_func3()) //17.3 + { + int misra_5_2_var_hides_var_1____31x; + int misra_5_2_var_hides_var_1____31y;//5.2 + } + } + } while(misra_5_2_func2()); //17.3 + } + } + // cppcheck-suppress-end misra-c2012-16.4 +} diff --git a/cppcheck-2.14.0/addons/test/misra/misra-suppressions2-test.c b/cppcheck-2.14.0/addons/test/misra/misra-suppressions2-test.c new file mode 100644 index 00000000..79d3f681 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra-suppressions2-test.c @@ -0,0 +1,20 @@ +// To test: +// ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump +// There should be no violations reported + +// cppcheck-suppress-file misra-c2012-5.2 +union misra_5_2_field_hides_field__63x { //19.2 +int misra_5_2_field_hides_field__31x; +int misra_5_2_field_hides_field__31y;//5.2 +}; +struct misra_5_2_field_hides_field__63y { //5.2 +int misra_5_2_field_hides_field1_31x; +int misra_5_2_field_hides_field1_31y;//5.2 +}; +const char *s41_1 = "\x41g"; // 4.1 8.4 +const char *s41_2 = "\x41\x42"; // 8.4 + +// cppcheck-suppress misra-c2012-5.7 +struct misra_5_7_violation_t { + int x; +}; diff --git a/cppcheck-2.14.0/addons/test/misra/misra-test-avr8.c b/cppcheck-2.14.0/addons/test/misra/misra-test-avr8.c new file mode 100644 index 00000000..faa6eb6b --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra-test-avr8.c @@ -0,0 +1,29 @@ +// To test: +// ~/cppcheck/cppcheck--dump -DDUMMY --suppress=uninitvar misra/misra-test-avr8.c --std=c89 --platform=avr8 && python3 ../misra.py -verify misra/misra-test-avr8.c.dump + +static void misra_10_4(void) +{ + // #10480 + const char buf1[1] = {a}; + const char c = '0'; + x = buf1[0] - c; + + const char buf2[2] = {x,y}; + x = 'a' == buf2[0]; // no-warning + + typedef struct { + int t; + char buf[2]; + } foo_t; + const foo_t cmd = {0}; + x = 'b' == cmd.buf[0]; // no-warning + + const foo_t * pcmd = &cmd; + x='c' == pcmd->buf[0]; // no-warning + (void)cmd.t; +} + +static void misra_12_2(void) { + a = (((uint64_t)0xFF) << 32); +} + diff --git a/cppcheck-2.14.0/addons/test/misra/misra-test-c11.c b/cppcheck-2.14.0/addons/test/misra/misra-test-c11.c new file mode 100644 index 00000000..031bc361 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra-test-c11.c @@ -0,0 +1,25 @@ +// To test: +// ~/cppcheck/cppcheck --dump misra/misra-test-c11.c --std=c11 +// ~/cppcheck/cppcheck --dump -DDUMMY --suppress=uninitvar --inline-suppr misra/misra-test-c11.c --std=c11 --platform=unix64 && python3 ../misra.py -verify misra/misra-test-c11.c.dump + +#include + +typedef unsigned int UINT_TYPEDEF; +struct struct_with_bitfields +{ + unsigned int a:2; // Compliant + signed int b:2; // Compliant + UINT_TYPEDEF c:2; // Compliant + int d:2; // 6.1 - plain int not compliant + signed long f:2; // Compliant in c99 or later - explicitly signed integer type + unsigned int g:1; // Compliant + signed int h:1; // 6.2 - signed int with size 1 is not compliant + uint16_t i:1; // Compliant + bool j:1; // Compliant in C99 or later +}; + +static void misra6_1_fn(void) { + // "Use" occurrence should not generate warnings + struct_with_bitfields s; + s.h = 61; +} diff --git a/cppcheck-2.14.0/addons/test/misra/misra-test.c b/cppcheck-2.14.0/addons/test/misra/misra-test.c new file mode 100644 index 00000000..bfe9a562 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra-test.c @@ -0,0 +1,2075 @@ +// To test: +// ~/cppcheck/cppcheck --dump misra/misra-test.h --std=c89 +// ~/cppcheck/cppcheck --dump -DDUMMY --suppress=uninitvar --inline-suppr misra/misra-test.c --std=c89 --platform=unix64 && python3 ../misra.py -verify misra/misra-test.c.dump + +#include "path\file.h" // 20.2 +#include "file//.h" // 20.2 +#include "file/*.h" // 20.2 +#include "file'.h" // 20.2 +#include // 20.2 +#include "file,.h" // 20.2 + +#include "misra-test.h" + +#include /*abc*/ "file.h" // no warning +/*foo*/#include "file.h" // no warning +#include "./file.h" // no warning +#include \ + "file.h" +#include /*abc*/ \ + "file.h" +#include "fi" "le.h" // 20.3 (strings are concatenated after preprocessing) +#include "fi" // 20.3 +#include // 20.3 +#include PATH "file.h" // 20.3 +#define H_20_3_ok "file.h" +#include H_20_3_ok +#include file.h // 20.3 +#define H_20_3_bad file.h +#include H_20_3_bad // TODO: 20.3 Trac #9606 +#include "//file.h" // 20.2 +#include "//file.h" H_20_3_bad // 20.2 20.3 +//#include H_20_3_bad // no warning +#include H_20_3_ok H_20_3_ok // 20.3 +#include // no warning + +#include // 21.4 +#include // 21.5 +#include //21.6 +#include //21.6 +#include // 21.10 +#include // 21.11 +#include + +// Check that the addon doesn't crash +typedef struct { + union { // 19.2 + struct { + unsigned a : 2; + unsigned : 14; + }; + uint16_t value; + }; +} STRUCT_BITS; + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef signed int s32; +typedef unsigned long long u64; + +static void misra_1_2(bool expr) +{ + (void)(condition ? : 0); // 1.2 + a = 1 + ({if (!expr) {code;} 1;}); // 1.2 +} + +static _Atomic int misra_1_4_var; // 1.4 +static _Noreturn void misra_1_4_func(void) // 1.4 +{ + if (0 != _Generic(misra_1_4_var)) {} // 1.4 17.3 + printf_s("hello"); // 1.4 +} + +#define MISRA_2_2 (1*60) + +static void misra_2_2(int x) { + int a; + a = x + 0; // 2.2 + a = 0 + x; // 2.2 + a = x * 0; // 2.2 + a = 0 * x; // 2.2 + a = x * 1; // 2.2 + a = 1 * x; // 2.2 + a = MISRA_2_2; + (void)a; +} + +/* // */ // 3.1 +/* /* */ // 3.1 +//// +/* https://cppcheck.net */ + +// http://example.com // no warning + +static void misra_2_7_unused_param (int *param1, int unused_param) // 2.7 +{ + *param1 = 42U; +} + +static void misra_2_7_used_params (int *param1, int param2, int param3) +{ + (void)param3; + *param1 = param2; +} + +static void misra_2_7_a(int a, + int b, // 2.7 + int c, + int d) // 2.7 +{ + (void)a; + (void)c; +} +static void misra_2_7_b(int a, int b, int c, // 2.7 + int d) // 2.7 +{ + (void)a; +} +static void misra_2_7_c(int a, ...) { (void)a; } +static void misra_2_7_d(int) { } // 2.7 8.2 + +static void misra_3_2(int enable) +{ + // This won't generate a violation because of subsequent blank line \ + + int y = 0; + int x = 0; // 3.2 non-compliant comment ends with backslash \ + if (enable != 0) + { + ++x; // This is always executed + // 3.2 potentially non-compliant comment ends with trigraph resolved to backslash ??/ + ++y; // This is hidden if trigraph replacement is active + } + + (void)printf("x=%i, y=%i\n", x, y); +} + +extern int misra_5_1_extern_var_hides_var_x; +extern int misra_5_1_extern_var_hides_var_y; //5.1 +int misra_5_1_var_hides_var________a; // 8.4 +int misra_5_1_var_hides_var________b; int misra_5_1_var_hides_var________b1; int misra_5_1_var_hides_var________b2; //5.1 8.4 +int misra_5_1_var_hides_var________c; //5.1 8.4 +int misra_5_1_var_hides_var________d; //5.1 8.4 +int misra_5_1_var_hides_var________e; //5.1 8.4 + +extern const uint8_t misra_5_2_var1; +const uint8_t misra_5_2_var1 = 3; +static int misra_5_2_var_hides_var______31x; +static int misra_5_2_var_hides_var______31y;//5.2 +static int misra_5_2_function_hides_var_31x; +static void misra_5_2_function_hides_var_31y(void) {}//5.2 +static void foo(void) +{ + int i; + switch(misra_5_2_func1()) //16.4 16.6 + { + case 1: + { + do + { + for(i = 0; i < 10; i++) + { + if(misra_5_2_func3()) //17.3 + { + int misra_5_2_var_hides_var_1____31x; + int misra_5_2_var_hides_var_1____31y;//5.2 + } + } + } while(misra_5_2_func2()); //17.3 + } + break; + } +} + +union misra_5_2_field_hides_field__63x { //19.2 +int misra_5_2_field_hides_field__31x; +int misra_5_2_field_hides_field__31y;//5.2 +}; +struct misra_5_2_field_hides_field__63y { //5.2 +int misra_5_2_field_hides_field1_31x; +int misra_5_2_field_hides_field1_31y;//5.2 +}; +const char *s41_1 = "\x41g"; // 4.1 8.4 +const char *s41_2 = "\x41\x42"; // 8.4 +const char *s41_3 = "\x41" "\x42"; // 8.4 +const char *s41_4 = "\x41" "g"; // 8.4 +const char *s41_5 = "\x41\xA"; // 8.4 +const char *s41_6 = "\xA\x41"; // 8.4 +const char *s41_7 = "\xAA\xg\x41"; // 4.1 8.4 +const char *s41_8 = "\xAA\x\x41"; // 4.1 8.4 +const char *s41_9 = "unknown\gsequence"; // 8.4 +const char *s41_10 = "simple\nsequence"; // 8.4 +const char *s41_11 = "string"; // 8.4 +int c41_3 = '\141t'; // 4.1 8.4 +int c41_4 = '\141\t'; // 8.4 +int c41_5 = '\0'; // 10.3 8.4 +int c41_6 = '\0\t'; // 8.4 +int c41_7 = '\12\t'; // 8.4 +int c41_8 = '\0t'; // 4.1 8.4 +int c41_9 = '\12'; // 8.4 +int c41_10 = '\12\n'; // 8.4 +int c41_11 = '\12n'; // 4.1 8.4 +int c41_12 = '\12323'; // 4.1 8.4 +int c41_13 = '\123\3'; // 8.4 +// TODO int c41_14 = '\777\777'; +int c41_15 = 'a'; // 10.3 8.4 + +static void misra_4_1(void) +{ + (void)printf("\x41g"); // 4.1 + (void)printf("\x41\x42"); + (void)printf("\x41" "g"); +} + +const char *s42_1 = "String containing trigraphs ??-??-??"; // 4.2 8.4 +const char *s42_2 = "String containing trigraph???=preceded by question mark"; // 4.2 8.4 +const char *s42_3 = "No trigraph?(?'?)"; // 8.4 + +static void misra_4_2(void) +{ + (void)printf("??=Trigraph\n"); // 4.2 + (void)printf("No?/Trigraph\n"); +} + +#define misra_5_4_macro_hides_macro__31x 1 +#define misra_5_4_param_hides_macro__31x 1 +#define misra_5_4_macro_hides_macro__31y 2 //5.4 +#define m1(misra_5_4_param_hides_macro__31y) 1 //5.4 +#define m2(misra_5_4_param_hides_param__31x,misra_5_4_param_hides_param__31y) 1 //5.4 +#ifdef misra_5_4_macro_hides_macro__31x +#define misra_5_4_macro 1 // no warning +#else +#define misra_5_4_macro 2 // no warning +#endif + +#define misra_5_5_var_hides_macro____31x 1 +#define misra_5_5_functionhides_macro31x 1 +#define misra_5_5_param_hides_macro__31x 1 +#define misra_5_5_tag_hides_macro____31x 1 +#define misra_5_5_hides_macro________31x 1 + +int misra_5_5_var_hides_macro____31y; //5.5 8.4 +static void misra_5_5_functionhides_macro31y(int misra_5_5_param_hides_macro__31y){(void)misra_5_5_param_hides_macro__31y;} //5.5 +struct misra_5_5_tag_hides_macro____31y { //5.5 +int x; +}; +static void misra_5_5_func1(void) +{ + switch(misra_5_5_func2()) //16.4 16.6 + { + case 1: + { + do + { + if(misra_5_5_func3()) //17.3 + { + int misra_5_5_hides_macro________31y; //5.5 + } + } while(misra_5_5_func2()); //17.3 + } + break; + } +} + +typedef unsigned int UINT_TYPEDEF; +struct struct_with_bitfields +{ + unsigned int a:2; // Compliant + signed int b:2; // Compliant + UINT_TYPEDEF c:2; // Compliant + int d:2; // 6.1 - plain int not compliant + signed long f:2; // 6.1 - signed long not compliant + unsigned int g:1; // Compliant + signed int h:1; // 6.2 - signed int with size 1 is not compliant + uint16_t i:1; // Compliant + bool j:1; // 6.1 - bool not legal until c99 +}; + +static void misra6_1_fn(void) { + // "Use" occurrence should not generate warnings + struct_with_bitfields s; + s.h = 61; +} + +static void misra_7_1(void) { + int x = 066; // 7.1 +} + +static void misra_7_2(void) { + uint32_t a = 0x7fffffff; + uint32_t b = 0x80000000; // 7.2 + uint32_t c = 0x80000000U; + uint32_t d = 2147483647; + uint64_t e = 2147483648; + uint32_t f = 2147483648U; +} + +// The addon should not generate false positives for the identifiers. +struct misra_7_3_s +{ + uint32_t ul_clka; + uint32_t test123l; +}; + +static void misra_7_3(void) { + long misra_7_3_a = 0l; //7.3 + long misra_7_3_b = 0lU; //7.3 + long long misra_7_3_c = 0Ull; //7.3 + long long misra_7_3_d = 0ll; //7.3 + long double misra_7_3_e = 7.3l; //7.3 + struct misra_7_3_s misra_7_3_f = + { + .ul_clka = 19U, + .test123l = 23U + }; +} + +typedef const char* MISRA_7_4_CHAR_CONST; +static MISRA_7_4_CHAR_CONST misra_7_4_return_const_type_def (void) { return "return_typedef_const"; } +static char *misra_7_4_return_non_const (void) { return 1 + "return_non_const"; } // 7.4 18.4 +static const char *misra_7_4_return_const (void) { return 1 + "return_const"; } // 18.4 + +static void misra_7_4_const_call(int a, const char* b) { } // 2.7 +static void misra_7_4_const_ptr_call(int a, const char const* b) { } // 2.7 +static void misra_7_4_call(int a, char* b) { } // 2.7 +static void misra_7_4_call_2(int a, ...) { } // 2.7 + +static void misra_7_4(void) +{ + const char *a = "text a"; + char* const b = "text_b"; // 7.4 + char *c = "text c"; // 7.4 + char *d = 1 + "text d"; // 7.4 18.4 + char *e = "text e" + 1 + 2; // 7.4 18.4 + char *f = 1 + "text f" + 2; // 7.4 18.4 + const wchar_t *g = "text_g"; + wchar_t *h = "text_h"; // 7.4 + + misra_7_4_const_call(1, ("text_const_call")); + misra_7_4_const_ptr_call(1, ("text_const_call")); + misra_7_4_call(1, "text_call"); // 7.4 11.8 + misra_7_4_call_2(1, "a", "b"); +} + +const misra_8_1_a; // 8.1 8.4 + +static int misra_8_2_a (int n, ...); +extern int misra_8_2_b (int n); +extern int misra_8_2_c (int); // 8.2 +static int misra_8_2_d (); // 8.2 +static int misra_8_2_e (void); +static int misra_8_2_f (vec, n ) +int *vec; // 8.2 +int n; // 8.2 +{ + return vec[ n - 1 ]; +} +static int misra_8_2_g ( /* comment */ ); // 8.2 +static int misra_8_2_h ( /* comment 1 */ /* comment 2 */ ); // 8.2 +static int misra_8_2_i ( /* comment */ void); +static int misra_8_2_j ( /* comment */ void /* comment */); +static int misra_8_2_k ( // + void); +static int misra_8_2_l ( // 8.2 +); +static void misra_8_2_m(uint8_t * const x); +static void misra_8_2_m(uint8_t * const x) +{ +(void)x; +} +int16_t ( *misra_8_2_p_a ) (); // 8.2 8.4 +int16_t ( *misra_8_2_p_b ) (void); // 8.4 +int16_t ( *misra_8_2_p_c ) (int); // 8.4 +static int misra_8_2_n(int a) +{ return a + 42; } +static int misra_8_2_o( + const uint32_t a1, + const uint8_t *const a2 +) +{ return *a2 + a1; } +static int misra_8_2_p( + const uint32_t a1, + const uint8_t *const a2 +); +static int misra_8_2_q +(); // 8.2 + +void misra_8_4_foo(void) {} // 8.4 +extern void misra_8_4_func(void); +void misra_8_4_func(void) {} +static void misra_8_4_bar(void) {} // Declared in header +extern int16_t misra_8_4_count; // no-warning +int16_t misra_8_4_count = 0; // Compliant +extern uint8_t misra_8_4_buf1[13]; // no-warning +uint8_t misra_8_4_buf2[24]; // 8.4 +typedef struct { uint16_t a; uint16_t b; } misra_8_4_struct; +extern misra_8_4_struct bar[42]; +misra_8_4_struct bar[42]; // compliant +extern uint16_t misra_8_4_speed = 6000u; //8.4 +uint8_t misra_8_4_pressure = 101u; //8.4 +int32_t misra_8_4_ext_val2; +int32_t misra_8_4_ext_val2 = 3; // compliant +int32_t misra_8_4_ext_val4; //8.4 + +static int32_t misra_8_8 = 123; +extern int32_t misra_8_8; // 8.8 + +static int32_t misra_8_9_i; // 8.9 +static int32_t misra_8_9_foo(void) { return misra_8_9_i++; } + +inline int32_t misra_8_10_value(void) { return 123; } // 8.10 8.4 + +extern int a811[]; // 8.11 + +enum misra_8_12_a { misra_a1 = 1, misra_a2 = 2, misra_a3, misra_a4 = 3 }; //8.12 +enum misra_8_12_b { misra_b1, misra_b2, misra_b3 = 3, misra_b4 = 3 }; // no-warning +enum misra_8_12_c { misra_c1 = misra_a1, misra_c2 = 1 }; // no-warning +enum misra_8_12_d { misra_d1 = 1, misra_d2 = 2, misra_d3 = misra_d1 }; // no-warning +enum misra_8_12_e { misra_e1 = sizeof(int), misra_e2}; // no-crash + +static void misra_8_14(char * restrict str) {(void)str;} // 8.14 + +// #11707 -- false positive +struct S_9_3 { struct S_9_3* p; int x; }; +struct S_9_3* s_9_3_array[] = { x, NULL }; // 8.4 + +// #10854 +struct Entry_9_2{ + union{ // 19.2 + const int *p; + int x; + }; + int y; +}; + +static void misra_9_2_10854(void){ + struct Entry_9_2 e1[] = + { + {{ .x = 1 }, .y = 2 } + }; +} +static void misra_9_empty_or_zero_initializers(void) { + int a[2] = {}; // 9.2 + int b[2][2] = {}; // 9.2 + int c[2][2] = { {} }; // 9.2 9.3 + int d[2][2] = { {}, {} }; // 9.2 + int e[2][2] = { { 1 , 2 }, {} }; // 9.2 + + int f[5] = { 0 }; + int f1[5] = { 0u }; // 9.3 + unsigned int f1[ 3 ][ 2 ] = { 0U }; // 9.3 9.2 + unsigned int f2[ 3 ] = { 0U }; // 9.3 + float f3[ 3 ][ 2 ] = { 0.0F }; // 9.3 9.2 + int g[5][2] = { 0 }; + int h[2][2] = { { 0 } }; // 9.3 + int i[2][2] = { { 0 }, { 0 } }; + int j[2][2] = { { 1, 2 }, { 0 } }; + int k[2][2] = { [0] = { 1 , 2 }, { 0 } }; + int l[1][2] = { { 0 }, [0] = { 1 } }; // 9.3 9.4 + + typedef struct { + int a; + int b; + } struct1; + + struct1 m = { }; // 9.2 + struct1 n = { 0 }; +} + +static void misra_9_string_initializers(void) { + const char a[12] = { "Hello world" }; // 9.2 + const char b[2][20] = "Hello world"; // 9.2 9.3 + const char c[] = "Hello world"; + const char d[15] = "Hello world"; + const char e[1][12] = { "Hello world" }; + const char *f[2] = { "Hello", [1] = "world" }; + const char *g[1] = "Hello world"; // 9.2 + + const char h[2][15] = { { 0 }, "Hello world" }; + + char **str_p = &f[0]; + + char **i[1] = { str_p }; + char **j[1] = { { str_p } }; // 9.2 +} + +static void misra_9_array_initializers(void) { + char a[4] = { 1, 2, 3, 4 }; + char b[2][2] = { {1, 2}, {3, 4} }; + char c[2][2] = { 1, 2, 3, 4 }; // 9.2 + char d[6] = { { 1, 2 }, { 3, 4 }, { 5, 6 } }; // 9.2 9.3 + + char e[2][2] = { {1, 2}, {4} }; // 9.3 + char f[2][2] = { 1, 2, 3 }; // 9.2 9.3 + char g[2][2] = { {1, 2, 3, 4} }; // 9.3 + + char h[2][2] = { { 1, { 2 } }, { 3, { 5 } } }; // 9.2 + char i[2][2] = { { 1, { 2 } }, { 3 } }; // 9.2 9.3 + char j[2][3] = { { 1, { 2 }, 3 }, { 4, { 5 }, 6 } }; // 9.2 + char k[2][3] = { { 1, { 2 }, 3 }, { 4, { 5 } } }; // 9.2 9.3 + char l[3] = { 1, { 2, 3 } }; // 9.2 9.3 +} + +static void misra_9_array_initializers_with_designators(void) { + char a[1] = { [0][1] = 1 }; // 9.2 + char b[1] = { [0] = { 1, 2 } }; // 9.2 + char c[2][2] = { [0] = {1, 2, 3} }; + char d[1][2] = { [0] = 1 }; // 9.2 + char e[2][2] = { { 1, 2 }, [1][0] = {3, 4} }; // 9.2 + int e1[2][2] = { [ 0 ][ 1 ] = 0, { 5, 6 } }; // no warning #12419 + char f[2] = { [0] = 1, 2 }; + char g[2] = { [1] = 2, [0] = 1 }; + char h[2][2] = { { 1, 2 }, [1] = { 3 } }; // 9.3 + char i[2][2] = { { 1, 2 }, [1] = { 3, 4 } }; + char j[2][2] = { { 1, 2 }, [1] = { [0] = 3 } }; + char k[2][2] = { { 1, 2 }, [1][0] = 3 }; + char l[2][2] = { { 1, 2 }, [1][0] = 3, 4}; // 9.2 + char m[2][2] = { [0] = { [2] = 2 }, [1][5] = 4 }; + char n[2][2] = { [0] = { 1 } }; // 9.3 + char o[2][2] = { { 1 }, [1][0] = 3 }; // 9.3 + char p[2][2] = { { 1, 2 }, { 3, 4 }, [1] = { 3 } }; // 9.3 9.4 + // cppcheck-suppress unknownEvaluationOrder + char q[2][2] = { { 1, 2 }, { 1 }, [1] = { [1] = 3 } }; // 9.4 + char r[2][2][2] = { [0][0] = { 1, 2 }, [1] = { [0] = {5, 6} } }; + char s[2][2][2] = { [0][0] = { 1, 2 }, [1] = {5, 6, 7, 8}}; // 9.2 + char t[2][2][2] = { [0][0] = { 1, 2 }, {3, 4}, [1] = {5, 6}}; // 9.2 9.3 + char u[2][2][2] = { [0] = { 1, 2, {3, 4} } }; // 9.2 + char v[2][2][2] = { [0] = { 1, 2, [1] = {3, 4} }}; // 9.2 +} + +static void misra_9_struct_initializers(void) { + typedef struct { + int i1; + int i2; + } struct1; + + typedef struct { + char c1; + struct1 is1; + char c2[4]; + } struct2; + + typedef struct { + struct1 s[2][2]; + } struct3; + + typedef struct { + unknown_field_type f1; + unknown_field_type f2[2]; + int f3[2]; + } struct_with_unknown_fields; + + struct3 sa[2] = { [1].s[1][0].i1 = 3, 4 }; // 9.2 + + struct1 sa = 1; // 9.2 + + struct1 sb = { 1, 2 }; + struct2 sc = { 1, { 2 }, {4, 5, 6, 7} }; + struct2 sd = { 1, { 2, 3 }, {4, 5, 6} }; // 9.3 + struct2 se = { 1, 2, 3, 4, 5, 6, 7 }; // 9.2 + struct2 sf = { 1, { 2, 3 }, 4, 5, 6, 7 }; // 9.2 + struct2 sg = { 1, { 2 }, 4, 5, 6, 7 }; // 9.2 + struct2 sh = { 1, { 2, 3 }, 4, 5, 6 }; // 9.2 9.3 + struct2 si = { 1, 2, 3, {4,5,6,7} }; // 9.2 + + int a; + struct1 sj = { a = 1, 2 }; // 13.1 + + // Struct types + struct2 sta = { .is1 = sc }; // 9.2 + struct2 stb = { .is1 = sb }; + struct1 stc[1] = { sc }; // 9.2 + struct1 std[1] = { sb }; + + // Struct designators + struct1 sda = { 1, .i2 = 2 }; + struct2 sdb = { 1, { 2, .i2=3 }, .c2[1]=5 }; + struct2 sdc = { 1, { 2, .i2=3 }, .c2 = { 5 } }; // 9.3 + struct2 sdd = { 1, { 2, .i2=3 }, .c2 = 5 }; // 9.2 + struct2 sde = { .is1 = { 2, 3 }, { 4, 5, 6, 7 } }; + + // Struct arrays + struct1 asa[2] = { {1,2}, {3,4} }; + struct1 asb[2] = { {1}, {3,4} }; + struct1 asc[2] = { {1,2} }; // 9.3 + struct1 asd[2] = { 1,2, 3,4 }; // 9.2 + struct1 ase[2] = { 1,2, 3 }; // 9.2 + struct1 asf[2] = { 1,2 }; // 9.2 9.3 + struct1 asg[2] = { [1].i1 = 3 }; + struct3 ash[2] = { [1].s[1][0].i1 = 3 }; + struct3 asi[2] = { [0] = { .s[0] = { { 1, 2 } }}}; // 9.3 + struct3 asj[2] = { [0] = { .s[0] = { 1, 2 }}}; // 9.2 9.3 + + // Missing type information + dummy_struct dsa = { 1, .a = 2 }; + dummy_struct dsb[2] = { {1,2}, {3,4} }; + dummy_struct dsc[2][2] = { {1,2}, {3,4} }; + dummy_struct dsd[2][2] = { 1, 2, 3, 4 }; // 9.2 + dummy_struct dse[3] = { {1,2}, {3,4}, [1] = {5,6} }; // 9.3 9.4 + dummy_struct dsf[] = { [0] = 1 }; // 9.5 + dummy_struct dsg = { .a = {0}, .b = {0} }; + dummy_struct dsh[2][2] = { { {.a = 0, .b = {0}}, { 0 } }, { { 0 }, {.a = 0, .b = {0}}} }; + + // Struct with fields of unknown type + struct_with_unknown_fields ufa = { 1, { 1, 2 }, { 1, 2 } }; + struct_with_unknown_fields ufb = { 1, 1, 2 }; // 9.2 + struct_with_unknown_fields ufc[2] = { {1, { 1, 2 }, { 1, 2 } }, + { 2, { 1, 2 }, { 1, 2 } } }; + struct_with_unknown_fields ufd[2][2] = { {1, { 1, 2 }, { 1, 2 } }, // 9.2 9.3 + { 2, { 1, 2 }, { 1, 2 } } }; + struct_with_unknown_fields ufe[2] = { 1, { 1, 2 }, { 1, 2 }, // 9.2 9.3 + 2, { 1, 2 }, { 1, 2 } }; + struct_with_unknown_fields uff[3] = { { 1, { 1, 2 }, { 1, 2 }}, // 9.3 9.4 + {2, { 1, 2 }, { 1, 2 }}, + [1] = { 2, { 1, 2 }, { 1, 2 }} }; + + // Obsolete initialization syntax for GCC + struct1 os1 = { i1: 1, i2: 2 }; // 10.4 13.4 +} + +static void misra_9_2(void) { + union misra_9_2_union { // 19.2 + char c; + struct1 i; + } u = { 3 }; // 19.2 +} + +static void misra_9_5(void) { + char a[] = { 1, 2, 3 }; + char b[] = { [2] = 5 }; // 9.5 + char c[] = { 1, [1] = 5 }; // 9.5 + char d[] = { [1] = 2, [0] = 1 }; // 9.5 + + char e[][2] = { { 1, 2 }, { 3, 4 } }; + char f[][2] = { [1] = { 3, 4 } }; // 9.5 + char g[][2] = { { 1, 2 }, [1] = { 3, 4 } }; // 9.5 + char h[][2] = { [1] = { 1, 2 }, [0] = { 3, 4 } }; // 9.5 +} + +typedef char misra_10_1_char_t; +#define MISRA_10_1_CHAR char +static void misra_10_1(uint32_t u, char c1, char c2, uint8_t u8) { + int32_t i; + char c; + enum { E1 = 1 }; + i = 3 << 1; // 10.1 + i = (u & u) << 4; // no-warning + c = c1 & c2; // 10.1 + c = c1 << 1; // 10.1 + i = c1 > c2; // 10.3 + i = E1 + i; // no-warning + + char ch1 = 'a'; + char ch2 = 'b'; + char ch3; + ch3 = ch1 & ch2; // 10.1 + + misra_10_1_char_t ct1 = 'a'; + misra_10_1_char_t ct2 = 'b'; + misra_10_1_char_t ct3; + ct3 = ct1 & ct2; // 10.1 + + MISRA_10_1_CHAR cd1 = 'a'; + MISRA_10_1_CHAR cd2 = 'b'; + MISRA_10_1_CHAR cd3; + cd3 = cd1 & cd2; // 10.1 + + uint8_t temp1 = u8 & 0x42U; // no-warning +} +static void misra_10_1_ternary(void) +{ + int a; + uint8_t ui8; + uint16_t ui16; + int8_t i8; + int16_t i16; + + a = ui16 << ui16; // 10.6 + a = ui16 << (get_bool(42) ? ui16 : ui16); + a = ui16 << (get_bool(42) ? ui16 : (get_bool(34) ? ui16 : ui16)); + a = ui16 << (get_bool(42) ? (get_bool(34) ? ui16 : ui16) : ui16); + a = ui16 << (get_bool(42) ? i16 : (get_bool(34) ? ui16 : ui16)); // 10.1 10.4 + a = ui16 << (get_bool(42) ? (get_bool(34) ? ui16 : i16) : ui16); // 10.1 10.4 + a = ui16 << (get_bool(42) ? (get_bool(34) ? ui16 : ui16) : i16); // 10.1 10.4 + a = ui16 << (get_bool(42) ? (get_bool(34) ? ui16 : ui8) : ui8); // 10.4 + a = ui16 << (get_bool(42) ? (get_bool(34) ? i16 : ui8) : ui8); // 10.1 10.4 + a = (get_bool(42) ? (get_bool(34) ? ui16 : ui8) : ui8) << ui16; // 10.4 + a = (get_bool(42) ? (get_bool(34) ? i16 : ui8) : ui8) << ui16; // 10.1 10.4 + a = (get_bool(42) ? (get_bool(34) ? ui16 : i8) : ui8) << ui16; // 10.1 10.4 + a = (get_bool(42) ? (get_bool(34) ? ui16 : ui8) : i8) << ui16; // 10.1 + a = (get_bool(42) ? (get_bool(34) ? ui16 : ui8) : ui8) << (get_bool(19) ? ui16 : ui8); // 10.4 + a = (get_bool(42) ? (get_bool(34) ? i16 : ui8) : ui8) << (get_bool(19) ? ui16 : ui8); // 10.1 10.4 + a = (get_bool(42) ? (get_bool(34) ? ui16 : ui8) : ui8) << (get_bool(19) ? i16 : ui8); // 10.1 10.4 +} + +static void misra_10_2(void) { + uint8_t u8a = 0; + char cha = 0; + int8_t s8a = 0; + int16_t s16a = 0; + float f32a = 0.0; + char res; + + res = '0' + u8a; // Convert u8a to digit + res = s8a + '0'; + res = cha - '0'; + res = '0' - s8a; + res = cha + ':'; // 10.2 + + res = s16a - 'a'; // 10.2 10.3 10.4 + res = '0' + f32a; // 10.2 10.4 + + // 10481 - crash + char buf[1] = {'f'}; + x = buf[0] - '0'; +} + +static void misra_10_3(uint32_t u32a, uint32_t u32b) { + uint8_t res; + res = u32a + u32b; // 10.3 + res = (uint16_t)(2U + 3U); // 10.3 10.8 + res = 2U + 3U; // no warning, utlr=unsigned char + res = 0.1f; // 10.3 + const char c = '0'; // no-warning + bool b = true; // no-warning + uint32_t u = UINT32_C(10); // no-warning +} + +static void misra_10_4(u32 x, s32 y) { + z = x + 3; // 10.4 + enum misra_10_4_enuma { misra_10_4_A1, misra_10_4_A2, misra_10_4_A3 } a; + enum misra_10_4_enumb { misra_10_4_B1, misra_10_4_B2, misra_10_4_B3 }; + if ( misra_10_4_B1 > misra_10_4_A1 ) //10.4 + { + ; + } + z = x + y; //10.4 + z = (a == misra_10_4_A3) ? x : y; //10.4 + z = (a == misra_10_4_A3) ? y : y; // no-warning + + // #10499 + const char buf[10] = {0}; + if ('0' == buf[x]) // no-warning + { + } + + const struct foo_s{ + int t; + char buf[2]; + } cmd = {0}; + if ('\0' == cmd.buf[0]) //no-warning + { + } + + // #10652 + char c; + if ((char)'1' == c) {} // no warning + if ((unsigned char)'1' == c) {} //10.4 + if ((signed char)'1' == c) {} //10.4 +} + +static void misra_10_5(uint16_t x) { + // bool + res = (uint16_t) (x > 10u); // 10.5 + res = (bool) 1u; // no-warning + + // char <=> float + res = (char) 0.1f; + res = (float) 'x'; +} + +struct misra_10_6_s { + unsigned int a:4; +}; +static void misra_10_6(u8 x, char c1, char c2) { + u16 y1 = x+x; // 10.6 + u16 y2 = (0x100u - 0x80u); // rhs is not a composite expression because it's a constant expression + int b = (y2 == y2) ? 0 : 1; // no-warning + u16 z = ~u8 x ;//10.6 + s32 i = c1 - c2; // 10.3 + struct misra_10_6_s s; + s.a = x & 1U; // no-warning (#10487) +} +static void misra_10_6_1(uint32_t *a, uint16_t b, uint16_t c) +{ + *a = b + c ; // 10.6 +} + +static void misra_10_7_f1(struct Timer *pSelf, uint32_t interval_ms); +static void misra_10_7(uint16_t u16a, uint16_t u16b) { + uint32_t u32a = 100u; + res = u32a * u16a + u16b; // 12.1 no-warning + res = (u32a * u16a) + u16b; // no-warning + res = u32a * ( ( uint32_t ) u16a + u16b ); // no-warning + res = u32a * (u16a + u16b); // 10.7 + u32a *= u16a + u16b; // 10.7 + u32a = ((uint32_t)4 * (uint32_t)2 * (uint32_t)4 ); // no-warning (#10488) + dostuff(&t, (2*60*1000)); // no-warning +} + +static void misra_10_8(u8 x, s32 a, s32 b) { + y = (u16)x; + y = (u16)(x+x); // 10.8 + y = (u16) (a + b) //10.8 +} + +int (*misra_11_1_p)(void); // 8.4 +void *misra_11_1_bad1 = (void*)misra_11_1_p; // 11.1 8.4 + +// #12172 +typedef void (*pfFunc_11_1)(uint32_t some); +extern pfFunc_11_1 data_11_1[10]; +void func_11_1(pfFunc_11_1 ptr){ //8.4 + data_11_1[index] = ptr; // no-warning +} + +struct misra_11_2_s; +struct misra_11_2_t; + +static struct misra_11_2_s * sp; +static struct misra_11_2_t * tp = sp; // 11.2 + +struct Fred {}; struct Wilma {}; +static void misra_11_3(u8* p, struct Fred *fred) { + x = (u64*)p; // 11.3 + struct Wilma *wilma = (struct Wilma *)fred; // 11.3 +} + +typedef struct { uint32_t something; } struct_11_4; +#define A_11_4 ((struct_11_4 *)0x40000U) // 11.4 + +static void misra_11_4(u8*p) { + u64 y = (u64)p; // 11.4 + u8 *misra_11_4_A = ( u8 * ) 0x0005;// 11.4 + s32 misra_11_4_B; + u8 *q = ( u8 * ) misra_11_4_B; // 11.4 + dummy = A_11_4->something; // no-warning +} + +static void misra_11_5(void *p) { + u16 *p16; + x = (u8 *)p; // 11.5 + p16 = p; // 11.5 +} + +static intptr_t get_intptr_constant(void) { return 456; } +static void misra_11_6(void) { + void *p; + struct { + int i; + } s = { .i = 7 }; + p = (void*)123; // 11.6 + x = (u64)p; // 11.6 + p = (void*)(1+1);// 11.6 + p = (void*)get_intptr_constant(); // 11.6 + p = (void*)s.i; // 11.6 + p = ( void * )0; // no-warning + (void)p; // no-warning + // # 12184 + p = (void*)0U; // no-warning +} + + +static void misra_11_7(int *p, float f) { + x = ( float ) p; //11.7 + y = ( int * ) f; //11.7 +} + +static void misra_11_7_extra(int *p, float f, bool b) { + (void) p; // no-warning + (void) f; // no-warning + (void) b; // no-warning +} + +static void misra_11_8_const(const char *str) {(void)str;} +static char * misra_11_8(const char *str) { + (void)misra_11_8_const(str); // no-warning + return (char *)str; // 11.8 +} + +#define MISRA_11_9_NULL_1 (1-1) +#define MISRA_11_9_NULL_2 ( void * ) 0 +#define MISRA_11_9_NULL_3 NULL +static void misra_11_9(void) { + int *p1 = (5-5); //11.9 + int *p2 = MISRA_11_9_NULL_2 ; // no-warning + int *p3 = MISRA_11_9_NULL_3 ; // no-warning + if ( p1 == MISRA_11_9_NULL_1 ) //11.9 + { + ; + } + +} + + +static void misra_12_1(void) { + sz = sizeof x + y; // 12.1 + a = (b * c) + d; + a = b << c + d; // 12.1 +} + +static void misra_12_2(u8 x) { + a = x << 8; // 12.2 +} + +static int misra_12_3_v1 = 0, misra_12_3_v2; // 12.3 +static int misra_12_3_v3, misra_12_3_v4; // 12.3 +enum misra_12_3_e1 { M123A1, M123B1, M123C1 }; +enum misra_12_3_e2 { M123A2 = 3, M123B2 = 4, M123C2 }; +typedef enum misra_12_3_e3 { M123A3 , M123B3, M123C3 } misra_12_3_e3_t; +typedef enum { M123A4 , M123B4, M123C4 } misra_12_3_e4_t; +struct misra_12_3_s1 { int a; int b; int c, d; }; // 12.3 +static struct misra_12_3_s1 misra_12_3_s1_inst = { + 3, + 4, 5, + 6, // no warning +}; +typedef struct misra_12_3_s2 { int a; int b; int c, d; } misra_12_3_s2_t; // 12.3 +typedef struct { int a; int b; int c, d; } misra_12_3_s3_t; // 12.3 +static void misra_12_3_fn1(int, int); static int misra_12_3_v5, misra_12_4_v6; // 12.3 8.2 +static void misra_12_3_fn2(int a, int b) // 2.7 +{ int d, e; } // 12.3 +static int misra_12_3_fn3(int a, int b) { return a+b;} static int misra_12_3_v5, misra_12_4_v6; // 12.3 +static void misra_12_3_fn4(const uint32_t value, uint8_t * const y) {} // 2.7 +static void misra_12_3_fn5(const uint32_t * const, const uint8_t) {} // 2.7 8.2 +extern void misra_12_3_fn6(const uint32_t value, uint8_t * const y); +extern uint32_t misra_12_3_fn7(const uint32_t * const, const uint8_t); // 8.2 +#define MISRA_12_3_FN3_1(A, B) (misra_12_3_fn3(A, B)) +#define MISRA_12_3_FN3_2(A, B) (misra_12_3_fn3(A, \ + B)) +#define MISRA_12_3_FN3_2_MSG(x) x, fflush(stderr) +static void misra_12_3(int, int, int); // 8.2 +void misra_12_3(int a, int b, int c) { + int a1, a2; // 12.3 + int a3; int a4; // no warning + int a5 = 9, a6; // 12.3 + int a7, a8 = 11; // 12.3 + int a9 = foo(), a10; // 12.3 + int a11 = a = b = c; // 17.8 + + struct s1 {int a, b;}; int a12, a13; // 12.3 + int a14, a15; misra_12_3_fn3(a14, a15); // 12.3 17.7 + ; int a16, a17; // 12.3 + int a18; int a19, a20; // 12.3 + int a21, a22; int a23; // 12.3 + int a24, // 12.3 + a25; + int a26 + , a27; // 12.3 + int a28 + , // 12.3 + a29; + + struct misra_12_3_s2 a30 = {1, 2}, a31; // 12.3 + struct misra_12_3_s2 a32, a33; // 12.3 + struct misra_12_3_s2 a34, a35 = {1, 2}, a36; // 12.3 + + // cppcheck-suppress uninitStructMember + int a37 = MISRA_12_3_FN3_1(a34, a35), a38; // 12.3 + int a39, a40 = MISRA_12_3_FN3_1(a34, a35); // 12.3 + int a41 = MISRA_12_3_FN3_2(a34, a35), a42; // 12.3 + int a43, a44 = MISRA_12_3_FN3_2(a34, a35); // 12.3 + + MISRA_12_3_FN3_2_MSG(fprintf(stderr, "test\n")); // 12.3 + + f((1,2),3); // TODO + + // third clause: 2 persistent side effects instead of 1 (14.2) + for (i=0; i<10; i++, j++){} // 12.3 14.2 + for (int i = 0, p = &a1; // 12.3 14.2 + i < 42; + ++i, ++p ) // 12.3 + {} + + // No false positives in local and extern function calls + misra_12_3_fn4(misra_12_3_fn5(&a1, 32), &a1); + misra_12_3_fn4(misra_12_3_fn7(&a1, 32), &a1); + misra_12_3_fn6(misra_12_3_fn5(&a1, 32), &a1); + misra_12_3_fn6(misra_12_3_fn7(&a1, 32), &a1); + misra_12_3_fn7(maxlen, fn(va, unsigned long), false); + misra_12_3_fn8(maxlen, (unsigned long)((uintptr_t)fn(va, void*)), false); + + const struct fun_t + { + int64_t x; + uint32_t y; + } moreFun[2U] = + { + { 900000000000000LL, 0x20000UL }, + { 450000000000000LL, 0x10000UL } + }; +} + +#define MISRA12_4a 2000000000u +#define MISRA12_4b 4000000000u +static void misra_12_4(uint8_t t) { + x = 123456u * 123456u; // 12.4 + x = MISRA12_4a + MISRA12_4b; // 12.4 + x = 0u - 1u; // 12.4 + x = t ? 0u : (0u-1u); // 12.4 + x = (0u==0u) ? 0u : (0u-1u); + x = (0u!=0u) ? 0u : (0u-1u); // 12.4 + x = (0u==0u) ? 0u : (2*(0u-1u)); // 10.4 +} + +struct misra_13_1_t { int a; int b; }; +uint8_t misra_13_1_x = 0; // 8.4 +static void misra_13_1_bar(uint8_t a[2]); +static void misra_13_1(int *p) { + volatile int v; + int a1[3] = {0, (*p)++, 2}; // 13.1 + int a2[3] = {0, ((*p) += 1), 2}; // 13.1 + int a3[3] = {0, ((*p) = 19), 2}; // 13.1 + misra_13_1_bar((uint8_t[2]){ misra_13_1_x++, misra_13_1_x++ } ); // 13.1 + int b[2] = {v,1}; + struct misra_13_1_t c = { .a=4, .b=5 }; // no fp + volatile int vv; + int v = 42; + + int a1[3] = { 0, (*p)++, 2 }; // 13.1 + int a2[2] = { [0]=19, [1]=42 }; + int a3[2] = { [0]=v, [1]=42 }; + int a4[2] = { [0]=0, [1]=(v+=1) }; // 13.1 + int a5[2] = { [0]=0, [1]=(v+1) }; + int a6[2] = { v, 1 }; + int a6[2] = { v >>= 3 }; // 13.1 9.3 + int a7[2] = { v, ++v }; // 13.1 + int a8[1] = { vv }; // TODO: 13.1 Trac #9504 + + struct misra_13_1_t c01 = { 4, 5 }; + struct misra_13_1_t c02 = { 16 == 1, 5+1 }; + struct misra_13_1_t c03 = { (v += 1), 5+1 }; // 13.1 + struct misra_13_1_t c04 = { v <<= 1, 5+1 }; // 13.1 + struct misra_13_1_t c05 = { v += 1, 5+1 }; // 13.1 + struct misra_13_1_t c06 = { (4.5 + 0.5), 1 }; + struct misra_13_1_t c07 = { (4.5 + 0.5), ++v }; // 13.1 + struct misra_13_1_t c08 = { (int)4.5, 5 }; + struct misra_13_1_t c09 = { (int)4.5+(*p)++, 5 }; // 13.1 + struct misra_13_1_t c10 = { (int)4.5, (*p)++ }; // 13.1 + struct misra_13_1_t c11 = { .a=4+1, .b=3/3 }; + struct misra_13_1_t c12 = { .a=4, .b=5 }; + struct misra_13_1_t c13 = { (*v)<<=(int)(4.5), .b=5 }; // 13.1 + struct misra_13_1_t c14 = { (*p)/=(int)(4.5) }; // 13.1 +} + +// Large arrays for R13.1. Size exceeds default Python's max recursion depth. +static uint8_t misra_13_1_large_ok[1024] = { +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; +static uint8_t misra_13_1_large_bad[1024] = { // 13.1 +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, i++, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static void misra_13_3(void) { + x = y++; // 13.3 +} + +#define STRING_DEF_13_4 "This is a string" + +typedef struct +{ + char string[sizeof(STRING_DEF_13_4)]; +} s13_4_t; + +static s13_4_t s13_4 = +{ + .string = STRING_DEF_13_4 // no-warning +}; + +static void misra_13_4(int x, int z) { + int y; + if (x != (y = z)) {} // 13.4 + else {} +} + +static void misra_13_5(void) { + int x = 0; + int y = 0; + if (x && (y++ < 123)){} // 13.5 + if (x || ((y += 19) > 33)){} // 13.5 + if (x || ((y = 25) > 33)){} // 13.5 13.4 + if (x || ((--y) > 33)){} // 13.5 + else {} +} + +static void misra_13_6(void) { + int a = sizeof(x|=42); // 13.6 + a = sizeof(--x); // 13.6 13.3 + return sizeof(x++); // 13.6 +} + +static void misra_14_1(void) { + for (float f=0.1f; f<1.0f; f += 0.1f){} // 14.1 + float a = 0.0f; + int b = 10; + while ((a<100.0f) || (b > 100)) //14.1 + { + a++; + } + do + { + ; + } while ( a < 10.0f ); // no-warning + +} + +static void misra_14_2_init_value(int32_t *var) { + *var = 0; +} +static void misra_14_2_init_value_1(int32_t *var); + +static void misra_14_2_fn1(bool b) { + for (;i++<10;) {} // 14.2 + for (;i<10;dostuff()) {} // 14.2 + int32_t g = 0; + int g_arr[42]; + g += 2; // no-warning + for (int32_t i2 = 0; i2 < 8; ++i2) { + i2 += 2; // 14.2 + i2 |= 2; // 14.2 + g += 2; + i2 ^= 2; // 14.2 + if (i2 == 2) { + g += g_arr[i2]; // cppcheck-suppress legacyUninitvar + } + misra_14_2_init_value(&i2); // TODO: Fix false negative in function call + } + int i1; + int i2; + for (misra_14_2_init_value(&i1); i1 < 10; ++i1) {} // no-warning + for (misra_14_2_init_value_1(&i2); i2 < 10; ++i2) {} // no-warning + for (misra_14_2_init_value_2(&i2); i2 < 10; ++i2) {} // no-warning + + bool abort = false; + for (i = 0; (i < 10) && !abort; ++i) { // 14.2 as 'i' is not a variable + if (b) { + abort = true; + } + } + for (int i = 0; (i < 10) && !abort; ++i) { // no warning + if (b) { + abort = true; + } + } + for (;;) {} // no-warning + + int x = 10; + for (int i = x; i < 42; i++) { + x++; // no warning + } + // 1st clause item 2 + loop counter modification + for(x = 0; x < 10; x++) { + x++; // 14.2 + } + // third clause: 2 persistent side effects instead of 1 (14.2) + for (int i = 0; i < 10; i++, x++) { // 12.3 14.2 + } + + // 2 loop counters, there shall be only 1 + for(int i=0, j=0; (i<10) && (j<10); i++, j++) { // 12.3 14.2 + } + + for (int i = (x - 3); i < 42; i++) { + x ^= 3; // no warning + } + + for (int i = 0, j = 19; i < 42; i++) { // 12.3 14.2 + i += 12; // 14.2 + j /= 3; // TODO: 14.2 + } + + for (int i = 0; i < 19; i++) { + for (int j = 0; j < 42; j++) { + i--; // 14.2 + for (int k = j; k > 5; k--) { + i++; // 14.2 + for (int h = 35; h > 5; k++) // 14.2 + {} + } + } + } + + static struct + { + uint16_t block; + bool readSuccessful; + int32_t i; + } + opState; + for (opState.block = 0U; opState.block < 10U; opState.block++) {;} //no-warning + + for (misra_14_2_init_value(&opState.i); opState.i < 10; ++opState.i) {} //no-warning +} +static void misra_14_2_fn2(void) +{ + int y = 0; + + // Handle cases when i is not treated as loop counter according MISRA + // definition. + for (int i = 0, j = 19; y < 10, --j > 10; y++, j--) { // 14.2 12.3 + i++; // no warning + } + for (int i = 0, j = 19; y < 10, --j > 10; y++, j--) { // 14.2 12.3 + i++; // no warning + } + // 1st clause is not empty, but is not used in 2nd and 3rd clause + for (int i = 0; y < 10; y++) { // 14.2 + i++; // no warning + } + for (; y < 10; y++) {} // without 1st clause, no error + for (int i = 0; i < 10; y++) { // 14.2 + i++; // no warning + } + for (int i = 0; y < 10; i++) { // 14.2 + i++; // no warning + } + for (int i = 0; i < 10; (y+=i)) { // 14.2 + i++; // no warning + } + + // i is a loop counter according MISRA definition + for (int i = 0; i < 10; i++) { + i++; // 14.2 + if (++i > 5) { // 14.2 + break; + } + } + for (int i = 0; i < 10; (i+=42)) { + i++; // 14.2 + } + for (int i = 0; i < 10; (i|=y)) { + i++; // 14.2 + } + + return 0; +} + +struct { + unsigned int x:1; + unsigned int y:1; +} r14_4_struct; // 8.4 +static void misra_14_4(bool b) { + if (x+4){} //config + else {} + + if (b) {} + else {} + + if (r14_4_struct.x) {} + + // #12079 + if (z) {} //config +} + +// #12417 +struct bar_12417{ int a; }; +static int foo_12417(void){ + int ret = 1; + if (sizeof(struct bar_12417) == 0U){ // no warning for misra-config + ret = 0; + } + return ret; +} + +static void misra_15_1(void) { + goto a1; // 15.1 +a1: +} + +static void misra_15_2(void) { +label: + goto label; // 15.2 15.1 +} + +static void misra_15_3(int a) { + int x = 0; + int y; + if (x!=0) { + goto L1; // 15.3 15.1 + if (y!=0) { + L1: + } else {} + } else {} + + switch (x) { + case 0: + if (x == y) { + goto L2; // 15.3 15.1 + } + goto L2; // 15.3 15.1 + L3: + foo(); + if (a == 0x42) { + // Compliant: + goto L3; // 15.1 15.2 + } + break; + case 1: + y = x; + L2: + ++x; + break; + default: + break; + } +} + +static void misra_15_4(void) { + misra_15_4_label: + return; + + int x = 0; + int y = 0; + int z = 0; + + // Break on different loop scopes + for (x = 0; x < 42; ++x) { + if (x==1) { + break; + } + for (y = 0; y < 42; ++y) { // 15.4 + if (y==1) { + break; + } + if (y==2) { + break; + } + for (z = 0; y < 42; ++z) { // 14.2 + if (z==1) { + break; + } + } + } + } + + // Break in while loop + do { // 15.4 + if(x == 1) { + break; + } + if(x == 2) { + break + } + x++; + } while(x != 42); + + // Break and goto in same loop + for (int x = 0; x < 10; ++x) { // 15.4 + if (x == 1) { + break; + } + if (x == 2) { + goto misra_15_4_label; // 15.1 15.2 + } + } + + // Inner loop uses goto + for (x = 0; x < 42; ++x) { // 15.4 + if (x==1) { + break; + } + for (y = 0; y < 42; ++y) { + if (y == 1) { + goto misra_15_4_label; // 15.1 15.2 + } + } + } + + // Allow switch with multiple breaks inside loop + for (x = 0; x < 42; ++x) { + switch (x) { + case 1: + break; + default: + break; + } + } + + // Do not allow switch with multiple gotos inside loop + for (x = 0; x < 42; ++x) { // 15.4 + switch (x) { + case 1: + goto misra_15_4_label; // 15.1 15.2 + break; + default: + goto misra_15_4_label; // 15.1 15.2 + break; + } + } +} + +static int misra_15_5(int x) { + if (x!=0) { + return 1; // 15.5 + } else {} + return 2; +} + +static void misra_15_6(int x) { + if (x!=0); // 15.6 + else{} + +#if A>1 // 20.9 + (void)0; +#endif + +#if A > 0x42 // 20.9 + if (true) { + (void)0; + } + if (true) +#endif + { (void)0; } // no-warning + + do {} while (x<0); // no-warning +} + +static void misra_15_6_fp(void) +{ + uint8_t value = 0U; + do // Test + { + value++; + } + while (value < 2U); +} + +#if defined(M_20_9) && M_20_9 > 1 // no-warning (#10380) +#endif + +static void misra_15_7(int x, int a, int b) { + uint32_t var = 0; + uint32_t var2 = 0; + + if (x!=0){} // no-warning + if (x!=0){} else if(x==1){} // 15.7 + if (x!=0){} else if(x==1){}else{;} // no-warning + + if (x!=0) + { + } + else + { + var = 5u; + + if (var != 5u) + { + var2 = 10u; + } // no-warning + } + + if (a==2) {} else if (b==4) {} // 15.7 + if (a==2) {} else { if (b==4) {} } // no-warning +} + +static void misra_16_1(int32_t i) { + switch (i) { + int8_t x; // 16.1 + default: // 16.3 16.5 + break; + if (i != 18) {} // 16.1 + case 1: // 16.3 + break; + } +} + +static void misra_16_2(int y) { + switch (x) { + default: + break; + case 1: + while (y>4) { + case 2: break; // 16.2 + } + break; + } +} + +static void misra_16_3(int b) { + int a; + switch (x) { + case 1: + case 2: + a=1; + case 3: // 16.3 + a=2; + // fallthrough + case 5: + break; + case 7: + a=3; + [[fallthrough]]; + case 8: + a=4; + break; + case 9: + if (a==b) { + break; + } + case 10: // 16.3 + return; // 15.5 + case 11: + { break; } + case 12: + default: break; + } + + switch (x) { + case 1: // comment 1 + { + a = 1; + break; + } + case 2: // comment 2 + { + a = 2; + break; + } + default: + { + break; + } + } + + switch (x) { + case 1: + break; + default: // 16.5 + x++; + case 19: // 16.3 + break; + case 20: + x + 2; + x + 3; + break; + } + switch (x) { // 16.6 + default:; + } // 16.3 + + switch (x) { default:; } // 16.3 16.6 + + switch (x) { + case 20: + x + 2; + x + 3; + break; + case 21: + x + 2; + x + 3; + break; + default: + ; + } // 16.3 + + switch (x) { // 16.4 16.6 + case 1: + x++; + break; + case 2: + x++; + } // 16.3 + + #define M_16_3(a,b,default) { (a), (b), (default) }, +} + +static void misra_16_4(void) { + switch (x) { // 16.4 + case 1: + break; + case 2: + break; + } +} + +static void misra_16_5(void) { + switch (x) { + case 1: + break; + default: // 16.5 + break; + case 2: + break; + } +} + +static void misra_16_6(void) { + switch (x) { // 16.6 + default: + break; + } + + switch (x) { + case 1: break; + case 2: break; + default: break; + } + + // No 16 6 in this switch: + switch (x) { + case A: return 1; // 15.5 + case B: return 1; // 15.5 + case C: return 1; // 15.5 + default: return 2; // 15.5 + } +} + +static void misra_16_7(void) { + switch (x != 123) { // 16.7 + case 1: + break; + default: + break; + } +} + +static void misra_17_1(void) { + va_list(); // 17.1 17.7 + va_arg(); // 17.1 + va_start(); // 17.1 + va_end(); // 17.1 + va_copy(); // 17.1 +} + +static void misra_17_2_ok_1(void) { ; } +static void misra_17_2_ok_2(void) { + misra_17_2_ok_1(); // no-warning +} +static void misra_17_2_1(void) { + misra_17_2_ok_1(); // no-warning + misra_17_2_1(); // 17.2 + misra_17_2_ok_2(); // no-warning + misra_17_2_1(); // 17.2 +} +static void misra_17_2_2(void) { + misra_17_2_3(); // 17.2 +} +static void misra_17_2_3(void) { + misra_17_2_4(); // 17.2 +} +static void misra_17_2_4(void) { + misra_17_2_2(); // 17.2 + misra_17_2_3(); // 17.2 +} + +static void misra_17_2_5(void) { + misra_17_2_ok_1(); // no-warning + misra_17_2_5(); // 17.2 + misra_17_2_1(); // no-warning +} + +bool (*dostuff)(); //8.2 8.4 +static void misra_17_3(void) { + if (dostuff()) {} +} + +static void misra_config(const char* str) { + if (strlen(str) > 3){} //10.4 + if (sizeof(int) > 1){} //10.4 +} + +static void misra_17_6(int x[static 20]) {(void)x;} // 17.6 + +static int calculation(int x) { return x + 1; } +static void misra_17_7(void) { + calculation(123); // 17.7 + int (*calc_ptr)(int) = &calculation; + calc_ptr(123); // 17.7 + int y = calc_ptr(123); +} + +static void misra_17_8(int x) { + x = 3; // 17.8 +} + +static void misra_18_4(void) +{ + int b = 42; + int *bp = &b; + bp += 1; // 18.4 + bp -= 2; // 18.4 + int *p = bp - 2; // 18.4 + int *ab = &b + 1; // 18.4 + p = bp + p; // 18.4 + bp = 1 + p + 1; // 18.4 + b += 19; // no-warning + b = b + 9; // no-warning +} + +static void misra_18_5(void) { + int *** p; // 18.5 +} + +struct { + uint16_t len; + struct { + uint8_t data_1[]; // 18.7 + } nested_1; + struct named { + struct { + uint8_t len_1; + uint32_t data_2[]; // 18.7 + } nested_2; + uint8_t data_3[]; // 18.7 + } nested_3; +} r18_7_struct; // 8.4 +struct { + uint16_t len; + int (*array_param_func_ptr)(char const *argv[], int argc); // no-warning + uint8_t data_1[ 19 ]; + uint8_t data_2[ ]; // 18.7 +} r18_7_struct; // 8.4 + +typedef enum { + R18_8_ENUM_CONSTANT_0, + R18_8_ENUM_CONSTANT_1, +} r18_8_enum; + +static void misra_18_8(int x) { + int buf1[10]; + int buf2[sizeof(int)]; + int vla[x]; // 18.8 + // #9498 + int vlb[y]; // config + static const unsigned char arr18_8_1[] = UNDEFINED_ID; + static uint32_t enum_test_0[R18_8_ENUM_CONSTANT_0] = {0}; +} + +union misra_19_2 { }; // 19.2 + +#include "notfound.h" // 20.1 + +#define int short // 20.4 +#define inline "foo" // no warning in C90 standard +#undef X // 20.5 + +#define M_20_7_1(A) (A+1) // 20.7 +#define M_20_7_2(A,B) (1+AB+2) // no warning +#define M_20_7_3(A) ((A)+A) // 20.7 +#define M_20_7_4(A) x##A // 20.10 this test was written to see there are not FPs +#define M_20_7_5(A,B) f(A, B) // no warning +#define M_20_7_6(x) a ## x = ( x ) // 20.10 +#define M_20_7_7(x) a = # x // 20.10 +#define M_20_7_8(x, fn) a = fn ( # x ) // 20.7 20.10 +#define M_20_7_9(x, fn) a = (fn) ( # x ) // 20.10 +#define M_20_7_10(A, B) (A " " B) +#define M_20_7_11(A, B, C) (A " " B " " C) +#define M_20_7_12(A, B, C) (A " " B + C) // 20.7 +#define M_20_7_13(A, B, C) (A + B " " C) // 20.7 +#define M_20_7_14(STRING1, STRING2) (STRING1 " " STRING2) +#define M_20_7_15(STRING1, STRING2, STRING3) (STRING1 " " STRING2 " " STRING3) +#define M_20_7_16(STRING1, STRING2, STRING3) (STRING1 " " STRING2 + STRING3) // 20.7 +#define M_20_7_17(STRING1, STRING2, STRING3) (STRING1 + STRING2 " " STRING3) // 20.7 + +// Compliant: M is a structure member name, not an expression +struct { int a; } struct_20_7_s; // 8.4 +#define M_20_7_6(M) struct_20_7.M +#define M_20_7_7(M) (struct_20_7).M + +#define MUL(a ,b ) ( a * b ) // 20.7 + +#if __LINE__ // 20.8 +#elif 2+5 // 20.8 +#elif 2-2 +#endif + +#if A // 20.9 +#elif B || C // 20.9 +#endif + +#define M_20_10(a) (#a) // 20.10 + +#define M_20_11(a) # a ## 1 // 20.11 20.10 + +#define M_20_12_AA 0xffff +#define M_20_12_BB(x) (x) + wow ## x // 20.12 20.10 +misra_20_12 = M_20_12_BB(M_20_12_AA); + +#else1 // 20.13 + +#ifdef A +# define somethingis 5 // no warning +# define func_20_13(v) (v) // no warning +#else +# definesomethingis 6 // 20.13 +# def fun_2013(v) () // 20.13 +#endif + +#define _Incompatible 0xdeadbeef // 21.1 +#define __Incompatible 0xdeadbeef // 21.1 +#define __starts_with_lower 0xdeadbeef // 21.1 +#define __MY_HEADER_ // 21.1 +#define _macro_starts_with_lower 1 // no warning +static int _file_scope_id_21_1 = 42; // no warning +static int _file_scope_id_21_1_fn(void) { return 42; } // no warning +static int misra_21_1(void) { + int _a = 42; // no warning: only directives affected + errno = EINVAL; // no warning + _a ++; // no warning + _exit(1); // no warning + return _a; // no warning +} +static int _misra_21_1_2(void); // no warning +#define errno 11 // 21.1 +#undef errno // 20.5 + +#define __BUILTIN_SOMETHING 123 // 21.2 21.1 +extern void *memcpy ( void *restrict s1, const void *restrict s2, size_t n ); // 21.2 8.14 + +static void misra_21_3(void) { + p1=malloc(10); // 21.3 + p2=calloc(10); // 21.3 + realloc(10); // 21.3 + free(p1); // 21.3 +} + +static void misra_21_7(void) { + (void)atof(str); // 21.7 + (void)atoi(str); // 21.7 + (void)atol(str); // 21.7 + (void)atoll(str); // 21.7 +} + +static void misra_21_8(void) { + abort(); // 21.8 + (void)getenv("foo"); // 21.8 + exit(-1); // 21.8 +} + +static void misra_21_9(void) { + (void)bsearch(key,base,num,size,cmp); // 21.9 + qsort(base,num,size,cmp); // 21.9 +} + +static void misra_21_12(void) { + int rc; + fexcept_t f; // 21.12 + rc = feclearexcept(1); // 21.12 + rc = fegetexceptflag(&f, 1); // 21.12 + rc = feraiseexcept(1); // 21.12 + rc = fesetexceptflag(&f, 1); // 21.12 + rc = fetestexcept(1); // 21.12 +} + +static void misra_21_14(uint8_t *x) { + (void)strcpy(x, "123"); + (void)memcmp(x, y, 100); // 21.14 + (void)memcmp("abc", y, 100); // 21.14 21.16 +} + +static void misra_21_15(uint8_t *x, uint16_t *y) { + (void)memcpy(x, y, 10); // 21.15 + (void)memmove(x, y, 10); // 21.15 + (void)memcmp(x, y, 10); // 21.15 +} + +struct misra_21_16_S { int a; int b; }; +static void misra_21_16_f1(struct misra_21_16_S *s1, struct misra_21_16_S *s2) { + (void)memcmp(s1, s2, 10); // 21.16 +} +static void misra_21_16_f2(char *x, char *y) { + (void)memcmp(x, y, 10); // 21.16 +} +typedef enum { R21_16_A, R21_16_B} r21_16_enum; +static void misra_21_16_f3(void) { + int const a[2] = {0}; + int const b[2] = {0}; + (void)memcmp(a, b, 2); // no-warning + uint8_t const c[2] = {0}; + uint8_t const d[2] = {0}; + (void)memcmp(c, d, 2); // no-warning + bool const e[2] = {0}; + bool const f[2] = {0}; + (void)memcmp(e, f, 2); // no-warning + r21_16_enum const g[2] = {0}; + r21_16_enum const h[2] = {0}; + (void)memcmp(g, h, 2); // no-warning + char const i[2] = {0}; + char const j[2] = {0}; + (void)memcmp(i, j, 2); // 21.16 +} + +static void misra_21_19(void) { + char *s = setlocale(LC_ALL,0); // 21.19 + const struct lconv *conv = localeconv (); + conv->decimal_point = "^"; // 21.19 +} + +static void misra_21_20(void) { + const char *res1 = setlocale ( LC_ALL, 0 ); + (void) setlocale ( LC_MONETARY, "French" ); + if (res1) {} // 21.20 14.4 +} + +static void misra_21_21(void) { + (void)system("ls"); // 21.21 +} + +static void misra_22_5(FILE *f) { + int x = *f; // 22.5 + int y = f->pos; // 22.5 +} + +static void misra_22_7(char ch) +{ + if (EOF == ch) {} // 22.7 +} + +static void misra_22_8(void) +{ + (void)strtoll("123", NULL, 10); // 22.8 + if (errno == 0) {} +} + +static void misra_22_9(void) +{ + errno = 0; + (void)strtoll("123", NULL, 10); // 22.9 +} + +static void misra_22_10(void) +{ + errno = 0; + f = atof ( "A.12" ); // 21.7 + if ( 0 == errno ) {} // 22.10 + + errno = 0; + f = strtod ( "A.12", NULL ); + if ( 0 == errno ) {} + + // #10855 + f = strtol(numbuf, 0, (formatHex == 0U) ? 0 : 16); + if (errno != 0) {} + + // #11752 + #define NULL_PTR ((void*)0) + f = strtod(inStr, NULL_PTR); + if(errno != 0) {} +} + +// #12448 +static void check_misra_config(void) +{ + if (sizeof(struct bar) == 0U) {} //no warning + if (sizeof(int abc) == 0U) {} //no warning + if (sizeof(xyz) == 0U) {} //no warning + if (sizeof(const pqr) == 0U) {} //no warning + if (sizeof(const int* const pqrs) == 0U) {} //no-warning +} diff --git a/cppcheck-2.14.0/addons/test/misra/misra-test.cpp b/cppcheck-2.14.0/addons/test/misra/misra-test.cpp new file mode 100644 index 00000000..0aa9a0b9 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra-test.cpp @@ -0,0 +1,25 @@ +// #8441 +class C { + int a; + int b; + C(void) : a(1), b(1) { c; } +}; + +class misra_21_1_C { + public: + misra_21_1_C operator=(const misra_21_1_C &); +}; + +class C2 { +public: + C2(void); +private: + void* f; +}; +C2::C2(void) : f(NULL) {} + +static void test_misra_21_1_crash(void) +{ + auto misra_21_1_C a, b; // 12.3 + a = b; +} diff --git a/cppcheck-2.14.0/addons/test/misra/misra-test.h b/cppcheck-2.14.0/addons/test/misra/misra-test.h new file mode 100644 index 00000000..fe5cbf5c --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra-test.h @@ -0,0 +1,7 @@ +#ifndef MISRA_TEST_H +#define MISRA_TEST_H +struct misra_h_s { int foo; }; +bool test(char *a); // OK +int misra_8_2_no_fp(int a); +void misra_8_4_bar(void); +#endif // MISRA_TEST_H diff --git a/cppcheck-2.14.0/addons/test/misra/misra2012_rules_dummy_ascii.txt b/cppcheck-2.14.0/addons/test/misra/misra2012_rules_dummy_ascii.txt new file mode 100644 index 00000000..8880ca56 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra2012_rules_dummy_ascii.txt @@ -0,0 +1,5 @@ +Appendix A Summary of guidelines +Rule 1.1 +Text of rule 1.1 +Rule 1.2 +Text of rule 1.2 diff --git a/cppcheck-2.14.0/addons/test/misra/misra2012_rules_dummy_utf8.txt b/cppcheck-2.14.0/addons/test/misra/misra2012_rules_dummy_utf8.txt new file mode 100644 index 00000000..5f96010f --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra2012_rules_dummy_utf8.txt @@ -0,0 +1,5 @@ +Appendix A Summary of guidelines +Rule 1.1 +Text of rule 1.1, utf8 test: ∑ +Rule 1.2 +Text of rule 1.2 diff --git a/cppcheck-2.14.0/addons/test/misra/misra2012_rules_dummy_windows1250.txt b/cppcheck-2.14.0/addons/test/misra/misra2012_rules_dummy_windows1250.txt new file mode 100644 index 00000000..89480cc6 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra2012_rules_dummy_windows1250.txt @@ -0,0 +1,5 @@ +Appendix A Summary of guidelines +Rule 1.1 +Text of rule 1.1, windows1250 test: +Rule 1.2 +Text of rule 1.2 diff --git a/cppcheck-2.14.0/addons/test/misra/misra_rules_dummy.txt b/cppcheck-2.14.0/addons/test/misra/misra_rules_dummy.txt new file mode 100644 index 00000000..4ae6057a --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra_rules_dummy.txt @@ -0,0 +1,22 @@ +Appendix A Summary of guidelines +Rule 3.1 Required +R3.1 text. +Rule 4.1 Required +R4.1 text. +Rule 10.4 Mandatory +R10.4 text. +Rule 11.5 Advisory +R11.5 text. +Rule 15.5 Advisory +R15.5 text. +Rule 15.6 Required +R15.6 text. +Rule 17.7 Required +R17.7 text. +Rule 20.1 Advisory +R20.1 text. +Rule 21.3 Required +R21.3 text. +Rule 21.4 +R21.4 text. + diff --git a/cppcheck-2.14.0/addons/test/misra/misra_rules_empty_lines.txt b/cppcheck-2.14.0/addons/test/misra/misra_rules_empty_lines.txt new file mode 100644 index 00000000..2a47bb24 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra_rules_empty_lines.txt @@ -0,0 +1,21 @@ +Appendix A Summary of guidelines + + +Rule 1.1 +Add this rule and parse to next, skipping empty lines. + + + + +Rule 1.2 +Rule text. + +Rule 1.3 +There is 3 rules. + + + + + + + diff --git a/cppcheck-2.14.0/addons/test/misra/misra_rules_multiple_lines.txt b/cppcheck-2.14.0/addons/test/misra/misra_rules_multiple_lines.txt new file mode 100644 index 00000000..24ab0f2a --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra_rules_multiple_lines.txt @@ -0,0 +1,24 @@ +Appendix A Summary of guidelines + +Rule 1.1 +Multiple +lines +text. +Rule 1.2 +Multiple lines +text. +Rule 1.3 Required +Multiple +lines +text. +Rule 1.4 +Should +Starts from lowercase letter. +Rule 1.5 +Should + starts from lowercase letter. +Rule 1.6 +Can + +contain empty lines. + diff --git a/cppcheck-2.14.0/addons/test/misra/misra_rules_structure.txt b/cppcheck-2.14.0/addons/test/misra/misra_rules_structure.txt new file mode 100644 index 00000000..41d2d456 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/misra_rules_structure.txt @@ -0,0 +1,20 @@ +Here can be any text. + +Incorrect definitions: +Appendix A +Appendix A Summary: + +Rule 1.1 +Error! + +Here we go: +Appendix A Summary of guidelines + +Rule 1.2 +Rule text. + +Stop parsing after this line: +Appendix B + +Rule 1.3 +Error! diff --git a/cppcheck-2.14.0/addons/test/misra/suppressions.txt b/cppcheck-2.14.0/addons/test/misra/suppressions.txt new file mode 100644 index 00000000..139e0885 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra/suppressions.txt @@ -0,0 +1,6 @@ +misra-c2012-21.6:*/misra-suppressions1-test.c:7 +misra-c2012-17.3 +misra-c2012-8.4:*/misra-suppressions1-test.c +misra-c2012-4.1:*/misra-suppressions2-test.c +misra-c2012-8.4:*/misra-suppressions2-test.c +misra-c2012-19.2:*/misra-suppressions2-test.c diff --git a/cppcheck-2.14.0/addons/test/misra_test.py b/cppcheck-2.14.0/addons/test/misra_test.py new file mode 100644 index 00000000..546b075a --- /dev/null +++ b/cppcheck-2.14.0/addons/test/misra_test.py @@ -0,0 +1,177 @@ +# Running the test with Python 2: +# Be sure to install pytest version 4.6.4 (newer should also work) +# Command in cppcheck directory: +# python -m pytest addons/test/test-misra.py +# +# Running the test with Python 3: +# Command in cppcheck directory: +# PYTHONPATH=./addons python3 -m pytest addons/test/test-misra.py + +import os +import pytest +import re +import sys +import tempfile + +from .util import dump_create, dump_remove, convert_json_output + + +TEST_SOURCE_FILES = ['./addons/test/misra/misra-test.c'] + + +def remove_misra_config(s:str): + ret = '' + for line in s.splitlines(): + if '[misra-config]' not in line: + ret += line + '\n' + return ret + + +@pytest.fixture(scope="function") +def checker(): + from addons.misra import MisraChecker, MisraSettings, get_args_parser + parser = get_args_parser() + args = parser.parse_args([]) + settings = MisraSettings(args) + return MisraChecker(settings) + + +@pytest.fixture +def test_files(): + for f in TEST_SOURCE_FILES: + dump_create(f) + yield + for f in TEST_SOURCE_FILES: + dump_remove(f) + + +def test_loadRuleTexts_structure(checker): + checker.loadRuleTexts("./addons/test/misra/misra_rules_structure.txt") + assert(checker.ruleTexts.get(101, None) is None) + assert(checker.ruleTexts[102].text == "Rule text.") + assert(checker.ruleTexts.get(103, None) is None) + + +def test_loadRuleTexts_empty_lines(checker): + checker.loadRuleTexts("./addons/test/misra/misra_rules_empty_lines.txt") + assert(len(checker.ruleTexts) == 3) + assert(len(checker.ruleTexts[102].text) == len("Rule text.")) + + +def test_loadRuleTexts_mutiple_lines(checker): + checker.loadRuleTexts("./addons/test/misra/misra_rules_multiple_lines.txt") + assert(checker.ruleTexts[101].text == "Multiple lines text.") + assert(checker.ruleTexts[102].text == "Multiple lines text.") + assert(checker.ruleTexts[103].text == "Multiple lines text.") + assert(checker.ruleTexts[104].text == "Should") + assert(checker.ruleTexts[105].text == "Should") + assert(checker.ruleTexts[106].text == "Can contain empty lines.") + + +def test_verifyRuleTexts(checker, capsys): + checker.loadRuleTexts("./addons/test/misra/misra_rules_dummy.txt") + checker.verifyRuleTexts() + captured = capsys.readouterr().out + assert("21.3" not in captured) + assert("1.3" in captured) + + +def test_rules_misra_severity(checker): + checker.loadRuleTexts("./addons/test/misra/misra_rules_dummy.txt") + assert(checker.ruleTexts[1004].misra_severity == 'Mandatory') + assert(checker.ruleTexts[401].misra_severity == 'Required') + assert(checker.ruleTexts[1505].misra_severity == 'Advisory') + assert(checker.ruleTexts[2104].misra_severity == '') + + +def test_json_out(checker, capsys, test_files): + sys.argv.append("--cli") + checker.loadRuleTexts("./addons/test/misra/misra_rules_dummy.txt") + checker.parseDump("./addons/test/misra/misra-test.c.dump") + captured = capsys.readouterr() + captured = captured.out.splitlines() + sys.argv.remove("--cli") + json_output = convert_json_output(captured) + assert("Mandatory" in json_output['c2012-10.4'][0]['extra']) + assert("Required" in json_output['c2012-21.3'][0]['extra']) + assert("Advisory" in json_output['c2012-20.1'][0]['extra']) + + +def test_rules_cppcheck_severity(checker, capsys, test_files): + checker.loadRuleTexts("./addons/test/misra/misra_rules_dummy.txt") + checker.parseDump("./addons/test/misra/misra-test.c.dump") + captured = capsys.readouterr().err + assert("(error)" not in remove_misra_config(captured)) + assert("(warning)" not in captured) + assert("(style)" in captured) + +def test_rules_cppcheck_severity_custom(checker, capsys, test_files): + checker.loadRuleTexts("./addons/test/misra/misra_rules_dummy.txt") + checker.setSeverity("custom-severity") + checker.parseDump("./addons/test/misra/misra-test.c.dump") + captured = capsys.readouterr().err + assert("(error)" not in remove_misra_config(captured)) + assert("(warning)" not in captured) + assert("(style)" not in captured) + assert("(custom-severity)" in captured) + +def test_rules_suppression(checker, capsys): + test_sources = ["addons/test/misra/misra-suppressions1-test.c", + "addons/test/misra/misra-suppressions2-test.c"] + + for src in test_sources: + re_suppressed= r"\[%s\:[0-9]+\]" % src + dump_remove(src) + dump_create(src, "--suppressions-list=addons/test/misra/suppressions.txt","--inline-suppr") + checker.parseDump(src + ".dump") + captured = capsys.readouterr().err + found = re.search(re_suppressed, captured) + assert found is None, 'Unexptected output:\n' + captured + dump_remove(src) + +def test_arguments_regression(): + args_ok = ["-generate-table", + "--rule-texts=./addons/test/assets/misra_rules_multiple_lines.txt", + "--verify-rule-texts", + "-t=foo", "--template=foo", + "--suppress-rules=15.1", + "--quiet", + "--cli", + "--no-summary", + "--show-suppressed-rules", + "-P=src/", "--file-prefix=src/", + "--severity=misra-warning"] + # Arguments with expected SystemExit + args_exit = ["--non-exists", "--non-exists-param=42", "-h", "--help"] + + from addons.misra import get_args_parser + + # sys.argv contains all pytest arguments - so clear all existing arguments first and restore afterwards + sys_argv_old = sys.argv + sys.argv = [sys.argv[0]] + + try: + for arg in args_exit: + sys.argv.append(arg) + with pytest.raises(SystemExit): + parser = get_args_parser() + parser.parse_args() + sys.argv.remove(arg) + + for arg in args_ok: + sys.argv.append(arg) + try: + parser = get_args_parser() + parser.parse_args() + except SystemExit: + pytest.fail("Unexpected SystemExit with '%s'" % arg) + sys.argv.remove(arg) + finally: + sys.argv = sys_argv_old + + +def test_read_ctu_info_line(checker): + assert checker.read_ctu_info_line('{') is None + assert checker.read_ctu_info_line('{"summary":"123"}') is None + assert checker.read_ctu_info_line('{"data":123}') is None + assert checker.read_ctu_info_line('{"summary":"123","data":123}') is not None diff --git a/cppcheck-2.14.0/addons/test/naming_test.c b/cppcheck-2.14.0/addons/test/naming_test.c new file mode 100644 index 00000000..bf021b87 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/naming_test.c @@ -0,0 +1,5 @@ +// To test: +// ~/cppcheck/cppcheck --dump naming_test.c && python ../naming.py --var='[a-z].*' --function='[a-z].*' naming_test.c.dump + +// Should not crash when there is no name +void func(int number, int); diff --git a/cppcheck-2.14.0/addons/test/naming_test.cpp b/cppcheck-2.14.0/addons/test/naming_test.cpp new file mode 100644 index 00000000..07297dbb --- /dev/null +++ b/cppcheck-2.14.0/addons/test/naming_test.cpp @@ -0,0 +1,11 @@ +// To test: +// ~/cppcheck/cppcheck --dump naming_test.cpp && python ../naming.py --var='[a-z].*' --function='[a-z].*' naming_test.cpp.dump + +// No error for mismatching Constructor/Destructor names should be issued, they can not be changed. +class TestClass1 +{ + TestClass1() {} + ~TestClass1() {} + TestClass1(const TestClass1 &) {} + TestClass1(TestClass1 &&) {} +}; diff --git a/cppcheck-2.14.0/addons/test/path1/misra-suppressions1-test.c b/cppcheck-2.14.0/addons/test/path1/misra-suppressions1-test.c new file mode 100644 index 00000000..59c77598 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/path1/misra-suppressions1-test.c @@ -0,0 +1,33 @@ +// To test: +// ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump +// There should be no violations reported + +// This needs to stay at line number 7 to make the test pass +// If it is changed update suppressions.txt with the new line number +#include //21.6 + +extern int misra_5_2_var_hides_var______31x; +static int misra_5_2_var_hides_var______31y;//5.2 +static int misra_5_2_function_hides_var_31x; +void misra_5_2_function_hides_var_31y(void) {}//5.2 +void foo(void) +{ + int i; + switch(misra_5_2_func1()) //16.4 16.6 + { + case 1: + { + do + { + for(i = 0; i < 10; i++) + { + if(misra_5_2_func3()) //14.4 + { + int misra_5_2_var_hides_var_1____31x; + int misra_5_2_var_hides_var_1____31y;//5.2 + } + } + } while(misra_5_2_func2()); //14.4 + } + } +} diff --git a/cppcheck-2.14.0/addons/test/path1/misra-suppressions2-test.c b/cppcheck-2.14.0/addons/test/path1/misra-suppressions2-test.c new file mode 100644 index 00000000..7fadf186 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/path1/misra-suppressions2-test.c @@ -0,0 +1,14 @@ +// To test: +// ../../cppcheck --suppressions-list=suppressions.txt --dump misra-suppressions*-test.c && python ../misra.py misra-suppressions*-test.c.dump +// There should be no violations reported + +union misra_5_2_field_hides_field__63x { //19.2 +int misra_5_2_field_hides_field__31x; +int misra_5_2_field_hides_field__31y;//5.2 +}; +struct misra_5_2_field_hides_field__63y { //5.2 +int misra_5_2_field_hides_field1_31x; +int misra_5_2_field_hides_field1_31y;//5.2 +}; +const char *s41_1 = "\x41g"; // 4.1 +const char *s41_2 = "\x41\x42"; diff --git a/cppcheck-2.14.0/addons/test/threadsafety/MT-Unsafe.cpp b/cppcheck-2.14.0/addons/test/threadsafety/MT-Unsafe.cpp new file mode 100644 index 00000000..5b23cefe --- /dev/null +++ b/cppcheck-2.14.0/addons/test/threadsafety/MT-Unsafe.cpp @@ -0,0 +1,67 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* + * This does not match the standard cppchek test code, + * because I haven't figured that out yet. + * This code does compile and run, and does demonstrate + * the issues that the threadsafety.py addon is supposed to find. + * It does not use threads ! + */ + +#include +#include +#include + +void threadsafety_static() +{ + // cppcheck-suppress threadsafety-threadsafety + static unsigned int nCount = 0; + + nCount++; + printf("%d\n", nCount); +} + +void threadsafety_call() +{ + time_t now = time(nullptr); + // cppcheck-suppress threadsafety-unsafe-call + printf("%s\n", ctime(&now)); +} + +// cppcheck --addon=threadsafety +// should *not* find any problems with this function. +void threadsafety_safecall() +{ + char haystack[] = "alphabet"; + char needle[] = "Alph"; + char* found = strcasestr(haystack, needle); + printf("%s %sin %s\n", needle, found ? "" : "not ", haystack); +} + +int main() { + threadsafety_static(); + + threadsafety_call(); + + threadsafety_safecall(); + + threadsafety_static(); + + return 0; +} diff --git a/cppcheck-2.14.0/addons/test/threadsafety/local_static.cpp b/cppcheck-2.14.0/addons/test/threadsafety/local_static.cpp new file mode 100644 index 00000000..b41a0571 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/threadsafety/local_static.cpp @@ -0,0 +1,7 @@ +struct Dummy { + int x; +}; +void func() { + // cppcheck-suppress threadsafety-threadsafety + static Dummy dummy; +} diff --git a/cppcheck-2.14.0/addons/test/threadsafety/local_static_const.cpp b/cppcheck-2.14.0/addons/test/threadsafety/local_static_const.cpp new file mode 100644 index 00000000..eaed48be --- /dev/null +++ b/cppcheck-2.14.0/addons/test/threadsafety/local_static_const.cpp @@ -0,0 +1,7 @@ +struct Dummy { + int x; +}; +void func() { + // cppcheck-suppress threadsafety-threadsafety-const + static const Dummy dummy; +} diff --git a/cppcheck-2.14.0/addons/test/util.py b/cppcheck-2.14.0/addons/test/util.py new file mode 100644 index 00000000..dda3655d --- /dev/null +++ b/cppcheck-2.14.0/addons/test/util.py @@ -0,0 +1,47 @@ +# Helpers for pytest tests +import subprocess +import json +import os + + +def find_cppcheck_binary(): + possible_locations = [ + "./cppcheck", + "./build/bin/cppcheck", + r".\bin\cppcheck.exe", + ] + for location in possible_locations: + if os.path.exists(location): + break + else: + raise RuntimeError("Could not find cppcheck binary") + + return location + +def dump_create(fpath, *argv): + cppcheck_binary = find_cppcheck_binary() + cmd = [cppcheck_binary, "--dump", "-DDUMMY", "--quiet", fpath] + list(argv) + p = subprocess.Popen(cmd) + p.communicate() + if p.returncode != 0: + raise OSError("cppcheck returns error code: %d" % p.returncode) + p = subprocess.Popen(["sync"]) + p.communicate() + + +def dump_remove(fpath): + p = subprocess.Popen(["rm", "-f", fpath + ".dump"]) + p.communicate() + + +def convert_json_output(raw_json_strings): + """Convert raw stdout/stderr cppcheck JSON output to python dict.""" + json_output = {} + for line in raw_json_strings: + try: + json_line = json.loads(line) + # json_output[json_line['errorId']] = json_line + json_output.setdefault(json_line['errorId'], []).append(json_line) + except ValueError: + pass + return json_output diff --git a/cppcheck-2.14.0/addons/test/y2038/y2038-inc.h b/cppcheck-2.14.0/addons/test/y2038/y2038-inc.h new file mode 100644 index 00000000..e8190c61 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/y2038/y2038-inc.h @@ -0,0 +1,31 @@ +#ifndef __INC2038 +#define _INC2038 + +/* + * This file defines _USE_TIME_BITS64. + * It plays the role of a Y2038-proof glibc. + */ + +#define _USE_TIME_BITS64 + +/* + * Declare just enough for clock_gettime + */ + +typedef int clockid_t; + +typedef int __time_t; + +typedef long int __syscall_slong_t; + +struct timespec +{ + __time_t tv_sec; /* Seconds. */ + __syscall_slong_t tv_nsec; /* Nanoseconds. */ +}; + +extern int clock_gettime(clockid_t clk_id, struct timespec *tp); + +#define CLOCK_REALTIME 0 + +#endif /* INC2038 */ diff --git a/cppcheck-2.14.0/addons/test/y2038/y2038-test-1-bad-time-bits.c b/cppcheck-2.14.0/addons/test/y2038/y2038-test-1-bad-time-bits.c new file mode 100644 index 00000000..e9456db6 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/y2038/y2038-test-1-bad-time-bits.c @@ -0,0 +1,18 @@ +#include +#include + +/* + * Define _TIME_BITS unequal to 64 to trigger error + */ + +#define _TIME_BITS 62 + +#include "y2038-inc.h" + +int main(int argc, char **argv) +{ + clockid_t my_clk_id = CLOCK_REALTIME; + struct timespec *my_tp; + + return clock_gettime(my_clk_id, &my_tp); +} diff --git a/cppcheck-2.14.0/addons/test/y2038/y2038-test-2-no-time-bits.c b/cppcheck-2.14.0/addons/test/y2038/y2038-test-2-no-time-bits.c new file mode 100644 index 00000000..ab566039 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/y2038/y2038-test-2-no-time-bits.c @@ -0,0 +1,16 @@ +#include +#include + +/* + * Do not define _TIME_BITS but have _USE_TIME_BITS64 defined + */ + +#include "y2038-inc.h" + +int main(int argc, char **argv) +{ + clockid_t my_clk_id = CLOCK_REALTIME; + struct timespec *my_tp; + + return clock_gettime(my_clk_id, &my_tp); +} diff --git a/cppcheck-2.14.0/addons/test/y2038/y2038-test-3-no-use-time-bits.c b/cppcheck-2.14.0/addons/test/y2038/y2038-test-3-no-use-time-bits.c new file mode 100644 index 00000000..02654f7e --- /dev/null +++ b/cppcheck-2.14.0/addons/test/y2038/y2038-test-3-no-use-time-bits.c @@ -0,0 +1,16 @@ +#include +#include + +/* + * Include bad _USE_TIME_BITS64 definition to trigger error + */ + +#define _TIME_BITS 64 + +int main(int argc, char **argv) +{ + clockid_t my_clk_id = CLOCK_REALTIME; + struct timespec *my_tp; + + return clock_gettime(my_clk_id, &my_tp); +} diff --git a/cppcheck-2.14.0/addons/test/y2038/y2038-test-4-good.c b/cppcheck-2.14.0/addons/test/y2038/y2038-test-4-good.c new file mode 100644 index 00000000..aaa7ecf2 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/y2038/y2038-test-4-good.c @@ -0,0 +1,15 @@ +/* + * Define _TIME_BITS equal to 64 so that glibc knows we want Y2038 support. + */ + +#define _TIME_BITS 64 + +#include "y2038-inc.h" + +int main(int argc, char **argv) +{ + clockid_t my_clk_id = CLOCK_REALTIME; + struct timespec *my_tp; + + return clock_gettime(my_clk_id, &my_tp); +} diff --git a/cppcheck-2.14.0/addons/test/y2038/y2038-test-5-good-no-time-used.c b/cppcheck-2.14.0/addons/test/y2038/y2038-test-5-good-no-time-used.c new file mode 100644 index 00000000..4aedfcc2 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/y2038/y2038-test-5-good-no-time-used.c @@ -0,0 +1,14 @@ +/* + * C file that does not use any time functionality -> no errors should + * be reported. + */ + +#include + +int main(int argc, char **argv) +{ + if (argc > 1) { + printf("Hello"); + } + return 0; +} diff --git a/cppcheck-2.14.0/addons/test/y2038_test.py b/cppcheck-2.14.0/addons/test/y2038_test.py new file mode 100644 index 00000000..f76a8761 --- /dev/null +++ b/cppcheck-2.14.0/addons/test/y2038_test.py @@ -0,0 +1,139 @@ +# Running the test with Python 2: +# Be sure to install pytest version 4.6.4 (newer should also work) +# Command in cppcheck directory: +# python -m pytest addons/test/test-y2038.py +# +# Running the test with Python 3: +# Command in cppcheck directory: +# PYTHONPATH=./addons python3 -m pytest addons/test/test-y2038.py + +import sys +import pytest + +from addons.y2038 import check_y2038_safe + +from .util import dump_create, dump_remove, convert_json_output + + +TEST_SOURCE_FILES = ['./addons/test/y2038/y2038-test-1-bad-time-bits.c', + './addons/test/y2038/y2038-test-2-no-time-bits.c', + './addons/test/y2038/y2038-test-3-no-use-time-bits.c', + './addons/test/y2038/y2038-test-4-good.c', + './addons/test/y2038/y2038-test-5-good-no-time-used.c'] + + +def setup_module(module): + sys.argv.append("--cli") + for f in TEST_SOURCE_FILES: + dump_create(f) + + +def teardown_module(module): + sys.argv.remove("--cli") + for f in TEST_SOURCE_FILES: + dump_remove(f) + + +def test_1_bad_time_bits(capsys): + is_safe = check_y2038_safe('./addons/test/y2038/y2038-test-1-bad-time-bits.c.dump', quiet=True) + assert(is_safe is False) + captured = capsys.readouterr() + captured = captured.out.splitlines() + json_output = convert_json_output(captured) + + # Has exactly one warnings of _TIME_BITS and _USE_TIME_BITS64 kind. + assert(len(json_output['type-bits-undef']) == 1) + assert(len(json_output['type-bits-not-64']) == 1) + + # There are 2 unsafe calls in test source and 3 in y2038-in.h + unsafe_calls = json_output['unsafe-call'] + assert(len([c for c in unsafe_calls if c['file'].endswith('h')]) == 3) + assert(len([c for c in unsafe_calls if c['file'].endswith('c')]) == 0) + + +def test_2_no_time_bits(capsys): + is_safe = check_y2038_safe('./addons/test/y2038/y2038-test-2-no-time-bits.c.dump', quiet=True) + assert(is_safe is False) + captured = capsys.readouterr() + captured = captured.out.splitlines() + json_output = convert_json_output(captured) + + # _USE_TIME_BITS64 defined in y2038-inc.h header, but there is not + # _TIME_BITS definition. Here must be appropriate warning. + assert(len(json_output['type-bits-undef']) == 1) + assert(json_output.get('type-bits-not-64') is None) + + # y2038-in.h still has y2038-unsafe calls. + unsafe_calls = json_output['unsafe-call'] + assert(len([c for c in unsafe_calls if c['file'].endswith('h')]) == 3) + + +def test_3_no_use_time_bits(capsys): + is_safe = check_y2038_safe('./addons/test/y2038/y2038-test-3-no-use-time-bits.c.dump', quiet=True) + assert(is_safe is False) + captured = capsys.readouterr() + captured = captured.out.splitlines() + json_output = convert_json_output(captured) + + # Included bad _USE_TIME_BITS64 definition must trigger the errors. + unsafe_calls = json_output['unsafe-call'] + assert(len(unsafe_calls) == 2) + + +def test_4_good(capsys): + is_safe = check_y2038_safe('./addons/test/y2038/y2038-test-4-good.c.dump', quiet=True) + # assert(is_safe is True) # FIXME: This should be a "good" example returning "True" instead of "False" + captured = capsys.readouterr() + captured = captured.out.splitlines() + json_output = convert_json_output(captured) + + # Defined _TIME_BITS equal to 64 so that glibc knows we want Y2038 support. + # There are no warnings from C sources. + unsafe_calls = json_output['unsafe-call'] + assert(len([c for c in unsafe_calls if c['file'].endswith('.c')]) == 0) + + +def test_5_good(capsys): + is_safe = check_y2038_safe('./addons/test/y2038/y2038-test-5-good-no-time-used.c.dump', quiet=True) + assert(is_safe is True) + captured = capsys.readouterr() + captured = captured.out.splitlines() + json_output = convert_json_output(captured) + + # There are no warnings from C sources. + if 'unsafe-call' in json_output: + unsafe_calls = json_output['unsafe-call'] + assert(len([c for c in unsafe_calls if c['file'].endswith('.c')]) == 0) + + +def test_arguments_regression(): + args_ok = ["-t=foo", "--template=foo", + "-q", "--quiet", + "--cli"] + # Arguments with expected SystemExit + args_exit = ["--non-exists", "--non-exists-param=42", "-h", "--help"] + + from addons.y2038 import get_args_parser + + # sys.argv contains all pytest arguments - so clear all existing arguments first and restore afterwards + sys_argv_old = sys.argv + sys.argv = [sys.argv[0]] + + try: + for arg in args_exit: + sys.argv.append(arg) + with pytest.raises(SystemExit): + parser = get_args_parser() + parser.parse_args() + sys.argv.remove(arg) + + for arg in args_ok: + sys.argv.append(arg) + try: + parser = get_args_parser() + parser.parse_args() + except SystemExit: + pytest.fail("Unexpected SystemExit with '%s'" % arg) + sys.argv.remove(arg) + finally: + sys.argv = sys_argv_old \ No newline at end of file diff --git a/cppcheck-2.14.0/addons/threadsafety.py b/cppcheck-2.14.0/addons/threadsafety.py new file mode 100644 index 00000000..9475e1a5 --- /dev/null +++ b/cppcheck-2.14.0/addons/threadsafety.py @@ -0,0 +1,350 @@ +#!/usr/bin/env python3 +""" +cppcheck addon for threadsafety detection. + + This script analyses Cppcheck dump files to locate threadsafety issues. + It warns about + - static local objects + - MT-Unsafe symbols listed in the "Attributes" sections of man pages. + +""" + +import re +import sys + +import cppcheckdata + +# -------------------------------- +# List of MT-Unsafe identifiers +# -------------------------------- + +# This is Work In Progress. +# Eventually it should contain all identifiers (types +# and functions) which are MT-Unsafe. + +# The script tools/MT-Unsafe.py can help to re-generate this list. +# It reads a man-page tree and report identifiers marked as "MT-Unsafe" +# (see man 7 attributes for what this means), eg +# MT-Unsafe.py /usr/share/man/man3 + +id_MTunsafe_full = { + # MT-Unsafe types by definition + # 'pthread_t', + # Types marked MT-Unsafe + 'const:env', + 'const:hostid', + 'const:mallopt', + 'const:sigintr', + 'race:LogMask', + 'race:asctime', + 'race:crypt', + 'race:crypt_gensalt', + 'race:cuserid/!string', + 'race:dirstream', + 'race:drand48', + 'race:ecvt', + 'race:exit', + 'race:fcvt', + 'race:fgetgrent', + 'race:fgetpwent', + 'race:fgetspent', + 'race:fsent', + 'race:getdate', + 'race:getlogin', + 'race:getopt', + 'race:getspent', + 'race:getspnam', + 'race:grent', + 'race:grgid', + 'race:grnam', + 'race:hostbyaddr', + 'race:hostbyname', + 'race:hostbyname2', + 'race:hostent', + 'race:hsearch', + 'race:l64a', + 'race:localeconv', + 'race:mbrlen/!ps', + 'race:mbrtowc/!ps', + 'race:mbsnrtowcs/!ps', + 'race:mbsrtowcs/!ps', + 'race:mcheck', + 'race:mntentbuf', + 'race:netbyaddr', + 'race:netbyname', + 'race:netent', + 'race:netgrent', + 'race:protobyname', + 'race:protobynumber', + 'race:protoent', + 'race:ptsname', + 'race:pwent', + 'race:pwnam', + 'race:pwuid', + 'race:qecvt', + 'race:qfcvt', + 'race:servbyname', + 'race:servbyport', + 'race:servent', + 'race:sgetspent', + 'race:signgam', + 'race:stdin', + 'race:stdout', + 'race:streams', + 'race:strerror', + 'race:strsignal', + 'race:strtok', + 'race:tmbuf', + 'race:tmpnam/!s', + 'race:ttyent', + 'race:ttyname', + 'race:utent', + 'race:wcrtomb/!ps', + 'race:wcsnrtombs/!ps', + 'race:wcsrtombs/!ps', + 'sig:ALRM', + 'sig:SIGCHLD/linux', + # APIs marked MT-Unsafe + 'asctime', + 'clearenv', + 'ctime', + 'cuserid', + 'drand48', + 'ecvt', + 'encrypt', + 'endfsent', + 'endgrent', + 'endhostent', + 'endnetent', + 'endnetgrent', + 'endprotoent', + 'endpwent', + 'endservent', + 'endspent', + 'endttyent', + 'endusershell', + 'endutent', + 'erand48', + 'error_at_line', + 'ether_aton', + 'ether_ntoa', + 'exit', + 'fcloseall', + 'fcvt', + 'fgetgrent', + 'fgetpwent', + 'fgetspent', + 'fts_children', + 'fts_read', + 'gamma', + 'gammaf', + 'gammal', + 'getaliasbyname', + 'getaliasent', + 'getchar_unlocked', + 'getdate', + 'getfsent', + 'getfsfile', + 'getfsspec', + 'getgrent', + 'getgrent_r', + 'getgrgid', + 'getgrnam', + 'gethostbyaddr', + 'gethostbyname', + 'gethostbyname2', + 'gethostent', + 'gethostent_r', + 'getlogin', + 'getlogin_r', + 'getmntent', + 'getnetbyaddr', + 'getnetbyname', + 'getnetent', + 'getnetgrent', + 'getnetgrent_r', + 'getopt', + 'getopt_long', + 'getopt_long_only', + 'getpass', + 'getprotobyname', + 'getprotobynumber', + 'getprotoent', + 'getpwent', + 'getpwent_r', + 'getpwnam', + 'getpwuid', + 'getrpcbyname', + 'getrpcbynumber', + 'getrpcent', + 'getservbyname', + 'getservbyport', + 'getservent', + 'getspent', + 'getspent_r', + 'getspnam', + 'getttyent', + 'getttynam', + 'getusershell', + 'getutent', + 'getutid', + 'getutline', + 'getwchar_unlocked', + 'glob', + 'gmtime', + 'hcreate', + 'hdestroy', + 'hsearch', + 'innetgr', + 'jrand48', + 'l64a', + 'lcong48', + 'localeconv', + 'localtime', + 'login', + 'login_tty', + 'logout', + 'logwtmp', + 'lrand48', + 'mallinfo', + 'mallinfo2', + 'mblen', + 'mbrlen', + 'mbrtowc', + 'mbsnrtowcs', + 'mbsrtowcs', + 'mbtowc', + 'mcheck', + 'mcheck_check_all', + 'mcheck_pedantic', + 'mprobe', + 'mrand48', + 'mtrace', + 'muntrace', + 'nrand48', + 'profil', + 'ptsname', + 'putchar_unlocked', + 'putenv', + 'pututline', + 'putwchar_unlocked', + 'pvalloc', + 'qecvt', + 'qfcvt', + 'rcmd', + 'rcmd_af', + 're_comp', + 're_exec', + 'readdir', + 'rexec', + 'rexec_af', + 'seed48', + 'setenv', + 'setfsent', + 'setgrent', + 'sethostent', + 'sethostid', + 'setkey', + 'setlogmask', + 'setnetent', + 'setnetgrent', + 'setprotoent', + 'setpwent', + 'setservent', + 'setspent', + 'setttyent', + 'setusershell', + 'setutent', + 'sgetspent', + 'siginterrupt', + 'sleep', + 'srand48', + 'strerror', + 'strsignal', + 'strtok', + 'tmpnam', + 'ttyname', + 'ttyslot', + 'unsetenv', + 'updwtmp', + 'utmpname', + 'valloc', + 'wcrtomb', + 'wcsnrtombs', + 'wcsrtombs', + 'wctomb', + 'wordexp' +} + +# From man 7 attributes +# the full token could be feature:function/condition - we just want function. +id_MTunsafe = [re.sub('^.*:', '', re.sub('/.*$', '', x)) + for x in id_MTunsafe_full + ] + + +def reportError(token, severity, msg, errid): # noqa: D103 + cppcheckdata.reportError(token, severity, msg, 'threadsafety', errid) + + +def checkstatic(data): # noqa: D103 + for var in data.variables: + if var.isStatic and var.isLocal: + vartype = None + if var.isClass: + vartype = 'object' + else: + vartype = 'variable' + if var.isConst: + if data.standards.cpp == 'c++03': + reportError( + var.typeStartToken, + 'warning', + 'Local constant static ' + + vartype + "'" + var.nameToken.str + + "', dangerous if it is initialized" + + ' in parallel threads', + 'threadsafety-const') + else: + reportError(var.typeStartToken, 'warning', + 'Local static ' + vartype + ': ' + + var.nameToken.str, + 'threadsafety') + + +def check_MTunsafe(cfg): + """ + Look for functions marked MT-unsafe in their man pages. + + The MT-unsafe functions are listed in id_MTunsafe (and id_MTunsafe_full). + That section of code can be regenerated by the external script MT-Unsafe.py + """ + for token in cfg.tokenlist: + if token.str in id_MTunsafe: + reportError(token, 'warning', token.str + ' is MT-unsafe', + 'unsafe-call') + + +if __name__ == '__main__': + parser = cppcheckdata.ArgumentParser() + args = parser.parse_args() + + quiet = args.quiet or args.cli + + if not args.dumpfile: + if not args.quiet: + print('no input files.') + sys.exit(0) + + for dumpfile in args.dumpfile: + # load XML from .dump file + data = cppcheckdata.CppcheckData(dumpfile) + + for cfg in data.iterconfigurations(): + if not args.quiet: + srcfile = data.files[0] + print('Checking %s, config %s...' % (srcfile, cfg.name)) + check_MTunsafe(cfg) + checkstatic(cfg) + + sys.exit(cppcheckdata.EXIT_CODE) diff --git a/cppcheck-2.14.0/addons/y2038.py b/cppcheck-2.14.0/addons/y2038.py new file mode 100644 index 00000000..7c8f409d --- /dev/null +++ b/cppcheck-2.14.0/addons/y2038.py @@ -0,0 +1,242 @@ +#!/usr/bin/env python3 +# +# cppcheck addon for Y2038 safeness detection +# +# Detects: +# +# 1. _TIME_BITS being defined to something else than 64 bits +# 2. _USE_TIME_BITS64 being defined when _TIME_BITS is not +# 3. Any Y2038-unsafe symbol when _USE_TIME_BITS64 is not defined. +# +# Example usage: +# $ cppcheck --addon=y2038 path-to-src/test.c +# + +from __future__ import print_function + +import cppcheckdata +import sys +import re + + +# -------------------------------------------- +# #define/#undef detection regular expressions +# -------------------------------------------- + +# test for '#define _TIME_BITS 64' +re_define_time_bits_64 = re.compile(r'^\s*#\s*define\s+_TIME_BITS\s+64\s*$') + +# test for '#define _TIME_BITS ...' (combine w/ above to test for 'not 64') +re_define_time_bits = re.compile(r'^\s*#\s*define\s+_TIME_BITS\s+.*$') + +# test for '#undef _TIME_BITS' (if it ever happens) +re_undef_time_bits = re.compile(r'^\s*#\s*undef\s+_TIME_BITS\s*$') + +# test for '#define _USE_TIME_BITS64' +re_define_use_time_bits64 = re.compile(r'^\s*#\s*define\s+_USE_TIME_BITS64\s*$') + +# test for '#undef _USE_TIME_BITS64' (if it ever happens) +re_undef_use_time_bits64 = re.compile(r'^\s*#\s*undef\s+_USE_TIME_BITS64\s*$') + +# -------------------------------- +# List of Y2038-unsafe identifiers +# -------------------------------- + +# This is WIP. Eventually it should contain all identifiers (types +# and functions) which would be affected by the Y2038 bug. + +id_Y2038 = { + # Y2038-unsafe types by definition + 'time_t' + # Types using Y2038-unsafe types + 'lastlog', + 'msqid_ds', + 'semid_ds', + 'timeb', + 'timespec', + 'timeval', + 'utimbuf', + 'itimerspec', + 'stat', + 'clnt_ops', + 'elf_prstatus', + 'itimerval', + 'ntptimeval', + 'rusage', + 'timex', + 'utmp', + 'utmpx', + # APIs using 2038-unsafe types + 'ctime', + 'ctime_r', + 'difftime', + 'gmtime', + 'gmtime_r', + 'localtime', + 'localtime_r', + 'mktime', + 'stime', + 'timegm', + 'timelocal', + 'time', + 'msgctl', + 'ftime', + 'aio_suspend', + 'clock_getres', + 'clock_gettime', + 'clock_nanosleep', + 'clock_settime', + 'futimens', + 'mq_timedreceive', + 'mq_timedsend', + 'nanosleep', + 'pselect', + 'pthread_cond_timedwait', + 'pthread_mutex_timedlock', + 'pthread_rwlock_timedrdlock', + 'pthread_rwlock_timedwrlock', + 'sched_rr_get_interval', + 'sem_timedwait', + 'sigtimedwait', + 'timespec_get', + 'utimensat', + 'adjtime', + 'pmap_rmtcall', + 'clntudp_bufcreate', + 'clntudp_create', + 'futimes', + 'gettimeofday', + 'lutimes', + 'select', + 'settimeofday', + 'utimes', + 'utime', + 'timerfd_gettime', + 'timerfd_settime', + 'timer_gettime', + 'timer_settime', + 'fstatat', + 'fstat', + '__fxstatat', + '__fxstat', + 'lstat', + '__lxstat', + 'stat', + '__xstat', + 'struct itimerval', + 'setitimer', + 'getitimer', + 'ntp_gettime', + 'getrusage', + 'wait3', + 'wait4', + 'adjtimex', + 'ntp_adjtime', + 'getutent_r', + 'getutent', + 'getutid_r', + 'getutid', + 'getutline_r', + 'getutline', + 'login', + 'pututline', + 'updwtmp', + 'getutxent', + 'getutxid', + 'getutxline', + 'pututxline' +} + + +def check_y2038_safe(dumpfile, quiet=False): + # Assume that the code is Y2038 safe until proven otherwise + y2038safe = True + # load XML from .dump file + data = cppcheckdata.CppcheckData(dumpfile) + + srcfile = data.files[0] + for cfg in data.iterconfigurations(): + if not quiet: + print('Checking %s, config %s...' % (srcfile, cfg.name)) + safe_ranges = [] + safe = -1 + time_bits_defined = False + srclinenr = 0 + + for directive in cfg.directives: + # track source line number + if directive.file == srcfile: + srclinenr = directive.linenr + # check for correct _TIME_BITS if present + if re_define_time_bits_64.match(directive.str): + time_bits_defined = True + elif re_define_time_bits.match(directive.str): + cppcheckdata.reportError(directive, 'error', + '_TIME_BITS must be defined equal to 64', + 'y2038', + 'type-bits-not-64') + time_bits_defined = False + y2038safe = False + elif re_undef_time_bits.match(directive.str): + time_bits_defined = False + # check for _USE_TIME_BITS64 (un)definition + if re_define_use_time_bits64.match(directive.str): + safe = int(srclinenr) + # warn about _TIME_BITS not being defined + if not time_bits_defined: + cppcheckdata.reportError(directive, 'warning', + '_USE_TIME_BITS64 is defined but _TIME_BITS was not', + 'y2038', + 'type-bits-undef') + elif re_undef_use_time_bits64.match(directive.str): + unsafe = int(srclinenr) + # do we have a safe..unsafe area? + if unsafe > safe > 0: + safe_ranges.append((safe, unsafe)) + safe = -1 + + # check end of source beyond last directive + if len(cfg.tokenlist) > 0: + unsafe = int(cfg.tokenlist[-1].linenr) + if unsafe > safe > 0: + safe_ranges.append((safe, unsafe)) + + # go through all tokens + for token in cfg.tokenlist: + if token.str in id_Y2038: + if not any(lower <= int(token.linenr) <= upper + for (lower, upper) in safe_ranges): + cppcheckdata.reportError(token, 'warning', + token.str + ' is Y2038-unsafe', + 'y2038', + 'unsafe-call') + y2038safe = False + token = token.next + + return y2038safe + + +def get_args_parser(): + parser = cppcheckdata.ArgumentParser() + return parser + + +if __name__ == '__main__': + parser = get_args_parser() + args = parser.parse_args() + + exit_code = 0 + quiet = args.quiet or args.cli + + if not args.dumpfile: + if not args.quiet: + print("no input files.") + sys.exit(0) + + for dumpfile in args.dumpfile: + if not quiet: + print('Checking ' + dumpfile + '...') + + check_y2038_safe(dumpfile, quiet) + + sys.exit(cppcheckdata.EXIT_CODE) diff --git a/cppcheck-2.14.0/build-pcre.txt b/cppcheck-2.14.0/build-pcre.txt new file mode 100644 index 00000000..3dad4274 --- /dev/null +++ b/cppcheck-2.14.0/build-pcre.txt @@ -0,0 +1,76 @@ +PCRE is a library that is used by the optional "rules" feature for the command +line version of cppcheck. It is readily available on Linux and Mac OS X, but +must be obtained separately for Windows. + +If you're using qmake to generate makefiles, the following behavior applies: + +- If you're not on Windows, it assumes by default that you have PCRE and want + to enable rules support. You can disable rules support (removing the PCRE + dependency) by passing HAVE_RULES=no to qmake. + +- If you are on Windows, but have PCRE available, you can enable rules support + by passing HAVE_RULES=yes to qmake. + + - Note: This includes using build.bat since it calls qmake - to use PCRE and + build.bat, you need to run set HAVE_RULES=yes before each run of build.bat + + +Build instructions +------------------ + +Windows +------- + +Visual Studio + +To build PCRE, download the source code from www.pcre.org and +CMake (https://cmake.org/download/). We assume you use Visual Studio 2015 - +otherwise adapt the commands for your version. + +VS Solution file + cmake . -G "Visual Studio 14 2015" + Open PCRE.sln with VS IDE or via cmd: + call "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" x86 + MSBuild PCRE.sln /target:Build /property:Configuration="Release" + + For 64-bit target: cmake . -G "Visual Studio 14 2015 Win64" + +or using NMake + call "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" x86 + cmake . -G "NMake Makefiles" + nmake + +or using MSYS + cmake . -G "MSYS Makefiles" + make + + +Linux +----- + +The normal Makefile should work. + +Install PCRE on Ubuntu might be needed: + sudo apt-get install libpcre3 libpcre3-dev + + +Mac OSX +------- + +Install PCRE: + +homebre + brew install pcre + +or macport + sudo port install pcre + +Ensure /path/to/pcre.h is in CXXFLAGS, e.g: + +for homebrew + export CXXFLAGS=${CXXFLAGS}:/usr/local/include + +or macport + export CXXFLAGS=${CXXFLAGS}:/opt/local/include + +Or for MSVC copy pcre.lib and pcre.h in /externals directory. diff --git a/cppcheck-2.14.0/cfg/avr.cfg b/cppcheck-2.14.0/cfg/avr.cfg new file mode 100644 index 00000000..bb21afac --- /dev/null +++ b/cppcheck-2.14.0/cfg/avr.cfg @@ -0,0 +1,402 @@ + + + + + + + + + + + + + + + + + + + + + + + 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 + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/bento4.cfg b/cppcheck-2.14.0/cfg/bento4.cfg new file mode 100644 index 00000000..50f3cac0 --- /dev/null +++ b/cppcheck-2.14.0/cfg/bento4.cfg @@ -0,0 +1,142 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/boost.cfg b/cppcheck-2.14.0/cfg/boost.cfg new file mode 100644 index 00000000..fc6836da --- /dev/null +++ b/cppcheck-2.14.0/cfg/boost.cfg @@ -0,0 +1,1283 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + arg1==0?0:(arg1<0?-1:1) + + + + + + + + false + + + + arg1<0?1:0 + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + 5.000000000000000000000000000000000000e-01 + + + false + + + + 3.333333333333333333333333333333333333e-01 + + + false + + + + 6.666666666666666666666666666666666666e-01 + + + false + + + + 6.666666666666666666666666666666666666e-01 + + + false + + + + 1.66666666666666666666666666666666666666666e-01 + + + false + + + + 7.500000000000000000000000000000000000e-01 + + + false + + + + 1.414213562373095048801688724209698078e+00 + + + false + + + + 1.732050807568877293527446341505872366e+00 + + + false + + + + 7.071067811865475244008443621048490392e-01 + + + false + + + + 6.931471805599453094172321214581765680e-01 + + + false + + + + -3.665129205816643270124391582326694694e-01 + + + false + + + + 1.177410022515474691011569326459699637e+00 + + + false + + + + 7.071067811865475244008443621048490392e-01 + + + false + + + + 3.141592653589793238462643383279502884e+00 + + + false + + + + 1.570796326794896619231321691639751442e+00 + + + false + + + + 1.047197551196597746154214461093167628e+00 + + + false + + + + 5.235987755982988730771072305465838140e-01 + + + false + + + + 6.283185307179586476925286766559005768e+00 + + + false + + + + 2.094395102393195492308428922186335256e+00 + + + false + + + + 2.356194490192344928846982537459627163e+00 + + + false + + + + 4.188790204786390984616857844372670512e+00 + + + false + + + + 1.591549430918953357688837633725143620e-01 + + + false + + + + 3.989422804014326779399460599343818684e-01 + + + false + + + + 1.772453850905516027298167483341145182e+00 + + + false + + + + 1.253314137315500251207882642405522626e+00 + + + false + + + + 2.506628274631000502415765284811045253e+00 + + + false + + + + 9.189385332046727417803297364056176398e-01 + + + false + + + + 5.641895835477562869480794515607725858e-01 + + + false + + + + 5.641895835477562869480794515607725858e-01 + + + false + + + + 1.415926535897932384626433832795028841e-01 + + + false + + + + 8.584073464102067615373566167204971158e-01 + + + false + + + + 7.953167673715975443483953350568065807e-01 + + + false + + + + 2.245915771836104547342715220454373502e+01 + + + false + + + + 9.869604401089358618834490999876151135e+00 + + + false + + + + 1.644934066848226436472415166646025189e+00 + + + false + + + + 3.100627668029982017547631506710139520e+01 + + + false + + + + 1.464591887561523263020142527263790391e+00 + + + false + + + + 6.827840632552956814670208331581645981e-01 + + + false + + + + 2.718281828459045235360287471352662497e+00 + + + false + + + + 6.065306597126334236037995349911804534e-01 + + + false + + + + 2.314069263277926900572908636794854738e+01 + + + false + + + + 1.648721270700128146848650787814163571e+00 + + + false + + + + 4.342944819032518276511289189166050822e-01 + + + false + + + + 2.302585092994045684017991454684364207e+00 + + + false + + + + 2.302585092994045684017991454684364207e+00 + + + false + + + + 1.745329251994329576923690768488612713e-02 + + + false + + + + 5.729577951308232087679815481410517033e+01 + + + false + + + + 8.414709848078965066525023216302989996e-01 + + + false + + + + 5.403023058681397174009366074429766037e-01 + + + false + + + + 1.175201193643801456882381850595600815e+00 + + + false + + + + 1.543080634815243778477905620757061682e+00 + + + false + + + + 1.618033988749894848204586834365638117e+00 + + + false + + + + 4.812118250596034474977589134243684231e-01 + + + false + + + + 2.078086921235027537601322606117795767e+00 + + + false + + + + 5.772156649015328606065120900824024310e-01 + + + false + + + + 1.732454714600633473583025315860829681e+00 + + + false + + + + 3.331779238077186743183761363552442266e-01 + + + false + + + + 1.644934066848226436472415166646025189e+00 + + + false + + + + 1.202056903159594285399738161511449990e+00 + + + false + + + + 9.159655941772190150546035149323841107e-01 + + + false + + + + 1.282427129100622636875342568869791727e+00 + + + false + + + + 2.685452001065306445309714835481795693e+00 + + + false + + + + 1.139547099404648657492793019389846112e+00 + + + false + + + + 6.311106578189371381918993515442277798e-01 + + + false + + + + 3.245089300687638062848660410619754415e+00 + + + false + + + + 2.450893006876380628486604106197544154e-01 + + + false + + + + 6.366197723675813430755350534900574481e-01 + + + false + + + + 7.978845608028653558798921198687637369e-01 + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + true + + + + + + + boost::lock_guard + boost::mutex::scoped_lock + boost::recursive_mutex::scoped_lock + boost::unique_lock + boost::shared_lock + + + boost::mutex + boost::recursive_mutex + + + diff --git a/cppcheck-2.14.0/cfg/bsd.cfg b/cppcheck-2.14.0/cfg/bsd.cfg new file mode 100644 index 00000000..158d7a8a --- /dev/null +++ b/cppcheck-2.14.0/cfg/bsd.cfg @@ -0,0 +1,567 @@ + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + 0: + + + + + + false + + + + + + + + + + + + + 0: + + + + + + + false + + + + + + + + + + + + 0: + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + + + 0: + + + + + + + + + + + 0: + + + + + + + + 0: + + + + + + + + + + + 0: + + + + + + + + 0: + + + + + 0: + + + + + + + + + + + 0: + + + + + + + + 0: + + + + + 0: + + + + + + + + + + + + + + 0: + + + + + + + + 0: + + + + + 0: + + + + + + + + + + + 0: + + + + + + + + 0: + + + + + 0: + + + + + + + + + false + + + + + + false + + + + + + + + + 1: + + + + + false + + + + + 2: + + + + + false + + + + false + + + + + + + 0: + + + + + + + + + false + + + + + + + 1: + + + + + + + + + + -1: + + + + + + + + true + + + + + + + + + + + + true + + + + + + + + + + + + + + + true + + + + + + + + + + + true + + + + + + + + + + + + + + + + + + false + + + + + + 0: + + + + 0: + + + + reallocarray + free + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/cairo.cfg b/cppcheck-2.14.0/cfg/cairo.cfg new file mode 100644 index 00000000..e077c2fc --- /dev/null +++ b/cppcheck-2.14.0/cfg/cairo.cfg @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + diff --git a/cppcheck-2.14.0/cfg/cppcheck-cfg.rng b/cppcheck-2.14.0/cfg/cppcheck-cfg.rng new file mode 100644 index 00000000..5dd446c1 --- /dev/null +++ b/cppcheck-2.14.0/cfg/cppcheck-cfg.rng @@ -0,0 +1,730 @@ + + + + + + + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + false + maybe + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + error-code + + + + + + + + + + (::)?([a-zA-Z_:][a-zA-Z_0-9:]*[ ])*([a-zA-Z_][a-zA-Z_0-9]*::)*([a-zA-Z_][a-zA-Z_0-9]*([ ]?[*& ])*)+ + + + + + + + + + + + + all|([0-9]*:[0-9]*) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + error + warning + style + performance + portability + information + + + + + + Obsolescent + Obsolete + + + + + + + + + + + c99 + + + + + + + + + + + any + variadic + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ([-!]?[0-9]*(\.[0-9]+)?([eE][-+]?[0-9]+)?[,:])*([-!]?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+)?)? + + + + + + + + + strlen + sizeof + mul + + + + + + + + + + + + + + + value + + + + + + + + + + + + + argvalue + + + + + + + + + + + + + + + + + + + + + + + + first + middle + last + + + + + + + + + + + + + + [.][a-z]+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + std-like + + + + + std-like + + + + + + + + + erase + insert + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + array-like + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bool + char + short + int + long + long long + + + + + + + 1 + 2 + 4 + 8 + + + + + + + s + u + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 40 + + + + + + 0 + 2 + + + + + + in + out + inout + + + + + + true + false + + + + + + [a-zA-Z_][a-zA-Z_0-9]* + + + + + + [a-zA-Z_][a-zA-Z_0-9:]* + + + + + + [a-zA-Z_][a-zA-Z_0-9:,]* + + + + + + malloc(:[1-5])?|calloc(:[1-5],[1-5])?|strdup(:[1-5])? + + + + + + resize + clear + push + pop + find + find-const + insert + erase + change-content + change-internal + change + + + + + + at_index + item + buffer + buffer-nt + start-iterator + end-iterator + iterator + size + empty + + + + + + 1 + 9223372036854775807 + + + diff --git a/cppcheck-2.14.0/cfg/cppcheck-lib.cfg b/cppcheck-2.14.0/cfg/cppcheck-lib.cfg new file mode 100644 index 00000000..6a76155a --- /dev/null +++ b/cppcheck-2.14.0/cfg/cppcheck-lib.cfg @@ -0,0 +1,72 @@ + + + + false + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + -50:50 + + + + + + false + + + + + + -50:50 + + + + + + false + + + + + + diff --git a/cppcheck-2.14.0/cfg/cppunit.cfg b/cppcheck-2.14.0/cfg/cppunit.cfg new file mode 100644 index 00000000..1caf400d --- /dev/null +++ b/cppcheck-2.14.0/cfg/cppunit.cfg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + false + + + + + + + + + false + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/dpdk.cfg b/cppcheck-2.14.0/cfg/dpdk.cfg new file mode 100644 index 00000000..32ef8bca --- /dev/null +++ b/cppcheck-2.14.0/cfg/dpdk.cfg @@ -0,0 +1,11 @@ + + + + true + + + + + + + diff --git a/cppcheck-2.14.0/cfg/embedded_sql.cfg b/cppcheck-2.14.0/cfg/embedded_sql.cfg new file mode 100644 index 00000000..81281fac --- /dev/null +++ b/cppcheck-2.14.0/cfg/embedded_sql.cfg @@ -0,0 +1,4 @@ + + + + diff --git a/cppcheck-2.14.0/cfg/emscripten.cfg b/cppcheck-2.14.0/cfg/emscripten.cfg new file mode 100644 index 00000000..0d647915 --- /dev/null +++ b/cppcheck-2.14.0/cfg/emscripten.cfg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/ginac.cfg b/cppcheck-2.14.0/cfg/ginac.cfg new file mode 100644 index 00000000..c40d5c92 --- /dev/null +++ b/cppcheck-2.14.0/cfg/ginac.cfg @@ -0,0 +1,12848 @@ + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + false + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + false + + + + + + + false + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + false + + + + + + false + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + diff --git a/cppcheck-2.14.0/cfg/gnu.cfg b/cppcheck-2.14.0/cfg/gnu.cfg new file mode 100644 index 00000000..e6b4fb85 --- /dev/null +++ b/cppcheck-2.14.0/cfg/gnu.cfg @@ -0,0 +1,1690 @@ + + + + free + get_current_dir_name + + + asprintf + free + + + xmalloc + xcalloc + xstrdup + xrealloc + free + xfree + + + + backtrace_symbols + free + + + pvalloc + free + + + false + + ((arg1 & 0xff00u) >> 8) | ((arg1 & 0x00ffu) << 8) + + + + + + + + + + + + false + + ((arg1 & 0xff000000ul) >> 24) | ((arg1 & 0x00ff0000ul) >> 8) | ((arg1 & 0x0000ff00ul) << 8) | ((arg1 & 0x000000fful) << 24) + + + + + + + + + + + + false + + ((arg1 & 0xff00000000000000ull) >> 56) | ((arg1 & 0x00ff000000000000ull) >> 40) | ((arg1 & 0x0000ff0000000000ull) >> 24) | ((arg1 & 0x000000ff00000000ull) >> 8) | ((arg1 & 0x00000000ff000000ull) << 8) | ((arg1 & 0x0000000000ff0000ull) << 24) | ((arg1 & 0x000000000000ff00ull) << 40) | ((arg1 & 0x00000000000000ffull) << 56) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + 0: + + + + + + false + + + + + + + 0: + + + + + + + + + + 0: + + + + + + + + + + + false + arg1 + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + 0: + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + true + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + true + + + + + + + + + + + + 0: + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + 0: + + + + + + 0: + + + + + + 1: + + + + + + + + + false + + + + + 0: + + + + + 0: + + + + + 1: + + + + + + + + + false + + + + + + + + + + + + + + + + + 1: + + + + 0: + + + + + + + + + + + + + 0: + + + + + + + + + false + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + arg1>=0 && arg1<=0x7F + false + + + + 0:255 + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + 0: + + + + + + + + + + + + + + + + + + + false + + + + + + + + + 0: + + + + + + + + + + + + + + + 0: + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + 0: + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + 0: + + + + + + + + + + + + + + + false + + + + + + 0: + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + false + + + + + 0: + + + + + 0: + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + 0: + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + 0: + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + + + + + + 0: + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + 0:4294967295 + + + + + 0:4294967295 + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + 0: + + + + + + + + + 0: + + + + + + + false + + + + + + + + + + + + 0: + + + + + 0: + + + + + + false + + + + + + + + + + + 0: + + + + + + false + + + + + + + + + + 0: + + + + 0: + + + + + + + + + + + + arg1==0 &0 + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + 1: + + + + + false + + + + + + + + + + + false + + + + 0: + + + + + + + + + 0: + + + + + + + + + + false + + + + 0: + + + + + + + + + 1: + + + + + + + + + + false + + + + 0: + + + + + + + + + 1: + + + + + + + + + + + + + + false + + + + + + + + + + 0: + + + + + + false + + + + + + + + + + + + + + + false + + + + + false + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + 1: + + + + + + + + + + -1: + + + + + + + + + + false + + + + + 0: + + + + + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + + + + + + + + + + true + + + + + + + + + mkostemp + mkstemps + mkostemps + close + + + close + epoll_create + + + close + epoll_create1 + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/googletest.cfg b/cppcheck-2.14.0/cfg/googletest.cfg new file mode 100644 index 00000000..c93862d6 --- /dev/null +++ b/cppcheck-2.14.0/cfg/googletest.cfg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/gtk.cfg b/cppcheck-2.14.0/cfg/gtk.cfg new file mode 100644 index 00000000..fafb3466 --- /dev/null +++ b/cppcheck-2.14.0/cfg/gtk.cfg @@ -0,0 +1,21360 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + g_thread_new + g_thread_try_new + g_thread_ref + g_thread_unref + g_thread_join + + + g_variant_iter_copy + g_variant_iter_new + g_variant_iter_free + + + g_source_new + g_idle_source_new + g_timeout_source_new + g_timeout_source_new_seconds + g_child_watch_source_new + g_cancellable_source_new + g_io_create_watch + g_source_ref + g_source_unref + + + g_date_time_new + g_date_time_new_now + g_date_time_new_now_local + g_date_time_new_now_utc + g_date_time_new_from_unix_local + g_date_time_new_from_unix_utc + g_date_time_new_from_timeval_local + g_date_time_new_from_timeval_utc + g_date_time_new_local + g_date_time_new_utc + g_date_time_add + g_date_time_add_years + g_date_time_add_months + g_date_time_add_weeks + g_date_time_add_days + g_date_time_add_hours + g_date_time_add_minutes + g_date_time_add_seconds + g_date_time_add_full + g_date_time_to_timezone + g_date_time_to_local + g_date_time_to_utc + g_date_time_ref + g_date_time_unref + + + g_dir_open + g_dir_rewind + g_dir_close + + + g_timer_new + g_timer_destroy + + + g_file_attribute_info_list_new + g_file_attribute_info_list_dup + g_file_attribute_info_list_ref + g_file_attribute_info_list_unref + + + g_slist_alloc + g_slist_copy + g_slist_copy_deep + g_slist_free + g_slist_free_1 + g_slist_free_full + + + g_variant_new + g_variant_new_va + g_variant_new_boolean + g_variant_new_byte + g_variant_new_int16 + g_variant_new_uint16 + g_variant_new_int32 + g_variant_new_uint32 + g_variant_new_int64 + g_variant_new_uint64 + g_variant_new_handle + g_variant_new_double + g_variant_new_string + g_variant_new_take_string + g_variant_new_printf + g_variant_new_signature + g_variant_new_object_path + g_variant_new_variant + g_variant_new_objv + g_variant_new_strv + g_variant_new_bytestring + g_variant_new_bytestring_array + g_variant_new_maybe + g_variant_new_array + g_variant_new_tuple + g_variant_new_dict_entry + g_variant_new_fixed_array + g_variant_new_from_data + g_variant_new_from_bytes + g_variant_builder_end + g_variant_new_parsed_va + g_variant_new_parsed + g_variant_byteswap + g_variant_get_child_value + g_variant_get_normal_form + g_variant_parse + g_variant_ref + g_variant_take_ref + g_variant_ref_sink + g_variant_unref + + + g_variant_iter_new + g_variant_iter_free + + + g_variant_type_new + g_variant_type_copy + g_variant_type_new_array + g_variant_type_new_dict_entry + g_variant_type_new_maybe + g_variant_type_new_tuple + g_variant_type_free + + + g_allocator_new + g_allocator_free + + + g_bookmark_file_new + g_bookmark_file_free + + + g_srv_target_new + g_srv_target_free + + + g_string_chunk_new + g_string_chunk_free + + + g_test_log_buffer_new + g_test_log_buffer_free + + + g_value_array_new + g_value_array_free + + + g_cache_new + g_cache_destroy + + + g_cclosure_new + g_cclosure_new_swap + g_cclosure_new_object + g_cclosure_new_object_swap + g_closure_new_object + g_closure_new_simple + g_closure_ref + g_closure_unref + + + g_array_new + g_array_sized_new + g_array_ref + g_array_free + g_array_unref + + + g_async_queue_new + g_async_queue_new_full + g_async_queue_ref + g_async_queue_unref + + + g_byte_array_new + g_byte_array_sized_new + g_byte_array_new_take + g_byte_array_sized_new + g_bytes_unref_to_array + g_byte_array_ref + g_byte_array_free + g_byte_array_unref + + + g_checksum_new + g_checksum_copy + g_checksum_free + + + g_main_loop_new + g_main_new + g_main_loop_ref + g_main_loop_unref + g_main_destroy + + + g_main_context_new + g_main_context_ref + g_main_context_unref + g_main_destroy + + + g_thread_pool_new + g_thread_pool_free + + + g_error_copy + g_error_new_valist + g_error_new_literal + g_error_new + g_error_free + + + g_string_new + g_string_new_len + g_string_sized_new + g_variant_print_string + g_string_free + + + g_ptr_array_new + g_ptr_array_new_full + g_ptr_array_new_with_free_func + g_ptr_array_ref + g_ptr_array_free + g_ptr_array_unref + + + g_pattern_spec_new + g_pattern_spec_free + + + g_key_file_new + g_key_file_ref + g_key_file_free + g_key_file_unref + + + g_io_module_scope_new + g_io_module_scope_free + + + g_ascii_strdown + g_ascii_strup + g_base64_decode + g_base64_encode + g_bookmark_file_get_description + g_bookmark_file_get_mime_type + g_bookmark_file_get_title + g_bookmark_file_to_data + g_build_filename + g_build_filenamev + g_build_path + g_build_pathv + g_bytes_unref_to_data + g_compute_checksum_for_bytes + g_compute_checksum_for_data + g_compute_checksum_for_string + g_compute_hmac_for_data + g_compute_hmac_for_string + g_convert + g_convert_with_fallback + g_convert_with_iconv + g_credentials_to_string + g_date_time_format + g_filename_display_basename + g_filename_display_name + g_filename_from_uri + g_filename_to_uri + g_get_codeset + g_get_current_dir + g_get_locale_variants + g_key_file_get_start_group + g_key_file_to_data + g_malloc + g_realloc + g_malloc0 + g_malloc0_n + g_malloc_n + g_realloc_n + g_memdup + g_path_get_basename + g_path_get_dirname + g_slice_alloc + g_slice_alloc0 + g_slice_copy + g_strcompress + g_strconcat + g_strdup + g_strdup_printf + g_strdup_vprintf + g_strescape + g_strjoin + g_strjoinv + g_strndup + g_strnfill + g_time_val_to_iso8601 + g_try_malloc + g_try_realloc + g_try_malloc0 + g_try_malloc0_n + g_try_malloc_n + g_try_realloc_n + g_ucs4_to_utf16 + g_ucs4_to_utf8 + g_unicode_canonical_decomposition + g_utf16_to_ucs4 + g_utf16_to_utf8 + g_utf8_casefold + g_utf8_collate_key + g_utf8_collate_key_for_filename + g_utf8_normalize + g_utf8_strdown + g_utf8_strreverse + g_utf8_strup + g_utf8_substring + g_utf8_to_ucs4 + g_utf8_to_ucs4_fast + g_utf8_to_ucs4_fast + g_utf8_to_utf16 + g_key_file_get_locale_string + g_key_file_get_value + g_key_file_get_string + g_key_file_get_boolean_list + g_key_file_get_integer_list + g_key_file_get_double_list + g_key_file_get_comment + g_dbus_proxy_get_name_owner + g_file_info_get_attribute_as_string + g_file_attribute_matcher_to_string + g_app_launch_context_get_environment + g_app_launch_context_get_startup_notify_id + g_filename_completer_get_completion_suffix + g_inet_address_mask_to_string + g_variant_dup_string + g_variant_dup_bytestring + g_variant_get_objv + g_variant_get_strv + g_variant_print + g_datalist_id_dup_data + g_dir_make_tmp + g_filename_from_utf8 + g_filename_to_utf8 + g_file_read_link + g_find_program_in_path + g_format_size + g_format_size_for_display + g_format_size_full + g_hostname_to_ascii + g_hostname_to_unicode + g_locale_from_utf8 + g_locale_to_utf8 + g_markup_escape_text + g_markup_printf_escaped + g_markup_vprintf_escaped + g_match_info_expand_references + g_match_info_fetch + g_match_info_fetch_named + g_option_context_get_help + g_regex_escape_nul + g_regex_escape_string + g_regex_replace + g_regex_replace_eval + g_regex_replace_literal + g_shell_quote + g_shell_unquote + g_uri_escape_string + g_uri_parse_scheme + g_uri_unescape_segment + g_uri_unescape_string + g_variant_type_dup_string + g_value_dup_string + g_register_data + g_free + + + g_hash_table_new_full + g_hash_table_new + g_hash_table_ref + g_hash_table_destroy + g_hash_table_unref + + + g_io_channel_unix_new + g_io_channel_win32_new_fd + g_io_channel_win32_new_socket + g_io_channel_win32_new_messages + g_io_channel_new_file + g_io_channel_ref + g_io_channel_close + g_io_channel_shutdown + g_io_channel_unref + + + g_emblemed_icon_get_emblems + g_list_alloc + g_list_copy + g_list_copy_deep + g_app_info_get_all + g_app_info_get_all_for_type + g_app_info_get_fallback_for_type + g_app_info_get_recommended_for_type + g_io_modules_load_all_in_directory + g_io_modules_load_all_in_directory_with_scope + g_hash_table_get_keys + g_hash_table_get_values + g_list_free + g_list_free_1 + g_list_free_full + + + g_regex_new + g_regex_ref + g_regex_unref + + + g_node_new + g_node_copy + g_node_copy_deep + g_node_destroy + + + g_time_zone_new + g_time_zone_new_local + g_time_zone_new_utc + g_time_zone_ref + g_time_zone_unref + + + g_markup_parse_context_new + g_markup_parse_context_free + + + g_mapped_file_new + g_mapped_file_new_from_fd + g_mapped_file_ref + g_mapped_file_free + g_mapped_file_unref + + + g_mutex_new + g_mutex_free + + + g_mem_chunk_new + g_mem_chunk_free + + + g_option_group_new + g_option_group_free + + + g_option_context_new + g_option_context_free + + + g_rand_new + g_rand_copy + g_rand_new_with_seed + g_rand_new_with_seed_array + g_rand_free + + + g_queue_new + g_queue_copy + g_queue_free + + + g_slice_new + g_slice_free + g_slice_free1 + + + g_sequence_new + g_sequence_free + + + g_completion_new + g_completion_free + + + g_chunk_new + g_chunk_free + + + g_bytes_new + g_bytes_new_take + g_bytes_new_static + g_bytes_new_with_free_func + g_bytes_new_from_bytes + g_byte_array_free_to_bytes + g_memory_output_stream_steal_as_bytes + g_variant_get_data_as_bytes + g_mapped_file_get_bytes + g_bytes_ref + g_bytes_unref + + + g_bookmark_file_get_uris + g_bookmark_file_get_groups + g_bookmark_file_get_applications + g_key_file_get_groups + g_key_file_get_keys + g_strdupv + g_strsplit + g_strsplit_set + g_uri_list_extract_uris + g_key_file_get_string_list + g_key_file_get_locale_string_list + g_file_info_list_attributes + g_file_info_get_attribute_stringv + g_app_launch_context_get_environment + g_filename_completer_get_completions + g_io_module_query + g_variant_dup_objv + g_variant_dup_bytestring_array + g_environ_setenv + g_environ_unsetenv + g_get_environ + g_listenv + g_match_info_fetch_all + g_regex_split + g_regex_split_full + g_regex_split_simple + g_regex_split_simple + g_variant_dup_strv + g_strfreev + + + g_hmac_new + g_hmac_copy + g_hmac_ref + g_hmac_unref + + + g_hook_alloc + g_hook_ref + g_hook_unref + g_hook_destroy + g_hook_free + + + g_date_new + g_date_new_dmy + g_date_new_julian + g_date_free + + + g_variant_builder_new + g_variant_builder_ref + g_variant_builder_unref + + + g_cond_new + g_cond_free + + + g_app_launch_context_new + g_app_info_create_from_commandline + g_app_info_dup + g_app_info_get_default_for_type + g_app_info_get_default_for_uri_scheme + g_application_new + g_application_get_dbus_connection + g_application_get_default + g_buffered_input_stream_new + g_buffered_output_stream_new + g_cancellable_new + g_charset_converter_new + g_converter_input_stream_new + g_converter_output_stream_new + g_credentials_new + g_data_input_stream_new + g_data_output_stream_new + g_dbus_auth_observer_new + g_dbus_connection_new_finish + g_dbus_connection_new_sync + g_dbus_connection_new_for_address_finish + g_dbus_connection_new_for_address_sync + g_dbus_message_new + g_dbus_message_new_signal + g_dbus_message_new_method_call + g_dbus_message_new_method_reply + g_dbus_message_new_method_error + g_dbus_message_new_method_error_valist + g_dbus_message_new_method_error_literal + g_dbus_object_manager_client_new_finish + g_dbus_object_manager_client_new_sync + g_dbus_object_manager_client_new_for_bus_finish + g_dbus_object_manager_client_new_for_bus_sync + g_dbus_object_manager_server_new + g_dbus_object_manager_server_get_connection + g_dbus_object_proxy_new + g_dbus_object_skeleton_new + g_dbus_proxy_new_finish + g_dbus_proxy_new_sync + g_dbus_proxy_new_for_bus_finish + g_dbus_proxy_new_for_bus_sync + g_emblemed_icon_new + g_emblem_new + g_emblem_new_with_origin + g_file_icon_new + g_file_icon_get_file + g_file_info_new + g_file_info_dup + g_file_info_get_icon + g_file_info_get_symbolic_icon + g_file_info_get_attribute_object + g_file_info_get_deletion_date + g_filename_completer_new + g_inet_address_mask_new + g_inet_address_mask_new_from_string + g_inet_address_mask_get_address + g_inet_socket_address_new + g_inet_socket_address_get_address + g_initable_new + g_initable_new_valist + g_initable_newv + g_io_module_new + g_io_module_scope_new + g_keyfile_settings_backend_new + g_memory_input_stream_new + g_memory_input_stream_new_from_data + g_memory_input_stream_new_from_bytes + g_memory_output_stream_new + g_memory_output_stream_new_resizable + g_memory_settings_backend_new + g_null_settings_backend_new + g_menu_item_new + g_menu_item_new_section + g_menu_item_new_submenu + g_menu_item_new_from_model + g_menu_new + g_mount_operation_new + g_network_address_new + g_network_service_new + g_object_new + g_param_spec_pool_new + g_pollable_source_new + g_private_new + g_proxy_address_new + g_ptr_array_sized_new + g_relation_new + g_scanner_new + g_settings_new + g_signal_type_cclosure_new + g_simple_action_group_new + g_simple_action_new + g_simple_async_result_new + g_simple_permission_new + g_socket_client_new + g_socket_listener_new + g_socket_new + g_socket_service_new + g_tcp_wrapper_connection_new + g_test_dbus_new + g_themed_icon_new + g_threaded_socket_service_new + g_tls_client_connection_new + g_tls_file_database_new + g_tls_password_new + g_tls_server_connection_new + g_unix_signal_source_new + g_zlib_compressor_new + g_zlib_decompressor_new + g_object_ref + g_object_unref + gtk_widget_destroy + + + g_tree_new + g_tree_new_full + g_tree_new_with_data + g_tree_ref + g_tree_unref + + + g_file_attribute_matcher_new + g_file_attribute_matcher_subtract + g_file_attribute_matcher_ref + g_file_attribute_matcher_unref + + + true + + + + true + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + false + + + false + + + false + + + + false + + + + + + + + + + false + + + + + + + false + + + false + + + false + + + false + + + false + + + + false + + + + + + + + + + + + + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + false + + + + false + + + + + + + + + false + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + arg1 + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + g_value_set_object_take_ownership has been deprecated since version 2.4 and should not be used in newly-written code. Use g_value_take_object() instead. + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + g_value_set_string_take_ownership has been deprecated since version 2.4 and should not be used in newly-written code. Use g_value_take_string() instead. + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + false + + + + false + + + + false + + + + false + + + + + + + + 0:255 + + + + + false + + + + false + + + + false + + + + + + + + 0:255 + + + + + false + + + + + + + + 0:255 + + + + + false + + + + + + + + 0:255 + + + + + false + + + + + + + + 0:255 + + + + + false + + + + + + + + 0:255 + + + + + false + + + + + + + + 0:255 + + + + + false + + + + + + + + 0:255 + + + + + false + + + + + + + + 0:255 + + + + + false + + + + + + + + 0:255 + + + + + false + + + + + + + + 0:255 + + + + + false + + + + + + + + 0:255 + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + 0,2:36 + + + + + false + + + + + + + + + + + 0,2:36 + + + + + false + + + + + + + + + + + + + + false + + + + + + + 0:255 + + + + + + false + + + + + + + 0:255 + + + + + false + + + + + + + + 0:255 + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + g_basename has been deprecated since version 2.2 and should not be used in newly-written code. Use g_path_get_basename() instead, but notice that g_path_get_basename() allocates new memory for the returned string, unlike this function which returns a pointer into the argument. + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + + + + false + + + + false + + + + false + + + g_dirname is deprecated and should not be used in newly-written code. Use g_path_get_dirname() instead. + + + + + + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + + 0: + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + + + + + false + + + + false + + + + false + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + false + + + + + false + + + + + + + + + + + false + + + + + + + + 1: + + + + + false + + + + false + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + false + + + + + + + + + false + + + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + + 0: + + + + + + + + + + 0: + + + + + 0: + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + 0: + + + + + + + + + + + + false + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + + + + + + false + + + + + false + + + + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + + false + + + g_strcasecmp has been deprecated since version 2.2 and should not be used in newly-written code. + + + + + + + + + + + + + + + + false + arg1 + + + + + + + + + + false + arg1 + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + false + + + + + false + + + + + + + + false + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + g_strncasecmp has been deprecated since version 2.2 and should not be used in newly-written code. + + + + + + + + + + + + + 0: + + + + + false + + + + + + + + + 0: + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + 0: + + + + + + false + + + + + 0: + + + + + 0: + + + + + + + false + + + + + 0: + + + + + + + false + + + + 0: + + + + + 0: + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + + + 0: + + + + + + + + + + + 0: + + + + + 0: + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + false + + + + + + + + + + + + + + false + + + + false + + + + + false + + + + + + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + g_string_down has been deprecated since version 2.2 and should not be used in newly-written code. This function uses the locale-specific tolower() function, which is almost never the right thing. Use g_string_ascii_down() or g_utf8_strdown() instead. + + + + + + + + + false + + + + + + + + + + + + + false + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + 0: + + + + + + false + + g_string_sprintf is deprecated and should not be used in newly-written code. This function has been renamed to g_string_printf(). + + + + + + + + + + + + + + + + false + + g_string_sprintfa is deprecated and should not be used in newly-written code. This function has been renamed to g_string_append_printf() + + + + + + + + + + + + + + + false + + + + + false + + g_string_up has been deprecated since version 2.2 and should not be used in newly-written code. This function uses the locale-specific toupper() function, which is almost never the right thing. Use g_string_ascii_up() or g_utf8_strup() instead. + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + false + + + + + + + + + false + + g_type_class_add_private has been deprecated since version 2.58 and should not be used in newly-written code. Use the G_ADD_PRIVATE() macro with the G_DEFINE_* family of macros to add instance private data to a type. + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + false + + + + + + + + + + + + + true + + + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + gtk_hbox_new has been deprecated since version 3.2 and should not be used in newly-written code. You can use gtk_box_new() with GTK_ORIENTATION_HORIZONTAL instead, which is a quick and easy change. But the recommendation is to switch to GtkGrid, since GtkBox is going to go away eventually. See Migrating from other containers to GtkGrid. + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + gtk_vbox_new has been deprecated since version 3.2 and should not be used in newly-written code. You can use gtk_box_new() with GTK_ORIENTATION_VERTICAL instead, which is a quick and easy change. But the recommendation is to switch to GtkGrid, since GtkBox is going to go away eventually. See Migrating from other containers to GtkGrid. + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + false + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + true + + gtk_exit is deprecated and should not be used in newly-written code. Use the standard exit() function instead. + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + false + + gtk_signal_connect_object_while_alive is deprecated and should not be used in newly-written code. Use g_signal_connect_object() instead, passing G_CONNECT_SWAPPED as connect_flags. + + + + + + + + false + + + + + + false + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + + + false + + gtk_label_get is deprecated and should not be used in newly-written code. + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + + false + + + + false + + + + + false + + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/icu.cfg b/cppcheck-2.14.0/cfg/icu.cfg new file mode 100644 index 00000000..5e1f9163 --- /dev/null +++ b/cppcheck-2.14.0/cfg/icu.cfg @@ -0,0 +1,221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/kde.cfg b/cppcheck-2.14.0/cfg/kde.cfg new file mode 100644 index 00000000..5973916f --- /dev/null +++ b/cppcheck-2.14.0/cfg/kde.cfg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/libcerror.cfg b/cppcheck-2.14.0/cfg/libcerror.cfg new file mode 100644 index 00000000..ce621e99 --- /dev/null +++ b/cppcheck-2.14.0/cfg/libcerror.cfg @@ -0,0 +1,203 @@ + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + 0: + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/libcurl.cfg b/cppcheck-2.14.0/cfg/libcurl.cfg new file mode 100644 index 00000000..381fbb23 --- /dev/null +++ b/cppcheck-2.14.0/cfg/libcurl.cfg @@ -0,0 +1,487 @@ + + + + + + + + + + + curl_easy_init + curl_easy_duphandle + curl_easy_cleanup + + + curl_easy_escape + curl_easy_unescape + curl_escape + curl_unescape + curl_free + + + curl_getenv + curl_maprintf + curl_mvaprintf + free + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + 0: + + + + + false + + + + + + + + + + + + + + + false + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + 0: + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + 0: + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + false + + + + + + + + + + 0: + + + + + false + + + + + + false + + + This function will be removed from the public libcurl API in a near future. It will instead be made "available" by source code access only, and then as curlx_getenv(). + + + + + + + + false + + + These functions will be removed from the public libcurl API in the future. Do not use them in any new programs or projects. + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + 0: + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + Usage of curl_multi_socket is deprecated, whereas the function is equivalent to curl_multi_socket_action with ev_bitmask set to 0. + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + These functions will be removed from the public libcurl API in the future. Do not use them in any new programs or projects. + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + 0: + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + These functions will be removed from the public libcurl API in a near future. They will instead be made "available" by source code access only, and then as curlx_strequal() and curlx_strenqual(). + + + + + + + + + + + + + + false + + + + + These functions will be removed from the public libcurl API in a near future. They will instead be made "available" by source code access only, and then as curlx_strequal() and curlx_strenqual(). + + + + + + + + + + + + 0: + + + + + false + + + + + + + + + + 0: + + + diff --git a/cppcheck-2.14.0/cfg/libsigc++.cfg b/cppcheck-2.14.0/cfg/libsigc++.cfg new file mode 100644 index 00000000..f03804fa --- /dev/null +++ b/cppcheck-2.14.0/cfg/libsigc++.cfg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + false + + + + + diff --git a/cppcheck-2.14.0/cfg/lua.cfg b/cppcheck-2.14.0/cfg/lua.cfg new file mode 100644 index 00000000..bf3971df --- /dev/null +++ b/cppcheck-2.14.0/cfg/lua.cfg @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + + + true + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + 1: + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + 0: + + + + + false + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + true + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/mfc.cfg b/cppcheck-2.14.0/cfg/mfc.cfg new file mode 100644 index 00000000..1a8100f2 --- /dev/null +++ b/cppcheck-2.14.0/cfg/mfc.cfg @@ -0,0 +1,271 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/microsoft_atl.cfg b/cppcheck-2.14.0/cfg/microsoft_atl.cfg new file mode 100644 index 00000000..3cd784bb --- /dev/null +++ b/cppcheck-2.14.0/cfg/microsoft_atl.cfg @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/microsoft_sal.cfg b/cppcheck-2.14.0/cfg/microsoft_sal.cfg new file mode 100644 index 00000000..36371a36 --- /dev/null +++ b/cppcheck-2.14.0/cfg/microsoft_sal.cfg @@ -0,0 +1,490 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/microsoft_unittest.cfg b/cppcheck-2.14.0/cfg/microsoft_unittest.cfg new file mode 100644 index 00000000..35c9c5a9 --- /dev/null +++ b/cppcheck-2.14.0/cfg/microsoft_unittest.cfg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/motif.cfg b/cppcheck-2.14.0/cfg/motif.cfg new file mode 100644 index 00000000..f4221a23 --- /dev/null +++ b/cppcheck-2.14.0/cfg/motif.cfg @@ -0,0 +1,318 @@ + + + + false + + + + + + false + + + + + false + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + false + + + + + false + + + + + + + MrmCloseHierarchy + MrmOpenHierarchy + MrmOpenHierarchyPerDisplay + + + + XmStringFree + XmStringCreateLocalized + XmStringCreateSimple + XmStringGenerate + XmCvtCTToXmString + + + XtFree + XmFontListEntryGetTag + XmTextGetString + XmCvtXmStringToCT + XmWidgetGetBaselines + + + XmFontListEntryFree + XmFontListCreate + XmFontListAppendEntry + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + false + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XtFree + XtMalloc + XtCalloc + XtRealloc + XtNew + XtNewString + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + XOpenDisplay + XCloseDisplay + + + diff --git a/cppcheck-2.14.0/cfg/nspr.cfg b/cppcheck-2.14.0/cfg/nspr.cfg new file mode 100644 index 00000000..99ad4658 --- /dev/null +++ b/cppcheck-2.14.0/cfg/nspr.cfg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/ntl.cfg b/cppcheck-2.14.0/cfg/ntl.cfg new file mode 100644 index 00000000..6c42dfb0 --- /dev/null +++ b/cppcheck-2.14.0/cfg/ntl.cfg @@ -0,0 +1,6907 @@ + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + diff --git a/cppcheck-2.14.0/cfg/opencv2.cfg b/cppcheck-2.14.0/cfg/opencv2.cfg new file mode 100644 index 00000000..80b51ad7 --- /dev/null +++ b/cppcheck-2.14.0/cfg/opencv2.cfg @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + cv::fastMalloc + cv::fastFree + + + + + true + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + 0: + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + diff --git a/cppcheck-2.14.0/cfg/opengl.cfg b/cppcheck-2.14.0/cfg/opengl.cfg new file mode 100644 index 00000000..72dcaddb --- /dev/null +++ b/cppcheck-2.14.0/cfg/opengl.cfg @@ -0,0 +1,580 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + diff --git a/cppcheck-2.14.0/cfg/openmp.cfg b/cppcheck-2.14.0/cfg/openmp.cfg new file mode 100644 index 00000000..d853af22 --- /dev/null +++ b/cppcheck-2.14.0/cfg/openmp.cfg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + omp_target_alloc + omp_target_free + + + omp_alloc + omp_free + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + This routine has been deprecated. See OpenMP specification. + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + This routine has been deprecated. See OpenMP specification. + + + + + + + false + + + + + + 1: + + + + + false + + + + + + + + 0: + + + + + false + + + + + + + + 0: + + + diff --git a/cppcheck-2.14.0/cfg/openssl.cfg b/cppcheck-2.14.0/cfg/openssl.cfg new file mode 100644 index 00000000..d6dd39bd --- /dev/null +++ b/cppcheck-2.14.0/cfg/openssl.cfg @@ -0,0 +1,174 @@ + + + + + + + + + EVP_CIPHER_CTX_new + EVP_CIPHER_CTX_free + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + 0: + + + diff --git a/cppcheck-2.14.0/cfg/pcre.cfg b/cppcheck-2.14.0/cfg/pcre.cfg new file mode 100644 index 00000000..2ab79ee9 --- /dev/null +++ b/cppcheck-2.14.0/cfg/pcre.cfg @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/posix.cfg b/cppcheck-2.14.0/cfg/posix.cfg new file mode 100644 index 00000000..413ecae4 --- /dev/null +++ b/cppcheck-2.14.0/cfg/posix.cfg @@ -0,0 +1,6438 @@ + + + + + + + + false + + + + + + + + + + false + + + + + + + 0: + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + 0: + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + 0: + + + + + + + + + + + + false + + + + + + + + + 0: + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + 0: + + + + + + + arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1 <=0x5A || arg1>=0x61 && arg1 <=0x7A + false + + + + 0:255 + + + + + + + + + + + arg1>='A' && arg1<='Z' || arg1>='a' && arg1 <='z' + false + + + + 0:255 + + + + + + + + + + + arg1==' ' || arg1=='\t' + false + + + + 0:255 + + + + + + + + + + + arg1==0x7F || arg1<=0x1F + false + + + + 0:255 + + + + + + + + + + + arg1>='0' && arg1<='9' + false + + + + 0:255 + + + + + + + + + + + arg1>=0x21 && arg1<=0x7E + false + + + + 0:255 + + + + + + + + + + + arg1>=0x61 && arg1<=0x7A + false + + + + 0:255 + + + + + + + + + + + arg1>=0x20 && arg1<=0x7E + false + + + + 0:255 + + + + + + + + + + + arg1>=0x21 && arg1<=0x2F || arg1>=0x3A && arg1<=0x40 || arg1>=0x5B && arg1<=0x60 || arg1>=0x7B && arg1<=0x7E + false + + + + 0:255 + + + + + + + + + + + arg1>=0x09 && arg1<=0x0D || arg1==0x20 + false + + + + 0:255 + + + + + + + + + + + arg1>=0 && arg1<=0x7F + false + + + + + 0:255 + + + + + + + arg1>=0x41 && arg1<=0x5A + false + + + + 0:255 + + + + + + + + + + arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1<=0x46 || arg1>=0x61 && arg1<=0x66 + false + + + + 0:255 + + + + + + + + + false + + + + + 0: + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + dlopen + dlclose + + + + false + + + + + + 0: + + + + + + + + + 0: + + + + + false + + + + + + 0: + + + + + + + + + + false + + + + + + 0: + + + + + + + + + + + + false + + + + + 0: + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + 0: + + + + + false + + + + + 0: + + + + + 0: + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + 0: + + + + + + + + false + + + + + + 0: + + + + + + + + + false + + + + + + 0: + + + + + + + + false + + + + + + + + + + false + + + + + + 0: + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + 0: + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + 0: + + + + + + + + + false + + + + + + 0: + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + 0: + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + 0: + + + + + false + + + + + 0:999999 + + Obsolescent function 'usleep' called. It is recommended to use 'nanosleep' or 'setitimer' instead. +The obsolescent function 'usleep' is called. POSIX.1-2001 declares usleep() function obsolescent and POSIX.1-2008 removes it. It is recommended that new applications use the 'nanosleep' or 'setitimer' function. + + + + true + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + 0: + + + + + + + false + + Non reentrant function 'getrpcent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getrpcent_r'. + + + + + false + + + + + + + + Non reentrant function 'getrpcbyname' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getrpcbyname_r'. + + + + + false + + + + + + + Non reentrant function 'getrpcbynumber' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getrpcbynumber_r'. + + + + + false + + + Non reentrant function 'getprotoent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getprotoent_r'. + + + + + false + + + + + + + + + Non reentrant function 'getprotobyname' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getprotobyname_r'. + + + + + false + + + + + + + Non reentrant function 'getprotobynumber' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getprotobynumber_r'. + + + + + false + + + Non reentrant function 'getservent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getservent_r'. + + + + + false + + + + + + + + + + + + + Non reentrant function 'getservbyname' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getservbyname_r'. + + + + + false + + + + + + + + + + + + Non reentrant function 'getservbyport' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getservbyport_r'. + + + + + false + + + Non reentrant function 'getnetent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getnetent_r'. + + + + + false + + + + + + + + + Non reentrant function 'getnetbyname' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getnetbyname_r'. + + + + + false + + + + + + + + + + + Non reentrant function 'getnetbyaddr' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getnetbyaddr_r'. + + + + + false + + + Non reentrant function 'gethostent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'gethostent_r'. + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + Non reentrant function 'gethostbyname2' called. For threadsafe applications it is recommended to use the reentrant replacement function 'gethostbyname2_r'. + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + 0: + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + Obsolete function 'mktemp' called. It is recommended to use 'mkstemp' or 'mkdtemp' instead. +The function 'mktemp' is considered to be dangerous due to race conditions and some implementations generating only up to 26 different filenames out of each template. This function has been removed in POSIX.1-2008. Use 'mkstemp' or 'mkdtemp' instead. + + + + false + + + + + + + + + 0: + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + 0: + + + + + + + + + + + 0: + + + + + + + + + + + false + + + + + 0: + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + 0: + + + + + + + + + + false + + + + + 0: + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + 0: + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + 0: + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + 0: + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + 0: + + + + + + + + false + + + 0: + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + 0: + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + 0: + + + + + false + + + + + + + + + + + + + 0: + + + + + false + + + + + + 0: + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + 0: + + + + + + + + + + + + + false + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + + + + + 0: + + + + + 0: + + + + + + + 1: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0: + + + + + + + + 0: + + + + + + + + + + + 0: + + + + + + + + 0: + + + + + + + + + false + + + + + + 0: + + + + + + + + + 0: + + + + + + + false + + + 0: + + + + + + + + + + + 0: + + + + + + + + + + + + + 0: + + + + + + + + 0: + + + + + + + + + + + + + 0: + + + + + + + + + 0: + + + + + + + + + + + + + + + + 0: + + + + + + + + + + + + + 0: + + + + + + + + + 0: + + + + + + + + + + + + + + 0: + + + + + + + + + + + + + + + + + + + 0: + + + + + + + + + 0: + + + + + + + + + + + + + + + false + + + + + + + + 1: + + + + + + + + + + + + + 0: + + + + + + + + + + + false + + + + + + + + 1: + + + + + + + + + + + + + 0: + + + + + + + + + + + false + + + + + + + + 1: + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + 0: + + + + + + + + + + + + + false + + + + + + 0: + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + 0: + + + + + + + + + + false + + + + + + + + + + + 0: + + + + 0: + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + 0: + + + + + + + 0: + + + + + + + 0: + + + + + + + + + false + + + + + + + + + + + false + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + false + Non reentrant function 'getpwent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwent_r'. + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + + + + + + + false + + + + + + + + + + + 0: + + + + + + + + + + + false + + + + + + Non reentrant function 'getpwnam' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwnam_r'. + + + + + Non reentrant function 'strtok' called. For threadsafe applications it is recommended to use the reentrant replacement function 'strtok_r'. + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + 0: + + + + + + + + + + + false + + + + + Non reentrant function 'getpwuid' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwuid_r'. + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + 1: + + + + + + + + + + + false + + + + + + + + + + + + + + + + 1: + + + + + + + + + + + + false + + + + + + + + + + + 0: + + + + + 0: + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + 0: + + + + + + + false + + + + + + + + + + + + + 0: + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + 0: + + + + + + + + + + + false + + + + 0: + + + + + + false + + + + + + + + + + + + + + + false + + + + 0: + + + + + + + + + + + false + + + + 0: + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + 0: + + + + + + + + + 0:2 + + + + + + + false + + + + + 0: + + + + + + + + + 0:2 + + + + + + + false + + + + + 0: + + + + + + + + + 0:2 + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + Non reentrant function 'localtime' called. For threadsafe applications it is recommended to use the reentrant replacement function 'localtime_r'. + + + + false + + + + + + + + + + + + + + + + false + + + + + + + Non reentrant function 'readdir' called. For threadsafe applications it is recommended to use the reentrant replacement function 'readdir_r'. + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + + false + + + + + 0: + + + + + + + + + + 0: + + + + + + + + + + + + false + + + + + 0: + + + + + + + + + + + + + + 0: + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + Non reentrant function 'gmtime' called. For threadsafe applications it is recommended to use the reentrant replacement function 'gmtime_r'. + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + Obsolescent function 'makecontext' called. Applications are recommended to be rewritten to use POSIX threads. + + + + false + + + + + + + + Obsolescent function 'swapcontext' called. Applications are recommended to be rewritten to use POSIX threads. + + + + false + + + + + Obsolescent function 'getcontext' called. Applications are recommended to be rewritten to use POSIX threads. + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + 0: + + + + + + false + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + true + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + 0: + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + 0: + + + + + + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + + + + + + + + + + + false + + + + 1: + + + + + + + + 0: + + + + + + false + + + + + + + + + + + + + + + + 0: + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + Non reentrant function 'tempnam' called. For threadsafe applications it is recommended to use the reentrant replacement function 'tempnam_r'. + + + + + + false + + + + + + + + + + + + + Non reentrant function 'crypt' called. For threadsafe applications it is recommended to use the reentrant replacement function 'crypt_r'. + + + + + + false + + + 0: + + + + Non reentrant function 'ttyname' called. For threadsafe applications it is recommended to use the reentrant replacement function 'ttyname_r'. + + + + + + false + + + 0: + + + + + + + + + + + + 0: + + + + + + + false + + + + + + + + Non reentrant function 'getspnam' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getspnam_r'. + + + + + + false + + Non reentrant function 'getspent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getspent_r'. + + + + + + false + + + + + + + Non reentrant function 'fgetspent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'fgetspent_r'. + + + + + + false + + + + + + + + Non reentrant function 'sgetspent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'sgetspent_r'. + + + + + + false + + + + + + + Non reentrant function 'fgetpwent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'fgetpwent_r'. + + + + + + false + + Non reentrant function 'getgrent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getgrent_r'. + + + + + + false + + + + + + + + + 0: + + + + + + + + + + false + + + + + + + Non reentrant function 'fgetgrent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'fgetgrent_r'. + + + + false + + + + + + + + + + + + + Non reentrant function 'getnetgrent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getnetgrent_r'. + + + + + + false + + + + + + + + Non reentrant function 'getgrnam' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getgrnam_r'. + + + + + + false + + + + + + Non reentrant function 'getgrgid' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getgrgid_r'. + + + + + + + false + + + + + + + + + + + + + + + + + 0: + + + + + + + + + + + false + + + + + + + + + + + + + + + + 0: + + + + + + + + + + false + + Non reentrant function 'getlogin' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getlogin_r'. + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + 0:255 + + + + + + + + + + + + false + + + + + + + + + 0: + + + + + + false + + + + + Non reentrant function 'ctermid' called. For threadsafe applications it is recommended to use the reentrant replacement function 'ctermid_r'. + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + 0:2 + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + arg1==0 &0 + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + 0: + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + 0: + + + + + + + false + + + + + + 0: + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + true + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + + + + + + false + + + + + 0: + + + + + + + + + + + false + + + + + 0: + + + + + + + + + + + + false + + + + + 0: + + + + + + + + + + + + false + + + + + 0: + + + + + + + + + + + false + + + + + 0: + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + 0: + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + false + + + + + false + + + + + + + + + + 1: + + + + + + + + false + + + + + + + + + + + 0: + + + + + + + + false + + + + 0: + + + + + valloc + free + + + posix_memalign + free + + + scandir + free + + + strndup + wcsdup + free + + + getline + getdelim + free + + + mmap + mmap64 + munmap + + + open + mkstemp + creat + openat + socket + close + fdopen + + + opendir + fdopendir + closedir + + + fdopen + fclose + + + popen + pclose + + + mq_open + mq_close + + + getaddrinfo + freeaddrinfo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/python.cfg b/cppcheck-2.14.0/cfg/python.cfg new file mode 100644 index 00000000..30479cd8 --- /dev/null +++ b/cppcheck-2.14.0/cfg/python.cfg @@ -0,0 +1,674 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PyMem_Malloc + PyMem_Calloc + PyMem_Realloc + PyMem_Free + + + PyMem_RawMalloc + PyMem_RawCalloc + PyMem_RawRealloc + PyMem_RawFree + + + PyObject_Malloc + PyObject_Calloc + PyObject_Realloc + PyObject_Free + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + true + + + + + + + + true + + + + + + + + + false + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + 0: + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + false + NULL + + + + + + + + + + + + + + + false + NULL + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + NULL + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + 1: + + + + 0: + + + + + + false + + + + + + + + + false + + + + + 0: + + + + + + + + false + + + + + + 0: + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/qt.cfg b/cppcheck-2.14.0/cfg/qt.cfg new file mode 100644 index 00000000..824c4bf2 --- /dev/null +++ b/cppcheck-2.14.0/cfg/qt.cfg @@ -0,0 +1,5442 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + READ + + READ + WRITE + NOTIFY + + + + + connect + + + + + invokeMethod + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + false + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + false + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + 0: + + + + + false + + + + + 0: + + + + + false + + + + + + 0: + + + + + false + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + 0: + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + false + + + + 0: + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + 0: + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + 0,2:36 + + + + + + false + + + + + + + + + 0,2:36 + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + false + + + + + + + false + + + + + + + + + 0,2:36 + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + 0,2:36 + + + + + + false + + + + + + + + + 0,2:36 + + + + + + false + + + + + + + + + 0,2:36 + + + + + + false + + + + + + + + + 0,2:36 + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + true + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + :-1,1: + + + + 1:12 + + + + + 1:31 + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + 0: + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + 0: + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + false + + + + + + + + + false + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + false + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + QApplication + QMutexLocker + QRect + QRectF + QSize + QSizeF + QPoint + QPointF + QRegion + QTransform + + + QMutex + QRecursiveMutex + QSemaphore + QReadWriteLock + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/ruby.cfg b/cppcheck-2.14.0/cfg/ruby.cfg new file mode 100644 index 00000000..fbf037fb --- /dev/null +++ b/cppcheck-2.14.0/cfg/ruby.cfg @@ -0,0 +1,142 @@ + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + true + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/sdl.cfg b/cppcheck-2.14.0/cfg/sdl.cfg new file mode 100644 index 00000000..effc3eab --- /dev/null +++ b/cppcheck-2.14.0/cfg/sdl.cfg @@ -0,0 +1,354 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + -1 + + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + SDL_FreeSurface + SDL_CreateRGBSurface + SDL_CreateRGBSurfaceFrom + SDL_ConvertSurface + TTF_RenderUTF8_Blended + IMG_LoadPNG_RW + IMG_LoadJPG_RW + IMG_Load + + + SDL_DestroyMutex + SDL_CreateMutex + + + SDL_WaitThread + SDL_CreateThread + + + SDL_RWclose + SDL_RWFromFile + + + SDL_FreeRW + SDL_AllocRW + + + Mix_FreeMusic + Mix_LoadMUSType_RW + + diff --git a/cppcheck-2.14.0/cfg/sfml.cfg b/cppcheck-2.14.0/cfg/sfml.cfg new file mode 100644 index 00000000..4b9be7bb --- /dev/null +++ b/cppcheck-2.14.0/cfg/sfml.cfg @@ -0,0 +1,301 @@ + + + + false + + + + + false + + + + + + + false + + + + + false + + + + + false + + + + + false + + + + false + + + + + + false + + + + + + false + + + + + + + + 0: + + + + false + + + + + 0: + + + + + + + false + + + + 0: + + + + + + + false + + + + + + + false + + + + + + + false + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + false + + + + + + + + false + + + + false + + + + + + + + + + + + + + + + false + + + + false + + + + + + false + + + + + false + + + + + + false + + + + + false + + + + + + + false + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + false + + + + 0: + + + + false + + + + 0:100 + + + + false + + + + false + + + + + + false + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/sqlite3.cfg b/cppcheck-2.14.0/cfg/sqlite3.cfg new file mode 100644 index 00000000..0b0a439b --- /dev/null +++ b/cppcheck-2.14.0/cfg/sqlite3.cfg @@ -0,0 +1,1527 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + sqlite3_malloc + sqlite3_malloc64 + sqlite3_free + + + + sqlite3_str_new + sqlite3_str_finish + + + + sqlite3_str_finish + sqlite3_free + + + + sqlite3_mprintf + sqlite3_vmprintf + sqlite3_free + + + + sqlite3_expanded_sql + sqlite3_free + + + + + sqlite3_open + sqlite3_open16 + sqlite3_open_v2 + sqlite3_close + sqlite3_close_v2 + + + + + + false + + + + + + + 1: + + + + + + + 0: + + + + + + + + + + false + + + + + + + 1: + + + + + + + + + + + false + + + + + + + 1: + + + + + + + + + + false + + + + + + + + 1: + + + + + + false + + + + + + + 1: + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + 0: + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + 0: + + + + + + false + + + + + + + + + 0: + + + + + + false + + + + + + + + + 0: + + + + + + false + + + + + + + + + 0: + + + + + + false + + + + + + + + + 0: + + + + + + false + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + 0: + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + -1:127 + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + 1: + + + + + false + + + + + 1: + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + The sqlite3_prepare() interface is legacy and should be avoided + + + + + + + + + + + + + + + + + + + + + false + + The sqlite3_prepare16() interface is legacy and should be avoided + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + 0: + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + 0: + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/std.cfg b/cppcheck-2.14.0/cfg/std.cfg new file mode 100644 index 00000000..d2cc8e24 --- /dev/null +++ b/cppcheck-2.14.0/cfg/std.cfg @@ -0,0 +1,9060 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + + + + arg1>0?arg1:-arg1 + false + + + + + + + + + + + arg1>0?arg1:-arg1 + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + acos(arg1) + false + + + + -1.0:1.0 + + + + + + + acos(arg1) + false + + + + -1.0:1.0 + + + + + + + acos(arg1) + false + + + + -1.0:1.0 + + + + + + + acosh(arg1) + false + + + + 1.0: + + + + + + + acosh(arg1) + false + + + + 1.0: + + + + + + + acosh(arg1) + false + + + + 1.0: + + + + + + + false + + + + + + + + + + false + + + + + + + + + 26: + + + + + + + + + + + + + + + + + + + + + sqrt(arg1) + false + + + + 0.0: + + + + + + + sqrt(arg1) + false + + + + 0.0: + + + + + + + sqrt(arg1) + false + + + + 0.0: + + + + + + + + + false + + + + + + + + + + sinh(arg1) + false + + + + + + + + + + sinh(arg1) + false + + + + + + + + + + sinh(arg1) + false + + + + + + + + + + sin(arg1) + false + + + + + + + + + + sin(arg1) + false + + + + + + + + + + sin(arg1) + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + asin(arg1) + false + + + -1.0:1.0 + + + + + + + + asin(arg1) + false + + + -1.0:1.0 + + + + + + + + asin(arg1) + false + + + -1.0:1.0 + + + + + + + + + + false + + + + + + + + + + asinh(arg1) + false + + + + + + + + + + asinh(arg1) + false + + + + + + + + + + asinh(arg1) + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + + + + + + tan(arg1) + false + + + + + + + + + + tan(arg1) + false + + + + + + + + + + tan(arg1) + false + + + + + + + + + + + + false + + + + + + + + + + tanh(arg1) + false + + + + + + + + + + tanh(arg1) + false + + + + + + + + + + tanh(arg1) + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + 1:31 + + + + + + false + + + + + + + + + false + + + + + 1:31 + + + + + + false + + + + + + + + + + + atan(arg1) + false + + + + + + + + + + atan(arg1) + false + + + + + + + + + + atan(arg1) + false + + + + + + + + + + + + false + + + + + + + + + + + tgamma(arg1) + false + + + + + !0.0: + + + + + + + + tgamma(arg1) + false + + + + + !0.0: + + + + + + + + tgamma(arg1) + false + + + + + !0.0: + + + + + + + trunc(arg1) + false + + + + + + + + + + trunc(arg1) + false + + + + + + + + + + trunc(arg1) + false + + + + + + + + + + atanh(arg1) + false + + + + -1.0:1.0 + + + + + + + atanh(arg1) + false + + + + -1.0:1.0 + + + + + + + atanh(arg1) + false + + + + -1.0:1.0 + + + + + + + + + false + + + + + + + + + + atan2(arg1, arg2) + false + + + + + + + + + + + + + atan2(arg1, arg2) + false + + + + + + + + + + + + + atan2(arg1, arg2) + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + 0: + + + + 0: + + + + + + + ceil(arg1) + false + + + + + + + + + + ceil(arg1) + false + + + + + + + + + + ceil(arg1) + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + cbrt(arg1) + false + + + + + + + + + + cbrt(arg1) + false + + + + + + + + + + cbrt(arg1) + false + + + + + + + + + + cos(arg1) + false + + + + + + + + + + cos(arg1) + false + + + + + + + + + + cos(arg1) + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + 26: + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + :-1,1: + + + + + + + false + + + + + + + :-1,1: + + + + + true + + + + + + + + + + + erf(arg1) + false + + + + + + + + + + erf(arg1) + false + + + + + + + + + + erf(arg1) + false + + + + + + + + + + erfc(arg1) + false + + + + + + + + + + erfc(arg1) + false + + + + + + + + + + erfc(arg1) + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + exp(arg1) + false + + + + + + + + + + exp(arg1) + false + + + + + + + + + + exp(arg1) + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + exp2(arg1) + false + + + + + + + + + + exp2(arg1) + false + + + + + + + + + + exp2(arg1) + false + + + + + + + + + + expm1(arg1) + false + + + + + + + + + + expm1(arg1) + false + + + + + + + + + + expm1(arg1) + false + + + + + + + + + true + + + + + + + false + + + + + + + + + + + + fabs(arg1) + false + + + + + + + + + + fabs(arg1) + false + + + + + + + + + + fabs(arg1) + false + + + + + + + + + + fdim(arg1, arg2) + false + + + + + + + + + + + + + fdim(arg1, arg2) + false + + + + + + + + + + + + + fdim(arg1, arg2) + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + floor(arg1) + false + + + + + + + + + + floor(arg1) + false + + + + + + + + + + floor(arg1) + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + fmax(arg1,arg2) + false + + + + + + + + + + + + + fmax(arg1,arg2) + false + + + + + + + + + + + + + fmax(arg1,arg2) + false + + + + + + + + + + + + + fmin(arg1,arg2) + false + + + + + + + + + + + + + fmin(arg1,arg2) + false + + + + + + + + + + + + + fmin(arg1,arg2) + false + + + + + + + + + + + + + fmod(arg1,arg2) + false + + + + + + + !0.0 + + + + + + + fmod(arg1,arg2) + false + + + + + + + !0.0 + + + + + + + fmod(arg1,arg2) + false + + + + + + + !0.0 + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + 0: + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + 0: + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + hypot(arg1, arg2) + false + + + + + + + + + + + + + + + + hypot(arg1, arg2) + false + + + + + + + + + + + + + hypot(arg1, arg2) + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + 0:2 + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + 0: + + + + + + + + + + false + + + + + + + + + 0: + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + 0: + + + + 0: + + + + + + + + + + false + + + + + + + + 0: + + + + + + false + + + + + + + + 0: + + + + + + false + + + + + + + 0: + + + + + + + + + + + + false + + + + 0:255 + + + + + + false + + + + + + + + + false + + + + + + false + + + + + + + + + + + + 0: + + + + + + false + + + + + + + + + + + 0: + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + 0: + + + + + + false + + + + + + false + + + + 0:255 + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead. +The obsolete function 'gets' is called. With 'gets' you'll get a buffer overrun if the input data exceeds the size of the buffer. It is recommended to use the functions 'fgets' or 'gets_s' instead. + + + + + false + + + + + + + + + 0: + + + + + + + false + + + + + + + + + + + + arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1 <=0x5A || arg1>=0x61 && arg1 <=0x7A + false + + + + + 0:255 + + + + + + + arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1 <=0x5A || arg1>=0x61 && arg1 <=0x7A + false + + + + + + + + + + + arg1>='A' && arg1<='Z' || arg1>='a' && arg1 <='z' + false + + + + + 0:255 + + + + + + + arg1>='A' && arg1<='Z' || arg1>='a' && arg1 <='z' + false + + + + + + + + + + + arg1==' ' || arg1=='\t' + false + + + + + 0:255 + + + + + + + arg1==' ' || arg1=='\t' + false + + + + + + + + + + + arg1==0x7F || arg1<=0x1F + false + + + + + 0:255 + + + + + + + arg1==0x7F || arg1<=0x1F + false + + + + + + + + + + + + false + + + + + + + + + + + + + arg1>='0' && arg1<='9' + false + + + + + 0:255 + + + + + + + arg1>='0' && arg1<='9' + false + + + + + + + + + + + arg1>=0x21 && arg1<=0x7E + false + + + + + 0:255 + + + + + + + arg1>=0x21 && arg1<=0x7E + false + + + + + + + + + + + arg1>=0x61 && arg1<=0x7A + false + + + + + 0:255 + + + + + + + arg1>=0x61 && arg1<=0x7A + false + + + + + + + + + + + arg1>=0x20 && arg1<=0x7E + false + + + + + 0:255 + + + + + + + arg1>=0x20 && arg1<=0x7E + false + + + + + + + + + + + arg1>=0x21 && arg1<=0x2F || arg1>=0x3A && arg1<=0x40 || arg1>=0x5B && arg1<=0x60 || arg1>=0x7B && arg1<=0x7E + false + + + + + 0:255 + + + + + + + arg1>=0x21 && arg1<=0x2F || arg1>=0x3A && arg1<=0x40 || arg1>=0x5B && arg1<=0x60 || arg1>=0x7B && arg1<=0x7E + false + + + + + + + + + + + arg1>=0x09 && arg1<=0x0D || arg1==0x20 + false + + + + + 0:255 + + + + + + + arg1>=0x09 && arg1<=0x0D || arg1==0x20 + false + + + + + + + + + + + arg1>=0x41 && arg1<=0x5A + false + + + + + 0:255 + + + + + + + arg1>=0x41 && arg1<=0x5A + false + + + + + + + + + + + arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1<=0x46 || arg1>=0x61 && arg1<=0x66 + false + + + + + 0:255 + + + + + + + arg1>=0x30 && arg1<=0x39 || arg1>=0x41 && arg1<=0x46 || arg1>=0x61 && arg1<=0x66 + false + + + + + + + + + + + + false + + + + + + + + + + + + + arg1 < 'A' || arg1 > 'Z' ? arg1 : arg1 + 32 + false + + + + + + + + + + arg1 < 'a' || arg1 > 'z' ? arg1 : arg1 - 32 + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + arg1>0?arg1:-arg1 + false + + + + + + + + + + arg1>0?arg1:-arg1 + false + + + + + + + + + + ldexp(arg1,arg2) + false + + + + + + + + + + + + + ldexp(arg1,arg2) + false + + + + + + + + + + + + + ldexp(arg1,arg2) + false + + + + + + + + + + + + + + lgamma(arg1) + false + + + + + !0.0: + + + + + + + + lgamma(arg1) + false + + + + + !0.0: + + + + + + + + lgamma(arg1) + false + + + + + !0.0: + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + false + + + + + 0: + + + + + + + false + + + + + + + :-1,1: + + + + + + + false + + + + + + + :-1,1: + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + log(arg1) + false + + + + 4.94066e-324: + + + + + + log(arg1) + false + + + + 1.4013e-45: + + + + + + log(arg1) + false + + + + 4.94066e-324: + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + arg1>arg2?1:0 + false + + + + + + + + + + + + + arg1 >= arg2?1:0 + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + logb(arg1) + false + + + + + + + + + + logb(arg1) + false + + + + + + + + + + logb(arg1) + false + + + + + + + + + + arg1<arg2?1:0 + false + + + + + + + + + + + + + arg1 <= arg2?1:0 + false + + + + + + + + + + + + + (arg1<arg2 || arg1>arg2)?1:0 + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + ilogb(arg1) + false + + + + + + + + + + ilogb(arg1) + false + + + + + + + + + + ilogb(arg1) + false + + + + + + + + + + log10(arg1) + false + + + + 4.94066e-324: + + + + + + + log10(arg1) + false + + + + 1.4013e-45: + + + + + + + log10(arg1) + false + + + + 4.94066e-324: + + + + + + + log1p(arg1) + false + + + + + + + + + + log1p(arg1) + false + + + + + + + + + + log1p(arg1) + false + + + + + + + + + + log2(arg1) + false + + + + 4.94066e-324: + + + + + + + log2(arg1) + false + + + + 1.4013e-45: + + + + + + + log2(arg1) + false + + + + 4.94066e-324: + + + + + + + nearbyint(arg1) + false + + + + + + + + + + nearbyint(arg1) + false + + + + + + + + + + nearbyint(arg1) + false + + + + + + + + + + nextafter(arg1) + false + + + + + + + + + + + + + nextafter(arg1) + false + + + + + + + + + + + + + nextafter(arg1) + false + + + + + + + + + + + + + nexttoward(arg1) + false + + + + + + + + + + + + + nexttoward(arg1) + false + + + + + + + + + + + + + nexttoward(arg1) + false + + + + + + + + + + + true + + + + + + + + + + + + + + false + + + 0: + + + + + + + false + + + 0: + + + + + + + false + + + + + + 0: + + + + + + + + + false + + + + + + + + + + 0: + + + + + 0: + + + + + + + + false + + + + + + + + + + 0: + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + + false + + + + + + + + 0: + + + + + + + + + 0: + + + + + false + + + + + + + + + + + + + + + + 0: + + + + + false + + + + + + + + + + + + + + + + 0: + + + + + + false + + + + + + + + 0: + + + + + + + 0: + + + + + + false + + + + + + + + + + + + 0: + + + + + + false + + + + + + + + + + + + 0: + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + pow(arg1, arg2) + false + + + + + + + + + + + + + pow(arg1, arg2) + false + + + + + + + + + + + + + pow(arg1, arg2) + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + !0.0 + + + + + + + + + + + false + + + + + + + !0.0 + + + + + + + + + + + false + + + + + + + !0.0 + + + + + + + + + + remainder(arg1,arg2) + false + + + + + + + !0.0 + + + + + + + remainder(arg1,arg2) + false + + + + + + + !0.0 + + + + + + + remainder(arg1,arg2) + false + + + + + + + !0.0 + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + 0: + + + + 0: + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + 0: + + + + 0: + + + + + + + + + false + + + + + + + + + + 0: + + + + 0: + + + + + + + + + + + false + + + + + 0: + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + 0: + + + + + + false + + + + + 0: + + + + + + false + + + + + 0: + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + 0: + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + round(arg1) + false + + + + + + + + + + round(arg1) + false + + + + + + + + + + round(arg1) + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + maybe + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + 0:2 + + + + 0: + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + 0:255 + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + arg1 + false + + + + + + + + + + + + + + + false + + + + + + + + + 1: + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + + + + + + + + strlen(arg1) + false + + + + + + + + + + arg1 + false + + + + + + + + + + + + + + 0: + + + + + + false + + + + + + + + + 0: + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + 0: + + + + + + + false + + + + + + + + + + 0: + + + + + + + + + + false + + + + + + + + + + 0: + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + 0: + + + + + + + + + false + + + + + + + + + + + + + 0: + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + + false + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + false + + + + + + + + + false + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + 0:255 + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + 0: + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + 0: + + + + + + + false + + + + + arg1 < 'A' || arg1 > 'Z' ? arg1 : arg1 + 32 + false + + + + + 0:255 + + + + + + arg1 < 'a' || arg1 > 'z' ? arg1 : arg1 - 32 + false + + + + + 0:255 + + + + + + false + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + 0: + + + + 0:255 + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + arg1<arg2?arg1:arg2 + + + + + + + + + + + false + + + arg1>arg2?arg1:arg2 + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + 0: + + + + + + false + + + + + + + + + + false + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + 0: + + + + + + + false + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + false + + + + + + + + 0: + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + 0: + + + + + false + + + + + 0: + + + + + false + + + + + + + + + + false + + + + + 0: + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + arg1 + + + + + + + + + + + + + false + & arg1 + + + + + false + + + + false + + + + + + + false + + + 0: + + + + + + + + false + + + 0: + + + + + + + + false + + + 0: + + + + + + + + false + + + 0: + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + 0: + + + + + 0: + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + 0: + + + + 0: + + + + + + + false + + + 0: + + + + 0: + + + + + + + false + + + 0: + + + + 0: + + + + + + + false + + + 0: + + + + 0: + + + + + + + false + + + + false + + + + + + + + false + + + + + + + + + 0: + + + + + false + + + + + + + + + 0: + + + + + false + + + + + + + + + 0: + + + + + false + + + + + + + + 0: + + + + + + false + + + + + + + + + 0: + + + + + + + + + false + + + + + + + + + + + 2:36 + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + 0: + + + + + + + + false + + + 0: + + + + 0: + + + + + + false + + + 0: + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + 0: + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + 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 + + + 0: + + + + + + false + + + 0: + + + + + + false + + + 0: + + + + 0: + + + + + + false + + + + + + + + false + + + + + + + + false + + + + false + + + + + + + + false + + + + false + + + + + + false + + + + + malloc,std::malloc + calloc,std::calloc + aligned_alloc,std::aligned_alloc + realloc,std::realloc + free,std::free + + + strdup,std::strdup + free,std::free + + + fopen,std::fopen + tmpfile,std::tmpfile + freopen,std::freopen + fclose,std::fclose + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + std::fstream + std::wfstream + std::ofstream + std::wofstream + std::basic_fstream + std::basic_ofstream + std::insert_iterator + std::lock_guard + std::scoped_lock + std::unique_lock + std::shared_lock + std::pair + std::exception + std::logic_error + std::domain_error + std::invalid_argument + std::length_error + std::out_of_range + std::future_error + std::runtime_error + std::range_error + std::overflow_error + std::underflow_error + std::regex_error + std::system_error + std::bad_typeid + std::bad_cast + std::bad_optional_access + std::bad_expected_access + std::bad_weak_ptr + std::bad_function_call + std::bad_alloc + std::bad_array_new_length + std::bad_exception + std::ios_base::failure + std::filesystem::filesystem_error + std::bad_variant_access + std::span + + + std::mutex + std::recursive_mutex + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/tinyxml2.cfg b/cppcheck-2.14.0/cfg/tinyxml2.cfg new file mode 100644 index 00000000..e652fa9c --- /dev/null +++ b/cppcheck-2.14.0/cfg/tinyxml2.cfg @@ -0,0 +1,37 @@ + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/vcl.cfg b/cppcheck-2.14.0/cfg/vcl.cfg new file mode 100644 index 00000000..bf288ce3 --- /dev/null +++ b/cppcheck-2.14.0/cfg/vcl.cfg @@ -0,0 +1,4 @@ + + + + diff --git a/cppcheck-2.14.0/cfg/windows.cfg b/cppcheck-2.14.0/cfg/windows.cfg new file mode 100644 index 00000000..26448004 --- /dev/null +++ b/cppcheck-2.14.0/cfg/windows.cfg @@ -0,0 +1,17158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CreatePen + CreateBrushIndirect + CreateDIBPatternBrush + CreateDIBPatternBrushPt + CreateHatchBrush + CreatePatternBrush + CreateSolidBrush + CreateFont + CreateFontIndirect + CreateFontIndirectEx + CreateBitmap + CreateBitmapIndirect + CreateCompatibleBitmap + CreateDIBitmap + CreateDIBSection + CreateDiscardableBitmap + CreateEllipticRgn + CreateEllipticRgnIndirect + CreatePolygonRgn + CreatePolyPolygonRgn + CreateRectRgn + CreateRectRgnIndirect + CreateRoundRectRgn + CreateHalftonePalette + CreatePalette + DeleteObject + + + closesocket + socket + + + CreateThread + CreateFile + CreateFileA + CreateFileW + OpenFile + CreateJobObject + CreateRemoteThread + CreateConsoleScreenBuffer + OpenBackupEventLog + OpenEventLog + CreateFileMapping + CreateFileMappingFromApp + CreateFileMappingNuma + CreateMemoryResourceNotification + OpenFileMapping + CreateNamedPipe + CreateEvent + CreateEventA + CreateEventW + CreateEventEx + CreateEventExA + CreateEventExW + CreateMutex + CreateMutexA + CreateMutexW + CreateMutexEx + CreateMutexExA + CreateMutexExW + CreateSemaphore + CreateSemaphoreA + CreateSemaphoreW + CreateSemaphoreEx + CreateSemaphoreExA + CreateSemaphoreExW + CreateTimerQueue + CreateWaitableTimer + OpenEvent + OpenEventA + OpenEventW + OpenMutex + OpenMutexA + OpenMutexW + OpenSemaphore + OpenSemaphoreA + OpenSemaphoreW + OpenWaitableTimer + OpenJobObject + OpenProcess + OpenThread + CreateMailslot + CloseHandle + + + FindFirstFile + FindFirstFileW + FindFirstFileA + FindFirstFileEx + FindFirstFileExW + FindFirstFileExA + FindFirstFileNameW + FindFirstFileNameTransactedW + FindFirstStreamTransactedW + FindFirstFileTransacted + FindFirstFileTransactedA + FindFirstFileTransactedW + FindFirstStreamW + FindClose + + + OpenSCManager + OpenService + CreateService + CloseServiceHandle + + + LockServiceDatabase + UnlockServiceDatabase + + + HeapCreate + HeapDestroy + + + _wfopen + _tfopen + _wfopen_s + _tfopen_s + fclose + _fcloseall + + + _open + _topen + _wopen + _close + + + _popen + _wpopen + _tpopen + _pclose + + + LoadLibrary + LoadLibraryA + LoadLibraryW + LoadLibraryEx + LoadLibraryExA + LoadLibraryExW + + + FreeLibrary + FreeLibraryAndExitThread + + + UuidToString + UuidToStringA + UuidToStringW + RpcStringFree + + + ExAllocatePool + ExAllocatePoolWithQuota + ExAllocatePoolWithQuotaTag + ExAllocatePoolWithTag + ExAllocatePoolWithTagPriority + ExFreePool + ExFreePoolWithTag + + + _dupenv_s + _wdupenv_s + _tdupenv_s + free + + + HeapAlloc + HeapReAlloc + HeapFree + + + IoAllocateErrorLogEntry + IoWriteErrorLogEntry + IoFreeErrorLogEntry + + + IoAllocateIrp + IoFreeIrp + IofCallDriver + IoCallDriver + + + IoAllocateMdl + IoFreeMdl + + + MmAllocateContiguousMemory + MmFreeContiguousMemory + + + MmAllocateContiguousMemorySpecifyCache + MmAllocateContiguousMemorySpecifyCacheNode + MmFreeContiguousMemorySpecifyCache + + + IoAllocateWorkItem + IoFreeWorkItem + + + RtlAllocateHeap + RtlFreeHeap + + + ExAllocateFromPagedLookasideList + ExFreeToPagedLookasideList + + + ExAllocateFromNPagedLookasideList + ExFreeToNPagedLookasideList + + + AllocateHeap + FreeHeap + + + AllocateLsaHeap + FreeLsaHeap + + + AllocatePrivateHeap + FreePrivateHeap + + + VirtualAlloc + VirtualFree + + + VirtualAllocEx + VirtualAllocExNuma + VirtualFreeEx + + + LocalAlloc + LocalFree + + + GlobalAlloc + GlobalFree + SetClipboardData + + + MapViewOfFile + MapViewOfFileEx + MapViewOfFileExNuma + MapViewOfFileFromApp + UnmapViewOfFile + + + RtlCreateHeap + RtlDestroyHeap + + + strdup + wcsdup + _strdup + _wcsdup + _mbsdup + _tcsdup + _malloc_dbg + _calloc_dbg + _strdup_dbg + _wcsdup_dbg + _tcsdup_dbg + free + _free_dbg + + + _aligned_malloc + _aligned_malloc_dbg + _aligned_offset_malloc + _aligned_offset_malloc_dbg + _aligned_free + _aligned_free_dbg + + + CoTaskMemAlloc + CoTaskMemFree + + + _malloca + + _freea + + + AllocateAndInitializeSid + FreeSid + + + + false + + + + + + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + false + + + + + + + 0: + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + 0: + + + + false + + + + + + + + 0: + + + + + true + + + true + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + 0: + + + + + + + false + + + + + + 0: + + + + + false + + + + + + + + + + false + + + + + 0: + + + + + + + + 0: + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + false + + + + + + arg1 + false + + + + + + + + + + + + + + + arg1 + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + 1: + + Security Warning: Consider using StringCchCopy instead. Using this function incorrectly can compromise the security of your application. This function uses structured exception handling (SEH) to catch access violations and other errors. When this function catches SEH errors, it returns NULL without null-terminating the string and without notifying the caller of the error. The caller is not safe to assume that insufficient space is the error condition. + + + + false + + + + + + + + + + + + + + + 1: + + Security Warning: Consider using StringCchCopy instead. Using this function incorrectly can compromise the security of your application. This function uses structured exception handling (SEH) to catch access violations and other errors. When this function catches SEH errors, it returns NULL without null-terminating the string and without notifying the caller of the error. The caller is not safe to assume that insufficient space is the error condition. + + + + + false + + + + + + + + + + 1: + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + 1: + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + 0: + + + + + + + + + + + false + + + + + + + + + 0: + + + + + + + + + + + + + + false + + + + + + + + + 0: + + + + 0: + + + + + + + + + + + false + + + + + + + + + 0: + + + + 0: + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + These POSIX functions are deprecated. Use the ISO C++ conformant _strdup, _wcsdup, _mbsdup instead. + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + + + false + strcmp(arg1,arg2) + + + + + + + + + + + + + + + + + false + strcmp(arg1,arg2) + + + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + 0:2 + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + 0: + + + + + + false + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + arg1>0?arg1:-arg1 + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + 0 + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + 0: + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + + false + + + + + + + + + + + + + false + + + + 0: + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + 0: + + + + 0: + + + + + + false + + + + + + false + + + + + + + + + + + + + + + false + + + 0: + + + + + false + + + + + + + + + + + + + + + + 0: + + + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + + + + + + false + + + + + + + + + + + + 0: + + + + + + false + + + + 0: + + + + + + + + false + + + + + + + + + + + false + + strlen(arg1) + + + + + + + + + + + + + + + false + + strlen(arg1) + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + false + + + + + + + + + 0: + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + + + + + + false + + + + + + + + + 0: + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + 0 + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + 0: + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + true + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + 1: + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + Due to security concerns it is not recommended to use this function, see MSDN for details. + + + + + + + + + + + + + + + + false + + + Due to security concerns it is not recommended to use this function, see MSDN for details. + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + Due to security concerns it is not recommended to use this function, see MSDN for details. + + + + + + + + + + + + + + + + false + + Due to security concerns it is not recommended to use this function, see MSDN for details. + + + + + + + + + false + + + + + + + + + 1: + + + + + + + + + + false + + + + + + + + + 1: + + + + + + + + + 0 + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + 1: + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + 0,1 + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + 0: + + + + + + false + + + + 0: + + This function is deprecated because a more secure version is available '_malloca'. + + + + false + + + + + + + + arg1 + false + + + + + + + + + + arg1 + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + 0: + + + + + false + + + + + + + + + + + + + + + 0: + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + 1: + + + + + false + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + false + + + + + + + + + + + + 0: + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + 0: + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + false + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + arg1 < 'A' || arg1 > 'Z' ? arg1 : arg1 + 32 + false + + + + 0:255 + + + + + + + false + + + + 0:255 + + + + + + + + + + false + + + + + + + + + + + + arg1 < 'a' || arg1 > 'z' ? arg1 : arg1 - 32 + false + + + + 0:255 + + + + + + + false + + + + 0:255 + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + 0: + + + + + + + + + + false + + + + + + + + + + + + + 0: + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + 0: + + + + + + false + + + + + + + + + + + + + + + + + 0: + + + + + + false + + + + + + + + + + + + + + + + + 0: + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + 2:36 + + + + + false + + + + + + + + + + + + + + 2:36 + + + + + + false + + + + + + + + + + 0: + + + + + + + + + + false + + + + + + 0: + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + + + 0: + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + GetVersion may be altered or unavailable for releases after Windows 8.1. Instead, use the Version Helper functions + + + + false + + + GetVersionEx may be altered or unavailable for releases after Windows 8.1. Instead, use the Version Helper functions + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/wxsqlite3.cfg b/cppcheck-2.14.0/cfg/wxsqlite3.cfg new file mode 100644 index 00000000..0987c3d5 --- /dev/null +++ b/cppcheck-2.14.0/cfg/wxsqlite3.cfg @@ -0,0 +1,1955 @@ + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + false + + + + + + + false + + + + diff --git a/cppcheck-2.14.0/cfg/wxsvg.cfg b/cppcheck-2.14.0/cfg/wxsvg.cfg new file mode 100644 index 00000000..c4e60d63 --- /dev/null +++ b/cppcheck-2.14.0/cfg/wxsvg.cfg @@ -0,0 +1,16905 @@ + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -3.402823e+38:3.402823e+38 + + + + + false + + + + + + -3.402823e+38:3.402823e+38 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + -3.402823e+38:3.402823e+38 + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + false + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -3.402823e+38:3.402823e+38 + + + + + false + + + + + + -3.402823e+38:3.402823e+38 + + + + + -3.402823e+38:3.402823e+38 + + + + + false + + + + + + -3.402823e+38:3.402823e+38 + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + + + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + + + + + + + false + + + + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + false + + + + + + false + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + -1.79769e+308:1.79769e+308 + + + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + -1.79769e+308:1.79769e+308 + + + + + -1.79769e+308:1.79769e+308 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + diff --git a/cppcheck-2.14.0/cfg/wxwidgets.cfg b/cppcheck-2.14.0/cfg/wxwidgets.cfg new file mode 100644 index 00000000..ad20645b --- /dev/null +++ b/cppcheck-2.14.0/cfg/wxwidgets.cfg @@ -0,0 +1,17229 @@ + + + + + wxAcceleratorEntry + wxAny + wxArchiveIterator + wxArrayDouble + wxArrayInt + wxArrayPtrVoid + wxArrayShort + wxArrayString + wxBitmap + wxBitmapBundle + wxBitmapHandler + wxBoxSizer + wxBrush + wxChar + wxUniChar + wxUniCharRef + wxColour + wxColourDatabase + wxCursor + wxDateSpan + wxDelegateRendererNative + wxHeaderButtonParams + wxRendererNative + wxRendererVersion + wxTextWrapper + wxDCClipper + wxDCBrushChanger + wxDCFontChanger + wxDCPenChanger + wxDCTextColourChanger + wxDCTextBgColourChanger + wxDCTextBgModeChanger + wxFileType + wxFont + wxFlexGridSizer + wxFontEnumerator + wxFontInfo + wxFontList + wxFontMetrics + wxGBPosition + wxGBSizerItem + wxGBSpan + wxGDIObject + wxGraphicsBrush + wxGraphicsFont + wxGraphicsGradientStop + wxGraphicsGradientStops + wxGraphicsMatrix + wxGraphicsPath + wxGridBagSizer + wxGridSizer + wxIcon + wxIconBundle + wxIconLocation + wxIFFHandler + wxImage + wxImageHandler + wxGIFHandler + wxJPEGHandler + wxPCXHandler + wxPNGHandler + wxPNMHandler + wxTGAHandler + wxTIFFHandler + wxXPMHandler + wxMask + wxMetafile + wxNativeFontInfo + wxPalette + wxPen + wxPenList + wxPoint + wxPoint2DDouble + wxPoint2DInt + wxPosition + wxRealPoint + wxRegion + wxRegionContain + wxRegionIterator + wxRegEx + wxRect + wxSize + wxSizer + wxSizerItem + wxSplitterRenderParams + wxStaticBoxSizer + wxStdDialogButtonSizer + wxSystemOptions + wxSystemSettings + wxTarEntry + wxTarInputStream + wxTimeSpan + wxUString + wxVariant + wxVariantData + wxVariantDataCurrency + wxVariantDataErrorCode + wxVariantDataSafeArray + wxVector + wxVersionInfo + wxWrapSizer + wxZipEntry + wxZipInputStream + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + 0: + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + "#arg1" + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + This is the same as 'wxString::IsEmpty' and is kept for wxWidgets 1.xx compatibility. You should not use it in new code. + + + + false + + + + + + This is a wxWidgets 1.xx compatibility function; you should not use it in new code. + + + + + false + + + + This is a wxWidgets 1.xx compatibility function; you should not use it in new code. + + + + + false + + + + + + + + false + + + + This is a wxWidgets 1.xx compatibility function; you should not use it in new code. + + + + + false + + + + + This is a wxWidgets 1.xx compatibility function; you should not use it in new code. + + + + + + false + + + + + This is the same as 'wxString::Len' and is kept for wxWidgets 1.xx compatibility. You should not use it in new code. + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + true + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + 10,16 + + + + + + + false + + + + + + + + + + + + false + + + + 0: + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + 0: + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + 0: + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + 1: + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + 0: + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + This function is deprecated and kept mostly for backwards compatibility. Please override 'wxApp::MacOpenFiles' method instead in any new code. + + + + false + + + + + This function is deprecated, use 'wxPlatformInfo::GetBitness()' instead. + + + + false + + + + + + + + + This function is deprecated, please use 'ToWChar()' instead. + + + + false + + + + + + + + + This function is deprecated, please use 'FromWChar()' instead. + + + + false + + + + + + + This function has been deprecated in favour of 'wxMenuBar::SetMenuLabel()'. + + + + false + + + + + This function only exists for compatibility, please override FormatTimeMS() in the new code. + + + + false + + + + + This function is deprecated. Please use 'wxMenuItem::GetMenuLabel()' or 'wxMenuItem::GetMenuLabelText()' instead. + + + + false + + + + + This function is deprecated. Please use 'wxMenuItem::GetItemLabel()' or 'wxMenuItem::GetItemLabelText()' instead. + + + + false + + + + + This function is deprecated in favour of 'wxMenuItem::GetItemLabel()'. + + + + false + + + + + + This function is deprecated, use 'wxPlatformInfo::GetBitness()' instead. + + + + false + + + + + + + This function is deprecated, use 'wxPGProperty::AddPrivateChild()' instead. + + + + false + + + + This function is deprecated. Ids generated by it can conflict with the Ids defined by the user code, use wxID_ANY to assign ids which are guaranteed to not conflict with the user-defined ids for the controls and menu items you create instead of using this function. + + + + false + + + + + + + + This function is deprecated, use 'wxPGProperty::GetValueAsString()' instead. + + + + + false + + + + + + + + + + false + + + + + This function is deprecated, use 'wxPlatformInfo::GetBitnessName()' instead. + + + + false + + + + This function is deprecated, use 'wxPlatformInfo::SetBitness()' instead. + + + + false + + + + This function is deprecated in favour of 'wxMenuItem::SetItemLabel()'. + + + + false + + + + + + + This function is deprecated, please use 'wxFileName::SplitPath()' instead. + + + + false + + + + + + + + + + + + This function is deprecated. This is the old way of detecting tool right clicks, although it will still work, you should use the EVT_TOOL_RCLICKED() macro instead. + + + + false + + + + + + The setup dialog is deprecated, though retained for backward compatibility. + + + + false + + + + + + + This function has been deprecated, use 'wxWindow::Close()' instead. + + + + false + + + + + + The setup dialog is deprecated, though retained for backward compatibility. + + + + false + + + + + + + This function is deprecated use 'CreateThread()' instead. + + + + false + + + + This function is deprecated in favour of 'IsShown()'. + + + + false + + + + + + + This function is deprecated and kept mostly for backwards compatibility. Please override 'PushBack' method instead in any new code. + + + + + false + + + + + + This function should be used instead of changing 'wxCAL_NO_YEAR_CHANGE' style bit directly. It allows or disallows the user to change the year interactively. Only in generic 'wxCalendarCtrl'. + + + + false + + + + + + + This function is deprecated. Construct a wxFileName with wxPATH_UNIX and then use wxFileName::GetFullPath(wxPATH_DOS) instead. + + + + false + + + + + + This function is deprecated. + + + + false + + + + + This function is kept mostly for backwards compatibility. Use 'LastReadCount()' or 'LastWriteCount()' instead. 'LastCount()' is still needed for use with less commonly used functions: 'Discard()', 'Peek()', and 'Unread()'. + + + + + false + + + + + This function is deprecated. + + + + false + + + + This function is deprecated, use GetStream() instead + + + + false + + + + + + This function is deprecated because its name is misleading: notice that the argument is in milliseconds, not microseconds. Please use either 'wxMilliSleep()' or 'wxMicroSleep()' depending on the resolution you need. + + + + false + + + + + + + This function is kept mostly for backwards compatibility. + + + + false + + + + This function is deprecated, use 'wxWindow::SetInitialSize' instead. + + + + false + + + + This function is deprecated, use 'wxTextInputStream::ReadLine()' or 'wxTextInputStream::ReadWord()' instead. + + + + + false + + + + Use 'wxStyledTextEvent::GetString()' instead. + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + This function is deprecated. + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + This function is deprecated, use 'wxDC::SetPalette' instead. + + + + + false + + + + + This function is deprecated and kept mostly for backwards compatibility. Please override 'GetMargins()' method instead in any new code. + + + + false + + + + + + + + + false + + + + + This function is deprecated and kept mostly for backwards compatibility. Please override 'Dismiss()' method instead in any new code. + + + + + false + + This function is deprecated and kept mostly for backwards compatibility. Please override 'Popup()' method instead in any new code. + + + + + false + + + + + + This function is deprecated and kept mostly for backwards compatibility. Please override 'SetMargins()' method instead in any new code. + + + + + false + + + + + + + + + + + + + + + + This function is deprecated and kept mostly for backwards compatibility. Please override 'wxDataViewCustomRenderer::ActivateCell()' method instead in any new code. + + + + + false + + + + + + + + + + + + + + + + + + + This function is deprecated and kept mostly for backwards compatibility. Please override 'wxDataViewCustomRenderer::ActivateCell()' method instead in any new code. + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + This function is deprecated and is replaced by 'wxLog' functionality. + + + + + false + + + + + + This function is deprecated and is replaced by 'wxLog' functionality. + + + + + false + + + + + + This function is deprecated. Construct a 'wxFileName' with 'wxPATH_DOS' and then use 'wxFileName::GetFullPath(wxPATH_UNIX)' instead. + + + + + false + + + + + + + This function is deprecated. Please use 'wxFileName::SplitPath()' instead. + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + This function is deprecated. + + + + + + false + + + + + + + + false + + + + + + This function is deprecated. This is exactly the same as 'FitInside()' in wxWidgets 2.9 and later, please replace calls to it with 'FitInside()'. + + + + false + + + + + + This function is deprecated. This function does not free the old sizer which may result in memory leaks, use 'wxSizerItem::AssignSizer' which does free it instead. + + + + false + + + + + + + + + This is identical to 'wxSizerItem::SetMinSize()', prefer to use the other function, as its name is more clear + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + 0: + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + This function is deprecated. This function does not free the old sizer which may result in memory leaks, use 'wxSizerItem::AssignSpacer' which does free it instead. + + + + false + + + + + + This function is for backward compatibility only, and applications should use 'wxHelpControllerBase::DisplaySection()' instead. + + + + + + false + + + + + This function is deprecated. + + + + false + + + + + This function is deprecated in favour of 'wxIconizeEvent::IsIconized()'. + + + + false + + + + + This function is deprecated, use 'wxList::GetCount()' instead. Returns the number of elements in the list. + + + + + + false + + + + + + This function is deprecated since version 3.1.2, dimensions and depth can only be set at construction time. + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + This function is deprecated, please use 'wxWindow::FindWindowByLabel()' instead. + + + + false + + + + + + This function is deprecated, please use 'wxWindow::FindWindowByName()' instead. + + + + false + + + + + + This function is deprecated, please use 'wxFileName::CreateTempFileName()' instead. + + + + false + + + + + + + + + + This function is deprecated, use 'wxGetUserId()' with no arguments instead. + + + + false + + + + + + + + + + This function is deprecated, use 'wxGetEmailAddress()' with no arguments instead. + + + + false + + + + + + + + + + This function is deprecated, use 'wxGetHostName()' with no arguments instead. + + + + false + + + + + + + + + + This function is deprecated, use 'wxGetUserName()' with no arguments instead. + + + + false + + + + + + + + + + + + This function is deprecated, use 'wxGetCwd()' instead. + + + + false + + + + + + + + + + + + + + This function is deprecated. Please use 'wxGrid::SetCellAlignment(row, col, horiz, vert)' instead. + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + 0 + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + 0: + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + false + + strlen(arg1) + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + This is the same as wxString::Trim() except that it doesn't change this string. This is a wxWidgets 1.xx compatibility function; you should not use it in new code. + + + + + + + + + + + false + + + + + + + + + false + + + + + + This function is deprecated since version 3.1.2, dimensions and depth can only be set at construction time. + + + + + false + + + + 0: + + + + + + + + + + false + + + + + + + + + + 0,2:36 + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + 0,1 + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + 0: + + + + + 0: + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + 0: + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + true + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + 0: + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + false + + + + + + false + + + + + + 0: + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + 1: + + + + + + + + false + + + 1: + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + 0: + + + + + false + + + + + + + + + + 0: + + + + + false + + + + + + + false + + + + + + + + false + + + + + + false + + + + + + + + false + + + + + 0: + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + 0: + + + + + + false + + + + + false + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + false + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + 0: + + + + + + + + + false + + + + + + 0: + + + + + false + + + + + + 0: + + + + + false + + + + + + + 0: + + + + + false + + + + + + 1: + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + 0: + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + false + + + + + 1: + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + 0: + + + + + + + false + + + + + + + 0: + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + 0: + + + + + 0: + + + + + + false + + + + + + + 0: + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + false + + + + + + 0: + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + 0:59 + + + + + + + false + + + + + 0:60 + + + + + + + false + + + + + 0:23 + + + + + + + false + + + + + + + + + + false + + + + 1:12 + + + + + + + false + + + + + 1:31 + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + 0: + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + 1: + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + false + + + + + 0.0:1.0 + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + This is a wxWidgets 1.xx compatibility function. Use 'wxString::Mid' in new applications instead. + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + arg1<arg2?arg1:arg2 + + + + + + + + + + + false + + arg1>arg2?arg1:arg2 + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + 1: + + + + + + + + + false + + + + + 0: + + + + + + + false + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + This is a wxWidgets 1.xx compatibility function. Use 'wxString::Find' in new applications instead. + + + + + + + + false + + This is a wxWidgets 1.xx compatibility function. You should not use it in new code. + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + This is a wxWidgets 1.xx compatibility function. Use 'wxString::Truncate' in new applications instead. + + + + false + + + + + + + + + + false + + + This is a wxWidgets 1.xx compatibility function. Use 'wxString::MakeUpper' in new applications instead. + + + + false + + + This is a wxWidgets 1.xx compatibility function. Use 'wxString::MakeLower' in new applications instead. + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + 0:255 + + + + 0:255 + + + + 0:255 + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + 0: + + + + 1: + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + 1: + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + 0: + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + 0: + + + + + 0: + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + 0: + + + + + false + + + + + + + + 0: + + + + + false + + + + + + + + 0: + + + + + + false + + + + + + + + 0: + + + + + + + false + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + 0:15 + + + + + + + + false + + + + + + + + + 0:15 + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + 0: + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + 0: + + + + + false + + + + + 0: + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + false + + + + + + + + 0: + + + + + 1: + + + + + false + + + + + + 0: + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + false + + + + + 0: + + + + 0: + + + + + + + 0: + + + + + + false + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + Please note that this method does the same thing as the standard 'reserve()' one and should not be used in new code. + + + + + false + + + + + + + + false + + + + + + + false + + + + + + + + false + + + + + + 0: + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + false + + + + + + + + + false + + + + + + false + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + + + false + + + + + + diff --git a/cppcheck-2.14.0/cfg/zephyr.cfg b/cppcheck-2.14.0/cfg/zephyr.cfg new file mode 100644 index 00000000..999de4ea --- /dev/null +++ b/cppcheck-2.14.0/cfg/zephyr.cfg @@ -0,0 +1,113 @@ + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + 0:2147483647 + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cfg/zlib.cfg b/cppcheck-2.14.0/cfg/zlib.cfg new file mode 100644 index 00000000..e6584cd6 --- /dev/null +++ b/cppcheck-2.14.0/cfg/zlib.cfg @@ -0,0 +1,1450 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + :16 + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + :16 + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + false + + + + + + + + false + + + + + + + + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + + + + + 0: + + + + + + + + + + + + false + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + gzopen + gzopen64 + gzdopen + gzopen_w + gzclose + gzclose_r + gzclose_w + + + + + + false + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + false + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + 0: + + + + + 0: + + + + + + + + + + + + + false + + + + + + + + + + + + + + + 0: + + + + + + + + false + + + + + + + + + + 0: + + + + + 0: + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + 0: + + + + + + + false + + + + + + + + + 0:255 + + + + + + + + false + + + + + + + + + + + false + + + + 0:255 + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + 0: + + + + + + + + + + 0: + + + + + + + + false + + + + 0: + + + + + + + + + + 0: + + + + + + + + false + + + + 0: + + + + + 0: + + + + + + + + + + + + false + + + + 0: + + + + + + + + + + 0: + + + + + + + + false + + + + 0: + + + + + + + + + + 0: + + + + + + + + false + + + + 0: + + + + + 0: + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + false + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + false + + + + + + + + + + + + + false + + + + + + + + + + + false + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/clang-tidy.md b/cppcheck-2.14.0/clang-tidy.md new file mode 100644 index 00000000..108af31a --- /dev/null +++ b/cppcheck-2.14.0/clang-tidy.md @@ -0,0 +1,198 @@ +# clang-tidy + +Below are the reasoning why certain checks are (currently) disabled for out code base + +## Externals + +We do not perform static analysis of the source of the external libraries. `simplecpp` has its own CI with a clang-tidy workflow. + +## Disabled Checks + +`abseil-*`
+`altera-*`
+`android-*`
+`boost-*`
+`darwin-*`
+`fuchsia-*`
+`linuxkernel-*`
+`llvm-*`
+`llvmlibc-*`
+`mpi-*`
+`objc-*`
+`openmp-*`
+`zircon-*`
+ +These are disabled since the platforms/libraries in question are not targeted by us. + +`cert-*`
+`cppcoreguidelines-*`
+`google-*`
+`hicpp-*`
+ +These are coding guidelines we do not follow. Some of the checks might be explicitly enabled though. + +`readability-braces-around-statements`
+`readability-isolate-declaration`
+`modernize-use-trailing-return-type`
+`modernize-use-auto`
+`readability-uppercase-literal-suffix`
+`readability-else-after-return`
+`readability-identifier-length`
+ +These do not reflect the style we are (currently) enforcing. + +`readability-function-size`
+`readability-function-cognitive-complexity`
+ +We are not interested in the size/complexity of a function. + +`readability-magic-numbers`
+ +These do not (always) increase readability. + +`bugprone-macro-parentheses`
+ +To be documented. + +`readability-implicit-bool-conversion`
+ +This does not appear to be useful as it is reported on very common code. + +`bugprone-narrowing-conversions`
+`performance-no-automatic-move`
+ +It was decided not to apply these. + +`modernize-loop-convert`
+ +These might change the behavior of code which might not be intended (need to file an upstream issue) + +`modernize-raw-string-literal`
+ +This leads to a mismatch of raw string literals and regular ones and does reduce the readability. + +`-clang-analyzer-*`
+ +Disabled because of false positives (needs to file an upstream bug report). + +`misc-non-private-member-variables-in-classes`
+ +We intentionally use this. + +`misc-no-recursion`
+ +Leads to lots of "false positives". This seem to enforce a coding guidelines of certain codebases. + + +`bugprone-easily-swappable-parameters`
+ +This produces a lot of noise and they are not fixable that easily. + +`readability-container-data-pointer`
+ +Disable because of false positives and inconsistent warnings (need to file an upstream bug report). + +`misc-const-correctness`
+ +Work in progress. + +`bugprone-assignment-in-if-condition`
+ +Is reported for valid patterns we are using. + +`readability-suspicious-call-argument`
+ +Produces a lot of false positives since it is too vague in its analysis. + +`performance-inefficient-string-concatenation`
+ +Produces warnings which might be considered false positives starting with C++11 - see https://github.com/llvm/llvm-project/issues/54526. + +`modernize-avoid-c-arrays`
+ +Produces warnings when `const char[]` is being used which is quite common in our code. Does not make sense to enable before C++17 when `std::string_view` becomes available. +Also reports a false positive about templates which deduce the array length: https://github.com/llvm/llvm-project/issues/60053. + +`misc-include-cleaner`
+ +We run this separately via `clang-include-cleaner` in the `iwyu.yml` workflow as the findings of the include checkers still need to be reviewed manually before applying them. + +`bugprone-branch-clone`
+`modernize-return-braced-init-list`
+`misc-throw-by-value-catch-by-reference`
+`readability-avoid-const-params-in-decls`
+`bugprone-signed-char-misuse`
+`readability-redundant-access-specifiers`
+`concurrency-mt-unsafe`
+`misc-use-anonymous-namespace`
+`performance-avoid-endl`
+`performance-noexcept-swap`
+`bugprone-switch-missing-default-case`
+`bugprone-empty-catch`
+`performance-enum-size`
+`readability-avoid-nested-conditional-operator`
+ +To be evaluated (need to remove exclusion). + +`cppcoreguidelines-missing-std-forward`
+`cppcoreguidelines-avoid-const-or-ref-data-members`
+`cppcoreguidelines-macro-usage`
+`cppcoreguidelines-pro-type-member-init`
+`cppcoreguidelines-pro-type-static-cast-downcast`
+`cppcoreguidelines-prefer-member-initializer`
+`cppcoreguidelines-misleading-capture-default-by-value`
+`bugprone-argument-comment.CommentBoolLiterals`
+`cert-err33-c`
+`google-readability-namespace-comments`
+`cppcoreguidelines-special-member-functions`
+ +To be evaluated (need to enable explicitly). + +`modernize-type-traits`
+`modernize-use-nodiscard`
+ +These apply to codebases which use later standards then C++11 (C++17 is used when building with Qt6) so we cannot simply apply them. + +### Disabled for performance reasons + +`portability-std-allocator-const`
+ +Only necessary for code which is exclusively compiled with `libc++`. Also disabled for performance reasons - see https://github.com/llvm/llvm-project/issues/57527#issuecomment-1237935132. + +`modernize-deprecated-ios-base-aliases`
+ +Warns about aliases which are removed in C++20. Also disabled for performance reasons - see https://github.com/llvm/llvm-project/issues/57527#issuecomment-1237935132. + +`bugprone-unchecked-optional-access`
+ +We are not using any `optional` implementation. Also disabled for performance reasons - see https://github.com/llvm/llvm-project/issues/57527#issuecomment-1237935132. + +`modernize-replace-auto-ptr`
+ +Still available until C++17. It is unlikely such code will ever be introduced. Also disabled for performance reasons - see https://github.com/llvm/llvm-project/issues/57527#issuecomment-1237935132. + +`readability-identifier-naming`
+ +We are currently using our own `naming.json` to enforce naming schemes. Also disabled for performance reasons - see https://github.com/llvm/llvm-project/issues/57527#issuecomment-1237935132. + +`portability-simd-intrinsics`
+ +We are not using SIMD instructions and it suggests to use `std::experiemental::` features which might not be commonly available. Also disabled for performance reasons - see https://github.com/llvm/llvm-project/issues/57527#issuecomment-1237935132. + +`modernize-macro-to-enum`
+ +It does not seem to produce any warnings for us (needs to be investigated) and it is one of the more expensive checks. + +`misc-unused-using-decls` + +This is the most expensive check for several files and it is providing much in terms of code quality. Reported upstream as https://github.com/llvm/llvm-project/issues/72300. + +### Disabled for GUI only + +`readability-convert-member-functions-to-static`
+ +Disabled because of false positives with Qt `slot` methods (see https://github.com/llvm/llvm-project/issues/57520). + +`readability-redundant-access-specifiers`
+ +Reports warning with the Qt ` slots:` syntax in class declarations - see https://github.com/llvm/llvm-project/issues/60055. diff --git a/cppcheck-2.14.0/cli/CMakeLists.txt b/cppcheck-2.14.0/cli/CMakeLists.txt new file mode 100644 index 00000000..38a060df --- /dev/null +++ b/cppcheck-2.14.0/cli/CMakeLists.txt @@ -0,0 +1,86 @@ +file(GLOB hdrs "*.h") +file(GLOB srcs "*.cpp") +file(GLOB mainfile "main.cpp") +list(REMOVE_ITEM srcs ${mainfile}) + +add_library(cli_objs OBJECT ${hdrs} ${srcs}) +target_include_directories(cli_objs PRIVATE ${PROJECT_SOURCE_DIR}/lib/) +if(USE_BUNDLED_TINYXML2) + target_externals_include_directories(cli_objs PRIVATE ${PROJECT_SOURCE_DIR}/externals/tinyxml2/) +else() + target_include_directories(cli_objs SYSTEM PRIVATE ${tinyxml2_INCLUDE_DIRS}) +endif() +target_externals_include_directories(cli_objs PRIVATE ${PROJECT_SOURCE_DIR}/externals/simplecpp/) +if (NOT CMAKE_DISABLE_PRECOMPILE_HEADERS) + target_precompile_headers(cli_objs PRIVATE precompiled.h) +endif() +if (BUILD_CORE_DLL) + target_compile_definitions(cli_objs PRIVATE CPPCHECKLIB_IMPORT TINYXML2_IMPORT) +endif() + +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 13) + # false positive warning in Clang 13 - caused by FD_ZERO macro + set_source_files_properties(processexecutor.cpp PROPERTIES COMPILE_FLAGS -Wno-reserved-identifier) +endif() + +list(APPEND cppcheck_SOURCES ${hdrs} ${mainfile} $) +if (NOT BUILD_CORE_DLL) + list(APPEND cppcheck_SOURCES $) + list(APPEND cppcheck_SOURCES $) + if(USE_BUNDLED_TINYXML2) + list(APPEND cppcheck_SOURCES $) + endif() +endif() +if (WIN32) + list(APPEND cppcheck_SOURCES version.rc) +endif() + +add_executable(cppcheck ${cppcheck_SOURCES}) +target_include_directories(cppcheck PRIVATE ${PROJECT_SOURCE_DIR}/lib/) +if(USE_BUNDLED_TINYXML2) + target_externals_include_directories(cppcheck PRIVATE ${PROJECT_SOURCE_DIR}/externals/tinyxml2/) +else() + target_include_directories(cppcheck SYSTEM PRIVATE ${tinyxml2_INCLUDE_DIRS}) +endif() +target_externals_include_directories(cppcheck PRIVATE ${PROJECT_SOURCE_DIR}/externals/simplecpp/) +if (HAVE_RULES) + target_link_libraries(cppcheck ${PCRE_LIBRARY}) +endif() +if (WIN32 AND NOT BORLAND) + if(NOT MINGW) + target_link_libraries(cppcheck Shlwapi.lib) + else() + target_link_libraries(cppcheck shlwapi) + endif() +endif() +if(tinyxml2_FOUND AND NOT USE_BUNDLED_TINYXML2) + target_link_libraries(cppcheck ${tinyxml2_LIBRARIES}) +endif() +target_link_libraries(cppcheck ${CMAKE_THREAD_LIBS_INIT}) +if (BUILD_CORE_DLL) + target_link_libraries(cppcheck cppcheck-core) +endif() + +add_dependencies(cppcheck copy_cfg) +add_dependencies(cppcheck copy_addons) +add_dependencies(cppcheck copy_platforms) +if (NOT DISABLE_DMAKE) + add_dependencies(cppcheck run-dmake) +endif() + +install(TARGETS cppcheck + RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} + COMPONENT applications) + +install(FILES ${addons} + DESTINATION ${FILESDIR}/addons + COMPONENT headers) + +install(FILES ${cfgs} + DESTINATION ${FILESDIR}/cfg + COMPONENT headers) + +install(FILES ${platforms} + DESTINATION ${FILESDIR}/platforms + COMPONENT headers) + diff --git a/cppcheck-2.14.0/cli/cli.vcxproj b/cppcheck-2.14.0/cli/cli.vcxproj new file mode 100644 index 00000000..f532fa46 --- /dev/null +++ b/cppcheck-2.14.0/cli/cli.vcxproj @@ -0,0 +1,260 @@ + + + + + Debug-PCRE + x64 + + + Debug + x64 + + + Release-PCRE + x64 + + + Release + x64 + + + + {35CBDF51-2456-3EC3-99ED-113C30858883} + cli + 10.0 + + + + Application + Unicode + false + v142 + + + Application + Unicode + false + v142 + + + Application + Unicode + false + v142 + + + Application + Unicode + false + v142 + + + + + + + + + + + + + + + + + + $(SolutionDir)bin\debug\ + $(SolutionDir)bin\debug\ + temp\$(Configuration)_$(PlatformName)\ + temp\$(Configuration)_$(PlatformName)\ + cppcheck + cppcheck + true + true + $(SolutionDir)bin\ + $(SolutionDir)bin\ + temp\$(Configuration)_$(PlatformName)\ + temp\$(Configuration)_$(PlatformName)\ + cppcheck + cppcheck + true + true + true + true + + + + ..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) + true + ProgramDatabase + Disabled + CPPCHECKLIB_IMPORT;TINYXML2_IMPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Level4 + 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 + true + Use + precompiled.h + precompiled.h + true + stdcpp14 + /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) + + + shlwapi.lib;%(AdditionalDependencies) + ../externals;%(AdditionalLibraryDirectories) + true + Console + true + 8000000 + 8000000 + true + + + + + ..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) + true + ProgramDatabase + Disabled + CPPCHECKLIB_IMPORT;TINYXML2_IMPORT;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Level4 + 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 + true + Use + precompiled.h + precompiled.h + true + stdcpp14 + /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) + + + shlwapi.lib;%(AdditionalDependencies) + ../externals;%(AdditionalLibraryDirectories) + true + Console + true + 8000000 + 8000000 + true + + + + + ..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) + false + MaxSpeed + CPPCHECKLIB_IMPORT;TINYXML2_IMPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) + MultiThreadedDLL + Level4 + AnySuitable + true + Speed + true + true + true + 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 + ProgramDatabase + true + Use + precompiled.h + precompiled.h + /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) + true + stdcpp14 + + + shlwapi.lib;%(AdditionalDependencies) + ../externals;%(AdditionalLibraryDirectories) + true + Console + true + true + true + true + 8000000 + 8000000 + true + + + + + ..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) + false + MaxSpeed + CPPCHECKLIB_IMPORT;TINYXML2_IMPORT;NDEBUG;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) + MultiThreadedDLL + Level4 + AnySuitable + true + Speed + true + true + true + 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 + ProgramDatabase + true + Use + precompiled.h + precompiled.h + /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) + true + stdcpp14 + + + shlwapi.lib;%(AdditionalDependencies) + ../externals;%(AdditionalLibraryDirectories) + true + Console + true + true + true + true + 8000000 + 8000000 + true + + + + + + + + + + + + + + + + + + + + {c183db5b-ad6c-423d-80ca-1f9549555a1a} + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/cli/cli.vcxproj.filters b/cppcheck-2.14.0/cli/cli.vcxproj.filters new file mode 100644 index 00000000..2320255e --- /dev/null +++ b/cppcheck-2.14.0/cli/cli.vcxproj.filters @@ -0,0 +1,88 @@ + + + + + {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 + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/cli/cmdlinelogger.h b/cppcheck-2.14.0/cli/cmdlinelogger.h new file mode 100644 index 00000000..6f165785 --- /dev/null +++ b/cppcheck-2.14.0/cli/cmdlinelogger.h @@ -0,0 +1,37 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CMD_LINE_LOGGER_H +#define CMD_LINE_LOGGER_H + +#include + +class CmdLineLogger +{ +public: + virtual ~CmdLineLogger() = default; + + /** print a regular message */ + virtual void printMessage(const std::string &message) = 0; + /** print an error message */ + virtual void printError(const std::string &message) = 0; + /** print to the output */ + virtual void printRaw(const std::string &message) = 0; +}; + +#endif // CMD_LINE_LOGGER_H diff --git a/cppcheck-2.14.0/cli/cmdlineparser.cpp b/cppcheck-2.14.0/cli/cmdlineparser.cpp new file mode 100644 index 00000000..c1232a95 --- /dev/null +++ b/cppcheck-2.14.0/cli/cmdlineparser.cpp @@ -0,0 +1,1903 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "addoninfo.h" +#include "check.h" +#include "color.h" +#include "config.h" +#include "cppcheck.h" +#include "cppcheckexecutor.h" +#include "errorlogger.h" +#include "errortypes.h" +#include "filelister.h" +#include "filesettings.h" +#include "importproject.h" +#include "library.h" +#include "path.h" +#include "pathmatch.h" +#include "platform.h" +#include "settings.h" +#include "standards.h" +#include "suppressions.h" +#include "timer.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include // EXIT_FAILURE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_RULES +// xml is used for rules +#include "xml.h" +#endif + +static bool addFilesToList(const std::string& fileList, std::vector& pathNames) +{ + std::istream *files; + std::ifstream infile; + if (fileList == "-") { // read from stdin + files = &std::cin; + } else { + infile.open(fileList); + if (!infile.is_open()) + return false; + files = &infile; + } + if (files && *files) { + std::string fileName; + // cppcheck-suppress accessMoved - FP + while (std::getline(*files, fileName)) { // next line + // cppcheck-suppress accessMoved - FP + if (!fileName.empty()) { + pathNames.emplace_back(std::move(fileName)); + } + } + } + return true; +} + +static bool addIncludePathsToList(const std::string& fileList, std::list& pathNames) +{ + std::ifstream files(fileList); + if (files) { + std::string pathName; + // cppcheck-suppress accessMoved - FP + while (std::getline(files, pathName)) { // next line + if (!pathName.empty()) { + pathName = Path::removeQuotationMarks(std::move(pathName)); + pathName = Path::fromNativeSeparators(std::move(pathName)); + + // If path doesn't end with / or \, add it + if (!endsWith(pathName, '/')) + pathName += '/'; + + pathNames.emplace_back(std::move(pathName)); + } + } + return true; + } + return false; +} + +static bool addPathsToSet(const std::string& fileName, std::set& set) +{ + std::list templist; + if (!addIncludePathsToList(fileName, templist)) + return false; + set.insert(templist.cbegin(), templist.cend()); + return true; +} + +namespace { + class XMLErrorMessagesLogger : public ErrorLogger + { + void reportOut(const std::string & outmsg, Color /*c*/ = Color::Reset) override + { + std::cout << outmsg << std::endl; + } + + void reportErr(const ErrorMessage &msg) override + { + reportOut(msg.toXML()); + } + + void reportProgress(const std::string & /*filename*/, const char /*stage*/[], const std::size_t /*value*/) override + {} + }; +} + +CmdLineParser::CmdLineParser(CmdLineLogger &logger, Settings &settings, Suppressions &suppressions) + : mLogger(logger) + , mSettings(settings) + , mSuppressions(suppressions) +{} + +bool CmdLineParser::fillSettingsFromArgs(int argc, const char* const argv[]) +{ + const Result result = parseFromArgs(argc, argv); + + switch (result) { + case Result::Success: + break; + case Result::Exit: + Settings::terminate(); + return true; + case Result::Fail: + return false; + } + + // Libraries must be loaded before FileLister is executed to ensure markup files will be + // listed properly. + if (!loadLibraries(mSettings)) + return false; + + if (!loadAddons(mSettings)) + return false; + + // Check that all include paths exist + { + for (std::list::iterator iter = mSettings.includePaths.begin(); + iter != mSettings.includePaths.end(); + ) { + const std::string path(Path::toNativeSeparators(*iter)); + if (Path::isDirectory(path)) + ++iter; + else { + // TODO: this bypasses the template format and other settings + // If the include path is not found, warn user and remove the non-existing path from the list. + if (mSettings.severity.isEnabled(Severity::information)) + std::cout << "(information) Couldn't find path given by -I '" << path << '\'' << std::endl; + iter = mSettings.includePaths.erase(iter); + } + } + } + + // Output a warning for the user if he tries to exclude headers + const std::vector& ignored = getIgnoredPaths(); + const bool warn = std::any_of(ignored.cbegin(), ignored.cend(), [](const std::string& i) { + return Path::isHeader2(i); + }); + if (warn) { + mLogger.printMessage("filename exclusion does not apply to header (.h and .hpp) files."); + mLogger.printMessage("Please use --suppress for ignoring results from the header files."); + } + + const std::vector& pathnamesRef = getPathNames(); + const std::list& fileSettingsRef = getFileSettings(); + + // the inputs can only be used exclusively - CmdLineParser should already handle this + assert(!(!pathnamesRef.empty() && !fileSettingsRef.empty())); + + if (!fileSettingsRef.empty()) { + // TODO: de-duplicate + + std::list fileSettings; + if (!mSettings.fileFilters.empty()) { + // filter only for the selected filenames from all project files + std::copy_if(fileSettingsRef.cbegin(), fileSettingsRef.cend(), std::back_inserter(fileSettings), [&](const FileSettings &fs) { + return matchglobs(mSettings.fileFilters, fs.filename); + }); + if (fileSettings.empty()) { + mLogger.printError("could not find any files matching the filter."); + return false; + } + } + else { + fileSettings = fileSettingsRef; + } + + mFileSettings.clear(); + + // sort the markup last + std::copy_if(fileSettings.cbegin(), fileSettings.cend(), std::back_inserter(mFileSettings), [&](const FileSettings &fs) { + return !mSettings.library.markupFile(fs.filename) || !mSettings.library.processMarkupAfterCode(fs.filename); + }); + + std::copy_if(fileSettings.cbegin(), fileSettings.cend(), std::back_inserter(mFileSettings), [&](const FileSettings &fs) { + return mSettings.library.markupFile(fs.filename) && mSettings.library.processMarkupAfterCode(fs.filename); + }); + + if (mFileSettings.empty()) { + mLogger.printError("could not find or open any of the paths given."); + return false; + } + } + + if (!pathnamesRef.empty()) { + std::list> filesResolved; + // TODO: this needs to be inlined into PathMatch as it depends on the underlying filesystem +#if defined(_WIN32) + // For Windows we want case-insensitive path matching + const bool caseSensitive = false; +#else + const bool caseSensitive = true; +#endif + // Execute recursiveAddFiles() to each given file parameter + // TODO: verbose log which files were ignored? + const PathMatch matcher(ignored, caseSensitive); + for (const std::string &pathname : pathnamesRef) { + const std::string err = FileLister::recursiveAddFiles(filesResolved, Path::toNativeSeparators(pathname), mSettings.library.markupExtensions(), matcher); + if (!err.empty()) { + // TODO: bail out? + mLogger.printMessage(err); + } + } + + if (filesResolved.empty()) { + mLogger.printError("could not find or open any of the paths given."); + // TODO: PathMatch should provide the information if files were ignored + if (!ignored.empty()) + mLogger.printMessage("Maybe all paths were ignored?"); + return false; + } + + // de-duplicate files + { + auto it = filesResolved.begin(); + while (it != filesResolved.end()) { + const std::string& name = it->first; + // TODO: log if duplicated files were dropped + filesResolved.erase(std::remove_if(std::next(it), filesResolved.end(), [&](const std::pair& entry) { + return entry.first == name; + }), filesResolved.end()); + ++it; + } + } + + std::list> files; + if (!mSettings.fileFilters.empty()) { + std::copy_if(filesResolved.cbegin(), filesResolved.cend(), std::inserter(files, files.end()), [&](const decltype(filesResolved)::value_type& entry) { + return matchglobs(mSettings.fileFilters, entry.first); + }); + if (files.empty()) { + mLogger.printError("could not find any files matching the filter."); + return false; + } + } + else { + files = std::move(filesResolved); + } + + // sort the markup last + std::copy_if(files.cbegin(), files.cend(), std::inserter(mFiles, mFiles.end()), [&](const decltype(files)::value_type& entry) { + return !mSettings.library.markupFile(entry.first) || !mSettings.library.processMarkupAfterCode(entry.first); + }); + + std::copy_if(files.cbegin(), files.cend(), std::inserter(mFiles, mFiles.end()), [&](const decltype(files)::value_type& entry) { + return mSettings.library.markupFile(entry.first) && mSettings.library.processMarkupAfterCode(entry.first); + }); + + if (mFiles.empty()) { + mLogger.printError("could not find or open any of the paths given."); + return false; + } + } + + return true; +} + +// TODO: normalize/simplify/native all path parameters +// TODO: error out on all missing given files/paths +CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const argv[]) +{ + mSettings.exename = Path::getCurrentExecutablePath(argv[0]); + + // default to --check-level=normal from CLI for now + mSettings.setCheckLevel(Settings::CheckLevel::normal); + + if (argc <= 1) { + printHelp(); + return Result::Exit; + } + + // check for exclusive options + for (int i = 1; i < argc; i++) { + // documentation.. + if (std::strcmp(argv[i], "--doc") == 0) { + std::ostringstream doc; + // Get documentation.. + for (const Check * it : Check::instances()) { + const std::string& name(it->name()); + const std::string info(it->classInfo()); + if (!name.empty() && !info.empty()) + doc << "## " << name << " ##\n" + << info << "\n"; + } + + mLogger.printRaw(doc.str()); + return Result::Exit; + } + + // print all possible error messages.. + if (std::strcmp(argv[i], "--errorlist") == 0) { + if (!loadCppcheckCfg()) + return Result::Fail; + { + XMLErrorMessagesLogger xmlLogger; + std::cout << ErrorMessage::getXMLHeader(mSettings.cppcheckCfgProductName); + CppCheck::getErrorMessages(xmlLogger); + std::cout << ErrorMessage::getXMLFooter() << std::endl; + } + return Result::Exit; + } + + // Print help + if (std::strcmp(argv[i], "-h") == 0 || std::strcmp(argv[i], "--help") == 0) { + printHelp(); + return Result::Exit; + } + + if (std::strcmp(argv[i], "--version") == 0) { + if (!loadCppcheckCfg()) + return Result::Fail; + const std::string version = getVersion(); + mLogger.printRaw(version); + return Result::Exit; + } + } + + bool def = false; + bool maxconfigs = false; + + ImportProject project; + + bool executorAuto = true; + int8_t logMissingInclude{0}; + + for (int i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + // User define + 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] == '-') { + mLogger.printError("argument to '-D' is missing."); + return Result::Fail; + } + + define = argv[i]; + } + // "-Ddefine" + else { + define = 2 + argv[i]; + } + + // No "=", append a "=1" + if (define.find('=') == std::string::npos) + define += "=1"; + + if (!mSettings.userDefines.empty()) + mSettings.userDefines += ";"; + mSettings.userDefines += define; + + def = true; + } + + // -E + else if (std::strcmp(argv[i], "-E") == 0) { + mSettings.preprocessOnly = true; + mSettings.quiet = true; + } + + // Include paths + else if (std::strncmp(argv[i], "-I", 2) == 0) { + std::string path; + + // "-I path/" + if (std::strcmp(argv[i], "-I") == 0) { + ++i; + if (i >= argc || argv[i][0] == '-') { + mLogger.printError("argument to '-I' is missing."); + return Result::Fail; + } + path = argv[i]; + } + + // "-Ipath/" + else { + path = 2 + argv[i]; + } + path = Path::removeQuotationMarks(std::move(path)); + path = Path::fromNativeSeparators(std::move(path)); + + // If path doesn't end with / or \, add it + if (!endsWith(path,'/')) + path += '/'; + + mSettings.includePaths.emplace_back(std::move(path)); + } + + // 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] == '-') { + mLogger.printError("argument to '-U' is missing."); + return Result::Fail; + } + + undef = argv[i]; + } + // "-Uundef" + else { + undef = 2 + argv[i]; + } + + mSettings.userUndefs.insert(std::move(undef)); + } + + else if (std::strncmp(argv[i], "--addon=", 8) == 0) + mSettings.addons.emplace(argv[i]+8); + + else if (std::strncmp(argv[i],"--addon-python=", 15) == 0) + mSettings.addonPython.assign(argv[i]+15); + + // Check configuration + else if (std::strcmp(argv[i], "--check-config") == 0) + mSettings.checkConfiguration = true; + + // Check level + else if (std::strncmp(argv[i], "--check-level=", 14) == 0) { + Settings::CheckLevel level = Settings::CheckLevel::normal; + const std::string level_s(argv[i] + 14); + if (level_s == "normal") + level = Settings::CheckLevel::normal; + else if (level_s == "exhaustive") + level = Settings::CheckLevel::exhaustive; + else { + mLogger.printError("unknown '--check-level' value '" + level_s + "'."); + return Result::Fail; + } + + mSettings.setCheckLevel(level); + } + + // Check library definitions + else if (std::strcmp(argv[i], "--check-library") == 0) { + mSettings.checkLibrary = true; + } + + else if (std::strncmp(argv[i], "--check-version=", 16) == 0) { + if (!loadCppcheckCfg()) + return Result::Fail; + const std::string actualVersion = getVersion(); + const std::string wantedVersion = argv[i] + 16; + if (actualVersion != wantedVersion) { + mLogger.printError("--check-version check failed. Aborting."); + return Result::Fail; + } + } + + else if (std::strncmp(argv[i], "--checkers-report=", 18) == 0) + mSettings.checkersReportFilename = argv[i] + 18; + + else if (std::strncmp(argv[i], "--checks-max-time=", 18) == 0) { + if (!parseNumberArg(argv[i], 18, mSettings.checksMaxTime, true)) + return Result::Fail; + } + + else if (std::strcmp(argv[i], "--clang") == 0) { + mSettings.clang = true; + } + + else if (std::strncmp(argv[i], "--clang=", 8) == 0) { + mSettings.clang = true; + mSettings.clangExecutable = argv[i] + 8; + } + + else if (std::strncmp(argv[i], "--config-exclude=",17) ==0) { + mSettings.configExcludePaths.insert(Path::fromNativeSeparators(argv[i] + 17)); + } + + else if (std::strncmp(argv[i], "--config-excludes-file=", 23) == 0) { + // open this file and read every input file (1 file name per line) + const std::string cfgExcludesFile(23 + argv[i]); + if (!addPathsToSet(cfgExcludesFile, mSettings.configExcludePaths)) { + mLogger.printError("unable to open config excludes file at '" + cfgExcludesFile + "'"); + return Result::Fail; + } + } + + else if (std::strncmp(argv[i], "--cppcheck-build-dir=", 21) == 0) { + mSettings.buildDir = Path::fromNativeSeparators(argv[i] + 21); + if (endsWith(mSettings.buildDir, '/')) + mSettings.buildDir.pop_back(); + + if (!Path::isDirectory(mSettings.buildDir)) { + mLogger.printError("Directory '" + mSettings.buildDir + "' specified by --cppcheck-build-dir argument has to be existent."); + return Result::Fail; + } + } + + // Show --debug output after the first simplifications + else if (std::strcmp(argv[i], "--debug") == 0 || + std::strcmp(argv[i], "--debug-normal") == 0) + mSettings.debugnormal = true; + + // Flag used for various purposes during debugging + else if (std::strcmp(argv[i], "--debug-simplified") == 0) + mSettings.debugSimplified = true; + + // Show template information + else if (std::strcmp(argv[i], "--debug-template") == 0) + mSettings.debugtemplate = true; + + // Show debug warnings + else if (std::strcmp(argv[i], "--debug-warnings") == 0) + mSettings.debugwarnings = true; + + else if (std::strncmp(argv[i], "--disable=", 10) == 0) { + const std::string errmsg = mSettings.removeEnabled(argv[i] + 10); + if (!errmsg.empty()) { + mLogger.printError(errmsg); + return Result::Fail; + } + if (std::string(argv[i] + 10).find("missingInclude") != std::string::npos) { + --logMissingInclude; + } + } + + // dump cppcheck data + else if (std::strcmp(argv[i], "--dump") == 0) + mSettings.dump = true; + + else if (std::strncmp(argv[i], "--enable=", 9) == 0) { + const std::string enable_arg = argv[i] + 9; + const std::string errmsg = mSettings.addEnabled(enable_arg); + if (!errmsg.empty()) { + mLogger.printError(errmsg); + return Result::Fail; + } + // when "style" is enabled, also enable "warning", "performance" and "portability" + if (enable_arg.find("style") != std::string::npos) { + mSettings.addEnabled("warning"); + mSettings.addEnabled("performance"); + mSettings.addEnabled("portability"); + } + if (enable_arg.find("information") != std::string::npos && logMissingInclude == 0) { + ++logMissingInclude; + mSettings.addEnabled("missingInclude"); + } + if (enable_arg.find("missingInclude") != std::string::npos) { + --logMissingInclude; + } + } + + // --error-exitcode=1 + else if (std::strncmp(argv[i], "--error-exitcode=", 17) == 0) { + if (!parseNumberArg(argv[i], 17, mSettings.exitCode)) + return Result::Fail; + } + + // Exception handling inside cppcheck client + else if (std::strcmp(argv[i], "--exception-handling") == 0) { +#if defined(USE_WINDOWS_SEH) || defined(USE_UNIX_SIGNAL_HANDLING) + mSettings.exceptionHandling = true; +#else + mLogger.printError("Option --exception-handling is not supported since Cppcheck has not been built with any exception handling enabled."); + return Result::Fail; +#endif + } + + // Exception handling inside cppcheck client + else if (std::strncmp(argv[i], "--exception-handling=", 21) == 0) { +#if defined(USE_WINDOWS_SEH) || defined(USE_UNIX_SIGNAL_HANDLING) + const std::string exceptionOutfilename = argv[i] + 21; + if (exceptionOutfilename != "stderr" && exceptionOutfilename != "stdout") { + mLogger.printError("invalid '--exception-handling' argument"); + return Result::Fail; + } + mSettings.exceptionHandling = true; + CppCheckExecutor::setExceptionOutput((exceptionOutfilename == "stderr") ? stderr : stdout); +#else + mLogger.printError("Option --exception-handling is not supported since Cppcheck has not been built with any exception handling enabled."); + return Result::Fail; +#endif + } + + else if (std::strncmp(argv[i], "--executor=", 11) == 0) { + const std::string type = 11 + argv[i]; + if (type == "auto") { + executorAuto = true; + mSettings.executor = Settings::defaultExecutor(); + } + else if (type == "thread") { +#if defined(HAS_THREADING_MODEL_THREAD) + executorAuto = false; + mSettings.executor = Settings::ExecutorType::Thread; +#else + mLogger.printError("executor type 'thread' cannot be used as Cppcheck has not been built with a respective threading model."); + return Result::Fail; +#endif + } + else if (type == "process") { +#if defined(HAS_THREADING_MODEL_FORK) + executorAuto = false; + mSettings.executor = Settings::ExecutorType::Process; +#else + mLogger.printError("executor type 'process' cannot be used as Cppcheck has not been built with a respective threading model."); + return Result::Fail; +#endif + } + else { + mLogger.printError("unknown executor: '" + type + "'."); + return Result::Fail; + } + } + + // Filter errors + else if (std::strncmp(argv[i], "--exitcode-suppressions=", 24) == 0) { + // exitcode-suppressions=filename.txt + std::string filename = 24 + argv[i]; + + std::ifstream f(filename); + if (!f.is_open()) { + mLogger.printError("couldn't open the file: \"" + filename + "\"."); + return Result::Fail; + } + const std::string errmsg(mSuppressions.nofail.parseFile(f)); + if (!errmsg.empty()) { + mLogger.printError(errmsg); + return Result::Fail; + } + } + + // use a file filter + else if (std::strncmp(argv[i], "--file-filter=", 14) == 0) { + const char *filter = argv[i] + 14; + if (std::strcmp(filter, "-") == 0) { + if (!addFilesToList(filter, mSettings.fileFilters)) { + mLogger.printError("Failed: --file-filter=-"); + return Result::Fail; + } + } else { + mSettings.fileFilters.emplace_back(filter); + } + } + + // 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) + const std::string fileList = argv[i] + 12; + if (!addFilesToList(fileList, mPathNames)) { + mLogger.printError("couldn't open the file: \"" + fileList + "\"."); + return Result::Fail; + } + } + + // Force checking of files that have "too many" configurations + else if (std::strcmp(argv[i], "-f") == 0 || std::strcmp(argv[i], "--force") == 0) + mSettings.force = true; + + else if (std::strcmp(argv[i], "--fsigned-char") == 0) + mSettings.platform.defaultSign = 's'; + + else if (std::strcmp(argv[i], "--funsigned-char") == 0) + mSettings.platform.defaultSign = 'u'; + + // 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] == '-') { + mLogger.printError("argument to '-i' is missing."); + return Result::Fail; + } + path = argv[i]; + } + + // "-ipath/" + else { + path = 2 + argv[i]; + } + + if (!path.empty()) { + path = Path::removeQuotationMarks(std::move(path)); + path = Path::fromNativeSeparators(std::move(path)); + path = Path::simplifyPath(std::move(path)); + + if (Path::isDirectory(path)) { + // If directory name doesn't end with / or \, add it + if (!endsWith(path, '/')) + path += '/'; + } + mIgnoredPaths.emplace_back(std::move(path)); + } + } + + else if (std::strncmp(argv[i], "--include=", 10) == 0) { + mSettings.userIncludes.emplace_back(Path::fromNativeSeparators(argv[i] + 10)); + } + + else if (std::strncmp(argv[i], "--includes-file=", 16) == 0) { + // open this file and read every input file (1 file name per line) + const std::string includesFile(16 + argv[i]); + if (!addIncludePathsToList(includesFile, mSettings.includePaths)) { + mLogger.printError("unable to open includes file at '" + includesFile + "'"); + return Result::Fail; + } + } + + // Inconclusive checking + else if (std::strcmp(argv[i], "--inconclusive") == 0) + mSettings.certainty.enable(Certainty::inconclusive); + + // Enables inline suppressions. + else if (std::strcmp(argv[i], "--inline-suppr") == 0) + mSettings.inlineSuppressions = true; + + // 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] == '-') { + mLogger.printError("argument to '-j' is missing."); + return Result::Fail; + } + + numberString = argv[i]; + } + + // "-j3" + else + numberString = argv[i]+2; + + unsigned int tmp; + std::string err; + if (!strToInt(numberString, tmp, &err)) { + mLogger.printError("argument to '-j' is not valid - " + err + "."); + return Result::Fail; + } + if (tmp == 0) { + // TODO: implement get CPU logical core count and use that. + // Usually, -j 0 would mean "use all available cores," but + // if we get a 0, we just stall and don't do any work. + mLogger.printError("argument for '-j' must be greater than 0."); + return Result::Fail; + } + if (tmp > 1024) { + // Almost nobody has 1024 logical cores, but somebody out + // there does. + mLogger.printError("argument for '-j' is allowed to be 1024 at max."); + return Result::Fail; + } + mSettings.jobs = tmp; + } + + else if (std::strncmp(argv[i], "-l", 2) == 0) { +#ifdef HAS_THREADING_MODEL_FORK + std::string numberString; + + // "-l 3" + if (std::strcmp(argv[i], "-l") == 0) { + ++i; + if (i >= argc || argv[i][0] == '-') { + mLogger.printError("argument to '-l' is missing."); + return Result::Fail; + } + + numberString = argv[i]; + } + + // "-l3" + else + numberString = argv[i]+2; + + int tmp; + std::string err; + if (!strToInt(numberString, tmp, &err)) { + mLogger.printError("argument to '-l' is not valid - " + err + "."); + return Result::Fail; + } + mSettings.loadAverage = tmp; +#else + mLogger.printError("Option -l cannot be used as Cppcheck has not been built with fork threading model."); + return Result::Fail; +#endif + } + + // 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] == '-') { + mLogger.printError("no language given to '-x' option."); + return Result::Fail; + } + str = argv[i]; + } + + if (str == "c") + mSettings.enforcedLang = Standards::Language::C; + else if (str == "c++") + mSettings.enforcedLang = Standards::Language::CPP; + else { + mLogger.printError("unknown language '" + str + "' enforced."); + return Result::Fail; + } + } + + // --library + else if (std::strncmp(argv[i], "--library=", 10) == 0) { + mSettings.libraries.emplace_back(argv[i] + 10); + } + + // Set maximum number of #ifdef configurations to check + else if (std::strncmp(argv[i], "--max-configs=", 14) == 0) { + int tmp; + if (!parseNumberArg(argv[i], 14, tmp)) + return Result::Fail; + if (tmp < 1) { + mLogger.printError("argument to '--max-configs=' must be greater than 0."); + return Result::Fail; + } + + mSettings.maxConfigs = tmp; + mSettings.force = false; + maxconfigs = true; + } + + // max ctu depth + else if (std::strncmp(argv[i], "--max-ctu-depth=", 16) == 0) { + if (!parseNumberArg(argv[i], 16, mSettings.maxCtuDepth)) + return Result::Fail; + } + + // Write results in file + else if (std::strncmp(argv[i], "--output-file=", 14) == 0) + mSettings.outputFile = Path::simplifyPath(Path::fromNativeSeparators(argv[i] + 14)); + + // Experimental: limit execution time for extended valueflow analysis. basic valueflow analysis + // is always executed. + else if (std::strncmp(argv[i], "--performance-valueflow-max-time=", 33) == 0) { + if (!parseNumberArg(argv[i], 33, mSettings.performanceValueFlowMaxTime, true)) + return Result::Fail; + } + + else if (std::strncmp(argv[i], "--performance-valueflow-max-if-count=", 37) == 0) { + if (!parseNumberArg(argv[i], 37, mSettings.performanceValueFlowMaxIfCount, true)) + return Result::Fail; + } + + // Specify platform + else if (std::strncmp(argv[i], "--platform=", 11) == 0) { + const std::string platform(11+argv[i]); + + std::string errstr; + const std::vector paths = {argv[0]}; + if (!mSettings.platform.set(platform, errstr, paths)) { + mLogger.printError(errstr); + return Result::Fail; + } + + // TODO: remove + // these are loaded via external files and thus have Settings::PlatformFile set instead. + // override the type so they behave like the regular platforms. + if (platform == "unix32-unsigned") + mSettings.platform.type = Platform::Type::Unix32; + else if (platform == "unix64-unsigned") + mSettings.platform.type = Platform::Type::Unix64; + } + + // Write results in results.plist + else if (std::strncmp(argv[i], "--plist-output=", 15) == 0) { + mSettings.plistOutput = Path::simplifyPath(Path::fromNativeSeparators(argv[i] + 15)); + if (mSettings.plistOutput.empty()) + mSettings.plistOutput = "."; + + const std::string plistOutput = Path::toNativeSeparators(mSettings.plistOutput); + if (!Path::isDirectory(plistOutput)) { + std::string message("plist folder does not exist: '"); + message += plistOutput; + message += "'."; + mLogger.printError(message); + return Result::Fail; + } + + if (!endsWith(mSettings.plistOutput,'/')) + mSettings.plistOutput += '/'; + } + + // Special Cppcheck Premium options + else if (std::strncmp(argv[i], "--premium=", 10) == 0 && isCppcheckPremium()) { + const std::set valid{ + "autosar", + "cert-c-2016", + "cert-c++-2016", + "cert-cpp-2016", + "misra-c-2012", + "misra-c-2023", + "misra-c++-2008", + "misra-cpp-2008", + "misra-c++-2023", + "misra-cpp-2023", + "bughunting", + "safety"}; + + if (std::strcmp(argv[i], "--premium=safety") == 0) + mSettings.safety = true; + if (!mSettings.premiumArgs.empty()) + mSettings.premiumArgs += " "; + const std::string p(argv[i] + 10); + if (!valid.count(p) && !startsWith(p, "cert-c-int-precision=")) { + mLogger.printError("invalid --premium option '" + p + "'."); + return Result::Fail; + } + mSettings.premiumArgs += "--" + p; + if (p == "misra-c-2012" || p == "misra-c-2023") + mSettings.addons.emplace("misra"); + if (startsWith(p, "autosar") || startsWith(p, "cert") || startsWith(p, "misra")) { + // All checkers related to the coding standard should be enabled. The coding standards + // do not all undefined behavior or portability issues. + mSettings.addEnabled("warning"); + mSettings.addEnabled("portability"); + } + } + + // --project + else if (std::strncmp(argv[i], "--project=", 10) == 0) { + if (project.projectType != ImportProject::Type::NONE) + { + mLogger.printError("multiple --project options are not supported."); + return Result::Fail; + } + + mSettings.checkAllConfigurations = false; // Can be overridden with --max-configs or --force + std::string projectFile = argv[i]+10; + ImportProject::Type projType = project.import(projectFile, &mSettings); + project.projectType = projType; + if (projType == ImportProject::Type::CPPCHECK_GUI) { + for (const std::string &lib : project.guiProject.libraries) + mSettings.libraries.emplace_back(lib); + + const auto& excludedPaths = project.guiProject.excludedPaths; + std::copy(excludedPaths.cbegin(), excludedPaths.cend(), std::back_inserter(mIgnoredPaths)); + + std::string platform(project.guiProject.platform); + + // keep existing platform from command-line intact + if (!platform.empty()) { + std::string errstr; + const std::vector paths = {projectFile, argv[0]}; + if (!mSettings.platform.set(platform, errstr, paths)) { + mLogger.printError(errstr); + return Result::Fail; + } + } + + const auto& projectFileGui = project.guiProject.projectFile; + if (!projectFileGui.empty()) { + // read underlying project + projectFile = projectFileGui; + projType = project.import(projectFileGui, &mSettings); + } + } + if (projType == ImportProject::Type::VS_SLN || projType == ImportProject::Type::VS_VCXPROJ) { + if (project.guiProject.analyzeAllVsConfigs == "false") + project.selectOneVsConfig(mSettings.platform.type); + mSettings.libraries.emplace_back("windows"); + } + if (projType == ImportProject::Type::MISSING) { + mLogger.printError("failed to open project '" + projectFile + "'. The file does not exist."); + return Result::Fail; + } + if (projType == ImportProject::Type::UNKNOWN) { + mLogger.printError("failed to load project '" + projectFile + "'. The format is unknown."); + return Result::Fail; + } + if (projType == ImportProject::Type::FAILURE) { + mLogger.printError("failed to load project '" + projectFile + "'. An error occurred."); + return Result::Fail; + } + } + + // --project-configuration + else if (std::strncmp(argv[i], "--project-configuration=", 24) == 0) { + mVSConfig = argv[i] + 24; + if (!mVSConfig.empty() && (project.projectType == ImportProject::Type::VS_SLN || project.projectType == ImportProject::Type::VS_VCXPROJ)) + project.ignoreOtherConfigs(mVSConfig); + } + + // Only print something when there are errors + else if (std::strcmp(argv[i], "-q") == 0 || std::strcmp(argv[i], "--quiet") == 0) + mSettings.quiet = true; + + // Output relative paths + else if (std::strcmp(argv[i], "-rp") == 0 || std::strcmp(argv[i], "--relative-paths") == 0) + mSettings.relativePaths = true; + else if (std::strncmp(argv[i], "-rp=", 4) == 0 || std::strncmp(argv[i], "--relative-paths=", 17) == 0) { + mSettings.relativePaths = true; + if (argv[i][argv[i][3]=='='?4:17] != 0) { + std::string paths = argv[i]+(argv[i][3]=='='?4:17); + for (;;) { + const std::string::size_type pos = paths.find(';'); + if (pos == std::string::npos) { + mSettings.basePaths.emplace_back(Path::fromNativeSeparators(paths)); + break; + } + mSettings.basePaths.emplace_back(Path::fromNativeSeparators(paths.substr(0, pos))); + paths.erase(0, pos + 1); + } + } else { + mLogger.printError("no paths specified for the '" + std::string(argv[i]) + "' option."); + return Result::Fail; + } + } + + // Report progress + else if (std::strcmp(argv[i], "--report-progress") == 0) { + mSettings.reportProgress = 10; + } + + else if (std::strncmp(argv[i], "--report-progress=", 18) == 0) { + int tmp; + if (!parseNumberArg(argv[i], 18, tmp, true)) + return Result::Fail; + mSettings.reportProgress = tmp; + } + + // Rule given at command line + else if (std::strncmp(argv[i], "--rule=", 7) == 0) { +#ifdef HAVE_RULES + Settings::Rule rule; + rule.pattern = 7 + argv[i]; + + if (rule.pattern.empty()) { + mLogger.printError("no rule pattern provided."); + return Result::Fail; + } + + mSettings.rules.emplace_back(std::move(rule)); +#else + mLogger.printError("Option --rule cannot be used as Cppcheck has not been built with rules support."); + return Result::Fail; +#endif + } + + // Rule file + else if (std::strncmp(argv[i], "--rule-file=", 12) == 0) { +#ifdef HAVE_RULES + // TODO: improved error handling - wrong root node, etc. + // TODO: consume unused "version" attribute + const std::string ruleFile = argv[i] + 12; + tinyxml2::XMLDocument doc; + const tinyxml2::XMLError err = doc.LoadFile(ruleFile.c_str()); + if (err == tinyxml2::XML_SUCCESS) { + const tinyxml2::XMLElement *node = doc.FirstChildElement(); + // check if it is a single or multi rule configuration + if (node && strcmp(node->Value(), "rules") == 0) + node = node->FirstChildElement("rule"); + for (; node && strcmp(node->Value(), "rule") == 0; node = node->NextSiblingElement()) { + Settings::Rule rule; + + for (const tinyxml2::XMLElement *subnode = node->FirstChildElement(); subnode; subnode = subnode->NextSiblingElement()) { + const char * const subtext = subnode->GetText(); + if (std::strcmp(subnode->Name(), "tokenlist") == 0) { + rule.tokenlist = empty_if_null(subtext); + } + else if (std::strcmp(subnode->Name(), "pattern") == 0) { + rule.pattern = empty_if_null(subtext); + } + else if (std::strcmp(subnode->Name(), "message") == 0) { + for (const tinyxml2::XMLElement *msgnode = subnode->FirstChildElement(); msgnode; msgnode = msgnode->NextSiblingElement()) { + const char * const msgtext = msgnode->GetText(); + if (std::strcmp(msgnode->Name(), "severity") == 0) { + rule.severity = severityFromString(empty_if_null(msgtext)); + } + else if (std::strcmp(msgnode->Name(), "id") == 0) { + rule.id = empty_if_null(msgtext); + } + else if (std::strcmp(msgnode->Name(), "summary") == 0) { + rule.summary = empty_if_null(msgtext); + } + else { + mLogger.printError("unable to load rule-file '" + ruleFile + "' - unknown element '" + msgnode->Name() + "' encountered in 'message'."); + return Result::Fail; + } + } + } + else { + mLogger.printError("unable to load rule-file '" + ruleFile + "' - unknown element '" + subnode->Name() + "' encountered in 'rule'."); + return Result::Fail; + } + } + + if (rule.pattern.empty()) { + mLogger.printError("unable to load rule-file '" + ruleFile + "' - a rule is lacking a pattern."); + return Result::Fail; + } + + if (rule.id.empty()) { + mLogger.printError("unable to load rule-file '" + ruleFile + "' - a rule is lacking an id."); + return Result::Fail; + } + + if (rule.tokenlist.empty()) { + mLogger.printError("unable to load rule-file '" + ruleFile + "' - a rule is lacking a tokenlist."); + return Result::Fail; + } + + if (rule.tokenlist != "normal" && rule.tokenlist != "define" && rule.tokenlist != "raw") { + mLogger.printError("unable to load rule-file '" + ruleFile + "' - a rule is using the unsupported tokenlist '" + rule.tokenlist + "'."); + return Result::Fail; + } + + if (rule.severity == Severity::none) { + mLogger.printError("unable to load rule-file '" + ruleFile + "' - a rule has an invalid severity."); + return Result::Fail; + } + + mSettings.rules.emplace_back(std::move(rule)); + } + } else { + mLogger.printError("unable to load rule-file '" + ruleFile + "' (" + tinyxml2::XMLDocument::ErrorIDToName(err) + ")."); + return Result::Fail; + } +#else + mLogger.printError("Option --rule-file cannot be used as Cppcheck has not been built with rules support."); + return Result::Fail; +#endif + } + + // Safety certified behavior + else if (std::strcmp(argv[i], "--safety") == 0) + mSettings.safety = true; + + // show timing information.. + else if (std::strncmp(argv[i], "--showtime=", 11) == 0) { + const std::string showtimeMode = argv[i] + 11; + if (showtimeMode == "file") + mSettings.showtime = SHOWTIME_MODES::SHOWTIME_FILE; + else if (showtimeMode == "file-total") + mSettings.showtime = SHOWTIME_MODES::SHOWTIME_FILE_TOTAL; + else if (showtimeMode == "summary") + mSettings.showtime = SHOWTIME_MODES::SHOWTIME_SUMMARY; + else if (showtimeMode == "top5") { + mSettings.showtime = SHOWTIME_MODES::SHOWTIME_TOP5_FILE; + mLogger.printMessage("--showtime=top5 is deprecated and will be removed in Cppcheck 2.14. Please use --showtime=top5_file or --showtime=top5_summary instead."); + } + else if (showtimeMode == "top5_file") + mSettings.showtime = SHOWTIME_MODES::SHOWTIME_TOP5_FILE; + else if (showtimeMode == "top5_summary") + mSettings.showtime = SHOWTIME_MODES::SHOWTIME_TOP5_SUMMARY; + else if (showtimeMode == "none") + mSettings.showtime = SHOWTIME_MODES::SHOWTIME_NONE; + else if (showtimeMode.empty()) { + mLogger.printError("no mode provided for --showtime"); + return Result::Fail; + } + else { + mLogger.printError("unrecognized --showtime mode: '" + showtimeMode + "'. Supported modes: file, file-total, summary, top5, top5_file, top5_summary."); + return Result::Fail; + } + } + + // --std + else if (std::strncmp(argv[i], "--std=", 6) == 0) { + const std::string std = argv[i] + 6; + // TODO: print error when standard is unknown + if (std::strncmp(std.c_str(), "c++", 3) == 0) { + mSettings.standards.cpp = Standards::getCPP(std); + } + else if (std::strncmp(std.c_str(), "c", 1) == 0) { + mSettings.standards.c = Standards::getC(std); + } + else { + mLogger.printError("unknown --std value '" + std + "'"); + return Result::Fail; + } + } + + else if (std::strncmp(argv[i], "--suppress=", 11) == 0) { + const std::string suppression = argv[i]+11; + const std::string errmsg(mSuppressions.nomsg.addSuppressionLine(suppression)); + if (!errmsg.empty()) { + mLogger.printError(errmsg); + return Result::Fail; + } + } + + // Filter errors + else if (std::strncmp(argv[i], "--suppressions-list=", 20) == 0) { + std::string filename = argv[i]+20; + std::ifstream f(filename); + if (!f.is_open()) { + std::string message("couldn't open the file: \""); + message += filename; + message += "\"."; + if (std::count(filename.cbegin(), filename.cend(), ',') > 0 || + std::count(filename.cbegin(), filename.cend(), '.') > 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"; + } + + mLogger.printError(message); + return Result::Fail; + } + const std::string errmsg(mSuppressions.nomsg.parseFile(f)); + if (!errmsg.empty()) { + mLogger.printError(errmsg); + return Result::Fail; + } + } + + else if (std::strncmp(argv[i], "--suppress-xml=", 15) == 0) { + const char * filename = argv[i] + 15; + const std::string errmsg(mSuppressions.nomsg.parseXmlFile(filename)); + if (!errmsg.empty()) { + mLogger.printError(errmsg); + return Result::Fail; + } + } + + // Output formatter + else if (std::strncmp(argv[i], "--template=", 11) == 0) { + mSettings.templateFormat = argv[i] + 11; + // TODO: bail out when no template is provided? + + if (mSettings.templateFormat == "gcc") { + mSettings.templateFormat = "{bold}{file}:{line}:{column}: {magenta}warning:{default} {message} [{id}]{reset}\\n{code}"; + mSettings.templateLocation = "{bold}{file}:{line}:{column}: {dim}note:{reset} {info}\\n{code}"; + } else if (mSettings.templateFormat == "daca2") { + mSettings.daca = true; + mSettings.templateFormat = "{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]"; + mSettings.templateLocation = "{file}:{line}:{column}: note: {info}"; + } else if (mSettings.templateFormat == "vs") + mSettings.templateFormat = "{file}({line}): {severity}: {message}"; + else if (mSettings.templateFormat == "edit") + mSettings.templateFormat = "{file} +{line}: {severity}: {message}"; + else if (mSettings.templateFormat == "cppcheck1") + mSettings.templateFormat = "{callstack}: ({severity}{inconclusive:, inconclusive}) {message}"; + else if (mSettings.templateFormat == "selfcheck") { + mSettings.templateFormat = "{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\\n{code}"; + mSettings.templateLocation = "{file}:{line}:{column}: note: {info}\\n{code}"; + mSettings.daca = true; + } else if (mSettings.templateFormat == "simple") { + mSettings.templateFormat = "{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]"; + mSettings.templateLocation = ""; + } + // TODO: bail out when no placeholders are found? + } + + else if (std::strncmp(argv[i], "--template-location=", 20) == 0) { + mSettings.templateLocation = argv[i] + 20; + // TODO: bail out when no template is provided? + // TODO: bail out when no placeholders are found? + } + + else if (std::strncmp(argv[i], "--template-max-time=", 20) == 0) { + if (!parseNumberArg(argv[i], 20, mSettings.templateMaxTime)) + return Result::Fail; + } + + else if (std::strncmp(argv[i], "--typedef-max-time=", 19) == 0) { + if (!parseNumberArg(argv[i], 19, mSettings.typedefMaxTime)) + return Result::Fail; + } + + else if (std::strncmp(argv[i], "--valueflow-max-iterations=", 27) == 0) { + if (!parseNumberArg(argv[i], 27, mSettings.valueFlowMaxIterations)) + return Result::Fail; + } + + else if (std::strcmp(argv[i], "-v") == 0 || std::strcmp(argv[i], "--verbose") == 0) + mSettings.verbose = true; + + // Write results in results.xml + else if (std::strcmp(argv[i], "--xml") == 0) + mSettings.xml = true; + + // Define the XML file version (and enable XML output) + else if (std::strncmp(argv[i], "--xml-version=", 14) == 0) { + int tmp; + if (!parseNumberArg(argv[i], 14, tmp)) + return Result::Fail; + if (tmp != 2) { + // We only have xml version 2 + mLogger.printError("'--xml-version' can only be 2."); + return Result::Fail; + } + + mSettings.xml_version = tmp; + // Enable also XML if version is set + mSettings.xml = true; + } + + else { + std::string message("unrecognized command line option: \""); + message += argv[i]; + message += "\"."; + mLogger.printError(message); + return Result::Fail; + } + } + + else { + mPathNames.emplace_back(Path::fromNativeSeparators(Path::removeQuotationMarks(argv[i]))); + } + } + + if (logMissingInclude == 1) + mLogger.printMessage("'--enable=information' will no longer implicitly enable 'missingInclude' starting with 2.16. Please enable it explicitly if you require it."); + + if (!loadCppcheckCfg()) + return Result::Fail; + + // TODO: bail out? + if (!executorAuto && mSettings.useSingleJob()) + mLogger.printMessage("'--executor' has no effect as only a single job will be used."); + + // Default template format.. + if (mSettings.templateFormat.empty()) { + mSettings.templateFormat = "{bold}{file}:{line}:{column}: {red}{inconclusive:{magenta}}{severity}:{inconclusive: inconclusive:}{default} {message} [{id}]{reset}\\n{code}"; + if (mSettings.templateLocation.empty()) + mSettings.templateLocation = "{bold}{file}:{line}:{column}: {dim}note:{reset} {info}\\n{code}"; + } + // replace static parts of the templates + substituteTemplateFormatStatic(mSettings.templateFormat); + substituteTemplateLocationStatic(mSettings.templateLocation); + + if (mSettings.force || maxconfigs) + mSettings.checkAllConfigurations = true; + + if (mSettings.force) + mSettings.maxConfigs = INT_MAX; + + else if ((def || mSettings.preprocessOnly) && !maxconfigs) + mSettings.maxConfigs = 1U; + + if (mSettings.checks.isEnabled(Checks::unusedFunction) && mSettings.jobs > 1 && mSettings.buildDir.empty()) { + // TODO: bail out + mLogger.printMessage("unusedFunction check can't be used with '-j' option. Disabling unusedFunction check."); + } + + if (!mPathNames.empty() && project.projectType != ImportProject::Type::NONE) { + mLogger.printError("--project cannot be used in conjunction with source files."); + return Result::Fail; + } + + // Print error only if we have "real" command and expect files + if (mPathNames.empty() && project.guiProject.pathNames.empty() && project.fileSettings.empty()) { + // TODO: this message differs from the one reported in fillSettingsFromArgs() + mLogger.printError("no C or C++ source files found."); + return Result::Fail; + } + + if (!project.guiProject.pathNames.empty()) + mPathNames = project.guiProject.pathNames; + + if (!project.fileSettings.empty()) { + project.ignorePaths(mIgnoredPaths); + if (project.fileSettings.empty()) { + mLogger.printError("no C or C++ source files found."); + mLogger.printMessage("all paths were ignored"); // TODO: log this differently? + return Result::Fail; + } + mFileSettings = project.fileSettings; + } + + // Use paths _pathnames if no base paths for relative path output are given + if (mSettings.basePaths.empty() && mSettings.relativePaths) + mSettings.basePaths = mPathNames; + + return Result::Success; +} + +void CmdLineParser::printHelp() const +{ + const std::string manualUrl(isCppcheckPremium() ? + "https://cppcheck.sourceforge.io/manual.pdf" : + "https://files.cppchecksolutions.com/manual.pdf"); + + std::ostringstream oss; + oss << "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, *.ipp,\n" + "*.ixx, *.tpp, and *.txx files are checked recursively from the given directory.\n\n" + "Options:\n" + " --addon=\n" + " Execute addon. i.e. --addon=misra. If options must be\n" + " provided a json configuration is needed.\n" + " --addon-python=\n" + " You can specify the python interpreter either in the\n" + " addon json files or through this command line option.\n" + " If not present, Cppcheck will try \"python3\" first and\n" + " then \"python\".\n" + " --cppcheck-build-dir=
\n" + " Cppcheck work folder. Advantages:\n" + " * whole program analysis\n" + " * faster analysis; Cppcheck will reuse the results if\n" + " the hash for a file is unchanged.\n" + " * some useful debug information, i.e. commands used to\n" + " execute clang/clang-tidy/addons.\n" + " --check-config Check cppcheck configuration. The normal code\n" + " analysis is disabled by this flag.\n" + " --check-level=\n" + " Configure how much checking you want:\n" + " * normal: Cppcheck uses some compromises in the checking so\n" + " the checking will finish in reasonable time.\n" + " * exhaustive: deeper analysis that you choose when you can\n" + " wait.\n" + " The default choice is 'normal'.\n" + " --check-library Show information messages when library files have\n" + " incomplete info.\n" + " --checkers-report=\n" + " Write a report of all the active checkers to the given file.\n" + " --clang= Experimental: Use Clang parser instead of the builtin Cppcheck\n" + " parser. Takes the executable as optional parameter and\n" + " defaults to `clang`. Cppcheck will run the given Clang\n" + " executable, import the Clang AST and convert it into\n" + " Cppcheck data. After that the normal Cppcheck analysis is\n" + " used. You must have the executable in PATH if no path is\n" + " given.\n" + " --config-exclude=\n" + " Path (prefix) to be excluded from configuration\n" + " checking. Preprocessor configurations defined in\n" + " headers (but not sources) matching the prefix will not\n" + " be considered for evaluation.\n" + " --config-excludes-file=\n" + " A file that contains a list of config-excludes\n" + " --disable= Disable individual checks.\n" + " Please refer to the documentation of --enable=\n" + " for further details.\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" + " -E Print preprocessor output on stdout and don't do any\n" + " further processing.\n" + " --enable= Enable additional checks. The available ids are:\n" + " * all\n" + " Enable all checks. It is recommended to only\n" + " use --enable=all when the whole program is\n" + " scanned, because this enables unusedFunction.\n" + " * warning\n" + " Enable warning messages\n" + " * style\n" + " Enable all coding style checks. All messages\n" + " with the severities 'style', 'warning',\n" + " 'performance' and '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 recommended\n" + " to only enable this when the whole program is\n" + " scanned.\n" + " * missingInclude\n" + " Warn if there are missing includes.\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-filter= Analyze only those files matching the given filter str\n" + " Can be used multiple times\n" + " Example: --file-filter=*bar.cpp analyzes only files\n" + " that end with bar.cpp.\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" + " --fsigned-char Treat char type as signed.\n" + " --funsigned-char Treat char type as unsigned.\n" + " -h, --help Print this help.\n" + " -I Give path to search for include files. Give several -I\n" + " parameters to give several paths. First given path is\n" + " searched for contained header files first. If paths are\n" + " relative to source files, this is not needed.\n" + " --includes-file=\n" + " Specify directory paths to search for included header\n" + " files in a text file. Add one include path per line.\n" + " First given path is searched for contained header\n" + " files first. If paths are relative to source files,\n" + " this is not needed.\n" + " --include=\n" + " Force inclusion of a file before the checked file.\n" + " -i Give a source file or source file directory to exclude\n" + " from the check. This applies only to source files so\n" + " header files included by source files are not matched.\n" + " Directory name is matched to all parts of the path.\n" + " --inconclusive Allow that Cppcheck reports even though the analysis is\n" + " inconclusive.\n" + " There are false positives with this option. Each result\n" + " must be carefully investigated before you know if it is\n" + " good or bad.\n" + " --inline-suppr Enable inline suppressions. Use them by placing one or\n" + " more comments, like: '// cppcheck-suppress warningId'\n" + " on the lines before the warning to suppress.\n" + " -j Start threads to do the checking simultaneously.\n" + " -l Specifies that no new threads should be started if\n" + " there are other threads running and the load average is\n" + " at least .\n" + " --language=, -x \n" + " Forces cppcheck to check all files as the given\n" + " language. Valid values are: c, c++\n" + " --library= Load file that contains information about types\n" + " and functions. With such information Cppcheck\n" + " understands your code better and therefore you\n" + " get better results. The std.cfg file that is\n" + " distributed with Cppcheck is loaded automatically.\n" + " For more information about library files, read the\n" + " manual.\n" + " --max-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" + " --max-ctu-depth=N Max depth in whole program analysis. The default value\n" + " is 2. A larger value will mean more errors can be found\n" + " but also means the analysis will be slower.\n" + " --output-file= Write results to file, rather than standard error.\n" + " --platform=, --platform=\n" + " Specifies platform specific types and sizes. The\n" + " available builtin platforms are:\n" + " * unix32\n" + " 32 bit unix variant\n" + " * unix64\n" + " 64 bit unix variant\n" + " * win32A\n" + " 32 bit Windows ASCII character encoding\n" + " * win32W\n" + " 32 bit Windows UNICODE character encoding\n" + " * win64\n" + " 64 bit Windows\n" + " * avr8\n" + " 8 bit AVR microcontrollers\n" + " * elbrus-e1cp\n" + " Elbrus e1c+ architecture\n" + " * pic8\n" + " 8 bit PIC microcontrollers\n" + " Baseline and mid-range architectures\n" + " * pic8-enhanced\n" + " 8 bit PIC microcontrollers\n" + " Enhanced mid-range and high end (PIC18) architectures\n" + " * pic16\n" + " 16 bit PIC microcontrollers\n" + " * mips32\n" + " 32 bit MIPS microcontrollers\n" + " * native\n" + " Type sizes of host system are assumed, but no\n" + " further assumptions.\n" + " * unspecified\n" + " Unknown type sizes\n" + " --plist-output=\n" + " Generate Clang-plist output files in folder.\n"; + + if (isCppcheckPremium()) { + oss << + " --premium= + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/cppcheck.sln b/cppcheck-2.14.0/cppcheck.sln new file mode 100644 index 00000000..8ea1af26 --- /dev/null +++ b/cppcheck-2.14.0/cppcheck.sln @@ -0,0 +1,63 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29020.237 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cli", "cli\cli.vcxproj", "{35CBDF51-2456-3EC3-99ED-113C30858883}" + ProjectSection(ProjectDependencies) = postProject + {C183DB5B-AD6C-423D-80CA-1F9549555A1A} = {C183DB5B-AD6C-423D-80CA-1F9549555A1A} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testrunner", "test\testrunner.vcxproj", "{4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}" + ProjectSection(ProjectDependencies) = postProject + {C183DB5B-AD6C-423D-80CA-1F9549555A1A} = {C183DB5B-AD6C-423D-80CA-1F9549555A1A} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cppcheck", "lib\cppcheck.vcxproj", "{C183DB5B-AD6C-423D-80CA-1F9549555A1A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dmake", "tools\dmake\dmake.vcxproj", "{19EC86CD-0004-4917-B852-E6BD110B6E6F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug-PCRE|x64 = Debug-PCRE|x64 + Release|x64 = Release|x64 + Release-PCRE|x64 = Release-PCRE|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {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|x64.ActiveCfg = Debug-PCRE|x64 + {35CBDF51-2456-3EC3-99ED-113C30858883}.Debug-PCRE|x64.Build.0 = Debug-PCRE|x64 + {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|x64.ActiveCfg = Release-PCRE|x64 + {35CBDF51-2456-3EC3-99ED-113C30858883}.Release-PCRE|x64.Build.0 = Release-PCRE|x64 + {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|x64.ActiveCfg = Debug-PCRE|x64 + {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Debug-PCRE|x64.Build.0 = Debug-PCRE|x64 + {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|x64.ActiveCfg = Release-PCRE|x64 + {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4}.Release-PCRE|x64.Build.0 = Release-PCRE|x64 + {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|x64.ActiveCfg = Debug-PCRE|x64 + {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Debug-PCRE|x64.Build.0 = Debug-PCRE|x64 + {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|x64.ActiveCfg = Release-PCRE|x64 + {C183DB5B-AD6C-423D-80CA-1F9549555A1A}.Release-PCRE|x64.Build.0 = Release-PCRE|x64 + {19EC86CD-0004-4917-B852-E6BD110B6E6F}.Debug|x64.ActiveCfg = Debug|x64 + {19EC86CD-0004-4917-B852-E6BD110B6E6F}.Debug-PCRE|x64.ActiveCfg = Debug|x64 + {19EC86CD-0004-4917-B852-E6BD110B6E6F}.Release|x64.ActiveCfg = Release|x64 + {19EC86CD-0004-4917-B852-E6BD110B6E6F}.Release-PCRE|x64.ActiveCfg = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8CB50DEA-06DB-48E8-9C7B-F917494480A1} + EndGlobalSection +EndGlobal diff --git a/cppcheck-2.14.0/cppcheckpremium-suppressions b/cppcheck-2.14.0/cppcheckpremium-suppressions new file mode 100644 index 00000000..50284ee2 --- /dev/null +++ b/cppcheck-2.14.0/cppcheckpremium-suppressions @@ -0,0 +1,228 @@ + +# False positives +premium-misra-cpp-2008-5-17-1 +premium-misra-cpp-2008-5-0-6 +premium-misra-cpp-2008-7-2-1 +premium-misra-cpp-2008-3-3-2 + +# open source warnings are handled by the selfcheck.yml +noExplicitConstructor +postfixOperator +shadowFunction +useStlAlgorithm + +# we need to declare reserved identifier _CRTDBG_MAP_ALLOC +premium-cert-dcl51-cpp + +# TODO: Is there unsafe allocations, in case of exceptions) in cppcheck +# we have global objects +premium-cert-err58-cpp + +# TODO: Exception objects must be nothrow copy constructible. +premium-cert-err60-cpp + +# TODO should we throw Token? +premium-cert-err61-cpp + +# TODO: Detect errors when converting a string to a number. The library function 'atoi()' shall not be used. +premium-cert-err62-cpp + +# TODO: Can we reduce some const_cast? +premium-cert-exp55-cpp + +# sometimes a void function does not have side effects +premium-misra-cpp-2008-0-1-8 + +# unused arguments, misra rules are too strict +premium-misra-cpp-2008-0-1-11 +premium-misra-cpp-2008-0-1-12 + +# we sometimes don't care about return value from functions +premium-misra-cpp-2008-0-1-7 + +# c++11 +premium-misra-cpp-2008-1-0-1 + +# TODO: can we prevent commented out code? +premium-misra-cpp-2008-2-7-2 +premium-misra-cpp-2008-2-7-3 + +# NA +premium-misra-cpp-2008-2-10-1 + +# objects of a class often has the lowercase name of the class. +premium-misra-cpp-2008-2-10-4 + +# no suffix on numeric literals +premium-misra-cpp-2008-2-13-3 + +# flag |= .. +premium-misra-cpp-2008-4-5-1 + +# Token/Variable flags are enum constants and we use those in bitwise operations by intention. +premium-misra-cpp-2008-4-5-2 + +# intentional addition of char to string: const std::string end(':' + cfg + ':' + Path::simplifyPath(sourcefile)); +premium-misra-cpp-2008-4-5-3 + +# too strict operator precedence warnings +premium-misra-cpp-2008-5-0-2 + +# we are less strict about signedness. what bug is there here: unsigned int col = 0 +premium-misra-cpp-2008-5-0-4 + +# intentional integral-to-float conversion +premium-misra-cpp-2008-5-0-5 + +# intentional addition of char literal: c = 'a' + (temp - 10); +premium-misra-cpp-2008-5-0-11 + +# conversion of char-to-int is intentional sometimes +premium-misra-cpp-2008-5-0-12 + +# pointer-to-bool conversion in condition +premium-misra-cpp-2008-5-0-13 + +# pointer-to-bool conversion is common +premium-misra-cpp-2008-5-0-14 + +# pointer arithmetic is not uncommon in cppcheck code +premium-misra-cpp-2008-5-0-15 + +# it's only a problem if signed expression is negative +premium-misra-cpp-2008-5-0-21 + +# Intentional safe operands of &&: return !stdValue.empty() && str == getCPP(); +premium-misra-cpp-2008-5-2-1 + +# const_cast performs intentional const casting +premium-misra-cpp-2008-5-2-5 + +# safe code: const char *next = static_cast(std::memchr(pattern, ' ', pattern_len)); +premium-misra-cpp-2008-5-2-8 + +# we intentionally cast pointer to integer when creating id for dumpfile +premium-misra-cpp-2008-5-2-9 + +# we intentionally mix increment with other operators in expressions +premium-misra-cpp-2008-5-2-10 + +# intentional array-to-pointer decay +premium-misra-cpp-2008-5-2-12 + +# we write !pointer by intention +premium-misra-cpp-2008-5-3-1 + +# side effects in conditional code is intentional +premium-misra-cpp-2008-5-14-1 + +# intentional use of comma operator in variable declarations +premium-misra-cpp-2008-5-18-1 + +# nested assignments are intentional +premium-misra-cpp-2008-6-2-1 + +# for (;;) +premium-misra-cpp-2008-6-2-3 + +# we don't always use braces for single statement loop/switch bodies +premium-misra-cpp-2008-6-3-1 + +# we don't always use braces for single statement if/else +premium-misra-cpp-2008-6-4-1 + +# we do not require a final else +premium-misra-cpp-2008-6-4-2 + +# return in case +premium-misra-cpp-2008-6-4-5 + +# it's not a bug to not put default at the end of a switch body +premium-misra-cpp-2008-6-4-6 + +# looping linked list => not well formed for loop +premium-misra-cpp-2008-6-5-1 +premium-misra-cpp-2008-6-5-2 +premium-misra-cpp-2008-6-5-3 +premium-misra-cpp-2008-6-5-4 +premium-misra-cpp-2008-6-5-5 +premium-misra-cpp-2008-6-5-6 + +# we like early returns +premium-misra-cpp-2008-6-6-3 +premium-misra-cpp-2008-6-6-4 +premium-misra-cpp-2008-6-6-5 + +# we have local functions by intention +premium-misra-cpp-2008-7-3-1 + +# intentional: return reference from method to non-const reference parameter +premium-misra-cpp-2008-7-5-3 + +# intentional declaration of multiple variables +premium-misra-cpp-2008-8-0-1 + +# we intentionally don't use & before function names +premium-misra-cpp-2008-8-4-4 + +# cppcheck does not care about this enumerator rule +premium-misra-cpp-2008-8-5-3 + +# TODO Fix these +premium-misra-cpp-2008-9-3-1 + +# returning non-const pointer/reference from method that is non-const +premium-misra-cpp-2008-9-3-2 + +# we use unions by intention sometimes +premium-misra-cpp-2008-9-5-1 + +# overridden methods is safe +premium-misra-cpp-2008-10-3-1 + +# use override/final +premium-misra-cpp-2008-10-3-2 + +# some classes have public members by intention +premium-misra-cpp-2008-11-0-1 + +# intentional: clang-tidy warns for redundant base class initializations +premium-misra-cpp-2008-12-1-2 + +# rule should not apply to deleted copy assignment operator +premium-misra-cpp-2008-12-8-2 + +# TODO: this can be fixed by refactoring the code. +premium-misra-cpp-2008-14-6-2 + +# function specializations: TODO check if we should refactor +premium-misra-cpp-2008-14-8-2 + +# we throw a pointer by intention +premium-misra-cpp-2008-15-0-2 +premium-misra-cpp-2008-15-3-5 + +# we use preprocessor when it makes sense +premium-misra-cpp-2008-16-0-1 +premium-misra-cpp-2008-16-0-7 +premium-misra-cpp-2008-16-2-1 +premium-misra-cpp-2008-16-2-2 +premium-misra-cpp-2008-16-3-2 + +# TODO do we need to catch string conversion errors (using atoi)? +premium-misra-cpp-2008-18-0-2 + +# what standard alternative is there for std::getenv +premium-misra-cpp-2008-18-0-3 + +# is used by intention +premium-misra-cpp-2008-18-0-4 + +# code is safe. we use std::strcmp by intention +premium-misra-cpp-2008-18-0-5 + +# we do avoid using new/delete +premium-misra-cpp-2008-18-4-1 + +# is used by intention +premium-misra-cpp-2008-27-0-1 diff --git a/cppcheck-2.14.0/createrelease b/cppcheck-2.14.0/createrelease new file mode 100644 index 00000000..71498659 --- /dev/null +++ b/cppcheck-2.14.0/createrelease @@ -0,0 +1,165 @@ +#!/bin/bash +# +# A script for creating release packages. The release packages are create in the home directory. +# +# Create release candidate +# ======================== +# +# check every isPremiumEnabled call: +# - every id should be in --errorlist +# git grep 'isPremiumEnabled[(]"' | sed 's/.*isPremiumEnabled[(]"//' | sed 's/".*//' | sort | uniq > ids1.txt +# ./cppcheck --errorlist | grep ' id="' | sed 's/.* id="//' | sed 's/".*//' | sort | uniq > ids2.txt +# diff -y ids1.txt ids2.txt +# - premiumaddon: check coverage.py +# python3 coverage.py --id ; sort ids-*.txt | uniq > ~/cppcheck/ids3.txt +# diff -y ids2.txt ids3.txt +# +# Windows installer: +# - ensure latest build was successful +# - ensure cfg files etc are included (win_installer/cppcheck.wxs) +# +# self check, fix critical issues: +# make clean && make CXXFLAGS=-O2 MATCHCOMPILER=yes -j4 +# ./cppcheck -D__CPPCHECK__ -D__GNUC__ -DCHECK_INTERNAL -DHAVE_RULES --std=c++11 --library=cppcheck-lib --library=qt --enable=style --inconclusive --inline-suppr --suppress=bitwiseOnBoolean --suppress=shadowFunction --suppress=useStlAlgorithm --suppress=*:externals/picojson.h --suppress=functionConst --suppress=functionStatic --xml cli gui/*.cpp lib 2> selfcheck.xml +# +# Generate lib/checkers.cpp (TODO the premium checkers should not be statically coded) +# cd ~/cppchecksolutions/cppcheck && python3 tools/get_checkers.py > lib/checkers.cpp +# +# Update translations +# lupdate gui.pro +# +# Update copyright year +# git diff 2.8 -- */*.cpp */*.h | grep '^diff --git a/' | sed 's|.* b/||' | xargs sed -i 's/Copyright (C) 2007-20[12]./Copyright (C) 2007-2022/' +# git diff | grep '^diff --git a/' +# +# Make sure "cppcheck --errorlist" works: +# make clean && make -j4 && ./cppcheck --errorlist > errlist.xml && xmllint --noout errlist.xml +# +# Update AUTHORS using output from: +# git log --format='%aN' 2.7..HEAD | sort -u > AUTHORS2 && diff -y AUTHORS AUTHORS2 | less +# +# Create 2.8.x branch +# git checkout -b 2.8.x ; git push -u origin 2.8.x +# +# Release notes: +# - ensure safety critical issues are listed properly +# - empty the releasenotes.txt in main branch +# +# Update version numbers in: +# sed -i -r "s/version 2[.][0-9]+([.]99)*/version 2.13.0/" cli/main.cpp +# sed -i -r "s|2[.][0-9]+([.]99)*|2.13.0|" cmake/versions.cmake # version must have 3 parts. +# sed -i -r "s/CPPCHECK_MINOR_VERSION [0-9]+/CPPCHECK_MINOR_VERSION 13/" lib/version.h +# sed -i -r "s/2[.][0-9]+([.]99)*( dev)*/2.13.0/" win_installer/productInfo.wxi +# sed -i -r "s/subtitle: Version 2\.[0-9]+.*/subtitle: Version 2.13/" man/*.md +# Ensure that "-rc1" is added in productInfo.wxi and lib/version.h +# Verify: +# grep '\.99' */*.[ch]* && grep '[0-9][0-9] dev' */*.[ch]* +# egrep "2\.[0-9]+" */*.h */*.cpp man/*.md | grep -v "test/test" | less +# git commit -a -m "2.8: Set versions" +# +# Build and test the windows installer +# +# Update the Makefile: +# make dmake && ./dmake --release +# Uppdatera CI så att dmake körs med --release +# git commit -a -m "2.8: Updated Makefile" +# +# Ensure that CI is happy +# +# Tag: +# git tag 2.8-rc1 +# git push --tags +# +# Release +# ======= +# +# Remove "-rc1" from versions. Test: git grep "\-rc[0-9]" +# +# Create a release folder on sourceforge: +# https://sourceforge.net/projects/cppcheck/files/cppcheck/ +# +# git tag 2.8 ; git push --tags +# ./createrelease 2.8 +# +# copy msi from release-windows, install and test cppcheck +# copy manual from build-manual +# +# Update download link on index.php main page +# +# Write Changelog +# git log 2.11..2.12 > Changelog +# +# +# Trac: +# 1. Create ticket "2.12 safety cosmetic changes" +# 2. Check priorities for all tickets in milestone. Should be: safety-* +# 3. Create new milestone +# 4. Close old milestone +# +# write a news +# +# save "cppcheck --doc" output on wiki +# +# compile new democlient: +# ssh -t danielmarjamaki,cppcheck@shell.sourceforge.net create +# ./build-cppcheck.sh +# +# run daca with new release +# 1. edit tools/donate-cpu-server.py. Update OLD_VERSION and SERVER_VERSION +# 2. scp -i ~/.ssh/osuosl_id_rsa tools/donate-cpu-server.py danielmarjamaki@cppcheck1.osuosl.org:/var/daca@home/ +# +# Backup: +# * trac: cd /var && nice tar -cJf trac.tar.xz trac-cppcheck +# * git: git checkout -f && git checkout main && git pull && tar -cJf git.tar.xz .git +# * Changelog +# * ci status: screenshot(s) showing that all tests pass for tagged commit + +# Folder/tag to use +folder=$1 +tag=$folder.0 + +# Name of release +releasename=cppcheck-$tag + +set -e + +cd ~/cppcheck + +git checkout $tag + +rm -rf upload +mkdir -p upload + +make clean + +# Create archives.. +git archive --format=tar --prefix=$releasename/ $tag | gzip > upload/$releasename.tar.gz +git archive --format=tar --prefix=$releasename/ $tag | bzip2 > upload/$releasename.tar.bz2 +git archive --format=zip -9 --prefix=$releasename/ $tag > upload/$releasename.zip +cd upload +scp $releasename.* danielmarjamaki,cppcheck@frs.sourceforge.net:/home/frs/project/c/cp/cppcheck/cppcheck/$folder/ +rm $releasename.* +cd .. + +# Generate version.txt +make -j12 +rm -f cppcheck.cfg +./cppcheck --version > upload/version.txt + +cd ~/cppcheck/upload +scp version.txt danielmarjamaki,cppcheck@web.sourceforge.net:htdocs/ + +cd ~/cppcheck +rm -rf upload + +# Local cppcheck binary +mkdir -p ~/.cppcheck/$tag +cd ~/.cppcheck/$tag +cp -R ~/cppcheck/cfg . +cp -R ~/cppcheck/addons . +cp -R ~/cppcheck/platforms . +cd ~/cppcheck +make clean ; make -j12 FILESDIR=~/.cppcheck/$tag MATCHCOMPILER=yes CXXFLAGS=-O2 +mv cppcheck ~/.cppcheck/cppcheck-$tag + +git checkout main diff --git a/cppcheck-2.14.0/democlient/build.sh b/cppcheck-2.14.0/democlient/build.sh new file mode 100644 index 00000000..51364fe0 --- /dev/null +++ b/cppcheck-2.14.0/democlient/build.sh @@ -0,0 +1,25 @@ +#!/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/tinyxml2 cppcheck-$1/democlient/democlient.cpp $cppchecklib/*.cpp cppcheck-$1/externals/tinyxml2/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! diff --git a/cppcheck-2.14.0/democlient/democlient.cpp b/cppcheck-2.14.0/democlient/democlient.cpp new file mode 100644 index 00000000..c904ff78 --- /dev/null +++ b/cppcheck-2.14.0/democlient/democlient.cpp @@ -0,0 +1,129 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "cppcheck.h" +#include "version.h" + +static void unencode(const char *src, char *dest) +{ + for (; *src; src++, dest++) { + if (*src == '+') + *dest = ' '; + else if (*src == '%') { + unsigned int code; + if (std::sscanf(src+1, "%2x", &code) != 1) + code = '?'; + *dest = code; + src += 2; + } else + *dest = *src; + } + *dest = '\0'; +} + +static FILE *logfile = nullptr; + +class CppcheckExecutor : public ErrorLogger { +private: + const std::time_t stoptime; + CppCheck cppcheck; + +public: + CppcheckExecutor() + : ErrorLogger() + , stoptime(std::time(nullptr)+2U) + , cppcheck(*this, false, nullptr) { + cppcheck.settings().addEnabled("all"); + cppcheck.settings().certainty.enable(Certainty::inconclusive); + } + + void run(const char code[]) { + cppcheck.check("test.cpp", code); + } + + void reportOut(const std::string & /*outmsg*/, Color /*c*/) override {} + void reportErr(const ErrorMessage &msg) override { + const std::string s = msg.toString(true); + + std::cout << s << std::endl; + + if (logfile != nullptr) + std::fprintf(logfile, "%s\n", s.c_str()); + } + + void reportProgress(const std::string& /*filename*/, + const char /*stage*/[], + const std::size_t /*value*/) override { + if (std::time(nullptr) >= stoptime) { + std::cout << "Time to analyse the code exceeded 2 seconds. Terminating.\n\n"; + Settings::terminate(); + } + } +}; + + +int main() +{ + std::cout << "Content-type: text/html\r\n\r\n" + << "\n"; + + char data[4096] = {0}; + + const char *query_string = std::getenv("QUERY_STRING"); + if (query_string) + std::strncpy(data, query_string, sizeof(data)-2); + + const char *lenstr = std::getenv("CONTENT_LENGTH"); + if (lenstr) { + int len = std::min(1 + std::atoi(lenstr), (int)(sizeof(data) - 2)); + std::fgets(data, len, stdin); + } + + if (data[4000] != '\0') { + std::cout << "For performance reasons the code must be shorter than 1000 chars."; + return EXIT_SUCCESS; + } + + const char *pdata = data; + if (std::strncmp(pdata, "code=", 5)==0) + pdata += 5; + + char code[4096] = {0}; + unencode(pdata, code); + + logfile = std::fopen("democlient.log", "at"); + if (logfile != nullptr) + std::fprintf(logfile, "===========================================================\n%s\n", code); + + std::cout << "Cppcheck " CPPCHECK_VERSION_STRING "
";
+
+    CppcheckExecutor cppcheckExecutor;
+    cppcheckExecutor.run(code);
+
+    std::fclose(logfile);
+
+    std::cout << "
Done!"; + + return EXIT_SUCCESS; +} diff --git a/cppcheck-2.14.0/doxyfile b/cppcheck-2.14.0/doxyfile new file mode 100644 index 00000000..3efe6a43 --- /dev/null +++ b/cppcheck-2.14.0/doxyfile @@ -0,0 +1,1906 @@ +# Doxyfile 1.8.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed +# in front of the TAG it is preceding . +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follows. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = Cppcheck + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = doxyoutput + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian, +# Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, +# Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = YES + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES (the +# default) will make doxygen replace the get and set methods by a property in +# the documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields or simple typedef fields will be shown +# inline in the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO (the default), structs, classes, and unions are shown on a separate +# page (for HTML and Man pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can +# be an expensive process and often the same symbol appear multiple times in +# the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too +# small doxygen will become slower. If the cache is too large, memory is wasted. +# The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid +# range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536 +# symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if section-label ... \endif +# and \cond section-label ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. Do not use +# file names with spaces, bibtex cannot handle them. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = cli/ \ + gui/ \ + lib/ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be ignored. +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +# If CLANG_ASSISTED_PARSING is set to YES, then doxygen will use the clang parser +# for more accurate parsing at the cost of reduced performance. This can be +# particularly helpful with template rich C++ code for which doxygen's built-in +# parser lacks the necessary type information. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified at INPUT and INCLUDE_PATH. + +CLANG_OPTIONS = + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is included after the standard +# style sheets created by doxygen. Using this option one can overrule +# certain style aspects. This is preferred over using HTML_STYLESHEET +# since it does not replace the standard style sheet and is therefore more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely +# identify the documentation publisher. This should be a reverse domain-name +# style string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +#
+# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and +# SVG. The default value is HTML-CSS, which is slower, but has the best +# compatibility. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript +# pieces of code that will be used on startup of the MathJax code. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. +# There are two flavours of web server based search depending on the +# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for +# searching and an index file used by the script. When EXTERNAL_SEARCH is +# enabled the indexing and searching needs to be provided by external tools. +# See the manual for details. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain +# the search results. Doxygen ships with an example indexer (doxyindexer) and +# search engine (doxysearch.cgi) which are based on the open source search +# engine library Xapian. See the manual for configuration details. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will returned the search results when EXTERNAL_SEARCH is enabled. +# Doxygen ships with an example search engine (doxysearch) which is based on +# the open source search engine library Xapian. See the manual for configuration +# details. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id +# of to a relative location where the documentation can be found. +# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4 will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images +# or other source files which should be copied to the LaTeX output directory. +# Note that the files will be copied as-is; there are no commands or markers +# available. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files +# that can be used to generate PDF. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. If left blank docbook will be used as the default path. + +DOCBOOK_OUTPUT = docbook + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed +# in the related pages index. If set to NO, only the current project's +# pages will be listed. + +EXTERNAL_PAGES = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# manageable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = YES + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/cppcheck-2.14.0/externals/.clang-tidy b/cppcheck-2.14.0/externals/.clang-tidy new file mode 100644 index 00000000..45892f90 --- /dev/null +++ b/cppcheck-2.14.0/externals/.clang-tidy @@ -0,0 +1,5 @@ +--- +Checks: '-*,misc-definitions-in-headers' +WarningsAsErrors: '*' +CheckOptions: + - { key: HeaderFileExtensions, value: "x" } diff --git a/cppcheck-2.14.0/externals/externals.pri b/cppcheck-2.14.0/externals/externals.pri new file mode 100644 index 00000000..023e8053 --- /dev/null +++ b/cppcheck-2.14.0/externals/externals.pri @@ -0,0 +1,11 @@ +INCLUDEPATH += $${PWD} \ + $${PWD}/picojson \ + $${PWD}/simplecpp \ + $${PWD}/tinyxml2 + +HEADERS += $${PWD}/picojson/picojson.h \ + $${PWD}/simplecpp/simplecpp.h \ + $${PWD}/tinyxml2/tinyxml2.h + +SOURCES += $${PWD}/simplecpp/simplecpp.cpp \ + $${PWD}/tinyxml2/tinyxml2.cpp diff --git a/cppcheck-2.14.0/externals/picojson/LICENSE b/cppcheck-2.14.0/externals/picojson/LICENSE new file mode 100644 index 00000000..72f35539 --- /dev/null +++ b/cppcheck-2.14.0/externals/picojson/LICENSE @@ -0,0 +1,25 @@ +Copyright 2009-2010 Cybozu Labs, Inc. +Copyright 2011-2014 Kazuho Oku +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/cppcheck-2.14.0/externals/picojson/picojson.h b/cppcheck-2.14.0/externals/picojson/picojson.h new file mode 100644 index 00000000..76742fe0 --- /dev/null +++ b/cppcheck-2.14.0/externals/picojson/picojson.h @@ -0,0 +1,1200 @@ +/* + * Copyright 2009-2010 Cybozu Labs, Inc. + * Copyright 2011-2014 Kazuho Oku + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef picojson_h +#define picojson_h + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// for isnan/isinf +#if __cplusplus >= 201103L +#include +#else +extern "C" { +#ifdef _MSC_VER +#include +#elif defined(__INTEL_COMPILER) +#include +#else +#include +#endif +} +#endif + +#ifndef PICOJSON_USE_RVALUE_REFERENCE +#if (defined(__cpp_rvalue_references) && __cpp_rvalue_references >= 200610) || (defined(_MSC_VER) && _MSC_VER >= 1600) +#define PICOJSON_USE_RVALUE_REFERENCE 1 +#else +#define PICOJSON_USE_RVALUE_REFERENCE 0 +#endif +#endif // PICOJSON_USE_RVALUE_REFERENCE + +#ifndef PICOJSON_NOEXCEPT +#if PICOJSON_USE_RVALUE_REFERENCE +#define PICOJSON_NOEXCEPT noexcept +#else +#define PICOJSON_NOEXCEPT throw() +#endif +#endif + +// experimental support for int64_t (see README.mkdn for detail) +#ifdef PICOJSON_USE_INT64 +#define __STDC_FORMAT_MACROS +#include +#if __cplusplus >= 201103L +#include +#else +extern "C" { +#include +} +#endif +#endif + +// to disable the use of localeconv(3), set PICOJSON_USE_LOCALE to 0 +#ifndef PICOJSON_USE_LOCALE +#define PICOJSON_USE_LOCALE 1 +#endif +#if PICOJSON_USE_LOCALE +extern "C" { +#include +} +#endif + +#ifndef PICOJSON_ASSERT +#define PICOJSON_ASSERT(e) \ + do { \ + if (!(e)) \ + throw std::runtime_error(#e); \ + } while (0) +#endif + +#ifdef _MSC_VER +#define SNPRINTF _snprintf_s +#pragma warning(push) +#pragma warning(disable : 4244) // conversion from int to char +#pragma warning(disable : 4127) // conditional expression is constant +#pragma warning(disable : 4702) // unreachable code +#pragma warning(disable : 4706) // assignment within conditional expression +#else +#define SNPRINTF snprintf +#endif + +namespace picojson { + +enum { + null_type, + boolean_type, + number_type, + string_type, + array_type, + object_type +#ifdef PICOJSON_USE_INT64 + , + int64_type +#endif +}; + +enum { INDENT_WIDTH = 2, DEFAULT_MAX_DEPTHS = 100 }; + +struct null {}; + +class value { +public: + typedef std::vector array; + typedef std::map object; + union _storage { + bool boolean_; + double number_; +#ifdef PICOJSON_USE_INT64 + int64_t int64_; +#endif + std::string *string_; + array *array_; + object *object_; + }; + +protected: + int type_; + _storage u_; + +public: + value(); + value(int type, bool); + explicit value(bool b); +#ifdef PICOJSON_USE_INT64 + explicit value(int64_t i); +#endif + explicit value(double n); + explicit value(const std::string &s); + explicit value(const array &a); + explicit value(const object &o); +#if PICOJSON_USE_RVALUE_REFERENCE + explicit value(std::string &&s); + explicit value(array &&a); + explicit value(object &&o); +#endif + explicit value(const char *s); + value(const char *s, size_t len); + ~value(); + value(const value &x); + value &operator=(const value &x); +#if PICOJSON_USE_RVALUE_REFERENCE + value(value &&x) PICOJSON_NOEXCEPT; + value &operator=(value &&x) PICOJSON_NOEXCEPT; +#endif + void swap(value &x) PICOJSON_NOEXCEPT; + template bool is() const; + template const T &get() const; + template T &get(); + template void set(const T &); +#if PICOJSON_USE_RVALUE_REFERENCE + template void set(T &&); +#endif + bool evaluate_as_boolean() const; + const value &get(const size_t idx) const; + const value &get(const std::string &key) const; + value &get(const size_t idx); + value &get(const std::string &key); + + bool contains(const size_t idx) const; + bool contains(const std::string &key) const; + std::string to_str() const; + template void serialize(Iter os, bool prettify = false) const; + std::string serialize(bool prettify = false) const; + +private: + template value(const T *); // intentionally defined to block implicit conversion of pointer to bool + template static void _indent(Iter os, int indent); + template void _serialize(Iter os, int indent) const; + std::string _serialize(int indent) const; + void clear(); +}; + +typedef value::array array; +typedef value::object object; + +inline value::value() : type_(null_type), u_() { +} + +inline value::value(int type, bool) : type_(type), u_() { + switch (type) { +#define INIT(p, v) \ + case p##type: \ + u_.p = v; \ + break + INIT(boolean_, false); + INIT(number_, 0.0); +#ifdef PICOJSON_USE_INT64 + INIT(int64_, 0); +#endif + INIT(string_, new std::string()); + INIT(array_, new array()); + INIT(object_, new object()); +#undef INIT + default: + break; + } +} + +inline value::value(bool b) : type_(boolean_type), u_() { + u_.boolean_ = b; +} + +#ifdef PICOJSON_USE_INT64 +inline value::value(int64_t i) : type_(int64_type), u_() { + u_.int64_ = i; +} +#endif + +inline value::value(double n) : type_(number_type), u_() { + if ( +#ifdef _MSC_VER + !_finite(n) +#elif __cplusplus >= 201103L + std::isnan(n) || std::isinf(n) +#else + isnan(n) || isinf(n) +#endif + ) { + throw std::overflow_error(""); + } + u_.number_ = n; +} + +inline value::value(const std::string &s) : type_(string_type), u_() { + u_.string_ = new std::string(s); +} + +inline value::value(const array &a) : type_(array_type), u_() { + u_.array_ = new array(a); +} + +inline value::value(const object &o) : type_(object_type), u_() { + u_.object_ = new object(o); +} + +#if PICOJSON_USE_RVALUE_REFERENCE +inline value::value(std::string &&s) : type_(string_type), u_() { + u_.string_ = new std::string(std::move(s)); +} + +inline value::value(array &&a) : type_(array_type), u_() { + u_.array_ = new array(std::move(a)); +} + +inline value::value(object &&o) : type_(object_type), u_() { + u_.object_ = new object(std::move(o)); +} +#endif + +inline value::value(const char *s) : type_(string_type), u_() { + u_.string_ = new std::string(s); +} + +inline value::value(const char *s, size_t len) : type_(string_type), u_() { + u_.string_ = new std::string(s, len); +} + +inline void value::clear() { + switch (type_) { +#define DEINIT(p) \ + case p##type: \ + delete u_.p; \ + break + DEINIT(string_); + DEINIT(array_); + DEINIT(object_); +#undef DEINIT + default: + break; + } +} + +inline value::~value() { + clear(); +} + +inline value::value(const value &x) : type_(x.type_), u_() { + switch (type_) { +#define INIT(p, v) \ + case p##type: \ + u_.p = v; \ + break + INIT(string_, new std::string(*x.u_.string_)); + INIT(array_, new array(*x.u_.array_)); + INIT(object_, new object(*x.u_.object_)); +#undef INIT + default: + u_ = x.u_; + break; + } +} + +inline value &value::operator=(const value &x) { + if (this != &x) { + value t(x); + swap(t); + } + return *this; +} + +#if PICOJSON_USE_RVALUE_REFERENCE +inline value::value(value &&x) PICOJSON_NOEXCEPT : type_(null_type), u_() { + swap(x); +} +inline value &value::operator=(value &&x) PICOJSON_NOEXCEPT { + swap(x); + return *this; +} +#endif +inline void value::swap(value &x) PICOJSON_NOEXCEPT { + std::swap(type_, x.type_); + std::swap(u_, x.u_); +} + +#define IS(ctype, jtype) \ + template <> inline bool value::is() const { \ + return type_ == jtype##_type; \ + } +IS(null, null) +IS(bool, boolean) +#ifdef PICOJSON_USE_INT64 +IS(int64_t, int64) +#endif +IS(std::string, string) +IS(array, array) +IS(object, object) +#undef IS +template <> inline bool value::is() const { + return type_ == number_type +#ifdef PICOJSON_USE_INT64 + || type_ == int64_type +#endif + ; +} + +#define GET(ctype, var) \ + template <> inline const ctype &value::get() const { \ + PICOJSON_ASSERT("type mismatch! call is() before get()" && is()); \ + return var; \ + } \ + template <> inline ctype &value::get() { \ + PICOJSON_ASSERT("type mismatch! call is() before get()" && is()); \ + return var; \ + } +GET(bool, u_.boolean_) +GET(std::string, *u_.string_) +GET(array, *u_.array_) +GET(object, *u_.object_) +#ifdef PICOJSON_USE_INT64 +GET(double, + (type_ == int64_type && (const_cast(this)->type_ = number_type, (const_cast(this)->u_.number_ = u_.int64_)), + u_.number_)) +GET(int64_t, u_.int64_) +#else +GET(double, u_.number_) +#endif +#undef GET + +#define SET(ctype, jtype, setter) \ + template <> inline void value::set(const ctype &_val) { \ + clear(); \ + type_ = jtype##_type; \ + setter \ + } +SET(bool, boolean, u_.boolean_ = _val;) +SET(std::string, string, u_.string_ = new std::string(_val);) +SET(array, array, u_.array_ = new array(_val);) +SET(object, object, u_.object_ = new object(_val);) +SET(double, number, u_.number_ = _val;) +#ifdef PICOJSON_USE_INT64 +SET(int64_t, int64, u_.int64_ = _val;) +#endif +#undef SET + +#if PICOJSON_USE_RVALUE_REFERENCE +#define MOVESET(ctype, jtype, setter) \ + template <> inline void value::set(ctype && _val) { \ + clear(); \ + type_ = jtype##_type; \ + setter \ + } +MOVESET(std::string, string, u_.string_ = new std::string(std::move(_val));) +MOVESET(array, array, u_.array_ = new array(std::move(_val));) +MOVESET(object, object, u_.object_ = new object(std::move(_val));) +#undef MOVESET +#endif + +inline bool value::evaluate_as_boolean() const { + switch (type_) { + case null_type: + return false; + case boolean_type: + return u_.boolean_; + case number_type: + return u_.number_ != 0; +#ifdef PICOJSON_USE_INT64 + case int64_type: + return u_.int64_ != 0; +#endif + case string_type: + return !u_.string_->empty(); + default: + return true; + } +} + +inline const value &value::get(const size_t idx) const { + static value s_null; + PICOJSON_ASSERT(is()); + return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; +} + +inline value &value::get(const size_t idx) { + static value s_null; + PICOJSON_ASSERT(is()); + return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null; +} + +inline const value &value::get(const std::string &key) const { + static value s_null; + PICOJSON_ASSERT(is()); + object::const_iterator i = u_.object_->find(key); + return i != u_.object_->end() ? i->second : s_null; +} + +inline value &value::get(const std::string &key) { + static value s_null; + PICOJSON_ASSERT(is()); + object::iterator i = u_.object_->find(key); + return i != u_.object_->end() ? i->second : s_null; +} + +inline bool value::contains(const size_t idx) const { + PICOJSON_ASSERT(is()); + return idx < u_.array_->size(); +} + +inline bool value::contains(const std::string &key) const { + PICOJSON_ASSERT(is()); + object::const_iterator i = u_.object_->find(key); + return i != u_.object_->end(); +} + +inline std::string value::to_str() const { + switch (type_) { + case null_type: + return "null"; + case boolean_type: + return u_.boolean_ ? "true" : "false"; +#ifdef PICOJSON_USE_INT64 + case int64_type: { + char buf[sizeof("-9223372036854775808")]; + SNPRINTF(buf, sizeof(buf), "%" PRId64, u_.int64_); + return buf; + } +#endif + case number_type: { + char buf[256]; + double tmp; + SNPRINTF(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && modf(u_.number_, &tmp) == 0 ? "%.f" : "%.17g", u_.number_); +#if PICOJSON_USE_LOCALE + char *decimal_point = localeconv()->decimal_point; + if (strcmp(decimal_point, ".") != 0) { + size_t decimal_point_len = strlen(decimal_point); + for (char *p = buf; *p != '\0'; ++p) { + if (strncmp(p, decimal_point, decimal_point_len) == 0) { + return std::string(buf, p) + "." + (p + decimal_point_len); + } + } + } +#endif + return buf; + } + case string_type: + return *u_.string_; + case array_type: + return "array"; + case object_type: + return "object"; + default: + PICOJSON_ASSERT(0); +#ifdef _MSC_VER + __assume(0); +#endif + } + return std::string(); +} + +template void copy(const std::string &s, Iter oi) { + std::copy(s.begin(), s.end(), oi); +} + +template struct serialize_str_char { + Iter oi; + void operator()(char c) { + switch (c) { +#define MAP(val, sym) \ + case val: \ + copy(sym, oi); \ + break + MAP('"', "\\\""); + MAP('\\', "\\\\"); + MAP('/', "\\/"); + MAP('\b', "\\b"); + MAP('\f', "\\f"); + MAP('\n', "\\n"); + MAP('\r', "\\r"); + MAP('\t', "\\t"); +#undef MAP + default: + if (static_cast(c) < 0x20 || c == 0x7f) { + char buf[7]; + SNPRINTF(buf, sizeof(buf), "\\u%04x", c & 0xff); + copy(buf, buf + 6, oi); + } else { + *oi++ = c; + } + break; + } + } +}; + +template void serialize_str(const std::string &s, Iter oi) { + *oi++ = '"'; + serialize_str_char process_char = {oi}; + std::for_each(s.begin(), s.end(), process_char); + *oi++ = '"'; +} + +template void value::serialize(Iter oi, bool prettify) const { + return _serialize(oi, prettify ? 0 : -1); +} + +inline std::string value::serialize(bool prettify) const { + return _serialize(prettify ? 0 : -1); +} + +template void value::_indent(Iter oi, int indent) { + *oi++ = '\n'; + for (int i = 0; i < indent * INDENT_WIDTH; ++i) { + *oi++ = ' '; + } +} + +template void value::_serialize(Iter oi, int indent) const { + switch (type_) { + case string_type: + serialize_str(*u_.string_, oi); + break; + case array_type: { + *oi++ = '['; + if (indent != -1) { + ++indent; + } + for (array::const_iterator i = u_.array_->begin(); i != u_.array_->end(); ++i) { + if (i != u_.array_->begin()) { + *oi++ = ','; + } + if (indent != -1) { + _indent(oi, indent); + } + i->_serialize(oi, indent); + } + if (indent != -1) { + --indent; + if (!u_.array_->empty()) { + _indent(oi, indent); + } + } + *oi++ = ']'; + break; + } + case object_type: { + *oi++ = '{'; + if (indent != -1) { + ++indent; + } + for (object::const_iterator i = u_.object_->begin(); i != u_.object_->end(); ++i) { + if (i != u_.object_->begin()) { + *oi++ = ','; + } + if (indent != -1) { + _indent(oi, indent); + } + serialize_str(i->first, oi); + *oi++ = ':'; + if (indent != -1) { + *oi++ = ' '; + } + i->second._serialize(oi, indent); + } + if (indent != -1) { + --indent; + if (!u_.object_->empty()) { + _indent(oi, indent); + } + } + *oi++ = '}'; + break; + } + default: + copy(to_str(), oi); + break; + } + if (indent == 0) { + *oi++ = '\n'; + } +} + +inline std::string value::_serialize(int indent) const { + std::string s; + _serialize(std::back_inserter(s), indent); + return s; +} + +template class input { +protected: + Iter cur_, end_; + bool consumed_; + int line_; + +public: + input(const Iter &first, const Iter &last) : cur_(first), end_(last), consumed_(false), line_(1) { + } + int getc() { + if (consumed_) { + if (*cur_ == '\n') { + ++line_; + } + ++cur_; + } + if (cur_ == end_) { + consumed_ = false; + return -1; + } + consumed_ = true; + return *cur_ & 0xff; + } + void ungetc() { + consumed_ = false; + } + Iter cur() const { + if (consumed_) { + input *self = const_cast *>(this); + self->consumed_ = false; + ++self->cur_; + } + return cur_; + } + int line() const { + return line_; + } + void skip_ws() { + while (1) { + int ch = getc(); + if (!(ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) { + ungetc(); + break; + } + } + } + bool expect(const int expected) { + skip_ws(); + if (getc() != expected) { + ungetc(); + return false; + } + return true; + } + bool match(const std::string &pattern) { + for (std::string::const_iterator pi(pattern.begin()); pi != pattern.end(); ++pi) { + if (getc() != *pi) { + ungetc(); + return false; + } + } + return true; + } +}; + +template inline int _parse_quadhex(input &in) { + int uni_ch = 0, hex; + for (int i = 0; i < 4; i++) { + if ((hex = in.getc()) == -1) { + return -1; + } + if ('0' <= hex && hex <= '9') { + hex -= '0'; + } else if ('A' <= hex && hex <= 'F') { + hex -= 'A' - 0xa; + } else if ('a' <= hex && hex <= 'f') { + hex -= 'a' - 0xa; + } else { + in.ungetc(); + return -1; + } + uni_ch = uni_ch * 16 + hex; + } + return uni_ch; +} + +template inline bool _parse_codepoint(String &out, input &in) { + int uni_ch; + if ((uni_ch = _parse_quadhex(in)) == -1) { + return false; + } + if (0xd800 <= uni_ch && uni_ch <= 0xdfff) { + if (0xdc00 <= uni_ch) { + // a second 16-bit of a surrogate pair appeared + return false; + } + // first 16-bit of surrogate pair, get the next one + if (in.getc() != '\\' || in.getc() != 'u') { + in.ungetc(); + return false; + } + int second = _parse_quadhex(in); + if (!(0xdc00 <= second && second <= 0xdfff)) { + return false; + } + uni_ch = ((uni_ch - 0xd800) << 10) | ((second - 0xdc00) & 0x3ff); + uni_ch += 0x10000; + } + if (uni_ch < 0x80) { + out.push_back(static_cast(uni_ch)); + } else { + if (uni_ch < 0x800) { + out.push_back(static_cast(0xc0 | (uni_ch >> 6))); + } else { + if (uni_ch < 0x10000) { + out.push_back(static_cast(0xe0 | (uni_ch >> 12))); + } else { + out.push_back(static_cast(0xf0 | (uni_ch >> 18))); + out.push_back(static_cast(0x80 | ((uni_ch >> 12) & 0x3f))); + } + out.push_back(static_cast(0x80 | ((uni_ch >> 6) & 0x3f))); + } + out.push_back(static_cast(0x80 | (uni_ch & 0x3f))); + } + return true; +} + +template inline bool _parse_string(String &out, input &in) { + while (1) { + int ch = in.getc(); + if (ch < ' ') { + in.ungetc(); + return false; + } else if (ch == '"') { + return true; + } else if (ch == '\\') { + if ((ch = in.getc()) == -1) { + return false; + } + switch (ch) { +#define MAP(sym, val) \ + case sym: \ + out.push_back(val); \ + break + MAP('"', '\"'); + MAP('\\', '\\'); + MAP('/', '/'); + MAP('b', '\b'); + MAP('f', '\f'); + MAP('n', '\n'); + MAP('r', '\r'); + MAP('t', '\t'); +#undef MAP + case 'u': + if (!_parse_codepoint(out, in)) { + return false; + } + break; + default: + return false; + } + } else { + out.push_back(static_cast(ch)); + } + } + return false; +} + +template inline bool _parse_array(Context &ctx, input &in) { + if (!ctx.parse_array_start()) { + return false; + } + size_t idx = 0; + if (in.expect(']')) { + return ctx.parse_array_stop(idx); + } + do { + if (!ctx.parse_array_item(in, idx)) { + return false; + } + idx++; + } while (in.expect(',')); + return in.expect(']') && ctx.parse_array_stop(idx); +} + +template inline bool _parse_object(Context &ctx, input &in) { + if (!ctx.parse_object_start()) { + return false; + } + if (in.expect('}')) { + return ctx.parse_object_stop(); + } + do { + std::string key; + if (!in.expect('"') || !_parse_string(key, in) || !in.expect(':')) { + return false; + } + if (!ctx.parse_object_item(in, key)) { + return false; + } + } while (in.expect(',')); + return in.expect('}') && ctx.parse_object_stop(); +} + +template inline std::string _parse_number(input &in) { + std::string num_str; + while (1) { + int ch = in.getc(); + if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == 'e' || ch == 'E') { + num_str.push_back(static_cast(ch)); + } else if (ch == '.') { +#if PICOJSON_USE_LOCALE + num_str += localeconv()->decimal_point; +#else + num_str.push_back('.'); +#endif + } else { + in.ungetc(); + break; + } + } + return num_str; +} + +template inline bool _parse(Context &ctx, input &in) { + in.skip_ws(); + int ch = in.getc(); + switch (ch) { +#define IS(ch, text, op) \ + case ch: \ + if (in.match(text) && op) { \ + return true; \ + } else { \ + return false; \ + } + IS('n', "ull", ctx.set_null()); + IS('f', "alse", ctx.set_bool(false)); + IS('t', "rue", ctx.set_bool(true)); +#undef IS + case '"': + return ctx.parse_string(in); + case '[': + return _parse_array(ctx, in); + case '{': + return _parse_object(ctx, in); + default: + if (('0' <= ch && ch <= '9') || ch == '-') { + double f; + char *endp; + in.ungetc(); + std::string num_str(_parse_number(in)); + if (num_str.empty()) { + return false; + } +#ifdef PICOJSON_USE_INT64 + { + errno = 0; + intmax_t ival = strtoimax(num_str.c_str(), &endp, 10); + if (errno == 0 && std::numeric_limits::min() <= ival && ival <= std::numeric_limits::max() && + endp == num_str.c_str() + num_str.size()) { + ctx.set_int64(ival); + return true; + } + } +#endif + f = strtod(num_str.c_str(), &endp); + if (endp == num_str.c_str() + num_str.size()) { + ctx.set_number(f); + return true; + } + return false; + } + break; + } + in.ungetc(); + return false; +} + +class deny_parse_context { +public: + bool set_null() { + return false; + } + bool set_bool(bool) { + return false; + } +#ifdef PICOJSON_USE_INT64 + bool set_int64(int64_t) { + return false; + } +#endif + bool set_number(double) { + return false; + } + template bool parse_string(input &) { + return false; + } + bool parse_array_start() { + return false; + } + template bool parse_array_item(input &, size_t) { + return false; + } + bool parse_array_stop(size_t) { + return false; + } + bool parse_object_start() { + return false; + } + template bool parse_object_item(input &, const std::string &) { + return false; + } +}; + +class default_parse_context { +protected: + value *out_; + size_t depths_; + +public: + default_parse_context(value *out, size_t depths = DEFAULT_MAX_DEPTHS) : out_(out), depths_(depths) { + } + bool set_null() { + *out_ = value(); + return true; + } + bool set_bool(bool b) { + *out_ = value(b); + return true; + } +#ifdef PICOJSON_USE_INT64 + bool set_int64(int64_t i) { + *out_ = value(i); + return true; + } +#endif + bool set_number(double f) { + *out_ = value(f); + return true; + } + template bool parse_string(input &in) { + *out_ = value(string_type, false); + return _parse_string(out_->get(), in); + } + bool parse_array_start() { + if (depths_ == 0) + return false; + --depths_; + *out_ = value(array_type, false); + return true; + } + template bool parse_array_item(input &in, size_t) { + array &a = out_->get(); + a.push_back(value()); + default_parse_context ctx(&a.back(), depths_); + return _parse(ctx, in); + } + bool parse_array_stop(size_t) { + ++depths_; + return true; + } + bool parse_object_start() { + if (depths_ == 0) + return false; + *out_ = value(object_type, false); + return true; + } + template bool parse_object_item(input &in, const std::string &key) { + object &o = out_->get(); + default_parse_context ctx(&o[key], depths_); + return _parse(ctx, in); + } + bool parse_object_stop() { + ++depths_; + return true; + } + +private: + default_parse_context(const default_parse_context &); + default_parse_context &operator=(const default_parse_context &); +}; + +class null_parse_context { +protected: + size_t depths_; + +public: + struct dummy_str { + void push_back(int) { + } + }; + +public: + null_parse_context(size_t depths = DEFAULT_MAX_DEPTHS) : depths_(depths) { + } + bool set_null() { + return true; + } + bool set_bool(bool) { + return true; + } +#ifdef PICOJSON_USE_INT64 + bool set_int64(int64_t) { + return true; + } +#endif + bool set_number(double) { + return true; + } + template bool parse_string(input &in) { + dummy_str s; + return _parse_string(s, in); + } + bool parse_array_start() { + if (depths_ == 0) + return false; + --depths_; + return true; + } + template bool parse_array_item(input &in, size_t) { + return _parse(*this, in); + } + bool parse_array_stop(size_t) { + ++depths_; + return true; + } + bool parse_object_start() { + if (depths_ == 0) + return false; + --depths_; + return true; + } + template bool parse_object_item(input &in, const std::string &) { + ++depths_; + return _parse(*this, in); + } + bool parse_object_stop() { + return true; + } + +private: + null_parse_context(const null_parse_context &); + null_parse_context &operator=(const null_parse_context &); +}; + +// obsolete, use the version below +template inline std::string parse(value &out, Iter &pos, const Iter &last) { + std::string err; + pos = parse(out, pos, last, &err); + return err; +} + +template inline Iter _parse(Context &ctx, const Iter &first, const Iter &last, std::string *err) { + input in(first, last); + if (!_parse(ctx, in) && err != NULL) { + char buf[64]; + SNPRINTF(buf, sizeof(buf), "syntax error at line %d near: ", in.line()); + *err = buf; + while (1) { + int ch = in.getc(); + if (ch == -1 || ch == '\n') { + break; + } else if (ch >= ' ') { + err->push_back(static_cast(ch)); + } + } + } + return in.cur(); +} + +template inline Iter parse(value &out, const Iter &first, const Iter &last, std::string *err) { + default_parse_context ctx(&out); + return _parse(ctx, first, last, err); +} + +inline std::string parse(value &out, const std::string &s) { + std::string err; + parse(out, s.begin(), s.end(), &err); + return err; +} + +inline std::string parse(value &out, std::istream &is) { + std::string err; + parse(out, std::istreambuf_iterator(is.rdbuf()), std::istreambuf_iterator(), &err); + return err; +} + +template struct last_error_t { static std::string s; }; +template std::string last_error_t::s; + +inline void set_last_error(const std::string &s) { + last_error_t::s = s; +} + +inline const std::string &get_last_error() { + return last_error_t::s; +} + +inline bool operator==(const value &x, const value &y) { + if (x.is()) + return y.is(); +#define PICOJSON_CMP(type) \ + if (x.is()) \ + return y.is() && x.get() == y.get() + PICOJSON_CMP(bool); + PICOJSON_CMP(double); + PICOJSON_CMP(std::string); + PICOJSON_CMP(array); + PICOJSON_CMP(object); +#undef PICOJSON_CMP + PICOJSON_ASSERT(0); +#ifdef _MSC_VER + __assume(0); +#endif + return false; +} + +inline bool operator!=(const value &x, const value &y) { + return !(x == y); +} +} + +#if !PICOJSON_USE_RVALUE_REFERENCE +namespace std { +template <> inline void swap(picojson::value &x, picojson::value &y) { + x.swap(y); +} +} +#endif + +inline std::istream &operator>>(std::istream &is, picojson::value &x) { + picojson::set_last_error(std::string()); + const std::string err(picojson::parse(x, is)); + if (!err.empty()) { + picojson::set_last_error(err); + is.setstate(std::ios::failbit); + } + return is; +} + +inline std::ostream &operator<<(std::ostream &os, const picojson::value &x) { + x.serialize(std::ostream_iterator(os)); + return os; +} +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif diff --git a/cppcheck-2.14.0/externals/simplecpp/CMakeLists.txt b/cppcheck-2.14.0/externals/simplecpp/CMakeLists.txt new file mode 100644 index 00000000..18430fea --- /dev/null +++ b/cppcheck-2.14.0/externals/simplecpp/CMakeLists.txt @@ -0,0 +1,11 @@ +file(GLOB hdrs "*.h") +file(GLOB srcs "*.cpp") + +add_library(simplecpp_objs OBJECT ${srcs} ${hdrs}) +if (BUILD_CORE_DLL) + target_compile_definitions(simplecpp_objs PRIVATE SIMPLECPP_EXPORT) +endif() + +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options_safe(simplecpp_objs -Wno-zero-as-null-pointer-constant) +endif() diff --git a/cppcheck-2.14.0/externals/simplecpp/LICENSE b/cppcheck-2.14.0/externals/simplecpp/LICENSE new file mode 100644 index 00000000..b1f013e9 --- /dev/null +++ b/cppcheck-2.14.0/externals/simplecpp/LICENSE @@ -0,0 +1,14 @@ +BSD Zero Clause License + +Copyright (c) 2023 simplecpp team + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/cppcheck-2.14.0/externals/simplecpp/simplecpp.cpp b/cppcheck-2.14.0/externals/simplecpp/simplecpp.cpp new file mode 100644 index 00000000..d3bd1c0c --- /dev/null +++ b/cppcheck-2.14.0/externals/simplecpp/simplecpp.cpp @@ -0,0 +1,3761 @@ +/* + * simplecpp - A simple and high-fidelity C/C++ preprocessor library + * Copyright (C) 2016-2023 simplecpp team + */ + +#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) +#define SIMPLECPP_WINDOWS +#define NOMINMAX +#endif + +#include "simplecpp.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if __cplusplus >= 201103L +#ifdef SIMPLECPP_WINDOWS +#include +#endif +#include +#endif +#include +#include + +#ifdef SIMPLECPP_WINDOWS +#include +#undef ERROR +#endif + +#if __cplusplus >= 201103L +#define OVERRIDE override +#define EXPLICIT explicit +#else +#define OVERRIDE +#define EXPLICIT +#endif + +#if (__cplusplus < 201103L) && !defined(__APPLE__) +#define nullptr NULL +#endif + +static bool isHex(const std::string &s) +{ + return s.size()>2 && (s.compare(0,2,"0x")==0 || s.compare(0,2,"0X")==0); +} + +static bool isOct(const std::string &s) +{ + return s.size()>1 && (s[0]=='0') && (s[1] >= '0') && (s[1] < '8'); +} + +// TODO: added an undercore since this conflicts with a function of the same name in utils.h from Cppcheck source when building Cppcheck with MSBuild +static bool isStringLiteral_(const std::string &s) +{ + return s.size() > 1 && (s[0]=='\"') && (*s.rbegin()=='\"'); +} + +// TODO: added an undercore since this conflicts with a function of the same name in utils.h from Cppcheck source when building Cppcheck with MSBuild +static bool isCharLiteral_(const std::string &s) +{ + // char literal patterns can include 'a', '\t', '\000', '\xff', 'abcd', and maybe '' + // This only checks for the surrounding '' but doesn't parse the content. + return s.size() > 1 && (s[0]=='\'') && (*s.rbegin()=='\''); +} + +static const simplecpp::TokenString DEFINE("define"); +static const simplecpp::TokenString UNDEF("undef"); + +static const simplecpp::TokenString INCLUDE("include"); + +static const simplecpp::TokenString ERROR("error"); +static const simplecpp::TokenString WARNING("warning"); + +static const simplecpp::TokenString IF("if"); +static const simplecpp::TokenString IFDEF("ifdef"); +static const simplecpp::TokenString IFNDEF("ifndef"); +static const simplecpp::TokenString DEFINED("defined"); +static const simplecpp::TokenString ELSE("else"); +static const simplecpp::TokenString ELIF("elif"); +static const simplecpp::TokenString ENDIF("endif"); + +static const simplecpp::TokenString PRAGMA("pragma"); +static const simplecpp::TokenString ONCE("once"); + +static const simplecpp::TokenString HAS_INCLUDE("__has_include"); + +static const simplecpp::TokenString INNER_COMMA(",,"); + +template static std::string toString(T t) +{ + // NOLINTNEXTLINE(misc-const-correctness) - false positive + std::ostringstream ostr; + ostr << t; + return ostr.str(); +} + +static long long stringToLL(const std::string &s) +{ + long long ret; + const bool hex = isHex(s); + const bool oct = isOct(s); + std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s); + if (hex) + istr >> std::hex; + else if (oct) + istr >> std::oct; + istr >> ret; + return ret; +} + +static unsigned long long stringToULL(const std::string &s) +{ + unsigned long long ret; + const bool hex = isHex(s); + const bool oct = isOct(s); + std::istringstream istr(hex ? s.substr(2) : oct ? s.substr(1) : s); + if (hex) + istr >> std::hex; + else if (oct) + istr >> std::oct; + istr >> ret; + return ret; +} + +static bool endsWith(const std::string &s, const std::string &e) +{ + return (s.size() >= e.size()) && std::equal(e.rbegin(), e.rend(), s.rbegin()); +} + +static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2) +{ + return tok1 && tok2 && tok1->location.sameline(tok2->location); +} + +static bool isAlternativeBinaryOp(const simplecpp::Token *tok, const std::string &alt) +{ + return (tok->name && + tok->str() == alt && + tok->previous && + tok->next && + (tok->previous->number || tok->previous->name || tok->previous->op == ')') && + (tok->next->number || tok->next->name || tok->next->op == '(')); +} + +static bool isAlternativeUnaryOp(const simplecpp::Token *tok, const std::string &alt) +{ + return ((tok->name && tok->str() == alt) && + (!tok->previous || tok->previous->op == '(') && + (tok->next && (tok->next->name || tok->next->number))); +} + +static std::string replaceAll(std::string s, const std::string& from, const std::string& to) +{ + for (size_t pos = s.find(from); pos != std::string::npos; pos = s.find(from, pos + to.size())) + s.replace(pos, from.size(), to); + return s; +} + +const std::string simplecpp::Location::emptyFileName; + +void simplecpp::Location::adjust(const std::string &str) +{ + if (strpbrk(str.c_str(), "\r\n") == nullptr) { + col += str.size(); + return; + } + + for (std::size_t i = 0U; i < str.size(); ++i) { + col++; + if (str[i] == '\n' || str[i] == '\r') { + col = 1; + line++; + if (str[i] == '\r' && (i+1)previous) + tok = tok->previous; + for (; tok; tok = tok->next) { + if (tok->previous) { + std::cout << (sameline(tok, tok->previous) ? ' ' : '\n'); + } + std::cout << tok->str(); + } + std::cout << std::endl; +} + +void simplecpp::Token::printOut() const +{ + for (const Token *tok = this; tok; tok = tok->next) { + if (tok != this) { + std::cout << (sameline(tok, tok->previous) ? ' ' : '\n'); + } + std::cout << tok->str(); + } + std::cout << std::endl; +} + +// cppcheck-suppress noConstructor - we call init() in the inherited to initialize the private members +class simplecpp::TokenList::Stream { +public: + virtual ~Stream() {} + + virtual int get() = 0; + virtual int peek() = 0; + virtual void unget() = 0; + virtual bool good() = 0; + + unsigned char readChar() + { + unsigned char ch = static_cast(get()); + + // For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the + // character is non-ASCII character then replace it with 0xff + if (isUtf16) { + const unsigned char ch2 = static_cast(get()); + const int ch16 = makeUtf16Char(ch, ch2); + ch = static_cast(((ch16 >= 0x80) ? 0xff : ch16)); + } + + // Handling of newlines.. + if (ch == '\r') { + ch = '\n'; + + int ch2 = get(); + if (isUtf16) { + const int c2 = get(); + ch2 = makeUtf16Char(ch2, c2); + } + + if (ch2 != '\n') + ungetChar(); + } + + return ch; + } + + unsigned char peekChar() + { + unsigned char ch = static_cast(peek()); + + // For UTF-16 encoded files the BOM is 0xfeff/0xfffe. If the + // character is non-ASCII character then replace it with 0xff + if (isUtf16) { + (void)get(); + const unsigned char ch2 = static_cast(peek()); + unget(); + const int ch16 = makeUtf16Char(ch, ch2); + ch = static_cast(((ch16 >= 0x80) ? 0xff : ch16)); + } + + // Handling of newlines.. + if (ch == '\r') + ch = '\n'; + + return ch; + } + + void ungetChar() + { + unget(); + if (isUtf16) + unget(); + } + +protected: + void init() { + // initialize since we use peek() in getAndSkipBOM() + isUtf16 = false; + bom = getAndSkipBOM(); + isUtf16 = (bom == 0xfeff || bom == 0xfffe); + } + +private: + inline int makeUtf16Char(const unsigned char ch, const unsigned char ch2) const + { + return (bom == 0xfeff) ? (ch<<8 | ch2) : (ch2<<8 | ch); + } + + unsigned short getAndSkipBOM() + { + const int ch1 = peek(); + + // The UTF-16 BOM is 0xfffe or 0xfeff. + if (ch1 >= 0xfe) { + (void)get(); + const unsigned short byte = (static_cast(ch1) << 8); + if (peek() >= 0xfe) + return byte | static_cast(get()); + unget(); + return 0; + } + + // Skip UTF-8 BOM 0xefbbbf + if (ch1 == 0xef) { + (void)get(); + if (peek() == 0xbb) { + (void)get(); + if (peek() == 0xbf) { + (void)get(); + return 0; + } + unget(); + } + unget(); + } + + return 0; + } + + unsigned short bom; +protected: + bool isUtf16; +}; + +class StdIStream : public simplecpp::TokenList::Stream { +public: + // cppcheck-suppress uninitDerivedMemberVar - we call Stream::init() to initialize the private members + EXPLICIT StdIStream(std::istream &istr) + : istr(istr) + { + assert(istr.good()); + init(); + } + + virtual int get() OVERRIDE { + return istr.get(); + } + virtual int peek() OVERRIDE { + return istr.peek(); + } + virtual void unget() OVERRIDE { + istr.unget(); + } + virtual bool good() OVERRIDE { + return istr.good(); + } + +private: + std::istream &istr; +}; + +class FileStream : public simplecpp::TokenList::Stream { +public: + // cppcheck-suppress uninitDerivedMemberVar - we call Stream::init() to initialize the private members + EXPLICIT FileStream(const std::string &filename, std::vector &files) + : file(fopen(filename.c_str(), "rb")) + , lastCh(0) + , lastStatus(0) + { + if (!file) { + files.push_back(filename); + throw simplecpp::Output(files, simplecpp::Output::FILE_NOT_FOUND, "File is missing: " + filename); + } + init(); + } + + ~FileStream() OVERRIDE { + fclose(file); + file = nullptr; + } + + virtual int get() OVERRIDE { + lastStatus = lastCh = fgetc(file); + return lastCh; + } + virtual int peek() OVERRIDE{ + // keep lastCh intact + const int ch = fgetc(file); + unget_internal(ch); + return ch; + } + virtual void unget() OVERRIDE { + unget_internal(lastCh); + } + virtual bool good() OVERRIDE { + return lastStatus != EOF; + } + +private: + void unget_internal(int ch) { + if (isUtf16) { + // TODO: use ungetc() as well + // UTF-16 has subsequent unget() calls + fseek(file, -1, SEEK_CUR); + } + else + ungetc(ch, file); + } + + FileStream(const FileStream&); + FileStream &operator=(const FileStream&); + + FILE *file; + int lastCh; + int lastStatus; +}; + +simplecpp::TokenList::TokenList(std::vector &filenames) : frontToken(nullptr), backToken(nullptr), files(filenames) {} + +simplecpp::TokenList::TokenList(std::istream &istr, std::vector &filenames, const std::string &filename, OutputList *outputList) + : frontToken(nullptr), backToken(nullptr), files(filenames) +{ + StdIStream stream(istr); + readfile(stream,filename,outputList); +} + +simplecpp::TokenList::TokenList(const std::string &filename, std::vector &filenames, OutputList *outputList) + : frontToken(nullptr), backToken(nullptr), files(filenames) +{ + try + { + FileStream stream(filename, filenames); + readfile(stream,filename,outputList); + } + catch(const simplecpp::Output & e) // TODO handle extra type of errors + { + outputList->push_back(e); + } +} + +simplecpp::TokenList::TokenList(const TokenList &other) : frontToken(nullptr), backToken(nullptr), files(other.files) +{ + *this = other; +} + +#if __cplusplus >= 201103L +simplecpp::TokenList::TokenList(TokenList &&other) : frontToken(nullptr), backToken(nullptr), files(other.files) +{ + *this = std::move(other); +} +#endif + +simplecpp::TokenList::~TokenList() +{ + clear(); +} + +simplecpp::TokenList &simplecpp::TokenList::operator=(const TokenList &other) +{ + if (this != &other) { + clear(); + files = other.files; + for (const Token *tok = other.cfront(); tok; tok = tok->next) + push_back(new Token(*tok)); + sizeOfType = other.sizeOfType; + } + return *this; +} + +#if __cplusplus >= 201103L +simplecpp::TokenList &simplecpp::TokenList::operator=(TokenList &&other) +{ + if (this != &other) { + clear(); + frontToken = other.frontToken; + other.frontToken = nullptr; + backToken = other.backToken; + other.backToken = nullptr; + files = other.files; + sizeOfType = std::move(other.sizeOfType); + } + return *this; +} +#endif + +void simplecpp::TokenList::clear() +{ + backToken = nullptr; + while (frontToken) { + Token * const next = frontToken->next; + delete frontToken; + frontToken = next; + } + sizeOfType.clear(); +} + +void simplecpp::TokenList::push_back(Token *tok) +{ + if (!frontToken) + frontToken = tok; + else + backToken->next = tok; + tok->previous = backToken; + backToken = tok; +} + +void simplecpp::TokenList::dump() const +{ + std::cout << stringify() << std::endl; +} + +std::string simplecpp::TokenList::stringify() const +{ + std::ostringstream ret; + Location loc(files); + for (const Token *tok = cfront(); tok; tok = tok->next) { + if (tok->location.line < loc.line || tok->location.fileIndex != loc.fileIndex) { + ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n"; + loc = tok->location; + } + + while (tok->location.line > loc.line) { + ret << '\n'; + loc.line++; + } + + if (sameline(tok->previous, tok)) + ret << ' '; + + ret << tok->str(); + + loc.adjust(tok->str()); + } + + return ret.str(); +} + +static bool isNameChar(unsigned char ch) +{ + return std::isalnum(ch) || ch == '_' || ch == '$'; +} + +static std::string escapeString(const std::string &str) +{ + std::ostringstream ostr; + ostr << '\"'; + for (std::size_t i = 1U; i < str.size() - 1; ++i) { + const char c = str[i]; + if (c == '\\' || c == '\"' || c == '\'') + ostr << '\\'; + ostr << c; + } + ostr << '\"'; + return ostr.str(); +} + +static void portabilityBackslash(simplecpp::OutputList *outputList, const std::vector &files, const simplecpp::Location &location) +{ + if (!outputList) + return; + simplecpp::Output err(files); + err.type = simplecpp::Output::PORTABILITY_BACKSLASH; + err.location = location; + err.msg = "Combination 'backslash space newline' is not portable."; + outputList->push_back(err); +} + +static bool isStringLiteralPrefix(const std::string &str) +{ + return str == "u" || str == "U" || str == "L" || str == "u8" || + str == "R" || str == "uR" || str == "UR" || str == "LR" || str == "u8R"; +} + +void simplecpp::TokenList::lineDirective(unsigned int fileIndex, unsigned int line, Location *location) +{ + if (fileIndex != location->fileIndex || line >= location->line) { + location->fileIndex = fileIndex; + location->line = line; + return; + } + + if (line + 2 >= location->line) { + location->line = line; + while (cback()->op != '#') + deleteToken(back()); + deleteToken(back()); + return; + } +} + +static const std::string COMMENT_END("*/"); + +void simplecpp::TokenList::readfile(Stream &stream, const std::string &filename, OutputList *outputList) +{ + std::stack loc; + + unsigned int multiline = 0U; + + const Token *oldLastToken = nullptr; + + Location location(files); + location.fileIndex = fileIndex(filename); + location.line = 1U; + location.col = 1U; + while (stream.good()) { + unsigned char ch = stream.readChar(); + if (!stream.good()) + break; + + if (ch >= 0x80) { + if (outputList) { + simplecpp::Output err(files); + err.type = simplecpp::Output::UNHANDLED_CHAR_ERROR; + err.location = location; + std::ostringstream s; + s << static_cast(ch); + err.msg = "The code contains unhandled character(s) (character code=" + s.str() + "). Neither unicode nor extended ascii is supported."; + outputList->push_back(err); + } + clear(); + return; + } + + if (ch == '\n') { + if (cback() && cback()->op == '\\') { + if (location.col > cback()->location.col + 1U) + portabilityBackslash(outputList, files, cback()->location); + ++multiline; + deleteToken(back()); + } else { + location.line += multiline + 1; + multiline = 0U; + } + if (!multiline) + location.col = 1; + + if (oldLastToken != cback()) { + oldLastToken = cback(); + if (!isLastLinePreprocessor()) + continue; + const std::string lastline(lastLine()); + if (lastline == "# file %str%") { + const Token *strtok = cback(); + while (strtok->comment) + strtok = strtok->previous; + loc.push(location); + location.fileIndex = fileIndex(strtok->str().substr(1U, strtok->str().size() - 2U)); + location.line = 1U; + } else if (lastline == "# line %num%") { + const Token *numtok = cback(); + while (numtok->comment) + numtok = numtok->previous; + lineDirective(location.fileIndex, std::atol(numtok->str().c_str()), &location); + } else if (lastline == "# %num% %str%" || lastline == "# line %num% %str%") { + const Token *strtok = cback(); + while (strtok->comment) + strtok = strtok->previous; + const Token *numtok = strtok->previous; + while (numtok->comment) + numtok = numtok->previous; + lineDirective(fileIndex(replaceAll(strtok->str().substr(1U, strtok->str().size() - 2U),"\\\\","\\")), + std::atol(numtok->str().c_str()), &location); + } + // #endfile + else if (lastline == "# endfile" && !loc.empty()) { + location = loc.top(); + loc.pop(); + } + } + + continue; + } + + if (ch <= ' ') { + location.col++; + continue; + } + + TokenString currentToken; + + if (cback() && cback()->location.line == location.line && cback()->previous && cback()->previous->op == '#') { + const Token* const llTok = lastLineTok(); + if (llTok && llTok->op == '#' && llTok->next && (llTok->next->str() == "error" || llTok->next->str() == "warning")) { + char prev = ' '; + while (stream.good() && (prev == '\\' || (ch != '\r' && ch != '\n'))) { + currentToken += ch; + prev = ch; + ch = stream.readChar(); + } + stream.ungetChar(); + push_back(new Token(currentToken, location)); + location.adjust(currentToken); + continue; + } + } + + // number or name + if (isNameChar(ch)) { + const bool num = std::isdigit(ch); + while (stream.good() && isNameChar(ch)) { + currentToken += ch; + ch = stream.readChar(); + if (num && ch=='\'' && isNameChar(stream.peekChar())) + ch = stream.readChar(); + } + + stream.ungetChar(); + } + + // comment + else if (ch == '/' && stream.peekChar() == '/') { + while (stream.good() && ch != '\r' && ch != '\n') { + currentToken += ch; + ch = stream.readChar(); + } + const std::string::size_type pos = currentToken.find_last_not_of(" \t"); + if (pos < currentToken.size() - 1U && currentToken[pos] == '\\') + portabilityBackslash(outputList, files, location); + if (currentToken[currentToken.size() - 1U] == '\\') { + ++multiline; + currentToken.erase(currentToken.size() - 1U); + } else { + stream.ungetChar(); + } + } + + // comment + else if (ch == '/' && stream.peekChar() == '*') { + currentToken = "/*"; + (void)stream.readChar(); + ch = stream.readChar(); + while (stream.good()) { + currentToken += ch; + if (currentToken.size() >= 4U && endsWith(currentToken, COMMENT_END)) + break; + ch = stream.readChar(); + } + // multiline.. + + std::string::size_type pos = 0; + while ((pos = currentToken.find("\\\n",pos)) != std::string::npos) { + currentToken.erase(pos,2); + ++multiline; + } + if (multiline || isLastLinePreprocessor()) { + pos = 0; + while ((pos = currentToken.find('\n',pos)) != std::string::npos) { + currentToken.erase(pos,1); + ++multiline; + } + } + } + + // string / char literal + else if (ch == '\"' || ch == '\'') { + std::string prefix; + if (cback() && cback()->name && isStringLiteralPrefix(cback()->str()) && + ((cback()->location.col + cback()->str().size()) == location.col) && + (cback()->location.line == location.line)) { + prefix = cback()->str(); + } + // C++11 raw string literal + if (ch == '\"' && !prefix.empty() && *cback()->str().rbegin() == 'R') { + std::string delim; + currentToken = ch; + prefix.resize(prefix.size() - 1); + ch = stream.readChar(); + while (stream.good() && ch != '(' && ch != '\n') { + delim += ch; + ch = stream.readChar(); + } + if (!stream.good() || ch == '\n') { + if (outputList) { + Output err(files); + err.type = Output::SYNTAX_ERROR; + err.location = location; + err.msg = "Invalid newline in raw string delimiter."; + outputList->push_back(err); + } + return; + } + const std::string endOfRawString(')' + delim + currentToken); + while (stream.good() && !(endsWith(currentToken, endOfRawString) && currentToken.size() > 1)) + currentToken += stream.readChar(); + if (!endsWith(currentToken, endOfRawString)) { + if (outputList) { + Output err(files); + err.type = Output::SYNTAX_ERROR; + err.location = location; + err.msg = "Raw string missing terminating delimiter."; + outputList->push_back(err); + } + return; + } + currentToken.erase(currentToken.size() - endOfRawString.size(), endOfRawString.size() - 1U); + currentToken = escapeString(currentToken); + currentToken.insert(0, prefix); + back()->setstr(currentToken); + location.adjust(currentToken); + if (currentToken.find_first_of("\r\n") == std::string::npos) + location.col += 2 + 2 * delim.size(); + else + location.col += 1 + delim.size(); + + continue; + } + + currentToken = readUntil(stream,location,ch,ch,outputList); + if (currentToken.size() < 2U) + // Error is reported by readUntil() + return; + + std::string s = currentToken; + std::string::size_type pos; + int newlines = 0; + while ((pos = s.find_first_of("\r\n")) != std::string::npos) { + s.erase(pos,1); + newlines++; + } + + if (prefix.empty()) + push_back(new Token(s, location)); // push string without newlines + else + back()->setstr(prefix + s); + + if (newlines > 0 ) { + const Token * const llTok = lastLineTok(); + if (llTok && llTok->op == '#' && llTok->next && llTok->next->str() == "define" && llTok->next->next) { + multiline += newlines; + location.adjust(s); + continue; + } + } + + location.adjust(currentToken); + continue; + } + + else { + currentToken += ch; + } + + if (*currentToken.begin() == '<') { + const Token * const llTok = lastLineTok(); + if (llTok && llTok->op == '#' && llTok->next && llTok->next->str() == "include") { + currentToken = readUntil(stream, location, '<', '>', outputList); + if (currentToken.size() < 2U) + return; + } + } + + push_back(new Token(currentToken, location)); + + if (multiline) + location.col += currentToken.size(); + else + location.adjust(currentToken); + } + + combineOperators(); +} + +void simplecpp::TokenList::constFold() +{ + while (cfront()) { + // goto last '(' + Token *tok = back(); + while (tok && tok->op != '(') + tok = tok->previous; + + // no '(', goto first token + if (!tok) + tok = front(); + + // Constant fold expression + constFoldUnaryNotPosNeg(tok); + constFoldMulDivRem(tok); + constFoldAddSub(tok); + constFoldShift(tok); + constFoldComparison(tok); + constFoldBitwise(tok); + constFoldLogicalOp(tok); + constFoldQuestionOp(&tok); + + // If there is no '(' we are done with the constant folding + if (tok->op != '(') + break; + + if (!tok->next || !tok->next->next || tok->next->next->op != ')') + break; + + tok = tok->next; + deleteToken(tok->previous); + deleteToken(tok->next); + } +} + +static bool isFloatSuffix(const simplecpp::Token *tok) +{ + if (!tok || tok->str().size() != 1U) + return false; + const char c = std::tolower(tok->str()[0]); + return c == 'f' || c == 'l'; +} + +void simplecpp::TokenList::combineOperators() +{ + std::stack executableScope; + executableScope.push(false); + for (Token *tok = front(); tok; tok = tok->next) { + if (tok->op == '{') { + if (executableScope.top()) { + executableScope.push(true); + continue; + } + const Token *prev = tok->previous; + while (prev && prev->isOneOf(";{}()")) + prev = prev->previous; + executableScope.push(prev && prev->op == ')'); + continue; + } + if (tok->op == '}') { + if (executableScope.size() > 1) + executableScope.pop(); + continue; + } + + if (tok->op == '.') { + // ellipsis ... + if (tok->next && tok->next->op == '.' && tok->next->location.col == (tok->location.col + 1) && + tok->next->next && tok->next->next->op == '.' && tok->next->next->location.col == (tok->location.col + 2)) { + tok->setstr("..."); + deleteToken(tok->next); + deleteToken(tok->next); + continue; + } + // float literals.. + if (tok->previous && tok->previous->number && sameline(tok->previous, tok)) { + tok->setstr(tok->previous->str() + '.'); + deleteToken(tok->previous); + if (sameline(tok, tok->next) && (isFloatSuffix(tok->next) || (tok->next && tok->next->startsWithOneOf("AaBbCcDdEeFfPp")))) { + tok->setstr(tok->str() + tok->next->str()); + deleteToken(tok->next); + } + } + if (tok->next && tok->next->number) { + tok->setstr(tok->str() + tok->next->str()); + deleteToken(tok->next); + } + } + // match: [0-9.]+E [+-] [0-9]+ + const char lastChar = tok->str()[tok->str().size() - 1]; + if (tok->number && !isOct(tok->str()) && + ((!isHex(tok->str()) && (lastChar == 'E' || lastChar == 'e')) || + (isHex(tok->str()) && (lastChar == 'P' || lastChar == 'p'))) && + tok->next && tok->next->isOneOf("+-") && tok->next->next && tok->next->next->number) { + tok->setstr(tok->str() + tok->next->op + tok->next->next->str()); + deleteToken(tok->next); + deleteToken(tok->next); + } + + if (tok->op == '\0' || !tok->next || tok->next->op == '\0') + continue; + if (!sameline(tok,tok->next)) + continue; + if (tok->location.col + 1U != tok->next->location.col) + continue; + + if (tok->next->op == '=' && tok->isOneOf("=!<>+-*/%&|^")) { + if (tok->op == '&' && !executableScope.top()) { + // don't combine &= if it is a anonymous reference parameter with default value: + // void f(x&=2) + int indentlevel = 0; + const Token *start = tok; + while (indentlevel >= 0 && start) { + if (start->op == ')') + ++indentlevel; + else if (start->op == '(') + --indentlevel; + else if (start->isOneOf(";{}")) + break; + start = start->previous; + } + if (indentlevel == -1 && start) { + const Token * const ftok = start; + bool isFuncDecl = ftok->name; + while (isFuncDecl) { + if (!start->name && start->str() != "::" && start->op != '*' && start->op != '&') + isFuncDecl = false; + if (!start->previous) + break; + if (start->previous->isOneOf(";{}:")) + break; + start = start->previous; + } + isFuncDecl &= start != ftok && start->name; + if (isFuncDecl) { + // TODO: we could loop through the parameters here and check if they are correct. + continue; + } + } + } + tok->setstr(tok->str() + "="); + deleteToken(tok->next); + } else if ((tok->op == '|' || tok->op == '&') && tok->op == tok->next->op) { + tok->setstr(tok->str() + tok->next->str()); + deleteToken(tok->next); + } else if (tok->op == ':' && tok->next->op == ':') { + tok->setstr(tok->str() + tok->next->str()); + deleteToken(tok->next); + } else if (tok->op == '-' && tok->next->op == '>') { + tok->setstr(tok->str() + tok->next->str()); + deleteToken(tok->next); + } else if ((tok->op == '<' || tok->op == '>') && tok->op == tok->next->op) { + tok->setstr(tok->str() + tok->next->str()); + deleteToken(tok->next); + if (tok->next && tok->next->op == '=' && tok->next->next && tok->next->next->op != '=') { + tok->setstr(tok->str() + tok->next->str()); + deleteToken(tok->next); + } + } else if ((tok->op == '+' || tok->op == '-') && tok->op == tok->next->op) { + if (tok->location.col + 1U != tok->next->location.col) + continue; + if (tok->previous && tok->previous->number) + continue; + if (tok->next->next && tok->next->next->number) + continue; + tok->setstr(tok->str() + tok->next->str()); + deleteToken(tok->next); + } + } +} + +static const std::string COMPL("compl"); +static const std::string NOT("not"); +void simplecpp::TokenList::constFoldUnaryNotPosNeg(simplecpp::Token *tok) +{ + for (; tok && tok->op != ')'; tok = tok->next) { + // "not" might be ! + if (isAlternativeUnaryOp(tok, NOT)) + tok->op = '!'; + // "compl" might be ~ + else if (isAlternativeUnaryOp(tok, COMPL)) + tok->op = '~'; + + if (tok->op == '!' && tok->next && tok->next->number) { + tok->setstr(tok->next->str() == "0" ? "1" : "0"); + deleteToken(tok->next); + } else if (tok->op == '~' && tok->next && tok->next->number) { + tok->setstr(toString(~stringToLL(tok->next->str()))); + deleteToken(tok->next); + } else { + if (tok->previous && (tok->previous->number || tok->previous->name)) + continue; + if (!tok->next || !tok->next->number) + continue; + switch (tok->op) { + case '+': + tok->setstr(tok->next->str()); + deleteToken(tok->next); + break; + case '-': + tok->setstr(tok->op + tok->next->str()); + deleteToken(tok->next); + break; + } + } + } +} + +void simplecpp::TokenList::constFoldMulDivRem(Token *tok) +{ + for (; tok && tok->op != ')'; tok = tok->next) { + if (!tok->previous || !tok->previous->number) + continue; + if (!tok->next || !tok->next->number) + continue; + + long long result; + if (tok->op == '*') + result = (stringToLL(tok->previous->str()) * stringToLL(tok->next->str())); + else if (tok->op == '/' || tok->op == '%') { + const long long rhs = stringToLL(tok->next->str()); + if (rhs == 0) + throw std::overflow_error("division/modulo by zero"); + const long long lhs = stringToLL(tok->previous->str()); + if (rhs == -1 && lhs == std::numeric_limits::min()) + throw std::overflow_error("division overflow"); + if (tok->op == '/') + result = (lhs / rhs); + else + result = (lhs % rhs); + } else + continue; + + tok = tok->previous; + tok->setstr(toString(result)); + deleteToken(tok->next); + deleteToken(tok->next); + } +} + +void simplecpp::TokenList::constFoldAddSub(Token *tok) +{ + for (; tok && tok->op != ')'; tok = tok->next) { + if (!tok->previous || !tok->previous->number) + continue; + if (!tok->next || !tok->next->number) + continue; + + long long result; + if (tok->op == '+') + result = stringToLL(tok->previous->str()) + stringToLL(tok->next->str()); + else if (tok->op == '-') + result = stringToLL(tok->previous->str()) - stringToLL(tok->next->str()); + else + continue; + + tok = tok->previous; + tok->setstr(toString(result)); + deleteToken(tok->next); + deleteToken(tok->next); + } +} + +void simplecpp::TokenList::constFoldShift(Token *tok) +{ + for (; tok && tok->op != ')'; tok = tok->next) { + if (!tok->previous || !tok->previous->number) + continue; + if (!tok->next || !tok->next->number) + continue; + + long long result; + if (tok->str() == "<<") + result = stringToLL(tok->previous->str()) << stringToLL(tok->next->str()); + else if (tok->str() == ">>") + result = stringToLL(tok->previous->str()) >> stringToLL(tok->next->str()); + else + continue; + + tok = tok->previous; + tok->setstr(toString(result)); + deleteToken(tok->next); + deleteToken(tok->next); + } +} + +static const std::string NOTEQ("not_eq"); +void simplecpp::TokenList::constFoldComparison(Token *tok) +{ + for (; tok && tok->op != ')'; tok = tok->next) { + if (isAlternativeBinaryOp(tok,NOTEQ)) + tok->setstr("!="); + + if (!tok->startsWithOneOf("<>=!")) + continue; + if (!tok->previous || !tok->previous->number) + continue; + if (!tok->next || !tok->next->number) + continue; + + int result; + if (tok->str() == "==") + result = (stringToLL(tok->previous->str()) == stringToLL(tok->next->str())); + else if (tok->str() == "!=") + result = (stringToLL(tok->previous->str()) != stringToLL(tok->next->str())); + else if (tok->str() == ">") + result = (stringToLL(tok->previous->str()) > stringToLL(tok->next->str())); + else if (tok->str() == ">=") + result = (stringToLL(tok->previous->str()) >= stringToLL(tok->next->str())); + else if (tok->str() == "<") + result = (stringToLL(tok->previous->str()) < stringToLL(tok->next->str())); + else if (tok->str() == "<=") + result = (stringToLL(tok->previous->str()) <= stringToLL(tok->next->str())); + else + continue; + + tok = tok->previous; + tok->setstr(toString(result)); + deleteToken(tok->next); + deleteToken(tok->next); + } +} + +static const std::string BITAND("bitand"); +static const std::string BITOR("bitor"); +static const std::string XOR("xor"); +void simplecpp::TokenList::constFoldBitwise(Token *tok) +{ + Token * const tok1 = tok; + for (const char *op = "&^|"; *op; op++) { + const std::string* alternativeOp; + if (*op == '&') + alternativeOp = &BITAND; + else if (*op == '|') + alternativeOp = &BITOR; + else + alternativeOp = &XOR; + for (tok = tok1; tok && tok->op != ')'; tok = tok->next) { + if (tok->op != *op && !isAlternativeBinaryOp(tok, *alternativeOp)) + continue; + if (!tok->previous || !tok->previous->number) + continue; + if (!tok->next || !tok->next->number) + continue; + long long result; + if (*op == '&') + result = (stringToLL(tok->previous->str()) & stringToLL(tok->next->str())); + else if (*op == '^') + result = (stringToLL(tok->previous->str()) ^ stringToLL(tok->next->str())); + else /*if (*op == '|')*/ + result = (stringToLL(tok->previous->str()) | stringToLL(tok->next->str())); + tok = tok->previous; + tok->setstr(toString(result)); + deleteToken(tok->next); + deleteToken(tok->next); + } + } +} + +static const std::string AND("and"); +static const std::string OR("or"); +void simplecpp::TokenList::constFoldLogicalOp(Token *tok) +{ + for (; tok && tok->op != ')'; tok = tok->next) { + if (tok->name) { + if (isAlternativeBinaryOp(tok,AND)) + tok->setstr("&&"); + else if (isAlternativeBinaryOp(tok,OR)) + tok->setstr("||"); + } + if (tok->str() != "&&" && tok->str() != "||") + continue; + if (!tok->previous || !tok->previous->number) + continue; + if (!tok->next || !tok->next->number) + continue; + + int result; + if (tok->str() == "||") + result = (stringToLL(tok->previous->str()) || stringToLL(tok->next->str())); + else /*if (tok->str() == "&&")*/ + result = (stringToLL(tok->previous->str()) && stringToLL(tok->next->str())); + + tok = tok->previous; + tok->setstr(toString(result)); + deleteToken(tok->next); + deleteToken(tok->next); + } +} + +void simplecpp::TokenList::constFoldQuestionOp(Token **tok1) +{ + bool gotoTok1 = false; + for (Token *tok = *tok1; tok && tok->op != ')'; tok = gotoTok1 ? *tok1 : tok->next) { + gotoTok1 = false; + if (tok->str() != "?") + continue; + if (!tok->previous || !tok->next || !tok->next->next) + throw std::runtime_error("invalid expression"); + if (!tok->previous->number) + continue; + if (tok->next->next->op != ':') + continue; + Token * const condTok = tok->previous; + Token * const trueTok = tok->next; + Token * const falseTok = trueTok->next->next; + if (!falseTok) + throw std::runtime_error("invalid expression"); + if (condTok == *tok1) + *tok1 = (condTok->str() != "0" ? trueTok : falseTok); + deleteToken(condTok->next); // ? + deleteToken(trueTok->next); // : + deleteToken(condTok->str() == "0" ? trueTok : falseTok); + deleteToken(condTok); + gotoTok1 = true; + } +} + +void simplecpp::TokenList::removeComments() +{ + Token *tok = frontToken; + while (tok) { + Token * const tok1 = tok; + tok = tok->next; + if (tok1->comment) + deleteToken(tok1); + } +} + +std::string simplecpp::TokenList::readUntil(Stream &stream, const Location &location, const char start, const char end, OutputList *outputList) +{ + std::string ret; + ret += start; + + bool backslash = false; + char ch = 0; + while (ch != end && ch != '\r' && ch != '\n' && stream.good()) { + ch = stream.readChar(); + if (backslash && ch == '\n') { + ch = 0; + backslash = false; + continue; + } + backslash = false; + ret += ch; + if (ch == '\\') { + bool update_ch = false; + char next = 0; + do { + next = stream.readChar(); + if (next == '\r' || next == '\n') { + ret.erase(ret.size()-1U); + backslash = (next == '\r'); + update_ch = false; + } else if (next == '\\') + update_ch = !update_ch; + ret += next; + } while (next == '\\'); + if (update_ch) + ch = next; + } + } + + if (!stream.good() || ch != end) { + clear(); + if (outputList) { + Output err(files); + err.type = Output::SYNTAX_ERROR; + err.location = location; + err.msg = std::string("No pair for character (") + start + "). Can't process file. File is either invalid or unicode, which is currently not supported."; + outputList->push_back(err); + } + return ""; + } + + return ret; +} + +std::string simplecpp::TokenList::lastLine(int maxsize) const +{ + std::string ret; + int count = 0; + for (const Token *tok = cback(); ; tok = tok->previous) { + if (!sameline(tok, cback())) { + break; + } + if (tok->comment) + continue; + if (++count > maxsize) + return ""; + if (!ret.empty()) + ret += ' '; + // add tokens in reverse for performance reasons + if (tok->str()[0] == '\"') + ret += "%rts%"; // %str% + else if (tok->number) + ret += "%mun%"; // %num% + else { + ret += tok->str(); + std::reverse(ret.end() - tok->str().length(), ret.end()); + } + } + std::reverse(ret.begin(), ret.end()); + return ret; +} + +const simplecpp::Token* simplecpp::TokenList::lastLineTok(int maxsize) const +{ + const Token* prevTok = nullptr; + int count = 0; + for (const Token *tok = cback(); ; tok = tok->previous) { + if (!sameline(tok, cback())) + break; + if (tok->comment) + continue; + if (++count > maxsize) + return nullptr; + prevTok = tok; + } + return prevTok; +} + +bool simplecpp::TokenList::isLastLinePreprocessor(int maxsize) const +{ + const Token * const prevTok = lastLineTok(maxsize); + return prevTok && prevTok->op == '#'; +} + +unsigned int simplecpp::TokenList::fileIndex(const std::string &filename) +{ + for (unsigned int i = 0; i < files.size(); ++i) { + if (files[i] == filename) + return i; + } + files.push_back(filename); + return files.size() - 1U; +} + + +namespace simplecpp { + class Macro; +#if __cplusplus >= 201103L + using MacroMap = std::unordered_map; +#else + typedef std::map MacroMap; +#endif + + class Macro { + public: + explicit Macro(std::vector &f) : nameTokDef(nullptr), valueToken(nullptr), endToken(nullptr), files(f), tokenListDefine(f), variadic(false), valueDefinedInCode_(false) {} + + Macro(const Token *tok, std::vector &f) : nameTokDef(nullptr), files(f), tokenListDefine(f), valueDefinedInCode_(true) { + if (sameline(tok->previousSkipComments(), tok)) + throw std::runtime_error("bad macro syntax"); + if (tok->op != '#') + throw std::runtime_error("bad macro syntax"); + const Token * const hashtok = tok; + tok = tok->next; + if (!tok || tok->str() != DEFINE) + throw std::runtime_error("bad macro syntax"); + tok = tok->next; + if (!tok || !tok->name || !sameline(hashtok,tok)) + throw std::runtime_error("bad macro syntax"); + if (!parseDefine(tok)) + throw std::runtime_error("bad macro syntax"); + } + + Macro(const std::string &name, const std::string &value, std::vector &f) : nameTokDef(nullptr), files(f), tokenListDefine(f), valueDefinedInCode_(false) { + const std::string def(name + ' ' + value); + std::istringstream istr(def); + StdIStream stream(istr); + tokenListDefine.readfile(stream); + if (!parseDefine(tokenListDefine.cfront())) + throw std::runtime_error("bad macro syntax. macroname=" + name + " value=" + value); + } + + Macro(const Macro &other) : nameTokDef(nullptr), files(other.files), tokenListDefine(other.files), valueDefinedInCode_(other.valueDefinedInCode_) { + *this = other; + } + + Macro &operator=(const Macro &other) { + if (this != &other) { + files = other.files; + valueDefinedInCode_ = other.valueDefinedInCode_; + if (other.tokenListDefine.empty()) + parseDefine(other.nameTokDef); + else { + tokenListDefine = other.tokenListDefine; + parseDefine(tokenListDefine.cfront()); + } + usageList = other.usageList; + } + return *this; + } + + bool valueDefinedInCode() const { + return valueDefinedInCode_; + } + + /** + * Expand macro. This will recursively expand inner macros. + * @param output destination tokenlist + * @param rawtok macro token + * @param macros list of macros + * @param inputFiles the input files + * @return token after macro + * @throw Can throw wrongNumberOfParameters or invalidHashHash + */ + const Token * expand(TokenList * const output, + const Token * rawtok, + const MacroMap ¯os, + std::vector &inputFiles) const { + std::set expandedmacros; + + TokenList output2(inputFiles); + + if (functionLike() && rawtok->next && rawtok->next->op == '(') { + // Copy macro call to a new tokenlist with no linebreaks + const Token * const rawtok1 = rawtok; + TokenList rawtokens2(inputFiles); + rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location)); + rawtok = rawtok->next; + rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location)); + rawtok = rawtok->next; + int par = 1; + while (rawtok && par > 0) { + if (rawtok->op == '(') + ++par; + else if (rawtok->op == ')') + --par; + else if (rawtok->op == '#' && !sameline(rawtok->previous, rawtok)) + throw Error(rawtok->location, "it is invalid to use a preprocessor directive as macro parameter"); + rawtokens2.push_back(new Token(rawtok->str(), rawtok1->location)); + rawtok = rawtok->next; + } + bool first = true; + if (valueToken && valueToken->str() == rawtok1->str()) + first = false; + if (expand(&output2, rawtok1->location, rawtokens2.cfront(), macros, expandedmacros, first)) + rawtok = rawtok1->next; + } else { + rawtok = expand(&output2, rawtok->location, rawtok, macros, expandedmacros); + } + while (output2.cback() && rawtok) { + unsigned int par = 0; + Token* macro2tok = output2.back(); + while (macro2tok) { + if (macro2tok->op == '(') { + if (par==0) + break; + --par; + } else if (macro2tok->op == ')') + ++par; + macro2tok = macro2tok->previous; + } + if (macro2tok) { // macro2tok->op == '(' + macro2tok = macro2tok->previous; + expandedmacros.insert(name()); + } else if (rawtok->op == '(') + macro2tok = output2.back(); + if (!macro2tok || !macro2tok->name) + break; + if (output2.cfront() != output2.cback() && macro2tok->str() == this->name()) + break; + const MacroMap::const_iterator macro = macros.find(macro2tok->str()); + if (macro == macros.end() || !macro->second.functionLike()) + break; + TokenList rawtokens2(inputFiles); + const Location loc(macro2tok->location); + while (macro2tok) { + Token * const next = macro2tok->next; + rawtokens2.push_back(new Token(macro2tok->str(), loc)); + output2.deleteToken(macro2tok); + macro2tok = next; + } + par = (rawtokens2.cfront() != rawtokens2.cback()) ? 1U : 0U; + const Token *rawtok2 = rawtok; + for (; rawtok2; rawtok2 = rawtok2->next) { + rawtokens2.push_back(new Token(rawtok2->str(), loc)); + if (rawtok2->op == '(') + ++par; + else if (rawtok2->op == ')') { + if (par <= 1U) + break; + --par; + } + } + if (!rawtok2 || par != 1U) + break; + if (macro->second.expand(&output2, rawtok->location, rawtokens2.cfront(), macros, expandedmacros) != nullptr) + break; + rawtok = rawtok2->next; + } + output->takeTokens(output2); + for (Token* tok = output->front(); tok; tok = tok->next) { + if (tok->str() == INNER_COMMA) + tok->setstr(","); + } + return rawtok; + } + + /** macro name */ + const TokenString &name() const { + return nameTokDef->str(); + } + + /** location for macro definition */ + const Location &defineLocation() const { + return nameTokDef->location; + } + + /** how has this macro been used so far */ + const std::list &usage() const { + return usageList; + } + + /** is this a function like macro */ + bool functionLike() const { + return nameTokDef->next && + nameTokDef->next->op == '(' && + sameline(nameTokDef, nameTokDef->next) && + nameTokDef->next->location.col == nameTokDef->location.col + nameTokDef->str().size(); + } + + /** base class for errors */ + struct Error { + Error(const Location &loc, const std::string &s) : location(loc), what(s) {} + const Location location; + const std::string what; + }; + + /** Struct that is thrown when macro is expanded with wrong number of parameters */ + struct wrongNumberOfParameters : public Error { + wrongNumberOfParameters(const Location &loc, const std::string ¯oName) : Error(loc, "Wrong number of parameters for macro \'" + macroName + "\'.") {} + }; + + /** Struct that is thrown when there is invalid ## usage */ + struct invalidHashHash : public Error { + static inline std::string format(const std::string ¯oName, const std::string &message) { + return "Invalid ## usage when expanding \'" + macroName + "\': " + message; + } + + invalidHashHash(const Location &loc, const std::string ¯oName, const std::string &message) + : Error(loc, format(macroName, message)) { } + + static inline invalidHashHash unexpectedToken(const Location &loc, const std::string ¯oName, const Token *tokenA) { + return invalidHashHash(loc, macroName, "Unexpected token '"+ tokenA->str()+"'"); + } + + static inline invalidHashHash cannotCombine(const Location &loc, const std::string ¯oName, const Token *tokenA, const Token *tokenB) { + return invalidHashHash(loc, macroName, "Combining '"+ tokenA->str()+ "' and '"+ tokenB->str() + "' yields an invalid token."); + } + + static inline invalidHashHash unexpectedNewline(const Location &loc, const std::string ¯oName) { + return invalidHashHash(loc, macroName, "Unexpected newline"); + } + + static inline invalidHashHash universalCharacterUB(const Location &loc, const std::string ¯oName, const Token* tokenA, const std::string& strAB) { + return invalidHashHash(loc, macroName, "Combining '\\"+ tokenA->str()+ "' and '"+ strAB.substr(tokenA->str().size()) + "' yields universal character '\\" + strAB + "'. This is undefined behavior according to C standard chapter 5.1.1.2, paragraph 4."); + } + }; + private: + /** Create new token where Token::macro is set for replaced tokens */ + Token *newMacroToken(const TokenString &str, const Location &loc, bool replaced, const Token *expandedFromToken=nullptr) const { + Token *tok = new Token(str,loc); + if (replaced) + tok->macro = nameTokDef->str(); + if (expandedFromToken) + tok->setExpandedFrom(expandedFromToken, this); + return tok; + } + + bool parseDefine(const Token *nametoken) { + nameTokDef = nametoken; + variadic = false; + if (!nameTokDef) { + valueToken = endToken = nullptr; + args.clear(); + return false; + } + + // function like macro.. + if (functionLike()) { + args.clear(); + const Token *argtok = nameTokDef->next->next; + while (sameline(nametoken, argtok) && argtok->op != ')') { + if (argtok->str() == "..." && + argtok->next && argtok->next->op == ')') { + variadic = true; + if (!argtok->previous->name) + args.push_back("__VA_ARGS__"); + argtok = argtok->next; // goto ')' + break; + } + if (argtok->op != ',') + args.push_back(argtok->str()); + argtok = argtok->next; + } + if (!sameline(nametoken, argtok)) { + endToken = argtok ? argtok->previous : argtok; + valueToken = nullptr; + return false; + } + valueToken = argtok ? argtok->next : nullptr; + } else { + args.clear(); + valueToken = nameTokDef->next; + } + + if (!sameline(valueToken, nameTokDef)) + valueToken = nullptr; + endToken = valueToken; + while (sameline(endToken, nameTokDef)) + endToken = endToken->next; + return true; + } + + unsigned int getArgNum(const TokenString &str) const { + unsigned int par = 0; + while (par < args.size()) { + if (str == args[par]) + return par; + par++; + } + return ~0U; + } + + std::vector getMacroParameters(const Token *nameTokInst, bool calledInDefine) const { + if (!nameTokInst->next || nameTokInst->next->op != '(' || !functionLike()) + return std::vector(); + + std::vector parametertokens; + parametertokens.push_back(nameTokInst->next); + unsigned int par = 0U; + for (const Token *tok = nameTokInst->next->next; calledInDefine ? sameline(tok, nameTokInst) : (tok != nullptr); tok = tok->next) { + if (tok->op == '(') + ++par; + else if (tok->op == ')') { + if (par == 0U) { + parametertokens.push_back(tok); + break; + } + --par; + } else if (par == 0U && tok->op == ',' && (!variadic || parametertokens.size() < args.size())) + parametertokens.push_back(tok); + } + return parametertokens; + } + + const Token *appendTokens(TokenList *tokens, + const Location &rawloc, + const Token * const lpar, + const MacroMap ¯os, + const std::set &expandedmacros, + const std::vector ¶metertokens) const { + if (!lpar || lpar->op != '(') + return nullptr; + unsigned int par = 0; + const Token *tok = lpar; + while (sameline(lpar, tok)) { + if (tok->op == '#' && sameline(tok,tok->next) && tok->next->op == '#' && sameline(tok,tok->next->next)) { + // A##B => AB + tok = expandHashHash(tokens, rawloc, tok, macros, expandedmacros, parametertokens); + } else if (tok->op == '#' && sameline(tok, tok->next) && tok->next->op != '#') { + tok = expandHash(tokens, rawloc, tok, macros, expandedmacros, parametertokens); + } else { + if (!expandArg(tokens, tok, rawloc, macros, expandedmacros, parametertokens)) { + bool expanded = false; + const MacroMap::const_iterator it = macros.find(tok->str()); + if (it != macros.end() && expandedmacros.find(tok->str()) == expandedmacros.end()) { + const Macro &m = it->second; + if (!m.functionLike()) { + Token* mtok = tokens->back(); + m.expand(tokens, rawloc, tok, macros, expandedmacros); + for (mtok = mtok->next; mtok; mtok = mtok->next) { + if (mtok->op == ',') + mtok->setstr(INNER_COMMA); + } + expanded = true; + } + } + if (!expanded) { + tokens->push_back(new Token(*tok)); + if (tok->macro.empty() && (par > 0 || tok->str() != "(")) + tokens->back()->macro = name(); + } + } + + if (tok->op == '(') + ++par; + else if (tok->op == ')') { + --par; + if (par == 0U) + break; + } + tok = tok->next; + } + } + for (Token *tok2 = tokens->front(); tok2; tok2 = tok2->next) + tok2->location = lpar->location; + return sameline(lpar,tok) ? tok : nullptr; + } + + const Token * expand(TokenList * const output, const Location &loc, const Token * const nameTokInst, const MacroMap ¯os, std::set expandedmacros, bool first=false) const { + + if (!first) + expandedmacros.insert(nameTokInst->str()); + + usageList.push_back(loc); + + if (nameTokInst->str() == "__FILE__") { + output->push_back(new Token('\"'+loc.file()+'\"', loc)); + return nameTokInst->next; + } + if (nameTokInst->str() == "__LINE__") { + output->push_back(new Token(toString(loc.line), loc)); + return nameTokInst->next; + } + if (nameTokInst->str() == "__COUNTER__") { + output->push_back(new Token(toString(usageList.size()-1U), loc)); + return nameTokInst->next; + } + + const bool calledInDefine = (loc.fileIndex != nameTokInst->location.fileIndex || + loc.line < nameTokInst->location.line); + + std::vector parametertokens1(getMacroParameters(nameTokInst, calledInDefine)); + + if (functionLike()) { + // No arguments => not macro expansion + if (nameTokInst->next && nameTokInst->next->op != '(') { + output->push_back(new Token(nameTokInst->str(), loc)); + return nameTokInst->next; + } + + // Parse macro-call + if (variadic) { + if (parametertokens1.size() < args.size()) { + throw wrongNumberOfParameters(nameTokInst->location, name()); + } + } else { + if (parametertokens1.size() != args.size() + (args.empty() ? 2U : 1U)) + throw wrongNumberOfParameters(nameTokInst->location, name()); + } + } + + // If macro call uses __COUNTER__ then expand that first + TokenList tokensparams(files); + std::vector parametertokens2; + if (!parametertokens1.empty()) { + bool counter = false; + for (const Token *tok = parametertokens1[0]; tok != parametertokens1.back(); tok = tok->next) { + if (tok->str() == "__COUNTER__") { + counter = true; + break; + } + } + + const MacroMap::const_iterator m = macros.find("__COUNTER__"); + + if (!counter || m == macros.end()) + parametertokens2.swap(parametertokens1); + else { + const Macro &counterMacro = m->second; + unsigned int par = 0; + for (const Token *tok = parametertokens1[0]; tok && par < parametertokens1.size(); tok = tok->next) { + if (tok->str() == "__COUNTER__") { + tokensparams.push_back(new Token(toString(counterMacro.usageList.size()), tok->location)); + counterMacro.usageList.push_back(tok->location); + } else { + tokensparams.push_back(new Token(*tok)); + if (tok == parametertokens1[par]) { + parametertokens2.push_back(tokensparams.cback()); + par++; + } + } + } + } + } + + Token * const output_end_1 = output->back(); + + // expand + for (const Token *tok = valueToken; tok != endToken;) { + if (tok->op != '#') { + // A##B => AB + if (sameline(tok, tok->next) && tok->next && tok->next->op == '#' && tok->next->next && tok->next->next->op == '#') { + if (!sameline(tok, tok->next->next->next)) + throw invalidHashHash::unexpectedNewline(tok->location, name()); + TokenList new_output(files); + if (!expandArg(&new_output, tok, parametertokens2)) + output->push_back(newMacroToken(tok->str(), loc, isReplaced(expandedmacros), tok)); + else if (new_output.empty()) // placemarker token + output->push_back(newMacroToken("", loc, isReplaced(expandedmacros))); + else + for (const Token *tok2 = new_output.cfront(); tok2; tok2 = tok2->next) + output->push_back(newMacroToken(tok2->str(), loc, isReplaced(expandedmacros), tok2)); + tok = tok->next; + } else { + tok = expandToken(output, loc, tok, macros, expandedmacros, parametertokens2); + } + continue; + } + + int numberOfHash = 1; + const Token *hashToken = tok->next; + while (sameline(tok,hashToken) && hashToken->op == '#') { + hashToken = hashToken->next; + ++numberOfHash; + } + if (numberOfHash == 4 && tok->next->location.col + 1 == tok->next->next->location.col) { + // # ## # => ## + output->push_back(newMacroToken("##", loc, isReplaced(expandedmacros))); + tok = hashToken; + continue; + } + + if (numberOfHash >= 2 && tok->location.col + 1 < tok->next->location.col) { + output->push_back(new Token(*tok)); + tok = tok->next; + continue; + } + + tok = tok->next; + if (tok == endToken) { + output->push_back(new Token(*tok->previous)); + break; + } + if (tok->op == '#') { + // A##B => AB + tok = expandHashHash(output, loc, tok->previous, macros, expandedmacros, parametertokens2); + } else { + // #123 => "123" + tok = expandHash(output, loc, tok->previous, macros, expandedmacros, parametertokens2); + } + } + + if (!functionLike()) { + for (Token *tok = output_end_1 ? output_end_1->next : output->front(); tok; tok = tok->next) { + tok->macro = nameTokInst->str(); + } + } + + if (!parametertokens1.empty()) + parametertokens1.swap(parametertokens2); + + return functionLike() ? parametertokens2.back()->next : nameTokInst->next; + } + + const Token *recursiveExpandToken(TokenList *output, TokenList &temp, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { + if (!(temp.cback() && temp.cback()->name && tok->next && tok->next->op == '(')) { + output->takeTokens(temp); + return tok->next; + } + + if (!sameline(tok, tok->next)) { + output->takeTokens(temp); + return tok->next; + } + + const MacroMap::const_iterator it = macros.find(temp.cback()->str()); + if (it == macros.end() || expandedmacros.find(temp.cback()->str()) != expandedmacros.end()) { + output->takeTokens(temp); + return tok->next; + } + + const Macro &calledMacro = it->second; + if (!calledMacro.functionLike()) { + output->takeTokens(temp); + return tok->next; + } + + TokenList temp2(files); + temp2.push_back(new Token(temp.cback()->str(), tok->location)); + + const Token * const tok2 = appendTokens(&temp2, loc, tok->next, macros, expandedmacros, parametertokens); + if (!tok2) + return tok->next; + output->takeTokens(temp); + output->deleteToken(output->back()); + calledMacro.expand(output, loc, temp2.cfront(), macros, expandedmacros); + return tok2->next; + } + + const Token *expandToken(TokenList *output, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { + // Not name.. + if (!tok->name) { + output->push_back(newMacroToken(tok->str(), loc, true, tok)); + return tok->next; + } + + // Macro parameter.. + { + TokenList temp(files); + if (tok->str() == "__VA_OPT__") { + if (sameline(tok, tok->next) && tok->next->str() == "(") { + tok = tok->next; + int paren = 1; + while (sameline(tok, tok->next)) { + if (tok->next->str() == "(") + ++paren; + else if (tok->next->str() == ")") + --paren; + if (paren == 0) + return tok->next->next; + tok = tok->next; + if (parametertokens.front()->next->str() != ")" && parametertokens.size() > args.size()) + tok = expandToken(output, loc, tok, macros, expandedmacros, parametertokens)->previous; + } + } + throw Error(tok->location, "Missing parenthesis for __VA_OPT__(content)"); + } + if (expandArg(&temp, tok, loc, macros, expandedmacros, parametertokens)) { + if (tok->str() == "__VA_ARGS__" && temp.empty() && output->cback() && output->cback()->str() == "," && + tok->nextSkipComments() && tok->nextSkipComments()->str() == ")") + output->deleteToken(output->back()); + return recursiveExpandToken(output, temp, loc, tok, macros, expandedmacros, parametertokens); + } + } + + // Macro.. + const MacroMap::const_iterator it = macros.find(tok->str()); + if (it != macros.end() && expandedmacros.find(tok->str()) == expandedmacros.end()) { + std::set expandedmacros2(expandedmacros); + expandedmacros2.insert(tok->str()); + + const Macro &calledMacro = it->second; + if (!calledMacro.functionLike()) { + TokenList temp(files); + calledMacro.expand(&temp, loc, tok, macros, expandedmacros); + return recursiveExpandToken(output, temp, loc, tok, macros, expandedmacros2, parametertokens); + } + if (!sameline(tok, tok->next) || tok->next->op != '(') { + output->push_back(newMacroToken(tok->str(), loc, true, tok)); + return tok->next; + } + TokenList tokens(files); + tokens.push_back(new Token(*tok)); + const Token * const tok2 = appendTokens(&tokens, loc, tok->next, macros, expandedmacros, parametertokens); + if (!tok2) { + output->push_back(newMacroToken(tok->str(), loc, true, tok)); + return tok->next; + } + TokenList temp(files); + calledMacro.expand(&temp, loc, tokens.cfront(), macros, expandedmacros); + return recursiveExpandToken(output, temp, loc, tok2, macros, expandedmacros2, parametertokens); + } + + if (tok->str() == DEFINED) { + const Token * const tok2 = tok->next; + const Token * const tok3 = tok2 ? tok2->next : nullptr; + const Token * const tok4 = tok3 ? tok3->next : nullptr; + const Token *defToken = nullptr; + const Token *lastToken = nullptr; + if (sameline(tok, tok4) && tok2->op == '(' && tok3->name && tok4->op == ')') { + defToken = tok3; + lastToken = tok4; + } else if (sameline(tok,tok2) && tok2->name) { + defToken = lastToken = tok2; + } + if (defToken) { + std::string macroName = defToken->str(); + if (defToken->next && defToken->next->op == '#' && defToken->next->next && defToken->next->next->op == '#' && defToken->next->next->next && defToken->next->next->next->name && sameline(defToken,defToken->next->next->next)) { + TokenList temp(files); + if (expandArg(&temp, defToken, parametertokens)) + macroName = temp.cback()->str(); + if (expandArg(&temp, defToken->next->next->next, parametertokens)) + macroName += temp.cback()->str(); + else + macroName += defToken->next->next->next->str(); + lastToken = defToken->next->next->next; + } + const bool def = (macros.find(macroName) != macros.end()); + output->push_back(newMacroToken(def ? "1" : "0", loc, true)); + return lastToken->next; + } + } + + output->push_back(newMacroToken(tok->str(), loc, true, tok)); + return tok->next; + } + + bool expandArg(TokenList *output, const Token *tok, const std::vector ¶metertokens) const { + if (!tok->name) + return false; + + const unsigned int argnr = getArgNum(tok->str()); + if (argnr >= args.size()) + return false; + + // empty variadic parameter + if (variadic && argnr + 1U >= parametertokens.size()) + return true; + + for (const Token *partok = parametertokens[argnr]->next; partok != parametertokens[argnr + 1U]; partok = partok->next) + output->push_back(new Token(*partok)); + + return true; + } + + bool expandArg(TokenList *output, const Token *tok, const Location &loc, const MacroMap ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { + if (!tok->name) + return false; + const unsigned int argnr = getArgNum(tok->str()); + if (argnr >= args.size()) + return false; + if (variadic && argnr + 1U >= parametertokens.size()) // empty variadic parameter + return true; + for (const Token *partok = parametertokens[argnr]->next; partok != parametertokens[argnr + 1U];) { + const MacroMap::const_iterator it = macros.find(partok->str()); + if (it != macros.end() && !partok->isExpandedFrom(&it->second) && (partok->str() == name() || expandedmacros.find(partok->str()) == expandedmacros.end())) + partok = it->second.expand(output, loc, partok, macros, expandedmacros); + else { + output->push_back(newMacroToken(partok->str(), loc, isReplaced(expandedmacros), partok)); + output->back()->macro = partok->macro; + partok = partok->next; + } + } + return true; + } + + /** + * Expand #X => "X" + * @param output destination tokenlist + * @param loc location for expanded token + * @param tok The # token + * @param macros all macros + * @param expandedmacros set with expanded macros, with this macro + * @param parametertokens parameters given when expanding this macro + * @return token after the X + */ + const Token *expandHash(TokenList *output, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { + TokenList tokenListHash(files); + tok = expandToken(&tokenListHash, loc, tok->next, macros, expandedmacros, parametertokens); + std::ostringstream ostr; + ostr << '\"'; + for (const Token *hashtok = tokenListHash.cfront(); hashtok; hashtok = hashtok->next) + ostr << hashtok->str(); + ostr << '\"'; + output->push_back(newMacroToken(escapeString(ostr.str()), loc, isReplaced(expandedmacros))); + return tok; + } + + /** + * Expand A##B => AB + * The A should already be expanded. Call this when you reach the first # token + * @param output destination tokenlist + * @param loc location for expanded token + * @param tok first # token + * @param macros all macros + * @param expandedmacros set with expanded macros, with this macro + * @param parametertokens parameters given when expanding this macro + * @return token after B + */ + const Token *expandHashHash(TokenList *output, const Location &loc, const Token *tok, const MacroMap ¯os, const std::set &expandedmacros, const std::vector ¶metertokens) const { + Token *A = output->back(); + if (!A) + throw invalidHashHash(tok->location, name(), "Missing first argument"); + if (!sameline(tok, tok->next) || !sameline(tok, tok->next->next)) + throw invalidHashHash::unexpectedNewline(tok->location, name()); + + const bool canBeConcatenatedWithEqual = A->isOneOf("+-*/%&|^") || A->str() == "<<" || A->str() == ">>"; + const bool canBeConcatenatedStringOrChar = isStringLiteral_(A->str()) || isCharLiteral_(A->str()); + if (!A->name && !A->number && A->op != ',' && !A->str().empty() && !canBeConcatenatedWithEqual && !canBeConcatenatedStringOrChar) + throw invalidHashHash::unexpectedToken(tok->location, name(), A); + + Token * const B = tok->next->next; + if (!B->name && !B->number && B->op && !B->isOneOf("#=")) + throw invalidHashHash::unexpectedToken(tok->location, name(), B); + + if ((canBeConcatenatedWithEqual && B->op != '=') || + (!canBeConcatenatedWithEqual && B->op == '=')) + throw invalidHashHash::cannotCombine(tok->location, name(), A, B); + + // Superficial check; more in-depth would in theory be possible _after_ expandArg + if (canBeConcatenatedStringOrChar && (B->number || !B->name)) + throw invalidHashHash::cannotCombine(tok->location, name(), A, B); + + TokenList tokensB(files); + const Token *nextTok = B->next; + + if (canBeConcatenatedStringOrChar) { + // It seems clearer to handle this case separately even though the code is similar-ish, but we don't want to merge here. + // TODO The question is whether the ## or varargs may still apply, and how to provoke? + if (expandArg(&tokensB, B, parametertokens)) { + for (Token *b = tokensB.front(); b; b = b->next) + b->location = loc; + } else { + tokensB.push_back(new Token(*B)); + tokensB.back()->location = loc; + } + output->takeTokens(tokensB); + } else { + std::string strAB; + + const bool varargs = variadic && !args.empty() && B->str() == args[args.size()-1U]; + + if (expandArg(&tokensB, B, parametertokens)) { + if (tokensB.empty()) + strAB = A->str(); + else if (varargs && A->op == ',') { + strAB = ","; + } else { + strAB = A->str() + tokensB.cfront()->str(); + tokensB.deleteToken(tokensB.front()); + } + } else { + strAB = A->str() + B->str(); + } + + // producing universal character is undefined behavior + if (A->previous && A->previous->str() == "\\") { + if (strAB[0] == 'u' && strAB.size() == 5) + throw invalidHashHash::universalCharacterUB(tok->location, name(), A, strAB); + if (strAB[0] == 'U' && strAB.size() == 9) + throw invalidHashHash::universalCharacterUB(tok->location, name(), A, strAB); + } + + if (varargs && tokensB.empty() && tok->previous->str() == ",") + output->deleteToken(A); + else if (strAB != "," && macros.find(strAB) == macros.end()) { + A->setstr(strAB); + for (Token *b = tokensB.front(); b; b = b->next) + b->location = loc; + output->takeTokens(tokensB); + } else if (sameline(B, nextTok) && sameline(B, nextTok->next) && nextTok->op == '#' && nextTok->next->op == '#') { + TokenList output2(files); + output2.push_back(new Token(strAB, tok->location)); + nextTok = expandHashHash(&output2, loc, nextTok, macros, expandedmacros, parametertokens); + output->deleteToken(A); + output->takeTokens(output2); + } else { + output->deleteToken(A); + TokenList tokens(files); + tokens.push_back(new Token(strAB, tok->location)); + // for function like macros, push the (...) + if (tokensB.empty() && sameline(B,B->next) && B->next->op=='(') { + const MacroMap::const_iterator it = macros.find(strAB); + if (it != macros.end() && expandedmacros.find(strAB) == expandedmacros.end() && it->second.functionLike()) { + const Token * const tok2 = appendTokens(&tokens, loc, B->next, macros, expandedmacros, parametertokens); + if (tok2) + nextTok = tok2->next; + } + } + expandToken(output, loc, tokens.cfront(), macros, expandedmacros, parametertokens); + for (Token *b = tokensB.front(); b; b = b->next) + b->location = loc; + output->takeTokens(tokensB); + } + } + + return nextTok; + } + + static bool isReplaced(const std::set &expandedmacros) { + // return true if size > 1 + std::set::const_iterator it = expandedmacros.begin(); + if (it == expandedmacros.end()) + return false; + ++it; + return (it != expandedmacros.end()); + } + + /** name token in definition */ + const Token *nameTokDef; + + /** arguments for macro */ + std::vector args; + + /** first token in replacement string */ + const Token *valueToken; + + /** token after replacement string */ + const Token *endToken; + + /** files */ + std::vector &files; + + /** this is used for -D where the definition is not seen anywhere in code */ + TokenList tokenListDefine; + + /** usage of this macro */ + mutable std::list usageList; + + /** is macro variadic? */ + bool variadic; + + /** was the value of this macro actually defined in the code? */ + bool valueDefinedInCode_; + }; +} + +namespace simplecpp { + +#ifdef __CYGWIN__ + bool startsWith(const std::string &str, const std::string &s) + { + return (str.size() >= s.size() && str.compare(0, s.size(), s) == 0); + } + + std::string convertCygwinToWindowsPath(const std::string &cygwinPath) + { + std::string windowsPath; + + std::string::size_type pos = 0; + if (cygwinPath.size() >= 11 && startsWith(cygwinPath, "/cygdrive/")) { + const unsigned char driveLetter = cygwinPath[10]; + if (std::isalpha(driveLetter)) { + if (cygwinPath.size() == 11) { + windowsPath = toupper(driveLetter); + windowsPath += ":\\"; // volume root directory + pos = 11; + } else if (cygwinPath[11] == '/') { + windowsPath = toupper(driveLetter); + windowsPath += ":"; + pos = 11; + } + } + } + + for (; pos < cygwinPath.size(); ++pos) { + unsigned char c = cygwinPath[pos]; + if (c == '/') + c = '\\'; + windowsPath += c; + } + + return windowsPath; + } +#endif +} + +#ifdef SIMPLECPP_WINDOWS + +#if __cplusplus >= 201103L +using MyMutex = std::mutex; +template +using MyLock = std::lock_guard; +#else +class MyMutex { +public: + MyMutex() { + InitializeCriticalSection(&m_criticalSection); + } + + ~MyMutex() { + DeleteCriticalSection(&m_criticalSection); + } + + CRITICAL_SECTION* lock() { + return &m_criticalSection; + } +private: + CRITICAL_SECTION m_criticalSection; +}; + +template +class MyLock { +public: + explicit MyLock(T& m) + : m_mutex(m) { + EnterCriticalSection(m_mutex.lock()); + } + + ~MyLock() { + LeaveCriticalSection(m_mutex.lock()); + } + +private: + MyLock& operator=(const MyLock&); + MyLock(const MyLock&); + + T& m_mutex; +}; +#endif + +class RealFileNameMap { +public: + RealFileNameMap() {} + + bool getCacheEntry(const std::string& path, std::string& returnPath) { + MyLock lock(m_mutex); + + const std::map::iterator it = m_fileMap.find(path); + if (it != m_fileMap.end()) { + returnPath = it->second; + return true; + } + return false; + } + + void addToCache(const std::string& path, const std::string& actualPath) { + MyLock lock(m_mutex); + m_fileMap[path] = actualPath; + } + +private: + std::map m_fileMap; + MyMutex m_mutex; +}; + +static RealFileNameMap realFileNameMap; + +static bool realFileName(const std::string &f, std::string &result) +{ + // are there alpha characters in last subpath? + bool alpha = false; + for (std::string::size_type pos = 1; pos <= f.size(); ++pos) { + const unsigned char c = f[f.size() - pos]; + if (c == '/' || c == '\\') + break; + if (std::isalpha(c)) { + alpha = true; + break; + } + } + + // do not convert this path if there are no alpha characters (either pointless or cause wrong results for . and ..) + if (!alpha) + return false; + + // Lookup filename or foldername on file system + if (!realFileNameMap.getCacheEntry(f, result)) { + + WIN32_FIND_DATAA FindFileData; + +#ifdef __CYGWIN__ + const std::string fConverted = simplecpp::convertCygwinToWindowsPath(f); + const HANDLE hFind = FindFirstFileExA(fConverted.c_str(), FindExInfoBasic, &FindFileData, FindExSearchNameMatch, NULL, 0); +#else + HANDLE hFind = FindFirstFileExA(f.c_str(), FindExInfoBasic, &FindFileData, FindExSearchNameMatch, NULL, 0); +#endif + + if (INVALID_HANDLE_VALUE == hFind) + return false; + result = FindFileData.cFileName; + realFileNameMap.addToCache(f, result); + FindClose(hFind); + } + return true; +} + +static RealFileNameMap realFilePathMap; + +/** Change case in given path to match filesystem */ +static std::string realFilename(const std::string &f) +{ + std::string ret; + ret.reserve(f.size()); // this will be the final size + if (realFilePathMap.getCacheEntry(f, ret)) + return ret; + + // Current subpath + std::string subpath; + + for (std::string::size_type pos = 0; pos < f.size(); ++pos) { + const unsigned char c = f[pos]; + + // Separator.. add subpath and separator + if (c == '/' || c == '\\') { + // if subpath is empty just add separator + if (subpath.empty()) { + ret += c; + continue; + } + + const bool isDriveSpecification = + (pos == 2 && subpath.size() == 2 && std::isalpha(subpath[0]) && subpath[1] == ':'); + + // Append real filename (proper case) + std::string f2; + if (!isDriveSpecification && realFileName(f.substr(0, pos), f2)) + ret += f2; + else + ret += subpath; + + subpath.clear(); + + // Append separator + ret += c; + } else { + subpath += c; + } + } + + if (!subpath.empty()) { + std::string f2; + if (realFileName(f,f2)) + ret += f2; + else + ret += subpath; + } + + realFilePathMap.addToCache(f, ret); + return ret; +} + +static bool isAbsolutePath(const std::string &path) +{ + if (path.length() >= 3 && path[0] > 0 && std::isalpha(path[0]) && path[1] == ':' && (path[2] == '\\' || path[2] == '/')) + return true; + return path.length() > 1U && (path[0] == '/' || path[0] == '\\'); +} +#else +#define realFilename(f) f + +static bool isAbsolutePath(const std::string &path) +{ + return path.length() > 1U && path[0] == '/'; +} +#endif + +namespace simplecpp { + /** + * perform path simplifications for . and .. + */ + std::string simplifyPath(std::string path) + { + if (path.empty()) + return path; + + std::string::size_type pos; + + // replace backslash separators + std::replace(path.begin(), path.end(), '\\', '/'); + + const bool unc(path.compare(0,2,"//") == 0); + + // replace "//" with "/" + pos = 0; + while ((pos = path.find("//",pos)) != std::string::npos) { + path.erase(pos,1); + } + + // remove "./" + pos = 0; + while ((pos = path.find("./",pos)) != std::string::npos) { + if (pos == 0 || path[pos - 1U] == '/') + path.erase(pos,2); + else + pos += 2; + } + + // remove trailing dot if path ends with "/." + if (endsWith(path,"/.")) + path.erase(path.size()-1); + + // simplify ".." + pos = 1; // don't simplify ".." if path starts with that + while ((pos = path.find("/..", pos)) != std::string::npos) { + // not end of path, then string must be "/../" + if (pos + 3 < path.size() && path[pos + 3] != '/') { + ++pos; + continue; + } + // get previous subpath + std::string::size_type pos1 = path.rfind('/', pos - 1U); + if (pos1 == std::string::npos) { + pos1 = 0; + } else { + pos1 += 1U; + } + const std::string previousSubPath = path.substr(pos1, pos - pos1); + if (previousSubPath == "..") { + // don't simplify + ++pos; + } else { + // remove previous subpath and ".." + path.erase(pos1, pos - pos1 + 4); + if (path.empty()) + path = "."; + // update pos + pos = (pos1 == 0) ? 1 : (pos1 - 1); + } + } + + // Remove trailing '/'? + //if (path.size() > 1 && endsWith(path, "/")) + // path.erase(path.size()-1); + + if (unc) + path = '/' + path; + + // cppcheck-suppress duplicateExpressionTernary - platform-dependent implementation + return strpbrk(path.c_str(), "*?") == nullptr ? realFilename(path) : path; + } +} + +/** Evaluate sizeof(type) */ +static void simplifySizeof(simplecpp::TokenList &expr, const std::map &sizeOfType) +{ + for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { + if (tok->str() != "sizeof") + continue; + simplecpp::Token *tok1 = tok->next; + if (!tok1) { + throw std::runtime_error("missing sizeof argument"); + } + simplecpp::Token *tok2 = tok1->next; + if (!tok2) { + throw std::runtime_error("missing sizeof argument"); + } + if (tok1->op == '(') { + tok1 = tok1->next; + while (tok2->op != ')') { + tok2 = tok2->next; + if (!tok2) { + throw std::runtime_error("invalid sizeof expression"); + } + } + } + + std::string type; + for (simplecpp::Token *typeToken = tok1; typeToken != tok2; typeToken = typeToken->next) { + if ((typeToken->str() == "unsigned" || typeToken->str() == "signed") && typeToken->next->name) + continue; + if (typeToken->str() == "*" && type.find('*') != std::string::npos) + continue; + if (!type.empty()) + type += ' '; + type += typeToken->str(); + } + + const std::map::const_iterator it = sizeOfType.find(type); + if (it != sizeOfType.end()) + tok->setstr(toString(it->second)); + else + continue; + + tok2 = tok2->next; + while (tok->next != tok2) + expr.deleteToken(tok->next); + } +} + +/** Evaluate __has_include(file) */ +static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader); +static void simplifyHasInclude(simplecpp::TokenList &expr, const simplecpp::DUI &dui) +{ + for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { + if (tok->str() != "__has_include") + continue; + simplecpp::Token *tok1 = tok->next; + if (!tok1) { + throw std::runtime_error("missing __has_include argument"); + } + simplecpp::Token *tok2 = tok1->next; + if (!tok2) { + throw std::runtime_error("missing __has_include argument"); + } + if (tok1->op == '(') { + tok1 = tok1->next; + while (tok2->op != ')') { + tok2 = tok2->next; + if (!tok2) { + throw std::runtime_error("invalid __has_include expression"); + } + } + } + + const std::string &sourcefile = tok->location.file(); + const bool systemheader = (tok1 && tok1->op == '<'); + std::string header; + if (systemheader) { + simplecpp::Token *tok3 = tok1->next; + if (!tok3) { + throw std::runtime_error("missing __has_include closing angular bracket"); + } + while (tok3->op != '>') { + tok3 = tok3->next; + if (!tok3) { + throw std::runtime_error("invalid __has_include expression"); + } + } + + for (simplecpp::Token *headerToken = tok1->next; headerToken != tok3; headerToken = headerToken->next) + header += headerToken->str(); + // cppcheck-suppress selfAssignment - platform-dependent implementation + header = realFilename(header); + } + else { + header = realFilename(tok1->str().substr(1U, tok1->str().size() - 2U)); + } + std::ifstream f; + const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader); + tok->setstr(header2.empty() ? "0" : "1"); + + tok2 = tok2->next; + while (tok->next != tok2) + expr.deleteToken(tok->next); + } +} + +static const char * const altopData[] = {"and","or","bitand","bitor","compl","not","not_eq","xor"}; +static const std::set altop(&altopData[0], &altopData[8]); +static void simplifyName(simplecpp::TokenList &expr) +{ + for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { + if (tok->name) { + if (altop.find(tok->str()) != altop.end()) { + bool alt; + if (tok->str() == "not" || tok->str() == "compl") { + alt = isAlternativeUnaryOp(tok,tok->str()); + } else { + alt = isAlternativeBinaryOp(tok,tok->str()); + } + if (alt) + continue; + } + tok->setstr("0"); + } + } +} + +/* + * Reads at least minlen and at most maxlen digits (inc. prefix) in base base + * from s starting at position pos and converts them to a + * unsigned long long value, updating pos to point to the first + * unused element of s. + * Returns ULLONG_MAX if the result is not representable and + * throws if the above requirements were not possible to satisfy. + */ +static unsigned long long stringToULLbounded( + const std::string& s, + std::size_t& pos, + int base = 0, + std::ptrdiff_t minlen = 1, + std::size_t maxlen = std::string::npos +) +{ + const std::string sub = s.substr(pos, maxlen); + const char * const start = sub.c_str(); + char* end; + const unsigned long long value = std::strtoull(start, &end, base); + pos += end - start; + if (end - start < minlen) + throw std::runtime_error("expected digit"); + return value; +} + +/* Converts character literal (including prefix, but not ud-suffix) + * to long long value. + * + * Assumes ASCII-compatible single-byte encoded str for narrow literals + * and UTF-8 otherwise. + * + * For target assumes + * - execution character set encoding matching str + * - UTF-32 execution wide-character set encoding + * - requirements for __STDC_UTF_16__, __STDC_UTF_32__ and __STDC_ISO_10646__ satisfied + * - char16_t is 16bit wide + * - char32_t is 32bit wide + * - wchar_t is 32bit wide and unsigned + * - matching char signedness to host + * - matching sizeof(int) to host + * + * For host assumes + * - ASCII-compatible execution character set + * + * For host and target assumes + * - CHAR_BIT == 8 + * - two's complement + * + * Implements multi-character narrow literals according to GCC's behavior, + * except multi code unit universal character names are not supported. + * Multi-character wide literals are not supported. + * Limited support of universal character names for non-UTF-8 execution character set encodings. + */ +long long simplecpp::characterLiteralToLL(const std::string& str) +{ + // default is wide/utf32 + bool narrow = false; + bool utf8 = false; + bool utf16 = false; + + std::size_t pos; + + if (!str.empty() && str[0] == '\'') { + narrow = true; + pos = 1; + } else if (str.size() >= 2 && str[0] == 'u' && str[1] == '\'') { + utf16 = true; + pos = 2; + } else if (str.size() >= 3 && str[0] == 'u' && str[1] == '8' && str[2] == '\'') { + utf8 = true; + pos = 3; + } else if (str.size() >= 2 && (str[0] == 'L' || str[0] == 'U') && str[1] == '\'') { + pos = 2; + } else + throw std::runtime_error("expected a character literal"); + + unsigned long long multivalue = 0; + + std::size_t nbytes = 0; + + while (pos + 1 < str.size()) { + if (str[pos] == '\'' || str[pos] == '\n') + throw std::runtime_error("raw single quotes and newlines not allowed in character literals"); + + if (nbytes >= 1 && !narrow) + throw std::runtime_error("multiple characters only supported in narrow character literals"); + + unsigned long long value; + + if (str[pos] == '\\') { + pos++; + const char escape = str[pos++]; + + if (pos >= str.size()) + throw std::runtime_error("unexpected end of character literal"); + + switch (escape) { + // obscure GCC extensions + case '%': + case '(': + case '[': + case '{': + // standard escape sequences + case '\'': + case '"': + case '?': + case '\\': + value = static_cast(escape); + break; + + case 'a': + value = static_cast('\a'); + break; + case 'b': + value = static_cast('\b'); + break; + case 'f': + value = static_cast('\f'); + break; + case 'n': + value = static_cast('\n'); + break; + case 'r': + value = static_cast('\r'); + break; + case 't': + value = static_cast('\t'); + break; + case 'v': + value = static_cast('\v'); + break; + + // GCC extension for ESC character + case 'e': + case 'E': + value = static_cast('\x1b'); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + // octal escape sequences consist of 1 to 3 digits + value = stringToULLbounded(str, --pos, 8, 1, 3); + break; + + case 'x': + // hexadecimal escape sequences consist of at least 1 digit + value = stringToULLbounded(str, pos, 16); + break; + + case 'u': + case 'U': { + // universal character names have exactly 4 or 8 digits + const std::size_t ndigits = (escape == 'u' ? 4 : 8); + value = stringToULLbounded(str, pos, 16, ndigits, ndigits); + + // UTF-8 encodes code points above 0x7f in multiple code units + // code points above 0x10ffff are not allowed + if (((narrow || utf8) && value > 0x7f) || (utf16 && value > 0xffff) || value > 0x10ffff) + throw std::runtime_error("code point too large"); + + if (value >= 0xd800 && value <= 0xdfff) + throw std::runtime_error("surrogate code points not allowed in universal character names"); + + break; + } + + default: + throw std::runtime_error("invalid escape sequence"); + } + } else { + value = static_cast(str[pos++]); + + if (!narrow && value >= 0x80) { + // Assuming this is a UTF-8 encoded code point. + // This decoder may not completely validate the input. + // Noncharacters are neither rejected nor replaced. + + int additional_bytes; + if (value >= 0xf5) // higher values would result in code points above 0x10ffff + throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid"); + if (value >= 0xf0) + additional_bytes = 3; + else if (value >= 0xe0) + additional_bytes = 2; + else if (value >= 0xc2) // 0xc0 and 0xc1 are always overlong 2-bytes encodings + additional_bytes = 1; + else + throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid"); + + value &= (1 << (6 - additional_bytes)) - 1; + + while (additional_bytes--) { + if (pos + 1 >= str.size()) + throw std::runtime_error("assumed UTF-8 encoded source, but character literal ends unexpectedly"); + + const unsigned char c = str[pos++]; + + if (((c >> 6) != 2) // ensure c has form 0xb10xxxxxx + || (!value && additional_bytes == 1 && c < 0xa0) // overlong 3-bytes encoding + || (!value && additional_bytes == 2 && c < 0x90)) // overlong 4-bytes encoding + throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid"); + + value = (value << 6) | (c & ((1 << 7) - 1)); + } + + if (value >= 0xd800 && value <= 0xdfff) + throw std::runtime_error("assumed UTF-8 encoded source, but sequence is invalid"); + + if ((utf8 && value > 0x7f) || (utf16 && value > 0xffff) || value > 0x10ffff) + throw std::runtime_error("code point too large"); + } + } + + if (((narrow || utf8) && value > std::numeric_limits::max()) || (utf16 && value >> 16) || value >> 32) + throw std::runtime_error("numeric escape sequence too large"); + + multivalue <<= CHAR_BIT; + multivalue |= value; + nbytes++; + } + + if (pos + 1 != str.size() || str[pos] != '\'') + throw std::runtime_error("missing closing quote in character literal"); + + if (!nbytes) + throw std::runtime_error("empty character literal"); + + // ordinary narrow character literal's value is determined by (possibly signed) char + if (narrow && nbytes == 1) + return static_cast(multivalue); + + // while multi-character literal's value is determined by (signed) int + if (narrow) + return static_cast(multivalue); + + // All other cases are unsigned. Since long long is at least 64bit wide, + // while the literals at most 32bit wide, the conversion preserves all values. + return multivalue; +} + +static void simplifyNumbers(simplecpp::TokenList &expr) +{ + for (simplecpp::Token *tok = expr.front(); tok; tok = tok->next) { + if (tok->str().size() == 1U) + continue; + if (tok->str().compare(0,2,"0x") == 0) + tok->setstr(toString(stringToULL(tok->str()))); + else if (!tok->number && tok->str().find('\'') != std::string::npos) + tok->setstr(toString(simplecpp::characterLiteralToLL(tok->str()))); + } +} + +static void simplifyComments(simplecpp::TokenList &expr) +{ + for (simplecpp::Token *tok = expr.front(); tok;) { + simplecpp::Token * const d = tok; + tok = tok->next; + if (d->comment) + expr.deleteToken(d); + } +} + +static long long evaluate(simplecpp::TokenList &expr, const simplecpp::DUI &dui, const std::map &sizeOfType) +{ + simplifyComments(expr); + simplifySizeof(expr, sizeOfType); + simplifyHasInclude(expr, dui); + simplifyName(expr); + simplifyNumbers(expr); + expr.constFold(); + // TODO: handle invalid expressions + return expr.cfront() && expr.cfront() == expr.cback() && expr.cfront()->number ? stringToLL(expr.cfront()->str()) : 0LL; +} + +static const simplecpp::Token *gotoNextLine(const simplecpp::Token *tok) +{ + const unsigned int line = tok->location.line; + const unsigned int file = tok->location.fileIndex; + while (tok && tok->location.line == line && tok->location.fileIndex == file) + tok = tok->next; + return tok; +} + +#ifdef SIMPLECPP_WINDOWS + +class NonExistingFilesCache { +public: + NonExistingFilesCache() {} + + bool contains(const std::string& path) { + MyLock lock(m_mutex); + return (m_pathSet.find(path) != m_pathSet.end()); + } + + void add(const std::string& path) { + MyLock lock(m_mutex); + m_pathSet.insert(path); + } + + void clear() { + MyLock lock(m_mutex); + m_pathSet.clear(); + } + +private: + std::set m_pathSet; + MyMutex m_mutex; +}; + +static NonExistingFilesCache nonExistingFilesCache; + +#endif + +static std::string openHeader(std::ifstream &f, const std::string &path) +{ + std::string simplePath = simplecpp::simplifyPath(path); +#ifdef SIMPLECPP_WINDOWS + if (nonExistingFilesCache.contains(simplePath)) + return ""; // file is known not to exist, skip expensive file open call +#endif + f.open(simplePath.c_str()); + if (f.is_open()) + return simplePath; +#ifdef SIMPLECPP_WINDOWS + nonExistingFilesCache.add(simplePath); +#endif + return ""; +} + +static std::string getRelativeFileName(const std::string &sourcefile, const std::string &header) +{ + if (sourcefile.find_first_of("\\/") != std::string::npos) + return simplecpp::simplifyPath(sourcefile.substr(0, sourcefile.find_last_of("\\/") + 1U) + header); + return simplecpp::simplifyPath(header); +} + +static std::string openHeaderRelative(std::ifstream &f, const std::string &sourcefile, const std::string &header) +{ + return openHeader(f, getRelativeFileName(sourcefile, header)); +} + +static std::string getIncludePathFileName(const std::string &includePath, const std::string &header) +{ + std::string path = includePath; + if (!path.empty() && path[path.size()-1U]!='/' && path[path.size()-1U]!='\\') + path += '/'; + return path + header; +} + +static std::string openHeaderIncludePath(std::ifstream &f, const simplecpp::DUI &dui, const std::string &header) +{ + for (std::list::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) { + std::string simplePath = openHeader(f, getIncludePathFileName(*it, header)); + if (!simplePath.empty()) + return simplePath; + } + return ""; +} + +static std::string openHeader(std::ifstream &f, const simplecpp::DUI &dui, const std::string &sourcefile, const std::string &header, bool systemheader) +{ + if (isAbsolutePath(header)) + return openHeader(f, header); + + std::string ret; + + if (systemheader) { + ret = openHeaderIncludePath(f, dui, header); + return ret; + } + + ret = openHeaderRelative(f, sourcefile, header); + if (ret.empty()) + return openHeaderIncludePath(f, dui, header); + return ret; +} + +static std::string getFileName(const std::map &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader) +{ + if (filedata.empty()) { + return ""; + } + if (isAbsolutePath(header)) { + return (filedata.find(header) != filedata.end()) ? simplecpp::simplifyPath(header) : ""; + } + + const std::string relativeFilename = getRelativeFileName(sourcefile, header); + if (!systemheader && filedata.find(relativeFilename) != filedata.end()) + return relativeFilename; + + for (std::list::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) { + std::string s = simplecpp::simplifyPath(getIncludePathFileName(*it, header)); + if (filedata.find(s) != filedata.end()) + return s; + } + + if (systemheader && filedata.find(header) != filedata.end()) + return header; + + return ""; +} + +static bool hasFile(const std::map &filedata, const std::string &sourcefile, const std::string &header, const simplecpp::DUI &dui, bool systemheader) +{ + return !getFileName(filedata, sourcefile, header, dui, systemheader).empty(); +} + +std::map simplecpp::load(const simplecpp::TokenList &rawtokens, std::vector &filenames, const simplecpp::DUI &dui, simplecpp::OutputList *outputList) +{ +#ifdef SIMPLECPP_WINDOWS + if (dui.clearIncludeCache) + nonExistingFilesCache.clear(); +#endif + + std::map ret; + + std::list filelist; + + // -include files + for (std::list::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) { + const std::string &filename = realFilename(*it); + + if (ret.find(filename) != ret.end()) + continue; + + std::ifstream fin(filename.c_str()); + if (!fin.is_open()) { + if (outputList) { + simplecpp::Output err(filenames); + err.type = simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND; + err.location = Location(filenames); + err.msg = "Can not open include file '" + filename + "' that is explicitly included."; + outputList->push_back(err); + } + continue; + } + fin.close(); + + TokenList *tokenlist = new TokenList(filename, filenames, outputList); + if (!tokenlist->front()) { + delete tokenlist; + continue; + } + + ret[filename] = tokenlist; + filelist.push_back(tokenlist->front()); + } + + for (const Token *rawtok = rawtokens.cfront(); rawtok || !filelist.empty(); rawtok = rawtok ? rawtok->next : nullptr) { + if (rawtok == nullptr) { + rawtok = filelist.back(); + filelist.pop_back(); + } + + if (rawtok->op != '#' || sameline(rawtok->previousSkipComments(), rawtok)) + continue; + + rawtok = rawtok->nextSkipComments(); + if (!rawtok || rawtok->str() != INCLUDE) + continue; + + const std::string &sourcefile = rawtok->location.file(); + + const Token * const htok = rawtok->nextSkipComments(); + if (!sameline(rawtok, htok)) + continue; + + const bool systemheader = (htok->str()[0] == '<'); + const std::string header(realFilename(htok->str().substr(1U, htok->str().size() - 2U))); + if (hasFile(ret, sourcefile, header, dui, systemheader)) + continue; + + std::ifstream f; + const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader); + if (!f.is_open()) + continue; + f.close(); + + TokenList *tokens = new TokenList(header2, filenames, outputList); + ret[header2] = tokens; + if (tokens->front()) + filelist.push_back(tokens->front()); + } + + return ret; +} + +static bool preprocessToken(simplecpp::TokenList &output, const simplecpp::Token **tok1, simplecpp::MacroMap ¯os, std::vector &files, simplecpp::OutputList *outputList) +{ + const simplecpp::Token * const tok = *tok1; + const simplecpp::MacroMap::const_iterator it = macros.find(tok->str()); + if (it != macros.end()) { + simplecpp::TokenList value(files); + try { + *tok1 = it->second.expand(&value, tok, macros, files); + } catch (simplecpp::Macro::Error &err) { + if (outputList) { + simplecpp::Output out(files); + out.type = simplecpp::Output::SYNTAX_ERROR; + out.location = err.location; + out.msg = "failed to expand \'" + tok->str() + "\', " + err.what; + outputList->push_back(out); + } + return false; + } + output.takeTokens(value); + } else { + if (!tok->comment) + output.push_back(new simplecpp::Token(*tok)); + *tok1 = tok->next; + } + return true; +} + +static void getLocaltime(struct tm <ime) +{ + time_t t; + time(&t); +#ifndef _WIN32 + // NOLINTNEXTLINE(misc-include-cleaner) - false positive + localtime_r(&t, <ime); +#else + localtime_s(<ime, &t); +#endif +} + +static std::string getDateDefine(const struct tm *timep) +{ + char buf[] = "??? ?? ????"; + strftime(buf, sizeof(buf), "%b %d %Y", timep); + return std::string("\"").append(buf).append("\""); +} + +static std::string getTimeDefine(const struct tm *timep) +{ + char buf[] = "??:??:??"; + strftime(buf, sizeof(buf), "%T", timep); + return std::string("\"").append(buf).append("\""); +} + +void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenList &rawtokens, std::vector &files, std::map &filedata, const simplecpp::DUI &dui, simplecpp::OutputList *outputList, std::list *macroUsage, std::list *ifCond) +{ +#ifdef SIMPLECPP_WINDOWS + if (dui.clearIncludeCache) + nonExistingFilesCache.clear(); +#endif + + std::map sizeOfType(rawtokens.sizeOfType); + sizeOfType.insert(std::make_pair("char", sizeof(char))); + sizeOfType.insert(std::make_pair("short", sizeof(short))); + sizeOfType.insert(std::make_pair("short int", sizeOfType["short"])); + sizeOfType.insert(std::make_pair("int", sizeof(int))); + sizeOfType.insert(std::make_pair("long", sizeof(long))); + sizeOfType.insert(std::make_pair("long int", sizeOfType["long"])); + sizeOfType.insert(std::make_pair("long long", sizeof(long long))); + sizeOfType.insert(std::make_pair("float", sizeof(float))); + sizeOfType.insert(std::make_pair("double", sizeof(double))); + sizeOfType.insert(std::make_pair("long double", sizeof(long double))); + sizeOfType.insert(std::make_pair("char *", sizeof(char *))); + sizeOfType.insert(std::make_pair("short *", sizeof(short *))); + sizeOfType.insert(std::make_pair("short int *", sizeOfType["short *"])); + sizeOfType.insert(std::make_pair("int *", sizeof(int *))); + sizeOfType.insert(std::make_pair("long *", sizeof(long *))); + sizeOfType.insert(std::make_pair("long int *", sizeOfType["long *"])); + sizeOfType.insert(std::make_pair("long long *", sizeof(long long *))); + sizeOfType.insert(std::make_pair("float *", sizeof(float *))); + sizeOfType.insert(std::make_pair("double *", sizeof(double *))); + sizeOfType.insert(std::make_pair("long double *", sizeof(long double *))); + + const bool hasInclude = (dui.std.size() == 5 && dui.std.compare(0,3,"c++") == 0 && dui.std >= "c++17"); + MacroMap macros; + for (std::list::const_iterator it = dui.defines.begin(); it != dui.defines.end(); ++it) { + const std::string ¯ostr = *it; + const std::string::size_type eq = macrostr.find('='); + const std::string::size_type par = macrostr.find('('); + const std::string macroname = macrostr.substr(0, std::min(eq,par)); + if (dui.undefined.find(macroname) != dui.undefined.end()) + continue; + const std::string lhs(macrostr.substr(0,eq)); + const std::string rhs(eq==std::string::npos ? std::string("1") : macrostr.substr(eq+1)); + const Macro macro(lhs, rhs, files); + macros.insert(std::pair(macro.name(), macro)); + } + + macros.insert(std::make_pair("__FILE__", Macro("__FILE__", "__FILE__", files))); + macros.insert(std::make_pair("__LINE__", Macro("__LINE__", "__LINE__", files))); + macros.insert(std::make_pair("__COUNTER__", Macro("__COUNTER__", "__COUNTER__", files))); + struct tm ltime = {}; + getLocaltime(ltime); + macros.insert(std::make_pair("__DATE__", Macro("__DATE__", getDateDefine(<ime), files))); + macros.insert(std::make_pair("__TIME__", Macro("__TIME__", getTimeDefine(<ime), files))); + + if (!dui.std.empty()) { + std::string std_def = simplecpp::getCStdString(dui.std); + if (!std_def.empty()) { + macros.insert(std::make_pair("__STDC_VERSION__", Macro("__STDC_VERSION__", std_def, files))); + } else { + std_def = simplecpp::getCppStdString(dui.std); + if (!std_def.empty()) + macros.insert(std::make_pair("__cplusplus", Macro("__cplusplus", std_def, files))); + } + } + + // True => code in current #if block should be kept + // ElseIsTrue => code in current #if block should be dropped. the code in the #else should be kept. + // AlwaysFalse => drop all code in #if and #else + enum IfState { True, ElseIsTrue, AlwaysFalse }; + std::stack ifstates; + ifstates.push(True); + + std::stack includetokenstack; + + std::set pragmaOnce; + + includetokenstack.push(rawtokens.cfront()); + for (std::list::const_iterator it = dui.includes.begin(); it != dui.includes.end(); ++it) { + const std::map::const_iterator f = filedata.find(*it); + if (f != filedata.end()) + includetokenstack.push(f->second->cfront()); + } + + std::map > maybeUsedMacros; + + for (const Token *rawtok = nullptr; rawtok || !includetokenstack.empty();) { + if (rawtok == nullptr) { + rawtok = includetokenstack.top(); + includetokenstack.pop(); + continue; + } + + if (rawtok->op == '#' && !sameline(rawtok->previousSkipComments(), rawtok)) { + if (!sameline(rawtok, rawtok->next)) { + rawtok = rawtok->next; + continue; + } + rawtok = rawtok->next; + if (!rawtok->name) { + rawtok = gotoNextLine(rawtok); + continue; + } + + if (ifstates.size() <= 1U && (rawtok->str() == ELIF || rawtok->str() == ELSE || rawtok->str() == ENDIF)) { + if (outputList) { + simplecpp::Output err(files); + err.type = Output::SYNTAX_ERROR; + err.location = rawtok->location; + err.msg = "#" + rawtok->str() + " without #if"; + outputList->push_back(err); + } + output.clear(); + return; + } + + if (ifstates.top() == True && (rawtok->str() == ERROR || rawtok->str() == WARNING)) { + if (outputList) { + simplecpp::Output err(rawtok->location.files); + err.type = rawtok->str() == ERROR ? Output::ERROR : Output::WARNING; + err.location = rawtok->location; + for (const Token *tok = rawtok->next; tok && sameline(rawtok,tok); tok = tok->next) { + if (!err.msg.empty() && isNameChar(tok->str()[0])) + err.msg += ' '; + err.msg += tok->str(); + } + err.msg = '#' + rawtok->str() + ' ' + err.msg; + outputList->push_back(err); + } + if (rawtok->str() == ERROR) { + output.clear(); + return; + } + } + + if (rawtok->str() == DEFINE) { + if (ifstates.top() != True) + continue; + try { + const Macro ¯o = Macro(rawtok->previous, files); + if (dui.undefined.find(macro.name()) == dui.undefined.end()) { + const MacroMap::iterator it = macros.find(macro.name()); + if (it == macros.end()) + macros.insert(std::pair(macro.name(), macro)); + else + it->second = macro; + } + } catch (const std::runtime_error &) { + if (outputList) { + simplecpp::Output err(files); + err.type = Output::SYNTAX_ERROR; + err.location = rawtok->location; + err.msg = "Failed to parse #define"; + outputList->push_back(err); + } + output.clear(); + return; + } + } else if (ifstates.top() == True && rawtok->str() == INCLUDE) { + TokenList inc1(files); + for (const Token *inctok = rawtok->next; sameline(rawtok,inctok); inctok = inctok->next) { + if (!inctok->comment) + inc1.push_back(new Token(*inctok)); + } + TokenList inc2(files); + if (!inc1.empty() && inc1.cfront()->name) { + const Token *inctok = inc1.cfront(); + if (!preprocessToken(inc2, &inctok, macros, files, outputList)) { + output.clear(); + return; + } + } else { + inc2.takeTokens(inc1); + } + + if (!inc2.empty() && inc2.cfront()->op == '<' && inc2.cback()->op == '>') { + TokenString hdr; + // TODO: Sometimes spaces must be added in the string + // Somehow preprocessToken etc must be told that the location should be source location not destination location + for (const Token *tok = inc2.cfront(); tok; tok = tok->next) { + hdr += tok->str(); + } + inc2.clear(); + inc2.push_back(new Token(hdr, inc1.cfront()->location)); + inc2.front()->op = '<'; + } + + if (inc2.empty() || inc2.cfront()->str().size() <= 2U) { + if (outputList) { + simplecpp::Output err(files); + err.type = Output::SYNTAX_ERROR; + err.location = rawtok->location; + err.msg = "No header in #include"; + outputList->push_back(err); + } + output.clear(); + return; + } + + const Token * const inctok = inc2.cfront(); + + const bool systemheader = (inctok->str()[0] == '<'); + const std::string header(realFilename(inctok->str().substr(1U, inctok->str().size() - 2U))); + std::string header2 = getFileName(filedata, rawtok->location.file(), header, dui, systemheader); + if (header2.empty()) { + // try to load file.. + std::ifstream f; + header2 = openHeader(f, dui, rawtok->location.file(), header, systemheader); + if (f.is_open()) { + TokenList * const tokens = new TokenList(f, files, header2, outputList); + filedata[header2] = tokens; + } + } + if (header2.empty()) { + if (outputList) { + simplecpp::Output out(files); + out.type = Output::MISSING_HEADER; + out.location = rawtok->location; + out.msg = "Header not found: " + inctok->str(); + outputList->push_back(out); + } + } else if (includetokenstack.size() >= 400) { + if (outputList) { + simplecpp::Output out(files); + out.type = Output::INCLUDE_NESTED_TOO_DEEPLY; + out.location = rawtok->location; + out.msg = "#include nested too deeply"; + outputList->push_back(out); + } + } else if (pragmaOnce.find(header2) == pragmaOnce.end()) { + includetokenstack.push(gotoNextLine(rawtok)); + const TokenList * const includetokens = filedata.find(header2)->second; + rawtok = includetokens ? includetokens->cfront() : nullptr; + continue; + } + } else if (rawtok->str() == IF || rawtok->str() == IFDEF || rawtok->str() == IFNDEF || rawtok->str() == ELIF) { + if (!sameline(rawtok,rawtok->next)) { + if (outputList) { + simplecpp::Output out(files); + out.type = Output::SYNTAX_ERROR; + out.location = rawtok->location; + out.msg = "Syntax error in #" + rawtok->str(); + outputList->push_back(out); + } + output.clear(); + return; + } + + bool conditionIsTrue; + if (ifstates.top() == AlwaysFalse || (ifstates.top() == ElseIsTrue && rawtok->str() != ELIF)) + conditionIsTrue = false; + else if (rawtok->str() == IFDEF) { + conditionIsTrue = (macros.find(rawtok->next->str()) != macros.end() || (hasInclude && rawtok->next->str() == HAS_INCLUDE)); + maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location); + } else if (rawtok->str() == IFNDEF) { + conditionIsTrue = (macros.find(rawtok->next->str()) == macros.end() && !(hasInclude && rawtok->next->str() == HAS_INCLUDE)); + maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location); + } else { /*if (rawtok->str() == IF || rawtok->str() == ELIF)*/ + TokenList expr(files); + for (const Token *tok = rawtok->next; tok && tok->location.sameline(rawtok->location); tok = tok->next) { + if (!tok->name) { + expr.push_back(new Token(*tok)); + continue; + } + + if (tok->str() == DEFINED) { + tok = tok->next; + const bool par = (tok && tok->op == '('); + if (par) + tok = tok->next; + maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location); + if (tok) { + if (macros.find(tok->str()) != macros.end()) + expr.push_back(new Token("1", tok->location)); + else if (hasInclude && tok->str() == HAS_INCLUDE) + expr.push_back(new Token("1", tok->location)); + else + expr.push_back(new Token("0", tok->location)); + } + if (par) + tok = tok ? tok->next : nullptr; + if (!tok || !sameline(rawtok,tok) || (par && tok->op != ')')) { + if (outputList) { + Output out(rawtok->location.files); + out.type = Output::SYNTAX_ERROR; + out.location = rawtok->location; + out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; + outputList->push_back(out); + } + output.clear(); + return; + } + continue; + } + + if (hasInclude && tok->str() == HAS_INCLUDE) { + tok = tok->next; + const bool par = (tok && tok->op == '('); + if (par) + tok = tok->next; + bool closingAngularBracket = false; + if (tok) { + const std::string &sourcefile = rawtok->location.file(); + const bool systemheader = (tok && tok->op == '<'); + std::string header; + + if (systemheader) { + while ((tok = tok->next) && tok->op != '>') + header += tok->str(); + // cppcheck-suppress selfAssignment - platform-dependent implementation + header = realFilename(header); + if (tok && tok->op == '>') + closingAngularBracket = true; + } + else { + header = realFilename(tok->str().substr(1U, tok->str().size() - 2U)); + closingAngularBracket = true; + } + std::ifstream f; + const std::string header2 = openHeader(f,dui,sourcefile,header,systemheader); + expr.push_back(new Token(header2.empty() ? "0" : "1", tok->location)); + } + if (par) + tok = tok ? tok->next : nullptr; + if (!tok || !sameline(rawtok,tok) || (par && tok->op != ')') || (!closingAngularBracket)) { + if (outputList) { + Output out(rawtok->location.files); + out.type = Output::SYNTAX_ERROR; + out.location = rawtok->location; + out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; + outputList->push_back(out); + } + output.clear(); + return; + } + continue; + } + + maybeUsedMacros[rawtok->next->str()].push_back(rawtok->next->location); + + const Token *tmp = tok; + if (!preprocessToken(expr, &tmp, macros, files, outputList)) { + output.clear(); + return; + } + if (!tmp) + break; + tok = tmp->previous; + } + try { + if (ifCond) { + std::string E; + for (const simplecpp::Token *tok = expr.cfront(); tok; tok = tok->next) + E += (E.empty() ? "" : " ") + tok->str(); + const long long result = evaluate(expr, dui, sizeOfType); + conditionIsTrue = (result != 0); + ifCond->push_back(IfCond(rawtok->location, E, result)); + } else { + const long long result = evaluate(expr, dui, sizeOfType); + conditionIsTrue = (result != 0); + } + } catch (const std::exception &e) { + if (outputList) { + Output out(rawtok->location.files); + out.type = Output::SYNTAX_ERROR; + out.location = rawtok->location; + out.msg = "failed to evaluate " + std::string(rawtok->str() == IF ? "#if" : "#elif") + " condition"; + if (e.what() && *e.what()) + out.msg += std::string(", ") + e.what(); + outputList->push_back(out); + } + output.clear(); + return; + } + } + + if (rawtok->str() != ELIF) { + // push a new ifstate.. + if (ifstates.top() != True) + ifstates.push(AlwaysFalse); + else + ifstates.push(conditionIsTrue ? True : ElseIsTrue); + } else if (ifstates.top() == True) { + ifstates.top() = AlwaysFalse; + } else if (ifstates.top() == ElseIsTrue && conditionIsTrue) { + ifstates.top() = True; + } + } else if (rawtok->str() == ELSE) { + ifstates.top() = (ifstates.top() == ElseIsTrue) ? True : AlwaysFalse; + } else if (rawtok->str() == ENDIF) { + ifstates.pop(); + } else if (rawtok->str() == UNDEF) { + if (ifstates.top() == True) { + const Token *tok = rawtok->next; + while (sameline(rawtok,tok) && tok->comment) + tok = tok->next; + if (sameline(rawtok, tok)) + macros.erase(tok->str()); + } + } else if (ifstates.top() == True && rawtok->str() == PRAGMA && rawtok->next && rawtok->next->str() == ONCE && sameline(rawtok,rawtok->next)) { + pragmaOnce.insert(rawtok->location.file()); + } + rawtok = gotoNextLine(rawtok); + continue; + } + + if (ifstates.top() != True) { + // drop code + rawtok = gotoNextLine(rawtok); + continue; + } + + bool hash=false, hashhash=false; + if (rawtok->op == '#' && sameline(rawtok,rawtok->next)) { + if (rawtok->next->op != '#') { + hash = true; + rawtok = rawtok->next; // skip '#' + } else if (sameline(rawtok,rawtok->next->next)) { + hashhash = true; + rawtok = rawtok->next->next; // skip '#' '#' + } + } + + const Location loc(rawtok->location); + TokenList tokens(files); + + if (!preprocessToken(tokens, &rawtok, macros, files, outputList)) { + output.clear(); + return; + } + + if (hash || hashhash) { + std::string s; + for (const Token *hashtok = tokens.cfront(); hashtok; hashtok = hashtok->next) + s += hashtok->str(); + if (hash) + output.push_back(new Token('\"' + s + '\"', loc)); + else if (output.back()) + output.back()->setstr(output.cback()->str() + s); + else + output.push_back(new Token(s, loc)); + } else { + output.takeTokens(tokens); + } + } + + if (macroUsage) { + for (simplecpp::MacroMap::const_iterator macroIt = macros.begin(); macroIt != macros.end(); ++macroIt) { + const Macro ¯o = macroIt->second; + std::list usage = macro.usage(); + const std::list& temp = maybeUsedMacros[macro.name()]; + usage.insert(usage.end(), temp.begin(), temp.end()); + for (std::list::const_iterator usageIt = usage.begin(); usageIt != usage.end(); ++usageIt) { + MacroUsage mu(usageIt->files, macro.valueDefinedInCode()); + mu.macroName = macro.name(); + mu.macroLocation = macro.defineLocation(); + mu.useLocation = *usageIt; + macroUsage->push_back(mu); + } + } + } +} + +void simplecpp::cleanup(std::map &filedata) +{ + for (std::map::iterator it = filedata.begin(); it != filedata.end(); ++it) + delete it->second; + filedata.clear(); +} + +std::string simplecpp::getCStdString(const std::string &std) +{ + if (std == "c90" || std == "c89" || std == "iso9899:1990" || std == "iso9899:199409" || std == "gnu90" || std == "gnu89") { + // __STDC_VERSION__ is not set for C90 although the macro was added in the 1994 amendments + return ""; + } + if (std == "c99" || std == "c9x" || std == "iso9899:1999" || std == "iso9899:199x" || std == "gnu99"|| std == "gnu9x") + return "199901L"; + if (std == "c11" || std == "c1x" || std == "iso9899:2011" || std == "gnu11" || std == "gnu1x") + return "201112L"; + if (std == "c17" || std == "c18" || std == "iso9899:2017" || std == "iso9899:2018" || std == "gnu17"|| std == "gnu18") + return "201710L"; + if (std == "c23" || std == "gnu23" || std == "c2x" || std == "gnu2x") { + // supported by GCC 9+ and Clang 9+ + // Clang 9, 10, 11, 12, 13 return "201710L" + // Clang 14, 15, 16, 17 return "202000L" + // Clang 9, 10, 11, 12, 13, 14, 15, 16, 17 do not support "c23" and "gnu23" + return "202311L"; + } + return ""; +} + +std::string simplecpp::getCppStdString(const std::string &std) +{ + if (std == "c++98" || std == "c++03" || std == "gnu++98" || std == "gnu++03") + return "199711L"; + if (std == "c++11" || std == "gnu++11" || std == "c++0x" || std == "gnu++0x") + return "201103L"; + if (std == "c++14" || std == "c++1y" || std == "gnu++14" || std == "gnu++1y") + return "201402L"; + if (std == "c++17" || std == "c++1z" || std == "gnu++17" || std == "gnu++1z") + return "201703L"; + if (std == "c++20" || std == "c++2a" || std == "gnu++20" || std == "gnu++2a") { + // GCC 10 returns "201703L" - correct in 11+ + return "202002L"; + } + if (std == "c++23" || std == "c++2b" || std == "gnu++23" || std == "gnu++2b") { + // supported by GCC 11+ and Clang 12+ + // GCC 11, 12, 13 return "202100L" + // Clang 12, 13, 14, 15, 16 do not support "c++23" and "gnu++23" and return "202101L" + // Clang 17, 18 return "202302L" + return "202302L"; + } + if (std == "c++26" || std == "c++2c" || std == "gnu++26" || std == "gnu++2c") { + // supported by Clang 17+ + return "202400L"; + } + return ""; +} + +#if (__cplusplus < 201103L) && !defined(__APPLE__) +#undef nullptr +#endif diff --git a/cppcheck-2.14.0/externals/simplecpp/simplecpp.h b/cppcheck-2.14.0/externals/simplecpp/simplecpp.h new file mode 100644 index 00000000..7ef0740c --- /dev/null +++ b/cppcheck-2.14.0/externals/simplecpp/simplecpp.h @@ -0,0 +1,376 @@ +/* + * simplecpp - A simple and high-fidelity C/C++ preprocessor library + * Copyright (C) 2016-2023 simplecpp team + */ + +#ifndef simplecppH +#define simplecppH + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +# ifdef SIMPLECPP_EXPORT +# define SIMPLECPP_LIB __declspec(dllexport) +# elif defined(SIMPLECPP_IMPORT) +# define SIMPLECPP_LIB __declspec(dllimport) +# else +# define SIMPLECPP_LIB +# endif +#else +# define SIMPLECPP_LIB +#endif + +#if (__cplusplus < 201103L) && !defined(__APPLE__) +#define nullptr NULL +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +// suppress warnings about "conversion from 'type1' to 'type2', possible loss of data" +# pragma warning(disable : 4267) +# pragma warning(disable : 4244) +#endif + +namespace simplecpp { + + typedef std::string TokenString; + class Macro; + + /** + * Location in source code + */ + class SIMPLECPP_LIB Location { + public: + explicit Location(const std::vector &f) : files(f), fileIndex(0), line(1U), col(0U) {} + + Location(const Location &loc) : files(loc.files), fileIndex(loc.fileIndex), line(loc.line), col(loc.col) {} + + Location &operator=(const Location &other) { + if (this != &other) { + fileIndex = other.fileIndex; + line = other.line; + col = other.col; + } + return *this; + } + + /** increment this location by string */ + void adjust(const std::string &str); + + bool operator<(const Location &rhs) const { + if (fileIndex != rhs.fileIndex) + return fileIndex < rhs.fileIndex; + if (line != rhs.line) + return line < rhs.line; + return col < rhs.col; + } + + bool sameline(const Location &other) const { + return fileIndex == other.fileIndex && line == other.line; + } + + const std::string& file() const { + return fileIndex < files.size() ? files[fileIndex] : emptyFileName; + } + + const std::vector &files; + unsigned int fileIndex; + unsigned int line; + unsigned int col; + private: + static const std::string emptyFileName; + }; + + /** + * token class. + * @todo don't use std::string representation - for both memory and performance reasons + */ + class SIMPLECPP_LIB Token { + public: + Token(const TokenString &s, const Location &loc) : + location(loc), previous(nullptr), next(nullptr), string(s) { + flags(); + } + + Token(const Token &tok) : + macro(tok.macro), op(tok.op), comment(tok.comment), name(tok.name), number(tok.number), location(tok.location), previous(nullptr), next(nullptr), string(tok.string), mExpandedFrom(tok.mExpandedFrom) { + } + + void flags() { + name = (std::isalpha(static_cast(string[0])) || string[0] == '_' || string[0] == '$') + && (std::memchr(string.c_str(), '\'', string.size()) == nullptr); + comment = string.size() > 1U && string[0] == '/' && (string[1] == '/' || string[1] == '*'); + number = isNumberLike(string); + op = (string.size() == 1U && !name && !comment && !number) ? string[0] : '\0'; + } + + const TokenString& str() const { + return string; + } + void setstr(const std::string &s) { + string = s; + flags(); + } + + bool isOneOf(const char ops[]) const; + bool startsWithOneOf(const char c[]) const; + bool endsWithOneOf(const char c[]) const; + static bool isNumberLike(const std::string& str) { + return std::isdigit(static_cast(str[0])) || + (str.size() > 1U && (str[0] == '-' || str[0] == '+') && std::isdigit(static_cast(str[1]))); + } + + TokenString macro; + char op; + bool comment; + bool name; + bool number; + Location location; + Token *previous; + Token *next; + + const Token *previousSkipComments() const { + const Token *tok = this->previous; + while (tok && tok->comment) + tok = tok->previous; + return tok; + } + + const Token *nextSkipComments() const { + const Token *tok = this->next; + while (tok && tok->comment) + tok = tok->next; + return tok; + } + + void setExpandedFrom(const Token *tok, const Macro* m) { + mExpandedFrom = tok->mExpandedFrom; + mExpandedFrom.insert(m); + } + bool isExpandedFrom(const Macro* m) const { + return mExpandedFrom.find(m) != mExpandedFrom.end(); + } + + void printAll() const; + void printOut() const; + private: + TokenString string; + + std::set mExpandedFrom; + + // Not implemented - prevent assignment + Token &operator=(const Token &tok); + }; + + /** Output from preprocessor */ + struct SIMPLECPP_LIB Output { + explicit Output(const std::vector &files) : type(ERROR), location(files) {} + enum Type { + ERROR, /* #error */ + WARNING, /* #warning */ + MISSING_HEADER, + INCLUDE_NESTED_TOO_DEEPLY, + SYNTAX_ERROR, + PORTABILITY_BACKSLASH, + UNHANDLED_CHAR_ERROR, + EXPLICIT_INCLUDE_NOT_FOUND, + FILE_NOT_FOUND + } type; + explicit Output(const std::vector& files, Type type, const std::string& msg) : type(type), location(files), msg(msg) {} + Location location; + std::string msg; + }; + + typedef std::list OutputList; + + /** List of tokens. */ + class SIMPLECPP_LIB TokenList { + public: + class Stream; + + explicit TokenList(std::vector &filenames); + /** generates a token list from the given std::istream parameter */ + TokenList(std::istream &istr, std::vector &filenames, const std::string &filename=std::string(), OutputList *outputList = nullptr); + /** generates a token list from the given filename parameter */ + TokenList(const std::string &filename, std::vector &filenames, OutputList *outputList = nullptr); + TokenList(const TokenList &other); +#if __cplusplus >= 201103L + TokenList(TokenList &&other); +#endif + ~TokenList(); + TokenList &operator=(const TokenList &other); +#if __cplusplus >= 201103L + TokenList &operator=(TokenList &&other); +#endif + + void clear(); + bool empty() const { + return !frontToken; + } + void push_back(Token *tok); + + void dump() const; + std::string stringify() const; + + void readfile(Stream &stream, const std::string &filename=std::string(), OutputList *outputList = nullptr); + void constFold(); + + void removeComments(); + + Token *front() { + return frontToken; + } + + const Token *cfront() const { + return frontToken; + } + + Token *back() { + return backToken; + } + + const Token *cback() const { + return backToken; + } + + void deleteToken(Token *tok) { + if (!tok) + return; + Token * const prev = tok->previous; + Token * const next = tok->next; + if (prev) + prev->next = next; + if (next) + next->previous = prev; + if (frontToken == tok) + frontToken = next; + if (backToken == tok) + backToken = prev; + delete tok; + } + + void takeTokens(TokenList &other) { + if (!other.frontToken) + return; + if (!frontToken) { + frontToken = other.frontToken; + } else { + backToken->next = other.frontToken; + other.frontToken->previous = backToken; + } + backToken = other.backToken; + other.frontToken = other.backToken = nullptr; + } + + /** sizeof(T) */ + std::map sizeOfType; + + private: + void combineOperators(); + + void constFoldUnaryNotPosNeg(Token *tok); + void constFoldMulDivRem(Token *tok); + void constFoldAddSub(Token *tok); + void constFoldShift(Token *tok); + void constFoldComparison(Token *tok); + void constFoldBitwise(Token *tok); + void constFoldLogicalOp(Token *tok); + void constFoldQuestionOp(Token **tok1); + + std::string readUntil(Stream &stream, const Location &location, char start, char end, OutputList *outputList); + void lineDirective(unsigned int fileIndex, unsigned int line, Location *location); + + std::string lastLine(int maxsize=1000) const; + const Token* lastLineTok(int maxsize=1000) const; + bool isLastLinePreprocessor(int maxsize=1000) const; + + unsigned int fileIndex(const std::string &filename); + + Token *frontToken; + Token *backToken; + std::vector &files; + }; + + /** Tracking how macros are used */ + struct SIMPLECPP_LIB MacroUsage { + explicit MacroUsage(const std::vector &f, bool macroValueKnown_) : macroLocation(f), useLocation(f), macroValueKnown(macroValueKnown_) {} + std::string macroName; + Location macroLocation; + Location useLocation; + bool macroValueKnown; + }; + + /** Tracking #if/#elif expressions */ + struct SIMPLECPP_LIB IfCond { + explicit IfCond(const Location& location, const std::string &E, long long result) : location(location), E(E), result(result) {} + Location location; // location of #if/#elif + std::string E; // preprocessed condition + long long result; // condition result + }; + + /** + * Command line preprocessor settings. + * On the command line these are configured by -D, -U, -I, --include, -std + */ + struct SIMPLECPP_LIB DUI { + DUI() : clearIncludeCache(false) {} + std::list defines; + std::set undefined; + std::list includePaths; + std::list includes; + std::string std; + bool clearIncludeCache; + }; + + SIMPLECPP_LIB long long characterLiteralToLL(const std::string& str); + + SIMPLECPP_LIB std::map load(const TokenList &rawtokens, std::vector &filenames, const DUI &dui, OutputList *outputList = nullptr); + + /** + * Preprocess + * @todo simplify interface + * @param output TokenList that receives the preprocessing output + * @param rawtokens Raw tokenlist for top sourcefile + * @param files internal data of simplecpp + * @param filedata output from simplecpp::load() + * @param dui defines, undefs, and include paths + * @param outputList output: list that will receive output messages + * @param macroUsage output: macro usage + * @param ifCond output: #if/#elif expressions + */ + SIMPLECPP_LIB void preprocess(TokenList &output, const TokenList &rawtokens, std::vector &files, std::map &filedata, const DUI &dui, OutputList *outputList = nullptr, std::list *macroUsage = nullptr, std::list *ifCond = nullptr); + + /** + * Deallocate data + */ + SIMPLECPP_LIB void cleanup(std::map &filedata); + + /** Simplify path */ + SIMPLECPP_LIB std::string simplifyPath(std::string path); + + /** Convert Cygwin path to Windows path */ + SIMPLECPP_LIB std::string convertCygwinToWindowsPath(const std::string &cygwinPath); + + /** Returns the __STDC_VERSION__ value for a given standard */ + SIMPLECPP_LIB std::string getCStdString(const std::string &std); + + /** Returns the __cplusplus value for a given standard */ + SIMPLECPP_LIB std::string getCppStdString(const std::string &std); +} + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + +#if (__cplusplus < 201103L) && !defined(__APPLE__) +#undef nullptr +#endif + +#endif diff --git a/cppcheck-2.14.0/externals/tinyxml2/CMakeLists.txt b/cppcheck-2.14.0/externals/tinyxml2/CMakeLists.txt new file mode 100644 index 00000000..aa44e241 --- /dev/null +++ b/cppcheck-2.14.0/externals/tinyxml2/CMakeLists.txt @@ -0,0 +1,21 @@ +file(GLOB hdrs "*.h") +file(GLOB srcs "*.cpp") + +add_library(tinyxml2_objs OBJECT ${srcs} ${hdrs}) +if (BUILD_CORE_DLL) + target_compile_definitions(tinyxml2_objs PRIVATE TINYXML2_EXPORT) +endif() + +# TODO: needs to be fixed upstream +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(tinyxml2_objs PRIVATE -Wno-suggest-attribute=format) +endif() +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options_safe(tinyxml2_objs -Wno-implicit-fallthrough) + target_compile_options_safe(tinyxml2_objs -Wno-suggest-destructor-override) + target_compile_options_safe(tinyxml2_objs -Wno-zero-as-null-pointer-constant) + target_compile_options_safe(tinyxml2_objs -Wno-format-nonliteral) + target_compile_options_safe(tinyxml2_objs -Wno-old-style-cast) + target_compile_options_safe(tinyxml2_objs -Wno-inconsistent-missing-destructor-override) +endif() + diff --git a/cppcheck-2.14.0/externals/tinyxml2/LICENSE b/cppcheck-2.14.0/externals/tinyxml2/LICENSE new file mode 100644 index 00000000..85a6a36f --- /dev/null +++ b/cppcheck-2.14.0/externals/tinyxml2/LICENSE @@ -0,0 +1,18 @@ +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. diff --git a/cppcheck-2.14.0/externals/tinyxml2/tinyxml2.cpp b/cppcheck-2.14.0/externals/tinyxml2/tinyxml2.cpp new file mode 100644 index 00000000..083f54b9 --- /dev/null +++ b/cppcheck-2.14.0/externals/tinyxml2/tinyxml2.cpp @@ -0,0 +1,3031 @@ +/* +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml2.h" + +#include // yes, this one new style header, is in the Android SDK. +#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) +# include +# include +#else +# include +# include +#endif + +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) + // Microsoft Visual Studio, version 2005 and higher. Not WinCE. + /*int _snprintf_s( + char *buffer, + size_t sizeOfBuffer, + size_t count, + const char *format [, + argument] ... + );*/ + static inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) + { + va_list va; + va_start( va, format ); + const int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + va_end( va ); + return result; + } + + static inline int TIXML_VSNPRINTF( char* buffer, size_t size, const char* format, va_list va ) + { + const int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); + return result; + } + + #define TIXML_VSCPRINTF _vscprintf + #define TIXML_SSCANF sscanf_s +#elif defined _MSC_VER + // Microsoft Visual Studio 2003 and earlier or WinCE + #define TIXML_SNPRINTF _snprintf + #define TIXML_VSNPRINTF _vsnprintf + #define TIXML_SSCANF sscanf + #if (_MSC_VER < 1400 ) && (!defined WINCE) + // Microsoft Visual Studio 2003 and not WinCE. + #define TIXML_VSCPRINTF _vscprintf // VS2003's C runtime has this, but VC6 C runtime or WinCE SDK doesn't have. + #else + // Microsoft Visual Studio 2003 and earlier or WinCE. + static inline int TIXML_VSCPRINTF( const char* format, va_list va ) + { + int len = 512; + for (;;) { + len = len*2; + char* str = new char[len](); + const int required = _vsnprintf(str, len, format, va); + delete[] str; + if ( required != -1 ) { + TIXMLASSERT( required >= 0 ); + len = required; + break; + } + } + TIXMLASSERT( len >= 0 ); + return len; + } + #endif +#else + // GCC version 3 and higher + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_VSNPRINTF vsnprintf + static inline int TIXML_VSCPRINTF( const char* format, va_list va ) + { + int len = vsnprintf( 0, 0, format, va ); + TIXMLASSERT( len >= 0 ); + return len; + } + #define TIXML_SSCANF sscanf +#endif + +#if defined(_WIN64) + #define TIXML_FSEEK _fseeki64 + #define TIXML_FTELL _ftelli64 +#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || defined(__CYGWIN__) + #define TIXML_FSEEK fseeko + #define TIXML_FTELL ftello +#elif defined(__ANDROID__) + #if __ANDROID_API__ > 24 + #define TIXML_FSEEK fseeko64 + #define TIXML_FTELL ftello64 + #else + #define TIXML_FSEEK fseeko + #define TIXML_FTELL ftello + #endif +#else + #define TIXML_FSEEK fseek + #define TIXML_FTELL ftell +#endif + + +static const char LINE_FEED = static_cast(0x0a); // all line endings are normalized to LF +static const char LF = LINE_FEED; +static const char CARRIAGE_RETURN = static_cast(0x0d); // CR gets filtered out +static const char CR = CARRIAGE_RETURN; +static const char SINGLE_QUOTE = '\''; +static const char DOUBLE_QUOTE = '\"'; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// ef bb bf (Microsoft "lead bytes") - designates UTF-8 + +static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +namespace tinyxml2 +{ + +struct Entity { + const char* pattern; + int length; + char value; +}; + +static const int NUM_ENTITIES = 5; +static const Entity entities[NUM_ENTITIES] = { + { "quot", 4, DOUBLE_QUOTE }, + { "amp", 3, '&' }, + { "apos", 4, SINGLE_QUOTE }, + { "lt", 2, '<' }, + { "gt", 2, '>' } +}; + + +StrPair::~StrPair() +{ + Reset(); +} + + +void StrPair::TransferTo( StrPair* other ) +{ + if ( this == other ) { + return; + } + // This in effect implements the assignment operator by "moving" + // ownership (as in auto_ptr). + + TIXMLASSERT( other != 0 ); + TIXMLASSERT( other->_flags == 0 ); + TIXMLASSERT( other->_start == 0 ); + TIXMLASSERT( other->_end == 0 ); + + other->Reset(); + + other->_flags = _flags; + other->_start = _start; + other->_end = _end; + + _flags = 0; + _start = 0; + _end = 0; +} + + +void StrPair::Reset() +{ + if ( _flags & NEEDS_DELETE ) { + delete [] _start; + } + _flags = 0; + _start = 0; + _end = 0; +} + + +void StrPair::SetStr( const char* str, int flags ) +{ + TIXMLASSERT( str ); + Reset(); + size_t len = strlen( str ); + TIXMLASSERT( _start == 0 ); + _start = new char[ len+1 ]; + memcpy( _start, str, len+1 ); + _end = _start + len; + _flags = flags | NEEDS_DELETE; +} + + +char* StrPair::ParseText( char* p, const char* endTag, int strFlags, int* curLineNumPtr ) +{ + TIXMLASSERT( p ); + TIXMLASSERT( endTag && *endTag ); + TIXMLASSERT(curLineNumPtr); + + char* start = p; + const char endChar = *endTag; + size_t length = strlen( endTag ); + + // Inner loop of text parsing. + while ( *p ) { + if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { + Set( start, p, strFlags ); + return p + length; + } else if (*p == '\n') { + ++(*curLineNumPtr); + } + ++p; + TIXMLASSERT( p ); + } + return 0; +} + + +char* StrPair::ParseName( char* p ) +{ + if ( !p || !(*p) ) { + return 0; + } + if ( !XMLUtil::IsNameStartChar( (unsigned char) *p ) ) { + return 0; + } + + char* const start = p; + ++p; + while ( *p && XMLUtil::IsNameChar( (unsigned char) *p ) ) { + ++p; + } + + Set( start, p, 0 ); + return p; +} + + +void StrPair::CollapseWhitespace() +{ + // Adjusting _start would cause undefined behavior on delete[] + TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 ); + // Trim leading space. + _start = XMLUtil::SkipWhiteSpace( _start, 0 ); + + if ( *_start ) { + const char* p = _start; // the read pointer + char* q = _start; // the write pointer + + while( *p ) { + if ( XMLUtil::IsWhiteSpace( *p )) { + p = XMLUtil::SkipWhiteSpace( p, 0 ); + if ( *p == 0 ) { + break; // don't write to q; this trims the trailing space. + } + *q = ' '; + ++q; + } + *q = *p; + ++q; + ++p; + } + *q = 0; + } +} + + +const char* StrPair::GetStr() +{ + TIXMLASSERT( _start ); + TIXMLASSERT( _end ); + if ( _flags & NEEDS_FLUSH ) { + *_end = 0; + _flags ^= NEEDS_FLUSH; + + if ( _flags ) { + const char* p = _start; // the read pointer + char* q = _start; // the write pointer + + while( p < _end ) { + if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { + // CR-LF pair becomes LF + // CR alone becomes LF + // LF-CR becomes LF + if ( *(p+1) == LF ) { + p += 2; + } + else { + ++p; + } + *q = LF; + ++q; + } + else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { + if ( *(p+1) == CR ) { + p += 2; + } + else { + ++p; + } + *q = LF; + ++q; + } + else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { + // Entities handled by tinyXML2: + // - special entities in the entity table [in/out] + // - numeric character reference [in] + // 中 or 中 + + if ( *(p+1) == '#' ) { + const int buflen = 10; + char buf[buflen] = { 0 }; + int len = 0; + const char* adjusted = const_cast( XMLUtil::GetCharacterRef( p, buf, &len ) ); + if ( adjusted == 0 ) { + *q = *p; + ++p; + ++q; + } + else { + TIXMLASSERT( 0 <= len && len <= buflen ); + TIXMLASSERT( q + len <= adjusted ); + p = adjusted; + memcpy( q, buf, len ); + q += len; + } + } + else { + bool entityFound = false; + for( int i = 0; i < NUM_ENTITIES; ++i ) { + const Entity& entity = entities[i]; + if ( strncmp( p + 1, entity.pattern, entity.length ) == 0 + && *( p + entity.length + 1 ) == ';' ) { + // Found an entity - convert. + *q = entity.value; + ++q; + p += entity.length + 2; + entityFound = true; + break; + } + } + if ( !entityFound ) { + // fixme: treat as error? + ++p; + ++q; + } + } + } + else { + *q = *p; + ++p; + ++q; + } + } + *q = 0; + } + // The loop below has plenty going on, and this + // is a less useful mode. Break it out. + if ( _flags & NEEDS_WHITESPACE_COLLAPSING ) { + CollapseWhitespace(); + } + _flags = (_flags & NEEDS_DELETE); + } + TIXMLASSERT( _start ); + return _start; +} + + + + +// --------- XMLUtil ----------- // + +const char* XMLUtil::writeBoolTrue = "true"; +const char* XMLUtil::writeBoolFalse = "false"; + +void XMLUtil::SetBoolSerialization(const char* writeTrue, const char* writeFalse) +{ + static const char* defTrue = "true"; + static const char* defFalse = "false"; + + writeBoolTrue = (writeTrue) ? writeTrue : defTrue; + writeBoolFalse = (writeFalse) ? writeFalse : defFalse; +} + + +const char* XMLUtil::ReadBOM( const char* p, bool* bom ) +{ + TIXMLASSERT( p ); + TIXMLASSERT( bom ); + *bom = false; + const unsigned char* pu = reinterpret_cast(p); + // Check for BOM: + if ( *(pu+0) == TIXML_UTF_LEAD_0 + && *(pu+1) == TIXML_UTF_LEAD_1 + && *(pu+2) == TIXML_UTF_LEAD_2 ) { + *bom = true; + p += 3; + } + TIXMLASSERT( p ); + return p; +} + + +void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) { + *length = 1; + } + else if ( input < 0x800 ) { + *length = 2; + } + else if ( input < 0x10000 ) { + *length = 3; + } + else if ( input < 0x200000 ) { + *length = 4; + } + else { + *length = 0; // This code won't convert this correctly anyway. + return; + } + + output += *length; + + // Scary scary fall throughs are annotated with carefully designed comments + // to suppress compiler warnings such as -Wimplicit-fallthrough in gcc + switch (*length) { + case 4: + --output; + *output = static_cast((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + //fall through + case 3: + --output; + *output = static_cast((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + //fall through + case 2: + --output; + *output = static_cast((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + //fall through + case 1: + --output; + *output = static_cast(input | FIRST_BYTE_MARK[*length]); + break; + default: + TIXMLASSERT( false ); + } +} + + +const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) +{ + // Presume an entity, and pull it out. + *length = 0; + + if ( *(p+1) == '#' && *(p+2) ) { + unsigned long ucs = 0; + TIXMLASSERT( sizeof( ucs ) >= 4 ); + ptrdiff_t delta = 0; + unsigned mult = 1; + static const char SEMICOLON = ';'; + + if ( *(p+2) == 'x' ) { + // Hexadecimal. + const char* q = p+3; + if ( !(*q) ) { + return 0; + } + + q = strchr( q, SEMICOLON ); + + if ( !q ) { + return 0; + } + TIXMLASSERT( *q == SEMICOLON ); + + delta = q-p; + --q; + + while ( *q != 'x' ) { + unsigned int digit = 0; + + if ( *q >= '0' && *q <= '9' ) { + digit = *q - '0'; + } + else if ( *q >= 'a' && *q <= 'f' ) { + digit = *q - 'a' + 10; + } + else if ( *q >= 'A' && *q <= 'F' ) { + digit = *q - 'A' + 10; + } + else { + return 0; + } + TIXMLASSERT( digit < 16 ); + TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); + const unsigned int digitScaled = mult * digit; + TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); + ucs += digitScaled; + TIXMLASSERT( mult <= UINT_MAX / 16 ); + mult *= 16; + --q; + } + } + else { + // Decimal. + const char* q = p+2; + if ( !(*q) ) { + return 0; + } + + q = strchr( q, SEMICOLON ); + + if ( !q ) { + return 0; + } + TIXMLASSERT( *q == SEMICOLON ); + + delta = q-p; + --q; + + while ( *q != '#' ) { + if ( *q >= '0' && *q <= '9' ) { + const unsigned int digit = *q - '0'; + TIXMLASSERT( digit < 10 ); + TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); + const unsigned int digitScaled = mult * digit; + TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); + ucs += digitScaled; + } + else { + return 0; + } + TIXMLASSERT( mult <= UINT_MAX / 10 ); + mult *= 10; + --q; + } + } + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + return p + delta + 1; + } + return p+1; +} + + +void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); +} + + +void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%u", v ); +} + + +void XMLUtil::ToStr( bool v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%s", v ? writeBoolTrue : writeBoolFalse); +} + +/* + ToStr() of a number is a very tricky topic. + https://github.com/leethomason/tinyxml2/issues/106 +*/ +void XMLUtil::ToStr( float v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v ); +} + + +void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) +{ + TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v ); +} + + +void XMLUtil::ToStr( int64_t v, char* buffer, int bufferSize ) +{ + // horrible syntax trick to make the compiler happy about %lld + TIXML_SNPRINTF(buffer, bufferSize, "%lld", static_cast(v)); +} + +void XMLUtil::ToStr( uint64_t v, char* buffer, int bufferSize ) +{ + // horrible syntax trick to make the compiler happy about %llu + TIXML_SNPRINTF(buffer, bufferSize, "%llu", (long long)v); +} + +bool XMLUtil::ToInt(const char* str, int* value) +{ + if (IsPrefixHex(str)) { + unsigned v; + if (TIXML_SSCANF(str, "%x", &v) == 1) { + *value = static_cast(v); + return true; + } + } + else { + if (TIXML_SSCANF(str, "%d", value) == 1) { + return true; + } + } + return false; +} + +bool XMLUtil::ToUnsigned(const char* str, unsigned* value) +{ + if (TIXML_SSCANF(str, IsPrefixHex(str) ? "%x" : "%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; + } + static const char* TRUE_VALS[] = { "true", "True", "TRUE", 0 }; + static const char* FALSE_VALS[] = { "false", "False", "FALSE", 0 }; + + for (int i = 0; TRUE_VALS[i]; ++i) { + if (StringEqual(str, TRUE_VALS[i])) { + *value = true; + return true; + } + } + for (int i = 0; FALSE_VALS[i]; ++i) { + if (StringEqual(str, FALSE_VALS[i])) { + *value = false; + return true; + } + } + return false; +} + + +bool XMLUtil::ToFloat( const char* str, float* value ) +{ + if ( TIXML_SSCANF( str, "%f", value ) == 1 ) { + return true; + } + return false; +} + + +bool XMLUtil::ToDouble( const char* str, double* value ) +{ + if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { + return true; + } + return false; +} + + +bool XMLUtil::ToInt64(const char* str, int64_t* value) +{ + if (IsPrefixHex(str)) { + unsigned long long v = 0; // horrible syntax trick to make the compiler happy about %llx + if (TIXML_SSCANF(str, "%llx", &v) == 1) { + *value = static_cast(v); + return true; + } + } + else { + long long v = 0; // horrible syntax trick to make the compiler happy about %lld + if (TIXML_SSCANF(str, "%lld", &v) == 1) { + *value = static_cast(v); + return true; + } + } + return false; +} + + +bool XMLUtil::ToUnsigned64(const char* str, uint64_t* value) { + unsigned long long v = 0; // horrible syntax trick to make the compiler happy about %llu + if(TIXML_SSCANF(str, IsPrefixHex(str) ? "%llx" : "%llu", &v) == 1) { + *value = (uint64_t)v; + return true; + } + return false; +} + + +char* XMLDocument::Identify( char* p, XMLNode** node, bool first ) +{ + TIXMLASSERT( node ); + TIXMLASSERT( p ); + char* const start = p; + int const startLine = _parseCurLineNum; + p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); + if( !*p ) { + *node = 0; + TIXMLASSERT( p ); + return p; + } + + // These strings define the matching patterns: + static const char* xmlHeader = { "( _commentPool ); + returnNode->_parseLineNum = _parseCurLineNum; + p += xmlHeaderLen; + } + else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { + returnNode = CreateUnlinkedNode( _commentPool ); + returnNode->_parseLineNum = _parseCurLineNum; + p += commentHeaderLen; + } + else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { + XMLText* text = CreateUnlinkedNode( _textPool ); + returnNode = text; + returnNode->_parseLineNum = _parseCurLineNum; + p += cdataHeaderLen; + text->SetCData( true ); + } + else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { + returnNode = CreateUnlinkedNode( _commentPool ); + returnNode->_parseLineNum = _parseCurLineNum; + p += dtdHeaderLen; + } + else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { + + // Preserve whitespace pedantically before closing tag, when it's immediately after opening tag + if (WhitespaceMode() == PEDANTIC_WHITESPACE && first && p != start && *(p + elementHeaderLen) == '/') { + returnNode = CreateUnlinkedNode(_textPool); + returnNode->_parseLineNum = startLine; + p = start; // Back it up, all the text counts. + _parseCurLineNum = startLine; + } + else { + returnNode = CreateUnlinkedNode(_elementPool); + returnNode->_parseLineNum = _parseCurLineNum; + p += elementHeaderLen; + } + } + else { + returnNode = CreateUnlinkedNode( _textPool ); + returnNode->_parseLineNum = _parseCurLineNum; // Report line of first non-whitespace character + p = start; // Back it up, all the text counts. + _parseCurLineNum = startLine; + } + + TIXMLASSERT( returnNode ); + TIXMLASSERT( p ); + *node = returnNode; + return p; +} + + +bool XMLDocument::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + if ( visitor->VisitEnter( *this ) ) { + for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { + if ( !node->Accept( visitor ) ) { + break; + } + } + } + return visitor->VisitExit( *this ); +} + + +// --------- XMLNode ----------- // + +XMLNode::XMLNode( XMLDocument* doc ) : + _document( doc ), + _parent( 0 ), + _value(), + _parseLineNum( 0 ), + _firstChild( 0 ), _lastChild( 0 ), + _prev( 0 ), _next( 0 ), + _userData( 0 ), + _memPool( 0 ) +{ +} + + +XMLNode::~XMLNode() +{ + DeleteChildren(); + if ( _parent ) { + _parent->Unlink( this ); + } +} + +// ChildElementCount was originally suggested by msteiger on the sourceforge page for TinyXML and modified by KB1SPH for TinyXML-2. + +int XMLNode::ChildElementCount(const char *value) const { + int count = 0; + + const XMLElement *e = FirstChildElement(value); + + while (e) { + e = e->NextSiblingElement(value); + count++; + } + + return count; +} + +int XMLNode::ChildElementCount() const { + int count = 0; + + const XMLElement *e = FirstChildElement(); + + while (e) { + e = e->NextSiblingElement(); + count++; + } + + return count; +} + +const char* XMLNode::Value() const +{ + // Edge case: XMLDocuments don't have a Value. Return null. + if ( this->ToDocument() ) + return 0; + return _value.GetStr(); +} + +void XMLNode::SetValue( const char* str, bool staticMem ) +{ + if ( staticMem ) { + _value.SetInternedStr( str ); + } + else { + _value.SetStr( str ); + } +} + +XMLNode* XMLNode::DeepClone(XMLDocument* target) const +{ + XMLNode* clone = this->ShallowClone(target); + if (!clone) return 0; + + for (const XMLNode* child = this->FirstChild(); child; child = child->NextSibling()) { + XMLNode* childClone = child->DeepClone(target); + TIXMLASSERT(childClone); + clone->InsertEndChild(childClone); + } + return clone; +} + +void XMLNode::DeleteChildren() +{ + while( _firstChild ) { + TIXMLASSERT( _lastChild ); + DeleteChild( _firstChild ); + } + _firstChild = _lastChild = 0; +} + + +void XMLNode::Unlink( XMLNode* child ) +{ + TIXMLASSERT( child ); + TIXMLASSERT( child->_document == _document ); + TIXMLASSERT( child->_parent == this ); + if ( child == _firstChild ) { + _firstChild = _firstChild->_next; + } + if ( child == _lastChild ) { + _lastChild = _lastChild->_prev; + } + + if ( child->_prev ) { + child->_prev->_next = child->_next; + } + if ( child->_next ) { + child->_next->_prev = child->_prev; + } + child->_next = 0; + child->_prev = 0; + child->_parent = 0; +} + + +void XMLNode::DeleteChild( XMLNode* node ) +{ + TIXMLASSERT( node ); + TIXMLASSERT( node->_document == _document ); + TIXMLASSERT( node->_parent == this ); + Unlink( node ); + TIXMLASSERT(node->_prev == 0); + TIXMLASSERT(node->_next == 0); + TIXMLASSERT(node->_parent == 0); + DeleteNode( node ); +} + + +XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) +{ + TIXMLASSERT( addThis ); + if ( addThis->_document != _document ) { + TIXMLASSERT( false ); + return 0; + } + InsertChildPreamble( addThis ); + + if ( _lastChild ) { + TIXMLASSERT( _firstChild ); + TIXMLASSERT( _lastChild->_next == 0 ); + _lastChild->_next = addThis; + addThis->_prev = _lastChild; + _lastChild = addThis; + + addThis->_next = 0; + } + else { + TIXMLASSERT( _firstChild == 0 ); + _firstChild = _lastChild = addThis; + + addThis->_prev = 0; + addThis->_next = 0; + } + addThis->_parent = this; + return addThis; +} + + +XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis ) +{ + TIXMLASSERT( addThis ); + if ( addThis->_document != _document ) { + TIXMLASSERT( false ); + return 0; + } + InsertChildPreamble( addThis ); + + if ( _firstChild ) { + TIXMLASSERT( _lastChild ); + TIXMLASSERT( _firstChild->_prev == 0 ); + + _firstChild->_prev = addThis; + addThis->_next = _firstChild; + _firstChild = addThis; + + addThis->_prev = 0; + } + else { + TIXMLASSERT( _lastChild == 0 ); + _firstChild = _lastChild = addThis; + + addThis->_prev = 0; + addThis->_next = 0; + } + addThis->_parent = this; + return addThis; +} + + +XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) +{ + TIXMLASSERT( addThis ); + if ( addThis->_document != _document ) { + TIXMLASSERT( false ); + return 0; + } + + TIXMLASSERT( afterThis ); + + if ( afterThis->_parent != this ) { + TIXMLASSERT( false ); + return 0; + } + if ( afterThis == addThis ) { + // Current state: BeforeThis -> AddThis -> OneAfterAddThis + // Now AddThis must disappear from it's location and then + // reappear between BeforeThis and OneAfterAddThis. + // So just leave it where it is. + return addThis; + } + + if ( afterThis->_next == 0 ) { + // The last node or the only node. + return InsertEndChild( addThis ); + } + InsertChildPreamble( addThis ); + addThis->_prev = afterThis; + addThis->_next = afterThis->_next; + afterThis->_next->_prev = addThis; + afterThis->_next = addThis; + addThis->_parent = this; + return addThis; +} + + + + +const XMLElement* XMLNode::FirstChildElement( const char* name ) const +{ + for( const XMLNode* node = _firstChild; node; node = node->_next ) { + const XMLElement* element = node->ToElementWithName( name ); + if ( element ) { + return element; + } + } + return 0; +} + + +const XMLElement* XMLNode::LastChildElement( const char* name ) const +{ + for( const XMLNode* node = _lastChild; node; node = node->_prev ) { + const XMLElement* element = node->ToElementWithName( name ); + if ( element ) { + return element; + } + } + return 0; +} + + +const XMLElement* XMLNode::NextSiblingElement( const char* name ) const +{ + for( const XMLNode* node = _next; node; node = node->_next ) { + const XMLElement* element = node->ToElementWithName( name ); + if ( element ) { + return element; + } + } + return 0; +} + + +const XMLElement* XMLNode::PreviousSiblingElement( const char* name ) const +{ + for( const XMLNode* node = _prev; node; node = node->_prev ) { + const XMLElement* element = node->ToElementWithName( name ); + if ( element ) { + return element; + } + } + return 0; +} + + +char* XMLNode::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) +{ + // This is a recursive method, but thinking about it "at the current level" + // it is a pretty simple flat list: + // + // + // + // With a special case: + // + // + // + // + // Where the closing element (/foo) *must* be the next thing after the opening + // element, and the names must match. BUT the tricky bit is that the closing + // element will be read by the child. + // + // 'endTag' is the end tag for this node, it is returned by a call to a child. + // 'parentEnd' is the end tag for the parent, which is filled in and returned. + + XMLDocument::DepthTracker tracker(_document); + if (_document->Error()) + return 0; + + bool first = true; + while( p && *p ) { + XMLNode* node = 0; + + p = _document->Identify( p, &node, first ); + TIXMLASSERT( p ); + if ( node == 0 ) { + break; + } + first = false; + + const int initialLineNum = node->_parseLineNum; + + StrPair endTag; + p = node->ParseDeep( p, &endTag, curLineNumPtr ); + if ( !p ) { + _document->DeleteNode( node ); + if ( !_document->Error() ) { + _document->SetError( XML_ERROR_PARSING, initialLineNum, 0); + } + break; + } + + const XMLDeclaration* const decl = node->ToDeclaration(); + if ( decl ) { + // Declarations are only allowed at document level + // + // Multiple declarations are allowed but all declarations + // must occur before anything else. + // + // Optimized due to a security test case. If the first node is + // a declaration, and the last node is a declaration, then only + // declarations have so far been added. + bool wellLocated = false; + + if (ToDocument()) { + if (FirstChild()) { + wellLocated = + FirstChild() && + FirstChild()->ToDeclaration() && + LastChild() && + LastChild()->ToDeclaration(); + } + else { + wellLocated = true; + } + } + if ( !wellLocated ) { + _document->SetError( XML_ERROR_PARSING_DECLARATION, initialLineNum, "XMLDeclaration value=%s", decl->Value()); + _document->DeleteNode( node ); + break; + } + } + + XMLElement* ele = node->ToElement(); + if ( ele ) { + // We read the end tag. Return it to the parent. + if ( ele->ClosingType() == XMLElement::CLOSING ) { + if ( parentEndTag ) { + ele->_value.TransferTo( parentEndTag ); + } + node->_memPool->SetTracked(); // created and then immediately deleted. + DeleteNode( node ); + return p; + } + + // Handle an end tag returned to this level. + // And handle a bunch of annoying errors. + bool mismatch = false; + if ( endTag.Empty() ) { + if ( ele->ClosingType() == XMLElement::OPEN ) { + mismatch = true; + } + } + else { + if ( ele->ClosingType() != XMLElement::OPEN ) { + mismatch = true; + } + else if ( !XMLUtil::StringEqual( endTag.GetStr(), ele->Name() ) ) { + mismatch = true; + } + } + if ( mismatch ) { + _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, initialLineNum, "XMLElement name=%s", ele->Name()); + _document->DeleteNode( node ); + break; + } + } + InsertEndChild( node ); + } + return 0; +} + +/*static*/ void XMLNode::DeleteNode( XMLNode* node ) +{ + if ( node == 0 ) { + return; + } + TIXMLASSERT(node->_document); + if (!node->ToDocument()) { + node->_document->MarkInUse(node); + } + + MemPool* pool = node->_memPool; + node->~XMLNode(); + pool->Free( node ); +} + +void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const +{ + TIXMLASSERT( insertThis ); + TIXMLASSERT( insertThis->_document == _document ); + + if (insertThis->_parent) { + insertThis->_parent->Unlink( insertThis ); + } + else { + insertThis->_document->MarkInUse(insertThis); + insertThis->_memPool->SetTracked(); + } +} + +const XMLElement* XMLNode::ToElementWithName( const char* name ) const +{ + const XMLElement* element = this->ToElement(); + if ( element == 0 ) { + return 0; + } + if ( name == 0 ) { + return element; + } + if ( XMLUtil::StringEqual( element->Name(), name ) ) { + return element; + } + return 0; +} + +// --------- XMLText ---------- // +char* XMLText::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) +{ + if ( this->CData() ) { + p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_CDATA, _parseLineNum, 0 ); + } + return p; + } + else { + int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; + if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { + flags |= StrPair::NEEDS_WHITESPACE_COLLAPSING; + } + + p = _value.ParseText( p, "<", flags, curLineNumPtr ); + if ( p && *p ) { + return p-1; + } + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_TEXT, _parseLineNum, 0 ); + } + } + return 0; +} + + +XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern? + text->SetCData( this->CData() ); + return text; +} + + +bool XMLText::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLText* text = compare->ToText(); + return ( text && XMLUtil::StringEqual( text->Value(), Value() ) ); +} + + +bool XMLText::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + return visitor->Visit( *this ); +} + + +// --------- XMLComment ---------- // + +XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLComment::~XMLComment() +{ +} + + +char* XMLComment::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) +{ + // Comment parses as text. + p = _value.ParseText( p, "-->", StrPair::COMMENT, curLineNumPtr ); + if ( p == 0 ) { + _document->SetError( XML_ERROR_PARSING_COMMENT, _parseLineNum, 0 ); + } + return p; +} + + +XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern? + return comment; +} + + +bool XMLComment::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLComment* comment = compare->ToComment(); + return ( comment && XMLUtil::StringEqual( comment->Value(), Value() )); +} + + +bool XMLComment::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + return visitor->Visit( *this ); +} + + +// --------- XMLDeclaration ---------- // + +XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLDeclaration::~XMLDeclaration() +{ + //printf( "~XMLDeclaration\n" ); +} + + +char* XMLDeclaration::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) +{ + // Declaration parses as text. + p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); + if ( p == 0 ) { + _document->SetError( XML_ERROR_PARSING_DECLARATION, _parseLineNum, 0 ); + } + return p; +} + + +XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern? + return dec; +} + + +bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLDeclaration* declaration = compare->ToDeclaration(); + return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() )); +} + + + +bool XMLDeclaration::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + return visitor->Visit( *this ); +} + +// --------- XMLUnknown ---------- // + +XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) +{ +} + + +XMLUnknown::~XMLUnknown() +{ +} + + +char* XMLUnknown::ParseDeep( char* p, StrPair*, int* curLineNumPtr ) +{ + // Unknown parses as text. + p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION, curLineNumPtr ); + if ( !p ) { + _document->SetError( XML_ERROR_PARSING_UNKNOWN, _parseLineNum, 0 ); + } + return p; +} + + +XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern? + return text; +} + + +bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLUnknown* unknown = compare->ToUnknown(); + return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() )); +} + + +bool XMLUnknown::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + return visitor->Visit( *this ); +} + +// --------- XMLAttribute ---------- // + +const char* XMLAttribute::Name() const +{ + return _name.GetStr(); +} + +const char* XMLAttribute::Value() const +{ + return _value.GetStr(); +} + +char* XMLAttribute::ParseDeep( char* p, bool processEntities, int* curLineNumPtr ) +{ + // Parse using the name rules: bug fix, was using ParseText before + p = _name.ParseName( p ); + if ( !p || !*p ) { + return 0; + } + + // Skip white space before = + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + if ( *p != '=' ) { + return 0; + } + + ++p; // move up to opening quote + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + if ( *p != '\"' && *p != '\'' ) { + return 0; + } + + const char endTag[2] = { *p, 0 }; + ++p; // move past opening quote + + p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES, curLineNumPtr ); + return p; +} + + +void XMLAttribute::SetName( const char* n ) +{ + _name.SetStr( n ); +} + + +XMLError XMLAttribute::QueryIntValue( int* value ) const +{ + if ( XMLUtil::ToInt( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const +{ + if ( XMLUtil::ToUnsigned( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryInt64Value(int64_t* value) const +{ + if (XMLUtil::ToInt64(Value(), value)) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryUnsigned64Value(uint64_t* value) const +{ + if(XMLUtil::ToUnsigned64(Value(), value)) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryBoolValue( bool* value ) const +{ + if ( XMLUtil::ToBool( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryFloatValue( float* value ) const +{ + if ( XMLUtil::ToFloat( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +XMLError XMLAttribute::QueryDoubleValue( double* value ) const +{ + if ( XMLUtil::ToDouble( Value(), value )) { + return XML_SUCCESS; + } + return XML_WRONG_ATTRIBUTE_TYPE; +} + + +void XMLAttribute::SetAttribute( const char* v ) +{ + _value.SetStr( v ); +} + + +void XMLAttribute::SetAttribute( int v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +void XMLAttribute::SetAttribute( unsigned v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +void XMLAttribute::SetAttribute(int64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + _value.SetStr(buf); +} + +void XMLAttribute::SetAttribute(uint64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + _value.SetStr(buf); +} + + +void XMLAttribute::SetAttribute( bool v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + +void XMLAttribute::SetAttribute( double v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + +void XMLAttribute::SetAttribute( float v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + _value.SetStr( buf ); +} + + +// --------- XMLElement ---------- // +XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ), + _closingType( OPEN ), + _rootAttribute( 0 ) +{ +} + + +XMLElement::~XMLElement() +{ + while( _rootAttribute ) { + XMLAttribute* next = _rootAttribute->_next; + DeleteAttribute( _rootAttribute ); + _rootAttribute = next; + } +} + + +const XMLAttribute* XMLElement::FindAttribute( const char* name ) const +{ + for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) { + if ( XMLUtil::StringEqual( a->Name(), name ) ) { + return a; + } + } + return 0; +} + + +const char* XMLElement::Attribute( const char* name, const char* value ) const +{ + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return 0; + } + if ( !value || XMLUtil::StringEqual( a->Value(), value )) { + return a->Value(); + } + return 0; +} + +int XMLElement::IntAttribute(const char* name, int defaultValue) const +{ + int i = defaultValue; + QueryIntAttribute(name, &i); + return i; +} + +unsigned XMLElement::UnsignedAttribute(const char* name, unsigned defaultValue) const +{ + unsigned i = defaultValue; + QueryUnsignedAttribute(name, &i); + return i; +} + +int64_t XMLElement::Int64Attribute(const char* name, int64_t defaultValue) const +{ + int64_t i = defaultValue; + QueryInt64Attribute(name, &i); + return i; +} + +uint64_t XMLElement::Unsigned64Attribute(const char* name, uint64_t defaultValue) const +{ + uint64_t i = defaultValue; + QueryUnsigned64Attribute(name, &i); + return i; +} + +bool XMLElement::BoolAttribute(const char* name, bool defaultValue) const +{ + bool b = defaultValue; + QueryBoolAttribute(name, &b); + return b; +} + +double XMLElement::DoubleAttribute(const char* name, double defaultValue) const +{ + double d = defaultValue; + QueryDoubleAttribute(name, &d); + return d; +} + +float XMLElement::FloatAttribute(const char* name, float defaultValue) const +{ + float f = defaultValue; + QueryFloatAttribute(name, &f); + return f; +} + +const char* XMLElement::GetText() const +{ + /* skip comment node */ + const XMLNode* node = FirstChild(); + while (node) { + if (node->ToComment()) { + node = node->NextSibling(); + continue; + } + break; + } + + if ( node && node->ToText() ) { + return node->Value(); + } + return 0; +} + + +void XMLElement::SetText( const char* inText ) +{ + if ( FirstChild() && FirstChild()->ToText() ) + FirstChild()->SetValue( inText ); + else { + XMLText* theText = GetDocument()->NewText( inText ); + InsertFirstChild( theText ); + } +} + + +void XMLElement::SetText( int v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText( unsigned v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText(int64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + SetText(buf); +} + +void XMLElement::SetText(uint64_t v) { + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + SetText(buf); +} + + +void XMLElement::SetText( bool v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText( float v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +void XMLElement::SetText( double v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + SetText( buf ); +} + + +XMLError XMLElement::QueryIntText( int* ival ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToInt( t, ival ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToUnsigned( t, uval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryInt64Text(int64_t* ival) const +{ + if (FirstChild() && FirstChild()->ToText()) { + const char* t = FirstChild()->Value(); + if (XMLUtil::ToInt64(t, ival)) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryUnsigned64Text(uint64_t* uval) const +{ + if(FirstChild() && FirstChild()->ToText()) { + const char* t = FirstChild()->Value(); + if(XMLUtil::ToUnsigned64(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()->Value(); + if ( XMLUtil::ToBool( t, bval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryDoubleText( double* dval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToDouble( t, dval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + + +XMLError XMLElement::QueryFloatText( float* fval ) const +{ + if ( FirstChild() && FirstChild()->ToText() ) { + const char* t = FirstChild()->Value(); + if ( XMLUtil::ToFloat( t, fval ) ) { + return XML_SUCCESS; + } + return XML_CAN_NOT_CONVERT_TEXT; + } + return XML_NO_TEXT_NODE; +} + +int XMLElement::IntText(int defaultValue) const +{ + int i = defaultValue; + QueryIntText(&i); + return i; +} + +unsigned XMLElement::UnsignedText(unsigned defaultValue) const +{ + unsigned i = defaultValue; + QueryUnsignedText(&i); + return i; +} + +int64_t XMLElement::Int64Text(int64_t defaultValue) const +{ + int64_t i = defaultValue; + QueryInt64Text(&i); + return i; +} + +uint64_t XMLElement::Unsigned64Text(uint64_t defaultValue) const +{ + uint64_t i = defaultValue; + QueryUnsigned64Text(&i); + return i; +} + +bool XMLElement::BoolText(bool defaultValue) const +{ + bool b = defaultValue; + QueryBoolText(&b); + return b; +} + +double XMLElement::DoubleText(double defaultValue) const +{ + double d = defaultValue; + QueryDoubleText(&d); + return d; +} + +float XMLElement::FloatText(float defaultValue) const +{ + float f = defaultValue; + QueryFloatText(&f); + return f; +} + + +XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) +{ + XMLAttribute* last = 0; + XMLAttribute* attrib = 0; + for( attrib = _rootAttribute; + attrib; + last = attrib, attrib = attrib->_next ) { + if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { + break; + } + } + if ( !attrib ) { + attrib = CreateAttribute(); + TIXMLASSERT( attrib ); + if ( last ) { + TIXMLASSERT( last->_next == 0 ); + last->_next = attrib; + } + else { + TIXMLASSERT( _rootAttribute == 0 ); + _rootAttribute = attrib; + } + attrib->SetName( name ); + } + return attrib; +} + + +void XMLElement::DeleteAttribute( const char* name ) +{ + XMLAttribute* prev = 0; + for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) { + if ( XMLUtil::StringEqual( name, a->Name() ) ) { + if ( prev ) { + prev->_next = a->_next; + } + else { + _rootAttribute = a->_next; + } + DeleteAttribute( a ); + break; + } + prev = a; + } +} + + +char* XMLElement::ParseAttributes( char* p, int* curLineNumPtr ) +{ + XMLAttribute* prevAttribute = 0; + + // Read the attributes. + while( p ) { + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + if ( !(*p) ) { + _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, "XMLElement name=%s", Name() ); + return 0; + } + + // attribute. + if (XMLUtil::IsNameStartChar( (unsigned char) *p ) ) { + XMLAttribute* attrib = CreateAttribute(); + TIXMLASSERT( attrib ); + attrib->_parseLineNum = _document->_parseCurLineNum; + + const int attrLineNum = attrib->_parseLineNum; + + p = attrib->ParseDeep( p, _document->ProcessEntities(), curLineNumPtr ); + if ( !p || Attribute( attrib->Name() ) ) { + DeleteAttribute( attrib ); + _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, attrLineNum, "XMLElement name=%s", Name() ); + return 0; + } + // There is a minor bug here: if the attribute in the source xml + // document is duplicated, it will not be detected and the + // attribute will be doubly added. However, tracking the 'prevAttribute' + // avoids re-scanning the attribute list. Preferring performance for + // now, may reconsider in the future. + if ( prevAttribute ) { + TIXMLASSERT( prevAttribute->_next == 0 ); + prevAttribute->_next = attrib; + } + else { + TIXMLASSERT( _rootAttribute == 0 ); + _rootAttribute = attrib; + } + prevAttribute = attrib; + } + // end of the tag + else if ( *p == '>' ) { + ++p; + break; + } + // end of the tag + else if ( *p == '/' && *(p+1) == '>' ) { + _closingType = CLOSED; + return p+2; // done; sealed element. + } + else { + _document->SetError( XML_ERROR_PARSING_ELEMENT, _parseLineNum, 0 ); + return 0; + } + } + return p; +} + +void XMLElement::DeleteAttribute( XMLAttribute* attribute ) +{ + if ( attribute == 0 ) { + return; + } + MemPool* pool = attribute->_memPool; + attribute->~XMLAttribute(); + pool->Free( attribute ); +} + +XMLAttribute* XMLElement::CreateAttribute() +{ + TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); + XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); + TIXMLASSERT( attrib ); + attrib->_memPool = &_document->_attributePool; + attrib->_memPool->SetTracked(); + return attrib; +} + + +XMLElement* XMLElement::InsertNewChildElement(const char* name) +{ + XMLElement* node = _document->NewElement(name); + return InsertEndChild(node) ? node : 0; +} + +XMLComment* XMLElement::InsertNewComment(const char* comment) +{ + XMLComment* node = _document->NewComment(comment); + return InsertEndChild(node) ? node : 0; +} + +XMLText* XMLElement::InsertNewText(const char* text) +{ + XMLText* node = _document->NewText(text); + return InsertEndChild(node) ? node : 0; +} + +XMLDeclaration* XMLElement::InsertNewDeclaration(const char* text) +{ + XMLDeclaration* node = _document->NewDeclaration(text); + return InsertEndChild(node) ? node : 0; +} + +XMLUnknown* XMLElement::InsertNewUnknown(const char* text) +{ + XMLUnknown* node = _document->NewUnknown(text); + return InsertEndChild(node) ? node : 0; +} + + + +// +// +// foobar +// +char* XMLElement::ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) +{ + // Read the element name. + p = XMLUtil::SkipWhiteSpace( p, curLineNumPtr ); + + // The closing element is the form. It is + // parsed just like a regular element then deleted from + // the DOM. + if ( *p == '/' ) { + _closingType = CLOSING; + ++p; + } + + p = _value.ParseName( p ); + if ( _value.Empty() ) { + return 0; + } + + p = ParseAttributes( p, curLineNumPtr ); + if ( !p || !*p || _closingType != OPEN ) { + return p; + } + + p = XMLNode::ParseDeep( p, parentEndTag, curLineNumPtr ); + return p; +} + + + +XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const +{ + if ( !doc ) { + doc = _document; + } + XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern? + for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) { + element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern? + } + return element; +} + + +bool XMLElement::ShallowEqual( const XMLNode* compare ) const +{ + TIXMLASSERT( compare ); + const XMLElement* other = compare->ToElement(); + if ( other && XMLUtil::StringEqual( other->Name(), Name() )) { + + const XMLAttribute* a=FirstAttribute(); + const XMLAttribute* b=other->FirstAttribute(); + + while ( a && b ) { + if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) { + return false; + } + a = a->Next(); + b = b->Next(); + } + if ( a || b ) { + // different count + return false; + } + return true; + } + return false; +} + + +bool XMLElement::Accept( XMLVisitor* visitor ) const +{ + TIXMLASSERT( visitor ); + if ( visitor->VisitEnter( *this, _rootAttribute ) ) { + for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { + if ( !node->Accept( visitor ) ) { + break; + } + } + } + return visitor->VisitExit( *this ); +} + + +// --------- XMLDocument ----------- // + +// Warning: List must match 'enum XMLError' +const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = { + "XML_SUCCESS", + "XML_NO_ATTRIBUTE", + "XML_WRONG_ATTRIBUTE_TYPE", + "XML_ERROR_FILE_NOT_FOUND", + "XML_ERROR_FILE_COULD_NOT_BE_OPENED", + "XML_ERROR_FILE_READ_ERROR", + "XML_ERROR_PARSING_ELEMENT", + "XML_ERROR_PARSING_ATTRIBUTE", + "XML_ERROR_PARSING_TEXT", + "XML_ERROR_PARSING_CDATA", + "XML_ERROR_PARSING_COMMENT", + "XML_ERROR_PARSING_DECLARATION", + "XML_ERROR_PARSING_UNKNOWN", + "XML_ERROR_EMPTY_DOCUMENT", + "XML_ERROR_MISMATCHED_ELEMENT", + "XML_ERROR_PARSING", + "XML_CAN_NOT_CONVERT_TEXT", + "XML_NO_TEXT_NODE", + "XML_ELEMENT_DEPTH_EXCEEDED" +}; + + +XMLDocument::XMLDocument( bool processEntities, Whitespace whitespaceMode ) : + XMLNode( 0 ), + _writeBOM( false ), + _processEntities( processEntities ), + _errorID(XML_SUCCESS), + _whitespaceMode( whitespaceMode ), + _errorStr(), + _errorLineNum( 0 ), + _charBuffer( 0 ), + _parseCurLineNum( 0 ), + _parsingDepth(0), + _unlinked(), + _elementPool(), + _attributePool(), + _textPool(), + _commentPool() +{ + // avoid VC++ C4355 warning about 'this' in initializer list (C4355 is off by default in VS2012+) + _document = this; +} + + +XMLDocument::~XMLDocument() +{ + Clear(); +} + + +void XMLDocument::MarkInUse(const XMLNode* const node) +{ + TIXMLASSERT(node); + TIXMLASSERT(node->_parent == 0); + + for (int i = 0; i < _unlinked.Size(); ++i) { + if (node == _unlinked[i]) { + _unlinked.SwapRemove(i); + break; + } + } +} + +void XMLDocument::Clear() +{ + DeleteChildren(); + while( _unlinked.Size()) { + DeleteNode(_unlinked[0]); // Will remove from _unlinked as part of delete. + } + +#ifdef TINYXML2_DEBUG + const bool hadError = Error(); +#endif + ClearError(); + + delete [] _charBuffer; + _charBuffer = 0; + _parsingDepth = 0; + +#if 0 + _textPool.Trace( "text" ); + _elementPool.Trace( "element" ); + _commentPool.Trace( "comment" ); + _attributePool.Trace( "attribute" ); +#endif + +#ifdef TINYXML2_DEBUG + if ( !hadError ) { + TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); + TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); + TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); + TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); + } +#endif +} + + +void XMLDocument::DeepCopy(XMLDocument* target) const +{ + TIXMLASSERT(target); + if (target == this) { + return; // technically success - a no-op. + } + + target->Clear(); + for (const XMLNode* node = this->FirstChild(); node; node = node->NextSibling()) { + target->InsertEndChild(node->DeepClone(target)); + } +} + +XMLElement* XMLDocument::NewElement( const char* name ) +{ + XMLElement* ele = CreateUnlinkedNode( _elementPool ); + ele->SetName( name ); + return ele; +} + + +XMLComment* XMLDocument::NewComment( const char* str ) +{ + XMLComment* comment = CreateUnlinkedNode( _commentPool ); + comment->SetValue( str ); + return comment; +} + + +XMLText* XMLDocument::NewText( const char* str ) +{ + XMLText* text = CreateUnlinkedNode( _textPool ); + text->SetValue( str ); + return text; +} + + +XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) +{ + XMLDeclaration* dec = CreateUnlinkedNode( _commentPool ); + dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); + return dec; +} + + +XMLUnknown* XMLDocument::NewUnknown( const char* str ) +{ + XMLUnknown* unk = CreateUnlinkedNode( _commentPool ); + unk->SetValue( str ); + return unk; +} + +static FILE* callfopen( const char* filepath, const char* mode ) +{ + TIXMLASSERT( filepath ); + TIXMLASSERT( mode ); +#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) + FILE* fp = 0; + const errno_t err = fopen_s( &fp, filepath, mode ); + if ( err ) { + return 0; + } +#else + FILE* fp = fopen( filepath, mode ); +#endif + return fp; +} + +void XMLDocument::DeleteNode( XMLNode* node ) { + TIXMLASSERT( node ); + TIXMLASSERT(node->_document == this ); + if (node->_parent) { + node->_parent->DeleteChild( node ); + } + else { + // Isn't in the tree. + // Use the parent delete. + // Also, we need to mark it tracked: we 'know' + // it was never used. + node->_memPool->SetTracked(); + // Call the static XMLNode version: + XMLNode::DeleteNode(node); + } +} + + +XMLError XMLDocument::LoadFile( const char* filename ) +{ + if ( !filename ) { + TIXMLASSERT( false ); + SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=" ); + return _errorID; + } + + Clear(); + FILE* fp = callfopen( filename, "rb" ); + if ( !fp ) { + SetError( XML_ERROR_FILE_NOT_FOUND, 0, "filename=%s", filename ); + return _errorID; + } + LoadFile( fp ); + fclose( fp ); + return _errorID; +} + +XMLError XMLDocument::LoadFile( FILE* fp ) +{ + Clear(); + + TIXML_FSEEK( fp, 0, SEEK_SET ); + if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + + TIXML_FSEEK( fp, 0, SEEK_END ); + + unsigned long long filelength; + { + const long long fileLengthSigned = TIXML_FTELL( fp ); + TIXML_FSEEK( fp, 0, SEEK_SET ); + if ( fileLengthSigned == -1L ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + TIXMLASSERT( fileLengthSigned >= 0 ); + filelength = static_cast(fileLengthSigned); + } + + const size_t maxSizeT = static_cast(-1); + // We'll do the comparison as an unsigned long long, because that's guaranteed to be at + // least 8 bytes, even on a 32-bit platform. + if ( filelength >= static_cast(maxSizeT) ) { + // Cannot handle files which won't fit in buffer together with null terminator + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + + if ( filelength == 0 ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return _errorID; + } + + const size_t size = static_cast(filelength); + TIXMLASSERT( _charBuffer == 0 ); + _charBuffer = new char[size+1]; + const size_t read = fread( _charBuffer, 1, size, fp ); + if ( read != size ) { + SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); + return _errorID; + } + + _charBuffer[size] = 0; + + Parse(); + return _errorID; +} + + +XMLError XMLDocument::SaveFile( const char* filename, bool compact ) +{ + if ( !filename ) { + TIXMLASSERT( false ); + SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=" ); + return _errorID; + } + + FILE* fp = callfopen( filename, "w" ); + if ( !fp ) { + SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, 0, "filename=%s", filename ); + return _errorID; + } + SaveFile(fp, compact); + fclose( fp ); + return _errorID; +} + + +XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) +{ + // Clear any error from the last save, otherwise it will get reported + // for *this* call. + ClearError(); + XMLPrinter stream( fp, compact ); + Print( &stream ); + return _errorID; +} + + +XMLError XMLDocument::Parse( const char* xml, size_t nBytes ) +{ + Clear(); + + if ( nBytes == 0 || !xml || !*xml ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return _errorID; + } + if ( nBytes == static_cast(-1) ) { + nBytes = strlen( xml ); + } + TIXMLASSERT( _charBuffer == 0 ); + _charBuffer = new char[ nBytes+1 ]; + memcpy( _charBuffer, xml, nBytes ); + _charBuffer[nBytes] = 0; + + Parse(); + if ( Error() ) { + // clean up now essentially dangling memory. + // and the parse fail can put objects in the + // pools that are dead and inaccessible. + DeleteChildren(); + _elementPool.Clear(); + _attributePool.Clear(); + _textPool.Clear(); + _commentPool.Clear(); + } + return _errorID; +} + + +void XMLDocument::Print( XMLPrinter* streamer ) const +{ + if ( streamer ) { + Accept( streamer ); + } + else { + XMLPrinter stdoutStreamer( stdout ); + Accept( &stdoutStreamer ); + } +} + + +void XMLDocument::ClearError() { + _errorID = XML_SUCCESS; + _errorLineNum = 0; + _errorStr.Reset(); +} + + +void XMLDocument::SetError( XMLError error, int lineNum, const char* format, ... ) +{ + TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT ); + _errorID = error; + _errorLineNum = lineNum; + _errorStr.Reset(); + + const size_t BUFFER_SIZE = 1000; + char* buffer = new char[BUFFER_SIZE]; + + TIXMLASSERT(sizeof(error) <= sizeof(int)); + TIXML_SNPRINTF(buffer, BUFFER_SIZE, "Error=%s ErrorID=%d (0x%x) Line number=%d", ErrorIDToName(error), int(error), int(error), lineNum); + + if (format) { + size_t len = strlen(buffer); + TIXML_SNPRINTF(buffer + len, BUFFER_SIZE - len, ": "); + len = strlen(buffer); + + va_list va; + va_start(va, format); + TIXML_VSNPRINTF(buffer + len, BUFFER_SIZE - len, format, va); + va_end(va); + } + _errorStr.SetStr(buffer); + delete[] buffer; +} + + +/*static*/ const char* XMLDocument::ErrorIDToName(XMLError errorID) +{ + TIXMLASSERT( errorID >= 0 && errorID < XML_ERROR_COUNT ); + const char* errorName = _errorNames[errorID]; + TIXMLASSERT( errorName && errorName[0] ); + return errorName; +} + +const char* XMLDocument::ErrorStr() const +{ + return _errorStr.Empty() ? "" : _errorStr.GetStr(); +} + + +void XMLDocument::PrintError() const +{ + printf("%s\n", ErrorStr()); +} + +const char* XMLDocument::ErrorName() const +{ + return ErrorIDToName(_errorID); +} + +void XMLDocument::Parse() +{ + TIXMLASSERT( NoChildren() ); // Clear() must have been called previously + TIXMLASSERT( _charBuffer ); + _parseCurLineNum = 1; + _parseLineNum = 1; + char* p = _charBuffer; + p = XMLUtil::SkipWhiteSpace( p, &_parseCurLineNum ); + p = const_cast( XMLUtil::ReadBOM( p, &_writeBOM ) ); + if ( !*p ) { + SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); + return; + } + ParseDeep(p, 0, &_parseCurLineNum ); +} + +void XMLDocument::PushDepth() +{ + _parsingDepth++; + if (_parsingDepth == TINYXML2_MAX_ELEMENT_DEPTH) { + SetError(XML_ELEMENT_DEPTH_EXCEEDED, _parseCurLineNum, "Element nesting is too deep." ); + } +} + +void XMLDocument::PopDepth() +{ + TIXMLASSERT(_parsingDepth > 0); + --_parsingDepth; +} + +XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : + _elementJustOpened( false ), + _stack(), + _firstElement( true ), + _fp( file ), + _depth( depth ), + _textDepth( -1 ), + _processEntities( true ), + _compactMode( compact ), + _buffer() +{ + for( int i=0; i(entityValue); + TIXMLASSERT( flagIndex < ENTITY_RANGE ); + _entityFlag[flagIndex] = true; + } + _restrictedEntityFlag[static_cast('&')] = true; + _restrictedEntityFlag[static_cast('<')] = true; + _restrictedEntityFlag[static_cast('>')] = true; // not required, but consistency is nice + _buffer.Push( 0 ); +} + + +void XMLPrinter::Print( const char* format, ... ) +{ + va_list va; + va_start( va, format ); + + if ( _fp ) { + vfprintf( _fp, format, va ); + } + else { + const int len = TIXML_VSCPRINTF( format, va ); + // Close out and re-start the va-args + va_end( va ); + TIXMLASSERT( len >= 0 ); + va_start( va, format ); + TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 ); + char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator. + TIXML_VSNPRINTF( p, len+1, format, va ); + } + va_end( va ); +} + + +void XMLPrinter::Write( const char* data, size_t size ) +{ + if ( _fp ) { + fwrite ( data , sizeof(char), size, _fp); + } + else { + char* p = _buffer.PushArr( static_cast(size) ) - 1; // back up over the null terminator. + memcpy( p, data, size ); + p[size] = 0; + } +} + + +void XMLPrinter::Putc( char ch ) +{ + if ( _fp ) { + fputc ( ch, _fp); + } + else { + char* p = _buffer.PushArr( sizeof(char) ) - 1; // back up over the null terminator. + p[0] = ch; + p[1] = 0; + } +} + + +void XMLPrinter::PrintSpace( int depth ) +{ + for( int i=0; i 0 && *q < ENTITY_RANGE ) { + // Check for entities. If one is found, flush + // the stream up until the entity, write the + // entity, and keep looking. + if ( flag[static_cast(*q)] ) { + while ( p < q ) { + const size_t delta = q - p; + const int toPrint = ( INT_MAX < delta ) ? INT_MAX : static_cast(delta); + Write( p, toPrint ); + p += toPrint; + } + bool entityPatternPrinted = false; + for( int i=0; i(delta); + Write( p, toPrint ); + } + } + else { + Write( 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 }; + Write( reinterpret_cast< const char* >( bom ) ); + } + if ( writeDec ) { + PushDeclaration( "xml version=\"1.0\"" ); + } +} + +void XMLPrinter::PrepareForNewNode( bool compactMode ) +{ + SealElementIfJustOpened(); + + if ( compactMode ) { + return; + } + + if ( _firstElement ) { + PrintSpace (_depth); + } else if ( _textDepth < 0) { + Putc( '\n' ); + PrintSpace( _depth ); + } + + _firstElement = false; +} + +void XMLPrinter::OpenElement( const char* name, bool compactMode ) +{ + PrepareForNewNode( compactMode ); + _stack.Push( name ); + + Write ( "<" ); + Write ( name ); + + _elementJustOpened = true; + ++_depth; +} + + +void XMLPrinter::PushAttribute( const char* name, const char* value ) +{ + TIXMLASSERT( _elementJustOpened ); + Putc ( ' ' ); + Write( name ); + Write( "=\"" ); + PrintString( value, false ); + Putc ( '\"' ); +} + + +void XMLPrinter::PushAttribute( const char* name, int v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::PushAttribute( const char* name, unsigned v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::PushAttribute(const char* name, int64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + PushAttribute(name, buf); +} + + +void XMLPrinter::PushAttribute(const char* name, uint64_t v) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(v, buf, BUF_SIZE); + PushAttribute(name, buf); +} + + +void XMLPrinter::PushAttribute( const char* name, bool v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::PushAttribute( const char* name, double v ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( v, buf, BUF_SIZE ); + PushAttribute( name, buf ); +} + + +void XMLPrinter::CloseElement( bool compactMode ) +{ + --_depth; + const char* name = _stack.Pop(); + + if ( _elementJustOpened ) { + Write( "/>" ); + } + else { + if ( _textDepth < 0 && !compactMode) { + Putc( '\n' ); + PrintSpace( _depth ); + } + Write ( "" ); + } + + if ( _textDepth == _depth ) { + _textDepth = -1; + } + if ( _depth == 0 && !compactMode) { + Putc( '\n' ); + } + _elementJustOpened = false; +} + + +void XMLPrinter::SealElementIfJustOpened() +{ + if ( !_elementJustOpened ) { + return; + } + _elementJustOpened = false; + Putc( '>' ); +} + + +void XMLPrinter::PushText( const char* text, bool cdata ) +{ + _textDepth = _depth-1; + + SealElementIfJustOpened(); + if ( cdata ) { + Write( "" ); + } + else { + PrintString( text, true ); + } +} + + +void XMLPrinter::PushText( int64_t value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( uint64_t value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr(value, buf, BUF_SIZE); + PushText(buf, false); +} + + +void XMLPrinter::PushText( int value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( unsigned value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( bool value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( float value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushText( double value ) +{ + char buf[BUF_SIZE]; + XMLUtil::ToStr( value, buf, BUF_SIZE ); + PushText( buf, false ); +} + + +void XMLPrinter::PushComment( const char* comment ) +{ + PrepareForNewNode( _compactMode ); + + Write( "" ); +} + + +void XMLPrinter::PushDeclaration( const char* value ) +{ + PrepareForNewNode( _compactMode ); + + Write( "" ); +} + + +void XMLPrinter::PushUnknown( const char* value ) +{ + PrepareForNewNode( _compactMode ); + + Write( "' ); +} + + +bool XMLPrinter::VisitEnter( const XMLDocument& doc ) +{ + _processEntities = doc.ProcessEntities(); + if ( doc.HasBOM() ) { + PushHeader( true, false ); + } + return true; +} + + +bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) +{ + const XMLElement* parentElem = 0; + if ( element.Parent() ) { + parentElem = element.Parent()->ToElement(); + } + const bool compactMode = parentElem ? CompactMode( *parentElem ) : _compactMode; + OpenElement( element.Name(), compactMode ); + while ( attribute ) { + PushAttribute( attribute->Name(), attribute->Value() ); + attribute = attribute->Next(); + } + return true; +} + + +bool XMLPrinter::VisitExit( const XMLElement& element ) +{ + CloseElement( CompactMode(element) ); + return true; +} + + +bool XMLPrinter::Visit( const XMLText& text ) +{ + PushText( text.Value(), text.CData() ); + return true; +} + + +bool XMLPrinter::Visit( const XMLComment& comment ) +{ + PushComment( comment.Value() ); + return true; +} + +bool XMLPrinter::Visit( const XMLDeclaration& declaration ) +{ + PushDeclaration( declaration.Value() ); + return true; +} + + +bool XMLPrinter::Visit( const XMLUnknown& unknown ) +{ + PushUnknown( unknown.Value() ); + return true; +} + +} // namespace tinyxml2 diff --git a/cppcheck-2.14.0/externals/tinyxml2/tinyxml2.h b/cppcheck-2.14.0/externals/tinyxml2/tinyxml2.h new file mode 100644 index 00000000..8b918511 --- /dev/null +++ b/cppcheck-2.14.0/externals/tinyxml2/tinyxml2.h @@ -0,0 +1,2384 @@ +/* +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#ifndef TINYXML2_INCLUDED +#define TINYXML2_INCLUDED + +#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) +# include +# include +# include +# include +# include +# if defined(__PS3__) +# include +# endif +#else +# include +# include +# include +# include +# include +#endif +#include + +/* + gcc: + g++ -Wall -DTINYXML2_DEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe + + Formatting, Artistic Style: + AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h +*/ + +#if defined( _DEBUG ) || defined (__DEBUG__) +# ifndef TINYXML2_DEBUG +# define TINYXML2_DEBUG +# endif +#endif + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4251) +#endif + +#ifdef _MSC_VER +# ifdef TINYXML2_EXPORT +# define TINYXML2_LIB __declspec(dllexport) +# elif defined(TINYXML2_IMPORT) +# define TINYXML2_LIB __declspec(dllimport) +# else +# define TINYXML2_LIB +# endif +#elif __GNUC__ >= 4 +# define TINYXML2_LIB __attribute__((visibility("default"))) +#else +# define TINYXML2_LIB +#endif + + +#if !defined(TIXMLASSERT) +#if defined(TINYXML2_DEBUG) +# if defined(_MSC_VER) +# // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like +# define TIXMLASSERT( x ) do { if ( !((void)0,(x))) { __debugbreak(); } } while(false) +# elif defined (ANDROID_NDK) +# include +# define TIXMLASSERT( x ) do { if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } } while(false) +# else +# include +# define TIXMLASSERT assert +# endif +#else +# define TIXMLASSERT( x ) do {} while(false) +#endif +#endif + +/* Versioning, past 1.0.14: + http://semver.org/ +*/ +static const int TIXML2_MAJOR_VERSION = 10; +static const int TIXML2_MINOR_VERSION = 0; +static const int TIXML2_PATCH_VERSION = 0; + +#define TINYXML2_MAJOR_VERSION 10 +#define TINYXML2_MINOR_VERSION 0 +#define TINYXML2_PATCH_VERSION 0 + +// A fixed element depth limit is problematic. There needs to be a +// limit to avoid a stack overflow. However, that limit varies per +// system, and the capacity of the stack. On the other hand, it's a trivial +// attack that can result from ill, malicious, or even correctly formed XML, +// so there needs to be a limit in place. +static const int TINYXML2_MAX_ELEMENT_DEPTH = 500; + +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[] + + Isn't clear why TINYXML2_LIB is needed; but seems to fix #719 +*/ +class TINYXML2_LIB StrPair +{ +public: + enum Mode { + NEEDS_ENTITY_PROCESSING = 0x01, + NEEDS_NEWLINE_NORMALIZATION = 0x02, + NEEDS_WHITESPACE_COLLAPSING = 0x04, + + TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, + TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, + ATTRIBUTE_NAME = 0, + ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, + ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, + COMMENT = NEEDS_NEWLINE_NORMALIZATION + }; + + StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {} + ~StrPair(); + + void Set( char* start, char* end, int flags ) { + TIXMLASSERT( start ); + TIXMLASSERT( end ); + Reset(); + _start = start; + _end = end; + _flags = flags | NEEDS_FLUSH; + } + + const char* GetStr(); + + bool Empty() const { + return _start == _end; + } + + void SetInternedStr( const char* str ) { + Reset(); + _start = const_cast(str); + } + + void SetStr( const char* str, int flags=0 ); + + char* ParseText( char* in, const char* endTag, int strFlags, int* curLineNumPtr ); + char* ParseName( char* in ); + + void TransferTo( StrPair* other ); + void Reset(); + +private: + void CollapseWhitespace(); + + enum { + NEEDS_FLUSH = 0x100, + NEEDS_DELETE = 0x200 + }; + + int _flags; + char* _start; + char* _end; + + StrPair( const StrPair& other ); // not supported + void operator=( const StrPair& other ); // not supported, use TransferTo() +}; + + +/* + A dynamic array of Plain Old Data. Doesn't support constructors, etc. + Has a small initial memory pool, so that low or no usage will not + cause a call to new/delete +*/ +template +class DynArray +{ +public: + DynArray() : + _mem( _pool ), + _allocated( INITIAL_SIZE ), + _size( 0 ) + { + } + + ~DynArray() { + if ( _mem != _pool ) { + delete [] _mem; + } + } + + void Clear() { + _size = 0; + } + + void Push( T t ) { + TIXMLASSERT( _size < INT_MAX ); + EnsureCapacity( _size+1 ); + _mem[_size] = t; + ++_size; + } + + T* PushArr( int count ) { + TIXMLASSERT( count >= 0 ); + TIXMLASSERT( _size <= INT_MAX - count ); + EnsureCapacity( _size+count ); + T* ret = &_mem[_size]; + _size += count; + return ret; + } + + T Pop() { + TIXMLASSERT( _size > 0 ); + --_size; + return _mem[_size]; + } + + void PopArr( int count ) { + TIXMLASSERT( _size >= count ); + _size -= count; + } + + bool Empty() const { + return _size == 0; + } + + T& operator[](int i) { + TIXMLASSERT( i>= 0 && i < _size ); + return _mem[i]; + } + + const T& operator[](int i) const { + TIXMLASSERT( i>= 0 && i < _size ); + return _mem[i]; + } + + const T& PeekTop() const { + TIXMLASSERT( _size > 0 ); + return _mem[ _size - 1]; + } + + int Size() const { + TIXMLASSERT( _size >= 0 ); + return _size; + } + + int Capacity() const { + TIXMLASSERT( _allocated >= INITIAL_SIZE ); + return _allocated; + } + + void SwapRemove(int i) { + TIXMLASSERT(i >= 0 && i < _size); + TIXMLASSERT(_size > 0); + _mem[i] = _mem[_size - 1]; + --_size; + } + + const T* Mem() const { + TIXMLASSERT( _mem ); + return _mem; + } + + T* Mem() { + TIXMLASSERT( _mem ); + return _mem; + } + +private: + DynArray( const DynArray& ); // not supported + void operator=( const DynArray& ); // not supported + + void EnsureCapacity( int cap ) { + TIXMLASSERT( cap > 0 ); + if ( cap > _allocated ) { + TIXMLASSERT( cap <= INT_MAX / 2 ); + const int newAllocated = cap * 2; + T* newMem = new T[static_cast(newAllocated)]; + TIXMLASSERT( newAllocated >= _size ); + memcpy( newMem, _mem, sizeof(T)*static_cast(_size) ); // warning: not using constructors, only works for PODs + if ( _mem != _pool ) { + delete [] _mem; + } + _mem = newMem; + _allocated = newAllocated; + } + } + + T* _mem; + T _pool[static_cast(INITIAL_SIZE)]; + int _allocated; // objects allocated + int _size; // number objects in use +}; + + +/* + Parent virtual class of a pool for fast allocation + and deallocation of objects. +*/ +class MemPool +{ +public: + MemPool() {} + virtual ~MemPool() {} + + virtual int ItemSize() const = 0; + virtual void* Alloc() = 0; + virtual void Free( void* ) = 0; + virtual void SetTracked() = 0; +}; + + +/* + Template child class to create pools of the correct type. +*/ +template< int ITEM_SIZE > +class MemPoolT : public MemPool +{ +public: + MemPoolT() : _blockPtrs(), _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} + ~MemPoolT() { + MemPoolT< ITEM_SIZE >::Clear(); + } + + void Clear() { + // Delete the blocks. + while( !_blockPtrs.Empty()) { + Block* lastBlock = _blockPtrs.Pop(); + delete lastBlock; + } + _root = 0; + _currentAllocs = 0; + _nAllocs = 0; + _maxAllocs = 0; + _nUntracked = 0; + } + + virtual int ItemSize() const override{ + return ITEM_SIZE; + } + int CurrentAllocs() const { + return _currentAllocs; + } + + virtual void* Alloc() override{ + if ( !_root ) { + // Need a new block. + Block* block = new Block; + _blockPtrs.Push( block ); + + Item* blockItems = block->items; + for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) { + blockItems[i].next = &(blockItems[i + 1]); + } + blockItems[ITEMS_PER_BLOCK - 1].next = 0; + _root = blockItems; + } + Item* const result = _root; + TIXMLASSERT( result != 0 ); + _root = _root->next; + + ++_currentAllocs; + if ( _currentAllocs > _maxAllocs ) { + _maxAllocs = _currentAllocs; + } + ++_nAllocs; + ++_nUntracked; + return result; + } + + virtual void Free( void* mem ) override { + if ( !mem ) { + return; + } + --_currentAllocs; + Item* item = static_cast( mem ); +#ifdef TINYXML2_DEBUG + memset( item, 0xfe, sizeof( *item ) ); +#endif + item->next = _root; + _root = item; + } + void Trace( const char* name ) { + printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", + name, _maxAllocs, _maxAllocs * ITEM_SIZE / 1024, _currentAllocs, + ITEM_SIZE, _nAllocs, _blockPtrs.Size() ); + } + + void SetTracked() override { + --_nUntracked; + } + + int Untracked() const { + return _nUntracked; + } + + // This number is perf sensitive. 4k seems like a good tradeoff on my machine. + // The test file is large, 170k. + // Release: VS2010 gcc(no opt) + // 1k: 4000 + // 2k: 4000 + // 4k: 3900 21000 + // 16k: 5200 + // 32k: 4300 + // 64k: 4000 21000 + // Declared public because some compilers do not accept to use ITEMS_PER_BLOCK + // in private part if ITEMS_PER_BLOCK is private + enum { ITEMS_PER_BLOCK = (4 * 1024) / ITEM_SIZE }; + +private: + MemPoolT( const MemPoolT& ); // not supported + void operator=( const MemPoolT& ); // not supported + + union Item { + Item* next; + char itemData[static_cast(ITEM_SIZE)]; + }; + struct Block { + Item items[ITEMS_PER_BLOCK]; + }; + DynArray< Block*, 10 > _blockPtrs; + Item* _root; + + int _currentAllocs; + int _nAllocs; + int _maxAllocs; + int _nUntracked; +}; + + + +/** + Implements the interface to the "Visitor pattern" (see the Accept() method.) + If you call the Accept() method, it requires being passed a XMLVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs + are simply called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, no children of this node or its siblings will be visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the XMLDocument, although all nodes support visiting. + + You should never change the document from a callback. + + @sa XMLNode::Accept() +*/ +class TINYXML2_LIB XMLVisitor +{ +public: + virtual ~XMLVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const XMLDocument& /*doc*/ ) { + return true; + } + /// Visit a document. + virtual bool VisitExit( const XMLDocument& /*doc*/ ) { + return true; + } + + /// Visit an element. + virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) { + return true; + } + /// Visit an element. + virtual bool VisitExit( const XMLElement& /*element*/ ) { + return true; + } + + /// Visit a declaration. + virtual bool Visit( const XMLDeclaration& /*declaration*/ ) { + return true; + } + /// Visit a text node. + virtual bool Visit( const XMLText& /*text*/ ) { + return true; + } + /// Visit a comment node. + virtual bool Visit( const XMLComment& /*comment*/ ) { + return true; + } + /// Visit an unknown node. + virtual bool Visit( const XMLUnknown& /*unknown*/ ) { + return true; + } +}; + +// WARNING: must match XMLDocument::_errorNames[] +enum XMLError { + XML_SUCCESS = 0, + XML_NO_ATTRIBUTE, + XML_WRONG_ATTRIBUTE_TYPE, + XML_ERROR_FILE_NOT_FOUND, + XML_ERROR_FILE_COULD_NOT_BE_OPENED, + XML_ERROR_FILE_READ_ERROR, + XML_ERROR_PARSING_ELEMENT, + XML_ERROR_PARSING_ATTRIBUTE, + XML_ERROR_PARSING_TEXT, + XML_ERROR_PARSING_CDATA, + XML_ERROR_PARSING_COMMENT, + XML_ERROR_PARSING_DECLARATION, + XML_ERROR_PARSING_UNKNOWN, + XML_ERROR_EMPTY_DOCUMENT, + XML_ERROR_MISMATCHED_ELEMENT, + XML_ERROR_PARSING, + XML_CAN_NOT_CONVERT_TEXT, + XML_NO_TEXT_NODE, + XML_ELEMENT_DEPTH_EXCEEDED, + + XML_ERROR_COUNT +}; + + +/* + Utility functionality. +*/ +class TINYXML2_LIB XMLUtil +{ +public: + static const char* SkipWhiteSpace( const char* p, int* curLineNumPtr ) { + TIXMLASSERT( p ); + + while( IsWhiteSpace(*p) ) { + if (curLineNumPtr && *p == '\n') { + ++(*curLineNumPtr); + } + ++p; + } + TIXMLASSERT( p ); + return p; + } + static char* SkipWhiteSpace( char* const p, int* curLineNumPtr ) { + return const_cast( SkipWhiteSpace( const_cast(p), curLineNumPtr ) ); + } + + // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't + // correct, but simple, and usually works. + static bool IsWhiteSpace( char p ) { + return !IsUTF8Continuation(p) && isspace( static_cast(p) ); + } + + inline static bool IsNameStartChar( unsigned char ch ) { + if ( ch >= 128 ) { + // This is a heuristic guess in attempt to not implement Unicode-aware isalpha() + return true; + } + if ( isalpha( ch ) ) { + return true; + } + return ch == ':' || ch == '_'; + } + + inline static bool IsNameChar( unsigned char ch ) { + return IsNameStartChar( ch ) + || isdigit( ch ) + || ch == '.' + || ch == '-'; + } + + inline static bool IsPrefixHex( const char* p) { + p = SkipWhiteSpace(p, 0); + return p && *p == '0' && ( *(p + 1) == 'x' || *(p + 1) == 'X'); + } + + inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { + if ( p == q ) { + return true; + } + TIXMLASSERT( p ); + TIXMLASSERT( q ); + TIXMLASSERT( nChar >= 0 ); + return strncmp( p, q, static_cast(nChar) ) == 0; + } + + inline static bool IsUTF8Continuation( const char p ) { + return ( p & 0x80 ) != 0; + } + + static const char* ReadBOM( const char* p, bool* hasBOM ); + // p is the starting location, + // the UTF-8 value of the entity will be placed in value, and length filled in. + static const char* GetCharacterRef( const char* p, char* value, int* length ); + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + + // converts primitive types to strings + static void ToStr( int v, char* buffer, int bufferSize ); + static void ToStr( unsigned v, char* buffer, int bufferSize ); + static void ToStr( bool v, char* buffer, int bufferSize ); + static void ToStr( float v, char* buffer, int bufferSize ); + static void ToStr( double v, char* buffer, int bufferSize ); + static void ToStr(int64_t v, char* buffer, int bufferSize); + static void ToStr(uint64_t v, char* buffer, int bufferSize); + + // converts strings to primitive types + static bool ToInt( const char* str, int* value ); + static bool ToUnsigned( const char* str, unsigned* value ); + static bool ToBool( const char* str, bool* value ); + static bool ToFloat( const char* str, float* value ); + static bool ToDouble( const char* str, double* value ); + static bool ToInt64(const char* str, int64_t* value); + static bool ToUnsigned64(const char* str, uint64_t* value); + // Changes what is serialized for a boolean value. + // Default to "true" and "false". Shouldn't be changed + // unless you have a special testing or compatibility need. + // Be careful: static, global, & not thread safe. + // Be sure to set static const memory as parameters. + static void SetBoolSerialization(const char* writeTrue, const char* writeFalse); + +private: + static const char* writeBoolTrue; + static const char* writeBoolFalse; +}; + + +/** XMLNode is a base class for every object that is in the + XML Document Object Model (DOM), except XMLAttributes. + Nodes have siblings, a parent, and children which can + be navigated. A node is always in a XMLDocument. + The type of a XMLNode can be queried, and it can + be cast to its more defined type. + + A XMLDocument allocates memory for all its Nodes. + When the XMLDocument gets deleted, all its Nodes + will also be deleted. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + @endverbatim +*/ +class TINYXML2_LIB XMLNode +{ + friend class XMLDocument; + friend class XMLElement; +public: + + /// Get the XMLDocument that owns this XMLNode. + const XMLDocument* GetDocument() const { + TIXMLASSERT( _document ); + return _document; + } + /// Get the XMLDocument that owns this XMLNode. + XMLDocument* GetDocument() { + TIXMLASSERT( _document ); + return _document; + } + + /// Safely cast to an Element, or null. + virtual XMLElement* ToElement() { + return 0; + } + /// Safely cast to Text, or null. + virtual XMLText* ToText() { + return 0; + } + /// Safely cast to a Comment, or null. + virtual XMLComment* ToComment() { + return 0; + } + /// Safely cast to a Document, or null. + virtual XMLDocument* ToDocument() { + return 0; + } + /// Safely cast to a Declaration, or null. + virtual XMLDeclaration* ToDeclaration() { + return 0; + } + /// Safely cast to an Unknown, or null. + virtual XMLUnknown* ToUnknown() { + return 0; + } + + virtual const XMLElement* ToElement() const { + return 0; + } + virtual const XMLText* ToText() const { + return 0; + } + virtual const XMLComment* ToComment() const { + return 0; + } + virtual const XMLDocument* ToDocument() const { + return 0; + } + virtual const XMLDeclaration* ToDeclaration() const { + return 0; + } + virtual const XMLUnknown* ToUnknown() const { + return 0; + } + + // ChildElementCount was originally suggested by msteiger on the sourceforge page for TinyXML and modified by KB1SPH for TinyXML-2. + + int ChildElementCount(const char *value) const; + + int ChildElementCount() const; + + /** The meaning of 'value' changes for the specific type. + @verbatim + Document: empty (NULL is returned, not an empty string) + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + const char* Value() const; + + /** Set the Value of an XML node. + @sa Value() + */ + void SetValue( const char* val, bool staticMem=false ); + + /// Gets the line number the node is in, if the document was parsed from a file. + int GetLineNum() const { return _parseLineNum; } + + /// Get the parent of this node on the DOM. + const XMLNode* Parent() const { + return _parent; + } + + XMLNode* Parent() { + return _parent; + } + + /// Returns true if this node has no children. + bool NoChildren() const { + return !_firstChild; + } + + /// Get the first child node, or null if none exists. + const XMLNode* FirstChild() const { + return _firstChild; + } + + XMLNode* FirstChild() { + return _firstChild; + } + + /** Get the first child element, or optionally the first child + element with the specified name. + */ + const XMLElement* FirstChildElement( const char* name = 0 ) const; + + XMLElement* FirstChildElement( const char* name = 0 ) { + return const_cast(const_cast(this)->FirstChildElement( name )); + } + + /// Get the last child node, or null if none exists. + const XMLNode* LastChild() const { + return _lastChild; + } + + XMLNode* LastChild() { + return _lastChild; + } + + /** Get the last child element or optionally the last child + element with the specified name. + */ + const XMLElement* LastChildElement( const char* name = 0 ) const; + + XMLElement* LastChildElement( const char* name = 0 ) { + return const_cast(const_cast(this)->LastChildElement(name) ); + } + + /// Get the previous (left) sibling node of this node. + const XMLNode* PreviousSibling() const { + return _prev; + } + + XMLNode* PreviousSibling() { + return _prev; + } + + /// Get the previous (left) sibling element of this node, with an optionally supplied name. + const XMLElement* PreviousSiblingElement( const char* name = 0 ) const ; + + XMLElement* PreviousSiblingElement( const char* name = 0 ) { + return const_cast(const_cast(this)->PreviousSiblingElement( name ) ); + } + + /// Get the next (right) sibling node of this node. + const XMLNode* NextSibling() const { + return _next; + } + + XMLNode* NextSibling() { + return _next; + } + + /// Get the next (right) sibling element of this node, with an optionally supplied name. + const XMLElement* NextSiblingElement( const char* name = 0 ) const; + + XMLElement* NextSiblingElement( const char* name = 0 ) { + return const_cast(const_cast(this)->NextSiblingElement( name ) ); + } + + /** + Add a child node as the last (right) child. + If the child node is already part of the document, + it is moved from its old location to the new location. + Returns the addThis argument or 0 if the node does not + belong to the same document. + */ + XMLNode* InsertEndChild( XMLNode* addThis ); + + XMLNode* LinkEndChild( XMLNode* addThis ) { + return InsertEndChild( addThis ); + } + /** + Add a child node as the first (left) child. + If the child node is already part of the document, + it is moved from its old location to the new location. + Returns the addThis argument or 0 if the node does not + belong to the same document. + */ + XMLNode* InsertFirstChild( XMLNode* addThis ); + /** + Add a node after the specified child node. + If the child node is already part of the document, + it is moved from its old location to the new location. + Returns the addThis argument or 0 if the afterThis node + is not a child of this node, or if the node does not + belong to the same document. + */ + XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ); + + /** + Delete all the children of this node. + */ + void DeleteChildren(); + + /** + Delete a child of this node. + */ + void DeleteChild( XMLNode* node ); + + /** + Make a copy of this node, but not its children. + You may pass in a Document pointer that will be + the owner of the new Node. If the 'document' is + null, then the node returned will be allocated + from the current Document. (this->GetDocument()) + + Note: if called on a XMLDocument, this will return null. + */ + virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0; + + /** + Make a copy of this node and all its children. + + If the 'target' is null, then the nodes will + be allocated in the current document. If 'target' + is specified, the memory will be allocated is the + specified XMLDocument. + + NOTE: This is probably not the correct tool to + copy a document, since XMLDocuments can have multiple + top level XMLNodes. You probably want to use + XMLDocument::DeepCopy() + */ + XMLNode* DeepClone( XMLDocument* target ) const; + + /** + Test if 2 nodes are the same, but don't test children. + The 2 nodes do not need to be in the same Document. + + Note: if called on a XMLDocument, this will return false. + */ + virtual bool ShallowEqual( const XMLNode* compare ) const = 0; + + /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the XMLVisitor interface. + + This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + XMLPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( XMLVisitor* visitor ) const = 0; + + /** + Set user data into the XMLNode. TinyXML-2 in + no way processes or interprets user data. + It is initially 0. + */ + void SetUserData(void* userData) { _userData = userData; } + + /** + Get user data set into the XMLNode. TinyXML-2 in + no way processes or interprets user data. + It is initially 0. + */ + void* GetUserData() const { return _userData; } + +protected: + explicit XMLNode( XMLDocument* ); + virtual ~XMLNode(); + + virtual char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr); + + XMLDocument* _document; + XMLNode* _parent; + mutable StrPair _value; + int _parseLineNum; + + XMLNode* _firstChild; + XMLNode* _lastChild; + + XMLNode* _prev; + XMLNode* _next; + + void* _userData; + +private: + MemPool* _memPool; + void Unlink( XMLNode* child ); + static void DeleteNode( XMLNode* node ); + void InsertChildPreamble( XMLNode* insertThis ) const; + const XMLElement* ToElementWithName( const char* name ) const; + + XMLNode( const XMLNode& ); // not supported + XMLNode& operator=( const XMLNode& ); // not supported +}; + + +/** XML text. + + Note that a text node can have child element nodes, for example: + @verbatim + This is bold + @endverbatim + + A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCData() and query it with CData(). +*/ +class TINYXML2_LIB XMLText : public XMLNode +{ + friend class XMLDocument; +public: + virtual bool Accept( XMLVisitor* visitor ) const override; + + virtual XMLText* ToText() override { + return this; + } + virtual const XMLText* ToText() const override { + return this; + } + + /// Declare whether this should be CDATA or standard text. + void SetCData( bool isCData ) { + _isCData = isCData; + } + /// Returns true if this is a CDATA text element. + bool CData() const { + return _isCData; + } + + virtual XMLNode* ShallowClone( XMLDocument* document ) const override; + virtual bool ShallowEqual( const XMLNode* compare ) const override; + +protected: + explicit XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} + virtual ~XMLText() {} + + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) override; + +private: + bool _isCData; + + XMLText( const XMLText& ); // not supported + XMLText& operator=( const XMLText& ); // not supported +}; + + +/** An XML Comment. */ +class TINYXML2_LIB XMLComment : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLComment* ToComment() override { + return this; + } + virtual const XMLComment* ToComment() const override { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const override; + + virtual XMLNode* ShallowClone( XMLDocument* document ) const override; + virtual bool ShallowEqual( const XMLNode* compare ) const override; + +protected: + explicit XMLComment( XMLDocument* doc ); + virtual ~XMLComment(); + + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr) override; + +private: + XMLComment( const XMLComment& ); // not supported + XMLComment& operator=( const XMLComment& ); // not supported +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXML-2 will happily read or write files without a declaration, + however. + + The text of the declaration isn't interpreted. It is parsed + and written as a string. +*/ +class TINYXML2_LIB XMLDeclaration : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLDeclaration* ToDeclaration() override { + return this; + } + virtual const XMLDeclaration* ToDeclaration() const override { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const override; + + virtual XMLNode* ShallowClone( XMLDocument* document ) const override; + virtual bool ShallowEqual( const XMLNode* compare ) const override; + +protected: + explicit XMLDeclaration( XMLDocument* doc ); + virtual ~XMLDeclaration(); + + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) override; + +private: + XMLDeclaration( const XMLDeclaration& ); // not supported + XMLDeclaration& operator=( const XMLDeclaration& ); // not supported +}; + + +/** Any tag that TinyXML-2 doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into XMLUnknowns. +*/ +class TINYXML2_LIB XMLUnknown : public XMLNode +{ + friend class XMLDocument; +public: + virtual XMLUnknown* ToUnknown() override { + return this; + } + virtual const XMLUnknown* ToUnknown() const override { + return this; + } + + virtual bool Accept( XMLVisitor* visitor ) const override; + + virtual XMLNode* ShallowClone( XMLDocument* document ) const override; + virtual bool ShallowEqual( const XMLNode* compare ) const override; + +protected: + explicit XMLUnknown( XMLDocument* doc ); + virtual ~XMLUnknown(); + + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) override; + +private: + XMLUnknown( const XMLUnknown& ); // not supported + XMLUnknown& operator=( const XMLUnknown& ); // not supported +}; + + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not XMLNodes. You may only query the + Next() attribute in a list. +*/ +class TINYXML2_LIB XMLAttribute +{ + friend class XMLElement; +public: + /// The name of the attribute. + const char* Name() const; + + /// The value of the attribute. + const char* Value() const; + + /// Gets the line number the attribute is in, if the document was parsed from a file. + int GetLineNum() const { return _parseLineNum; } + + /// The next attribute in the list. + const XMLAttribute* Next() const { + return _next; + } + + /** IntValue interprets the attribute as an integer, and returns the value. + If the value isn't an integer, 0 will be returned. There is no error checking; + use QueryIntValue() if you need error checking. + */ + int IntValue() const { + int i = 0; + QueryIntValue(&i); + return i; + } + + int64_t Int64Value() const { + int64_t i = 0; + QueryInt64Value(&i); + return i; + } + + uint64_t Unsigned64Value() const { + uint64_t i = 0; + QueryUnsigned64Value(&i); + return i; + } + + /// Query as an unsigned integer. See IntValue() + unsigned UnsignedValue() const { + unsigned i=0; + QueryUnsignedValue( &i ); + return i; + } + /// Query as a boolean. See IntValue() + bool BoolValue() const { + bool b=false; + QueryBoolValue( &b ); + return b; + } + /// Query as a double. See IntValue() + double DoubleValue() const { + double d=0; + QueryDoubleValue( &d ); + return d; + } + /// Query as a float. See IntValue() + float FloatValue() const { + float f=0; + QueryFloatValue( &f ); + return f; + } + + /** QueryIntValue interprets the attribute as an integer, and returns the value + in the provided parameter. The function will return XML_SUCCESS on success, + and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful. + */ + XMLError QueryIntValue( int* value ) const; + /// See QueryIntValue + XMLError QueryUnsignedValue( unsigned int* value ) const; + /// See QueryIntValue + XMLError QueryInt64Value(int64_t* value) const; + /// See QueryIntValue + XMLError QueryUnsigned64Value(uint64_t* value) const; + /// See QueryIntValue + XMLError QueryBoolValue( bool* value ) const; + /// See QueryIntValue + XMLError QueryDoubleValue( double* value ) const; + /// See QueryIntValue + XMLError QueryFloatValue( float* value ) const; + + /// Set the attribute to a string value. + void SetAttribute( const char* value ); + /// Set the attribute to value. + void SetAttribute( int value ); + /// Set the attribute to value. + void SetAttribute( unsigned value ); + /// Set the attribute to value. + void SetAttribute(int64_t value); + /// Set the attribute to value. + void SetAttribute(uint64_t value); + /// Set the attribute to value. + void SetAttribute( bool value ); + /// Set the attribute to value. + void SetAttribute( double value ); + /// Set the attribute to value. + void SetAttribute( float value ); + +private: + enum { BUF_SIZE = 200 }; + + XMLAttribute() : _name(), _value(),_parseLineNum( 0 ), _next( 0 ), _memPool( 0 ) {} + virtual ~XMLAttribute() {} + + XMLAttribute( const XMLAttribute& ); // not supported + void operator=( const XMLAttribute& ); // not supported + void SetName( const char* name ); + + char* ParseDeep( char* p, bool processEntities, int* curLineNumPtr ); + + mutable StrPair _name; + mutable StrPair _value; + int _parseLineNum; + XMLAttribute* _next; + MemPool* _memPool; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TINYXML2_LIB XMLElement : public XMLNode +{ + friend class XMLDocument; +public: + /// Get the name of an element (which is the Value() of the node.) + const char* Name() const { + return Value(); + } + /// Set the name of the element. + void SetName( const char* str, bool staticMem=false ) { + SetValue( str, staticMem ); + } + + virtual XMLElement* ToElement() override { + return this; + } + virtual const XMLElement* ToElement() const override { + return this; + } + virtual bool Accept( XMLVisitor* visitor ) const override; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none + exists. For example: + + @verbatim + const char* value = ele->Attribute( "foo" ); + @endverbatim + + The 'value' parameter is normally null. However, if specified, + the attribute will only be returned if the 'name' and 'value' + match. This allow you to write code: + + @verbatim + if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar(); + @endverbatim + + rather than: + @verbatim + if ( ele->Attribute( "foo" ) ) { + if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar(); + } + @endverbatim + */ + const char* Attribute( const char* name, const char* value=0 ) const; + + /** Given an attribute name, IntAttribute() returns the value + of the attribute interpreted as an integer. The default + value will be returned if the attribute isn't present, + or if there is an error. (For a method with error + checking, see QueryIntAttribute()). + */ + int IntAttribute(const char* name, int defaultValue = 0) const; + /// See IntAttribute() + unsigned UnsignedAttribute(const char* name, unsigned defaultValue = 0) const; + /// See IntAttribute() + int64_t Int64Attribute(const char* name, int64_t defaultValue = 0) const; + /// See IntAttribute() + uint64_t Unsigned64Attribute(const char* name, uint64_t defaultValue = 0) const; + /// See IntAttribute() + bool BoolAttribute(const char* name, bool defaultValue = false) const; + /// See IntAttribute() + double DoubleAttribute(const char* name, double defaultValue = 0) const; + /// See IntAttribute() + float FloatAttribute(const char* name, float defaultValue = 0) const; + + /** Given an attribute name, QueryIntAttribute() returns + XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion + can't be performed, or XML_NO_ATTRIBUTE if the attribute + doesn't exist. If successful, the result of the conversion + will be written to 'value'. If not successful, nothing will + be written to 'value'. This allows you to provide default + value: + + @verbatim + int value = 10; + QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 + @endverbatim + */ + XMLError QueryIntAttribute( const char* name, int* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryIntValue( value ); + } + + /// See QueryIntAttribute() + XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryUnsignedValue( value ); + } + + /// See QueryIntAttribute() + XMLError QueryInt64Attribute(const char* name, int64_t* value) const { + const XMLAttribute* a = FindAttribute(name); + if (!a) { + return XML_NO_ATTRIBUTE; + } + return a->QueryInt64Value(value); + } + + /// See QueryIntAttribute() + XMLError QueryUnsigned64Attribute(const char* name, uint64_t* value) const { + const XMLAttribute* a = FindAttribute(name); + if(!a) { + return XML_NO_ATTRIBUTE; + } + return a->QueryUnsigned64Value(value); + } + + /// See QueryIntAttribute() + XMLError QueryBoolAttribute( const char* name, bool* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryBoolValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryDoubleAttribute( const char* name, double* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryDoubleValue( value ); + } + /// See QueryIntAttribute() + XMLError QueryFloatAttribute( const char* name, float* value ) const { + const XMLAttribute* a = FindAttribute( name ); + if ( !a ) { + return XML_NO_ATTRIBUTE; + } + return a->QueryFloatValue( value ); + } + + /// See QueryIntAttribute() + XMLError QueryStringAttribute(const char* name, const char** value) const { + const XMLAttribute* a = FindAttribute(name); + if (!a) { + return XML_NO_ATTRIBUTE; + } + *value = a->Value(); + return XML_SUCCESS; + } + + + + /** Given an attribute name, QueryAttribute() returns + XML_SUCCESS, XML_WRONG_ATTRIBUTE_TYPE if the conversion + can't be performed, or XML_NO_ATTRIBUTE if the attribute + doesn't exist. It is overloaded for the primitive types, + and is a generally more convenient replacement of + QueryIntAttribute() and related functions. + + If successful, the result of the conversion + will be written to 'value'. If not successful, nothing will + be written to 'value'. This allows you to provide default + value: + + @verbatim + int value = 10; + QueryAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 + @endverbatim + */ + XMLError QueryAttribute( const char* name, int* value ) const { + return QueryIntAttribute( name, value ); + } + + XMLError QueryAttribute( const char* name, unsigned int* value ) const { + return QueryUnsignedAttribute( name, value ); + } + + XMLError QueryAttribute(const char* name, int64_t* value) const { + return QueryInt64Attribute(name, value); + } + + XMLError QueryAttribute(const char* name, uint64_t* value) const { + return QueryUnsigned64Attribute(name, value); + } + + XMLError QueryAttribute( const char* name, bool* value ) const { + return QueryBoolAttribute( name, value ); + } + + XMLError QueryAttribute( const char* name, double* value ) const { + return QueryDoubleAttribute( name, value ); + } + + XMLError QueryAttribute( const char* name, float* value ) const { + return QueryFloatAttribute( name, value ); + } + + XMLError QueryAttribute(const char* name, const char** value) const { + return QueryStringAttribute(name, value); + } + + /// Sets the named attribute to value. + void SetAttribute( const char* name, const char* value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, int value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, unsigned value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + + /// Sets the named attribute to value. + void SetAttribute(const char* name, int64_t value) { + XMLAttribute* a = FindOrCreateAttribute(name); + a->SetAttribute(value); + } + + /// Sets the named attribute to value. + void SetAttribute(const char* name, uint64_t value) { + XMLAttribute* a = FindOrCreateAttribute(name); + a->SetAttribute(value); + } + + /// Sets the named attribute to value. + void SetAttribute( const char* name, bool value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, double value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + /// Sets the named attribute to value. + void SetAttribute( const char* name, float value ) { + XMLAttribute* a = FindOrCreateAttribute( name ); + a->SetAttribute( value ); + } + + /** + Delete an attribute. + */ + void DeleteAttribute( const char* name ); + + /// Return the first attribute in the list. + const XMLAttribute* FirstAttribute() const { + return _rootAttribute; + } + /// Query a specific attribute in the list. + const XMLAttribute* FindAttribute( const char* name ) const; + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the XMLText child + and accessing it directly. + + If the first child of 'this' is a XMLText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + */ + const char* GetText() const; + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, SetText() is limited compared to creating an XMLText child + and mutating it directly. + + If the first child of 'this' is a XMLText, SetText() sets its value to + the given string, otherwise it will create a first child that is an XMLText. + + This is a convenient method for setting the text of simple contained text: + @verbatim + This is text + fooElement->SetText( "Hullaballoo!" ); + Hullaballoo! + @endverbatim + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then it will not change "This is text", but rather prefix it with a text element: + @verbatim + Hullaballoo!This is text + @endverbatim + + For this XML: + @verbatim + + @endverbatim + SetText() will generate + @verbatim + Hullaballoo! + @endverbatim + */ + void SetText( const char* inText ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( int value ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( unsigned value ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText(int64_t value); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText(uint64_t value); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( bool value ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( double value ); + /// Convenience method for setting text inside an element. See SetText() for important limitations. + void SetText( float value ); + + /** + Convenience method to query the value of a child text node. This is probably best + shown by example. Given you have a document is this form: + @verbatim + + 1 + 1.4 + + @endverbatim + + The QueryIntText() and similar functions provide a safe and easier way to get to the + "value" of x and y. + + @verbatim + int x = 0; + float y = 0; // types of x and y are contrived for example + const XMLElement* xElement = pointElement->FirstChildElement( "x" ); + const XMLElement* yElement = pointElement->FirstChildElement( "y" ); + xElement->QueryIntText( &x ); + yElement->QueryFloatText( &y ); + @endverbatim + + @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted + to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. + + */ + XMLError QueryIntText( int* ival ) const; + /// See QueryIntText() + XMLError QueryUnsignedText( unsigned* uval ) const; + /// See QueryIntText() + XMLError QueryInt64Text(int64_t* uval) const; + /// See QueryIntText() + XMLError QueryUnsigned64Text(uint64_t* uval) const; + /// See QueryIntText() + XMLError QueryBoolText( bool* bval ) const; + /// See QueryIntText() + XMLError QueryDoubleText( double* dval ) const; + /// See QueryIntText() + XMLError QueryFloatText( float* fval ) const; + + int IntText(int defaultValue = 0) const; + + /// See QueryIntText() + unsigned UnsignedText(unsigned defaultValue = 0) const; + /// See QueryIntText() + int64_t Int64Text(int64_t defaultValue = 0) const; + /// See QueryIntText() + uint64_t Unsigned64Text(uint64_t defaultValue = 0) const; + /// See QueryIntText() + bool BoolText(bool defaultValue = false) const; + /// See QueryIntText() + double DoubleText(double defaultValue = 0) const; + /// See QueryIntText() + float FloatText(float defaultValue = 0) const; + + /** + Convenience method to create a new XMLElement and add it as last (right) + child of this node. Returns the created and inserted element. + */ + XMLElement* InsertNewChildElement(const char* name); + /// See InsertNewChildElement() + XMLComment* InsertNewComment(const char* comment); + /// See InsertNewChildElement() + XMLText* InsertNewText(const char* text); + /// See InsertNewChildElement() + XMLDeclaration* InsertNewDeclaration(const char* text); + /// See InsertNewChildElement() + XMLUnknown* InsertNewUnknown(const char* text); + + + // internal: + enum ElementClosingType { + OPEN, // + CLOSED, // + CLOSING // + }; + ElementClosingType ClosingType() const { + return _closingType; + } + virtual XMLNode* ShallowClone( XMLDocument* document ) const override; + virtual bool ShallowEqual( const XMLNode* compare ) const override; + +protected: + char* ParseDeep( char* p, StrPair* parentEndTag, int* curLineNumPtr ) override; + +private: + XMLElement( XMLDocument* doc ); + virtual ~XMLElement(); + XMLElement( const XMLElement& ); // not supported + void operator=( const XMLElement& ); // not supported + + XMLAttribute* FindOrCreateAttribute( const char* name ); + char* ParseAttributes( char* p, int* curLineNumPtr ); + static void DeleteAttribute( XMLAttribute* attribute ); + XMLAttribute* CreateAttribute(); + + enum { BUF_SIZE = 200 }; + ElementClosingType _closingType; + // The attribute list is ordered; there is no 'lastAttribute' + // because the list needs to be scanned for dupes before adding + // a new attribute. + XMLAttribute* _rootAttribute; +}; + + +enum Whitespace { + PRESERVE_WHITESPACE, + COLLAPSE_WHITESPACE, + PEDANTIC_WHITESPACE +}; + + +/** A Document binds together all the functionality. + It can be saved, loaded, and printed to the screen. + All Nodes are connected and allocated to a Document. + If the Document is deleted, all its Nodes are also deleted. +*/ +class TINYXML2_LIB XMLDocument : public XMLNode +{ + friend class XMLElement; + // Gives access to SetError and Push/PopDepth, but over-access for everything else. + // Wishing C++ had "internal" scope. + friend class XMLNode; + friend class XMLText; + friend class XMLComment; + friend class XMLDeclaration; + friend class XMLUnknown; +public: + /// constructor + XMLDocument( bool processEntities = true, Whitespace whitespaceMode = PRESERVE_WHITESPACE ); + ~XMLDocument(); + + virtual XMLDocument* ToDocument() override { + TIXMLASSERT( this == _document ); + return this; + } + virtual const XMLDocument* ToDocument() const override { + TIXMLASSERT( this == _document ); + return this; + } + + /** + Parse an XML file from a character string. + Returns XML_SUCCESS (0) on success, or + an errorID. + + You may optionally pass in the 'nBytes', which is + the number of bytes which will be parsed. If not + specified, TinyXML-2 will assume 'xml' points to a + null terminated string. + */ + XMLError Parse( const char* xml, size_t nBytes=static_cast(-1) ); + + /** + Load an XML file from disk. + Returns XML_SUCCESS (0) on success, or + an errorID. + */ + XMLError LoadFile( const char* filename ); + + /** + Load an XML file from disk. You are responsible + for providing and closing the FILE*. + + NOTE: The file should be opened as binary ("rb") + not text in order for TinyXML-2 to correctly + do newline normalization. + + Returns XML_SUCCESS (0) on success, or + an errorID. + */ + XMLError LoadFile( FILE* ); + + /** + Save the XML file to disk. + Returns XML_SUCCESS (0) on success, or + an errorID. + */ + XMLError SaveFile( const char* filename, bool compact = false ); + + /** + Save the XML file to disk. You are responsible + for providing and closing the FILE*. + + Returns XML_SUCCESS (0) on success, or + an errorID. + */ + XMLError SaveFile( FILE* fp, bool compact = false ); + + bool ProcessEntities() const { + return _processEntities; + } + Whitespace WhitespaceMode() const { + return _whitespaceMode; + } + + /** + Returns true if this document has a leading Byte Order Mark of UTF8. + */ + bool HasBOM() const { + return _writeBOM; + } + /** Sets whether to write the BOM when writing the file. + */ + void SetBOM( bool useBOM ) { + _writeBOM = useBOM; + } + + /** Return the root element of DOM. Equivalent to FirstChildElement(). + To get the first node, use FirstChild(). + */ + XMLElement* RootElement() { + return FirstChildElement(); + } + const XMLElement* RootElement() const { + return FirstChildElement(); + } + + /** Print the Document. If the Printer is not provided, it will + print to stdout. If you provide Printer, this can print to a file: + @verbatim + XMLPrinter printer( fp ); + doc.Print( &printer ); + @endverbatim + + Or you can use a printer to print to memory: + @verbatim + XMLPrinter printer; + doc.Print( &printer ); + // printer.CStr() has a const char* to the XML + @endverbatim + */ + void Print( XMLPrinter* streamer=0 ) const; + virtual bool Accept( XMLVisitor* visitor ) const override; + + /** + 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 ); + + /// Clears the error flags. + void ClearError(); + + /// Return true if there was an error parsing the document. + bool Error() const { + return _errorID != XML_SUCCESS; + } + /// Return the errorID. + XMLError ErrorID() const { + return _errorID; + } + const char* ErrorName() const; + static const char* ErrorIDToName(XMLError errorID); + + /** Returns a "long form" error description. A hopefully helpful + diagnostic with location, line number, and/or additional info. + */ + const char* ErrorStr() const; + + /// A (trivial) utility function that prints the ErrorStr() to stdout. + void PrintError() const; + + /// Return the line where the error occurred, or zero if unknown. + int ErrorLineNum() const + { + return _errorLineNum; + } + + /// Clear the document, resetting it to the initial state. + void Clear(); + + /** + Copies this document to a target document. + The target will be completely cleared before the copy. + If you want to copy a sub-tree, see XMLNode::DeepClone(). + + NOTE: that the 'target' must be non-null. + */ + void DeepCopy(XMLDocument* target) const; + + // internal + char* Identify( char* p, XMLNode** node, bool first ); + + // internal + void MarkInUse(const XMLNode* const); + + virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const override{ + return 0; + } + virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const override{ + return false; + } + +private: + XMLDocument( const XMLDocument& ); // not supported + void operator=( const XMLDocument& ); // not supported + + bool _writeBOM; + bool _processEntities; + XMLError _errorID; + Whitespace _whitespaceMode; + mutable StrPair _errorStr; + int _errorLineNum; + char* _charBuffer; + int _parseCurLineNum; + int _parsingDepth; + // Memory tracking does add some overhead. + // However, the code assumes that you don't + // have a bunch of unlinked nodes around. + // Therefore it takes less memory to track + // in the document vs. a linked list in the XMLNode, + // and the performance is the same. + DynArray _unlinked; + + MemPoolT< sizeof(XMLElement) > _elementPool; + MemPoolT< sizeof(XMLAttribute) > _attributePool; + MemPoolT< sizeof(XMLText) > _textPool; + MemPoolT< sizeof(XMLComment) > _commentPool; + + static const char* _errorNames[XML_ERROR_COUNT]; + + void Parse(); + + void SetError( XMLError error, int lineNum, const char* format, ... ); + + // Something of an obvious security hole, once it was discovered. + // Either an ill-formed XML or an excessively deep one can overflow + // the stack. Track stack depth, and error out if needed. + class DepthTracker { + public: + explicit DepthTracker(XMLDocument * document) { + this->_document = document; + document->PushDepth(); + } + ~DepthTracker() { + _document->PopDepth(); + } + private: + XMLDocument * _document; + }; + void PushDepth(); + void PopDepth(); + + template + NodeType* CreateUnlinkedNode( MemPoolT& pool ); +}; + +template +inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT& pool ) +{ + TIXMLASSERT( sizeof( NodeType ) == PoolElementSize ); + TIXMLASSERT( sizeof( NodeType ) == pool.ItemSize() ); + NodeType* returnNode = new (pool.Alloc()) NodeType( this ); + TIXMLASSERT( returnNode ); + returnNode->_memPool = &pool; + + _unlinked.Push(returnNode); + return returnNode; +} + +/** + A XMLHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2 + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + XMLElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + XMLElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + XMLElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + XMLElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. XMLHandle addresses the verbosity + of such code. A XMLHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + XMLHandle docHandle( &document ); + XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + XMLHandle handleCopy = handle; + @endverbatim + + See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects. +*/ +class TINYXML2_LIB XMLHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + explicit XMLHandle( XMLNode* node ) : _node( node ) { + } + /// Create a handle from a node. + explicit XMLHandle( XMLNode& node ) : _node( &node ) { + } + /// Copy constructor + XMLHandle( const XMLHandle& ref ) : _node( ref._node ) { + } + /// Assignment + XMLHandle& operator=( const XMLHandle& ref ) { + _node = ref._node; + return *this; + } + + /// Get the first child of this handle. + XMLHandle FirstChild() { + return XMLHandle( _node ? _node->FirstChild() : 0 ); + } + /// Get the first child element of this handle. + XMLHandle FirstChildElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->FirstChildElement( name ) : 0 ); + } + /// Get the last child of this handle. + XMLHandle LastChild() { + return XMLHandle( _node ? _node->LastChild() : 0 ); + } + /// Get the last child element of this handle. + XMLHandle LastChildElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->LastChildElement( name ) : 0 ); + } + /// Get the previous sibling of this handle. + XMLHandle PreviousSibling() { + return XMLHandle( _node ? _node->PreviousSibling() : 0 ); + } + /// Get the previous sibling element of this handle. + XMLHandle PreviousSiblingElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); + } + /// Get the next sibling of this handle. + XMLHandle NextSibling() { + return XMLHandle( _node ? _node->NextSibling() : 0 ); + } + /// Get the next sibling element of this handle. + XMLHandle NextSiblingElement( const char* name = 0 ) { + return XMLHandle( _node ? _node->NextSiblingElement( name ) : 0 ); + } + + /// Safe cast to XMLNode. This can return null. + XMLNode* ToNode() { + return _node; + } + /// Safe cast to XMLElement. This can return null. + XMLElement* ToElement() { + return ( _node ? _node->ToElement() : 0 ); + } + /// Safe cast to XMLText. This can return null. + XMLText* ToText() { + return ( _node ? _node->ToText() : 0 ); + } + /// Safe cast to XMLUnknown. This can return null. + XMLUnknown* ToUnknown() { + return ( _node ? _node->ToUnknown() : 0 ); + } + /// Safe cast to XMLDeclaration. This can return null. + XMLDeclaration* ToDeclaration() { + return ( _node ? _node->ToDeclaration() : 0 ); + } + +private: + XMLNode* _node; +}; + + +/** + A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the + same in all regards, except for the 'const' qualifiers. See XMLHandle for API. +*/ +class TINYXML2_LIB XMLConstHandle +{ +public: + explicit XMLConstHandle( const XMLNode* node ) : _node( node ) { + } + explicit XMLConstHandle( const XMLNode& node ) : _node( &node ) { + } + XMLConstHandle( const XMLConstHandle& ref ) : _node( ref._node ) { + } + + XMLConstHandle& operator=( const XMLConstHandle& ref ) { + _node = ref._node; + return *this; + } + + const XMLConstHandle FirstChild() const { + return XMLConstHandle( _node ? _node->FirstChild() : 0 ); + } + const XMLConstHandle FirstChildElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->FirstChildElement( name ) : 0 ); + } + const XMLConstHandle LastChild() const { + return XMLConstHandle( _node ? _node->LastChild() : 0 ); + } + const XMLConstHandle LastChildElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->LastChildElement( name ) : 0 ); + } + const XMLConstHandle PreviousSibling() const { + return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); + } + const XMLConstHandle PreviousSiblingElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->PreviousSiblingElement( name ) : 0 ); + } + const XMLConstHandle NextSibling() const { + return XMLConstHandle( _node ? _node->NextSibling() : 0 ); + } + const XMLConstHandle NextSiblingElement( const char* name = 0 ) const { + return XMLConstHandle( _node ? _node->NextSiblingElement( name ) : 0 ); + } + + + const XMLNode* ToNode() const { + return _node; + } + const XMLElement* ToElement() const { + return ( _node ? _node->ToElement() : 0 ); + } + const XMLText* ToText() const { + return ( _node ? _node->ToText() : 0 ); + } + const XMLUnknown* ToUnknown() const { + return ( _node ? _node->ToUnknown() : 0 ); + } + const XMLDeclaration* ToDeclaration() const { + return ( _node ? _node->ToDeclaration() : 0 ); + } + +private: + const XMLNode* _node; +}; + + +/** + Printing functionality. The XMLPrinter gives you more + options than the XMLDocument::Print() method. + + It can: + -# Print to memory. + -# Print to a file you provide. + -# Print XML without a XMLDocument. + + Print to Memory + + @verbatim + XMLPrinter printer; + doc.Print( &printer ); + SomeFunction( printer.CStr() ); + @endverbatim + + Print to a File + + You provide the file pointer. + @verbatim + XMLPrinter printer( fp ); + doc.Print( &printer ); + @endverbatim + + Print without a XMLDocument + + When loading, an XML parser is very useful. However, sometimes + when saving, it just gets in the way. The code is often set up + for streaming, and constructing the DOM is just overhead. + + The Printer supports the streaming case. The following code + prints out a trivially simple XML file without ever creating + an XML document. + + @verbatim + XMLPrinter printer( fp ); + printer.OpenElement( "foo" ); + printer.PushAttribute( "foo", "bar" ); + printer.CloseElement(); + @endverbatim +*/ +class TINYXML2_LIB XMLPrinter : public XMLVisitor +{ +public: + /** Construct the printer. If the FILE* is specified, + this will print to the FILE. Else it will print + to memory, and the result is available in CStr(). + If 'compact' is set to true, then output is created + with only required whitespace and newlines. + */ + XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 ); + virtual ~XMLPrinter() {} + + /** If streaming, write the BOM and declaration. */ + void PushHeader( bool writeBOM, bool writeDeclaration ); + /** If streaming, start writing an element. + The element must be closed with CloseElement() + */ + void OpenElement( const char* name, bool compactMode=false ); + /// If streaming, add an attribute to an open element. + void PushAttribute( const char* name, const char* value ); + void PushAttribute( const char* name, int value ); + void PushAttribute( const char* name, unsigned value ); + void PushAttribute( const char* name, int64_t value ); + void PushAttribute( const char* name, uint64_t value ); + void PushAttribute( const char* name, bool value ); + void PushAttribute( const char* name, double value ); + /// If streaming, close the Element. + virtual void CloseElement( bool compactMode=false ); + + /// Add a text node. + void PushText( const char* text, bool cdata=false ); + /// Add a text node from an integer. + void PushText( int value ); + /// Add a text node from an unsigned. + void PushText( unsigned value ); + /// Add a text node from a signed 64bit integer. + void PushText( int64_t value ); + /// Add a text node from an unsigned 64bit integer. + void PushText( uint64_t value ); + /// Add a text node from a bool. + void PushText( bool value ); + /// Add a text node from a float. + void PushText( float value ); + /// Add a text node from a double. + void PushText( double value ); + + /// Add a comment + void PushComment( const char* comment ); + + void PushDeclaration( const char* value ); + void PushUnknown( const char* value ); + + virtual bool VisitEnter( const XMLDocument& /*doc*/ ) override; + virtual bool VisitExit( const XMLDocument& /*doc*/ ) override { + return true; + } + + virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) override; + virtual bool VisitExit( const XMLElement& element ) override; + + virtual bool Visit( const XMLText& text ) override; + virtual bool Visit( const XMLComment& comment ) override; + virtual bool Visit( const XMLDeclaration& declaration ) override; + virtual bool Visit( const XMLUnknown& unknown ) override; + + /** + 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( bool resetToFirstElement = true ) { + _buffer.Clear(); + _buffer.Push(0); + _firstElement = resetToFirstElement; + } + +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 ); + virtual void Print( const char* format, ... ); + virtual void Write( const char* data, size_t size ); + virtual void Putc( char ch ); + + inline void Write(const char* data) { Write(data, strlen(data)); } + + void SealElementIfJustOpened(); + bool _elementJustOpened; + DynArray< const char*, 10 > _stack; + +private: + /** + Prepares to write a new node. This includes sealing an element that was + just opened, and writing any whitespace necessary if not in compact mode. + */ + void PrepareForNewNode( bool compactMode ); + void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities. + + bool _firstElement; + FILE* _fp; + int _depth; + int _textDepth; + bool _processEntities; + bool _compactMode; + + enum { + ENTITY_RANGE = 64, + BUF_SIZE = 200 + }; + bool _entityFlag[ENTITY_RANGE]; + bool _restrictedEntityFlag[ENTITY_RANGE]; + + DynArray< char, 20 > _buffer; + + // Prohibit cloning, intentionally not implemented + XMLPrinter( const XMLPrinter& ); + XMLPrinter& operator=( const XMLPrinter& ); +}; + + +} // tinyxml2 + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + +#endif // TINYXML2_INCLUDED diff --git a/cppcheck-2.14.0/generate_coverage_report b/cppcheck-2.14.0/generate_coverage_report new file mode 100644 index 00000000..df29b8ff --- /dev/null +++ b/cppcheck-2.14.0/generate_coverage_report @@ -0,0 +1,20 @@ +#!/bin/bash +set -e +make clean +rm -rf coverage_report +make test CXXFLAGS="-g -fprofile-arcs -ftest-coverage" +test/cfg/runtests.sh +gcov lib/*.cpp -o lib/ +lcov --directory ./ --capture --output-file lcov_tmp.info -b ./ +lcov --extract lcov_tmp.info "$(pwd)/*" --output-file lcov.info +genhtml lcov.info -o coverage_report --frame --legend --demangle-cpp +rm cli/*.gcda +rm cli/*.gcno +rm lib/*.gcda +rm lib/*.gcno +rm test/*.gcda +rm test/*.gcno +rm externals/tinyxml2/*.gcda +rm externals/tinyxml2/*.gcno +rm lcov.info lcov_tmp.info +make clean diff --git a/cppcheck-2.14.0/gui/.clang-tidy b/cppcheck-2.14.0/gui/.clang-tidy new file mode 100644 index 00000000..8a62240d --- /dev/null +++ b/cppcheck-2.14.0/gui/.clang-tidy @@ -0,0 +1,5 @@ +--- +Checks: > + -readability-convert-member-functions-to-static, + -readability-redundant-access-specifiers +InheritParentConfig: true diff --git a/cppcheck-2.14.0/gui/CMakeLists.txt b/cppcheck-2.14.0/gui/CMakeLists.txt new file mode 100644 index 00000000..31fe62ea --- /dev/null +++ b/cppcheck-2.14.0/gui/CMakeLists.txt @@ -0,0 +1,86 @@ +if (BUILD_GUI) + + # disable all clang-tidy checks for Qt generated files + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/.clang-tidy" +"--- +Checks: '-*,misc-definitions-in-headers' +WarningsAsErrors: '*' +CheckOptions: + - { key: HeaderFileExtensions, value: 'x' } +") + + add_compile_definitions($<$>:QT_NO_DEBUG>) + add_compile_definitions($<$>:QT_NO_DEBUG_OUTPUT>) + add_compile_definitions($<$>:QT_NO_WARNING_OUTPUT>) + add_compile_definitions($<$:QT_DEBUG>) + + file(GLOB hdrs "*.h") + file(GLOB srcs "*.cpp") + file(GLOB uis "*.ui") + file(GLOB tss "*.ts") + QT_WRAP_UI(uis_hdrs ${uis}) + QT_ADD_RESOURCES(resources "gui.qrc") + # TODO: passing "-no-obsolete" here breaks the translations + QT_CREATE_TRANSLATION(qms ${CMAKE_CURRENT_SOURCE_DIR} ${tss}) + list(APPEND cppcheck-gui-deps ${hdrs} ${uis_hdrs} ${resources} ${qms}) + add_custom_target(gui-build-deps SOURCES ${cppcheck-gui-deps}) + + list(APPEND cppcheck-gui_SOURCES ${srcs}) + if (NOT BUILD_CORE_DLL) + list(APPEND cppcheck-gui_SOURCES $ $) + if(USE_BUNDLED_TINYXML2) + list(APPEND cppcheck-gui_SOURCES $) + endif() + endif() + + add_executable(cppcheck-gui ${cppcheck-gui-deps} ${cppcheck-gui_SOURCES}) + set_target_properties(cppcheck-gui PROPERTIES AUTOMOC ON) + set_target_properties(cppcheck-gui PROPERTIES WIN32_EXECUTABLE ON) + target_include_directories(cppcheck-gui PRIVATE ${PROJECT_SOURCE_DIR}/lib/) + if(USE_BUNDLED_TINYXML2) + target_externals_include_directories(cppcheck-gui PRIVATE ${PROJECT_SOURCE_DIR}/externals/tinyxml2/) + else() + target_include_directories(cppcheck-gui SYSTEM PRIVATE ${tinyxml2_INCLUDE_DIRS}) + endif() + target_include_directories(cppcheck-gui PRIVATE ${PROJECT_SOURCE_DIR}/externals/picojson/) + if (NOT CMAKE_DISABLE_PRECOMPILE_HEADERS) + target_precompile_headers(cppcheck-gui PRIVATE precompiled.h) + endif() + if (HAVE_RULES) + target_link_libraries(cppcheck-gui ${PCRE_LIBRARY}) + endif() + if(tinyxml2_FOUND AND NOT USE_BUNDLED_TINYXML2) + target_link_libraries(cppcheck-gui ${tinyxml2_LIBRARIES}) + endif() + target_link_libraries(cppcheck-gui ${QT_CORE_LIB} ${QT_GUI_LIB} ${QT_WIDGETS_LIB} ${QT_PRINTSUPPORT_LIB} ${QT_HELP_LIB} ${QT_NETWORK_LIB}) + if(WITH_QCHART) + target_link_libraries(cppcheck-gui ${QT_CHARTS_LIB}) + endif() + if (BUILD_CORE_DLL) + target_compile_definitions(cppcheck-gui PRIVATE CPPCHECKLIB_IMPORT TINYXML2_IMPORT) + target_link_libraries(cppcheck-gui cppcheck-core) + endif() + if(MSVC) + # compilation will fail as e.g. QList::realloc would be replaced by MSVC's macro definition + target_compile_definitions(cppcheck-gui PRIVATE $<$:DISABLE_CRTDBG_MAP_ALLOC>) + endif() + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # Q_UNUSED() in generated code + target_compile_options_safe(cppcheck-gui -Wno-extra-semi-stmt) + # caused by Qt generated moc code + target_compile_options_safe(cppcheck-gui -Wno-redundant-parens) + endif() + + install(TARGETS cppcheck-gui RUNTIME DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} COMPONENT applications) + install(FILES ${qms} DESTINATION ${CMAKE_INSTALL_FULL_BINDIR} COMPONENT applications) + + install(FILES cppcheck-gui.desktop DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/applications) + + # icons + install(FILES cppcheck-gui.svg DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps) + install(FILES cppcheck-gui.png DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/64x64/apps) + + if (BUILD_TESTS) + add_subdirectory(test) + endif() +endif() diff --git a/cppcheck-2.14.0/gui/about.ui b/cppcheck-2.14.0/gui/about.ui new file mode 100644 index 00000000..72cec833 --- /dev/null +++ b/cppcheck-2.14.0/gui/about.ui @@ -0,0 +1,172 @@ + + + About + + + + 0 + 0 + 478 + 375 + + + + About Cppcheck + + + + + + + + + + + + + :/cppcheck-gui.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Version %1 + + + + + + + Cppcheck - A tool for static C/C++ code analysis. + + + true + + + + + + + Copyright © 2007-%1 Cppcheck team. + + + true + + + + + + + This program is licensed under the terms +of the GNU General Public License version 3 + + + true + + + + + + + Visit Cppcheck homepage at %1 + + + true + + + true + + + + + + + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PCRE</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PicoJSON</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">TinyXML2</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Boost</li></ul></body></html> + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + mButtons + accepted() + About + accept() + + + 248 + 254 + + + 157 + 274 + + + + + mButtons + rejected() + About + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/cppcheck-2.14.0/gui/aboutdialog.cpp b/cppcheck-2.14.0/gui/aboutdialog.cpp new file mode 100644 index 00000000..d93d8e8a --- /dev/null +++ b/cppcheck-2.14.0/gui/aboutdialog.cpp @@ -0,0 +1,47 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "aboutdialog.h" + +#include "ui_about.h" + +#include +#include + +AboutDialog::AboutDialog(const QString &version, const QString &extraVersion, QWidget *parent) + : QDialog(parent) + , mUI(new Ui::About) +{ + mUI->setupUi(this); + + QString fmtVersion(version); + if (!extraVersion.isEmpty()) { + fmtVersion += " (" + extraVersion + ")"; + } + mUI->mVersion->setText(mUI->mVersion->text().arg(fmtVersion)); + QString date = __DATE__; + mUI->mCopyright->setText(mUI->mCopyright->text().arg(date.right(4))); + QString url = "https://cppcheck.sourceforge.io/"; + mUI->mHomepage->setText(mUI->mHomepage->text().arg(url)); + connect(mUI->mButtons, &QDialogButtonBox::accepted, this, &AboutDialog::accept); +} + +AboutDialog::~AboutDialog() +{ + delete mUI; +} diff --git a/cppcheck-2.14.0/gui/aboutdialog.h b/cppcheck-2.14.0/gui/aboutdialog.h new file mode 100644 index 00000000..7e46839d --- /dev/null +++ b/cppcheck-2.14.0/gui/aboutdialog.h @@ -0,0 +1,51 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 + +class QWidget; +namespace Ui { + class About; +} + +/// @addtogroup GUI +/// @{ + +/** + * @brief About dialog + * + */ +class AboutDialog : public QDialog { + Q_OBJECT +public: + AboutDialog(const QString &version, + const QString &extraVersion, + QWidget *parent = nullptr); + + ~AboutDialog() override; + +private: + Ui::About* mUI; +}; +/// @} +#endif // ABOUT_DIALOG_H diff --git a/cppcheck-2.14.0/gui/application.cpp b/cppcheck-2.14.0/gui/application.cpp new file mode 100644 index 00000000..e3cc7b99 --- /dev/null +++ b/cppcheck-2.14.0/gui/application.cpp @@ -0,0 +1,28 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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" + +#include + +Application::Application(QString name, QString path, + QString params) + : mName(std::move(name)) + , mPath(std::move(path)) + , mParameters(std::move(params)) +{} diff --git a/cppcheck-2.14.0/gui/application.h b/cppcheck-2.14.0/gui/application.h new file mode 100644 index 00000000..384f50d1 --- /dev/null +++ b/cppcheck-2.14.0/gui/application.h @@ -0,0 +1,114 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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() = default; + Application(QString name, QString path, QString params); + + /** + * @brief Get application name. + * @return Application name. + */ + const QString& getName() const { + return mName; + } + + /** + * @brief Get application path. + * @return Application path. + */ + const QString& getPath() const { + return mPath; + } + + /** + * @brief Get application command line parameters. + * @return Application command line parameters. + */ + const 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 diff --git a/cppcheck-2.14.0/gui/applicationdialog.cpp b/cppcheck-2.14.0/gui/applicationdialog.cpp new file mode 100644 index 00000000..bc8804d6 --- /dev/null +++ b/cppcheck-2.14.0/gui/applicationdialog.cpp @@ -0,0 +1,99 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "applicationdialog.h" + +#include "application.h" +#include "common.h" + +#include "ui_applicationdialog.h" + +#include +#include +#include +#include +#include +#include + + +ApplicationDialog::ApplicationDialog(const QString &title, + Application &app, + QWidget *parent) : + QDialog(parent), + mUI(new Ui::ApplicationDialog), + mApplication(app) +{ + mUI->setupUi(this); + + connect(mUI->mButtonBrowse, &QPushButton::clicked, this, &ApplicationDialog::browse); + connect(mUI->mButtons, &QDialogButtonBox::accepted, this, &ApplicationDialog::ok); + connect(mUI->mButtons, &QDialogButtonBox::rejected, this, &ApplicationDialog::reject); + mUI->mPath->setText(app.getPath()); + mUI->mName->setText(app.getName()); + mUI->mParameters->setText(app.getParameters()); + setWindowTitle(title); + adjustSize(); +} + + +ApplicationDialog::~ApplicationDialog() +{ + delete mUI; +} + +void ApplicationDialog::browse() +{ + QString filter; +#ifdef Q_OS_WIN + // In Windows (almost) all executables have .exe extension + // so it does not make sense to show everything. + filter += tr("Executable files (*.exe);;All files(*.*)"); +#endif // Q_OS_WIN + QString selectedFile = QFileDialog::getOpenFileName(this, + tr("Select viewer application"), + getPath(SETTINGS_LAST_APP_PATH), + filter); + + if (!selectedFile.isEmpty()) { + setPath(SETTINGS_LAST_APP_PATH, selectedFile); + QString path(QDir::toNativeSeparators(selectedFile)); + mUI->mPath->setText(path); + } +} + +void ApplicationDialog::ok() +{ + if (mUI->mName->text().isEmpty() || mUI->mPath->text().isEmpty()) { + QMessageBox msg(QMessageBox::Warning, + tr("Cppcheck"), + tr("You must specify a name, a path and optionally parameters for the application!"), + QMessageBox::Ok, + this); + + msg.exec(); + + reject(); + } else { + // Convert possible native (Windows) path to internal presentation format + mApplication.setName(mUI->mName->text()); + mApplication.setPath(QDir::fromNativeSeparators(mUI->mPath->text())); + mApplication.setParameters(mUI->mParameters->text()); + + accept(); + } +} diff --git a/cppcheck-2.14.0/gui/applicationdialog.h b/cppcheck-2.14.0/gui/applicationdialog.h new file mode 100644 index 00000000..4bc68bf5 --- /dev/null +++ b/cppcheck-2.14.0/gui/applicationdialog.h @@ -0,0 +1,82 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 + +class QWidget; +class Application; +namespace Ui { + class ApplicationDialog; +} + +/// @addtogroup GUI +/// @{ + +/** + * @brief Dialog to edit a startable application. + * User can open errors with user specified applications. This is a dialog + * to modify/add an application to open errors with. + * + */ +class ApplicationDialog : public QDialog { + Q_OBJECT + +public: + /** + * @brief Constructor. + * @param title Title for the dialog. + * @param app Application definition. + * @param parent Parent widget. + */ + ApplicationDialog(const QString &title, + Application &app, + QWidget *parent = nullptr); + ~ApplicationDialog() override; + +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 diff --git a/cppcheck-2.14.0/gui/applicationdialog.ui b/cppcheck-2.14.0/gui/applicationdialog.ui new file mode 100644 index 00000000..212525f4 --- /dev/null +++ b/cppcheck-2.14.0/gui/applicationdialog.ui @@ -0,0 +1,200 @@ + + + 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 + + + + + diff --git a/cppcheck-2.14.0/gui/applicationlist.cpp b/cppcheck-2.14.0/gui/applicationlist.cpp new file mode 100644 index 00000000..80efbddb --- /dev/null +++ b/cppcheck-2.14.0/gui/applicationlist.cpp @@ -0,0 +1,266 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "applicationlist.h" + +#include "application.h" +#include "common.h" + +#include +#include +#include +#include + +ApplicationList::ApplicationList(QObject *parent) : + QObject(parent) +{ + //ctor +} + +ApplicationList::~ApplicationList() +{ + clear(); +} + +bool ApplicationList::loadSettings() +{ + QSettings settings; + QStringList names = settings.value(SETTINGS_APPLICATION_NAMES, QStringList()).toStringList(); + QStringList paths = settings.value(SETTINGS_APPLICATION_PATHS, QStringList()).toStringList(); + QStringList params = settings.value(SETTINGS_APPLICATION_PARAMS, QStringList()).toStringList(); + int defapp = settings.value(SETTINGS_APPLICATION_DEFAULT, -1).toInt(); + + // Params will be empty first time starting with the new setting. + // Return false and inform user about problem with application settings. + bool succeeded = true; + if (!names.empty() && !paths.empty() && params.empty()) { + for (int i = 0; i < paths.length(); i++) + params << QString(); + succeeded = false; + } + + if (names.empty() && paths.empty() && params.empty()) { +#ifndef _WIN32 + // use as default for gnome environments + if (QFileInfo("/usr/bin/gedit").isExecutable()) { + Application app; + app.setName("gedit"); + app.setPath("/usr/bin/gedit"); + app.setParameters("+(line) (file)"); + addApplication(app); + defapp = 0; + } + checkAndAddApplication("/usr/bin/geany","geany","+(line) (file)"); + checkAndAddApplication("/usr/bin/qtcreator","Qt Creator","-client (file):(line)"); + // use as default for kde environments + if (QFileInfo("/usr/bin/kate").isExecutable()) { + Application app; + app.setName("kate"); + app.setPath("/usr/bin/kate"); + app.setParameters("-l(line) (file)"); + addApplication(app); + defapp = 0; + } +#else + if (findDefaultWindowsEditor()) { + defapp = 0; + } +#endif + } else if (names.size() == paths.size()) { + for (int i = 0; i < names.size(); i++) { + const Application app(names[i], paths[i], params[i]); + addApplication(app); + } + } + + if (defapp == -1) + mDefaultApplicationIndex = 0; + else if (defapp < names.size()) + mDefaultApplicationIndex = defapp; + else + mDefaultApplicationIndex = 0; + + return succeeded; +} + +void ApplicationList::saveSettings() const +{ + QSettings settings; + QStringList names; + QStringList paths; + QStringList params; + + for (int i = 0; i < getApplicationCount(); i++) { + const Application& app = getApplication(i); + names << app.getName(); + paths << app.getPath(); + params << app.getParameters(); + } + + settings.setValue(SETTINGS_APPLICATION_NAMES, names); + settings.setValue(SETTINGS_APPLICATION_PATHS, paths); + settings.setValue(SETTINGS_APPLICATION_PARAMS, params); + settings.setValue(SETTINGS_APPLICATION_DEFAULT, mDefaultApplicationIndex); +} + +int ApplicationList::getApplicationCount() const +{ + return mApplications.size(); +} + +Application& ApplicationList::getApplication(const int index) +{ + if (index >= 0 && index < mApplications.size()) { + return mApplications[index]; + } + + static Application dummy; // TODO: Throw exception instead? + return dummy; +} + +const Application& ApplicationList::getApplication(const int index) const +{ + if (index >= 0 && index < mApplications.size()) { + return mApplications[index]; + } + + static const Application dummy; // TODO: Throw exception instead? + return dummy; +} + +void ApplicationList::addApplication(const Application &app) +{ + if (app.getName().isEmpty() || app.getPath().isEmpty()) { + return; + } + mApplications << app; +} + +void ApplicationList::removeApplication(const int index) +{ + mApplications.removeAt(index); +} + +void ApplicationList::setDefault(const int index) +{ + if (index < mApplications.size() && index >= 0) { + mDefaultApplicationIndex = index; + } +} + +void ApplicationList::copy(const ApplicationList *list) +{ + if (!list) { + return; + } + + clear(); + for (int i = 0; i < list->getApplicationCount(); i++) { + const Application& app = list->getApplication(i); + addApplication(app); + } + mDefaultApplicationIndex = list->getDefaultApplication(); +} + +void ApplicationList::clear() +{ + mApplications.clear(); + mDefaultApplicationIndex = -1; +} + +bool ApplicationList::checkAndAddApplication(const QString& appPath, const QString& name, const QString& parameters) +{ + if (QFileInfo::exists(appPath) && QFileInfo(appPath).isExecutable()) { + Application app; + app.setName(name); + app.setPath("\"" + appPath + "\""); + app.setParameters(parameters); + addApplication(app); + return true; + } + return false; +} + +#ifdef _WIN32 +bool ApplicationList::findDefaultWindowsEditor() +{ + bool foundOne = false; +#ifdef WIN64 // As long as we do support 32-bit XP, we cannot be sure that the environment variable "ProgramFiles(x86)" exists + const QString appPathx86(getenv("ProgramFiles(x86)")); +#else + const QString appPathx86(getenv("ProgramFiles")); +#endif + const QString appPathx64(getenv("ProgramW6432")); + const QString windowsPath(getenv("windir")); + + if (checkAndAddApplication(appPathx86 + "\\Notepad++\\notepad++.exe", "Notepad++", "-n(line) (file)")) + foundOne = true; + else if (checkAndAddApplication(appPathx64 + "\\Notepad++\\notepad++.exe", "Notepad++", "-n(line) (file)")) + foundOne = true; + + if (checkAndAddApplication(appPathx86 + "\\Notepad2\\Notepad2.exe", "Notepad2", "/g (line) (file)")) + foundOne = true; + else if (checkAndAddApplication(appPathx64 + "\\Notepad2\\Notepad2.exe", "Notepad2", "/g (line) (file)")) + foundOne = true; + + if (checkAndAddApplication(windowsPath + "\\system32\\notepad.exe", "Notepad", "(file)")) + foundOne = true; + + QString regPath = "HKEY_CLASSES_ROOT\\Applications\\QtProject.QtCreator.pro\\shell\\Open\\command"; + QSettings registry(regPath, QSettings::NativeFormat); + QString qtCreatorRegistry = registry.value("Default", QString()).toString(); + QString qtCreatorPath = qtCreatorRegistry.left(qtCreatorRegistry.indexOf(".exe") + 4); + if (!qtCreatorRegistry.isEmpty() && checkAndAddApplication(qtCreatorPath, "Qt Creator", "-client (file):(line)")) { + foundOne = true; + } + + const QString regPathUEdit32 = "HKEY_CLASSES_ROOT\\Applications\\Uedit32.exe\\shell\\open\\Command"; + const QSettings registryUEdit32(regPathUEdit32, QSettings::NativeFormat); + const QString uedit32Registry = registryUEdit32.value("Default", QString()).toString(); + if (!uedit32Registry.isEmpty()) { + // Extract path to executable and make sure there is no single quotation mark at the beginning + const QString uedit32Path = uedit32Registry.left(uedit32Registry.indexOf(".exe") + 4).replace("\"", ""); + if (checkAndAddApplication(uedit32Path, "UltraEdit 32", "(file)/(line)")) { + foundOne = true; + } + } + + const QString regPathUEdit64 = "HKEY_CLASSES_ROOT\\Applications\\uedit64.exe\\shell\\open\\Command"; + const QSettings registryUEdit64(regPathUEdit64, QSettings::NativeFormat); + const QString uedit64Registry = registryUEdit64.value("Default", QString()).toString(); + if (!uedit64Registry.isEmpty()) { + // Extract path to executable and make sure there is no single quotation mark at the beginning + const QString uedit64Path = uedit64Registry.left(uedit64Registry.indexOf(".exe") + 4).replace("\"", ""); + if (checkAndAddApplication(uedit64Path, "UltraEdit 64", "(file)/(line)")) { + foundOne = true; + } + } + + const QString regPathMSVSCode = "HKEY_CLASSES_ROOT\\Applications\\Code.exe\\shell\\open\\command"; + const QSettings registryMSVSCode(regPathMSVSCode, QSettings::NativeFormat); + const QString msvscodeRegistry = registryMSVSCode.value("Default", QString()).toString(); + if (!msvscodeRegistry.isEmpty()) { + const QString msvscodePath = msvscodeRegistry.left(msvscodeRegistry.indexOf(".exe") + 4).replace("\"", ""); + if (checkAndAddApplication(msvscodePath, "Microsoft VS Code", "-g (file):(line)")) { + foundOne = true; + } + } + + return foundOne; +} +#endif diff --git a/cppcheck-2.14.0/gui/applicationlist.h b/cppcheck-2.14.0/gui/applicationlist.h new file mode 100644 index 00000000..537ca51a --- /dev/null +++ b/cppcheck-2.14.0/gui/applicationlist.h @@ -0,0 +1,139 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "application.h" + +#include +#include +#include + +/// @addtogroup GUI +/// @{ + + +/** + * @brief List of applications user has specified to open errors with. + */ +class ApplicationList : public QObject { + Q_OBJECT +public: + + explicit ApplicationList(QObject *parent = nullptr); + ~ApplicationList() override; + + /** + * @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(); + +#ifdef _WIN32 + /** + * @brief Find editor used by default in Windows. + * Check if Notepad++ is installed and use it. If not, use Notepad. + */ + bool findDefaultWindowsEditor(); +#endif + +private: + + bool checkAndAddApplication(const QString& appPath, const QString& name, const QString& parameters); + + /** + * @brief List of applications + * + */ + QList mApplications; + + /** + * @brief Index of the default application. + * + */ + int mDefaultApplicationIndex = -1; +}; +/// @} +#endif // APPLICATIONLIST_H diff --git a/cppcheck-2.14.0/gui/checkstatistics.cpp b/cppcheck-2.14.0/gui/checkstatistics.cpp new file mode 100644 index 00000000..4f812b8e --- /dev/null +++ b/cppcheck-2.14.0/gui/checkstatistics.cpp @@ -0,0 +1,117 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "checkstatistics.h" + +#include +#include +#include + +CheckStatistics::CheckStatistics(QObject *parent) + : QObject(parent) +{ + clear(); +} + +static void addItem(QMap &m, const QString &key) +{ + if (m.contains(key)) + m[key]++; + else + m[key] = 0; +} + +void CheckStatistics::addItem(const QString &tool, ShowTypes::ShowType type) +{ + const QString lower = tool.toLower(); + switch (type) { + case ShowTypes::ShowStyle: + ::addItem(mStyle, lower); + break; + case ShowTypes::ShowWarnings: + ::addItem(mWarning, lower); + break; + case ShowTypes::ShowPerformance: + ::addItem(mPerformance, lower); + break; + case ShowTypes::ShowPortability: + ::addItem(mPortability, lower); + break; + case ShowTypes::ShowErrors: + ::addItem(mError, lower); + break; + case ShowTypes::ShowInformation: + ::addItem(mInformation, lower); + break; + case ShowTypes::ShowNone: + default: + qDebug() << "Unknown error type - not added to statistics."; + break; + } +} + +void CheckStatistics::addChecker(const QString &checker) +{ + mActiveCheckers.insert(checker.toStdString()); +} + +void CheckStatistics::clear() +{ + mStyle.clear(); + mWarning.clear(); + mPerformance.clear(); + mPortability.clear(); + mInformation.clear(); + mError.clear(); + mActiveCheckers.clear(); + mCheckersReport.clear(); +} + +unsigned CheckStatistics::getCount(const QString &tool, ShowTypes::ShowType type) const +{ + const QString lower = tool.toLower(); + switch (type) { + case ShowTypes::ShowStyle: + return mStyle.value(lower,0); + case ShowTypes::ShowWarnings: + return mWarning.value(lower,0); + case ShowTypes::ShowPerformance: + return mPerformance.value(lower,0); + case ShowTypes::ShowPortability: + return mPortability.value(lower,0); + case ShowTypes::ShowErrors: + return mError.value(lower,0); + case ShowTypes::ShowInformation: + return mInformation.value(lower,0); + case ShowTypes::ShowNone: + default: + qDebug() << "Unknown error type - returning zero statistics."; + return 0; + } +} + +QStringList CheckStatistics::getTools() const +{ + QSet ret; + for (const QString& tool: mStyle.keys()) ret.insert(tool); + for (const QString& tool: mWarning.keys()) ret.insert(tool); + for (const QString& tool: mPerformance.keys()) ret.insert(tool); + for (const QString& tool: mPortability.keys()) ret.insert(tool); + for (const QString& tool: mError.keys()) ret.insert(tool); + return QStringList(ret.values()); +} diff --git a/cppcheck-2.14.0/gui/checkstatistics.h b/cppcheck-2.14.0/gui/checkstatistics.h new file mode 100644 index 00000000..e4d4da9d --- /dev/null +++ b/cppcheck-2.14.0/gui/checkstatistics.h @@ -0,0 +1,102 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "showtypes.h" + +#include +#include +#include +#include + +#include +#include +#include + +/// @addtogroup GUI +/// @{ + +/** + * A class for check statistics. + */ +class CheckStatistics : public QObject { +public: + explicit CheckStatistics(QObject *parent = nullptr); + + /** + * @brief Add new checked item to statistics. + * + * @param tool Tool. + * @param type Type of the item to add. + */ + void addItem(const QString &tool, ShowTypes::ShowType type); + + /** + * @brief Add checker to statistics + */ + void addChecker(const QString& checker); + + /** + * @brief Clear the statistics. + * + */ + void clear(); + + /** + * @brief Return statistics for given type. + * + * @param tool Tool. + * @param type Type for which the statistics are returned. + * @return Number of items of given type. + */ + unsigned getCount(const QString &tool, ShowTypes::ShowType type) const; + + const std::set& getActiveCheckers() const { + return mActiveCheckers; + } + + int getNumberOfActiveCheckers() const { + return mActiveCheckers.size(); + } + + /** Get tools with results */ + QStringList getTools() const; + + void setCheckersReport(QString report) { + mCheckersReport = std::move(report); + } + const QString& getCheckersReport() const { + return mCheckersReport; + } + +private: + QMap mStyle; + QMap mWarning; + QMap mPerformance; + QMap mPortability; + QMap mInformation; + QMap mError; + std::set mActiveCheckers; + QString mCheckersReport; +}; + +/// @} + +#endif // CHECKSTATISTICS_H diff --git a/cppcheck-2.14.0/gui/checkthread.cpp b/cppcheck-2.14.0/gui/checkthread.cpp new file mode 100644 index 00000000..7d8d22eb --- /dev/null +++ b/cppcheck-2.14.0/gui/checkthread.cpp @@ -0,0 +1,468 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "checkthread.h" + +#include "analyzerinfo.h" +#include "common.h" +#include "cppcheck.h" +#include "erroritem.h" +#include "errorlogger.h" +#include "errortypes.h" +#include "filesettings.h" +#include "settings.h" +#include "standards.h" +#include "threadresult.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#include +#endif + +// NOLINTNEXTLINE(performance-unnecessary-value-param) - used as callback so we need to preserve the signature +int CheckThread::executeCommand(std::string exe, std::vector args, std::string redirect, std::string &output) // cppcheck-suppress passedByValue +{ + output.clear(); + + QStringList args2; + for (const std::string &arg: args) + args2 << QString::fromStdString(arg); + + QProcess process; + process.start(QString::fromStdString(exe), args2); + process.waitForFinished(); + + if (redirect == "2>&1") { + QString s1 = process.readAllStandardOutput(); + QString s2 = process.readAllStandardError(); + output = (s1 + "\n" + s2).toStdString(); + } else + output = process.readAllStandardOutput().toStdString(); + + if (startsWith(redirect, "2> ")) { + std::ofstream fout(redirect.substr(3)); + fout << process.readAllStandardError().toStdString(); + } + return process.exitCode(); +} + + +CheckThread::CheckThread(ThreadResult &result) : + mResult(result), + mCppcheck(result, true, executeCommand) +{} + +void CheckThread::check(const Settings &settings) +{ + mFiles.clear(); + mCppcheck.settings() = settings; + start(); +} + +void CheckThread::analyseWholeProgram(const QStringList &files) +{ + mFiles = files; + mAnalyseWholeProgram = true; + start(); +} + +// cppcheck-suppress unusedFunction - TODO: false positive +void CheckThread::run() +{ + mState = Running; + + if (!mFiles.isEmpty() || mAnalyseWholeProgram) { + mAnalyseWholeProgram = false; + qDebug() << "Whole program analysis"; + std::list> files2; + std::transform(mFiles.cbegin(), mFiles.cend(), std::back_inserter(files2), [&](const QString& file) { + return std::pair{file.toStdString(), 0}; + }); + mCppcheck.analyseWholeProgram(mCppcheck.settings().buildDir, files2, {}); + mFiles.clear(); + emit done(); + return; + } + + QString file = mResult.getNextFile(); + while (!file.isEmpty() && mState == Running) { + qDebug() << "Checking file" << file; + mCppcheck.check(file.toStdString()); + runAddonsAndTools(nullptr, file); + emit fileChecked(file); + + if (mState == Running) + file = mResult.getNextFile(); + } + + FileSettings fileSettings = mResult.getNextFileSettings(); + while (!fileSettings.filename.empty() && mState == Running) { + file = QString::fromStdString(fileSettings.filename); + qDebug() << "Checking file" << file; + mCppcheck.check(fileSettings); + runAddonsAndTools(&fileSettings, QString::fromStdString(fileSettings.filename)); + emit fileChecked(file); + + if (mState == Running) + fileSettings = mResult.getNextFileSettings(); + } + + if (mState == Running) + mState = Ready; + else + mState = Stopped; + + emit done(); +} + +void CheckThread::runAddonsAndTools(const FileSettings *fileSettings, const QString &fileName) +{ + for (const QString& addon : mAddonsAndTools) { + if (addon == CLANG_ANALYZER || addon == CLANG_TIDY) { + if (!fileSettings) + continue; + + if (!fileSettings->cfg.empty() && !startsWith(fileSettings->cfg,"Debug")) + continue; + + QStringList args; + for (std::list::const_iterator incIt = fileSettings->includePaths.cbegin(); incIt != fileSettings->includePaths.cend(); ++incIt) + args << ("-I" + QString::fromStdString(*incIt)); + for (std::list::const_iterator i = fileSettings->systemIncludePaths.cbegin(); i != fileSettings->systemIncludePaths.cend(); ++i) + args << "-isystem" << QString::fromStdString(*i); + for (const QString& def : QString::fromStdString(fileSettings->defines).split(";")) { + args << ("-D" + def); + } + for (const std::string& U : fileSettings->undefs) { + args << QString::fromStdString("-U" + U); + } + + const QString clangPath = CheckThread::clangTidyCmd(); + if (!clangPath.isEmpty()) { + QDir dir(clangPath + "/../lib/clang"); + for (const QString& ver : dir.entryList()) { + QString includePath = dir.absolutePath() + '/' + ver + "/include"; + if (ver[0] != '.' && QDir(includePath).exists()) { + args << "-isystem" << includePath; + break; + } + } + } + +#ifdef Q_OS_WIN + // To create compile_commands.json in windows see: + // https://bitsmaker.gitlab.io/post/clang-tidy-from-vs2015/ + + for (QString includePath : mClangIncludePaths) { + if (!includePath.isEmpty()) { + includePath.replace("\\", "/"); + args << "-isystem" << includePath.trimmed(); + } + } + + args << "-U__STDC__" << "-fno-ms-compatibility"; +#endif + + if (!fileSettings->standard.empty()) + args << ("-std=" + QString::fromStdString(fileSettings->standard)); + else { + // TODO: pass C or C++ standard based on file type + const std::string std = mCppcheck.settings().standards.getCPP(); + if (!std.empty()) { + args << ("-std=" + QString::fromStdString(std)); + } + } + + QString analyzerInfoFile; + + const std::string &buildDir = mCppcheck.settings().buildDir; + if (!buildDir.empty()) { + analyzerInfoFile = QString::fromStdString(AnalyzerInformation::getAnalyzerInfoFile(buildDir, fileSettings->filename, fileSettings->cfg)); + + QStringList args2(args); + args2.insert(0,"-E"); + args2 << fileName; + QProcess process; + process.start(clangCmd(),args2); + process.waitForFinished(); + const QByteArray &ba = process.readAllStandardOutput(); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + const quint16 chksum = qChecksum(QByteArrayView(ba)); +#else + const quint16 chksum = qChecksum(ba.data(), ba.length()); +#endif + + QFile f1(analyzerInfoFile + '.' + addon + "-E"); + if (f1.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in1(&f1); + const quint16 oldchksum = in1.readAll().toInt(); + if (oldchksum == chksum) { + QFile f2(analyzerInfoFile + '.' + addon + "-results"); + if (f2.open(QIODevice::ReadOnly | QIODevice::Text)) { + QTextStream in2(&f2); + parseClangErrors(addon, fileName, in2.readAll()); + continue; + } + } + f1.close(); + } + f1.open(QIODevice::WriteOnly | QIODevice::Text); + QTextStream out1(&f1); + out1 << chksum; + + QFile::remove(analyzerInfoFile + '.' + addon + "-results"); + } + + if (addon == CLANG_ANALYZER) { + /* + // Using clang + args.insert(0,"--analyze"); + args.insert(1, "-Xanalyzer"); + args.insert(2, "-analyzer-output=text"); + args << fileName; + */ + // Using clang-tidy + args.insert(0,"-checks=-*,clang-analyzer-*"); + args.insert(1, fileName); + args.insert(2, "--"); + } else { + args.insert(0,"-checks=*,-clang-analyzer-*,-llvm*"); + args.insert(1, fileName); + args.insert(2, "--"); + } + + { + const QString cmd(clangTidyCmd()); + QString debug(cmd.contains(" ") ? ('\"' + cmd + '\"') : cmd); + for (const QString& arg : args) { + if (arg.contains(" ")) + debug += " \"" + arg + '\"'; + else + debug += ' ' + arg; + } + qDebug() << debug; + + if (!analyzerInfoFile.isEmpty()) { + QFile f(analyzerInfoFile + '.' + addon + "-cmd"); + if (f.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream out(&f); + out << debug; + } + } + } + + QProcess process; + process.start(clangTidyCmd(), args); + process.waitForFinished(600*1000); + const QString errout(process.readAllStandardOutput() + "\n\n\n" + process.readAllStandardError()); + if (!analyzerInfoFile.isEmpty()) { + QFile f(analyzerInfoFile + '.' + addon + "-results"); + if (f.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream out(&f); + out << errout; + } + } + + parseClangErrors(addon, fileName, errout); + } + } +} + +void CheckThread::stop() +{ + mState = Stopping; + Settings::terminate(); +} + +void CheckThread::parseClangErrors(const QString &tool, const QString &file0, QString err) +{ + QList errorItems; + ErrorItem errorItem; + static const QRegularExpression r1("^(.+):([0-9]+):([0-9]+): (note|warning|error|fatal error): (.*)$"); + static const QRegularExpression r2("^(.*)\\[([a-zA-Z0-9\\-_\\.]+)\\]$"); + QTextStream in(&err, QIODevice::ReadOnly); + while (!in.atEnd()) { + QString line = in.readLine(); + + if (line.startsWith("Assertion failed:")) { + ErrorItem e; + e.errorPath.append(QErrorPathItem()); + e.errorPath.last().file = file0; + e.errorPath.last().line = 1; + e.errorPath.last().column = 1; + e.errorId = tool + "-internal-error"; + e.file0 = file0; + e.message = line; + e.severity = Severity::information; + errorItems.append(e); + continue; + } + + const QRegularExpressionMatch r1MatchRes = r1.match(line); + if (!r1MatchRes.hasMatch()) + continue; + if (r1MatchRes.captured(4) != "note") { + errorItems.append(errorItem); + errorItem = ErrorItem(); + errorItem.file0 = r1MatchRes.captured(1); + } + + errorItem.errorPath.append(QErrorPathItem()); + errorItem.errorPath.last().file = r1MatchRes.captured(1); + errorItem.errorPath.last().line = r1MatchRes.captured(2).toInt(); + errorItem.errorPath.last().column = r1MatchRes.captured(3).toInt(); + if (r1MatchRes.captured(4) == "warning") + errorItem.severity = Severity::warning; + else if (r1MatchRes.captured(4) == "error" || r1MatchRes.captured(4) == "fatal error") + errorItem.severity = Severity::error; + + QString message,id; + const QRegularExpressionMatch r2MatchRes = r2.match(r1MatchRes.captured(5)); + if (r2MatchRes.hasMatch()) { + message = r2MatchRes.captured(1); + const QString id1(r2MatchRes.captured(2)); + if (id1.startsWith("clang")) + id = id1; + else + id = tool + '-' + r2MatchRes.captured(2); + if (tool == CLANG_TIDY) { + if (id1.startsWith("performance")) + errorItem.severity = Severity::performance; + else if (id1.startsWith("portability")) + errorItem.severity = Severity::portability; + else if (id1.startsWith("misc") && !id1.contains("unused")) + errorItem.severity = Severity::warning; + else + errorItem.severity = Severity::style; + } + } else { + message = r1MatchRes.captured(5); + id = CLANG_ANALYZER; + } + + if (errorItem.errorPath.size() == 1) { + errorItem.message = message; + errorItem.errorId = id; + } + + errorItem.errorPath.last().info = message; + } + errorItems.append(errorItem); + + for (const ErrorItem &e : errorItems) { + if (e.errorPath.isEmpty()) + continue; + SuppressionList::ErrorMessage errorMessage; + errorMessage.setFileName(e.errorPath.back().file.toStdString()); + errorMessage.lineNumber = e.errorPath.back().line; + errorMessage.errorId = e.errorId.toStdString(); + errorMessage.symbolNames = e.symbolNames.toStdString(); + + if (isSuppressed(errorMessage)) + continue; + + std::list callstack; + std::transform(e.errorPath.cbegin(), e.errorPath.cend(), std::back_inserter(callstack), [](const QErrorPathItem& path) { + return ErrorMessage::FileLocation(path.file.toStdString(), path.info.toStdString(), path.line, path.column); + }); + const std::string f0 = file0.toStdString(); + const std::string msg = e.message.toStdString(); + const std::string id = e.errorId.toStdString(); + ErrorMessage errmsg(callstack, f0, e.severity, msg, id, Certainty::normal); + mResult.reportErr(errmsg); + } +} + +bool CheckThread::isSuppressed(const SuppressionList::ErrorMessage &errorMessage) const +{ + return std::any_of(mSuppressions.cbegin(), mSuppressions.cend(), [&](const SuppressionList::Suppression& s) { + return s.isSuppressed(errorMessage); + }); +} + +QString CheckThread::clangCmd() +{ + QString path = QSettings().value(SETTINGS_CLANG_PATH,QString()).toString(); + if (!path.isEmpty()) + path += '/'; + path += "clang"; +#ifdef Q_OS_WIN + path += ".exe"; +#endif + + QProcess process; + process.start(path, QStringList() << "--version"); + process.waitForFinished(); + if (process.exitCode() == 0) + return path; + +#ifdef Q_OS_WIN + // Try to autodetect clang + if (QFileInfo("C:/Program Files/LLVM/bin/clang.exe").exists()) + return "C:/Program Files/LLVM/bin/clang.exe"; +#endif + + return QString(); +} + +QString CheckThread::clangTidyCmd() +{ + QString path = QSettings().value(SETTINGS_CLANG_PATH,QString()).toString(); + if (!path.isEmpty()) + path += '/'; + path += "clang-tidy"; +#ifdef Q_OS_WIN + path += ".exe"; +#endif + + QProcess process; + process.start(path, QStringList() << "--version"); + process.waitForFinished(); + if (process.exitCode() == 0) + return path; + +#ifdef Q_OS_WIN + // Try to autodetect clang-tidy + if (QFileInfo("C:/Program Files/LLVM/bin/clang-tidy.exe").exists()) + return "C:/Program Files/LLVM/bin/clang-tidy.exe"; +#endif + + return QString(); +} diff --git a/cppcheck-2.14.0/gui/checkthread.h b/cppcheck-2.14.0/gui/checkthread.h new file mode 100644 index 00000000..67e2eabe --- /dev/null +++ b/cppcheck-2.14.0/gui/checkthread.h @@ -0,0 +1,150 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "cppcheck.h" +#include "suppressions.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +class Settings; +class ThreadResult; +struct FileSettings; + +/// @addtogroup GUI +/// @{ + +/** + * @brief Thread to run cppcheck + * + */ +class CheckThread : public QThread { + Q_OBJECT +public: + explicit CheckThread(ThreadResult &result); + + /** + * @brief Set settings for cppcheck + * + * @param settings settings for cppcheck + */ + void check(const Settings &settings); + + /** + * @brief Run whole program analysis + * @param files All files + */ + void analyseWholeProgram(const QStringList &files); + + void setAddonsAndTools(const QStringList &addonsAndTools) { + mAddonsAndTools = addonsAndTools; + } + + void setClangIncludePaths(const QStringList &s) { + mClangIncludePaths = s; + } + + void setSuppressions(const QList &s) { + mSuppressions = s; + } + + /** + * @brief method that is run in a thread + * + */ + void run() override; + + void stop(); + + /** + * Determine command to run clang + * \return Command to run clang, empty if it is not found + */ + static QString clangCmd(); + + /** + * Determine command to run clang-tidy + * \return Command to run clang-tidy, empty if it is not found + */ + static QString clangTidyCmd(); + + static int executeCommand(std::string exe, std::vector args, std::string redirect, std::string &output); + +signals: + + /** + * @brief cpp checking is done + * + */ + void done(); + + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + 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. Can be changed from outside + */ + std::atomic mState{Ready}; + + ThreadResult &mResult; + /** + * @brief Cppcheck itself + */ + CppCheck mCppcheck; + +private: + void runAddonsAndTools(const FileSettings *fileSettings, const QString &fileName); + + void parseClangErrors(const QString &tool, const QString &file0, QString err); + + bool isSuppressed(const SuppressionList::ErrorMessage &errorMessage) const; + + QStringList mFiles; + bool mAnalyseWholeProgram{}; + QStringList mAddonsAndTools; + QStringList mClangIncludePaths; + QList mSuppressions; +}; +/// @} +#endif // CHECKTHREAD_H diff --git a/cppcheck-2.14.0/gui/codeeditor.cpp b/cppcheck-2.14.0/gui/codeeditor.cpp new file mode 100644 index 00000000..0399541b --- /dev/null +++ b/cppcheck-2.14.0/gui/codeeditor.cpp @@ -0,0 +1,462 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "codeeditor.h" + +#include "codeeditorstyle.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class QTextDocument; + + +Highlighter::Highlighter(QTextDocument *parent, + CodeEditorStyle *widgetStyle) : + QSyntaxHighlighter(parent), + mWidgetStyle(widgetStyle) +{ + HighlightingRule rule; + + mKeywordFormat.setForeground(mWidgetStyle->keywordColor); + mKeywordFormat.setFontWeight(mWidgetStyle->keywordWeight); + QStringList keywordPatterns; + // TODO: use Keywords::getX() + keywordPatterns << "alignas" + << "alignof" + << "asm" + << "auto" + << "bool" + << "break" + << "case" + << "catch" + << "char" + << "char8_t" + << "char16_t" + << "char32_t" + << "class" + << "concept" + << "const" + << "consteval" + << "constexpr" + << "constinit" + << "const_cast" + << "continue" + << "co_await" + << "co_return" + << "co_yield" + << "decltype" + << "default" + << "delete" + << "do" + << "double" + << "dynamic_cast" + << "else" + << "enum" + << "explicit" + << "export" + << "extern" + << "false" + << "final" + << "float" + << "for" + << "friend" + << "goto" + << "if" + << "import" + << "inline" + << "int" + << "long" + << "module" + << "mutable" + << "namespace" + << "new" + << "noexcept" + << "nullptr" + << "operator" + << "override" + << "private" + << "protected" + << "public" + << "reinterpret_cast" + << "requires" + << "return" + << "short" + << "signed" + << "static" + << "static_assert" + << "static_cast" + << "struct" + << "switch" + << "template" + << "this" + << "thread_local" + << "throw" + << "true" + << "try" + << "typedef" + << "typeid" + << "typename" + << "union" + << "unsigned" + << "virtual" + << "void" + << "volatile" + << "wchar_t" + << "while"; + for (const QString &pattern : keywordPatterns) { + rule.pattern = QRegularExpression("\\b" + pattern + "\\b"); + rule.format = mKeywordFormat; + rule.ruleRole = RuleRole::Keyword; + mHighlightingRules.append(rule); + } + + mClassFormat.setForeground(mWidgetStyle->classColor); + mClassFormat.setFontWeight(mWidgetStyle->classWeight); + rule.pattern = QRegularExpression("\\bQ[A-Za-z]+\\b"); + rule.format = mClassFormat; + rule.ruleRole = RuleRole::Class; + mHighlightingRules.append(rule); + + mQuotationFormat.setForeground(mWidgetStyle->quoteColor); + mQuotationFormat.setFontWeight(mWidgetStyle->quoteWeight); + // We use lazy `*?` instead greed `*` quantifier to find the real end of the c-string. + // We use negative lookbehind assertion `(?commentColor); + mSingleLineCommentFormat.setFontWeight(mWidgetStyle->commentWeight); + rule.pattern = QRegularExpression("//[^\n]*"); + rule.format = mSingleLineCommentFormat; + rule.ruleRole = RuleRole::Comment; + mHighlightingRules.append(rule); + + mHighlightingRulesWithSymbols = mHighlightingRules; + + mMultiLineCommentFormat.setForeground(mWidgetStyle->commentColor); + mMultiLineCommentFormat.setFontWeight(mWidgetStyle->commentWeight); + + mSymbolFormat.setForeground(mWidgetStyle->symbolFGColor); + mSymbolFormat.setBackground(mWidgetStyle->symbolBGColor); + mSymbolFormat.setFontWeight(mWidgetStyle->symbolWeight); + + // We use negative lookbehind assertion `(?= 0) { + QRegularExpressionMatch match = mCommentEndExpression.match(text, startIndex); + const int endIndex = match.capturedStart(); + int commentLength = 0; + if (endIndex == -1) { + setCurrentBlockState(1); + commentLength = text.length() - startIndex; + } else { + commentLength = endIndex - startIndex + + match.capturedLength(); + } + setFormat(startIndex, commentLength, mMultiLineCommentFormat); + startIndex = text.indexOf(mCommentStartExpression, startIndex + commentLength); + } +} + +void Highlighter::applyFormat(HighlightingRule &rule) +{ + switch (rule.ruleRole) { + case RuleRole::Keyword: + rule.format = mKeywordFormat; + break; + case RuleRole::Class: + rule.format = mClassFormat; + break; + case RuleRole::Comment: + rule.format = mSingleLineCommentFormat; + break; + case RuleRole::Quote: + rule.format = mQuotationFormat; + break; + case RuleRole::Symbol: + rule.format = mSymbolFormat; + break; + } +} + +CodeEditor::CodeEditor(QWidget *parent) : + QPlainTextEdit(parent), + mWidgetStyle(new CodeEditorStyle(defaultStyleLight)) +{ + mLineNumberArea = new LineNumberArea(this); + mHighlighter = new Highlighter(document(), mWidgetStyle); + mErrorPosition = -1; + + QFont font("Monospace"); + font.setStyleHint(QFont::TypeWriter); + setFont(font); + mLineNumberArea->setFont(font); + + // set widget coloring by overriding widget style sheet + setObjectName("CodeEditor"); + setStyleSheet(generateStyleString()); + +#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) + auto *copyText = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_C),this); + auto *allText = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key_A),this); +#else + const auto *copyText = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_C),this); + const auto *allText = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_A),this); +#endif + + connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateLineNumberAreaWidth(int))); + connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateLineNumberArea(QRect,int))); + connect(copyText, SIGNAL(activated()), this, SLOT(copy())); + connect(allText, SIGNAL(activated()), this, SLOT(selectAll())); + + updateLineNumberAreaWidth(0); +} + +CodeEditor::~CodeEditor() +{ + // NOTE: not a Qt Object - delete manually + delete mWidgetStyle; +} + +static int getPos(const QString &fileData, int lineNumber) +{ + if (lineNumber <= 1) + return 0; + for (int pos = 0, line = 1; pos < fileData.size(); ++pos) { + if (fileData[pos] != '\n') + continue; + ++line; + if (line >= lineNumber) + return pos + 1; + } + return fileData.size(); +} + +void CodeEditor::setStyle(const CodeEditorStyle& newStyle) +{ + *mWidgetStyle = newStyle; + // apply new styling + setStyleSheet(generateStyleString()); + mHighlighter->setStyle(newStyle); + mHighlighter->rehighlight(); + highlightErrorLine(); +} + +void CodeEditor::setError(const QString &code, int errorLine, const QStringList &symbols) +{ + mHighlighter->setSymbols(symbols); + + setPlainText(code); + + mErrorPosition = getPos(code, errorLine); + QTextCursor tc = textCursor(); + tc.setPosition(mErrorPosition); + setTextCursor(tc); + centerCursor(); + + highlightErrorLine(); +} + +void CodeEditor::setError(int errorLine, const QStringList &symbols) +{ + mHighlighter->setSymbols(symbols); + + mErrorPosition = getPos(toPlainText(), errorLine); + QTextCursor tc = textCursor(); + tc.setPosition(mErrorPosition); + setTextCursor(tc); + centerCursor(); + + highlightErrorLine(); +} + +int CodeEditor::lineNumberAreaWidth() +{ + int digits = 1; + int max = qMax(1, blockCount()); + while (max >= 10) { + max /= 10; + ++digits; + } + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + const int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits; +#else + const int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits; +#endif + return space; +} + +void CodeEditor::updateLineNumberAreaWidth(int /* newBlockCount */) +{ + setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); +} + +void CodeEditor::updateLineNumberArea(const QRect &rect, int dy) +{ + if (dy) + mLineNumberArea->scroll(0, dy); + else + mLineNumberArea->update(0, rect.y(), mLineNumberArea->width(), rect.height()); + + if (rect.contains(viewport()->rect())) + updateLineNumberAreaWidth(0); +} + +void CodeEditor::resizeEvent(QResizeEvent *event) +{ + QPlainTextEdit::resizeEvent(event); + QRect cr = contentsRect(); + mLineNumberArea->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height())); +} + +void CodeEditor::highlightErrorLine() +{ + QList extraSelections; + + QTextEdit::ExtraSelection selection; + + selection.format.setBackground(mWidgetStyle->highlightBGColor); + selection.format.setProperty(QTextFormat::FullWidthSelection, true); + selection.cursor = QTextCursor(document()); + if (mErrorPosition >= 0) { + selection.cursor.setPosition(mErrorPosition); + } else { + selection.cursor.setPosition(0); + } + selection.cursor.clearSelection(); + extraSelections.append(selection); + + setExtraSelections(extraSelections); +} + +void CodeEditor::lineNumberAreaPaintEvent(const QPaintEvent *event) +{ + QPainter painter(mLineNumberArea); + painter.fillRect(event->rect(), mWidgetStyle->lineNumBGColor); + + QTextBlock block = firstVisibleBlock(); + int blockNumber = block.blockNumber(); + int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top(); + int bottom = top + (int) blockBoundingRect(block).height(); + + while (block.isValid() && top <= event->rect().bottom()) { + if (block.isVisible() && bottom >= event->rect().top()) { + QString number = QString::number(blockNumber + 1); + painter.setPen(mWidgetStyle->lineNumFGColor); + painter.drawText(0, top, mLineNumberArea->width(), fontMetrics().height(), + Qt::AlignRight, number); + } + + block = block.next(); + top = bottom; + bottom = top + (int) blockBoundingRect(block).height(); + ++blockNumber; + } +} + +QString CodeEditor::generateStyleString() +{ + QString bgcolor = QString("background:rgb(%1,%2,%3);") + .arg(mWidgetStyle->widgetBGColor.red()) + .arg(mWidgetStyle->widgetBGColor.green()) + .arg(mWidgetStyle->widgetBGColor.blue()); + QString fgcolor = QString("color:rgb(%1,%2,%3);") + .arg(mWidgetStyle->widgetFGColor.red()) + .arg(mWidgetStyle->widgetFGColor.green()) + .arg(mWidgetStyle->widgetFGColor.blue()); + QString style = QString("%1 %2") + .arg(bgcolor) + .arg(fgcolor); + return style; +} diff --git a/cppcheck-2.14.0/gui/codeeditor.h b/cppcheck-2.14.0/gui/codeeditor.h new file mode 100644 index 00000000..7fb7a5f1 --- /dev/null +++ b/cppcheck-2.14.0/gui/codeeditor.h @@ -0,0 +1,165 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CODEEDITOR_H +#define CODEEDITOR_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class CodeEditorStyle; +class QPaintEvent; +class QRect; +class QResizeEvent; +class QTextDocument; + +class Highlighter : public QSyntaxHighlighter { + Q_OBJECT + +public: + explicit Highlighter(QTextDocument *parent, + CodeEditorStyle *widgetStyle); + + void setSymbols(const QStringList &symbols); + + void setStyle(const CodeEditorStyle &newStyle); + +protected: + void highlightBlock(const QString &text) override; + +private: + enum RuleRole { + Keyword = 1, + Class = 2, + Comment = 3, + Quote = 4, + Symbol = 5 + }; + struct HighlightingRule { + QRegularExpression pattern; + QTextCharFormat format; + RuleRole ruleRole; + }; + + void applyFormat(HighlightingRule &rule); + + QVector mHighlightingRules; + QVector mHighlightingRulesWithSymbols; + + QRegularExpression mCommentStartExpression; + QRegularExpression mCommentEndExpression; + + QTextCharFormat mKeywordFormat; + QTextCharFormat mClassFormat; + QTextCharFormat mSingleLineCommentFormat; + QTextCharFormat mMultiLineCommentFormat; + QTextCharFormat mQuotationFormat; + QTextCharFormat mSymbolFormat; + + CodeEditorStyle *mWidgetStyle; +}; + +class CodeEditor : public QPlainTextEdit { + Q_OBJECT + +public: + explicit CodeEditor(QWidget *parent); + CodeEditor(const CodeEditor &) = delete; + CodeEditor &operator=(const CodeEditor &) = delete; + ~CodeEditor() override; + + void lineNumberAreaPaintEvent(const QPaintEvent *event); + int lineNumberAreaWidth(); + void setStyle(const CodeEditorStyle& newStyle); + + /** + * Set source code to show, goto error line and highlight that line. + * \param code The source code. + * \param errorLine line number + * \param symbols the related symbols, these are marked + */ + void setError(const QString &code, int errorLine, const QStringList &symbols); + + /** + * Goto another error in existing source file + * \param errorLine line number + * \param symbols the related symbols, these are marked + */ + void setError(int errorLine, const QStringList &symbols); + + void setFileName(const QString &fileName) { + mFileName = fileName; + } + + const QString& getFileName() const { + return mFileName; + } + + void clear() { + mFileName.clear(); + setPlainText(QString()); + } + +protected: + void resizeEvent(QResizeEvent *event) override; + +private slots: + void updateLineNumberAreaWidth(int newBlockCount); + void highlightErrorLine(); + void updateLineNumberArea(const QRect & /*rect*/, int /*dy*/); + +private: + QString generateStyleString(); + +private: + QWidget *mLineNumberArea; + Highlighter *mHighlighter; + CodeEditorStyle *mWidgetStyle; + int mErrorPosition; + QString mFileName; +}; + + +class LineNumberArea : public QWidget { +public: + explicit LineNumberArea(CodeEditor *editor) : QWidget(editor) { + mCodeEditor = editor; + } + + QSize sizeHint() const override { + return QSize(mCodeEditor->lineNumberAreaWidth(), 0); + } + +protected: + void paintEvent(QPaintEvent *event) override { + mCodeEditor->lineNumberAreaPaintEvent(event); + } + +private: + CodeEditor *mCodeEditor; +}; + +#endif // CODEEDITOR_H diff --git a/cppcheck-2.14.0/gui/codeeditorstyle.cpp b/cppcheck-2.14.0/gui/codeeditorstyle.cpp new file mode 100644 index 00000000..e99ce2f6 --- /dev/null +++ b/cppcheck-2.14.0/gui/codeeditorstyle.cpp @@ -0,0 +1,232 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "codeeditorstyle.h" + +#include +#include +#include + +CodeEditorStyle::CodeEditorStyle( + // cppcheck-suppress naming-varname - TODO: fix this + QColor CtrlFGColor, QColor CtrlBGColor, + // cppcheck-suppress naming-varname - TODO: fix this + QColor HiLiBGColor, + // cppcheck-suppress naming-varname - TODO: fix this + QColor LnNumFGColor, QColor LnNumBGColor, + // cppcheck-suppress naming-varname - TODO: fix this + QColor KeyWdFGColor, QFont::Weight KeyWdWeight, + // cppcheck-suppress naming-varname - TODO: fix this + QColor ClsFGColor, QFont::Weight ClsWeight, + // cppcheck-suppress naming-varname - TODO: fix this + QColor QteFGColor, QFont::Weight QteWeight, + // cppcheck-suppress naming-varname - TODO: fix this + QColor CmtFGColor, QFont::Weight CmtWeight, + // cppcheck-suppress naming-varname - TODO: fix this + QColor SymbFGColor, QColor SymbBGColor, + // cppcheck-suppress naming-varname - TODO: fix this + QFont::Weight SymbWeight) : + widgetFGColor(CtrlFGColor), + widgetBGColor(CtrlBGColor), + highlightBGColor(HiLiBGColor), + lineNumFGColor(LnNumFGColor), + lineNumBGColor(LnNumBGColor), + keywordColor(KeyWdFGColor), + keywordWeight(KeyWdWeight), + classColor(ClsFGColor), + classWeight(ClsWeight), + quoteColor(QteFGColor), + quoteWeight(QteWeight), + commentColor(CmtFGColor), + commentWeight(CmtWeight), + symbolFGColor(SymbFGColor), + symbolBGColor(SymbBGColor), + symbolWeight(SymbWeight) +{} + +bool CodeEditorStyle::operator==(const CodeEditorStyle& rhs) const +{ + if (mSystemTheme != rhs.mSystemTheme) return false; + if (widgetFGColor != rhs.widgetFGColor) return false; + if (widgetBGColor != rhs.widgetBGColor) return false; + if (highlightBGColor != rhs.highlightBGColor) return false; + if (lineNumFGColor != rhs.lineNumFGColor) return false; + if (lineNumBGColor != rhs.lineNumBGColor) return false; + if (keywordColor != rhs.keywordColor) return false; + if (keywordWeight != rhs.keywordWeight) return false; + if (classColor != rhs.classColor) return false; + if (classWeight != rhs.classWeight) return false; + if (quoteColor != rhs.quoteColor) return false; + if (quoteWeight != rhs.quoteWeight) return false; + if (commentColor != rhs.commentColor) return false; + if (commentWeight != rhs.commentWeight) return false; + if (symbolFGColor != rhs.symbolFGColor) return false; + if (symbolBGColor != rhs.symbolBGColor) return false; + if (symbolWeight != rhs.symbolWeight) return false; + return true; +} + +bool CodeEditorStyle::operator!=(const CodeEditorStyle& rhs) const +{ + return !(*this == rhs); +} + +CodeEditorStyle CodeEditorStyle::getSystemTheme() +{ + CodeEditorStyle theStyle(defaultStyleLight); + theStyle.mSystemTheme = true; + return theStyle; +} + +CodeEditorStyle CodeEditorStyle::loadSettings(QSettings *settings) +{ + CodeEditorStyle theStyle(CodeEditorStyle::getSystemTheme()); + if (!settings) + return theStyle; + + if (!settings->childGroups().contains(SETTINGS_STYLE_GROUP)) + return theStyle; + + // style section exists - load values + settings->beginGroup(SETTINGS_STYLE_GROUP); + QString type = settings->value( + SETTINGS_STYLE_TYPE, + QVariant(SETTINGS_STYLE_TYPE_LIGHT) + ).toString(); + if (type == SETTINGS_STYLE_TYPE_LIGHT) { + settings->endGroup(); + return theStyle; + } + if (type == SETTINGS_STYLE_TYPE_DARK) { + theStyle = defaultStyleDark; + settings->endGroup(); + return theStyle; + } + if (type == SETTINGS_STYLE_TYPE_CUSTOM) { + theStyle.widgetFGColor = settings->value( + SETTINGS_STYLE_WIDGETFG, + QVariant(defaultStyleLight.widgetFGColor)).value(); + theStyle.widgetBGColor = settings->value( + SETTINGS_STYLE_WIDGETBG, + QVariant(defaultStyleLight.widgetBGColor)).value(); + theStyle.highlightBGColor = settings->value( + SETTINGS_STYLE_HILIFG, + QVariant(defaultStyleLight.highlightBGColor)).value(); + theStyle.lineNumFGColor = settings->value( + SETTINGS_STYLE_LINENUMFG, + QVariant(defaultStyleLight.lineNumFGColor)).value(); + theStyle.lineNumBGColor = settings->value( + SETTINGS_STYLE_LINENUMBG, + QVariant(defaultStyleLight.lineNumBGColor)).value(); + theStyle.keywordColor = settings->value( + SETTINGS_STYLE_KEYWORDFG, + QVariant(defaultStyleLight.keywordColor)).value(); + QVariant defKeyWWt(static_cast(defaultStyleLight.keywordWeight)); + theStyle.keywordWeight = static_cast( + settings->value(SETTINGS_STYLE_KEYWORDWT, defKeyWWt).toInt()); + theStyle.classColor = settings->value( + SETTINGS_STYLE_CLASSFG, + QVariant(defaultStyleLight.classColor)).value(); + QVariant defClsWt(static_cast(defaultStyleLight.classWeight)); + theStyle.classWeight = static_cast( + settings->value(SETTINGS_STYLE_CLASSWT, defClsWt).toInt()); + theStyle.quoteColor = settings->value( + SETTINGS_STYLE_QUOTEFG, + QVariant(defaultStyleLight.quoteColor)).value(); + QVariant defQteWt(static_cast(defaultStyleLight.quoteWeight)); + theStyle.quoteWeight = static_cast( + settings->value(SETTINGS_STYLE_QUOTEWT, defQteWt).toInt()); + theStyle.commentColor = settings->value( + SETTINGS_STYLE_COMMENTFG, + QVariant(defaultStyleLight.commentColor)).value(); + QVariant defCmtWt(static_cast(defaultStyleLight.commentWeight)); + theStyle.commentWeight = static_cast( + settings->value(SETTINGS_STYLE_COMMENTWT, defCmtWt).toInt()); + theStyle.symbolFGColor = settings->value( + SETTINGS_STYLE_SYMBOLFG, + QVariant(defaultStyleLight.symbolFGColor)).value(); + theStyle.symbolBGColor = settings->value( + SETTINGS_STYLE_SYMBOLBG, + QVariant(defaultStyleLight.symbolBGColor)).value(); + QVariant defSymWt(static_cast(defaultStyleLight.symbolWeight)); + theStyle.symbolWeight = static_cast( + settings->value(SETTINGS_STYLE_SYMBOLWT, defSymWt).toInt()); + } + settings->endGroup(); + return theStyle; +} + +void CodeEditorStyle::saveSettings(QSettings *settings, + const CodeEditorStyle& theStyle) +{ + if (!settings) + return; + + if (settings->childGroups().contains(SETTINGS_STYLE_GROUP)) { + settings->remove(SETTINGS_STYLE_GROUP); + if (theStyle.isSystemTheme()) + return; + } + + settings->beginGroup(SETTINGS_STYLE_GROUP); + const bool isDefaultLight = (defaultStyleLight == theStyle); + const bool isDefaultDark = (defaultStyleDark == theStyle); + if (isDefaultLight && !isDefaultDark) { + settings->setValue(SETTINGS_STYLE_TYPE, + SETTINGS_STYLE_TYPE_LIGHT); + } else if (!isDefaultLight && isDefaultDark) { + settings->setValue(SETTINGS_STYLE_TYPE, + SETTINGS_STYLE_TYPE_DARK); + } else { + settings->setValue(SETTINGS_STYLE_TYPE, + SETTINGS_STYLE_TYPE_CUSTOM); + settings->setValue(SETTINGS_STYLE_WIDGETFG, + QVariant(theStyle.widgetFGColor)); + settings->setValue(SETTINGS_STYLE_WIDGETBG, + QVariant(theStyle.widgetBGColor)); + settings->setValue(SETTINGS_STYLE_HILIFG, + QVariant(theStyle.highlightBGColor)); + settings->setValue(SETTINGS_STYLE_LINENUMFG, + QVariant(theStyle.lineNumFGColor)); + settings->setValue(SETTINGS_STYLE_LINENUMBG, + QVariant(theStyle.lineNumBGColor)); + settings->setValue(SETTINGS_STYLE_KEYWORDFG, + QVariant(theStyle.keywordColor)); + settings->setValue(SETTINGS_STYLE_KEYWORDWT, + QVariant(static_cast(theStyle.keywordWeight))); + settings->setValue(SETTINGS_STYLE_CLASSFG, + QVariant(theStyle.classColor)); + settings->setValue(SETTINGS_STYLE_CLASSWT, + QVariant(static_cast(theStyle.classWeight))); + settings->setValue(SETTINGS_STYLE_QUOTEFG, + QVariant(theStyle.quoteColor)); + settings->setValue(SETTINGS_STYLE_QUOTEWT, + QVariant(static_cast(theStyle.quoteWeight))); + settings->setValue(SETTINGS_STYLE_COMMENTFG, + QVariant(theStyle.commentColor)); + settings->setValue(SETTINGS_STYLE_COMMENTWT, + QVariant(static_cast(theStyle.commentWeight))); + settings->setValue(SETTINGS_STYLE_SYMBOLFG, + QVariant(theStyle.symbolFGColor)); + settings->setValue(SETTINGS_STYLE_SYMBOLBG, + QVariant(theStyle.symbolBGColor)); + settings->setValue(SETTINGS_STYLE_SYMBOLWT, + QVariant(static_cast(theStyle.symbolWeight))); + } + settings->endGroup(); +} diff --git a/cppcheck-2.14.0/gui/codeeditorstyle.h b/cppcheck-2.14.0/gui/codeeditorstyle.h new file mode 100644 index 00000000..a700aa81 --- /dev/null +++ b/cppcheck-2.14.0/gui/codeeditorstyle.h @@ -0,0 +1,128 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CODEEDITORSTYLE_H +#define CODEEDITORSTYLE_H + +#include +#include +#include +#include + +const QString SETTINGS_STYLE_GROUP("EditorStyle"); +const QString SETTINGS_STYLE_TYPE("StyleType"); +const QString SETTINGS_STYLE_TYPE_LIGHT("DefaultLight"); +const QString SETTINGS_STYLE_TYPE_DARK("DefaultDark"); +const QString SETTINGS_STYLE_TYPE_CUSTOM("Custom"); +const QString SETTINGS_STYLE_WIDGETFG("StyleWidgetFG"); +const QString SETTINGS_STYLE_WIDGETBG("StyleWidgetBG"); +const QString SETTINGS_STYLE_HILIFG("StyleHighlightFG"); +const QString SETTINGS_STYLE_LINENUMFG("StyleLineNumFG"); +const QString SETTINGS_STYLE_LINENUMBG("StyleLineNumBG"); +const QString SETTINGS_STYLE_KEYWORDFG("StyleKeywordFG"); +const QString SETTINGS_STYLE_KEYWORDWT("StyleKeywordWeight"); +const QString SETTINGS_STYLE_CLASSFG("StyleClassFG"); +const QString SETTINGS_STYLE_CLASSWT("StyleClassWeight"); +const QString SETTINGS_STYLE_QUOTEFG("StyleQuoteFG"); +const QString SETTINGS_STYLE_QUOTEWT("StyleQuoteWeight"); +const QString SETTINGS_STYLE_COMMENTFG("StyleCommentFG"); +const QString SETTINGS_STYLE_COMMENTWT("StyleCommentWeight"); +const QString SETTINGS_STYLE_SYMBOLFG("StyleSymbolFG"); +const QString SETTINGS_STYLE_SYMBOLBG("StyleSymbolBG"); +const QString SETTINGS_STYLE_SYMBOLWT("StyleSymbolWeight"); + +class QSettings; + +class CodeEditorStyle { +public: + explicit CodeEditorStyle( + // cppcheck-suppress naming-varname - TODO: fix this + QColor CtrlFGColor, QColor CtrlBGColor, + // cppcheck-suppress naming-varname - TODO: fix this + QColor HiLiBGColor, + // cppcheck-suppress naming-varname - TODO: fix this + QColor LnNumFGColor, QColor LnNumBGColor, + // cppcheck-suppress naming-varname - TODO: fix this + QColor KeyWdFGColor, QFont::Weight KeyWdWeight, + // cppcheck-suppress naming-varname - TODO: fix this + QColor ClsFGColor, QFont::Weight ClsWeight, + // cppcheck-suppress naming-varname - TODO: fix this + QColor QteFGColor, QFont::Weight QteWeight, + // cppcheck-suppress naming-varname - TODO: fix this + QColor CmtFGColor, QFont::Weight CmtWeight, + // cppcheck-suppress naming-varname - TODO: fix this + QColor SymbFGColor, QColor SymbBGColor, + // cppcheck-suppress naming-varname - TODO: fix this + QFont::Weight SymbWeight); + + bool operator==(const CodeEditorStyle& rhs) const; + bool operator!=(const CodeEditorStyle& rhs) const; + + bool isSystemTheme() const { + return mSystemTheme; + } + + static CodeEditorStyle getSystemTheme(); + static CodeEditorStyle loadSettings(QSettings *settings); + static void saveSettings(QSettings *settings, const CodeEditorStyle& theStyle); + +public: + bool mSystemTheme{}; + QColor widgetFGColor; + QColor widgetBGColor; + QColor highlightBGColor; + QColor lineNumFGColor; + QColor lineNumBGColor; + QColor keywordColor; + QFont::Weight keywordWeight; + QColor classColor; + QFont::Weight classWeight; + QColor quoteColor; + QFont::Weight quoteWeight; + QColor commentColor; + QFont::Weight commentWeight; + QColor symbolFGColor; + QColor symbolBGColor; + QFont::Weight symbolWeight; +}; + +static const CodeEditorStyle defaultStyleLight( + /* editor FG/BG */ Qt::black, QColor(240, 240, 240), + /* highlight BG */ QColor(255, 220, 220), + /* line number FG/BG */ Qt::black, QColor(240, 240, 240), + /* keyword FG/Weight */ Qt::darkBlue, QFont::Bold, + /* class FG/Weight */ Qt::darkMagenta, QFont::Bold, + /* quote FG/Weight */ Qt::darkGreen, QFont::Normal, + /* comment FG/Weight */ Qt::gray, QFont::Normal, + /* Symbol FG/BG/Weight */ Qt::red, QColor(220, 220, 255), QFont::Normal + ); + +// Styling derived from Eclipse Color Theme - 'RecognEyes' +// http://www.eclipsecolorthemes.org/?view=theme&id=30 +static const CodeEditorStyle defaultStyleDark( + /* editor FG/BG */ QColor(218, 218, 218), QColor(16, 16, 32), + /* highlight BG */ QColor(64, 64, 64), + /* line number FG/BG */ QColor(43, 145, 175), QColor(16, 16, 32), + /* keyword FG/Weight */ QColor(0, 204, 204), QFont::Bold, + /* class FG/Weight */ QColor(218, 0, 218), QFont::Bold, + /* quote FG/Weight */ QColor(0, 204, 0), QFont::Normal, + /* comment FG/Weight */ QColor(180, 180, 180), QFont::Normal, + /* Symbol FG/BG/Weight */ QColor(218, 32, 32), QColor(32, 32, 108), QFont::Normal + ); + +#endif /* CODEEDITORSTYLE_H */ diff --git a/cppcheck-2.14.0/gui/codeeditstylecontrols.cpp b/cppcheck-2.14.0/gui/codeeditstylecontrols.cpp new file mode 100644 index 00000000..8d5c3ec9 --- /dev/null +++ b/cppcheck-2.14.0/gui/codeeditstylecontrols.cpp @@ -0,0 +1,126 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "codeeditstylecontrols.h" + +#include +#include +#include + +class QWidget; + +SelectColorButton::SelectColorButton(QWidget* parent) : + QPushButton(parent), + mColor(QColor(255, 255, 255)) +{ + updateColor(); + connect(this, SIGNAL(clicked()), this, SLOT(changeColor())); +} + +void SelectColorButton::updateColor() +{ + QString btnColorStyle = QString( + "background-color:rgb(%1,%2,%3);" + "border-style:outset;" + "border-width: 1px;") + .arg(mColor.red()) + .arg(mColor.green()) + .arg(mColor.blue()); + setObjectName("SelectColorButton"); + setStyleSheet(btnColorStyle); +} + +void SelectColorButton::changeColor() +{ + QColorDialog pDlg(mColor); + pDlg.setModal(true); + const int nResult = pDlg.exec(); + if (nResult == QDialog::Accepted) { + setColor(pDlg.selectedColor()); + emit colorChanged(mColor); + } +} + +void SelectColorButton::setColor(const QColor& color) +{ + mColor = color; + updateColor(); +} + +// cppcheck-suppress unusedFunction +const QColor& SelectColorButton::getColor() +{ + return mColor; +} + +SelectFontWeightCombo::SelectFontWeightCombo(QWidget* parent) : + QComboBox(parent) +{ + addItem(QObject::tr("Thin"), + QVariant(static_cast(QFont::Thin))); + addItem(QObject::tr("ExtraLight"), + QVariant(static_cast(QFont::ExtraLight))); + addItem(QObject::tr("Light"), + QVariant(static_cast(QFont::Light))); + addItem(QObject::tr("Normal"), + QVariant(static_cast(QFont::Normal))); + addItem(QObject::tr("Medium"), + QVariant(static_cast(QFont::Medium))); + addItem(QObject::tr("DemiBold"), + QVariant(static_cast(QFont::DemiBold))); + addItem(QObject::tr("Bold"), + QVariant(static_cast(QFont::Bold))); + addItem(QObject::tr("ExtraBold"), + QVariant(static_cast(QFont::ExtraBold))); + addItem(QObject::tr("Black"), + QVariant(static_cast(QFont::Black))); + updateWeight(); + connect(this, SIGNAL(currentIndexChanged(int)), + this, SLOT(changeWeight(int))); +} + +void SelectFontWeightCombo::updateWeight() +{ + const int nResult = findData(QVariant(static_cast(mWeight))); + + if (nResult != -1) { + setCurrentIndex(nResult); + } else { + setCurrentIndex(findData(static_cast(QFont::Normal))); + } +} + +void SelectFontWeightCombo::changeWeight(int index) +{ + if (index != -1) { + setWeight(static_cast(itemData(index).toInt())); + emit weightChanged(mWeight); + } +} + +void SelectFontWeightCombo::setWeight(QFont::Weight weight) +{ + mWeight = weight; + updateWeight(); +} + +// cppcheck-suppress unusedFunction +const QFont::Weight& SelectFontWeightCombo::getWeight() +{ + return mWeight; +} diff --git a/cppcheck-2.14.0/gui/codeeditstylecontrols.h b/cppcheck-2.14.0/gui/codeeditstylecontrols.h new file mode 100644 index 00000000..719d84d2 --- /dev/null +++ b/cppcheck-2.14.0/gui/codeeditstylecontrols.h @@ -0,0 +1,76 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// widget subclass methodology derived from here: +// https://stackoverflow.com/questions/18257281/qt-color-picker-widget/43871405#43871405 + +#ifndef CODEEDITORSTYLECONTROLS_H +#define CODEEDITORSTYLECONTROLS_H + +#include +#include +#include +#include +#include +#include + +class QWidget; + +class SelectColorButton : public QPushButton { + Q_OBJECT +public: + explicit SelectColorButton(QWidget* parent); + + void setColor(const QColor& color); + const QColor& getColor(); + +signals: + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void colorChanged(const QColor& newColor); + +public slots: + void updateColor(); + void changeColor(); + +private: + QColor mColor; +}; + + +class SelectFontWeightCombo : public QComboBox { + Q_OBJECT +public: + explicit SelectFontWeightCombo(QWidget* parent); + + void setWeight(QFont::Weight weight); + const QFont::Weight& getWeight(); + +signals: + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void weightChanged(QFont::Weight newWeight); + +public slots: + void updateWeight(); + void changeWeight(int index); + +private: + QFont::Weight mWeight = QFont::Normal; +}; + +#endif //CODEEDITORSTYLECONTROLS_H + diff --git a/cppcheck-2.14.0/gui/codeeditstyledialog.cpp b/cppcheck-2.14.0/gui/codeeditstyledialog.cpp new file mode 100644 index 00000000..cf2988da --- /dev/null +++ b/cppcheck-2.14.0/gui/codeeditstyledialog.cpp @@ -0,0 +1,357 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "codeeditstyledialog.h" + +#include "codeeditor.h" +#include "codeeditstylecontrols.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class QWidget; + +const QString StyleEditDialog::mSampleDocument( + "/*****\n" + "* Multiline Comment\n" + "*****/\n" + "#include \n" + "#include \n" + "\n" + "class fwdClass;\n" + "\n" + "int main(int argc, char *argv[])\n" + "{\n" + " QApplication a(argc, argv);\n" + " int nLife = 42;\n" + " w.show();\n" + " // single line comment\n" + " // line below is highlighted\n" + " fwdClass( nLife );\n" + " return a.exec();\n" + "}\n" + "\n" + "void class fwdClass( double dValue ) {\n" + " std::cout << \"Ipsum Lorem: \"\n" + " << nValue\n" + " << std::endl;\n" + "}\n"); + +const QStringList StyleEditDialog::mErrSymbolsList = ( + QStringList(QStringList() + << "nLife" + << "dValue" + << "nValue")); +const int StyleEditDialog::mErrLineNum = 16; + +StyleEditDialog::StyleEditDialog(const CodeEditorStyle& newStyle, + QWidget *parent /*= nullptr*/) : + QDialog(parent), + mStyleIncoming(newStyle), + mStyleOutgoing(newStyle) +{ + auto *vboxMain = new QVBoxLayout(this); + auto *hboxEdit = new QHBoxLayout(); + // Color/Weight controls + auto *flEditControls = new QFormLayout(); + mBtnWidgetColorFG = new SelectColorButton(this); + flEditControls->addRow(QObject::tr("Editor Foreground Color"), + mBtnWidgetColorFG); + mBtnWidgetColorBG = new SelectColorButton(this); + flEditControls->addRow(QObject::tr("Editor Background Color"), + mBtnWidgetColorBG); + mBtnHighlightBG = new SelectColorButton(this); + flEditControls->addRow(QObject::tr("Highlight Background Color"), + mBtnHighlightBG); + mBtnLineNumFG = new SelectColorButton(this); + flEditControls->addRow(QObject::tr("Line Number Foreground Color"), + mBtnLineNumFG); + mBtnLineNumBG = new SelectColorButton(this); + flEditControls->addRow(QObject::tr("Line Number Background Color"), + mBtnLineNumBG); + mBtnKeywordFG = new SelectColorButton(this); + flEditControls->addRow(QObject::tr("Keyword Foreground Color"), + mBtnKeywordFG); + mCBKeywordWeight = new SelectFontWeightCombo(this); + flEditControls->addRow(QObject::tr("Keyword Font Weight"), + mCBKeywordWeight); + mBtnClassFG = new SelectColorButton(this); + flEditControls->addRow(QObject::tr("Class Foreground Color"), + mBtnClassFG); + mCBClassWeight = new SelectFontWeightCombo(this); + flEditControls->addRow(QObject::tr("Class Font Weight"), + mCBClassWeight); + mBtnQuoteFG = new SelectColorButton(this); + flEditControls->addRow(QObject::tr("Quote Foreground Color"), + mBtnQuoteFG); + mCBQuoteWeight = new SelectFontWeightCombo(this); + flEditControls->addRow(QObject::tr("Quote Font Weight"), + mCBQuoteWeight); + mBtnCommentFG = new SelectColorButton(this); + flEditControls->addRow(QObject::tr("Comment Foreground Color"), + mBtnCommentFG); + mCBCommentWeight = new SelectFontWeightCombo(this); + flEditControls->addRow(QObject::tr("Comment Font Weight"), + mCBCommentWeight); + mBtnSymbolFG = new SelectColorButton(this); + flEditControls->addRow(QObject::tr("Symbol Foreground Color"), + mBtnSymbolFG); + mBtnSymbolBG = new SelectColorButton(this); + flEditControls->addRow(QObject::tr("Symbol Background Color"), + mBtnSymbolBG); + mCBSymbolWeight = new SelectFontWeightCombo(this); + flEditControls->addRow(QObject::tr("Symbol Font Weight"), + mCBSymbolWeight); + hboxEdit->addLayout(flEditControls); + // CodeEditor to display Style + mSampleEditor = new CodeEditor(this); + QFont sampleFont("Monospace"); + QFontMetrics fm(sampleFont); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) + mSampleEditor->setMinimumWidth(fm.horizontalAdvance(QString(40, 'W'))); +#else + mSampleEditor->setMinimumWidth(fm.width(QString(40, 'W'))); +#endif + // designate highlight, errors, and symbols + mSampleEditor->setError(mSampleDocument, mErrLineNum, mErrSymbolsList); + // End Controls + hboxEdit->addWidget(mSampleEditor); + vboxMain->addLayout(hboxEdit); + + // Default Controls + auto *hboxDefaultControls = new QHBoxLayout(); + mBtnDefaultLight = new QPushButton(QObject::tr("Set to Default Light"), + this); + mBtnDefaultDark = new QPushButton(QObject::tr("Set to Default Dark"), + this); + hboxDefaultControls->addStretch(1); + hboxDefaultControls->addWidget(mBtnDefaultLight); + hboxDefaultControls->addWidget(mBtnDefaultDark); + hboxDefaultControls->addStretch(1); + vboxMain->addLayout(hboxDefaultControls); + vboxMain->addStretch(2); + // dialog controls + auto *dBtnBox = new QDialogButtonBox( + QDialogButtonBox::Cancel | + QDialogButtonBox::Ok | + QDialogButtonBox::Reset); + vboxMain->addStretch(1); + vboxMain->addWidget(dBtnBox); + + // setup values for style controls + updateControls(); + updateStyle(); + + connect(dBtnBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(dBtnBox, SIGNAL(rejected()), this, SLOT(reject())); + connect(dBtnBox->button(QDialogButtonBox::Reset), SIGNAL(clicked()), + this, SLOT(resetStyle())); + connect(mBtnDefaultLight, SIGNAL(clicked()), + this, SLOT(setStyleDefaultLight())); + connect(mBtnDefaultDark, SIGNAL(clicked()), + this, SLOT(setStyleDefaultDark())); + connect(mBtnWidgetColorFG, SIGNAL(colorChanged(QColor)), + this, SLOT(colorChangedWidgetFG(QColor))); + connect(mBtnWidgetColorBG, SIGNAL(colorChanged(QColor)), + this, SLOT(colorChangedWidgetBG(QColor))); + connect(mBtnHighlightBG, SIGNAL(colorChanged(QColor)), + this, SLOT(colorChangedHighlightBG(QColor))); + connect(mBtnLineNumFG, SIGNAL(colorChanged(QColor)), + this, SLOT(colorChangedLineNumFG(QColor))); + connect(mBtnLineNumBG, SIGNAL(colorChanged(QColor)), + this, SLOT(colorChangedLineNumBG(QColor))); + connect(mBtnKeywordFG, SIGNAL(colorChanged(QColor)), + this, SLOT(colorChangedKeywordFG(QColor))); + connect(mCBKeywordWeight, SIGNAL(weightChanged(QFont::Weight)), + this, SLOT(weightChangedKeyword(QFont::Weight))); + connect(mBtnClassFG, SIGNAL(colorChanged(QColor)), + this, SLOT(colorChangedClassFG(QColor))); + connect(mCBClassWeight, SIGNAL(weightChanged(QFont::Weight)), + this, SLOT(weightChangedClass(QFont::Weight))); + connect(mBtnQuoteFG, SIGNAL(colorChanged(QColor)), + this, SLOT(colorChangedQuoteFG(QColor))); + connect(mCBQuoteWeight, SIGNAL(weightChanged(QFont::Weight)), + this, SLOT(weightChangedQuote(QFont::Weight))); + connect(mBtnCommentFG, SIGNAL(colorChanged(QColor)), + this, SLOT(colorChangedCommentFG(QColor))); + connect(mCBCommentWeight, SIGNAL(weightChanged(QFont::Weight)), + this, SLOT(weightChangedComment(QFont::Weight))); + connect(mBtnSymbolFG, SIGNAL(colorChanged(QColor)), + this, SLOT(colorChangedSymbolFG(QColor))); + connect(mBtnSymbolBG, SIGNAL(colorChanged(QColor)), + this, SLOT(colorChangedSymbolBG(QColor))); + connect(mCBSymbolWeight, SIGNAL(weightChanged(QFont::Weight)), + this, SLOT(weightChangedSymbol(QFont::Weight))); +} + +void StyleEditDialog::updateControls() +{ + mBtnWidgetColorFG->setColor(mStyleOutgoing.widgetFGColor); + mBtnWidgetColorBG->setColor(mStyleOutgoing.widgetBGColor); + mBtnHighlightBG->setColor(mStyleOutgoing.highlightBGColor); + mBtnLineNumFG->setColor(mStyleOutgoing.lineNumFGColor); + mBtnLineNumBG->setColor(mStyleOutgoing.lineNumBGColor); + mBtnKeywordFG->setColor(mStyleOutgoing.keywordColor); + mCBKeywordWeight->setWeight(mStyleOutgoing.keywordWeight); + mBtnClassFG->setColor(mStyleOutgoing.classColor); + mCBClassWeight->setWeight(mStyleOutgoing.classWeight); + mBtnQuoteFG->setColor(mStyleOutgoing.quoteColor); + mCBQuoteWeight->setWeight(mStyleOutgoing.quoteWeight); + mBtnCommentFG->setColor(mStyleOutgoing.commentColor); + mCBCommentWeight->setWeight(mStyleOutgoing.commentWeight); + mBtnSymbolFG->setColor(mStyleOutgoing.symbolFGColor); + mBtnSymbolBG->setColor(mStyleOutgoing.symbolBGColor); + mCBSymbolWeight->setWeight(mStyleOutgoing.symbolWeight); +} + +void StyleEditDialog::updateStyle() +{ + mBtnDefaultLight->setEnabled(mStyleOutgoing != defaultStyleLight); + mBtnDefaultDark->setEnabled(mStyleOutgoing != defaultStyleDark); + // set Editor Styling + mSampleEditor->setStyle(mStyleOutgoing); +} + +CodeEditorStyle StyleEditDialog::getStyle() +{ + return mStyleOutgoing; +} + +void StyleEditDialog::resetStyle() +{ + mStyleOutgoing = mStyleIncoming; + updateControls(); + updateStyle(); +} + +void StyleEditDialog::setStyleDefaultLight() +{ + mStyleOutgoing = defaultStyleLight; + updateControls(); + updateStyle(); +} + +void StyleEditDialog::setStyleDefaultDark() +{ + mStyleOutgoing = defaultStyleDark; + updateControls(); + updateStyle(); +} + +void StyleEditDialog::colorChangedWidgetFG(const QColor& newColor) +{ + mStyleOutgoing.widgetFGColor = newColor; + updateStyle(); +} + +void StyleEditDialog::colorChangedWidgetBG(const QColor& newColor) +{ + mStyleOutgoing.widgetBGColor = newColor; + updateStyle(); +} + +void StyleEditDialog::colorChangedHighlightBG(const QColor& newColor) +{ + mStyleOutgoing.highlightBGColor = newColor; + updateStyle(); +} + +void StyleEditDialog::colorChangedLineNumFG(const QColor& newColor) +{ + mStyleOutgoing.lineNumFGColor = newColor; + updateStyle(); +} + +void StyleEditDialog::colorChangedLineNumBG(const QColor& newColor) +{ + mStyleOutgoing.lineNumBGColor = newColor; + updateStyle(); +} + +void StyleEditDialog::colorChangedKeywordFG(const QColor& newColor) +{ + mStyleOutgoing.keywordColor = newColor; + updateStyle(); +} + +void StyleEditDialog::weightChangedKeyword(QFont::Weight newWeight) +{ + mStyleOutgoing.keywordWeight = newWeight; + updateStyle(); +} + +void StyleEditDialog::colorChangedClassFG(const QColor& newColor) +{ + mStyleOutgoing.classColor = newColor; + updateStyle(); +} + +void StyleEditDialog::weightChangedClass(QFont::Weight newWeight) +{ + mStyleOutgoing.classWeight = newWeight; + updateStyle(); +} + +void StyleEditDialog::colorChangedQuoteFG(const QColor& newColor) +{ + mStyleOutgoing.quoteColor = newColor; + updateStyle(); +} + +void StyleEditDialog::weightChangedQuote(QFont::Weight newWeight) +{ + mStyleOutgoing.quoteWeight = newWeight; + updateStyle(); +} + +void StyleEditDialog::colorChangedCommentFG(const QColor& newColor) +{ + mStyleOutgoing.commentColor = newColor; + updateStyle(); +} + +void StyleEditDialog::weightChangedComment(QFont::Weight newWeight) +{ + mStyleOutgoing.commentWeight = newWeight; + updateStyle(); +} + +void StyleEditDialog::colorChangedSymbolFG(const QColor& newColor) +{ + mStyleOutgoing.symbolFGColor = newColor; + updateStyle(); +} + +void StyleEditDialog::colorChangedSymbolBG(const QColor& newColor) +{ + mStyleOutgoing.symbolBGColor = newColor; + updateStyle(); +} + +void StyleEditDialog::weightChangedSymbol(QFont::Weight newWeight) +{ + mStyleOutgoing.symbolWeight = newWeight; + updateStyle(); +} diff --git a/cppcheck-2.14.0/gui/codeeditstyledialog.h b/cppcheck-2.14.0/gui/codeeditstyledialog.h new file mode 100644 index 00000000..331795d4 --- /dev/null +++ b/cppcheck-2.14.0/gui/codeeditstyledialog.h @@ -0,0 +1,105 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CODEEDITSTYLEDIALOG_H +#define CODEEDITSTYLEDIALOG_H + +#include "codeeditorstyle.h" + +#include +#include +#include +#include +#include + +class CodeEditor; +class SelectColorButton; +class SelectFontWeightCombo; +class QPushButton; +class QWidget; + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +class QStringList; +#endif + +class StyleEditDialog : public QDialog { + Q_OBJECT +public: + explicit StyleEditDialog(const CodeEditorStyle& newStyle, + QWidget *parent = nullptr); + + CodeEditorStyle getStyle(); + +private: + void updateControls(); + void updateStyle(); + +public slots: + void resetStyle(); + void setStyleDefaultLight(); + void setStyleDefaultDark(); + void colorChangedWidgetFG(const QColor& newColor); + void colorChangedWidgetBG(const QColor& newColor); + void colorChangedHighlightBG(const QColor& newColor); + void colorChangedLineNumFG(const QColor& newColor); + void colorChangedLineNumBG(const QColor& newColor); + void colorChangedKeywordFG(const QColor& newColor); + void weightChangedKeyword(QFont::Weight newWeight); + void colorChangedClassFG(const QColor& newColor); + void weightChangedClass(QFont::Weight newWeight); + void colorChangedQuoteFG(const QColor& newColor); + void weightChangedQuote(QFont::Weight newWeight); + void colorChangedCommentFG(const QColor& newColor); + void weightChangedComment(QFont::Weight newWeight); + void colorChangedSymbolFG(const QColor& newColor); + void colorChangedSymbolBG(const QColor& newColor); + void weightChangedSymbol(QFont::Weight newWeight); + +private: + CodeEditorStyle mStyleIncoming; + CodeEditorStyle mStyleOutgoing; + + CodeEditor *mSampleEditor; + + SelectColorButton *mBtnWidgetColorFG; + SelectColorButton *mBtnWidgetColorBG; + SelectColorButton *mBtnHighlightBG; + SelectColorButton *mBtnLineNumFG; + SelectColorButton *mBtnLineNumBG; + SelectColorButton *mBtnKeywordFG; + SelectFontWeightCombo *mCBKeywordWeight; + SelectColorButton *mBtnClassFG; + SelectFontWeightCombo *mCBClassWeight; + SelectColorButton *mBtnQuoteFG; + SelectFontWeightCombo *mCBQuoteWeight; + SelectColorButton *mBtnCommentFG; + SelectFontWeightCombo *mCBCommentWeight; + SelectColorButton *mBtnSymbolFG; + SelectColorButton *mBtnSymbolBG; + SelectFontWeightCombo *mCBSymbolWeight; + + QPushButton *mBtnDefaultLight; + QPushButton *mBtnDefaultDark; + + static const QString mSampleDocument; + static const QStringList mErrSymbolsList; + static const int mErrLineNum; +}; + +#endif //CODEEDITSTYLEDIALOG_H + diff --git a/cppcheck-2.14.0/gui/common.cpp b/cppcheck-2.14.0/gui/common.cpp new file mode 100644 index 00000000..abbd5666 --- /dev/null +++ b/cppcheck-2.14.0/gui/common.cpp @@ -0,0 +1,87 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "common.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +QString getPath(const QString &type) +{ + QSettings settings; + QString path = settings.value(type, QString()).toString(); + if (path.isEmpty()) { + // if not set, fallback to last check path hoping that it will be close enough + path = settings.value(SETTINGS_LAST_CHECK_PATH, QString()).toString(); + if (path.isEmpty()) + // if not set, return user's home directory as the best we can do for now + return QDir::homePath(); + } + return path; +} + +void setPath(const QString &type, const QString &value) +{ + QSettings settings; + settings.setValue(type, value); +} + +QString toFilterString(const QMap& filters, bool addAllSupported, bool addAll) +{ + QStringList entries; + + if (addAllSupported) { + entries << QCoreApplication::translate("toFilterString", "All supported files (%1)") + .arg(QStringList(filters.values()).join(" ")); + } + + if (addAll) { + entries << QCoreApplication::translate("toFilterString", "All files (%1)").arg("*.*"); + } + + // We're using the description of the filters as the map keys, the file + // name patterns are our values. The generated filter string list will + // thus be sorted alphabetically over the descriptions. + for (const auto& k: filters.keys()) { + entries << QString("%1 (%2)").arg(k).arg(filters.value(k)); + } + + return entries.join(";;"); +} + +QString getDataDir() +{ + QSettings settings; + const QString dataDir = settings.value("DATADIR", QString()).toString(); + if (!dataDir.isEmpty()) + return dataDir; + const QString appPath = QFileInfo(QCoreApplication::applicationFilePath()).canonicalPath(); + if (QFileInfo::exists(appPath + "/std.cfg")) + return appPath; + if (appPath.indexOf("/cppcheck/", 0, Qt::CaseInsensitive) > 0) + return appPath.left(appPath.indexOf("/cppcheck/", 0, Qt::CaseInsensitive) + 9); + return appPath; +} diff --git a/cppcheck-2.14.0/gui/common.h b/cppcheck-2.14.0/gui/common.h new file mode 100644 index 00000000..e98d260e --- /dev/null +++ b/cppcheck-2.14.0/gui/common.h @@ -0,0 +1,155 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef COMMON_H +#define COMMON_H + +#include +#include + +/// @addtogroup GUI +/// @{ + +#define CLANG_ANALYZER "clang-analyzer" +#define CLANG_TIDY "clang-tidy" + +/** + * QSetting value names + */ + +// Window/dialog sizes +#define SETTINGS_WINDOW_MAXIMIZED "Window maximized" +#define SETTINGS_WINDOW_WIDTH "Window width" +#define SETTINGS_WINDOW_HEIGHT "Window height" +#define SETTINGS_MAINWND_SPLITTER_STATE "Mainwindow/Vertical splitter state" +#define SETTINGS_CHECK_DIALOG_WIDTH "Check dialog width" +#define SETTINGS_CHECK_DIALOG_HEIGHT "Check dialog height" +#define SETTINGS_PROJECT_DIALOG_WIDTH "Project dialog width" +#define SETTINGS_PROJECT_DIALOG_HEIGHT "Project dialog height" + +// Main window settings +#define SETTINGS_RESULT_COLUMN_WIDTH "Result column %1 width" +#define SETTINGS_TOOLBARS_MAIN_SHOW "Toolbars/ShowStandard" +#define SETTINGS_TOOLBARS_VIEW_SHOW "Toolbars/ShowView" +#define SETTINGS_TOOLBARS_FILTER_SHOW "Toolbars/ShowFilter" + +// Show * states +#define SETTINGS_SHOW_STYLE "Show style" +#define SETTINGS_SHOW_ERRORS "Show errors" +#define SETTINGS_SHOW_WARNINGS "Show warnings" +#define SETTINGS_SHOW_PERFORMANCE "Show performance" +#define SETTINGS_SHOW_INFORMATION "Show information" +#define SETTINGS_SHOW_PORTABILITY "Show portability" + +// Standards support +#define SETTINGS_STD_CPP "Standard CPP" +#define SETTINGS_STD_C "Standard C" + +// Language enforcement +#define SETTINGS_ENFORCED_LANGUAGE "Enforced language" + +// Other settings +#define SETTINGS_CHECK_FORCE "Check force" +#define SETTINGS_CHECK_THREADS "Check threads" +#define SETTINGS_SHOW_FULL_PATH "Show full path" +#define SETTINGS_SHOW_NO_ERRORS "Show no errors message" +#define SETTINGS_SHOW_DEBUG_WARNINGS "Show debug warnings" +#define SETTINGS_SAVE_ALL_ERRORS "Save all errors" +#define SETTINGS_SAVE_FULL_PATH "Save full path" +#define SETTINGS_APPLICATION_NAMES "Application names" +#define SETTINGS_APPLICATION_PATHS "Application paths" +#define SETTINGS_APPLICATION_PARAMS "Application parameters" +#define SETTINGS_APPLICATION_DEFAULT "Default Application" +#define SETTINGS_LANGUAGE "Application language" +#define SETTINGS_GLOBAL_INCLUDE_PATHS "Global include paths" +#define SETTINGS_PYTHON_PATH "Python path" +#define SETTINGS_MISRA_FILE "MISRA C 2012 file" +#define SETTINGS_CLANG_PATH "Clang path" +#define SETTINGS_VS_INCLUDE_PATHS "VS include paths" +#define SETTINGS_INLINE_SUPPRESSIONS "Inline suppressions" +#define SETTINGS_INCONCLUSIVE_ERRORS "Inconclusive errors" +#define SETTINGS_MRU_PROJECTS "MRU Projects" +#define SETTINGS_SHOW_ERROR_ID "Show error Id" +#define SETTINGS_SHOW_STATISTICS "Show statistics" +#define SETTINGS_OPEN_PROJECT "Open Project" +#define SETTINGS_CHECK_VERSION "Check Version" +#define SETTINGS_CHECK_FOR_UPDATES "Check for updates" + +// 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_APP_PATH "Last application path" + +#define SETTINGS_LAST_ANALYZE_FILES_FILTER "Last analyze files filter" + +/** + * @brief Obtains the path of specified type + * Returns the path of specified type if not empty. Otherwise returns last check + * path if valid or user's home directory. + * @param type Type of path to obtain + * @return Best path for provided type + */ +QString getPath(const QString &type); + +/** + * @brief Stores last used path of specified type + * Stores provided path as last used path for specified type. + * @param type Type of the path to store + * @param value Path to store + */ +void setPath(const QString &type, const QString &value); + +/** + * @brief Creates a string suitable for passing as the filter argument to + * methods like QFileDialog::getOpenFileName. + * @param filters A map of filter descriptions to the associated file name + * patterns. + * @param addAllSupported If set to true (the default), the function will + * include a filter entry containing all the file name patterns found in + * \p filters. This entry will be the first in the resulting filter string. + * @param addAll If set to true (the default), the function will + * include a filter entry displaying all files. This entry will be placed + * after the entry for \p addAllSupported files. + * + * Example usage: + * + * @code + * QMap filters; + * filters[tr("Supported images")] = "*.bmp *.jpg *.png"; + * filters[tr("Plain text")] = "*.txt"; + * + * const QString filterString = toFilterString(filters); + * + * // filterString contains "All supported files (*.txt *.bmp *.jpg *.png);;All files (*.*);;Plain text (*.txt);;Supported images (*.bmp *.jpg *.png)" + * @endcode + */ +QString toFilterString(const QMap& filters, bool addAllSupported=true, bool addAll=true); + +/** + * Get configured data dir. If not configured then it will try to determine that from exe path. + */ +QString getDataDir(); + +/// @} +#endif diff --git a/cppcheck-2.14.0/gui/compliancereportdialog.cpp b/cppcheck-2.14.0/gui/compliancereportdialog.cpp new file mode 100644 index 00000000..ff3812f3 --- /dev/null +++ b/cppcheck-2.14.0/gui/compliancereportdialog.cpp @@ -0,0 +1,236 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "compliancereportdialog.h" + +#include "ui_compliancereportdialog.h" + +#include "errortypes.h" +#include "filelist.h" +#include "filesettings.h" +#include "importproject.h" +#include "projectfile.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void addHeaders(const QString& file1, QSet &allFiles) { + if (allFiles.contains(file1)) + return; + QFile file(file1); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + return; + allFiles << file1; + const QRegularExpression re("^#include[ ]*\"([^\">]+)\".*"); + QTextStream in(&file); + QString line = in.readLine(); + while (!in.atEnd()) { + if (line.startsWith("#include")) { + const QRegularExpressionMatch match = re.match(line); + if (match.hasMatch()) { + QString hfile = match.captured(1); + if (file1.contains("/")) + hfile = file1.mid(0,file1.lastIndexOf("/") + 1) + hfile; + addHeaders(hfile, allFiles); + } + } + line = in.readLine(); + } +} + +static std::vector toStdStringList(const QStringList& from) { + std::vector ret; + std::transform(from.cbegin(), from.cend(), std::back_inserter(ret), [](const QString& e) { + return e.toStdString(); + }); + return ret; +} + +ComplianceReportDialog::ComplianceReportDialog(ProjectFile* projectFile, QString resultsFile) : + QDialog(nullptr), + mUI(new Ui::ComplianceReportDialog), + mProjectFile(projectFile), + mResultsFile(std::move(resultsFile)) +{ + mUI->setupUi(this); + mUI->mEditProjectName->setText(projectFile->getProjectName()); + connect(mUI->buttonBox, &QDialogButtonBox::clicked, this, &ComplianceReportDialog::buttonClicked); + mUI->mCodingStandard->clear(); + if (!projectFile->getCodingStandards().contains("misra-c-2023") && projectFile->getAddons().contains("misra")) + mUI->mCodingStandard->addItem("Misra C 2012"); + for (QString std: projectFile->getCodingStandards()) { + std[0] = std[0].toUpper(); + std = std.replace("-", " ").replace(" c ", " C ").replace(" cpp ", " C++ ").replace(" c++ ", " C++ "); + mUI->mCodingStandard->addItem(std); + } +} + +ComplianceReportDialog::~ComplianceReportDialog() +{ + delete mUI; +} + +void ComplianceReportDialog::buttonClicked(QAbstractButton* button) +{ + switch (mUI->buttonBox->standardButton(button)) { + case QDialogButtonBox::StandardButton::Save: + save(); + break; + case QDialogButtonBox::StandardButton::Close: + close(); + break; + default: + break; + }; +} + +void ComplianceReportDialog::save() +{ + const QString std(mUI->mCodingStandard->currentText().toLower().replace(" ", "-")); + + const QString outFile = QFileDialog::getSaveFileName(this, + tr("Compliance report"), + QDir::homePath() + "/" + std + "-compliance-report.html", + tr("HTML files (*.html)")); + if (outFile.isEmpty()) + return; + + close(); + + const QString& projectName = mUI->mEditProjectName->text(); + const QString& projectVersion = mUI->mEditProjectVersion->text(); + const bool files = mUI->mCheckFiles->isChecked(); + + if (projectName != mProjectFile->getProjectName()) { + mProjectFile->setProjectName(projectName); + mProjectFile->write(); + } + + QTemporaryFile tempFiles; + if (files && tempFiles.open()) { + QTextStream out(&tempFiles); + FileList fileList; + fileList.addPathList(mProjectFile->getCheckPaths()); + if (!mProjectFile->getImportProject().isEmpty()) { + QFileInfo inf(mProjectFile->getFilename()); + + QString prjfile; + if (QFileInfo(mProjectFile->getImportProject()).isAbsolute()) + prjfile = mProjectFile->getImportProject(); + else + prjfile = inf.canonicalPath() + '/' + mProjectFile->getImportProject(); + + ImportProject p; + try { + p.import(prjfile.toStdString()); + } catch (InternalError &e) { + QMessageBox msg(QMessageBox::Critical, + tr("Save compliance report"), + tr("Failed to import '%1' (%2), can not show files in compliance report").arg(prjfile).arg(QString::fromStdString(e.errorMessage)), + QMessageBox::Ok, + this); + msg.exec(); + return; + } + + p.ignorePaths(toStdStringList(mProjectFile->getExcludedPaths())); + + QDir dir(inf.absoluteDir()); + for (const FileSettings& fs: p.fileSettings) + fileList.addFile(dir.relativeFilePath(QString::fromStdString(fs.filename))); + } + + QSet allFiles; + for (const QString &sourcefile: fileList.getFileList()) + addHeaders(sourcefile, allFiles); + for (const QString& fileName: allFiles) { + QFile f(fileName); + if (f.open(QFile::ReadOnly)) { + QCryptographicHash hash(QCryptographicHash::Algorithm::Md5); + if (hash.addData(&f)) { + for (auto b: hash.result()) + out << QString::number((unsigned char)b,16); + out << " " << fileName << "\n"; + } + } + } + tempFiles.close(); + } + + QStringList suppressions; + for (const auto& suppression: mProjectFile->getSuppressions()) { + if (!suppression.errorId.empty()) + suppressions.append(QString::fromStdString(suppression.errorId)); + } + + QStringList args{"--project-name=" + projectName, + "--project-version=" + projectVersion, + "--output-file=" + outFile}; + if (!suppressions.isEmpty()) + args << "--suppressions=" + suppressions.join(","); + + args << ("--" + std); + + if (files) + args << "--files=" + tempFiles.fileName(); + args << mResultsFile; + + const QString appPath = QFileInfo(QCoreApplication::applicationFilePath()).canonicalPath(); + + QProcess process; +#ifdef Q_OS_WIN + process.start(appPath + "/compliance-report.exe", args); +#else + process.start(appPath + "/compliance-report", args); +#endif + process.waitForFinished(); + const QString output = process.readAll(); + if (!output.isEmpty()) { + QMessageBox msg(QMessageBox::Critical, + tr("Save compliance report"), + output, + QMessageBox::Ok, + this); + msg.exec(); + } +} diff --git a/cppcheck-2.14.0/gui/compliancereportdialog.h b/cppcheck-2.14.0/gui/compliancereportdialog.h new file mode 100644 index 00000000..ad3b79c5 --- /dev/null +++ b/cppcheck-2.14.0/gui/compliancereportdialog.h @@ -0,0 +1,52 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef COMPLIANCEREPORTDIALOG_H +#define COMPLIANCEREPORTDIALOG_H + +#include +#include +#include + +namespace Ui { + class ComplianceReportDialog; +} + +class ProjectFile; +class QAbstractButton; + +class ComplianceReportDialog final : public QDialog +{ + Q_OBJECT + +public: + explicit ComplianceReportDialog(ProjectFile* projectFile, QString resultsFile); + ~ComplianceReportDialog() final; + +private slots: + void buttonClicked(QAbstractButton* button); + +private: + void save(); + + Ui::ComplianceReportDialog *mUI; + ProjectFile* mProjectFile; + QString mResultsFile; +}; + +#endif // COMPLIANCEREPORTDIALOG_H diff --git a/cppcheck-2.14.0/gui/compliancereportdialog.ui b/cppcheck-2.14.0/gui/compliancereportdialog.ui new file mode 100644 index 00000000..b756ec7e --- /dev/null +++ b/cppcheck-2.14.0/gui/compliancereportdialog.ui @@ -0,0 +1,104 @@ + + + ComplianceReportDialog + + + + 0 + 0 + 403 + 199 + + + + Compliance Report + + + + + + + + Project version + + + + + + + Project name + + + + + + + + + + + + + Coding Standard + + + + + + + + Misra C + + + + + Cert C + + + + + Cert C++ + + + + + + + + + + List of files with md5 checksums + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close|QDialogButtonBox::Save + + + + + + + + diff --git a/cppcheck-2.14.0/gui/cppcheck-gui.desktop b/cppcheck-2.14.0/gui/cppcheck-gui.desktop new file mode 100644 index 00000000..21e611f4 --- /dev/null +++ b/cppcheck-2.14.0/gui/cppcheck-gui.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=Cppcheck +Comment=A tool for static C/C++ code analysis +Exec=cppcheck-gui +Icon=cppcheck-gui +Categories=Development;Debugger;Qt; diff --git a/cppcheck-2.14.0/gui/cppcheck-gui.png b/cppcheck-2.14.0/gui/cppcheck-gui.png new file mode 100644 index 0000000000000000000000000000000000000000..25d7aef5f31c1b84351df253ff310a63274d842c GIT binary patch literal 2543 zcmV?=cpjhPFPyw?>MD+H_#NVdpRgMPkbs+_3Sb*9 zK_D?PxujODJ~Cjyc1cQlMrO|ZNR}`EQ#NltBkR{6Q~y10-d8ed(yMBl7AeT5kvuA%IJ9l1_UAx3%+qYkq88hCO?%h{NnKDgi zGbn8!Cs7>%Q$bh^fz+(oTNW<-&eaJL&Zp9{Obk*ceBLB<a@VC5XOyrOzPDe>?nlAPzU6s_b*(ynr^f|JgG+@?c2`|xDcXn80rz`E`T|6KGO{z zKo~r!M&JojB;Z0wgu2H$D7X6c+oBu18=h8>4t&v~9|9_bk0Im7KjA2Zx^)NY2FJsj zdKfZv=`)Q|=ZhD&^W{wxonEu?hm#*T-- zj0Sasaqy;kLu?Ge$#gE-gmA%TyAd{jcG)I`b2i&qOO4*zjSU-)>&8yO+scnb8_gkh z%n)pigV-@cut7cNNq?|l!MA$HAK>q*1a-S@+<3yUZQDh4yYkTg6ew0}u6f9WD@vX^Z%?N>cjHCT_?6_it@H1rb;-4Jt zJay_@ddIi2QwT?(E)OR=PnhtM-tnc(3L$UaGK?X9G_Y8sj*eXgTJc`eoE(< zO$bM9w(D>6XQypK*l)9)^)F=Q%72`Mz`EgUy0O>b|H`7l($`>N^h-w}OrQQ<>Ka0J z3gJztZlV6WT1Ff zgir}Aj~8?l0@4mui@}@P2g$&KtOy|pN5QD^XZInP^Z#yMzQ>fXut=uT)ir%}2^!gj z(5zXa)UQ7@qY%Pfh0vwT5~)z3joU?)2O;g+&2$n1+s)Dqu7;;I1g!q{NNsP;nuEH% zvk3EG$vp%caR?I;?#q&UJxF9^JTt75ZbL{)dRjO50X(hU`tsK84Ih4&Zts;0`mDFo z8HxnF2+{Bth9el1wZvxMHpsBvf_hKrE(C(o3oF-A22Lv;2cX6qx;5~}aLC5+d zBsPJ|eK_-tLPi+hqX#^$j{42h(xtz;4We=5(fR{-!f9=TI%)M5${cqbM>O13YaE9Z zDq+d&otCVA#FCW{S<)wo4nDUL#z$xg&)*5qQL%t=-c<-{7RI`yCmm!vT%!XtMyHi4 zchGIDhr3F_6-br(mMpzj1z=cy|BWjnCf*3+71V*Fio-8ZJ+Zr&FuYFFA95K}5Q+2^ zjl3%PaSCn)mXxh# z9LL>=hhM7%nxi)oF&|4X8N<*Hx#6yA;2%h?e3nev;CldrCNuDhNj>?a=#7A_-Z%#- zRmqYCxBGVTi|;Z!KfH*7{#*%;fj?^@u0!gzQN6cM0gPQ_44cm(8GfrooW}vAARN9_ z6dneHC`-DH^8Up2P`POrPCN*f@(IepFO>&RLDKmHLlF#5t1v!;x@X5M@GXEz>ln}# zwbDL%7b6sYsW#XLiHfyk==2*sc7b9>7~i2goK}78hvY5Hbb5<#kG@VrEC~rsJ(gck z4}Pg|tkd^u>B{O+V|;l3391+Objmsy7a`?ps1D5Q0A}rAI$qKE{9TBKU#dJl*Y|4s zff>)%Ry?5YWfnEuPFaJo3Ib`;Sy4gar|fc3Zp36D?*#ig0~0nFHHNwKoVn&5gwAj_&Wo`67&$@@>xk4*pTHC_d- z1Na*)5HQsOUqbX!SMqZIo&5Y;EvZ=B==|OJ0Bf049|c}M=#lRu9X+|< + + + + + + + + + image/svg+xml + + + + + + + + c + ++ + + diff --git a/cppcheck-2.14.0/gui/cppcheck.ico b/cppcheck-2.14.0/gui/cppcheck.ico new file mode 100644 index 0000000000000000000000000000000000000000..3bd55f00d4bacb114db96fe8319860de9a793216 GIT binary patch literal 25214 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 literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/cppcheck_de.ts b/cppcheck-2.14.0/gui/cppcheck_de.ts new file mode 100644 index 00000000..a61ebf59 --- /dev/null +++ b/cppcheck-2.14.0/gui/cppcheck_de.ts @@ -0,0 +1,3042 @@ + + + + + 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-%1 Cppcheck team. + Copyright © 2007-2021 Cppcheck team. + Copyright © 2007-%1 Cppcheck-Team. + + + + This program is licensed under the terms +of the GNU General Public License version 3 + Dieses Programm ist unter den Bedingungen +der GNU General Public License Version 3 lizenziert + + + + Visit Cppcheck homepage at %1 + Besuchen Sie die Cppcheck-Homepage unter %1 + + + + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PCRE</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PicoJSON</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">TinyXML2</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Boost</li></ul></body></html> + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">pcre</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">picojson</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">tinyxml2</li></ul></body></html> + + + + + ApplicationDialog + + + Add an application + Anwendung hinzufügen + + + + Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. + +The following texts in parameters are replaced with appropriate values when application is executed: +(file) - Filename containing the error +(line) - Line number containing the error +(message) - Error message +(severity) - Error severity + +Example opening a file with Kate and make Kate scroll to the correct line: +Executable: kate +Parameters: -l(line) (file) + Hier können Sie Anwendungen hinzufügen, die Codedateien öffnen können. Geben Sie den Namen der Anwendung, deren ausführbare Datei und Kommandozeilenparameter für die Ausführung an. + +Die folgenden Texte in Parametern werden durch die passenden Werte ersetzt, wenn die Anwendung ausgeführt wird: +(file) - Name der Datei, die den Fehler enthält +(line) - Zeile, die den Fehler enthält +(message) - Fehlermeldung +(severity) - Schweregrad des Fehlers + +Beispiel: Öffnen einer Datei mit Kate, automatisch zur korrekten Zeile scrollen: +Ausführbare Datei: kate +Parameter: -l(line) (file) + + + + + &Name: + &Name: + + + + &Executable: + &Ausführbare Datei: + + + + &Parameters: + &Parameter: + + + + Browse + Suchen + + + + Executable files (*.exe);;All files(*.*) + Ausführbare Dateien (*.exe);;Alle Dateien(*.*) + + + + Select viewer application + Anzeigeanwendung auswählen + + + + Cppcheck + Cppcheck + + + + You must specify a name, a path and optionally parameters for the application! + Sie müssen einen Namen, einen Pfad und ggf. Parameter für die Anwendung angeben! + + + + ComplianceReportDialog + + + Compliance Report + + + + + Project name + + + + + Project version + + + + + Coding Standard + + + + + Misra C + + + + + Cert C + + + + + Cert C++ + + + + + List of files with md5 checksums + + + + + Compliance report + + + + + HTML files (*.html) + + + + + + Save compliance report + + + + + Failed to import '%1' (%2), can not show files in compliance report + + + + + 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 + + + + HelpDialog + + + Cppcheck GUI help + + + + + Contents + + + + + Index + + + + + Helpfile '%1' was not found + + + + + Cppcheck + Cppcheck + + + + LibraryAddFunctionDialog + + + Add function + Funktion hinzufügen + + + + Function name(s) + Funktionsname(n) + + + + Number of arguments + Anzahl Argumente + + + + LibraryDialog + + + Library Editor + Bibliothekseditor + + + + Open + Öffnen + + + + Save + Speichern + + + + Save as + Speichern unter + + + + Functions + Funktionen + + + + Sort + Sortiere + + + + Add + Hinzufügen + + + + Filter: + Filter: + + + + Comments + Kommentare + + + + noreturn + Zurückkehrend + + + + False + Ja + + + + True + Nein + + + + Unknown + Unbekannt + + + + return value must be used + Rückgabewert muss genutzt werden + + + + ignore function in leaks checking + Ignoriere Funktion in Speicherleck-Prüfung + + + + Arguments + Argumente + + + + Edit + Bearbeiten + + + + + Library files (*.cfg) + Bibliotheksdateien (*.cfg) + + + + Open library file + Bibliothek öffnen + + + + + + Cppcheck + Cppcheck + + + + Cannot open file %1. + Datei %1 kann nicht geöffnet werden. + + + + Failed to load %1. %2. + %1 kann nicht geladen werden. %2. + + + + Cannot save file %1. + Datei %1 kann nicht gespeichert werden. + + + + Save the library as + Speichere Bibliothek unter + + + + LibraryEditArgDialog + + + Edit argument + Argument bearbeiten + + + + <html><head/><body> +<p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> +<p>Typically, set this if the argument is a pointer, size, etc.</p> +<p>Example:</p> +<pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> +</body></html> + <html><head/><body> +<p>Ist ein boolscher Wert, beispielsweise das Ergebnis eines Vergleichsoperators, oder von '!' zulässig?</p> +<p>Diese Option wird typischerweise gesetzt, wenn das Argument ein Zeiger, eine Größe, etc. ist.</p> +<p>Beispiel:</p> +<pre> memcmp(x, y, i == 123); // Das letzte Argument sollte kein boolscher Wert sein.</pre> +</body></html> + + + + Not bool + Nicht boolsch + + + + <html><head/><body> +<p>Is a null parameter value allowed?</p> +<p>Typically this should be used on any pointer parameter that does not allow null.</p> +<p>Example:</p> +<pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> +</body></html> + <html><head/><body> +<p>Ist die Übergabe von Null zulässig?</p> +<p>Dies wird typischerweise für Funktionen mit Zeigern als Parameter genutzt, die nicht Null sein dürfen.</p> +<p>Beispiel:</p> +<pre> strcpy(x,y); // Weder x noch y dürfen ein Nullzeiger sein.</pre> +</body></html> + + + + Not null + Nicht Null + + + + Not uninit + Nicht uninitialisiert + + + + String + String + + + + Format string + Formatstring + + + + Min size of buffer + Minimale Puffergröße + + + + + Type + Typ + + + + + None + Keine + + + + + argvalue + Argumentwert + + + + + mul + Multiplikation + + + + + strlen + strlen + + + + + Arg + Argument 1 + + + + + Arg2 + Argument 2 + + + + and + und + + + + Valid values + Zulässige Werte + + + + MainWindow + + + + + + + + + + + + + + + + + Cppcheck + Cppcheck + + + + Standard + Standard + + + + &File + &Datei + + + + &View + &Ansicht + + + + &Toolbars + &Symbolleisten + + + + A&nalyze + A&nalysieren + + + + C++ standard + C++-Standard + + + + &C standard + &C-Standard + + + + &Edit + &Bearbeiten + + + + &License... + &Lizenz... + + + + A&uthors... + &Autoren... + + + + &About... + Ü&ber... + + + + &Files... + &Dateien... + + + + + Analyze files + Analysiere Dateien + + + + Ctrl+F + Strg+F + + + + &Directory... + &Verzeichnis... + + + + + Analyze directory + Analysiere Verzeichnis + + + + Ctrl+D + Strg+D + + + + Ctrl+R + Strg+R + + + + &Stop + &Stoppen + + + + + Stop analysis + Analyse abbrechen + + + + Esc + Esc + + + + &Save results to file... + &Ergebnisse in Datei speichern... + + + + Ctrl+S + Strg+S + + + + &Quit + &Beenden + + + + &Clear results + Ergebnisse &löschen + + + + &Preferences + &Einstellungen + + + + + Show errors + Zeige Fehler + + + + + Show warnings + Zeige Warnungen + + + + + Show performance warnings + Zeige Performance-Warnungen + + + + Show &hidden + Zeige &versteckte + + + + + Information + Information + + + + Show information messages + Zeige Informationsmeldungen + + + + Show portability warnings + Zeige Portabilitätswarnungen + + + + Show Cppcheck results + Zeige Cppcheck-Ergebnisse + + + + Clang + Clang + + + + Show Clang results + Zeige Clang-Ergebnisse + + + + &Filter + &Filter + + + + Filter results + Gefilterte Ergebnisse + + + + Windows 32-bit ANSI + Windows 32-bit, ANSI + + + + Windows 32-bit Unicode + Windows 32-bit, Unicode + + + + Unix 32-bit + Unix 32-bit + + + + Unix 64-bit + Unix 64-bit + + + + Windows 64-bit + Windows 64-bit + + + + &Print... + Drucken... + + + + Print the Current Report + Aktuellen Bericht ausdrucken + + + + Print Pre&view... + Druckvorschau + + + + Open a Print Preview Dialog for the Current Results + Druckvorschaudialog für aktuelle Ergebnisse öffnen + + + + Open library editor + Bibliothekseditor öffnen + + + + &Check all + Alle &auswählen + + + + Checking for updates + + + + + Hide + Verstecken + + + + Filter + Filter + + + + &Reanalyze modified files + Veränderte Dateien neu analysieren + + + + Reanal&yze all files + Alle Dateien erneut anal&ysieren + + + + Ctrl+Q + + + + + Style war&nings + Stilwar&nungen + + + + E&rrors + F&ehler + + + + &Uncheck all + Alle a&bwählen + + + + Collapse &all + Alle &reduzieren + + + + &Expand all + Alle &erweitern + + + + &Standard + &Standard + + + + Standard items + Standardeinträge + + + + Toolbar + Symbolleiste + + + + &Categories + &Kategorien + + + + Error categories + Fehler-Kategorien + + + + &Open XML... + Öffne &XML... + + + + Open P&roject File... + Pr&ojektdatei öffnen... + + + + Ctrl+Shift+O + + + + + Sh&ow Scratchpad... + &Zeige Schmierzettel... + + + + &New Project File... + &Neue Projektdatei... + + + + Ctrl+Shift+N + + + + + &Log View + &Loganzeige + + + + Log View + Loganzeige + + + + C&lose Project File + Projektdatei &schließen + + + + &Edit Project File... + Projektdatei &bearbeiten... + + + + &Statistics + &Statistik + + + + &Warnings + &Warnungen + + + + Per&formance warnings + Per&formance-Warnungen + + + + &Information + &Information + + + + &Portability + &Portabilität + + + + P&latforms + P&lattformen + + + + C++&11 + C++&11 + + + + C&99 + C&99 + + + + &Posix + Posix + + + + C&11 + C&11 + + + + &C89 + &C89 + + + + &C++03 + &C++03 + + + + &Library Editor... + &Bibliothekseditor + + + + &Auto-detect language + Sprache &automatisch erkennen + + + + &Enforce C++ + C++ &erzwingen + + + + E&nforce C + C e&rzwingen + + + + C++14 + C++14 + + + + Reanalyze and check library + Neu analysieren und Bibliothek prüfen + + + + Check configuration (defines, includes) + Prüfe Konfiguration (Definitionen, Includes) + + + + C++17 + C++17 + + + + C++20 + C++20 + + + + Compliance report... + + + + + &Contents + &Inhalte + + + + Categories + Kategorien + + + + + Show style warnings + Zeige Stilwarnungen + + + + Open the help contents + Öffnet die Hilfe-Inhalte + + + + F1 + F1 + + + + &Help + &Hilfe + + + + + Quick Filter: + Schnellfilter: + + + + Select configuration + Konfiguration wählen + + + + Found project file: %1 + +Do you want to load this project file instead? + Gefundene Projektdatei: %1 + +Möchten Sie stattdessen diese öffnen? + + + + File not found + Datei nicht gefunden + + + + Bad XML + Fehlerhaftes XML + + + + Missing attribute + Fehlendes Attribut + + + + Bad attribute value + Falscher Attributwert + + + + Duplicate platform type + Plattformtyp doppelt + + + + Platform type redefined + Plattformtyp neu definiert + + + + Duplicate define + + + + + Failed to load the selected library '%1'. +%2 + Laden der ausgewählten Bibliothek '%1' schlug fehl. +%2 + + + + File not found: '%1' + + + + + Failed to load/setup addon %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. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + +Analysis is aborted. + + + + + Failed to load %1 - %2 + +Analysis is aborted. + + + + + + %1 + +Analysis is aborted. + + + + + License + Lizenz + + + + Authors + Autoren + + + + Save the report file + Speichert die Berichtdatei + + + + + XML files (*.xml) + XML-Dateien (*.xml) + + + + There was a problem with loading the editor application settings. + +This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. + Beim Laden der Editor-Anwendungseinstellungen trat ein Problem auf. + +Dies wurde vermutlich durch einen Wechsel der Cppcheck-Version hervorgerufen. Bitte prüfen (und korrigieren) Sie die Einstellungen, andernfalls könnte die Editor-Anwendung nicht korrekt starten. + + + + You must close the project file before selecting new files or directories! + Sie müssen die Projektdatei schließen, bevor Sie neue Dateien oder Verzeichnisse auswählen! + + + + The library '%1' contains unknown elements: +%2 + Die Bibliothek '%1' enthält unbekannte Elemente: +%2 + + + + Unsupported format + Nicht unterstütztes Format + + + + Unknown element + Unbekanntes Element + + + + Unknown issue + Unbekannter Fehler + + + + + + + Error + Fehler + + + Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + Laden von %1 fehlgeschlagen. Ihre Cppcheck-Installation ist defekt. Sie können --data-dir=<Verzeichnis> als Kommandozeilenparameter verwenden, um anzugeben, wo die Datei sich befindet. Bitte beachten Sie, dass --data-dir in Installationsroutinen genutzt werden soll, und die GUI bei dessen Nutzung nicht startet, sondern die Einstellungen konfiguriert. + + + + Open the report file + Berichtdatei öffnen + + + + Text files (*.txt) + Textdateien (*.txt) + + + + CSV files (*.csv) + CSV-Dateien (*.csv) + + + + Project files (*.cppcheck);;All files(*.*) + Projektdateien (*.cppcheck);;Alle Dateien(*.*) + + + + Select Project File + Projektdatei auswählen + + + + + + + Project: + Projekt: + + + + No suitable files found to analyze! + Keine passenden Dateien für Analyse gefunden! + + + + C/C++ Source + C/C++-Quellcode + + + + Compile database + Compilerdatenbank + + + + Visual Studio + Visual Studio + + + + Borland C++ Builder 6 + Borland C++-Builder 6 + + + + Select files to analyze + Dateien für Analyse auswählen + + + + Select directory to analyze + Verzeichnis für Analyse auswählen + + + + Select the configuration that will be analyzed + Zu analysierende Konfiguration auswählen + + + + Found project files from the directory. + +Do you want to proceed analysis without using any of these project files? + Projektdateien im Verzeichnis gefunden. + +Wollen sie fortfahren, ohne diese Projektdateien zu nutzen? + + + + Current results will be cleared. + +Opening a new XML file will clear current results. +Do you want to proceed? + Aktuelle Ergebnisse werden gelöscht. + +Eine neue XML-Datei zu öffnen wird die aktuellen Ergebnisse löschen +Möchten sie fortfahren? + + + + Analyzer is running. + +Do you want to stop the analysis and exit Cppcheck? + Analyse läuft. + +Wollen sie die Analyse abbrechen und Cppcheck beenden? + + + + About + + + + + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) + XML-Dateien (*.xml);;Textdateien (*.txt);;CSV-Dateien (*.csv) + + + + Cannot generate a compliance report right now, an analysis must finish successfully. Try to reanalyze the code and ensure there are no critical errors. + + + + + Build dir '%1' does not exist, create it? + Erstellungsverzeichnis '%1' existiert nicht. Erstellen? + + + + To check the project using addons, you need a build directory. + + + + + Failed to open file + + + + + Unknown project file format + + + + + Failed to import project file + + + + + Failed to import '%1': %2 + +Analysis is stopped. + + + + + Failed to import '%1' (%2), analysis is stopped + + + + Failed to import '%1', analysis is stopped + Import von '%1' fehlgeschlagen; Analyse wurde abgebrochen. + + + + Project files (*.cppcheck) + Projektdateien (*.cppcheck) + + + + Select Project Filename + Projektnamen auswählen + + + + No project file loaded + Keine Projektdatei geladen + + + + The project file + +%1 + + could not be found! + +Do you want to remove the file from the recently used projects -list? + Die Projektdatei + +%1 + + konnte nicht gefunden werden! + +Möchten Sie die Datei von der Liste der zuletzt benutzten Projekte entfernen? + + + + Install + + + + + New version available: %1. %2 + + + + + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> This option is for installation scripts so they can configure the directory where + datafiles are located (translations, cfg). The GUI is not started when this option + is used. + + Cppcheck GUI. + + Syntax: + cppcheck-gui [OPTIONEN] [Dateien oder Pfade] + + Options: + -h, --help Gibt diese Hilfeinformationen aus + -p <file> Öffnet das angegebene Projekt und beginnt die Prüfung + -l <file> Öffnet die angegebene XML-Ergebnisdatei + -d <directory> Gibt das Verzeichnis an, das geprüft wurde, um das unter -l angegebene XML-Ergebnis zu erzeugen + -v, --version Zeigt Programmversion an + --data-dir=<directory> Gibt das Verzeichnis an, unter dem sich die Konfigurationsdateien für die GUI (Übersetzungen, Cfg) befinden. Die GUI startet bei Nutzung dieser Option nicht. + + + + + Cppcheck GUI - Command line parameters + Cppcheck GUI - Kommandozeilenparameter + + + + NewSuppressionDialog + + + New suppression + Neue Fehlerunterdrückung + + + + Error ID + Fehler-ID + + + + File name + Dateiname + + + + Line number + Zeilennummer + + + + Symbol name + Symbolname + + + + Edit suppression + Fehlerunterdrückung bearbeiten + + + + Platforms + + + Native + Nativ + + + + Unix 32-bit + Unix 32-bit + + + + Unix 64-bit + Unix 64-bit + + + + Windows 32-bit ANSI + Windows 32-bit, ANSI + + + + Windows 32-bit Unicode + Windows 32-bit, Unicode + + + + Windows 64-bit + Windows 64-bit + + + + ProjectFile + + + Project File + Projektdatei + + + + Paths and Defines + Pfade und Definitionen + + + + Import Project (Visual studio / compile database/ Borland C++ Builder 6) + Importiere Projekt (Visual Studio / Compile-Datenbank / Borland C++-Builder 6) + + + + Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int + Definitionen müssen mit einem Semikolon getrennt werden. Beispiel: DEF1;DEF2=5;DEF3=int + + + + Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. + Hinweis: Legen Sie eigene .cfg-Dateien in den Ordner der Projektdatei. Dann sollten sie oben sichtbar werden. + + + MISRA C 2012 + MISRA C 2012 + + + + MISRA rule texts + MISRA-Regeltexte + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + <html><head/><body><p>Text aus Anhang A &quot;Summary of guidelines&quot; aus der MISRA-C-2012-PDF in eine Textdatei einfügen.</p></body></html> + + + + ... + ... + + + + <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> + <html><head/><body><p>Sie haben die Auswahl:</p><p> * Alle Debug- und Release-Konfigurationen analysieren</p><p> * Nur die erste passende Debug-Konfiguration analysieren</p><p><br/></p></body></html> + + + + + Browse... + Durchsuchen... + + + + Analyze all Visual Studio configurations + Alle Visual-Studio-Konfigurationen analysieren + + + + Selected VS Configurations + + + + + Paths: + Pfade: + + + + + Add... + Hinzufügen... + + + + + + Edit + Bearbeiten + + + + + + + Remove + Entfernen + + + + Undefines: + Un-Definitionen: + + + + Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 + Un-Definitionen müssen Semikolon-getrennt sein. Beispiel: UNDEF1;UNDEF2;UNDEF3 + + + + Include Paths: + Includepfade: + + + + Up + Auf + + + + Down + Ab + + + + Platform + Plattform + + + + + Analysis + Analyse + + + + This is a workfolder that Cppcheck will use for various purposes. + + + + + Clang (experimental) + + + + + Check level + + + + + Normal -- meant for normal analysis in CI. Analysis should finish in reasonable time. + + + + + Exhaustive -- meant for nightly builds etc. Analysis time can be longer (10x slower than compilation is OK). + + + + + If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. + + + + + Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) + Check code in unused templates (slower and less accurate analysis) + Prüfe Code in ungenutzten Templates (langsamere und weniger genaue Analyse) + + + + Max CTU depth + Maximale CTU-Tiefe + + + + Max recursion in template instantiation + + + + + Warning options + Warnoptionen + + + + Root path: + Wurzelverzeichnis: + + + + Filepaths in warnings will be relative to this path + + + + + Warning tags (separated by semicolon) + Warnungs-Tags (Semikolon-getrennt) + + + + Cert C + + + + + CERT-INT35-C: int precision (if size equals precision, you can leave empty) + + + + + Misra C++ 2008 + + + + + Autosar + + + + + Bug hunting + + + + + External tools + Externe Werkzeuge + + + + Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) + Cppcheck-Arbeitsverzeichnis (Vollständige Programmanalyse, inkrementelle Analyse, Statistiken, etc.) + + + + Types and Functions + + + + + Libraries + Bibliotheken + + + + Parser + + + + + Cppcheck (built in) + + + + + Check that each class has a safe public interface + + + + + Limit analysis + + + + + Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) + + + + + If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. + + + + + Exclude source files + + + + + Exclude folder... + + + + + Exclude file... + + + + + Suppressions + Fehlerunterdrückungen + + + + Add + Hinzufügen + + + + + Addons + Add-Ons + + + + Note: Addons require <a href="https://www.python.org/">Python</a> being installed. + + + + + Y2038 + Y2038 + + + + Thread safety + Threadsicherheit + + + + Coding standards + Programmierstandards + + + + Misra C + + + + + 2012 + + + + + 2023 + + + + + Cert C++ + + + + + Bug hunting (Premium) + + + + + Clang analyzer + Clang-Analyzer + + + + Clang-tidy + Clang-Tidy + + + + Defines: + Definitionen: + + + + ProjectFileDialog + + + Project file: %1 + Projektdatei: %1 + + + + Select Cppcheck build dir + Wähle Cppcheck-Erstellungsverzeichnis + + + + Select include directory + Wähle Include-Verzeichnisse + + + + Select a directory to check + Wähle zu prüfendes Verzeichnis + + + + Clang-tidy (not found) + Clang-tidy (nicht gefunden) + + + + Visual Studio + Visual Studio + + + + Compile database + Compilerdatenbank + + + + Borland C++ Builder 6 + Borland C++-Builder 6 + + + + Import Project + Projekt importieren + + + + Select directory to ignore + Wähle zu ignorierendes Verzeichnis + + + + Source files + + + + + All files + + + + + Exclude file + + + + + Select MISRA rule texts file + Wähle MISRA-Regeltext-Datei + + + + MISRA rule texts file (%1) + MISRA-Regeltext-Datei + + + + QObject + + + Unknown language specified! + Unbekannte Sprache angegeben! + + + + Language file %1 not found! + Sprachdatei %1 nicht gefunden! + + + + Failed to load translation for language %1 from file %2 + Die Übersetzungen der Sprache %1 konnten nicht aus der Datei %2 geladen werden + + + + line %1: Unhandled element %2 + Zeile %1: Nicht behandeltes Element %2 + + + + line %1: Mandatory attribute '%2' missing in '%3' + + + + + (Not found) + (nicht gefunden) + + + + Thin + + + + + ExtraLight + + + + + Light + + + + + Normal + + + + + Medium + + + + + DemiBold + + + + + Bold + + + + + ExtraBold + + + + + Black + + + + + Editor Foreground Color + + + + + Editor Background Color + + + + + Highlight Background Color + + + + + Line Number Foreground Color + + + + + Line Number Background Color + + + + + Keyword Foreground Color + + + + + Keyword Font Weight + + + + + Class Foreground Color + Klassen-Vordergrundfarbe + + + + Class Font Weight + + + + + Quote Foreground Color + + + + + Quote Font Weight + + + + + Comment Foreground Color + + + + + Comment Font Weight + + + + + Symbol Foreground Color + + + + + Symbol Background Color + + + + + Symbol Font Weight + + + + + Set to Default Light + + + + + Set to Default Dark + + + + + QPlatformTheme + + + OK + OK + + + + Cancel + Abbrechen + + + + Close + Schließen + + + + Save + Speichern + + + + ResultsTree + + + File + Datei + + + + Severity + Schweregrad + + + + Line + Zeile + + + + Summary + Zusammenfassung + + + + Undefined file + Undefinierte Datei + + + + Copy + Kopieren + + + + Could not find file: + Kann Datei nicht finden: + + + + Please select the folder '%1' + Bitte wählen Sie den Ordner '%1' + + + + Select Directory '%1' + Wähle Verzeichnis '%1' + + + + Please select the directory where file is located. + Bitte wählen Sie das Verzeichnis, wo sich die Datei befindet + + + + debug + Debug + + + + note + Anmerkung + + + + Recheck + Erneut prüfen + + + + Hide + Verstecken + + + + Hide all with id + Verstecke alle mit gleicher ID + + + + Suppress selected id(s) + Ausgewählte ID(s) unterdrücken + + + + Open containing folder + Übergeordneten Ordner öffnen + + + + internal + + + + + + Tag + Tag + + + + No tag + Kein Tag + + + + + Cppcheck + Cppcheck + + + + No editor application configured. + +Configure the editor application for Cppcheck in preferences/Applications. + Keine Editor-Anwendung eingestellt. + +Konfigurieren Sie diese unter Einstellungen/Anwendungen. + + + + No default editor application selected. + +Please select the default editor application in preferences/Applications. + Keine Standard-Editor-Anwendung eingestellt. + + Bitte wählen Sie eine Standardanwendung unter Einstellungen/Anwendungen. + + + + Could not find the file! + Datei konnte nicht gefunden werden! + + + + Could not start %1 + +Please check the application path and parameters are correct. + %1 konnte nicht gestartet werden. + +Bitte überprüfen Sie ob der Pfad und die Parameter der Anwendung richtig eingestellt sind. + + + + Select Directory + Wähle Verzeichnis + + + + Id + Id + + + + Inconclusive + Unklar + + + + Since date + Seit Datum + + + + style + Stil + + + + error + Fehler + + + + warning + Warnung + + + + performance + Performance + + + + portability + Portabilität + + + + information + Information + + + + ResultsView + + + Print Report + Bericht drucken + + + + No errors found, nothing to print. + Keine Funde, nichts zu drucken. + + + + %p% (%1 of %2 files checked) + %p% (%1 von %2 Dateien geprüft) + + + + + Cppcheck + Cppcheck + + + + No errors found. + Keine Fehler gefunden. + + + + Errors were found, but they are configured to be hidden. +To toggle what kind of errors are shown, open view menu. + Es wurden Fehler gefunden, aber sie sind so konfiguriert, ausgeblendet zu werden. +Legen Sie unter dem Menü Ansicht fest, welche Arten von Fehlern angezeigt werden sollen. + + + + + Failed to read the report. + Lesen des Berichts fehlgeschlagen. + + + + XML format version 1 is no longer supported. + XML-Format-Version 1 wird nicht länger unterstützt. + + + + First included by + Zuerst inkludiert von + + + + Id + Id + + + + Bug hunting analysis is incomplete + + + + + Clear Log + Protokoll leeren + + + + Copy this Log entry + Diesen Protokolleintrag kopieren + + + + Copy complete Log + Gesamtes Protokoll kopieren + + + + Analysis was stopped + + + + + There was a critical error with id '%1' + + + + + when checking %1 + + + + + when checking a file + + + + + Analysis was aborted. + + + + + + Failed to save the report. + Der Bericht konnte nicht speichern werden. + + + + Results + Berichte + + + + Critical errors + + + + + Analysis Log + Analyseprotokoll + + + + Warning Details + Warnungs-Details + + + + ScratchPad + + + Scratchpad + Schmierzettel + + + + Copy or write some C/C++ code here: + Kopieren oder schreiben Sie C/C++-Code hierher: + + + + Optionally enter a filename (mainly for automatic language detection) and click on "Check": + Optional einen Dateinamen (hauptsächlich für automatische Spracherkennung) eingeben und auf "Prüfe" klicken: + + + + filename + Dateiname + + + + Check + Prüfe + + + + Settings + + + Preferences + Einstellungen + + + + General + Allgemein + + + + Add... + Hinzufügen... + + + + Number of threads: + Anzahl der Threads: + + + + Ideal count: + Ideale Anzahl: + + + + Force checking all #ifdef configurations + Erzwinge Prüfung aller #ifdef-Konfigurationen + + + + Show full path of files + Vollständigen Dateipfad anzeigen + + + + Show "No errors found" message when no errors found + "Keine Fehler gefunden"-Meldung anzeigen, wenn keine Fehler gefunden werden + + + + Display error Id in column "Id" + Zeige Meldungs-Id in Spalte "Id" + + + + Enable inline suppressions + Inline-Fehlerunterdrückung aktivieren + + + + Check for inconclusive errors also + Auch nach unklaren Fehlern suchen + + + + Show statistics on check completion + Zeige Statistiken nach Prüfungsabschluss + + + + Check for updates + + + + + Show internal warnings in log + Interne Warnungen im Log anzeigen + + + + Addons + Add-Ons + + + + Python binary (leave this empty to use python in the PATH) + Python-Binärdatei (Python aus PATH wird genutzt, wenn leer) + + + + + + ... + ... + + + + MISRA addon + MISRA-Addon + + + + MISRA rule texts file + MISRA-Regeltext-Datei + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + <html><head/><body><p>Text aus Anhang A &quot;Summary of guidelines&quot; aus der MISRA-C-2012-PDF in eine Textdatei einfügen.</p></body></html> + + + + Clang + Clang + + + + Clang path (leave empty to use system PATH) + Clang-Verzeichnis (PATH wird genutzt, wenn leer) + + + + Visual Studio headers + Visual-Studio-Header + + + + <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> + <html><head/><body><p>Pfade zu Visual-Studio-Headern, Semikolon-getrennt.</p><p>Sie können eine Visual-Studio-Kommandozeile öffnen, &quot;SET INCLUDE&quot; eingeben und dann die Pfade hier reinkopieren.</p></body></html> + + + + Code Editor + Code-Editor + + + + Code Editor Style + Code-Editor-Stil + + + + System Style + Systemstil + + + + Default Light Style + Heller Standardstil + + + + Default Dark Style + Dunkler Standardstil + + + + Custom + Benutzerdefiniert + + + + Remove + Entfernen + + + + Applications + Anwendungen + + + + + Edit... + Bearbeiten... + + + + Set as default + Als Standard festlegen + + + + Reports + Berichte + + + + Save all errors when creating report + Alle Fehler beim Erstellen von Berichten speichern + + + + Save full path to files in reports + Vollständigen Dateipfad in Berichten speichern + + + + Language + Sprache + + + + SettingsDialog + + + N/A + kA + + + + The executable file "%1" is not available + + + + + Add a new application + Neue Anwendung hinzufügen + + + + Modify an application + Anwendung ändern + + + + [Default] + [Standard] + + + + [Default] + [Standard] + + + + Select python binary + Python-Binärdatei auswählen + + + + Select MISRA File + Wähle MISRA-Datei + + + + Select clang path + Clang-Verzeichnis auswählen + + + + StatsDialog + + + + + + Statistics + Statistik + + + + + Project + Projekt + + + + Project: + Projekt: + + + + Paths: + Pfade: + + + + Include paths: + Include-Pfade: + + + + Defines: + Definitionen: + + + + Undefines: + Un-Definitionen: + + + + + Previous Scan + Vorherige Prüfung + + + + Path Selected: + Ausgewählte Pfade: + + + + Number of Files Scanned: + Anzahl der geprüften Dateien: + + + + Scan Duration: + Prüfungsdauer: + + + + Errors: + Fehler: + + + + Warnings: + Warnungen: + + + + Stylistic warnings: + Stilwarnungen: + + + + Portability warnings: + Portabilitätswarnungen: + + + + Performance issues: + Performance-Probleme: + + + + Information messages: + Informationsmeldungen: + + + + Active checkers: + + + + + Checkers + + + + + History + Verlauf + + + + File: + Datei: + + + + Copy to Clipboard + In die Zwischenablage kopieren + + + + Pdf Export + PDF-Export + + + + 1 day + 1 Tag + + + + %1 days + %1 Tage + + + + 1 hour + 1 Stunde + + + + %1 hours + %1 Stunden + + + + 1 minute + 1 Minute + + + + %1 minutes + %1 Minuten + + + + 1 second + 1 Sekunde + + + + %1 seconds + %1 Sekunden + + + + 0.%1 seconds + 0,%1 Sekunden + + + + and + und + + + + Export PDF + Exportiere PDF + + + + Project Settings + Projekteinstellungen + + + + Paths + Pfade + + + + Include paths + Include-Pfade + + + + Defines + Definitionen + + + + Undefines + Un-Definitionen + + + + Path selected + Gewählte Pfade + + + + Number of files scanned + Anzahl geprüfter Dateien + + + + Scan duration + Prüfungsdauer + + + + + Errors + Fehler + + + + File: + Datei: + + + + No cppcheck build dir + Kein Cppcheck-Analyseverzeichnis + + + + + Warnings + Warnungen + + + + + Style warnings + Stilwarnungen + + + + + Portability warnings + Portabilitätswarnungen + + + + + Performance warnings + Performance-Warnungen + + + + + Information messages + Informationsmeldungen + + + + ThreadResult + + + %1 of %2 files checked + %1 von %2 Dateien geprüft + + + + TranslationHandler + + + Failed to change the user interface language: + +%1 + +The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. + Wechsel der Sprache der Benutzeroberfläche fehlgeschlagen: + +%1 + +Die Sprache wurde auf Englisch zurückgesetzt. Öffnen Sie den Einstellungen-Dialog um eine verfügbare Sprache auszuwählen. + + + + Cppcheck + Cppcheck + + + + TxtReport + + + inconclusive + unklar + + + + toFilterString + + + All supported files (%1) + Alle unterstützten Dateien (%1) + + + + All files (%1) + Alle Dateien (%1) + + + diff --git a/cppcheck-2.14.0/gui/cppcheck_es.ts b/cppcheck-2.14.0/gui/cppcheck_es.ts new file mode 100644 index 00000000..040dd216 --- /dev/null +++ b/cppcheck-2.14.0/gui/cppcheck_es.ts @@ -0,0 +1,3008 @@ + + + + + About + + + About Cppcheck + Acerca de Cppcheck + + + + Version %1 + Versión %1 + + + + Cppcheck - A tool for static C/C++ code analysis. + Cppcheck - Una utilidad para el análisis estático de código C/C++. + + + + Copyright © 2007-%1 Cppcheck team. + Copyright © 2007-2021 Cppcheck team. + Copyright © 2007-2021 el equipo de cppcheck. + + + + This program is licensed under the terms +of the GNU General Public License version 3 + Este programa está licenciado bajo los términos de GNU General Public License versión 3 + + + + Visit Cppcheck homepage at %1 + Visita el sitio de Cppcheck en %1 + + + + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PCRE</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PicoJSON</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">TinyXML2</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Boost</li></ul></body></html> + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">pcre</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">picojson</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">tinyxml2</li></ul></body></html> + + + + + ApplicationDialog + + + Add an application + Añade una aplicación + + + + Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. + +The following texts in parameters are replaced with appropriate values when application is executed: +(file) - Filename containing the error +(line) - Line number containing the error +(message) - Error message +(severity) - Error severity + +Example opening a file with Kate and make Kate scroll to the correct line: +Executable: kate +Parameters: -l(line) (file) + + + + + &Name: + &Nombre: + + + + &Executable: + &Ejecutable: + + + + &Parameters: + &Parámetros: + + + + Browse + Buscar + + + + Executable files (*.exe);;All files(*.*) + Archivos ejecutables (*.exe);;Todos los archivos(*.*) + + + + Select viewer application + Selecciona la aplicación para visualizar + + + + Cppcheck + Cppcheck + + + + You must specify a name, a path and optionally parameters for the application! + ¡Debes especificar el nombre, la ruta y opcionalmente los parámetros para la aplicación! + + + + ComplianceReportDialog + + + Compliance Report + + + + + Project name + + + + + Project version + + + + + Coding Standard + + + + + Misra C + + + + + Cert C + + + + + Cert C++ + + + + + List of files with md5 checksums + + + + + Compliance report + + + + + HTML files (*.html) + + + + + + Save compliance report + + + + + Failed to import '%1' (%2), can not show files in compliance report + + + + + 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 + + + + HelpDialog + + + Cppcheck GUI help + + + + + Contents + + + + + Index + + + + + Helpfile '%1' was not found + + + + + Cppcheck + Cppcheck + + + + LibraryAddFunctionDialog + + + Add function + Añadir función + + + + Function name(s) + Nombre(s) de la función + + + + Number of arguments + Número de argumentos + + + + LibraryDialog + + + Library Editor + Editor de bibliotecas + + + + Open + Abrir + + + + Save + Guardar + + + + Save as + + + + + Functions + Funciones + + + + Sort + + + + + Add + Añadir + + + + Filter: + + + + + Comments + + + + + noreturn + + + + + False + Falso + + + + True + Verdadero + + + + Unknown + Desconocido + + + + return value must be used + + + + + ignore function in leaks checking + + + + + Arguments + Argumentos + + + + Edit + Editar + + + + + Library files (*.cfg) + Archivos de biblioteca (*.cfg) + + + + Open library file + Abrir archivo de biblioteca + + + + + + Cppcheck + Cppcheck + + + + Cannot open file %1. + Can not open file %1. + + + + + Failed to load %1. %2. + + + + + Cannot save file %1. + Can not save file %1. + + + + + Save the library as + + + + + LibraryEditArgDialog + + + Edit argument + Editar argumento + + + + <html><head/><body> +<p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> +<p>Typically, set this if the argument is a pointer, size, etc.</p> +<p>Example:</p> +<pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> +</body></html> + + + + + Not bool + No bool + + + + <html><head/><body> +<p>Is a null parameter value allowed?</p> +<p>Typically this should be used on any pointer parameter that does not allow null.</p> +<p>Example:</p> +<pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> +</body></html> + + + + + Not null + No null + + + + Not uninit + No uninit + + + + String + String + + + + Format string + + + + + Min size of buffer + + + + + + Type + Tipo + + + + + None + Ninguno + + + + + argvalue + + + + + + mul + + + + + + strlen + + + + + + Arg + + + + + + Arg2 + + + + + and + + + + + Valid values + Valores válidos + + + + MainWindow + + + + + + + + + + + + + + + + + Cppcheck + Cppcheck + + + + &File + &Archivo + + + + &View + &Ver + + + + &Toolbars + &Herramientas + + + + &Help + &Ayuda + + + + C++ standard + C++ estándar + + + + &C standard + C standard + C estándar + + + + &Edit + &Editar + + + + Standard + Estándar + + + + Categories + Categorías + + + + &License... + &Licencia... + + + + A&uthors... + A&utores... + + + + &About... + &Acerca de... + + + + &Files... + &Ficheros... + + + + + Analyze files + Check files + Comprobar archivos + + + + Ctrl+F + Ctrl+F + + + + &Directory... + &Carpeta... + + + + + Analyze directory + Check directory + Comprobar carpeta + + + + Ctrl+D + Ctrl+D + + + + Ctrl+R + Ctrl+R + + + + &Stop + &Detener + + + + + Stop analysis + Stop checking + Detener comprobación + + + + Esc + Esc + + + + &Save results to file... + &Guardar los resultados en el fichero... + + + + Ctrl+S + Ctrl+S + + + + &Quit + &Salir + + + + &Clear results + &Limpiar resultados + + + + &Preferences + &Preferencias + + + + + Show style warnings + Mostrar advertencias de estilo + + + + + Show errors + Mostrar errores + + + + + Information + Información + + + + Show information messages + Mostrar mensajes de información + + + + Show portability warnings + Mostrar advertencias de portabilidad + + + + Show Cppcheck results + + + + + Clang + + + + + Show Clang results + + + + + &Filter + &Filtro + + + + Filter results + Resultados del filtro + + + + Windows 32-bit ANSI + Windows 32-bit ANSI + + + + Windows 32-bit Unicode + Windows 32-bit Unicode + + + + Unix 32-bit + Unix 32-bit + + + + Unix 64-bit + Unix 64-bit + + + + Windows 64-bit + Windows 64-bit + + + + &Print... + Im&primir... + + + + Print the Current Report + Imprimir el informe actual + + + + Print Pre&view... + Pre&visualización de impresión... + + + + Open a Print Preview Dialog for the Current Results + Abre el diálogo de previsualización de impresión para el informe actual + + + + Open library editor + Abrir el editor de bibliotecas + + + + &Check all + &Seleccionar todo + + + + Checking for updates + + + + + Hide + Ocultar + + + + A&nalyze + + + + + Filter + Filtro + + + + &Reanalyze modified files + &Recheck modified files + + + + + Reanal&yze all files + + + + + Ctrl+Q + + + + + Style war&nings + + + + + E&rrors + + + + + &Uncheck all + &Deseleccionar todo + + + + Collapse &all + Contraer &todo + + + + &Expand all + &Expandir todo + + + + &Standard + &Estándar + + + + Standard items + Elementos estándar + + + + &Contents + &Contenidos + + + + Open the help contents + Abrir la ayuda de contenidos + + + + F1 + F1 + + + + Toolbar + Barra de herramientas + + + + &Categories + &Categorías + + + + Error categories + Categorías de error + + + + &Open XML... + &Abrir XML... + + + + Open P&roject File... + Abrir P&royecto... + + + + Ctrl+Shift+O + + + + + Sh&ow Scratchpad... + + + + + &New Project File... + &Nuevo Proyecto... + + + + Ctrl+Shift+N + + + + + &Log View + &Visor del log + + + + Log View + Visor del log + + + + C&lose Project File + C&errar Proyecto + + + + &Edit Project File... + &Editar Proyecto... + + + + &Statistics + &Estadísticas + + + + &Warnings + + + + + Per&formance warnings + + + + + &Information + + + + + &Portability + + + + + P&latforms + + + + + C++&11 + + + + + C&99 + + + + + &Posix + + + + + C&11 + + + + + &C89 + + + + + &C++03 + + + + + &Library Editor... + + + + + &Auto-detect language + + + + + &Enforce C++ + + + + + E&nforce C + + + + + C++14 + C++14 + + + + Reanalyze and check library + + + + + Check configuration (defines, includes) + + + + + C++17 + C++17 + + + + C++20 + C++20 + + + + Compliance report... + + + + + + Show warnings + Mostrar advertencias + + + + + Show performance warnings + Mostrar advertencias de rendimiento + + + + Show &hidden + Mostrar &ocultos + + + + There was a problem with loading the editor application settings. + +This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. + + + + + You must close the project file before selecting new files or directories! + ¡Tienes que cerrar el proyecto antes de seleccionar nuevos ficheros o carpetas! + + + + Select configuration + + + + + File not found + Archivo no encontrado + + + + Bad XML + XML malformado + + + + Missing attribute + Falta el atributo + + + + Bad attribute value + + + + + Unsupported format + Formato no soportado + + + + Duplicate define + + + + + Failed to load the selected library '%1'. +%2 + + + + + File not found: '%1' + + + + + Failed to load/setup addon %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. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + +Analysis is aborted. + + + + + Failed to load %1 - %2 + +Analysis is aborted. + + + + + + %1 + +Analysis is aborted. + + + + + + XML files (*.xml) + Archivos XML (*.xml) + + + + Open the report file + Abrir informe + + + + License + Licencia + + + + Authors + Autores + + + + Save the report file + Guardar informe + + + + + Quick Filter: + Filtro rápido: + + + + Found project file: %1 + +Do you want to load this project file instead? + Se encontró el fichero de proyecto: %1 + +¿Quiere cargar este fichero de proyecto en su lugar? + + + + The library '%1' contains unknown elements: +%2 + La biblioteca '%1' contiene elementos deconocidos: +%2 + + + + Duplicate platform type + + + + + Platform type redefined + + + + + Unknown element + + + + + Unknown issue + + + + + + + + Error + Error + + + + Text files (*.txt) + Ficheros de texto (*.txt) + + + + CSV files (*.csv) + Ficheros CVS (*.cvs) + + + + Project files (*.cppcheck);;All files(*.*) + Ficheros de proyecto (*.cppcheck;;Todos los ficheros (*.*) + + + + Select Project File + Selecciona el archivo de proyecto + + + + + + + Project: + Proyecto: + + + + No suitable files found to analyze! + + + + + C/C++ Source + + + + + Compile database + + + + + Visual Studio + + + + + Borland C++ Builder 6 + + + + + Select files to analyze + + + + + Select directory to analyze + + + + + Select the configuration that will be analyzed + + + + + Found project files from the directory. + +Do you want to proceed analysis without using any of these project files? + + + + + Current results will be cleared. + +Opening a new XML file will clear current results. +Do you want to proceed? + + + + + Analyzer is running. + +Do you want to stop the analysis and exit Cppcheck? + + + + + About + + + + + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) + + + + + Cannot generate a compliance report right now, an analysis must finish successfully. Try to reanalyze the code and ensure there are no critical errors. + + + + + Build dir '%1' does not exist, create it? + + + + + To check the project using addons, you need a build directory. + + + + + Failed to open file + + + + + Unknown project file format + + + + + Failed to import project file + + + + + Failed to import '%1': %2 + +Analysis is stopped. + + + + + Failed to import '%1' (%2), analysis is stopped + + + + + Project files (*.cppcheck) + + + + + Select Project Filename + Selecciona el nombre del proyecto + + + + No project file loaded + No hay ningún proyecto cargado + + + + The project file + +%1 + + could not be found! + +Do you want to remove the file from the recently used projects -list? + ¡El fichero de proyecto + +%1 + + no puede ser encontrado! + +¿Quiere eliminar el fichero de la lista de proyectos recientes? + + + + Install + + + + + New version available: %1. %2 + + + + + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> This option is for installation scripts so they can configure the directory where + datafiles are located (translations, cfg). The GUI is not started when this option + is used. + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) + + + + + Cppcheck GUI - Command line parameters + + + + + NewSuppressionDialog + + + New suppression + + + + + Error ID + + + + + File name + + + + + Line number + + + + + Symbol name + + + + + Edit suppression + + + + + Platforms + + + Native + + + + + Unix 32-bit + Unix 32-bit + + + + Unix 64-bit + Unix 64-bit + + + + Windows 32-bit ANSI + Windows 32-bit ANSI + + + + Windows 32-bit Unicode + Windows 32-bit Unicode + + + + Windows 64-bit + Windows 64-bit + + + + ProjectFile + + + Project File + Archivo de proyecto + + + + Paths and Defines + + + + + Import Project (Visual studio / compile database/ Borland C++ Builder 6) + Import Project (Visual studio / compile database) + + + + + Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int + Defines must be separated by a semicolon ';' + + + + + Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. + Nota: Ponga sus propios archivos .cfg en la misma carpeta que el proyecto. Debería verlos arriba. + + + + If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. + + + + + Exclude source files + + + + + Exclude folder... + + + + + Exclude file... + + + + + Misra C + + + + + 2012 + + + + + 2023 + + + + + MISRA rule texts + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + ... + + + + + <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> + + + + + + Browse... + + + + + Analyze all Visual Studio configurations + + + + + Selected VS Configurations + + + + + Paths: + Rutas: + + + + + Add... + Añadir... + + + + + + Edit + Editar + + + + + + + Remove + Eliminar + + + + Undefines: + + + + + Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 + + + + + Include Paths: + + + + + Types and Functions + + + + + + Analysis + + + + + This is a workfolder that Cppcheck will use for various purposes. + + + + + Parser + + + + + Cppcheck (built in) + + + + + Check level + + + + + Normal -- meant for normal analysis in CI. Analysis should finish in reasonable time. + + + + + Exhaustive -- meant for nightly builds etc. Analysis time can be longer (10x slower than compilation is OK). + + + + + Check that each class has a safe public interface + + + + + Limit analysis + + + + + Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) + Check code in unused templates (slower and less accurate analysis) + + + + + Max CTU depth + + + + + Cert C + + + + + CERT-INT35-C: int precision (if size equals precision, you can leave empty) + + + + + Misra C++ 2008 + + + + + Autosar + + + + + Bug hunting + + + + + External tools + + + + + Up + Subir + + + + Down + Bajar + + + + Platform + + + + + Clang (experimental) + + + + + If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. + + + + + Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) + + + + + Max recursion in template instantiation + + + + + Warning options + + + + + Root path: + + + + + Filepaths in warnings will be relative to this path + + + + + Warning tags (separated by semicolon) + + + + + Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) + + + + + Libraries + + + + + Suppressions + Supresiones + + + + Add + Añadir + + + + + Addons + + + + + Note: Addons require <a href="https://www.python.org/">Python</a> being installed. + + + + + Y2038 + + + + + Thread safety + + + + + Coding standards + + + + + Cert C++ + + + + + Bug hunting (Premium) + + + + + Clang analyzer + + + + + Clang-tidy + + + + + Defines: + Definiciones: + + + + ProjectFileDialog + + + Project file: %1 + Archivo de proyecto: %1 + + + + Select Cppcheck build dir + + + + + Select include directory + Selecciona una carpeta para incluir + + + + Select a directory to check + Selecciona la carpeta a comprobar + + + + Clang-tidy (not found) + + + + + Visual Studio + + + + + Compile database + + + + + Borland C++ Builder 6 + + + + + Import Project + + + + + Select directory to ignore + Selecciona la carpeta a ignorar + + + + Source files + + + + + All files + + + + + Exclude file + + + + + Select MISRA rule texts file + + + + + MISRA rule texts file (%1) + + + + + QObject + + + Unknown language specified! + ¡Idioma especificado desconocido! + + + + Language file %1 not found! + ¡Fichero de idioma %1 no encontrado! + + + + Failed to load translation for language %1 from file %2 + Fallo al cargar la traducción para el idioma %1 desde el fichero %2 + + + + line %1: Unhandled element %2 + + + + + line %1: Mandatory attribute '%2' missing in '%3' + + + + + (Not found) + + + + + Thin + + + + + ExtraLight + + + + + Light + + + + + Normal + + + + + Medium + + + + + DemiBold + + + + + Bold + + + + + ExtraBold + + + + + Black + + + + + Editor Foreground Color + + + + + Editor Background Color + + + + + Highlight Background Color + + + + + Line Number Foreground Color + + + + + Line Number Background Color + + + + + Keyword Foreground Color + + + + + Keyword Font Weight + + + + + Class Foreground Color + Class ForegroundColor + + + + + Class Font Weight + + + + + Quote Foreground Color + + + + + Quote Font Weight + + + + + Comment Foreground Color + + + + + Comment Font Weight + + + + + Symbol Foreground Color + + + + + Symbol Background Color + + + + + Symbol Font Weight + + + + + Set to Default Light + + + + + Set to Default Dark + + + + + QPlatformTheme + + + OK + Aceptar + + + + Cancel + Cancelar + + + + Close + Cerrar + + + + Save + Guardar + + + + ResultsTree + + + File + Archivo + + + + Severity + Severidad + + + + Line + Línea + + + + Summary + Resumen + + + + Undefined file + Fichero no definido + + + + Copy + + + + + Could not find file: + + + + + Please select the folder '%1' + + + + + Select Directory '%1' + + + + + Please select the directory where file is located. + + + + + portability + portabilidad + + + + note + + + + + information + información + + + + debug + depuración + + + + Recheck + + + + + Hide + Ocultar + + + + Hide all with id + Ocultar todos con el mismo id + + + + Suppress selected id(s) + + + + + Open containing folder + Abrir carpeta contenedora + + + + internal + + + + + + Tag + + + + + No tag + + + + + + Cppcheck + Cppcheck + + + + No editor application configured. + +Configure the editor application for Cppcheck in preferences/Applications. + Configure the text file viewer program in Cppcheck preferences/Applications. + No se ha configurado una aplicación para editar. + +Configura el programa para editar en Preferencias/Aplicaciones. + + + + No default editor application selected. + +Please select the default editor application in preferences/Applications. + No se ha definido una aplicación para editar prefeterminada. + +Configura el programa para editar por defecto en Preferencias/Aplicaciones. + + + + Could not find the file! + ¡No se ha encontrado el fichero! + + + + Could not start %1 + +Please check the application path and parameters are correct. + No se ha podido ejecutar %1 + +Por favor comprueba que la ruta a la aplicación y los parámetros son correctos. + + + + Select Directory + Selecciona carpeta + + + + Id + Id + + + + Inconclusive + + + + + Since date + + + + + style + estilo + + + + error + error + + + + warning + advertencia + + + + performance + ajuste + + + + ResultsView + + + Results + Resultados + + + + Critical errors + + + + + Analysis Log + + + + + Warning Details + + + + + + Failed to save the report. + Error al guardar el informe. + + + + Print Report + Imprimir informe + + + + No errors found, nothing to print. + No se encontraron errores, nada que imprimir. + + + + %p% (%1 of %2 files checked) + %p% (%1 of %2 archivos comprobados) + + + + + Cppcheck + Cppcheck + + + + No errors found. + No se han encontrado errores. + + + + Errors were found, but they are configured to be hidden. +To toggle what kind of errors are shown, open view menu. + Se han encontrado errores, pero están configurados para que no se muestren. +Para cambiar el tipo de comportamiento, abra el menú Ver. + + + + + Failed to read the report. + Error al leer el informe. + + + + XML format version 1 is no longer supported. + + + + + First included by + + + + + Id + Id + + + + Bug hunting analysis is incomplete + + + + + Clear Log + + + + + Copy this Log entry + + + + + Copy complete Log + + + + + Analysis was stopped + + + + + There was a critical error with id '%1' + + + + + when checking %1 + + + + + when checking a file + + + + + Analysis was aborted. + + + + + ScratchPad + + + Scratchpad + Scratchpad + + + + Copy or write some C/C++ code here: + + + + + Optionally enter a filename (mainly for automatic language detection) and click on "Check": + + + + + filename + nombre de archivo + + + + Check + Comprobar + + + + Settings + + + Preferences + Preferencias + + + + General + General + + + + Ideal count: + Cantidad ideal: + + + + Force checking all #ifdef configurations + Forzar comprobación de todas las configuraciones #ifdef + + + + Display error Id in column "Id" + Mostrar el Id del error en la columna "Id" + + + + Enable inline suppressions + Habilitar supresiones inline + + + + Check for inconclusive errors also + + + + + Show statistics on check completion + + + + + Check for updates + + + + + Show internal warnings in log + + + + + Addons + + + + + Python binary (leave this empty to use python in the PATH) + + + + + + + ... + + + + + MISRA addon + + + + + MISRA rule texts file + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + Clang + + + + + Clang path (leave empty to use system PATH) + + + + + Visual Studio headers + + + + + <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> + + + + + Code Editor + + + + + Code Editor Style + + + + + System Style + + + + + Default Light Style + + + + + Default Dark Style + + + + + Custom + + + + + Add... + Añadir... + + + + + Edit... + Editar... + + + + Set as default + Definir por defecto + + + + Language + Idioma + + + + 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" + + + + Remove + Eliminar + + + + Applications + Aplicaciones + + + + Reports + Informes + + + + Save all errors when creating report + Guardar todos los errores cuando se cree el informe + + + + Save full path to files in reports + Guardar la ruta completa en los ficheros de informes + + + + SettingsDialog + + + N/A + N/A + + + + The executable file "%1" is not available + + + + + Add a new application + Añadir una nueva aplicación + + + + Modify an application + Modificar una aplicación + + + + [Default] + + + + + [Default] + [Predeterminada] + + + + Select python binary + + + + + Select MISRA File + + + + + Select clang path + + + + + StatsDialog + + + + + + Statistics + Estadísticas + + + + + Project + Proyecto + + + + Project: + Proyecto: + + + + Paths: + Rutas: + + + + Include paths: + Incluye las rutas: + + + + Defines: + Definiciones: + + + + Undefines: + + + + + + Previous Scan + Análisis anterior + + + + Path Selected: + Ruta seleccionada: + + + + Number of Files Scanned: + Número de archivos analizados: + + + + Scan Duration: + Duración del análisis: + + + + Errors: + Errores: + + + + Warnings: + Advertencias: + + + + Stylistic warnings: + Advertencias de estilo: + + + + Portability warnings: + Advertencias de portabilidad: + + + + Performance issues: + Problemas de rendimiento: + + + + Information messages: + Mensajes de información: + + + + Active checkers: + + + + + Checkers + + + + + History + + + + + File: + + + + + Copy to Clipboard + Copiar al portapapeles + + + + Pdf Export + + + + + 1 day + 1 día + + + + %1 days + %1 días + + + + 1 hour + 1 hora + + + + %1 hours + %1 horas + + + + 1 minute + 1 minuto + + + + %1 minutes + %1 minutos + + + + 1 second + 1 segundo + + + + %1 seconds + %1 segundos + + + + 0.%1 seconds + 0.%1 segundos + + + + and + y + + + + Export PDF + + + + + Project Settings + Preferencias del proyecto + + + + Paths + Rutas + + + + Include paths + Incluye las rutas + + + + Defines + Definiciones + + + + Undefines + + + + + Path selected + Ruta seleccionada + + + + Number of files scanned + Número de archivos analizados + + + + Scan duration + Duración del análisis + + + + + Errors + Errores + + + + File: + + + + + No cppcheck build dir + + + + + + Warnings + Advertencias + + + + + Style warnings + Advertencias de estilo + + + + + Portability warnings + Advertencias de portabilidad + + + + + Performance warnings + Advertencias de rendimiento + + + + + Information messages + Mensajes de información + + + + ThreadResult + + + %1 of %2 files checked + %1 de %2 archivos comprobados + + + + TranslationHandler + + + Failed to change the user interface language: + +%1 + +The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. + Ocurrió un error al cambiar el idioma de la interfaz gráfica: + +%1 + +El idioma de la interfaz gráfica ha sido cambiado a Inglés. Abra la ventana de Preferencias para seleccionar alguno de los idiomas disponibles. + + + + Cppcheck + Cppcheck + + + + TxtReport + + + inconclusive + no concluyente + + + + toFilterString + + + All supported files (%1) + + + + + All files (%1) + + + + diff --git a/cppcheck-2.14.0/gui/cppcheck_fi.ts b/cppcheck-2.14.0/gui/cppcheck_fi.ts new file mode 100644 index 00000000..1a20ef6d --- /dev/null +++ b/cppcheck-2.14.0/gui/cppcheck_fi.ts @@ -0,0 +1,2997 @@ + + + + + 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-%1 Cppcheck team. + Copyright © 2007-2021 Cppcheck team. + Copyright (C) 2007-2021 Daniel Marjamäki ja cppcheck tiimi. + + + + This program is licensed under the terms +of the GNU General Public License version 3 + Tämä ohjelma on lisensoitu GNU General +Public lisenssin version 3 alaisuuteen + + + + Visit Cppcheck homepage at %1 + Cppcheckin kotisivu löytyy osoitteesta %1 + + + + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PCRE</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PicoJSON</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">TinyXML2</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Boost</li></ul></body></html> + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">pcre</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">picojson</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">tinyxml2</li></ul></body></html> + + + + + ApplicationDialog + + + Add an application + Lisää uusi ohjelma + + + + Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. + +The following texts in parameters are replaced with appropriate values when application is executed: +(file) - Filename containing the error +(line) - Line number containing the error +(message) - Error message +(severity) - Error severity + +Example opening a file with Kate and make Kate scroll to the correct line: +Executable: kate +Parameters: -l(line) (file) + + + + + &Name: + + + + + &Executable: + + + + + &Parameters: + + + + + Browse + Selaa + + + + Executable files (*.exe);;All files(*.*) + Suoritettavat tiedostot (*.exe);;Kaikki tiedostot(*.*) + + + + Select viewer application + Valitse ohjelma jolla avata virhetiedosto + + + + Cppcheck + Cppcheck + + + + You must specify a name, a path and optionally parameters for the application! + + + + + ComplianceReportDialog + + + Compliance Report + + + + + Project name + + + + + Project version + + + + + Coding Standard + + + + + Misra C + + + + + Cert C + + + + + Cert C++ + + + + + List of files with md5 checksums + + + + + Compliance report + + + + + HTML files (*.html) + + + + + + Save compliance report + + + + + Failed to import '%1' (%2), can not show files in compliance report + + + + + 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 + + + + HelpDialog + + + Cppcheck GUI help + + + + + Contents + + + + + Index + + + + + Helpfile '%1' was not found + + + + + Cppcheck + Cppcheck + + + + LibraryAddFunctionDialog + + + Add function + + + + + Function name(s) + + + + + Number of arguments + + + + + LibraryDialog + + + Library Editor + + + + + Open + + + + + Save + + + + + Save as + + + + + Functions + + + + + Sort + + + + + Add + + + + + Filter: + + + + + Comments + + + + + noreturn + + + + + False + + + + + True + + + + + Unknown + + + + + return value must be used + + + + + ignore function in leaks checking + + + + + Arguments + + + + + Edit + + + + + + Library files (*.cfg) + + + + + Open library file + + + + + + + Cppcheck + Cppcheck + + + + Cannot open file %1. + Can not open file %1. + + + + + Failed to load %1. %2. + + + + + Cannot save file %1. + Can not save file %1. + + + + + Save the library as + + + + + LibraryEditArgDialog + + + Edit argument + + + + + <html><head/><body> +<p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> +<p>Typically, set this if the argument is a pointer, size, etc.</p> +<p>Example:</p> +<pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> +</body></html> + + + + + Not bool + + + + + <html><head/><body> +<p>Is a null parameter value allowed?</p> +<p>Typically this should be used on any pointer parameter that does not allow null.</p> +<p>Example:</p> +<pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> +</body></html> + + + + + Not null + + + + + Not uninit + + + + + String + + + + + Format string + + + + + Min size of buffer + + + + + + Type + + + + + + None + + + + + + argvalue + + + + + + mul + + + + + + strlen + + + + + + Arg + + + + + + Arg2 + + + + + and + + + + + Valid values + + + + + MainWindow + + + + + + + + + + + + + + + + + Cppcheck + Cppcheck + + + + A&nalyze + + + + + Standard + Vakio + + + + &File + &Tiedosto + + + + &View + &Näytä + + + + &Toolbars + + + + + C++ standard + + + + + &C standard + C standard + + + + + &Edit + &Muokkaa + + + + &License... + &Lisenssi... + + + + A&uthors... + &Tekijät... + + + + &About... + &Tietoa ohjelmasta Cppcheck... + + + + &Files... + &Tiedostot... + + + + + Analyze files + Check files + + + + + Ctrl+F + Ctrl+F + + + + &Directory... + &Hakemisto... + + + + + Analyze directory + Check directory + + + + + Ctrl+D + Ctrl+D + + + + Ctrl+R + Ctrl+R + + + + &Stop + &Pysäytä + + + + + Stop analysis + Stop checking + + + + + Esc + Esc + + + + &Save results to file... + &Tallenna tulokset tiedostoon... + + + + Ctrl+S + Ctrl+S + + + + &Quit + &Lopeta + + + + &Clear results + &Tyhjennä tulokset + + + + &Preferences + &Asetukset + + + + + Show errors + + + + + + Show warnings + + + + + + Show performance warnings + + + + + Show &hidden + + + + + + Information + + + + + Show information messages + + + + + Show portability warnings + + + + + Show Cppcheck results + + + + + Clang + + + + + Show Clang results + + + + + &Filter + + + + + Filter results + + + + + Windows 32-bit ANSI + + + + + Windows 32-bit Unicode + + + + + Unix 32-bit + + + + + Unix 64-bit + + + + + Windows 64-bit + + + + + &Print... + + + + + Print the Current Report + + + + + Print Pre&view... + + + + + Open a Print Preview Dialog for the Current Results + + + + + Open library editor + + + + + &Check all + &Valitse kaikki + + + + Checking for updates + + + + + Hide + + + + + Filter + + + + + &Reanalyze modified files + &Recheck modified files + + + + + Reanal&yze all files + + + + + Ctrl+Q + + + + + Style war&nings + + + + + E&rrors + + + + + &Uncheck all + &Poista kaikista valinta + + + + Collapse &all + &Pienennä kaikki + + + + &Expand all + &Laajenna kaikki + + + + &Standard + + + + + Standard items + + + + + Toolbar + + + + + &Categories + + + + + Error categories + + + + + &Open XML... + + + + + Open P&roject File... + + + + + Ctrl+Shift+O + + + + + Sh&ow Scratchpad... + + + + + &New Project File... + + + + + Ctrl+Shift+N + + + + + &Log View + + + + + Log View + + + + + C&lose Project File + + + + + &Edit Project File... + + + + + &Statistics + + + + + &Warnings + + + + + Per&formance warnings + + + + + &Information + + + + + &Portability + + + + + P&latforms + + + + + C++&11 + + + + + C&99 + + + + + &Posix + + + + + C&11 + + + + + &C89 + + + + + &C++03 + + + + + &Library Editor... + + + + + &Auto-detect language + + + + + &Enforce C++ + + + + + E&nforce C + + + + + C++14 + + + + + Reanalyze and check library + + + + + Check configuration (defines, includes) + + + + + C++17 + + + + + C++20 + + + + + Compliance report... + + + + + &Contents + + + + + Categories + + + + + + Show style warnings + + + + + Open the help contents + + + + + F1 + + + + + &Help + &Ohje + + + + + Quick Filter: + + + + + Select configuration + + + + + Found project file: %1 + +Do you want to load this project file instead? + + + + + File not found + + + + + Bad XML + + + + + Missing attribute + + + + + Bad attribute value + + + + + Duplicate define + + + + + Failed to load the selected library '%1'. +%2 + + + + + File not found: '%1' + + + + + Failed to load/setup addon %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. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + +Analysis is aborted. + + + + + Failed to load %1 - %2 + +Analysis is aborted. + + + + + + %1 + +Analysis is aborted. + + + + + License + Lisenssi + + + + Authors + Tekijät + + + + 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! + + + + + The library '%1' contains unknown elements: +%2 + + + + + Unsupported format + + + + + Duplicate platform type + + + + + Platform type redefined + + + + + Unknown element + + + + + Unknown issue + + + + + + + + Error + + + + + Open the report file + + + + + Text files (*.txt) + Tekstitiedostot (*.txt) + + + + CSV files (*.csv) + + + + + Project files (*.cppcheck);;All files(*.*) + + + + + Select Project File + + + + + + + + Project: + + + + + No suitable files found to analyze! + + + + + C/C++ Source + + + + + Compile database + + + + + Visual Studio + + + + + Borland C++ Builder 6 + + + + + Select files to analyze + + + + + Select directory to analyze + + + + + Select the configuration that will be analyzed + + + + + Found project files from the directory. + +Do you want to proceed analysis without using any of these project files? + + + + + Current results will be cleared. + +Opening a new XML file will clear current results. +Do you want to proceed? + + + + + Analyzer is running. + +Do you want to stop the analysis and exit Cppcheck? + + + + + About + + + + + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) + + + + + Cannot generate a compliance report right now, an analysis must finish successfully. Try to reanalyze the code and ensure there are no critical errors. + + + + + Build dir '%1' does not exist, create it? + + + + + To check the project using addons, you need a build directory. + + + + + Failed to open file + + + + + Unknown project file format + + + + + Failed to import project file + + + + + Failed to import '%1': %2 + +Analysis is stopped. + + + + + Failed to import '%1' (%2), analysis is stopped + + + + + Project files (*.cppcheck) + + + + + Select Project Filename + + + + + No project file loaded + + + + + The project file + +%1 + + could not be found! + +Do you want to remove the file from the recently used projects -list? + + + + + Install + + + + + New version available: %1. %2 + + + + + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> This option is for installation scripts so they can configure the directory where + datafiles are located (translations, cfg). The GUI is not started when this option + is used. + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) + + + + + Cppcheck GUI - Command line parameters + + + + + NewSuppressionDialog + + + New suppression + + + + + Error ID + + + + + File name + + + + + Line number + + + + + Symbol name + + + + + Edit suppression + + + + + Platforms + + + Native + + + + + Unix 32-bit + + + + + Unix 64-bit + + + + + Windows 32-bit ANSI + + + + + Windows 32-bit Unicode + + + + + Windows 64-bit + + + + + ProjectFile + + + Project File + + + + + Paths and Defines + + + + + Import Project (Visual studio / compile database/ Borland C++ Builder 6) + Import Project (Visual studio / compile database) + + + + + Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int + Defines must be separated by a semicolon ';' + + + + + Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. + + + + + MISRA rule texts + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + ... + + + + + <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> + + + + + + Browse... + + + + + Analyze all Visual Studio configurations + + + + + Selected VS Configurations + + + + + Paths: + + + + + + Add... + + + + + + + Edit + + + + + + + + Remove + + + + + Undefines: + + + + + Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 + + + + + Include Paths: + + + + + Up + + + + + Down + + + + + Platform + + + + + + Analysis + + + + + This is a workfolder that Cppcheck will use for various purposes. + + + + + Clang (experimental) + + + + + Check level + + + + + Normal -- meant for normal analysis in CI. Analysis should finish in reasonable time. + + + + + Exhaustive -- meant for nightly builds etc. Analysis time can be longer (10x slower than compilation is OK). + + + + + If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. + + + + + Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) + Check code in unused templates (slower and less accurate analysis) + + + + + Max CTU depth + + + + + Max recursion in template instantiation + + + + + Warning options + + + + + Root path: + + + + + Filepaths in warnings will be relative to this path + + + + + Warning tags (separated by semicolon) + + + + + Cert C + + + + + CERT-INT35-C: int precision (if size equals precision, you can leave empty) + + + + + Misra C++ 2008 + + + + + Autosar + + + + + Bug hunting + + + + + External tools + + + + + Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) + + + + + Types and Functions + + + + + Libraries + + + + + Parser + + + + + Cppcheck (built in) + + + + + Check that each class has a safe public interface + + + + + Limit analysis + + + + + Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) + + + + + If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. + + + + + Exclude source files + + + + + Exclude folder... + + + + + Exclude file... + + + + + Suppressions + + + + + Add + + + + + + Addons + + + + + Note: Addons require <a href="https://www.python.org/">Python</a> being installed. + + + + + Y2038 + + + + + Thread safety + + + + + Coding standards + + + + + Misra C + + + + + 2012 + + + + + 2023 + + + + + Cert C++ + + + + + Bug hunting (Premium) + + + + + Clang analyzer + + + + + Clang-tidy + + + + + Defines: + + + + + ProjectFileDialog + + + Project file: %1 + + + + + Select Cppcheck build dir + + + + + Select include directory + + + + + Select a directory to check + + + + + Clang-tidy (not found) + + + + + Visual Studio + + + + + Compile database + + + + + Borland C++ Builder 6 + + + + + Import Project + + + + + Select directory to ignore + + + + + Source files + + + + + All files + + + + + Exclude file + + + + + Select MISRA rule texts file + + + + + MISRA rule texts file (%1) + + + + + QObject + + + Unknown language specified! + + + + + Language file %1 not found! + Language file %1.qm not found! + Käännöstiedostoa %1 ei löytynyt! + + + + Failed to load translation for language %1 from file %2 + Failed to load translation for language %1 from file %2.qm + Käänöksen lataaminen kielelle %1 tiedostosta %2 epäonnistui + + + + line %1: Unhandled element %2 + + + + + line %1: Mandatory attribute '%2' missing in '%3' + + + + + (Not found) + + + + + Thin + + + + + ExtraLight + + + + + Light + + + + + Normal + + + + + Medium + + + + + DemiBold + + + + + Bold + + + + + ExtraBold + + + + + Black + + + + + Editor Foreground Color + + + + + Editor Background Color + + + + + Highlight Background Color + + + + + Line Number Foreground Color + + + + + Line Number Background Color + + + + + Keyword Foreground Color + + + + + Keyword Font Weight + + + + + Class Foreground Color + Class ForegroundColor + + + + + Class Font Weight + + + + + Quote Foreground Color + + + + + Quote Font Weight + + + + + Comment Foreground Color + + + + + Comment Font Weight + + + + + Symbol Foreground Color + + + + + Symbol Background Color + + + + + Symbol Font Weight + + + + + Set to Default Light + + + + + Set to Default Dark + + + + + QPlatformTheme + + + OK + + + + + Cancel + + + + + Close + + + + + Save + + + + + ResultsTree + + + File + Tiedosto + + + + Severity + Tyyppi + + + + Line + Rivi + + + + Summary + + + + + Undefined file + Määrittelemätön tiedosto + + + + Copy + + + + + Could not find file: + + + + + Please select the folder '%1' + + + + + Select Directory '%1' + + + + + Please select the directory where file is located. + + + + + debug + + + + + note + + + + + Recheck + + + + + Hide + + + + + Hide all with id + + + + + Suppress selected id(s) + + + + + Open containing folder + + + + + internal + + + + + + Tag + + + + + No tag + + + + + + Cppcheck + Cppcheck + + + + No editor application configured. + +Configure the editor application for Cppcheck in preferences/Applications. + Configure the text file viewer program in Cppcheck preferences/Applications. + Voit asetuksista määritellä muita ohjelmia joilla avata tämän virheen sisältävän tiedoston. + + + + No default editor application selected. + +Please select the default editor application in preferences/Applications. + + + + + Could not find the file! + + + + + Could not start %1 + +Please check the application path and parameters are correct. + Ohjelman %1 käynnistäminen epäonnistui + +Tarkista että ohjelman polku ja parametrit ovat oikeat. + + + + Select Directory + + + + + Id + + + + + Inconclusive + + + + + Since date + + + + + style + Tyyli + + + + error + Yleinen + + + + warning + + + + + performance + + + + + portability + + + + + information + + + + + ResultsView + + + Print Report + + + + + No errors found, nothing to print. + + + + + %p% (%1 of %2 files checked) + + + + + + Cppcheck + Cppcheck + + + + No errors found. + Virheitä ei löytynyt. + + + + Errors were found, but they are configured to be hidden. +To toggle what kind of errors are shown, open view menu. + Virheitä löytyi, mutta asetuksissa kyseiset virheet on määritelty piilotettavaksi. +Määrittääksesi minkä tyyppisiä virheitä näytetään, avaa näkymä valikko. + + + + + Failed to read the report. + + + + + XML format version 1 is no longer supported. + + + + + First included by + + + + + Id + + + + + Bug hunting analysis is incomplete + + + + + Clear Log + + + + + Copy this Log entry + + + + + Copy complete Log + + + + + Analysis was stopped + + + + + There was a critical error with id '%1' + + + + + when checking %1 + + + + + when checking a file + + + + + Analysis was aborted. + + + + + + Failed to save the report. + Raportin tallentaminen epäonnistui. + + + + Results + Tulokset + + + + Critical errors + + + + + Analysis Log + + + + + Warning Details + + + + + ScratchPad + + + Scratchpad + + + + + Copy or write some C/C++ code here: + + + + + Optionally enter a filename (mainly for automatic language detection) and click on "Check": + + + + + filename + + + + + Check + + + + + Settings + + + Preferences + Asetukset + + + + General + Yleiset + + + + Add... + + + + + Number of threads: + Säikeiden lukumäärä: + + + + Ideal count: + + + + + Force checking all #ifdef configurations + Check all #ifdef configurations + Tarkista kaikki #ifdef kombinaatiot + + + + Show full path of files + Näytä tiedostojen täysi polku + + + + Show "No errors found" message when no errors found + Näytä "virheitä ei löytynyt"-viesti jos virheitä ei löydy + + + + Display error Id in column "Id" + + + + + Enable inline suppressions + + + + + Check for inconclusive errors also + + + + + Show statistics on check completion + + + + + Check for updates + + + + + Show internal warnings in log + + + + + Addons + + + + + Python binary (leave this empty to use python in the PATH) + + + + + + + ... + + + + + MISRA addon + + + + + MISRA rule texts file + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + Clang + + + + + Clang path (leave empty to use system PATH) + + + + + Visual Studio headers + + + + + <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> + + + + + Code Editor + + + + + Code Editor Style + + + + + System Style + + + + + Default Light Style + + + + + Default Dark Style + + + + + Custom + + + + + Remove + + + + + Applications + Ohjelmat + + + + + Edit... + + + + + Set as default + + + + + Reports + Raportit + + + + Save all errors when creating report + Tallenna kaikki virheet raporttia luodessa + + + + Save full path to files in reports + Tallenna tiedostojen koko polku raportteihin + + + + Language + + + + + SettingsDialog + + + N/A + + + + + The executable file "%1" is not available + + + + + Add a new application + Lisää uusi ohjelma + + + + Modify an application + Muokkaa ohjelmaa + + + + [Default] + + + + + [Default] + + + + + Select python binary + + + + + Select MISRA File + + + + + Select clang path + + + + + StatsDialog + + + + + + Statistics + + + + + + Project + + + + + Project: + + + + + Paths: + + + + + Include paths: + + + + + Defines: + + + + + Undefines: + + + + + + Previous Scan + + + + + Path Selected: + + + + + Number of Files Scanned: + + + + + Scan Duration: + + + + + Errors: + + + + + Warnings: + + + + + Stylistic warnings: + + + + + Portability warnings: + + + + + Performance issues: + + + + + Information messages: + + + + + Active checkers: + + + + + Checkers + + + + + History + + + + + File: + + + + + Copy to Clipboard + + + + + Pdf Export + + + + + 1 day + + + + + %1 days + + + + + 1 hour + + + + + %1 hours + + + + + 1 minute + + + + + %1 minutes + + + + + 1 second + + + + + %1 seconds + + + + + 0.%1 seconds + + + + + and + + + + + Export PDF + + + + + Project Settings + + + + + Paths + + + + + Include paths + + + + + Defines + + + + + Undefines + + + + + Path selected + + + + + Number of files scanned + + + + + Scan duration + + + + + + Errors + + + + + File: + + + + + No cppcheck build dir + + + + + + Warnings + + + + + + Style warnings + + + + + + Portability warnings + + + + + + Performance warnings + + + + + + Information messages + + + + + ThreadResult + + + %1 of %2 files checked + + + + + TranslationHandler + + + Failed to change the user interface language: + +%1 + +The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. + + + + + Cppcheck + Cppcheck + + + + TxtReport + + + inconclusive + + + + + toFilterString + + + All supported files (%1) + + + + + All files (%1) + + + + diff --git a/cppcheck-2.14.0/gui/cppcheck_fr.ts b/cppcheck-2.14.0/gui/cppcheck_fr.ts new file mode 100644 index 00000000..d4fb5241 --- /dev/null +++ b/cppcheck-2.14.0/gui/cppcheck_fr.ts @@ -0,0 +1,2981 @@ + + + + + About + + + About Cppcheck + A propos + + + + Version %1 + Version %1 + + + + Cppcheck - A tool for static C/C++ code analysis. + Cppcheck - Un outil d'analyse statique de code C/C++. + + + + This program is licensed under the terms +of the GNU General Public License version 3 + Ce programme est sous licence GNU +General Public License version 3 + + + + Visit Cppcheck homepage at %1 + Visitez le site Cppcheck : %1 + + + + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PCRE</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PicoJSON</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">TinyXML2</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Boost</li></ul></body></html> + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">pcre</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">picojson</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">tinyxml2</li></ul></body></html> + + + + + Copyright © 2007-%1 Cppcheck team. + Copyright © 2007-2021 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: + &Exécutable : + + + + &Parameters: + &Paramètres : + + + + Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. + +The following texts in parameters are replaced with appropriate values when application is executed: +(file) - Filename containing the error +(line) - Line number containing the error +(message) - Error message +(severity) - Error severity + +Example opening a file with Kate and make Kate scroll to the correct line: +Executable: kate +Parameters: -l(line) (file) + Vous pouvez ajouter une application capable d'ouvrir les fichiers d'erreurs. Spécifiez le nom de l'application, la commande à exécuter, ainsi que les paramètres de la commande + +Les textes en paramètres sont remplacés par les valeurs appropriées lorsque l'application est exécutée : +(fichier) - Fichier contenant l'erreur +(ligne) - Numéro de ligne contenant l'erreur +(message) - Message d'erreur +(sévérité) - Sévérité de l'erreur + +Exemple pour ouvrir un fichier avec Kate et se déplacer vers une ligne spécifique : +Exécutable : kate +Paramètres : -l(ligne) (fichier) + + + + &Name: + &Nom : + + + + You must specify a name, a path and optionally parameters for the application! + Vous devez spécifier un nom, un chemin, et eventuellement des paramètres en option à l'application ! + + + + ComplianceReportDialog + + + Compliance Report + + + + + Project name + + + + + Project version + + + + + Coding Standard + + + + + Misra C + + + + + Cert C + + + + + Cert C++ + + + + + List of files with md5 checksums + + + + + Compliance report + + + + + HTML files (*.html) + + + + + + Save compliance report + + + + + Failed to import '%1' (%2), can not show files in compliance report + + + + + 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 + + + + HelpDialog + + + Cppcheck GUI help + + + + + Contents + + + + + Index + + + + + Helpfile '%1' was not found + + + + + Cppcheck + + + + + LibraryAddFunctionDialog + + + Add function + Ajouter une fonction + + + + Function name(s) + Nom(s) de la fonction + + + + Number of arguments + Nombre d'arguments + + + + LibraryDialog + + + Library Editor + + + + + Open + + + + + Save + Sauvegarder + + + + Functions + + + + + Add + Ajouter + + + + noreturn + + + + + False + + + + + True + + + + + Unknown + + + + + return value must be used + + + + + ignore function in leaks checking + + + + + Arguments + + + + + Edit + Editer + + + + + Library files (*.cfg) + + + + + Open library file + + + + + Sort + + + + + Filter: + + + + + Comments + + + + + Save as + + + + + + + Cppcheck + + + + + Save the library as + + + + + Failed to load %1. %2. + + + + + Cannot open file %1. + + + + + Cannot save file %1. + + + + + LibraryEditArgDialog + + + Edit argument + + + + + <html><head/><body> +<p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> +<p>Typically, set this if the argument is a pointer, size, etc.</p> +<p>Example:</p> +<pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> +</body></html> + + + + + Not bool + + + + + <html><head/><body> +<p>Is a null parameter value allowed?</p> +<p>Typically this should be used on any pointer parameter that does not allow null.</p> +<p>Example:</p> +<pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> +</body></html> + + + + + Not null + + + + + Not uninit + + + + + String + + + + + Format string + + + + + Min size of buffer + + + + + + Type + + + + + + None + + + + + + argvalue + + + + + + mul + + + + + + strlen + + + + + + Arg + + + + + + Arg2 + + + + + and + + + + + Valid values + + + + + MainWindow + + + + + + + + + + + + + + + + + Cppcheck + + + + + Checking for updates + + + + + Hide + Cacher + + + + &File + &Fichier + + + + &View + &Affichage + + + + &Help + &Aide + + + + &Edit + &Édition + + + + Standard + Standard + + + + &License... + &Licence... + + + + A&uthors... + A&uteurs... + + + + &About... + À &Propos... + + + + &Files... + &Fichiers... + + + + Ctrl+F + + + + + &Directory... + &Répertoires... + + + + Ctrl+D + + + + + Ctrl+R + + + + + &Stop + &Arrêter + + + + Esc + + + + + &Save results to file... + &Sauvegarder les résultats dans un fichier... + + + + Ctrl+S + + + + + &Quit + &Quitter + + + + &Clear results + &Effacer les résultats + + + + &Preferences + &Préférences + + + + &Check all + &Tout cocher + + + + &Uncheck all + &Tout décocher + + + + Collapse &all + &Tout réduire + + + + &Expand all + &Tout dérouler + + + + &Contents + &Contenus + + + + Open the help contents + Ouvir l'aide + + + + F1 + + + + + Compliance report... + + + + + License + Licence + + + + Authors + Auteurs + + + + Save the report file + Sauvegarder le rapport + + + + + XML files (*.xml) + Fichiers XML (*.xml) + + + + About + + + + + Text files (*.txt) + Fichiers Texte (*.txt) + + + + CSV files (*.csv) + Fichiers CSV (*.csv) + + + + &Toolbars + &Boite à outils + + + + Categories + Catégories + + + + + Show style warnings + Afficher les avertissements de style + + + + + Show errors + Afficher les erreurs + + + + &Standard + + + + + Standard items + + + + + Toolbar + + + + + &Categories + + + + + Error categories + + + + + &Open XML... + &Ouvrir un fichier XML... + + + + Open P&roject File... + Ouvrir un P&rojet... + + + + &New Project File... + &Nouveau Projet... + + + + &Log View + &Journal + + + + Log View + Journal + + + + C&lose Project File + F&ermer le projet + + + + &Edit Project File... + &Editer le projet + + + + &Statistics + Statistiques + + + + + Show warnings + Afficher les avertissements + + + + + Show performance warnings + Afficher les avertissements de performance + + + + Show &hidden + + + + + + Information + Information + + + + Show information messages + Afficher les messages d'information + + + + Show portability warnings + Afficher les problèmes de portabilité + + + + You must close the project file before selecting new files or directories! + Vous devez d'abord fermer le projet avant de choisir des fichiers/répertoires + + + + Open the report file + Ouvrir le rapport + + + + Cannot generate a compliance report right now, an analysis must finish successfully. Try to reanalyze the code and ensure there are no critical errors. + + + + + Project files (*.cppcheck);;All files(*.*) + + + + + Select Project File + + + + + To check the project using addons, you need a build directory. + + + + + Failed to open file + + + + + Unknown project file format + + + + + Failed to import project file + + + + + Failed to import '%1': %2 + +Analysis is stopped. + + + + + Failed to import '%1' (%2), analysis is stopped + + + + + Select Project Filename + + + + + No project file loaded + + + + + Install + + + + + New version available: %1. %2 + + + + + There was a problem with loading the editor application settings. + +This is probably because the settings were changed between the Cppcheck versions. Please check (and fix) the editor application settings, otherwise the editor program might not start correctly. + + + + + &Filter + &Filtre + + + + Filter results + + + + + + Quick Filter: + Filtre rapide : + + + + Found project file: %1 + +Do you want to load this project file instead? + + + + + + + + Project: + Projet : + + + + The project file + +%1 + + could not be found! + +Do you want to remove the file from the recently used projects -list? + + + + + Filter + Filtre + + + + Windows 32-bit ANSI + + + + + Windows 32-bit Unicode + + + + + Unix 32-bit + + + + + Unix 64-bit + + + + + Windows 64-bit + + + + + Cppcheck GUI - Command line parameters + + + + + C++ standard + + + + + + + + Error + Erreur + + + + File not found + Fichier introuvable + + + + Bad XML + Mauvais fichier XML + + + + Missing attribute + Attribut manquant + + + + Bad attribute value + Mauvaise valeur d'attribut + + + + Failed to load the selected library '%1'. +%2 + Echec lors du chargement de la bibliothèque '%1'. +%2 + + + + Unsupported format + Format non supporté + + + + The library '%1' contains unknown elements: +%2 + La bibliothèque '%1' contient des éléments inconnus: +%2 + + + + Duplicate platform type + + + + + Platform type redefined + + + + + &Print... + &Imprimer... + + + + Print the Current Report + Imprimer le rapport + + + + Print Pre&view... + Apercu d'impression... + + + + Open a Print Preview Dialog for the Current Results + + + + + Open library editor + + + + + Unknown element + + + + + Unknown issue + + + + + Select configuration + + + + + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> This option is for installation scripts so they can configure the directory where + datafiles are located (translations, cfg). The GUI is not started when this option + is used. + + + + + Build dir '%1' does not exist, create it? + + + + + + Analyze files + + + + + + Analyze directory + + + + + &Reanalyze modified files + + + + + + Stop analysis + + + + + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) + + + + + No suitable files found to analyze! + + + + + Select files to analyze + + + + + Select directory to analyze + + + + + Select the configuration that will be analyzed + + + + + Found project files from the directory. + +Do you want to proceed analysis without using any of these project files? + + + + + Duplicate define + + + + + File not found: '%1' + + + + + Failed to load/setup addon %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. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + +Analysis is aborted. + + + + + Failed to load %1 - %2 + +Analysis is aborted. + + + + + + %1 + +Analysis is aborted. + + + + + Analyzer is running. + +Do you want to stop the analysis and exit Cppcheck? + + + + + A&nalyze + + + + + &C standard + + + + + Reanal&yze all files + + + + + Ctrl+Q + + + + + Style war&nings + + + + + E&rrors + + + + + Ctrl+Shift+O + + + + + Sh&ow Scratchpad... + + + + + Ctrl+Shift+N + + + + + &Warnings + + + + + Per&formance warnings + + + + + &Information + + + + + &Portability + + + + + Show Cppcheck results + + + + + Clang + + + + + Show Clang results + + + + + P&latforms + + + + + C++&11 + + + + + C&99 + + + + + &Posix + + + + + C&11 + + + + + &C89 + + + + + &C++03 + + + + + &Library Editor... + + + + + &Auto-detect language + + + + + &Enforce C++ + + + + + E&nforce C + + + + + C++14 + + + + + Project files (*.cppcheck) + + + + + Reanalyze and check library + + + + + Check configuration (defines, includes) + + + + + C++17 + + + + + C++20 + + + + + C/C++ Source + + + + + Compile database + + + + + Visual Studio + + + + + Borland C++ Builder 6 + + + + + Current results will be cleared. + +Opening a new XML file will clear current results. +Do you want to proceed? + + + + + NewSuppressionDialog + + + New suppression + + + + + Error ID + + + + + File name + + + + + Line number + + + + + Symbol name + + + + + Edit suppression + + + + + Platforms + + + Unix 32-bit + + + + + Unix 64-bit + + + + + Windows 32-bit ANSI + + + + + Windows 32-bit Unicode + + + + + Windows 64-bit + + + + + Native + + + + + ProjectFile + + + Project File + Fichier Projet + + + + Paths: + Chemins : + + + + Defines: + + + + + + Add... + Ajouter... + + + + + + Edit + Editer + + + + + + + Remove + Supprimer + + + + Up + Monter + + + + Down + Descendre + + + + Suppressions + Suppressions + + + + Add + Ajouter + + + + Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. + + + + + ... + + + + + Include Paths: + + + + + Paths and Defines + + + + + <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> + + + + + Analyze all Visual Studio configurations + + + + + Root path: + + + + + Warning tags (separated by semicolon) + + + + + Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) + + + + + Selected VS Configurations + + + + + Types and Functions + + + + + Libraries + + + + + Parser + + + + + Cppcheck (built in) + + + + + Check that each class has a safe public interface + + + + + Limit analysis + + + + + + Addons + + + + + Note: Addons require <a href="https://www.python.org/">Python</a> being installed. + + + + + Y2038 + + + + + Thread safety + + + + + Coding standards + + + + + Cert C + + + + + CERT-INT35-C: int precision (if size equals precision, you can leave empty) + + + + + Misra C++ 2008 + + + + + Autosar + + + + + Bug hunting + + + + + Clang analyzer + + + + + Clang-tidy + + + + + + Browse... + + + + + Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int + + + + + Platform + + + + + This is a workfolder that Cppcheck will use for various purposes. + + + + + Clang (experimental) + + + + + Check level + + + + + Normal -- meant for normal analysis in CI. Analysis should finish in reasonable time. + + + + + Exhaustive -- meant for nightly builds etc. Analysis time can be longer (10x slower than compilation is OK). + + + + + If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. + + + + + Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) + + + + + Max recursion in template instantiation + + + + + Warning options + + + + + Filepaths in warnings will be relative to this path + + + + + If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. + + + + + Exclude source files + + + + + Exclude folder... + + + + + Exclude file... + + + + + Misra C + + + + + 2012 + + + + + 2023 + + + + + MISRA rule texts + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + Cert C++ + + + + + Bug hunting (Premium) + + + + + External tools + + + + + Import Project (Visual studio / compile database/ Borland C++ Builder 6) + + + + + Undefines: + + + + + Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 + + + + + + Analysis + + + + + Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) + Check code in unused templates (slower and less accurate analysis) + + + + + Max CTU depth + + + + + ProjectFileDialog + + + Project file: %1 + Fichier projet : %1 + + + + Select include directory + Selectionner un répertoire à inclure + + + + Select directory to ignore + Selectionner un répertoire à ignorer + + + + Select a directory to check + Selectionner un répertoire à vérifier + + + + Select Cppcheck build dir + + + + + Import Project + + + + + Clang-tidy (not found) + + + + + Source files + + + + + All files + + + + + Exclude file + + + + + Select MISRA rule texts file + + + + + MISRA rule texts file (%1) + + + + + Visual Studio + + + + + Compile database + + + + + Borland C++ Builder 6 + + + + + QObject + + + Language file %1 not found! + Fichier de langue %1 non trouvé ! + + + + Failed to load translation for language %1 from file %2 + Erreur lors du chargement de la langue %1 depuis le fichier %2 + + + + Unknown language specified! + + + + + line %1: Unhandled element %2 + + + + + line %1: Mandatory attribute '%2' missing in '%3' + + + + + (Not found) + + + + + Thin + + + + + ExtraLight + + + + + Light + + + + + Normal + + + + + Medium + + + + + DemiBold + + + + + Bold + + + + + ExtraBold + + + + + Black + + + + + Editor Foreground Color + + + + + Editor Background Color + + + + + Highlight Background Color + + + + + Line Number Foreground Color + + + + + Line Number Background Color + + + + + Keyword Foreground Color + + + + + Keyword Font Weight + + + + + Class Font Weight + + + + + Quote Foreground Color + + + + + Quote Font Weight + + + + + Comment Foreground Color + + + + + Comment Font Weight + + + + + Symbol Foreground Color + + + + + Symbol Background Color + + + + + Symbol Font Weight + + + + + Set to Default Light + + + + + Set to Default Dark + + + + + Class Foreground Color + + + + + QPlatformTheme + + + OK + OK + + + + Cancel + Annuler + + + + Close + Fermer + + + + Save + Sauvegarder + + + + ResultsTree + + + File + Fichier + + + + Severity + Sévérité + + + + Line + Ligne + + + + Undefined file + Fichier indéterminé + + + + + Cppcheck + + + + + Could not start %1 + +Please check the application path and parameters are correct. + Ne peut pas démarrer %1 + +Merci de vérifier que le chemin de l'application et que les paramètres sont corrects. + + + + style + erreur de style + + + + error + erreur + + + + Summary + Résumé + + + + Hide + Cacher + + + + Could not find the file! + Fichier introuvable ! + + + + Select Directory + Selectionner dossier + + + + warning + avertissement + + + + performance + performance + + + + portability + portabilité + + + + information + information + + + + debug + débogage + + + + internal + + + + + No editor application configured. + +Configure the editor application for Cppcheck in preferences/Applications. + + + + + No default editor application selected. + +Please select the default editor application in preferences/Applications. + + + + + Id + Id + + + + Hide all with id + + + + + Open containing folder + Ouvrir l'emplacement du fichier + + + + Inconclusive + + + + + Recheck + Revérifier + + + + note + + + + + Suppress selected id(s) + + + + + + Tag + + + + + No tag + + + + + Since date + + + + + Could not find file: + + + + + Please select the folder '%1' + + + + + Select Directory '%1' + + + + + Please select the directory where file is located. + + + + + Copy + + + + + ResultsView + + + Results + Résultats + + + + + Cppcheck + + + + + No errors found. + Pas d'erreurs trouvées. + + + + Errors were found, but they are configured to be hidden. +To toggle what kind of errors are shown, open view menu. + Des erreurs ont été trouvées mais sont configurées pour rester cachées. +Pour configurer les erreurs affichées, ouvrez le menu d'affichage. + + + + Bug hunting analysis is incomplete + + + + + Analysis was stopped + + + + + There was a critical error with id '%1' + + + + + when checking %1 + + + + + when checking a file + + + + + Analysis was aborted. + + + + + + Failed to save the report. + Erreur lors de la sauvegarde du rapport. + + + + + Failed to read the report. + Erreur lors de la lecture du rapport + + + + %p% (%1 of %2 files checked) + %p% (%1 fichiers sur %2 vérifiés) + + + + Id + Id + + + + Print Report + Imprimer le rapport + + + + No errors found, nothing to print. + Aucune erreur trouvée. Il n'y a rien à imprimer + + + + First included by + + + + + XML format version 1 is no longer supported. + + + + + Critical errors + + + + + Analysis Log + + + + + Warning Details + + + + + Clear Log + + + + + Copy this Log entry + + + + + Copy complete Log + + + + + ScratchPad + + + Scratchpad + + + + + filename + + + + + Check + + + + + Copy or write some C/C++ code here: + + + + + Optionally enter a filename (mainly for automatic language detection) and click on "Check": + + + + + Settings + + + Preferences + Préférences + + + + General + Général + + + + Number of threads: + Nombre de fils : + + + + Show full path of files + Montrer le chemin complet des fichiers + + + + Show "No errors found" message when no errors found + Afficher un message "Pas d'erreur trouvée" lorsque aucune erreur est trouvée + + + + Check for updates + + + + + 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 + + + + Add... + Ajouter... + + + + Ideal count: + + + + + Force checking all #ifdef configurations + + + + + Enable inline suppressions + + + + + Language + Langue + + + + Remove + Supprimer + + + + + Edit... + Editer... + + + + Set as default + + + + + Display error Id in column "Id" + Afficher l'identifiant d'erreur Id dans la colonne "Id" + + + + Check for inconclusive errors also + + + + + Show internal warnings in log + Montrer les avertissements internes dans le journal + + + + Show statistics on check completion + + + + + Addons + + + + + Python binary (leave this empty to use python in the PATH) + + + + + + + ... + + + + + Clang + + + + + Clang path (leave empty to use system PATH) + + + + + Visual Studio headers + + + + + <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> + + + + + MISRA addon + + + + + MISRA rule texts file + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + Code Editor + + + + + Code Editor Style + + + + + Default Light Style + + + + + Default Dark Style + + + + + Custom + + + + + System Style + + + + + SettingsDialog + + + Add a new application + Ajouter une nouvelle application + + + + Modify an application + Modifier une application + + + + N/A + + + + + [Default] + + + + + [Default] + + + + + The executable file "%1" is not available + + + + + Select python binary + + + + + Select clang path + + + + + Select MISRA File + + + + + StatsDialog + + + + + + Statistics + Statistiques + + + + + Project + Projet + + + + Project: + Projet : + + + + Paths: + Chemins : + + + + Include paths: + Inclure les chemins : + + + + Defines: + + + + + + Previous Scan + Analyse précédente + + + + Path Selected: + Chemin sélectionné : + + + + Number of Files Scanned: + Nombre de fichiers analysés : + + + + Scan Duration: + Durée de l'analyse : + + + + Errors: + Erreurs : + + + + Warnings: + Avertissements + + + + Stylistic warnings: + Avertissements de style + + + + Portability warnings: + Avertissements de portabilité + + + + Performance issues: + Problème de performance + + + + Information messages: + Messages d'information : + + + + Active checkers: + + + + + Checkers + + + + + Copy to Clipboard + Copier vers le presse-papier + + + + 1 day + + + + + %1 days + + + + + 1 hour + + + + + %1 hours + + + + + 1 minute + + + + + %1 minutes + + + + + 1 second + + + + + %1 seconds + + + + + 0.%1 seconds + + + + + and + + + + + Project Settings + + + + + Paths + Chemins + + + + Include paths + + + + + Defines + + + + + Path selected + + + + + Number of files scanned + + + + + Scan duration + + + + + + Errors + Erreurs + + + + + Warnings + Avertissements + + + + + Style warnings + Avertissement de style + + + + + Portability warnings + + + + + + Performance warnings + Avertissements de performance + + + + + Information messages + + + + + Pdf Export + + + + + Export PDF + + + + + History + + + + + File: + + + + + File: + + + + + No cppcheck build dir + + + + + Undefines: + + + + + Undefines + + + + + ThreadResult + + + %1 of %2 files checked + + + + + TranslationHandler + + + Failed to change the user interface language: + +%1 + +The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. + + + + + Cppcheck + + + + + TxtReport + + + inconclusive + + + + + toFilterString + + + All supported files (%1) + + + + + All files (%1) + + + + diff --git a/cppcheck-2.14.0/gui/cppcheck_it.ts b/cppcheck-2.14.0/gui/cppcheck_it.ts new file mode 100644 index 00000000..dd076580 --- /dev/null +++ b/cppcheck-2.14.0/gui/cppcheck_it.ts @@ -0,0 +1,3020 @@ + + + + + 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-%1 Cppcheck team. + Copyright © 2007-2021 Cppcheck team. + Copyright © 2007-2021 il team Cppcheck. + + + + This program is licensed under the terms +of the GNU General Public License version 3 + Questo programma è rilasciato sotto i termini +della GNU General Public License versione 3 + + + + Visit Cppcheck homepage at %1 + Visita la pagina iniziale di Cppcheck all'indirizzo: %1 + + + + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PCRE</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PicoJSON</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">TinyXML2</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Boost</li></ul></body></html> + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">pcre</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">picojson</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">tinyxml2</li></ul></body></html> + + + + + ApplicationDialog + + + Add an application + Aggiungi un'applicazione + + + + Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. + +The following texts in parameters are replaced with appropriate values when application is executed: +(file) - Filename containing the error +(line) - Line number containing the error +(message) - Error message +(severity) - Error severity + +Example opening a file with Kate and make Kate scroll to the correct line: +Executable: kate +Parameters: -l(line) (file) + Qui puoi aggiungere un'applicazione che può aprire i file contenente l'errore. Specifica un nome per l'applicazione, l'eseguibile dell'applicazione e i parametri di comando per l'applicazione. + +I seguenti testi nei parametri sono sostituiti con appropriati valori quando l'applicazione è eseguita: +(file) - Il nome del file contenente l'errore +(line) - La linea contenente l'errore +(message) - Messaggio d'errore +(severity) - Severità dell'errore + +Un'esempio di apertura di un file con Kate e lo scorrimento alla giusta linea: +Eseguibile: kate +Parametri: -l(line) (file) + + + + + &Name: + &Nome: + + + + &Executable: + &Eseguibile: + + + + &Parameters: + &Parametri: + + + + Browse + Esplora + + + + Executable files (*.exe);;All files(*.*) + File di esecuzione (*.exe);;Tutti i file(*.*) + + + + Select viewer application + Seleziona l'applicazione di lettura + + + + Cppcheck + Cppcheck + + + + You must specify a name, a path and optionally parameters for the application! + Devi specificare un nome, un percorso ed, opzionalmente, i parametri per l'applicazione! + + + + ComplianceReportDialog + + + Compliance Report + + + + + Project name + + + + + Project version + + + + + Coding Standard + + + + + Misra C + + + + + Cert C + + + + + Cert C++ + + + + + List of files with md5 checksums + + + + + Compliance report + + + + + HTML files (*.html) + + + + + + Save compliance report + + + + + Failed to import '%1' (%2), can not show files in compliance report + + + + + 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 + + + + HelpDialog + + + Cppcheck GUI help + + + + + Contents + + + + + Index + + + + + Helpfile '%1' was not found + + + + + Cppcheck + Cppcheck + + + + LibraryAddFunctionDialog + + + Add function + + + + + Function name(s) + + + + + Number of arguments + + + + + LibraryDialog + + + Library Editor + + + + + Open + + + + + Save + + + + + Save as + + + + + Functions + + + + + Sort + + + + + Add + + + + + Filter: + + + + + Comments + + + + + noreturn + + + + + False + + + + + True + + + + + Unknown + + + + + return value must be used + + + + + ignore function in leaks checking + + + + + Arguments + + + + + Edit + Modifica + + + + + Library files (*.cfg) + + + + + Open library file + + + + + + + Cppcheck + Cppcheck + + + + Cannot open file %1. + Can not open file %1. + + + + + Failed to load %1. %2. + + + + + Cannot save file %1. + Can not save file %1. + + + + + Save the library as + + + + + LibraryEditArgDialog + + + Edit argument + + + + + <html><head/><body> +<p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> +<p>Typically, set this if the argument is a pointer, size, etc.</p> +<p>Example:</p> +<pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> +</body></html> + + + + + Not bool + + + + + <html><head/><body> +<p>Is a null parameter value allowed?</p> +<p>Typically this should be used on any pointer parameter that does not allow null.</p> +<p>Example:</p> +<pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> +</body></html> + + + + + Not null + + + + + Not uninit + + + + + String + + + + + Format string + + + + + Min size of buffer + + + + + + Type + + + + + + None + + + + + + argvalue + + + + + + mul + + + + + + strlen + + + + + + Arg + + + + + + Arg2 + + + + + and + + + + + Valid values + + + + + MainWindow + + + + + + + + + + + + + + + + + Cppcheck + Cppcheck + + + + A&nalyze + + + + + Standard + Standard + + + + &File + &File + + + + &View + &Visualizza + + + + &Toolbars + &Barre degli strumenti + + + + C++ standard + + + + + &C standard + C standard + + + + + &Edit + &Modifica + + + + &License... + &Licenza... + + + + A&uthors... + A&utori... + + + + &About... + I&nformazioni su... + + + + &Files... + &File... + + + + + Analyze files + Check files + Scansiona i file + + + + Ctrl+F + Ctrl+F + + + + &Directory... + &Cartella... + + + + + Analyze directory + Check directory + Scansiona la cartella + + + + Ctrl+D + Ctrl+D + + + + Ctrl+R + Ctrl+R + + + + &Stop + &Ferma + + + + + Stop analysis + Stop checking + Ferma la scansione + + + + Esc + Esc + + + + &Save results to file... + &Salva i risultati nel file... + + + + Ctrl+S + Ctrl+S + + + + &Quit + &Esci + + + + &Clear results + &Cancella i risultati + + + + &Preferences + &Preferenze + + + + + Show errors + Mostra gli errori + + + + + Show warnings + Mostra gli avvisi + + + + + Show performance warnings + Mostra gli avvisi sulle prestazioni + + + + Show &hidden + Mostra &i nascosti + + + + + Information + Informazione + + + + Show information messages + Mostra messaggi di informazione + + + + Show portability warnings + Mostra gli avvisi sulla portabilità + + + + Show Cppcheck results + + + + + Clang + + + + + Show Clang results + + + + + &Filter + &Filtro + + + + Filter results + Filtra i risultati + + + + Windows 32-bit ANSI + Windows 32-bit, ANSI + + + + Windows 32-bit Unicode + Windows 32-bit, Unicode + + + + Unix 32-bit + Unix 32-bit + + + + Unix 64-bit + Unix 64-bit + + + + Windows 64-bit + Windows 64-bit + + + + &Print... + + + + + Print the Current Report + + + + + Print Pre&view... + + + + + Open a Print Preview Dialog for the Current Results + + + + + Open library editor + + + + + &Check all + &Seleziona tutto + + + + Checking for updates + + + + + Hide + Nascondi + + + + Filter + Filtro + + + + &Reanalyze modified files + &Recheck modified files + + + + + Reanal&yze all files + + + + + Ctrl+Q + + + + + Style war&nings + + + + + E&rrors + + + + + &Uncheck all + &Deseleziona tutto + + + + Collapse &all + Riduci &tutto + + + + &Expand all + &Espandi tutto + + + + &Standard + &Standard + + + + Standard items + Oggetti standard + + + + Toolbar + Barra degli strumenti + + + + &Categories + &Categorie + + + + Error categories + Categorie di errore + + + + &Open XML... + &Apri XML... + + + + Open P&roject File... + Apri file di p&rogetto... + + + + Ctrl+Shift+O + + + + + Sh&ow Scratchpad... + + + + + &New Project File... + &Nuovo file di progetto... + + + + Ctrl+Shift+N + + + + + &Log View + &Visualizza il rapporto + + + + Log View + Visualizza il rapporto + + + + C&lose Project File + C&hiudi il file di progetto + + + + &Edit Project File... + &Modifica il file di progetto... + + + + &Statistics + &Statistiche + + + + &Warnings + + + + + Per&formance warnings + + + + + &Information + + + + + &Portability + + + + + P&latforms + + + + + C++&11 + + + + + C&99 + + + + + &Posix + + + + + C&11 + + + + + &C89 + + + + + &C++03 + + + + + &Library Editor... + + + + + &Auto-detect language + + + + + &Enforce C++ + + + + + E&nforce C + + + + + C++14 + C++14 + + + + Reanalyze and check library + + + + + Check configuration (defines, includes) + + + + + C++17 + C++17 + + + + C++20 + C++20 + + + + Compliance report... + + + + + &Contents + &Contenuti + + + + Categories + Categorie + + + + + Show style warnings + Mostra gli avvisi sullo stile + + + + Open the help contents + Apri i contenuti di aiuto + + + + F1 + F1 + + + + &Help + &Aiuto + + + + + Quick Filter: + Rapido filtro: + + + + Select configuration + + + + + Found project file: %1 + +Do you want to load this project file instead? + Trovato il file di progetto: %1 + +Vuoi piuttosto caricare questo file di progetto? + + + + File not found + + + + + Bad XML + + + + + Missing attribute + + + + + Bad attribute value + + + + + Unsupported format + + + + + Duplicate define + + + + + Failed to load the selected library '%1'. +%2 + + + + + File not found: '%1' + + + + + Failed to load/setup addon %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. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + +Analysis is aborted. + + + + + Failed to load %1 - %2 + +Analysis is aborted. + + + + + + %1 + +Analysis is aborted. + + + + + License + Licenza + + + + Authors + Autori + + + + 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! + + + + The library '%1' contains unknown elements: +%2 + + + + + Duplicate platform type + + + + + Platform type redefined + + + + + Unknown element + + + + + Unknown issue + + + + + + + + Error + + + + + Open the report file + Apri il file di rapporto + + + + Text files (*.txt) + File di testo (*.txt) + + + + CSV files (*.csv) + Files CSV (*.csv) + + + + Project files (*.cppcheck);;All files(*.*) + Files di progetto (*.cppcheck);;Tutti i files(*.*) + + + + Select Project File + Seleziona il file di progetto + + + + + + + Project: + Progetto: + + + + No suitable files found to analyze! + + + + + C/C++ Source + + + + + Compile database + + + + + Visual Studio + + + + + Borland C++ Builder 6 + + + + + Select files to analyze + + + + + Select directory to analyze + + + + + Select the configuration that will be analyzed + + + + + Found project files from the directory. + +Do you want to proceed analysis without using any of these project files? + + + + + Current results will be cleared. + +Opening a new XML file will clear current results. +Do you want to proceed? + + + + + Analyzer is running. + +Do you want to stop the analysis and exit Cppcheck? + + + + + About + + + + + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) + + + + + Cannot generate a compliance report right now, an analysis must finish successfully. Try to reanalyze the code and ensure there are no critical errors. + + + + + Build dir '%1' does not exist, create it? + + + + + To check the project using addons, you need a build directory. + + + + + Failed to open file + + + + + Unknown project file format + + + + + Failed to import project file + + + + + Failed to import '%1': %2 + +Analysis is stopped. + + + + + Failed to import '%1' (%2), analysis is stopped + + + + + Project files (*.cppcheck) + + + + + Select Project Filename + Seleziona il nome del file di progetto + + + + No project file loaded + Nessun file di progetto caricato + + + + The project file + +%1 + + could not be found! + +Do you want to remove the file from the recently used projects -list? + Il file di progetto + +%1 + + non è stato trovato! + +Vuoi rimuovere il file dalla lista dei progetti recentemente usati? + + + + Install + + + + + New version available: %1. %2 + + + + + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> This option is for installation scripts so they can configure the directory where + datafiles are located (translations, cfg). The GUI is not started when this option + is used. + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) + + + + + Cppcheck GUI - Command line parameters + + + + + NewSuppressionDialog + + + New suppression + + + + + Error ID + + + + + File name + + + + + Line number + + + + + Symbol name + + + + + Edit suppression + + + + + Platforms + + + Native + + + + + Unix 32-bit + Unix 32-bit + + + + Unix 64-bit + Unix 64-bit + + + + Windows 32-bit ANSI + Windows 32-bit, ANSI + + + + Windows 32-bit Unicode + Windows 32-bit, Unicode + + + + Windows 64-bit + Windows 64-bit + + + + ProjectFile + + + Project File + File di progetto + + + + Paths and Defines + + + + + Import Project (Visual studio / compile database/ Borland C++ Builder 6) + Import Project (Visual studio / compile database) + + + + + Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int + Defines must be separated by a semicolon ';' + + + + + Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. + + + + + If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. + + + + + Exclude source files + + + + + Exclude folder... + + + + + Exclude file... + + + + + Misra C + + + + + 2012 + + + + + 2023 + + + + + MISRA rule texts + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + ... + + + + + <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> + + + + + + Browse... + + + + + Analyze all Visual Studio configurations + + + + + Selected VS Configurations + + + + + Paths: + Percorsi: + + + + + Add... + Aggiungi... + + + + + + Edit + Modifica + + + + + + + Remove + Rimuovi + + + + Undefines: + + + + + Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 + + + + + Include Paths: + + + + + Types and Functions + + + + + + Analysis + + + + + This is a workfolder that Cppcheck will use for various purposes. + + + + + Parser + + + + + Cppcheck (built in) + + + + + Check level + + + + + Normal -- meant for normal analysis in CI. Analysis should finish in reasonable time. + + + + + Exhaustive -- meant for nightly builds etc. Analysis time can be longer (10x slower than compilation is OK). + + + + + Check that each class has a safe public interface + + + + + Limit analysis + + + + + Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) + Check code in unused templates (slower and less accurate analysis) + + + + + Max CTU depth + + + + + Cert C + + + + + CERT-INT35-C: int precision (if size equals precision, you can leave empty) + + + + + Misra C++ 2008 + + + + + Autosar + + + + + Bug hunting + + + + + External tools + + + + + Up + Su + + + + Down + Giù + + + + Platform + + + + + Clang (experimental) + + + + + If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. + + + + + Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) + + + + + Max recursion in template instantiation + + + + + Warning options + + + + + Root path: + + + + + Filepaths in warnings will be relative to this path + + + + + Warning tags (separated by semicolon) + + + + + Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) + + + + + Libraries + + + + + Suppressions + + + + + Add + + + + + + Addons + + + + + Note: Addons require <a href="https://www.python.org/">Python</a> being installed. + + + + + Y2038 + + + + + Thread safety + + + + + Coding standards + + + + + Cert C++ + + + + + Bug hunting (Premium) + + + + + Clang analyzer + + + + + Clang-tidy + + + + + Defines: + Definizioni: + + + + ProjectFileDialog + + + Project file: %1 + File di progetto: %1 + + + + Select Cppcheck build dir + + + + + Select include directory + Seleziona la cartella da includere + + + + Select a directory to check + Seleziona una cartella da scansionare + + + + Clang-tidy (not found) + + + + + Visual Studio + + + + + Compile database + + + + + Borland C++ Builder 6 + + + + + Import Project + + + + + Select directory to ignore + Seleziona la cartella da ignorare + + + + Source files + + + + + All files + + + + + Exclude file + + + + + Select MISRA rule texts file + + + + + MISRA rule texts file (%1) + + + + + QObject + + + Unknown language specified! + Lingua specificata sconosciuta! + + + + Language file %1 not found! + Il file di lingua %1 non trovato! + + + + Failed to load translation for language %1 from file %2 + Fallito il tentativo di aprire la traduzione per la lingua %1 dal file %2 + + + + line %1: Unhandled element %2 + + + + + line %1: Mandatory attribute '%2' missing in '%3' + + + + + (Not found) + + + + + Thin + + + + + ExtraLight + + + + + Light + + + + + Normal + + + + + Medium + + + + + DemiBold + + + + + Bold + + + + + ExtraBold + + + + + Black + + + + + Editor Foreground Color + + + + + Editor Background Color + + + + + Highlight Background Color + + + + + Line Number Foreground Color + + + + + Line Number Background Color + + + + + Keyword Foreground Color + + + + + Keyword Font Weight + + + + + Class Foreground Color + Class ForegroundColor + + + + + Class Font Weight + + + + + Quote Foreground Color + + + + + Quote Font Weight + + + + + Comment Foreground Color + + + + + Comment Font Weight + + + + + Symbol Foreground Color + + + + + Symbol Background Color + + + + + Symbol Font Weight + + + + + Set to Default Light + + + + + Set to Default Dark + + + + + QPlatformTheme + + + OK + + + + + Cancel + + + + + Close + Chiudi + + + + Save + + + + + ResultsTree + + + File + File + + + + Severity + Severità + + + + Line + Linea + + + + Summary + Riassunto + + + + Undefined file + File indefinito + + + + Copy + + + + + Could not find file: + + + + + Please select the folder '%1' + + + + + Select Directory '%1' + + + + + Please select the directory where file is located. + + + + + debug + debug + + + + note + + + + + Recheck + + + + + Hide + Nascondi + + + + Hide all with id + + + + + Suppress selected id(s) + + + + + Open containing folder + + + + + internal + + + + + + Tag + + + + + No tag + + + + + + Cppcheck + Cppcheck + + + + No editor application configured. + +Configure the editor application for Cppcheck in preferences/Applications. + Nessun'applicazione di scrittura è stato configurato. + +Configura l'applicazione di scrittura per Cppcheck in Preferenze/Applicazioni. + + + + No default editor application selected. + +Please select the default editor application in preferences/Applications. + Nessun'applicazione di scrittura predefinito Keine è stato selezionato. + +Per favore seleziona l'applicazione di scrittura predefinito in Preferenze/Applicazioni. + + + + Could not find the file! + Non è stato possibile trovare il file! + + + + Could not start %1 + +Please check the application path and parameters are correct. + Non è stato possibile avviare %1 + +Per favore verifica che il percorso dell'applicazione e i parametri siano corretti. + + + + Select Directory + Seleziona Cartella + + + + Id + Id + + + + Inconclusive + + + + + Since date + + + + + style + stile + + + + error + errore + + + + warning + avviso + + + + performance + performance + + + + portability + portabilità + + + + information + Informazione + + + + ResultsView + + + Print Report + + + + + No errors found, nothing to print. + + + + + %p% (%1 of %2 files checked) + %p% (%1 su %2 file scansionati) + + + + + Cppcheck + Cppcheck + + + + No errors found. + Nessun errore trovato. + + + + Errors were found, but they are configured to be hidden. +To toggle what kind of errors are shown, open view menu. + Sono stati trovati errori, ma sono stati configurati per essere nascosti. +Per vedere il tipo di errori che sono mostrati, apri il menu Visualizza. + + + + + Failed to read the report. + Apertura del report fallito. + + + + XML format version 1 is no longer supported. + + + + + First included by + + + + + Id + Id + + + + Bug hunting analysis is incomplete + + + + + Clear Log + + + + + Copy this Log entry + + + + + Copy complete Log + + + + + Analysis was stopped + + + + + There was a critical error with id '%1' + + + + + when checking %1 + + + + + when checking a file + + + + + Analysis was aborted. + + + + + + Failed to save the report. + Salvataggio del report fallito. + + + + Results + Risultati + + + + Critical errors + + + + + Analysis Log + + + + + Warning Details + + + + + ScratchPad + + + Scratchpad + Blocchetto per appunti + + + + Copy or write some C/C++ code here: + + + + + Optionally enter a filename (mainly for automatic language detection) and click on "Check": + + + + + filename + Nome file + + + + Check + Scansiona + + + + Settings + + + Preferences + Preferenze + + + + General + Generale + + + + Add... + Aggiungi... + + + + Number of threads: + Numero di threads: + + + + Ideal count: + Numero ideale: + + + + Force checking all #ifdef configurations + Forza la scansione di tutte le configurazioni #ifdef + + + + Show full path of files + Mostra tutto il percorso dei files + + + + Show "No errors found" message when no errors found + Mostra il messaggio "Nessun errore trovato" quando nessun errore è stato trovato + + + + Display error Id in column "Id" + Mostra l'id dell'errore nella colonna "Id" + + + + Enable inline suppressions + Abilita le soppressioni + + + + Check for inconclusive errors also + + + + + Show statistics on check completion + + + + + Check for updates + + + + + Show internal warnings in log + + + + + Addons + + + + + Python binary (leave this empty to use python in the PATH) + + + + + + + ... + + + + + MISRA addon + + + + + MISRA rule texts file + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + Clang + + + + + Clang path (leave empty to use system PATH) + + + + + Visual Studio headers + + + + + <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> + + + + + Code Editor + + + + + Code Editor Style + + + + + System Style + + + + + Default Light Style + + + + + Default Dark Style + + + + + Custom + + + + + Remove + 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 + + + + SettingsDialog + + + N/A + N/A + + + + The executable file "%1" is not available + + + + + Add a new application + Aggiungi una nuova applicazione + + + + Modify an application + Modifica un'applicazione + + + + [Default] + + + + + [Default] + [Predefinito] + + + + Select python binary + + + + + Select MISRA File + + + + + Select clang path + + + + + StatsDialog + + + + + + Statistics + Statistiche + + + + + Project + Progetto + + + + Project: + Progetto: + + + + Paths: + Percorsi: + + + + Include paths: + Percorsi di inclusione: + + + + Defines: + Definizioni: + + + + Undefines: + + + + + + Previous Scan + Precedente Scansione + + + + Path Selected: + Selezionato percorso: + + + + Number of Files Scanned: + Numero di Files Scansionati: + + + + Scan Duration: + Durata della scansione: + + + + Errors: + Errori: + + + + Warnings: + Avvisi: + + + + Stylistic warnings: + Avvisi sullo stile: + + + + Portability warnings: + Avvisi sulla portabilità: + + + + Performance issues: + Casi sulla performance: + + + + Information messages: + Messaggi di informazione: + + + + Active checkers: + + + + + Checkers + + + + + History + + + + + File: + + + + + Copy to Clipboard + Copia negli Appunti + + + + Pdf Export + + + + + 1 day + 1 giorno + + + + %1 days + %1 giorni + + + + 1 hour + 1 ora + + + + %1 hours + %1 ore + + + + 1 minute + 1 minuto + + + + %1 minutes + %1 minuti + + + + 1 second + 1 secondo + + + + %1 seconds + %1 secondi + + + + 0.%1 seconds + 0,%1 secondi + + + + and + e + + + + Export PDF + + + + + Project Settings + Impostazioni progetto + + + + Paths + Percorsi + + + + Include paths + Percorsi di inclusione + + + + Defines + Definizioni + + + + Undefines + + + + + Path selected + Selezionato percorso + + + + Number of files scanned + Numero di file scansionati + + + + Scan duration + Durata della scansione + + + + + Errors + Errori + + + + File: + + + + + No cppcheck build dir + + + + + + Warnings + Avvisi + + + + + Style warnings + Stilwarnungen + + + + + Portability warnings + Avvisi sulla portabilità + + + + + Performance warnings + Avvisi sulle performance + + + + + Information messages + Messaggi di informazione + + + + ThreadResult + + + %1 of %2 files checked + %1 su %2 file scansionati + + + + TranslationHandler + + + Failed to change the user interface language: + +%1 + +The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. + Fallito il tentativo di cambio della lingua dell'interfaccia utente: + +%1 + +L'interfaccia utente è stata risettata in Inglese. Apri la finestra di dialogo Preferenze per selezionare una qualunque lingua a disposizione. + + + + Cppcheck + Cppcheck + + + + TxtReport + + + inconclusive + inconcludente + + + + toFilterString + + + All supported files (%1) + + + + + All files (%1) + + + + diff --git a/cppcheck-2.14.0/gui/cppcheck_ja.ts b/cppcheck-2.14.0/gui/cppcheck_ja.ts new file mode 100644 index 00000000..1fd05154 --- /dev/null +++ b/cppcheck-2.14.0/gui/cppcheck_ja.ts @@ -0,0 +1,3069 @@ + + + + + About + + + About Cppcheck + cppcheckについて + + + + Version %1 + Version %1 + + + + Cppcheck - A tool for static C/C++ code analysis. + CppcheckはC/C++ 静的コード解析ツールです. + + + + Copyright © 2007-%1 Cppcheck team. + Copyright © 2007-2021 Cppcheck team. + Copyright © 2007-%1 Cppcheck team. + + + + This program is licensed under the terms +of the GNU General Public License version 3 + 本ソフトウェアはGNU General Public License Version3 ライセンスの元で配布されます + + + + Visit Cppcheck homepage at %1 + Cppcheckのホームページはこちら %1 + + + + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PCRE</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PicoJSON</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">TinyXML2</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Boost</li></ul></body></html> + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">pcre</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">picojson</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">tinyxml2</li></ul></body></html> + <html><head/><body><p>ライブラリの開発者に感謝を捧げます:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">pcre</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">picojson</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">tinyxml2</li></ul></body></html> + + + + ApplicationDialog + + + Add an application + アプリケーションの追加 + + + + Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. + +The following texts in parameters are replaced with appropriate values when application is executed: +(file) - Filename containing the error +(line) - Line number containing the error +(message) - Error message +(severity) - Error severity + +Example opening a file with Kate and make Kate scroll to the correct line: +Executable: kate +Parameters: -l(line) (file) + fix Japanese word 名前 to 表示名 + ここにエラー指摘のあるファイルを開くアプリケーションを追加できます。そのアプリケーションの表示名、実行ファイル名、コマンドラインパラメータを指定してください。 + +パラメータ中の以下の文字列を使用してパラメータ(Parameters:)に設定します。これらの文字列はアプリケーションが実行されたときに、適切な値に変換されます。: +(file) - エラー指摘のあるファイル +(line) - エラー指摘のある行 +(message) - エラー指摘メッセージ +(severity) - エラー指摘重大度 + +Kate テキストエディタでファイルを開き、該当する行に移動する例: +Executable: kate +Parameters: -l(line) (file) + + + + &Name: + 表示名(&N): + + + + &Executable: + 実行ファイルのパス(&E): + + + + &Parameters: + パラメータ(&P): + + + + Browse + 参照 + + + + Executable files (*.exe);;All files(*.*) + 実行ファイル (*.exe);;すべてのファイル(*.*) + + + + Select viewer application + 表示アプリケーションの選択 + + + + Cppcheck + Cppcheck + + + + You must specify a name, a path and optionally parameters for the application! + アプリケーションの表示名と実行ファイルのパスと(オプションの)引数を指定してください! + + + + ComplianceReportDialog + + + Compliance Report + コンプライアンスレポート + + + + Project name + プロジェクト名 + + + + Project version + プロジェクトバージョン + + + + Coding Standard + コーディング標準 + + + + Misra C + MISRA C + + + + Cert C + CERT C + + + + Cert C++ + Cert C++ + + + + List of files with md5 checksums + md5チェックサムつきファイルリスト + + + + Compliance report + コンプライアンスレポート + + + + HTML files (*.html) + HTMLファイル(*.html) + + + + + Save compliance report + コンプライアンスレポートの保存 + + + + Failed to import '%1' (%2), can not show files in compliance report + '%1' (%2)のインポートに失敗しました。コンプライアンスレポートに表示できません + + + Failed to import '%1', can not show files in compliance report + '%1'のインポートに失敗しました、コンプライアンスレポートでファイルを表示できません + + + + FileViewDialog + + + Could not find the file: %1 + ファイル:%1 が見つかりません + + + + + Cppcheck + Cppcheck + + + + Could not read the file: %1 + ファイル:%1 が読み込めません + + + + HelpDialog + + + Cppcheck GUI help + Cppcheck GUI ヘルプ + + + + Contents + 目次 + + + + Index + インデックス + + + + Helpfile '%1' was not found + ヘルプファイル '%1' が見つかりません + + + + Cppcheck + Cppcheck + + + + LibraryAddFunctionDialog + + + Add function + 関数の追加 + + + + Function name(s) + 関数の名称 + + + + Number of arguments + 引数の数 + + + + LibraryDialog + + + Library Editor + ライブラリエディタ + + + + Open + 開く + + + + Save + 保存する + + + + Save as + 別名で保存する + + + + Functions + 関数 + + + + Sort + ソート + + + + Add + 追加 + + + + Filter: + フィルタ: + + + + Comments + コメント + + + + noreturn + noreturn(返り値なし) + + + + False + False(偽) + + + + True + True(真) + + + + Unknown + Unknown(不明) + + + + return value must be used + 返り値は使用されなければならない + + + + ignore function in leaks checking + リークの解析中に無視する関数 + + + + Arguments + Arguments(引数) + + + + Edit + 編集 + + + + + Library files (*.cfg) + ライブラリファイル(*.cfg) + + + + Open library file + ライブラリファイルを開く + + + + + + Cppcheck + Cppcheck + + + + Cannot open file %1. + Can not open file %1. + ファイルが見つかりません %1。 + + + + Failed to load %1. %2. + 読み込みに失敗しました(%1.%2)。 + + + + Cannot save file %1. + Can not save file %1. + ファイルが保存できません %1。 + + + + Save the library as + このライブラリに名前をつけて保存する + + + + LibraryEditArgDialog + + + Edit argument + 引数の編集 + + + + <html><head/><body> +<p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> +<p>Typically, set this if the argument is a pointer, size, etc.</p> +<p>Example:</p> +<pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> +</body></html> + <html><head/><body> +<p>ブール値は許可されていますか? 例えば、比較の結果または '!' 演算子</p> +<p>典型的に引数がポインタやサイズを表す場合、これを設定します。</p> +<p>例:</p> +<pre> memcmp(x, y, i == 123); // 最後の引数は、ブール型であってはならない +</body></html> + + + + Not bool + 非ブール型 + 非bool値 + + + + <html><head/><body> +<p>Is a null parameter value allowed?</p> +<p>Typically this should be used on any pointer parameter that does not allow null.</p> +<p>Example:</p> +<pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> +</body></html> + <html><head/><body> +<p>null値が許可されていますか?</p> +<p>典型的には、nullを渡してはいけないポインタのパラメータに使用してください。</p> +<p>例:</p> +<pre> strcpy(x,y); // x も y も null であってはならない。</pre> +</body></html> + + + + Not null + 非NULL + + + + Not uninit + 未初期化 + + + + String + 文字列 + + + + Format string + フォーマット文字列 + + + + Min size of buffer + バッファの最小サイズ + + + + + Type + Type(型) + + + + + None + None(無) + + + + + argvalue + argvalue(引数の値) + + + + + mul + mul(積) + + + + + strlen + strlen(文字数) + + + + + Arg + Arg(引数) + + + + + Arg2 + Arg2(第二引数) + + + + and + and(和) + + + + Valid values + 妥当な値 + + + + MainWindow + + + + + + + + + + + + + + + + + Cppcheck + Cppcheck + + + + &File + ファイル(&F) + + + + &View + 表示(&V) + + + + &Toolbars + ツールバー(&T) + + + + &Help + ヘルプ(&H) + + + + C++ standard + C++標準 + + + + &C standard + C standard + &C標準 + + + + &Edit + 編集(&E) + + + + Standard + 言語規格 + + + + Categories + カテゴリ + + + + &License... + ライセンス(&L)... + + + + A&uthors... + 作者(&u)... + + + + &About... + Cppcheckについて(&A)... + + + + &Files... + ファイル選択(&F)... + + + + + Analyze files + Check files + ファイルをチェックする + + + + Ctrl+F + Ctrl+F + + + + &Directory... + ディレクトリ選択(&D)... + + + + + Analyze directory + Check directory + ディレクトリをチェックする + + + + Ctrl+D + Ctrl+D + + + + Ctrl+R + Ctrl+R + + + + &Stop + 停止(&S) + + + + + Stop analysis + Stop checking + チェックを停止する + + + + Esc + Esc + + + + &Save results to file... + 結果をファイルに保存(&S)... + + + + Ctrl+S + Ctrl+S + + + + &Quit + 終了(&Q) + + + + &Clear results + 結果をクリア(&C) + + + + &Preferences + 設定(&P) + + + + + Show style warnings + スタイル警告を表示 + + + + + Show errors + エラーを表示 + + + + + Information + 情報 + + + + Show information messages + 情報メッセージを表示 + + + + Show portability warnings + 移植可能性の問題を表示 + + + + Show Cppcheck results + Cppcheck結果を表示する + + + + Clang + Clang + + + + Show Clang results + Clangの結果を表示 + + + + &Filter + フィルター(&F) + + + + Filter results + フィルタ結果 + + + + Windows 32-bit ANSI + Windows 32-bit ANSIエンコード + + + + Windows 32-bit Unicode + Windows 32-bit Unicode + + + + Unix 32-bit + Unix 32-bit + + + + Unix 64-bit + Unix 64-bit + + + + Windows 64-bit + Windows 64-bit + + + + &Print... + 印刷(&P)... + + + + Print the Current Report + 現在のレポートを印刷 + + + + Print Pre&view... + 印刷プレビュー(&v)... + + + + Open a Print Preview Dialog for the Current Results + 現在のレポートをプレビュー表示 + + + + Open library editor + ライブラリエディタを開く + + + + C&lose Project File + プロジェクトを閉じる(&l) + + + + &Edit Project File... + プロジェクトの編集(&E)... + + + + &Statistics + 統計情報(&S) + + + + + Show warnings + 警告を表示 + + + + + Show performance warnings + パフォーマンス警告を表示 + + + + Show &hidden + 非表示を表示(&h) + + + + &Check all + すべてのエラーを表示(&C) + + + + Checking for updates + 更新の確認 + + + + Hide + 非表示 + + + + A&nalyze + チェック(&n) + + + + Filter + フィルター + + + + &Reanalyze modified files + &Recheck modified files + 変更ありファイルを再解析(&R) + + + + Reanal&yze all files + 全ファイル再解析(&y) + + + + Ctrl+Q + Ctrl+Q + + + + Style war&nings + スタイル警告(&n) + + + + E&rrors + エラー(&r) + + + + &Uncheck all + すべてのエラーを非表示(&U) + + + + Collapse &all + ツリーを折り畳む(&a) + + + + &Expand all + ツリーを展開(&E) + + + + &Standard + 言語規格(&S) + + + + Standard items + 標準項目 + + + + &Contents + コンテンツ(&C) + + + + Open the help contents + ヘルプファイルを開く + + + + F1 + F1 + + + + Toolbar + ツールバー + + + + &Categories + カテゴリ(&C) + + + + Error categories + エラーカテゴリ + + + + &Open XML... + XMLを開く(&O)... + + + + Open P&roject File... + プロジェクトを開く(&R)... + + + + Ctrl+Shift+O + Ctrl+Shift+O + + + + Sh&ow Scratchpad... + スクラッチパッドを表示(&o)... + + + + &New Project File... + 新規プロジェクト(&N)... + + + + Ctrl+Shift+N + Ctrl+Shift+N + + + + &Log View + ログを表示(&L) + + + + Log View + ログ表示 + + + + &Warnings + 警告(&W) + + + + Per&formance warnings + パフォーマンス警告(&f) + + + + &Information + 情報(&I) + + + + &Portability + 移植可能性(&P) + + + + P&latforms + プラットフォーム(&l) + + + + C++&11 + C++11(&1) + + + + C&99 + C99(&9) + + + + &Posix + Posix(&P) + + + + C&11 + C11(&1) + + + + &C89 + C89(&C) + + + + &C++03 + C++03(&C) + + + + &Library Editor... + ライブラリエディタ(&L)... + + + + &Auto-detect language + 自動言語検出(&A) + + + + &Enforce C++ + C++ 強制(&E) + + + + E&nforce C + C 強制(&n) + + + + C++14 + C++14 + + + + Reanalyze and check library + ライブラリを再チェックする + + + + Check configuration (defines, includes) + チェックの設定(define、インクルード) + + + + C++17 + C++17 + + + + C++20 + C++20 + + + + Compliance report... + コンプライアンスレポート... + + + + 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の古いバージョンの設定には互換性がありません。エディタアプリケーションの設定を確認して修正してください、そうしないと正しく起動できないかもしれません。 + + + + You must close the project file before selecting new files or directories! + 新しいファイル/ディレクトリをチェックするには現在のプロジェクトを閉じてください! + + + + + Quick Filter: + クイックフィルタ: + + + + Select configuration + コンフィグレーションの選択 + + + + Found project file: %1 + +Do you want to load this project file instead? + プロジェクトファイルを検出しました: %1 + +現在のプロジェクトの代わりにこのプロジェクトファイルを読み込んでもかまいませんか? + + + + The library '%1' contains unknown elements: +%2 + このライブラリ '%1' には次の不明な要素が含まれています。 +%2 + + + + File not found + ファイルがありません + + + + Bad XML + 不正なXML + + + + Missing attribute + 属性がありません + + + + Bad attribute value + 不正な属性があります + + + + Unsupported format + サポートされていないフォーマット + + + + Duplicate platform type + プラットフォームの種類が重複しています + + + + Platform type redefined + プラットフォームの種類が再定義されました + + + + Unknown element + 不明な要素 + + + + Unknown issue + 不明な課題 + + + + Failed to load the selected library '%1'. +%2 + 選択したライブラリの読み込みに失敗しました '%1' +%2 + + + + + + + Error + エラー + + + Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + %1のロードに失敗しました。あなたの Cppcheck は正しくインストールされていません。あなたは --data-dir=<directory> コマンドラインオプションでロードするファイルの場所を指定できます。ただし、この --data-dir はインストールスクリプトによってサポートされており、GUI版ではサポートされていません。全ての設定は調整済みでなければなりません。 + + + Failed to load %1 - %2 + %1 - %2 の読み込みに失敗 + + + + + XML files (*.xml) + XML ファイル (*.xml) + + + + Open the report file + レポートを開く + + + + License + ライセンス + + + + Authors + 作者 + + + + Save the report file + レポートを保存 + + + + Text files (*.txt) + テキストファイル (*.txt) + + + + CSV files (*.csv) + CSV形式ファイル (*.csv) + + + + Cannot generate a compliance report right now, an analysis must finish successfully. Try to reanalyze the code and ensure there are no critical errors. + コンプライアンスレポートをすぐに生成できません。解析が完了し成功していなければなりません。コードを再解析して、致命的なエラーがないことを確認してください。 + + + + Project files (*.cppcheck);;All files(*.*) + プロジェクトファイル (*.cppcheck);;すべてのファイル(*.*) + + + + Select Project File + プロジェクトファイルを選択 + + + + Failed to open file + ファイルを開くのに失敗しました + + + + Unknown project file format + プロジェクトファイルの形式が不明です + + + + Failed to import project file + プロジェクトファイルのインポートに失敗しました + + + + Failed to import '%1': %2 + +Analysis is stopped. + インポートに失敗'%1': %2 + +解析を停止しました。 + + + + Failed to import '%1' (%2), analysis is stopped + '%1' (%2) のインポートに失敗しました。解析は停止 + + + + Install + インストール + + + + New version available: %1. %2 + 新しいバージョンが利用可能です。: %1. %2 + + + + + + + Project: + プロジェクト: + + + + No suitable files found to analyze! + チェック対象のファイルがみつかりません! + + + + C/C++ Source + C/C++のソースコード + + + + Compile database + コンパイルデータベース + + + + Visual Studio + Visual Studio + + + + Borland C++ Builder 6 + Borland C++ Builder 6 + + + + Select files to analyze + チェック対象のファイルを選択 + + + + Select directory to analyze + チェックするディレクトリを選択してください + + + + Select the configuration that will be analyzed + チェックの設定を選択 + + + + Found project files from the directory. + +Do you want to proceed analysis without using any of these project files? + ディレクトリ内にプロジェクトファイルがありました。 + +みつかったプロジェクトファイルを使用せずにチェックしますか? + + + + Duplicate define + + + + + File not found: '%1' + + + + + Failed to load/setup addon %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. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + +Analysis is aborted. + + + + + Failed to load %1 - %2 + +Analysis is aborted. + + + + + + %1 + +Analysis is aborted. + + + + + Current results will be cleared. + +Opening a new XML file will clear current results. +Do you want to proceed? + 現在の結果を作成します。 + +新しくXMLファイルを開くと現在の結果が削除されます。実行しますか? + + + + Analyzer is running. + +Do you want to stop the analysis and exit Cppcheck? + チェック中です。 + +チェックを中断して、Cppcheckを終了しますか? + + + + About + CppCheckについて + + + + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) + XML ファイル (*.xml);;テキストファイル (*.txt);;CSVファイル (*.csv) + + + + Build dir '%1' does not exist, create it? + ビルドディレクトリ'%1'がありません。作成しますか? + + + + To check the project using addons, you need a build directory. + アドオンを使用してプロジェクトをチェックするためには、ビルドディレクトリが必要です。 + + + Failed to import '%1', analysis is stopped + '%1'のインポートに失敗しました。(チェック中断) + + + + Project files (*.cppcheck) + プロジェクトファイル (*.cppcheck) + + + + Select Project Filename + プロジェクトファイル名を選択 + + + + No project file loaded + プロジェクトファイルが読み込まれていません + + + + The project file + +%1 + + could not be found! + +Do you want to remove the file from the recently used projects -list? + このプロジェクトファイル %1 が見つかりません。 +最近使用したプロジェクトのリストからこのファイルを取り除きますか? + + + + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> This option is for installation scripts so they can configure the directory where + datafiles are located (translations, cfg). The GUI is not started when this option + is used. + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) + Cppcheck GUI. + +シンタックス: + cppcheck-gui [OPTIONS] [files または paths] + +オプション: + -h, --help このヘルプを表示する。 + -p <file> 指定のプロジェクトファイルを開き、チェックを開始する。 + -l <file> 指定の、結果XMLファイルを開く + -d <directory> フォルダを指定してチェックする。これは -l オプションで 指定した、結果XMLファイルを生成する。 + -v, --version バージョンを表示する。 + --data-dir=<directory> GUI のデータファイル(翻訳やcfg)のあるディレクトリを指定する。このオプションを指定した場合、GUIで起動しません。 + + + + Cppcheck GUI - Command line parameters + Cppcheck GUI - コマンドラインパラメータ + + + + NewSuppressionDialog + + + New suppression + 新しい指摘の抑制 + + + + Error ID + エラーID + + + + File name + ファイル名 + + + + Line number + 行数 + + + + Symbol name + シンボル名 + + + + Edit suppression + 抑制の編集 + + + + Platforms + + + Native + ネイティブ + + + + Unix 32-bit + Unix 32-bit + + + + Unix 64-bit + Unix 64-bit + + + + Windows 32-bit ANSI + Windows 32-bit ANSIエンコード + + + + Windows 32-bit Unicode + Windows 32-bit Unicode + + + + Windows 64-bit + Windows 64-bit + + + + ProjectFile + + + Project File + プロジェクトファイル + + + + Paths and Defines + パスと定義 + + + + Import Project (Visual studio / compile database/ Borland C++ Builder 6) + Import Project (Visual studio / compile database) + プロジェクトのインポート(Visual studio / compile database Borland C++ Builder 6)) + + + + Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int + Defines must be separated by a semicolon ';' + 定義(Define)はセミコロン';'で区切る必要があります。 例: DEF1;DEF2=5;DEF3=int + + + + Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. + カスタマイズした cfgファイルを同じフォルダにプロジェクトファイルとして保存してください。ここに表示できるようになります。 + + + MISRA C 2012 + MISRA C 2012 + + + + MISRA rule texts + MISRA ルールテキスト + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + <html><head/><body><p>MISRA C 2012 pdfのAppendix A &quot;Summary of guidelines&quot; からテキストをコピーペーストしてください。</p></body></html> + + + + ... + ... + + + + <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> + <html><head/><body><p>選択済み:</p><p> * 全Debug と Release設定をチェックする</p><p> * 最初にマッチした Debug 設定のみチェックする</p><p><br/></p></body></html> + + + + + Browse... + 参照... + + + + Analyze all Visual Studio configurations + Visual Studioの全ての設定をチェックする + + + + Selected VS Configurations + 選択したVS設定 + + + + Paths: + パス: + + + + + Add... + 追加... + + + + + + Edit + 編集 + + + + + + + Remove + 取り除く + + + + Undefines: + 定義取り消し(Undefines): + + + + Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 + 定義の取り消しはセミコロンで区切ります。例: UNDEF1;UNDEF2;UNDEF3 + + + + Include Paths: + インクルードパス: + + + + Types and Functions + 型と関数 + + + + + Analysis + チェック + + + + This is a workfolder that Cppcheck will use for various purposes. + Cppcheckがさまざまな目的で使用するワークディレクトリ。 + + + + Parser + パーサー + + + + Cppcheck (built in) + Cppcheckビルトイン + + + + Check level + チェックレベル + + + + Normal -- meant for normal analysis in CI. Analysis should finish in reasonable time. + #N 通常 -- CIでの通常の解析を意味します。解析が合理的な時間内に完了すべきです。 + + + + Exhaustive -- meant for nightly builds etc. Analysis time can be longer (10x slower than compilation is OK). + #E 徹底的 -- ナイトリービルド等を意味します。解析時間はより長くなることがあります (コンパイルの10倍以上時間がかかってもよい)。 + + + + Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) + ヘッダファイルのコードもチェック (通常はONにしてください、制限するときのみOFF) + + + + If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. + タグが追加された場合、警告上で右クリックしてそれらのタグの中の一つを設定できます。警告を分類できます。 + + + + Exclude source files + 除外するソースファイル + + + + Exclude folder... + フォルダで除外... + + + + Exclude file... + ファイルで除外... + + + + Check that each class has a safe public interface + クラスが安全で公開されたインターフェースをもっているか確認 + + + + Limit analysis + 解析の制限 + + + + Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) + Check code in unused templates (slower and less accurate analysis) + 未使用テンプレートのコードもチェック (解析に時間がかかり、また正確性は低い) + + + + Max CTU depth + CTUの最大深さ + + + + Misra C + MISRA C + + + + 2012 + 2012 + + + + 2023 + 2023 + + + + Cert C++ + Cert C++ + + + + Bug hunting (Premium) + バグハンティング(プレミアム) + + + + External tools + 外部ツール + + + + Up + + + + + Down + + + + + Platform + プラットフォーム + + + + Clang (experimental) + Clang (実験的) + + + + If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. + 可能な限りクラスが柔軟であり堅牢であることを望む場合、公開されたインターフェースが非常に堅牢です。Cppcheckは引数があらゆる値をとりうると仮定します。 + + + + Max recursion in template instantiation + テンプレートインスタンス化の最大再帰回数 + + + + Warning options + 警告オプション + + + + Root path: + ルートパス: + + + + Filepaths in warnings will be relative to this path + 警告中のファイルパスはこのパスからの相対パスになります + + + + Warning tags (separated by semicolon) + 警告タグ(セミコロン区切り) + + + + Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) + Cppcheck ビルドディレクトリ (全プログラムチェック, 差分チェック, 統計等) + + + + Libraries + ライブラリ + + + + Suppressions + 指摘の抑制 + + + + Add + 追加 + + + + + Addons + アドオン + + + + Note: Addons require <a href="https://www.python.org/">Python</a> being installed. + 注意: アドオンには<a href="https://www.python.org/">Python</a>が必要です。 + + + + Y2038 + Y2038 + + + + Thread safety + スレッドセーフ + + + + Coding standards + コーディング標準 + + + Misra C 2012 + MISRA C 2012 + + + + Cert C + CERT C + + + + CERT-INT35-C: int precision (if size equals precision, you can leave empty) + CERT-INT35-C: int型の精度 (もしサイズが精度と一致する場合空のままにしてください) + + + + Misra C++ 2008 + MISRA C++ 2008 + + + + Autosar + AUTOSAR + + + + Bug hunting + バグハント + + + + Clang analyzer + Clang Analyzer + + + + Clang-tidy + Clang-tidy + + + + Defines: + 定義(Defines): + + + + ProjectFileDialog + + + Project file: %1 + プロジェクトファイル:%1 + + + + Select Cppcheck build dir + Cppcheckビルドディレクトリ + + + + Select include directory + includeディレクトリを選択 + + + + Select a directory to check + チェックするディレクトリを選択してください + + + + Clang-tidy (not found) + Clang-tidy (みつかりません) + + + + Visual Studio + Visual Studio + + + + Compile database + コンパイルデータベース + + + + Borland C++ Builder 6 + Borland C++ Builder 6 + + + + Import Project + プロジェクトのインポート + + + + Select directory to ignore + 除外するディレクトリを選択してください + + + + Source files + ソースファイル + + + + All files + 全ファイル + + + + Exclude file + 除外ファイル + + + + Select MISRA rule texts file + MISRAルールテキストファイルを選択 + + + + MISRA rule texts file (%1) + MISRAルールテキストファイル (%1) + + + + QObject + + + Unknown language specified! + 未知の言語が指定されました! + + + + Language file %1 not found! + 言語ファイル %1 が見つかりません! + + + + Failed to load translation for language %1 from file %2 + 言語 %2 から %1 への翻訳ファイルの読み込みに失敗 + + + + line %1: Unhandled element %2 + 行 %1: 扱われていない要素(Unhandled element) %2 + + + + line %1: Mandatory attribute '%2' missing in '%3' + 行 %1: 必須の属性 '%2' が '%3'にない + + + + (Not found) + (見つかりません) + + + + Thin + シン(細) + + + + ExtraLight + エクストラライト + + + + Light + ライト + + + + Normal + ノーマル + + + + Medium + メディウム + + + + DemiBold + デミボールト + + + + Bold + ボールド + + + + ExtraBold + エクストラボールド + + + + Black + + + + + Editor Foreground Color + エディタの前景色 + + + + Editor Background Color + エディタの背景色 + + + + Highlight Background Color + ハイライトの背景色 + + + + Line Number Foreground Color + 行番号の前景色 + + + + Line Number Background Color + 行番号の背景色 + + + + Keyword Foreground Color + キーワードの前景色 + + + + Keyword Font Weight + キーワードのフォントのウェイト + + + + Class Foreground Color + Class ForegroundColor + クラスの前景色 + + + + Class Font Weight + クラスフォントのウェイト + + + + Quote Foreground Color + クォートの前景色 + + + + Quote Font Weight + クォートのフォントウェイト + + + + Comment Foreground Color + コメントの前景色 + + + + Comment Font Weight + コメントフォントのウェイト + + + + Symbol Foreground Color + シンボルの前景色 + + + + Symbol Background Color + シンボルの背景色 + + + + Symbol Font Weight + シンボルのフォントウェイト + + + + Set to Default Light + デフォルトをライトに設定 + + + + Set to Default Dark + デフォルトをダークに設定 + + + + QPlatformTheme + + + OK + OK + + + + Cancel + キャンセル + + + + Close + 閉じる + + + + Save + 保存する + + + + ResultsTree + + + File + ファイル + + + + Severity + 警告の種別 + + + + Line + + + + + Summary + 要約 + + + + Undefined file + 未定義ファイル + + + + Copy + コピー + + + + Could not find file: + ファイルが見つかりません: + + + + Please select the folder '%1' + フォルダ '%1' を選択してください + + + + Select Directory '%1' + ディレクトリ '%1' 選択 + + + + Please select the directory where file is located. + ファイルのあるディレクトリを選択してください。 + + + + debug + デバッグ + + + + note + 注意 + + + + Recheck + 再チェック + + + + Hide + 非表示 + + + + Hide all with id + IDで非表示を指定 + + + + Suppress selected id(s) + 選択したidを抑制 + + + + Open containing folder + 含まれるフォルダを開く + + + + internal + 内部 + + + + + Tag + タグ + + + + No tag + タグなし + + + + + Cppcheck + Cppcheck + + + + No editor application configured. + +Configure the editor application for Cppcheck in preferences/Applications. + Configure the text file viewer program in Cppcheck preferences/Applications. + エディタアプリが設定されていません。 + +Cppcheckの「設定」からテキストファイルを編集するアプリケーションを設定してください。 + + + + No default editor application selected. + +Please select the default editor application in preferences/Applications. + デフォルトのエディタアプリケーションが指定されていません。 + +設定からデフォルトのエディタアプリケーションを設定してください。 + + + + Could not find the file! + ファイルが見つかりません! + + + + Could not start %1 + +Please check the application path and parameters are correct. + %1 が実行できません。 + +実行ファイルパスや引数の設定を確認してください。 + + + + Select Directory + ディレクトリを選択 + + + + Id + Id + + + + Inconclusive + 結論のでない + + + + Since date + 日付 + + + + style + スタイル + + + + error + エラー + + + + warning + 警告 + + + + performance + パフォーマンス + + + + portability + 移植可能性 + + + + information + 情報 + + + + ResultsView + + + Results + 結果 + + + + Critical errors + 致命的なエラー + + + + Analysis Log + チェックログ + + + + Warning Details + 警告の詳細 + + + + + Failed to save the report. + レポートの保存に失敗しました。 + + + + Print Report + レポートの印刷 + + + + No errors found, nothing to print. + 指摘がないため、印刷するものがありません。 + + + + %p% (%1 of %2 files checked) + %p% (%1 / %2 :ファイル数) + + + + + Cppcheck + Cppcheck + + + + No errors found. + 警告/エラーは見つかりませんでした。 + + + + Errors were found, but they are configured to be hidden. +To toggle what kind of errors are shown, open view menu. + 警告/エラーが見つかりましたが、非表示設定になっています。 + + + + + Failed to read the report. + レポートの読み込みに失敗. + + + + XML format version 1 is no longer supported. + XML フォーマットバージョン 1 はもうサポートされていません。 + + + + First included by + は次のものが最初にインクルードしました + + + + Id + ID + + + + Bug hunting analysis is incomplete + バグハントの解析は不完全です + + + + Clear Log + ログの消去 + + + + Copy this Log entry + このログ項目をコピー + + + + Copy complete Log + ログ全体をコピー + + + + Analysis was stopped + 解析は停止しした + + + + There was a critical error with id '%1' + id '%1'の致命的なエラーがあります + + + + when checking %1 + %1 をチェックするとき + + + + when checking a file + ファイルをチェックするとき + + + + Analysis was aborted. + 解析は中止した。 + + + + ScratchPad + + + Scratchpad + スクラッチパッド + + + + Copy or write some C/C++ code here: + ここに C/C++のコードをコピーペーストまたは記入してください: + + + + Optionally enter a filename (mainly for automatic language detection) and click on "Check": + オプション: ファイル名を入力(言語は自動判定)して"チェック"をクリックしてください: + + + + filename + ファイル名 + + + + Check + チェック + + + + Settings + + + Preferences + 設定 + + + + General + 全般 + + + + Add... + 追加... + + + + Number of threads: + 解析用のスレッド数: + + + + Ideal count: + 理想的な数: + + + + Force checking all #ifdef configurations + Check all #ifdef configurations + すべての #ifdef をチェックする + + + + Show full path of files + ファイルのフルパスを表示 + + + + Show "No errors found" message when no errors found + エラーが無いときは"エラーなし"を表示 + + + + Display error Id in column "Id" + エラーIDを "Id" に表示する + + + + Enable inline suppressions + inline抑制を有効にする + + + + Check for inconclusive errors also + 結論のでない指摘もチェックする + + + + Show statistics on check completion + チェック完了時に統計情報を表示する + + + + Check for updates + 更新の確認 + + + + Show internal warnings in log + ログの内部警告も表示する + + + + Addons + アドオン + + + + Python binary (leave this empty to use python in the PATH) + Pythonインタプリタの場所(空白の場合システムのPATHから検索) + + + + + + ... + ... + + + + MISRA addon + MISRAアドオン + + + + MISRA rule texts file + MISRA ルールテキストファイル + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + <html><head/><body><p>Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdfのテキストをテキストファイルにコピー</p></body></html> + + + + Clang + Clang + + + + Clang path (leave empty to use system PATH) + Clangの場所(空白の場合システムのPATHから検索) + + + + Visual Studio headers + Visual Studioのヘッダ + + + + <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> + <html><head/><body><p>Visual Studioのヘッダーファイル(セミコロン区切り';')。</p><p>Visual Studio コマンドプロンプトを開き、 &quot;SET INCLUDE&quot;. と入力後、そのパスをコピーペーストしてください。</p></body></html> + + + + Code Editor + コードエディタ + + + + Code Editor Style + コードエディタスタイル + + + + System Style + システムのデフォルトのスタイル + + + + Default Light Style + ライトスタイルをデフォルトに + + + + Default Dark Style + ダークスタイルをデフォルトに + + + + Custom + カスタム + + + + Remove + 削除 + + + + Applications + アプリケーション + + + + + Edit... + 編集... + + + + Set as default + デフォルトとして設定 + + + + Reports + レポート + + + + Save all errors when creating report + レポート作成時にすべての警告/エラーを保存 + + + + Save full path to files in reports + レポートにファイルのフルパスを保存 + + + + Language + 言語 + + + + SettingsDialog + + + N/A + N/A + + + + The executable file "%1" is not available + 実行ファイル "%1" が利用できません + + + + Add a new application + 新しいアプリケーションの追加 + + + + Modify an application + アプリケーションの変更 + + + + [Default] + [デフォルト] + + + + [Default] + [デフォルト] + + + + Select python binary + pythonの場所の選択 + + + + Select MISRA File + MISRAファイルの選択 + + + + Select clang path + clangのパスの選択 + + + + StatsDialog + + + + + + Statistics + 統計情報 + + + + + Project + プロジェクト + + + + Project: + プロジェクト: + + + + Paths: + パス: + + + + Include paths: + インクルードパス: + + + + Defines: + 定義(define): + + + + Undefines: + 定義取り消し(undef): + + + + + Previous Scan + 前回の解析 + + + + Path Selected: + ディレクトリ選択: + + + + Number of Files Scanned: + 解析済みファイル数: + + + + Scan Duration: + 解析にかかった時間: + + + + Errors: + エラー: + + + + Warnings: + 警告: + + + + Stylistic warnings: + スタイル警告: + + + + Portability warnings: + 移植可能性の警告: + + + + Performance issues: + パフォーマンス警告: + + + + Information messages: + 情報メッセージ: + + + + Active checkers: + 有効なチェッカー: + + + + Checkers + チェッカー + + + + History + ヒストリー + + + + File: + ファイル: + + + + Copy to Clipboard + クリップボードにコピー + + + + Pdf Export + PDF エクスポート + + + + 1 day + 一日 + + + + %1 days + %1日 + + + + 1 hour + 一時間 + + + + %1 hours + %1時間 + + + + 1 minute + 一分 + + + + %1 minutes + %1分 + + + + 1 second + 一秒 + + + + %1 seconds + %1秒 + + + + 0.%1 seconds + 0.%1秒 + + + + and + + + + + Export PDF + PDF エクスポート + + + + Project Settings + プロジェクトの設定 + + + + Paths + パス + + + + Include paths + インクルードパス + + + + Defines + 定義(define) + + + + Undefines + 定義取り消し(Undef) + + + + Path selected + 選択されたパス + + + + Number of files scanned + スキャンしたファイルの数 + + + + Scan duration + スキャン期間 + + + + + Errors + エラー + + + + File: + ファイル: + + + + No cppcheck build dir + cppcheckビルドディレクトリがありません + + + + + Warnings + 警告 + + + + + Style warnings + スタイル警告 + + + + + Portability warnings + 移植可能性警告 + + + + + Performance warnings + パフォーマンス警告 + + + + + Information messages + 情報メッセージ + + + + ThreadResult + + + %1 of %2 files checked + チェック: %1 / %2 (ファイル数) + + + + TranslationHandler + + + Failed to change the user interface language: + +%1 + +The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. + ユーザーインターフェースの言語 %1 への変更に失敗しました。 + +そのため言語を 英語にリセットします。設定ダイアログから利用可能な言語を選択してください。 + + + + Cppcheck + Cppcheck + + + + TxtReport + + + inconclusive + 結論の出ない + + + + toFilterString + + + All supported files (%1) + 全サポートファイル (%1) + + + + All files (%1) + 全ファイル(%1) + + + diff --git a/cppcheck-2.14.0/gui/cppcheck_ko.ts b/cppcheck-2.14.0/gui/cppcheck_ko.ts new file mode 100644 index 00000000..c00068ca --- /dev/null +++ b/cppcheck-2.14.0/gui/cppcheck_ko.ts @@ -0,0 +1,2997 @@ + + + + + About + + + About Cppcheck + Cppcheck 정보 + + + + Version %1 + 버전 %1 + + + + Cppcheck - A tool for static C/C++ code analysis. + Cppcheck - 정적 C/C++ 코드 분석 도구. + + + + This program is licensed under the terms +of the GNU General Public License version 3 + 이 프로그램은 GNU General Public License version 3을 +준수합니다 + + + + Visit Cppcheck homepage at %1 + Cppcheck 홈페이지(%1)를 방문해보세요 + + + + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PCRE</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PicoJSON</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">TinyXML2</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Boost</li></ul></body></html> + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">pcre</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">picojson</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">tinyxml2</li></ul></body></html> + + + + + Copyright © 2007-%1 Cppcheck team. + Copyright © 2007-2021 Cppcheck team. + + + + + ApplicationDialog + + + Add an application + 응용 프로그램 추가 + + + + Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. + +The following texts in parameters are replaced with appropriate values when application is executed: +(file) - Filename containing the error +(line) - Line number containing the error +(message) - Error message +(severity) - Error severity + +Example opening a file with Kate and make Kate scroll to the correct line: +Executable: kate +Parameters: -l(line) (file) + 에러 파일을 열 응용 프로그램을 추가할 수 있습니다. 응용 프로그램의 이름, 실행 파일, 명령행 인자를 입력하세요. + +인자 중에서 아래와 같은 텍스트는 응용 프로그램 실행 시 해당 값으로 대치됩니다: +(file) - 에러를 포함한 파일이름 +(line) - 에러를 포함한 행번호 +(message) - 에러 메시지 +(severity) - 에러 종류 + +Kate로 파일을 열고, 해당 행으로 이동하는 예제: +실행파일: kate +인자: -l(line) (file) + + + + &Name: + 이름(&N): + + + + &Executable: + 실행 파일(&E): + + + + &Parameters: + 명령행 인자(&P): + + + + Browse + 찾기 + + + + Executable files (*.exe);;All files(*.*) + 실행 파일(*.exe);;모든 파일(*.*) + + + + Select viewer application + 뷰어 프로그램 선택 + + + + Cppcheck + Cppcheck + + + + You must specify a name, a path and optionally parameters for the application! + + + + + ComplianceReportDialog + + + Compliance Report + + + + + Project name + + + + + Project version + + + + + Coding Standard + + + + + Misra C + + + + + Cert C + + + + + Cert C++ + + + + + List of files with md5 checksums + + + + + Compliance report + + + + + HTML files (*.html) + + + + + + Save compliance report + + + + + Failed to import '%1' (%2), can not show files in compliance report + + + + + FileViewDialog + + + Could not find the file: %1 + 파일 찾기 실패: %1 + + + + + Cppcheck + Cppcheck + + + + Could not read the file: %1 + 파일 읽기 실패: %1 + + + + HelpDialog + + + Cppcheck GUI help + + + + + Contents + + + + + Index + + + + + Helpfile '%1' was not found + + + + + Cppcheck + Cppcheck + + + + LibraryAddFunctionDialog + + + Add function + + + + + Function name(s) + + + + + Number of arguments + + + + + LibraryDialog + + + Library Editor + + + + + Open + + + + + Save + + + + + Functions + + + + + Add + + + + + noreturn + + + + + False + + + + + True + + + + + Unknown + + + + + return value must be used + + + + + ignore function in leaks checking + + + + + Arguments + + + + + Edit + 편집 + + + + + Library files (*.cfg) + + + + + Open library file + + + + + Sort + + + + + Filter: + + + + + Comments + + + + + Save as + + + + + + + Cppcheck + Cppcheck + + + + Save the library as + + + + + Failed to load %1. %2. + + + + + Cannot open file %1. + + + + + Cannot save file %1. + + + + + LibraryEditArgDialog + + + Edit argument + + + + + <html><head/><body> +<p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> +<p>Typically, set this if the argument is a pointer, size, etc.</p> +<p>Example:</p> +<pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> +</body></html> + + + + + Not bool + + + + + <html><head/><body> +<p>Is a null parameter value allowed?</p> +<p>Typically this should be used on any pointer parameter that does not allow null.</p> +<p>Example:</p> +<pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> +</body></html> + + + + + Not null + + + + + Not uninit + + + + + String + + + + + Format string + + + + + Min size of buffer + + + + + + Type + + + + + + None + + + + + + argvalue + + + + + + mul + + + + + + strlen + + + + + + Arg + + + + + + Arg2 + + + + + and + + + + + Valid values + + + + + MainWindow + + + + + + + + + + + + + + + + + Cppcheck + Cppcheck + + + + Checking for updates + + + + + Hide + 숨기기 + + + + &File + 파일(&F) + + + + &View + 보기(&V) + + + + &Toolbars + 도구바(&T) + + + + &Help + 도움말(&H) + + + + &Edit + 편집(&E) + + + + Standard + 표준 도구 + + + + Categories + 분류 도구 + + + + Filter + 필터 도구 + + + + &License... + 저작권(&L)... + + + + A&uthors... + 제작자(&u)... + + + + &About... + 정보(&A)... + + + + &Files... + 파일(&F)... + + + + Ctrl+F + Ctrl+F + + + + &Directory... + 디렉토리(&D)... + + + + Ctrl+D + Ctrl+D + + + + Ctrl+R + Ctrl+R + + + + &Stop + 중지(&S) + + + + Esc + Esc + + + + &Save results to file... + 결과를 파일에 저장(&S)... + + + + Ctrl+S + Ctrl+S + + + + &Quit + 종료(&Q) + + + + &Clear results + 결과 지우기(&C) + + + + &Preferences + 설정(&P) + + + + + Show style warnings + 스타일 경고 표시 + + + + + 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) + + + + + Show warnings + 경고 표시 + + + + + Show performance warnings + 성능 경고 표시 + + + + Show &hidden + 숨기기 보기(&H) + + + + Compliance report... + + + + + + Information + 정보 + + + + Show information messages + 정보 표시 + + + + 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 + + + + + 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 버전간 설정 방법 차이때문인 것으로 보입니다. 편집기 설정을 검사(및 수정)해주세요, 그렇지 않으면 편집기가 제대로 시작하지 않습니다. + + + + You must close the project file before selecting new files or directories! + 새로운 파일이나 디렉토리를 선택하기 전에 프로젝트 파일을 닫으세요! + + + + Found project file: %1 + +Do you want to load this project file instead? + 프로젝트 파일 존재: %1 + +이 프로젝트 파일을 불러오겠습니까? + + + + + XML files (*.xml) + XML 파일 (*.xml) + + + + Open the report file + 보고서 파일 열기 + + + + License + 저작권 + + + + Authors + 제작자 + + + + Save the report file + 보고서 파일 저장 + + + + Text files (*.txt) + 텍스트 파일 (*.txt) + + + + CSV files (*.csv) + CSV 파일 (*.csv) + + + + Cannot generate a compliance report right now, an analysis must finish successfully. Try to reanalyze the code and ensure there are no critical errors. + + + + + Project files (*.cppcheck);;All files(*.*) + 프로젝트 파일 (*.cppcheck);;모든 파일(*.*) + + + + Select Project File + 프로젝트 파일 선택 + + + + Failed to open file + + + + + Unknown project file format + + + + + Failed to import project file + + + + + Failed to import '%1': %2 + +Analysis is stopped. + + + + + Failed to import '%1' (%2), analysis is stopped + + + + + Install + + + + + New version available: %1. %2 + + + + + + + + Project: + 프로젝트: + + + + Duplicate define + + + + + File not found: '%1' + + + + + Failed to load/setup addon %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. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + +Analysis is aborted. + + + + + Failed to load %1 - %2 + +Analysis is aborted. + + + + + + %1 + +Analysis is aborted. + + + + + About + + + + + To check the project using addons, you need a build directory. + + + + + Select Project Filename + 프로젝트 파일이름 선택 + + + + No project file loaded + 프로젝트 파일 불러오기 실패 + + + + The project file + +%1 + + could not be found! + +Do you want to remove the file from the recently used projects -list? + 프로젝트 파일 + +%1 + +이 존재하지 않습니다! + +최근 프로젝트 목록에서 파일을 제거하시겠습니까? + + + + Cppcheck GUI - Command line parameters + + + + + C++ standard + + + + + + + + Error + + + + + File not found + + + + + Bad XML + + + + + Missing attribute + + + + + Bad attribute value + + + + + Failed to load the selected library '%1'. +%2 + + + + + Unsupported format + + + + + The library '%1' contains unknown elements: +%2 + + + + + Duplicate platform type + + + + + Platform type redefined + + + + + &Print... + + + + + Print the Current Report + + + + + Print Pre&view... + + + + + Open a Print Preview Dialog for the Current Results + + + + + Open library editor + + + + + Unknown element + + + + + Unknown issue + + + + + Select configuration + + + + + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> This option is for installation scripts so they can configure the directory where + datafiles are located (translations, cfg). The GUI is not started when this option + is used. + + + + + Build dir '%1' does not exist, create it? + + + + + + Analyze files + + + + + + Analyze directory + + + + + &Reanalyze modified files + + + + + + Stop analysis + + + + + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) + + + + + No suitable files found to analyze! + + + + + Select files to analyze + + + + + Select directory to analyze + + + + + Select the configuration that will be analyzed + + + + + Found project files from the directory. + +Do you want to proceed analysis without using any of these project files? + + + + + Analyzer is running. + +Do you want to stop the analysis and exit Cppcheck? + + + + + A&nalyze + + + + + &C standard + + + + + Reanal&yze all files + + + + + Ctrl+Q + + + + + Style war&nings + + + + + E&rrors + + + + + Ctrl+Shift+O + + + + + Sh&ow Scratchpad... + + + + + Ctrl+Shift+N + + + + + &Warnings + + + + + Per&formance warnings + + + + + &Information + + + + + &Portability + + + + + Show Cppcheck results + + + + + Clang + + + + + Show Clang results + + + + + P&latforms + + + + + C++&11 + + + + + C&99 + + + + + &Posix + + + + + C&11 + + + + + &C89 + + + + + &C++03 + + + + + &Library Editor... + + + + + &Auto-detect language + + + + + &Enforce C++ + + + + + E&nforce C + + + + + C++14 + C++14 + + + + Project files (*.cppcheck) + + + + + Reanalyze and check library + + + + + Check configuration (defines, includes) + + + + + C++17 + C++17 + + + + C++20 + C++20 + + + + C/C++ Source + + + + + Compile database + + + + + Visual Studio + + + + + Borland C++ Builder 6 + + + + + Current results will be cleared. + +Opening a new XML file will clear current results. +Do you want to proceed? + + + + + NewSuppressionDialog + + + New suppression + + + + + Error ID + + + + + File name + + + + + Line number + + + + + Symbol name + + + + + Edit suppression + + + + + Platforms + + + Unix 32-bit + Unix 32-bit + + + + Unix 64-bit + Unix 64-bit + + + + Windows 32-bit ANSI + Windows 32-bit ANSI + + + + Windows 32-bit Unicode + Windows 32-bit Unicode + + + + Windows 64-bit + Windows 64-bit + + + + Native + + + + + ProjectFile + + + Project File + 프로젝트 파일 + + + + Defines: + Defines: + + + + Paths: + 경로: + + + + + Add... + 추가... + + + + + + Edit + 편집 + + + + + + + Remove + 제거 + + + + Up + 위로 + + + + Down + 아래로 + + + + Suppressions + + + + + Add + + + + + Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. + + + + + ... + + + + + Include Paths: + + + + + Paths and Defines + + + + + <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> + + + + + Analyze all Visual Studio configurations + + + + + Root path: + + + + + Warning tags (separated by semicolon) + + + + + Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) + + + + + Selected VS Configurations + + + + + Types and Functions + + + + + Libraries + + + + + Parser + + + + + Cppcheck (built in) + + + + + Check that each class has a safe public interface + + + + + Limit analysis + + + + + + Addons + + + + + Note: Addons require <a href="https://www.python.org/">Python</a> being installed. + + + + + Y2038 + + + + + Thread safety + + + + + Coding standards + + + + + Cert C + + + + + CERT-INT35-C: int precision (if size equals precision, you can leave empty) + + + + + Misra C++ 2008 + + + + + Autosar + + + + + Bug hunting + + + + + Clang analyzer + + + + + Clang-tidy + + + + + + Browse... + + + + + Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int + + + + + Platform + + + + + This is a workfolder that Cppcheck will use for various purposes. + + + + + Clang (experimental) + + + + + Check level + + + + + Normal -- meant for normal analysis in CI. Analysis should finish in reasonable time. + + + + + Exhaustive -- meant for nightly builds etc. Analysis time can be longer (10x slower than compilation is OK). + + + + + If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. + + + + + Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) + + + + + Max recursion in template instantiation + + + + + Warning options + + + + + Filepaths in warnings will be relative to this path + + + + + If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. + + + + + Exclude source files + + + + + Exclude folder... + + + + + Exclude file... + + + + + Misra C + + + + + 2012 + + + + + 2023 + + + + + MISRA rule texts + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + Cert C++ + + + + + Bug hunting (Premium) + + + + + External tools + + + + + Import Project (Visual studio / compile database/ Borland C++ Builder 6) + + + + + Undefines: + + + + + Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 + + + + + + Analysis + + + + + Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) + Check code in unused templates (slower and less accurate analysis) + + + + + Max CTU depth + + + + + ProjectFileDialog + + + Project file: %1 + 프로젝트 파일: %1 + + + + Select include directory + Include 디렉토리 선택 + + + + Select a directory to check + 검사할 디렉토리 선택 + + + + Select directory to ignore + 무시할 디렉토리 선택 + + + + Select Cppcheck build dir + + + + + Import Project + + + + + Clang-tidy (not found) + + + + + Source files + + + + + All files + + + + + Exclude file + + + + + Select MISRA rule texts file + + + + + MISRA rule texts file (%1) + + + + + Visual Studio + + + + + Compile database + + + + + Borland C++ Builder 6 + + + + + QObject + + + Unknown language specified! + 알 수 없는 언어입니다! + + + + Language file %1 not found! + 언어 파일(%1)이 없습니다! + + + + Failed to load translation for language %1 from file %2 + 파일(%2)로부터 언어(%1) 불러오기 실패 + + + + line %1: Unhandled element %2 + + + + + line %1: Mandatory attribute '%2' missing in '%3' + + + + + (Not found) + + + + + Thin + + + + + ExtraLight + + + + + Light + + + + + Normal + + + + + Medium + + + + + DemiBold + + + + + Bold + + + + + ExtraBold + + + + + Black + + + + + Editor Foreground Color + + + + + Editor Background Color + + + + + Highlight Background Color + + + + + Line Number Foreground Color + + + + + Line Number Background Color + + + + + Keyword Foreground Color + + + + + Keyword Font Weight + + + + + Class Font Weight + + + + + Quote Foreground Color + + + + + Quote Font Weight + + + + + Comment Foreground Color + + + + + Comment Font Weight + + + + + Symbol Foreground Color + + + + + Symbol Background Color + + + + + Symbol Font Weight + + + + + Set to Default Light + + + + + Set to Default Dark + + + + + Class Foreground Color + + + + + QPlatformTheme + + + OK + + + + + Cancel + + + + + Close + 닫기 + + + + Save + + + + + ResultsTree + + + File + 파일 + + + + Severity + 분류 + + + + Line + + + + + Summary + 요약 + + + + Undefined file + 미정의된 파일 + + + + style + 스타일 + + + + error + 에러 + + + + warning + 경고 + + + + performance + 성능 + + + + portability + 이식성 + + + + information + 정보 + + + + debug + 디버그 + + + + internal + + + + + 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을 시잘할 수 없습니다 + +경로와 인자가 정확한지 확인하세요. + + + + Select Directory + 디렉토리 선택 + + + + Id + + + + + Hide all with id + + + + + Open containing folder + + + + + Inconclusive + + + + + Recheck + + + + + note + + + + + Suppress selected id(s) + + + + + + Tag + + + + + No tag + + + + + Since date + + + + + Could not find file: + + + + + Please select the folder '%1' + + + + + Select Directory '%1' + + + + + Please select the directory where file is located. + + + + + Copy + + + + + ResultsView + + + Results + 결과 + + + + + 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. + 결과 불러오기 실패. + + + + Bug hunting analysis is incomplete + + + + + Analysis was stopped + + + + + There was a critical error with id '%1' + + + + + when checking %1 + + + + + when checking a file + + + + + Analysis was aborted. + + + + + Id + + + + + Print Report + + + + + No errors found, nothing to print. + + + + + First included by + + + + + XML format version 1 is no longer supported. + + + + + Critical errors + + + + + Analysis Log + + + + + Warning Details + + + + + Clear Log + + + + + Copy this Log entry + + + + + Copy complete Log + + + + + ScratchPad + + + Scratchpad + + + + + filename + + + + + Check + 검사 + + + + Copy or write some C/C++ code here: + + + + + Optionally enter a filename (mainly for automatic language detection) and click on "Check": + + + + + Settings + + + Preferences + 설정 + + + + General + 일반 + + + + Number of threads: + 쓰레드 수: + + + + Ideal count: + 최적 값: + + + + Force checking all #ifdef configurations + 모든 #ifdef 설정을 강제로 검사 + + + + Show full path of files + 파일의 전체 경로 표시 + + + + Show "No errors found" message when no errors found + 에러가 발견되지 않는 경우 "에러가 없습니다." 메시지 표시 + + + + Enable inline suppressions + Inline suppression 사용 + + + + Check for updates + + + + + Add... + 추가... + + + + Remove + 제거 + + + + Applications + 응용 프로그램 + + + + + Edit... + 편집... + + + + Set as default + 기본으로 지정 + + + + Reports + 보고서 + + + + Save all errors when creating report + 보고서 생성 시 모든 에러 저장 + + + + Save full path to files in reports + 보고서에 파일의 전체 경로 저장 + + + + Language + 언어 + + + + Display error Id in column "Id" + + + + + Check for inconclusive errors also + + + + + Show internal warnings in log + + + + + Show statistics on check completion + + + + + Addons + + + + + Python binary (leave this empty to use python in the PATH) + + + + + + + ... + + + + + Clang + + + + + Clang path (leave empty to use system PATH) + + + + + Visual Studio headers + + + + + <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> + + + + + MISRA addon + + + + + MISRA rule texts file + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + Code Editor + + + + + Code Editor Style + + + + + Default Light Style + + + + + Default Dark Style + + + + + Custom + + + + + System Style + + + + + SettingsDialog + + + N/A + N/A + + + + The executable file "%1" is not available + + + + + Add a new application + 새 응용 프로그램 추가 + + + + Modify an application + 응용 프로그램 편집 + + + + [Default] + [기본] + + + + [Default] + + + + + Select python binary + + + + + Select clang path + + + + + Select MISRA File + + + + + StatsDialog + + + + + + Statistics + 통계 + + + + + Project + 프로젝트 + + + + Project: + 프로젝트: + + + + Paths: + 경로: + + + + Include paths: + Include 경로: + + + + Defines: + Defines: + + + + + Previous Scan + 직전 검사 + + + + Path Selected: + 선택된 경로: + + + + Number of Files Scanned: + 검사된 파일 수: + + + + Scan Duration: + 검사 시간: + + + + Errors: + 에러: + + + + Warnings: + 경고: + + + + Stylistic warnings: + 스타일 경고: + + + + Portability warnings: + 이식성 경고: + + + + Performance issues: + 성능 경고: + + + + Information messages: + 정보 메시지: + + + + Active checkers: + + + + + Checkers + + + + + Copy to Clipboard + 클립보드에 복사 + + + + 1 day + 1일 + + + + %1 days + %1일 + + + + 1 hour + 1시간 + + + + %1 hours + %1시간 + + + + 1 minute + 1분 + + + + %1 minutes + %1분 + + + + 1 second + 1초 + + + + %1 seconds + %1초 + + + + 0.%1 seconds + 0.%1초 + + + + and + + + + + Project Settings + 프로젝트 설정 + + + + Paths + 경로 + + + + Include paths + Include 경로 + + + + Defines + Defines + + + + Path selected + 선택된 경로 + + + + Number of files scanned + 검사된 파일 수 + + + + Scan duration + 검사 시간 + + + + + Errors + 에러 + + + + + Warnings + 경고 + + + + + Style warnings + 스타일 경고 + + + + + Portability warnings + 이식성 경고 + + + + + Performance warnings + 성능 경고 + + + + + Information messages + 정보 메시지 + + + + Pdf Export + + + + + Export PDF + + + + + History + + + + + File: + + + + + File: + + + + + No cppcheck build dir + + + + + Undefines: + + + + + Undefines + + + + + ThreadResult + + + %1 of %2 files checked + %2 중 %1 파일 검사됨 + + + + TranslationHandler + + + Failed to change the user interface language: + +%1 + +The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. + 언어 변경 실패: + +%1 + +언어가 영어로 초기화 됐습니다. 설정창을 열어서 설정 가능한 언어를 선택하세요. + + + + Cppcheck + Cppcheck + + + + TxtReport + + + inconclusive + 불확실 + + + + toFilterString + + + All supported files (%1) + + + + + All files (%1) + + + + diff --git a/cppcheck-2.14.0/gui/cppcheck_nl.ts b/cppcheck-2.14.0/gui/cppcheck_nl.ts new file mode 100644 index 00000000..0bfd6fae --- /dev/null +++ b/cppcheck-2.14.0/gui/cppcheck_nl.ts @@ -0,0 +1,3022 @@ + + + + + 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-%1 Cppcheck team. + Copyright © 2007-2021 Cppcheck team. + Copyright © 2007-2021 het cppcheck team. + + + + This program is licensed under the terms +of the GNU General Public License version 3 + Dit programma is beschikbaar onder te termen +van de GNU General Public License versie 3 + + + + Visit Cppcheck homepage at %1 + Bezoek de Cppcheck homepage op %1 + + + + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PCRE</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PicoJSON</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">TinyXML2</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Boost</li></ul></body></html> + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">pcre</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">picojson</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">tinyxml2</li></ul></body></html> + + + + + ApplicationDialog + + + Add an application + Voeg een nieuwe applicatie toe + + + + Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. + +The following texts in parameters are replaced with appropriate values when application is executed: +(file) - Filename containing the error +(line) - Line number containing the error +(message) - Error message +(severity) - Error severity + +Example opening a file with Kate and make Kate scroll to the correct line: +Executable: kate +Parameters: -l(line) (file) + Hier kunt u toepassingen toevoegen die de foute bestanden kan openen. Geef naam op van de toepassing, het uitvoerbare bestand en command lijn parameters voor de toepassing + +De volgende tekst in de parameters word vervangen door de juiste waarden wanneer de toepassing wordt uitgevoerd: +(bestand) - Bestandsnaam waarin de fout zit +(lijn) - Lijnnummer waar de fout zit +(bericht) Foutmelding +(ernst) Ernst van foutmelding + +Voorbeeld een bestand openen met KonOs2 en laat KonOs2 scrollen naar de juiste lijn: +Uitvoerbaar: KonOs2 +Parameters: -l(lijn) (bestand) + + + + &Name: + &Naam: + + + + &Executable: + &Uitvoerbaar: + + + + &Parameters: + + + + + Browse + Bladeren + + + + Executable files (*.exe);;All files(*.*) + Uitvoerbare bestanden (*.exe);;Alle bestanden(*.*) + + + + Select viewer application + Selecteer applicatie + + + + Cppcheck + Cppcheck + + + + You must specify a name, a path and optionally parameters for the application! + Geef een naam op, een pad en eventueel parameters voor de toepassing! + + + + ComplianceReportDialog + + + Compliance Report + + + + + Project name + + + + + Project version + + + + + Coding Standard + + + + + Misra C + + + + + Cert C + + + + + Cert C++ + + + + + List of files with md5 checksums + + + + + Compliance report + + + + + HTML files (*.html) + + + + + + Save compliance report + + + + + Failed to import '%1' (%2), can not show files in compliance report + + + + + 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 + + + + HelpDialog + + + Cppcheck GUI help + + + + + Contents + + + + + Index + + + + + Helpfile '%1' was not found + + + + + Cppcheck + Cppcheck + + + + LibraryAddFunctionDialog + + + Add function + + + + + Function name(s) + + + + + Number of arguments + + + + + LibraryDialog + + + Library Editor + + + + + Open + + + + + Save + Opslaan + + + + Save as + + + + + Functions + + + + + Sort + + + + + Add + + + + + Filter: + + + + + Comments + + + + + noreturn + + + + + False + + + + + True + + + + + Unknown + + + + + return value must be used + + + + + ignore function in leaks checking + + + + + Arguments + + + + + Edit + Bewerk + + + + + Library files (*.cfg) + + + + + Open library file + + + + + + + Cppcheck + Cppcheck + + + + Cannot open file %1. + Can not open file %1. + + + + + Failed to load %1. %2. + + + + + Cannot save file %1. + Can not save file %1. + + + + + Save the library as + + + + + LibraryEditArgDialog + + + Edit argument + + + + + <html><head/><body> +<p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> +<p>Typically, set this if the argument is a pointer, size, etc.</p> +<p>Example:</p> +<pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> +</body></html> + + + + + Not bool + + + + + <html><head/><body> +<p>Is a null parameter value allowed?</p> +<p>Typically this should be used on any pointer parameter that does not allow null.</p> +<p>Example:</p> +<pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> +</body></html> + + + + + Not null + + + + + Not uninit + + + + + String + + + + + Format string + + + + + Min size of buffer + + + + + + Type + + + + + + None + + + + + + argvalue + + + + + + mul + + + + + + strlen + + + + + + Arg + + + + + + Arg2 + + + + + and + + + + + Valid values + + + + + MainWindow + + + + + + + + + + + + + + + + + Cppcheck + Cppcheck + + + + A&nalyze + + + + + Standard + Standaard + + + + &File + &Bestand + + + + &View + &Weergave + + + + &Toolbars + &Werkbalken + + + + C++ standard + C++standaard + + + + &C standard + C standard + C standaard + + + + &Edit + Be&werken + + + + &License... + &Licentie... + + + + A&uthors... + A&uteurs... + + + + &About... + &Over... + + + + &Files... + &Bestanden... + + + + + Analyze files + Check files + Controleer bestanden + + + + Ctrl+F + Ctrl+F + + + + &Directory... + &Mappen... + + + + + Analyze directory + Check directory + Controleer Map + + + + Ctrl+D + Ctrl+D + + + + Ctrl+R + Ctrl+R + + + + &Stop + &Stop + + + + + Stop analysis + Stop checking + Stop controle + + + + Esc + Esc + + + + &Save results to file... + &Resultaten opslaan... + + + + Ctrl+S + Ctrl+S + + + + &Quit + &Afsluiten + + + + &Clear results + &Resultaten wissen + + + + &Preferences + &Voorkeuren + + + + + Show errors + Toon fouten + + + + + Show warnings + Toon waarschuwingen + + + + + Show performance warnings + Toon presentatie waarschuwingen + + + + Show &hidden + Toon &verborgen + + + + + Information + Informatie + + + + Show information messages + Toon informatie bericht + + + + Show portability warnings + Toon portabiliteit waarschuwingen + + + + Show Cppcheck results + + + + + Clang + + + + + Show Clang results + + + + + &Filter + &Filter + + + + Filter results + Filter resultaten + + + + Windows 32-bit ANSI + Windows 32-bit ANSI + + + + Windows 32-bit Unicode + + + + + Unix 32-bit + + + + + Unix 64-bit + + + + + Windows 64-bit + + + + + &Print... + + + + + Print the Current Report + + + + + Print Pre&view... + + + + + Open a Print Preview Dialog for the Current Results + + + + + Open library editor + + + + + &Check all + &Controleer alles + + + + Checking for updates + + + + + Hide + Verberg + + + + Filter + + + + + &Reanalyze modified files + &Recheck modified files + + + + + Reanal&yze all files + + + + + Ctrl+Q + + + + + Style war&nings + + + + + E&rrors + + + + + &Uncheck all + Selecteer &niets + + + + Collapse &all + Alles Inkl&appen + + + + &Expand all + Alles &Uitklappen + + + + &Standard + &Standaard + + + + Standard items + Standaard items + + + + Toolbar + Werkbalk + + + + &Categories + &Categorieën + + + + Error categories + Foute Categorieën + + + + &Open XML... + + + + + Open P&roject File... + Open P&oject bestand... + + + + Ctrl+Shift+O + + + + + Sh&ow Scratchpad... + + + + + &New Project File... + &Nieuw Project Bestand... + + + + Ctrl+Shift+N + + + + + &Log View + &Log weergave + + + + Log View + Log weergave + + + + C&lose Project File + &Sluit Project Bestand + + + + &Edit Project File... + &Bewerk Project Bestand... + + + + &Statistics + &Statistieken + + + + &Warnings + + + + + Per&formance warnings + + + + + &Information + + + + + &Portability + + + + + P&latforms + + + + + C++&11 + + + + + C&99 + + + + + &Posix + + + + + C&11 + + + + + &C89 + + + + + &C++03 + + + + + &Library Editor... + + + + + &Auto-detect language + + + + + &Enforce C++ + + + + + E&nforce C + + + + + C++14 + + + + + Reanalyze and check library + + + + + Check configuration (defines, includes) + + + + + C++17 + + + + + C++20 + + + + + Compliance report... + + + + + &Contents + &Inhoud + + + + Categories + Categorieën + + + + + Show style warnings + Toon stijl waarschuwingen + + + + Open the help contents + Open de help inhoud + + + + F1 + + + + + &Help + &Help + + + + + Quick Filter: + Snel Filter: + + + + Select configuration + + + + + Found project file: %1 + +Do you want to load this project file instead? + Project bestand gevonden: %1 +Wilt u dit project laden in plaats van? + + + + File not found + + + + + Bad XML + + + + + Missing attribute + + + + + Bad attribute value + + + + + Duplicate define + + + + + Failed to load the selected library '%1'. +%2 + + + + + File not found: '%1' + + + + + Failed to load/setup addon %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. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + +Analysis is aborted. + + + + + Failed to load %1 - %2 + +Analysis is aborted. + + + + + + %1 + +Analysis is aborted. + + + + + License + Licentie + + + + Authors + Auteurs + + + + 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! + + + + The library '%1' contains unknown elements: +%2 + + + + + Unsupported format + + + + + Duplicate platform type + + + + + Platform type redefined + + + + + Unknown element + + + + + Unknown issue + + + + + + + + Error + + + + + Open the report file + Open het rapport bestand + + + + Text files (*.txt) + Tekst bestanden (*.txt) + + + + CSV files (*.csv) + CSV bestanden (*.csv) + + + + Project files (*.cppcheck);;All files(*.*) + Project bestanden (*.cppcheck);;Alle bestanden(*.*) + + + + Select Project File + Selecteer project bestand + + + + + + + Project: + Project: + + + + No suitable files found to analyze! + + + + + C/C++ Source + + + + + Compile database + + + + + Visual Studio + + + + + Borland C++ Builder 6 + + + + + Select files to analyze + + + + + Select directory to analyze + + + + + Select the configuration that will be analyzed + + + + + Found project files from the directory. + +Do you want to proceed analysis without using any of these project files? + + + + + Current results will be cleared. + +Opening a new XML file will clear current results. +Do you want to proceed? + + + + + Analyzer is running. + +Do you want to stop the analysis and exit Cppcheck? + + + + + About + + + + + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) + + + + + Cannot generate a compliance report right now, an analysis must finish successfully. Try to reanalyze the code and ensure there are no critical errors. + + + + + Build dir '%1' does not exist, create it? + + + + + To check the project using addons, you need a build directory. + + + + + Failed to open file + + + + + Unknown project file format + + + + + Failed to import project file + + + + + Failed to import '%1': %2 + +Analysis is stopped. + + + + + Failed to import '%1' (%2), analysis is stopped + + + + + Project files (*.cppcheck) + + + + + Select Project Filename + Selecteer project bestandsnaam + + + + No project file loaded + Geen project bestand geladen + + + + The project file + +%1 + + could not be found! + +Do you want to remove the file from the recently used projects -list? + Het project bestand + +%1 + +Kan niet worden gevonden! +Wilt u het bestand van de onlangs gebruikte project verwijderen -lijst? + + + + Install + + + + + New version available: %1. %2 + + + + + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> This option is for installation scripts so they can configure the directory where + datafiles are located (translations, cfg). The GUI is not started when this option + is used. + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) + + + + + Cppcheck GUI - Command line parameters + Cppcheck GUI - Command lijn parameters + + + + NewSuppressionDialog + + + New suppression + + + + + Error ID + + + + + File name + + + + + Line number + + + + + Symbol name + + + + + Edit suppression + + + + + Platforms + + + Native + + + + + Unix 32-bit + + + + + Unix 64-bit + + + + + Windows 32-bit ANSI + + + + + Windows 32-bit Unicode + + + + + Windows 64-bit + + + + + ProjectFile + + + Project File + Project Bestand + + + + Paths and Defines + + + + + Import Project (Visual studio / compile database/ Borland C++ Builder 6) + Import Project (Visual studio / compile database) + + + + + Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int + Defines must be separated by a semicolon ';' + + + + + Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. + + + + + If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. + + + + + Exclude source files + + + + + Exclude folder... + + + + + Exclude file... + + + + + Misra C + + + + + 2012 + + + + + 2023 + + + + + MISRA rule texts + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + ... + + + + + <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> + + + + + + Browse... + + + + + Analyze all Visual Studio configurations + + + + + Selected VS Configurations + + + + + Paths: + Paden: + + + + + Add... + Toevoegen... + + + + + + Edit + Bewerk + + + + + + + Remove + Verwijder + + + + Undefines: + + + + + Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 + + + + + Include Paths: + + + + + Types and Functions + + + + + + Analysis + + + + + This is a workfolder that Cppcheck will use for various purposes. + + + + + Parser + + + + + Cppcheck (built in) + + + + + Check level + + + + + Normal -- meant for normal analysis in CI. Analysis should finish in reasonable time. + + + + + Exhaustive -- meant for nightly builds etc. Analysis time can be longer (10x slower than compilation is OK). + + + + + Check that each class has a safe public interface + + + + + Limit analysis + + + + + Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) + Check code in unused templates (slower and less accurate analysis) + + + + + Max CTU depth + + + + + Cert C + + + + + CERT-INT35-C: int precision (if size equals precision, you can leave empty) + + + + + Misra C++ 2008 + + + + + Autosar + + + + + Bug hunting + + + + + External tools + + + + + Up + Omhoog + + + + Down + Omlaag + + + + Platform + + + + + Clang (experimental) + + + + + If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. + + + + + Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) + + + + + Max recursion in template instantiation + + + + + Warning options + + + + + Root path: + + + + + Filepaths in warnings will be relative to this path + + + + + Warning tags (separated by semicolon) + + + + + Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) + + + + + Libraries + + + + + Suppressions + + + + + Add + + + + + + Addons + + + + + Note: Addons require <a href="https://www.python.org/">Python</a> being installed. + + + + + Y2038 + + + + + Thread safety + + + + + Coding standards + + + + + Cert C++ + + + + + Bug hunting (Premium) + + + + + Clang analyzer + + + + + Clang-tidy + + + + + Defines: + Omschrijft: + + + + ProjectFileDialog + + + Project file: %1 + Project Bestand %1 + + + + Select Cppcheck build dir + + + + + Select include directory + Selecteer include map + + + + Select a directory to check + Selecteer een map om te controleren + + + + Clang-tidy (not found) + + + + + Visual Studio + + + + + Compile database + + + + + Borland C++ Builder 6 + + + + + Import Project + + + + + Select directory to ignore + Selecteer een map om te negeren + + + + Source files + + + + + All files + + + + + Exclude file + + + + + Select MISRA rule texts file + + + + + MISRA rule texts file (%1) + + + + + QObject + + + Unknown language specified! + Onbekende taal gekozen! + + + + Language file %1 not found! + Language file %1.qm not found! + Kon het taalbestand niet vinden: %1! + + + + Failed to load translation for language %1 from file %2 + Failed to load translation for language %1 from file %2.qm + Kon de vertaling voor taal %1 in bestand %2 niet laden + + + + line %1: Unhandled element %2 + + + + + line %1: Mandatory attribute '%2' missing in '%3' + + + + + (Not found) + + + + + Thin + + + + + ExtraLight + + + + + Light + + + + + Normal + + + + + Medium + + + + + DemiBold + + + + + Bold + + + + + ExtraBold + + + + + Black + + + + + Editor Foreground Color + + + + + Editor Background Color + + + + + Highlight Background Color + + + + + Line Number Foreground Color + + + + + Line Number Background Color + + + + + Keyword Foreground Color + + + + + Keyword Font Weight + + + + + Class Foreground Color + Class ForegroundColor + + + + + Class Font Weight + + + + + Quote Foreground Color + + + + + Quote Font Weight + + + + + Comment Foreground Color + + + + + Comment Font Weight + + + + + Symbol Foreground Color + + + + + Symbol Background Color + + + + + Symbol Font Weight + + + + + Set to Default Light + + + + + Set to Default Dark + + + + + QPlatformTheme + + + OK + + + + + Cancel + Annuleer + + + + Close + Sluit + + + + Save + Opslaan + + + + ResultsTree + + + File + Bestand + + + + Severity + Ernst + + + + Line + Regel + + + + Summary + Overzicht + + + + Undefined file + Niet gedefinieerd bestand + + + + Copy + + + + + Could not find file: + + + + + Please select the folder '%1' + + + + + Select Directory '%1' + + + + + Please select the directory where file is located. + + + + + debug + + + + + note + + + + + Recheck + + + + + Hide + Verberg + + + + Hide all with id + Verberg alles met id + + + + Suppress selected id(s) + + + + + Open containing folder + + + + + internal + + + + + + Tag + + + + + No tag + + + + + + Cppcheck + Cppcheck + + + + No editor application configured. + +Configure the editor application for Cppcheck in preferences/Applications. + Configure the text file viewer program in Cppcheck preferences/Applications. + Er is geen bewerker toepassing geconfigureerd. + +Configureer de bewerker toepassing voor cppcheck in voorkeuren/Applicaties. + + + + No default editor application selected. + +Please select the default editor application in preferences/Applications. + Geen standaard bewerker geselecteerd. +Selecteer de standaard bewerker in voorkeuren/Applicaties. + + + + Could not find the file! + Kon het bestand niet vinden! + + + + Could not start %1 + +Please check the application path and parameters are correct. + Kon applicatie %1 niet starten + +Gelieve te controleren of de het pad en de parameters correct zijn. + + + + Select Directory + Selecteer map + + + + Id + Id + + + + Inconclusive + + + + + Since date + + + + + style + Stijlfouten + + + + error + Fouten + + + + warning + Waarschuwing + + + + performance + Presentatie + + + + portability + Portabiliteit + + + + information + Informatie + + + + ResultsView + + + Print Report + + + + + No errors found, nothing to print. + + + + + %p% (%1 of %2 files checked) + %p% (%1 van %2 bestanden gecontroleerd) + + + + + Cppcheck + Cppcheck + + + + No errors found. + Geen fouten gevonden. + + + + Errors were found, but they are configured to be hidden. +To toggle what kind of errors are shown, open view menu. + Fouten werden gevonden, maar volgens de configuratie zijn deze verborgen. +Gebruik het uitzicht menu om te selecteren welke fouten getoond worden. + + + + + Failed to read the report. + Kon rapport niet lezen. + + + + XML format version 1 is no longer supported. + + + + + First included by + + + + + Id + Id + + + + Bug hunting analysis is incomplete + + + + + Clear Log + + + + + Copy this Log entry + + + + + Copy complete Log + + + + + Analysis was stopped + + + + + There was a critical error with id '%1' + + + + + when checking %1 + + + + + when checking a file + + + + + Analysis was aborted. + + + + + + Failed to save the report. + Kon het rapport niet opslaan. + + + + Results + Resultaten + + + + Critical errors + + + + + Analysis Log + + + + + Warning Details + + + + + ScratchPad + + + Scratchpad + Scratchpad + + + + Copy or write some C/C++ code here: + + + + + Optionally enter a filename (mainly for automatic language detection) and click on "Check": + + + + + filename + bestandsnaam + + + + Check + Controleer + + + + Settings + + + Preferences + Instellingen + + + + General + Algemeen + + + + Add... + Toevoegen... + + + + Number of threads: + Aantal threads: + + + + Ideal count: + Ideale telling: + + + + Force checking all #ifdef configurations + Check all #ifdef configurations + Controleer alle #ifdef combinaties + + + + Show full path of files + Toon het volledige pad van bestanden + + + + Show "No errors found" message when no errors found + Toon "Geen fouten gevonden" indien geen fouten gevonden werden + + + + Display error Id in column "Id" + Toon fout ld in kolom "Id" + + + + Enable inline suppressions + Schakel inline suppressies in + + + + Check for inconclusive errors also + + + + + Show statistics on check completion + + + + + Check for updates + + + + + Show internal warnings in log + + + + + Addons + + + + + Python binary (leave this empty to use python in the PATH) + + + + + + + ... + + + + + MISRA addon + + + + + MISRA rule texts file + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + Clang + + + + + Clang path (leave empty to use system PATH) + + + + + Visual Studio headers + + + + + <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> + + + + + Code Editor + + + + + Code Editor Style + + + + + System Style + + + + + Default Light Style + + + + + Default Dark Style + + + + + Custom + + + + + Remove + 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 + + + + SettingsDialog + + + N/A + + + + + The executable file "%1" is not available + + + + + Add a new application + Nieuwe applicatie toevoegen + + + + Modify an application + Applicatie wijzigen + + + + [Default] + + + + + [Default] + [Standaard] + + + + Select python binary + + + + + Select MISRA File + + + + + Select clang path + + + + + StatsDialog + + + + + + Statistics + Statistieken + + + + + Project + Project + + + + Project: + Project: + + + + Paths: + Paden: + + + + Include paths: + Bevat paden: + + + + Defines: + Omschrijft: + + + + Undefines: + + + + + + Previous Scan + Vorige scan + + + + Path Selected: + Pad Geselekteerd: + + + + Number of Files Scanned: + Aantal bestanden gescanned: + + + + Scan Duration: + Scan tijd: + + + + Errors: + Fouten: + + + + Warnings: + Waarschuwingen: + + + + Stylistic warnings: + Stilistisch waarschuwingen: + + + + Portability warnings: + Portabiliteit waarschuwingen: + + + + Performance issues: + Presentatie problemen: + + + + Information messages: + Informatie bericht: + + + + Active checkers: + + + + + Checkers + + + + + History + + + + + File: + + + + + Copy to Clipboard + Kopieer naar Clipbord + + + + Pdf Export + + + + + 1 day + 1 dag + + + + %1 days + %1 dagen + + + + 1 hour + 1 uur + + + + %1 hours + %1 uren + + + + 1 minute + 1 minuut + + + + %1 minutes + %1 minuten + + + + 1 second + 1 seconde + + + + %1 seconds + %1 secondes + + + + 0.%1 seconds + 0.%1 secondes + + + + and + en + + + + Export PDF + + + + + Project Settings + Project instellingen + + + + Paths + Paden + + + + Include paths + Bevat paden + + + + Defines + Omschrijft + + + + Undefines + + + + + Path selected + Pad Geselekteerd + + + + Number of files scanned + Aantal bestanden gescanned + + + + Scan duration + Scan tijd + + + + + Errors + Fouten + + + + File: + + + + + No cppcheck build dir + + + + + + Warnings + Waarschuwingen + + + + + Style warnings + Stijl waarschuwingen + + + + + Portability warnings + Portabiliteit waarschuwingen + + + + + Performance warnings + Presentatie waarschuwingen + + + + + Information messages + Informatie bericht + + + + ThreadResult + + + %1 of %2 files checked + %1 van %2 bestanden gecontroleerd + + + + TranslationHandler + + + Failed to change the user interface language: + +%1 + +The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. + Mislukt om de gebruikers taal te wijzigen: + +%1 + +De gebruikerstaal is gereset naar Engels. Open het dialoogvenster om een van de beschikbare talen te selecteren. + + + + Cppcheck + Cppcheck + + + + TxtReport + + + inconclusive + Onduidelijk + + + + toFilterString + + + All supported files (%1) + + + + + All files (%1) + + + + diff --git a/cppcheck-2.14.0/gui/cppcheck_ru.ts b/cppcheck-2.14.0/gui/cppcheck_ru.ts new file mode 100644 index 00000000..63b0b519 --- /dev/null +++ b/cppcheck-2.14.0/gui/cppcheck_ru.ts @@ -0,0 +1,3055 @@ + + + + + About + + + About Cppcheck + О Cppcheck + + + + Version %1 + Версия %1 + + + + Cppcheck - A tool for static C/C++ code analysis. + Cppcheck - программа для статического анализа кода на языках С/С++. + + + + Copyright © 2007-%1 Cppcheck team. + Copyright © 2007-2021 Cppcheck team. + Copyright © 2007-2021 Cppcheck team. + + + + This program is licensed under the terms +of the GNU General Public License version 3 + Эта программа распространяется на +условиях лицензии GNU General Public License, версии 3 + + + + Visit Cppcheck homepage at %1 + Посетите домашнюю страницу: %1 + + + + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PCRE</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PicoJSON</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">TinyXML2</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Boost</li></ul></body></html> + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">pcre</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">picojson</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">tinyxml2</li></ul></body></html> + + + + + ApplicationDialog + + + Add an application + Добавление приложения + + + + Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. + +The following texts in parameters are replaced with appropriate values when application is executed: +(file) - Filename containing the error +(line) - Line number containing the error +(message) - Error message +(severity) - Error severity + +Example opening a file with Kate and make Kate scroll to the correct line: +Executable: kate +Parameters: -l(line) (file) + Вы можете добавить приложение, которое можно будет использовать для открытия файла с ошибками. Задайте название приложения, путь до него и параметры командной строки. + +Следующие текстовые параметры будут заменены реальными значениями при запуске приложения: +(file) - файл, содержащий ошибку +(line) - номер строки с ошибкой +(message) - текст ошибки +(severity) - тип ошибки + +Пример открытия файла с помощью Kate (скролл переместится на нужную строчку): +Программа: kate +Параметры: -l(line) (file) + + + + &Name: + &Название: + + + + &Executable: + &Программа: + + + + &Parameters: + &Параметры: + + + + Browse + Просмотреть + + + + Executable files (*.exe);;All files(*.*) + Выполняемые файлы (*.exe);;Все файлы(*.*) + + + + Select viewer application + Выберите приложение + + + + Cppcheck + Cppcheck + + + + You must specify a name, a path and optionally parameters for the application! + Вы должны задать название и путь к приложению! + + + + ComplianceReportDialog + + + Compliance Report + + + + + Project name + + + + + Project version + + + + + Coding Standard + + + + + Misra C + + + + + Cert C + + + + + Cert C++ + + + + + List of files with md5 checksums + + + + + Compliance report + + + + + HTML files (*.html) + + + + + + Save compliance report + + + + + Failed to import '%1' (%2), can not show files in compliance report + + + + + FileViewDialog + + + Could not find the file: %1 + Could not find the file: + + Невозможно найти файл: %1 + + + + + Cppcheck + Cppcheck + + + + Could not read the file: %1 + Невозможно прочитать файл: %1 + + + + HelpDialog + + + Cppcheck GUI help + + + + + Contents + + + + + Index + + + + + Helpfile '%1' was not found + + + + + Cppcheck + Cppcheck + + + + LibraryAddFunctionDialog + + + Add function + Добавить функцию + + + + Function name(s) + Имя(имена) функции + + + + Number of arguments + Количество аргументов + + + + LibraryDialog + + + Library Editor + Редактор библиотек + + + + Open + Открыть + + + + Save + Сохранить + + + + Save as + Сохранить как + + + + Functions + Функции + + + + Sort + Сортировать + + + + Add + Добавить + + + + Filter: + Фильтр: + + + + Comments + Комментарии + + + + noreturn + + + + + False + + + + + True + + + + + Unknown + + + + + return value must be used + должно быть использовано возвращаемое значение + + + + ignore function in leaks checking + пропускать функцию при проверке на утечки + + + + Arguments + Аргументы + + + + Edit + Изменить + + + + + Library files (*.cfg) + Файлы библиотек (*.cfg) + + + + Open library file + Открыть файл библиотеки + + + + + + Cppcheck + Cppcheck + + + + Cannot open file %1. + Can not open file %1. + Невозможно открыть файл %1. + + + + Failed to load %1. %2. + Ошибка загрузки %1. %2. + + + + Cannot save file %1. + Can not save file %1. + Невозможно сохранить файл %1. + + + + Save the library as + Сохранить библиотеку как + + + + LibraryEditArgDialog + + + Edit argument + Редактировать аргумент + + + + <html><head/><body> +<p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> +<p>Typically, set this if the argument is a pointer, size, etc.</p> +<p>Example:</p> +<pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> +</body></html> + + + + + Not bool + + + + + <html><head/><body> +<p>Is a null parameter value allowed?</p> +<p>Typically this should be used on any pointer parameter that does not allow null.</p> +<p>Example:</p> +<pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> +</body></html> + + + + + Not null + + + + + Not uninit + + + + + String + + + + + Format string + + + + + Min size of buffer + + + + + + Type + + + + + + None + + + + + + argvalue + + + + + + mul + + + + + + strlen + + + + + + Arg + + + + + + Arg2 + + + + + and + + + + + Valid values + + + + + MainWindow + + + + + + + + + + + + + + + + + Cppcheck + Cppcheck + + + + A&nalyze + Анализ + + + + Standard + Стандартные + + + + &File + &Файл + + + + &View + &Вид + + + + &Toolbars + &Панель инструментов + + + + C++ standard + Стандарт C++ + + + + &C standard + C standard + &Стандарт C + + + + &Edit + &Правка + + + + &License... + &Лицензия... + + + + A&uthors... + &Авторы... + + + + &About... + &О программе... + + + + &Files... + &Файлы... + + + + + Analyze files + Check files + Проверить файлы + + + + Ctrl+F + Ctrl+F + + + + &Directory... + &Каталог... + + + + + Analyze directory + Check directory + Проверка каталога + + + + Ctrl+D + Ctrl+D + + + + Ctrl+R + Ctrl+R + + + + &Stop + Остановить + + + + + Stop analysis + Stop checking + Остановить проверку + + + + Esc + Esc + + + + &Save results to file... + Сохранить отчёт в файл... + + + + Ctrl+S + Ctrl+S + + + + &Quit + Выход + + + + &Clear results + Очистить отчёт + + + + &Preferences + Параметры + + + + + Show errors + Показать ошибки + + + + + Show warnings + Показать предупреждения + + + + + Show performance warnings + Показать предупреждения производительности + + + + Show &hidden + Показать скрытые + + + + + Information + Информационные сообщения + + + + Show information messages + Показать информационные сообщения + + + + Show portability warnings + Показать предупреждения переносимости + + + + Show Cppcheck results + Просмотр результатов Cppcheck + + + + Clang + Clang + + + + Show Clang results + Просмотр результатов Clang + + + + &Filter + Фильтр + + + + Filter results + Результаты фильтрации + + + + Windows 32-bit ANSI + Windows 32-bit ANSI + + + + Windows 32-bit Unicode + Windows 32-bit Unicode + + + + Unix 32-bit + Unix 32-bit + + + + Unix 64-bit + Unix 64-bit + + + + Windows 64-bit + Windows 64-bit + + + + &Print... + Печать... + + + + Print the Current Report + Напечатать текущий отчет + + + + Print Pre&view... + Предварительный просмотр... + + + + Open a Print Preview Dialog for the Current Results + Открыть диалог печати для текущих результатов + + + + Open library editor + Открыть редактор библиотек + + + + &Check all + Отметить все + + + + Checking for updates + + + + + Hide + Скрыть + + + + Filter + Фильтр + + + + &Reanalyze modified files + &Recheck modified files + Заново проверить измененные файлы + + + + Reanal&yze all files + Заново проверить все файлы + + + + Ctrl+Q + + + + + Style war&nings + Стилистические предупреждения + + + + E&rrors + Ошибки + + + + &Uncheck all + Сбросить все + + + + Collapse &all + Свернуть все + + + + &Expand all + Развернуть все + + + + &Standard + Стандартные + + + + Standard items + Стандартные элементы + + + + Toolbar + Панель инструментов + + + + &Categories + Категории + + + + Error categories + Категории ошибок + + + + &Open XML... + &Открыть XML... + + + + Open P&roject File... + Открыть файл &проекта... + + + + Ctrl+Shift+O + + + + + Sh&ow Scratchpad... + Показать Блокнот + + + + &New Project File... + &Новый файл проекта... + + + + Ctrl+Shift+N + + + + + &Log View + Посмотреть &лог + + + + Log View + Посмотреть лог + + + + C&lose Project File + &Закрыть файл проекта + + + + &Edit Project File... + &Изменить файл проекта... + + + + &Statistics + &Статистика + + + + &Warnings + Предупреждения + + + + Per&formance warnings + Предупреждения производительности + + + + &Information + Информационные предупреждения + + + + &Portability + Предупреждения переносимости + + + + P&latforms + Платформы + + + + C++&11 + + + + + C&99 + + + + + &Posix + + + + + C&11 + + + + + &C89 + + + + + &C++03 + + + + + &Library Editor... + Редактор библиотеки + + + + &Auto-detect language + Автоматическое определение языка + + + + &Enforce C++ + Принудительно C++ + + + + E&nforce C + Принудительно C + + + + C++14 + C++14 + + + + Reanalyze and check library + Повторный анализ библиотеки + + + + Check configuration (defines, includes) + Проверить конфигурацию (defines, includes) + + + + C++17 + C++17 + + + + C++20 + C++20 + + + + Compliance report... + + + + + &Contents + Помощь + + + + Categories + Категории + + + + + Show style warnings + Показать стилистические предупреждения + + + + Open the help contents + Открыть помощь + + + + F1 + F1 + + + + &Help + Помощь + + + + + Quick Filter: + Быстрый фильтр: + + + + Select configuration + Выбор конфигурации + + + + Found project file: %1 + +Do you want to load this project file instead? + Найден файл проекта: %1 + +Вы хотите загрузить этот проект? + + + + File not found + Файл не найден + + + + Bad XML + Некорректный XML + + + + Missing attribute + Пропущен атрибут + + + + Bad attribute value + Некорректное значение атрибута + + + + Unsupported format + Неподдерживаемый формат + + + + Duplicate define + + + + + Failed to load the selected library '%1'. +%2 + Не удалось загрузить выбранную библиотеку '%1'. +%2 + + + + File not found: '%1' + + + + + Failed to load/setup addon %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. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + +Analysis is aborted. + + + + + Failed to load %1 - %2 + +Analysis is aborted. + + + + + + %1 + +Analysis is aborted. + + + + + License + Лицензия + + + + Authors + Авторы + + + + 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! + Вы должны закрыть проект перед выбором новых файлов или каталогов! + + + + The library '%1' contains unknown elements: +%2 + Библиотека '%1' содержит неизвестные элементы: +%2 + + + + Duplicate platform type + Дубликат типа платформы + + + + Platform type redefined + Переобъявление типа платформы + + + + Unknown element + Неизвестный элемент + + + + Unknown issue + Неизвестная проблема + + + + + + + Error + Ошибка + + + Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + Невозможно загрузить %1. Cppcheck установлен некорректно. Вы можете использовать --data-dir=<directory> в командной строке для указания расположения файлов конфигурации. Обратите внимание, что --data-dir предназначен для использования сценариями установки. При включении данной опции, графический интерфейс пользователя не запускается. + + + + Open the report file + Открыть файл с отчетом + + + + Text files (*.txt) + Текстовые файлы (*.txt) + + + + CSV files (*.csv) + CSV файлы(*.csv) + + + + Project files (*.cppcheck);;All files(*.*) + Файлы проекта (*.cppcheck);;Все файлы(*.*) + + + + Select Project File + Выберите файл проекта + + + + + + + Project: + Проект: + + + + No suitable files found to analyze! + Не найдено подходящих файлов для анализа + + + + C/C++ Source + Исходный код C/C++ + + + + Compile database + + + + + Visual Studio + Visual Studio + + + + Borland C++ Builder 6 + Borland C++ Builder 6 + + + + Select files to analyze + Выбор файлов для анализа + + + + Select directory to analyze + Выбор каталога для анализа + + + + Select the configuration that will be analyzed + Выбор используемой конфигурации + + + + Found project files from the directory. + +Do you want to proceed analysis without using any of these project files? + Обнаружены файлы проекты из каталога. + +Вы хотите продолжить анализ без использования этих файлов проекта? + + + + Current results will be cleared. + +Opening a new XML file will clear current results. +Do you want to proceed? + Текущие результаты будут очищены. + +Открытие нового XML-файла приведет к очистке текущих результатов. +Вы хотите продолжить? + + + + Analyzer is running. + +Do you want to stop the analysis and exit Cppcheck? + Анализатор запущен. + +Вы хотите остановить анализ и выйти из Cppcheck? + + + + About + + + + + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) + XML файлы (*.xml);;Текстовые файлы (*.txt);;CSV файлы (*.csv) + + + + Cannot generate a compliance report right now, an analysis must finish successfully. Try to reanalyze the code and ensure there are no critical errors. + + + + + Build dir '%1' does not exist, create it? + Директория для сборки '%1' не существует, создать? + + + + To check the project using addons, you need a build directory. + + + + + Failed to open file + + + + + Unknown project file format + + + + + Failed to import project file + + + + + Failed to import '%1': %2 + +Analysis is stopped. + + + + + Failed to import '%1' (%2), analysis is stopped + + + + Failed to import '%1', analysis is stopped + Невозможно импортировать '%1', анализ остановлен + + + + Project files (*.cppcheck) + Файлы проекта (*.cppcheck) + + + + Select Project Filename + Выберите имя файла для проекта + + + + No project file loaded + Файл с проектом не загружен + + + + The project file + +%1 + + could not be found! + +Do you want to remove the file from the recently used projects -list? + Файл с проектом + +%1 + +не найден! +Хотите удалить его из списка проектов? + + + + Install + + + + + New version available: %1. %2 + + + + + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> This option is for installation scripts so they can configure the directory where + datafiles are located (translations, cfg). The GUI is not started when this option + is used. + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) + Cppcheck GUI. + +Синтаксис: + cppcheck-gui [ОПЦИИ] [файлы или пути] + +Опции: + -h, --help Выдать подсказку на стандартный вывод и успешно завершиться. + -p <file> Открыть указанный файл проекта и начать проверку + -l <file> Открыть xml-файл с полученными результатами + -d <directory> Указать каталог, который был проверен для создания результатов xml, указанных с помощью -l + -v, --version Выдать информацию о версии на стандартный вывод и успешно завершиться. + --data-dir=<directory> Этот параметр предназначен для сценариев установки, чтобы они могли + настроить каталог, в котором расположены файлы данных (конфигурация, переводы). + Графический интерфейс пользователя не будет запущен, если указана эта опция. + + + + Cppcheck GUI - Command line parameters + Cppcheck GUI - параметры Командной строки + + + + NewSuppressionDialog + + + New suppression + Новое подавление + + + + Error ID + ID + + + + File name + Имя файла + + + + Line number + Номер строки + + + + Symbol name + Имя символа + + + + Edit suppression + Редактировать подавление + + + + Platforms + + + Native + + + + + Unix 32-bit + Unix 32-bit + + + + Unix 64-bit + Unix 64-bit + + + + Windows 32-bit ANSI + Windows 32-bit ANSI + + + + Windows 32-bit Unicode + Windows 32-bit Unicode + + + + Windows 64-bit + Windows 64-bit + + + + ProjectFile + + + Project File + Файл проекта + + + + Paths and Defines + Каталоги и определения + + + + Import Project (Visual studio / compile database/ Borland C++ Builder 6) + Import Project (Visual studio / compile database) + Импорт проекта (Visual studio / compile database/ Borland C++ Builder 6) + + + + Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int + Defines must be separated by a semicolon ';' + Defines должны быть разделены точкой с запятой ';' + + + + Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. + Положите свои .cfg-файлы в один каталог с файлом проекта. Вы увидите их сверху. + + + + Clang (experimental) + + + + + If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. + + + + + Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) + + + + + Max recursion in template instantiation + + + + + Filepaths in warnings will be relative to this path + + + + + If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. + + + + + Exclude source files + + + + + Exclude folder... + + + + + Exclude file... + + + + MISRA C 2012 + MISRA C 2012 + + + + MISRA rule texts + Файл с текстами правил MISRA + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + <html><head/><body><p>Скопируйте текст из Appendix A &quot;Summary of guidelines&quot; из фала правил MISRA C 2012 pdf в текстовый файл.</p></body></html> + + + + ... + ... + + + + <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> + <html><head/><body><p>Выберите:</p><p> * Анализ всех конфигураций Debug и Release</p><p> * Анализ только первой подходящей конфигурации Debug</p><p><br/></p></body></html> + + + + + Browse... + Обзор... + + + + Analyze all Visual Studio configurations + Анализировать все конфигурации Visual Studio + + + + Selected VS Configurations + + + + + Paths: + Пути: + + + + + Add... + Добавить... + + + + + + Edit + Изменить + + + + + + + Remove + Удалить + + + + Undefines: + Удаленные макроопределения: + + + + Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 + Удаленные макроопределения должны быть разделены точкой с запятой, например: UNDEF1;UNDEF2;UNDEF3 + + + + Include Paths: + Пути заголовочных файлов: + + + + Types and Functions + + + + + + Analysis + Анализ + + + + This is a workfolder that Cppcheck will use for various purposes. + + + + + Parser + + + + + Cppcheck (built in) + + + + + Check level + + + + + Normal -- meant for normal analysis in CI. Analysis should finish in reasonable time. + + + + + Exhaustive -- meant for nightly builds etc. Analysis time can be longer (10x slower than compilation is OK). + + + + + Check that each class has a safe public interface + + + + + Limit analysis + + + + + Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) + Check code in unused templates (slower and less accurate analysis) + Проверить код в неиспользуемых шаблонах + + + + Max CTU depth + Максимальная глубина CTU + + + + Cert C + + + + + CERT-INT35-C: int precision (if size equals precision, you can leave empty) + + + + + Misra C++ 2008 + + + + + Autosar + + + + + Bug hunting + + + + + External tools + Внешние инструменты + + + + Up + Вверх + + + + Down + Вниз + + + + Platform + Платформа + + + + Warning options + Опции предупреждений + + + + Root path: + Корневой каталог: + + + + Warning tags (separated by semicolon) + Теги предупреждений (через ';') + + + + Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) + Каталог сборки Cppcheck + + + + Libraries + Библиотеки + + + + Suppressions + Подавления + + + + Add + Добавить + + + + + Addons + Дополнения + + + + Note: Addons require <a href="https://www.python.org/">Python</a> being installed. + + + + + Y2038 + + + + + Thread safety + + + + + Coding standards + Стандарты кодирования + + + + Misra C + + + + + 2012 + + + + + 2023 + + + + + Cert C++ + + + + + Bug hunting (Premium) + + + + + Clang analyzer + + + + + Clang-tidy + + + + + Defines: + Объявленные макроопределения: + + + + ProjectFileDialog + + + Project file: %1 + Файл проекта: %1 + + + + Select Cppcheck build dir + Выбрать директорию сборки Cppcheck + + + + Select include directory + Выберите директорию для поиска заголовочных файлов + + + + Select a directory to check + Выберите директорию для проверки + + + + Clang-tidy (not found) + Clang-tidy (не найден) + + + + Visual Studio + Visual Studio + + + + Compile database + + + + + Borland C++ Builder 6 + Borland C++ Builder 6 + + + + Import Project + Импорт проекта + + + + Select directory to ignore + Выберите директорию, которую надо проигнорировать + + + + Source files + + + + + All files + + + + + Exclude file + + + + + Select MISRA rule texts file + Выбрать файл текстов правил MISRA + + + + MISRA rule texts file (%1) + Файл текстов правил MISRA (%1) + + + + QObject + + + Unknown language specified! + Неизвестный язык! + + + + Language file %1 not found! + Language file %1.qm not found! + Языковой файл %1 не найден! + + + + Failed to load translation for language %1 from file %2 + Failed to load translation for language %1 from file %2.qm + Ошибка загрузки переводов для языка %1 из файла %2 + + + + line %1: Unhandled element %2 + + + + + line %1: Mandatory attribute '%2' missing in '%3' + + + + + (Not found) + (Недоступно) + + + + Thin + + + + + ExtraLight + + + + + Light + + + + + Normal + + + + + Medium + + + + + DemiBold + + + + + Bold + + + + + ExtraBold + + + + + Black + + + + + Editor Foreground Color + + + + + Editor Background Color + + + + + Highlight Background Color + + + + + Line Number Foreground Color + + + + + Line Number Background Color + + + + + Keyword Foreground Color + + + + + Keyword Font Weight + + + + + Class Foreground Color + Class ForegroundColor + + + + + Class Font Weight + + + + + Quote Foreground Color + + + + + Quote Font Weight + + + + + Comment Foreground Color + + + + + Comment Font Weight + + + + + Symbol Foreground Color + + + + + Symbol Background Color + + + + + Symbol Font Weight + + + + + Set to Default Light + + + + + Set to Default Dark + + + + + QPlatformTheme + + + OK + OK + + + + Cancel + Отмена + + + + Close + Закрыть + + + + Save + Сохранить + + + + ResultsTree + + + File + Файл + + + + Severity + Важность + + + + Line + Строка + + + + Summary + Кратко + + + + Undefined file + Неопределенный файл + + + + Copy + Копировать + + + + Could not find file: + Невозможно найти файл: + + + + Please select the folder '%1' + Выберите каталог '%1' + + + + Select Directory '%1' + Выбрать каталог '%1' + + + + Please select the directory where file is located. + Укажите каталог с расположением файла. + + + + debug + отлаживать + + + + note + заметка + + + + Recheck + Проверить заново + + + + Hide + Скрыть + + + + Hide all with id + Скрыть все с id + + + + Suppress selected id(s) + Подавить выбранные id + + + + Open containing folder + Открыть содержащую папку + + + + internal + + + + + + Tag + Тег + + + + No tag + Тег отсутствует + + + + + Cppcheck + Cppcheck + + + + No editor application configured. + +Configure the editor application for Cppcheck in preferences/Applications. + Configure the text file viewer program in Cppcheck preferences/Applications. + Никакое приложение редактора не сконфигурировано. +Сконфигурируйте приложение редактора для Cppcheck в предпочтениях/Приложениях. + + + + No default editor application selected. + +Please select the default editor application in preferences/Applications. + Никакое приложение редактора по умолчанию не выбрано. +Выберите приложение редактора по умолчанию в предпочтениях/Приложениях. + + + + Could not find the file! + Не удается найти файл! + + + + Could not start %1 + +Please check the application path and parameters are correct. + Не удалось запустить %1 +Пожалуйста, проверьте путь приложения, и верны ли параметры. + + + + Select Directory + Выберите директорию + + + + Id + Id + + + + Inconclusive + Спорное + + + + Since date + Начиная с даты + + + + style + стиль + + + + error + ошибка + + + + warning + предупреждение + + + + performance + производительность + + + + portability + переносимость + + + + information + информация + + + + ResultsView + + + Print Report + Распечатать отчет + + + + No errors found, nothing to print. + Ошибок не найдено, нечего распечатывать. + + + + %p% (%1 of %2 files checked) + %p% (%1 из %2 файлов проверено) + + + + + Cppcheck + Cppcheck + + + + No errors found. + Ошибок не найдено. + + + + Errors were found, but they are configured to be hidden. +To toggle what kind of errors are shown, open view menu. + Были обнаружены ошибки, но они настроены быть скрыты. +Для переключения какие ошибки отображаются, откройте меню представления. + + + + + Failed to read the report. + Не удалось прочитать отчет. + + + + XML format version 1 is no longer supported. + XML формат версии 1 больше не поддерживается. + + + + First included by + Только первый включенный + + + + Id + Id + + + + Bug hunting analysis is incomplete + + + + + Clear Log + Очистить лог + + + + Copy this Log entry + Скопировать данную запись + + + + Copy complete Log + Скопировать полный лог + + + + Analysis was stopped + + + + + There was a critical error with id '%1' + + + + + when checking %1 + + + + + when checking a file + + + + + Analysis was aborted. + + + + + + Failed to save the report. + Не удалось сохранить отчет. + + + + Results + Результаты + + + + Critical errors + + + + + Analysis Log + Лог анализа + + + + Warning Details + Детали предупреждения + + + + ScratchPad + + + Scratchpad + Блокнот + + + + Copy or write some C/C++ code here: + Исходный код C/C++: + + + + Optionally enter a filename (mainly for automatic language detection) and click on "Check": + При необходимости введите имя файла и нажмите "Проверить": + + + + filename + имя файла + + + + Check + Проверить + + + + Settings + + + Preferences + Параметры + + + + General + Общие + + + + Add... + Добавить... + + + + Number of threads: + Количество потоков исполнения: + + + + Ideal count: + Рекомендуемое значение: + + + + Force checking all #ifdef configurations + Check all #ifdef configurations + Проверять все варианты #ifdef конфигураций + + + + Show full path of files + Показывать полные пути к файлам + + + + Show "No errors found" message when no errors found + Показывать сообщение, если ошибок не найдено + + + + Display error Id in column "Id" + Отображать номер ошибки в колонке "id" + + + + Enable inline suppressions + Включить inline-подавление ошибок + + + + Check for inconclusive errors also + Показывать также спорные ошибки + + + + Show statistics on check completion + Показывать статистику после завершения проверки + + + + Check for updates + + + + + Show internal warnings in log + Показывать внутренние предупреждения в логе + + + + Addons + Дополнения + + + + Python binary (leave this empty to use python in the PATH) + Python (оставьте пустым для использования python из PATH) + + + + + + ... + ... + + + + MISRA addon + Дополнение MISRA + + + + MISRA rule texts file + Файл с текстами правил MISRA: + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + <html><head/><body><p>Скопируйте текст из Appendix A &quot;Summary of guidelines&quot; из фала правил MISRA C 2012 pdf в текстовый файл.</p></body></html> + + + + Clang + Clang + + + + Clang path (leave empty to use system PATH) + Clang (оставьте пустым для использования clang из PATH) + + + + Visual Studio headers + Заголовочные файлы Visual Studio + + + + <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> + <html><head/><body><p>Путь до заголовочных файлов Visual Studio headers, разделенных символом ';'.</p><p>Вы можете открыть командную строку Visual Studio, ввести &quot;SET INCLUDE&quot; и скопировать пути.</p></body></html> + + + + Code Editor + Редактор + + + + Code Editor Style + Оформление + + + + System Style + + + + + Default Light Style + + + + + Default Dark Style + + + + + Custom + + + + + Remove + Удалить + + + + Applications + Приложения + + + + + Edit... + Изменить... + + + + Set as default + Установить по умолчанию + + + + Reports + Отчёты + + + + Save all errors when creating report + Сохранять все ошибки при создании отчёта + + + + Save full path to files in reports + Сохранять полные пути к файлам в отчётах + + + + Language + Язык + + + + SettingsDialog + + + N/A + Нет данных + + + + The executable file "%1" is not available + + + + + Add a new application + Добавить новое приложение + + + + Modify an application + Изменить приложение + + + + [Default] + [По умолчанию] + + + + [Default] + [По умолчанию] + + + + Select python binary + Выберите исполняемый файл python + + + + Select MISRA File + Выберите файл текстов правил MISRA + + + + Select clang path + Выберите исполняемый файл clang + + + + StatsDialog + + + + + + Statistics + Статистика + + + + + Project + Проект + + + + Project: + Проект: + + + + Paths: + Пути: + + + + Include paths: + Включенные пути: + + + + Defines: + Объявленные макроопределения: + + + + Undefines: + Удаленные макроопределения: + + + + + Previous Scan + Последнее сканирование + + + + Path Selected: + Выбранный путь: + + + + Number of Files Scanned: + Количество просканированных файлов: + + + + Scan Duration: + Продолжительность сканирования: + + + + Errors: + Ошибки: + + + + Warnings: + Предупреждения: + + + + Stylistic warnings: + Стилистические предупреждения: + + + + Portability warnings: + Предупреждения переносимости: + + + + Performance issues: + Проблемы с производительностью: + + + + Information messages: + Информационные сообщения: + + + + Active checkers: + + + + + Checkers + + + + + History + История + + + + File: + Файл: + + + + Copy to Clipboard + Скопировать в буфер обмена + + + + Pdf Export + Экспорт PDF + + + + 1 day + 1 день + + + + %1 days + %1 дней + + + + 1 hour + 1 час + + + + %1 hours + %1 часов + + + + 1 minute + 1 минута + + + + %1 minutes + %1 минут + + + + 1 second + 1 секунда + + + + %1 seconds + %1 секунд + + + + 0.%1 seconds + 0.1%1 секунд + + + + and + и + + + + Export PDF + Экспорт PDF + + + + Project Settings + Настройки проекта + + + + Paths + Пути + + + + Include paths + Включенные пути + + + + Defines + Объявленные макроопределения: + + + + Undefines + Удаленные макроопределения: + + + + Path selected + Выбранные пути + + + + Number of files scanned + Количество просканированных файлов + + + + Scan duration + Продолжительность сканирования + + + + + Errors + Ошибки + + + + File: + Файл: + + + + No cppcheck build dir + Не задана директория сборки + + + + + Warnings + Предупреждения + + + + + Style warnings + Стилистические предупреждения + + + + + Portability warnings + Предупреждения переносимости + + + + + Performance warnings + Предупреждения производительности + + + + + Information messages + Информационные сообщения + + + + ThreadResult + + + %1 of %2 files checked + %1 из %2 файлов проверены + + + + TranslationHandler + + + Failed to change the user interface language: + +%1 + +The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. + Не удалось изменить язык пользовательского интерфейса: + +%1 + +Язык пользовательского интерфейса был сброшен на английский. Откройте Настройки-диалог для выбора любого из доступных языков. + + + + Cppcheck + Cppcheck + + + + TxtReport + + + inconclusive + незначительная + + + + toFilterString + + + All supported files (%1) + Все поддерживаемые файлы (%1) + + + + All files (%1) + Все файлы (%1) + + + diff --git a/cppcheck-2.14.0/gui/cppcheck_sr.ts b/cppcheck-2.14.0/gui/cppcheck_sr.ts new file mode 100644 index 00000000..19cc510d --- /dev/null +++ b/cppcheck-2.14.0/gui/cppcheck_sr.ts @@ -0,0 +1,2991 @@ + + + + + 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-%1 Cppcheck team. + Copyright © 2007-2021 Cppcheck team. + + + + + This program is licensed under the terms +of the GNU General Public License version 3 + This program is licensed under the terms +of the GNU General Public License version 3 + + + + Visit Cppcheck homepage at %1 + Visit Cppcheck homepage at %1 + + + + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PCRE</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PicoJSON</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">TinyXML2</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Boost</li></ul></body></html> + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">pcre</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">picojson</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">tinyxml2</li></ul></body></html> + + + + + ApplicationDialog + + + Add an application + Add a new application + + + + Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. + +The following texts in parameters are replaced with appropriate values when application is executed: +(file) - Filename containing the error +(line) - Line number containing the error +(message) - Error message +(severity) - Error severity + +Example opening a file with Kate and make Kate scroll to the correct line: +Executable: kate +Parameters: -l(line) (file) + + + + + &Name: + + + + + &Executable: + + + + + &Parameters: + + + + + Browse + Browse + + + + Executable files (*.exe);;All files(*.*) + Executable files (*.exe);;All files(*.*) + + + + Select viewer application + Select viewer application + + + + Cppcheck + Cppcheck + + + + You must specify a name, a path and optionally parameters for the application! + + + + + ComplianceReportDialog + + + Compliance Report + + + + + Project name + + + + + Project version + + + + + Coding Standard + + + + + Misra C + + + + + Cert C + + + + + Cert C++ + + + + + List of files with md5 checksums + + + + + Compliance report + + + + + HTML files (*.html) + + + + + + Save compliance report + + + + + Failed to import '%1' (%2), can not show files in compliance report + + + + + 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 + + + + HelpDialog + + + Cppcheck GUI help + + + + + Contents + + + + + Index + + + + + Helpfile '%1' was not found + + + + + Cppcheck + Cppcheck + + + + LibraryAddFunctionDialog + + + Add function + + + + + Function name(s) + + + + + Number of arguments + + + + + LibraryDialog + + + Library Editor + + + + + Open + + + + + Save + + + + + Save as + + + + + Functions + + + + + Sort + + + + + Add + + + + + Filter: + + + + + Comments + + + + + noreturn + + + + + False + + + + + True + + + + + Unknown + + + + + return value must be used + + + + + ignore function in leaks checking + + + + + Arguments + + + + + Edit + + + + + + Library files (*.cfg) + + + + + Open library file + + + + + + + Cppcheck + Cppcheck + + + + Cannot open file %1. + Can not open file %1. + + + + + Failed to load %1. %2. + + + + + Cannot save file %1. + Can not save file %1. + + + + + Save the library as + + + + + LibraryEditArgDialog + + + Edit argument + + + + + <html><head/><body> +<p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> +<p>Typically, set this if the argument is a pointer, size, etc.</p> +<p>Example:</p> +<pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> +</body></html> + + + + + Not bool + + + + + <html><head/><body> +<p>Is a null parameter value allowed?</p> +<p>Typically this should be used on any pointer parameter that does not allow null.</p> +<p>Example:</p> +<pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> +</body></html> + + + + + Not null + + + + + Not uninit + + + + + String + + + + + Format string + + + + + Min size of buffer + + + + + + Type + + + + + + None + + + + + + argvalue + + + + + + mul + + + + + + strlen + + + + + + Arg + + + + + + Arg2 + + + + + and + + + + + Valid values + + + + + MainWindow + + + + + + + + + + + + + + + + + Cppcheck + Cppcheck + + + + A&nalyze + + + + + Standard + Standard + + + + &File + &File + + + + &View + &View + + + + &Toolbars + + + + + C++ standard + + + + + &C standard + C standard + + + + + &Edit + &Edit + + + + &License... + &License... + + + + A&uthors... + A&uthors... + + + + &About... + &About... + + + + &Files... + &Files... + + + + + Analyze files + Check files + + + + + Ctrl+F + Ctrl+F + + + + &Directory... + &Directory... + + + + + Analyze directory + Check directory + + + + + Ctrl+D + Ctrl+D + + + + Ctrl+R + Ctrl+R + + + + &Stop + &Stop + + + + + Stop analysis + Stop checking + + + + + Esc + Esc + + + + &Save results to file... + &Save results to file... + + + + Ctrl+S + Ctrl+S + + + + &Quit + &Quit + + + + &Clear results + &Clear results + + + + &Preferences + &Preferences + + + + + Show errors + + + + + + Show warnings + + + + + + Show performance warnings + + + + + Show &hidden + + + + + + Information + + + + + Show information messages + + + + + Show portability warnings + + + + + Show Cppcheck results + + + + + Clang + + + + + Show Clang results + + + + + &Filter + + + + + Filter results + + + + + Windows 32-bit ANSI + Windows 32-bit ANSI + + + + Windows 32-bit Unicode + Windows 32-bit Unicode + + + + Unix 32-bit + Unix 32-bit + + + + Unix 64-bit + Unix 64-bit + + + + Windows 64-bit + Windows 64-bit + + + + &Print... + + + + + Print the Current Report + + + + + Print Pre&view... + + + + + Open a Print Preview Dialog for the Current Results + + + + + Open library editor + + + + + &Check all + &Check all + + + + Checking for updates + + + + + Hide + + + + + Filter + + + + + &Reanalyze modified files + &Recheck modified files + + + + + Reanal&yze all files + + + + + Ctrl+Q + + + + + Style war&nings + + + + + E&rrors + + + + + &Uncheck all + &Uncheck all + + + + Collapse &all + Collapse &all + + + + &Expand all + &Expand all + + + + &Standard + + + + + Standard items + + + + + Toolbar + + + + + &Categories + + + + + Error categories + + + + + &Open XML... + + + + + Open P&roject File... + + + + + Ctrl+Shift+O + + + + + Sh&ow Scratchpad... + + + + + &New Project File... + + + + + Ctrl+Shift+N + + + + + &Log View + + + + + Log View + + + + + C&lose Project File + + + + + &Edit Project File... + + + + + &Statistics + + + + + &Warnings + + + + + Per&formance warnings + + + + + &Information + + + + + &Portability + + + + + P&latforms + + + + + C++&11 + + + + + C&99 + + + + + &Posix + + + + + C&11 + + + + + &C89 + + + + + &C++03 + + + + + &Library Editor... + + + + + &Auto-detect language + + + + + &Enforce C++ + + + + + E&nforce C + + + + + C++14 + C++14 + + + + Reanalyze and check library + + + + + Check configuration (defines, includes) + + + + + C++17 + C++17 + + + + C++20 + C++20 + + + + Compliance report... + + + + + &Contents + + + + + Categories + + + + + + Show style warnings + + + + + Open the help contents + + + + + F1 + F1 + + + + &Help + &Help + + + + + Quick Filter: + + + + + Select configuration + + + + + Found project file: %1 + +Do you want to load this project file instead? + + + + + File not found + + + + + Bad XML + + + + + Missing attribute + + + + + Bad attribute value + + + + + Duplicate define + + + + + Failed to load the selected library '%1'. +%2 + + + + + File not found: '%1' + + + + + Failed to load/setup addon %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. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + +Analysis is aborted. + + + + + Failed to load %1 - %2 + +Analysis is aborted. + + + + + + %1 + +Analysis is aborted. + + + + + License + License + + + + Authors + Authors + + + + 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! + + + + + The library '%1' contains unknown elements: +%2 + + + + + Unsupported format + + + + + Duplicate platform type + + + + + Platform type redefined + + + + + Unknown element + + + + + Unknown issue + + + + + + + + Error + + + + + Open the report file + + + + + Text files (*.txt) + Text files (*.txt) + + + + CSV files (*.csv) + + + + + Project files (*.cppcheck);;All files(*.*) + + + + + Select Project File + + + + + + + + Project: + + + + + No suitable files found to analyze! + + + + + C/C++ Source + + + + + Compile database + + + + + Visual Studio + + + + + Borland C++ Builder 6 + + + + + Select files to analyze + + + + + Select directory to analyze + + + + + Select the configuration that will be analyzed + + + + + Found project files from the directory. + +Do you want to proceed analysis without using any of these project files? + + + + + Current results will be cleared. + +Opening a new XML file will clear current results. +Do you want to proceed? + + + + + Analyzer is running. + +Do you want to stop the analysis and exit Cppcheck? + + + + + About + + + + + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) + + + + + Cannot generate a compliance report right now, an analysis must finish successfully. Try to reanalyze the code and ensure there are no critical errors. + + + + + Build dir '%1' does not exist, create it? + + + + + To check the project using addons, you need a build directory. + + + + + Failed to open file + + + + + Unknown project file format + + + + + Failed to import project file + + + + + Failed to import '%1': %2 + +Analysis is stopped. + + + + + Failed to import '%1' (%2), analysis is stopped + + + + + Project files (*.cppcheck) + + + + + Select Project Filename + + + + + No project file loaded + + + + + The project file + +%1 + + could not be found! + +Do you want to remove the file from the recently used projects -list? + + + + + Install + + + + + New version available: %1. %2 + + + + + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> This option is for installation scripts so they can configure the directory where + datafiles are located (translations, cfg). The GUI is not started when this option + is used. + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) + + + + + Cppcheck GUI - Command line parameters + + + + + NewSuppressionDialog + + + New suppression + + + + + Error ID + + + + + File name + + + + + Line number + + + + + Symbol name + + + + + Edit suppression + + + + + Platforms + + + Native + + + + + Unix 32-bit + Unix 32-bit + + + + Unix 64-bit + Unix 64-bit + + + + Windows 32-bit ANSI + Windows 32-bit ANSI + + + + Windows 32-bit Unicode + Windows 32-bit Unicode + + + + Windows 64-bit + Windows 64-bit + + + + ProjectFile + + + Project File + + + + + Paths and Defines + + + + + Import Project (Visual studio / compile database/ Borland C++ Builder 6) + Import Project (Visual studio / compile database) + + + + + Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int + Defines must be separated by a semicolon ';' + + + + + Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. + + + + + MISRA rule texts + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + ... + + + + + <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> + + + + + + Browse... + + + + + Analyze all Visual Studio configurations + + + + + Selected VS Configurations + + + + + Paths: + + + + + + Add... + + + + + + + Edit + + + + + + + + Remove + + + + + Undefines: + + + + + Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 + + + + + Include Paths: + + + + + Up + + + + + Down + + + + + Platform + + + + + + Analysis + + + + + This is a workfolder that Cppcheck will use for various purposes. + + + + + Clang (experimental) + + + + + Check level + + + + + Normal -- meant for normal analysis in CI. Analysis should finish in reasonable time. + + + + + Exhaustive -- meant for nightly builds etc. Analysis time can be longer (10x slower than compilation is OK). + + + + + If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. + + + + + Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) + Check code in unused templates (slower and less accurate analysis) + + + + + Max CTU depth + + + + + Max recursion in template instantiation + + + + + Warning options + + + + + Root path: + + + + + Filepaths in warnings will be relative to this path + + + + + Warning tags (separated by semicolon) + + + + + Cert C + + + + + CERT-INT35-C: int precision (if size equals precision, you can leave empty) + + + + + Misra C++ 2008 + + + + + Autosar + + + + + Bug hunting + + + + + External tools + + + + + Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) + + + + + Types and Functions + + + + + Libraries + + + + + Parser + + + + + Cppcheck (built in) + + + + + Check that each class has a safe public interface + + + + + Limit analysis + + + + + Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) + + + + + If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. + + + + + Exclude source files + + + + + Exclude folder... + + + + + Exclude file... + + + + + Suppressions + + + + + Add + + + + + + Addons + + + + + Note: Addons require <a href="https://www.python.org/">Python</a> being installed. + + + + + Y2038 + + + + + Thread safety + + + + + Coding standards + + + + + Misra C + + + + + 2012 + + + + + 2023 + + + + + Cert C++ + + + + + Bug hunting (Premium) + + + + + Clang analyzer + + + + + Clang-tidy + + + + + Defines: + + + + + ProjectFileDialog + + + Project file: %1 + + + + + Select Cppcheck build dir + + + + + Select include directory + + + + + Select a directory to check + + + + + Clang-tidy (not found) + + + + + Visual Studio + + + + + Compile database + + + + + Borland C++ Builder 6 + + + + + Import Project + + + + + Select directory to ignore + + + + + Source files + + + + + All files + + + + + Exclude file + + + + + Select MISRA rule texts file + + + + + MISRA rule texts file (%1) + + + + + QObject + + + Unknown language specified! + + + + + Language file %1 not found! + Could not find the file: %1! + + + + Failed to load translation for language %1 from file %2 + Failed to load translation for language %1 from file %2 + + + + line %1: Unhandled element %2 + + + + + line %1: Mandatory attribute '%2' missing in '%3' + + + + + (Not found) + + + + + Thin + + + + + ExtraLight + + + + + Light + + + + + Normal + + + + + Medium + + + + + DemiBold + + + + + Bold + + + + + ExtraBold + + + + + Black + + + + + Editor Foreground Color + + + + + Editor Background Color + + + + + Highlight Background Color + + + + + Line Number Foreground Color + + + + + Line Number Background Color + + + + + Keyword Foreground Color + + + + + Keyword Font Weight + + + + + Class Foreground Color + Class ForegroundColor + + + + + Class Font Weight + + + + + Quote Foreground Color + + + + + Quote Font Weight + + + + + Comment Foreground Color + + + + + Comment Font Weight + + + + + Symbol Foreground Color + + + + + Symbol Background Color + + + + + Symbol Font Weight + + + + + Set to Default Light + + + + + Set to Default Dark + + + + + QPlatformTheme + + + OK + + + + + Cancel + + + + + Close + + + + + Save + + + + + ResultsTree + + + File + File + + + + Severity + Severity + + + + Line + Line + + + + Summary + + + + + Undefined file + Undefined file + + + + Copy + + + + + Could not find file: + + + + + Please select the folder '%1' + + + + + Select Directory '%1' + + + + + Please select the directory where file is located. + + + + + debug + + + + + note + + + + + Recheck + + + + + Hide + + + + + Hide all with id + + + + + Suppress selected id(s) + + + + + Open containing folder + + + + + internal + + + + + + Tag + + + + + No tag + + + + + + Cppcheck + Cppcheck + + + + No editor application configured. + +Configure the editor application for Cppcheck in preferences/Applications. + You can open this error by specifying applications in program's settings. + + + + No default editor application selected. + +Please select the default editor application in preferences/Applications. + + + + + Could not find the file! + + + + + Could not start %1 + +Please check the application path and parameters are correct. + Could not start %1 + +Please check the application path and parameters are correct. + + + + Select Directory + + + + + Id + + + + + Inconclusive + + + + + Since date + + + + + style + Style + + + + error + Error + + + + warning + + + + + performance + + + + + portability + + + + + information + + + + + ResultsView + + + Print Report + + + + + No errors found, nothing to print. + + + + + %p% (%1 of %2 files checked) + + + + + + Cppcheck + Cppcheck + + + + No errors found. + No errors found. + + + + Errors were found, but they are configured to be hidden. +To toggle what kind of errors are shown, open view menu. + Errors were found, but they are configured to be hidden. +To toggle what kind of errors are shown, open view menu. + + + + + Failed to read the report. + + + + + XML format version 1 is no longer supported. + + + + + First included by + + + + + Id + + + + + Bug hunting analysis is incomplete + + + + + Clear Log + + + + + Copy this Log entry + + + + + Copy complete Log + + + + + Analysis was stopped + + + + + There was a critical error with id '%1' + + + + + when checking %1 + + + + + when checking a file + + + + + Analysis was aborted. + + + + + + Failed to save the report. + Failed to save the report. + + + + Results + Results + + + + Critical errors + + + + + Analysis Log + + + + + Warning Details + + + + + ScratchPad + + + Scratchpad + + + + + Copy or write some C/C++ code here: + + + + + Optionally enter a filename (mainly for automatic language detection) and click on "Check": + + + + + filename + + + + + Check + + + + + Settings + + + Preferences + Preferences + + + + General + General + + + + Add... + + + + + Number of threads: + Number of threads: + + + + Ideal count: + + + + + Force checking all #ifdef configurations + Check all #ifdef configurations + + + + Show full path of files + Show full path of files + + + + Show "No errors found" message when no errors found + Show "No errors found" message when no errors found + + + + Display error Id in column "Id" + + + + + Enable inline suppressions + + + + + Check for inconclusive errors also + + + + + Show statistics on check completion + + + + + Check for updates + + + + + Show internal warnings in log + + + + + Addons + + + + + Python binary (leave this empty to use python in the PATH) + + + + + + + ... + + + + + MISRA addon + + + + + MISRA rule texts file + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + Clang + + + + + Clang path (leave empty to use system PATH) + + + + + Visual Studio headers + + + + + <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> + + + + + Code Editor + + + + + Code Editor Style + + + + + System Style + + + + + Default Light Style + + + + + Default Dark Style + + + + + Custom + + + + + Remove + + + + + Applications + Applications + + + + + Edit... + + + + + Set as default + + + + + Reports + Reports + + + + Save all errors when creating report + Save all errors when creating report + + + + Save full path to files in reports + Save full path to files in reports + + + + Language + + + + + SettingsDialog + + + N/A + + + + + The executable file "%1" is not available + + + + + Add a new application + Add a new application + + + + Modify an application + Modify an application + + + + [Default] + + + + + [Default] + + + + + Select python binary + + + + + Select MISRA File + + + + + Select clang path + + + + + StatsDialog + + + + + + Statistics + + + + + + Project + + + + + Project: + + + + + Paths: + + + + + Include paths: + + + + + Defines: + + + + + Undefines: + + + + + + Previous Scan + + + + + Path Selected: + + + + + Number of Files Scanned: + + + + + Scan Duration: + + + + + Errors: + + + + + Warnings: + + + + + Stylistic warnings: + + + + + Portability warnings: + + + + + Performance issues: + + + + + Information messages: + + + + + Active checkers: + + + + + Checkers + + + + + History + + + + + File: + + + + + Copy to Clipboard + + + + + Pdf Export + + + + + 1 day + + + + + %1 days + + + + + 1 hour + + + + + %1 hours + + + + + 1 minute + + + + + %1 minutes + + + + + 1 second + + + + + %1 seconds + + + + + 0.%1 seconds + + + + + and + + + + + Export PDF + + + + + Project Settings + + + + + Paths + + + + + Include paths + + + + + Defines + + + + + Undefines + + + + + Path selected + + + + + Number of files scanned + + + + + Scan duration + + + + + + Errors + + + + + File: + + + + + No cppcheck build dir + + + + + + Warnings + + + + + + Style warnings + + + + + + Portability warnings + + + + + + Performance warnings + + + + + + Information messages + + + + + ThreadResult + + + %1 of %2 files checked + + + + + TranslationHandler + + + Failed to change the user interface language: + +%1 + +The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. + + + + + Cppcheck + Cppcheck + + + + TxtReport + + + inconclusive + + + + + toFilterString + + + All supported files (%1) + + + + + All files (%1) + + + + diff --git a/cppcheck-2.14.0/gui/cppcheck_sv.ts b/cppcheck-2.14.0/gui/cppcheck_sv.ts new file mode 100644 index 00000000..4edb60d9 --- /dev/null +++ b/cppcheck-2.14.0/gui/cppcheck_sv.ts @@ -0,0 +1,3059 @@ + + + + + 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-%1 Cppcheck team. + Copyright © 2007-2021 Cppcheck team. + Copyright © 2007-2021 Cppcheck team. + + + + This program is licensed under the terms +of the GNU General Public License version 3 + This program is licensed under the terms +of the GNU General Public License version 3 + + + + Visit Cppcheck homepage at %1 + Hemsida: %1 + + + + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PCRE</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PicoJSON</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">TinyXML2</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Boost</li></ul></body></html> + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">pcre</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">picojson</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">tinyxml2</li></ul></body></html> + + + + + ApplicationDialog + + + Add an application + Lägg till program + + + + Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. + +The following texts in parameters are replaced with appropriate values when application is executed: +(file) - Filename containing the error +(line) - Line number containing the error +(message) - Error message +(severity) - Error severity + +Example opening a file with Kate and make Kate scroll to the correct line: +Executable: kate +Parameters: -l(line) (file) + Här kan du ange en applikation som kan användas för att visa fel. Ange applikationens namn, körbara fil samt kommandorads parametrar. + +Följande texter i parametrarna ersätts med motsvarande värden när applikationen körs: +(file) - filnamn för källkodsfil +(line) - radnummer +(message) - felmeddelande +(severity) - typ / svårighetsgrad + +Exempel för att öppna en fil med Kate och ange att Kate skall skrolla till rätt rad: +Körbar fil: kate +Parametrar: -l(line) (file) + + + + &Name: + Namn: + + + + &Executable: + Körbar fil: + + + + &Parameters: + Parametrar: + + + + Browse + Bläddra + + + + Executable files (*.exe);;All files(*.*) + Exekverbara filer (*.exe);;Alla filer(*.*) + + + + Select viewer application + Välj program + + + + Cppcheck + Cppcheck + + + + You must specify a name, a path and optionally parameters for the application! + Du måste ange namn, sökväg samt eventuellt parametrar för applikationen! + + + + ComplianceReportDialog + + + Compliance Report + + + + + Project name + + + + + Project version + + + + + Coding Standard + + + + + Misra C + + + + + Cert C + + + + + Cert C++ + + + + + List of files with md5 checksums + + + + + Compliance report + + + + + HTML files (*.html) + + + + + + Save compliance report + + + + + Failed to import '%1' (%2), can not show files in compliance report + + + + + 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 + + + + HelpDialog + + + Cppcheck GUI help + + + + + Contents + + + + + Index + + + + + Helpfile '%1' was not found + + + + + Cppcheck + Cppcheck + + + + LibraryAddFunctionDialog + + + Add function + Lägg till funktion + + + + Function name(s) + Funktion namn + + + + Number of arguments + Antal argument + + + + LibraryDialog + + + Library Editor + Library Editor + + + + Open + Öppna + + + + Save + Spara + + + + Save as + Spara som + + + + Functions + Funktioner + + + + Sort + Sortera + + + + Add + Lägg till + + + + Filter: + Filter: + + + + Comments + Kommentar + + + + noreturn + noreturn + + + + False + False + + + + True + True + + + + Unknown + Vet ej + + + + return value must be used + retur värde måste användas + + + + ignore function in leaks checking + Ignorera funktionen när cppcheck letar efter läckor + + + + Arguments + Argument + + + + Edit + Redigera + + + + + Library files (*.cfg) + Library fil (*.cfg) + + + + Open library file + Öppna Library fil + + + + + + Cppcheck + Cppcheck + + + + Cannot open file %1. + Can not open file %1. + Kunde ej öppna filen %1. + + + + Failed to load %1. %2. + + + + + Cannot save file %1. + Can not save file %1. + Kunde ej spara filen %1. + + + + Save the library as + Spara library som + + + + LibraryEditArgDialog + + + Edit argument + Konfigurera argument + + + + <html><head/><body> +<p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> +<p>Typically, set this if the argument is a pointer, size, etc.</p> +<p>Example:</p> +<pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> +</body></html> + Är bool värde tillåtet? Exempelvis resultatet från jämförelse eller från ! operatorn. +Normalt bör inte bool värde användas om argumentet är en pekare eller en storlek etc. +Exempel: + memcmp(x, y, i == 123); // sista argumentet bör inte vara ett bool värde + + + + Not bool + Ej bool + + + + <html><head/><body> +<p>Is a null parameter value allowed?</p> +<p>Typically this should be used on any pointer parameter that does not allow null.</p> +<p>Example:</p> +<pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> +</body></html> + Är null värde tillåtet? +Klicka i denna om argumentet är en pointer parameter som ej tillåter null. +Exempel: + strcpy(x,y); // varken x eller y får vara null. + + + + Not null + Ej null + + + + Not uninit + Ej uninit + + + + String + Sträng + + + + Format string + Format sträng + + + + Min size of buffer + Minsta storlek för buffer + + + + + Type + Typ + + + + + None + Ingen + + + + + argvalue + argvalue + + + + + mul + mul + + + + + strlen + strlen + + + + + Arg + Arg + + + + + Arg2 + Arg2 + + + + and + och + + + + Valid values + Tillåtna värden + + + + MainWindow + + + + + + + + + + + + + + + + + Cppcheck + Cppcheck + + + + A&nalyze + Analysera + + + + Standard + Standard + + + + &File + &Arkiv + + + + &View + &Visa + + + + &Toolbars + Verktygsfält + + + + C++ standard + C++ standard + + + + &C standard + C standard + C standard + + + + &Edit + &Redigera + + + + &License... + &Licens... + + + + A&uthors... + &Utvecklat av... + + + + &About... + &Om... + + + + &Files... + &Filer... + + + + + Analyze files + Check files + Analysera filer + + + + Ctrl+F + Ctrl+F + + + + &Directory... + &Katalog... + + + + + Analyze directory + Check directory + Analysera mapp + + + + Ctrl+D + Ctrl+D + + + + Ctrl+R + Ctrl+R + + + + &Stop + &Stoppa + + + + + Stop analysis + Stop checking + Stoppa analys + + + + Esc + Esc + + + + &Save results to file... + &Spara resultat till fil... + + + + Ctrl+S + Ctrl+S + + + + &Quit + &Avsluta + + + + &Clear results + &Töm resultat + + + + &Preferences + &Inställningar + + + + + Show errors + Visa fel + + + + + Show warnings + Visa varningar + + + + + Show performance warnings + Visa prestanda varningar + + + + Show &hidden + Visa dolda + + + + + Information + Information + + + + Show information messages + Visa informations meddelanden + + + + Show portability warnings + Visa portabilitets varningar + + + + Show Cppcheck results + Visa Cppcheck resultat + + + + Clang + Clang + + + + Show Clang results + Visa Clang resultat + + + + &Filter + &Filter + + + + Filter results + Filtrera resultat + + + + Windows 32-bit ANSI + Windows 32-bit ANSI + + + + Windows 32-bit Unicode + Windows 32-bit Unicode + + + + Unix 32-bit + Unix 32-bit + + + + Unix 64-bit + Unix 64-bit + + + + Windows 64-bit + Windows 64-bit + + + + &Print... + Skriv ut... + + + + Print the Current Report + Skriv ut aktuell rapport + + + + Print Pre&view... + Förhandsgranska utskrift... + + + + Open a Print Preview Dialog for the Current Results + Öppnar förhandsgranskning för nuvarande resultat + + + + Open library editor + Öppna library editor + + + + &Check all + &Kryssa alla + + + + Checking for updates + + + + + Hide + Dölj + + + + Filter + Filter + + + + &Reanalyze modified files + &Recheck modified files + Analysera om ändrade filer + + + + Reanal&yze all files + Analysera om alla filer + + + + Ctrl+Q + + + + + Style war&nings + Style varningar + + + + E&rrors + Fel + + + + &Uncheck all + Kryssa &ur alla + + + + Collapse &all + Ingen bra översättning! + &Fäll ihop alla + + + + &Expand all + &Expandera alla + + + + &Standard + &Standard + + + + Standard items + Standard poster + + + + Toolbar + Verktygsfält + + + + &Categories + &Kategorier + + + + Error categories + Fel kategorier + + + + &Open XML... + &Öppna XML... + + + + Open P&roject File... + Öppna Projektfil... + + + + Ctrl+Shift+O + + + + + Sh&ow Scratchpad... + Visa Scratchpad... + + + + &New Project File... + Ny projektfil... + + + + Ctrl+Shift+N + + + + + &Log View + + + + + Log View + Logg vy + + + + C&lose Project File + Stäng projektfil + + + + &Edit Project File... + Redigera projektfil... + + + + &Statistics + Statistik + + + + &Warnings + Varningar + + + + Per&formance warnings + Optimerings varningar + + + + &Information + Information + + + + &Portability + Portabilitet + + + + P&latforms + Plattformar + + + + C++&11 + C++11 + + + + C&99 + C99 + + + + &Posix + Posix + + + + C&11 + C11 + + + + &C89 + C89 + + + + &C++03 + C++03 + + + + &Library Editor... + Library Editor... + + + + &Auto-detect language + Detektera språk automatiskt + + + + &Enforce C++ + Tvinga C++ + + + + E&nforce C + Tvinga C + + + + C++14 + C++14 + + + + Reanalyze and check library + + + + + Check configuration (defines, includes) + + + + + C++17 + C++17 + + + + C++20 + C++20 + + + + Compliance report... + + + + + &Contents + &Innehåll + + + + Categories + Kategorier + + + + + Show style warnings + Visa stil varningar + + + + Open the help contents + Öppna hjälp + + + + F1 + F1 + + + + &Help + &Hjälp + + + + + Quick Filter: + Snabbfilter: + + + + Select configuration + Välj konfiguration + + + + Found project file: %1 + +Do you want to load this project file instead? + Hittade projektfil: %1 + +Vill du ladda denna projektfil istället? + + + + File not found + Filen hittades ej + + + + Bad XML + Ogiltig XML + + + + Missing attribute + Attribut finns ej + + + + Bad attribute value + Ogiltigt attribut värde + + + + Unsupported format + Format stöds ej + + + + Duplicate define + + + + + Failed to load the selected library '%1'. +%2 + Misslyckades att ladda valda library '%1'. +%2 + + + + File not found: '%1' + + + + + Failed to load/setup addon %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. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + +Analysis is aborted. + + + + + Failed to load %1 - %2 + +Analysis is aborted. + + + + + + %1 + +Analysis is aborted. + + + + + License + Licens + + + + Authors + Utvecklare + + + + 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! + + + + The library '%1' contains unknown elements: +%2 + Library filen '%1' har element som ej hanteras: +%2 + + + + Duplicate platform type + Dubbel plattformstyp + + + + Platform type redefined + Plattformstyp definieras igen + + + + Unknown element + Element hanteras ej + + + + Unknown issue + Något problem + + + + + + + Error + Fel + + + Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + Misslyckades att ladda %1. Din Cppcheck installation är ej komplett. Du kan använda --data-dir<directory> på kommandoraden för att specificera var denna fil finns. Det är meningen att --data-dir kommandot skall köras under installationen,så GUIt kommer ej visas när --data-dir används allt som händer är att en inställning görs. + + + + Open the report file + Öppna rapportfilen + + + + Text files (*.txt) + Text filer (*.txt) + + + + CSV files (*.csv) + CSV filer (*.csv) + + + + Project files (*.cppcheck);;All files(*.*) + Projektfiler (*.cppcheck);;Alla filer(*.*) + + + + Select Project File + Välj projektfil + + + + + + + Project: + Projekt: + + + + No suitable files found to analyze! + Inga filer hittades att analysera! + + + + C/C++ Source + + + + + Compile database + + + + + Visual Studio + Visual Studio + + + + Borland C++ Builder 6 + + + + + Select files to analyze + Välj filer att analysera + + + + Select directory to analyze + Välj mapp att analysera + + + + Select the configuration that will be analyzed + Välj konfiguration som kommer analyseras + + + + Found project files from the directory. + +Do you want to proceed analysis without using any of these project files? + Hittade projekt filer i mappen. + +Vill du fortsätta analysen utan att använda någon av dessa projekt filer? + + + + Current results will be cleared. + +Opening a new XML file will clear current results. +Do you want to proceed? + + + + + Analyzer is running. + +Do you want to stop the analysis and exit Cppcheck? + Analys körs. + +Vill du stoppa analysen och avsluta Cppcheck? + + + + About + + + + + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) + XML filer (*.xml);;Text filer (*.txt);;CSV filer (*.csv) + + + + Cannot generate a compliance report right now, an analysis must finish successfully. Try to reanalyze the code and ensure there are no critical errors. + + + + + Build dir '%1' does not exist, create it? + Build dir '%1' existerar ej, skapa den? + + + + To check the project using addons, you need a build directory. + + + + + Failed to open file + + + + + Unknown project file format + + + + + Failed to import project file + + + + + Failed to import '%1': %2 + +Analysis is stopped. + + + + + Failed to import '%1' (%2), analysis is stopped + + + + Failed to import '%1', analysis is stopped + Misslyckades att importera '%1', analysen stoppas + + + + Project files (*.cppcheck) + Projekt filer (*.cppcheck) + + + + Select Project Filename + Välj Projektfil + + + + No project file loaded + Inget projekt laddat + + + + The project file + +%1 + + could not be found! + +Do you want to remove the file from the recently used projects -list? + Projektfilen + +%1 + + kunde inte hittas! + +Vill du ta bort filen från 'senast använda projekt'-listan? + + + + Install + + + + + New version available: %1. %2 + + + + + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> This option is for installation scripts so they can configure the directory where + datafiles are located (translations, cfg). The GUI is not started when this option + is used. + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> This option is for installation scripts so they can configure the directory where + datafiles are located (translations, cfg). The GUI is not started when this option + is used. + + + + Cppcheck GUI - Command line parameters + Cppcheck GUI - Command line parameters + + + + NewSuppressionDialog + + + New suppression + + + + + Error ID + + + + + File name + + + + + Line number + + + + + Symbol name + + + + + Edit suppression + + + + + Platforms + + + Native + Native + + + + Unix 32-bit + Unix 32-bit + + + + Unix 64-bit + Unix 64-bit + + + + Windows 32-bit ANSI + Windows 32-bit ANSI + + + + Windows 32-bit Unicode + Windows 32-bit Unicode + + + + Windows 64-bit + Windows 64-bit + + + + ProjectFile + + + Project File + Projektfil + + + + Paths and Defines + Sökvägar och defines + + + + Import Project (Visual studio / compile database/ Borland C++ Builder 6) + Import Project (Visual studio / compile database) + Importera Projekt (Visual Studio / compile database) + + + + Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int + Defines must be separated by a semicolon ';' + Defines separeras med semicolon ';' + + + + Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. + Obs: Lägg dina egna .cfg filer i samma folder som projekt filen. De skall isåfall visas ovan. + + + + ... + ... + + + + <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> + <html><head/><body><p>Du har ett val:</p><p> * Analysera alla Debug och Release konfigurationer</p><p> * Analysera bara den första matchande Debug konfigurationen</p><p><br/></p></body></html> + + + + + Browse... + + + + + Analyze all Visual Studio configurations + Analysera alla Visual Studio konfigurationer + + + + Selected VS Configurations + + + + + Paths: + Sökvägar: + + + + + Add... + Lägg till... + + + + + + Edit + Redigera + + + + + + + Remove + Ta bort + + + + Undefines: + + + + + Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 + + + + + Include Paths: + Include sökvägar: + + + + This is a workfolder that Cppcheck will use for various purposes. + + + + + Clang (experimental) + + + + + Check level + + + + + Normal -- meant for normal analysis in CI. Analysis should finish in reasonable time. + + + + + Exhaustive -- meant for nightly builds etc. Analysis time can be longer (10x slower than compilation is OK). + + + + + If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. + + + + + Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) + + + + + Max recursion in template instantiation + + + + + Filepaths in warnings will be relative to this path + + + + + If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. + + + + + Exclude source files + + + + + Exclude folder... + + + + + Exclude file... + + + + + Misra C + + + + + 2012 + + + + + 2023 + + + + + MISRA rule texts + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + Cert C++ + + + + + Bug hunting (Premium) + + + + + External tools + + + + + Up + Upp + + + + Down + Ned + + + + Platform + + + + + + Analysis + + + + + Parser + + + + + Cppcheck (built in) + + + + + Check that each class has a safe public interface + + + + + Limit analysis + + + + + Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) + Check code in unused templates (slower and less accurate analysis) + + + + + Max CTU depth + + + + + Warning options + + + + + Root path: + Bas sökväg: + + + + Warning tags (separated by semicolon) + Varnings taggar (separerade med semikolon) + + + + Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) + Cppcheck build dir (whole program analys, incremental analys, statistik, etc) + + + + Types and Functions + + + + + Libraries + Libraries + + + + Suppressions + Suppressions + + + + Add + Lägg till + + + + + Addons + Addons + + + + Note: Addons require <a href="https://www.python.org/">Python</a> being installed. + + + + + Y2038 + Y2038 + + + + Thread safety + Tråd säkerhet + + + + Coding standards + Kodstandarder + + + + Cert C + + + + + CERT-INT35-C: int precision (if size equals precision, you can leave empty) + + + + + Misra C++ 2008 + + + + + Autosar + + + + + Bug hunting + + + + + Clang analyzer + Clang analyzer + + + + Clang-tidy + Clang-tidy + + + + Defines: + Defines: + + + + ProjectFileDialog + + + Project file: %1 + Projektfil: %1 + + + + Clang-tidy (not found) + + + + + Select Cppcheck build dir + Välj Cppcheck build dir + + + + Select include directory + Välj include sökväg + + + + Source files + + + + + All files + + + + + Exclude file + + + + + Select MISRA rule texts file + + + + + MISRA rule texts file (%1) + + + + + Select a directory to check + Välj mapp att analysera + + + + Visual Studio + Visual Studio + + + + Compile database + + + + + Borland C++ Builder 6 + + + + + Import Project + Importera Projekt + + + + Select directory to ignore + Välj sökväg att ignorera + + + + QObject + + + Unknown language specified! + Okänt språk valt! + + + + Language file %1 not found! + Language file %1.qm not found! + Språk filen %1 hittades ej! + + + + Failed to load translation for language %1 from file %2 + Failed to load translation for language %1 from file %2.qm + Misslyckades med att ladda översättningen för %1 från filen %2 + + + + line %1: Unhandled element %2 + + + + + line %1: Mandatory attribute '%2' missing in '%3' + + + + + (Not found) + + + + + Thin + + + + + ExtraLight + + + + + Light + + + + + Normal + + + + + Medium + + + + + DemiBold + + + + + Bold + + + + + ExtraBold + + + + + Black + + + + + Editor Foreground Color + + + + + Editor Background Color + + + + + Highlight Background Color + + + + + Line Number Foreground Color + + + + + Line Number Background Color + + + + + Keyword Foreground Color + + + + + Keyword Font Weight + + + + + Class Foreground Color + Class ForegroundColor + + + + + Class Font Weight + + + + + Quote Foreground Color + + + + + Quote Font Weight + + + + + Comment Foreground Color + + + + + Comment Font Weight + + + + + Symbol Foreground Color + + + + + Symbol Background Color + + + + + Symbol Font Weight + + + + + Set to Default Light + + + + + Set to Default Dark + + + + + QPlatformTheme + + + OK + OK + + + + Cancel + Avbryt + + + + Close + Stäng + + + + Save + Spara + + + + ResultsTree + + + File + Fil + + + + Severity + Typ + + + + Line + Rad + + + + Summary + Sammanfattning + + + + Undefined file + Odefinierad fil + + + + Copy + + + + + Could not find file: + + + + + Please select the folder '%1' + + + + + Select Directory '%1' + + + + + Please select the directory where file is located. + + + + + debug + debug + + + + note + note + + + + Recheck + Analysera om + + + + Hide + Dölj + + + + Hide all with id + Dölj alla med id + + + + Suppress selected id(s) + Stäng av valda id + + + + Open containing folder + Öppna mapp + + + + internal + + + + + + Tag + Tag + + + + No tag + Ingen tag + + + + + Cppcheck + Cppcheck + + + + No editor application configured. + +Configure the editor application for Cppcheck in preferences/Applications. + Configure the text file viewer program in Cppcheck preferences/Applications. + Ingen editor konfigurerad. + +Konfigurera program i inställningar/program. + + + + No default editor application selected. + +Please select the default editor application in preferences/Applications. + Ingen standard editor vald. + +Vänligen välj standard editor i inställningar/Program. + + + + Could not find the file! + Kunde inte hitta filen! + + + + Could not start %1 + +Please check the application path and parameters are correct. + Kunde inte starta %1 + +Kontrollera att sökvägen och parametrarna är korrekta. + + + + Select Directory + Välj mapp + + + + Id + Id + + + + Inconclusive + Inconclusive + + + + Since date + Sedan datum + + + + style + stil + + + + error + fel + + + + warning + varning + + + + performance + prestanda + + + + portability + portabilitet + + + + information + information + + + + ResultsView + + + Print Report + Skriv ut rapport + + + + No errors found, nothing to print. + Inga fel hittades, inget att skriva ut. + + + + %p% (%1 of %2 files checked) + %p% (%1 av %2 filer analyserade) + + + + + Cppcheck + Cppcheck + + + + No errors found. + Inga fel hittades. + + + + Errors were found, but they are configured to be hidden. +To toggle what kind of errors are shown, open view menu. + Fel hittades, men de visas ej. +För att ställa in vilka fel som skall visas använd visa menyn. + + + + + Failed to read the report. + Misslyckades att läsa rapporten. + + + + XML format version 1 is no longer supported. + XML format version 1 stöds ej längre. + + + + First included by + Först inkluderad av + + + + Id + Id + + + + Bug hunting analysis is incomplete + + + + + Clear Log + + + + + Copy this Log entry + + + + + Copy complete Log + + + + + Analysis was stopped + + + + + There was a critical error with id '%1' + + + + + when checking %1 + + + + + when checking a file + + + + + Analysis was aborted. + + + + + + Failed to save the report. + Misslyckades med att spara rapporten. + + + + Results + Resultat + + + + Critical errors + + + + + Analysis Log + Analys Log + + + + Warning Details + Varningsdetaljer + + + + ScratchPad + + + Scratchpad + Scratchpad + + + + Copy or write some C/C++ code here: + + + + + Optionally enter a filename (mainly for automatic language detection) and click on "Check": + + + + + filename + Filnamn + + + + Check + Analysera + + + + Settings + + + Preferences + Inställningar + + + + General + Allmänt + + + + Add... + Lägg till... + + + + Number of threads: + Antal trådar: + + + + Ideal count: + Optimalt värde: + + + + Force checking all #ifdef configurations + Check all #ifdef configurations + Kontrollera alla #ifdef konfigurationer + + + + Show full path of files + Visa den fulla sökvägen för filer + + + + Show "No errors found" message when no errors found + Visa "Inga fel hittades" meddelande när inga fel hittas + + + + Display error Id in column "Id" + Visa meddelande id i kolumn "Id" + + + + Enable inline suppressions + Använd inline suppressions + + + + Check for inconclusive errors also + Kör inconclusive analys + + + + Show statistics on check completion + Visa statistik när analys är klar + + + + Check for updates + + + + + Show internal warnings in log + Visa interna fel i loggen + + + + Addons + Addons + + + + Python binary (leave this empty to use python in the PATH) + Python binär fil (lämna tom för att använda python i PATH) + + + + + + ... + ... + + + + MISRA addon + + + + + MISRA rule texts file + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + Clang + Clang + + + + Clang path (leave empty to use system PATH) + Clang sökväg (lämna tom för att använda PATH) + + + + Visual Studio headers + Visual Studio headers + + + + <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> + <html><head/><body><p>Sökvägar till Visual Studio headers, separerade med semikolon ';'.</p><p>Du kan öppna en Visual Studio command prompt, och skriva &quot;SET INCLUDE&quot;. Sedan kopiera och klistra in sökvägarna.</p></body></html> + + + + Code Editor + + + + + Code Editor Style + + + + + System Style + + + + + Default Light Style + + + + + Default Dark Style + + + + + Custom + + + + + 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 + + + + SettingsDialog + + + N/A + Ej tillgängligt + + + + The executable file "%1" is not available + + + + + Add a new application + Lägg till program + + + + Modify an application + Ändra program + + + + [Default] + [Vald] + + + + [Default] + [Förvald] + + + + Select python binary + Välj python binär + + + + Select MISRA File + + + + + Select clang path + Välj Clang sökväg + + + + StatsDialog + + + + + + Statistics + Statistik + + + + + Project + Projekt + + + + Project: + Projekt: + + + + Paths: + Sökvägar: + + + + Include paths: + Include sökvägar: + + + + Defines: + Defines: + + + + Undefines: + + + + + + Previous Scan + Föregående analys + + + + Path Selected: + Vald sökväg: + + + + Number of Files Scanned: + Antal analyserade filer: + + + + Scan Duration: + Analys tid: + + + + Errors: + Fel: + + + + Warnings: + Varningar: + + + + Stylistic warnings: + Stil varningar: + + + + Portability warnings: + Portabilitets varningar: + + + + Performance issues: + Prestanda varningar: + + + + Information messages: + Informations meddelanden: + + + + Active checkers: + + + + + Checkers + + + + + History + Historik + + + + File: + Fil: + + + + Copy to Clipboard + Kopiera + + + + Pdf Export + Pdf Export + + + + 1 day + 1 dag + + + + %1 days + %1 dagar + + + + 1 hour + 1 timme + + + + %1 hours + %1 timmar + + + + 1 minute + 1 minut + + + + %1 minutes + %1 minuter + + + + 1 second + 1 sekund + + + + %1 seconds + %1 sekunder + + + + 0.%1 seconds + 0.%1 sekunder + + + + and + och + + + + Export PDF + Exportera PDF + + + + Project Settings + Projekt inställningar + + + + Paths + Sökvägar + + + + Include paths + Include sökvägar + + + + Defines + Definitioner + + + + Undefines + + + + + Path selected + Vald sökväg + + + + Number of files scanned + Antal analyserade filer + + + + Scan duration + Tid + + + + + Errors + Fel + + + + File: + Fil: + + + + No cppcheck build dir + Ingen Cppcheck build dir + + + + + Warnings + Varningar + + + + + Style warnings + Stil varningar + + + + + Portability warnings + Portabilitetsvarningar + + + + + Performance warnings + Prestanda varningar + + + + + Information messages + Informationsmeddelanden + + + + ThreadResult + + + %1 of %2 files checked + %1 av %2 filer analyserade + + + + TranslationHandler + + + Failed to change the user interface language: + +%1 + +The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. + Misslyckades att ändra språk: + +%1 + +Språket har nollställts till Engelska. Öppna Preferences och välj något av de tillgängliga språken. + + + + Cppcheck + Cppcheck + + + + TxtReport + + + inconclusive + inconclusive + + + + toFilterString + + + All supported files (%1) + + + + + All files (%1) + + + + diff --git a/cppcheck-2.14.0/gui/cppcheck_zh_CN.ts b/cppcheck-2.14.0/gui/cppcheck_zh_CN.ts new file mode 100644 index 00000000..43fd2107 --- /dev/null +++ b/cppcheck-2.14.0/gui/cppcheck_zh_CN.ts @@ -0,0 +1,3063 @@ + + + + + About + + + About Cppcheck + 关于 Cppcheck + + + + Version %1 + 版本 %1 + + + + Cppcheck - A tool for static C/C++ code analysis. + Cppcheck - C/C++ 静态代码分析工具。 + + + + Copyright © 2007-%1 Cppcheck team. + Copyright © 2007-2021 Cppcheck team. + 版权所有 © 2007-%1 Cppcheck 团队。 + + + + This program is licensed under the terms +of the GNU General Public License version 3 + 该程序在 GNU 通用公共授权版本 3 的条款下发布 + + + + Visit Cppcheck homepage at %1 + 访问 Cppcheck 主页: %1 + + + + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PCRE</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PicoJSON</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">TinyXML2</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Boost</li></ul></body></html> + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">pcre</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">picojson</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">tinyxml2</li></ul></body></html> + + + + + ApplicationDialog + + + Add an application + 添加应用程序 + + + + Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. + +The following texts in parameters are replaced with appropriate values when application is executed: +(file) - Filename containing the error +(line) - Line number containing the error +(message) - Error message +(severity) - Error severity + +Example opening a file with Kate and make Kate scroll to the correct line: +Executable: kate +Parameters: -l(line) (file) + 在这里,你可以添加一个应用程序,用于打开错误文件。请为该应用程序指定一个名称、可执行文件和命令行参数。 + +当应用程序执行时,在参数中的以下文本将会替换为适当的值: +(file) - 包含错误的文件名称 +(line) - 包含错误的行号 +(message) - 错误消息 +(severity) - 错误严重性 + +示例:使用 Kate 打开一个文件,并使之滚动到相应的行: +可执行文件: kate +参数: -l(line) (file) + + + + &Name: + 名称(&N): + + + + &Executable: + 可执行文件(&E): + + + + &Parameters: + 参数(&P): + + + + Browse + 浏览 + + + + Executable files (*.exe);;All files(*.*) + 可执行文件(*.exe);;所有文件(*.*) + + + + Select viewer application + 选择查看应用程序 + + + + Cppcheck + Cppcheck + + + + You must specify a name, a path and optionally parameters for the application! + 你必须为应用程序指定名称、路径以及可选参数! + + + + ComplianceReportDialog + + + Compliance Report + + + + + Project name + + + + + Project version + + + + + Coding Standard + + + + + Misra C + + + + + Cert C + + + + + Cert C++ + + + + + List of files with md5 checksums + + + + + Compliance report + + + + + HTML files (*.html) + + + + + + Save compliance report + + + + + Failed to import '%1' (%2), can not show files in compliance report + + + + + FileViewDialog + + + Could not find the file: %1 + 无法找到文件: %1 + + + + + Cppcheck + Cppcheck + + + + Could not read the file: %1 + 无法读取文件: %1 + + + + HelpDialog + + + Cppcheck GUI help + Cppcheck GUI 帮助 + + + + Contents + 内容 + + + + Index + 索引 + + + + Helpfile '%1' was not found + 帮助文件 '%1' 未找到 + + + + Cppcheck + Cppcheck + + + + LibraryAddFunctionDialog + + + Add function + 添加函数 + + + + Function name(s) + 函数名 + + + + Number of arguments + 参数个数 + + + + LibraryDialog + + + Library Editor + 库编辑器 + + + + Open + 打开 + + + + Save + 保存 + + + + Save as + 另存为 + + + + Functions + 函数 + + + + Sort + 排序 + + + + Add + 添加 + + + + Filter: + 过滤: + + + + Comments + 注释 + + + + noreturn + 无返回 + + + + False + + + + + True + + + + + Unknown + 未知 + + + + return value must be used + 返回值必须被使用 + + + + ignore function in leaks checking + 在泄漏检查中忽略函数 + + + + Arguments + 参数 + + + + Edit + 编辑 + + + + + Library files (*.cfg) + 库文件 (*.cfg) + + + + Open library file + 打开库文件 + + + + + + Cppcheck + Cppcheck + + + + Cannot open file %1. + Can not open file %1. + 无法打开文件 %1。 + + + + Failed to load %1. %2. + 加载文件 %1 失败。%2。 + + + + Cannot save file %1. + Can not save file %1. + 无法保存文件 %1。 + + + + Save the library as + 库另存为 + + + + LibraryEditArgDialog + + + Edit argument + 编辑参数 + + + + <html><head/><body> +<p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> +<p>Typically, set this if the argument is a pointer, size, etc.</p> +<p>Example:</p> +<pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> +</body></html> + <html><head/><body> +<p>是否允许布尔值? 例如,来自比较结果或来自 '!' 操作符。</p> +<p>通常,如果参数是指针、大小等,则设置此参数。</p> +<p>例子:</p> +<pre> memcmp(x, y, i == 123); // 最后一个参数不应该有bool值</pre> +</body></html> + + + + Not bool + 非布尔值 + + + + <html><head/><body> +<p>Is a null parameter value allowed?</p> +<p>Typically this should be used on any pointer parameter that does not allow null.</p> +<p>Example:</p> +<pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> +</body></html> + <html><head/><body> +<p>是否允许空参数值?</p> +<p>通常这应该用于任何不允许空指针的参数。</p> +<p>例子:</p> +<pre> strcpy(x,y); // 无论 x 或 y 都不允许为空。</pre> +</body></html> + + + + Not null + 非空 + + + + Not uninit + 非未初始化 + + + + String + 字符串 + + + + Format string + 格式化字符串 + + + + Min size of buffer + 最小缓冲区大小 + + + + + Type + 类型 + + + + + None + + + + + + argvalue + argvalue + + + + + mul + mul + + + + + strlen + strlen + + + + + Arg + 参数1 + + + + + Arg2 + 参数2 + + + + and + 并且 + + + + Valid values + 有效值 + + + + MainWindow + + + + + + + + + + + + + + + + + Cppcheck + Cppcheck + + + + &File + 文件(&F) + + + + &View + 查看(&V) + + + + &Toolbars + 工具栏(&T) + + + + &Help + 帮助(&H) + + + + C++ standard + C++ 标准 + + + + &C standard + C standard + &C 标准 + + + + &Edit + 编辑(&E) + + + + Standard + 标准 + + + + Categories + 分类 + + + + &License... + 许可证(&L)... + + + + A&uthors... + 作者(&U)... + + + + &About... + 关于(&A)... + + + + &Files... + 文件(&F)... + + + + + Analyze files + Check files + 分析文件 + + + + Ctrl+F + Ctrl+F + + + + &Directory... + 目录(&D)... + + + + + Analyze directory + Check directory + 分析目录 + + + + Ctrl+D + Ctrl+D + + + + Ctrl+R + Ctrl+R + + + + &Stop + 停止(&S) + + + + + Stop analysis + Stop checking + 停止分析 + + + + Esc + Esc + + + + &Save results to file... + 保存结果到文件(&S)... + + + + Ctrl+S + Ctrl+S + + + + &Quit + 退出(&Q) + + + + &Clear results + 清空结果(&C) + + + + &Preferences + 首选项(&P) + + + + + Show style warnings + 显示风格警告 + + + + + Show errors + 显示错误 + + + + + Information + 信息 + + + + Show information messages + 显示信息消息 + + + + Show portability warnings + 显示可移植性警告 + + + + Show Cppcheck results + 显示 Cppcheck 结果 + + + + Clang + Clang + + + + Show Clang results + 显示 Clang 结果 + + + + &Filter + 滤器(&F) + + + + Filter results + 过滤结果 + + + + Windows 32-bit ANSI + + + + + Windows 32-bit Unicode + + + + + Unix 32-bit + + + + + Unix 64-bit + + + + + Windows 64-bit + + + + + &Print... + 打印(&P)... + + + + Print the Current Report + 打印当前报告 + + + + Print Pre&view... + 打印预览(&v)... + + + + Open a Print Preview Dialog for the Current Results + 打开当前结果的打印预览窗口 + + + + Open library editor + 打开库编辑器 + + + + C&lose Project File + 关闭项目文件(&L) + + + + &Edit Project File... + 编辑项目文件(&E)... + + + + &Statistics + 统计(&S) + + + + + Show warnings + 显示警告 + + + + + Show performance warnings + 显示性能警告 + + + + Show &hidden + 显示隐藏项(&H) + + + + &Check all + 全部选中(&C) + + + + Checking for updates + + + + + Hide + 隐藏 + + + + A&nalyze + 分析(&A) + + + + Filter + 滤器 + + + + &Reanalyze modified files + &Recheck modified files + 重新分析已修改的文件(&R) + + + + Reanal&yze all files + 重新分析全部文件(&y) + + + + Ctrl+Q + Ctrl+Q + + + + Style war&nings + 风格警告(&n) + + + + E&rrors + 编辑(&r) + + + + &Uncheck all + 全部取消选中(&U) + + + + Collapse &all + 全部折叠(&A) + + + + &Expand all + 全部展开(&E) + + + + &Standard + 标准(&S) + + + + Standard items + 标准项 + + + + &Contents + 内容(&C) + + + + Open the help contents + 打开帮助内容 + + + + F1 + F1 + + + + Toolbar + 工具栏 + + + + &Categories + 分类(&C) + + + + Error categories + 错误分类 + + + + &Open XML... + 打开 XML (&O)... + + + + Open P&roject File... + 打开项目文件(&R)... + + + + Ctrl+Shift+O + Ctrl+Shift+O + + + + Sh&ow Scratchpad... + 显示便条(&o)... + + + + &New Project File... + 新建项目文件(&N)... + + + + Ctrl+Shift+N + Ctrl+Shift+N + + + + &Log View + 日志视图(&L) + + + + Log View + 日志视图 + + + + &Warnings + 警告(&W) + + + + Per&formance warnings + 性能警告(&f) + + + + &Information + 信息(&I) + + + + &Portability + 可移植性(&P) + + + + P&latforms + 平台(&l) + + + + C++&11 + C++&11 + + + + C&99 + C&99 + + + + &Posix + &Posix + + + + C&11 + C&11 + + + + &C89 + &C89 + + + + &C++03 + &C++03 + + + + &Library Editor... + 库编辑器(&L)... + + + + &Auto-detect language + 自动检测语言(&A) + + + + &Enforce C++ + &Enforce C++ + + + + E&nforce C + E&nforce C + + + + C++14 + C++14 + + + + Reanalyze and check library + 重新分析并检查库 + + + + Check configuration (defines, includes) + 检查配置(defines, includes) + + + + C++17 + C++17 + + + + C++20 + C++20 + + + + Compliance report... + + + + + 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 不同版本间的设置有所不同。请检查(并修复)编辑器应用程序设置,否则编辑器程序可能不会正确启动。 + + + + You must close the project file before selecting new files or directories! + 在选择新的文件或目录之前,你必须先关闭此项目文件! + + + + + Quick Filter: + 快速滤器: + + + + Select configuration + 选择配置 + + + + Found project file: %1 + +Do you want to load this project file instead? + 找到项目文件: %1 + +你是否想加载该项目文件? + + + + The library '%1' contains unknown elements: +%2 + 库 '%1' 包含未知元素: +%2 + + + + File not found + 文件未找到 + + + + Bad XML + 无效的 XML + + + + Missing attribute + 缺失属性 + + + + Bad attribute value + 无效的属性值 + + + + Unsupported format + 不支持的格式 + + + + Duplicate platform type + 重复的平台类型 + + + + Platform type redefined + 平台类型重定义 + + + + Unknown element + 位置元素 + + + + Unknown issue + 未知问题 + + + + Failed to load the selected library '%1'. +%2 + 选择的库 '%1' 加载失败。 +%2 + + + + + + + Error + 错误 + + + Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + 加载 %1 失败。您的 Cppcheck 安装已损坏。您可以在命令行添加 --data-dir=<目录> 参数来指定文件位置。请注意,'--data-dir' 参数应当由安装脚本使用,因此,当使用此参数时,GUI不会启动,所发生的一切只是配置了设置。 + + + + + XML files (*.xml) + XML 文件(*.xml) + + + + Open the report file + 打开报告文件 + + + + License + 许可证 + + + + Authors + 作者 + + + + Save the report file + 保存报告文件 + + + + Text files (*.txt) + 文本文件(*.txt) + + + + CSV files (*.csv) + CSV 文件(*.csv) + + + + Cannot generate a compliance report right now, an analysis must finish successfully. Try to reanalyze the code and ensure there are no critical errors. + + + + + Project files (*.cppcheck);;All files(*.*) + 项目文件(*.cppcheck);;所有文件(*.*) + + + + Select Project File + 选择项目文件 + + + + Failed to open file + + + + + Unknown project file format + + + + + Failed to import project file + + + + + Failed to import '%1': %2 + +Analysis is stopped. + + + + + Failed to import '%1' (%2), analysis is stopped + + + + + Install + + + + + New version available: %1. %2 + + + + + + + + Project: + 项目: + + + + No suitable files found to analyze! + 没有找到合适的文件来分析! + + + + C/C++ Source + C/C++ 源码 + + + + Compile database + Compile database + + + + Visual Studio + Visual Studio + + + + Borland C++ Builder 6 + Borland C++ Builder 6 + + + + Select files to analyze + 选择要分析的文件 + + + + Select directory to analyze + 选择要分析的目录 + + + + Select the configuration that will be analyzed + 选择要分析的配置 + + + + Found project files from the directory. + +Do you want to proceed analysis without using any of these project files? + 在目录中发现项目文件。 + +您想在不使用这些项目文件的情况下进行分析吗? + + + + Duplicate define + + + + + File not found: '%1' + + + + + Failed to load/setup addon %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. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + +Analysis is aborted. + + + + + Failed to load %1 - %2 + +Analysis is aborted. + + + + + + %1 + +Analysis is aborted. + + + + + Current results will be cleared. + +Opening a new XML file will clear current results. +Do you want to proceed? + 当前结果将被清除。 + +打开一个新的XML文件将清除当前的结果。 +你想继续吗? + + + + Analyzer is running. + +Do you want to stop the analysis and exit Cppcheck? + 分析正在运行。 + +您想停止分析并退出 Cppcheck 吗? + + + + About + + + + + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) + XML 文件 (*.xml);;文本文件 (*.txt);;CSV 文件 (*.csv) + + + + Build dir '%1' does not exist, create it? + 构建文件夹 '%1' 不能存在,创建它吗? + + + + To check the project using addons, you need a build directory. + 要使用插件检查项目,您需要一个构建目录。 + + + Failed to import '%1', analysis is stopped + 导入 '%1' 失败,分析已停止 + + + + Project files (*.cppcheck) + 项目文件 (*.cppcheck) + + + + Select Project Filename + 选择项目文件名 + + + + No project file loaded + 项目文件未加载 + + + + The project file + +%1 + + could not be found! + +Do you want to remove the file from the recently used projects -list? + 项目文件 + +%1 + +未找到! + +你要从最近使用的项目列表中删除此文件吗? + + + + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> This option is for installation scripts so they can configure the directory where + datafiles are located (translations, cfg). The GUI is not started when this option + is used. + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> Specify directory where GUI datafiles are located (translations, cfg) + Cppcheck GUI. + +语法: + cppcheck-gui [选项] [文件或路径] + +选项: + -h, --help 打印此帮助 + -p <file> 打开指定的项目文件并开始检查它 + -l <file> 打开指定的 xml 结果文件 + -d <directory> 指定检查的目录,用以生成用 -l 指定的 xml 结果 + -v, --version 显示程序版本 + --data-dir=<directory> 这个选项用于安装脚本,这样他们可以配置数据文件所在的目录(translations, cfg)。 + 当使用这个选项时,GUI不会启动。 + + + + Cppcheck GUI - Command line parameters + Cppcheck GUI - 命令行参数 + + + + NewSuppressionDialog + + + New suppression + 新建抑制 + + + + Error ID + 错误 ID + + + + File name + 文件名 + + + + Line number + 行号 + + + + Symbol name + 符号名 + + + + Edit suppression + 编辑抑制 + + + + Platforms + + + Native + 本地 + + + + Unix 32-bit + + + + + Unix 64-bit + + + + + Windows 32-bit ANSI + + + + + Windows 32-bit Unicode + + + + + Windows 64-bit + + + + + ProjectFile + + + Project File + 项目文件 + + + + Paths and Defines + 路径和定义 + + + + Import Project (Visual studio / compile database/ Borland C++ Builder 6) + Import Project (Visual studio / compile database) + 导入项目 (Visual studio / compile database/ Borland C++ Builder 6) + + + + Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int + Defines must be separated by a semicolon ';' + 定义必须用分号分隔。例如:DEF1;DEF2=5;DEF3=int + + + + Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. + 注意:把你自己的 .cfg 文件放在和项目文件相同的文件夹中。你应该在上面看到它们。 + + + + If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. + 如果添加了标记,您将能够右键单击警告并设置其中一个标记。您可以手动对警告进行分类。 + + + + Exclude source files + 排除源文件 + + + + Exclude folder... + 排除文件夹... + + + + Exclude file... + 排除文件... + + + MISRA C 2012 + MISRA C 2012 + + + + MISRA rule texts + MISRA 规则文本 + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + <html><head/><body><p>从 MISRA C 2012 PDF 的附录 A &quot;指南摘要&quot; 复制/粘贴文本到一个文本文件。</p></body></html> + + + + ... + ... + + + + <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> + <html><head/><body><p>您有一个选择:</p><p> * 分析所有的 Debug 和 Release 配置</p><p> * 只分析第一个匹配的 Debug 配置</p><p><br/></p></body></html> + + + + + Browse... + 浏览... + + + + Analyze all Visual Studio configurations + 分析全部 Visual Studio 配置 + + + + Selected VS Configurations + 已选择的 VS 配置 + + + + Paths: + 路径: + + + + + Add... + 添加... + + + + + + Edit + 编辑 + + + + + + + Remove + 移除 + + + + Undefines: + 未定义: + + + + Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 + 未定义必须用分号分隔。例如:UNDEF1;UNDEF2;UNDEF3 + + + + Include Paths: + 包含目录: + + + + Types and Functions + 类型和函数 + + + + + Analysis + 分析 + + + + This is a workfolder that Cppcheck will use for various purposes. + 这是一个 Cppcheck 将用于各种目的的工作文件夹。 + + + + Parser + 解析器 + + + + Cppcheck (built in) + Cppcheck (内建) + + + + Check level + + + + + Normal -- meant for normal analysis in CI. Analysis should finish in reasonable time. + + + + + Exhaustive -- meant for nightly builds etc. Analysis time can be longer (10x slower than compilation is OK). + + + + + Check that each class has a safe public interface + 检查每个类是否有一个安全的公共接口 + + + + Limit analysis + 极限分析 + + + + Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) + Check code in unused templates (slower and less accurate analysis) + 检查未使用模板中的代码(正常情况下应该是打开,但理论上可以忽略未使用模板中的警告) + + + + Max CTU depth + 最大 CTU 深度 + + + + Cert C + + + + + CERT-INT35-C: int precision (if size equals precision, you can leave empty) + + + + + Misra C++ 2008 + + + + + Autosar + + + + + Bug hunting + + + + + External tools + 外部工具 + + + + Up + 向上 + + + + Down + 向下 + + + + Platform + 平台 + + + + Clang (experimental) + Clang (实验性的) + + + + If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. + 如果你想要设计你的类尽可能的灵活和健壮,那么公共接口必须非常健壮。Cppcheck 将假设参数可以取 *任何* 值。 + + + + Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) + 检查头文件中的代码(通常应该是打开的。如果您想要一个有限的快速分析,那么关掉它)) + + + + Max recursion in template instantiation + 模板实例化中的最大递归 + + + + Warning options + 警告选项 + + + + Root path: + 根路径: + + + + Filepaths in warnings will be relative to this path + 警告中的文件路径将相对于此路径 + + + + Warning tags (separated by semicolon) + 警告标志(用分号隔开) + + + + Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) + Cppcheck 构建目录 (整个程序分析、增量分析、统计数据等) + + + + Libraries + + + + + Suppressions + 抑制 + + + + Add + 添加 + + + + + Addons + 插件 + + + + Note: Addons require <a href="https://www.python.org/">Python</a> being installed. + 注意:插件需要安装 <a href="https://www.python.org/">Python</a>。 + + + + Y2038 + Y2038 + + + + Thread safety + 线程安全 + + + + Coding standards + 编码标准 + + + + Misra C + + + + + 2012 + + + + + 2023 + + + + + Cert C++ + + + + + Bug hunting (Premium) + + + + + Clang analyzer + Clang analyzer + + + + Clang-tidy + Clang-tidy + + + + Defines: + 定义: + + + + ProjectFileDialog + + + Project file: %1 + 项目文件: %1 + + + + Select Cppcheck build dir + 选择 Cppcheck 构建目录 + + + + Select include directory + 选择 Include 目录 + + + + Select a directory to check + 选择一个检查目录 + + + + Clang-tidy (not found) + Clang-tidy (未找到) + + + + Visual Studio + Visual Studio + + + + Compile database + Compile database + + + + Borland C++ Builder 6 + Borland C++ Builder 6 + + + + Import Project + 导入项目 + + + + Select directory to ignore + 选择忽略的目录 + + + + Source files + 源文件 + + + + All files + 全部文件 + + + + Exclude file + 排除文件 + + + + Select MISRA rule texts file + 选择 MISRA 规则文本文件 + + + + MISRA rule texts file (%1) + MISRA 规则文本文件 (%1) + + + + QObject + + + Unknown language specified! + 指定了未知语言! + + + + Language file %1 not found! + 语言文件 %1 不存在! + + + + Failed to load translation for language %1 from file %2 + 无法从文件 %2 中为语言 %1 加载翻译文件 + + + + line %1: Unhandled element %2 + 第%1行:未处理元素 %2 + + + + line %1: Mandatory attribute '%2' missing in '%3' + 第%1行:在 "%3" 中缺失的必选属性 "%2" + + + + (Not found) + (未找到) + + + + Thin + 极细 + + + + ExtraLight + 更细 + + + + Light + + + + + Normal + 常规 + + + + Medium + 中等 + + + + DemiBold + 较粗 + + + + Bold + + + + + ExtraBold + 更粗 + + + + Black + 极粗 + + + + Editor Foreground Color + 编辑器前景色 + + + + Editor Background Color + 编辑器背景色 + + + + Highlight Background Color + 高亮背景色 + + + + Line Number Foreground Color + 行号前景色 + + + + Line Number Background Color + 行号背景色 + + + + Keyword Foreground Color + 关键字前景色 + + + + Keyword Font Weight + 关键字字体大小 + + + + Class Foreground Color + Class ForegroundColor + 类前景色 + + + + Class Font Weight + 类字体大小 + + + + Quote Foreground Color + 引用前景色 + + + + Quote Font Weight + 引用字体大小 + + + + Comment Foreground Color + 注释前景色 + + + + Comment Font Weight + 注释字体大小 + + + + Symbol Foreground Color + 符号前景色 + + + + Symbol Background Color + 符号背景色 + + + + Symbol Font Weight + 符号字体大小 + + + + Set to Default Light + 设置为默认亮色 + + + + Set to Default Dark + 设置为默认暗色 + + + + QPlatformTheme + + + OK + 确定 + + + + Cancel + 取消 + + + + Close + 关闭 + + + + Save + 保存 + + + + ResultsTree + + + File + 文件 + + + + Severity + 严重性 + + + + Line + + + + + Summary + 概要 + + + + Undefined file + 未定义文件 + + + + Copy + 复制 + + + + Could not find file: + 找不到文件: + + + + Please select the folder '%1' + 请选择文件夹 '%1' + + + + Select Directory '%1' + 选择目录 '%1' + + + + Please select the directory where file is located. + 请选择文件所在的目录。 + + + + debug + 调试 + + + + note + 注意 + + + + Recheck + 重新检查 + + + + Hide + 隐藏 + + + + Hide all with id + 隐藏全部 ID + + + + Suppress selected id(s) + 抑制选择的 ID + + + + Open containing folder + 打开包含的文件夹 + + + + internal + + + + + + Tag + 标记 + + + + No tag + 取消标记 + + + + + Cppcheck + Cppcheck + + + + No editor application configured. + +Configure the editor application for Cppcheck in preferences/Applications. + Configure the text file viewer program in Cppcheck preferences/Applications. + 编辑应用程序未配置。 + +在“首先项 / 应用程序”中为 Cppcheck 配置编辑应用程序。 + + + + No default editor application selected. + +Please select the default editor application in preferences/Applications. + 未选中默认编辑应用程序。 + +请在“首先项 / 应用程序”中选择默认应用程序。 + + + + Could not find the file! + 找不到文件! + + + + Could not start %1 + +Please check the application path and parameters are correct. + 无法启动 %1 + +请检查此应用程序的路径与参数是否正确。 + + + + Select Directory + 选择目录 + + + + Id + Id + + + + Inconclusive + 不确定的 + + + + Since date + 日期 + + + + style + 风格 + + + + error + 错误 + + + + warning + 警告 + + + + performance + 性能 + + + + portability + 移植可能性 + + + + information + 信息 + + + + ResultsView + + + Results + 结果 + + + + Critical errors + + + + + Analysis Log + 分析日志 + + + + Warning Details + 警告详情 + + + + + Failed to save the report. + 保存报告失败。 + + + + Print Report + 打印报告 + + + + No errors found, nothing to print. + 没有错误发现,没有可打印内容。 + + + + %p% (%1 of %2 files checked) + %p% (%2 个文件已检查 %1 个) + + + + + Cppcheck + Cppcheck + + + + No errors found. + 未发现错误。 + + + + Errors were found, but they are configured to be hidden. +To toggle what kind of errors are shown, open view menu. + 发现错误,但它们被设为隐藏。 +打开“查看”菜单,切换需要显示的错误。 + + + + + Failed to read the report. + 读取报告失败。 + + + + XML format version 1 is no longer supported. + 不再支持 XML 格式版本 1。 + + + + First included by + 首次包含于 + + + + Id + Id + + + + Bug hunting analysis is incomplete + 错误搜寻分析未完成 + + + + Clear Log + 清空日志 + + + + Copy this Log entry + 复制此日志条目 + + + + Copy complete Log + 复制完整日志 + + + + Analysis was stopped + + + + + There was a critical error with id '%1' + + + + + when checking %1 + + + + + when checking a file + + + + + Analysis was aborted. + + + + + ScratchPad + + + Scratchpad + 便条 + + + + Copy or write some C/C++ code here: + 在这里复制或输入一些 C/C++ 代码: + + + + Optionally enter a filename (mainly for automatic language detection) and click on "Check": + 可选择输入文件名 (主要用来自动语言检测) 然后点击 "检查": + + + + filename + 文件名 + + + + Check + 检查 + + + + Settings + + + Preferences + 首选项 + + + + General + 常规 + + + + Add... + 添加... + + + + Number of threads: + 线程个数: + + + + Ideal count: + 理想个数: + + + + Force checking all #ifdef configurations + Check all #ifdef configurations + 强制检查所有 #ifdef 配置 + + + + Show full path of files + 显示文件的完整路径 + + + + Show "No errors found" message when no errors found + 当未找到错误,显示“未发现错误”消息 + + + + Display error Id in column "Id" + 在列“Id”中显示错误 Id + + + + Enable inline suppressions + 启用内联方案 + + + + Check for inconclusive errors also + 检查不确定的错误 + + + + Show statistics on check completion + 检查完成后显示统计数据 + + + + Check for updates + + + + + Show internal warnings in log + 在日志中显示内建警告 + + + + Addons + 插件 + + + + Python binary (leave this empty to use python in the PATH) + Python 二进制 (留空将使用 PATH 路径中的 python) + + + + + + ... + ... + + + + MISRA addon + MISRA 插件 + + + + MISRA rule texts file + MISRA 规则文本文件 + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + <html><head/><body><p>从 MISRA C 2012 PDF 的附录 A &quot;指南摘要&quot; 复制/粘贴文本到一个文本文件。</p></body></html> + + + + Clang + Clang + + + + Clang path (leave empty to use system PATH) + Clang 路径 (留空将使用系统 PATH 路径) + + + + Visual Studio headers + Visual Studio 头文件 + + + + <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> + <html><head/><body><p>Visual Studio 头文件路径,用分号 ';' 分割。</p><p>你可以打开一个 Visual Studio 命令提示符,输入 &quot;SET INCLUDE&quot;。然后复制/粘贴路径。</p></body></html> + + + + Code Editor + 代码编辑器 + + + + Code Editor Style + 代码编辑器风格 + + + + System Style + 系统风格 + + + + Default Light Style + 默认浅色风格 + + + + Default Dark Style + 默认深色风格 + + + + Custom + 自定义 + + + + Remove + 移除 + + + + Applications + 应用程序 + + + + + Edit... + 编辑... + + + + Set as default + 设为默认 + + + + Reports + 报告 + + + + Save all errors when creating report + 创建报告时,保存所有错误 + + + + Save full path to files in reports + 在报告中保存文件的完整路径 + + + + Language + 语言 + + + + SettingsDialog + + + N/A + N/A + + + + The executable file "%1" is not available + 可执行文件 "%1" 不可用 + + + + Add a new application + 添加一个新的应用程序 + + + + Modify an application + 修改一个应用程序 + + + + [Default] + [默认] + + + + [Default] + [默认] + + + + Select python binary + 选择 python 二进制 + + + + Select MISRA File + 选择 MISRA 文件 + + + + Select clang path + 选择 clang 路径 + + + + StatsDialog + + + + + + Statistics + 统计 + + + + + Project + 项目 + + + + Project: + 项目: + + + + Paths: + 路径: + + + + Include paths: + 包含路径: + + + + Defines: + 定义: + + + + Undefines: + 未定义: + + + + + Previous Scan + 上一次扫描 + + + + Path Selected: + 选中的路径: + + + + Number of Files Scanned: + 扫描的文件数: + + + + Scan Duration: + 扫描时间: + + + + Errors: + 错误: + + + + Warnings: + 警告: + + + + Stylistic warnings: + Stylistic 警告: + + + + Portability warnings: + 可移植性警告: + + + + Performance issues: + 性能警告: + + + + Information messages: + 信息: + + + + Active checkers: + + + + + Checkers + + + + + History + 历史 + + + + File: + 文件: + + + + Copy to Clipboard + 复制到剪贴板 + + + + Pdf Export + 导出 PDF + + + + 1 day + 1 天 + + + + %1 days + %1 天 + + + + 1 hour + 1 小时 + + + + %1 hours + %1 小时 + + + + 1 minute + 1 分钟 + + + + %1 minutes + %1 分钟 + + + + 1 second + 1 秒 + + + + %1 seconds + %1 秒 + + + + 0.%1 seconds + 0.%1 秒 + + + + and + + + + + Export PDF + 导出 PDF + + + + Project Settings + 项目设置 + + + + Paths + 路径 + + + + Include paths + 包含路径 + + + + Defines + 定义 + + + + Undefines + 未定义 + + + + Path selected + 选中的路径 + + + + Number of files scanned + 扫描的文件数 + + + + Scan duration + 扫描时间 + + + + + Errors + 错误 + + + + File: + 文件: + + + + No cppcheck build dir + 没有 cppcheck 构建目录 + + + + + Warnings + 警告 + + + + + Style warnings + 风格警告 + + + + + Portability warnings + 移植可能性警告 + + + + + Performance warnings + 性能警告 + + + + + Information messages + 信息 + + + + ThreadResult + + + %1 of %2 files checked + %2 个文件已检查 %1 个 + + + + TranslationHandler + + + Failed to change the user interface language: + +%1 + +The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. + 更改用户界面语言失败: + +%1 + +用户界面语言已被重置为英语。打开“首选项”对话框,选择任何可用的语言。 + + + + Cppcheck + Cppcheck + + + + TxtReport + + + inconclusive + 不确定的 + + + + toFilterString + + + All supported files (%1) + 全部支持的文件 (%1) + + + + All files (%1) + 全部文件 (%1) + + + diff --git a/cppcheck-2.14.0/gui/cppcheck_zh_TW.ts b/cppcheck-2.14.0/gui/cppcheck_zh_TW.ts new file mode 100644 index 00000000..655164c0 --- /dev/null +++ b/cppcheck-2.14.0/gui/cppcheck_zh_TW.ts @@ -0,0 +1,2984 @@ + + + + + About + + + About Cppcheck + 關於 Cppcheck + + + + Version %1 + 版本 %1 + + + + Cppcheck - A tool for static C/C++ code analysis. + Cppcheck - 一款靜態 C/C++ 程式碼分析工具。 + + + + Copyright © 2007-%1 Cppcheck team. + 著作權 © 2007-%1 Cppcheck 團隊。 + + + + This program is licensed under the terms +of the GNU General Public License version 3 + 該程式是根據 GNU 通用公眾授權條款第 3 版 +的規定進行授權的 + + + + Visit Cppcheck homepage at %1 + 訪問 Cppcheck 主頁: %1 + + + + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PCRE</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">PicoJSON</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">TinyXML2</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Boost</li></ul></body></html> + <html><head/><body><p>Many thanks to these libraries that we use:</p><ul style="margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;"><li style=" margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">pcre</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">picojson</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">qt</li><li style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">tinyxml2</li></ul></body></html> + + + + + ApplicationDialog + + + Add an application + 新增應用程式 + + + + Here you can add an application that can open error files. Specify a name for the application, the application executable and command line parameters for the application. + +The following texts in parameters are replaced with appropriate values when application is executed: +(file) - Filename containing the error +(line) - Line number containing the error +(message) - Error message +(severity) - Error severity + +Example opening a file with Kate and make Kate scroll to the correct line: +Executable: kate +Parameters: -l(line) (file) + + + + + &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! + 您必須為應用程式指定名稱、路徑與選用的參數! + + + + ComplianceReportDialog + + + Compliance Report + + + + + Project name + 專案名稱 + + + + Project version + 專案版本 + + + + Coding Standard + + + + + Misra C + + + + + Cert C + + + + + Cert C++ + + + + + List of files with md5 checksums + + + + + Compliance report + + + + + HTML files (*.html) + HTML 檔案 (*.html) + + + + + Save compliance report + + + + + Failed to import '%1' (%2), can not show files in compliance report + + + + + FileViewDialog + + + Could not find the file: %1 + 無法找到檔案: %1 + + + + + Cppcheck + Cppcheck + + + + Could not read the file: %1 + 無法讀取檔案: %1 + + + + HelpDialog + + + Cppcheck GUI help + Cppcheck GUI 幫助 + + + + Contents + 內容 + + + + Index + 索引 + + + + Helpfile '%1' was not found + 找不到幫助檔 '%1' + + + + Cppcheck + Cppcheck + + + + LibraryAddFunctionDialog + + + Add function + 新增函式 + + + + Function name(s) + 函式名稱 + + + + Number of arguments + 引數數量 + + + + LibraryDialog + + + Library Editor + 程式庫編輯器 + + + + Open + 開啟 + + + + Save + 儲存 + + + + Save as + 另存為 + + + + Functions + 函式 + + + + Sort + 排序 + + + + Add + 新增 + + + + Filter: + + + + + Comments + 註釋 + + + + noreturn + + + + + False + + + + + True + + + + + Unknown + 未知 + + + + return value must be used + + + + + ignore function in leaks checking + + + + + Arguments + 引數 + + + + Edit + 編輯 + + + + + Library files (*.cfg) + 程式庫檔案 (*.cfg) + + + + Open library file + 開啟程式庫檔案 + + + + + + Cppcheck + Cppcheck + + + + Cannot open file %1. + 無法開啟檔案 %1。 + + + + Failed to load %1. %2. + 無法載入 %1. %2。 + + + + Cannot save file %1. + 無法儲存檔案 %1。 + + + + Save the library as + 另存程式庫為 + + + + LibraryEditArgDialog + + + Edit argument + 編輯引數 + + + + <html><head/><body> +<p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> +<p>Typically, set this if the argument is a pointer, size, etc.</p> +<p>Example:</p> +<pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> +</body></html> + + + + + Not bool + + + + + <html><head/><body> +<p>Is a null parameter value allowed?</p> +<p>Typically this should be used on any pointer parameter that does not allow null.</p> +<p>Example:</p> +<pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> +</body></html> + + + + + Not null + + + + + Not uninit + + + + + String + 字串 + + + + Format string + + + + + Min size of buffer + + + + + + Type + 型別 + + + + + None + + + + + + argvalue + + + + + + mul + + + + + + strlen + + + + + + Arg + + + + + + Arg2 + + + + + and + + + + + Valid values + 有效值 + + + + MainWindow + + + + + + + + + + + + + + + + + Cppcheck + Cppcheck + + + + Checking for updates + 檢查更新 + + + + Hide + 隱藏 + + + + &File + 檔案(&F) + + + + &View + 檢視(&V) + + + + &Toolbars + 工具條(&T) + + + + &Help + 幫助(&H) + + + + A&nalyze + 分析(&N) + + + + C++ standard + C++ 標準 + + + + &C standard + C 標準(&C) + + + + &Edit + 編輯(&E) + + + + Standard + 標準 + + + + Categories + 分類 + + + + Filter + 篩選 + + + + &License... + 授權(&L)... + + + + A&uthors... + 作者(&U)... + + + + &About... + 關於(&A)... + + + + &Files... + 檔案(&F)... + + + + + Analyze files + 分析檔案 + + + + Ctrl+F + Ctrl+F + + + + &Directory... + 目錄(&D)... + + + + + Analyze directory + 分析目錄 + + + + Ctrl+D + Ctrl+D + + + + &Reanalyze modified files + 重新分析已修改的檔案(&R) + + + + Ctrl+R + Ctrl+R + + + + Reanal&yze all files + 重新分析所有檔案(&Y) + + + + &Stop + 停止(&S) + + + + + Stop analysis + 停止分析 + + + + Esc + Esc + + + + &Save results to file... + 儲存結果為檔案(&S)... + + + + Ctrl+S + Ctrl+S + + + + &Quit + 退出(&Q) + + + + Ctrl+Q + Ctrl+Q + + + + &Clear results + 清除結果(&C) + + + + &Preferences + 偏好設定(&P) + + + + Style war&nings + 樣式警告(&N) + + + + + Show style warnings + 顯示樣式警告 + + + + E&rrors + 錯誤(&R) + + + + + Show errors + 顯示錯誤 + + + + &Check all + 全部檢查(&C) + + + + &Uncheck all + + + + + 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)... + + + + Ctrl+Shift+O + Ctrl+Shift+O + + + + Sh&ow Scratchpad... + + + + + &New Project File... + 新增專案檔(&N)... + + + + Ctrl+Shift+N + Ctrl+Shift+N + + + + &Log View + 日誌檢視(&L) + + + + Log View + 日誌檢視 + + + + C&lose Project File + 關閉專案檔(&L) + + + + &Edit Project File... + 編輯專案檔(&E)... + + + + &Statistics + 統計資料(&S) + + + + &Warnings + 警告(&W) + + + + + Show warnings + 顯示警告 + + + + Per&formance warnings + 效能警告(&F) + + + + + Show performance warnings + 顯示下效能警告 + + + + Show &hidden + 顯示隱藏項目(&H) + + + + &Information + 資訊(&I) + + + + Show information messages + 顯示資訊訊息 + + + + &Portability + 可移植性(&P) + + + + Show portability warnings + 顯示可移植性警告 + + + + Show Cppcheck results + 顯示 Cppcheck 結果 + + + + Clang + Clang + + + + Show Clang results + 顯示 Clang 結果 + + + + &Filter + 篩選(&F) + + + + Filter results + 篩選結果 + + + + Windows 32-bit ANSI + Windows 32 位元 ANSI + + + + Windows 32-bit Unicode + Windows 32 位元 Unicode + + + + Unix 32-bit + Unix 32 位元 + + + + Unix 64-bit + Unix 64 位元 + + + + Windows 64-bit + Windows 64 位元 + + + + P&latforms + 平臺(&L) + + + + C++&11 + C++&11 + + + + C&99 + C&99 + + + + &Posix + + + + + C&11 + C&11 + + + + &C89 + &C89 + + + + &C++03 + &C++03 + + + + &Print... + 列印(&P)... + + + + Print the Current Report + 列印當前報告 + + + + Print Pre&view... + 列印預覽(&V)... + + + + Open a Print Preview Dialog for the Current Results + 開啟當前結果的列印預覽視窗 + + + + &Library Editor... + 程式庫編輯器(&L)... + + + + Open library editor + 開啟程式庫編輯器 + + + + &Auto-detect language + 自動偵測語言(&A) + + + + &Enforce C++ + + + + + E&nforce C + + + + + C++14 + C++14 + + + + Reanalyze and check library + 重新分析並檢查程式庫 + + + + Check configuration (defines, includes) + 檢查組態 (定義、包含) + + + + C++17 + C++17 + + + + C++20 + C++20 + + + + Compliance report... + + + + + Cppcheck GUI. + +Syntax: + cppcheck-gui [OPTIONS] [files or paths] + +Options: + -h, --help Print this help + -p <file> Open given project file and start checking it + -l <file> Open given results xml file + -d <directory> Specify the directory that was checked to generate the results xml specified with -l + -v, --version Show program version + --data-dir=<directory> This option is for installation scripts so they can configure the directory where + datafiles are located (translations, cfg). The GUI is not started when this option + is used. + + + + + Cppcheck GUI - Command line parameters + Cppcheck GUI - 命令行參數 + + + + + Quick Filter: + 快速篩選: + + + + + + + Project: + 專案: + + + + 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 analyze! + 找不到適合的檔案來分析! + + + + You must close the project file before selecting new files or directories! + 您必須在選取新檔案或目錄之前關閉該專案檔! + + + + C/C++ Source + C/C++ 來源檔 + + + + Compile database + 編譯資料庫 + + + + Visual Studio + Visual Studio + + + + Borland C++ Builder 6 + Borland C++ Builder 6 + + + + Select files to analyze + 選取要分析的檔案 + + + + Select directory to analyze + 選取要分析的目錄 + + + + Select configuration + 選取組態 + + + + Select the configuration that will be analyzed + 選取要分析的組態 + + + + Found project file: %1 + +Do you want to load this project file instead? + + + + + Found project files from the directory. + +Do you want to proceed analysis without using any of these project files? + + + + + + Information + 資訊 + + + + The library '%1' contains unknown elements: +%2 + + + + + File not found + 找不到檔案 + + + + Bad XML + + + + + Missing attribute + + + + + Bad attribute value + + + + + Unsupported format + 未支援的格式 + + + + Duplicate platform type + 重複的平臺型別 + + + + Platform type redefined + 平臺型別重定義 + + + + Duplicate define + + + + + Unknown element + 未知的元素 + + + + Unknown issue + 未知的議題 + + + + Failed to load the selected library '%1'. +%2 + 無法載入選取的程式庫 '%1'。 +%2 + + + + File not found: '%1' + + + + + Failed to load/setup addon %1: %2 + + + + + + + + Error + 錯誤 + + + + Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir=<directory> at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured. + +Analysis is aborted. + + + + + Failed to load %1 - %2 + +Analysis is aborted. + + + + + + %1 + +Analysis is aborted. + + + + + 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 + 開啟報告檔 + + + + Analyzer is running. + +Do you want to stop the analysis and exit Cppcheck? + 分析正在執行 + +您想停止分析並離開 Cppcheck 嗎? + + + + About + 關於 + + + + License + 授權 + + + + Authors + 作者 + + + + XML files (*.xml);;Text files (*.txt);;CSV files (*.csv) + XML 檔案 (*.xml);;文字檔 (*.txt);;CSV 檔案 (*.csv) + + + + Save the report file + 儲存報告檔 + + + + Text files (*.txt) + 文字檔 (*.txt) + + + + CSV files (*.csv) + CSV 檔案 (*.csv) + + + + Cannot generate a compliance report right now, an analysis must finish successfully. Try to reanalyze the code and ensure there are no critical errors. + + + + + Project files (*.cppcheck);;All files(*.*) + 專案檔 (*.cppcheck);;所有檔案 (*.*) + + + + Select Project File + 選取專案檔 + + + + Build dir '%1' does not exist, create it? + 建置目錄 '%1' 不存在,是否建立它? + + + + To check the project using addons, you need a build directory. + + + + + Failed to open file + 無法開啟檔案 + + + + Unknown project file format + 未知的專案檔格式 + + + + Failed to import project file + 無法匯入專案檔 + + + + Failed to import '%1': %2 + +Analysis is stopped. + 無法匯入 '%1': %2 + +停止分析。 + + + + Failed to import '%1' (%2), analysis is stopped + + + + Failed to import '%1', analysis is stopped + 無法匯入 '%1',停止分析 + + + + Project files (*.cppcheck) + 專案檔 (*.cppcheck) + + + + Select Project Filename + 選取專案檔案名稱 + + + + No project file loaded + + + + + The project file + +%1 + + could not be found! + +Do you want to remove the file from the recently used projects -list? + 專案檔 + +%1 + + 找不到! + +您要從最近使用的專案列表中移除該檔案嗎? + + + + Install + 安章 + + + + New version available: %1. %2 + 可用的新版本: %1. %2 + + + + NewSuppressionDialog + + + New suppression + 新建抑制 + + + + Error ID + 錯誤 ID + + + + File name + 檔案名稱 + + + + Line number + 行號 + + + + Symbol name + 符號名稱 + + + + Edit suppression + 編輯抑制 + + + + Platforms + + + Native + 原生 + + + + Unix 32-bit + Unix 32 位元 + + + + Unix 64-bit + Unix 64 位元 + + + + Windows 32-bit ANSI + Windows 32 位元 ANSI + + + + Windows 32-bit Unicode + Windows 32 位元 Unicode + + + + Windows 64-bit + Windows 64 位元 + + + + ProjectFile + + + Project File + 專案檔 + + + + Paths and Defines + 路徑與定義 + + + + Import Project (Visual studio / compile database/ Borland C++ Builder 6) + 匯入專案 (Visual Studio / 編譯資料庫 / Borland C++ Builder 6) + + + + + Browse... + 瀏覽... + + + + <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> + + + + + Analyze all Visual Studio configurations + 分析所有 Visual Studio 組態 + + + + Selected VS Configurations + 選取 VS 組態 + + + + Paths: + 路徑: + + + + + Add... + 新增... + + + + + + Edit + 編輯 + + + + + + + Remove + 移除 + + + + Defines: + 定義: + + + + Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int + + + + + Undefines: + 未定義: + + + + Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 + + + + + Include Paths: + 包含路徑: + + + + Up + + + + + Down + + + + + Types and Functions + 型別與函式 + + + + Platform + 平臺 + + + + Libraries + 程式庫 + + + + Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. + + + + + + Analysis + 分析 + + + + Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) + + + + + This is a workfolder that Cppcheck will use for various purposes. + + + + + Parser + 剖析器 + + + + Cppcheck (built in) + Cppcheck (內建) + + + + Clang (experimental) + + + + + Check level + + + + + Normal -- meant for normal analysis in CI. Analysis should finish in reasonable time. + + + + + Exhaustive -- meant for nightly builds etc. Analysis time can be longer (10x slower than compilation is OK). + + + + + If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. + + + + + Check that each class has a safe public interface + + + + + Limit analysis + + + + + Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) + + + + + Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) + + + + + Max CTU depth + + + + + Max recursion in template instantiation + + + + + Warning options + 警告選項 + + + + Root path: + 根路徑: + + + + Filepaths in warnings will be relative to this path + + + + + Warning tags (separated by semicolon) + 警告標記 (由分號分隔) + + + + If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. + + + + + Exclude source files + 排除來源檔 + + + + Exclude folder... + 排除資料夾... + + + + Exclude file... + 排除檔案... + + + + Suppressions + 抑制 + + + + Add + 新增 + + + + + Addons + + + + + Note: Addons require <a href="https://www.python.org/">Python</a> being installed. + + + + + Y2038 + Y2038 + + + + Thread safety + 執行緒安全 + + + + Coding standards + + + + + Misra C + + + + + 2012 + + + + + 2023 + + + + Misra C 2012 + Misra C 2012 + + + + MISRA rule texts + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + ... + ... + + + + Misra C++ 2008 + Misra C++ 2008 + + + + Cert C + + + + + CERT-INT35-C: int precision (if size equals precision, you can leave empty) + + + + + Cert C++ + + + + + Autosar + + + + + Bug hunting (Premium) + + + + + Bug hunting + + + + + External tools + 外部工具 + + + + Clang-tidy + Clang-tidy + + + + Clang analyzer + Clang 分析器 + + + + ProjectFileDialog + + + Project file: %1 + 專案檔: %1 + + + + Clang-tidy (not found) + Clang-tidy (找不到) + + + + Select Cppcheck build dir + 選取 Cppcheck 建置目錄 + + + + Visual Studio + Visual Studio + + + + Compile database + 編譯資料庫 + + + + Borland C++ Builder 6 + Borland C++ Builder 6 + + + + Import Project + 匯入專案 + + + + Select a directory to check + 選取要檢查的目錄 + + + + Select include directory + 選取包含目錄 + + + + Select directory to ignore + 選取要忽略的目錄 + + + + Source files + 來源檔 + + + + All files + 所有檔案 + + + + Exclude file + 排除檔案 + + + + Select MISRA rule texts file + 選取 MISRA 規則文字檔 + + + + MISRA rule texts file (%1) + MISRA 規則文字檔 (%1) + + + + QObject + + + Thin + + + + + ExtraLight + + + + + Light + + + + + Normal + + + + + Medium + + + + + DemiBold + + + + + Bold + + + + + ExtraBold + + + + + Black + + + + + Editor Foreground Color + 編輯器前景色 + + + + Editor Background Color + 編輯器背景色 + + + + Highlight Background Color + 標明背景色 + + + + Line Number Foreground Color + 行號前景色 + + + + Line Number Background Color + 行號背景色 + + + + Keyword Foreground Color + 關鍵字前景色 + + + + Keyword Font Weight + + + + + Class Foreground Color + 類別前景色 + + + + Class Font Weight + 類別字型粗細 + + + + Quote Foreground Color + + + + + Quote Font Weight + + + + + Comment Foreground Color + + + + + Comment Font Weight + + + + + Symbol Foreground Color + 符號前景色 + + + + Symbol Background Color + 符號被景色 + + + + Symbol Font Weight + 符號字型粗細 + + + + Set to Default Light + + + + + Set to Default Dark + + + + + line %1: Unhandled element %2 + + + + + line %1: Mandatory attribute '%2' missing in '%3' + + + + + (Not found) + (找不到) + + + + Unknown language specified! + 指定了未知語言! + + + + Language file %1 not found! + 找不到語言檔 %1! + + + + Failed to load translation for language %1 from file %2 + + + + + QPlatformTheme + + + OK + 確認 + + + + Cancel + 取消 + + + + Close + 關閉 + + + + Save + 儲存 + + + + ResultsTree + + + Undefined file + 未定義的檔案 + + + + note + + + + + style + 樣式 + + + + error + 錯誤 + + + + warning + 警告 + + + + performance + 效能 + + + + portability + + + + + information + 資訊 + + + + debug + + + + + internal + + + + + Recheck + + + + + Copy + 複製 + + + + Hide + 隱藏 + + + + Hide all with id + + + + + Open containing folder + + + + + Suppress selected id(s) + + + + + + Tag + 標記 + + + + No tag + 取消標記 + + + + + 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. + + + + + Could not find file: + + + + + Please select the folder '%1' + 請選取資料夾 '%1' + + + + Select Directory '%1' + 選取目錄 '%1' + + + + Please select the directory where file is located. + 請選取資料夾所在的目錄。 + + + + Select Directory + 選取目錄 + + + + File + 檔案 + + + + Severity + 安全性 + + + + Line + 行號 + + + + Id + 識別號 + + + + Inconclusive + + + + + Summary + + + + + Since date + + + + + ResultsView + + + Results + 結果 + + + + Critical errors + + + + + Analysis Log + 分析日誌 + + + + Warning Details + 警告詳細資訊 + + + + + Failed to save the report. + 無法載入報告。 + + + + Print Report + 列印報告 + + + + No errors found, nothing to print. + + + + + %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. + 無法讀取報告。 + + + + XML format version 1 is no longer supported. + 不再支援 XML 格式版本 1。 + + + + First included by + + + + + Id + 識別號 + + + + Bug hunting analysis is incomplete + + + + + Clear Log + 清除日誌 + + + + Copy this Log entry + 複製該日誌條目 + + + + Copy complete Log + 複製完整的日誌 + + + + Analysis was stopped + + + + + There was a critical error with id '%1' + + + + + when checking %1 + + + + + when checking a file + + + + + Analysis was aborted. + + + + + ScratchPad + + + Scratchpad + + + + + Copy or write some C/C++ code here: + + + + + Optionally enter a filename (mainly for automatic language detection) and click on "Check": + + + + + filename + 檔案名稱 + + + + Check + + + + + Settings + + + Preferences + 偏好設定 + + + + General + 一般 + + + + Number of threads: + 執行緒數量: + + + + Ideal count: + + + + + Force checking all #ifdef configurations + + + + + Show full path of files + 顯示檔案的完整路徑 + + + + Show "No errors found" message when no errors found + + + + + Display error Id in column "Id" + + + + + Enable inline suppressions + + + + + Check for inconclusive errors also + + + + + Show statistics on check completion + + + + + Check for updates + 檢查更新 + + + + Show internal warnings in log + 顯示日誌中的內部警告 + + + + Applications + 應用程式 + + + + Add... + 新增... + + + + + Edit... + 編輯... + + + + Remove + 移除 + + + + Set as default + 設定為預設值 + + + + Reports + 報告 + + + + Save all errors when creating report + + + + + Save full path to files in reports + 在報告中儲存檔案的完整路徑 + + + + Language + 語言 + + + + Addons + + + + + Python binary (leave this empty to use python in the PATH) + + + + + + + ... + ... + + + + MISRA addon + + + + + MISRA rule texts file + MISRA 規則文字檔 + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + Clang + Clang + + + + Clang path (leave empty to use system PATH) + + + + + Visual Studio headers + Visual Studio 標頭檔 + + + + <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> + + + + + Code Editor + 程式碼編輯器 + + + + Code Editor Style + 程式碼編輯器樣式 + + + + System Style + 系統樣式 + + + + Default Light Style + + + + + Default Dark Style + + + + + Custom + 自訂 + + + + SettingsDialog + + + N/A + + + + + The executable file "%1" is not available + + + + + Add a new application + 新增一個新應用程式 + + + + Modify an application + 修改一個應用程式 + + + + [Default] + + + + + [Default] + + + + + Select python binary + 選取 python 二進位檔 + + + + Select MISRA File + 選取 MISRA 檔案 + + + + Select clang path + 選取 clang 路徑 + + + + StatsDialog + + + + + + Statistics + 統計資料 + + + + + Project + 專案 + + + + Project: + 專案: + + + + Paths: + 路徑: + + + + Include paths: + 包含路徑: + + + + Defines: + 定義: + + + + Undefines: + 未定義: + + + + + Previous Scan + 上一次掃描 + + + + Path Selected: + 選取的路徑: + + + + Number of Files Scanned: + 已掃描的檔案數量: + + + + Scan Duration: + 掃描時間: + + + + Errors: + 錯誤: + + + + Warnings: + 警告: + + + + Stylistic warnings: + + + + + Portability warnings: + 可移植性警告: + + + + Performance issues: + 效能議題: + + + + Information messages: + 資訊訊息: + + + + Active checkers: + + + + + Checkers + + + + + History + 歷史紀錄 + + + + File: + 檔案: + + + + Copy to Clipboard + 複製到剪貼簿 + + + + Pdf Export + Pdf 匯出 + + + + File: + 檔案: + + + + No cppcheck build dir + 沒有 cppcheck 建置目錄 + + + + 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 + + + + + + Errors + 錯誤 + + + + + Warnings + 警告 + + + + + Style warnings + 樣式警告 + + + + + Portability warnings + 可移植性警告 + + + + + Performance warnings + 效能警告 + + + + + Information messages + 資訊訊息 + + + + Export PDF + 匯出 PDF + + + + Project Settings + 專案設定 + + + + Paths + 路徑 + + + + Include paths + 包含路徑 + + + + Defines + 定義 + + + + Undefines + 未定義 + + + + Path selected + 選取的路徑 + + + + Number of files scanned + 已掃描的檔案數量 + + + + Scan duration + 掃描時間 + + + + ThreadResult + + + %1 of %2 files checked + + + + + TranslationHandler + + + Failed to change the user interface language: + +%1 + +The user interface language has been reset to English. Open the Preferences-dialog to select any of the available languages. + + + + + Cppcheck + + + + + TxtReport + + + inconclusive + + + + + toFilterString + + + All supported files (%1) + 所有支援的檔案 (%1) + + + + All files (%1) + 所有檔案 (%1) + + + diff --git a/cppcheck-2.14.0/gui/cppchecklibrarydata.cpp b/cppcheck-2.14.0/gui/cppchecklibrarydata.cpp new file mode 100644 index 00000000..682e5441 --- /dev/null +++ b/cppcheck-2.14.0/gui/cppchecklibrarydata.cpp @@ -0,0 +1,944 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "cppchecklibrarydata.h" + +#include "utils.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#include +#endif + +const unsigned int CppcheckLibraryData::Function::Arg::ANY = ~0U; +const unsigned int CppcheckLibraryData::Function::Arg::VARIADIC = ~1U; + +static std::string unhandledElement(const QXmlStreamReader &xmlReader) +{ + throw std::runtime_error(QObject::tr("line %1: Unhandled element %2").arg(xmlReader.lineNumber()).arg(xmlReader.name().toString()).toStdString()); +} + +static std::string mandatoryAttibuteMissing(const QXmlStreamReader &xmlReader, const QString& attributeName) +{ + throw std::runtime_error(QObject::tr("line %1: Mandatory attribute '%2' missing in '%3'") + .arg(xmlReader.lineNumber()) + .arg(attributeName) + .arg(xmlReader.name().toString()).toStdString()); +} + +static CppcheckLibraryData::Container loadContainer(QXmlStreamReader &xmlReader) +{ + CppcheckLibraryData::Container container; + container.id = xmlReader.attributes().value("id").toString(); + container.inherits = xmlReader.attributes().value("inherits").toString(); + container.startPattern = xmlReader.attributes().value("startPattern").toString(); + container.endPattern = xmlReader.attributes().value("endPattern").toString(); + container.opLessAllowed = xmlReader.attributes().value("opLessAllowed").toString(); + container.itEndPattern = xmlReader.attributes().value("itEndPattern").toString(); + + QXmlStreamReader::TokenType type; + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "container") { + if (type != QXmlStreamReader::StartElement) + continue; + const QString elementName = xmlReader.name().toString(); + if (elementName == "type") { + container.type.templateParameter = xmlReader.attributes().value("templateParameter").toString(); + container.type.string = xmlReader.attributes().value("string").toString(); + } else if (elementName == "size" || elementName == "access" || elementName == "other" || elementName == "rangeItemRecordType") { + const QString indexOperator = xmlReader.attributes().value("indexOperator").toString(); + if (elementName == "access" && indexOperator == "array-like") + container.access_arrayLike = true; + const QString templateParameter = xmlReader.attributes().value("templateParameter").toString(); + if (elementName == "size" && !templateParameter.isEmpty()) + container.size_templateParameter = templateParameter.toInt(); + for (;;) { + type = xmlReader.readNext(); + if (xmlReader.name().toString() == elementName) + break; + if (type != QXmlStreamReader::StartElement) + continue; + CppcheckLibraryData::Container::Function function; + function.name = xmlReader.attributes().value("name").toString(); + function.action = xmlReader.attributes().value("action").toString(); + function.yields = xmlReader.attributes().value("yields").toString(); + if (elementName == "size") + container.sizeFunctions.append(function); + else if (elementName == "access") + container.accessFunctions.append(function); + else if (elementName == "rangeItemRecordType") { + CppcheckLibraryData::Container::RangeItemRecordType rangeItemRecordType; + rangeItemRecordType.name = xmlReader.attributes().value("name").toString(); + rangeItemRecordType.templateParameter = xmlReader.attributes().value("templateParameter").toString(); + container.rangeItemRecordTypeList.append(rangeItemRecordType); + } else + container.otherFunctions.append(function); + } + } else { + unhandledElement(xmlReader); + } + } + return container; +} + +static CppcheckLibraryData::Define loadDefine(const QXmlStreamReader &xmlReader) +{ + CppcheckLibraryData::Define define; + define.name = xmlReader.attributes().value("name").toString(); + define.value = xmlReader.attributes().value("value").toString(); + return define; +} + +static QString loadUndefine(const QXmlStreamReader &xmlReader) +{ + return xmlReader.attributes().value("name").toString(); +} + +static CppcheckLibraryData::SmartPointer loadSmartPointer(QXmlStreamReader &xmlReader) +{ + CppcheckLibraryData::SmartPointer smartPointer; + smartPointer.name = xmlReader.attributes().value("class-name").toString(); + QXmlStreamReader::TokenType type; + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "smart-pointer") { + if (type != QXmlStreamReader::StartElement) + continue; + const QString elementName = xmlReader.name().toString(); + if (elementName == "unique") { + smartPointer.unique = true; + } else { + unhandledElement(xmlReader); + } + } + return smartPointer; +} + +static CppcheckLibraryData::TypeChecks loadTypeChecks(QXmlStreamReader &xmlReader) +{ + CppcheckLibraryData::TypeChecks typeChecks; + QXmlStreamReader::TokenType type; + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "type-checks") { + if (type != QXmlStreamReader::StartElement) + continue; + const QString elementName = xmlReader.name().toString(); + if (elementName == "suppress" || elementName == "check") { + QPair entry(elementName, xmlReader.readElementText()); + typeChecks.append(entry); + } + } + return typeChecks; +} + +static CppcheckLibraryData::Function::Arg loadFunctionArg(QXmlStreamReader &xmlReader) +{ + CppcheckLibraryData::Function::Arg arg; + QString argnr = xmlReader.attributes().value("nr").toString(); + if (argnr == "any") + arg.nr = CppcheckLibraryData::Function::Arg::ANY; + else if (argnr == "variadic") + arg.nr = CppcheckLibraryData::Function::Arg::VARIADIC; + else + arg.nr = argnr.toUInt(); + arg.defaultValue = xmlReader.attributes().value("default").toString(); + + QXmlStreamReader::TokenType type; + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "arg") { + if (type != QXmlStreamReader::StartElement) + continue; + const QString elementName = xmlReader.name().toString(); + if (elementName == "not-bool") + arg.notbool = true; + else if (elementName == "not-null") + arg.notnull = true; + else if (elementName == "not-uninit") + arg.notuninit = true; + else if (elementName == "strz") + arg.strz = true; + else if (elementName == "formatstr") + arg.formatstr = true; + else if (elementName == "valid") + arg.valid = xmlReader.readElementText(); + else if (elementName == "minsize") { + CppcheckLibraryData::Function::Arg::MinSize minsize; + minsize.type = xmlReader.attributes().value("type").toString(); + minsize.arg = xmlReader.attributes().value("arg").toString(); + minsize.arg2 = xmlReader.attributes().value("arg2").toString(); + arg.minsizes.append(minsize); + } else if (elementName == "iterator") { + arg.iterator.container = xmlReader.attributes().value("container").toInt(); + arg.iterator.type = xmlReader.attributes().value("type").toString(); + } else { + unhandledElement(xmlReader); + } + } + return arg; +} + +static CppcheckLibraryData::Function loadFunction(QXmlStreamReader &xmlReader, const QString &comments) +{ + CppcheckLibraryData::Function function; + function.comments = comments; + function.name = xmlReader.attributes().value("name").toString(); + QXmlStreamReader::TokenType type; + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "function") { + if (type != QXmlStreamReader::StartElement) + continue; + const QString elementName = xmlReader.name().toString(); + if (elementName == "noreturn") + function.noreturn = (xmlReader.readElementText() == "true") ? CppcheckLibraryData::Function::True : CppcheckLibraryData::Function::False; + else if (elementName == "pure") + function.gccPure = true; + else if (elementName == "const") + function.gccConst = true; + else if (elementName == "leak-ignore") + function.leakignore = true; + else if (elementName == "use-retval") + function.useretval = true; + else if (elementName == "returnValue") { + const QString container = xmlReader.attributes().value("container").toString(); + function.returnValue.container = container.isNull() ? -1 : container.toInt(); + function.returnValue.type = xmlReader.attributes().value("type").toString(); + function.returnValue.value = xmlReader.readElementText(); + } else if (elementName == "formatstr") { + function.formatstr.scan = xmlReader.attributes().value("scan").toString(); + function.formatstr.secure = xmlReader.attributes().value("secure").toString(); + } else if (elementName == "arg") + function.args.append(loadFunctionArg(xmlReader)); + else if (elementName == "warn") { + function.warn.severity = xmlReader.attributes().value("severity").toString(); + function.warn.cstd = xmlReader.attributes().value("cstd").toString(); + function.warn.reason = xmlReader.attributes().value("reason").toString(); + function.warn.alternatives = xmlReader.attributes().value("alternatives").toString(); + function.warn.msg = xmlReader.readElementText(); + } else if (elementName == "not-overlapping-data") { + const QStringList attributeList {"ptr1-arg", "ptr2-arg", "size-arg", "strlen-arg"}; + for (const QString &attr : attributeList) { + if (xmlReader.attributes().hasAttribute(attr)) { + function.notOverlappingDataArgs[attr] = xmlReader.attributes().value(attr).toString(); + } + } + } else if (elementName == "container") { + const QStringList attributeList {"action", "yields"}; + for (const QString &attr : attributeList) { + if (xmlReader.attributes().hasAttribute(attr)) { + function.containerAttributes[attr] = xmlReader.attributes().value(attr).toString(); + } + } + } else { + unhandledElement(xmlReader); + } + } + return function; +} + +static CppcheckLibraryData::MemoryResource loadMemoryResource(QXmlStreamReader &xmlReader) +{ + CppcheckLibraryData::MemoryResource memoryresource; + memoryresource.type = xmlReader.name().toString(); + QXmlStreamReader::TokenType type; + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != memoryresource.type) { + if (type != QXmlStreamReader::StartElement) + continue; + const QString elementName = xmlReader.name().toString(); + if (elementName == "alloc" || elementName == "realloc") { + CppcheckLibraryData::MemoryResource::Alloc alloc; + alloc.isRealloc = (elementName == "realloc"); + alloc.init = (xmlReader.attributes().value("init").toString() == "true"); + if (xmlReader.attributes().hasAttribute("arg")) { + alloc.arg = xmlReader.attributes().value("arg").toInt(); + } + if (alloc.isRealloc && xmlReader.attributes().hasAttribute("realloc-arg")) { + alloc.reallocArg = xmlReader.attributes().value("realloc-arg").toInt(); + } + if (memoryresource.type == "memory") { + alloc.bufferSize = xmlReader.attributes().value("buffer-size").toString(); + } + alloc.name = xmlReader.readElementText(); + memoryresource.alloc.append(alloc); + } else if (elementName == "dealloc") { + CppcheckLibraryData::MemoryResource::Dealloc dealloc; + if (xmlReader.attributes().hasAttribute("arg")) { + dealloc.arg = xmlReader.attributes().value("arg").toInt(); + } + dealloc.name = xmlReader.readElementText(); + memoryresource.dealloc.append(dealloc); + } else if (elementName == "use") + memoryresource.use.append(xmlReader.readElementText()); + else + unhandledElement(xmlReader); + } + return memoryresource; +} + +static CppcheckLibraryData::PodType loadPodType(const QXmlStreamReader &xmlReader) +{ + CppcheckLibraryData::PodType podtype; + podtype.name = xmlReader.attributes().value("name").toString(); + if (podtype.name.isEmpty()) { + mandatoryAttibuteMissing(xmlReader, "name"); + } + podtype.stdtype = xmlReader.attributes().value("stdtype").toString(); + podtype.size = xmlReader.attributes().value("size").toString(); + podtype.sign = xmlReader.attributes().value("sign").toString(); + return podtype; +} + +static CppcheckLibraryData::PlatformType loadPlatformType(QXmlStreamReader &xmlReader) +{ + CppcheckLibraryData::PlatformType platformType; + platformType.name = xmlReader.attributes().value("name").toString(); + platformType.value = xmlReader.attributes().value("value").toString(); + + QXmlStreamReader::TokenType type; + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "platformtype") { + if (type != QXmlStreamReader::StartElement) + continue; + const QString elementName = xmlReader.name().toString(); + if (QStringList({"unsigned", "long", "pointer", "const_ptr", "ptr_ptr"}).contains(elementName)) { + platformType.types.append(elementName); + } else if (elementName == "platform") { + platformType.platforms.append(xmlReader.attributes().value("type").toString()); + } else { + unhandledElement(xmlReader); + } + } + return platformType; +} + +static CppcheckLibraryData::Reflection loadReflection(QXmlStreamReader &xmlReader) +{ + CppcheckLibraryData::Reflection reflection; + + QXmlStreamReader::TokenType type; + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "reflection") { + if (type != QXmlStreamReader::StartElement) + continue; + const QString elementName = xmlReader.name().toString(); + if (elementName == "call") { + CppcheckLibraryData::Reflection::Call call; + if (xmlReader.attributes().hasAttribute("arg")) { + call.arg = xmlReader.attributes().value("arg").toInt(); + } else { + mandatoryAttibuteMissing(xmlReader, "arg"); + } + call.name = xmlReader.readElementText(); + reflection.calls.append(call); + } else { + unhandledElement(xmlReader); + } + } + + return reflection; +} + +static CppcheckLibraryData::Markup loadMarkup(QXmlStreamReader &xmlReader) +{ + CppcheckLibraryData::Markup markup; + + QXmlStreamReader::TokenType type; + if (xmlReader.attributes().hasAttribute("ext")) { + markup.ext = xmlReader.attributes().value("ext").toString(); + } else { + mandatoryAttibuteMissing(xmlReader, "ext"); + } + if (xmlReader.attributes().hasAttribute("aftercode")) { + markup.afterCode = (xmlReader.attributes().value("aftercode") == QString("true")); + } else { + mandatoryAttibuteMissing(xmlReader, "aftercode"); + } + if (xmlReader.attributes().hasAttribute("reporterrors")) { + markup.reportErrors = (xmlReader.attributes().value("reporterrors") == QString("true")); + } else { + mandatoryAttibuteMissing(xmlReader, "reporterrors"); + } + + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "markup") { + if (type != QXmlStreamReader::StartElement) + continue; + const QString elementName = xmlReader.name().toString(); + if (elementName == "keywords") { + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "keywords") { + if (type != QXmlStreamReader::StartElement) + continue; + if (xmlReader.name().toString() == "keyword") { + markup.keywords.append(xmlReader.attributes().value("name").toString()); + } else { + unhandledElement(xmlReader); + } + } + } else if (elementName == "codeblocks") { + CppcheckLibraryData::Markup::CodeBlocks codeBlock; + + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "codeblocks") { + if (type != QXmlStreamReader::StartElement) + continue; + if (xmlReader.name().toString() == "block") { + codeBlock.blocks.append(xmlReader.attributes().value("name").toString()); + } else if (xmlReader.name().toString() == "structure") { + codeBlock.offset = xmlReader.attributes().value("offset").toInt(); + codeBlock.start = xmlReader.attributes().value("start").toString(); + codeBlock.end = xmlReader.attributes().value("end").toString(); + } else { + unhandledElement(xmlReader); + } + } + markup.codeBlocks.append(codeBlock); + } else if (elementName == "exported") { + CppcheckLibraryData::Markup::Exporter exporter; + + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "exported") { + if (type != QXmlStreamReader::StartElement) + continue; + if (xmlReader.name().toString() == "exporter") { + exporter.prefix = xmlReader.attributes().value("prefix").toString(); + } else if (xmlReader.name().toString() == "prefix") { + exporter.prefixList.append(xmlReader.readElementText()); + } else if (xmlReader.name().toString() == "suffix") { + exporter.suffixList.append(xmlReader.readElementText()); + } else { + unhandledElement(xmlReader); + } + } + markup.exporter.append(exporter); + } else if (elementName == "imported") { + while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement || + xmlReader.name().toString() != "imported") { + if (type != QXmlStreamReader::StartElement) + continue; + if (xmlReader.name().toString() == "importer") { + markup.importer.append(xmlReader.readElementText()); + } else { + unhandledElement(xmlReader); + } + } + } else { + unhandledElement(xmlReader); + } + } + + return markup; +} + +static CppcheckLibraryData::Entrypoint loadEntrypoint(const QXmlStreamReader &xmlReader) +{ + CppcheckLibraryData::Entrypoint entrypoint; + entrypoint.name = xmlReader.attributes().value("name").toString(); + return entrypoint; +} + +QString CppcheckLibraryData::open(QIODevice &file) +{ + clear(); + QString comments; + QXmlStreamReader xmlReader(&file); + while (!xmlReader.atEnd()) { + const QXmlStreamReader::TokenType t = xmlReader.readNext(); + switch (t) { + case QXmlStreamReader::Comment: + if (!comments.isEmpty()) + comments += "\n"; + comments += xmlReader.text().toString(); + break; + case QXmlStreamReader::StartElement: + try { + const QString elementName(xmlReader.name().toString()); + if (elementName == "def") + ; + else if (elementName == "container") + containers.append(loadContainer(xmlReader)); + else if (elementName == "define") + defines.append(loadDefine(xmlReader)); + else if (elementName == "undefine") + undefines.append(loadUndefine(xmlReader)); + else if (elementName == "function") + functions.append(loadFunction(xmlReader, comments)); + else if (elementName == "memory" || elementName == "resource") + memoryresource.append(loadMemoryResource(xmlReader)); + else if (elementName == "podtype") + podtypes.append(loadPodType(xmlReader)); + else if (elementName == "smart-pointer") + smartPointers.append(loadSmartPointer(xmlReader)); + else if (elementName == "type-checks") + typeChecks.append(loadTypeChecks(xmlReader)); + else if (elementName == "platformtype") + platformTypes.append(loadPlatformType(xmlReader)); + else if (elementName == "reflection") + reflections.append(loadReflection(xmlReader)); + else if (elementName == "markup") + markups.append(loadMarkup(xmlReader)); + else if (elementName == "entrypoint") + entrypoints.append(loadEntrypoint(xmlReader)); + else + unhandledElement(xmlReader); + } catch (std::runtime_error &e) { + return e.what(); + } + comments.clear(); + break; + default: + break; + } + } + if (xmlReader.hasError()) + return xmlReader.errorString(); + return QString(); +} + +static void writeContainerFunctions(QXmlStreamWriter &xmlWriter, const QString &name, int extra, const QList &functions) +{ + if (functions.isEmpty() && extra < 0) + return; + xmlWriter.writeStartElement(name); + if (extra >= 0) { + if (name == "access") + xmlWriter.writeAttribute("indexOperator", "array-like"); + else if (name == "size") + xmlWriter.writeAttribute("templateParameter", QString::number(extra)); + } + for (const CppcheckLibraryData::Container::Function &function : functions) { + xmlWriter.writeStartElement("function"); + xmlWriter.writeAttribute("name", function.name); + if (!function.action.isEmpty()) + xmlWriter.writeAttribute("action", function.action); + if (!function.yields.isEmpty()) + xmlWriter.writeAttribute("yields", function.yields); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); +} + +static void writeContainerRangeItemRecords(QXmlStreamWriter &xmlWriter, const QList &rangeItemRecords) +{ + if (rangeItemRecords.isEmpty()) + return; + xmlWriter.writeStartElement("rangeItemRecordType"); + for (const CppcheckLibraryData::Container::RangeItemRecordType &item : rangeItemRecords) { + xmlWriter.writeStartElement("member"); + xmlWriter.writeAttribute("name", item.name); + xmlWriter.writeAttribute("templateParameter", item.templateParameter); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); +} + +static void writeContainer(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::Container &container) +{ + xmlWriter.writeStartElement("container"); + xmlWriter.writeAttribute("id", container.id); + if (!container.startPattern.isEmpty()) + xmlWriter.writeAttribute("startPattern", container.startPattern); + if (!container.endPattern.isNull()) + xmlWriter.writeAttribute("endPattern", container.endPattern); + if (!container.inherits.isEmpty()) + xmlWriter.writeAttribute("inherits", container.inherits); + if (!container.opLessAllowed.isEmpty()) + xmlWriter.writeAttribute("opLessAllowed", container.opLessAllowed); + if (!container.itEndPattern.isEmpty()) + xmlWriter.writeAttribute("itEndPattern", container.itEndPattern); + + if (!container.type.templateParameter.isEmpty() || !container.type.string.isEmpty()) { + xmlWriter.writeStartElement("type"); + if (!container.type.templateParameter.isEmpty()) + xmlWriter.writeAttribute("templateParameter", container.type.templateParameter); + if (!container.type.string.isEmpty()) + xmlWriter.writeAttribute("string", container.type.string); + xmlWriter.writeEndElement(); + } + writeContainerFunctions(xmlWriter, "size", container.size_templateParameter, container.sizeFunctions); + writeContainerFunctions(xmlWriter, "access", container.access_arrayLike?1:-1, container.accessFunctions); + writeContainerFunctions(xmlWriter, "other", -1, container.otherFunctions); + writeContainerRangeItemRecords(xmlWriter, container.rangeItemRecordTypeList); + xmlWriter.writeEndElement(); +} + +static void writeFunction(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::Function &function) +{ + QString comments = function.comments; + while (comments.startsWith("\n")) + comments = comments.mid(1); + while (comments.endsWith("\n")) + comments.chop(1); + for (const QString &comment : comments.split('\n')) { + if (comment.length() >= 1) + xmlWriter.writeComment(comment); + } + + xmlWriter.writeStartElement("function"); + xmlWriter.writeAttribute("name", function.name); + + if (function.useretval) + xmlWriter.writeEmptyElement("use-retval"); + if (function.gccConst) + xmlWriter.writeEmptyElement("const"); + if (function.gccPure) + xmlWriter.writeEmptyElement("pure"); + if (!function.returnValue.empty()) { + xmlWriter.writeStartElement("returnValue"); + if (!function.returnValue.type.isNull()) + xmlWriter.writeAttribute("type", function.returnValue.type); + if (function.returnValue.container >= 0) + xmlWriter.writeAttribute("container", QString::number(function.returnValue.container)); + if (!function.returnValue.value.isNull()) + xmlWriter.writeCharacters(function.returnValue.value); + xmlWriter.writeEndElement(); + } + if (function.noreturn != CppcheckLibraryData::Function::Unknown) + xmlWriter.writeTextElement("noreturn", bool_to_string(function.noreturn == CppcheckLibraryData::Function::True)); + if (function.leakignore) + xmlWriter.writeEmptyElement("leak-ignore"); + // Argument info.. + for (const CppcheckLibraryData::Function::Arg &arg : function.args) { + if (arg.formatstr) { + xmlWriter.writeStartElement("formatstr"); + if (!function.formatstr.scan.isNull()) + xmlWriter.writeAttribute("scan", function.formatstr.scan); + if (!function.formatstr.secure.isNull()) + xmlWriter.writeAttribute("secure", function.formatstr.secure); + xmlWriter.writeEndElement(); + } + + xmlWriter.writeStartElement("arg"); + if (arg.nr == CppcheckLibraryData::Function::Arg::ANY) + xmlWriter.writeAttribute("nr", "any"); + else if (arg.nr == CppcheckLibraryData::Function::Arg::VARIADIC) + xmlWriter.writeAttribute("nr", "variadic"); + else + xmlWriter.writeAttribute("nr", QString::number(arg.nr)); + if (!arg.defaultValue.isNull()) + xmlWriter.writeAttribute("default", arg.defaultValue); + if (arg.formatstr) + xmlWriter.writeEmptyElement("formatstr"); + if (arg.notnull) + xmlWriter.writeEmptyElement("not-null"); + if (arg.notuninit) + xmlWriter.writeEmptyElement("not-uninit"); + if (arg.notbool) + xmlWriter.writeEmptyElement("not-bool"); + if (arg.strz) + xmlWriter.writeEmptyElement("strz"); + + if (!arg.valid.isEmpty()) + xmlWriter.writeTextElement("valid",arg.valid); + + for (const CppcheckLibraryData::Function::Arg::MinSize &minsize : arg.minsizes) { + xmlWriter.writeStartElement("minsize"); + xmlWriter.writeAttribute("type", minsize.type); + xmlWriter.writeAttribute("arg", minsize.arg); + if (!minsize.arg2.isEmpty()) + xmlWriter.writeAttribute("arg2", minsize.arg2); + xmlWriter.writeEndElement(); + } + + if (arg.iterator.container >= 0 || !arg.iterator.type.isNull()) { + xmlWriter.writeStartElement("iterator"); + if (arg.iterator.container >= 0) + xmlWriter.writeAttribute("container", QString::number(arg.iterator.container)); + if (!arg.iterator.type.isNull()) + xmlWriter.writeAttribute("type", arg.iterator.type); + xmlWriter.writeEndElement(); + } + + xmlWriter.writeEndElement(); + } + + if (!function.warn.isEmpty()) { + xmlWriter.writeStartElement("warn"); + + if (!function.warn.severity.isEmpty()) + xmlWriter.writeAttribute("severity", function.warn.severity); + + if (!function.warn.cstd.isEmpty()) + xmlWriter.writeAttribute("cstd", function.warn.cstd); + + if (!function.warn.alternatives.isEmpty()) + xmlWriter.writeAttribute("alternatives", function.warn.alternatives); + + if (!function.warn.reason.isEmpty()) + xmlWriter.writeAttribute("reason", function.warn.reason); + + if (!function.warn.msg.isEmpty()) + xmlWriter.writeCharacters(function.warn.msg); + + xmlWriter.writeEndElement(); + } + if (!function.notOverlappingDataArgs.isEmpty()) { + xmlWriter.writeStartElement("not-overlapping-data"); + foreach (const QString& value, function.notOverlappingDataArgs) { + xmlWriter.writeAttribute(function.notOverlappingDataArgs.key(value), value); + } + xmlWriter.writeEndElement(); + } + if (!function.containerAttributes.isEmpty()) { + xmlWriter.writeStartElement("container"); + foreach (const QString& value, function.containerAttributes) { + xmlWriter.writeAttribute(function.containerAttributes.key(value), value); + } + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); +} + +static void writeMemoryResource(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::MemoryResource &mr) +{ + xmlWriter.writeStartElement(mr.type); + for (const CppcheckLibraryData::MemoryResource::Alloc &alloc : mr.alloc) { + if (alloc.isRealloc) { + xmlWriter.writeStartElement("realloc"); + } else { + xmlWriter.writeStartElement("alloc"); + } + xmlWriter.writeAttribute("init", bool_to_string(alloc.init)); + if (alloc.arg != -1) { + xmlWriter.writeAttribute("arg", QString("%1").arg(alloc.arg)); + } + if (alloc.isRealloc && alloc.reallocArg != -1) { + xmlWriter.writeAttribute("realloc-arg", QString("%1").arg(alloc.reallocArg)); + } + if (mr.type == "memory" && !alloc.bufferSize.isEmpty()) { + xmlWriter.writeAttribute("buffer-size", alloc.bufferSize); + } + xmlWriter.writeCharacters(alloc.name); + xmlWriter.writeEndElement(); + } + + for (const CppcheckLibraryData::MemoryResource::Dealloc &dealloc : mr.dealloc) { + xmlWriter.writeStartElement("dealloc"); + if (dealloc.arg != -1) { + xmlWriter.writeAttribute("arg", QString("%1").arg(dealloc.arg)); + } + xmlWriter.writeCharacters(dealloc.name); + xmlWriter.writeEndElement(); + } + + for (const QString &use : mr.use) { + xmlWriter.writeTextElement("use", use); + } + xmlWriter.writeEndElement(); +} + +static void writeTypeChecks(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::TypeChecks &typeChecks) +{ + xmlWriter.writeStartElement("type-checks"); + if (!typeChecks.isEmpty()) { + xmlWriter.writeStartElement("unusedvar"); + } + for (const QPair &check : typeChecks) { + xmlWriter.writeStartElement(check.first); + xmlWriter.writeCharacters(check.second); + xmlWriter.writeEndElement(); + } + if (!typeChecks.isEmpty()) { + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); +} + +static void writePlatformType(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::PlatformType &pt) +{ + xmlWriter.writeStartElement("platformtype"); + xmlWriter.writeAttribute("name", pt.name); + xmlWriter.writeAttribute("value", pt.value); + for (const QString &type : pt.types) { + xmlWriter.writeStartElement(type); + xmlWriter.writeEndElement(); + } + for (const QString &platform : pt.platforms) { + xmlWriter.writeStartElement("platform"); + if (!platform.isEmpty()) { + xmlWriter.writeAttribute("type", platform); + } + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); +} + +static void writeReflection(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::Reflection &refl) +{ + xmlWriter.writeStartElement("reflection"); + for (const CppcheckLibraryData::Reflection::Call &call : refl.calls) { + xmlWriter.writeStartElement("call"); + xmlWriter.writeAttribute("arg", QString("%1").arg(call.arg)); + xmlWriter.writeCharacters(call.name); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); +} + +static void writeMarkup(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::Markup &mup) +{ + xmlWriter.writeStartElement("markup"); + xmlWriter.writeAttribute("ext", mup.ext); + xmlWriter.writeAttribute("aftercode", QVariant(mup.afterCode).toString()); + xmlWriter.writeAttribute("reporterrors", QVariant(mup.reportErrors).toString()); + if (!mup.keywords.isEmpty()) { + xmlWriter.writeStartElement("keywords"); + for (const QString &keyword : mup.keywords) { + xmlWriter.writeStartElement("keyword"); + xmlWriter.writeAttribute("name", keyword); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); + } + if (!mup.importer.isEmpty()) { + xmlWriter.writeStartElement("imported"); + for (const QString &import : mup.importer) { + xmlWriter.writeStartElement("importer"); + xmlWriter.writeCharacters(import); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); + } + if (!mup.exporter.isEmpty()) { + xmlWriter.writeStartElement("exported"); + for (const CppcheckLibraryData::Markup::Exporter &exporter : mup.exporter) { + xmlWriter.writeStartElement("exporter"); + xmlWriter.writeAttribute("prefix", exporter.prefix); + for (const QString &prefix : exporter.prefixList) { + xmlWriter.writeStartElement("prefix"); + xmlWriter.writeCharacters(prefix); + xmlWriter.writeEndElement(); + } + for (const QString &suffix : exporter.suffixList) { + xmlWriter.writeStartElement("suffix"); + xmlWriter.writeCharacters(suffix); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); + } + if (!mup.codeBlocks.isEmpty()) { + for (const CppcheckLibraryData::Markup::CodeBlocks &codeblock : mup.codeBlocks) { + xmlWriter.writeStartElement("codeblocks"); + for (const QString &block : codeblock.blocks) { + xmlWriter.writeStartElement("block"); + xmlWriter.writeAttribute("name", block); + xmlWriter.writeEndElement(); + } + xmlWriter.writeStartElement("structure"); + xmlWriter.writeAttribute("offset", QString("%1").arg(codeblock.offset)); + xmlWriter.writeAttribute("start", codeblock.start); + xmlWriter.writeAttribute("end", codeblock.end); + xmlWriter.writeEndElement(); + xmlWriter.writeEndElement(); + } + } + xmlWriter.writeEndElement(); +} + +QString CppcheckLibraryData::toString() const +{ + QString outputString; + QXmlStreamWriter xmlWriter(&outputString); + xmlWriter.setAutoFormatting(true); + xmlWriter.setAutoFormattingIndent(2); + xmlWriter.writeStartDocument("1.0"); + xmlWriter.writeStartElement("def"); + xmlWriter.writeAttribute("format","2"); + + for (const Define &define : defines) { + xmlWriter.writeStartElement("define"); + xmlWriter.writeAttribute("name", define.name); + xmlWriter.writeAttribute("value", define.value); + xmlWriter.writeEndElement(); + } + + for (const QString &undef : undefines) { + xmlWriter.writeStartElement("undefine"); + xmlWriter.writeAttribute("name", undef); + xmlWriter.writeEndElement(); + } + + for (const Function &function : functions) { + writeFunction(xmlWriter, function); + } + + for (const MemoryResource &mr : memoryresource) { + writeMemoryResource(xmlWriter, mr); + } + + for (const Container &container : containers) { + writeContainer(xmlWriter, container); + } + + for (const PodType &podtype : podtypes) { + xmlWriter.writeStartElement("podtype"); + xmlWriter.writeAttribute("name", podtype.name); + if (!podtype.stdtype.isEmpty()) + xmlWriter.writeAttribute("stdtype", podtype.stdtype); + if (!podtype.sign.isEmpty()) + xmlWriter.writeAttribute("sign", podtype.sign); + if (!podtype.size.isEmpty()) + xmlWriter.writeAttribute("size", podtype.size); + xmlWriter.writeEndElement(); + } + + for (const TypeChecks &check : typeChecks) { + writeTypeChecks(xmlWriter, check); + } + + for (const SmartPointer &smartPtr : smartPointers) { + xmlWriter.writeStartElement("smart-pointer"); + xmlWriter.writeAttribute("class-name", smartPtr.name); + if (smartPtr.unique) { + xmlWriter.writeEmptyElement("unique"); + } + xmlWriter.writeEndElement(); + } + + for (const PlatformType &pt : platformTypes) { + writePlatformType(xmlWriter, pt); + } + + for (const Reflection &refl : reflections) { + writeReflection(xmlWriter, refl); + } + + for (const Markup &mup : markups) { + writeMarkup(xmlWriter, mup); + } + + for (const Entrypoint &ent : entrypoints) { + xmlWriter.writeStartElement("entrypoint"); + xmlWriter.writeAttribute("name", ent.name); + xmlWriter.writeEndElement(); + } + + xmlWriter.writeEndElement(); + + return outputString; +} diff --git a/cppcheck-2.14.0/gui/cppchecklibrarydata.h b/cppcheck-2.14.0/gui/cppchecklibrarydata.h new file mode 100644 index 00000000..2ccf9d93 --- /dev/null +++ b/cppcheck-2.14.0/gui/cppchecklibrarydata.h @@ -0,0 +1,261 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef CPPCHECKLIBRARYDATA_H +#define CPPCHECKLIBRARYDATA_H + +#include +#include +#include +#include +#include +#include + +class QIODevice; + +class CppcheckLibraryData { +public: + CppcheckLibraryData() = default; + + struct Container { + QString id; + QString inherits; + QString startPattern; + QString endPattern; + QString opLessAllowed; + QString itEndPattern; + + bool access_arrayLike{}; + int size_templateParameter = -1; + + struct { + QString templateParameter; + QString string; + } type; + + struct RangeItemRecordType { + QString name; + QString templateParameter; + }; + + struct Function { + QString name; + QString yields; + QString action; + }; + QList accessFunctions; + QList otherFunctions; + QList sizeFunctions; + QList rangeItemRecordTypeList; + }; + + struct Define { + QString name; + QString value; + }; + + struct Function { + QString comments; + QString name; + enum TrueFalseUnknown { False, True, Unknown } noreturn = Unknown; + bool gccPure{}; + bool gccConst{}; + bool leakignore{}; + bool useretval{}; + struct ReturnValue { + QString type; + QString value; + int container = -1; + bool empty() const { + return type.isNull() && value.isNull() && container < 0; + } + } returnValue; + struct { + QString scan; + QString secure; + } formatstr; + struct Arg { + QString name; + unsigned int nr{}; + static const unsigned int ANY; + static const unsigned int VARIADIC; + QString defaultValue; + bool notbool{}; + bool notnull{}; + bool notuninit{}; + bool formatstr{}; + bool strz{}; + QString valid; + struct MinSize { + QString type; + QString arg; + QString arg2; + }; + QList minsizes; + struct Iterator { + int container = -1; + QString type; + } iterator; + }; + QList args; + + struct { + QString severity; + QString cstd; + QString reason; + QString alternatives; + QString msg; + + bool isEmpty() const { + return cstd.isEmpty() && + severity.isEmpty() && + reason.isEmpty() && + alternatives.isEmpty() && + msg.isEmpty(); + } + } warn; + + QMap notOverlappingDataArgs; + QMap containerAttributes; + }; + + struct MemoryResource { + QString type; // "memory" or "resource" + struct Alloc { + bool isRealloc{}; + bool init{}; + int arg = -1; // -1: Has no optional "realloc-arg" attribute + int reallocArg = -1; // -1: Has no optional "arg" attribute + QString bufferSize; + QString name; + }; + struct Dealloc { + int arg = -1; // -1: Has no optional "arg" attribute + QString name; + }; + + QList alloc; + QList dealloc; + QStringList use; + }; + + struct PodType { + QString name; + QString stdtype; + QString size; + QString sign; + }; + + struct PlatformType { + QString name; + QString value; + QStringList types; // Keeps element names w/o attribute (e.g. unsigned) + QStringList platforms; // Keeps "type" attribute of each "platform" element + }; + + using TypeChecks = QList>; + + struct Reflection { + struct Call { + int arg = -1; // -1: Mandatory "arg" attribute not available + QString name; + }; + + QList calls; + }; + + struct Markup { + struct CodeBlocks { + QStringList blocks; + int offset = -1; + QString start; + QString end; + }; + + struct Exporter { + QString prefix; + QStringList prefixList; + QStringList suffixList; + }; + + QString ext; + bool afterCode{}; + bool reportErrors{}; + QStringList keywords; + QStringList importer; + QList codeBlocks; + QList exporter; + }; + + struct SmartPointer { + QString name; + bool unique{}; + }; + + struct Entrypoint { + QString name; + }; + + void clear() { + containers.clear(); + defines.clear(); + undefines.clear(); + functions.clear(); + memoryresource.clear(); + podtypes.clear(); + smartPointers.clear(); + typeChecks.clear(); + platformTypes.clear(); + reflections.clear(); + markups.clear(); + entrypoints.clear(); + } + + void swap(CppcheckLibraryData &other) { + containers.swap(other.containers); + defines.swap(other.defines); + undefines.swap(other.undefines); + functions.swap(other.functions); + memoryresource.swap(other.memoryresource); + podtypes.swap(other.podtypes); + smartPointers.swap(other.smartPointers); + typeChecks.swap(other.typeChecks); + platformTypes.swap(other.platformTypes); + reflections.swap(other.reflections); + markups.swap(other.markups); + entrypoints.swap(other.entrypoints); + } + + QString open(QIODevice &file); + QString toString() const; + + QList containers; + QList defines; + QList functions; + QList memoryresource; + QList podtypes; + QList typeChecks; + QList platformTypes; + QStringList undefines; + QList smartPointers; + QList reflections; + QList markups; + QList entrypoints; +}; + +#endif // CPPCHECKLIBRARYDATA_H diff --git a/cppcheck-2.14.0/gui/csvreport.cpp b/cppcheck-2.14.0/gui/csvreport.cpp new file mode 100644 index 00000000..26e98300 --- /dev/null +++ b/cppcheck-2.14.0/gui/csvreport.cpp @@ -0,0 +1,72 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "csvreport.h" + +#include "erroritem.h" +#include "report.h" + +#include +#include +#include +#include + +CsvReport::CsvReport(const QString &filename) : + Report(filename) +{} + +bool CsvReport::create() +{ + if (Report::create()) { + mTxtWriter.setDevice(Report::getFile()); + return true; + } + return false; +} + +void CsvReport::writeHeader() +{ + // Added 5 columns to the header. +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + mTxtWriter << "File, Line, Severity, Id, Summary" << Qt::endl; +#else + mTxtWriter << "File, Line, Severity, Id, Summary" << endl; +#endif +} + +void CsvReport::writeFooter() +{ + // No footer for CSV report +} + +void CsvReport::writeError(const ErrorItem &error) +{ + /* + Error as CSV line + gui/test.cpp,23,error,Mismatching allocation and deallocation: k + */ + + const QString file = QDir::toNativeSeparators(error.errorPath.back().file); + QString line = QString("%1,%2,").arg(file).arg(error.errorPath.back().line); + line += QString("%1,%2,%3").arg(GuiSeverity::toString(error.severity)).arg(error.errorId).arg(error.summary); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + mTxtWriter << line << Qt::endl; +#else + mTxtWriter << line << endl; +#endif +} diff --git a/cppcheck-2.14.0/gui/csvreport.h b/cppcheck-2.14.0/gui/csvreport.h new file mode 100644 index 00000000..b1bed284 --- /dev/null +++ b/cppcheck-2.14.0/gui/csvreport.h @@ -0,0 +1,73 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "report.h" + +#include +#include + +class ErrorItem; + +/// @addtogroup GUI +/// @{ + + +/** + * @brief CSV text file report. + * This report exports results as CSV (comma separated values). CSV files are + * easy to import to many other programs. + * @todo This class should be inherited from TxtReport? + */ +class CsvReport : public Report { +public: + explicit CsvReport(const QString &filename); + + /** + * @brief Create the report (file). + * @return true if succeeded, false if file could not be created. + */ + bool create() override; + + /** + * @brief Write report header. + */ + void writeHeader() override; + + /** + * @brief Write report footer. + */ + void writeFooter() override; + + /** + * @brief Write error to report. + * @param error Error data. + */ + void writeError(const ErrorItem &error) override; + +private: + + /** + * @brief Text stream writer for writing the report in text format. + */ + QTextStream mTxtWriter; +}; +/// @} +#endif // CSV_REPORT_H diff --git a/cppcheck-2.14.0/gui/erroritem.cpp b/cppcheck-2.14.0/gui/erroritem.cpp new file mode 100644 index 00000000..ea6ed795 --- /dev/null +++ b/cppcheck-2.14.0/gui/erroritem.cpp @@ -0,0 +1,99 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "erroritem.h" + +#include "common.h" + +#include + +QErrorPathItem::QErrorPathItem(const ErrorMessage::FileLocation &loc) + : file(QString::fromStdString(loc.getfile(false))) + , line(loc.line) + , column(loc.column) + , info(QString::fromStdString(loc.getinfo())) +{} + +bool operator==(const QErrorPathItem &i1, const QErrorPathItem &i2) +{ + return i1.file == i2.file && i1.column == i2.column && i1.line == i2.line && i1.info == i2.info; +} + +ErrorItem::ErrorItem() + : severity(Severity::none) + , inconclusive(false) + , cwe(-1) + , hash(0) +{} + +ErrorItem::ErrorItem(const ErrorMessage &errmsg) + : file0(QString::fromStdString(errmsg.file0)) + , errorId(QString::fromStdString(errmsg.id)) + , severity(errmsg.severity) + , inconclusive(errmsg.certainty == Certainty::inconclusive) + , summary(QString::fromStdString(errmsg.shortMessage())) + , message(QString::fromStdString(errmsg.verboseMessage())) + , cwe(errmsg.cwe.id) + , hash(errmsg.hash) + , symbolNames(QString::fromStdString(errmsg.symbolNames())) +{ + for (std::list::const_iterator loc = errmsg.callStack.cbegin(); + loc != errmsg.callStack.cend(); + ++loc) { + errorPath << QErrorPathItem(*loc); + } +} + +QString ErrorItem::tool() const +{ + if (errorId == CLANG_ANALYZER) + return CLANG_ANALYZER; + if (errorId.startsWith(CLANG_TIDY)) + return CLANG_TIDY; + if (errorId.startsWith("clang-")) + return "clang"; + return "cppcheck"; +} + +QString ErrorItem::toString() const +{ + QString str = errorPath.back().file + " - " + errorId + " - "; + if (inconclusive) + str += "inconclusive "; + str += GuiSeverity::toString(severity) +"\n"; + str += summary + "\n"; + str += message + "\n"; + for (const QErrorPathItem& i : errorPath) { + str += " " + i.file + ": " + QString::number(i.line) + "\n"; + } + return str; +} + +bool ErrorItem::sameCID(const ErrorItem &errorItem1, const ErrorItem &errorItem2) +{ + if (errorItem1.hash || errorItem2.hash) + return errorItem1.hash == errorItem2.hash; + + // fallback + return errorItem1.errorId == errorItem2.errorId && + errorItem1.errorPath == errorItem2.errorPath && + errorItem1.file0 == errorItem2.file0 && + errorItem1.message == errorItem2.message && + errorItem1.inconclusive == errorItem2.inconclusive && + errorItem1.severity == errorItem2.severity; +} diff --git a/cppcheck-2.14.0/gui/erroritem.h b/cppcheck-2.14.0/gui/erroritem.h new file mode 100644 index 00000000..507d7286 --- /dev/null +++ b/cppcheck-2.14.0/gui/erroritem.h @@ -0,0 +1,128 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "errorlogger.h" +#include "errortypes.h" + +#include +#include +#include + +/// @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 severity) { + return QString::fromStdString(severityToString(severity)); + } + + static Severity fromString(const QString &severity) { + return severityFromString(severity.toStdString()); + } +}; + +/** + * @brief A class containing data for one error path item + */ +class QErrorPathItem { +public: + QErrorPathItem() : line(0), column(-1) {} + explicit QErrorPathItem(const ErrorMessage::FileLocation &loc); + QString file; + int line; + int column; + QString info; +}; + +bool operator==(const QErrorPathItem &i1, const QErrorPathItem &i2); + +/** + * @brief A class containing error data for one error. + * + * The paths are stored with internal ("/") separators. Only when we show the + * path or copy if for user (to clipboard) we convert to native separators. + * Full path is stored instead of relative path for flexibility. It is easy + * to get the relative path from full path when needed. + */ +class ErrorItem { +public: + ErrorItem(); + explicit ErrorItem(const ErrorMessage &errmsg); + + /** + * @brief Convert error item to string. + * @return Error item as string. + */ + QString toString() const; + QString tool() const; + + QString file0; + QString errorId; + Severity severity; + bool inconclusive; + QString summary; + QString message; + int cwe; + unsigned long long hash; + QList errorPath; + QString symbolNames; + + // Special GUI properties + QString sinceDate; + QString tags; + + /** + * Compare "CID" + */ + static bool sameCID(const ErrorItem &errorItem1, const ErrorItem &errorItem2); +}; + +// NOLINTNEXTLINE(performance-no-int-to-ptr) +Q_DECLARE_METATYPE(ErrorItem) + +/** + * @brief A class containing error data for one shown error line. + */ +class ErrorLine { +public: + QString file; + int line; + QString file0; + QString errorId; + int cwe; + unsigned long long hash; + bool inconclusive; + Severity severity; + QString summary; + QString message; + QString sinceDate; + QString tags; +}; + +/// @} +#endif // ERRORITEM_H diff --git a/cppcheck-2.14.0/gui/filelist.cpp b/cppcheck-2.14.0/gui/filelist.cpp new file mode 100644 index 00000000..3115967c --- /dev/null +++ b/cppcheck-2.14.0/gui/filelist.cpp @@ -0,0 +1,137 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "filelist.h" + +#include "pathmatch.h" + +#include +#include +#include +#include + +#include +#include + +QStringList FileList::getDefaultFilters() +{ + QStringList extensions; + extensions << "*.cpp" << "*.cxx" << "*.cc" << "*.c" << "*.c++" << "*.txx" << "*.tpp" << "*.ipp" << "*.ixx"; + return extensions; +} + +bool FileList::filterMatches(const QFileInfo &inf) +{ + if (inf.isFile()) { + const QStringList filters = FileList::getDefaultFilters(); + QString ext("*."); + ext += inf.suffix(); + if (filters.contains(ext, Qt::CaseInsensitive)) + return true; + } + return false; +} + +void FileList::addFile(const QString &filepath) +{ + QFileInfo inf(filepath); + if (filterMatches(inf)) + mFileList << inf; +} + +void FileList::addDirectory(const QString &directory, bool recursive) +{ + QDir dir(directory); + dir.setSorting(QDir::Name); + const QStringList filters = FileList::getDefaultFilters(); + const QStringList origNameFilters = dir.nameFilters(); + dir.setNameFilters(filters); + if (!recursive) { + dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); + QFileInfoList items = dir.entryInfoList(); + mFileList += items; + } else { + dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); + QFileInfoList items = dir.entryInfoList(); + mFileList += items; + + dir.setNameFilters(origNameFilters); + dir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); + for (const QFileInfo& item : dir.entryInfoList()) { + const QString path = item.canonicalFilePath(); + addDirectory(path, recursive); + } + } +} + +void FileList::addPathList(const QStringList &paths) +{ + for (const QString& path : paths) { + QFileInfo inf(path); + if (inf.isFile()) + addFile(path); + else + addDirectory(path, true); + } +} + +QStringList FileList::getFileList() const +{ + if (mExcludedPaths.empty()) { + QStringList names; + for (const QFileInfo& item : mFileList) { + QString name = QDir::fromNativeSeparators(item.filePath()); + names << name; + } + return names; + } + return applyExcludeList(); +} + +void FileList::addExcludeList(const QStringList &paths) +{ + mExcludedPaths = paths; +} + +static std::vector toStdStringList(const QStringList &stringList) +{ + std::vector ret; + std::transform(stringList.cbegin(), stringList.cend(), std::back_inserter(ret), [](const QString& s) { + return s.toStdString(); + }); + return ret; +} + +QStringList FileList::applyExcludeList() const +{ +#ifdef _WIN32 + const PathMatch pathMatch(toStdStringList(mExcludedPaths), true); +#else + const PathMatch pathMatch(toStdStringList(mExcludedPaths), false); +#endif + + QStringList paths; + for (const QFileInfo& item : mFileList) { + if (pathMatch.match(QDir::fromNativeSeparators(item.filePath()).toStdString())) + continue; + QString canonical = QDir::fromNativeSeparators(item.canonicalFilePath()); + if (!pathMatch.match(canonical.toStdString())) + paths << canonical; + } + return paths; +} diff --git a/cppcheck-2.14.0/gui/filelist.h b/cppcheck-2.14.0/gui/filelist.h new file mode 100644 index 00000000..6e685e86 --- /dev/null +++ b/cppcheck-2.14.0/gui/filelist.h @@ -0,0 +1,101 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 +#include +#include + +/** + * @brief A class for listing files and directories to check. + * This class creates a list of files to check. If directory name is given then + * all files in the directory matching the filter will be added. The directory + * can be also added recursively when all files in subdirectories are added too. + * The filenames are matched against the filter and only those files whose + * filename extension is included in the filter list are added. + * + * This class also handles filtering of paths against ignore filters given. If + * there is ignore filters then only paths not matching those filters are + * returned. + */ +class FileList { +public: + + /** + * @brief Add filename to the list. + * @param filepath Full path to the file. + */ + void addFile(const QString &filepath); + + /** + * @brief Add files in the directory to the list. + * @param directory Full pathname to directory to add. + * @param recursive If true also files in subdirectories are added. + */ + void addDirectory(const QString &directory, bool recursive = false); + + /** + * @brief Add list of filenames and directories to the list. + * @param paths List of paths to add. + */ + void addPathList(const QStringList &paths); + + /** + * @brief Return list of filenames (to check). + * @return list of filenames to check. + */ + QStringList getFileList() const; + + /** + * @brief Add list of paths to exclusion list. + * @param paths Paths to exclude. + */ + void addExcludeList(const QStringList &paths); + + /** + * @brief Return list of default filename extensions included. + * @return list of default filename extensions included. + */ + static QStringList getDefaultFilters(); + +protected: + + /** + * @brief Test if filename matches the filename extensions filtering. + * @return true if filename matches filtering. + */ + static bool filterMatches(const QFileInfo &inf); + + /** + * @brief Get filtered list of paths. + * This method takes the list of paths and applies the exclude lists to + * it. And then returns the list of paths that did not match the + * exclude filters. + * @return Filtered list of paths. + */ + QStringList applyExcludeList() const; + +private: + QFileInfoList mFileList; + QStringList mExcludedPaths; +}; + +#endif // FILELIST_H diff --git a/cppcheck-2.14.0/gui/fileview.ui b/cppcheck-2.14.0/gui/fileview.ui new file mode 100644 index 00000000..22a581d7 --- /dev/null +++ b/cppcheck-2.14.0/gui/fileview.ui @@ -0,0 +1,71 @@ + + + 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 + + + + + diff --git a/cppcheck-2.14.0/gui/fileviewdialog.cpp b/cppcheck-2.14.0/gui/fileviewdialog.cpp new file mode 100644 index 00000000..bebb1f65 --- /dev/null +++ b/cppcheck-2.14.0/gui/fileviewdialog.cpp @@ -0,0 +1,82 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "fileviewdialog.h" + +#include +#include +#include +#include +#include +#include + +#include "ui_fileview.h" + +FileViewDialog::FileViewDialog(const QString &file, + const QString &title, + QWidget *parent) + : QDialog(parent) + , mUI(new Ui::Fileview) +{ + mUI->setupUi(this); + + + setWindowTitle(title); + connect(mUI->mButtons, SIGNAL(accepted()), this, SLOT(accept())); + loadTextFile(file, mUI->mText); +} + +FileViewDialog::~FileViewDialog() +{ + delete mUI; +} + +void FileViewDialog::loadTextFile(const QString &filename, QTextEdit *edit) +{ + QFile file(filename); + if (!file.exists()) { + QString msg(tr("Could not find the file: %1")); + msg = msg.arg(filename); + + QMessageBox msgbox(QMessageBox::Critical, + tr("Cppcheck"), + msg, + QMessageBox::Ok, + this); + msgbox.exec(); + return; + } + + file.open(QIODevice::ReadOnly | QIODevice::Text); + if (!file.isReadable()) { + QString msg(tr("Could not read the file: %1")); + msg = msg.arg(filename); + + QMessageBox msgbox(QMessageBox::Critical, + tr("Cppcheck"), + msg, + QMessageBox::Ok, + this); + msgbox.exec(); + return; + } + QByteArray filedata = file.readAll(); + file.close(); + + edit->setPlainText(filedata); +} diff --git a/cppcheck-2.14.0/gui/fileviewdialog.h b/cppcheck-2.14.0/gui/fileviewdialog.h new file mode 100644 index 00000000..91c455eb --- /dev/null +++ b/cppcheck-2.14.0/gui/fileviewdialog.h @@ -0,0 +1,64 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 + +class QWidget; +class QTextEdit; +namespace Ui { + class Fileview; +} + +/// @addtogroup GUI +/// @{ + + +/** + * @brief File view -dialog. + * This dialog shows text files. It is used for showing the license file and + * the authors list. + * + */ +class FileViewDialog : public QDialog { + Q_OBJECT +public: + FileViewDialog(const QString &file, + const QString &title, + QWidget *parent = nullptr); + + ~FileViewDialog() override; + +protected: + + /** + * @brief Load text file contents to edit control. + * + * @param filename File to load. + * @param edit Control where to load the file contents. + */ + void loadTextFile(const QString &filename, QTextEdit *edit); + + Ui::Fileview* mUI; +}; +/// @} +#endif // FILEVIEW_DIALOG_H diff --git a/cppcheck-2.14.0/gui/gui.cppcheck b/cppcheck-2.14.0/gui/gui.cppcheck new file mode 100644 index 00000000..470f0bbf --- /dev/null +++ b/cppcheck-2.14.0/gui/gui.cppcheck @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/gui/gui.pro b/cppcheck-2.14.0/gui/gui.pro new file mode 100644 index 00000000..b3d4c9da --- /dev/null +++ b/cppcheck-2.14.0/gui/gui.pro @@ -0,0 +1,224 @@ +lessThan(QT_MAJOR_VERSION, 5): error(requires >= Qt 5 (You used: $$QT_VERSION)) +greaterThan(QT_MAJOR_VERSION, 5): error(Qt 6 is not supported via qmake - please use CMake instead) + +message("Building the GUI via qmake is deprecated and will be removed in a future release. Please use CMake instead.") + +TEMPLATE = app +TARGET = cppcheck-gui +CONFIG += warn_on debug +DEPENDPATH += . \ + ../lib +INCLUDEPATH += . \ + ../lib +QT += widgets +QT += printsupport +QT += help +QT += network + +# Build online help +onlinehelp.target = online-help.qhc +equals(QT_MAJOR_VERSION, 5):lessThan(QT_MINOR_VERSION, 12) { + # qcollectiongenerator is used in case of QT version < 5.12 + onlinehelp.commands = qcollectiongenerator $$PWD/help/online-help.qhcp -o $$PWD/help/online-help.qhc +} else { + onlinehelp.commands = qhelpgenerator $$PWD/help/online-help.qhcp -o $$PWD/help/online-help.qhc +} +QMAKE_EXTRA_TARGETS += onlinehelp +PRE_TARGETDEPS += online-help.qhc + +contains(LINKCORE, [yY][eE][sS]) { + LIBS += -l../bin/cppcheck-core + DEFINES += CPPCHECKLIB_IMPORT +} +LIBS += -L$$PWD/../externals + +DESTDIR = . +RCC_DIR = temp +MOC_DIR = temp +OBJECTS_DIR = temp +UI_DIR = temp + +isEmpty(QMAKE_CXX) { + isEmpty(CXX)) { + QMAKE_CXX = gcc + } else { + QMAKE_CXX = $$(CXX) + } +} + +win32 { + CONFIG += windows + contains(LINKCORE, [yY][eE][sS]) { + DESTDIR = ../bin + RCC_DIR = temp/generated + MOC_DIR = temp/generated + OBJECTS_DIR = temp/generated + UI_DIR = temp/generated + } else { + DESTDIR = ../Build/gui + RCC_DIR = ../BuildTmp/gui + MOC_DIR = ../BuildTmp/gui + OBJECTS_DIR = ../BuildTmp/gui + UI_DIR = ../BuildTmp/gui + } +} + +RESOURCES = gui.qrc +FORMS = about.ui \ + applicationdialog.ui \ + compliancereportdialog.ui \ + fileview.ui \ + helpdialog.ui \ + mainwindow.ui \ + projectfile.ui \ + resultsview.ui \ + scratchpad.ui \ + settings.ui \ + statsdialog.ui \ + librarydialog.ui \ + libraryaddfunctiondialog.ui \ + libraryeditargdialog.ui \ + newsuppressiondialog.ui + +TRANSLATIONS = cppcheck_de.ts \ + cppcheck_es.ts \ + cppcheck_fi.ts \ + cppcheck_fr.ts \ + cppcheck_it.ts \ + cppcheck_ja.ts \ + cppcheck_ko.ts \ + cppcheck_nl.ts \ + cppcheck_ru.ts \ + cppcheck_sr.ts \ + cppcheck_sv.ts \ + cppcheck_zh_CN.ts \ + cppcheck_zh_TW.ts + +# Windows-specific options +CONFIG += embed_manifest_exe + +contains(LINKCORE, [yY][eE][sS]) { +} else { + BASEPATH = ../lib/ + include($$PWD/../lib/lib.pri) +} + +win32-msvc* { + MSVC_VER = $$(VisualStudioVersion) + message($$MSVC_VER) + MSVC_VER_SPLIT = $$split(MSVC_VER, .) + MSVC_VER_MAJOR = $$first(MSVC_VER_SPLIT) + # doesn't compile with older VS versions - assume VS2019 (16.x) is the first working for now + !lessThan(MSVC_VER_MAJOR, 16) { + message("using precompiled header") + CONFIG += precompile_header + PRECOMPILED_HEADER = precompiled_qmake.h + } +} + +HEADERS += aboutdialog.h \ + application.h \ + applicationdialog.h \ + applicationlist.h \ + checkstatistics.h \ + checkthread.h \ + codeeditstylecontrols.h \ + codeeditorstyle.h \ + codeeditstyledialog.h \ + codeeditor.h \ + common.h \ + compliancereportdialog.h \ + csvreport.h \ + erroritem.h \ + filelist.h \ + fileviewdialog.h \ + helpdialog.h \ + mainwindow.h \ + platforms.h \ + printablereport.h \ + projectfile.h \ + projectfiledialog.h \ + report.h \ + resultstree.h \ + resultsview.h \ + scratchpad.h \ + settingsdialog.h \ + showtypes.h \ + statsdialog.h \ + threadhandler.h \ + threadresult.h \ + translationhandler.h \ + txtreport.h \ + xmlreport.h \ + xmlreportv2.h \ + librarydialog.h \ + cppchecklibrarydata.h \ + libraryaddfunctiondialog.h \ + libraryeditargdialog.h \ + newsuppressiondialog.h + +SOURCES += aboutdialog.cpp \ + application.cpp \ + applicationdialog.cpp \ + applicationlist.cpp \ + checkstatistics.cpp \ + checkthread.cpp \ + codeeditorstyle.cpp \ + codeeditstylecontrols.cpp \ + codeeditstyledialog.cpp \ + codeeditor.cpp \ + common.cpp \ + compliancereportdialog.cpp \ + csvreport.cpp \ + erroritem.cpp \ + filelist.cpp \ + fileviewdialog.cpp \ + helpdialog.cpp \ + main.cpp \ + mainwindow.cpp\ + platforms.cpp \ + printablereport.cpp \ + projectfile.cpp \ + projectfiledialog.cpp \ + report.cpp \ + resultstree.cpp \ + resultsview.cpp \ + scratchpad.cpp \ + settingsdialog.cpp \ + showtypes.cpp \ + statsdialog.cpp \ + threadhandler.cpp \ + threadresult.cpp \ + translationhandler.cpp \ + txtreport.cpp \ + xmlreport.cpp \ + xmlreportv2.cpp \ + librarydialog.cpp \ + cppchecklibrarydata.cpp \ + libraryaddfunctiondialog.cpp \ + libraryeditargdialog.cpp \ + newsuppressiondialog.cpp + +win32 { + RC_FILE = cppcheck-gui.rc + HEADERS += ../lib/version.h + contains(LINKCORE, [yY][eE][sS]) { + } else { + LIBS += -lshlwapi + } +} + +contains(QMAKE_CC, gcc) { + QMAKE_CXXFLAGS += -std=c++17 -pedantic -Wall -Wextra -Wcast-qual -Wno-deprecated-declarations -Wfloat-equal -Wmissing-declarations -Wmissing-format-attribute -Wno-long-long -Wpacked -Wredundant-decls -Wundef -Wno-shadow -Wno-missing-field-initializers -Wno-missing-braces -Wno-sign-compare -Wno-multichar +} + +contains(QMAKE_CXX, clang++) { + QMAKE_CXXFLAGS += -std=c++17 -pedantic -Wall -Wextra -Wcast-qual -Wno-deprecated-declarations -Wfloat-equal -Wmissing-declarations -Wmissing-format-attribute -Wno-long-long -Wpacked -Wredundant-decls -Wundef -Wno-shadow -Wno-missing-field-initializers -Wno-missing-braces -Wno-sign-compare -Wno-multichar +} + +contains(HAVE_QCHART, [yY][eE][sS]) { + QT += charts +} else { + message("Charts disabled - to enable it pass HAVE_QCHART=yes to qmake.") +} + diff --git a/cppcheck-2.14.0/gui/gui.qrc b/cppcheck-2.14.0/gui/gui.qrc new file mode 100644 index 00000000..c40e1ba7 --- /dev/null +++ b/cppcheck-2.14.0/gui/gui.qrc @@ -0,0 +1,33 @@ + + + cppcheck-gui.png + images/dialog-error.png + images/dialog-information.png + images/dialog-warning.png + images/edit-clear.png + images/go-down.png + images/help-browser.png + images/media-floppy.png + images/preferences-system.png + images/process-stop.png + images/text-x-generic.png + images/view-recheck.png + images/view-refresh.png + images/showerrors.png + images/showstylewarnings.png + images/openproject.png + images/scratchpad.png + images/showwarnings.png + images/showperformance.png + images/utilities-system-monitor.png + ../COPYING + ../AUTHORS + images/go-home.png + images/go-next.png + images/go-previous.png + images/applications-development.png + images/applications-system.png + images/llvm-dragon.png + images/verify.svg + + diff --git a/cppcheck-2.14.0/gui/help/images/index-mainwindow.png b/cppcheck-2.14.0/gui/help/images/index-mainwindow.png new file mode 100644 index 0000000000000000000000000000000000000000..aa67a5e836d26ec59ae8079091d4826ffdc9c968 GIT binary patch literal 201009 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໹@8OTfl4t2Pcavl+;m)e@aC6-!?j{^x; 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 rTUZMRihQ+mAJ|CnhvH#?|aP$4D|vd<~+ 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 literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/help/images/severities-error.png b/cppcheck-2.14.0/gui/help/images/severities-error.png new file mode 100644 index 0000000000000000000000000000000000000000..6bad55fbd4ac1cf05b83b99fd72def2f873f8077 GIT binary patch literal 809 zcmV+^1J?YBP)s%se$`dHCwa&3lcQmy z3x|2)9%u(v07n8l0DV9P_yl+sxbAo;v(!-N8FNa$xzkSab0i|^6)Pmac8#RYJVU-e z<`{Wx+ifLyAAGkQ=*7>CjL07+o+!CvkCpV!J4>J|9lzu`M!^q$x=eA!SmeEKOZ==z8Yk!Q(rH@IO7w# zVgi9#WfUmbXJQQA;2??UAE6&`{tb41hwFd*^2=1YtqtZR($M|284}!w(yeG|q1@U^ zG#X2N3pW6c3Rf@Obx^Oud7r-iI&8l^(h|T0-Dk84JA;;H+)X#-+poSP4;Nho4ZuU; ze}RFf)8YPKGMQ`Md*u~svop9;Q;6Rb!0q4Xd-$WH)W#?H^urJNv0S-+1w00{0=}{o z3ckK*2YS}QsawO@Gt<-ELt~?Kbaml5F7p^91C9f>6`FMq zFb?$UeKXLca)Aa=)(kVMygsV~8vx@20HP1dYODcmfUnY70MJmtdi}1d*BkVm0YpE> np21al*n6-cV5LA&AbQQ8tW6I*j8hA%lm_PEK5;$?LXC~OSCqQeGQmv4a8d0KhNF($p}2%9=p@6HjrAWWXI zETQJRBEA3L{^P_ZZ3y;Ocwp`U7K)nPauw&#RC2z=Io20DT4NVx?jwC`@^lfMtyLK` ze5$0D>sZ`x%)-2KI#<*fgo)cJsMQ8?&8ASCF!Qa1Md0!3Z!iSCtMfG6%)Nx~NR=aY zvW&>6rmx4;CCU!rB6D1|_i;GcQn9!9!a`8*LMegoW2{D>a;5cARCf)?Usm zr2iW@f#{^hW8+PF2}W&hfJH|b5yd=;oQz%6Ya9}#bxMurSbx!>D(m4bC5Cd8a{(cf z#Tv&u77v(cDQ$GZjJ;%h-XYp{s==XaG=><~F4d#FxK>5l`50pr~0{U!ZSYcAVqfd@NuKM6~BP|4w89Z50-akyK?Qp3!bAH6AZ% z@iMh776HY(JDU~HO`N(oQN{YA8w>S>PmFuh*<4JPm9(rVX+=?zIa!essVr4ade`$( zHZpzIiZ(3wW~WmQr`xKvTaSiGv*>O#glc2Sj1R9$Ns-!g)OJ zoV(9}aa+2&K(798z_^x*8dj*&@s**rWD5@3Gqt*aPBz4Sj~L~jBUpJ zb=_HgPYfG1BW!ZVj1~2o!}XMLg)0>OiPP%)(+oSLEusWQjh!W`xo+3xcU{tC(RtiH(b0FnSWSn54r=}R=Fu1%y&>PJ# zYu<7hzkk~!@oS$y|(rDiT> zujz{TM&uskG2|uWV^#d&haVc%D?a~*e1yEHW-eyCeEwa5Tz9cH_aYA=k0XyD4ZdSpWb4 literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/help/images/severities-performance.png b/cppcheck-2.14.0/gui/help/images/severities-performance.png new file mode 100644 index 0000000000000000000000000000000000000000..ea9a1c8abcec65f9eb9a91c98f56b589f3211349 GIT binary patch literal 847 zcmV-V1F-ywP)nOj`jw$0df^4s>aZQHhOBereZCwDsL_4!NEY7?GA3b&$|0t92#yHPm2&h~{sX%`5&)t{@U-sZN3hhVxqwv&Or1gadu5uFl=s@h1zrp7?)o&bs) zmw2dMW1(`2hSDLNtEb+|nzF~kDu+<0ouXJ{Jo@`0asBdkR5!#z=aT_Fi;jzTI&@yC z(0V3P-PlF80bx25t$QNkG83?GM+5+1>!v7}0`p-C%11(eIt&4M(D`O!x+)kSkJI7B zA9(}A6ZxK4Y>GlpZx}n>o=xSSl>lb?vlA=IYJ8eTr1rGveBY2KF@T@8wI>}3(?)Vh@s95az{nIl$v{cdH7nDLhI#q5|q}6*qIAZ zQce#K71gDWzZJm=61tyZ=^A`4jKP=kBt++12#zVV;d?_SzSX4TOIZ@_|9D9l-k<9? zF#PUJFMPNl5TEiQS#tuuR;5A{89?_}9~Xk3E%^-3!nbVu=aP6<7s2WS?tvfkNx#p* zi-PevKZ?%kTWvaiHs>L})Dgcsm*Z#qV*F^%rL+E8mdM~Jd^j(VdYfA7ABK;N>?cIM zZJo^z!7S&od+$!H-LM*6z3pgeYeZdR^`O$y-biRick5l)Xw=`BJv9sZoV?8M9$s$t z3_!pU7a!|Qt*Xh!&e{iLCIbjS8S+4^2gD%T+>o#@HjJjm9;#O!ViqRgz=8#EU`(}0 Z008rQ=0+00RS*CG002ovPDHLkV1k&mk!t_| literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/help/images/severities-portability.png b/cppcheck-2.14.0/gui/help/images/severities-portability.png new file mode 100644 index 0000000000000000000000000000000000000000..2bc00aa106f98f0cfb8eac52114fb217fc098c00 GIT binary patch literal 1288 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⪙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
=5(Nqv$y7dBI~Ow1OxlN=m} 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 literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/help/images/walkthrough-toolbar-severities.png b/cppcheck-2.14.0/gui/help/images/walkthrough-toolbar-severities.png new file mode 100644 index 0000000000000000000000000000000000000000..f06e3aa41cf97272c39ad85d14a877402bce42b8 GIT binary patch literal 6693 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

KWg|6F321>6xgS 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 Z7pJMZRCdKEu(bwFNlsn1S_T>NzX0gXG-dz* literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/help/index.html b/cppcheck-2.14.0/gui/help/index.html new file mode 100644 index 00000000..4d2cd43f --- /dev/null +++ b/cppcheck-2.14.0/gui/help/index.html @@ -0,0 +1,21 @@ + + +Cppcheck GUI + + + +

Cppcheck GUI

+ +

With the Cppcheck GUI you can analyze your code.

+ +

Contents

+ +
+ +

Example

+ +
+ + + + diff --git a/cppcheck-2.14.0/gui/help/investigating-warnings.html b/cppcheck-2.14.0/gui/help/investigating-warnings.html new file mode 100644 index 00000000..5eec66bd --- /dev/null +++ b/cppcheck-2.14.0/gui/help/investigating-warnings.html @@ -0,0 +1,22 @@ + + +Investigating warnings + + + +

Investigating warnings

+ +

When you have run the analysis it is time to look at the results.

+ +

If you click on a warning then the corresponding code will be shown in the +"Warning details" at the bottom.

+ +

You can right click warnings to get options. The difference of +"hiding" a warning and "suppressing" a warning is that +the suppression is permanent and hiding the warning is only temporary. When +suppressing warning(s), that is saved in the project file. + + + + + diff --git a/cppcheck-2.14.0/gui/help/manual.html b/cppcheck-2.14.0/gui/help/manual.html new file mode 100644 index 00000000..a237cb84 --- /dev/null +++ b/cppcheck-2.14.0/gui/help/manual.html @@ -0,0 +1,947 @@ + +Cppcheck 1.46


Chapter 1. Introduction

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

Supported code and platforms:

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

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

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

Accuracy

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

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


Chapter 2. Getting started

2.1. First test

Here is a simple code

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

If you save that into file1.c and + execute:

cppcheck file1.c

The output from cppcheck will then be:

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

2.2. Checking all files in a folder

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

cppcheck path

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

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

2.3. Excluding a file or folder from checking

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

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

cppcheck src/a src/b

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

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


2.4. Severities

The possible severities for messages are:

error

used when bugs are found

warning

suggestions about defensive programming to prevent + bugs

style

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

performance

suggestions for making the code faster


2.5. Enable messages

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


2.5.1. Stylistic issues

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

Here is a simple code example:

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

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

cppcheck --enable=style file3.c

The output from Cppcheck is now:

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


2.5.2. Unused functions

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

cppcheck --enable=unusedFunction path

2.5.3. Enable all checks

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

cppcheck --enable=all path

2.6. Saving results in file

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

cppcheck file1.c 2> err.txt

2.7. Multithreaded checking

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

cppcheck -j 4 path

Chapter 3. Preprocessor configurations

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

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

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

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

cppcheck -DDEBUG=1 -D__cplusplus path

Chapter 4. XML output

Cppcheck can generate the output in XML format.

Use the --xml flag when you execute cppcheck:

cppcheck --xml file1.cpp

The xml format is:

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

Attributes:

file

filename. Both relative and absolute paths are possible

line

a number

id

id of error. These are always valid symbolnames.

severity

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

msg

the error message


Chapter 5. Reformatting the output

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

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

cppcheck --template vs gui/test.cpp

This output will look like this:

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

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

cppcheck --template gcc gui/test.cpp

The output will look like this:

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

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

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

The output will look like this:

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


Chapter 6. Suppressions

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

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

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

Here is an example:

memleak:file1.cpp
+exceptNew:file1.cpp
+uninitvar

You can then use the suppressions file:

cppcheck --suppressions suppressions.txt src/

+

Chapter 7. Exception safety

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

Example:

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

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

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

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

The output will be:

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

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

Here is another example:

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

Check that with Cppcheck:

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

The output from Cppcheck is:

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

Chapter 8. Html report

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

This command generates the help screen:

htmlreport/cppcheck-htmlreport -h

The output screen says:

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

An example usage:

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

Chapter 9. Graphical user interface

9.1. Introduction

A Cppcheck GUI is available.

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


9.2. Check source code

Use the Check menu.


9.3. Inspecting results

The results are shown in a list.

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

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


9.4. Settings

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

More settings are available in + Edit>Preferences.


9.5. Project files

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

  • include folders

  • preprocessor defines

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

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

diff --git a/cppcheck-2.14.0/gui/help/online-help.qhcp b/cppcheck-2.14.0/gui/help/online-help.qhcp new file mode 100644 index 00000000..228649c1 --- /dev/null +++ b/cppcheck-2.14.0/gui/help/online-help.qhcp @@ -0,0 +1,14 @@ + + + + + + online-help.qhp + online-help.qch + + + + online-help.qch + + + diff --git a/cppcheck-2.14.0/gui/help/online-help.qhp b/cppcheck-2.14.0/gui/help/online-help.qhp new file mode 100644 index 00000000..23411d0f --- /dev/null +++ b/cppcheck-2.14.0/gui/help/online-help.qhp @@ -0,0 +1,50 @@ + + + cppcheck.sourceforge.io + doc + + +
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + index.html + images/index-mainwindow.png + investigating-warnings.html + preferences.html + projectfiledialog.html + severities.html + images/severities-error.png + images/severities-warning.png + images/severities-style.png + images/severities-performance.png + images/severities-portability.png + images/severities-information.png + standalone-analysis.html + tagging.html + walkthrough.html + images/walkthrough-analysis.png + images/walkthrough-library.png + images/walkthrough-toolbar-severities.png + images/walkthrough-import-project.png + images/walkthrough-new-project.png + images/walkthrough-warning-menu.png + + + diff --git a/cppcheck-2.14.0/gui/help/preferences.html b/cppcheck-2.14.0/gui/help/preferences.html new file mode 100644 index 00000000..5a591227 --- /dev/null +++ b/cppcheck-2.14.0/gui/help/preferences.html @@ -0,0 +1,74 @@ + + +Preferences + + + +

Preferences

+ +

Number of threads
Number of threads to use in analysis. Each +thread checks its own source file.

+ +

Force checking of all #ifdef configurations
Cppcheck try to check +all code and will therefore guess different preprocessor configurations. The +maximum number of configurations that is checked is 14 by default.

+ +

Show full path of files
Show the full paths in the results.

+ +

Show "No errors found" message when no errors found
+If you want to get a message box about this.

+ +

Display error id column "Id"
Show error id in results

+ +

Enable inline suppressions
You can suppress warnings with comments. +See the Cppcheck manual (https://cppcheck.sourceforge.io/manual.pdf) for more +information about inline suppressions.

+ +

Check for inconclusive errors also
When full analysis of the code +can not determine if there should be a warning or not, it is inconclusive. +Normally Cppcheck does not warn then.

+ +

Show statistics on check completion
Show statistics in a window +when analysis finish.

+ +

Show internal warnings in log
Internal warnings (for debugging) is +shown in the Analysis log.

+ +

Applications
Configure external editor to open from context menu +when you right click on a warning.

+ +

Save all errors when creating report
If hidden warnings should be +saved or not.

+ +

Save full path to files in report
If you use Root path the +warnings on the screen will not have the full path.

+ +

Language
Configure language to use for GUI.

+ +

Python binary
To be able to execute addons, Cppcheck needs to know +where python is. Unless you configure something, Cppcheck will try to execute +python in your PATH.

+ +

MISRA rule texts
Only needed if you want to use the MISRA addon. +Cppcheck is not legally allowed to distribute the MISRA rule texts and these +must be provided by users. The MISRA rule texts are proprietary. An example +rule text file: +

Appendix A Summary of guidelines
+Rule 1.1
+Text of rule 1.1
+Rule 1.2
+Text of rule 1.2
+

+ +

Clang path
The path to clang binary. If no path is provided +then system PATH is used.

+ +

Visual studio headers
If you want to use the Visual Studio headers +in the analysis you can provide the path(s) here. Hint: Open a visual studio +command prompt and type SET INCLUDE. Then copy/paste the paths.

+ +

Code editor style
The visual theme to use for the code editor that +is used when you investigate results.

+ + + diff --git a/cppcheck-2.14.0/gui/help/projectfiledialog.html b/cppcheck-2.14.0/gui/help/projectfiledialog.html new file mode 100644 index 00000000..52b4eba4 --- /dev/null +++ b/cppcheck-2.14.0/gui/help/projectfiledialog.html @@ -0,0 +1,179 @@ + + +Project File Dialog + + + +

Project File Dialog

+ +

The Project file dialog contains 4 tabs:

+
    +
  • Paths and defines; paths to check and basic preprocessor settings. +
  • Types and Functions; configuration of platform and 3rd party libraries +
  • Analysis; analysis options +
  • Warning options; formatting warnings, suppressing warnings, etc +
  • Addons; extra analysis with addons +
+ +

Paths and defines

+ +

It is recommended to import a project file.

+ +

Import project

+ +Project to import. Cppcheck will get: +
    +
  • What files to check +
  • Preprocessor defines +
  • Preprocessor include paths +
  • Language standard if set +
+ +

Paths (If you do not import project)

+ +

What paths to check.

+ +

Defines (If you do not import project)

+ +

Cppcheck automatically checks the code with different preprocessor +configurations.

+ +
#ifdef A
+code1
+#endif
+#ifdef B
+code2
+#endif
+ +

Cppcheck will automatically perform analysis both when A is defined and B is +defined. So any bugs in both code1 and code2 will be detected.

+ +

If you want to configure that A will always be defined in Cppcheck analysis +you can do that here.

+ +

Defines are separated by semicolon. So you can for instance write:

+ +
A;B=3;C
+ +

Undefines (If you do not import project)

+ +

Cppcheck automatically checks the code with different preprocessor +configurations.

+ +
#ifdef A
+code1
+#endif
+#ifdef B
+code2
+#endif
+ +

Cppcheck will automatically perform analysis both when A is defined and B is +defined. So any bugs in both code1 and code2 will be detected.

+ +

If you want to configure that A is never defined in Cppcheck analysis you +can do that here.

+ +

Undefines are separated by semicolon. So you can for instance write:

+ +
A;C
+ +

Include paths (If you do not import project)

+ +

Specify include paths.

+ +

Types and Functions

+ +

Cppcheck uses the Platform setting to determine size of +short/int/long/pointer/etc.

+ +

Check the libraries that you use in the Libraries listbox.

+ +

Analysis

+ +

Cppcheck build dir

+ +

This is a work-folder that Cppcheck uses. Each Cppcheck project should have +a separate build dir. It is used for:

+
    +
  • whole program analysis +
  • debug output +
  • faster analysis (if a source file has changed check it, if source file is + not changed then reuse old results) +
  • statistics +
+ +

Parser

+ +

It is in general recommended to use Cppcheck parser. However you can choose +to use Clang parser; Clang will be executed with a command line flag that tells +it to dump its AST and Cppcheck will read that AST and convert it into a +corresponding Cppcheck AST and use that.

+ +

Analysis

+ +

Configure what kind of analysis you want.

+ +

The Normal analysis is recommended for most use cases. Especially if +you use Cppcheck in CI.

+ +

The Bug hunting can be used if you really want to find a bug in your +code and can invest time looking at bad results and providing extra +configuration.

+ +

Limit analysis

+ +

You can turn off checking of headers. That could be interesting if Cppcheck +is very slow. But normally, you should check the code in headers.

+ +

It is possible to check the code in unused templates. However the Cppcheck +AST will be incomplete/wrong. The recommendation is that you do not check +unused templates to avoid wrong warnings. The templates will be checked +properly when you do use them.

+ +

Max CTU depth: How deep should the whole program analysis be. The risk with +a "too high" value is that Cppcheck will be slow.

+ +

Max recursion in template instantiation: Max recursion when Cppcheck +instantiates templates. The risk with a "too high" value is that +Cppcheck will be slow and can require much memory.

+ + +

Warning options

+ +

Root path

+ +

The root path for warnings. Cppcheck will strip away this part of the path +from warnings. For instance if there is a warning in +

../myproject/foo/bar/file.cpp
and the root path is +
../myproject/foo
then the path for the warning will be +
bar/file.cpp
.

+ +

Warning Tags

+ +

Tags allow you to manually categorize warnings.

+ +

Exclude source files

+ +

Excluded source files will not be analyzed by Cppcheck.

+ +

Suppressions

+ +

List of suppressions. These warnings will not be shown.

+ +

Addons

+ +

Y2038
32-bit timers that count number of seconds since 1970 will +overflow in year 2038. Check that the code does not use such timers.

+ +

Thread safety
Check that the code is thread safe

+ +

MISRA
Ensure that the MISRA coding standard is followed. Please +note you need to have a textfile with the misra rule texts to get proper +warning messages. Cppcheck is not legally allowed to distribute the misra +rule texts.

+ +

Clang-tidy
Run Clang-tidy

+ + + + diff --git a/cppcheck-2.14.0/gui/help/severities.html b/cppcheck-2.14.0/gui/help/severities.html new file mode 100644 index 00000000..791c78c6 --- /dev/null +++ b/cppcheck-2.14.0/gui/help/severities.html @@ -0,0 +1,47 @@ + + +Severities + + + +

Severities

+ +

error

+ +
+

when code is executed there is some bad behavior (undefined behavior, leak)

+ +

warning

+ +
+

when code is executed there might be undefined behavior

+ +

style

+ +
+

point out possible mistakes, and suggest more defensive programming. Examples: +

    +
  • unused code/variables/functions +
  • conditions that are always true/false +
  • constness +
  • operator precedence +
+

+ +

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. Implementation defined behavior. 64-bit portability. Some undefined behavior that probably works "as you want". etc.

+ +

information

+ +
+

Configuration problems. If you get such output then your code is ok but your cppcheck configuration could be improved.

+ + + diff --git a/cppcheck-2.14.0/gui/help/standalone-analysis.html b/cppcheck-2.14.0/gui/help/standalone-analysis.html new file mode 100644 index 00000000..ad7fae5f --- /dev/null +++ b/cppcheck-2.14.0/gui/help/standalone-analysis.html @@ -0,0 +1,17 @@ + + + +Standalone analysis + + + +

Standalone analysis

+ +

It is possible to quickly analyze files. Open the Analyze menu and click on +either Files... or Directory....

+ +

It is recommended that you create a project for analysis. A properly configured +project will give you better analysis.

+ + + diff --git a/cppcheck-2.14.0/gui/help/tagging.html b/cppcheck-2.14.0/gui/help/tagging.html new file mode 100644 index 00000000..7ac4e7ee --- /dev/null +++ b/cppcheck-2.14.0/gui/help/tagging.html @@ -0,0 +1,22 @@ + + +Tagging warnings + + + +

Tagging warnings

+ +

You can manually categorize warnings.

+ +

You choose the names of the categories yourself in the project file +dialog.

+ +

If tag names are configured, then you can tag the warnings by +right-clicking on them and selecting the proper tag in the +context menu.

+ +

Tags are saved in the project file and will be permanent.

+ + + + diff --git a/cppcheck-2.14.0/gui/help/walkthrough.html b/cppcheck-2.14.0/gui/help/walkthrough.html new file mode 100644 index 00000000..9f27ce05 --- /dev/null +++ b/cppcheck-2.14.0/gui/help/walkthrough.html @@ -0,0 +1,69 @@ + + +Walk through + + + +

Quick walk through

+ +This is a quick and short walk through to get you started. + +

Step 1: Create a project.

+ +

Create a new project:

+ +
+ +

In the Paths and Defines tab, it is recommended that you import your +project file at the top.

+ +
+ +

In the Types and Functions tab, try to activate all 3rd party +libraries you use (windows, posix, ...).

+ +
+ +

In the Analysis tab, leave the default settings to start with.

+ +

In the Warnings options tab, leave the default settings to start +with.

+ +

In the Addons tab, leave the default settings to start with.

+ + +

Step 2: Analyze code.

+ +

When the project file has been created, the analysis will start +automatically.

+ +

While analysis is performed in the background, you can investigate the +results.

+ +
+ + +

Step 3: Investigate warnings.

+ +

In the toolbar you choose what types of warnings you want to see +(error/warning/style/performance/portability/information).

+ +
+ + +

All warnings are shown in a list. If you select a warning in the list, then +details about that warning is shown.

+ +

If you right click on warning(s) then you get a context menu.

+ +
+ +

The difference of "Hide" and "Suppress" is that +suppressions are saved in the project file. The suppressed warnings will not be +shown again unless you remove the suppression. When you hide a warning then +they will be temporarily hidden; the next time you analyze your code these +warnings will be shown again.

+ + + + diff --git a/cppcheck-2.14.0/gui/helpdialog.cpp b/cppcheck-2.14.0/gui/helpdialog.cpp new file mode 100644 index 00000000..bb9e5d5f --- /dev/null +++ b/cppcheck-2.14.0/gui/helpdialog.cpp @@ -0,0 +1,120 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "helpdialog.h" + +#include "common.h" + +#include "ui_helpdialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class QWidget; + +void HelpBrowser::setHelpEngine(QHelpEngine *helpEngine) +{ + mHelpEngine = helpEngine; +} + +QVariant HelpBrowser::loadResource(int type, const QUrl &name) +{ + if (name.scheme() == "qthelp") { + QString url(name.toString()); + while (url.indexOf("/./") > 0) + url.remove(url.indexOf("/./"), 2); + return QVariant(mHelpEngine->fileData(QUrl(url))); + } + return QTextBrowser::loadResource(type, name); +} + +static QString getHelpFile() +{ + const QString datadir = getDataDir(); + + QStringList paths; + paths << (datadir + "/help") + << datadir + << (QApplication::applicationDirPath() + "/help") + << QApplication::applicationDirPath(); +#ifdef FILESDIR + const QString filesdir = FILESDIR; + paths << (filesdir + "/help") + << filesdir; +#endif + for (const QString &p: paths) { + QString filename = p + "/online-help.qhc"; + if (QFileInfo::exists(filename)) + return filename; + } + return QString(); +} + +HelpDialog::HelpDialog(QWidget *parent) : + QDialog(parent), + mUi(new Ui::HelpDialog) +{ + mUi->setupUi(this); + + QString helpFile = getHelpFile(); + if (helpFile.isEmpty()) { + const QString msg = tr("Helpfile '%1' was not found").arg("online-help.qhc"); + QMessageBox msgBox(QMessageBox::Warning, + tr("Cppcheck"), + msg, + QMessageBox::Ok, + this); + msgBox.exec(); + mHelpEngine = nullptr; + return; + } + + mHelpEngine = new QHelpEngine(helpFile); + // Disable the timestamp check of online-help.qhc by setting _q_readonly + mHelpEngine->setProperty("_q_readonly", QVariant::fromValue(true)); + mHelpEngine->setupData(); + + mUi->contents->addWidget(mHelpEngine->contentWidget()); + mUi->index->addWidget(mHelpEngine->indexWidget()); + + mUi->textBrowser->setHelpEngine(mHelpEngine); + + mUi->textBrowser->setSource(QUrl("qthelp://cppcheck.sourceforge.io/doc/index.html")); + connect(mHelpEngine->contentWidget(), + SIGNAL(linkActivated(QUrl)), + mUi->textBrowser, + SLOT(setSource(QUrl))); + + connect(mHelpEngine->indexWidget(), + SIGNAL(linkActivated(QUrl,QString)), + mUi->textBrowser, + SLOT(setSource(QUrl))); +} + +HelpDialog::~HelpDialog() +{ + delete mUi; + delete mHelpEngine; +} diff --git a/cppcheck-2.14.0/gui/helpdialog.h b/cppcheck-2.14.0/gui/helpdialog.h new file mode 100644 index 00000000..075005fd --- /dev/null +++ b/cppcheck-2.14.0/gui/helpdialog.h @@ -0,0 +1,60 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HELPDIALOG_H +#define HELPDIALOG_H + +#include +#include +#include +#include +#include + +class QHelpEngine; +class QUrl; +class QWidget; +namespace Ui { + class HelpDialog; +} + +class HelpBrowser : public QTextBrowser { +public: + explicit HelpBrowser(QWidget* parent = nullptr) : QTextBrowser(parent) {} + HelpBrowser(const HelpBrowser&) = delete; + HelpBrowser(HelpBrowser&&) = delete; + HelpBrowser& operator=(const HelpBrowser&) = delete; + HelpBrowser& operator=(HelpBrowser&&) = delete; + void setHelpEngine(QHelpEngine *helpEngine); + QVariant loadResource(int type, const QUrl& name) override; +private: + QHelpEngine* mHelpEngine{}; +}; + +class HelpDialog : public QDialog { + Q_OBJECT + +public: + explicit HelpDialog(QWidget *parent = nullptr); + ~HelpDialog() override; + +private: + Ui::HelpDialog *mUi; + QHelpEngine* mHelpEngine; +}; + +#endif // HELPDIALOG_H diff --git a/cppcheck-2.14.0/gui/helpdialog.ui b/cppcheck-2.14.0/gui/helpdialog.ui new file mode 100644 index 00000000..0fb74c6a --- /dev/null +++ b/cppcheck-2.14.0/gui/helpdialog.ui @@ -0,0 +1,67 @@ + + + HelpDialog + + + + 0 + 0 + 635 + 446 + + + + Cppcheck GUI help + + + + + + Qt::Horizontal + + + + + 200 + 16777215 + + + + 0 + + + + Contents + + + + + + + + + + Index + + + + + + + + + + + + + + + + HelpBrowser + QTextBrowser +
helpdialog.h
+
+
+ + +
diff --git a/cppcheck-2.14.0/gui/images/applications-development.png b/cppcheck-2.14.0/gui/images/applications-development.png new file mode 100644 index 0000000000000000000000000000000000000000..7320ae9fd4c6f403bd2cbded14c3a9ebccf9fee2 GIT binary patch literal 1215 zcmV;w1VHT&u$vZ+nELg? zVG|3Bhjw4FbXfQL@)7;D#Y4M_3x^Hgws7QZBPqW)f!$!&!o$Yy@N4&&dii=AHgB_W zv~DAR(}sS@R@X1Ki-&b@E*ykR;HeO)JyFv2el;}vv0u)=;vd-Pe590s!CFNWIW)>Q z)2fGT-nvJ{sNng)CQp#$@p9dK&pn-eFZ$*1+=p>J7su^IIz36)m&FSOfpk2^p7Bwh z_qLM;h0JX@Ugrr3JQn%53^#wNj)U3fy=_1G#$E{+5FJ1cYaKvfkyz8h#*1`xMZEkY zYxIr3_oGIIA4s4LGPR62c(b+Q)i3|ZrC=MV@HUL9fatOCm9f@hbd(S#1Zd^sdR`V6qMNvR z1zr6EOug{uT|&x#B#_HV}M7Fsn>(SxYClEsZ`qIHuvs*%fk z*#O!E1dS_bln$q7^tmLn{;ZX~A56ea4HFD?4ZOsOgWJ)njx{kt`Uv5Y36|pwOxc0)z!?HP{%H zB>*nQ@5CtxYXbbn4G1hoZKs}8{5Co&qIC_cY8btP)^)TB z+1#u$GMuB`aYnx9Szry{`9t8kZ~Q9J;$b=CdgD&exD_D;2nQh~t?gx++m`}@M`bgj zNTFhZwI)Ld2Wuj$uKS$r{j#OhV@A?0Z9& z7e*?m*65T#kJtCwVo=+lv-gz@Onfn`2w+T$-6RA6*;DTwNuU2XIyMw5+`nGLxPWuT zWlJaa4bJ{3RxP~zSTg$SvV*i%vzrPXL-Y>6mRw;Ti(+W{2h_H%0ZG61X%+xOVudSkfB-~x%oW_7y;-idIgx$>l!$DjCN{H9`+ zTLsVwjGXW#~kzg2>1xcnFc=kXSZQ&&bDt0(Y(bgi}sA z<&QuAH~#}l0+2u#Fn;#lxzneg@!n_6yyPcHM-tRm30pVQ?mR;}d2$`kq0sdLgwsv4 zd;=Rd@4>CtpLXd-zi&SWRDeK0CfT0(@Zo>rchh6u$LC)5%R*=0b48)&X*g~N(#Zh= zt+U^do%Qpm6_;%J-qWKK&x^kP&%-zqr~x5h1dmDpUVHZ4fB8|zm&S&~+dbDzI+6}4 z@mvST5AmD6C<-ac#frV~=l}GN0(O7^hyepwoq|h;)ycbRO4zIiyCmWM;JTI@M3XXO3rHxTT97DtIz28E{h=kHVcDIMNdoP&^^ zTx>rsWyGDL;NY;-FGF_<{S^O-U=I{W3W6s^$N!e^ss=}9M(?TbN_2pe>BjP{9Gr^R z^esCnK_Yd!_lydZ{%)W+ocrAwPJSTUR06B70XANOD3D&q-n-;~U>3-+Q+z@7Yk<7P4V=V2np~R7P zF@)JRs7(d%6*tnRH9#cyfg^P>=`kS}e(I8TF>~RL1i7XNl{Fv0=Aw;u)I*l#16N>0 zD#ygg8L{2dh-_^M$;*)@a^hlA9!^{|z*|_4;P7-m!RpPJNfASYGArJF|4W#1tC5^$ zM@qT_X&fiAEjSi5q8PO*j{qgY)sZ@D(>;cxe2u{YN}){9` z{uErk3u37cvJ4ke(@cT51htSjjkD%$&j_S)9Z2UnnbDM$zl%7I9y24) z2bOJ!!}(YR+S)$HyYGI1?5tuavP+PdtjFNc<0MsJBeDEBMXr4WmK%xGlN zLbzN_Xm0uptu3FUs^SBbm%PWIQ&k{GUyWmDWT3X+vm^OI^cY@6^nC0nk#+n=+3#|- z&#%ZXg*>wa%B)hTvdf@RltZVifWBR%twMeEN5sd-*Vn*es70Qk9OvV7pjrR^b~q=v zTi)IiJwks9Gd+Hjp&D9cIjs40u$vo5RLzh_5;IAx#yWWIEof`}9J#tu6j)o}v$Q}c z$j6`vPT-B@2}4MEHNn`gW{0JjCT|e?ZFvn)$i2)aOpLhv)tdd1fYV$DcYY(>d5!Q{ z8xfyw4GalQ{goY(^v$WbT)#q7h3)%N|9o>)F zDDcLL#4^g8{Nl`mLh1zNo_A4bZDm+_K#Qlxtv^TQlU`kPPBS_>#sBKO6MRZPP25gg z-zC0=xP^G=#aV}Tz8R8SO!3jgL}D_DnT;!Pv@s#2(VkCCAVv{y6VDP4_7`7G+;Gix zH{C_yW5i3uTf`7zc$b*yEgnV;?yUDb@i1{GadYPiuzHXBM&kCXuf5@c8}E4N$s%se$`dHCwa&3lcQmy z3x|2)9%u(v07n8l0DV9P_yl+sxbAo;v(!-N8FNa$xzkSab0i|^6)Pmac8#RYJVU-e z<`{Wx+ifLyAAGkQ=*7>CjL07+o+!CvkCpV!J4>J|9lzu`M!^q$x=eA!SmeEKOZ==z8Yk!Q(rH@IO7w# zVgi9#WfUmbXJQQA;2??UAE6&`{tb41hwFd*^2=1YtqtZR($M|284}!w(yeG|q1@U^ zG#X2N3pW6c3Rf@Obx^Oud7r-iI&8l^(h|T0-Dk84JA;;H+)X#-+poSP4;Nho4ZuU; ze}RFf)8YPKGMQ`Md*u~svop9;Q;6Rb!0q4Xd-$WH)W#?H^urJNv0S-+1w00{0=}{o z3ckK*2YS}QsawO@Gt<-ELt~?Kbaml5F7p^91C9f>6`FMq zFb?$UeKXLca)Aa=)(kVMygsV~8vx@20HP1dYODcmfUnY70MJmtdi}1d*BkVm0YpE> np21al*n6-cV5LA&AbQQ8tW6I*j8hA%lm_PEK5;$?LXC~OSCqQeGQmv4a8d0KhNF($p}2%9=p@6HjrAWWXI zETQJRBEA3L{^P_ZZ3y;Ocwp`U7K)nPauw&#RC2z=Io20DT4NVx?jwC`@^lfMtyLK` ze5$0D>sZ`x%)-2KI#<*fgo)cJsMQ8?&8ASCF!Qa1Md0!3Z!iSCtMfG6%)Nx~NR=aY zvW&>6rmx4;CCU!rB6D1|_i;GcQn9!9!a`8*LMegoW2{D>a;5cARCf)?Usm zr2iW@f#{^hW8+PF2}W&hfJH|b5yd=;oQz%6Ya9}#bxMurSbx!>D(m4bC5Cd8a{(cf z#Tv&u77v(cDQ$GZjJ;%h-XYp{s==XaG=><~F4d#FxK>5l`50pr~0{U!ZSYcAVqfd@NuKM6~BP|4w89Z50-akyK?Qp3!bAH6AZ% z@iMh776HY(JDU~HO`N(oQN{YA8w>S>PmFuh*<4JPm9(rVX+=?zIa!essVr4ade`$( zHZpzIiZ(3wW~WmQr`xKvTaSiGv*>O#glc2Sj1R9$Ns-!g)OJ zoV(9}aa+2&K(798z_^x*8dj*&@s**rWD5@3Gqt*aPBz4Sj~L~jBUpJ zb=_HgPYfG1BW!ZVj1~2o!}XMLg)0>OiPP%)(+oSLEusWQjh!W`xo+3xcU{tC(RtiH(b0FnSWSn54r=}R=Fu1%y&>PJ# zYu<7hzkk~!@oS$y|(rDiT> zujz{TM&uskG2|uWV^#d&haVc%D?a~*e1yEHW-eyCeEwa5Tz9cH_aYA=k0XyD4ZdSpWb4 literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/images/dialog-warning.png b/cppcheck-2.14.0/gui/images/dialog-warning.png new file mode 100644 index 0000000000000000000000000000000000000000..196884717cdf38028ac7890ca1d8525830596b85 GIT binary patch literal 805 zcmV+=1KRwFP)*9EZPiPLg|bx!GsqYL#cBw)1M+wmGOZsM|qpyB*aY)Lz?WE+&(;-|tx8A2Ww- zl>>_5mIJ*v!rWV=oZvuWxS)~iVA<;(9qv=Mz4|7Y#x_W@6)CtXR$sp+5e(8%Q6UY( z80o;FJL_%H^+GKFYOlTAA1urLslML*pu5|>5$5?h-w}^>xT(0I!8Pa2LmL=C_ViE{ zi4ZkSQ^09E1a_b*CB(Tsjg5iT|NoD9_0^b{UB=4Xyt}j07!40PU~V_K72d70ENRY~ zMc{}dP!$!Zq9Tlg4x&g1N+S_X!R6Zpb|ASz$}uhN?P~Rm83fNchnAKW_S$PNLT8=D z%E^=I>FLqaaH9hww;9|5Z`Y?%QlB~%^Ncf)u^5qjZlh6TIE;D134|9frgp>#sl%Zi^U3a6$lVH?>w|zni54tDggIv5$r%enpQO?_Yp)WMDU=40DN#E6;c+~ zp`jtxtXV^6PmfG1b(jP8X2DJHa%S{sT}+rj_^PY&0KYx*$Rpfw#~uD!DnhPTNqc*{VZ%#!5+3-- z4#RkMzrH?Q&*e7$@WTK;z-t?>{k_Qt9Dq?&#A}~^y5ieq%Z_^;UKId0!L0V?=A!Dw zi*f(?2V9r!RUngLYCO*8-+nvuHYnA+MCu|K1j|AtlX*}4{0i{BV-s^f64zzTqD278 zPsI=%KN=oOLWrHvKd|T*IN^18b92AZ0_uTrKo>9w>;dfMjf44p@4OZ;*$xTsfd!NU jb)K;*pah5k8kklMw7@;Kcj? z-lSC#aeFoV&i01aQBwr{y9no33oHNJ-?GBqx>5CIOQuJ#pMe{;fRkTZDpeWu&q4&+ zjm3kLy0~^+7A0wBcxG=@a%B&jLa?9V-2-(n<;=WB760y&!lTQo$d6x!^3+XmpQ49{ z5<%S~q3{gBew23)c9u-*ss+n@@rPnVdgutGIdz7kaa*{{wn4nAD(;H~wU^DU_^D0AbCS|r(f(*(``5`fl z_OY#%r1th1nc}A_TGc*oDnh)P8p0J7@UWQ}A*T(I9XXWNd?!*v7UIrX#d3RVp$}zs zzA5IoV#!pln6se|n+OY|m6YHi-5L+&n;`63Vuq*lBB*~eM7tsCg%~j|qIcrlFf|lL>7p#IBdRl(pe$oQT<;F4 zaIk62EzfeI@t4IsH1g_N3; ziKxxDMt00bT(J~ZeN{}o9`kzGt3EAcB#DKHb`m4ir!9%~P#CF)>XhCnPnv?<;0~xw zA5P*4s4qGW_q)BSXj5$o#^~}HB=7atp=PDovFj1*+6+;SLd3hZq|MVsUU+8|MH-_% zdm2g;XCd9M2Ws+4Utt-ZAgl{oC!CMHiX4(>v{6Sg8037y%a*v!8VOI2pYtVO)1 z0usG75dFF(azk}cp1J@@9^Fusv>T7E8dja%C2>)aXU2S%a|jq;0-`1VLyzc0^gp_p z`8r%?t&kBs3)hacsJ2`wx+@g00enV}ceEhhRSW*3F42+b zN28c}ZWqh;e?G7wBVs!85;h>-Ve)2O>-CM=mSknN>*qlkWdMwfR;*eBJ5kS8<= zW1>qJBR1)hnV>k#bz(!niyjSjHlnJ7>jkeAW!O0S-VLV9_h>}Hrm+A3002ovPDHLkV1h(49lZbm literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/images/go-down.png b/cppcheck-2.14.0/gui/images/go-down.png new file mode 100644 index 0000000000000000000000000000000000000000..de946cffd40c91b49498d65cef90f1039db3ec6d GIT binary patch literal 750 zcmVm7uV1Y&IkCVaFS<{Cfpm%B9b?hl+Z5yf*clhyQSuG0Zjklso}bEQK0&^(8mm|K&^nMIGCPP&^_e z*D?-RQcD!1^x&Z!ZrFll1K+&)ukILN%*UJ+6YEOK^M*Hjj&<+9-*(;IeO`Ow^XTr} z<*4Ts;PBzg8kG6D@OTwK04P9dBG#FB^y>GqYQw_1lljN{Y#hu6HoL|fhO%LYGq+D% zxowtr`(+Qob_f`$MX`Sz+^T^au@KS(Wi%SWqu0HO9s4(VnFYt@1kzSF@6vSf^~QtC zw(dQ)h247TLnxu?AR!sZ64(f79=!TBq-USx`;KlqP+uWFZr7E5j$Z7i?Jsbte93A7U$yT z^B;l}BC$=o`GN-#73X38lG)V?_EV;`UFUesFqO?Ie{R--DYcilx8btWZ;2DW9g}BH zto12-ef=QsNT;98hyQY3Kkv-*$G-p8r>EI2!G!6fYfDQ?|6Vtkj%@d{Npt|P4Xl6% zf3knS>Y8i*u)~C;A5WjUX@vL%fHV*R+Qm(AQs`=&0HbavO6$HK?E$uJUA!ZWeN8D? zLdZ2DQ8Eorz>;Fu5E}Jn+su%#7TVN7Jb0ocFv6Iom4IlJMz%0w8laSt<{G!U&Kle3 gEGZX;Tq#3#3XqnJQlm-u%K!iX07*qoM6N<$g0I+Y#{d8T literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/images/go-home.png b/cppcheck-2.14.0/gui/images/go-home.png new file mode 100644 index 0000000000000000000000000000000000000000..6db5e2b4934a86336f305c5201a0213ea157dffe GIT binary patch literal 726 zcmV;{0xA88P)qamLF4UN7O zL{l_c;zQ7M3dMSQoWT3GB-%2#8cmrySE7ldM>ic;quI-rpF`KWbFta5Y-Adp8RA?US2L)~tf#t{oDOtsj@@9*G_~q9ixD?%fSrJ8QVRyWy*| zv&5qtg3G6W<_wCD9h1YJdY5tI<~6*1_ZHv3f5ZFt@9@*t7ZM*iQjMc3+D4x3;lpys z|C$TDeZ27H%V)&K#&9t)F$fC_!@pOr;L)ay#A}1Q30MvUdwas)-w%RYzK9*v9S#M`T4=$-(MZS+X5a`6>dWfphdo{qsI^9 zf$Lp7biEH(H>C&sL|}S7os|sn1r8>rx%49@H>uCbg2Gmc*^am4x+ZAHdJddr&$yBAAwKg@6ms%g#Z8m07*qo IM6N<$g4L*GTL1t6 literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/images/go-next.png b/cppcheck-2.14.0/gui/images/go-next.png new file mode 100644 index 0000000000000000000000000000000000000000..dd318ec890f092eeb04813fe69eb697ae76bcedd GIT binary patch literal 819 zcmV-31I+x1P)3f8u5+j_Qbqcyf|d!|u!@$Sv58k;Bi=XCVzvcfsXSj@!2 zSH5LzWt9Ku`;n{~RrrcG41WCKr-e&ubKmoPXZ<{?@E1=_!@Ny@hSH0oXB|~QpIxwk z(@}xHes(s3k!Ea;T^jMHZ15XFa_iPVdF!CyE8a3H!Egk+jjAi*&mW(*XP{^W&N|{U zq~yfd>-_VIh)S;ue9KD*hQ*P6^~?-(9?ls!IGEU5b7nz-inEWr0u?o7Y}wWsjf9XJ zeLq?W1Hzv@H3>WO7C0!EBq*a277_7VB=(u&$hoCr$>ynTp>WHCJHIC@`-MM#;vaEj zv*C^exkNd2QL$|h(J6;sh(q@}5lc2r3pO;@e&qeZ{;^N^{l|XChRn$b*{!f?1ab`3 zgrS>&s(^!nF#-wO#KKYgorW_Gy##AL3);$Ril%tJBQNy|zx2?jm>c(*0663*DxeuE zG+hx}gQ=Us*Pt*3gi;*6?-@A#pewM>vp85@Q@qaet$lZ|@UsuRhlL5>z(tKcROsgJ zRDr9S0y&&(?SWy(?^4E1s!g3UmMBK|D+feZn_TFnRTzJUss3~pY%T_II zt1pN=p1Oc-2VhHB6cMH^VTtMh_PFeIy+DA6q1y=5di9Bq%$+rpaPg6MBQrgdZrZk? zuF%ULEA!JF7wlBXh4%m;+f&rzw>T5;YfpXzi?zZV<+0+DqVv-ikyn~aQR_hAPH0#* zX!yBD-olzKORcP|EKlNe_6?1o&4GG@gIz8~w@han0**WMz@x3A?6S#8GwCb z%<2Y*+?(g(dpe-eY3rA~q|It616>K97KEv|?!TxGenJ}_ci0B(U-AvcT3UhW=Wf3+>{Ip>gT z8mw{Y02h_K-bJTxzA|R<(ck{R~i&G~wD)o~Sos z5CjneAvZpI*3#e|Fmv(Fm%4)*>zm#@Z1Rh>o0hLtghl2GwQ zN1H>YdgpRQRN1)$t$&j(VIX>|}JTi9N*bTBf9v^EW zR7x7PeAe$*8pn8`6exxx1dm}h89R{iEuTp)Y}xX^AHMkZqf#MXWG<72j!6QZDczR= z@kO-dv*}Ic<9l_Rn1AIQ(ljH6eCV{(E&Cf0S!!tSP)5Qb-8J=@lz5Zg8uv2EMQnzOcT+qP}nwxU0MPh~1rSADB<&$r$G?HbB7~QpelUR4cR;^*~!Sj4SOxdZ2qYB=1xeSeMeHa^mfWhH0 zR5WzKz&#bKNxb5W4HT_nN#4tK&gH-2W$`>>*fA&@ZsB=MI(xq{8~IJ;9#sVKE6!Md z$q^B0m2e5mgRr<-J9)l-UhKH}iXECe`Z4dIF=ihy#-d{ua3gV8Y9;QOM&l$u5H1l# z5L8#+Ft*?I!j@a^*e&hJe&2e{f$MtL^WthPhj`EnV|j8Jt~_H4@vF|*eA5j(?s;La zoFC3VB z56SDUtP>_x4@G8aD+E>F){k4}iMVVSi*q`W?EIEV46Ww?m-7h0#FL1jHrmV0*PU3m z8>ri@w>_{|-VevrLJ*K#2|?Ah4M52$6VkS6xNVt?tHyCSryB`riv+ZG4MIem-$O9* zN5ohRjUjCk$+DH8Y^htyFp!N~-`)>F*@TyY@16%0*Br<@WN>oUxot=x;&W;tDyber z@Obovljj+^CDY2}(|+1PielI$;o@ZjS;1Y@?kEFSf zcg%oOWGS5wVe&TZA#w=6_S22lBy#s{mvQdx2Gb@SRSCo~eh7=>nn^tFSS3ToE)8-t zzLHZ0(rE=dXqp{2ori_R`DY290N(Szko;@4sJ{Mv!RcMX8&$jaOS_F)`DHy$D{Mq}{|F%#3bTB g;^(JAq30z}0F_twm6w}6V*mgE07*qoM6N<$f}Y(%5dZ)H literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/images/llvm-dragon.png b/cppcheck-2.14.0/gui/images/llvm-dragon.png new file mode 100644 index 0000000000000000000000000000000000000000..e1a9e190f889a668ad69a52f0642490a3fe13955 GIT binary patch literal 9482 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 literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/images/llvm-dragon.svg b/cppcheck-2.14.0/gui/images/llvm-dragon.svg new file mode 100644 index 00000000..57b5903e --- /dev/null +++ b/cppcheck-2.14.0/gui/images/llvm-dragon.svg @@ -0,0 +1 @@ +Dragon \ No newline at end of file diff --git a/cppcheck-2.14.0/gui/images/media-floppy.png b/cppcheck-2.14.0/gui/images/media-floppy.png new file mode 100644 index 0000000000000000000000000000000000000000..ce725b054b07c3689cbc6b5dbd22354ca1c95d0e GIT binary patch literal 671 zcmV;Q0$}}#P)uU|Z@ByECmp(*H5+wydiYXsHzbfyj1H3FPOLrFfMQf$ zOFXvM8FYFTcRcv&bq&C3W<34gBpcs$6{GORX2-GJMk@f+L!8`6$yxin>llSF`+dIu{(Jrn8*Q`^8M)(S$L>NdGOboC3jQmSQzZdV z17aaLqas%s1?KK$$E1|_=9_Q+a}+KoRb|G>%Z{nn>;EqbS2UHQ2(h~u6}gjK<;(Nf zJnHfnQ1(j*fyg5#pb*WZ=Ks@sG#8ZrCh zJ(dfXXn*|_2OMw!%P+q?&1REEqY-1C4}Wpb$HvCE>#n<4dgZMl%tXDXQd}vb$S1G5 z>Z&2Q`R1F+oOt4i^m;wo?RJRG3;<7wm`4^x6#06&M80xMFTHehSTZ|18^Ym_;Q=uc zL>&TZaVta?Tb4@7;yA2iZf>rW#t(AGwWxKVN=3(2<$#krUR+#^!haSU(U3VvV32ag z2J_#I%`Uh0DogX-*g}?sAXDy@aRhQCBaleBW5FJ8e*WX*+itM>a_g+NEFS|iGc!@R z+itrt7z|=Q%(xVXz|2^A`4(n%K)9ek2(e5OI~{%Z4hD}1R#<=XzBw~*VL5KhV002ovPDHLk FV1gMDI_>}f literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/images/openproject.png b/cppcheck-2.14.0/gui/images/openproject.png new file mode 100644 index 0000000000000000000000000000000000000000..63df868d1e4cd06f6d8f916361670234897250a8 GIT binary patch literal 1021 zcmVL~GtanD6v0{x!WcuR>^{48SFc{Z;=cRt`)Sv%UF|{ygb+!sRiQpQP=u!V(rXsv1GW-@3(Ua5J`&D?e-{vGXmL}$Ye5ZyY03{v)M$e)dERT5uEcg*PiEL!-fqc zd2WHl#l?6q7&!CubCkxEz+{aRhG90E$)b0ZX#KoDhU?a?6Dhpk?}HMc>h*eA(S26g z{RKT6jYcF;#W^_hoAB0KZ?Eg1_7b*}eceTg|Z0eFu&8iELNHB(OuE1b01SJqAi)hqJ@P+~$=fIx-MRsWb zM2Ut>6ku5!#KZ`aYPEv7xj7`7p_QBN_d_RNMI(#3+;~3I<>l3J9%BTW;9bUphRUKnO4xw~j;^c%cNXyeK$l=!_z8#>1s`>rCz* zNP!n=+;DvbwTgpEnHML(7~)uv$OuHv2qHR5lrUR!&?s3H32oXz6oDBtoDx{MawU7} zsi(9T3JjMmj&?@iJ%*xbw?}ErqiyR}uB@m32881YIG*RN*?fe(FJ|HH-QPx$SisQP z$oOD;+b`eb{X;@2v??<**VQX?*IHQ48J8#krL`7GTvv=K<#+l3PNhBjlLDCp2T(bk rj6!i#0K@=9+Rx-B@ZSasjCM`|v5B@l`8Q{D00000NkvXXu0mjfAc)(# literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/images/preferences-system.png b/cppcheck-2.14.0/gui/images/preferences-system.png new file mode 100644 index 0000000000000000000000000000000000000000..6e92b618a6f76a078bd9ef7841fe23037ee42b20 GIT binary patch literal 796 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 Z008rQ=0+00RS*CG002ovPDHLkV1k&mk!t_| literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/images/showstylewarnings.png b/cppcheck-2.14.0/gui/images/showstylewarnings.png new file mode 100644 index 0000000000000000000000000000000000000000..70de0695f8cf3c473f1fac2994b2f17064465cf4 GIT binary patch literal 1006 zcmVtW6I*j8hA%lm_PEK5;$?LXC~OSCqQeGQmv4a8d0KhNF($p}2%9=p@6HjrAWWXI zETQJRBEA3L{^P_ZZ3y;Ocwp`U7K)nPauw&#RC2z=Io20DT4NVx?jwC`@^lfMtyLK` ze5$0D>sZ`x%)-2KI#<*fgo)cJsMQ8?&8ASCF!Qa1Md0!3Z!iSCtMfG6%)Nx~NR=aY zvW&>6rmx4;CCU!rB6D1|_i;GcQn9!9!a`8*LMegoW2{D>a;5cARCf)?Usm zr2iW@f#{^hW8+PF2}W&hfJH|b5yd=;oQz%6Ya9}#bxMurSbx!>D(m4bC5Cd8a{(cf z#Tv&u77v(cDQ$GZjJ;%h-XYp{s==XaG=><~F4d#FxK>5l`50pr~0{U!ZSYcAVqfd@NuKM6~BP|4w89Z50-akyK?Qp3!bAH6AZ% z@iMh776HY(JDU~HO`N(oQN{YA8w>S>PmFuh*<4JPm9(rVX+=?zIa!essVr4ade`$( zHZpzIiZ(3wW~WmQr`xKvTaSiGv*>O#glc2Sj1R9$Ns-!g)OJ zoV(9}aa+2&K(798z_^x*8dj*&@s**rWD5@3Gqt*aPBz4Sj~L~jBUpJ zb=_HgPYfG1BW!ZVj1~2o!}XMLg)0>OiPP%)(+oSLEusWQjh!W`xo+3xcU{tC(RtiH(b0FnSWSn54r=}R=Fu1%y&>PJ# zYu<7hzkk~!@oS$y|(rDiT> zujz{TM&uskG2|uWV^#d&haVc%D?a~*e1yEHW-eyCeEwa5Tz9cH_aYA=k0XyD4ZdSpWb4 literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/images/showwarnings.png b/cppcheck-2.14.0/gui/images/showwarnings.png new file mode 100644 index 0000000000000000000000000000000000000000..196884717cdf38028ac7890ca1d8525830596b85 GIT binary patch literal 805 zcmV+=1KRwFP)*9EZPiPLg|bx!GsqYL#cBw)1M+wmGOZsM|qpyB*aY)Lz?WE+&(;-|tx8A2Ww- zl>>_5mIJ*v!rWV=oZvuWxS)~iVA<;(9qv=Mz4|7Y#x_W@6)CtXR$sp+5e(8%Q6UY( z80o;FJL_%H^+GKFYOlTAA1urLslML*pu5|>5$5?h-w}^>xT(0I!8Pa2LmL=C_ViE{ zi4ZkSQ^09E1a_b*CB(Tsjg5iT|NoD9_0^b{UB=4Xyt}j07!40PU~V_K72d70ENRY~ zMc{}dP!$!Zq9Tlg4x&g1N+S_X!R6Zpb|ASz$}uhN?P~Rm83fNchnAKW_S$PNLT8=D z%E^=I>FLqaaH9hww;9|5Z`Y?%QlB~%^Ncf)u^5qjZlh6TIE;D134|9frgp>#sl%Zi^U3a6$lVH?>w|zni54tDggIv5$r%enpQO?_Yp)WMDU=40DN#E6;c+~ zp`jtxtXV^6PmfG1b(jP8X2DJHa%S{sT}+rj_^PY&0KYx*$Rpfw#~uD!DnhPTNqc*{VZ%#!5+3-- z4#RkMzrH?Q&*e7$@WTK;z-t?>{k_Qt9Dq?&#A}~^y5ieq%Z_^;UKId0!L0V?=A!Dw zi*f(?2V9r!RUngLYCO*8-+nvuHYnA+MCu|K1j|AtlX*}4{0i{BV-s^f64zzTqD278 zPsI=%KN=oOLWrHvKd|T*IN^18b92AZ0_uTrKo>9w>;dfMjf44p@4OZ;*$xTsfd!NU jb)K;*pah5k8kXz@bn(yMH)60V?)zSvrcuS_gc-3Yil-ogwrvsf ztcn@kNL|-eK@6Ih6(Hq6UhcE&I)QN<1%_d;6C;JXu7#12P=x@|G))hv;@`g?Q){z| zd7eQPo6Alua=5OB>(Xu8WLXwn%zlv}wk=-Q^&>H`*K5pnOj`jw$0df^4s>aZQHhOBereZCwDsL_4!NEY7?GA3b&$|0t92#yHPm2&h~{sX%`5&)t{@U-sZN3hhVxqwv&Or1gadu5uFl=s@h1zrp7?)o&bs) zmw2dMW1(`2hSDLNtEb+|nzF~kDu+<0ouXJ{Jo@`0asBdkR5!#z=aT_Fi;jzTI&@yC z(0V3P-PlF80bx25t$QNkG83?GM+5+1>!v7}0`p-C%11(eIt&4M(D`O!x+)kSkJI7B zA9(}A6ZxK4Y>GlpZx}n>o=xSSl>lb?vlA=IYJ8eTr1rGveBY2KF@T@8wI>}3(?)Vh@s95az{nIl$v{cdH7nDLhI#q5|q}6*qIAZ zQce#K71gDWzZJm=61tyZ=^A`4jKP=kBt++12#zVV;d?_SzSX4TOIZ@_|9D9l-k<9? zF#PUJFMPNl5TEiQS#tuuR;5A{89?_}9~Xk3E%^-3!nbVu=aP6<7s2WS?tvfkNx#p* zi-PevKZ?%kTWvaiHs>L})Dgcsm*Z#qV*F^%rL+E8mdM~Jd^j(VdYfA7ABK;N>?cIM zZJo^z!7S&od+$!H-LM*6z3pgeYeZdR^`O$y-biRick5l)Xw=`BJv9sZoV?8M9$s$t z3_!pU7a!|Qt*Xh!&e{iLCIbjS8S+4^2gD%T+>o#@HjJjm9;#O!ViqRgz=8#EU`(}0 Z008rQ=0+00RS*CG002ovPDHLkV1k&mk!t_| literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/images/verify.svg b/cppcheck-2.14.0/gui/images/verify.svg new file mode 100644 index 00000000..41cdf3c0 --- /dev/null +++ b/cppcheck-2.14.0/gui/images/verify.svg @@ -0,0 +1,74 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + diff --git a/cppcheck-2.14.0/gui/images/view-recheck.png b/cppcheck-2.14.0/gui/images/view-recheck.png new file mode 100644 index 0000000000000000000000000000000000000000..caebff48a8c7520677775b7e6400fdd9ce9eca92 GIT binary patch literal 1253 zcmVd8!|{t78d2T0xkybuDimrr*oP8gJqYQ{%dExb9eTh`}v)F&iS5Q_P>5& zW7Tf|B`!#)*bvN9dn_5i=I8l^h?T#F#486l0?N!HC~J^VKE)L)G#sJ)6iYxxGl!^B93+xYBlPdXry3u;kydEr#F5W9K=CBb>!}3>31+o3 zy?73TAy^~_4nf&4yP24blIgGHoz_Y`w4!4a>a>t0ZMQXCpMdC;0#o22 zg^vR`1haiRv&@E9TXsAVu>Z|9l`Mm9Sds|lTki-==+9Fgy`4;DRXhTSbQn~U%z!{dXY&{ zuZ1E!AWd==I<B>#os!d@!WFCP8HDl$S5vbsPP#oj zQDrG_9fA{;8c0QO&(IVg!^|M$o({n{R>PR>k&h+aFCRYL>q*aJWOm)di=hmH+>Fy; zlw-E78a-w@3D*$Xf7<|Tf!yLW$McZv?1vmNi=ncon0#s!$I6Dawgih57BqzgXWK$U z@t!TtA#$@ zJ;=&!reE_rS7o7%faNGlJNpMOC8r}j>VtxtxCX|Z!MG2G^(Hubp&x$l9)o@a?><^; z#yXy7y{~xI2`nIA!9(@@V^`{Sd|0ff?Hrn*Y<2=O336@(!EJqdC`3D7>Y14D^yjj6 zD@Uj>q5HmN#*uqzqp*ciE+*0xo|JD5OHk2{v+YPLoMFdNJeT0n$@@^>t%DoG)6jME zE~s$<)n6NjYP9i}WXG)d$2!CAPfCqp@k(@=3f}pwg0?gKTzVaF``wE?C|T(Q#*A!@ zJ!23ol>ZZgi{%@t&D?Th9B?7cTebpK+&VJ9AIebl##3JzgfrC|h)7l$SiC%*t6sQa zM{TdAs0|yUL=h`i`}l8+$vzS)l4-@qE@|Ud?T}Icw5;q;aC#2Zm)SQMnTvX<* z@BWaD3@_9g)QW}Rl?*;<_J1>D7k+;pwF$*Uh5BrZi4T15cmta+#X8HZQAtWOHg1MZ zkTc8!IkmpQ`(iQ94@EcF6ZPmq@bWGDbN1kME$V4Id?P9V#X@mWVQ#C|ZeQmgv3Hf1 zUo^ckP_Lq1LiwWB(FxM5rJU<#4_J-zKzZ8_^R;K?WB<*=9^;O3`cM29Yap{ah}mc( P00000NkvXXu0mjfb}vwM literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/images/view-refresh.png b/cppcheck-2.14.0/gui/images/view-refresh.png new file mode 100644 index 0000000000000000000000000000000000000000..5fadc405f86f73574a622ef2e0cfceecb11c6322 GIT binary patch literal 610 zcmV-o0-gPdP)R&cm23F%l}njQ5y%YZXNYH%19 zlQJ91wyk%aVyYvJ(&}2y1-c`MjqeYXIS1EHo%hN zQewcF(FbhiNx_E#xz=o*UQp;230TZlUX5J7&@|Pyx1k(I_#?7^nsM^Ai%YJ(Txe$G z;-;Pg(BcZ$hWGWmO%j$=>r{LBDY$f!G4eHlRjj7PEiM*(?ohwVv@s;FkhQI2$W2Z$ zPLG=atgBFgokPmeY6BbUb(;>)3E0S$di>yEe*|iF9>~=_|C9JErP+!0_oM60c8M8k z?ImCn&xB%jxZi9nQ(~4k;5}D|<83#eU^%Mo;$qVx3F~*SWc0Z^X8EDCHZOUxn`X$>k&O|LS?IjZ&WVP@`xL5hnBZ5(`%jK?iO5zd+n#`B z?CgW!{@EsLau|?iPjznB;C7`hkftLl830B+A>Wn3Ua_OqdtknDT_m8u=8iJi*=EO8 zuNy$A?VN9_^P*>*X101~2{GbPL7bwRp9_|7f`OZj>UO5;A9e3_@5_ wg#r-5Kp?|TlvE)Bk)s@FFjxW8aV9<`0M3?Lle#2mXaE2J07*qoM6N<$f_L;O1^@s6 literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/gui/libraryaddfunctiondialog.cpp b/cppcheck-2.14.0/gui/libraryaddfunctiondialog.cpp new file mode 100644 index 00000000..225176b0 --- /dev/null +++ b/cppcheck-2.14.0/gui/libraryaddfunctiondialog.cpp @@ -0,0 +1,54 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "libraryaddfunctiondialog.h" + +#include "ui_libraryaddfunctiondialog.h" + +#include +#include +#include +#include + +class QWidget; + +LibraryAddFunctionDialog::LibraryAddFunctionDialog(QWidget *parent) : + QDialog(parent), + mUi(new Ui::LibraryAddFunctionDialog) +{ + mUi->setupUi(this); + static const QRegularExpression rx(NAMES); + QValidator *validator = new QRegularExpressionValidator(rx, this); + mUi->functionName->setValidator(validator); +} + +LibraryAddFunctionDialog::~LibraryAddFunctionDialog() +{ + delete mUi; +} + +QString LibraryAddFunctionDialog::functionName() const +{ + return mUi->functionName->text(); +} + +int LibraryAddFunctionDialog::numberOfArguments() const +{ + return mUi->numberOfArguments->value(); +} + diff --git a/cppcheck-2.14.0/gui/libraryaddfunctiondialog.h b/cppcheck-2.14.0/gui/libraryaddfunctiondialog.h new file mode 100644 index 00000000..d39666de --- /dev/null +++ b/cppcheck-2.14.0/gui/libraryaddfunctiondialog.h @@ -0,0 +1,51 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIBRARYADDFUNCTIONDIALOG_H +#define LIBRARYADDFUNCTIONDIALOG_H + +#include +#include +#include + +class QWidget; +namespace Ui { + class LibraryAddFunctionDialog; +} + +#define SIMPLENAME "[_a-zA-Z][_a-zA-Z0-9]*" // just a name +#define SCOPENAME SIMPLENAME "(::" SIMPLENAME ")*" // names with optional scope +#define NAMES SCOPENAME "(," SCOPENAME ")*" // names can be separated by comma + +class LibraryAddFunctionDialog : public QDialog { + Q_OBJECT + +public: + explicit LibraryAddFunctionDialog(QWidget *parent = nullptr); + LibraryAddFunctionDialog(const LibraryAddFunctionDialog &) = delete; + ~LibraryAddFunctionDialog() override; + LibraryAddFunctionDialog &operator=(const LibraryAddFunctionDialog &) = delete; + + QString functionName() const; + int numberOfArguments() const; + +private: + Ui::LibraryAddFunctionDialog *mUi; +}; + +#endif // LIBRARYADDFUNCTIONDIALOG_H diff --git a/cppcheck-2.14.0/gui/libraryaddfunctiondialog.ui b/cppcheck-2.14.0/gui/libraryaddfunctiondialog.ui new file mode 100644 index 00000000..e6c5e2c4 --- /dev/null +++ b/cppcheck-2.14.0/gui/libraryaddfunctiondialog.ui @@ -0,0 +1,120 @@ + + + LibraryAddFunctionDialog + + + Qt::WindowModal + + + + 0 + 0 + 353 + 89 + + + + + 0 + 0 + + + + Add function + + + true + + + + + + + + Function name(s) + + + + + + + + + + Number of arguments + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + LibraryAddFunctionDialog + accept() + + + 57 + 71 + + + 71 + 87 + + + + + buttonBox + rejected() + LibraryAddFunctionDialog + reject() + + + 132 + 69 + + + 156 + 87 + + + + + + validateName(QString) + + diff --git a/cppcheck-2.14.0/gui/librarydialog.cpp b/cppcheck-2.14.0/gui/librarydialog.cpp new file mode 100644 index 00000000..4469940f --- /dev/null +++ b/cppcheck-2.14.0/gui/librarydialog.cpp @@ -0,0 +1,367 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "librarydialog.h" + +#include "common.h" +#include "libraryaddfunctiondialog.h" +#include "libraryeditargdialog.h" +#include "path.h" +#include "utils.h" + +#include "ui_librarydialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class QWidget; + +// TODO: get/compare functions from header + +namespace { + class FunctionListItem : public QListWidgetItem { + public: + FunctionListItem(QListWidget *view, + CppcheckLibraryData::Function *function, + bool selected) + : QListWidgetItem(view), function(function) { + setText(function->name); + setFlags(flags() | Qt::ItemIsEditable); + setSelected(selected); + } + CppcheckLibraryData::Function *function; + }; +} + +LibraryDialog::LibraryDialog(QWidget *parent) : + QDialog(parent), + mUi(new Ui::LibraryDialog) +{ + mUi->setupUi(this); + mUi->buttonSave->setEnabled(false); + mUi->buttonSaveAs->setEnabled(false); + mUi->sortFunctions->setEnabled(false); + mUi->filter->setEnabled(false); + mUi->addFunction->setEnabled(false); + + //As no function selected, this disables function editing widgets + selectFunction(); +} + +LibraryDialog::~LibraryDialog() +{ + delete mUi; +} + +CppcheckLibraryData::Function *LibraryDialog::currentFunction() +{ + QList selitems = mUi->functions->selectedItems(); + if (selitems.count() != 1) + return nullptr; + return static_cast(selitems.first())->function; +} + +void LibraryDialog::openCfg() +{ + const QString datadir = getDataDir(); + + QString selectedFilter; + const QString filter(tr("Library files (*.cfg)")); + const QString selectedFile = QFileDialog::getOpenFileName(this, + tr("Open library file"), + datadir, + filter, + &selectedFilter); + + if (selectedFile.isEmpty()) + return; + + QFile file(selectedFile); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + QMessageBox msg(QMessageBox::Critical, + tr("Cppcheck"), + tr("Cannot open file %1.").arg(selectedFile), + QMessageBox::Ok, + this); + msg.exec(); + return; + } + + CppcheckLibraryData tempdata; + const QString errmsg = tempdata.open(file); + if (!errmsg.isNull()) { + QMessageBox msg(QMessageBox::Critical, + tr("Cppcheck"), + tr("Failed to load %1. %2.").arg(selectedFile).arg(errmsg), + QMessageBox::Ok, + this); + msg.exec(); + return; + } + + mIgnoreChanges = true; + mData.swap(tempdata); + mFileName = selectedFile; + mUi->buttonSave->setEnabled(false); + mUi->buttonSaveAs->setEnabled(true); + mUi->filter->clear(); + mUi->functions->clear(); + for (CppcheckLibraryData::Function &function : mData.functions) { + mUi->functions->addItem(new FunctionListItem(mUi->functions, + &function, + false)); + } + mUi->sortFunctions->setEnabled(!mData.functions.empty()); + mUi->filter->setEnabled(!mData.functions.empty()); + mUi->addFunction->setEnabled(true); + mIgnoreChanges = false; +} + +void LibraryDialog::saveCfg() +{ + if (mFileName.isNull()) + return; + QFile file(mFileName); + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { + QTextStream ts(&file); + ts << mData.toString() << '\n'; + mUi->buttonSave->setEnabled(false); + } else { + QMessageBox msg(QMessageBox::Critical, + tr("Cppcheck"), + tr("Cannot save file %1.").arg(mFileName), + QMessageBox::Ok, + this); + msg.exec(); + } +} + +void LibraryDialog::saveCfgAs() +{ + const QString filter(tr("Library files (*.cfg)")); + const QString path = Path::getPathFromFilename(mFileName.toStdString()).c_str(); + QString selectedFile = QFileDialog::getSaveFileName(this, + tr("Save the library as"), + path, + filter); + if (selectedFile.isEmpty()) + return; + + if (!selectedFile.endsWith(".cfg", Qt::CaseInsensitive)) + selectedFile += ".cfg"; + + mFileName = selectedFile; + saveCfg(); +} + +void LibraryDialog::addFunction() +{ + auto *d = new LibraryAddFunctionDialog; + if (d->exec() == QDialog::Accepted && !d->functionName().isEmpty()) { + + CppcheckLibraryData::Function f; + f.name = d->functionName(); + const int args = d->numberOfArguments(); + + for (int i = 1; i <= args; i++) { + CppcheckLibraryData::Function::Arg arg; + arg.nr = i; + f.args.append(arg); + } + mData.functions.append(f); + mUi->functions->addItem(new FunctionListItem(mUi->functions, &mData.functions.back(), false)); + mUi->buttonSave->setEnabled(true); + mUi->sortFunctions->setEnabled(!mData.functions.empty()); + mUi->filter->setEnabled(!mData.functions.empty()); + } + delete d; +} + +void LibraryDialog::editFunctionName(QListWidgetItem* item) +{ + if (mIgnoreChanges) + return; + QString functionName = item->text(); + CppcheckLibraryData::Function * const function = dynamic_cast(item)->function; + if (functionName != function->name) { + const QRegularExpressionMatch matchRes = QRegularExpression("^" NAMES "$").match(functionName); + if (matchRes.hasMatch()) { + function->name = functionName; + mUi->buttonSave->setEnabled(true); + } else { + mIgnoreChanges = true; + item->setText(function->name); + mIgnoreChanges = false; + } + } +} + +void LibraryDialog::selectFunction() +{ + const CppcheckLibraryData::Function * const function = currentFunction(); + + if (function == nullptr) { + mUi->comments->clear(); + mUi->comments->setEnabled(false); + + mUi->noreturn->setCurrentIndex(0); + mUi->noreturn->setEnabled(false); + + mUi->useretval->setChecked(false); + mUi->useretval->setEnabled(false); + + mUi->leakignore->setChecked(false); + mUi->leakignore->setEnabled(false); + + mUi->arguments->clear(); + mUi->arguments->setEnabled(false); + + mUi->editArgButton->setEnabled(false); + return; + } + + mIgnoreChanges = true; + mUi->comments->setPlainText(function->comments); + mUi->comments->setEnabled(true); + + mUi->noreturn->setCurrentIndex(function->noreturn); + mUi->noreturn->setEnabled(true); + + mUi->useretval->setChecked(function->useretval); + mUi->useretval->setEnabled(true); + + mUi->leakignore->setChecked(function->leakignore); + mUi->leakignore->setEnabled(true); + + updateArguments(*function); + mUi->arguments->setEnabled(true); + + mUi->editArgButton->setEnabled(true); + mIgnoreChanges = false; +} + +void LibraryDialog::sortFunctions(bool sort) +{ + if (sort) { + mUi->functions->sortItems(); + } else { + mIgnoreChanges = true; + const CppcheckLibraryData::Function* selfunction = currentFunction(); + mUi->functions->clear(); + for (CppcheckLibraryData::Function &function : mData.functions) { + mUi->functions->addItem(new FunctionListItem(mUi->functions, + &function, + selfunction == &function)); + } + if (!mUi->filter->text().isEmpty()) + filterFunctions(mUi->filter->text()); + mIgnoreChanges = false; + } +} + +void LibraryDialog::filterFunctions(const QString& filter) +{ + QList allItems = mUi->functions->findItems(QString(), Qt::MatchContains); + + if (filter.isEmpty()) { + for (QListWidgetItem *item : allItems) { + item->setHidden(false); + } + } else { + for (QListWidgetItem *item : allItems) { + item->setHidden(!item->text().startsWith(filter)); + } + } +} + +void LibraryDialog::changeFunction() +{ + if (mIgnoreChanges) + return; + + CppcheckLibraryData::Function *function = currentFunction(); + if (!function) + return; + + function->comments = mUi->comments->toPlainText(); + function->noreturn = (CppcheckLibraryData::Function::TrueFalseUnknown)mUi->noreturn->currentIndex(); + function->useretval = mUi->useretval->isChecked(); + function->leakignore = mUi->leakignore->isChecked(); + + mUi->buttonSave->setEnabled(true); +} + +void LibraryDialog::editArg() +{ + CppcheckLibraryData::Function *function = currentFunction(); + if (!function) + return; + + if (mUi->arguments->selectedItems().count() != 1) + return; + CppcheckLibraryData::Function::Arg &arg = function->args[mUi->arguments->row(mUi->arguments->selectedItems().first())]; + + LibraryEditArgDialog d(nullptr, arg); + if (d.exec() == QDialog::Accepted) { + const unsigned number = arg.nr; + arg = d.getArg(); + arg.nr = number; + mUi->arguments->selectedItems().first()->setText(getArgText(arg)); + } + mUi->buttonSave->setEnabled(true); +} + +QString LibraryDialog::getArgText(const CppcheckLibraryData::Function::Arg &arg) +{ + QString s("arg"); + if (arg.nr != CppcheckLibraryData::Function::Arg::ANY) + s += QString::number(arg.nr); + + s += "\n not bool: " + QString(bool_to_string(arg.notbool)); + s += "\n not null: " + QString(bool_to_string(arg.notnull)); + s += "\n not uninit: " + QString(bool_to_string(arg.notuninit)); + s += "\n format string: " + QString(bool_to_string(arg.formatstr)); + s += "\n strz: " + QString(bool_to_string(arg.strz)); + s += "\n valid: " + QString(arg.valid.isEmpty() ? "any" : arg.valid); + for (const CppcheckLibraryData::Function::Arg::MinSize &minsize : arg.minsizes) { + s += "\n minsize: " + minsize.type + " " + minsize.arg + " " + minsize.arg2; + } + return s; +} + +void LibraryDialog::updateArguments(const CppcheckLibraryData::Function &function) +{ + mUi->arguments->clear(); + for (const CppcheckLibraryData::Function::Arg &arg : function.args) { + mUi->arguments->addItem(getArgText(arg)); + } +} diff --git a/cppcheck-2.14.0/gui/librarydialog.h b/cppcheck-2.14.0/gui/librarydialog.h new file mode 100644 index 00000000..ac27d427 --- /dev/null +++ b/cppcheck-2.14.0/gui/librarydialog.h @@ -0,0 +1,66 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIBRARYDIALOG_H +#define LIBRARYDIALOG_H + +#include "cppchecklibrarydata.h" + +#include +#include +#include + +class QListWidgetItem; +class QWidget; +namespace Ui { + class LibraryDialog; +} + +class LibraryDialog : public QDialog { + Q_OBJECT + +public: + explicit LibraryDialog(QWidget *parent = nullptr); + LibraryDialog(const LibraryDialog &) = delete; + ~LibraryDialog() override; + LibraryDialog &operator=(const LibraryDialog &) = delete; + +private slots: + void openCfg(); + void saveCfg(); + void saveCfgAs(); + void addFunction(); + void changeFunction(); + void editArg(); + void editFunctionName(QListWidgetItem* /*item*/); + void filterFunctions(const QString& /*filter*/); + void selectFunction(); + void sortFunctions(bool /*sort*/); + +private: + Ui::LibraryDialog *mUi; + CppcheckLibraryData mData; + QString mFileName; + bool mIgnoreChanges{}; + + static QString getArgText(const CppcheckLibraryData::Function::Arg &arg); + CppcheckLibraryData::Function *currentFunction(); + void updateArguments(const CppcheckLibraryData::Function &function); +}; + +#endif // LIBRARYDIALOG_H diff --git a/cppcheck-2.14.0/gui/librarydialog.ui b/cppcheck-2.14.0/gui/librarydialog.ui new file mode 100644 index 00000000..0cf2f33e --- /dev/null +++ b/cppcheck-2.14.0/gui/librarydialog.ui @@ -0,0 +1,539 @@ + + + LibraryDialog + + + + 0 + 0 + 869 + 588 + + + + Library Editor + + + + + + + + Open + + + + + + + Save + + + + + + + Save as + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + Functions + + + + + + + + + Sort + + + true + + + false + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + QAbstractItemView::DoubleClicked + + + QAbstractItemView::SelectRows + + + + + + + + + Add + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Filter: + + + + + + + + + + + + + + + + + 0 + 32 + + + + + 16777215 + 80 + + + + + + + + + Comments + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + QPlainTextEdit::NoWrap + + + + + + + + + + + + + 0 + 0 + + + + noreturn + + + + + + + + False + + + + + True + + + + + Unknown + + + + + + + + + + return value must be used + + + + + + + ignore function in leaks checking + + + + + + + Arguments + + + + + + + + + + + + Edit + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + buttonOpen + clicked() + LibraryDialog + openCfg() + + + 59 + 18 + + + 66 + 34 + + + + + useretval + clicked() + LibraryDialog + changeFunction() + + + 550 + 157 + + + 561 + 157 + + + + + leakignore + clicked() + LibraryDialog + changeFunction() + + + 531 + 183 + + + 540 + 182 + + + + + buttonSave + clicked() + LibraryDialog + saveCfg() + + + 118 + 16 + + + 130 + 32 + + + + + addFunction + clicked() + LibraryDialog + addFunction() + + + 53 + 564 + + + 63 + 582 + + + + + editArgButton + clicked() + LibraryDialog + editArg() + + + 488 + 580 + + + 497 + 580 + + + + + arguments + itemDoubleClicked(QListWidgetItem*) + LibraryDialog + editArg() + + + 529 + 223 + + + 543 + 223 + + + + + sortFunctions + toggled(bool) + LibraryDialog + sortFunctions(bool) + + + 45 + 66 + + + 58 + 66 + + + + + filter + textChanged(QString) + LibraryDialog + filterFunctions(QString) + + + 429 + 575 + + + 273 + 582 + + + + + functions + itemSelectionChanged() + LibraryDialog + selectFunction() + + + 190 + 141 + + + 203 + 140 + + + + + noreturn + currentIndexChanged(int) + LibraryDialog + changeFunction() + + + 545 + 137 + + + 552 + 138 + + + + + functions + itemChanged(QListWidgetItem*) + LibraryDialog + editFunctionName(QListWidgetItem*) + + + 217 + 104 + + + 235 + 104 + + + + + noreturn + editTextChanged(QString) + LibraryDialog + changeFunction() + + + 548 + 128 + + + 555 + 128 + + + + + comments + textChanged() + LibraryDialog + changeFunction() + + + 567 + 50 + + + 584 + 48 + + + + + buttonSaveAs + clicked() + LibraryDialog + saveCfgAs() + + + 211 + 31 + + + 232 + 33 + + + + + + addFunction() + argumentChanged(QListWidgetItem*) + changeFunction() + editArg() + filterFunctions(QString) + openCfg() + saveCfg() + selectFunction() + sortFunctions(bool) + editFunctionName(QListWidgetItem*) + saveCfgAs() + + diff --git a/cppcheck-2.14.0/gui/libraryeditargdialog.cpp b/cppcheck-2.14.0/gui/libraryeditargdialog.cpp new file mode 100644 index 00000000..bf971afc --- /dev/null +++ b/cppcheck-2.14.0/gui/libraryeditargdialog.cpp @@ -0,0 +1,126 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "libraryeditargdialog.h" + +#include "ui_libraryeditargdialog.h" + +#include +#include +#include +#include +#include + +class QWidget; + +LibraryEditArgDialog::LibraryEditArgDialog(QWidget *parent, const CppcheckLibraryData::Function::Arg &arg) : + QDialog(parent), + mUi(new Ui::LibraryEditArgDialog), + mMinSizes(arg.minsizes) +{ + mUi->setupUi(this); + mUi->notbool->setChecked(arg.notbool); + mUi->notnull->setChecked(arg.notnull); + mUi->notuninit->setChecked(arg.notuninit); + mUi->strz->setChecked(arg.strz); + mUi->formatstr->setChecked(arg.formatstr); + mUi->valid->setText(arg.valid); + + mUi->minsize1type->setEnabled(true); + mUi->minsize1arg->setEnabled(arg.minsizes.count() >= 1); + mUi->minsize1arg2->setEnabled(arg.minsizes.count() >= 1 && arg.minsizes[0].type == "mul"); + mUi->minsize2type->setEnabled(arg.minsizes.count() >= 1); + mUi->minsize2arg->setEnabled(arg.minsizes.count() >= 2); + mUi->minsize2arg2->setEnabled(arg.minsizes.count() >= 2 && arg.minsizes[1].type == "mul"); + + QStringList items; + items << "None" << "argvalue" << "mul" << "sizeof" << "strlen"; + + mUi->minsize1type->clear(); + mUi->minsize1type->addItems(items); + if (arg.minsizes.count() >= 1) { + mUi->minsize1type->setCurrentIndex(items.indexOf(mMinSizes[0].type)); + mUi->minsize1arg->setValue(mMinSizes[0].arg.toInt()); + if (arg.minsizes[0].type == "mul") + mUi->minsize1arg2->setValue(mMinSizes[0].arg2.toInt()); + else + mUi->minsize1arg2->setValue(0); + } else { + mUi->minsize1type->setCurrentIndex(0); + mUi->minsize1arg->setValue(0); + mUi->minsize1arg2->setValue(0); + } + + mUi->minsize2type->clear(); + mUi->minsize2type->addItems(items); + if (arg.minsizes.count() >= 2) { + mUi->minsize2type->setCurrentIndex(items.indexOf(mMinSizes[1].type)); + mUi->minsize2arg->setValue(mMinSizes[1].arg.toInt()); + if (arg.minsizes[1].type == "mul") + mUi->minsize2arg2->setValue(mMinSizes[1].arg2.toInt()); + else + mUi->minsize2arg2->setValue(0); + } else { + mUi->minsize2type->setCurrentIndex(0); + mUi->minsize2arg->setValue(0); + mUi->minsize2arg2->setValue(0); + } +} + +LibraryEditArgDialog::~LibraryEditArgDialog() +{ + delete mUi; +} + +CppcheckLibraryData::Function::Arg LibraryEditArgDialog::getArg() const +{ + CppcheckLibraryData::Function::Arg ret; + ret.notbool = mUi->notbool->isChecked(); + ret.notnull = mUi->notnull->isChecked(); + ret.notuninit = mUi->notuninit->isChecked(); + ret.strz = mUi->strz->isChecked(); + ret.formatstr = mUi->formatstr->isChecked(); + if (mUi->minsize1type->currentIndex() != 0) { + CppcheckLibraryData::Function::Arg::MinSize minsize1; + minsize1.type = mUi->minsize1type->currentText(); + minsize1.arg = QString::number(mUi->minsize1arg->value()); + if (minsize1.type == "mul") + minsize1.arg2 = QString::number(mUi->minsize1arg2->value()); + ret.minsizes.append(minsize1); + + if (mUi->minsize2type->currentIndex() != 0) { + CppcheckLibraryData::Function::Arg::MinSize minsize2; + minsize2.type = mUi->minsize2type->currentText(); + minsize2.arg = QString::number(mUi->minsize2arg->value()); + if (minsize2.type == "mul") + minsize2.arg2 = QString::number(mUi->minsize2arg2->value()); + ret.minsizes.append(minsize2); + } + } + ret.valid = mUi->valid->text(); + return ret; +} + +void LibraryEditArgDialog::minsizeChanged() +{ + mUi->minsize1arg->setEnabled(mUi->minsize1type->currentIndex() != 0); + mUi->minsize1arg2->setEnabled(mUi->minsize1type->currentText() == "mul"); + mUi->minsize2type->setEnabled(mUi->minsize1type->currentIndex() != 0); + mUi->minsize2arg->setEnabled(mUi->minsize2type->currentIndex() != 0); + mUi->minsize2arg2->setEnabled(mUi->minsize2type->currentText() == "mul"); +} diff --git a/cppcheck-2.14.0/gui/libraryeditargdialog.h b/cppcheck-2.14.0/gui/libraryeditargdialog.h new file mode 100644 index 00000000..190f3d8f --- /dev/null +++ b/cppcheck-2.14.0/gui/libraryeditargdialog.h @@ -0,0 +1,54 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef LIBRARYEDITARGDIALOG_H +#define LIBRARYEDITARGDIALOG_H + +#include "cppchecklibrarydata.h" + +#include +#include +#include +#include + +class QWidget; +namespace Ui { + class LibraryEditArgDialog; +} + +class LibraryEditArgDialog : public QDialog { + Q_OBJECT + +public: + LibraryEditArgDialog(QWidget *parent, const CppcheckLibraryData::Function::Arg &arg); + LibraryEditArgDialog(const LibraryEditArgDialog &) = delete; + ~LibraryEditArgDialog() override; + LibraryEditArgDialog &operator=(const LibraryEditArgDialog &) = delete; + + CppcheckLibraryData::Function::Arg getArg() const; + +private slots: + void minsizeChanged(); + +private: + Ui::LibraryEditArgDialog *mUi; + + QList mMinSizes; +}; + +#endif // LIBRARYEDITARGDIALOG_H diff --git a/cppcheck-2.14.0/gui/libraryeditargdialog.ui b/cppcheck-2.14.0/gui/libraryeditargdialog.ui new file mode 100644 index 00000000..6994c782 --- /dev/null +++ b/cppcheck-2.14.0/gui/libraryeditargdialog.ui @@ -0,0 +1,401 @@ + + + LibraryEditArgDialog + + + + 0 + 0 + 448 + 465 + + + + Edit argument + + + + + + <html><head/><body> +<p>Is bool value allowed? For instance result from comparison or from '!' operator.</p> +<p>Typically, set this if the argument is a pointer, size, etc.</p> +<p>Example:</p> +<pre> memcmp(x, y, i == 123); // last argument should not have a bool value</pre> +</body></html> + + + Not bool + + + + + + + <html><head/><body> +<p>Is a null parameter value allowed?</p> +<p>Typically this should be used on any pointer parameter that does not allow null.</p> +<p>Example:</p> +<pre> strcpy(x,y); // neither x or y is allowed to be null.</pre> +</body></html> + + + Not null + + + + + + + Not uninit + + + + + + + String + + + + + + + + 0 + 0 + + + + Format string + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Min size of buffer + + + + + + + + + Type + + + + + + + + None + + + + + argvalue + + + + + mul + + + + + strlen + + + + + + + + Arg + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Arg2 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + 0 + 0 + + + + and + + + + + + + + + Type + + + + + + + true + + + + None + + + + + argvalue + + + + + mul + + + + + strlen + + + + + + + + Arg + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Arg2 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Valid values + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + LibraryEditArgDialog + accept() + + + 226 + 460 + + + 157 + 274 + + + + + buttonBox + rejected() + LibraryEditArgDialog + reject() + + + 290 + 439 + + + 286 + 274 + + + + + minsize1type + currentIndexChanged(int) + LibraryEditArgDialog + minsizeChanged() + + + 413 + 194 + + + 446 + 175 + + + + + minsize2type + currentIndexChanged(int) + LibraryEditArgDialog + minsizeChanged() + + + 436 + 299 + + + 447 + 297 + + + + + + minsizeChanged() + + diff --git a/cppcheck-2.14.0/gui/main.cpp b/cppcheck-2.14.0/gui/main.cpp new file mode 100644 index 00000000..91bc9e7a --- /dev/null +++ b/cppcheck-2.14.0/gui/main.cpp @@ -0,0 +1,146 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "common.h" +#include "mainwindow.h" +#include "erroritem.h" // IWYU pragma: keep +#include "translationhandler.h" + +#ifdef _WIN32 +#include "aboutdialog.h" + +#include +#else +#include +#endif +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +static void ShowUsage(); +static void ShowVersion(); +static bool CheckArgs(const QStringList &args); + +int main(int argc, char *argv[]) +{ + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); +#endif + + QApplication app(argc, argv); + + QCoreApplication::setOrganizationName("Cppcheck"); + QCoreApplication::setApplicationName("Cppcheck-GUI"); + + auto* settings = new QSettings("Cppcheck", "Cppcheck-GUI", &app); + + // Set data dir.. + const QStringList args = QApplication::arguments(); + auto it = std::find_if(args.cbegin(), args.cend(), [](const QString& arg) { + return arg.startsWith("--data-dir="); + }); + if (it != args.end()) { + settings->setValue("DATADIR", it->mid(11)); + return 0; + } + + auto* th = new TranslationHandler(&app); + th->setLanguage(settings->value(SETTINGS_LANGUAGE, th->suggestLanguage()).toString()); + + if (!CheckArgs(QApplication::arguments())) + return 0; + + QApplication::setWindowIcon(QIcon(":cppcheck-gui.png")); + + // Register this metatype that is used to transfer error info + qRegisterMetaType("ErrorItem"); + + MainWindow window(th, settings); + window.show(); + return QApplication::exec(); +} + +// Check only arguments needing action before GUI is shown. +// Rest of the arguments are handled in MainWindow::HandleCLIParams() +static bool CheckArgs(const QStringList &args) +{ + if (args.contains("-h") || args.contains("--help")) { + ShowUsage(); + return false; + } + if (args.contains("-v") || args.contains("--version")) { + ShowVersion(); + return false; + } + return true; +} + +static void ShowUsage() +{ + QString helpMessage = MainWindow::tr( + "Cppcheck GUI.\n\n" + "Syntax:\n" + " cppcheck-gui [OPTIONS] [files or paths]\n\n" + "Options:\n" + " -h, --help Print this help\n" + " -p Open given project file and start checking it\n" + " -l Open given results xml file\n" + " -d Specify the directory that was checked to generate the results xml specified with -l\n" + " -v, --version Show program version\n" + " --data-dir= This option is for installation scripts so they can configure the directory where\n" + " datafiles are located (translations, cfg). The GUI is not started when this option\n" + " is used."); +#if defined(_WIN32) + QMessageBox msgBox(QMessageBox::Information, + MainWindow::tr("Cppcheck GUI - Command line parameters"), + helpMessage, + QMessageBox::Ok + ); + (void)msgBox.exec(); +#else + std::cout << helpMessage.toStdString() << std::endl; +#endif +} + +static void ShowVersion() +{ +#if defined(_WIN32) + AboutDialog *dlg = new AboutDialog(CppCheck::version(), CppCheck::extraVersion(), 0); + dlg->exec(); + delete dlg; +#else + std::string versionMessage("Cppcheck "); + versionMessage += CppCheck::version(); + const char * extraVersion = CppCheck::extraVersion(); + if (*extraVersion != 0) + versionMessage += std::string(" (") + extraVersion + ")"; + + std::cout << versionMessage << std::endl; +#endif +} diff --git a/cppcheck-2.14.0/gui/mainwindow.cpp b/cppcheck-2.14.0/gui/mainwindow.cpp new file mode 100644 index 00000000..b5f8713f --- /dev/null +++ b/cppcheck-2.14.0/gui/mainwindow.cpp @@ -0,0 +1,2168 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "mainwindow.h" + +#include "addoninfo.h" +#include "applicationlist.h" +#include "aboutdialog.h" +#include "analyzerinfo.h" +#include "checkthread.h" +#include "common.h" +#include "cppcheck.h" +#include "errortypes.h" +#include "filelist.h" +#include "filesettings.h" +#include "compliancereportdialog.h" +#include "fileviewdialog.h" +#include "helpdialog.h" +#include "importproject.h" +#include "librarydialog.h" +#include "platform.h" +#include "projectfile.h" +#include "projectfiledialog.h" +#include "report.h" +#include "resultsview.h" +#include "scratchpad.h" +#include "settings.h" +#include "showtypes.h" +#include "statsdialog.h" +#include "settingsdialog.h" +#include "standards.h" +#include "suppressions.h" +#include "threadhandler.h" +#include "threadresult.h" +#include "translationhandler.h" + +#include "ui_mainwindow.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "json.h" + +static const QString compile_commands_json("compile_commands.json"); + +static QString fromNativePath(const QString& p) { +#ifdef Q_OS_WIN + QString ret(p); + ret.replace('\\', '/'); + return ret; +#else + return p; +#endif +} + +MainWindow::MainWindow(TranslationHandler* th, QSettings* settings) : + mSettings(settings), + mApplications(new ApplicationList(this)), + mTranslation(th), + mUI(new Ui::MainWindow), + mPlatformActions(new QActionGroup(this)), + mCStandardActions(new QActionGroup(this)), + mCppStandardActions(new QActionGroup(this)), + mSelectLanguageActions(new QActionGroup(this)) +{ + { + Settings tempSettings; + tempSettings.exename = QCoreApplication::applicationFilePath().toStdString(); + Settings::loadCppcheckCfg(tempSettings, tempSettings.supprs); // TODO: how to handle error? + mCppcheckCfgProductName = QString::fromStdString(tempSettings.cppcheckCfgProductName); + mCppcheckCfgAbout = QString::fromStdString(tempSettings.cppcheckCfgAbout); + } + + mUI->setupUi(this); + mThread = new ThreadHandler(this); + mUI->mResults->initialize(mSettings, mApplications, mThread); + + // Filter timer to delay filtering results slightly while typing + mFilterTimer = new QTimer(this); + mFilterTimer->setInterval(500); + mFilterTimer->setSingleShot(true); + connect(mFilterTimer, &QTimer::timeout, this, &MainWindow::filterResults); + + // "Filter" toolbar + mLineEditFilter = new QLineEdit(mUI->mToolBarFilter); + mLineEditFilter->setPlaceholderText(tr("Quick Filter:")); + mLineEditFilter->setClearButtonEnabled(true); + mUI->mToolBarFilter->addWidget(mLineEditFilter); + connect(mLineEditFilter, SIGNAL(textChanged(QString)), mFilterTimer, SLOT(start())); + connect(mLineEditFilter, &QLineEdit::returnPressed, this, &MainWindow::filterResults); + + connect(mUI->mActionPrint, SIGNAL(triggered()), mUI->mResults, SLOT(print())); + connect(mUI->mActionPrintPreview, SIGNAL(triggered()), mUI->mResults, SLOT(printPreview())); + connect(mUI->mActionQuit, &QAction::triggered, this, &MainWindow::close); + connect(mUI->mActionAnalyzeFiles, &QAction::triggered, this, &MainWindow::analyzeFiles); + connect(mUI->mActionAnalyzeDirectory, &QAction::triggered, this, &MainWindow::analyzeDirectory); + connect(mUI->mActionSettings, &QAction::triggered, this, &MainWindow::programSettings); + connect(mUI->mActionClearResults, &QAction::triggered, this, &MainWindow::clearResults); + connect(mUI->mActionOpenXML, &QAction::triggered, this, &MainWindow::openResults); + + connect(mUI->mActionShowStyle, &QAction::toggled, this, &MainWindow::showStyle); + connect(mUI->mActionShowErrors, &QAction::toggled, this, &MainWindow::showErrors); + connect(mUI->mActionShowWarnings, &QAction::toggled, this, &MainWindow::showWarnings); + connect(mUI->mActionShowPortability, &QAction::toggled, this, &MainWindow::showPortability); + connect(mUI->mActionShowPerformance, &QAction::toggled, this, &MainWindow::showPerformance); + connect(mUI->mActionShowInformation, &QAction::toggled, this, &MainWindow::showInformation); + connect(mUI->mActionShowCppcheck, &QAction::toggled, mUI->mResults, &ResultsView::showCppcheckResults); + connect(mUI->mActionShowClang, &QAction::toggled, mUI->mResults, &ResultsView::showClangResults); + connect(mUI->mActionCheckAll, &QAction::triggered, this, &MainWindow::checkAll); + connect(mUI->mActionUncheckAll, &QAction::triggered, this, &MainWindow::uncheckAll); + connect(mUI->mActionCollapseAll, &QAction::triggered, mUI->mResults, &ResultsView::collapseAllResults); + connect(mUI->mActionExpandAll, &QAction::triggered, mUI->mResults, &ResultsView::expandAllResults); + connect(mUI->mActionShowHidden, &QAction::triggered, mUI->mResults, &ResultsView::showHiddenResults); + connect(mUI->mActionViewStats, &QAction::triggered, this, &MainWindow::showStatistics); + connect(mUI->mActionLibraryEditor, &QAction::triggered, this, &MainWindow::showLibraryEditor); + + connect(mUI->mActionReanalyzeModified, &QAction::triggered, this, &MainWindow::reAnalyzeModified); + connect(mUI->mActionReanalyzeAll, &QAction::triggered, this, &MainWindow::reAnalyzeAll); + connect(mUI->mActionCheckLibrary, &QAction::triggered, this, &MainWindow::checkLibrary); + connect(mUI->mActionCheckConfiguration, &QAction::triggered, this, &MainWindow::checkConfiguration); + + connect(mUI->mActionStop, &QAction::triggered, this, &MainWindow::stopAnalysis); + connect(mUI->mActionSave, &QAction::triggered, this, &MainWindow::save); + connect(mUI->mActionComplianceReport, &QAction::triggered, this, &MainWindow::complianceReport); + + // About menu + connect(mUI->mActionAbout, &QAction::triggered, this, &MainWindow::about); + connect(mUI->mActionLicense, &QAction::triggered, this, &MainWindow::showLicense); + + // View > Toolbar menu + connect(mUI->mActionToolBarMain, SIGNAL(toggled(bool)), this, SLOT(toggleMainToolBar())); + connect(mUI->mActionToolBarView, SIGNAL(toggled(bool)), this, SLOT(toggleViewToolBar())); + connect(mUI->mActionToolBarFilter, SIGNAL(toggled(bool)), this, SLOT(toggleFilterToolBar())); + + connect(mUI->mActionAuthors, &QAction::triggered, this, &MainWindow::showAuthors); + connect(mThread, &ThreadHandler::done, this, &MainWindow::analysisDone); + connect(mThread, &ThreadHandler::log, mUI->mResults, &ResultsView::log); + connect(mThread, &ThreadHandler::debugError, mUI->mResults, &ResultsView::debugError); + connect(mUI->mResults, &ResultsView::gotResults, this, &MainWindow::resultsAdded); + connect(mUI->mResults, &ResultsView::resultsHidden, mUI->mActionShowHidden, &QAction::setEnabled); + connect(mUI->mResults, &ResultsView::checkSelected, this, &MainWindow::performSelectedFilesCheck); + connect(mUI->mResults, &ResultsView::suppressIds, this, &MainWindow::suppressIds); + connect(mUI->mMenuView, &QMenu::aboutToShow, this, &MainWindow::aboutToShowViewMenu); + + // File menu + connect(mUI->mActionNewProjectFile, &QAction::triggered, this, &MainWindow::newProjectFile); + connect(mUI->mActionOpenProjectFile, &QAction::triggered, this, &MainWindow::openProjectFile); + connect(mUI->mActionShowScratchpad, &QAction::triggered, this, &MainWindow::showScratchpad); + connect(mUI->mActionCloseProjectFile, &QAction::triggered, this, &MainWindow::closeProjectFile); + connect(mUI->mActionEditProjectFile, &QAction::triggered, this, &MainWindow::editProjectFile); + + connect(mUI->mActionHelpContents, &QAction::triggered, this, &MainWindow::openHelpContents); + + loadSettings(); + + mThread->initialize(mUI->mResults); + if (mProjectFile) + formatAndSetTitle(tr("Project:") + ' ' + mProjectFile->getFilename()); + else + formatAndSetTitle(); + + mUI->mActionComplianceReport->setVisible(isCppcheckPremium()); + + enableCheckButtons(true); + + mUI->mActionPrint->setShortcut(QKeySequence::Print); + enableResultsButtons(); + enableProjectOpenActions(true); + enableProjectActions(false); + + // Must setup MRU menu before CLI param handling as it can load a + // project file and update MRU menu. + for (int i = 0; i < MaxRecentProjects; ++i) { + mRecentProjectActs[i] = new QAction(this); + mRecentProjectActs[i]->setVisible(false); + connect(mRecentProjectActs[i], SIGNAL(triggered()), + this, SLOT(openRecentProject())); + } + mRecentProjectActs[MaxRecentProjects] = nullptr; // The separator + mUI->mActionProjectMRU->setVisible(false); + updateMRUMenuItems(); + + QStringList args = QCoreApplication::arguments(); + //Remove the application itself + args.removeFirst(); + if (!args.isEmpty()) { + handleCLIParams(args); + } + + mUI->mActionCloseProjectFile->setEnabled(mProjectFile != nullptr); + mUI->mActionEditProjectFile->setEnabled(mProjectFile != nullptr); + + for (int i = 0; i < mPlatforms.getCount(); i++) { + PlatformData platform = mPlatforms.mPlatforms[i]; + auto *action = new QAction(this); + platform.mActMainWindow = action; + mPlatforms.mPlatforms[i] = platform; + action->setText(platform.mTitle); + action->setData(platform.mType); + action->setCheckable(true); + action->setActionGroup(mPlatformActions); + mUI->mMenuAnalyze->insertAction(mUI->mActionPlatforms, action); + connect(action, SIGNAL(triggered()), this, SLOT(selectPlatform())); + } + + mUI->mActionC89->setActionGroup(mCStandardActions); + mUI->mActionC99->setActionGroup(mCStandardActions); + mUI->mActionC11->setActionGroup(mCStandardActions); + + mUI->mActionCpp03->setActionGroup(mCppStandardActions); + mUI->mActionCpp11->setActionGroup(mCppStandardActions); + mUI->mActionCpp14->setActionGroup(mCppStandardActions); + mUI->mActionCpp17->setActionGroup(mCppStandardActions); + mUI->mActionCpp20->setActionGroup(mCppStandardActions); + //mUI->mActionCpp23->setActionGroup(mCppStandardActions); + + mUI->mActionEnforceC->setActionGroup(mSelectLanguageActions); + mUI->mActionEnforceCpp->setActionGroup(mSelectLanguageActions); + mUI->mActionAutoDetectLanguage->setActionGroup(mSelectLanguageActions); + + // TODO: we no longer default to a Windows platform in CLI - so we should probably also get rid of this in the GUI + // 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) + constexpr Platform::Type defaultPlatform = Platform::Type::Win32W; +#else + constexpr Platform::Type defaultPlatform = Platform::Type::Unspecified; +#endif + PlatformData &platform = mPlatforms.get((Platform::Type)mSettings->value(SETTINGS_CHECKED_PLATFORM, defaultPlatform).toInt()); + platform.mActMainWindow->setChecked(true); + + mNetworkAccessManager = new QNetworkAccessManager(this); + connect(mNetworkAccessManager, &QNetworkAccessManager::finished, + this, &MainWindow::replyFinished); + + mUI->mLabelInformation->setVisible(false); + mUI->mButtonHideInformation->setVisible(false); + connect(mUI->mButtonHideInformation, &QPushButton::clicked, + this, &MainWindow::hideInformation); + + if (mSettings->value(SETTINGS_CHECK_FOR_UPDATES, false).toBool()) { + // Is there a new version? + if (isCppcheckPremium()) { + const QUrl url("https://files.cppchecksolutions.com/version.txt"); + mNetworkAccessManager->get(QNetworkRequest(url)); + } else { + const QUrl url("https://cppcheck.sourceforge.io/version.txt"); + mNetworkAccessManager->get(QNetworkRequest(url)); + } + } else { + delete mUI->mLayoutInformation; + } +} + +MainWindow::~MainWindow() +{ + delete mProjectFile; + delete mScratchPad; + delete mUI; +} + +void MainWindow::handleCLIParams(const QStringList ¶ms) +{ + int index; + if (params.contains("-p")) { + index = params.indexOf("-p"); + if ((index + 1) < params.length()) + loadProjectFile(params[index + 1]); + } else if (params.contains("-l")) { + QString logFile; + index = params.indexOf("-l"); + if ((index + 1) < params.length()) + logFile = params[index + 1]; + + if (params.contains("-d")) { + QString checkedDir; + index = params.indexOf("-d"); + if ((index + 1) < params.length()) + checkedDir = params[index + 1]; + + loadResults(logFile, checkedDir); + } else { + loadResults(logFile); + } + } else if ((index = params.indexOf(QRegularExpression(".*\\.cppcheck$", QRegularExpression::CaseInsensitiveOption))) >= 0 && index < params.length() && QFile(params[index]).exists()) { + loadProjectFile(params[index]); + } else if ((index = params.indexOf(QRegularExpression(".*\\.xml$", QRegularExpression::CaseInsensitiveOption))) >= 0 && index < params.length() && QFile(params[index]).exists()) { + loadResults(params[index],QDir::currentPath()); + } else + doAnalyzeFiles(params); +} + +void MainWindow::loadSettings() +{ + // Window/dialog sizes + if (mSettings->value(SETTINGS_WINDOW_MAXIMIZED, false).toBool()) { + showMaximized(); + } else { + resize(mSettings->value(SETTINGS_WINDOW_WIDTH, 800).toInt(), + mSettings->value(SETTINGS_WINDOW_HEIGHT, 600).toInt()); + } + + const ShowTypes &types = mUI->mResults->getShowTypes(); + mUI->mActionShowStyle->setChecked(types.isShown(ShowTypes::ShowStyle)); + mUI->mActionShowErrors->setChecked(types.isShown(ShowTypes::ShowErrors)); + mUI->mActionShowWarnings->setChecked(types.isShown(ShowTypes::ShowWarnings)); + mUI->mActionShowPortability->setChecked(types.isShown(ShowTypes::ShowPortability)); + mUI->mActionShowPerformance->setChecked(types.isShown(ShowTypes::ShowPerformance)); + mUI->mActionShowInformation->setChecked(types.isShown(ShowTypes::ShowInformation)); + mUI->mActionShowCppcheck->setChecked(true); + mUI->mActionShowClang->setChecked(true); + + Standards standards; + standards.setC(mSettings->value(SETTINGS_STD_C, QString()).toString().toStdString()); + mUI->mActionC89->setChecked(standards.c == Standards::C89); + mUI->mActionC99->setChecked(standards.c == Standards::C99); + mUI->mActionC11->setChecked(standards.c == Standards::C11); + standards.setCPP(mSettings->value(SETTINGS_STD_CPP, QString()).toString().toStdString()); + mUI->mActionCpp03->setChecked(standards.cpp == Standards::CPP03); + mUI->mActionCpp11->setChecked(standards.cpp == Standards::CPP11); + mUI->mActionCpp14->setChecked(standards.cpp == Standards::CPP14); + mUI->mActionCpp17->setChecked(standards.cpp == Standards::CPP17); + mUI->mActionCpp20->setChecked(standards.cpp == Standards::CPP20); + //mUI->mActionCpp23->setChecked(standards.cpp == Standards::CPP23); + + // 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); + + const Standards::Language enforcedLanguage = (Standards::Language)mSettings->value(SETTINGS_ENFORCED_LANGUAGE, 0).toInt(); + if (enforcedLanguage == Standards::Language::CPP) + mUI->mActionEnforceCpp->setChecked(true); + else if (enforcedLanguage == Standards::Language::C) + mUI->mActionEnforceC->setChecked(true); + else + mUI->mActionAutoDetectLanguage->setChecked(true); + + const bool succeeded = mApplications->loadSettings(); + if (!succeeded) { + const QString msg = tr("There was a problem with loading the editor application settings.\n\n" + "This is probably because the settings were changed between the Cppcheck versions. " + "Please check (and fix) the editor application settings, otherwise the editor " + "program might not start correctly."); + QMessageBox msgBox(QMessageBox::Warning, + tr("Cppcheck"), + msg, + QMessageBox::Ok, + this); + msgBox.exec(); + } + + const QString projectFile = mSettings->value(SETTINGS_OPEN_PROJECT, QString()).toString(); + if (!projectFile.isEmpty() && QCoreApplication::arguments().size()==1) { + QFileInfo inf(projectFile); + if (inf.exists() && inf.isReadable()) { + setPath(SETTINGS_LAST_PROJECT_PATH, projectFile); + mProjectFile = new ProjectFile(this); + mProjectFile->setActiveProject(); + mProjectFile->read(projectFile); + loadLastResults(); + QDir::setCurrent(inf.absolutePath()); + } + } +} + +void MainWindow::saveSettings() const +{ + // Window/dialog sizes + mSettings->setValue(SETTINGS_WINDOW_WIDTH, size().width()); + mSettings->setValue(SETTINGS_WINDOW_HEIGHT, size().height()); + mSettings->setValue(SETTINGS_WINDOW_MAXIMIZED, isMaximized()); + + // Show * states + mSettings->setValue(SETTINGS_SHOW_STYLE, mUI->mActionShowStyle->isChecked()); + mSettings->setValue(SETTINGS_SHOW_ERRORS, mUI->mActionShowErrors->isChecked()); + mSettings->setValue(SETTINGS_SHOW_WARNINGS, mUI->mActionShowWarnings->isChecked()); + mSettings->setValue(SETTINGS_SHOW_PORTABILITY, mUI->mActionShowPortability->isChecked()); + mSettings->setValue(SETTINGS_SHOW_PERFORMANCE, mUI->mActionShowPerformance->isChecked()); + mSettings->setValue(SETTINGS_SHOW_INFORMATION, mUI->mActionShowInformation->isChecked()); + + if (mUI->mActionC89->isChecked()) + mSettings->setValue(SETTINGS_STD_C, "C89"); + if (mUI->mActionC99->isChecked()) + mSettings->setValue(SETTINGS_STD_C, "C99"); + if (mUI->mActionC11->isChecked()) + mSettings->setValue(SETTINGS_STD_C, "C11"); + + if (mUI->mActionCpp03->isChecked()) + mSettings->setValue(SETTINGS_STD_CPP, "C++03"); + if (mUI->mActionCpp11->isChecked()) + mSettings->setValue(SETTINGS_STD_CPP, "C++11"); + if (mUI->mActionCpp14->isChecked()) + mSettings->setValue(SETTINGS_STD_CPP, "C++14"); + if (mUI->mActionCpp17->isChecked()) + mSettings->setValue(SETTINGS_STD_CPP, "C++17"); + if (mUI->mActionCpp20->isChecked()) + mSettings->setValue(SETTINGS_STD_CPP, "C++20"); + //if (mUI.mActionCpp23->isChecked()) + // mSettings->setValue(SETTINGS_STD_CPP, "C++23"); + + // Main window settings + mSettings->setValue(SETTINGS_TOOLBARS_MAIN_SHOW, mUI->mToolBarMain->isVisible()); + mSettings->setValue(SETTINGS_TOOLBARS_VIEW_SHOW, mUI->mToolBarView->isVisible()); + mSettings->setValue(SETTINGS_TOOLBARS_FILTER_SHOW, mUI->mToolBarFilter->isVisible()); + + if (mUI->mActionEnforceCpp->isChecked()) + mSettings->setValue(SETTINGS_ENFORCED_LANGUAGE, Standards::Language::CPP); + else if (mUI->mActionEnforceC->isChecked()) + mSettings->setValue(SETTINGS_ENFORCED_LANGUAGE, Standards::Language::C); + else + mSettings->setValue(SETTINGS_ENFORCED_LANGUAGE, Standards::Language::None); + + mApplications->saveSettings(); + + mSettings->setValue(SETTINGS_LANGUAGE, mTranslation->getCurrentLanguage()); + + mSettings->setValue(SETTINGS_OPEN_PROJECT, mProjectFile ? mProjectFile->getFilename() : QString()); + + mUI->mResults->saveSettings(mSettings); +} + +void MainWindow::doAnalyzeProject(ImportProject p, const bool checkLibrary, const bool checkConfiguration) +{ + QPair checkSettingsPair = getCppcheckSettings(); + if (!checkSettingsPair.first) + return; + Settings& checkSettings = checkSettingsPair.second; + + clearResults(); + + mIsLogfileLoaded = false; + if (mProjectFile) { + std::vector v; + const QStringList excluded = mProjectFile->getExcludedPaths(); + std::transform(excluded.cbegin(), excluded.cend(), std::back_inserter(v), [](const QString& e) { + return e.toStdString(); + }); + p.ignorePaths(v); + + if (!mProjectFile->getAnalyzeAllVsConfigs()) { + const Platform::Type platform = (Platform::Type) mSettings->value(SETTINGS_CHECKED_PLATFORM, 0).toInt(); + std::vector configurations; + const QStringList configs = mProjectFile->getVsConfigurations(); + std::transform(configs.cbegin(), configs.cend(), std::back_inserter(configurations), [](const QString& e) { + return e.toStdString(); + }); + p.selectVsConfigurations(platform, configurations); + } + } else { + enableProjectActions(false); + } + + mUI->mResults->clear(true); + mThread->clearFiles(); + + mUI->mResults->checkingStarted(p.fileSettings.size()); + + QDir inf(mCurrentDirectory); + const QString checkPath = inf.canonicalPath(); + setPath(SETTINGS_LAST_CHECK_PATH, checkPath); + + checkLockDownUI(); // lock UI while checking + + mUI->mResults->setCheckDirectory(checkPath); + checkSettings.force = false; + checkSettings.checkLibrary = checkLibrary; + checkSettings.checkConfiguration = checkConfiguration; + + if (mProjectFile) + qDebug() << "Checking project file" << mProjectFile->getFilename(); + + if (!checkSettings.buildDir.empty()) { + checkSettings.loadSummaries(); + std::list sourcefiles; + AnalyzerInformation::writeFilesTxt(checkSettings.buildDir, sourcefiles, checkSettings.userDefines, p.fileSettings); + } + + //mThread->SetanalyzeProject(true); + if (mProjectFile) { + mThread->setAddonsAndTools(mProjectFile->getAddonsAndTools()); + QString clangHeaders = mSettings->value(SETTINGS_VS_INCLUDE_PATHS).toString(); + mThread->setClangIncludePaths(clangHeaders.split(";")); + mThread->setSuppressions(mProjectFile->getSuppressions()); + } + mThread->setProject(p); + mThread->check(checkSettings); + mUI->mResults->setCheckSettings(checkSettings); +} + +void MainWindow::doAnalyzeFiles(const QStringList &files, const bool checkLibrary, const bool checkConfiguration) +{ + if (files.isEmpty()) + return; + + QPair checkSettingsPair = getCppcheckSettings(); + if (!checkSettingsPair.first) + return; + Settings& checkSettings = checkSettingsPair.second; + + clearResults(); + + mIsLogfileLoaded = false; + FileList pathList; + pathList.addPathList(files); + if (mProjectFile) { + pathList.addExcludeList(mProjectFile->getExcludedPaths()); + } else { + enableProjectActions(false); + } + QStringList fileNames = pathList.getFileList(); + + mUI->mResults->clear(true); + mThread->clearFiles(); + + if (fileNames.isEmpty()) { + QMessageBox msg(QMessageBox::Warning, + tr("Cppcheck"), + tr("No suitable files found to analyze!"), + QMessageBox::Ok, + this); + msg.exec(); + return; + } + + mUI->mResults->checkingStarted(fileNames.count()); + + mThread->setFiles(fileNames); + if (mProjectFile && !checkConfiguration) + mThread->setAddonsAndTools(mProjectFile->getAddonsAndTools()); + mThread->setSuppressions(mProjectFile ? mProjectFile->getCheckingSuppressions() : QList()); + QDir inf(mCurrentDirectory); + const QString checkPath = inf.canonicalPath(); + setPath(SETTINGS_LAST_CHECK_PATH, checkPath); + + checkLockDownUI(); // lock UI while checking + + mUI->mResults->setCheckDirectory(checkPath); + checkSettings.checkLibrary = checkLibrary; + checkSettings.checkConfiguration = checkConfiguration; + + if (mProjectFile) + qDebug() << "Checking project file" << mProjectFile->getFilename(); + + if (!checkSettings.buildDir.empty()) { + checkSettings.loadSummaries(); + std::list sourcefiles; + std::transform(fileNames.cbegin(), fileNames.cend(), std::back_inserter(sourcefiles), [](const QString& s) { + return s.toStdString(); + }); + AnalyzerInformation::writeFilesTxt(checkSettings.buildDir, sourcefiles, checkSettings.userDefines, {}); + } + + mThread->setCheckFiles(true); + mThread->check(checkSettings); + mUI->mResults->setCheckSettings(checkSettings); +} + +void MainWindow::analyzeCode(const QString& code, const QString& filename) +{ + const QPair& checkSettingsPair = getCppcheckSettings(); + if (!checkSettingsPair.first) + return; + const Settings& checkSettings = checkSettingsPair.second; + + // Initialize dummy ThreadResult as ErrorLogger + ThreadResult result; + result.setFiles(QStringList(filename)); + connect(&result, SIGNAL(progress(int,QString)), + mUI->mResults, SLOT(progress(int,QString))); + connect(&result, SIGNAL(error(ErrorItem)), + mUI->mResults, SLOT(error(ErrorItem))); + connect(&result, SIGNAL(log(QString)), + mUI->mResults, SLOT(log(QString))); + connect(&result, SIGNAL(debugError(ErrorItem)), + mUI->mResults, SLOT(debugError(ErrorItem))); + + // Create CppCheck instance + CppCheck cppcheck(result, true, nullptr); + cppcheck.settings() = checkSettings; + + // Check + checkLockDownUI(); + clearResults(); + mUI->mResults->checkingStarted(1); + cppcheck.check(filename.toStdString(), code.toStdString()); + analysisDone(); + + // Expand results + if (mUI->mResults->hasVisibleResults()) + mUI->mResults->expandAllResults(); +} + +QStringList MainWindow::selectFilesToAnalyze(QFileDialog::FileMode mode) +{ + if (mProjectFile) { + QMessageBox msgBox(this); + msgBox.setWindowTitle(tr("Cppcheck")); + const QString msg(tr("You must close the project file before selecting new files or directories!")); + msgBox.setText(msg); + msgBox.setIcon(QMessageBox::Critical); + msgBox.exec(); + return QStringList(); + } + + QStringList selected; + + // NOTE: we use QFileDialog::getOpenFileNames() and + // QFileDialog::getExistingDirectory() because they show native Windows + // selection dialog which is a lot more usable than Qt:s own dialog. + if (mode == QFileDialog::ExistingFiles) { + QMap filters; + filters[tr("C/C++ Source")] = FileList::getDefaultFilters().join(" "); + filters[tr("Compile database")] = compile_commands_json; + filters[tr("Visual Studio")] = "*.sln *.vcxproj"; + filters[tr("Borland C++ Builder 6")] = "*.bpr"; + QString lastFilter = mSettings->value(SETTINGS_LAST_ANALYZE_FILES_FILTER).toString(); + selected = QFileDialog::getOpenFileNames(this, + tr("Select files to analyze"), + getPath(SETTINGS_LAST_CHECK_PATH), + toFilterString(filters), + &lastFilter); + mSettings->setValue(SETTINGS_LAST_ANALYZE_FILES_FILTER, lastFilter); + + if (selected.isEmpty()) + mCurrentDirectory.clear(); + else { + QFileInfo inf(selected[0]); + mCurrentDirectory = inf.absolutePath(); + } + formatAndSetTitle(); + } else if (mode == QFileDialog::Directory) { + QString dir = QFileDialog::getExistingDirectory(this, + tr("Select directory to analyze"), + getPath(SETTINGS_LAST_CHECK_PATH)); + if (!dir.isEmpty()) { + qDebug() << "Setting current directory to: " << dir; + mCurrentDirectory = dir; + selected.append(dir); + dir = QDir::toNativeSeparators(dir); + formatAndSetTitle(dir); + } + } + if (!mCurrentDirectory.isEmpty()) + setPath(SETTINGS_LAST_CHECK_PATH, mCurrentDirectory); + + return selected; +} + +void MainWindow::analyzeFiles() +{ + Settings::terminate(false); + + QStringList selected = selectFilesToAnalyze(QFileDialog::ExistingFiles); + + const QString file0 = (!selected.empty() ? selected[0].toLower() : QString()); + if (file0.endsWith(".sln") + || file0.endsWith(".vcxproj") + || file0.endsWith(compile_commands_json) + || file0.endsWith(".bpr")) { + ImportProject p; + p.import(selected[0].toStdString()); + + if (file0.endsWith(".sln")) { + QStringList configs; + for (std::list::const_iterator it = p.fileSettings.cbegin(); it != p.fileSettings.cend(); ++it) { + const QString cfg(QString::fromStdString(it->cfg)); + if (!configs.contains(cfg)) + configs.push_back(cfg); + } + configs.sort(); + + bool ok = false; + const QString cfg = QInputDialog::getItem(this, tr("Select configuration"), tr("Select the configuration that will be analyzed"), configs, 0, false, &ok); + if (!ok) + return; + p.ignoreOtherConfigs(cfg.toStdString()); + } + + doAnalyzeProject(p); + return; + } + + doAnalyzeFiles(selected); +} + +void MainWindow::analyzeDirectory() +{ + QStringList dir = selectFilesToAnalyze(QFileDialog::Directory); + 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); + const int dlgResult = msgBox.exec(); + if (dlgResult == QMessageBox::Yes) { + QString path = checkDir.canonicalPath(); + if (!path.endsWith("/")) + path += "/"; + path += projFiles[0]; + loadProjectFile(path); + } else { + doAnalyzeFiles(dir); + } + } else { + // If multiple project files found inform that there are project + // files also available. + QMessageBox msgBox(this); + msgBox.setWindowTitle(tr("Cppcheck")); + const QString msg(tr("Found project files from the directory.\n\n" + "Do you want to proceed analysis without " + "using any of these project files?")); + msgBox.setText(msg); + msgBox.setIcon(QMessageBox::Warning); + msgBox.addButton(QMessageBox::Yes); + msgBox.addButton(QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + const int dlgResult = msgBox.exec(); + if (dlgResult == QMessageBox::Yes) { + doAnalyzeFiles(dir); + } + } + } else { + doAnalyzeFiles(dir); + } +} + +void MainWindow::addIncludeDirs(const QStringList &includeDirs, Settings &result) +{ + for (const QString& dir : includeDirs) { + QString incdir; + if (!QDir::isAbsolutePath(dir)) + incdir = mCurrentDirectory + "/"; + incdir += dir; + incdir = QDir::cleanPath(incdir); + + // include paths must end with '/' + if (!incdir.endsWith("/")) + incdir += "/"; + result.includePaths.push_back(incdir.toStdString()); + } +} + +Library::Error MainWindow::loadLibrary(Library &library, const QString &filename) +{ + Library::Error ret; + + // Try to load the library from the project folder.. + if (mProjectFile) { + QString path = QFileInfo(mProjectFile->getFilename()).canonicalPath(); + ret = library.load(nullptr, (path+"/"+filename).toLatin1()); + if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) + return ret; + } + + // Try to load the library from the application folder.. + const QString appPath = QFileInfo(QCoreApplication::applicationFilePath()).canonicalPath(); + ret = library.load(nullptr, (appPath+"/"+filename).toLatin1()); + if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) + return ret; + ret = library.load(nullptr, (appPath+"/cfg/"+filename).toLatin1()); + if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) + return ret; + +#ifdef FILESDIR + // Try to load the library from FILESDIR/cfg.. + const QString filesdir = FILESDIR; + if (!filesdir.isEmpty()) { + ret = library.load(nullptr, (filesdir+"/cfg/"+filename).toLatin1()); + if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) + return ret; + ret = library.load(nullptr, (filesdir+filename).toLatin1()); + if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) + return ret; + } +#endif + + // Try to load the library from the cfg subfolder.. + const QString datadir = getDataDir(); + if (!datadir.isEmpty()) { + ret = library.load(nullptr, (datadir+"/"+filename).toLatin1()); + if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) + return ret; + ret = library.load(nullptr, (datadir+"/cfg/"+filename).toLatin1()); + if (ret.errorcode != Library::ErrorCode::FILE_NOT_FOUND) + return ret; + } + + return ret; +} + +bool MainWindow::tryLoadLibrary(Library &library, const QString& filename) +{ + const Library::Error error = loadLibrary(library, filename); + if (error.errorcode != Library::ErrorCode::OK) { + if (error.errorcode == Library::ErrorCode::UNKNOWN_ELEMENT) { + QMessageBox::information(this, tr("Information"), tr("The library '%1' contains unknown elements:\n%2").arg(filename).arg(error.reason.c_str())); + return true; + } + + QString errmsg; + switch (error.errorcode) { + case Library::ErrorCode::OK: + break; + case Library::ErrorCode::FILE_NOT_FOUND: + errmsg = tr("File not found"); + break; + case Library::ErrorCode::BAD_XML: + errmsg = tr("Bad XML"); + break; + case Library::ErrorCode::MISSING_ATTRIBUTE: + errmsg = tr("Missing attribute"); + break; + case Library::ErrorCode::BAD_ATTRIBUTE_VALUE: + errmsg = tr("Bad attribute value"); + break; + case Library::ErrorCode::UNSUPPORTED_FORMAT: + errmsg = tr("Unsupported format"); + break; + case Library::ErrorCode::DUPLICATE_PLATFORM_TYPE: + errmsg = tr("Duplicate platform type"); + break; + case Library::ErrorCode::PLATFORM_TYPE_REDEFINED: + errmsg = tr("Platform type redefined"); + break; + case Library::ErrorCode::DUPLICATE_DEFINE: + errmsg = tr("Duplicate define"); + break; + case Library::ErrorCode::UNKNOWN_ELEMENT: + errmsg = tr("Unknown element"); + break; + default: + errmsg = tr("Unknown issue"); + break; + } + if (!error.reason.empty()) + errmsg += " '" + QString::fromStdString(error.reason) + "'"; + QMessageBox::information(this, tr("Information"), tr("Failed to load the selected library '%1'.\n%2").arg(filename).arg(errmsg)); + return false; + } + return true; +} + +QString MainWindow::loadAddon(Settings &settings, const QString &filesDir, const QString &pythonCmd, const QString& addon) +{ + const QString addonFilePath = fromNativePath(ProjectFile::getAddonFilePath(filesDir, addon)); + + if (addonFilePath.isEmpty()) + return tr("File not found: '%1'").arg(addon); + + picojson::object obj; + obj["script"] = picojson::value(addonFilePath.toStdString()); + if (!pythonCmd.isEmpty()) + obj["python"] = picojson::value(pythonCmd.toStdString()); + + if (!isCppcheckPremium() && addon == "misra") { + const QString misraFile = fromNativePath(mSettings->value(SETTINGS_MISRA_FILE).toString()); + if (!misraFile.isEmpty()) { + QString arg; + if (misraFile.endsWith(".pdf", Qt::CaseInsensitive)) + arg = "--misra-pdf=" + misraFile; + else + arg = "--rule-texts=" + misraFile; + obj["args"] = picojson::value(arg.toStdString()); + } + } + + const std::string& json_str = picojson::value(obj).serialize(); + + AddonInfo addonInfo; + const std::string errmsg = addonInfo.getAddonInfo(json_str, settings.exename); + if (!errmsg.empty()) + return tr("Failed to load/setup addon %1: %2").arg(addon, QString::fromStdString(errmsg)); + settings.addonInfos.emplace_back(std::move(addonInfo)); + + settings.addons.emplace(json_str); + + return ""; +} + +QPair MainWindow::getCppcheckSettings() +{ + saveSettings(); // Save settings + + Settings::terminate(true); + Settings result; + + result.exename = QCoreApplication::applicationFilePath().toStdString(); + + // default to --check-level=normal for GUI for now + result.setCheckLevel(Settings::CheckLevel::normal); + + const bool std = tryLoadLibrary(result.library, "std.cfg"); + if (!std) { + QMessageBox::critical(this, tr("Error"), tr("Failed to load %1. Your Cppcheck installation is broken. You can use --data-dir= at the command line to specify where this file is located. Please note that --data-dir is supposed to be used by installation scripts and therefore the GUI does not start when it is used, all that happens is that the setting is configured.\n\nAnalysis is aborted.").arg("std.cfg")); + return {false, {}}; + } + + const QString filesDir(getDataDir()); + const QString pythonCmd = fromNativePath(mSettings->value(SETTINGS_PYTHON_PATH).toString()); + + { + const QString cfgErr = QString::fromStdString(Settings::loadCppcheckCfg(result, result.supprs)); + if (!cfgErr.isEmpty()) { + QMessageBox::critical(this, tr("Error"), tr("Failed to load %1 - %2\n\nAnalysis is aborted.").arg("cppcheck.cfg").arg(cfgErr)); + return {false, {}}; + } + + const auto cfgAddons = result.addons; + result.addons.clear(); + for (const std::string& addon : cfgAddons) { + // TODO: support addons which are a script and not a file + const QString addonError = loadAddon(result, filesDir, pythonCmd, QString::fromStdString(addon)); + if (!addonError.isEmpty()) { + QMessageBox::critical(this, tr("Error"), tr("%1\n\nAnalysis is aborted.").arg(addonError)); + return {false, {}}; + } + } + } + + // If project file loaded, read settings from it + if (mProjectFile) { + QStringList dirs = mProjectFile->getIncludeDirs(); + addIncludeDirs(dirs, result); + + const QStringList defines = mProjectFile->getDefines(); + for (const QString& define : defines) { + if (!result.userDefines.empty()) + result.userDefines += ";"; + result.userDefines += define.toStdString(); + } + + result.clang = mProjectFile->clangParser; + + const QStringList undefines = mProjectFile->getUndefines(); + for (const QString& undefine : undefines) + result.userUndefs.insert(undefine.toStdString()); + + const QStringList libraries = mProjectFile->getLibraries(); + for (const QString& library : libraries) { + result.libraries.emplace_back(library.toStdString()); + const QString filename = library + ".cfg"; + tryLoadLibrary(result.library, filename); + } + + for (const SuppressionList::Suppression &suppression : mProjectFile->getCheckingSuppressions()) { + result.supprs.nomsg.addSuppression(suppression); + } + + // Only check the given -D configuration + if (!defines.isEmpty()) + result.maxConfigs = 1; + + // If importing a project, only check the given configuration + if (!mProjectFile->getImportProject().isEmpty()) + result.checkAllConfigurations = false; + + const QString &buildDir = fromNativePath(mProjectFile->getBuildDir()); + if (!buildDir.isEmpty()) { + if (QDir(buildDir).isAbsolute()) { + result.buildDir = buildDir.toStdString(); + } else { + QString prjpath = QFileInfo(mProjectFile->getFilename()).absolutePath(); + result.buildDir = (prjpath + '/' + buildDir).toStdString(); + } + } + + const QString platform = mProjectFile->getPlatform(); + if (platform.endsWith(".xml")) { + const QString applicationFilePath = QCoreApplication::applicationFilePath(); + result.platform.loadFromFile(applicationFilePath.toStdString().c_str(), platform.toStdString()); + } else { + for (int i = Platform::Type::Native; i <= Platform::Type::Unix64; i++) { + const auto p = (Platform::Type)i; + if (platform == Platform::toString(p)) { + result.platform.set(p); + break; + } + } + } + + result.maxCtuDepth = mProjectFile->getMaxCtuDepth(); + result.maxTemplateRecursion = mProjectFile->getMaxTemplateRecursion(); + if (mProjectFile->isCheckLevelExhaustive()) + result.setCheckLevel(Settings::CheckLevel::exhaustive); + else + result.setCheckLevel(Settings::CheckLevel::normal); + result.checkHeaders = mProjectFile->getCheckHeaders(); + result.checkUnusedTemplates = mProjectFile->getCheckUnusedTemplates(); + result.safeChecks.classes = mProjectFile->safeChecks.classes; + result.safeChecks.externalFunctions = mProjectFile->safeChecks.externalFunctions; + result.safeChecks.internalFunctions = mProjectFile->safeChecks.internalFunctions; + result.safeChecks.externalVariables = mProjectFile->safeChecks.externalVariables; + for (const QString& s : mProjectFile->getCheckUnknownFunctionReturn()) + result.checkUnknownFunctionReturn.insert(s.toStdString()); + + for (const QString& addon : mProjectFile->getAddons()) { + const QString addonError = loadAddon(result, filesDir, pythonCmd, addon); + if (!addonError.isEmpty()) { + QMessageBox::critical(this, tr("Error"), tr("%1\n\nAnalysis is aborted.").arg(addonError)); + return {false, {}}; + } + } + + if (isCppcheckPremium()) { + QString premiumArgs; + if (mProjectFile->getBughunting()) + premiumArgs += " --bughunting"; + if (mProjectFile->getCertIntPrecision() > 0) + premiumArgs += " --cert-c-int-precision=" + QString::number(mProjectFile->getCertIntPrecision()); + for (const QString& c: mProjectFile->getCodingStandards()) + premiumArgs += " --" + c; + if (!premiumArgs.contains("misra") && mProjectFile->getAddons().contains("misra")) + premiumArgs += " --misra-c-2012"; + result.premiumArgs = premiumArgs.mid(1).toStdString(); + result.setMisraRuleTexts(CheckThread::executeCommand); + } + } + + // 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.severity.enable(Severity::warning); + result.severity.enable(Severity::style); + result.severity.enable(Severity::performance); + result.severity.enable(Severity::portability); + result.severity.enable(Severity::information); + result.checks.enable(Checks::missingInclude); + if (!result.buildDir.empty()) + result.checks.enable(Checks::unusedFunction); + result.debugwarnings = mSettings->value(SETTINGS_SHOW_DEBUG_WARNINGS, false).toBool(); + result.quiet = false; + result.verbose = true; + result.force = mSettings->value(SETTINGS_CHECK_FORCE, 1).toBool(); + result.xml = false; + result.jobs = mSettings->value(SETTINGS_CHECK_THREADS, 1).toInt(); + result.inlineSuppressions = mSettings->value(SETTINGS_INLINE_SUPPRESSIONS, false).toBool(); + result.certainty.setEnabled(Certainty::inconclusive, mSettings->value(SETTINGS_INCONCLUSIVE_ERRORS, false).toBool()); + if (!mProjectFile || result.platform.type == Platform::Type::Unspecified) + result.platform.set((Platform::Type) mSettings->value(SETTINGS_CHECKED_PLATFORM, 0).toInt()); + result.standards.setCPP(mSettings->value(SETTINGS_STD_CPP, QString()).toString().toStdString()); + result.standards.setC(mSettings->value(SETTINGS_STD_C, QString()).toString().toStdString()); + result.enforcedLang = (Standards::Language)mSettings->value(SETTINGS_ENFORCED_LANGUAGE, 0).toInt(); + + if (result.jobs <= 1) { + result.jobs = 1; + } + + Settings::terminate(false); + + return {true, std::move(result)}; +} + +void MainWindow::analysisDone() +{ + if (mExiting) { + close(); + return; + } + + mUI->mResults->checkingFinished(); + enableCheckButtons(true); + mUI->mActionSettings->setEnabled(true); + mUI->mActionOpenXML->setEnabled(true); + if (mProjectFile) { + enableProjectActions(true); + } else if (mIsLogfileLoaded) { + mUI->mActionReanalyzeModified->setEnabled(false); + mUI->mActionReanalyzeAll->setEnabled(false); + } + enableProjectOpenActions(true); + mPlatformActions->setEnabled(true); + mCStandardActions->setEnabled(true); + mCppStandardActions->setEnabled(true); + mSelectLanguageActions->setEnabled(true); + mUI->mActionPosix->setEnabled(true); + if (mScratchPad) + mScratchPad->setEnabled(true); + mUI->mActionViewStats->setEnabled(true); + + if (mProjectFile && !mProjectFile->getBuildDir().isEmpty()) { + const QString prjpath = QFileInfo(mProjectFile->getFilename()).absolutePath(); + const QString buildDir = prjpath + '/' + mProjectFile->getBuildDir(); + if (QDir(buildDir).exists()) { + mUI->mResults->saveStatistics(buildDir + "/statistics.txt"); + mUI->mResults->updateFromOldReport(buildDir + "/lastResults.xml"); + mUI->mResults->save(buildDir + "/lastResults.xml", Report::XMLV2, mCppcheckCfgProductName); + } + } + + enableResultsButtons(); + + for (QAction* recentProjectAct : mRecentProjectActs) { + if (recentProjectAct != nullptr) + recentProjectAct->setEnabled(true); + } + + // Notify user - if the window is not active - that check is ready + QApplication::alert(this, 3000); + if (mSettings->value(SETTINGS_SHOW_STATISTICS, false).toBool()) + showStatistics(); +} + +void MainWindow::checkLockDownUI() +{ + enableCheckButtons(false); + mUI->mActionSettings->setEnabled(false); + mUI->mActionOpenXML->setEnabled(false); + enableProjectActions(false); + enableProjectOpenActions(false); + mPlatformActions->setEnabled(false); + mCStandardActions->setEnabled(false); + mCppStandardActions->setEnabled(false); + mSelectLanguageActions->setEnabled(false); + mUI->mActionPosix->setEnabled(false); + if (mScratchPad) + mScratchPad->setEnabled(false); + + for (QAction* recentProjectAct : mRecentProjectActs) { + if (recentProjectAct != nullptr) + recentProjectAct->setEnabled(false); + } +} + +void MainWindow::programSettings() +{ + SettingsDialog dialog(mApplications, mTranslation, isCppcheckPremium(), this); + if (dialog.exec() == QDialog::Accepted) { + dialog.saveSettingValues(); + mSettings->sync(); + mUI->mResults->updateSettings(dialog.showFullPath(), + dialog.saveFullPath(), + dialog.saveAllErrors(), + dialog.showNoErrorsMessage(), + dialog.showErrorId(), + dialog.showInconclusive()); + mUI->mResults->updateStyleSetting(mSettings); + const QString newLang = mSettings->value(SETTINGS_LANGUAGE, "en").toString(); + setLanguage(newLang); + } +} + +void MainWindow::reAnalyzeModified() +{ + reAnalyze(false); +} + +void MainWindow::reAnalyzeAll() +{ + if (mProjectFile) + analyzeProject(mProjectFile); + else + reAnalyze(true); +} + +void MainWindow::checkLibrary() +{ + if (mProjectFile) + analyzeProject(mProjectFile, true); +} + +void MainWindow::checkConfiguration() +{ + if (mProjectFile) + analyzeProject(mProjectFile, false, true); +} + +void MainWindow::reAnalyzeSelected(const QStringList& files) +{ + if (files.empty()) + return; + if (mThread->isChecking()) + return; + + const QPair checkSettingsPair = getCppcheckSettings(); + if (!checkSettingsPair.first) + return; + const Settings& checkSettings = checkSettingsPair.second; + + // Clear details, statistics and progress + mUI->mResults->clear(false); + for (int i = 0; i < files.size(); ++i) + mUI->mResults->clearRecheckFile(files[i]); + + mCurrentDirectory = mUI->mResults->getCheckDirectory(); + FileList pathList; + pathList.addPathList(files); + if (mProjectFile) + pathList.addExcludeList(mProjectFile->getExcludedPaths()); + QStringList fileNames = pathList.getFileList(); + checkLockDownUI(); // lock UI while checking + mUI->mResults->checkingStarted(fileNames.size()); + mThread->setCheckFiles(fileNames); + + // Saving last check start time, otherwise unchecked modified files will not be + // considered in "Modified Files Check" performed after "Selected Files Check" + // TODO: Should we store per file CheckStartTime? + QDateTime saveCheckStartTime = mThread->getCheckStartTime(); + mThread->check(checkSettings); + mUI->mResults->setCheckSettings(checkSettings); + mThread->setCheckStartTime(std::move(saveCheckStartTime)); +} + +void MainWindow::reAnalyze(bool all) +{ + const QStringList files = mThread->getReCheckFiles(all); + if (files.empty()) + return; + + const QPair& checkSettingsPair = getCppcheckSettings(); + if (!checkSettingsPair.first) + return; + const Settings& checkSettings = checkSettingsPair.second; + + // Clear details, statistics and progress + mUI->mResults->clear(all); + + // Clear results for changed files + for (int i = 0; i < files.size(); ++i) + mUI->mResults->clear(files[i]); + + checkLockDownUI(); // lock UI while checking + mUI->mResults->checkingStarted(files.size()); + + if (mProjectFile) + qDebug() << "Rechecking project file" << mProjectFile->getFilename(); + + mThread->setCheckFiles(all); + mThread->check(checkSettings); + mUI->mResults->setCheckSettings(checkSettings); +} + +void MainWindow::clearResults() +{ + if (mProjectFile && !mProjectFile->getBuildDir().isEmpty()) { + QDir dir(QFileInfo(mProjectFile->getFilename()).absolutePath() + '/' + mProjectFile->getBuildDir()); + for (const QString& f: dir.entryList(QDir::Files)) { + if (!f.endsWith("files.txt")) { + static const QRegularExpression rx("^.*.s[0-9]+$"); + if (!rx.match(f).hasMatch()) + dir.remove(f); + } + } + } + mUI->mResults->clear(true); + Q_ASSERT(false == mUI->mResults->hasResults()); + enableResultsButtons(); +} + +void MainWindow::openResults() +{ + if (mUI->mResults->hasResults()) { + QMessageBox msgBox(this); + msgBox.setWindowTitle(tr("Cppcheck")); + const QString msg(tr("Current results will be cleared.\n\n" + "Opening a new XML file will clear current results.\n" + "Do you want to proceed?")); + msgBox.setText(msg); + msgBox.setIcon(QMessageBox::Warning); + msgBox.addButton(QMessageBox::Yes); + msgBox.addButton(QMessageBox::No); + msgBox.setDefaultButton(QMessageBox::Yes); + const int dlgResult = msgBox.exec(); + if (dlgResult == QMessageBox::No) { + return; + } + } + + QString selectedFilter; + const QString filter(tr("XML files (*.xml)")); + QString selectedFile = QFileDialog::getOpenFileName(this, + tr("Open the report file"), + getPath(SETTINGS_LAST_RESULT_PATH), + filter, + &selectedFilter); + + if (!selectedFile.isEmpty()) { + loadResults(selectedFile); + } +} + +void MainWindow::loadResults(const QString &selectedFile) +{ + if (selectedFile.isEmpty()) + return; + if (mProjectFile) + closeProjectFile(); + mIsLogfileLoaded = true; + mUI->mResults->clear(true); + mUI->mActionReanalyzeModified->setEnabled(false); + mUI->mActionReanalyzeAll->setEnabled(false); + mUI->mResults->readErrorsXml(selectedFile); + setPath(SETTINGS_LAST_RESULT_PATH, selectedFile); + formatAndSetTitle(selectedFile); +} + +void MainWindow::loadResults(const QString &selectedFile, const QString &sourceDirectory) +{ + loadResults(selectedFile); + mUI->mResults->setCheckDirectory(sourceDirectory); +} + +void MainWindow::enableCheckButtons(bool enable) +{ + mUI->mActionStop->setEnabled(!enable); + mUI->mActionAnalyzeFiles->setEnabled(enable); + + if (mProjectFile) { + mUI->mActionReanalyzeModified->setEnabled(false); + mUI->mActionReanalyzeAll->setEnabled(enable); + } else if (!enable || mThread->hasPreviousFiles()) { + mUI->mActionReanalyzeModified->setEnabled(enable); + mUI->mActionReanalyzeAll->setEnabled(enable); + } + + mUI->mActionAnalyzeDirectory->setEnabled(enable); + + if (isCppcheckPremium()) { + mUI->mActionComplianceReport->setEnabled(enable && mProjectFile && (mProjectFile->getAddons().contains("misra") || !mProjectFile->getCodingStandards().empty())); + } +} + +void MainWindow::enableResultsButtons() +{ + const bool enabled = mUI->mResults->hasResults(); + mUI->mActionClearResults->setEnabled(enabled); + mUI->mActionSave->setEnabled(enabled); + mUI->mActionPrint->setEnabled(enabled); + mUI->mActionPrintPreview->setEnabled(enabled); +} + +void MainWindow::showStyle(bool checked) +{ + mUI->mResults->showResults(ShowTypes::ShowStyle, checked); +} + +void MainWindow::showErrors(bool checked) +{ + mUI->mResults->showResults(ShowTypes::ShowErrors, checked); +} + +void MainWindow::showWarnings(bool checked) +{ + mUI->mResults->showResults(ShowTypes::ShowWarnings, checked); +} + +void MainWindow::showPortability(bool checked) +{ + mUI->mResults->showResults(ShowTypes::ShowPortability, checked); +} + +void MainWindow::showPerformance(bool checked) +{ + mUI->mResults->showResults(ShowTypes::ShowPerformance, checked); +} + +void MainWindow::showInformation(bool checked) +{ + mUI->mResults->showResults(ShowTypes::ShowInformation, checked); +} + +void MainWindow::checkAll() +{ + toggleAllChecked(true); +} + +void MainWindow::uncheckAll() +{ + toggleAllChecked(false); +} + +void MainWindow::closeEvent(QCloseEvent *event) +{ + // Check that we aren't checking files + if (!mThread->isChecking()) { + saveSettings(); + event->accept(); + } else { + const QString text(tr("Analyzer is running.\n\n" \ + "Do you want to stop the analysis and exit Cppcheck?")); + + QMessageBox msg(QMessageBox::Warning, + tr("Cppcheck"), + text, + QMessageBox::Yes | QMessageBox::No, + this); + + msg.setDefaultButton(QMessageBox::No); + const 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() +{ + if (!mCppcheckCfgAbout.isEmpty()) { + QMessageBox msg(QMessageBox::Information, + tr("About"), + mCppcheckCfgAbout, + QMessageBox::Ok, + this); + msg.exec(); + } + else { + auto *dlg = new AboutDialog(CppCheck::version(), CppCheck::extraVersion(), this); + dlg->exec(); + } +} + +void MainWindow::showLicense() +{ + auto *dlg = new FileViewDialog(":COPYING", tr("License"), this); + dlg->resize(570, 400); + dlg->exec(); +} + +void MainWindow::showAuthors() +{ + auto *dlg = new FileViewDialog(":AUTHORS", tr("Authors"), this); + dlg->resize(350, 400); + dlg->exec(); +} + +void MainWindow::performSelectedFilesCheck(const QStringList &selectedFilesList) +{ + reAnalyzeSelected(selectedFilesList); +} + +void MainWindow::save() +{ + QString selectedFilter; + const QString filter(tr("XML files (*.xml);;Text files (*.txt);;CSV files (*.csv)")); + QString selectedFile = QFileDialog::getSaveFileName(this, + tr("Save the report file"), + getPath(SETTINGS_LAST_RESULT_PATH), + filter, + &selectedFilter); + + if (!selectedFile.isEmpty()) { + Report::Type type = Report::TXT; + if (selectedFilter == tr("XML files (*.xml)")) { + type = Report::XMLV2; + if (!selectedFile.endsWith(".xml", Qt::CaseInsensitive)) + selectedFile += ".xml"; + } else if (selectedFilter == tr("Text files (*.txt)")) { + type = Report::TXT; + if (!selectedFile.endsWith(".txt", Qt::CaseInsensitive)) + selectedFile += ".txt"; + } else if (selectedFilter == tr("CSV files (*.csv)")) { + type = Report::CSV; + if (!selectedFile.endsWith(".csv", Qt::CaseInsensitive)) + selectedFile += ".csv"; + } else { + if (selectedFile.endsWith(".xml", Qt::CaseInsensitive)) + type = Report::XMLV2; + else if (selectedFile.endsWith(".txt", Qt::CaseInsensitive)) + type = Report::TXT; + else if (selectedFile.endsWith(".csv", Qt::CaseInsensitive)) + type = Report::CSV; + } + + mUI->mResults->save(selectedFile, type, mCppcheckCfgProductName); + setPath(SETTINGS_LAST_RESULT_PATH, selectedFile); + } +} + +void MainWindow::complianceReport() +{ + if (!mUI->mResults->isSuccess()) { + QMessageBox m(QMessageBox::Critical, + "Cppcheck", + tr("Cannot generate a compliance report right now, an analysis must finish successfully. Try to reanalyze the code and ensure there are no critical errors."), + QMessageBox::Ok, + this); + m.exec(); + return; + } + + QTemporaryFile tempResults; + tempResults.open(); + tempResults.close(); + + mUI->mResults->save(tempResults.fileName(), Report::XMLV2, mCppcheckCfgProductName); + + ComplianceReportDialog dlg(mProjectFile, tempResults.fileName()); + dlg.exec(); +} + +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 nameWithVersion = QString("Cppcheck %1").arg(CppCheck::version()); + + QString extraVersion = CppCheck::extraVersion(); + if (!extraVersion.isEmpty()) { + nameWithVersion += " (" + extraVersion + ")"; + } + + if (!mCppcheckCfgProductName.isEmpty()) + nameWithVersion = mCppcheckCfgProductName; + + QString title; + if (text.isEmpty()) + title = nameWithVersion; + else + title = QString("%1 - %2").arg(nameWithVersion, 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(); + mLineEditFilter->setPlaceholderText(QCoreApplication::translate("MainWindow", "Quick Filter:")); + if (mProjectFile) + formatAndSetTitle(tr("Project:") + ' ' + mProjectFile->getFilename()); + if (mScratchPad) + mScratchPad->translate(); + } +} + +void MainWindow::aboutToShowViewMenu() +{ + mUI->mActionToolBarMain->setChecked(mUI->mToolBarMain->isVisible()); + mUI->mActionToolBarView->setChecked(mUI->mToolBarView->isVisible()); + mUI->mActionToolBarFilter->setChecked(mUI->mToolBarFilter->isVisible()); +} + +void MainWindow::stopAnalysis() +{ + mThread->stop(); + mUI->mResults->stopAnalysis(); + mUI->mResults->disableProgressbar(); + const QString &lastResults = getLastResults(); + if (!lastResults.isEmpty()) { + mUI->mResults->updateFromOldReport(lastResults); + } +} + +void MainWindow::openHelpContents() +{ + openOnlineHelp(); +} + +void MainWindow::openOnlineHelp() +{ + auto *helpDialog = new HelpDialog; + helpDialog->showMaximized(); +} + +void MainWindow::openProjectFile() +{ + const QString filter = tr("Project files (*.cppcheck);;All files(*.*)"); + const QString filepath = QFileDialog::getOpenFileName(this, + tr("Select Project File"), + getPath(SETTINGS_LAST_PROJECT_PATH), + filter); + + if (!filepath.isEmpty()) { + const QFileInfo fi(filepath); + if (fi.exists() && fi.isFile() && fi.isReadable()) { + setPath(SETTINGS_LAST_PROJECT_PATH, filepath); + loadProjectFile(filepath); + } + } +} + +void MainWindow::showScratchpad() +{ + if (!mScratchPad) + mScratchPad = new ScratchPad(*this); + + mScratchPad->show(); + + if (!mScratchPad->isActiveWindow()) + mScratchPad->activateWindow(); +} + +void MainWindow::loadProjectFile(const QString &filePath) +{ + QFileInfo inf(filePath); + const QString filename = inf.fileName(); + formatAndSetTitle(tr("Project:") + ' ' + filename); + addProjectMRU(filePath); + + mIsLogfileLoaded = false; + mUI->mActionCloseProjectFile->setEnabled(true); + mUI->mActionEditProjectFile->setEnabled(true); + delete mProjectFile; + mProjectFile = new ProjectFile(filePath, this); + mProjectFile->setActiveProject(); + if (!loadLastResults()) + analyzeProject(mProjectFile); +} + +QString MainWindow::getLastResults() const +{ + if (!mProjectFile || mProjectFile->getBuildDir().isEmpty()) + return QString(); + return QFileInfo(mProjectFile->getFilename()).absolutePath() + '/' + mProjectFile->getBuildDir() + "/lastResults.xml"; +} + +bool MainWindow::loadLastResults() +{ + const QString &lastResults = getLastResults(); + if (lastResults.isEmpty()) + return false; + if (!QFileInfo::exists(lastResults)) + return false; + mUI->mResults->readErrorsXml(lastResults); + mUI->mResults->setCheckDirectory(mSettings->value(SETTINGS_LAST_CHECK_PATH,QString()).toString()); + mUI->mActionViewStats->setEnabled(true); + enableResultsButtons(); + return true; +} + +void MainWindow::analyzeProject(const ProjectFile *projectFile, const bool checkLibrary, const bool checkConfiguration) +{ + Settings::terminate(false); + + QFileInfo inf(projectFile->getFilename()); + const QString& rootpath = projectFile->getRootPath(); + + QDir::setCurrent(inf.absolutePath()); + + mThread->setAddonsAndTools(projectFile->getAddonsAndTools()); + + // If the root path is not given or is not "current dir", use project + // file's location directory as root path + if (rootpath.isEmpty() || rootpath == ".") + mCurrentDirectory = inf.canonicalPath(); + else if (rootpath.startsWith("./")) + mCurrentDirectory = inf.canonicalPath() + rootpath.mid(1); + else + mCurrentDirectory = rootpath; + + if (!projectFile->getBuildDir().isEmpty()) { + QString buildDir = projectFile->getBuildDir(); + if (!QDir::isAbsolutePath(buildDir)) + buildDir = inf.canonicalPath() + '/' + buildDir; + if (!QDir(buildDir).exists()) { + QMessageBox msg(QMessageBox::Question, + tr("Cppcheck"), + tr("Build dir '%1' does not exist, create it?").arg(buildDir), + QMessageBox::Yes | QMessageBox::No, + this); + if (msg.exec() == QMessageBox::Yes) { + QDir().mkpath(buildDir); + } else if (!projectFile->getAddons().isEmpty()) { + QMessageBox m(QMessageBox::Critical, + tr("Cppcheck"), + tr("To check the project using addons, you need a build directory."), + QMessageBox::Ok, + this); + m.exec(); + return; + } + } + } + + if (!projectFile->getImportProject().isEmpty()) { + ImportProject p; + QString prjfile; + + if (QFileInfo(projectFile->getImportProject()).isAbsolute()) { + prjfile = projectFile->getImportProject(); + } else { + prjfile = inf.canonicalPath() + '/' + projectFile->getImportProject(); + } + try { + + const ImportProject::Type result = p.import(prjfile.toStdString()); + + QString errorMessage; + switch (result) { + case ImportProject::Type::COMPILE_DB: + case ImportProject::Type::VS_SLN: + case ImportProject::Type::VS_VCXPROJ: + case ImportProject::Type::BORLAND: + case ImportProject::Type::CPPCHECK_GUI: + // Loading was successful + break; + case ImportProject::Type::MISSING: + errorMessage = tr("Failed to open file"); + break; + case ImportProject::Type::UNKNOWN: + errorMessage = tr("Unknown project file format"); + break; + case ImportProject::Type::FAILURE: + errorMessage = tr("Failed to import project file"); + break; + case ImportProject::Type::NONE: + // can never happen + break; + } + + if (!errorMessage.isEmpty()) { + QMessageBox msg(QMessageBox::Critical, + tr("Cppcheck"), + tr("Failed to import '%1': %2\n\nAnalysis is stopped.").arg(prjfile).arg(errorMessage), + QMessageBox::Ok, + this); + msg.exec(); + return; + } + } catch (InternalError &e) { + QMessageBox msg(QMessageBox::Critical, + tr("Cppcheck"), + tr("Failed to import '%1' (%2), analysis is stopped").arg(prjfile).arg(QString::fromStdString(e.errorMessage)), + QMessageBox::Ok, + this); + msg.exec(); + return; + } + doAnalyzeProject(p, checkLibrary, checkConfiguration); + return; + } + + QStringList paths = projectFile->getCheckPaths(); + + // If paths not given then check the root path (which may be the project + // file's location, see above). This is to keep the compatibility with + // old "silent" project file loading when we checked the director where the + // project file was located. + if (paths.isEmpty()) { + paths << mCurrentDirectory; + } + doAnalyzeFiles(paths, checkLibrary, checkConfiguration); +} + +void MainWindow::newProjectFile() +{ + const QString filter = tr("Project files (*.cppcheck)"); + QString filepath = QFileDialog::getSaveFileName(this, + tr("Select Project Filename"), + getPath(SETTINGS_LAST_PROJECT_PATH), + filter); + + if (filepath.isEmpty()) + return; + if (!filepath.endsWith(".cppcheck", Qt::CaseInsensitive)) + filepath += ".cppcheck"; + + setPath(SETTINGS_LAST_PROJECT_PATH, filepath); + + QFileInfo inf(filepath); + const QString filename = inf.fileName(); + formatAndSetTitle(tr("Project:") + QString(" ") + filename); + + delete mProjectFile; + mProjectFile = new ProjectFile(this); + mProjectFile->setActiveProject(); + mProjectFile->setFilename(filepath); + mProjectFile->setProjectName(filename.left(filename.indexOf("."))); + mProjectFile->setBuildDir(filename.left(filename.indexOf(".")) + "-cppcheck-build-dir"); + + ProjectFileDialog dlg(mProjectFile, isCppcheckPremium(), this); + if (dlg.exec() == QDialog::Accepted) { + addProjectMRU(filepath); + analyzeProject(mProjectFile); + } else { + closeProjectFile(); + } +} + +void MainWindow::closeProjectFile() +{ + delete mProjectFile; + mProjectFile = nullptr; + mUI->mResults->clear(true); + enableProjectActions(false); + enableProjectOpenActions(true); + formatAndSetTitle(); +} + +void MainWindow::editProjectFile() +{ + if (!mProjectFile) { + QMessageBox msg(QMessageBox::Critical, + tr("Cppcheck"), + QString(tr("No project file loaded")), + QMessageBox::Ok, + this); + msg.exec(); + return; + } + + ProjectFileDialog dlg(mProjectFile, isCppcheckPremium(), this); + if (dlg.exec() == QDialog::Accepted) { + mProjectFile->write(); + analyzeProject(mProjectFile); + } +} + +void MainWindow::showStatistics() +{ + StatsDialog statsDialog(this); + + // Show a dialog with the previous scan statistics and project information + statsDialog.setProject(mProjectFile); + statsDialog.setPathSelected(mCurrentDirectory); + statsDialog.setNumberOfFilesScanned(mThread->getPreviousFilesCount()); + statsDialog.setScanDuration(mThread->getPreviousScanDuration() / 1000.0); + statsDialog.setStatistics(mUI->mResults->getStatistics()); + + statsDialog.exec(); +} + +void MainWindow::showLibraryEditor() +{ + LibraryDialog libraryDialog(this); + libraryDialog.exec(); +} + +void MainWindow::filterResults() +{ + mUI->mResults->filterResults(mLineEditFilter->text()); +} + +void MainWindow::enableProjectActions(bool enable) +{ + mUI->mActionCloseProjectFile->setEnabled(enable); + mUI->mActionEditProjectFile->setEnabled(enable); + mUI->mActionCheckLibrary->setEnabled(enable); + mUI->mActionCheckConfiguration->setEnabled(enable); +} + +void MainWindow::enableProjectOpenActions(bool enable) +{ + mUI->mActionNewProjectFile->setEnabled(enable); + mUI->mActionOpenProjectFile->setEnabled(enable); +} + +void MainWindow::openRecentProject() +{ + auto *action = qobject_cast(sender()); + if (!action) + return; + const QString project = action->data().toString(); + QFileInfo inf(project); + if (inf.exists()) { + if (inf.suffix() == "xml") + loadResults(project); + else { + loadProjectFile(project); + loadLastResults(); + } + } else { + const QString text(tr("The project file\n\n%1\n\n could not be found!\n\n" + "Do you want to remove the file from the recently " + "used projects -list?").arg(project)); + + QMessageBox msg(QMessageBox::Warning, + tr("Cppcheck"), + text, + QMessageBox::Yes | QMessageBox::No, + this); + + msg.setDefaultButton(QMessageBox::No); + const int rv = msg.exec(); + if (rv == QMessageBox::Yes) { + removeProjectMRU(project); + } + } +} + +void MainWindow::updateMRUMenuItems() +{ + for (QAction* recentProjectAct : mRecentProjectActs) { + if (recentProjectAct != nullptr) + mUI->mMenuFile->removeAction(recentProjectAct); + } + + QStringList projects = mSettings->value(SETTINGS_MRU_PROJECTS).toStringList(); + + // Do a sanity check - remove duplicates and non-existing projects + int removed = projects.removeDuplicates(); + for (int i = projects.size() - 1; i >= 0; i--) { + if (!QFileInfo::exists(projects[i])) { + 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() +{ + auto *action = qobject_cast(sender()); + if (action) { + const Platform::Type platform = (Platform::Type) action->data().toInt(); + mSettings->setValue(SETTINGS_CHECKED_PLATFORM, platform); + } +} + +void MainWindow::suppressIds(QStringList ids) +{ + if (!mProjectFile) + return; + ids.removeDuplicates(); + + QList suppressions = mProjectFile->getSuppressions(); + for (const QString& id : ids) { + // Remove all matching suppressions + std::string id2 = id.toStdString(); + for (int i = 0; i < suppressions.size();) { + if (suppressions[i].errorId == id2) + suppressions.removeAt(i); + else + ++i; + } + + SuppressionList::Suppression newSuppression; + newSuppression.errorId = id2; + suppressions << newSuppression; + } + + mProjectFile->setSuppressions(suppressions); + mProjectFile->write(); +} + +static int getVersion(const QString& nameWithVersion) { + int ret = 0; + int v = 0; + int dot = 0; + for (const auto c: nameWithVersion) { + if (c == '\n' || c == '\r') + break; + if (c == ' ') { + if (ret > 0 && dot == 1 && nameWithVersion.endsWith(" dev")) + return ret * 1000000 + v * 1000 + 500; + dot = ret = v = 0; + } + else if (c == '.') { + ++dot; + ret = ret * 1000 + v; + v = 0; + } else if (c >= '0' && c <= '9') + v = v * 10 + (c.toLatin1() - '0'); + } + ret = ret * 1000 + v; + while (dot < 2) { + ++dot; + ret *= 1000; + } + return ret; +} + +void MainWindow::replyFinished(QNetworkReply *reply) { + reply->deleteLater(); + if (reply->error()) { + mUI->mLayoutInformation->deleteLater(); + qDebug() << "Response: ERROR"; + return; + } + const QString str = reply->readAll(); + qDebug() << "Response: " << str; + if (reply->url().fileName() == "version.txt") { + QString nameWithVersion = QString("Cppcheck %1").arg(CppCheck::version()); + if (!mCppcheckCfgProductName.isEmpty()) + nameWithVersion = mCppcheckCfgProductName; + const int appVersion = getVersion(nameWithVersion); + const int latestVersion = getVersion(str.trimmed()); + if (appVersion < latestVersion) { + if (mSettings->value(SETTINGS_CHECK_VERSION, 0).toInt() != latestVersion) { + QString install; + if (isCppcheckPremium()) { +#ifdef Q_OS_WIN + const QString url("https://cppchecksolutions.com/cppcheck-premium-installation"); +#else + const QString url("https://cppchecksolutions.com/cppcheck-premium-linux-installation"); +#endif + install = "" + tr("Install") + ""; + } + mUI->mButtonHideInformation->setVisible(true); + mUI->mLabelInformation->setVisible(true); + mUI->mLabelInformation->setText(tr("New version available: %1. %2").arg(str.trimmed()).arg(install)); + } + } + } + if (!mUI->mLabelInformation->isVisible()) { + mUI->mLayoutInformation->deleteLater(); + } +} + +void MainWindow::hideInformation() { + int version = getVersion(mUI->mLabelInformation->text()); + mSettings->setValue(SETTINGS_CHECK_VERSION, version); + mUI->mLabelInformation->setVisible(false); + mUI->mButtonHideInformation->setVisible(false); + mUI->mLayoutInformation->deleteLater(); +} + +bool MainWindow::isCppcheckPremium() const { + return mCppcheckCfgProductName.startsWith("Cppcheck Premium "); +} + diff --git a/cppcheck-2.14.0/gui/mainwindow.h b/cppcheck-2.14.0/gui/mainwindow.h new file mode 100644 index 00000000..6a039bf0 --- /dev/null +++ b/cppcheck-2.14.0/gui/mainwindow.h @@ -0,0 +1,485 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "library.h" +#include "platforms.h" + +#include +#include +#include +#include +#include +#include + +class ThreadHandler; +class TranslationHandler; +class ScratchPad; +class ProjectFile; +class ApplicationList; +class QAction; +class QActionGroup; +class QSettings; +class QTimer; +class QLineEdit; +class ImportProject; +class QCloseEvent; +class QNetworkAccessManager; +class QNetworkReply; +class Settings; +namespace Ui { + class MainWindow; +} + +/// @addtogroup GUI +/// @{ + +/** + * @brief Main window for cppcheck-gui + * + */ +class MainWindow : public QMainWindow { + Q_OBJECT +public: + + /** + * @brief Maximum number of MRU project items in File-menu. + */ + enum { MaxRecentProjects = 5 }; + + MainWindow(TranslationHandler* th, QSettings* settings); + MainWindow(const MainWindow &) = delete; + ~MainWindow() override; + MainWindow &operator=(const MainWindow &) = delete; + + /** + * List of checked platforms. + */ + Platforms mPlatforms; + + /** + * @brief Analyze given code + * + * @param code Content of the (virtual) file to be analyzed + * @param filename Name of the (virtual) file to be analyzed - determines language. + */ + void analyzeCode(const QString& code, const QString& filename); + +public slots: + /** @brief Slot for analyze files menu item */ + void analyzeFiles(); + + /** @brief Slot to reanalyze all files */ + void reAnalyzeAll(); + + /** @brief Slot to reanalyze with checking library configuration */ + void checkLibrary(); + + /** @brief Slot to check configuration */ + void checkConfiguration(); + + /** + * @brief Slot to reanalyze selected files + * @param selectedFilesList list of selected files + */ + void performSelectedFilesCheck(const QStringList &selectedFilesList); + + /** @brief Slot to reanalyze modified files */ + void reAnalyzeModified(); + + /** @brief Slot to clear all search results */ + void clearResults(); + + /** @brief Slot to open XML report file */ + void openResults(); + + /** + * @brief Show errors with type "style" + * @param checked Should errors be shown (true) or hidden (false) + */ + void showStyle(bool checked); + + /** + * @brief Show errors with type "error" + * @param checked Should errors be shown (true) or hidden (false) + */ + void showErrors(bool checked); + + /** + * @brief Show errors with type "warning" + * @param checked Should errors be shown (true) or hidden (false) + */ + void showWarnings(bool checked); + + /** + * @brief Show errors with type "portability" + * @param checked Should errors be shown (true) or hidden (false) + */ + void showPortability(bool checked); + + /** + * @brief Show errors with type "performance" + * @param checked Should errors be shown (true) or hidden (false) + */ + void showPerformance(bool checked); + + /** + * @brief Show errors with type "information" + * @param checked Should errors be shown (true) or hidden (false) + */ + void showInformation(bool checked); + + /** @brief Slot to check all "Show errors" menu items */ + void checkAll(); + + /** @brief Slot to uncheck all "Show errors" menu items */ + void uncheckAll(); + + /** @brief Slot for analyze directory menu item */ + void analyzeDirectory(); + + /** @brief Slot to open program's settings dialog */ + void programSettings(); + + /** @brief Slot to open program's about dialog */ + void about(); + + /** @brief Slot to to show license text */ + void showLicense(); + + /** @brief Slot to to show authors list */ + void showAuthors(); + + /** @brief Slot to save results */ + void save(); + + /** @brief Slot to generate compliance report */ + void complianceReport(); + + /** @brief Slot to create new project file */ + void newProjectFile(); + + /** @brief Slot to open project file and start analyzing contained paths. */ + void openProjectFile(); + + /** @brief Slot to show scratchpad. */ + void showScratchpad(); + + /** @brief Slot to close open project file. */ + void closeProjectFile(); + + /** @brief Slot to edit project file. */ + void editProjectFile(); + + /** @brief Slot for showing the scan and project statistics. */ + void showStatistics(); + + /** @brief Slot for showing the library editor */ + void showLibraryEditor(); + +private slots: + + /** @brief Slot for checkthread's done signal */ + void analysisDone(); + + /** @brief Lock down UI while analyzing */ + void checkLockDownUI(); + + /** @brief Slot for enabling save and clear button */ + void resultsAdded(); + + /** @brief Slot for showing/hiding standard toolbar */ + void toggleMainToolBar(); + + /** @brief Slot for showing/hiding Categories toolbar */ + void toggleViewToolBar(); + + /** @brief Slot for showing/hiding Filter toolbar */ + void toggleFilterToolBar(); + + /** @brief Slot for updating View-menu before it is shown. */ + void aboutToShowViewMenu(); + + /** @brief Slot when stop analysis button is pressed */ + void stopAnalysis(); + + /** @brief Open help file contents */ + void openHelpContents(); + + /** @brief Filters the results in the result list. */ + void filterResults(); + + /** @brief Opens recently opened project file. */ + void openRecentProject(); + + /** @brief Selects the platform as analyzed platform. */ + void selectPlatform(); + + /** Suppress error ids */ + void suppressIds(QStringList ids); + +private slots: + void replyFinished(QNetworkReply *reply); + + void hideInformation(); +private: + + bool isCppcheckPremium() const; + + /** Get filename for last results */ + QString getLastResults() const; + + /** @brief Reanalyzes files */ + void reAnalyze(bool all); + + /** + * @brief Reanalyze selected files + * @param files list of selected files + */ + void reAnalyzeSelected(const QStringList& files); + + /** + * @brief Analyze the project. + * @param projectFile Pointer to the project to analyze. + * @param checkLibrary Flag to indicate if the library should be checked. + * @param checkConfiguration Flag to indicate if the configuration should be checked. + */ + void analyzeProject(const ProjectFile *projectFile, const bool checkLibrary = false, const bool checkConfiguration = false); + + /** + * @brief Set current language + * @param code Language code of the language to set (e.g. "en"). + */ + void setLanguage(const QString &code); + + /** @brief Event coming when application is about to close. */ + void closeEvent(QCloseEvent *event) override; + + /** + * @brief Helper function to toggle all show error menu items + * @param checked Should all errors be shown (true) or hidden (false) + */ + void toggleAllChecked(bool checked); + + /** @brief Helper function to enable/disable all check,recheck buttons */ + void enableCheckButtons(bool enable); + + /** @brief Helper function to enable/disable results buttons (clear,save,print) */ + void enableResultsButtons(); + + /** + * @brief Select files/or directory to analyze. + * Helper function to open a dialog to ask user to select files or + * directory to analyze. Use native dialogs instead of Qt:s own dialogs. + * + * @param mode Dialog open mode (files or directories) + * @return QStringList of files or directories that were selected to analyze + */ + QStringList selectFilesToAnalyze(QFileDialog::FileMode mode); + + /** + * @brief Analyze project + * @param p imported project + * @param checkLibrary Flag to indicate if library should be checked + * @param checkConfiguration Flag to indicate if the configuration should be checked. + */ + void doAnalyzeProject(ImportProject p, const bool checkLibrary = false, const bool checkConfiguration = false); + + /** + * @brief Analyze all files specified in parameter files + * + * @param files List of files and/or directories to analyze + * @param checkLibrary Flag to indicate if library should be checked + * @param checkConfiguration Flag to indicate if the configuration should be checked. + */ + void doAnalyzeFiles(const QStringList &files, const bool checkLibrary = false, const bool checkConfiguration = false); + + /** + * @brief Get our default cppcheck settings and read project file. + * + * @return Default cppcheck settings + */ + QPair 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 */ + static void openOnlineHelp(); + + /** + * @brief Enable or disable project file actions. + * Project editing and closing actions should be only enabled when project is + * open and we are not analyzing files. + * @param enable If true then actions are enabled. + */ + void enableProjectActions(bool enable); + + /** + * @brief Enable or disable project file actions. + * Project opening and creating actions should be disabled when analyzing. + * @param enable If true then actions are enabled. + */ + void enableProjectOpenActions(bool enable); + + /** + * @brief Add include directories. + * @param includeDirs List of include directories to add. + * @param result Settings class where include directories are added. + */ + void addIncludeDirs(const QStringList &includeDirs, Settings &result); + + /** + * @brief Handle command line parameters given to GUI. + * @param params List of string given to command line. + */ + void handleCLIParams(const QStringList ¶ms); + + /** + * @brief Load XML file to the GUI. + * @param selectedFile Filename (inc. path) of XML file to load. + */ + void loadResults(const QString &selectedFile); + + /** + * @brief Load XML file to the GUI. + * @param selectedFile Filename (inc. path) of XML file to load. + * @param sourceDirectory Path to the directory that the results were generated for. + */ + void loadResults(const QString &selectedFile, const QString &sourceDirectory); + + /** + * @brief Load last project results to the GUI. + * @return Returns true if last results was loaded + */ + bool loadLastResults(); + + /** + * @brief Load project file to the GUI. + * @param filePath Filename (inc. path) of project file to load. + */ + void loadProjectFile(const QString &filePath); + + /** + * @brief Load library file + * @param library library to use + * @param filename filename (no path) + * @return error code + */ + Library::Error loadLibrary(Library &library, const QString &filename); + + /** + * @brief Tries to load library file, prints message on error + * @param library library to use + * @param filename filename (no path) + * @return True if no error + */ + bool tryLoadLibrary(Library &library, const QString& filename); + + QString loadAddon(Settings &settings, const QString &filesDir, const QString &pythonCmd, const QString& addon); + + /** + * @brief Update project MRU items in File-menu. + */ + void updateMRUMenuItems(); + + /** + * @brief Add project file (path) to the MRU list. + * @param project Full path to the project file to add. + */ + void addProjectMRU(const QString &project); + + /** + * @brief Remove project file (path) from the MRU list. + * @param project Full path of the project file to remove. + */ + void removeProjectMRU(const QString &project); + + /** @brief Program settings */ + QSettings *mSettings; + + /** @brief Thread to analyze files */ + ThreadHandler *mThread; + + /** @brief List of user defined applications to open errors with */ + ApplicationList *mApplications; + + /** @brief Class to handle translation changes */ + TranslationHandler *mTranslation; + + /** @brief Class holding all UI components */ + Ui::MainWindow *mUI; + + /** @brief Current analyzed directory. */ + QString mCurrentDirectory; + + /** @brief Scratchpad. */ + ScratchPad* mScratchPad{}; + + /** @brief Project (file). */ + ProjectFile* mProjectFile{}; + + /** @brief Filter field in the Filter toolbar. */ + QLineEdit* mLineEditFilter; + + /** @brief Timer to delay filtering while typing. */ + QTimer* mFilterTimer; + + /** @brief GUI actions for selecting the analyzed platform. */ + QActionGroup *mPlatformActions; + + /** @brief GUI actions for selecting the coding standard. */ + QActionGroup *mCStandardActions, *mCppStandardActions; + + /** @brief GUI actions for selecting language. */ + QActionGroup *mSelectLanguageActions; + + /** + * @brief Are we exiting the cppcheck? + * If this is true then the cppcheck is waiting for check threads to exit + * so that the application can be closed. + */ + bool mExiting{}; + + /** @brief Set to true in case of loading log file. */ + bool mIsLogfileLoaded{}; + + /** + * @brief Project MRU menu actions. + * List of MRU menu actions. Needs also to store the separator. + */ + QAction *mRecentProjectActs[MaxRecentProjects + 1]; + + QString mCppcheckCfgAbout; + QString mCppcheckCfgProductName; + + QNetworkAccessManager *mNetworkAccessManager = nullptr; +}; +/// @} +#endif // MAINWINDOW_H diff --git a/cppcheck-2.14.0/gui/mainwindow.ui b/cppcheck-2.14.0/gui/mainwindow.ui new file mode 100644 index 00000000..fc2e6cbe --- /dev/null +++ b/cppcheck-2.14.0/gui/mainwindow.ui @@ -0,0 +1,936 @@ + + + MainWindow + + + + 0 + 0 + 640 + 480 + + + + + 0 + 0 + + + + + 640 + 480 + + + + Cppcheck + + + + 22 + 22 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + + 0 + 0 + + + + + + + + QLayout::SetDefaultConstraint + + + + + + 0 + 0 + + + + + 16777215 + 25 + + + + Checking for updates + + + Qt::RichText + + + true + + + Qt::TextBrowserInteraction + + + + + + + Hide + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + 0 + 640 + 30 + + + + + &File + + + + + + + + + + + + + + + + + + + + &View + + + + &Toolbars + + + + + + + + + + + + + + + + + + + + + + + + + + + + &Help + + + + + + + + + + A&nalyze + + + + C++ standard + + + + + + + + + + + &C standard + + + + + + + + + + + + + + + + + + + + + + + + + &Edit + + + + + + + + + + + + + Standard + + + TopToolBarArea + + + false + + + + + + + + + + + + Categories + + + TopToolBarArea + + + false + + + + + + + + + + + + + Filter + + + TopToolBarArea + + + false + + + + + &License... + + + + + A&uthors... + + + + + + :/images/help-browser.png:/images/help-browser.png + + + &About... + + + + + &Files... + + + Analyze files + + + Analyze files + + + Ctrl+F + + + + + + :/cppcheck-gui.png:/cppcheck-gui.png + + + &Directory... + + + Analyze directory + + + Analyze directory + + + Ctrl+D + + + + + + :/images/view-refresh.png:/images/view-refresh.png + + + &Reanalyze modified files + + + Ctrl+R + + + + + + :/images/view-recheck.png:/images/view-recheck.png + + + Reanal&yze all files + + + + + + :/images/process-stop.png:/images/process-stop.png + + + &Stop + + + Stop analysis + + + Stop analysis + + + Esc + + + + + + :/images/media-floppy.png:/images/media-floppy.png + + + &Save results to file... + + + Ctrl+S + + + + + &Quit + + + Ctrl+Q + + + + + + :/images/edit-clear.png:/images/edit-clear.png + + + &Clear results + + + + + + :/images/preferences-system.png:/images/preferences-system.png + + + &Preferences + + + + + true + + + + :/images/applications-development.png:/images/applications-development.png + + + Style war&nings + + + Show style warnings + + + Show style warnings + + + + + true + + + + :/images/showerrors.png:/images/showerrors.png + + + E&rrors + + + Show errors + + + Show errors + + + + + &Check all + + + + + &Uncheck all + + + + + Collapse &all + + + + + &Expand all + + + + + true + + + &Standard + + + Standard items + + + + + &Contents + + + Open the help contents + + + F1 + + + + + Toolbar + + + + + true + + + &Categories + + + Error categories + + + + + &Open XML... + + + + + + :/images/openproject.png:/images/openproject.png + + + Open P&roject File... + + + Ctrl+Shift+O + + + + + + :/images/scratchpad.png:/images/scratchpad.png + + + Sh&ow Scratchpad... + + + + + &New Project File... + + + Ctrl+Shift+N + + + + + &Log View + + + Log View + + + + + false + + + C&lose Project File + + + + + false + + + &Edit Project File... + + + + + false + + + + :/images/text-x-generic.png:/images/text-x-generic.png + + + &Statistics + + + + + true + + + + :/images/showwarnings.png:/images/showwarnings.png + + + &Warnings + + + Show warnings + + + Show warnings + + + + + true + + + + :/images/showperformance.png:/images/showperformance.png + + + Per&formance warnings + + + Show performance warnings + + + Show performance warnings + + + + + false + + + Show &hidden + + + + + true + + + + :/images/dialog-information.png:/images/dialog-information.png + + + &Information + + + Show information messages + + + + + true + + + + :/images/applications-system.png:/images/applications-system.png + + + &Portability + + + Show portability warnings + + + + + true + + + + :/cppcheck-gui.png:/cppcheck-gui.png + + + Cppcheck + + + Show Cppcheck results + + + + + true + + + + :/images/llvm-dragon.png:/images/llvm-dragon.png + + + Clang + + + Show Clang results + + + + + true + + + &Filter + + + Filter results + + + + + Project &MRU placeholder + + + true + + + + + true + + + Windows 32-bit ANSI + + + + + true + + + Windows 32-bit Unicode + + + + + true + + + Unix 32-bit + + + + + true + + + Unix 64-bit + + + + + true + + + Windows 64-bit + + + + + false + + + P&latforms + + + false + + + + + true + + + false + + + C++&11 + + + + + true + + + true + + + C&99 + + + + + true + + + &Posix + + + + + true + + + C&11 + + + + + true + + + &C89 + + + + + true + + + &C++03 + + + + + &Print... + + + Print the Current Report + + + + + Print Pre&view... + + + Open a Print Preview Dialog for the Current Results + + + + + &Library Editor... + + + Open library editor + + + + + true + + + &Auto-detect language + + + + + true + + + &Enforce C++ + + + + + true + + + E&nforce C + + + + + true + + + false + + + C++14 + + + + + false + + + Reanalyze and check library + + + + + false + + + Check configuration (defines, includes) + + + + + true + + + C++17 + + + + + true + + + true + + + C++20 + + + + + + Compliance report... + + + + + + ResultsView + QWidget +
resultsview.h
+ 1 +
+
+ + + + +
diff --git a/cppcheck-2.14.0/gui/newsuppressiondialog.cpp b/cppcheck-2.14.0/gui/newsuppressiondialog.cpp new file mode 100644 index 00000000..af12621d --- /dev/null +++ b/cppcheck-2.14.0/gui/newsuppressiondialog.cpp @@ -0,0 +1,85 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "newsuppressiondialog.h" + +#include "cppcheck.h" +#include "errorlogger.h" +#include "suppressions.h" + +#include "ui_newsuppressiondialog.h" + +#include + +#include +#include +#include + +class QWidget; +enum class Color; + +NewSuppressionDialog::NewSuppressionDialog(QWidget *parent) : + QDialog(parent), + mUI(new Ui::NewSuppressionDialog) +{ + mUI->setupUi(this); + + class QErrorLogger : public ErrorLogger { + public: + void reportOut(const std::string & /*outmsg*/, Color /*c*/) override {} + void reportErr(const ErrorMessage &msg) override { + errorIds << QString::fromStdString(msg.id); + } + QStringList errorIds; + }; + + QErrorLogger errorLogger; + CppCheck::getErrorMessages(errorLogger); + errorLogger.errorIds.sort(); + + mUI->mComboErrorId->addItems(errorLogger.errorIds); + mUI->mComboErrorId->setCurrentIndex(-1); + mUI->mComboErrorId->setCurrentText(""); +} + +NewSuppressionDialog::~NewSuppressionDialog() +{ + delete mUI; +} + +SuppressionList::Suppression NewSuppressionDialog::getSuppression() const +{ + SuppressionList::Suppression ret; + ret.errorId = mUI->mComboErrorId->currentText().toStdString(); + if (ret.errorId.empty()) + ret.errorId = "*"; + ret.fileName = mUI->mTextFileName->text().toStdString(); + if (!mUI->mTextLineNumber->text().isEmpty()) + ret.lineNumber = mUI->mTextLineNumber->text().toInt(); + ret.symbolName = mUI->mTextSymbolName->text().toStdString(); + return ret; +} + +void NewSuppressionDialog::setSuppression(const SuppressionList::Suppression &suppression) +{ + setWindowTitle(tr("Edit suppression")); + mUI->mComboErrorId->setCurrentText(QString::fromStdString(suppression.errorId)); + mUI->mTextFileName->setText(QString::fromStdString(suppression.fileName)); + mUI->mTextLineNumber->setText(suppression.lineNumber > 0 ? QString::number(suppression.lineNumber) : QString()); + mUI->mTextSymbolName->setText(QString::fromStdString(suppression.symbolName)); +} diff --git a/cppcheck-2.14.0/gui/newsuppressiondialog.h b/cppcheck-2.14.0/gui/newsuppressiondialog.h new file mode 100644 index 00000000..cba4de74 --- /dev/null +++ b/cppcheck-2.14.0/gui/newsuppressiondialog.h @@ -0,0 +1,59 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef NEWSUPPRESSIONDIALOG_H +#define NEWSUPPRESSIONDIALOG_H + +#include "suppressions.h" + +#include +#include +#include + +class QWidget; +namespace Ui { + class NewSuppressionDialog; +} + +class NewSuppressionDialog : public QDialog { + Q_OBJECT + +public: + explicit NewSuppressionDialog(QWidget *parent = nullptr); + NewSuppressionDialog(const NewSuppressionDialog &) = delete; + ~NewSuppressionDialog() override; + NewSuppressionDialog &operator=(const NewSuppressionDialog &) = delete; + + /** + * @brief Translate the user input in the GUI into a suppression + * @return Cppcheck suppression + */ + SuppressionList::Suppression getSuppression() const; + + /** + * @brief Update the GUI so it corresponds with the given + * Cppcheck suppression + * @param suppression Cppcheck suppression + */ + void setSuppression(const SuppressionList::Suppression &suppression); + +private: + Ui::NewSuppressionDialog *mUI; +}; + +#endif // NEWSUPPRESSIONDIALOG_H diff --git a/cppcheck-2.14.0/gui/newsuppressiondialog.ui b/cppcheck-2.14.0/gui/newsuppressiondialog.ui new file mode 100644 index 00000000..ae9a7699 --- /dev/null +++ b/cppcheck-2.14.0/gui/newsuppressiondialog.ui @@ -0,0 +1,121 @@ + + + NewSuppressionDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 334 + 184 + + + + New suppression + + + + + + + + Error ID + + + + + + + File name + + + + + + + + + + Line number + + + + + + + + + + Symbol name + + + + + + + + + + true + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + mComboErrorId + mTextFileName + mTextLineNumber + mTextSymbolName + + + + + buttonBox + accepted() + NewSuppressionDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + NewSuppressionDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/cppcheck-2.14.0/gui/platforms.cpp b/cppcheck-2.14.0/gui/platforms.cpp new file mode 100644 index 00000000..ed7ad880 --- /dev/null +++ b/cppcheck-2.14.0/gui/platforms.cpp @@ -0,0 +1,61 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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, Platform::Type platform) +{ + PlatformData plat; + plat.mTitle = title; + plat.mType = platform; + plat.mActMainWindow = nullptr; + mPlatforms << plat; +} + +void Platforms::init() +{ + add(tr("Native"), Platform::Type::Native); + add(tr("Unix 32-bit"), Platform::Type::Unix32); + add(tr("Unix 64-bit"), Platform::Type::Unix64); + add(tr("Windows 32-bit ANSI"), Platform::Type::Win32A); + add(tr("Windows 32-bit Unicode"), Platform::Type::Win32W); + add(tr("Windows 64-bit"), Platform::Type::Win64); +} + +int Platforms::getCount() const +{ + return mPlatforms.count(); +} + +PlatformData& Platforms::get(Platform::Type platform) +{ + QList::iterator iter = mPlatforms.begin(); + while (iter != mPlatforms.end()) { + if (iter->mType == platform) { + return *iter; + } + ++iter; + } + return mPlatforms.first(); +} diff --git a/cppcheck-2.14.0/gui/platforms.h b/cppcheck-2.14.0/gui/platforms.h new file mode 100644 index 00000000..dc941efb --- /dev/null +++ b/cppcheck-2.14.0/gui/platforms.h @@ -0,0 +1,59 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "platform.h" + +#include +#include +#include + +class QAction; + +/// @addtogroup GUI +/// @{ + +/** + * @brief Checked platform GUI-data. + */ +struct PlatformData { + QString mTitle; /**< Text visible in the GUI. */ + Platform::Type mType; /**< Type in the core. */ + QAction *mActMainWindow; /**< Pointer to main window action item. */ +}; + +/** + * @brief List of checked platforms. + */ +class Platforms : public QObject { + Q_OBJECT + +public: + explicit Platforms(QObject *parent = nullptr); + void add(const QString &title, Platform::Type platform); + int getCount() const; + void init(); + PlatformData& get(Platform::Type platform); + + QList mPlatforms; +}; + +/// @} +#endif // PLATFORMS_H diff --git a/cppcheck-2.14.0/gui/precompiled.h b/cppcheck-2.14.0/gui/precompiled.h new file mode 100644 index 00000000..7e10f3e1 --- /dev/null +++ b/cppcheck-2.14.0/gui/precompiled.h @@ -0,0 +1,33 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +// IWYU pragma: begin_keep +#include "checkthread.h" +#include "codeeditor.h" +#include "codeeditorstyle.h" +#include "config.h" +#include "cppcheck.h" +#include "cppchecklibrarydata.h" +#include "report.h" +#include "showtypes.h" + +#include +#include +// IWYU pragma: end_keep diff --git a/cppcheck-2.14.0/gui/precompiled_qmake.h b/cppcheck-2.14.0/gui/precompiled_qmake.h new file mode 100644 index 00000000..9ef90ecf --- /dev/null +++ b/cppcheck-2.14.0/gui/precompiled_qmake.h @@ -0,0 +1,23 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include "precompiled.h" +#include +#include +#include diff --git a/cppcheck-2.14.0/gui/printablereport.cpp b/cppcheck-2.14.0/gui/printablereport.cpp new file mode 100644 index 00000000..f6b9808b --- /dev/null +++ b/cppcheck-2.14.0/gui/printablereport.cpp @@ -0,0 +1,59 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "printablereport.h" + +#include "erroritem.h" + +#include +#include + +PrintableReport::PrintableReport() : + Report(QString()) +{} + +bool PrintableReport::create() +{ + return true; +} + +void PrintableReport::writeHeader() +{ + // No header for printable report +} + +void PrintableReport::writeFooter() +{ + // No footer for printable report +} + +void PrintableReport::writeError(const ErrorItem &error) +{ + const QString file = QDir::toNativeSeparators(error.errorPath.back().file); + QString line = QString("%1,%2,").arg(file).arg(error.errorPath.back().line); + line += QString("%1,%2").arg(GuiSeverity::toString(error.severity)).arg(error.summary); + + mFormattedReport += line; + mFormattedReport += "\n"; +} + +const QString& PrintableReport::getFormattedReportText() const +{ + return mFormattedReport; +} + diff --git a/cppcheck-2.14.0/gui/printablereport.h b/cppcheck-2.14.0/gui/printablereport.h new file mode 100644 index 00000000..8df59ba9 --- /dev/null +++ b/cppcheck-2.14.0/gui/printablereport.h @@ -0,0 +1,75 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PRINTABLE_REPORT_H +#define PRINTABLE_REPORT_H + +#include "report.h" + +#include + +class ErrorItem; + +/// @addtogroup GUI +/// @{ + + +/** + * @brief Printable (in-memory) report. + * This report formats results and exposes them for printing. + */ +class PrintableReport : public Report { +public: + PrintableReport(); + + /** + * @brief Create the report (file). + * @return true if succeeded, false if file could not be created. + */ + bool create() override; + + /** + * @brief Write report header. + */ + void writeHeader() override; + + /** + * @brief Write report footer. + */ + void writeFooter() override; + + /** + * @brief Write error to report. + * @param error Error data. + */ + void writeError(const ErrorItem &error) override; + + /** + * @brief Returns the formatted report. + */ + const QString& getFormattedReportText() const; + +private: + + /** + * @brief Stores the formatted report contents. + */ + QString mFormattedReport; +}; +/// @} +#endif // PRINTABLE_REPORT_H diff --git a/cppcheck-2.14.0/gui/projectfile.cpp b/cppcheck-2.14.0/gui/projectfile.cpp new file mode 100644 index 00000000..b1540436 --- /dev/null +++ b/cppcheck-2.14.0/gui/projectfile.cpp @@ -0,0 +1,1154 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "projectfile.h" + +#include "common.h" +#include "config.h" +#include "importproject.h" +#include "settings.h" +#include "utils.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#include +#endif + +ProjectFile *ProjectFile::mActiveProject; + +ProjectFile::ProjectFile(QObject *parent) : + QObject(parent) +{ + clear(); +} + +ProjectFile::ProjectFile(QString filename, QObject *parent) : + QObject(parent), + mFilename(std::move(filename)) +{ + clear(); + read(); +} + +void ProjectFile::clear() +{ + const Settings settings; + clangParser = false; + mCheckLevel = CheckLevel::normal; + mRootPath.clear(); + mBuildDir.clear(); + mImportProject.clear(); + mIncludeDirs.clear(); + mDefines.clear(); + mUndefines.clear(); + mPaths.clear(); + mExcludedPaths.clear(); + mLibraries.clear(); + mPlatform.clear(); + mProjectName.clear(); + mSuppressions.clear(); + mAddons.clear(); + mClangAnalyzer = mClangTidy = false; + mAnalyzeAllVsConfigs = false; + mCheckHeaders = true; + mCheckUnusedTemplates = true; + mMaxCtuDepth = settings.maxCtuDepth; + mMaxTemplateRecursion = settings.maxTemplateRecursion; + mCheckUnknownFunctionReturn.clear(); + safeChecks.clear(); + mVsConfigurations.clear(); + mTags.clear(); + mWarningTags.clear(); + + // Premium + mBughunting = false; + mCertIntPrecision = 0; + mCodingStandards.clear(); +} + +bool ProjectFile::read(const QString &filename) +{ + if (!filename.isEmpty()) + mFilename = filename; + + QFile file(mFilename); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + return false; + + clear(); + + QXmlStreamReader xmlReader(&file); + bool insideProject = false; + bool projectTagFound = false; + while (!xmlReader.atEnd()) { + switch (xmlReader.readNext()) { + case QXmlStreamReader::StartElement: + if (xmlReader.name() == QString(CppcheckXml::ProjectElementName)) { + insideProject = true; + projectTagFound = true; + break; + } + if (!insideProject) + break; + + // Read root path from inside project element + if (xmlReader.name() == QString(CppcheckXml::RootPathName)) + readRootPath(xmlReader); + + // Read root path from inside project element + if (xmlReader.name() == QString(CppcheckXml::BuildDirElementName)) + readBuildDir(xmlReader); + + // Find paths to check from inside project element + if (xmlReader.name() == QString(CppcheckXml::PathsElementName)) + readCheckPaths(xmlReader); + + if (xmlReader.name() == QString(CppcheckXml::ImportProjectElementName)) + readImportProject(xmlReader); + + if (xmlReader.name() == QString(CppcheckXml::AnalyzeAllVsConfigsElementName)) + mAnalyzeAllVsConfigs = readBool(xmlReader); + + if (xmlReader.name() == QString(CppcheckXml::Parser)) + clangParser = true; + + if (xmlReader.name() == QString(CppcheckXml::CheckHeadersElementName)) + mCheckHeaders = readBool(xmlReader); + + if (xmlReader.name() == QString(CppcheckXml::CheckUnusedTemplatesElementName)) + mCheckUnusedTemplates = readBool(xmlReader); + + if (xmlReader.name() == QString(CppcheckXml::CheckLevelExhaustiveElementName)) + mCheckLevel = CheckLevel::exhaustive; + + // Find include directory from inside project element + if (xmlReader.name() == QString(CppcheckXml::IncludeDirElementName)) + readIncludeDirs(xmlReader); + + // Find preprocessor define from inside project element + if (xmlReader.name() == QString(CppcheckXml::DefinesElementName)) + readDefines(xmlReader); + + // Find preprocessor define from inside project element + if (xmlReader.name() == QString(CppcheckXml::UndefinesElementName)) + readStringList(mUndefines, xmlReader, CppcheckXml::UndefineName); + + // Find exclude list from inside project element + if (xmlReader.name() == QString(CppcheckXml::ExcludeElementName)) + readExcludes(xmlReader); + + // Find ignore list from inside project element + // These are read for compatibility + if (xmlReader.name() == QString(CppcheckXml::IgnoreElementName)) + readExcludes(xmlReader); + + // Find libraries list from inside project element + if (xmlReader.name() == QString(CppcheckXml::LibrariesElementName)) + readStringList(mLibraries, xmlReader, CppcheckXml::LibraryElementName); + + if (xmlReader.name() == QString(CppcheckXml::PlatformElementName)) + readPlatform(xmlReader); + + // Find suppressions list from inside project element + if (xmlReader.name() == QString(CppcheckXml::SuppressionsElementName)) + readSuppressions(xmlReader); + + // Unknown function return values + if (xmlReader.name() == QString(CppcheckXml::CheckUnknownFunctionReturn)) + readStringList(mCheckUnknownFunctionReturn, xmlReader, CppcheckXml::Name); + + // check all function parameter values + if (xmlReader.name() == QString(Settings::SafeChecks::XmlRootName)) + safeChecks.loadFromXml(xmlReader); + + // Addons + if (xmlReader.name() == QString(CppcheckXml::AddonsElementName)) + readStringList(mAddons, xmlReader, CppcheckXml::AddonElementName); + + // Tools + if (xmlReader.name() == QString(CppcheckXml::ToolsElementName)) { + QStringList tools; + readStringList(tools, xmlReader, CppcheckXml::ToolElementName); + mClangAnalyzer = tools.contains(CLANG_ANALYZER); + mClangTidy = tools.contains(CLANG_TIDY); + } + + if (xmlReader.name() == QString(CppcheckXml::TagsElementName)) + readStringList(mTags, xmlReader, CppcheckXml::TagElementName); + + if (xmlReader.name() == QString(CppcheckXml::TagWarningsElementName)) + readTagWarnings(xmlReader, xmlReader.attributes().value(QString(), CppcheckXml::TagAttributeName).toString()); + + if (xmlReader.name() == QString(CppcheckXml::MaxCtuDepthElementName)) + mMaxCtuDepth = readInt(xmlReader, mMaxCtuDepth); + + if (xmlReader.name() == QString(CppcheckXml::MaxTemplateRecursionElementName)) + mMaxTemplateRecursion = readInt(xmlReader, mMaxTemplateRecursion); + + // VSConfiguration + if (xmlReader.name() == QString(CppcheckXml::VSConfigurationElementName)) + readVsConfigurations(xmlReader); + + // Cppcheck Premium + if (xmlReader.name() == QString(CppcheckXml::BughuntingElementName)) + mBughunting = true; + if (xmlReader.name() == QString(CppcheckXml::CodingStandardsElementName)) + readStringList(mCodingStandards, xmlReader, CppcheckXml::CodingStandardElementName); + if (xmlReader.name() == QString(CppcheckXml::CertIntPrecisionElementName)) + mCertIntPrecision = readInt(xmlReader, 0); + if (xmlReader.name() == QString(CppcheckXml::ProjectNameElementName)) + mProjectName = readString(xmlReader); + + break; + + case QXmlStreamReader::EndElement: + if (xmlReader.name() == QString(CppcheckXml::ProjectElementName)) + insideProject = false; + break; + + // Not handled + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Characters: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } + + file.close(); + return projectTagFound; +} + +void ProjectFile::readRootPath(const QXmlStreamReader &reader) +{ + QXmlStreamAttributes attribs = reader.attributes(); + QString name = attribs.value(QString(), CppcheckXml::RootPathNameAttrib).toString(); + if (!name.isEmpty()) + mRootPath = name; +} + +void ProjectFile::readBuildDir(QXmlStreamReader &reader) +{ + mBuildDir.clear(); + do { + const QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::Characters: + mBuildDir = reader.text().toString(); + FALLTHROUGH; + case QXmlStreamReader::EndElement: + return; + // Not handled + case QXmlStreamReader::StartElement: + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (true); +} + +void ProjectFile::readImportProject(QXmlStreamReader &reader) +{ + mImportProject.clear(); + do { + const QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::Characters: + mImportProject = reader.text().toString(); + FALLTHROUGH; + case QXmlStreamReader::EndElement: + return; + // Not handled + case QXmlStreamReader::StartElement: + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (true); +} + +bool ProjectFile::readBool(QXmlStreamReader &reader) +{ + bool ret = false; + do { + const QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::Characters: + ret = (reader.text().toString() == "true"); + FALLTHROUGH; + case QXmlStreamReader::EndElement: + return ret; + // Not handled + case QXmlStreamReader::StartElement: + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (true); +} + +int ProjectFile::readInt(QXmlStreamReader &reader, int defaultValue) +{ + int ret = defaultValue; + do { + const QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::Characters: + ret = reader.text().toString().toInt(); + FALLTHROUGH; + case QXmlStreamReader::EndElement: + return ret; + // Not handled + case QXmlStreamReader::StartElement: + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (true); +} + +QString ProjectFile::readString(QXmlStreamReader &reader) +{ + QString ret; + do { + const QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::Characters: + ret = reader.text().toString(); + FALLTHROUGH; + case QXmlStreamReader::EndElement: + return ret; + // Not handled + case QXmlStreamReader::StartElement: + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (true); +} + +void ProjectFile::readIncludeDirs(QXmlStreamReader &reader) +{ + bool allRead = false; + do { + QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::StartElement: + + // Read dir-elements + if (reader.name().toString() == CppcheckXml::DirElementName) { + QXmlStreamAttributes attribs = reader.attributes(); + QString name = attribs.value(QString(), CppcheckXml::DirNameAttrib).toString(); + if (!name.isEmpty()) + mIncludeDirs << name; + } + break; + + case QXmlStreamReader::EndElement: + if (reader.name().toString() == CppcheckXml::IncludeDirElementName) + allRead = true; + break; + + // Not handled + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Characters: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (!allRead); +} + +void ProjectFile::readDefines(QXmlStreamReader &reader) +{ + bool allRead = false; + do { + QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::StartElement: + // Read define-elements + if (reader.name().toString() == CppcheckXml::DefineName) { + QXmlStreamAttributes attribs = reader.attributes(); + QString name = attribs.value(QString(), CppcheckXml::DefineNameAttrib).toString(); + if (!name.isEmpty()) + mDefines << name; + } + break; + + case QXmlStreamReader::EndElement: + if (reader.name().toString() == CppcheckXml::DefinesElementName) + allRead = true; + break; + + // Not handled + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Characters: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (!allRead); +} + +void ProjectFile::readCheckPaths(QXmlStreamReader &reader) +{ + bool allRead = false; + do { + QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::StartElement: + + // Read dir-elements + if (reader.name().toString() == CppcheckXml::PathName) { + QXmlStreamAttributes attribs = reader.attributes(); + QString name = attribs.value(QString(), CppcheckXml::PathNameAttrib).toString(); + if (!name.isEmpty()) + mPaths << name; + } + break; + + case QXmlStreamReader::EndElement: + if (reader.name().toString() == CppcheckXml::PathsElementName) + allRead = true; + break; + + // Not handled + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Characters: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (!allRead); +} + +void ProjectFile::readExcludes(QXmlStreamReader &reader) +{ + bool allRead = false; + do { + QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::StartElement: + // Read exclude-elements + if (reader.name().toString() == CppcheckXml::ExcludePathName) { + QXmlStreamAttributes attribs = reader.attributes(); + QString name = attribs.value(QString(), CppcheckXml::ExcludePathNameAttrib).toString(); + if (!name.isEmpty()) + mExcludedPaths << name; + } + // Read ignore-elements - deprecated but support reading them + else if (reader.name().toString() == CppcheckXml::IgnorePathName) { + QXmlStreamAttributes attribs = reader.attributes(); + QString name = attribs.value(QString(), CppcheckXml::IgnorePathNameAttrib).toString(); + if (!name.isEmpty()) + mExcludedPaths << name; + } + break; + + case QXmlStreamReader::EndElement: + if (reader.name().toString() == CppcheckXml::IgnoreElementName) + allRead = true; + if (reader.name().toString() == CppcheckXml::ExcludeElementName) + allRead = true; + break; + + // Not handled + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Characters: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (!allRead); +} + +void ProjectFile::readVsConfigurations(QXmlStreamReader &reader) +{ + do { + QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::StartElement: + // Read library-elements + if (reader.name().toString() == CppcheckXml::VSConfigurationName) { + QString config; + type = reader.readNext(); + if (type == QXmlStreamReader::Characters) { + config = reader.text().toString(); + } + mVsConfigurations << config; + } + break; + + case QXmlStreamReader::EndElement: + if (reader.name().toString() != CppcheckXml::VSConfigurationName) + return; + break; + + // Not handled + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Characters: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (true); +} + +void ProjectFile::readPlatform(QXmlStreamReader &reader) +{ + do { + const QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::Characters: + mPlatform = reader.text().toString(); + FALLTHROUGH; + case QXmlStreamReader::EndElement: + return; + // Not handled + case QXmlStreamReader::StartElement: + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (true); +} + + +void ProjectFile::readSuppressions(QXmlStreamReader &reader) +{ + do { + QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::StartElement: + // Read library-elements + if (reader.name().toString() == CppcheckXml::SuppressionElementName) { + SuppressionList::Suppression suppression; + if (reader.attributes().hasAttribute(QString(),"fileName")) + suppression.fileName = reader.attributes().value(QString(),"fileName").toString().toStdString(); + if (reader.attributes().hasAttribute(QString(),"lineNumber")) + suppression.lineNumber = reader.attributes().value(QString(),"lineNumber").toInt(); + if (reader.attributes().hasAttribute(QString(),"symbolName")) + suppression.symbolName = reader.attributes().value(QString(),"symbolName").toString().toStdString(); + if (reader.attributes().hasAttribute(QString(),"hash")) + suppression.hash = reader.attributes().value(QString(),"hash").toULongLong(); + type = reader.readNext(); + if (type == QXmlStreamReader::Characters) { + suppression.errorId = reader.text().toString().toStdString(); + } + mSuppressions << suppression; + } + break; + + case QXmlStreamReader::EndElement: + if (reader.name().toString() != CppcheckXml::SuppressionElementName) + return; + break; + + // Not handled + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Characters: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (true); +} + + +void ProjectFile::readTagWarnings(QXmlStreamReader &reader, const QString &tag) +{ + do { + QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::StartElement: + // Read library-elements + if (reader.name().toString() == CppcheckXml::WarningElementName) { + const std::size_t hash = reader.attributes().value(QString(), CppcheckXml::HashAttributeName).toULongLong(); + mWarningTags[hash] = tag; + } + break; + + case QXmlStreamReader::EndElement: + if (reader.name().toString() != CppcheckXml::WarningElementName) + return; + break; + + // Not handled + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Characters: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (true); +} + + +void ProjectFile::readStringList(QStringList &stringlist, QXmlStreamReader &reader, const char elementname[]) +{ + bool allRead = false; + do { + QXmlStreamReader::TokenType type = reader.readNext(); + switch (type) { + case QXmlStreamReader::StartElement: + // Read library-elements + if (reader.name().toString() == elementname) { + type = reader.readNext(); + if (type == QXmlStreamReader::Characters) { + QString text = reader.text().toString(); + stringlist << text; + } + } + break; + + case QXmlStreamReader::EndElement: + if (reader.name().toString() != elementname) + allRead = true; + break; + + // Not handled + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Characters: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (!allRead); +} + +void ProjectFile::setIncludes(const QStringList &includes) +{ + mIncludeDirs = includes; +} + +void ProjectFile::setDefines(const QStringList &defines) +{ + mDefines = defines; +} + +void ProjectFile::setUndefines(const QStringList &undefines) +{ + mUndefines = undefines; +} + +void ProjectFile::setCheckPaths(const QStringList &paths) +{ + mPaths = paths; +} + +void ProjectFile::setExcludedPaths(const QStringList &paths) +{ + mExcludedPaths = paths; +} + +void ProjectFile::setLibraries(const QStringList &libraries) +{ + mLibraries = libraries; +} + +void ProjectFile::setPlatform(const QString &platform) +{ + mPlatform = platform; +} + +QList ProjectFile::getCheckingSuppressions() const +{ + const QRegularExpression re1("^[a-zA-Z0-9_\\-]+/.*"); + const QRegularExpression re2("^[^/]+$"); + QList result; + for (SuppressionList::Suppression suppression : mSuppressions) { + if (re1.match(suppression.fileName.c_str()).hasMatch() || re2.match(suppression.fileName.c_str()).hasMatch()) { + if (suppression.fileName[0] != '*') + suppression.fileName = QFileInfo(mFilename).absolutePath().toStdString() + "/" + suppression.fileName; + } + result << suppression; + } + return result; +} + +void ProjectFile::setSuppressions(const QList &suppressions) +{ + mSuppressions = suppressions; +} + +void ProjectFile::addSuppression(const SuppressionList::Suppression &suppression) +{ + mSuppressions.append(suppression); +} + +void ProjectFile::setAddons(const QStringList &addons) +{ + mAddons = addons; +} + +void ProjectFile::setVSConfigurations(const QStringList &vsConfigs) +{ + mVsConfigurations = vsConfigs; +} + +void ProjectFile::setCheckLevel(ProjectFile::CheckLevel checkLevel) +{ + mCheckLevel = checkLevel; +} + +bool ProjectFile::isCheckLevelExhaustive() const +{ + return mCheckLevel == CheckLevel::exhaustive; +} + +void ProjectFile::setWarningTags(std::size_t hash, const QString& tags) +{ + if (tags.isEmpty()) + mWarningTags.erase(hash); + else if (hash > 0) + mWarningTags[hash] = tags; +} + +QString ProjectFile::getWarningTags(std::size_t hash) const +{ + auto it = mWarningTags.find(hash); + return (it != mWarningTags.end()) ? it->second : QString(); +} + +bool ProjectFile::write(const QString &filename) +{ + if (!filename.isEmpty()) + mFilename = filename; + + QFile file(mFilename); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + return false; + + QXmlStreamWriter xmlWriter(&file); + xmlWriter.setAutoFormatting(true); + xmlWriter.writeStartDocument("1.0"); + xmlWriter.writeStartElement(CppcheckXml::ProjectElementName); + xmlWriter.writeAttribute(CppcheckXml::ProjectVersionAttrib, CppcheckXml::ProjectFileVersion); + + if (!mRootPath.isEmpty()) { + xmlWriter.writeStartElement(CppcheckXml::RootPathName); + xmlWriter.writeAttribute(CppcheckXml::RootPathNameAttrib, mRootPath); + xmlWriter.writeEndElement(); + } + + if (!mBuildDir.isEmpty()) { + xmlWriter.writeStartElement(CppcheckXml::BuildDirElementName); + xmlWriter.writeCharacters(mBuildDir); + xmlWriter.writeEndElement(); + } + + if (!mPlatform.isEmpty()) { + xmlWriter.writeStartElement(CppcheckXml::PlatformElementName); + xmlWriter.writeCharacters(mPlatform); + xmlWriter.writeEndElement(); + } + + if (!mImportProject.isEmpty()) { + xmlWriter.writeStartElement(CppcheckXml::ImportProjectElementName); + xmlWriter.writeCharacters(mImportProject); + xmlWriter.writeEndElement(); + } + + xmlWriter.writeStartElement(CppcheckXml::AnalyzeAllVsConfigsElementName); + xmlWriter.writeCharacters(bool_to_string(mAnalyzeAllVsConfigs)); + xmlWriter.writeEndElement(); + + if (clangParser) { + xmlWriter.writeStartElement(CppcheckXml::Parser); + xmlWriter.writeCharacters("clang"); + xmlWriter.writeEndElement(); + } + + xmlWriter.writeStartElement(CppcheckXml::CheckHeadersElementName); + xmlWriter.writeCharacters(bool_to_string(mCheckHeaders)); + xmlWriter.writeEndElement(); + + xmlWriter.writeStartElement(CppcheckXml::CheckUnusedTemplatesElementName); + xmlWriter.writeCharacters(bool_to_string(mCheckUnusedTemplates)); + xmlWriter.writeEndElement(); + + xmlWriter.writeStartElement(CppcheckXml::MaxCtuDepthElementName); + xmlWriter.writeCharacters(QString::number(mMaxCtuDepth)); + xmlWriter.writeEndElement(); + + xmlWriter.writeStartElement(CppcheckXml::MaxTemplateRecursionElementName); + xmlWriter.writeCharacters(QString::number(mMaxTemplateRecursion)); + xmlWriter.writeEndElement(); + + if (!mIncludeDirs.isEmpty()) { + xmlWriter.writeStartElement(CppcheckXml::IncludeDirElementName); + for (const QString& incdir : mIncludeDirs) { + xmlWriter.writeStartElement(CppcheckXml::DirElementName); + xmlWriter.writeAttribute(CppcheckXml::DirNameAttrib, incdir); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); + } + + if (!mDefines.isEmpty()) { + xmlWriter.writeStartElement(CppcheckXml::DefinesElementName); + for (const QString& define : mDefines) { + xmlWriter.writeStartElement(CppcheckXml::DefineName); + xmlWriter.writeAttribute(CppcheckXml::DefineNameAttrib, define); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); + } + + if (!mVsConfigurations.isEmpty()) { + writeStringList(xmlWriter, + mVsConfigurations, + CppcheckXml::VSConfigurationElementName, + CppcheckXml::VSConfigurationName); + } + + writeStringList(xmlWriter, + mUndefines, + CppcheckXml::UndefinesElementName, + CppcheckXml::UndefineName); + + if (!mPaths.isEmpty()) { + xmlWriter.writeStartElement(CppcheckXml::PathsElementName); + for (const QString& path : mPaths) { + xmlWriter.writeStartElement(CppcheckXml::PathName); + xmlWriter.writeAttribute(CppcheckXml::PathNameAttrib, path); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); + } + + if (!mExcludedPaths.isEmpty()) { + xmlWriter.writeStartElement(CppcheckXml::ExcludeElementName); + for (const QString& path : mExcludedPaths) { + xmlWriter.writeStartElement(CppcheckXml::ExcludePathName); + xmlWriter.writeAttribute(CppcheckXml::ExcludePathNameAttrib, path); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); + } + + writeStringList(xmlWriter, + mLibraries, + CppcheckXml::LibrariesElementName, + CppcheckXml::LibraryElementName); + + if (!mSuppressions.isEmpty()) { + xmlWriter.writeStartElement(CppcheckXml::SuppressionsElementName); + for (const SuppressionList::Suppression &suppression : mSuppressions) { + xmlWriter.writeStartElement(CppcheckXml::SuppressionElementName); + if (!suppression.fileName.empty()) + xmlWriter.writeAttribute("fileName", QString::fromStdString(suppression.fileName)); + if (suppression.lineNumber > 0) + xmlWriter.writeAttribute("lineNumber", QString::number(suppression.lineNumber)); + if (!suppression.symbolName.empty()) + xmlWriter.writeAttribute("symbolName", QString::fromStdString(suppression.symbolName)); + if (suppression.hash > 0) + xmlWriter.writeAttribute(CppcheckXml::HashAttributeName, QString::number(suppression.hash)); + if (!suppression.errorId.empty()) + xmlWriter.writeCharacters(QString::fromStdString(suppression.errorId)); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); + } + + writeStringList(xmlWriter, + mCheckUnknownFunctionReturn, + CppcheckXml::CheckUnknownFunctionReturn, + CppcheckXml::Name); + + safeChecks.saveToXml(xmlWriter); + + writeStringList(xmlWriter, + mAddons, + CppcheckXml::AddonsElementName, + CppcheckXml::AddonElementName); + + QStringList tools; + if (mClangAnalyzer) + tools << CLANG_ANALYZER; + if (mClangTidy) + tools << CLANG_TIDY; + writeStringList(xmlWriter, + tools, + CppcheckXml::ToolsElementName, + CppcheckXml::ToolElementName); + + writeStringList(xmlWriter, mTags, CppcheckXml::TagsElementName, CppcheckXml::TagElementName); + if (!mWarningTags.empty()) { + QStringList tags; + for (const auto& wt: mWarningTags) { + if (!tags.contains(wt.second)) + tags.append(wt.second); + } + for (const QString &tag: tags) { + xmlWriter.writeStartElement(CppcheckXml::TagWarningsElementName); + xmlWriter.writeAttribute(CppcheckXml::TagAttributeName, tag); + for (const auto& wt: mWarningTags) { + if (wt.second == tag) { + xmlWriter.writeStartElement(CppcheckXml::WarningElementName); + xmlWriter.writeAttribute(CppcheckXml::HashAttributeName, QString::number(wt.first)); + xmlWriter.writeEndElement(); + } + } + xmlWriter.writeEndElement(); + } + } + + if (mCheckLevel == CheckLevel::exhaustive) { + xmlWriter.writeStartElement(CppcheckXml::CheckLevelExhaustiveElementName); + xmlWriter.writeEndElement(); + } + + // Cppcheck Premium + if (mBughunting) { + xmlWriter.writeStartElement(CppcheckXml::BughuntingElementName); + xmlWriter.writeEndElement(); + } + + writeStringList(xmlWriter, + mCodingStandards, + CppcheckXml::CodingStandardsElementName, + CppcheckXml::CodingStandardElementName); + + if (mCertIntPrecision > 0) { + xmlWriter.writeStartElement(CppcheckXml::CertIntPrecisionElementName); + xmlWriter.writeCharacters(QString::number(mCertIntPrecision)); + xmlWriter.writeEndElement(); + } + + if (!mProjectName.isEmpty()) { + xmlWriter.writeStartElement(CppcheckXml::ProjectNameElementName); + xmlWriter.writeCharacters(mProjectName); + xmlWriter.writeEndElement(); + } + + 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); + for (const QString& str : stringlist) { + xmlWriter.writeStartElement(stringelementname); + xmlWriter.writeCharacters(str); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); +} + +QStringList ProjectFile::fromNativeSeparators(const QStringList &paths) +{ + QStringList ret; + for (const QString &path : paths) + ret << QDir::fromNativeSeparators(path); + return ret; +} + +QStringList ProjectFile::getAddonsAndTools() const +{ + QStringList ret(mAddons); + if (mClangAnalyzer) + ret << CLANG_ANALYZER; + if (mClangTidy) + ret << CLANG_TIDY; + return ret; +} + +void ProjectFile::SafeChecks::loadFromXml(QXmlStreamReader &xmlReader) +{ + classes = externalFunctions = internalFunctions = externalVariables = false; + + int level = 0; + + do { + const QXmlStreamReader::TokenType type = xmlReader.readNext(); + switch (type) { + case QXmlStreamReader::StartElement: + ++level; + if (xmlReader.name() == QString(Settings::SafeChecks::XmlClasses)) + classes = true; + else if (xmlReader.name() == QString(Settings::SafeChecks::XmlExternalFunctions)) + externalFunctions = true; + else if (xmlReader.name() == QString(Settings::SafeChecks::XmlInternalFunctions)) + internalFunctions = true; + else if (xmlReader.name() == QString(Settings::SafeChecks::XmlExternalVariables)) + externalVariables = true; + break; + case QXmlStreamReader::EndElement: + if (level <= 0) + return; + level--; + break; + // Not handled + case QXmlStreamReader::Characters: + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } while (true); +} + +void ProjectFile::SafeChecks::saveToXml(QXmlStreamWriter &xmlWriter) const +{ + if (!classes && !externalFunctions && !internalFunctions && !externalVariables) + return; + xmlWriter.writeStartElement(QString(Settings::SafeChecks::XmlRootName)); + if (classes) { + xmlWriter.writeStartElement(QString(Settings::SafeChecks::XmlClasses)); + xmlWriter.writeEndElement(); + } + if (externalFunctions) { + xmlWriter.writeStartElement(QString(Settings::SafeChecks::XmlExternalFunctions)); + xmlWriter.writeEndElement(); + } + if (internalFunctions) { + xmlWriter.writeStartElement(QString(Settings::SafeChecks::XmlInternalFunctions)); + xmlWriter.writeEndElement(); + } + if (externalVariables) { + xmlWriter.writeStartElement(QString(Settings::SafeChecks::XmlExternalVariables)); + xmlWriter.writeEndElement(); + } + xmlWriter.writeEndElement(); +} + +QString ProjectFile::getAddonFilePath(QString filesDir, const QString &addon) +{ + if (QFile(addon).exists()) + return addon; + + if (!filesDir.endsWith("/")) + filesDir += "/"; + + QStringList searchPaths; + searchPaths << filesDir << (filesDir + "addons/") << (filesDir + "../addons/") +#ifdef FILESDIR + << (QLatin1String(FILESDIR) + "/addons/") +#endif + ; + + for (const QString& path : searchPaths) { + QString f = path + addon + ".py"; + if (QFile(f).exists()) + return f; + } + + return QString(); +} diff --git a/cppcheck-2.14.0/gui/projectfile.h b/cppcheck-2.14.0/gui/projectfile.h new file mode 100644 index 00000000..c86a68a0 --- /dev/null +++ b/cppcheck-2.14.0/gui/projectfile.h @@ -0,0 +1,648 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "settings.h" +#include "suppressions.h" + +#include +#include +#include + +#include +#include +#include +#include + +class QXmlStreamReader; +class QXmlStreamWriter; + +/// @addtogroup GUI +/// @{ + + +/** + * @brief A class that reads and writes project files. + * The project files contain project-specific settings for checking. For + * example a list of include paths. + */ +class ProjectFile : public QObject { + Q_OBJECT + +public: + explicit ProjectFile(QObject *parent = nullptr); + explicit ProjectFile(QString filename, QObject *parent = nullptr); + ~ProjectFile() override { + if (this == mActiveProject) mActiveProject = nullptr; + } + + enum class CheckLevel { + normal, + exhaustive + }; + + static ProjectFile* getActiveProject() { + return mActiveProject; + } + void setActiveProject() { + mActiveProject = this; + } + + /** + * @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. + */ + const QString& getRootPath() const { + return mRootPath; + } + + const QString& getBuildDir() const { + return mBuildDir; + } + + const QString& getImportProject() const { + return mImportProject; + } + + bool getAnalyzeAllVsConfigs() const { + return mAnalyzeAllVsConfigs; + } + + bool getCheckHeaders() const { + return mCheckHeaders; + } + + void setCheckHeaders(bool b) { + mCheckHeaders = b; + } + + bool getCheckUnusedTemplates() const { + return mCheckUnusedTemplates; + } + + void setCheckUnusedTemplates(bool b) { + mCheckUnusedTemplates = b; + } + + /** + * @brief Get list of include directories. + * @return list of directories. + */ + QStringList getIncludeDirs() const { + return ProjectFile::fromNativeSeparators(mIncludeDirs); + } + + /** + * @brief Get list of defines. + * @return list of defines. + */ + const QStringList& getDefines() const { + return mDefines; + } + + /** + * @brief Get list of undefines. + * @return list of undefines. + */ + const QStringList& getUndefines() const { + return mUndefines; + } + + /** + * @brief Get list of paths to check. + * @return list of paths. + */ + QStringList getCheckPaths() const { + return ProjectFile::fromNativeSeparators(mPaths); + } + + /** + * @brief Get list of paths to exclude from the check. + * @return list of paths. + */ + QStringList getExcludedPaths() const { + return ProjectFile::fromNativeSeparators(mExcludedPaths); + } + + /** + * @brief Get list of paths to exclude from the check. + * @return list of paths. + */ + const QStringList& getVsConfigurations() const { + return mVsConfigurations; + } + + /** + * @brief Get list libraries. + * @return list of libraries. + */ + const QStringList& getLibraries() const { + return mLibraries; + } + + /** + * @brief Get platform. + * @return Current platform. If it ends with .xml then it is a file. Otherwise it must match one of the return values from @sa cppcheck::Platform::toString() ("win32A", "unix32", ..) + */ + const QString& getPlatform() const { + return mPlatform; + } + + const QString& getProjectName() const { + return mProjectName; + } + + void setProjectName(QString projectName) { + mProjectName = std::move(projectName); + } + + /** + * @brief Get "raw" suppressions. + * @return list of suppressions. + */ + const QList& getSuppressions() const { + return mSuppressions; + } + + /** + * @brief Get "checking" suppressions. Relative paths are converted to absolute paths. + * @return list of suppressions. + */ + QList getCheckingSuppressions() const; + + /** + * @brief Get list addons. + * @return list of addons. + */ + const QStringList& getAddons() const { + return mAddons; + } + + /** + * @brief Get path to addon python script + * @param filesDir Data files folder set by --data-dir + * @param addon addon i.e. "misra" to lookup + */ + static QString getAddonFilePath(QString filesDir, const QString &addon); + + /** + * @brief Get list of addons and tools. + * @return list of addons and tools. + */ + QStringList getAddonsAndTools() const; + + bool getClangAnalyzer() const { + return false; //mClangAnalyzer; + } + + void setClangAnalyzer(bool c) { + mClangAnalyzer = c; + } + + bool getClangTidy() const { + return mClangTidy; + } + + void setClangTidy(bool c) { + mClangTidy = c; + } + + const QStringList& getTags() const { + return mTags; + } + + int getMaxCtuDepth() const { + return mMaxCtuDepth; + } + + void setMaxCtuDepth(int maxCtuDepth) { + mMaxCtuDepth = maxCtuDepth; + } + + int getMaxTemplateRecursion() const { + return mMaxTemplateRecursion; + } + + void setMaxTemplateRecursion(int maxTemplateRecursion) { + mMaxTemplateRecursion = maxTemplateRecursion; + } + + /** + * @brief Get filename for the project file. + * @return file name. + */ + const QString& getFilename() const { + return mFilename; + } + + /** + * @brief Set project root path. + * @param rootpath new project root path. + */ + void setRootPath(const QString &rootpath) { + mRootPath = rootpath; + } + + void setBuildDir(const QString &buildDir) { + mBuildDir = buildDir; + } + + void setImportProject(const QString &importProject) { + mImportProject = importProject; + } + + void setAnalyzeAllVsConfigs(bool b) { + mAnalyzeAllVsConfigs = b; + } + + /** + * @brief Set list of includes. + * @param includes List of defines. + */ + void setIncludes(const QStringList &includes); + + /** + * @brief Set list of defines. + * @param defines List of defines. + */ + void setDefines(const QStringList &defines); + + /** + * @brief Set list of undefines. + * @param undefines List of undefines. + */ + void setUndefines(const QStringList &undefines); + + /** + * @brief Set list of paths to check. + * @param paths List of paths. + */ + void setCheckPaths(const QStringList &paths); + + /** + * @brief Set list of paths to exclude from the check. + * @param paths List of paths. + */ + void setExcludedPaths(const QStringList &paths); + + /** + * @brief Set list of libraries. + * @param libraries List of libraries. + */ + void setLibraries(const QStringList &libraries); + + /** + * @brief Set platform. + * @param platform platform. + */ + void setPlatform(const QString &platform); + + /** + * @brief Set list of suppressions. + * @param suppressions List of suppressions. + */ + void setSuppressions(const QList &suppressions); + + /** Add suppression */ + void addSuppression(const SuppressionList::Suppression &suppression); + + /** + * @brief Set list of addons. + * @param addons List of addons. + */ + void setAddons(const QStringList &addons); + + /** @brief Set list of Visual Studio configurations to be checked + * @param vsConfigs List of configurations + */ + void setVSConfigurations(const QStringList &vsConfigs); + + /** CheckLevel: normal/exhaustive */ + void setCheckLevel(CheckLevel checkLevel); + bool isCheckLevelExhaustive() const; + + /** + * @brief Set tags. + * @param tags tag list + */ + void setTags(const QStringList &tags) { + mTags = tags; + } + + /** Set tags for a warning */ + void setWarningTags(std::size_t hash, const QString& tags); + + /** Get tags for a warning */ + QString getWarningTags(std::size_t hash) const; + + /** Bughunting (Cppcheck Premium) */ + void setBughunting(bool bughunting) { + mBughunting = bughunting; + } + bool getBughunting() const { + return mBughunting; + } + + /** @brief Get list of coding standards (checked by Cppcheck Premium). */ + const QStringList& getCodingStandards() const { + return mCodingStandards; + } + + /** + * @brief Set list of coding standards (checked by Cppcheck Premium). + * @param codingStandards List of coding standards. + */ + void setCodingStandards(QStringList codingStandards) { + mCodingStandards = std::move(codingStandards); + } + + /** Cert C: int precision */ + void setCertIntPrecision(int p) { + mCertIntPrecision = p; + } + int getCertIntPrecision() const { + return mCertIntPrecision; + } + + + /** + * @brief Write project file (to disk). + * @param filename Filename to use. + */ + bool write(const QString &filename = QString()); + + /** + * @brief Set filename for the project file. + * @param filename Filename to use. + */ + void setFilename(const QString &filename) { + mFilename = filename; + } + + /** Do not only check how interface is used. Also check that interface is safe. */ + class SafeChecks : public Settings::SafeChecks { + public: + SafeChecks() : Settings::SafeChecks() {} + + void loadFromXml(QXmlStreamReader &xmlReader); + void saveToXml(QXmlStreamWriter &xmlWriter) const; + }; + + SafeChecks safeChecks; + + /** Check unknown function return values */ + const QStringList& getCheckUnknownFunctionReturn() const { + return mCheckUnknownFunctionReturn; + } + /* + void setCheckUnknownFunctionReturn(const QStringList &s) { + mCheckUnknownFunctionReturn = s; + } + */ + + /** Use Clang parser */ + bool clangParser; + +protected: + + /** + * @brief Read optional root path from XML. + * @param reader XML stream reader. + */ + void readRootPath(const QXmlStreamReader &reader); + + void readBuildDir(QXmlStreamReader &reader); + + /** + * @brief Read importproject from XML. + * @param reader XML stream reader. + */ + void readImportProject(QXmlStreamReader &reader); + + static bool readBool(QXmlStreamReader &reader); + + static int readInt(QXmlStreamReader &reader, int defaultValue); + + static QString readString(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 lists of Visual Studio configurations + * @param reader XML stream reader. + */ + void readVsConfigurations(QXmlStreamReader &reader); + + /** + * @brief Read platform text. + * @param reader XML stream reader. + */ + void readPlatform(QXmlStreamReader &reader); + + /** + * @brief Read suppressions. + * @param reader XML stream reader. + */ + void readSuppressions(QXmlStreamReader &reader); + + /** + * @brief Read tag warnings, what warnings are tagged with a specific tag + * @param reader XML stream reader. + */ + void readTagWarnings(QXmlStreamReader &reader, const QString &tag); + + /** + * @brief Read string list + * @param stringlist destination string list + * @param reader XML stream reader + * @param elementname elementname for each string + */ + static void readStringList(QStringList &stringlist, QXmlStreamReader &reader, const char elementname[]); + + /** + * @brief Write string list + * @param xmlWriter xml writer + * @param stringlist string list to write + * @param startelementname name of start element + * @param stringelementname name of each string element + */ + static void writeStringList(QXmlStreamWriter &xmlWriter, const QStringList &stringlist, const char startelementname[], const char stringelementname[]); + +private: + + void clear(); + + /** + * @brief Convert paths + */ + static QStringList fromNativeSeparators(const QStringList &paths); + + /** + * @brief Filename (+path) of the project file. + */ + QString mFilename; + + /** + * @brief Root path (optional) for the project. + * This is the project root path. If it is present then all relative paths in + * the project file are relative to this path. Otherwise paths are relative + * to project file's path. + */ + QString mRootPath; + + /** Cppcheck build dir */ + QString mBuildDir; + + /** Visual studio project/solution , compile database */ + QString mImportProject; + + /** + * Should all visual studio configurations be analyzed? + * If this is false then only the Debug configuration + * for the set platform is analyzed. + */ + bool mAnalyzeAllVsConfigs; + + /** Check only a selected VS configuration */ + QStringList mVsConfigurations; + + /** Check code in headers */ + bool mCheckHeaders; + + /** Check code in unused templates */ + bool mCheckUnusedTemplates; + + /** + * @brief List of include directories used to search include files. + */ + QStringList mIncludeDirs; + + /** + * @brief List of defines. + */ + QStringList mDefines; + + /** + * @brief List of undefines. + */ + QStringList mUndefines; + + /** + * @brief List of paths to check. + */ + QStringList mPaths; + + /** + * @brief Paths excluded from the check. + */ + QStringList mExcludedPaths; + + /** + * @brief List of libraries. + */ + QStringList mLibraries; + + /** + * @brief Platform + */ + QString mPlatform; + + /** + * @brief List of suppressions. + */ + QList mSuppressions; + + /** + * @brief List of addons. + */ + QStringList mAddons; + + bool mBughunting = false; + + /** @brief Should Cppcheck run normal or exhaustive analysis? */ + CheckLevel mCheckLevel = CheckLevel::normal; + + /** + * @brief List of coding standards, checked by Cppcheck Premium. + */ + QStringList mCodingStandards; + + /** @brief Project name, used when generating compliance report */ + QString mProjectName; + + /** @brief Cppcheck Premium: This value is passed to the Cert C checker if that is enabled */ + int mCertIntPrecision; + + /** @brief Execute clang analyzer? */ + bool mClangAnalyzer; + + /** @brief Execute clang-tidy? */ + bool mClangTidy; + + /** + * @brief Tags + */ + QStringList mTags; + + /** + * @brief Warning tags + */ + std::map mWarningTags; + + /** Max CTU depth */ + int mMaxCtuDepth; + + /** Max template instantiation recursion */ + int mMaxTemplateRecursion; + + QStringList mCheckUnknownFunctionReturn; + + static ProjectFile *mActiveProject; +}; +/// @} +#endif // PROJECT_FILE_H diff --git a/cppcheck-2.14.0/gui/projectfile.txt b/cppcheck-2.14.0/gui/projectfile.txt new file mode 100644 index 00000000..e3f9518c --- /dev/null +++ b/cppcheck-2.14.0/gui/projectfile.txt @@ -0,0 +1,48 @@ +Project 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. diff --git a/cppcheck-2.14.0/gui/projectfile.ui b/cppcheck-2.14.0/gui/projectfile.ui new file mode 100644 index 00000000..9c1cd709 --- /dev/null +++ b/cppcheck-2.14.0/gui/projectfile.ui @@ -0,0 +1,1022 @@ + + + ProjectFile + + + + 0 + 0 + 940 + 701 + + + + Project File + + + + + + 0 + + + + Paths and Defines + + + + + + Import Project (Visual studio / compile database/ Borland C++ Builder 6) + + + + + + + + true + + + + + + + false + + + + + + + :/images/edit-clear.png + + + + + + + + Browse... + + + + + + + + + + + + + <html><head/><body><p>You have a choice:</p><p> * Analyze all Debug and Release configurations</p><p> * Only analyze the first matching Debug configuration</p><p><br/></p></body></html> + + + Analyze all Visual Studio configurations + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Selected VS Configurations + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + + + + + + Paths: + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + 16777215 + 140 + + + + + + + + + + Add... + + + + + + + Edit + + + + + + + Remove + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + Defines: + + + mEditDefines + + + + + + + Defines must be separated by a semicolon. Example: DEF1;DEF2=5;DEF3=int + + + + + + + + + + + Undefines: + + + mEditUndefines + + + + + + + Undefines must be separated by a semicolon. Example: UNDEF1;UNDEF2;UNDEF3 + + + + + + + + + + + + + Include Paths: + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + QAbstractItemView::SelectRows + + + + + + + + + Add... + + + + + + + Edit + + + + + + + Remove + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Up + + + + + + + Down + + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + Types and Functions + + + + + + Platform + + + + + + + + + + + + Libraries + + + + + + + + + Note: Put your own custom .cfg files in the same folder as the project file. You should see them above. + + + true + + + + + + + + + + + Analysis + + + + + + Cppcheck build dir (whole program analysis, incremental analysis, statistics, etc) + + + + + + This is a workfolder that Cppcheck will use for various purposes. + + + + + + + Browse... + + + + + + + + + + Parser + + + + + + Cppcheck (built in) + + + true + + + + + + + Clang (experimental) + + + + + + + + + + Check level + + + + + + Normal -- meant for normal analysis in CI. Analysis should finish in reasonable time. + + + + + + + Exhaustive -- meant for nightly builds etc. Analysis time can be longer (10x slower than compilation is OK). + + + + + + + + + + Analysis + + + + + + If you want to design your classes to be as flexible and robust as possible then the public interface must be very robust. Cppcheck will asumme that arguments can take *any* value. + + + Check that each class has a safe public interface + + + + + + + + + + + 0 + 0 + + + + Limit analysis + + + + + + Check code in headers (should be ON normally. if you want a limited quick analysis then turn this OFF) + + + true + + + + + + + Check code in unused templates (should be ON normally, however in theory you can safely ignore warnings in unused templates) + + + true + + + + + + + + + Max CTU depth + + + + + + + 10 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Max recursion in template instantiation + + + + + + + 1000 + + + 100 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 73 + + + + + + + + + Warning options + + + + + + Root path: + + + + + + Filepaths in warnings will be relative to this path + + + + + + + + + + Warning tags (separated by semicolon) + + + + + + If tags are added, you will be able to right click on warnings and set one of these tags. You can manually categorize warnings. + + + + + + + + + + Exclude source files + + + + + + + + + + + Exclude folder... + + + + + + + Exclude file... + + + + + + + Edit + + + + + + + Remove + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + Suppressions + + + + + + + + + + + Add + + + + + + + Remove + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 96 + + + + + + + + + Addons + + + + + + Addons + + + + + + Note: Addons require <a href="https://www.python.org/">Python</a> being installed. + + + true + + + + + + + Y2038 + + + + + + + Thread safety + + + + + + + + + + Coding standards + + + + + + + + Misra C + + + + + + + + 2012 + + + + + 2023 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + MISRA rule texts + + + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + + + ... + + + + + + + + + Misra C++ 2008 + + + + + + + Cert C + + + + + + + + + CERT-INT35-C: int precision (if size equals precision, you can leave empty) + + + + + + + + + + + + Cert C++ + + + + + + + Autosar + + + + + + + + + + Bug hunting (Premium) + + + + + + Bug hunting + + + + + + + + + + External tools + + + + + + Clang-tidy + + + + + + + Clang analyzer + + + + + + + + + + Qt::Vertical + + + + 20 + 368 + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + mButtons + + + + + mButtons + accepted() + ProjectFile + accept() + + + 270 + 352 + + + 157 + 158 + + + + + mButtons + rejected() + ProjectFile + reject() + + + 338 + 352 + + + 286 + 158 + + + + + diff --git a/cppcheck-2.14.0/gui/projectfiledialog.cpp b/cppcheck-2.14.0/gui/projectfiledialog.cpp new file mode 100644 index 00000000..1d89c899 --- /dev/null +++ b/cppcheck-2.14.0/gui/projectfiledialog.cpp @@ -0,0 +1,931 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "projectfiledialog.h" + +#include "checkthread.h" +#include "common.h" +#include "importproject.h" +#include "library.h" +#include "newsuppressiondialog.h" +#include "platform.h" +#include "platforms.h" +#include "projectfile.h" +#include "settings.h" + +#include "ui_projectfile.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static constexpr char ADDON_MISRA[] = "misra"; +static constexpr char CODING_STANDARD_MISRA_C_2023[] = "misra-c-2023"; +static constexpr char CODING_STANDARD_MISRA_CPP_2008[] = "misra-cpp-2008"; +static constexpr char CODING_STANDARD_CERT_C[] = "cert-c-2016"; +static constexpr char CODING_STANDARD_CERT_CPP[] = "cert-cpp-2016"; +static constexpr char CODING_STANDARD_AUTOSAR[] = "autosar"; + +/** Return paths from QListWidget */ +static QStringList getPaths(const QListWidget *list) +{ + const int count = list->count(); + QStringList paths; + for (int i = 0; i < count; i++) { + QListWidgetItem *item = list->item(i); + paths << QDir::fromNativeSeparators(item->text()); + } + return paths; +} + +/** Platforms shown in the platform combobox */ +static constexpr Platform::Type builtinPlatforms[] = { + Platform::Type::Native, + Platform::Type::Win32A, + Platform::Type::Win32W, + Platform::Type::Win64, + Platform::Type::Unix32, + Platform::Type::Unix64 +}; + +static constexpr int numberOfBuiltinPlatforms = sizeof(builtinPlatforms) / sizeof(builtinPlatforms[0]); + +QStringList ProjectFileDialog::getProjectConfigs(const QString &fileName) +{ + if (!fileName.endsWith(".sln") && !fileName.endsWith(".vcxproj")) + return QStringList(); + QStringList ret; + ImportProject importer; + Settings projSettings; + importer.import(fileName.toStdString(), &projSettings); + for (const std::string &cfg : importer.getVSConfigs()) + ret << QString::fromStdString(cfg); + return ret; +} + +ProjectFileDialog::ProjectFileDialog(ProjectFile *projectFile, bool premium, QWidget *parent) + : QDialog(parent) + , mUI(new Ui::ProjectFile) + , mProjectFile(projectFile) + , mPremium(premium) +{ + mUI->setupUi(this); + + mUI->mToolClangAnalyzer->hide(); + + const QFileInfo inf(projectFile->getFilename()); + QString filename = inf.fileName(); + QString title = tr("Project file: %1").arg(filename); + setWindowTitle(title); + loadSettings(); + + // Checkboxes for the libraries.. + const QString applicationFilePath = QCoreApplication::applicationFilePath(); + const QString appPath = QFileInfo(applicationFilePath).canonicalPath(); + const QString datadir = getDataDir(); + QStringList searchPaths; + searchPaths << appPath << appPath + "/cfg" << inf.canonicalPath(); +#ifdef FILESDIR + if (FILESDIR[0]) + searchPaths << FILESDIR << FILESDIR "/cfg"; +#endif + if (!datadir.isEmpty()) + searchPaths << datadir << datadir + "/cfg"; + QStringList libs; + // Search the std.cfg first since other libraries could depend on it + QString stdLibraryFilename; + for (const QString &sp : searchPaths) { + QDir dir(sp); + dir.setSorting(QDir::Name); + dir.setNameFilters(QStringList("*.cfg")); + dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); + for (const QFileInfo& item : dir.entryInfoList()) { + QString library = item.fileName(); + if (library.compare("std.cfg", Qt::CaseInsensitive) != 0) + continue; + Library lib; + const QString fullfilename = sp + "/" + library; + const Library::Error err = lib.load(nullptr, fullfilename.toLatin1()); + if (err.errorcode != Library::ErrorCode::OK) + continue; + // Working std.cfg found + stdLibraryFilename = fullfilename; + break; + } + if (!stdLibraryFilename.isEmpty()) + break; + } + // Search other libraries + for (const QString &sp : searchPaths) { + QDir dir(sp); + dir.setSorting(QDir::Name); + dir.setNameFilters(QStringList("*.cfg")); + dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); + for (const QFileInfo& item : dir.entryInfoList()) { + QString library = item.fileName(); + { + Library lib; + const QString fullfilename = sp + "/" + library; + Library::Error err = lib.load(nullptr, fullfilename.toLatin1()); + if (err.errorcode != Library::ErrorCode::OK) { + // Some libraries depend on std.cfg so load it first and test again + lib.load(nullptr, stdLibraryFilename.toLatin1()); + err = lib.load(nullptr, fullfilename.toLatin1()); + } + if (err.errorcode != Library::ErrorCode::OK) + continue; + } + library.chop(4); + if (library.compare("std", Qt::CaseInsensitive) == 0) + continue; + if (libs.indexOf(library) == -1) + libs << library; + } + } + libs.sort(); + mUI->mLibraries->clear(); + for (const QString &lib : libs) { + auto* item = new QListWidgetItem(lib, mUI->mLibraries); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag + item->setCheckState(Qt::Unchecked); // AND initialize check state + } + + // Platforms.. + Platforms platforms; + for (const Platform::Type builtinPlatform : builtinPlatforms) + mUI->mComboBoxPlatform->addItem(platforms.get(builtinPlatform).mTitle); + QStringList platformFiles; + for (QString sp : searchPaths) { + if (sp.endsWith("/cfg")) + sp = sp.mid(0,sp.length()-3) + "platforms"; + QDir dir(sp); + dir.setSorting(QDir::Name); + dir.setNameFilters(QStringList("*.xml")); + dir.setFilter(QDir::Files | QDir::NoDotAndDotDot); + for (const QFileInfo& item : dir.entryInfoList()) { + const QString platformFile = item.fileName(); + + Platform plat2; + if (!plat2.loadFromFile(applicationFilePath.toStdString().c_str(), platformFile.toStdString())) + continue; + + if (platformFiles.indexOf(platformFile) == -1) + platformFiles << platformFile; + } + } + platformFiles.sort(); + mUI->mComboBoxPlatform->addItems(platformFiles); + + // integer. allow empty. + mUI->mEditCertIntPrecision->setValidator(new QRegularExpressionValidator(QRegularExpression("[0-9]*"),this)); + + mUI->mEditTags->setValidator(new QRegularExpressionValidator(QRegularExpression("[a-zA-Z0-9 ;]*"),this)); + + const QRegularExpression undefRegExp("\\s*([a-zA-Z_][a-zA-Z0-9_]*[; ]*)*"); + mUI->mEditUndefines->setValidator(new QRegularExpressionValidator(undefRegExp, this)); + + connect(mUI->mButtons, &QDialogButtonBox::accepted, this, &ProjectFileDialog::ok); + connect(mUI->mBtnBrowseBuildDir, &QPushButton::clicked, this, &ProjectFileDialog::browseBuildDir); + connect(mUI->mBtnClearImportProject, &QPushButton::clicked, this, &ProjectFileDialog::clearImportProject); + connect(mUI->mBtnBrowseImportProject, &QPushButton::clicked, this, &ProjectFileDialog::browseImportProject); + connect(mUI->mBtnAddCheckPath, SIGNAL(clicked()), this, SLOT(addCheckPath())); + connect(mUI->mBtnEditCheckPath, &QPushButton::clicked, this, &ProjectFileDialog::editCheckPath); + connect(mUI->mBtnRemoveCheckPath, &QPushButton::clicked, this, &ProjectFileDialog::removeCheckPath); + connect(mUI->mBtnAddInclude, SIGNAL(clicked()), this, SLOT(addIncludeDir())); + connect(mUI->mBtnEditInclude, &QPushButton::clicked, this, &ProjectFileDialog::editIncludeDir); + connect(mUI->mBtnRemoveInclude, &QPushButton::clicked, this, &ProjectFileDialog::removeIncludeDir); + connect(mUI->mBtnAddIgnorePath, SIGNAL(clicked()), this, SLOT(addExcludePath())); + connect(mUI->mBtnAddIgnoreFile, SIGNAL(clicked()), this, SLOT(addExcludeFile())); + connect(mUI->mBtnEditIgnorePath, &QPushButton::clicked, this, &ProjectFileDialog::editExcludePath); + connect(mUI->mBtnRemoveIgnorePath, &QPushButton::clicked, this, &ProjectFileDialog::removeExcludePath); + connect(mUI->mBtnIncludeUp, &QPushButton::clicked, this, &ProjectFileDialog::moveIncludePathUp); + connect(mUI->mBtnIncludeDown, &QPushButton::clicked, this, &ProjectFileDialog::moveIncludePathDown); + connect(mUI->mBtnAddSuppression, &QPushButton::clicked, this, &ProjectFileDialog::addSuppression); + connect(mUI->mBtnRemoveSuppression, &QPushButton::clicked, this, &ProjectFileDialog::removeSuppression); + connect(mUI->mListSuppressions, &QListWidget::doubleClicked, this, &ProjectFileDialog::editSuppression); + connect(mUI->mBtnBrowseMisraFile, &QPushButton::clicked, this, &ProjectFileDialog::browseMisraFile); + connect(mUI->mChkAllVsConfigs, &QCheckBox::clicked, this, &ProjectFileDialog::checkAllVSConfigs); + loadFromProjectFile(projectFile); +} + +ProjectFileDialog::~ProjectFileDialog() +{ + saveSettings(); + delete mUI; +} + +void ProjectFileDialog::loadSettings() +{ + QSettings settings; + resize(settings.value(SETTINGS_PROJECT_DIALOG_WIDTH, 470).toInt(), + settings.value(SETTINGS_PROJECT_DIALOG_HEIGHT, 330).toInt()); +} + +void ProjectFileDialog::saveSettings() const +{ + QSettings settings; + settings.setValue(SETTINGS_PROJECT_DIALOG_WIDTH, size().width()); + settings.setValue(SETTINGS_PROJECT_DIALOG_HEIGHT, size().height()); +} + +static void updateAddonCheckBox(QCheckBox *cb, const ProjectFile *projectFile, const QString &dataDir, const QString &addon) +{ + if (projectFile) + cb->setChecked(projectFile->getAddons().contains(addon)); + if (ProjectFile::getAddonFilePath(dataDir, addon).isEmpty()) { + cb->setEnabled(false); + cb->setText(cb->text() + QObject::tr(" (Not found)")); + } +} + +void ProjectFileDialog::checkAllVSConfigs() +{ + if (mUI->mChkAllVsConfigs->isChecked()) { + for (int row = 0; row < mUI->mListVsConfigs->count(); ++row) { + QListWidgetItem *item = mUI->mListVsConfigs->item(row); + item->setCheckState(Qt::Checked); + } + } + mUI->mListVsConfigs->setEnabled(!mUI->mChkAllVsConfigs->isChecked()); +} + +void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile) +{ + setRootPath(projectFile->getRootPath()); + setBuildDir(projectFile->getBuildDir()); + setIncludepaths(projectFile->getIncludeDirs()); + setDefines(projectFile->getDefines()); + setUndefines(projectFile->getUndefines()); + setCheckPaths(projectFile->getCheckPaths()); + setImportProject(projectFile->getImportProject()); + mUI->mChkAllVsConfigs->setChecked(projectFile->getAnalyzeAllVsConfigs()); + setProjectConfigurations(getProjectConfigs(mUI->mEditImportProject->text())); + for (int row = 0; row < mUI->mListVsConfigs->count(); ++row) { + QListWidgetItem *item = mUI->mListVsConfigs->item(row); + if (projectFile->getAnalyzeAllVsConfigs() || projectFile->getVsConfigurations().contains(item->text())) + item->setCheckState(Qt::Checked); + else + item->setCheckState(Qt::Unchecked); + } + if (projectFile->isCheckLevelExhaustive()) + mUI->mCheckLevelExhaustive->setChecked(true); + else + mUI->mCheckLevelNormal->setChecked(true); + mUI->mCheckHeaders->setChecked(projectFile->getCheckHeaders()); + mUI->mCheckUnusedTemplates->setChecked(projectFile->getCheckUnusedTemplates()); + mUI->mMaxCtuDepth->setValue(projectFile->getMaxCtuDepth()); + mUI->mMaxTemplateRecursion->setValue(projectFile->getMaxTemplateRecursion()); + if (projectFile->clangParser) + mUI->mBtnClangParser->setChecked(true); + else + mUI->mBtnCppcheckParser->setChecked(true); + mUI->mBtnSafeClasses->setChecked(projectFile->safeChecks.classes); + setExcludedPaths(projectFile->getExcludedPaths()); + setLibraries(projectFile->getLibraries()); + const QString platform = projectFile->getPlatform(); + if (platform.endsWith(".xml")) { + int i; + for (i = numberOfBuiltinPlatforms; i < mUI->mComboBoxPlatform->count(); ++i) { + if (mUI->mComboBoxPlatform->itemText(i) == platform) + break; + } + if (i < mUI->mComboBoxPlatform->count()) + mUI->mComboBoxPlatform->setCurrentIndex(i); + else { + mUI->mComboBoxPlatform->addItem(platform); + mUI->mComboBoxPlatform->setCurrentIndex(i); + } + } else { + int i; + for (i = 0; i < numberOfBuiltinPlatforms; ++i) { + const Platform::Type p = builtinPlatforms[i]; + if (platform == Platform::toString(p)) + break; + } + if (i < numberOfBuiltinPlatforms) + mUI->mComboBoxPlatform->setCurrentIndex(i); + else + mUI->mComboBoxPlatform->setCurrentIndex(-1); + } + + mUI->mComboBoxPlatform->setCurrentText(projectFile->getPlatform()); + setSuppressions(projectFile->getSuppressions()); + + // TODO + // Human knowledge.. + /* + mUI->mListUnknownFunctionReturn->clear(); + mUI->mListUnknownFunctionReturn->addItem("rand()"); + for (int row = 0; row < mUI->mListUnknownFunctionReturn->count(); ++row) { + QListWidgetItem *item = mUI->mListUnknownFunctionReturn->item(row); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag + const bool unknownValues = projectFile->getCheckUnknownFunctionReturn().contains(item->text()); + item->setCheckState(unknownValues ? Qt::Checked : Qt::Unchecked); // AND initialize check state + } + mUI->mCheckSafeClasses->setChecked(projectFile->getSafeChecks().classes); + mUI->mCheckSafeExternalFunctions->setChecked(projectFile->getSafeChecks().externalFunctions); + mUI->mCheckSafeInternalFunctions->setChecked(projectFile->getSafeChecks().internalFunctions); + mUI->mCheckSafeExternalVariables->setChecked(projectFile->getSafeChecks().externalVariables); + */ + + // Addons.. + QSettings settings; + const QString dataDir = getDataDir(); + updateAddonCheckBox(mUI->mAddonThreadSafety, projectFile, dataDir, "threadsafety"); + updateAddonCheckBox(mUI->mAddonY2038, projectFile, dataDir, "y2038"); + + // Misra checkbox.. + mUI->mMisraC->setText(mPremium ? "Misra C" : "Misra C 2012"); + updateAddonCheckBox(mUI->mMisraC, projectFile, dataDir, ADDON_MISRA); + mUI->mMisraVersion->setEnabled(mUI->mMisraC->isChecked()); + connect(mUI->mMisraC, &QCheckBox::toggled, mUI->mMisraVersion, &QComboBox::setEnabled); + + const QString &misraFile = settings.value(SETTINGS_MISRA_FILE, QString()).toString(); + mUI->mEditMisraFile->setText(misraFile); + mUI->mMisraVersion->setVisible(mPremium); + mUI->mMisraVersion->setCurrentIndex(projectFile->getCodingStandards().contains(CODING_STANDARD_MISRA_C_2023)); + if (mPremium) { + mUI->mLabelMisraFile->setVisible(false); + mUI->mEditMisraFile->setVisible(false); + mUI->mBtnBrowseMisraFile->setVisible(false); + } else if (!mUI->mMisraC->isEnabled()) { + mUI->mEditMisraFile->setEnabled(false); + mUI->mBtnBrowseMisraFile->setEnabled(false); + } + + mUI->mCertC2016->setChecked(mPremium && projectFile->getCodingStandards().contains(CODING_STANDARD_CERT_C)); + mUI->mCertCpp2016->setChecked(mPremium && projectFile->getCodingStandards().contains(CODING_STANDARD_CERT_CPP)); + mUI->mMisraCpp2008->setChecked(mPremium && projectFile->getCodingStandards().contains(CODING_STANDARD_MISRA_CPP_2008)); + mUI->mAutosar->setChecked(mPremium && projectFile->getCodingStandards().contains(CODING_STANDARD_AUTOSAR)); + + if (projectFile->getCertIntPrecision() <= 0) + mUI->mEditCertIntPrecision->setText(QString()); + else + mUI->mEditCertIntPrecision->setText(QString::number(projectFile->getCertIntPrecision())); + + mUI->mMisraCpp2008->setEnabled(mPremium); + mUI->mCertC2016->setEnabled(mPremium); + mUI->mCertCpp2016->setEnabled(mPremium); + mUI->mAutosar->setEnabled(mPremium); + mUI->mLabelCertIntPrecision->setVisible(mPremium); + mUI->mEditCertIntPrecision->setVisible(mPremium); + mUI->mBughunting->setChecked(mPremium && projectFile->getBughunting()); + mUI->mBughunting->setEnabled(mPremium); + + mUI->mToolClangAnalyzer->setChecked(projectFile->getClangAnalyzer()); + mUI->mToolClangTidy->setChecked(projectFile->getClangTidy()); + if (CheckThread::clangTidyCmd().isEmpty()) { + mUI->mToolClangTidy->setText(tr("Clang-tidy (not found)")); + mUI->mToolClangTidy->setEnabled(false); + } + mUI->mEditTags->setText(projectFile->getTags().join(';')); + updatePathsAndDefines(); +} + +void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const +{ + projectFile->setRootPath(getRootPath()); + projectFile->setBuildDir(getBuildDir()); + projectFile->setImportProject(getImportProject()); + projectFile->setAnalyzeAllVsConfigs(mUI->mChkAllVsConfigs->isChecked()); + projectFile->setVSConfigurations(getProjectConfigurations()); + projectFile->setCheckHeaders(mUI->mCheckHeaders->isChecked()); + projectFile->setCheckUnusedTemplates(mUI->mCheckUnusedTemplates->isChecked()); + projectFile->setMaxCtuDepth(mUI->mMaxCtuDepth->value()); + projectFile->setMaxTemplateRecursion(mUI->mMaxTemplateRecursion->value()); + projectFile->setIncludes(getIncludePaths()); + projectFile->setDefines(getDefines()); + projectFile->setUndefines(getUndefines()); + projectFile->setCheckPaths(getCheckPaths()); + projectFile->setExcludedPaths(getExcludedPaths()); + projectFile->setLibraries(getLibraries()); + projectFile->setCheckLevel(mUI->mCheckLevelExhaustive->isChecked() ? ProjectFile::CheckLevel::exhaustive : ProjectFile::CheckLevel::normal); + projectFile->clangParser = mUI->mBtnClangParser->isChecked(); + projectFile->safeChecks.classes = mUI->mBtnSafeClasses->isChecked(); + if (mUI->mComboBoxPlatform->currentText().endsWith(".xml")) + projectFile->setPlatform(mUI->mComboBoxPlatform->currentText()); + else { + const int i = mUI->mComboBoxPlatform->currentIndex(); + if (i>=0 && i < numberOfBuiltinPlatforms) + projectFile->setPlatform(Platform::toString(builtinPlatforms[i])); + else + projectFile->setPlatform(QString()); + } + projectFile->setSuppressions(getSuppressions()); + // Human knowledge + /* + QStringList unknownReturnValues; + for (int row = 0; row < mUI->mListUnknownFunctionReturn->count(); ++row) { + QListWidgetItem *item = mUI->mListUnknownFunctionReturn->item(row); + if (item->checkState() == Qt::Checked) + unknownReturnValues << item->text(); + } + projectFile->setCheckUnknownFunctionReturn(unknownReturnValues); + ProjectFile::SafeChecks safeChecks; + safeChecks.classes = mUI->mCheckSafeClasses->isChecked(); + safeChecks.externalFunctions = mUI->mCheckSafeExternalFunctions->isChecked(); + safeChecks.internalFunctions = mUI->mCheckSafeInternalFunctions->isChecked(); + safeChecks.externalVariables = mUI->mCheckSafeExternalVariables->isChecked(); + projectFile->setSafeChecks(safeChecks); + */ + // Addons + QStringList addons; + if (mUI->mAddonThreadSafety->isChecked()) + addons << "threadsafety"; + if (mUI->mAddonY2038->isChecked()) + addons << "y2038"; + if (mUI->mMisraC->isChecked()) + addons << ADDON_MISRA; + projectFile->setAddons(addons); + QStringList codingStandards; + if (mUI->mCertC2016->isChecked()) + codingStandards << CODING_STANDARD_CERT_C; + if (mUI->mCertCpp2016->isChecked()) + codingStandards << CODING_STANDARD_CERT_CPP; + if (mPremium && mUI->mMisraVersion->currentIndex() == 1) + codingStandards << CODING_STANDARD_MISRA_C_2023; + if (mUI->mMisraCpp2008->isChecked()) + codingStandards << CODING_STANDARD_MISRA_CPP_2008; + if (mUI->mAutosar->isChecked()) + codingStandards << CODING_STANDARD_AUTOSAR; + projectFile->setCodingStandards(std::move(codingStandards)); + projectFile->setCertIntPrecision(mUI->mEditCertIntPrecision->text().toInt()); + projectFile->setBughunting(mUI->mBughunting->isChecked()); + projectFile->setClangAnalyzer(mUI->mToolClangAnalyzer->isChecked()); + projectFile->setClangTidy(mUI->mToolClangTidy->isChecked()); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + projectFile->setTags(mUI->mEditTags->text().split(";", Qt::SkipEmptyParts)); +#else + projectFile->setTags(mUI->mEditTags->text().split(";", QString::SkipEmptyParts)); +#endif +} + +void ProjectFileDialog::ok() +{ + saveToProjectFile(mProjectFile); + mProjectFile->write(); + accept(); +} + +QString ProjectFileDialog::getExistingDirectory(const QString &caption, bool trailingSlash) +{ + const QFileInfo inf(mProjectFile->getFilename()); + const QString rootpath = inf.absolutePath(); + QString selectedDir = QFileDialog::getExistingDirectory(this, + caption, + rootpath); + + if (selectedDir.isEmpty()) + return QString(); + + // Check if the path is relative to project file's path and if so + // make it a relative path instead of absolute path. + const QDir dir(rootpath); + const QString relpath(dir.relativeFilePath(selectedDir)); + if (!relpath.startsWith("../..")) + selectedDir = relpath; + + // Trailing slash.. + if (trailingSlash && !selectedDir.endsWith('/')) + selectedDir += '/'; + + return selectedDir; +} + +void ProjectFileDialog::browseBuildDir() +{ + const QString dir(getExistingDirectory(tr("Select Cppcheck build dir"), false)); + if (!dir.isEmpty()) + mUI->mEditBuildDir->setText(dir); +} + +void ProjectFileDialog::updatePathsAndDefines() +{ + const QString &fileName = mUI->mEditImportProject->text(); + const bool importProject = !fileName.isEmpty(); + const bool hasConfigs = fileName.endsWith(".sln") || fileName.endsWith(".vcxproj"); + mUI->mBtnClearImportProject->setEnabled(importProject); + mUI->mListCheckPaths->setEnabled(!importProject); + mUI->mListIncludeDirs->setEnabled(!importProject); + mUI->mBtnAddCheckPath->setEnabled(!importProject); + mUI->mBtnEditCheckPath->setEnabled(!importProject); + mUI->mBtnRemoveCheckPath->setEnabled(!importProject); + mUI->mEditDefines->setEnabled(!importProject); + mUI->mEditUndefines->setEnabled(!importProject); + mUI->mBtnAddInclude->setEnabled(!importProject); + mUI->mBtnEditInclude->setEnabled(!importProject); + mUI->mBtnRemoveInclude->setEnabled(!importProject); + mUI->mBtnIncludeUp->setEnabled(!importProject); + mUI->mBtnIncludeDown->setEnabled(!importProject); + mUI->mChkAllVsConfigs->setEnabled(hasConfigs); + mUI->mListVsConfigs->setEnabled(hasConfigs && !mUI->mChkAllVsConfigs->isChecked()); + if (!hasConfigs) + mUI->mListVsConfigs->clear(); +} + +void ProjectFileDialog::clearImportProject() +{ + mUI->mEditImportProject->clear(); + updatePathsAndDefines(); +} + +void ProjectFileDialog::browseImportProject() +{ + const QFileInfo inf(mProjectFile->getFilename()); + const QDir &dir = inf.absoluteDir(); + QMap filters; + filters[tr("Visual Studio")] = "*.sln *.vcxproj"; + filters[tr("Compile database")] = "compile_commands.json"; + filters[tr("Borland C++ Builder 6")] = "*.bpr"; + QString fileName = QFileDialog::getOpenFileName(this, tr("Import Project"), + dir.canonicalPath(), + toFilterString(filters)); + if (!fileName.isEmpty()) { + mUI->mEditImportProject->setText(dir.relativeFilePath(fileName)); + updatePathsAndDefines(); + setProjectConfigurations(getProjectConfigs(fileName)); + for (int row = 0; row < mUI->mListVsConfigs->count(); ++row) { + QListWidgetItem *item = mUI->mListVsConfigs->item(row); + item->setCheckState(Qt::Checked); + } + } +} + +QStringList ProjectFileDialog::getProjectConfigurations() const +{ + QStringList configs; + for (int row = 0; row < mUI->mListVsConfigs->count(); ++row) { + QListWidgetItem *item = mUI->mListVsConfigs->item(row); + if (item->checkState() == Qt::Checked) + configs << item->text(); + } + return configs; +} + +void ProjectFileDialog::setProjectConfigurations(const QStringList &configs) +{ + mUI->mListVsConfigs->clear(); + mUI->mListVsConfigs->setEnabled(!configs.isEmpty() && !mUI->mChkAllVsConfigs->isChecked()); + for (const QString &cfg : configs) { + auto* item = new QListWidgetItem(cfg, mUI->mListVsConfigs); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); // set checkable flag + item->setCheckState(Qt::Unchecked); + } +} + +QString ProjectFileDialog::getImportProject() const +{ + return mUI->mEditImportProject->text(); +} + +void ProjectFileDialog::addIncludeDir(const QString &dir) +{ + if (dir.isEmpty()) + return; + + const QString newdir = QDir::toNativeSeparators(dir); + auto *item = new QListWidgetItem(newdir); + item->setFlags(item->flags() | Qt::ItemIsEditable); + mUI->mListIncludeDirs->addItem(item); +} + +void ProjectFileDialog::addCheckPath(const QString &path) +{ + if (path.isEmpty()) + return; + + const QString newpath = QDir::toNativeSeparators(path); + auto *item = new QListWidgetItem(newpath); + item->setFlags(item->flags() | Qt::ItemIsEditable); + mUI->mListCheckPaths->addItem(item); +} + +void ProjectFileDialog::addExcludePath(const QString &path) +{ + if (path.isEmpty()) + return; + + const QString newpath = QDir::toNativeSeparators(path); + auto *item = new QListWidgetItem(newpath); + item->setFlags(item->flags() | Qt::ItemIsEditable); + mUI->mListExcludedPaths->addItem(item); +} + +QString ProjectFileDialog::getRootPath() const +{ + QString root = mUI->mEditProjectRoot->text(); + root = root.trimmed(); + root = QDir::fromNativeSeparators(root); + return root; +} + +QString ProjectFileDialog::getBuildDir() const +{ + return mUI->mEditBuildDir->text(); +} + +QStringList ProjectFileDialog::getIncludePaths() const +{ + return getPaths(mUI->mListIncludeDirs); +} + +QStringList ProjectFileDialog::getDefines() const +{ +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + return mUI->mEditDefines->text().trimmed().split(QRegularExpression("\\s*;\\s*"), Qt::SkipEmptyParts); +#else + return mUI->mEditDefines->text().trimmed().split(QRegularExpression("\\s*;\\s*"), QString::SkipEmptyParts); +#endif +} + +QStringList ProjectFileDialog::getUndefines() const +{ + const QString undefine = mUI->mEditUndefines->text().trimmed(); +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + QStringList undefines = undefine.split(QRegularExpression("\\s*;\\s*"), Qt::SkipEmptyParts); +#else + QStringList undefines = undefine.split(QRegularExpression("\\s*;\\s*"), QString::SkipEmptyParts); +#endif + undefines.removeDuplicates(); + return undefines; +} + +QStringList ProjectFileDialog::getCheckPaths() const +{ + return getPaths(mUI->mListCheckPaths); +} + +QStringList ProjectFileDialog::getExcludedPaths() const +{ + return getPaths(mUI->mListExcludedPaths); +} + +QStringList ProjectFileDialog::getLibraries() const +{ + QStringList libraries; + for (int row = 0; row < mUI->mLibraries->count(); ++row) { + QListWidgetItem *item = mUI->mLibraries->item(row); + if (item->checkState() == Qt::Checked) + libraries << item->text(); + } + return libraries; +} + +void ProjectFileDialog::setRootPath(const QString &root) +{ + mUI->mEditProjectRoot->setText(QDir::toNativeSeparators(root)); +} + +void ProjectFileDialog::setBuildDir(const QString &buildDir) +{ + mUI->mEditBuildDir->setText(buildDir); +} + +void ProjectFileDialog::setImportProject(const QString &importProject) +{ + mUI->mEditImportProject->setText(importProject); +} + +void ProjectFileDialog::setIncludepaths(const QStringList &includes) +{ + for (const QString& dir : includes) { + addIncludeDir(dir); + } +} + +void ProjectFileDialog::setDefines(const QStringList &defines) +{ + mUI->mEditDefines->setText(defines.join(";")); +} + +void ProjectFileDialog::setUndefines(const QStringList &undefines) +{ + mUI->mEditUndefines->setText(undefines.join(";")); +} + +void ProjectFileDialog::setCheckPaths(const QStringList &paths) +{ + for (const QString& path : paths) { + addCheckPath(path); + } +} + +void ProjectFileDialog::setExcludedPaths(const QStringList &paths) +{ + for (const QString& path : paths) { + addExcludePath(path); + } +} + +void ProjectFileDialog::setLibraries(const QStringList &libraries) +{ + for (int row = 0; row < mUI->mLibraries->count(); ++row) { + QListWidgetItem *item = mUI->mLibraries->item(row); + item->setCheckState(libraries.contains(item->text()) ? Qt::Checked : Qt::Unchecked); + } +} + +void ProjectFileDialog::addSingleSuppression(const SuppressionList::Suppression &suppression) +{ + mSuppressions += suppression; + mUI->mListSuppressions->addItem(QString::fromStdString(suppression.getText())); +} + +void ProjectFileDialog::setSuppressions(const QList &suppressions) +{ + mUI->mListSuppressions->clear(); + QList new_suppressions = suppressions; + mSuppressions.clear(); + for (const SuppressionList::Suppression &suppression : new_suppressions) { + addSingleSuppression(suppression); + } + mUI->mListSuppressions->sortItems(); +} + +void ProjectFileDialog::addCheckPath() +{ + QString dir = getExistingDirectory(tr("Select a directory to check"), false); + if (!dir.isEmpty()) + addCheckPath(dir); +} + +void ProjectFileDialog::editCheckPath() +{ + QListWidgetItem *item = mUI->mListCheckPaths->currentItem(); + mUI->mListCheckPaths->editItem(item); +} + +void ProjectFileDialog::removeCheckPath() +{ + const int row = mUI->mListCheckPaths->currentRow(); + QListWidgetItem *item = mUI->mListCheckPaths->takeItem(row); + delete item; +} + +void ProjectFileDialog::addIncludeDir() +{ + const QString dir = getExistingDirectory(tr("Select include directory"), true); + if (!dir.isEmpty()) + addIncludeDir(dir); +} + +void ProjectFileDialog::removeIncludeDir() +{ + const int row = mUI->mListIncludeDirs->currentRow(); + QListWidgetItem *item = mUI->mListIncludeDirs->takeItem(row); + delete item; +} + +void ProjectFileDialog::editIncludeDir() +{ + QListWidgetItem *item = mUI->mListIncludeDirs->currentItem(); + mUI->mListIncludeDirs->editItem(item); +} + +void ProjectFileDialog::addExcludePath() +{ + addExcludePath(getExistingDirectory(tr("Select directory to ignore"), true)); +} + +void ProjectFileDialog::addExcludeFile() +{ + const QFileInfo inf(mProjectFile->getFilename()); + const QDir &dir = inf.absoluteDir(); + QMap filters; + filters[tr("Source files")] = "*.c *.cpp"; + filters[tr("All files")] = "*.*"; + addExcludePath(QFileDialog::getOpenFileName(this, tr("Exclude file"), dir.canonicalPath(), toFilterString(filters))); +} + +void ProjectFileDialog::editExcludePath() +{ + QListWidgetItem *item = mUI->mListExcludedPaths->currentItem(); + mUI->mListExcludedPaths->editItem(item); +} + +void ProjectFileDialog::removeExcludePath() +{ + const int row = mUI->mListExcludedPaths->currentRow(); + QListWidgetItem *item = mUI->mListExcludedPaths->takeItem(row); + delete item; +} + +void ProjectFileDialog::moveIncludePathUp() +{ + int row = mUI->mListIncludeDirs->currentRow(); + QListWidgetItem *item = mUI->mListIncludeDirs->takeItem(row); + row = row > 0 ? row - 1 : 0; + mUI->mListIncludeDirs->insertItem(row, item); + mUI->mListIncludeDirs->setCurrentItem(item); +} + +void ProjectFileDialog::moveIncludePathDown() +{ + int row = mUI->mListIncludeDirs->currentRow(); + QListWidgetItem *item = mUI->mListIncludeDirs->takeItem(row); + const int count = mUI->mListIncludeDirs->count(); + row = row < count ? row + 1 : count; + mUI->mListIncludeDirs->insertItem(row, item); + mUI->mListIncludeDirs->setCurrentItem(item); +} + +void ProjectFileDialog::addSuppression() +{ + NewSuppressionDialog dlg; + if (dlg.exec() == QDialog::Accepted) { + addSingleSuppression(dlg.getSuppression()); + } +} + +void ProjectFileDialog::removeSuppression() +{ + const int row = mUI->mListSuppressions->currentRow(); + QListWidgetItem *item = mUI->mListSuppressions->takeItem(row); + if (!item) + return; + + const int suppressionIndex = getSuppressionIndex(item->text()); + if (suppressionIndex >= 0) + mSuppressions.removeAt(suppressionIndex); + delete item; +} + +void ProjectFileDialog::editSuppression(const QModelIndex & /*index*/) +{ + const int row = mUI->mListSuppressions->currentRow(); + QListWidgetItem *item = mUI->mListSuppressions->item(row); + const int suppressionIndex = getSuppressionIndex(item->text()); + if (suppressionIndex >= 0) { // TODO what if suppression is not found? + NewSuppressionDialog dlg; + dlg.setSuppression(mSuppressions[suppressionIndex]); + if (dlg.exec() == QDialog::Accepted) { + mSuppressions[suppressionIndex] = dlg.getSuppression(); + setSuppressions(mSuppressions); + } + } +} + +int ProjectFileDialog::getSuppressionIndex(const QString &shortText) const +{ + const std::string s = shortText.toStdString(); + for (int i = 0; i < mSuppressions.size(); ++i) { + if (mSuppressions[i].getText() == s) + return i; + } + return -1; +} + +void ProjectFileDialog::browseMisraFile() +{ + const QString fileName = QFileDialog::getOpenFileName(this, + tr("Select MISRA rule texts file"), + QDir::homePath(), + tr("MISRA rule texts file (%1)").arg("*.txt")); + if (!fileName.isEmpty()) { + QSettings settings; + mUI->mEditMisraFile->setText(fileName); + settings.setValue(SETTINGS_MISRA_FILE, fileName); + + mUI->mMisraC->setText("MISRA C 2012"); + mUI->mMisraC->setEnabled(true); + updateAddonCheckBox(mUI->mMisraC, nullptr, getDataDir(), ADDON_MISRA); + } +} diff --git a/cppcheck-2.14.0/gui/projectfiledialog.h b/cppcheck-2.14.0/gui/projectfiledialog.h new file mode 100644 index 00000000..29d13f38 --- /dev/null +++ b/cppcheck-2.14.0/gui/projectfiledialog.h @@ -0,0 +1,338 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "suppressions.h" + +#include +#include +#include +#include +#include + +class QModelIndex; +class QWidget; +namespace Ui { + class ProjectFile; +} + +/// @addtogroup GUI +/// @{ + + +class ProjectFile; + +/** + * @brief A dialog for editing project file data. + */ +class ProjectFileDialog : public QDialog { + Q_OBJECT +public: + explicit ProjectFileDialog(ProjectFile *projectFile, bool premium, QWidget *parent = nullptr); + ~ProjectFileDialog() override; + +private: + void loadFromProjectFile(const ProjectFile *projectFile); + void saveToProjectFile(ProjectFile *projectFile) const; + + /** Enable and disable widgets in the 'Paths and Defines' tab */ + void updatePathsAndDefines(); + + /** + * @brief Return project root path from the dialog control. + * @return Project root path. + */ + QString getRootPath() const; + + QStringList getProjectConfigurations() const; + void setProjectConfigurations(const QStringList &configs); + + QString getImportProject() const; + + /** Get Cppcheck build dir */ + QString getBuildDir() const; + + /** + * @brief Return include paths from the dialog control. + * @return List of include paths. + */ + QStringList getIncludePaths() const; + + /** + * @brief Return define names from the dialog control. + * @return List of define names. + */ + QStringList getDefines() const; + + /** + * @brief Return undefine names from the dialog control. + * @return List of undefine names. + */ + QStringList getUndefines() const; + + /** + * @brief Return check paths from the dialog control. + * @return List of check paths. + */ + QStringList getCheckPaths() const; + + /** + * @brief Return excluded paths from the dialog control. + * @return List of excluded paths. + */ + QStringList getExcludedPaths() const; + + /** + * @brief Return selected libraries from the dialog control. + * @return List of libraries. + */ + QStringList getLibraries() const; + + /** + * @brief Return suppressions from the dialog control. + * @return List of suppressions. + */ + const QList& getSuppressions() const { + return mSuppressions; + } + + /** + * @brief Set project root path to dialog control. + * @param root Project root path to set to dialog control. + */ + void setRootPath(const QString &root); + + /** Set build dir */ + void setBuildDir(const QString &buildDir); + + void setImportProject(const QString &importProject); + + /** + * @brief Set include paths to dialog control. + * @param includes List of include paths to set to dialog control. + */ + void setIncludepaths(const QStringList &includes); + + /** + * @brief Set define names to dialog control. + * @param defines List of define names to set to dialog control. + */ + void setDefines(const QStringList &defines); + + /** + * @brief Set undefine names to dialog control. + * @param undefines List of undefine names to set to dialog control. + */ + void setUndefines(const QStringList &undefines); + + /** + * @brief Set check paths to dialog control. + * @param paths List of path names to set to dialog control. + */ + void setCheckPaths(const QStringList &paths); + + /** + * @brief Set excluded paths to dialog control. + * @param paths List of path names to set to dialog control. + */ + void setExcludedPaths(const QStringList &paths); + + /** + * @brief Set libraries to dialog control. + * @param libraries List of libraries to set to dialog control. + */ + void setLibraries(const QStringList &libraries); + + /** + * @brief Add a single suppression to dialog control. + * @param suppression A suppressions to add to dialog control. + */ + void addSingleSuppression(const SuppressionList::Suppression &suppression); + + /** + * @brief Set suppressions to dialog control. + * @param suppressions List of suppressions to set to dialog control. + */ + void setSuppressions(const QList &suppressions); + +protected slots: + + /** ok button pressed, save changes and accept */ + void ok(); + + /** + * @brief Browse for build dir. + */ + void browseBuildDir(); + + /** + * @brief Clear 'import project'. + */ + void clearImportProject(); + + /** + * @brief Browse for solution / project / compile database. + */ + void browseImportProject(); + + /** + * @brief Add new path to check. + */ + void addCheckPath(); + + /** + * @brief Edit path in the list. + */ + void editCheckPath(); + + /** + * @brief Remove path from the list. + */ + void removeCheckPath(); + + /** + * @brief Browse for include directory. + * Allow user to add new include directory to the list. + */ + void addIncludeDir(); + + /** + * @brief Remove include directory from the list. + */ + void removeIncludeDir(); + + /** + * @brief Edit include directory in the list. + */ + void editIncludeDir(); + + /** + * @brief Add new path to exclude list. + */ + void addExcludePath(); + + /** + * @brief Add new file to exclude list. + */ + void addExcludeFile(); + + /** + * @brief Edit excluded path in the list. + */ + void editExcludePath(); + + /** + * @brief Remove excluded path from the list. + */ + void removeExcludePath(); + + /** + * @brief Move include path up in the list. + */ + void moveIncludePathUp(); + + /** + * @brief Move include path down in the list. + */ + void moveIncludePathDown(); + + /** + * @brief Add suppression to the list + */ + void addSuppression(); + + /** + * @brief Remove selected suppression from the list + */ + void removeSuppression(); + + /** + * @brief Edit suppression (double clicking on suppression) + */ + void editSuppression(const QModelIndex &index); + + /** + * @brief Browse for misra file + */ + void browseMisraFile(); + + /** + * @brief Check for all VS configurations + */ + void checkAllVSConfigs(); + +protected: + + /** + * @brief Save dialog settings. + */ + void loadSettings(); + + /** + * @brief Load dialog settings. + */ + void saveSettings() const; + + /** + * @brief Add new indlude directory. + * @param dir Directory to add. + */ + void addIncludeDir(const QString &dir); + + /** + * @brief Add new path to check. + * @param path Path to add. + */ + void addCheckPath(const QString &path); + + /** + * @brief Add new path to ignore list. + * @param path Path to add. + */ + void addExcludePath(const QString &path); + + /** + * @brief Get mSuppressions index that match the + * given short text + * @param shortText text as generated by Suppression::getText + * @return index of matching suppression, -1 if not found + */ + int getSuppressionIndex(const QString &shortText) const; + +private: + static QStringList getProjectConfigs(const QString &fileName); + + Ui::ProjectFile *mUI; + + /** + * @brief Projectfile path. + */ + ProjectFile *mProjectFile; + + /** Is this Cppcheck Premium? */ + bool mPremium; + + QString getExistingDirectory(const QString &caption, bool trailingSlash); + + QList mSuppressions; +}; + +/// @} +#endif // PROJECTFILE_DIALOG_H diff --git a/cppcheck-2.14.0/gui/readme.txt b/cppcheck-2.14.0/gui/readme.txt new file mode 100644 index 00000000..3974af60 --- /dev/null +++ b/cppcheck-2.14.0/gui/readme.txt @@ -0,0 +1,70 @@ +Cppcheck GUI +============ +This is a GUI for cppcheck. It allows selecting folder or set of files to check +with cppcheck and shows list of found errors. + +Running +------- +You need Qt5 libraries installed in your system. Packages/files to install +depends on your operating system: +- Windows: download Qt from http://www.qt.io/download/ +- Linux: install Qt using your package manager, look for packages having Qt + in their name, e.g. for Ubuntu install libqt5core5a, libqt5gui5, libqt5widgets5 + and libqt5printsupport5. + +Compiling +--------- +Windows: +- The easy ways are: +-- download Qt SDK from http://www.qt.io/download/ and use + QtCreator to build the GUI. +-- Download precompiled libraries for your platform and use your preferred + IDE/environment to build GUI. Be careful to download the correct version of + library for your compiler! +- The harder way is to download Qt sources and build Qt. Compiling Qt alone may + take over 4 hours! + +Linux: +- Install Qt development packages (make sure qmake -tool gets installed!). The + names depend on distribution, but e.g. for Ubuntu the needed packages are: + * qt5-default + +After you have needed libraries and tools installed, open command +prompt/console, go to gui directory and run command: +- qmake (in Linux and in Windows if build with MinGW/gcc or nmake) +- qmake -tp vc (to generate Visual Studio project file) +- qmake -tp vc LINKCORE=yes (to generate Visual Studio project file, linking + dynamically to core. Recommended.) + +On Windows, you have to either call qtvars.bat in Qt folder or use the Qt command +line prompt shortcut added in the start menu by Qt installation. + +These commands generate makefiles to actually build the software. After that +the actual building is done in IDE or command line as usual. Note that you +don't need to run qmake again unless you add/remove files from the project. + +The Visual Studio solution does not contain a configuration for x64 platform, but +it can be added easily. + +Tests +----- +There are tests for the GUI in gui/test -directory. There is test.pro +-projectfile for building all the tests. Each test is in own subdirectory and +builds own binary. Test is run by simple running that binary. The binary also +has several options to select tests etc. You can get the help by running +"binaryname -help" -command. + +Translations +------------ +The GUI is translated to several languages. Qt comes with two tools to update +and compile the translations. lupdate updates translations files from the code +and lrelease compiles translation files use with the executable. + +To update translations: +- run "lupdate gui.pro -no-obsolete" 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. diff --git a/cppcheck-2.14.0/gui/report.cpp b/cppcheck-2.14.0/gui/report.cpp new file mode 100644 index 00000000..e7b7e06e --- /dev/null +++ b/cppcheck-2.14.0/gui/report.cpp @@ -0,0 +1,63 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "report.h" + +#include + +#include + +Report::Report(QString filename) : + mFilename(std::move(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; +} diff --git a/cppcheck-2.14.0/gui/report.h b/cppcheck-2.14.0/gui/report.h new file mode 100644 index 00000000..5cea9be5 --- /dev/null +++ b/cppcheck-2.14.0/gui/report.h @@ -0,0 +1,98 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 + +class ErrorItem; + +/// @addtogroup GUI +/// @{ + +/** + * @brief A base class for reports. + */ +class Report : public QObject { +public: + enum Type { + TXT, + XMLV2, + CSV, + }; + + explicit Report(QString filename); + ~Report() override; + + /** + * @brief Create the report (file). + * @return true if succeeded, false if file could not be created. + */ + virtual bool create(); + + /** + * @brief Open the existing report (file). + * @return true if succeeded, false if file could not be created. + */ + virtual bool open(); + + /** + * @brief Close the report (file). + */ + void close(); + + /** + * @brief Write report header. + */ + virtual void writeHeader() = 0; + + /** + * @brief Write report footer. + */ + virtual void writeFooter() = 0; + + /** + * @brief Write error to report. + * @param error Error data. + */ + virtual void writeError(const ErrorItem &error) = 0; + +protected: + + /** + * @brief Get the file object where the report is written to. + */ + QFile* getFile(); + +private: + + /** + * @brief Filename of the report. + */ + QString mFilename; + + /** + * @brief Fileobject for the report file. + */ + QFile mFile; +}; +/// @} +#endif // REPORT_H diff --git a/cppcheck-2.14.0/gui/resultstree.cpp b/cppcheck-2.14.0/gui/resultstree.cpp new file mode 100644 index 00000000..2ee74f67 --- /dev/null +++ b/cppcheck-2.14.0/gui/resultstree.cpp @@ -0,0 +1,1463 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "resultstree.h" + +#include "application.h" +#include "applicationlist.h" +#include "common.h" +#include "config.h" +#include "erroritem.h" +#include "errorlogger.h" +#include "errortypes.h" +#include "path.h" +#include "projectfile.h" +#include "report.h" +#include "showtypes.h" +#include "suppressions.h" +#include "threadhandler.h" +#include "xmlreportv2.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static constexpr char COLUMN[] = "column"; +static constexpr char CWE[] = "cwe"; +static constexpr char ERRORID[] = "id"; +static constexpr char FILENAME[] = "file"; +static constexpr char FILE0[] = "file0"; +static constexpr char HASH[] = "hash"; +static constexpr char HIDE[] = "hide"; +static constexpr char INCONCLUSIVE[] = "inconclusive"; +static constexpr char LINE[] = "line"; +static constexpr char MESSAGE[] = "message"; +static constexpr char SEVERITY[] = "severity"; +static constexpr char SINCEDATE[] = "sinceDate"; +static constexpr char SYMBOLNAMES[] = "symbolNames"; +static constexpr char SUMMARY[] = "summary"; +static constexpr char TAGS[] = "tags"; + +// These must match column headers given in ResultsTree::translate() +static constexpr int COLUMN_SINCE_DATE = 6; +static constexpr int COLUMN_TAGS = 7; + +ResultsTree::ResultsTree(QWidget * parent) : + QTreeView(parent) +{ + setModel(&mModel); + translate(); // Adds columns to grid + setExpandsOnDoubleClick(false); + setSortingEnabled(true); + + connect(this, &ResultsTree::doubleClicked, this, &ResultsTree::quickStartApplication); +} + +void ResultsTree::keyPressEvent(QKeyEvent *event) +{ + if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) { + quickStartApplication(this->currentIndex()); + } + QTreeView::keyPressEvent(event); +} + +void ResultsTree::initialize(QSettings *settings, ApplicationList *list, ThreadHandler *checkThreadHandler) +{ + mSettings = settings; + mApplications = list; + mThread = checkThreadHandler; + loadSettings(); +} + + +QStandardItem *ResultsTree::createNormalItem(const QString &name) +{ + auto *item = new QStandardItem(name); + item->setData(name, Qt::ToolTipRole); + item->setEditable(false); + return item; +} + +QStandardItem *ResultsTree::createCheckboxItem(bool checked) +{ + auto *item = new QStandardItem; + item->setCheckable(true); + item->setCheckState(checked ? Qt::Checked : Qt::Unchecked); + item->setEnabled(false); + return item; +} + +QStandardItem *ResultsTree::createLineNumberItem(const QString &linenumber) +{ + auto *item = new QStandardItem(); + item->setData(QVariant(linenumber.toInt()), Qt::DisplayRole); + item->setToolTip(linenumber); + item->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); + item->setEditable(false); + return item; +} + +bool ResultsTree::addErrorItem(const ErrorItem &item) +{ + if (item.errorPath.isEmpty()) { + return false; + } + + const QErrorPathItem &loc = item.errorId.startsWith("clang") ? item.errorPath.front() : item.errorPath.back(); + QString realfile = stripPath(loc.file, false); + + if (realfile.isEmpty()) { + realfile = tr("Undefined file"); + } + + bool hide = false; + + // Ids that are temporarily hidden.. + if (mHiddenMessageId.contains(item.errorId)) + hide = true; + + //If specified, filter on summary, message, filename, and id + if (!hide && !mFilter.isEmpty()) { + if (!item.summary.contains(mFilter, Qt::CaseInsensitive) && + !item.message.contains(mFilter, Qt::CaseInsensitive) && + !item.errorPath.back().file.contains(mFilter, Qt::CaseInsensitive) && + !item.errorId.contains(mFilter, Qt::CaseInsensitive)) { + hide = true; + } + } + + //if there is at least one error that is not hidden, we have a visible error + if (!hide) { + mVisibleErrors = true; + } + + ErrorLine line; + line.file = realfile; + line.line = loc.line; + line.errorId = item.errorId; + line.cwe = item.cwe; + line.hash = item.hash; + line.inconclusive = item.inconclusive; + line.summary = item.summary; + line.message = item.message; + line.severity = item.severity; + line.sinceDate = item.sinceDate; + if (const ProjectFile *activeProject = ProjectFile::getActiveProject()) { + line.tags = activeProject->getWarningTags(item.hash); + } + //Create the base item for the error and ensure it has a proper + //file item as a parent + QStandardItem* fileItem = ensureFileItem(loc.file, item.file0, hide); + QStandardItem* stditem = addBacktraceFiles(fileItem, + line, + hide, + severityToIcon(line.severity), + false); + + if (!stditem) + return false; + + //Add user data to that item + QMap data; + data[SEVERITY] = ShowTypes::SeverityToShowType(item.severity); + data[SUMMARY] = item.summary; + data[MESSAGE] = item.message; + data[FILENAME] = loc.file; + data[LINE] = loc.line; + data[COLUMN] = loc.column; + data[ERRORID] = item.errorId; + data[CWE] = item.cwe; + data[HASH] = item.hash; + data[INCONCLUSIVE] = item.inconclusive; + data[FILE0] = stripPath(item.file0, true); + data[SINCEDATE] = item.sinceDate; + data[SYMBOLNAMES] = item.symbolNames; + data[TAGS] = line.tags; + data[HIDE] = hide; + stditem->setData(QVariant(data)); + + //Add backtrace files as children + if (item.errorPath.size() > 1) { + for (int i = 0; i < item.errorPath.size(); i++) { + const QErrorPathItem &e = item.errorPath[i]; + line.file = e.file; + line.line = e.line; + line.message = line.summary = e.info; + QStandardItem *child_item = addBacktraceFiles(stditem, + line, + hide, + ":images/go-down.png", + true); + if (!child_item) + continue; + + // Add user data to that item + QMap child_data; + child_data[SEVERITY] = ShowTypes::SeverityToShowType(line.severity); + child_data[SUMMARY] = line.summary; + child_data[MESSAGE] = line.message; + child_data[FILENAME] = e.file; + child_data[LINE] = e.line; + child_data[COLUMN] = e.column; + child_data[ERRORID] = line.errorId; + child_data[CWE] = line.cwe; + child_data[HASH] = line.hash; + child_data[INCONCLUSIVE] = line.inconclusive; + child_data[SYMBOLNAMES] = item.symbolNames; + child_item->setData(QVariant(child_data)); + } + } + + // Partially refresh the tree: Unhide file item if necessary + if (!hide) { + setRowHidden(fileItem->row(), QModelIndex(), !mShowSeverities.isShown(item.severity)); + } + return true; +} + +QStandardItem *ResultsTree::addBacktraceFiles(QStandardItem *parent, + const ErrorLine &item, + const bool hide, + const QString &icon, + bool childOfMessage) +{ + if (!parent) { + return nullptr; + } + + QList list; + // Ensure shown path is with native separators + list << createNormalItem(QDir::toNativeSeparators(item.file)) + << createNormalItem(childOfMessage ? tr("note") : severityToTranslatedString(item.severity)) + << createLineNumberItem(QString::number(item.line)) + << createNormalItem(childOfMessage ? QString() : item.errorId) + << (childOfMessage ? createNormalItem(QString()) : createCheckboxItem(item.inconclusive)) + << createNormalItem(item.summary) + << createNormalItem(item.sinceDate) + << createNormalItem(item.tags); + + //TODO message has parameter names so we'll need changes to the core + //cppcheck so we can get proper translations + + // Check for duplicate rows and don't add them if found + for (int i = 0; i < parent->rowCount(); i++) { + // The first column is the file name and is always the same + + // the third column is the line number so check it first + if (parent->child(i, 2)->text() == list[2]->text()) { + // the second column is the severity so check it next + if (parent->child(i, 1)->text() == list[1]->text()) { + // the sixth column is the summary so check it last + if (parent->child(i, 5)->text() == list[5]->text()) { + // this row matches so don't add it + return nullptr; + } + } + } + } + + parent->appendRow(list); + + setRowHidden(parent->rowCount() - 1, parent->index(), hide); + + if (!icon.isEmpty()) { + list[0]->setIcon(QIcon(icon)); + } + + /* TODO: the list items leak memory + Indirect leak of 80624 byte(s) in 5039 object(s) allocated from: + #0 0xa15a2d in operator new(unsigned long) (/mnt/s/GitHub/cppcheck-fw/cmake-build-debug-wsl-kali-clang-asan-ubsan/bin/cppcheck-gui+0xa15a2d) + #1 0xdda276 in ResultsTree::createNormalItem(QString const&) /mnt/s/GitHub/cppcheck-fw/gui/resultstree.cpp:122:27 + #2 0xde4290 in ResultsTree::addBacktraceFiles(QStandardItem*, ErrorLine const&, bool, QString const&, bool) /mnt/s/GitHub/cppcheck-fw/gui/resultstree.cpp:289:13 + #3 0xddd754 in ResultsTree::addErrorItem(ErrorItem const&) /mnt/s/GitHub/cppcheck-fw/gui/resultstree.cpp:199:30 + #4 0xe37046 in ResultsView::error(ErrorItem const&) /mnt/s/GitHub/cppcheck-fw/gui/resultsview.cpp:129:21 + #5 0xd2448d in QtPrivate::FunctorCall, QtPrivate::List, void, void (ResultsView::*)(ErrorItem const&)>::call(void (ResultsView::*)(ErrorItem const&), ResultsView*, void**) /usr/include/x86_64-linux-gnu/qt5/QtCore/qobjectdefs_impl.h:152:13 + #6 0xd2402c in void QtPrivate::FunctionPointer::call, void>(void (ResultsView::*)(ErrorItem const&), ResultsView*, void**) /usr/include/x86_64-linux-gnu/qt5/QtCore/qobjectdefs_impl.h:185:13 + #7 0xd23b45 in QtPrivate::QSlotObject, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*) /usr/include/x86_64-linux-gnu/qt5/QtCore/qobjectdefs_impl.h:418:17 + #8 0x7fd2536cc0dd in QObject::event(QEvent*) (/usr/lib/x86_64-linux-gnu/libQt5Core.so.5+0x2dc0dd) + #9 0x7fd2541836be in QApplicationPrivate::notify_helper(QObject*, QEvent*) (/usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5+0x1636be) + */ + + return list[0]; +} + +QString ResultsTree::severityToTranslatedString(Severity 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::internal: + return tr("internal"); + + case Severity::none: + default: + return QString(); + } +} + +QStandardItem *ResultsTree::findFileItem(const QString &name) const +{ + // The first column contains the file name. In Windows we can get filenames + // "header.h" and "Header.h" and must compare them as identical. + + for (int i = 0; i < mModel.rowCount(); i++) { +#ifdef _WIN32 + if (QString::compare(mModel.item(i, 0)->text(), name, Qt::CaseInsensitive) == 0) +#else + if (mModel.item(i, 0)->text() == name) +#endif + return mModel.item(i, 0); + } + return nullptr; +} + +void ResultsTree::clear() +{ + mModel.removeRows(0, mModel.rowCount()); +} + +void ResultsTree::clear(const QString &filename) +{ + const QString stripped = stripPath(filename, false); + + for (int i = 0; i < mModel.rowCount(); ++i) { + const QStandardItem *fileItem = mModel.item(i, 0); + if (!fileItem) + continue; + + QVariantMap data = fileItem->data().toMap(); + if (stripped == data[FILENAME].toString() || + filename == data[FILE0].toString()) { + mModel.removeRow(i); + break; + } + } +} + +void ResultsTree::clearRecheckFile(const QString &filename) +{ + for (int i = 0; i < mModel.rowCount(); ++i) { + const QStandardItem *fileItem = mModel.item(i, 0); + if (!fileItem) + continue; + + QString actualfile((!mCheckPath.isEmpty() && filename.startsWith(mCheckPath)) ? filename.mid(mCheckPath.length() + 1) : filename); + QVariantMap data = fileItem->data().toMap(); + QString storedfile = data[FILENAME].toString(); + storedfile = ((!mCheckPath.isEmpty() && storedfile.startsWith(mCheckPath)) ? storedfile.mid(mCheckPath.length() + 1) : storedfile); + if (actualfile == storedfile) { + mModel.removeRow(i); + break; + } + } +} + + +void ResultsTree::loadSettings() +{ + for (int i = 0; i < mModel.columnCount(); i++) { + QString temp = QString(SETTINGS_RESULT_COLUMN_WIDTH).arg(i); + setColumnWidth(i, qMax(20, mSettings->value(temp, 800 / mModel.columnCount()).toInt())); + } + + mSaveFullPath = mSettings->value(SETTINGS_SAVE_FULL_PATH, false).toBool(); + mSaveAllErrors = mSettings->value(SETTINGS_SAVE_ALL_ERRORS, false).toBool(); + mShowFullPath = mSettings->value(SETTINGS_SHOW_FULL_PATH, false).toBool(); + + showIdColumn(mSettings->value(SETTINGS_SHOW_ERROR_ID, true).toBool()); + showInconclusiveColumn(mSettings->value(SETTINGS_INCONCLUSIVE_ERRORS, false).toBool()); +} + +void ResultsTree::saveSettings() const +{ + for (int i = 0; i < mModel.columnCount(); i++) { + QString temp = QString(SETTINGS_RESULT_COLUMN_WIDTH).arg(i); + mSettings->setValue(temp, columnWidth(i)); + } +} + +void ResultsTree::showResults(ShowTypes::ShowType type, bool show) +{ + if (type != ShowTypes::ShowNone && mShowSeverities.isShown(type) != show) { + mShowSeverities.show(type, show); + refreshTree(); + } +} + +void ResultsTree::showCppcheckResults(bool show) +{ + mShowCppcheck = show; + refreshTree(); +} + +void ResultsTree::showClangResults(bool show) +{ + mShowClang = show; + refreshTree(); +} + +void ResultsTree::filterResults(const QString& filter) +{ + mFilter = filter; + refreshTree(); +} + +void ResultsTree::showHiddenResults() +{ + //Clear the "hide" flag for each item + mHiddenMessageId.clear(); + const int filecount = mModel.rowCount(); + for (int i = 0; i < filecount; i++) { + QStandardItem *fileItem = mModel.item(i, 0); + if (!fileItem) + continue; + + QVariantMap data = fileItem->data().toMap(); + data[HIDE] = false; + fileItem->setData(QVariant(data)); + + const int errorcount = fileItem->rowCount(); + for (int j = 0; j < errorcount; j++) { + QStandardItem *child = fileItem->child(j, 0); + if (child) { + data = child->data().toMap(); + data[HIDE] = false; + child->setData(QVariant(data)); + } + } + } + refreshTree(); + emit resultsHidden(false); +} + + +void ResultsTree::refreshTree() +{ + mVisibleErrors = false; + //Get the amount of files in the tree + const int filecount = mModel.rowCount(); + + for (int i = 0; i < filecount; i++) { + //Get file i + QStandardItem *fileItem = mModel.item(i, 0); + if (!fileItem) { + continue; + } + + //Get the amount of errors this file contains + const int errorcount = fileItem->rowCount(); + + //By default it shouldn't be visible + bool show = false; + + for (int j = 0; j < errorcount; j++) { + //Get the error itself + QStandardItem *child = fileItem->child(j, 0); + if (!child) { + continue; + } + + //Get error's user data + QVariant userdata = child->data(); + //Convert it to QVariantMap + QVariantMap data = userdata.toMap(); + + //Check if this error should be hidden + bool hide = (data[HIDE].toBool() || !mShowSeverities.isShown(ShowTypes::VariantToShowType(data[SEVERITY]))); + + //If specified, filter on summary, message, filename, and id + if (!hide && !mFilter.isEmpty()) { + if (!data[SUMMARY].toString().contains(mFilter, Qt::CaseInsensitive) && + !data[MESSAGE].toString().contains(mFilter, Qt::CaseInsensitive) && + !data[FILENAME].toString().contains(mFilter, Qt::CaseInsensitive) && + !data[ERRORID].toString().contains(mFilter, Qt::CaseInsensitive)) { + hide = true; + } + } + + // Tool filter + if (!hide) { + if (data[ERRORID].toString().startsWith("clang")) + hide = !mShowClang; + else + hide = !mShowCppcheck; + } + + if (!hide) { + mVisibleErrors = true; + } + + //Hide/show accordingly + setRowHidden(j, fileItem->index(), hide); + + //If it was shown then the file itself has to be shown as well + if (!hide) { + show = true; + } + } + + //Hide the file if its "hide" attribute is set + if (fileItem->data().toMap()["hide"].toBool()) { + show = false; + } + + //Show the file if any of it's errors are visible + setRowHidden(i, QModelIndex(), !show); + } +} + +QStandardItem *ResultsTree::ensureFileItem(const QString &fullpath, const QString &file0, bool hide) +{ + QString name = stripPath(fullpath, false); + // Since item has path with native separators we must use path with + // native separators to find it. + QStandardItem *item = findFileItem(QDir::toNativeSeparators(name)); + + if (item) { + return item; + } + + // Ensure shown path is with native separators + name = QDir::toNativeSeparators(name); + item = createNormalItem(name); + item->setIcon(QIcon(":images/text-x-generic.png")); + + //Add user data to that item + QMap data; + data[FILENAME] = fullpath; + data[FILE0] = file0; + item->setData(QVariant(data)); + mModel.appendRow(item); + + setRowHidden(mModel.rowCount() - 1, QModelIndex(), hide); + + return item; +} + +void ResultsTree::contextMenuEvent(QContextMenuEvent * e) +{ + QModelIndex index = indexAt(e->pos()); + if (index.isValid()) { + bool multipleSelection = false; + + mSelectionModel = selectionModel(); + if (mSelectionModel->selectedRows().count() > 1) + multipleSelection = true; + + mContextItem = mModel.itemFromIndex(index); + + //Create a new context menu + QMenu menu(this); + + //Store all applications in a list + QList actions; + + //Create a signal mapper so we don't have to store data to class + //member variables + auto *signalMapper = new QSignalMapper(this); + + if (mContextItem && mApplications->getApplicationCount() > 0 && mContextItem->parent()) { + //Create an action for the application + int defaultApplicationIndex = mApplications->getDefaultApplication(); + if (defaultApplicationIndex < 0) + defaultApplicationIndex = 0; + const Application& app = mApplications->getApplication(defaultApplicationIndex); + auto *start = new QAction(app.getName(), &menu); + if (multipleSelection) + start->setDisabled(true); + + //Add it to our list so we can disconnect later on + actions << start; + + //Add it to context menu + menu.addAction(start); + + //Connect the signal to signal mapper + connect(start, SIGNAL(triggered()), signalMapper, SLOT(map())); + + //Add a new mapping + signalMapper->setMapping(start, defaultApplicationIndex); + + connect(signalMapper, SIGNAL(mapped(int)), + this, SLOT(context(int))); + } + + // Add popup menuitems + if (mContextItem) { + if (mApplications->getApplicationCount() > 0) { + menu.addSeparator(); + } + + //Create an action for the application + auto *recheckAction = new QAction(tr("Recheck"), &menu); + auto *copyAction = new QAction(tr("Copy"), &menu); + auto *hide = new QAction(tr("Hide"), &menu); + auto *hideallid = new QAction(tr("Hide all with id"), &menu); + auto *opencontainingfolder = new QAction(tr("Open containing folder"), &menu); + + if (multipleSelection) { + hideallid->setDisabled(true); + opencontainingfolder->setDisabled(true); + } + if (mThread->isChecking()) + recheckAction->setDisabled(true); + else + recheckAction->setDisabled(false); + + menu.addAction(recheckAction); + menu.addSeparator(); + menu.addAction(copyAction); + menu.addSeparator(); + menu.addAction(hide); + menu.addAction(hideallid); + + auto *suppress = new QAction(tr("Suppress selected id(s)"), &menu); + { + QVariantMap data = mContextItem->data().toMap(); + const QString messageId = data[ERRORID].toString(); + suppress->setEnabled(!ErrorLogger::isCriticalErrorId(messageId.toStdString())); + } + menu.addAction(suppress); + connect(suppress, &QAction::triggered, this, &ResultsTree::suppressSelectedIds); + + menu.addSeparator(); + menu.addAction(opencontainingfolder); + + connect(recheckAction, SIGNAL(triggered()), this, SLOT(recheckAction())); + connect(copyAction, SIGNAL(triggered()), this, SLOT(copyAction())); + connect(hide, SIGNAL(triggered()), this, SLOT(hideResult())); + connect(hideallid, SIGNAL(triggered()), this, SLOT(hideAllIdResult())); + connect(opencontainingfolder, SIGNAL(triggered()), this, SLOT(openContainingFolder())); + + const ProjectFile *currentProject = ProjectFile::getActiveProject(); + if (currentProject && !currentProject->getTags().isEmpty()) { + menu.addSeparator(); + QMenu *tagMenu = menu.addMenu(tr("Tag")); + { + auto *action = new QAction(tr("No tag"), tagMenu); + tagMenu->addAction(action); + connect(action, &QAction::triggered, [=]() { + tagSelectedItems(QString()); + }); + } + + for (const QString& tagstr : currentProject->getTags()) { + auto *action = new QAction(tagstr, tagMenu); + tagMenu->addAction(action); + connect(action, &QAction::triggered, [=]() { + tagSelectedItems(tagstr); + }); + } + } + } + + //Start the menu + menu.exec(e->globalPos()); + index = indexAt(e->pos()); + if (index.isValid()) { + mContextItem = mModel.itemFromIndex(index); + if (mContextItem && mApplications->getApplicationCount() > 0 && mContextItem->parent()) { + //Disconnect all signals + for (const QAction* action : actions) { + disconnect(action, SIGNAL(triggered()), signalMapper, SLOT(map())); + } + + disconnect(signalMapper, SIGNAL(mapped(int)), + this, SLOT(context(int))); + //And remove the signal mapper + delete signalMapper; + } + } + } +} + +void ResultsTree::startApplication(const 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[FILENAME].toString(); + file = QDir::toNativeSeparators(file); + qDebug() << "Opening file: " << file; + + QFileInfo info(file); + if (!info.exists()) { + if (info.isAbsolute()) { + QMessageBox msgbox(this); + msgbox.setWindowTitle("Cppcheck"); + msgbox.setText(tr("Could not find the file!")); + msgbox.setIcon(QMessageBox::Critical); + msgbox.exec(); + } else { + QDir checkdir(mCheckPath); + if (checkdir.isAbsolute() && checkdir.exists()) { + file = mCheckPath + "/" + file; + } else { + QString dir = askFileDir(file); + dir += '/'; + file = dir + file; + } + } + } + + if (file.indexOf(" ") > -1) { + file.insert(0, "\""); + file.append("\""); + } + + const Application& app = mApplications->getApplication(application); + QString params = app.getParameters(); + params.replace("(file)", file, Qt::CaseInsensitive); + + QVariant line = data[LINE]; + params.replace("(line)", QString("%1").arg(line.toInt()), Qt::CaseInsensitive); + + params.replace("(message)", data[MESSAGE].toString(), Qt::CaseInsensitive); + params.replace("(severity)", data[SEVERITY].toString(), Qt::CaseInsensitive); + + QString program = app.getPath(); + + // In Windows we must surround paths including spaces with quotation marks. +#ifdef Q_OS_WIN + if (program.indexOf(" ") > -1) { + if (!program.startsWith('"') && !program.endsWith('"')) { + program.insert(0, "\""); + program.append("\""); + } + } +#endif // Q_OS_WIN + + const QString cmdLine = QString("%1 %2").arg(program).arg(params); + + // this is reported as deprecated in Qt 5.15.2 but no longer in Qt 6 +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + SUPPRESS_WARNING_CLANG_PUSH("-Wdeprecated") + SUPPRESS_WARNING_GCC_PUSH("-Wdeprecated-declarations") +#endif + const bool success = QProcess::startDetached(cmdLine); +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) + SUPPRESS_WARNING_GCC_POP + SUPPRESS_WARNING_CLANG_POP +#endif + if (!success) { + QString text = tr("Could not start %1\n\nPlease check the application path and parameters are correct.").arg(program); + + QMessageBox msgbox(this); + msgbox.setWindowTitle("Cppcheck"); + msgbox.setText(text); + msgbox.setIcon(QMessageBox::Critical); + + msgbox.exec(); + } + } +} + +QString ResultsTree::askFileDir(const QString &file) +{ + QString text = tr("Could not find file:") + '\n' + file + '\n'; + QString title; + if (file.indexOf('/')) { + QString folderName = file.mid(0, file.indexOf('/')); + text += tr("Please select the folder '%1'").arg(folderName); + title = tr("Select Directory '%1'").arg(folderName); + } else { + text += tr("Please select the directory where file is located."); + title = tr("Select Directory"); + } + + QMessageBox msgbox(this); + msgbox.setWindowTitle("Cppcheck"); + msgbox.setText(text); + msgbox.setIcon(QMessageBox::Warning); + msgbox.exec(); + + QString dir = QFileDialog::getExistingDirectory(this, title, + getPath(SETTINGS_LAST_SOURCE_PATH), + QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + + if (dir.isEmpty()) + return QString(); + + // User selected root path + if (QFileInfo::exists(dir + '/' + file)) + mCheckPath = dir; + + // user selected checked folder + else if (file.indexOf('/') > 0) { + dir += '/'; + QString folderName = file.mid(0, file.indexOf('/')); + if (dir.indexOf('/' + folderName + '/')) + dir = dir.mid(0, dir.lastIndexOf('/' + folderName + '/')); + if (QFileInfo::exists(dir + '/' + file)) + mCheckPath = dir; + } + + // Otherwise; return + else + return QString(); + + setPath(SETTINGS_LAST_SOURCE_PATH, mCheckPath); + return mCheckPath; +} + +void ResultsTree::copy() +{ + if (!mSelectionModel) + return; + + QString text; + for (QModelIndex index : mSelectionModel->selectedRows()) { + QStandardItem *item = mModel.itemFromIndex(index); + if (!item->parent()) { + text += item->text() + '\n'; + continue; + } + if (item->parent()->parent()) + item = item->parent(); + QVariantMap data = item->data().toMap(); + if (!data.contains("id")) + continue; + QString inconclusive = data[INCONCLUSIVE].toBool() ? ",inconclusive" : ""; + text += '[' + data[FILENAME].toString() + ':' + QString::number(data[LINE].toInt()) + + "] (" + + QString::fromStdString(severityToString(ShowTypes::ShowTypeToSeverity((ShowTypes::ShowType)data[SEVERITY].toInt()))) + inconclusive + + ") " + + data[MESSAGE].toString() + + " [" + + data[ERRORID].toString() + + "]\n"; + } + + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(text); +} + +void ResultsTree::hideResult() +{ + if (!mSelectionModel) + return; + + for (QModelIndex index : mSelectionModel->selectedRows()) { + QStandardItem *item = mModel.itemFromIndex(index); + //Set the "hide" flag for this item + QVariantMap data = item->data().toMap(); + data[HIDE] = true; + item->setData(QVariant(data)); + + refreshTree(); + emit resultsHidden(true); + } +} + +void ResultsTree::recheckSelectedFiles() +{ + if (!mSelectionModel) + return; + + QStringList selectedItems; + for (QModelIndex index : mSelectionModel->selectedRows()) { + QStandardItem *item = mModel.itemFromIndex(index); + while (item->parent()) + item = item->parent(); + QVariantMap data = item->data().toMap(); + QString currentFile = data[FILENAME].toString(); + if (!currentFile.isEmpty()) { + QString fileNameWithCheckPath; + QFileInfo curfileInfo(currentFile); + if (!curfileInfo.exists() && !mCheckPath.isEmpty() && currentFile.indexOf(mCheckPath) != 0) + fileNameWithCheckPath = mCheckPath + "/" + currentFile; + else + fileNameWithCheckPath = currentFile; + const QFileInfo fileInfo(fileNameWithCheckPath); + if (!fileInfo.exists()) { + askFileDir(currentFile); + return; + } + if (Path::isHeader2(currentFile.toStdString())) { + if (!data[FILE0].toString().isEmpty() && !selectedItems.contains(data[FILE0].toString())) { + selectedItems<<((!mCheckPath.isEmpty() && (data[FILE0].toString().indexOf(mCheckPath) != 0)) ? (mCheckPath + "/" + data[FILE0].toString()) : data[FILE0].toString()); + if (!selectedItems.contains(fileNameWithCheckPath)) + selectedItems<parent()) + return; + + // Make sure we are working with the first column + if (mContextItem->column() != 0) + mContextItem = mContextItem->parent()->child(mContextItem->row(), 0); + QVariantMap data = mContextItem->data().toMap(); + + QString messageId = data[ERRORID].toString(); + + mHiddenMessageId.append(messageId); + + // hide all errors with that message Id + const 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 + const 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[ERRORID].toString() == messageId) { + userdata[HIDE] = true; + child->setData(QVariant(userdata)); + } + } + } + + refreshTree(); + emit resultsHidden(true); +} + +void ResultsTree::suppressSelectedIds() +{ + if (!mSelectionModel) + return; + + QSet selectedIds; + for (QModelIndex index : mSelectionModel->selectedRows()) { + QStandardItem *item = mModel.itemFromIndex(index); + if (!item->parent()) + continue; + if (item->parent()->parent()) + item = item->parent(); + QVariantMap data = item->data().toMap(); + if (!data.contains("id")) + continue; + selectedIds << data[ERRORID].toString(); + } + + // delete all errors with selected message Ids + for (int i = 0; i < mModel.rowCount(); i++) { + QStandardItem * const file = mModel.item(i, 0); + for (int j = 0; j < file->rowCount();) { + QStandardItem *errorItem = file->child(j, 0); + QVariantMap userdata = errorItem->data().toMap(); + if (selectedIds.contains(userdata[ERRORID].toString())) { + file->removeRow(j); + } else { + j++; + } + } + if (file->rowCount() == 0) + mModel.removeRow(file->row()); + } + + + emit suppressIds(selectedIds.values()); +} + +void ResultsTree::suppressHash() +{ + if (!mSelectionModel) + return; + + // Extract selected warnings + QSet selectedWarnings; + for (QModelIndex index : mSelectionModel->selectedRows()) { + QStandardItem *item = mModel.itemFromIndex(index); + if (!item->parent()) + continue; + while (item->parent()->parent()) + item = item->parent(); + selectedWarnings.insert(item); + } + + bool changed = false; + ProjectFile *projectFile = ProjectFile::getActiveProject(); + for (QStandardItem *item: selectedWarnings) { + QStandardItem *fileItem = item->parent(); + const QVariantMap data = item->data().toMap(); + if (projectFile && data.contains(HASH)) { + SuppressionList::Suppression suppression; + suppression.hash = data[HASH].toULongLong(); + suppression.errorId = data[ERRORID].toString().toStdString(); + suppression.fileName = data[FILENAME].toString().toStdString(); + suppression.lineNumber = data[LINE].toInt(); + projectFile->addSuppression(suppression); + changed = true; + } + fileItem->removeRow(item->row()); + if (fileItem->rowCount() == 0) + mModel.removeRow(fileItem->row()); + } + + if (changed) + projectFile->write(); +} + +void ResultsTree::openContainingFolder() +{ + QString filePath = getFilePath(mContextItem, true); + if (!filePath.isEmpty()) { + filePath = QFileInfo(filePath).absolutePath(); + QDesktopServices::openUrl(QUrl::fromLocalFile(filePath)); + } +} + +void ResultsTree::tagSelectedItems(const QString &tag) +{ + if (!mSelectionModel) + return; + bool isTagged = false; + ProjectFile *currentProject = ProjectFile::getActiveProject(); + for (QModelIndex index : mSelectionModel->selectedRows()) { + QStandardItem *item = mModel.itemFromIndex(index); + QVariantMap data = item->data().toMap(); + if (data.contains("tags")) { + data[TAGS] = tag; + item->setData(QVariant(data)); + item->parent()->child(index.row(), COLUMN_TAGS)->setText(tag); + if (currentProject && data.contains(HASH)) { + isTagged = true; + currentProject->setWarningTags(data[HASH].toULongLong(), tag); + } + } + } + if (isTagged) + currentProject->write(); +} + +void ResultsTree::context(int application) +{ + startApplication(mContextItem, application); +} + +void ResultsTree::quickStartApplication(const QModelIndex &index) +{ + startApplication(mModel.itemFromIndex(index)); +} + +QString ResultsTree::getFilePath(const 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(); + + //Replace (file) with filename + QString file = data[FILENAME].toString(); + QString pathStr = QDir::toNativeSeparators(file); + if (!fullPath) { + QFileInfo fi(pathStr); + pathStr = fi.fileName(); + } + + return pathStr; + } + + return QString(); +} + +QString ResultsTree::severityToIcon(Severity severity) +{ + switch (severity) { + case Severity::error: + return ":images/dialog-error.png"; + case Severity::style: + return ":images/applications-development.png"; + case Severity::warning: + return ":images/dialog-warning.png"; + case Severity::portability: + return ":images/applications-system.png"; + case Severity::performance: + return ":images/utilities-system-monitor.png"; + case Severity::information: + return ":images/dialog-information.png"; + default: + return QString(); + } +} + +void ResultsTree::saveResults(Report *report) const +{ + report->writeHeader(); + + for (int i = 0; i < mModel.rowCount(); i++) { + if (mSaveAllErrors || !isRowHidden(i, QModelIndex())) + saveErrors(report, mModel.item(i, 0)); + } + + report->writeFooter(); +} + +void ResultsTree::saveErrors(Report *report, const QStandardItem *fileItem) const +{ + if (!fileItem) { + return; + } + + for (int i = 0; i < fileItem->rowCount(); i++) { + const QStandardItem *error = fileItem->child(i, 0); + + if (!error) { + continue; + } + + if (isRowHidden(i, fileItem->index()) && !mSaveAllErrors) { + continue; + } + + ErrorItem item; + readErrorItem(error, &item); + + report->writeError(item); + } +} + +static int indexOf(const QList &list, const ErrorItem &item) +{ + for (int i = 0; i < list.size(); i++) { + if (ErrorItem::sameCID(item, list[i])) { + return i; + } + } + return -1; +} + +void ResultsTree::updateFromOldReport(const QString &filename) +{ + QList oldErrors; + XmlReportV2 oldReport(filename, QString()); + if (oldReport.open()) { + oldErrors = oldReport.read(); + oldReport.close(); + } + + // Read current results.. + for (int i = 0; i < mModel.rowCount(); i++) { + QStandardItem *fileItem = mModel.item(i,0); + for (int j = 0; j < fileItem->rowCount(); j++) { + QStandardItem *error = fileItem->child(j,0); + ErrorItem errorItem; + readErrorItem(error, &errorItem); + const int oldErrorIndex = indexOf(oldErrors, errorItem); + QVariantMap data = error->data().toMap(); + + // New error .. set the "sinceDate" property + if (oldErrorIndex >= 0 && !oldErrors[oldErrorIndex].sinceDate.isEmpty()) { + data[SINCEDATE] = oldErrors[oldErrorIndex].sinceDate; + error->setData(data); + fileItem->child(j, COLUMN_SINCE_DATE)->setText(oldErrors[oldErrorIndex].sinceDate); + } else if (oldErrorIndex < 0 || data[SINCEDATE].toString().isEmpty()) { + const QString sinceDate = QLocale::system().toString(QDate::currentDate(), QLocale::ShortFormat); + data[SINCEDATE] = sinceDate; + error->setData(data); + fileItem->child(j, COLUMN_SINCE_DATE)->setText(sinceDate); + if (oldErrorIndex < 0) + continue; + } + + if (!errorItem.tags.isEmpty()) + continue; + + const ErrorItem &oldErrorItem = oldErrors[oldErrorIndex]; + data[TAGS] = oldErrorItem.tags; + error->setData(data); + } + } +} + +void ResultsTree::readErrorItem(const QStandardItem *error, ErrorItem *item) const +{ + // Get error's user data + QVariantMap data = error->data().toMap(); + + item->severity = ShowTypes::ShowTypeToSeverity(ShowTypes::VariantToShowType(data[SEVERITY])); + item->summary = data[SUMMARY].toString(); + item->message = data[MESSAGE].toString(); + item->errorId = data[ERRORID].toString(); + item->cwe = data[CWE].toInt(); + item->hash = data[HASH].toULongLong(); + item->inconclusive = data[INCONCLUSIVE].toBool(); + item->file0 = data[FILE0].toString(); + item->sinceDate = data[SINCEDATE].toString(); + item->tags = data[TAGS].toString(); + + if (error->rowCount() == 0) { + QErrorPathItem e; + e.file = stripPath(data[FILENAME].toString(), true); + e.line = data[LINE].toInt(); + e.info = data[MESSAGE].toString(); + item->errorPath << e; + } + + for (int j = 0; j < error->rowCount(); j++) { + const QStandardItem *child_error = error->child(j, 0); + //Get error's user data + QVariant child_userdata = child_error->data(); + //Convert it to QVariantMap + QVariantMap child_data = child_userdata.toMap(); + + QErrorPathItem e; + e.file = stripPath(child_data[FILENAME].toString(), true); + e.line = child_data[LINE].toInt(); + e.info = child_data[MESSAGE].toString(); + item->errorPath << e; + } +} + +void ResultsTree::updateSettings(bool showFullPath, + bool saveFullPath, + bool saveAllErrors, + bool showErrorId, + bool showInconclusive) +{ + if (mShowFullPath != showFullPath) { + mShowFullPath = showFullPath; + refreshFilePaths(); + } + + mSaveFullPath = saveFullPath; + mSaveAllErrors = saveAllErrors; + + showIdColumn(showErrorId); + showInconclusiveColumn(showInconclusive); +} + +void ResultsTree::setCheckDirectory(const QString &dir) +{ + mCheckPath = dir; +} + + +const QString& ResultsTree::getCheckDirectory() +{ + return mCheckPath; +} + +QString ResultsTree::stripPath(const QString &path, bool saving) const +{ + if ((!saving && mShowFullPath) || (saving && mSaveFullPath)) { + return QString(path); + } + + QDir dir(mCheckPath); + return dir.relativeFilePath(path); +} + +void ResultsTree::refreshFilePaths(QStandardItem *item) +{ + if (!item) { + return; + } + + //Mark that this file's path hasn't been updated yet + bool updated = false; + + //Loop through all errors within this file + for (int i = 0; i < item->rowCount(); i++) { + //Get error i + QStandardItem *error = item->child(i, 0); + + if (!error) { + continue; + } + + //Get error's user data + QVariant userdata = error->data(); + //Convert it to QVariantMap + QVariantMap data = userdata.toMap(); + + //Get list of files + QString file = data[FILENAME].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[FILENAME].toString(); + //Update file's path + child->setText(stripPath(child_files, false)); + } + } + + //if the main file hasn't been updated yet, update it now + if (!updated) { + updated = true; + item->setText(error->text()); + } + + } +} + +void ResultsTree::refreshFilePaths() +{ + qDebug("Refreshing file paths"); + + //Go through all file items (these are parent items that contain the errors) + for (int i = 0; i < mModel.rowCount(); i++) { + refreshFilePaths(mModel.item(i, 0)); + } +} + +bool ResultsTree::hasVisibleResults() const +{ + return mVisibleErrors; +} + +bool ResultsTree::hasResults() const +{ + return mModel.rowCount() > 0; +} + +void ResultsTree::translate() +{ + QStringList labels; + labels << tr("File") << tr("Severity") << tr("Line") << tr("Id") << tr("Inconclusive") << tr("Summary") << tr("Since date") << tr("Tag"); + mModel.setHorizontalHeaderLabels(labels); + //TODO go through all the errors in the tree and translate severity and message +} + +void ResultsTree::showIdColumn(bool show) +{ + mShowErrorId = show; + if (show) + showColumn(3); + else + hideColumn(3); +} + +void ResultsTree::showInconclusiveColumn(bool show) +{ + if (show) + showColumn(4); + else + hideColumn(4); +} + +void ResultsTree::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + QTreeView::currentChanged(current, previous); + emit treeSelectionChanged(current); +} diff --git a/cppcheck-2.14.0/gui/resultstree.h b/cppcheck-2.14.0/gui/resultstree.h new file mode 100644 index 00000000..7819fef2 --- /dev/null +++ b/cppcheck-2.14.0/gui/resultstree.h @@ -0,0 +1,528 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "showtypes.h" + +#include +#include +#include +#include +#include + +class ApplicationList; +class Report; +class ErrorItem; +class ErrorLine; +class QModelIndex; +class QWidget; +class QItemSelectionModel; +class ThreadHandler; +class QContextMenuEvent; +class QKeyEvent; +class QSettings; +enum class Severity; + +/// @addtogroup GUI +/// @{ + + +/** + * @brief Cppcheck's results are shown in this tree + * + */ +class ResultsTree : public QTreeView { + Q_OBJECT +public: + explicit ResultsTree(QWidget * parent = nullptr); + + void initialize(QSettings *settings, ApplicationList *list, ThreadHandler *checkThreadHandler); + + /** + * @brief Add a new item to the tree + * + * @param item Error item data + */ + bool addErrorItem(const ErrorItem &item); + + /** + * @brief Clear all errors from the tree + * + */ + void clear(); + + /** + * @brief Clear errors for a specific file from the tree + */ + void clear(const QString &filename); + + /** + * @brief Clear errors of a file selected for recheck + */ + void clearRecheckFile(const QString &filename); + + /** + * @brief Function to filter the displayed list of errors. + * Refreshes the tree. + * + * @param filter String that must be found in the summary, description, file or id + */ + void filterResults(const QString& filter); + + /** + * @brief Function to show results that were previous hidden with HideResult() + */ + void showHiddenResults(); + + /** + * @brief Refresh tree by checking which of the items should be shown + * and which should be hidden + */ + void refreshTree(); + + /** + * @brief Save results to a text stream + * + */ + void saveResults(Report *report) const; + + /** + * @brief Update items from old report (tag, sinceDate) + */ + void updateFromOldReport(const QString &filename); + + /** + * @brief Update tree settings + * + * @param showFullPath Show full path of files in the tree + * @param saveFullPath Save full path of files in reports + * @param saveAllErrors Save all visible errors + * @param showErrorId Show error id + * @param showInconclusive Show inconclusive column + */ + void updateSettings(bool showFullPath, bool saveFullPath, bool saveAllErrors, bool showErrorId, bool showInconclusive); + + /** + * @brief Set the directory we are checking + * + * This is used to split error file path to relative if necessary + * @param dir Directory we are checking + */ + void setCheckDirectory(const QString &dir); + + /** + * @brief Get the directory we are checking + * + * @return Directory containing source files + */ + + const QString& getCheckDirectory(); + + /** + * @brief Check if there are any visible results in view. + * @return true if there is at least one visible warning/error. + */ + bool hasVisibleResults() const; + + /** + * @brief Do we have results from check? + * @return true if there is at least one warning/error, hidden or visible. + */ + bool hasResults() const; + + /** + * @brief Save all settings + * Column widths + */ + void saveSettings() const; + + /** + * @brief Change all visible texts language + * + */ + void translate(); + + /** + * @brief Show optional column "Id" + */ + void showIdColumn(bool show); + + /** + * @brief Show optional column "Inconclusve" + */ + void showInconclusiveColumn(bool show); + + /** + * @brief Returns true if column "Id" is shown + */ + bool showIdColumn() const { + return mShowErrorId; + } + + /** + * @brief GUI severities. + */ + ShowTypes mShowSeverities; + + void keyPressEvent(QKeyEvent *event) override; + +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 + */ + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void resultsHidden(bool hidden); + + /** + * @brief Signal to perform selected files recheck + * + * @param selectedItems list of selected files + */ + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void checkSelected(QStringList selectedItems); + + /** + * @brief Signal for selection change in result tree. + * + * @param current Model index to specify new selected item. + */ + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void treeSelectionChanged(const QModelIndex ¤t); + + /** Suppress Ids */ + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void suppressIds(QStringList ids); + +public slots: + + /** + * @brief Function to show/hide certain type of errors + * Refreshes the tree. + * + * @param type Type of error to show/hide + * @param show Should specified errors be shown (true) or hidden (false) + */ + void showResults(ShowTypes::ShowType type, bool show); + + + /** + * @brief Show/hide cppcheck errors. + * Refreshes the tree. + * + * @param show Should specified errors be shown (true) or hidden (false) + */ + void showCppcheckResults(bool show); + + /** + * @brief Show/hide clang-tidy/clang-analyzer errors. + * Refreshes the tree. + * + * @param show Should specified errors be shown (true) or hidden (false) + */ + void showClangResults(bool show); + +protected slots: + /** + * @brief Slot to quickstart an error with default application + * + * @param index Model index to specify which error item to open + */ + void quickStartApplication(const QModelIndex &index); + + /** + * @brief Slot for context menu item to open an error with specified application + * + * @param application Index of the application to open the error + */ + void context(int application); + + /** + * @brief Slot for context menu item to copy selection to clipboard + */ + void copy(); + + /** + * @brief Slot for context menu item to hide the current error message + * + */ + void hideResult(); + + /** + * @brief Slot for rechecking selected files + * + */ + void recheckSelectedFiles(); + + /** + * @brief Slot for context menu item to hide all messages with the current message Id + * + */ + void hideAllIdResult(); + + /** Slot for context menu item to suppress all messages with the current message id */ + void suppressSelectedIds(); + + /** Slot for context menu item to suppress message with hash */ + void suppressHash(); + + /** + * @brief Slot for context menu item to open the folder containing the current file. + */ + void openContainingFolder(); + + /** + * @brief Slot for selection change in the results tree. + * + * @param current Model index to specify new selected item. + * @param previous Model index to specify previous selected item. + */ + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous) override; + +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 children's paths to change + */ + void refreshFilePaths(QStandardItem *item); + + + /** + * @brief Removes checking directory from given path if mShowFullPath is false + * + * @param path Path to remove checking directory + * @param saving are we saving? Check mSaveFullPath instead + * @return Path that has checking directory removed + */ + QString stripPath(const QString &path, bool saving) const; + + + /** + * @brief Save all errors under specified item + * @param report Report that errors are saved to + * @param fileItem Item whose errors to save + */ + void saveErrors(Report *report, const QStandardItem *fileItem) const; + + /** + * @brief Convert a severity string to a icon filename + * + * @param severity Severity + */ + static QString severityToIcon(Severity severity); + + /** + * @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(const QStandardItem *target, int application = -1); + + /** + * @brief Helper function returning the filename/full path of the error tree item \a target. + * + * @param target The error tree item containing the filename/full path + * @param fullPath Whether or not to retrieve the full path or only the filename. + */ + static QString getFilePath(const QStandardItem *target, bool fullPath); + + /** + * @brief Context menu event (user right clicked on the tree) + * + * @param e Event + */ + void contextMenuEvent(QContextMenuEvent * e) override; + + /** + * @brief Add a new error item beneath a file or a backtrace item beneath an error + * + * @param parent Parent for the item. Either a file item or an error item + * @param item Error line data + * @param hide Should this be hidden (true) or shown (false) + * @param icon Should a default backtrace item icon be added + * @param childOfMessage Is this a child element of a message? + * @return newly created QStandardItem * + */ + QStandardItem *addBacktraceFiles(QStandardItem *parent, + const ErrorLine &item, + const bool hide, + const QString &icon, + bool childOfMessage); + + /** + * @brief Convert Severity to translated string for GUI. + * @param severity Severity to convert + * @return Severity as translated string + */ + static QString severityToTranslatedString(Severity severity); + + /** + * @brief Load all settings + * Column widths + */ + void loadSettings(); + + /** + * @brief Ask directory where file is located. + * @param file File name. + * @return Directory user chose. + */ + QString askFileDir(const QString &file); + + /** + * @brief Create new normal item. + * + * Normal item has left alignment and text set also as tooltip. + * @param name name for the item + * @return new QStandardItem + */ + static QStandardItem *createNormalItem(const QString &name); + + /** + * @brief Create new normal item. + * + * Normal item has left alignment and text set also as tooltip. + * @param checked checked + * @return new QStandardItem + */ + static QStandardItem *createCheckboxItem(bool checked); + + /** + * @brief Create new line number item. + * + * Line number item has right align and text set as tooltip. + * @param linenumber name for the item + * @return new QStandardItem + */ + static QStandardItem *createLineNumberItem(const QString &linenumber); + + /** + * @brief Finds a file item + * + * @param name name of the file item to find + * @return pointer to file item or null if none found + */ + QStandardItem *findFileItem(const QString &name) const; + + + /** + * @brief Ensures there's a item in the model for the specified file + * + * @param fullpath Full path to the file item. + * @param file0 Source file + * @param hide is the error (we want this file item for) hidden? + * @return QStandardItem to be used as a parent for all errors for specified file + */ + QStandardItem *ensureFileItem(const QString &fullpath, const QString &file0, bool hide); + + /** + * @brief Item model for tree + * + */ + QStandardItemModel mModel; + + /** + * @brief Program settings + * + */ + QSettings* mSettings{}; + + /** + * @brief A string used to filter the results for display. + * + */ + QString mFilter; + + /** + * @brief List of applications to open errors with + * + */ + ApplicationList* mApplications{}; + + /** + * @brief Right clicked item (used by context menu slots) + * + */ + QStandardItem* mContextItem{}; + + /** + * @brief Should full path of files be shown (true) or relative (false) + * + */ + bool mShowFullPath{}; + + /** + * @brief Should full path of files be saved + * + */ + bool mSaveFullPath{}; + + /** + * @brief Save all errors (true) or only visible (false) + * + */ + bool mSaveAllErrors = true; + + /** + * @brief true if optional column "Id" is shown + * + */ + bool mShowErrorId{}; + + /** + * @brief Path we are currently checking + * + */ + QString mCheckPath; + + /** + * @brief Are there any visible errors + * + */ + bool mVisibleErrors{}; + +private: + /** tag selected items */ + void tagSelectedItems(const QString &tag); + + /** @brief Convert GUI error item into data error item */ + void readErrorItem(const QStandardItem *error, ErrorItem *item) const; + + QStringList mHiddenMessageId; + + QItemSelectionModel* mSelectionModel{}; + ThreadHandler *mThread{}; + + bool mShowCppcheck = true; + bool mShowClang = true; +}; +/// @} +#endif // RESULTSTREE_H diff --git a/cppcheck-2.14.0/gui/resultsview.cpp b/cppcheck-2.14.0/gui/resultsview.cpp new file mode 100644 index 00000000..2fb4f7a3 --- /dev/null +++ b/cppcheck-2.14.0/gui/resultsview.cpp @@ -0,0 +1,585 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "resultsview.h" + +#include "checkstatistics.h" +#include "checkersreport.h" +#include "codeeditor.h" +#include "codeeditorstyle.h" +#include "common.h" +#include "csvreport.h" +#include "erroritem.h" +#include "errorlogger.h" +#include "errortypes.h" +#include "path.h" +#include "printablereport.h" +#include "resultstree.h" +#include "settings.h" +#include "txtreport.h" +#include "xmlreport.h" +#include "xmlreportv2.h" + +#include "ui_resultsview.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ResultsView::ResultsView(QWidget * parent) : + QWidget(parent), + mUI(new Ui::ResultsView), + mStatistics(new CheckStatistics(this)) +{ + mUI->setupUi(this); + + connect(mUI->mTree, &ResultsTree::resultsHidden, this, &ResultsView::resultsHidden); + connect(mUI->mTree, &ResultsTree::checkSelected, this, &ResultsView::checkSelected); + connect(mUI->mTree, &ResultsTree::treeSelectionChanged, this, &ResultsView::updateDetails); + connect(mUI->mTree, &ResultsTree::suppressIds, this, &ResultsView::suppressIds); + connect(this, &ResultsView::showResults, mUI->mTree, &ResultsTree::showResults); + connect(this, &ResultsView::showCppcheckResults, mUI->mTree, &ResultsTree::showCppcheckResults); + connect(this, &ResultsView::showClangResults, mUI->mTree, &ResultsTree::showClangResults); + connect(this, &ResultsView::collapseAllResults, mUI->mTree, &ResultsTree::collapseAll); + connect(this, &ResultsView::expandAllResults, mUI->mTree, &ResultsTree::expandAll); + connect(this, &ResultsView::showHiddenResults, mUI->mTree, &ResultsTree::showHiddenResults); + + mUI->mListLog->setContextMenuPolicy(Qt::CustomContextMenu); +} + +void ResultsView::initialize(QSettings *settings, ApplicationList *list, ThreadHandler *checkThreadHandler) +{ + mUI->mProgress->setMinimum(0); + mUI->mProgress->setVisible(false); + mUI->mLabelCriticalErrors->setVisible(false); + + CodeEditorStyle theStyle(CodeEditorStyle::loadSettings(settings)); + mUI->mCode->setStyle(theStyle); + + QByteArray state = settings->value(SETTINGS_MAINWND_SPLITTER_STATE).toByteArray(); + mUI->mVerticalSplitter->restoreState(state); + mShowNoErrorsMessage = settings->value(SETTINGS_SHOW_NO_ERRORS, true).toBool(); + + mUI->mTree->initialize(settings, list, checkThreadHandler); +} + +ResultsView::~ResultsView() +{ + delete mUI; + delete mCheckSettings; +} + +void ResultsView::clear(bool results) +{ + if (results) { + mUI->mTree->clear(); + } + + mUI->mDetails->setText(QString()); + + mStatistics->clear(); + delete mCheckSettings; + mCheckSettings = nullptr; + + //Clear the progressbar + mUI->mProgress->setMaximum(PROGRESS_MAX); + mUI->mProgress->setValue(0); + mUI->mProgress->setFormat("%p%"); + + mUI->mLabelCriticalErrors->setVisible(false); + + mSuccess = false; +} + +void ResultsView::clear(const QString &filename) +{ + mUI->mTree->clear(filename); +} + +void ResultsView::clearRecheckFile(const QString &filename) +{ + mUI->mTree->clearRecheckFile(filename); +} + +const ShowTypes & ResultsView::getShowTypes() const +{ + return mUI->mTree->mShowSeverities; +} + +void ResultsView::progress(int value, const QString& description) +{ + mUI->mProgress->setValue(value); + mUI->mProgress->setFormat(QString("%p% (%1)").arg(description)); +} + +void ResultsView::error(const ErrorItem &item) +{ + if (item.severity == Severity::internal && (item.errorId == "logChecker" || item.errorId.endsWith("-logChecker"))) { + mStatistics->addChecker(item.message); + return; + } + + handleCriticalError(item); + + if (item.severity == Severity::internal) + return; + + if (mUI->mTree->addErrorItem(item)) { + emit gotResults(); + mStatistics->addItem(item.tool(), ShowTypes::SeverityToShowType(item.severity)); + } +} + +void ResultsView::filterResults(const QString& filter) +{ + mUI->mTree->filterResults(filter); +} + +void ResultsView::saveStatistics(const QString &filename) const +{ + QFile f(filename); + if (!f.open(QIODevice::Text | QIODevice::Append)) + return; + QTextStream ts(&f); + ts << '[' << QDate::currentDate().toString("dd.MM.yyyy") << "]\n"; + ts << QDateTime::currentMSecsSinceEpoch() << '\n'; + for (const QString& tool : mStatistics->getTools()) { + ts << tool << "-error:" << mStatistics->getCount(tool, ShowTypes::ShowErrors) << '\n'; + ts << tool << "-warning:" << mStatistics->getCount(tool, ShowTypes::ShowWarnings) << '\n'; + ts << tool << "-style:" << mStatistics->getCount(tool, ShowTypes::ShowStyle) << '\n'; + ts << tool << "-performance:" << mStatistics->getCount(tool, ShowTypes::ShowPerformance) << '\n'; + ts << tool << "-portability:" << mStatistics->getCount(tool, ShowTypes::ShowPortability) << '\n'; + } +} + +void ResultsView::updateFromOldReport(const QString &filename) const +{ + mUI->mTree->updateFromOldReport(filename); +} + +void ResultsView::save(const QString &filename, Report::Type type, const QString& productName) const +{ + Report *report = nullptr; + + switch (type) { + case Report::CSV: + report = new CsvReport(filename); + break; + case Report::TXT: + report = new TxtReport(filename); + break; + case Report::XMLV2: + report = new XmlReportV2(filename, productName); + break; + } + + if (report) { + if (report->create()) + mUI->mTree->saveResults(report); + else { + QMessageBox msgBox; + msgBox.setText(tr("Failed to save the report.")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.exec(); + } + delete report; + report = nullptr; + } else { + QMessageBox msgBox; + msgBox.setText(tr("Failed to save the report.")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.exec(); + } +} + +void ResultsView::print() +{ + QPrinter printer; + QPrintDialog dialog(&printer, this); + dialog.setWindowTitle(tr("Print Report")); + if (dialog.exec() != QDialog::Accepted) + return; + + print(&printer); +} + +void ResultsView::printPreview() +{ + QPrinter printer; + QPrintPreviewDialog dialog(&printer, this); + connect(&dialog, SIGNAL(paintRequested(QPrinter*)), SLOT(print(QPrinter*))); + dialog.exec(); +} + +void ResultsView::print(QPrinter* printer) +{ + if (!hasResults()) { + QMessageBox msgBox; + msgBox.setText(tr("No errors found, nothing to print.")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.exec(); + return; + } + + PrintableReport report; + mUI->mTree->saveResults(&report); + QTextDocument doc(report.getFormattedReportText()); + doc.print(printer); +} + +void ResultsView::updateSettings(bool showFullPath, + bool saveFullPath, + bool saveAllErrors, + bool showNoErrorsMessage, + bool showErrorId, + bool showInconclusive) +{ + mUI->mTree->updateSettings(showFullPath, saveFullPath, saveAllErrors, showErrorId, showInconclusive); + mShowNoErrorsMessage = showNoErrorsMessage; +} + +void ResultsView::updateStyleSetting(QSettings *settings) +{ + CodeEditorStyle theStyle(CodeEditorStyle::loadSettings(settings)); + mUI->mCode->setStyle(theStyle); +} + +void ResultsView::setCheckDirectory(const QString &dir) +{ + mUI->mTree->setCheckDirectory(dir); +} + +QString ResultsView::getCheckDirectory() +{ + return mUI->mTree->getCheckDirectory(); +} + +void ResultsView::setCheckSettings(const Settings &settings) +{ + delete mCheckSettings; + mCheckSettings = new Settings; + *mCheckSettings = settings; +} + +void ResultsView::checkingStarted(int count) +{ + mSuccess = true; + 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%"); + + { + Settings checkSettings; + const std::set activeCheckers = mStatistics->getActiveCheckers(); + CheckersReport checkersReport(mCheckSettings ? *mCheckSettings : checkSettings, activeCheckers); + mStatistics->setCheckersReport(QString::fromStdString(checkersReport.getReport(mCriticalErrors.toStdString()))); + } + + // TODO: Items can be mysteriously hidden when checking is finished, this function + // call should be redundant but it "unhides" the wrongly hidden items. + mUI->mTree->refreshTree(); + + //Should we inform user of non visible/not found errors? + if (mShowNoErrorsMessage) { + //Tell user that we found no errors + if (!hasResults()) { + QMessageBox msg(QMessageBox::Information, + tr("Cppcheck"), + tr("No errors found."), + QMessageBox::Ok, + this); + + msg.exec(); + } //If we have errors but they aren't visible, tell user about it + else if (!mUI->mTree->hasVisibleResults()) { + QString text = tr("Errors were found, but they are configured to be hidden.\n" \ + "To toggle what kind of errors are shown, open view menu."); + QMessageBox msg(QMessageBox::Information, + tr("Cppcheck"), + text, + QMessageBox::Ok, + this); + + msg.exec(); + } + } +} + +bool ResultsView::hasVisibleResults() const +{ + return mUI->mTree->hasVisibleResults(); +} + +bool ResultsView::hasResults() const +{ + return mUI->mTree->hasResults(); +} + +void ResultsView::saveSettings(QSettings *settings) +{ + mUI->mTree->saveSettings(); + QByteArray state = mUI->mVerticalSplitter->saveState(); + settings->setValue(SETTINGS_MAINWND_SPLITTER_STATE, state); + mUI->mVerticalSplitter->restoreState(state); +} + +void ResultsView::translate() +{ + mUI->retranslateUi(this); + mUI->mTree->translate(); +} + +void ResultsView::disableProgressbar() +{ + mUI->mProgress->setEnabled(false); +} + +void ResultsView::readErrorsXml(const QString &filename) +{ + mSuccess = false; // Don't know if results come from an aborted analysis + + const int version = XmlReport::determineVersion(filename); + if (version == 0) { + QMessageBox msgBox; + msgBox.setText(tr("Failed to read the report.")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.exec(); + return; + } + if (version == 1) { + QMessageBox msgBox; + msgBox.setText(tr("XML format version 1 is no longer supported.")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.exec(); + return; + } + + XmlReportV2 report(filename, QString()); + QList errors; + if (report.open()) { + errors = report.read(); + } else { + QMessageBox msgBox; + msgBox.setText(tr("Failed to read the report.")); + msgBox.setIcon(QMessageBox::Critical); + msgBox.exec(); + } + + for (const ErrorItem& item : errors) { + handleCriticalError(item); + mUI->mTree->addErrorItem(item); + } + + QString dir; + if (!errors.isEmpty() && !errors[0].errorPath.isEmpty()) { + QString relativePath = QFileInfo(filename).canonicalPath(); + if (QFileInfo::exists(relativePath + '/' + errors[0].errorPath[0].file)) + dir = relativePath; + } + + mUI->mTree->setCheckDirectory(dir); +} + +void ResultsView::updateDetails(const QModelIndex &index) +{ + const auto *model = qobject_cast(mUI->mTree->model()); + QStandardItem *item = model->itemFromIndex(index); + + if (!item) { + mUI->mCode->clear(); + mUI->mDetails->setText(QString()); + return; + } + + // Make sure we are working with the first column + if (item->parent() && item->column() != 0) + item = item->parent()->child(item->row(), 0); + + QVariantMap data = item->data().toMap(); + + // If there is no severity data then it is a parent item without summary and message + if (!data.contains("severity")) { + mUI->mCode->clear(); + mUI->mDetails->setText(QString()); + return; + } + + const QString message = data["message"].toString(); + QString formattedMsg = message; + + const QString file0 = data["file0"].toString(); + if (!file0.isEmpty() && Path::isHeader2(data["file"].toString().toStdString())) + formattedMsg += QString("\n\n%1: %2").arg(tr("First included by")).arg(QDir::toNativeSeparators(file0)); + + if (data["cwe"].toInt() > 0) + formattedMsg.prepend("CWE: " + QString::number(data["cwe"].toInt()) + "\n"); + if (mUI->mTree->showIdColumn()) + formattedMsg.prepend(tr("Id") + ": " + data["id"].toString() + "\n"); + if (data["incomplete"].toBool()) + formattedMsg += "\n" + tr("Bug hunting analysis is incomplete"); + mUI->mDetails->setText(formattedMsg); + + const int lineNumber = data["line"].toInt(); + + QString filepath = data["file"].toString(); + if (!QFileInfo::exists(filepath) && QFileInfo::exists(mUI->mTree->getCheckDirectory() + '/' + filepath)) + filepath = mUI->mTree->getCheckDirectory() + '/' + filepath; + + QStringList symbols; + if (data.contains("symbolNames")) + symbols = data["symbolNames"].toString().split("\n"); + + if (filepath == mUI->mCode->getFileName()) { + mUI->mCode->setError(lineNumber, symbols); + return; + } + + QFile file(filepath); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { + mUI->mCode->clear(); + return; + } + + QTextStream in(&file); + mUI->mCode->setError(in.readAll(), lineNumber, symbols); + mUI->mCode->setFileName(filepath); +} + +void ResultsView::log(const QString &str) +{ + mUI->mListLog->addItem(str); +} + +void ResultsView::debugError(const ErrorItem &item) +{ + mUI->mListLog->addItem(item.toString()); +} + +void ResultsView::logClear() +{ + mUI->mListLog->clear(); +} + +void ResultsView::logCopyEntry() +{ + const QListWidgetItem * item = mUI->mListLog->currentItem(); + if (nullptr != item) { + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(item->text()); + } +} + +void ResultsView::logCopyComplete() +{ + QString logText; + for (int i=0; i < mUI->mListLog->count(); ++i) { + const QListWidgetItem * item = mUI->mListLog->item(i); + if (nullptr != item) { + logText += item->text(); + } + } + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(logText); +} + +void ResultsView::on_mListLog_customContextMenuRequested(const QPoint &pos) +{ + if (mUI->mListLog->count() <= 0) + return; + + const QPoint globalPos = mUI->mListLog->mapToGlobal(pos); + + QMenu contextMenu; + contextMenu.addAction(tr("Clear Log"), this, SLOT(logClear())); + contextMenu.addAction(tr("Copy this Log entry"), this, SLOT(logCopyEntry())); + contextMenu.addAction(tr("Copy complete Log"), this, SLOT(logCopyComplete())); + + contextMenu.exec(globalPos); +} + +void ResultsView::stopAnalysis() +{ + mSuccess = false; + mUI->mLabelCriticalErrors->setText(tr("Analysis was stopped")); + mUI->mLabelCriticalErrors->setVisible(true); +} + +void ResultsView::handleCriticalError(const ErrorItem &item) +{ + if (ErrorLogger::isCriticalErrorId(item.errorId.toStdString())) { + if (!mCriticalErrors.contains(item.errorId)) { + if (!mCriticalErrors.isEmpty()) + mCriticalErrors += ","; + mCriticalErrors += item.errorId; + if (item.severity == Severity::internal) + mCriticalErrors += " (suppressed)"; + } + QString msg = tr("There was a critical error with id '%1'").arg(item.errorId); + if (!item.file0.isEmpty()) + msg += ", " + tr("when checking %1").arg(item.file0); + else + msg += ", " + tr("when checking a file"); + msg += ". " + tr("Analysis was aborted."); + mUI->mLabelCriticalErrors->setText(msg); + mUI->mLabelCriticalErrors->setVisible(true); + mSuccess = false; + } +} + +bool ResultsView::isSuccess() const { + return mSuccess; +} diff --git a/cppcheck-2.14.0/gui/resultsview.h b/cppcheck-2.14.0/gui/resultsview.h new file mode 100644 index 00000000..ec97ccf7 --- /dev/null +++ b/cppcheck-2.14.0/gui/resultsview.h @@ -0,0 +1,396 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "report.h" +#include "showtypes.h" + +#include +#include +#include +#include + +class ErrorItem; +class Settings; +class ApplicationList; +class ThreadHandler; +class QModelIndex; +class QPrinter; +class QSettings; +class CheckStatistics; +class QPoint; +namespace Ui { + class ResultsView; +} + +/// @addtogroup GUI +/// @{ + +/** + * @brief Widget to show cppcheck progressbar and result + * + */ +class ResultsView : public QWidget { + Q_OBJECT +public: + + explicit ResultsView(QWidget * parent = nullptr); + void initialize(QSettings *settings, ApplicationList *list, ThreadHandler *checkThreadHandler); + ResultsView(const ResultsView &) = delete; + ~ResultsView() override; + ResultsView &operator=(const ResultsView &) = delete; + + /** + * @brief Clear results and statistics and reset progressinfo. + * @param results Remove all the results from view? + */ + void clear(bool results); + + /** + * @brief Remove a file from the results. + */ + void clear(const QString &filename); + + /** + * @brief Remove a recheck file from the results. + */ + void clearRecheckFile(const QString &filename); + + /** + * @brief Write statistics in file + * + * @param filename Filename to save statistics to + */ + void saveStatistics(const QString &filename) const; + + /** + * @brief Save results to a file + * + * @param filename Filename to save results to + * @param type Type of the report. + * @param productName Custom product name + */ + void save(const QString &filename, Report::Type type, const QString& productName) const; + + /** + * @brief Update results from old report (tag, sinceDate) + */ + void updateFromOldReport(const QString &filename) const; + + /** + * @brief Update tree settings + * + * @param showFullPath Show full path of files in the tree + * @param saveFullPath Save full path of files in reports + * @param saveAllErrors Save all visible errors + * @param showNoErrorsMessage Show "no errors"? + * @param showErrorId Show error id? + * @param showInconclusive Show inconclusive? + */ + void updateSettings(bool showFullPath, + bool saveFullPath, + bool saveAllErrors, + bool showNoErrorsMessage, + bool showErrorId, + bool showInconclusive); + + /** + * @brief Update Code Editor Style + * + * Function will read updated Code Editor styling from + * stored program settings. + * + * @param settings Pointer to QSettings Object + */ + void updateStyleSetting(QSettings *settings); + + /** + * @brief Set the directory we are checking + * + * This is used to split error file path to relative if necessary + * @param dir Directory we are checking + */ + void setCheckDirectory(const QString &dir); + + /** + * @brief Get the directory we are checking + * + * @return Directory containing source files + */ + + QString getCheckDirectory(); + + /** + * Set settings used in checking + */ + void setCheckSettings(const Settings& settings); + + /** + * @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(); + + /** + * @brief This function should be called when analysis is stopped + */ + void stopAnalysis(); + + /** + * @brief Are there successful results? + * @return true if analysis finished without critical errors etc + */ + bool isSuccess() const; + + 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. + */ + const CheckStatistics *getStatistics() const { + return mStatistics; + } + + /** + * @brief Return Showtypes. + * @return Pointer to Showtypes. + */ + const ShowTypes & getShowTypes() const; + +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 + */ + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void resultsHidden(bool hidden); + + /** + * @brief Signal to perform recheck of selected files + * + * @param selectedFilesList list of selected files + */ + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void checkSelected(QStringList selectedFilesList); + + /** Suppress Ids */ + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void suppressIds(QStringList ids); + + /** + * @brief Show/hide certain type of errors + * Refreshes the tree. + * + * @param type Type of error to show/hide + * @param show Should specified errors be shown (true) or hidden (false) + */ + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void showResults(ShowTypes::ShowType type, bool show); + + /** + * @brief Show/hide cppcheck errors. + * Refreshes the tree. + * + * @param show Should specified errors be shown (true) or hidden (false) + */ + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void showCppcheckResults(bool show); + + /** + * @brief Show/hide clang-tidy/clang-analyzer errors. + * Refreshes the tree. + * + * @param show Should specified errors be shown (true) or hidden (false) + */ + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void showClangResults(bool show); + + /** + * @brief Collapse all results in the result list. + */ + void collapseAllResults(); + + /** + * @brief Expand all results in the result list. + */ + void expandAllResults(); + + /** + * @brief Show hidden results in the result list. + */ + void showHiddenResults(); + +public slots: + + /** + * @brief Slot for updating the checking progress + * + * @param value Current progress value + * @param description Description to accompany the progress + */ + void progress(int value, const QString& description); + + /** + * @brief Slot for new error to be displayed + * + * @param item Error data + */ + void error(const ErrorItem &item); + + /** + * @brief Filters the results in the result list. + */ + void filterResults(const QString& filter); + + /** + * @brief Update detailed message when selected item is changed. + * + * @param index Position of new selected item. + */ + void updateDetails(const QModelIndex &index); + + /** + * @brief Slot opening a print dialog to print the current report + */ + void print(); + + /** + * @brief Slot printing the current report to the printer. + * @param printer The printer used for printing the report. + */ + void print(QPrinter* printer); + + /** + * @brief Slot opening a print preview dialog + */ + void printPreview(); + + /** + * \brief Log message + */ + void log(const QString &str); + + /** + * \brief debug message + */ + void debugError(const ErrorItem &item); + + /** + * \brief Clear log messages + */ + void logClear(); + + /** + * \brief Copy selected log message entry + */ + void logCopyEntry(); + + /** + * \brief Copy all log messages + */ + void logCopyComplete(); + +private: + + /** + * If provided ErrorItem is a critical error then display warning message + * in the resultsview + */ + void handleCriticalError(const ErrorItem& item); + + /** + * @brief Should we show a "No errors found dialog" every time no errors were found? + */ + bool mShowNoErrorsMessage = true; + + Ui::ResultsView *mUI; + + CheckStatistics *mStatistics; + + Settings* mCheckSettings = nullptr; + + /** + * Set to true when checking finish successfully. Set to false whenever analysis starts. + */ + bool mSuccess = false; + + /** Critical error ids */ + QString mCriticalErrors; + +private slots: + /** + * @brief Custom context menu for Analysis Log + * @param pos Mouse click position + */ + void on_mListLog_customContextMenuRequested(const QPoint &pos); +}; +/// @} +#endif // RESULTSVIEW_H diff --git a/cppcheck-2.14.0/gui/resultsview.ui b/cppcheck-2.14.0/gui/resultsview.ui new file mode 100644 index 00000000..7ab18fff --- /dev/null +++ b/cppcheck-2.14.0/gui/resultsview.ui @@ -0,0 +1,189 @@ + + + ResultsView + + + + 0 + 0 + 459 + 391 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Results + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + 24 + + + + + + + color: red; + + + Critical errors + + + + + + + Qt::Vertical + + + + + 0 + 0 + + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::ExtendedSelection + + + + + QTabWidget::South + + + 1 + + + + Analysis Log + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + Warning Details + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 1 + + + + false + + + true + + + + + + + + 0 + 4 + + + + false + + + QPlainTextEdit::NoWrap + + + true + + + + + + + + + + + + + ResultsTree + QTreeView +
resultstree.h
+
+ + CodeEditor + QPlainTextEdit +
codeeditor.h
+
+
+ + mTree + mDetails + + + +
diff --git a/cppcheck-2.14.0/gui/scratchpad.cpp b/cppcheck-2.14.0/gui/scratchpad.cpp new file mode 100644 index 00000000..6258df73 --- /dev/null +++ b/cppcheck-2.14.0/gui/scratchpad.cpp @@ -0,0 +1,55 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "codeeditor.h" +#include "mainwindow.h" + +#include "ui_scratchpad.h" + +#include +#include + +ScratchPad::ScratchPad(MainWindow& mainWindow) + : QDialog(&mainWindow) + , mUI(new Ui::ScratchPad) + , mMainWindow(mainWindow) +{ + mUI->setupUi(this); + + connect(mUI->mCheckButton, &QPushButton::clicked, this, &ScratchPad::checkButtonClicked); +} + +ScratchPad::~ScratchPad() +{ + delete mUI; +} + +void ScratchPad::translate() +{ + mUI->retranslateUi(this); +} + +void ScratchPad::checkButtonClicked() +{ + QString filename = mUI->lineEdit->text(); + if (filename.isEmpty()) + filename = "test.cpp"; + mMainWindow.analyzeCode(mUI->plainTextEdit->toPlainText(), filename); +} diff --git a/cppcheck-2.14.0/gui/scratchpad.h b/cppcheck-2.14.0/gui/scratchpad.h new file mode 100644 index 00000000..bd75afe9 --- /dev/null +++ b/cppcheck-2.14.0/gui/scratchpad.h @@ -0,0 +1,61 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 +#include + +class MainWindow; +namespace Ui { + class ScratchPad; +} + +/// @addtogroup GUI +/// @{ + +/** + * @brief A window with a text field that . + */ +class ScratchPad : public QDialog { + Q_OBJECT +public: + explicit ScratchPad(MainWindow& mainWindow); + ~ScratchPad() override; + + /** + * @brief Translate dialog + */ + void translate(); + +private slots: + /** + * @brief Called when check button is clicked. + */ + void checkButtonClicked(); + +private: + Ui::ScratchPad *mUI; + MainWindow& mMainWindow; +}; + +/// @} + +#endif // SCRATCHPAD_H diff --git a/cppcheck-2.14.0/gui/scratchpad.ui b/cppcheck-2.14.0/gui/scratchpad.ui new file mode 100644 index 00000000..9b90a99b --- /dev/null +++ b/cppcheck-2.14.0/gui/scratchpad.ui @@ -0,0 +1,128 @@ + + + ScratchPad + + + + 0 + 0 + 574 + 600 + + + + Scratchpad + + + + + + Copy or write some C/C++ code here: + + + + + + + + Courier New + 10 + + + + + + + + Optionally enter a filename (mainly for automatic language detection) and click on "Check": + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + filename + + + + + + + Check + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + + CodeEditor + QPlainTextEdit +
codeeditor.h
+
+
+ + + + mButtonBox + rejected() + ScratchPad + reject() + + + 20 + 20 + + + 20 + 20 + + + + +
diff --git a/cppcheck-2.14.0/gui/settings.ui b/cppcheck-2.14.0/gui/settings.ui new file mode 100644 index 00000000..3ead9bad --- /dev/null +++ b/cppcheck-2.14.0/gui/settings.ui @@ -0,0 +1,591 @@ + + + Settings + + + + 0 + 0 + 627 + 411 + + + + Preferences + + + + + + 0 + + + + General + + + + + + QLayout::SetDefaultConstraint + + + + + + 0 + 0 + + + + Number of threads: + + + mJobs + + + + + + + + 0 + 0 + + + + + 100 + 20 + + + + + 100 + 20 + + + + 009 + + + + + + 3 + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Ideal count: + + + + + + + TextLabel + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Force checking all #ifdef configurations + + + + + + + Show full path of files + + + + + + + Show "No errors found" message when no errors found + + + + + + + Display error Id in column "Id" + + + + + + + Enable inline suppressions + + + + + + + Check for inconclusive errors also + + + + + + + Show statistics on check completion + + + + + + + Check for updates + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Show internal warnings in log + + + + + + + + Applications + + + + + + + + + + + Add... + + + + + + + Edit... + + + + + + + Remove + + + + + + + Set as default + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Reports + + + + + + Save all errors when creating report + + + + + + + Save full path to files in reports + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Language + + + + + + QAbstractItemView::SelectRows + + + + + + + + Addons + + + + + + Python binary (leave this empty to use python in the PATH) + + + + + + + + + + + ... + + + + + + + + + + + + + + + + + + + MISRA addon + + + + + + + + MISRA rule texts file + + + + + + + <html><head/><body><p>Copy/paste the text from Appendix A &quot;Summary of guidelines&quot; from the MISRA C 2012 pdf to a text file.</p></body></html> + + + + + + + ... + + + + + + + + + + + + Qt::Vertical + + + + 20 + 168 + + + + + + + + + Clang + + + + + + Clang path (leave empty to use system PATH) + + + + + + false + + + + + + + ... + + + + + + + + + + Visual Studio headers + + + + + + <html><head/><body><p>Paths to Visual Studio headers, separated by semicolon ';'.</p><p>You can open a Visual Studio command prompt, write &quot;SET INCLUDE&quot;. Then copy/paste the paths.</p></body></html> + + + true + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 50 + + + + + + + + + Code Editor + + + + + + Code Editor Style + + + + + + System Style + + + + + + + Default Light Style + + + + + + + Default Dark Style + + + + + + + + + Custom + + + + + + + Edit... + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + tabWidget + mJobs + mForce + mShowFullPath + mShowNoErrorsMessage + mInlineSuppressions + mListWidget + mBtnAddApplication + mBtnEditApplication + mBtnRemoveApplication + mBtnDefaultApplication + mSaveAllErrors + mSaveFullPath + mListLanguages + mEnableInconclusive + mShowStatistics + mShowDebugWarnings + mButtons + + + + + mButtons + accepted() + Settings + accept() + + + 257 + 336 + + + 157 + 274 + + + + + mButtons + rejected() + Settings + reject() + + + 325 + 336 + + + 286 + 274 + + + + + diff --git a/cppcheck-2.14.0/gui/settingsdialog.cpp b/cppcheck-2.14.0/gui/settingsdialog.cpp new file mode 100644 index 00000000..e377c1e5 --- /dev/null +++ b/cppcheck-2.14.0/gui/settingsdialog.cpp @@ -0,0 +1,420 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "settingsdialog.h" + +#include "application.h" +#include "applicationdialog.h" +#include "applicationlist.h" +#include "codeeditorstyle.h" +#include "codeeditstyledialog.h" +#include "common.h" +#include "translationhandler.h" + +#include "ui_settings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +SettingsDialog::SettingsDialog(ApplicationList *list, + TranslationHandler *translator, + bool premium, + QWidget *parent) : + QDialog(parent), + mApplications(list), + mTempApplications(new ApplicationList(this)), + mTranslator(translator), + mUI(new Ui::Settings), + mPremium(premium) +{ + mUI->setupUi(this); + mUI->mPythonPathWarning->setStyleSheet("color: red"); + QSettings settings; + mTempApplications->copy(list); + + mUI->mJobs->setText(settings.value(SETTINGS_CHECK_THREADS, 1).toString()); + mUI->mForce->setCheckState(boolToCheckState(settings.value(SETTINGS_CHECK_FORCE, false).toBool())); + mUI->mShowFullPath->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_FULL_PATH, false).toBool())); + mUI->mShowNoErrorsMessage->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_NO_ERRORS, false).toBool())); + mUI->mShowDebugWarnings->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_DEBUG_WARNINGS, false).toBool())); + mUI->mSaveAllErrors->setCheckState(boolToCheckState(settings.value(SETTINGS_SAVE_ALL_ERRORS, false).toBool())); + mUI->mSaveFullPath->setCheckState(boolToCheckState(settings.value(SETTINGS_SAVE_FULL_PATH, false).toBool())); + mUI->mInlineSuppressions->setCheckState(boolToCheckState(settings.value(SETTINGS_INLINE_SUPPRESSIONS, false).toBool())); + mUI->mEnableInconclusive->setCheckState(boolToCheckState(settings.value(SETTINGS_INCONCLUSIVE_ERRORS, false).toBool())); + mUI->mShowStatistics->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_STATISTICS, false).toBool())); + mUI->mShowErrorId->setCheckState(boolToCheckState(settings.value(SETTINGS_SHOW_ERROR_ID, true).toBool())); + mUI->mCheckForUpdates->setCheckState(boolToCheckState(settings.value(SETTINGS_CHECK_FOR_UPDATES, false).toBool())); + mUI->mEditPythonPath->setText(settings.value(SETTINGS_PYTHON_PATH, QString()).toString()); + validateEditPythonPath(); + if (premium) + mUI->mGroupBoxMisra->setVisible(false); + mUI->mEditMisraFile->setText(settings.value(SETTINGS_MISRA_FILE, QString()).toString()); + +#ifdef Q_OS_WIN + //mUI->mTabClang->setVisible(true); + mUI->mEditClangPath->setText(settings.value(SETTINGS_CLANG_PATH, QString()).toString()); + mUI->mEditVsIncludePaths->setText(settings.value(SETTINGS_VS_INCLUDE_PATHS, QString()).toString()); + connect(mUI->mBtnBrowseClangPath, &QPushButton::released, this, &SettingsDialog::browseClangPath); +#else + mUI->mTabClang->setVisible(false); +#endif + mCurrentStyle = new CodeEditorStyle(CodeEditorStyle::loadSettings(&settings)); + manageStyleControls(); + + connect(mUI->mEditPythonPath, SIGNAL(textEdited(QString)), + this, SLOT(validateEditPythonPath())); + + connect(mUI->mButtons, &QDialogButtonBox::accepted, this, &SettingsDialog::ok); + connect(mUI->mButtons, &QDialogButtonBox::rejected, this, &SettingsDialog::reject); + connect(mUI->mBtnAddApplication, SIGNAL(clicked()), + this, SLOT(addApplication())); + connect(mUI->mBtnRemoveApplication, SIGNAL(clicked()), + this, SLOT(removeApplication())); + connect(mUI->mBtnEditApplication, SIGNAL(clicked()), + this, SLOT(editApplication())); + connect(mUI->mBtnDefaultApplication, SIGNAL(clicked()), + this, SLOT(defaultApplication())); + connect(mUI->mListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), + this, SLOT(editApplication())); + + connect(mUI->mBtnBrowsePythonPath, &QPushButton::clicked, this, &SettingsDialog::browsePythonPath); + connect(mUI->mBtnBrowseMisraFile, &QPushButton::clicked, this, &SettingsDialog::browseMisraFile); + connect(mUI->mBtnEditTheme, SIGNAL(clicked()), this, SLOT(editCodeEditorStyle())); + connect(mUI->mThemeSystem, SIGNAL(released()), this, SLOT(setCodeEditorStyleDefault())); + connect(mUI->mThemeDark, SIGNAL(released()), this, SLOT(setCodeEditorStyleDefault())); + connect(mUI->mThemeLight, SIGNAL(released()), this, SLOT(setCodeEditorStyleDefault())); + connect(mUI->mThemeCustom, SIGNAL(toggled(bool)), mUI->mBtnEditTheme, SLOT(setEnabled(bool))); + + mUI->mListWidget->setSortingEnabled(false); + populateApplicationList(); + + const int count = QThread::idealThreadCount(); + if (count != -1) + mUI->mLblIdealThreads->setText(QString::number(count)); + else + mUI->mLblIdealThreads->setText(tr("N/A")); + + loadSettings(); + initTranslationsList(); +} + +SettingsDialog::~SettingsDialog() +{ + saveSettings(); + delete mCurrentStyle; + delete mUI; +} + +void SettingsDialog::initTranslationsList() +{ + const QString current = mTranslator->getCurrentLanguage(); + for (const TranslationInfo& translation : mTranslator->getTranslations()) { + auto *item = new QListWidgetItem; + item->setText(translation.mName); + item->setData(mLangCodeRole, QVariant(translation.mCode)); + mUI->mListLanguages->addItem(item); + if (translation.mCode == current || translation.mCode == current.mid(0, 2)) + mUI->mListLanguages->setCurrentItem(item); + } +} + +Qt::CheckState SettingsDialog::boolToCheckState(bool yes) +{ + if (yes) { + return Qt::Checked; + } + return Qt::Unchecked; +} + +bool SettingsDialog::checkStateToBool(Qt::CheckState state) +{ + return state == Qt::Checked; +} + + +void SettingsDialog::loadSettings() +{ + QSettings settings; + resize(settings.value(SETTINGS_CHECK_DIALOG_WIDTH, 800).toInt(), + settings.value(SETTINGS_CHECK_DIALOG_HEIGHT, 600).toInt()); +} + +void SettingsDialog::saveSettings() const +{ + QSettings settings; + settings.setValue(SETTINGS_CHECK_DIALOG_WIDTH, size().width()); + settings.setValue(SETTINGS_CHECK_DIALOG_HEIGHT, size().height()); +} + +void SettingsDialog::saveSettingValues() const +{ + int jobs = mUI->mJobs->text().toInt(); + if (jobs <= 0) { + jobs = 1; + } + + QSettings settings; + settings.setValue(SETTINGS_CHECK_THREADS, jobs); + saveCheckboxValue(&settings, mUI->mForce, SETTINGS_CHECK_FORCE); + saveCheckboxValue(&settings, mUI->mSaveAllErrors, SETTINGS_SAVE_ALL_ERRORS); + saveCheckboxValue(&settings, mUI->mSaveFullPath, SETTINGS_SAVE_FULL_PATH); + saveCheckboxValue(&settings, mUI->mShowFullPath, SETTINGS_SHOW_FULL_PATH); + saveCheckboxValue(&settings, mUI->mShowNoErrorsMessage, SETTINGS_SHOW_NO_ERRORS); + saveCheckboxValue(&settings, mUI->mShowDebugWarnings, SETTINGS_SHOW_DEBUG_WARNINGS); + saveCheckboxValue(&settings, mUI->mInlineSuppressions, SETTINGS_INLINE_SUPPRESSIONS); + saveCheckboxValue(&settings, mUI->mEnableInconclusive, SETTINGS_INCONCLUSIVE_ERRORS); + saveCheckboxValue(&settings, mUI->mShowStatistics, SETTINGS_SHOW_STATISTICS); + saveCheckboxValue(&settings, mUI->mShowErrorId, SETTINGS_SHOW_ERROR_ID); + saveCheckboxValue(&settings, mUI->mCheckForUpdates, SETTINGS_CHECK_FOR_UPDATES); + settings.setValue(SETTINGS_PYTHON_PATH, mUI->mEditPythonPath->text()); + if (!mPremium) + settings.setValue(SETTINGS_MISRA_FILE, mUI->mEditMisraFile->text()); + +#ifdef Q_OS_WIN + settings.setValue(SETTINGS_CLANG_PATH, mUI->mEditClangPath->text()); + settings.setValue(SETTINGS_VS_INCLUDE_PATHS, mUI->mEditVsIncludePaths->text()); +#endif + + const QListWidgetItem *currentLang = mUI->mListLanguages->currentItem(); + if (currentLang) { + const QString langcode = currentLang->data(mLangCodeRole).toString(); + settings.setValue(SETTINGS_LANGUAGE, langcode); + } + CodeEditorStyle::saveSettings(&settings, *mCurrentStyle); +} + +void SettingsDialog::saveCheckboxValue(QSettings *settings, const QCheckBox *box, + const QString &name) +{ + settings->setValue(name, checkStateToBool(box->checkState())); +} + +void SettingsDialog::validateEditPythonPath() +{ + const auto pythonPath = mUI->mEditPythonPath->text(); + if (pythonPath.isEmpty()) { + mUI->mEditPythonPath->setStyleSheet(""); + mUI->mPythonPathWarning->hide(); + return; + } + + QFileInfo pythonPathInfo(pythonPath); + if (!pythonPathInfo.exists() || + !pythonPathInfo.isFile() || + !pythonPathInfo.isExecutable()) { + mUI->mEditPythonPath->setStyleSheet("QLineEdit {border: 1px solid red}"); + mUI->mPythonPathWarning->setText(tr("The executable file \"%1\" is not available").arg(pythonPath)); + mUI->mPythonPathWarning->show(); + } else { + mUI->mEditPythonPath->setStyleSheet(""); + mUI->mPythonPathWarning->hide(); + } +} + +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() +{ + for (QListWidgetItem *item : mUI->mListWidget->selectedItems()) { + 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() +{ + for (QListWidgetItem *item : mUI->mListWidget->selectedItems()) { + const int row = mUI->mListWidget->row(item); + Application& app = mTempApplications->getApplication(row); + ApplicationDialog dialog(tr("Modify an application"), app, this); + + if (dialog.exec() == QDialog::Accepted) { + QString name = app.getName(); + if (mTempApplications->getDefaultApplication() == row) + name += tr(" [Default]"); + item->setText(name); + } + } +} + +void SettingsDialog::defaultApplication() +{ + QList selected = mUI->mListWidget->selectedItems(); + if (!selected.isEmpty()) { + const int index = mUI->mListWidget->row(selected[0]); + mTempApplications->setDefault(index); + mUI->mListWidget->clear(); + populateApplicationList(); + } +} + +void SettingsDialog::populateApplicationList() +{ + const int defapp = mTempApplications->getDefaultApplication(); + for (int i = 0; i < mTempApplications->getApplicationCount(); i++) { + const Application& app = mTempApplications->getApplication(i); + QString name = app.getName(); + if (i == defapp) { + name += " "; + name += tr("[Default]"); + } + mUI->mListWidget->addItem(name); + } + + // Select default application, or if there is no default app then the + // first item. + if (defapp == -1) + mUI->mListWidget->setCurrentRow(0); + else { + if (mTempApplications->getApplicationCount() > defapp) + mUI->mListWidget->setCurrentRow(defapp); + else + mUI->mListWidget->setCurrentRow(0); + } +} + +void SettingsDialog::ok() +{ + mApplications->copy(mTempApplications); + accept(); +} + +bool SettingsDialog::showFullPath() const +{ + return checkStateToBool(mUI->mShowFullPath->checkState()); +} + +bool SettingsDialog::saveFullPath() const +{ + return checkStateToBool(mUI->mSaveFullPath->checkState()); +} + +bool SettingsDialog::saveAllErrors() const +{ + return checkStateToBool(mUI->mSaveAllErrors->checkState()); +} + +bool SettingsDialog::showNoErrorsMessage() const +{ + return checkStateToBool(mUI->mShowNoErrorsMessage->checkState()); +} + +bool SettingsDialog::showErrorId() const +{ + return checkStateToBool(mUI->mShowErrorId->checkState()); +} + +bool SettingsDialog::showInconclusive() const +{ + return checkStateToBool(mUI->mEnableInconclusive->checkState()); +} + +void SettingsDialog::browsePythonPath() +{ + QString fileName = QFileDialog::getOpenFileName(this, tr("Select python binary"), QDir::rootPath()); + if (fileName.contains("python", Qt::CaseInsensitive)) + mUI->mEditPythonPath->setText(fileName); +} + +void SettingsDialog::browseMisraFile() +{ + const QString fileName = QFileDialog::getOpenFileName(this, tr("Select MISRA File"), QDir::homePath(), "Misra File (*.pdf *.txt)"); + if (!fileName.isEmpty()) + mUI->mEditMisraFile->setText(fileName); +} + +// Slot to set default light style +void SettingsDialog::setCodeEditorStyleDefault() +{ + if (mUI->mThemeSystem->isChecked()) + *mCurrentStyle = CodeEditorStyle::getSystemTheme(); + if (mUI->mThemeLight->isChecked()) + *mCurrentStyle = defaultStyleLight; + if (mUI->mThemeDark->isChecked()) + *mCurrentStyle = defaultStyleDark; + manageStyleControls(); +} + +// Slot to edit custom style +void SettingsDialog::editCodeEditorStyle() +{ + StyleEditDialog dlg(*mCurrentStyle, this); + const int nResult = dlg.exec(); + if (nResult == QDialog::Accepted) { + *mCurrentStyle = dlg.getStyle(); + manageStyleControls(); + } +} + +void SettingsDialog::browseClangPath() +{ + QString selectedDir = QFileDialog::getExistingDirectory(this, + tr("Select clang path"), + QDir::rootPath()); + + if (!selectedDir.isEmpty()) { + mUI->mEditClangPath->setText(selectedDir); + } +} + +void SettingsDialog::manageStyleControls() +{ + const bool isSystemTheme = mCurrentStyle->isSystemTheme(); + const bool isDefaultLight = !isSystemTheme && *mCurrentStyle == defaultStyleLight; + const bool isDefaultDark = !isSystemTheme && *mCurrentStyle == defaultStyleDark; + mUI->mThemeSystem->setChecked(isSystemTheme); + mUI->mThemeLight->setChecked(isDefaultLight && !isDefaultDark); + mUI->mThemeDark->setChecked(!isDefaultLight && isDefaultDark); + mUI->mThemeCustom->setChecked(!isSystemTheme && !isDefaultLight && !isDefaultDark); + mUI->mBtnEditTheme->setEnabled(!isSystemTheme && !isDefaultLight && !isDefaultDark); +} + diff --git a/cppcheck-2.14.0/gui/settingsdialog.h b/cppcheck-2.14.0/gui/settingsdialog.h new file mode 100644 index 00000000..7b1fb694 --- /dev/null +++ b/cppcheck-2.14.0/gui/settingsdialog.h @@ -0,0 +1,248 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 +#include +#include + +class QSettings; +class QWidget; +class ApplicationList; +class TranslationHandler; +class CodeEditorStyle; +class QCheckBox; +namespace Ui { + class Settings; +} + +/// @addtogroup GUI +/// @{ + +/** + * @brief Settings dialog + * + */ +class SettingsDialog : public QDialog { + Q_OBJECT +public: + SettingsDialog(ApplicationList *list, + TranslationHandler *translator, + bool premium, + QWidget *parent = nullptr); + SettingsDialog(const SettingsDialog &) = delete; + ~SettingsDialog() override; + SettingsDialog &operator=(const SettingsDialog &) = delete; + + /** + * @brief Save all values to QSettings + * + */ + void saveSettingValues() const; + + /** + * @brief Get checkbox value for mShowFullPath + * + * @return should full path of errors be shown in the tree + */ + bool showFullPath() const; + + /** + * @brief Get checkbox value for mSaveFullPath + * + * @return should full path of files be saved when creating a report + */ + bool saveFullPath() const; + + + /** + * @brief Get checkbox value for mNoErrorsMessage + * + * @return Should "no errors message" be hidden + */ + bool showNoErrorsMessage() const; + + /** + * @brief Get checkbox value for mShowIdColumn + * + * @return Should error id column be displayed + */ + bool showErrorId() const; + + + /** + * @brief Get checkbox value for mEnableInconclusive + * + * @return Should inconclusive column be displayed + */ + bool showInconclusive() const; + + /** + * @brief Get checkbox value for mSaveAllErrors + * + * @return should all errors be saved to report + */ + bool saveAllErrors() const; + +protected slots: + /** + * @brief Slot for clicking OK. + * + */ + void ok(); + + /** @brief Slot for validating input value in @c editPythonPath */ + void validateEditPythonPath(); + + /** + * @brief Slot for adding a new application to the list + * + */ + void addApplication(); + + /** + * @brief Slot for deleting an application from the list + * + */ + void removeApplication(); + + /** + * @brief Slot for modifying an application in the list + * + */ + void editApplication(); + + /** + * @brief Slot for making the selected application as the default (first) + * + */ + void defaultApplication(); + + /** @brief Slot for browsing for the python binary */ + void browsePythonPath(); + + /** @brief Slot for browsing for the clang binary */ + void browseClangPath(); + + /** + * @brief Browse for MISRA file + */ + void browseMisraFile(); + + /** + * @brief Set Code Editor Style to Default + */ + void setCodeEditorStyleDefault(); + + /** + * @brief Edit Custom Code Editor Style + */ + void editCodeEditorStyle(); + +protected: + /** + * @brief Clear all applications from the list and re insert them from mTempApplications + * + */ + void populateApplicationList(); + + /** + * @brief Load saved values + * Loads dialog size and column widths. + * + */ + void loadSettings(); + + /** + * @brief Save settings + * Save dialog size and column widths. + */ + void saveSettings() const; + + /** + * @brief Save a single checkboxes value + * + * @param settings Pointer to Settings. + * @param box checkbox to save + * @param name name for QSettings to store the value + */ + static void saveCheckboxValue(QSettings *settings, const QCheckBox *box, const QString &name); + + /** + * @brief Convert bool to Qt::CheckState + * + * @param yes value to convert + * @return value converted to Qt::CheckState + */ + static Qt::CheckState boolToCheckState(bool yes); + + /** + * @brief Converts Qt::CheckState to bool + * + * @param state Qt::CheckState to convert + * @return converted value + */ + static bool checkStateToBool(Qt::CheckState state); + + /** + * @brief Populate the translations list. + */ + void initTranslationsList(); + + /** + * @brief Current Code Editor Style + */ + CodeEditorStyle *mCurrentStyle; + + /** + * @brief List of applications user has specified + * + */ + ApplicationList *mApplications; + + /** + * @brief Temporary list of applications + * This will be copied to actual list of applications (mApplications) + * when user clicks ok. + */ + ApplicationList *mTempApplications; + + /** + * @brief List of translations. + * + */ + TranslationHandler *mTranslator; + + /** + * @brief Dialog from UI designer + * + */ + Ui::Settings *mUI; +private: + void manageStyleControls(); + + static constexpr int mLangCodeRole = Qt::UserRole; + + bool mPremium; +}; +/// @} +#endif // SETTINGSDIALOG_H diff --git a/cppcheck-2.14.0/gui/showtypes.cpp b/cppcheck-2.14.0/gui/showtypes.cpp new file mode 100644 index 00000000..4b830f3d --- /dev/null +++ b/cppcheck-2.14.0/gui/showtypes.cpp @@ -0,0 +1,130 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "showtypes.h" + +#include "common.h" +#include "errortypes.h" + +#include + +ShowTypes::ShowTypes() +{ + load(); +} + +ShowTypes::~ShowTypes() +{ + save(); +} + +ShowTypes::ShowType ShowTypes::SeverityToShowType(Severity severity) +{ + switch (severity) { + case Severity::none: + case Severity::internal: + 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 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 severity) const +{ + return isShown(ShowTypes::SeverityToShowType(severity)); +} + +void ShowTypes::show(ShowTypes::ShowType category, bool showing) +{ + mVisible[category] = showing; +} diff --git a/cppcheck-2.14.0/gui/showtypes.h b/cppcheck-2.14.0/gui/showtypes.h new file mode 100644 index 00000000..605eaee1 --- /dev/null +++ b/cppcheck-2.14.0/gui/showtypes.h @@ -0,0 +1,125 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 + +enum class Severity; + +/// @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 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 severity); + + /** + * @brief Convert ShowType to severity string + * @param type ShowType to convert + * @return ShowType converted to severity + */ + static Severity 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 diff --git a/cppcheck-2.14.0/gui/statsdialog.cpp b/cppcheck-2.14.0/gui/statsdialog.cpp new file mode 100644 index 00000000..e4e19f5a --- /dev/null +++ b/cppcheck-2.14.0/gui/statsdialog.cpp @@ -0,0 +1,458 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "statsdialog.h" + +#include "checkstatistics.h" +#include "projectfile.h" +#include "showtypes.h" + +#include "ui_statsdialog.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef QT_CHARTS_LIB +#include "common.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +QT_CHARTS_USE_NAMESPACE +#endif + +static QLineSeries *numberOfReports(const QString &fileName, const QString &severity); +static QChartView *createChart(const QString &statsFile, const QString &tool); +#endif + +static const QString CPPCHECK("cppcheck"); + +StatsDialog::StatsDialog(QWidget *parent) + : QDialog(parent), + mUI(new Ui::StatsDialog) +{ + mUI->setupUi(this); + + QFont font("courier"); + font.setStyleHint(QFont::Monospace); + mUI->mCheckersReport->setFont(font); + + setWindowFlags(Qt::Window); + + connect(mUI->mCopyToClipboard, &QPushButton::pressed, this, &StatsDialog::copyToClipboard); + connect(mUI->mPDFexport, &QPushButton::pressed, this, &StatsDialog::pdfExport); +} + +StatsDialog::~StatsDialog() +{ + delete mUI; +} + +void StatsDialog::setProject(const ProjectFile* projectFile) +{ + if (projectFile) { + mUI->mProject->setText(projectFile->getRootPath()); + mUI->mPaths->setText(projectFile->getCheckPaths().join(";")); + mUI->mIncludePaths->setText(projectFile->getIncludeDirs().join(";")); + mUI->mDefines->setText(projectFile->getDefines().join(";")); + mUI->mUndefines->setText(projectFile->getUndefines().join(";")); +#ifndef QT_CHARTS_LIB + mUI->mTabHistory->setVisible(false); +#else + QString statsFile; + if (!projectFile->getBuildDir().isEmpty()) { + const QString prjpath = QFileInfo(projectFile->getFilename()).absolutePath(); + const QString buildDir = prjpath + '/' + projectFile->getBuildDir(); + if (QDir(buildDir).exists()) { + statsFile = buildDir + "/statistics.txt"; + } + } + mUI->mLblHistoryFile->setText(tr("File: ") + (statsFile.isEmpty() ? tr("No cppcheck build dir") : statsFile)); + if (!statsFile.isEmpty()) { + QChartView *chartView = createChart(statsFile, "cppcheck"); + mUI->mTabHistory->layout()->addWidget(chartView); + if (projectFile->getClangAnalyzer()) { + chartView = createChart(statsFile, CLANG_ANALYZER); + mUI->mTabHistory->layout()->addWidget(chartView); + } + if (projectFile->getClangTidy()) { + chartView = createChart(statsFile, CLANG_TIDY); + mUI->mTabHistory->layout()->addWidget(chartView); + } + } +#endif + } else { + mUI->mProject->setText(QString()); + mUI->mPaths->setText(QString()); + mUI->mIncludePaths->setText(QString()); + mUI->mDefines->setText(QString()); + mUI->mUndefines->setText(QString()); + } +} + +void StatsDialog::setPathSelected(const QString& path) +{ + mUI->mPath->setText(path); +} + +void StatsDialog::setNumberOfFilesScanned(int num) +{ + mUI->mNumberOfFilesScanned->setText(QString::number(num)); +} + +void StatsDialog::setScanDuration(double seconds) +{ + // Factor the duration into units (days/hours/minutes/seconds) + int secs = seconds; + const int days = secs / (24 * 60 * 60); + secs -= days * (24 * 60 * 60); + const int hours = secs / (60 * 60); + secs -= hours * (60 * 60); + const int mins = secs / 60; + secs -= mins * 60; + + // Concatenate the two most significant units (e.g. "1 day and 3 hours") + QStringList parts; + if (days) + parts << ((days == 1) ? tr("1 day") : tr("%1 days").arg(days)); + if (hours) + parts << ((hours == 1) ? tr("1 hour") : tr("%1 hours").arg(hours)); + if (mins && parts.size() < 2) + parts << ((mins == 1) ? tr("1 minute") : tr("%1 minutes").arg(mins)); + if (secs && parts.size() < 2) + parts << ((secs == 1) ? tr("1 second") : tr("%1 seconds").arg(secs)); + + // For durations < 1s, show the fraction of a second (e.g. "0.7 seconds") + if (parts.isEmpty()) + parts << tr("0.%1 seconds").arg(int(10.0 *(seconds - secs))); + + mUI->mScanDuration->setText(parts.join(tr(" and "))); +} +void StatsDialog::pdfExport() +{ + const QString Stat = QString( + "

%1 %2

\n" + "

%3 : %4

\n" + "

%5 : %6

\n" + "

%7 : %8

\n" + "

%9 : %10

\n" + "

%11 : %12

\n" + "

%13 : %14

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

%1

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

%1

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

%1

\n" + " %2:%3\n" + " %4:%5\n" + " %6:%7\n" + " %8:%9\n" + " %10:%11\n" + " %12:%13\n" + "\n" + ) + .arg(stats) + .arg(errors) + .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowErrors)) + .arg(warnings) + .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowWarnings)) + .arg(style) + .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowStyle)) + .arg(portability) + .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowPortability)) + .arg(performance) + .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowPerformance)) + .arg(information) + .arg(mStatistics->getCount(CPPCHECK,ShowTypes::ShowInformation)); + + const QString htmlSummary = htmlSettings + htmlPrevious + htmlStatistics; + + auto *mimeData = new QMimeData(); + mimeData->setText(textSummary); + mimeData->setHtml(htmlSummary); + clipboard->setMimeData(mimeData); +} + +void StatsDialog::setStatistics(const CheckStatistics *stats) +{ + mStatistics = stats; + mUI->mLblErrors->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowErrors))); + mUI->mLblWarnings->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowWarnings))); + mUI->mLblStyle->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowStyle))); + mUI->mLblPortability->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowPortability))); + mUI->mLblPerformance->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowPerformance))); + mUI->mLblInformation->setText(QString::number(stats->getCount(CPPCHECK,ShowTypes::ShowInformation))); + mUI->mLblActiveCheckers->setText(QString::number(stats->getNumberOfActiveCheckers())); + mUI->mCheckersReport->setPlainText(stats->getCheckersReport()); +} + +#ifdef QT_CHARTS_LIB +QChartView *createChart(const QString &statsFile, const QString &tool) +{ + auto *chart = new QChart; + chart->addSeries(numberOfReports(statsFile, tool + "-error")); + chart->addSeries(numberOfReports(statsFile, tool + "-warning")); + chart->addSeries(numberOfReports(statsFile, tool + "-style")); + chart->addSeries(numberOfReports(statsFile, tool + "-performance")); + chart->addSeries(numberOfReports(statsFile, tool + "-portability")); + + auto *axisX = new QDateTimeAxis; + axisX->setTitleText("Date"); + chart->addAxis(axisX, Qt::AlignBottom); + + for (QAbstractSeries *s : chart->series()) { + s->attachAxis(axisX); + } + + auto *axisY = new QValueAxis; + axisY->setLabelFormat("%i"); + axisY->setTitleText("Count"); + chart->addAxis(axisY, Qt::AlignLeft); + + qreal maxY = 0; + for (QAbstractSeries *s : chart->series()) { + s->attachAxis(axisY); + if (const auto *ls = dynamic_cast(s)) { + for (QPointF p : ls->points()) { + if (p.y() > maxY) + maxY = p.y(); + } + } + } + axisY->setMax(maxY); + + //chart->createDefaultAxes(); + chart->setTitle(tool); + + auto *chartView = new QChartView(chart); + chartView->setRenderHint(QPainter::Antialiasing); + return chartView; +} + +QLineSeries *numberOfReports(const QString &fileName, const QString &severity) +{ + auto *series = new QLineSeries(); + series->setName(severity); + QFile f(fileName); + if (f.open(QIODevice::ReadOnly | QIODevice::Text)) { + quint64 t = 0; + QTextStream in(&f); + while (!in.atEnd()) { + QString line = in.readLine(); + static const QRegularExpression rxdate("^\\[(\\d\\d)\\.(\\d\\d)\\.(\\d\\d\\d\\d)\\]$"); + const QRegularExpressionMatch matchRes = rxdate.match(line); + if (matchRes.hasMatch()) { + const int y = matchRes.captured(3).toInt(); + const int m = matchRes.captured(2).toInt(); + const int d = matchRes.captured(1).toInt(); + QDateTime dt; + dt.setDate(QDate(y,m,d)); + if (t == dt.toMSecsSinceEpoch()) + t += 1000; + else + t = dt.toMSecsSinceEpoch(); + } + if (line.startsWith(severity + ':')) { + const int y = line.mid(1+severity.length()).toInt(); + series->append(t, y); + } + } + } + return series; +} +#endif diff --git a/cppcheck-2.14.0/gui/statsdialog.h b/cppcheck-2.14.0/gui/statsdialog.h new file mode 100644 index 00000000..62c93fca --- /dev/null +++ b/cppcheck-2.14.0/gui/statsdialog.h @@ -0,0 +1,81 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 +#include + +class ProjectFile; +class CheckStatistics; +class QWidget; +namespace Ui { + class StatsDialog; +} + +/// @addtogroup GUI +/// @{ + +/** + * @brief A dialog that shows project and scan statistics. + * + */ +class StatsDialog : public QDialog { + Q_OBJECT +public: + explicit StatsDialog(QWidget *parent = nullptr); + ~StatsDialog() override; + + /** + * @brief Sets the project to extract statistics from + */ + void setProject(const ProjectFile *projectFile); + + /** + * @brief Sets the string to display beside "Path Selected:" + */ + void setPathSelected(const QString& path); + + /** + * @brief Sets the number to display beside "Number of Files Scanned:" + */ + void setNumberOfFilesScanned(int num); + + /** + * @brief Sets the number of seconds to display beside "Scan Duration:" + */ + void setScanDuration(double seconds); + + /** + * @brief Sets the numbers of different error/warnings found." + */ + void setStatistics(const CheckStatistics *stats); + +private slots: + void copyToClipboard(); + void pdfExport(); +private: + Ui::StatsDialog *mUI; + const CheckStatistics* mStatistics{}; +}; + +/// @} + +#endif // STATSDIALOG_H diff --git a/cppcheck-2.14.0/gui/statsdialog.ui b/cppcheck-2.14.0/gui/statsdialog.ui new file mode 100644 index 00000000..a749c51e --- /dev/null +++ b/cppcheck-2.14.0/gui/statsdialog.ui @@ -0,0 +1,519 @@ + + + StatsDialog + + + + 0 + 0 + 500 + 414 + + + + Statistics + + + + + + QTabWidget::Rounded + + + 0 + + + + Project + + + + + + Project: + + + + + + + + 0 + 0 + + + + true + + + + + + + Paths: + + + + + + + true + + + + 0 + 0 + + + + true + + + false + + + true + + + true + + + + + + + Include paths: + + + + + + + true + + + + 0 + 0 + + + + true + + + + + + + Defines: + + + + + + + true + + + + 0 + 0 + + + + true + + + + + + + Undefines: + + + + + + + + 0 + 0 + + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Previous Scan + + + + + + Path Selected: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + Number of Files Scanned: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + + + + + Scan Duration: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + + + Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 0 + 0 + + + + true + + + + + + + + Statistics + + + + + + Errors: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + + + + + + Warnings: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + + + + + + Stylistic warnings: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + + + + + + Portability warnings: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + + + + + + Performance issues: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + + + + + + Information messages: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + + + + + + Active checkers: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + TextLabel + + + + + + + + Checkers + + + + + + true + + + + Courier 10 Pitch + + + + true + + + true + + + + + + + + History + + + + + + File: + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Copy to Clipboard + + + + + + + Pdf Export + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + mTabWidget + mProject + mPaths + mIncludePaths + mDefines + mCopyToClipboard + mButtonBox + mPath + + + + + mButtonBox + accepted() + StatsDialog + accept() + + + 483 + 310 + + + 157 + 274 + + + + + mButtonBox + rejected() + StatsDialog + reject() + + + 483 + 310 + + + 286 + 274 + + + + + diff --git a/cppcheck-2.14.0/gui/test/CMakeLists.txt b/cppcheck-2.14.0/gui/test/CMakeLists.txt new file mode 100644 index 00000000..ea868eb9 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/CMakeLists.txt @@ -0,0 +1,6 @@ +add_subdirectory(cppchecklibrarydata) +add_subdirectory(filelist) +add_subdirectory(projectfile) +add_subdirectory(translationhandler) +add_subdirectory(xmlreportv2) +# TODO: add all tests to CTest \ No newline at end of file diff --git a/cppcheck-2.14.0/gui/test/common.pri b/cppcheck-2.14.0/gui/test/common.pri new file mode 100644 index 00000000..e6589d8e --- /dev/null +++ b/cppcheck-2.14.0/gui/test/common.pri @@ -0,0 +1,12 @@ +QT += testlib + +INCLUDEPATH += $${PWD}/.. \ + $${PWD}/../../lib + +contains(QMAKE_CC, gcc) { + QMAKE_CXXFLAGS += -std=c++11 +} + +contains(QMAKE_CXX, clang++) { + QMAKE_CXXFLAGS += -std=c++11 +} diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/CMakeLists.txt b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/CMakeLists.txt new file mode 100644 index 00000000..94e66119 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/CMakeLists.txt @@ -0,0 +1,22 @@ +qt_wrap_cpp(test-cppchecklibrarydata_SRC testcppchecklibrarydata.h) +QT_ADD_RESOURCES(test-cppchecklibrarydata_resources "resources.qrc") +add_custom_target(build-cppchecklibrarydata-deps SOURCES ${test-cppchecklibrarydata_SRC} ${test-cppchecklibrarydata_resources}) +add_dependencies(gui-build-deps build-cppchecklibrarydata-deps) +add_executable(test-cppchecklibrarydata + ${test-cppchecklibrarydata_SRC} + ${test-cppchecklibrarydata_resources} + testcppchecklibrarydata.cpp + ${CMAKE_SOURCE_DIR}/gui/cppchecklibrarydata.cpp + ) +target_include_directories(test-cppchecklibrarydata PRIVATE ${CMAKE_SOURCE_DIR}/lib ${CMAKE_SOURCE_DIR}/gui) +target_compile_definitions(test-cppchecklibrarydata PRIVATE SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(test-cppchecklibrarydata ${QT_CORE_LIB} ${QT_TEST_LIB}) + +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # Q_UNUSED() in generated code + target_compile_options_safe(test-cppchecklibrarydata -Wno-extra-semi-stmt) +endif() + +if (REGISTER_GUI_TESTS) + add_test(NAME test-cppchecklibrarydata COMMAND $) +endif() \ No newline at end of file diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/cppchecklibrarydata.pro b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/cppchecklibrarydata.pro new file mode 100644 index 00000000..787e95f8 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/cppchecklibrarydata.pro @@ -0,0 +1,23 @@ +TEMPLATE = app +TARGET = test-cppchecklibrarydata +DEPENDPATH += . +INCLUDEPATH += . +OBJECTS_DIR = ../../temp +MOC_DIR = ../../temp + +QT -= gui +QT += core +QT += testlib + +include(../common.pri) + +DEFINES += SRCDIR=\\\"$$PWD\\\" + +SOURCES += testcppchecklibrarydata.cpp \ + ../../cppchecklibrarydata.cpp + +HEADERS += testcppchecklibrarydata.h \ + ../../cppchecklibrarydata.h \ + +RESOURCES += \ + resources.qrc diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/container_unhandled_element.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/container_unhandled_element.cfg new file mode 100644 index 00000000..3d142fea --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/container_unhandled_element.cfg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/container_valid.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/container_valid.cfg new file mode 100644 index 00000000..494821c9 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/container_valid.cfg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/define_valid.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/define_valid.cfg new file mode 100644 index 00000000..c0cfda81 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/define_valid.cfg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/mandatory_attribute_missing.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/mandatory_attribute_missing.cfg new file mode 100644 index 00000000..12601c93 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/mandatory_attribute_missing.cfg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/markup_mandatory_attribute_missing.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/markup_mandatory_attribute_missing.cfg new file mode 100644 index 00000000..f3a2ff4d --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/markup_mandatory_attribute_missing.cfg @@ -0,0 +1,6 @@ + + + + + + diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/markup_unhandled_element.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/markup_unhandled_element.cfg new file mode 100644 index 00000000..5bfb80c9 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/markup_unhandled_element.cfg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + READ + READ + WRITE + NOTIFY + + + + connect + + + diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/markup_valid.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/markup_valid.cfg new file mode 100644 index 00000000..286c0c4f --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/markup_valid.cfg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + READ + READ + WRITE + NOTIFY + + + + connect + + + diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/memory_resource_unhandled_element.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/memory_resource_unhandled_element.cfg new file mode 100644 index 00000000..9a197cda --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/memory_resource_unhandled_element.cfg @@ -0,0 +1,12 @@ + + + + malloc + + calloc + aligned_alloc + realloc + reallocarray + free + + diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/memory_resource_valid.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/memory_resource_valid.cfg new file mode 100644 index 00000000..fe0e9329 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/memory_resource_valid.cfg @@ -0,0 +1,15 @@ + + + + malloc + calloc + realloc + UuidToString + HeapFree + + + + fclose + _wfopen_s + + diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/platform_type_unhandled_element.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/platform_type_unhandled_element.cfg new file mode 100644 index 00000000..b6b0245c --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/platform_type_unhandled_element.cfg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/platform_type_valid.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/platform_type_valid.cfg new file mode 100644 index 00000000..ccbd51f5 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/platform_type_valid.cfg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/podtype_valid.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/podtype_valid.cfg new file mode 100644 index 00000000..348c12aa --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/podtype_valid.cfg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/reflection_mandatory_attribute_missing.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/reflection_mandatory_attribute_missing.cfg new file mode 100644 index 00000000..fd9019a4 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/reflection_mandatory_attribute_missing.cfg @@ -0,0 +1,6 @@ + + + + invokeMethod + + \ No newline at end of file diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/reflection_unhandled_element.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/reflection_unhandled_element.cfg new file mode 100644 index 00000000..6827f83c --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/reflection_unhandled_element.cfg @@ -0,0 +1,7 @@ + + + + + invokeMethod + + diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/reflection_valid.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/reflection_valid.cfg new file mode 100644 index 00000000..1eca6368 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/reflection_valid.cfg @@ -0,0 +1,8 @@ + + + + invokeMethod + callFunction + + + diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/smartptr_valid.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/smartptr_valid.cfg new file mode 100644 index 00000000..e0b9245c --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/smartptr_valid.cfg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/typechecks_valid.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/typechecks_valid.cfg new file mode 100644 index 00000000..42cc5e73 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/typechecks_valid.cfg @@ -0,0 +1,15 @@ + + + + + std::insert_iterator + std::pair + + + + + + std::tuple + + + diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/undefine_valid.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/undefine_valid.cfg new file mode 100644 index 00000000..62fb7f04 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/undefine_valid.cfg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/unhandled_element.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/unhandled_element.cfg new file mode 100644 index 00000000..d9821227 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/unhandled_element.cfg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/xml_reader_error.cfg b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/xml_reader_error.cfg new file mode 100644 index 00000000..5c397386 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/files/xml_reader_error.cfg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/resources.qrc b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/resources.qrc new file mode 100644 index 00000000..57625f3b --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/resources.qrc @@ -0,0 +1,24 @@ + + + files/xml_reader_error.cfg + files/mandatory_attribute_missing.cfg + files/unhandled_element.cfg + files/typechecks_valid.cfg + files/podtype_valid.cfg + files/smartptr_valid.cfg + files/platform_type_valid.cfg + files/platform_type_unhandled_element.cfg + files/memory_resource_unhandled_element.cfg + files/memory_resource_valid.cfg + files/define_valid.cfg + files/undefine_valid.cfg + files/container_unhandled_element.cfg + files/reflection_unhandled_element.cfg + files/reflection_valid.cfg + files/reflection_mandatory_attribute_missing.cfg + files/markup_mandatory_attribute_missing.cfg + files/markup_valid.cfg + files/markup_unhandled_element.cfg + files/container_valid.cfg + + diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp new file mode 100644 index 00000000..015e8a60 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/testcppchecklibrarydata.cpp @@ -0,0 +1,635 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2021 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "testcppchecklibrarydata.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const QString TestCppcheckLibraryData::TempCfgFile = "./tmp.cfg"; + +void TestCppcheckLibraryData::init() +{ + result.clear(); + libraryData.clear(); + fileLibraryData.clear(); +} + +void TestCppcheckLibraryData::xmlReaderError() +{ + loadCfgFile(":/files/xml_reader_error.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), false); + qDebug() << result; +} + +void TestCppcheckLibraryData::unhandledElement() +{ + loadCfgFile(":/files/unhandled_element.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), false); + qDebug() << result; + + loadCfgFile(":/files/platform_type_unhandled_element.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), false); + qDebug() << result; + + loadCfgFile(":/files/memory_resource_unhandled_element.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), false); + qDebug() << result; + + loadCfgFile(":/files/container_unhandled_element.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), false); + qDebug() << result; + + loadCfgFile(":/files/reflection_unhandled_element.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), false); + qDebug() << result; + + loadCfgFile(":/files/markup_unhandled_element.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), false); + qDebug() << result; +} + +void TestCppcheckLibraryData::mandatoryAttributeMissing() +{ + loadCfgFile(":/files/mandatory_attribute_missing.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), false); + qDebug() << result; + + loadCfgFile(":/files/reflection_mandatory_attribute_missing.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), false); + qDebug() << result; + + loadCfgFile(":/files/markup_mandatory_attribute_missing.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), false); + qDebug() << result; +} + +void TestCppcheckLibraryData::podtypeValid() +{ + // Load library data from file + loadCfgFile(":/files/podtype_valid.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), true); + + // Swap library data read from file to other object + libraryData.swap(fileLibraryData); + + // Do size and content checks against swapped data. + QCOMPARE(libraryData.podtypes.size(), 2); + + QCOMPARE(libraryData.podtypes[0].name, QString("bool")); + QCOMPARE(libraryData.podtypes[0].stdtype.isEmpty(), true); + QCOMPARE(libraryData.podtypes[0].sign.isEmpty(), true); + QCOMPARE(libraryData.podtypes[0].size.isEmpty(), true); + + QCOMPARE(libraryData.podtypes[1].name, QString("ulong")); + QCOMPARE(libraryData.podtypes[1].stdtype, QString("uint32_t")); + QCOMPARE(libraryData.podtypes[1].sign, QString("u")); + QCOMPARE(libraryData.podtypes[1].size, QString("4")); + + // Save library data to file + saveCfgFile(TempCfgFile, libraryData); + + fileLibraryData.clear(); + QCOMPARE(fileLibraryData.podtypes.size(), 0); + + // Reload library data from file + loadCfgFile(TempCfgFile, fileLibraryData, result, true); + QCOMPARE(result.isNull(), true); + + // Verify no data got lost or modified + QCOMPARE(libraryData.podtypes.size(), fileLibraryData.podtypes.size()); + QCOMPARE(libraryData.podtypes.size(), 2); + for (int i=0; i < libraryData.podtypes.size(); i++) { + QCOMPARE(libraryData.podtypes[i].name, fileLibraryData.podtypes[i].name); + QCOMPARE(libraryData.podtypes[i].stdtype, fileLibraryData.podtypes[i].stdtype); + QCOMPARE(libraryData.podtypes[i].sign, fileLibraryData.podtypes[i].sign); + QCOMPARE(libraryData.podtypes[i].size, fileLibraryData.podtypes[i].size); + } +} + +void TestCppcheckLibraryData::typechecksValid() +{ + // Load library data from file + loadCfgFile(":/files/typechecks_valid.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), true); + + // Swap library data read from file to other object + libraryData.swap(fileLibraryData); + + // Do size and content checks against swapped data. + QCOMPARE(libraryData.typeChecks.size(), 3); + + CppcheckLibraryData::TypeChecks check = libraryData.typeChecks[0]; + QCOMPARE(check.size(), 2); + QCOMPARE(check[0].first, QString("suppress")); + QCOMPARE(check[0].second, QString("std::insert_iterator")); + QCOMPARE(check[1].first, QString("check")); + QCOMPARE(check[1].second, QString("std::pair")); + + check = libraryData.typeChecks[1]; + QCOMPARE(check.isEmpty(), true); + + check = libraryData.typeChecks[2]; + QCOMPARE(check.size(), 1); + QCOMPARE(check[0].first, QString("check")); + QCOMPARE(check[0].second, QString("std::tuple")); + + // Save library data to file + saveCfgFile(TempCfgFile, libraryData); + + fileLibraryData.clear(); + QCOMPARE(fileLibraryData.typeChecks.size(), 0); + + // Reload library data from file + loadCfgFile(TempCfgFile, fileLibraryData, result, true); + QCOMPARE(result.isNull(), true); + + // Verify no data got lost or modified + QCOMPARE(libraryData.typeChecks.size(), fileLibraryData.typeChecks.size()); + QCOMPARE(libraryData.typeChecks.size(), 3); + for (int idx=0; idx < libraryData.typeChecks.size(); idx++) { + CppcheckLibraryData::TypeChecks lhs = libraryData.typeChecks[idx]; + CppcheckLibraryData::TypeChecks rhs = fileLibraryData.typeChecks[idx]; + QCOMPARE(lhs.size(), rhs.size()); + QCOMPARE(lhs, rhs); + } +} + +void TestCppcheckLibraryData::smartPointerValid() +{ + // Load library data from file + loadCfgFile(":/files/smartptr_valid.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), true); + + // Swap library data read from file to other object + libraryData.swap(fileLibraryData); + + // Do size and content checks against swapped data. + QCOMPARE(libraryData.smartPointers.size(), 3); + + QCOMPARE(libraryData.smartPointers[0].name, QString("wxObjectDataPtr")); + QCOMPARE(libraryData.smartPointers[0].unique, false); + QCOMPARE(libraryData.smartPointers[1].name, QString("wxScopedArray")); + QCOMPARE(libraryData.smartPointers[1].unique, true); + QCOMPARE(libraryData.smartPointers[2].name, QString("wxScopedPtr")); + QCOMPARE(libraryData.smartPointers[2].unique, false); + + // Save library data to file + saveCfgFile(TempCfgFile, libraryData); + + fileLibraryData.clear(); + QCOMPARE(fileLibraryData.smartPointers.size(), 0); + + // Reload library data from file + loadCfgFile(TempCfgFile, fileLibraryData, result, true); + QCOMPARE(result.isNull(), true); + + // Verify no data got lost or modified + QCOMPARE(libraryData.smartPointers.size(), fileLibraryData.smartPointers.size()); + QCOMPARE(libraryData.smartPointers.size(), 3); + for (int idx=0; idx < libraryData.smartPointers.size(); idx++) { + QCOMPARE(libraryData.smartPointers[idx].name, fileLibraryData.smartPointers[idx].name); + QCOMPARE(libraryData.smartPointers[idx].unique, fileLibraryData.smartPointers[idx].unique); + } +} + +void TestCppcheckLibraryData::platformTypeValid() +{ + // Load library data from file + loadCfgFile(":/files/platform_type_valid.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), true); + + // Swap library data read from file to other object + libraryData.swap(fileLibraryData); + + // Do size and content checks against swapped data. + QCOMPARE(libraryData.platformTypes.size(), 3); + + QCOMPARE(libraryData.platformTypes[0].name, QString("platform")); + QCOMPARE(libraryData.platformTypes[0].value, QString("with attribute and empty")); + QCOMPARE(libraryData.platformTypes[0].types.size(), 0); + QCOMPARE(libraryData.platformTypes[0].platforms.size(), 2); + QCOMPARE(libraryData.platformTypes[0].platforms[0], QString("win64")); + QCOMPARE(libraryData.platformTypes[0].platforms[1].isEmpty(), true); + + QCOMPARE(libraryData.platformTypes[1].name, QString("types")); + QCOMPARE(libraryData.platformTypes[1].value, QString("all")); + QCOMPARE(libraryData.platformTypes[1].types.size(), 5); + QCOMPARE(libraryData.platformTypes[1].types, + QStringList({"unsigned", "long", "pointer", "const_ptr", "ptr_ptr"})); + QCOMPARE(libraryData.platformTypes[1].platforms.isEmpty(), true); + + QCOMPARE(libraryData.platformTypes[2].name, QString("types and platform")); + QCOMPARE(libraryData.platformTypes[2].value.isEmpty(), true); + QCOMPARE(libraryData.platformTypes[2].types.size(), 2); + QCOMPARE(libraryData.platformTypes[2].types, QStringList({"pointer", "ptr_ptr"})); + QCOMPARE(libraryData.platformTypes[2].platforms.size(), 1); + QCOMPARE(libraryData.platformTypes[2].platforms[0], QString("win32")); + + // Save library data to file + saveCfgFile(TempCfgFile, libraryData); + + fileLibraryData.clear(); + QCOMPARE(fileLibraryData.platformTypes.size(), 0); + + // Reload library data from file + loadCfgFile(TempCfgFile, fileLibraryData, result, true); + QCOMPARE(result.isNull(), true); + + // Verify no data got lost or modified + QCOMPARE(libraryData.platformTypes.size(), fileLibraryData.platformTypes.size()); + QCOMPARE(libraryData.platformTypes.size(), 3); + for (int idx=0; idx < libraryData.platformTypes.size(); idx++) { + CppcheckLibraryData::PlatformType lhs = libraryData.platformTypes[idx]; + CppcheckLibraryData::PlatformType rhs = fileLibraryData.platformTypes[idx]; + QCOMPARE(lhs.name, rhs.name); + QCOMPARE(lhs.value, rhs.value); + QCOMPARE(lhs.types.size(), rhs.types.size()); + QCOMPARE(lhs.types, rhs.types); + QCOMPARE(lhs.platforms.size(), rhs.platforms.size()); + QCOMPARE(lhs.platforms, rhs.platforms); + } +} + +void TestCppcheckLibraryData::memoryResourceValid() +{ + // Load library data from file + loadCfgFile(":/files/memory_resource_valid.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), true); + + // Swap library data read from file to other object + libraryData.swap(fileLibraryData); + + // Do size and content checks against swapped data. + QCOMPARE(libraryData.memoryresource.size(), 2); + QCOMPARE(libraryData.memoryresource[0].type, QString("memory")); + QCOMPARE(libraryData.memoryresource[0].alloc.size(), 4); + QCOMPARE(libraryData.memoryresource[0].dealloc.size(), 1); + QCOMPARE(libraryData.memoryresource[0].use.size(), 0); + + QCOMPARE(libraryData.memoryresource[0].alloc[0].name, QString("malloc")); + QCOMPARE(libraryData.memoryresource[0].alloc[0].bufferSize, QString("malloc")); + QCOMPARE(libraryData.memoryresource[0].alloc[0].isRealloc, false); + QCOMPARE(libraryData.memoryresource[0].alloc[0].init, false); + QCOMPARE(libraryData.memoryresource[0].alloc[0].arg, -1); + QCOMPARE(libraryData.memoryresource[0].alloc[0].reallocArg, -1); + + QCOMPARE(libraryData.memoryresource[0].alloc[1].name, QString("calloc")); + QCOMPARE(libraryData.memoryresource[0].alloc[1].bufferSize, QString("calloc")); + QCOMPARE(libraryData.memoryresource[0].alloc[1].isRealloc, false); + QCOMPARE(libraryData.memoryresource[0].alloc[1].init, true); + QCOMPARE(libraryData.memoryresource[0].alloc[1].arg, -1); + QCOMPARE(libraryData.memoryresource[0].alloc[1].reallocArg, -1); + + QCOMPARE(libraryData.memoryresource[0].alloc[2].name, QString("realloc")); + QCOMPARE(libraryData.memoryresource[0].alloc[2].bufferSize, QString("malloc:2")); + QCOMPARE(libraryData.memoryresource[0].alloc[2].isRealloc, true); + QCOMPARE(libraryData.memoryresource[0].alloc[2].init, false); + QCOMPARE(libraryData.memoryresource[0].alloc[2].arg, -1); + QCOMPARE(libraryData.memoryresource[0].alloc[2].reallocArg, -1); + + QCOMPARE(libraryData.memoryresource[0].alloc[3].name, QString("UuidToString")); + QCOMPARE(libraryData.memoryresource[0].alloc[3].bufferSize.isEmpty(), true); + QCOMPARE(libraryData.memoryresource[0].alloc[3].isRealloc, false); + QCOMPARE(libraryData.memoryresource[0].alloc[3].init, false); + QCOMPARE(libraryData.memoryresource[0].alloc[3].arg, 2); + QCOMPARE(libraryData.memoryresource[0].alloc[3].reallocArg, -1); + + QCOMPARE(libraryData.memoryresource[0].dealloc[0].name, QString("HeapFree")); + QCOMPARE(libraryData.memoryresource[0].dealloc[0].arg, 3); + + QCOMPARE(libraryData.memoryresource[1].type, QString("resource")); + QCOMPARE(libraryData.memoryresource[1].alloc.size(), 1); + QCOMPARE(libraryData.memoryresource[1].dealloc.size(), 1); + QCOMPARE(libraryData.memoryresource[1].use.size(), 0); + + QCOMPARE(libraryData.memoryresource[1].alloc[0].name, QString("_wfopen_s")); + QCOMPARE(libraryData.memoryresource[1].alloc[0].bufferSize.isEmpty(), true); + QCOMPARE(libraryData.memoryresource[1].alloc[0].isRealloc, false); + QCOMPARE(libraryData.memoryresource[1].alloc[0].init, true); + QCOMPARE(libraryData.memoryresource[1].alloc[0].arg, 1); + QCOMPARE(libraryData.memoryresource[1].alloc[0].reallocArg, -1); + + QCOMPARE(libraryData.memoryresource[1].dealloc[0].name, QString("fclose")); + QCOMPARE(libraryData.memoryresource[1].dealloc[0].arg, -1); + + // Save library data to file + saveCfgFile(TempCfgFile, libraryData); + + fileLibraryData.clear(); + QCOMPARE(fileLibraryData.memoryresource.size(), 0); + + // Reload library data from file + loadCfgFile(TempCfgFile, fileLibraryData, result, true); + QCOMPARE(result.isNull(), true); + + // Verify no data got lost or modified + QCOMPARE(libraryData.memoryresource.size(), fileLibraryData.memoryresource.size()); + QCOMPARE(libraryData.memoryresource.size(), 2); + + for (int idx=0; idx < libraryData.memoryresource.size(); idx++) { + CppcheckLibraryData::MemoryResource lhs = libraryData.memoryresource[idx]; + CppcheckLibraryData::MemoryResource rhs = fileLibraryData.memoryresource[idx]; + + QCOMPARE(lhs.type, rhs.type); + QCOMPARE(lhs.alloc.size(), rhs.alloc.size()); + QCOMPARE(lhs.dealloc.size(), rhs.dealloc.size()); + QCOMPARE(lhs.use, rhs.use); + + for (int num=0; num < lhs.alloc.size(); num++) { + QCOMPARE(lhs.alloc[num].name, rhs.alloc[num].name); + QCOMPARE(lhs.alloc[num].bufferSize, rhs.alloc[num].bufferSize); + QCOMPARE(lhs.alloc[num].isRealloc, rhs.alloc[num].isRealloc); + QCOMPARE(lhs.alloc[num].init, rhs.alloc[num].init); + QCOMPARE(lhs.alloc[num].arg, rhs.alloc[num].arg); + QCOMPARE(lhs.alloc[num].reallocArg, rhs.alloc[num].reallocArg); + } + for (int num=0; num < lhs.dealloc.size(); num++) { + QCOMPARE(lhs.dealloc[num].name, rhs.dealloc[num].name); + QCOMPARE(lhs.dealloc[num].arg, rhs.dealloc[num].arg); + } + } +} + +void TestCppcheckLibraryData::defineValid() +{ + // Load library data from file + loadCfgFile(":/files/define_valid.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), true); + + // Swap library data read from file to other object + libraryData.swap(fileLibraryData); + + // Do size and content checks against swapped data. + QCOMPARE(libraryData.defines.size(), 2); + QCOMPARE(libraryData.defines[0].name, QString("INT8_MIN")); + QCOMPARE(libraryData.defines[0].value, QString("-128")); + QCOMPARE(libraryData.defines[1].name.isEmpty(), true); + QCOMPARE(libraryData.defines[1].value.isEmpty(), true); + + // Save library data to file + saveCfgFile(TempCfgFile, libraryData); + + fileLibraryData.clear(); + QCOMPARE(fileLibraryData.defines.size(), 0); + + // Reload library data from file + loadCfgFile(TempCfgFile, fileLibraryData, result, true); + QCOMPARE(result.isNull(), true); + + // Verify no data got lost or modified + QCOMPARE(libraryData.defines.size(), fileLibraryData.defines.size()); + QCOMPARE(libraryData.defines.size(), 2); + for (int idx=0; idx < libraryData.defines.size(); idx++) { + QCOMPARE(libraryData.defines[idx].name, fileLibraryData.defines[idx].name); + QCOMPARE(libraryData.defines[idx].value, fileLibraryData.defines[idx].value); + } +} + +void TestCppcheckLibraryData::undefineValid() +{ + // Load library data from file + loadCfgFile(":/files/undefine_valid.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), true); + + // Swap library data read from file to other object + libraryData.swap(fileLibraryData); + + // Do size and content checks against swapped data. + QCOMPARE(libraryData.undefines.size(), 2); + QCOMPARE(libraryData.undefines[0], QString("INT8_MIN")); + QCOMPARE(libraryData.undefines[1].isEmpty(), true); + + // Save library data to file + saveCfgFile(TempCfgFile, libraryData); + + fileLibraryData.clear(); + QCOMPARE(fileLibraryData.undefines.size(), 0); + + // Reload library data from file + loadCfgFile(TempCfgFile, fileLibraryData, result, true); + QCOMPARE(result.isNull(), true); + + // Verify no data got lost or modified + QCOMPARE(libraryData.undefines.size(), fileLibraryData.undefines.size()); + QCOMPARE(libraryData.undefines.size(), 2); + QCOMPARE(libraryData.undefines, fileLibraryData.undefines); +} + +void TestCppcheckLibraryData::reflectionValid() +{ + // Load library data from file + loadCfgFile(":/files/reflection_valid.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), true); + + // Swap library data read from file to other object + libraryData.swap(fileLibraryData); + + // Do size and content checks against swapped data. + QCOMPARE(libraryData.reflections.size(), 2); + QCOMPARE(libraryData.reflections[0].calls.size(), 2); + QCOMPARE(libraryData.reflections[0].calls[0].arg, 2); + QCOMPARE(libraryData.reflections[0].calls[0].name, QString("invokeMethod")); + QCOMPARE(libraryData.reflections[0].calls[1].arg, 1); + QCOMPARE(libraryData.reflections[0].calls[1].name, QString("callFunction")); + QCOMPARE(libraryData.reflections[1].calls.isEmpty(), true); + + // Save library data to file + saveCfgFile(TempCfgFile, libraryData); + + fileLibraryData.clear(); + QCOMPARE(fileLibraryData.reflections.size(), 0); + + // Reload library data from file + loadCfgFile(TempCfgFile, fileLibraryData, result, true); + QCOMPARE(result.isNull(), true); + + // Verify no data got lost or modified + QCOMPARE(libraryData.reflections.size(), fileLibraryData.reflections.size()); + QCOMPARE(libraryData.reflections.size(), 2); + for (int idx=0; idx < libraryData.reflections.size(); idx++) { + CppcheckLibraryData::Reflection lhs = libraryData.reflections[idx]; + CppcheckLibraryData::Reflection rhs = fileLibraryData.reflections[idx]; + + QCOMPARE(lhs.calls.size(), rhs.calls.size()); + for (int num=0; num < lhs.calls.size(); num++) { + QCOMPARE(lhs.calls[num].arg, rhs.calls[num].arg); + QCOMPARE(lhs.calls[num].name, rhs.calls[num].name); + } + } +} + +void TestCppcheckLibraryData::markupValid() +{ + // Load library data from file + loadCfgFile(":/files/markup_valid.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), true); + + // Swap library data read from file to other object + libraryData.swap(fileLibraryData); + + // Do size and content checks against swapped data. + QCOMPARE(libraryData.markups.size(), 1); + QCOMPARE(libraryData.markups[0].ext, QString(".qml")); + QCOMPARE(libraryData.markups[0].reportErrors, false); + QCOMPARE(libraryData.markups[0].afterCode, true); + + QCOMPARE(libraryData.markups[0].keywords.size(), 4); + QCOMPARE(libraryData.markups[0].keywords, QStringList({"if", "while", "typeof", "for"})); + + QCOMPARE(libraryData.markups[0].importer.size(), 1); + QCOMPARE(libraryData.markups[0].importer, QStringList("connect")); + + QCOMPARE(libraryData.markups[0].exporter.size(), 1); + QCOMPARE(libraryData.markups[0].exporter[0].prefix, QString("Q_PROPERTY")); + QCOMPARE(libraryData.markups[0].exporter[0].suffixList.size(), 1); + QCOMPARE(libraryData.markups[0].exporter[0].suffixList, QStringList("READ")); + QCOMPARE(libraryData.markups[0].exporter[0].prefixList.size(), 3); + QCOMPARE(libraryData.markups[0].exporter[0].prefixList, QStringList({"READ", "WRITE", "NOTIFY"})); + + QCOMPARE(libraryData.markups[0].codeBlocks.size(), 2); + QCOMPARE(libraryData.markups[0].codeBlocks[0].blocks.size(), 5); + QCOMPARE(libraryData.markups[0].codeBlocks[0].blocks, QStringList({"onClicked", "onFinished", "onTriggered", "onPressed", "onTouch"})); + QCOMPARE(libraryData.markups[0].codeBlocks[0].offset, 3); + QCOMPARE(libraryData.markups[0].codeBlocks[0].start, QString("{")); + QCOMPARE(libraryData.markups[0].codeBlocks[0].end, QString("}")); + QCOMPARE(libraryData.markups[0].codeBlocks[1].blocks.size(), 1); + QCOMPARE(libraryData.markups[0].codeBlocks[1].blocks, QStringList("function")); + QCOMPARE(libraryData.markups[0].codeBlocks[1].offset, 2); + QCOMPARE(libraryData.markups[0].codeBlocks[1].start, QString("{")); + QCOMPARE(libraryData.markups[0].codeBlocks[1].end, QString("}")); + + // Save library data to file + saveCfgFile(TempCfgFile, libraryData); + + fileLibraryData.clear(); + QCOMPARE(fileLibraryData.markups.size(), 0); + + // Reload library data from file + loadCfgFile(TempCfgFile, fileLibraryData, result, true); + QCOMPARE(result.isNull(), true); + + // Verify no data got lost or modified + QCOMPARE(libraryData.markups.size(), fileLibraryData.markups.size()); + for (int idx=0; idx < libraryData.markups.size(); idx++) { + CppcheckLibraryData::Markup lhs = libraryData.markups[idx]; + CppcheckLibraryData::Markup rhs = fileLibraryData.markups[idx]; + + QCOMPARE(lhs.ext, rhs.ext); + QCOMPARE(lhs.reportErrors, rhs.reportErrors); + QCOMPARE(lhs.afterCode, rhs.afterCode); + QCOMPARE(lhs.keywords, rhs.keywords); + QCOMPARE(lhs.importer, rhs.importer); + for (int num=0; num < lhs.exporter.size(); num++) { + QCOMPARE(lhs.exporter[num].prefix, rhs.exporter[num].prefix); + QCOMPARE(lhs.exporter[num].suffixList, rhs.exporter[num].suffixList); + QCOMPARE(lhs.exporter[num].prefixList, rhs.exporter[num].prefixList); + } + for (int num=0; num < lhs.codeBlocks.size(); num++) { + QCOMPARE(lhs.codeBlocks[num].blocks, rhs.codeBlocks[num].blocks); + QCOMPARE(lhs.codeBlocks[num].offset, rhs.codeBlocks[num].offset); + QCOMPARE(lhs.codeBlocks[num].start, rhs.codeBlocks[num].start); + QCOMPARE(lhs.codeBlocks[num].end, rhs.codeBlocks[num].end); + } + } +} + +void TestCppcheckLibraryData::containerValid() +{ + // Load library data from file + loadCfgFile(":/files/container_valid.cfg", fileLibraryData, result); + QCOMPARE(result.isNull(), true); + + // Swap library data read from file to other object + libraryData.swap(fileLibraryData); + + // Do size and content checks against swapped data. + QCOMPARE(libraryData.containers.size(), 1); + + QCOMPARE(libraryData.containers[0].rangeItemRecordTypeList.size(), 2); + QCOMPARE(libraryData.containers[0].rangeItemRecordTypeList[0].name, QString("first")); + QCOMPARE(libraryData.containers[0].rangeItemRecordTypeList[0].templateParameter, QString("0")); + QCOMPARE(libraryData.containers[0].rangeItemRecordTypeList[1].name, QString("second")); + QCOMPARE(libraryData.containers[0].rangeItemRecordTypeList[1].templateParameter, QString("1")); + + // Save library data to file + saveCfgFile(TempCfgFile, libraryData); + + fileLibraryData.clear(); + QCOMPARE(fileLibraryData.containers.size(), 0); + + // Reload library data from file + loadCfgFile(TempCfgFile, fileLibraryData, result, true); + QCOMPARE(result.isNull(), true); + + // Verify no data got lost or modified + QCOMPARE(libraryData.containers.size(), fileLibraryData.containers.size()); + for (int idx=0; idx < libraryData.containers.size(); idx++) { + CppcheckLibraryData::Container lhs = libraryData.containers[idx]; + CppcheckLibraryData::Container rhs = fileLibraryData.containers[idx]; + for (int num=0; num < lhs.rangeItemRecordTypeList.size(); num++) { + QCOMPARE(lhs.rangeItemRecordTypeList[num].name, rhs.rangeItemRecordTypeList[num].name); + QCOMPARE(lhs.rangeItemRecordTypeList[num].templateParameter, rhs.rangeItemRecordTypeList[num].templateParameter); + } + } +} + +void TestCppcheckLibraryData::loadCfgFile(const QString &filename, CppcheckLibraryData &data, QString &res, bool removeFile) +{ + QFile file(filename); + QVERIFY(file.open(QIODevice::ReadOnly | QIODevice::Text)); + res = data.open(file); + file.close(); + if (removeFile) { + file.remove(); + } +} + +void TestCppcheckLibraryData::saveCfgFile(const QString &filename, CppcheckLibraryData &data) +{ + QFile file(filename); + QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text)); + QTextStream textStream(&file); + textStream << data.toString() << '\n'; + file.close(); +} + +void TestCppcheckLibraryData::validateAllCfg() +{ + const QDir dir(QString(SRCDIR) + "/../../../cfg/"); + const QStringList files = dir.entryList(QStringList() << "*.cfg",QDir::Files); + QVERIFY(!files.empty()); + bool error = false; + for (const QString& f : files) { + loadCfgFile(dir.absolutePath() + "/" + f, fileLibraryData, result); + if (!result.isNull()) { + error = true; + qDebug() << f << " - " << result; + } + } + QCOMPARE(error, false); +} + +QTEST_MAIN(TestCppcheckLibraryData) diff --git a/cppcheck-2.14.0/gui/test/cppchecklibrarydata/testcppchecklibrarydata.h b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/testcppchecklibrarydata.h new file mode 100644 index 00000000..079436f5 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/cppchecklibrarydata/testcppchecklibrarydata.h @@ -0,0 +1,56 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2021 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "cppchecklibrarydata.h" + +#include +#include + +class TestCppcheckLibraryData : public QObject { + Q_OBJECT + +private slots: + void init(); + + void xmlReaderError(); + void unhandledElement(); + void mandatoryAttributeMissing(); + + void podtypeValid(); + void typechecksValid(); + void smartPointerValid(); + void platformTypeValid(); + void memoryResourceValid(); + void defineValid(); + void undefineValid(); + void reflectionValid(); + void markupValid(); + void containerValid(); + + void validateAllCfg(); + +private: + static void loadCfgFile(const QString &filename, CppcheckLibraryData &data, QString &res, bool removeFile = false); + static void saveCfgFile(const QString &filename, CppcheckLibraryData &data); + + CppcheckLibraryData libraryData; + CppcheckLibraryData fileLibraryData; + QString result; + + static const QString TempCfgFile; +}; diff --git a/cppcheck-2.14.0/gui/test/data/files/bar1 b/cppcheck-2.14.0/gui/test/data/files/bar1 new file mode 100644 index 00000000..e4294ad5 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/files/bar1 @@ -0,0 +1 @@ +Dummy test file. diff --git a/cppcheck-2.14.0/gui/test/data/files/bar1.foo b/cppcheck-2.14.0/gui/test/data/files/bar1.foo new file mode 100644 index 00000000..e4294ad5 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/files/bar1.foo @@ -0,0 +1 @@ +Dummy test file. diff --git a/cppcheck-2.14.0/gui/test/data/files/dir1/dir11/foo11.cpp b/cppcheck-2.14.0/gui/test/data/files/dir1/dir11/foo11.cpp new file mode 100644 index 00000000..803876e0 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/files/dir1/dir11/foo11.cpp @@ -0,0 +1 @@ +Dummy test file . diff --git a/cppcheck-2.14.0/gui/test/data/files/dir1/foo1.cpp b/cppcheck-2.14.0/gui/test/data/files/dir1/foo1.cpp new file mode 100644 index 00000000..803876e0 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/files/dir1/foo1.cpp @@ -0,0 +1 @@ +Dummy test file . diff --git a/cppcheck-2.14.0/gui/test/data/files/dir2/foo1.cpp b/cppcheck-2.14.0/gui/test/data/files/dir2/foo1.cpp new file mode 100644 index 00000000..803876e0 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/files/dir2/foo1.cpp @@ -0,0 +1 @@ +Dummy test file . diff --git a/cppcheck-2.14.0/gui/test/data/files/foo1.cpp b/cppcheck-2.14.0/gui/test/data/files/foo1.cpp new file mode 100644 index 00000000..803876e0 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/files/foo1.cpp @@ -0,0 +1 @@ +Dummy test file . diff --git a/cppcheck-2.14.0/gui/test/data/files/foo2.cxx b/cppcheck-2.14.0/gui/test/data/files/foo2.cxx new file mode 100644 index 00000000..e4294ad5 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/files/foo2.cxx @@ -0,0 +1 @@ +Dummy test file. diff --git a/cppcheck-2.14.0/gui/test/data/files/foo3.cc b/cppcheck-2.14.0/gui/test/data/files/foo3.cc new file mode 100644 index 00000000..e4294ad5 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/files/foo3.cc @@ -0,0 +1 @@ +Dummy test file. diff --git a/cppcheck-2.14.0/gui/test/data/files/foo4.c b/cppcheck-2.14.0/gui/test/data/files/foo4.c new file mode 100644 index 00000000..803876e0 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/files/foo4.c @@ -0,0 +1 @@ +Dummy test file . diff --git a/cppcheck-2.14.0/gui/test/data/files/foo5.c++ b/cppcheck-2.14.0/gui/test/data/files/foo5.c++ new file mode 100644 index 00000000..e4294ad5 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/files/foo5.c++ @@ -0,0 +1 @@ +Dummy test file. diff --git a/cppcheck-2.14.0/gui/test/data/files/foo6.txx b/cppcheck-2.14.0/gui/test/data/files/foo6.txx new file mode 100644 index 00000000..e4294ad5 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/files/foo6.txx @@ -0,0 +1 @@ +Dummy test file. diff --git a/cppcheck-2.14.0/gui/test/data/files/foo7.tpp b/cppcheck-2.14.0/gui/test/data/files/foo7.tpp new file mode 100644 index 00000000..e4294ad5 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/files/foo7.tpp @@ -0,0 +1 @@ +Dummy test file. diff --git a/cppcheck-2.14.0/gui/test/data/files/foo8.ipp b/cppcheck-2.14.0/gui/test/data/files/foo8.ipp new file mode 100644 index 00000000..e4294ad5 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/files/foo8.ipp @@ -0,0 +1 @@ +Dummy test file. diff --git a/cppcheck-2.14.0/gui/test/data/files/foo9.ixx b/cppcheck-2.14.0/gui/test/data/files/foo9.ixx new file mode 100644 index 00000000..e4294ad5 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/files/foo9.ixx @@ -0,0 +1 @@ +Dummy test file. diff --git a/cppcheck-2.14.0/gui/test/data/projectfiles/simple.cppcheck b/cppcheck-2.14.0/gui/test/data/projectfiles/simple.cppcheck new file mode 100644 index 00000000..d73eb9ae --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/projectfiles/simple.cppcheck @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/gui/test/data/projectfiles/simple_ignore.cppcheck b/cppcheck-2.14.0/gui/test/data/projectfiles/simple_ignore.cppcheck new file mode 100644 index 00000000..7e2ae6c9 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/projectfiles/simple_ignore.cppcheck @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/gui/test/data/projectfiles/simple_noroot.cppcheck b/cppcheck-2.14.0/gui/test/data/projectfiles/simple_noroot.cppcheck new file mode 100644 index 00000000..94a7d20b --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/projectfiles/simple_noroot.cppcheck @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/gui/test/data/xmlfiles/xmlreport_v2.xml b/cppcheck-2.14.0/gui/test/data/xmlfiles/xmlreport_v2.xml new file mode 100644 index 00000000..31af1a92 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/data/xmlfiles/xmlreport_v2.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/gui/test/filelist/CMakeLists.txt b/cppcheck-2.14.0/gui/test/filelist/CMakeLists.txt new file mode 100644 index 00000000..4131b331 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/filelist/CMakeLists.txt @@ -0,0 +1,25 @@ +qt_wrap_cpp(test-filelist_SRC testfilelist.h) +add_custom_target(build-testfilelist-deps SOURCES ${test-filelist_SRC}) +add_dependencies(gui-build-deps build-testfilelist-deps) +add_executable(test-filelist + ${test-filelist_SRC} + testfilelist.cpp + ${CMAKE_SOURCE_DIR}/gui/filelist.cpp + ${CMAKE_SOURCE_DIR}/lib/pathmatch.cpp + ${CMAKE_SOURCE_DIR}/lib/path.cpp + ${CMAKE_SOURCE_DIR}/lib/utils.cpp + $ + ) +target_include_directories(test-filelist PRIVATE ${CMAKE_SOURCE_DIR}/gui ${CMAKE_SOURCE_DIR}/lib) +target_externals_include_directories(test-filelist PRIVATE ${CMAKE_SOURCE_DIR}/externals/simplecpp) +target_compile_definitions(test-filelist PRIVATE SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(test-filelist ${QT_CORE_LIB} ${QT_TEST_LIB}) + +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # Q_UNUSED() in generated code + target_compile_options_safe(test-filelist -Wno-extra-semi-stmt) +endif() + +if (REGISTER_GUI_TESTS) + add_test(NAME test-filelist COMMAND $) +endif() \ No newline at end of file diff --git a/cppcheck-2.14.0/gui/test/filelist/filelist.pro b/cppcheck-2.14.0/gui/test/filelist/filelist.pro new file mode 100644 index 00000000..110746a5 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/filelist/filelist.pro @@ -0,0 +1,29 @@ +TEMPLATE = app +TARGET = test-filelist +DEPENDPATH += . +INCLUDEPATH += . ../../../externals/simplecpp +OBJECTS_DIR = ../../temp +MOC_DIR = ../../temp + +QT -= gui +QT += core +QT += testlib + +include(../common.pri) + +DEFINES += SRCDIR=\\\"$$PWD\\\" + +# tests +SOURCES += testfilelist.cpp \ + ../../filelist.cpp \ + ../../../lib/pathmatch.cpp \ + ../../../lib/path.cpp \ + ../../../lib/utils.cpp \ + ../../../externals/simplecpp/simplecpp.cpp + +HEADERS += testfilelist.h \ + ../../filelist.h \ + ../../../lib/pathmatch.h \ + ../../../lib/path.h \ + ../../../lib/utils.h \ + ../../../externals/simplecpp/simplecpp.h diff --git a/cppcheck-2.14.0/gui/test/filelist/testfilelist.cpp b/cppcheck-2.14.0/gui/test/filelist/testfilelist.cpp new file mode 100644 index 00000000..d24b766b --- /dev/null +++ b/cppcheck-2.14.0/gui/test/filelist/testfilelist.cpp @@ -0,0 +1,187 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2021 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "testfilelist.h" + +#include "filelist.h" + +#include +#include +#include +#include + +void TestFileList::addFile() const +{ + // Accepted extensions: *.cpp, *.cxx, *.cc, *.c, *.c++, *.txx, *.tpp, *.ipp, *.ixx" + 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"); + list.addFile(QString(SRCDIR) + "/../data/files/foo8.ipp"); + list.addFile(QString(SRCDIR) + "/../data/files/foo9.ixx"); + QStringList files = list.getFileList(); + QCOMPARE(files.size(), 9); +} + +void TestFileList::addPathList() const +{ + // Accepted extensions: *.cpp, *.cxx, *.cc, *.c, *.c++, *.txx, *.tpp, *.ipp, *.ixx" + 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"; + paths << QString(SRCDIR) + "/../data/files/foo8.ipp"; + paths << QString(SRCDIR) + "/../data/files/foo9.ixx"; + FileList list; + list.addPathList(paths); + QStringList files = list.getFileList(); + QCOMPARE(files.size(), 9); +} + +void TestFileList::addFile_notexist() const +{ + FileList list; + list.addFile(QString(SRCDIR) + "/../data/files/bar1.cpp"); + QStringList files = list.getFileList(); + QCOMPARE(files.size(), 0); +} + +void TestFileList::addFile_unknown() const +{ + 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() const +{ + FileList list; + list.addDirectory(QString(SRCDIR) + "/../data/files"); + QStringList files = list.getFileList(); + QCOMPARE(files.size(), 9); +} + +void TestFileList::addDirectory_recursive() const +{ + FileList list; + list.addDirectory(QString(SRCDIR) + "/../data/files", true); + QStringList files = list.getFileList(); + QCOMPARE(files.size(), 12); + 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() const +{ + 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"); + list.addFile(QString(SRCDIR) + "/../data/files/foo8.ipp"); + list.addFile(QString(SRCDIR) + "/../data/files/foo9.ixx"); + QStringList files = list.getFileList(); + QCOMPARE(files.size(), 7); + 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() const +{ + 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(), 7); + 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() const +{ + 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(), 8); + 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() const +{ + FileList list; + QStringList filters; + filters << "dir1/"; + list.addExcludeList(filters); + 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")); +} + +void TestFileList::filterFiles5() const +{ + 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(), 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")); +} + +QTEST_MAIN(TestFileList) diff --git a/cppcheck-2.14.0/gui/test/filelist/testfilelist.h b/cppcheck-2.14.0/gui/test/filelist/testfilelist.h new file mode 100644 index 00000000..10894999 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/filelist/testfilelist.h @@ -0,0 +1,37 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2021 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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() const; + void addPathList() const; + void addFile_notexist() const; + void addFile_unknown() const; + void addDirectory() const; + void addDirectory_recursive() const; + void filterFiles() const; + void filterFiles2() const; + void filterFiles3() const; + void filterFiles4() const; + void filterFiles5() const; +}; diff --git a/cppcheck-2.14.0/gui/test/projectfile/CMakeLists.txt b/cppcheck-2.14.0/gui/test/projectfile/CMakeLists.txt new file mode 100644 index 00000000..1e87916f --- /dev/null +++ b/cppcheck-2.14.0/gui/test/projectfile/CMakeLists.txt @@ -0,0 +1,20 @@ +qt_wrap_cpp(test-projectfile_SRC testprojectfile.h ${CMAKE_SOURCE_DIR}/gui/projectfile.h) +add_custom_target(build-projectfile-deps SOURCES ${test-projectfile_SRC}) +add_dependencies(gui-build-deps build-projectfile-deps) +add_executable(test-projectfile + ${test-projectfile_SRC} + testprojectfile.cpp + ${CMAKE_SOURCE_DIR}/gui/projectfile.cpp + ) +target_include_directories(test-projectfile PRIVATE ${CMAKE_SOURCE_DIR}/gui ${CMAKE_SOURCE_DIR}/lib) +target_compile_definitions(test-projectfile PRIVATE SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(test-projectfile ${QT_CORE_LIB} ${QT_TEST_LIB}) + +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # Q_UNUSED() in generated code + target_compile_options_safe(test-projectfile -Wno-extra-semi-stmt) +endif() + +if (REGISTER_GUI_TESTS) + add_test(NAME test-projectfile COMMAND $) +endif() \ No newline at end of file diff --git a/cppcheck-2.14.0/gui/test/projectfile/projectfile.pro b/cppcheck-2.14.0/gui/test/projectfile/projectfile.pro new file mode 100644 index 00000000..68555272 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/projectfile/projectfile.pro @@ -0,0 +1,22 @@ +TEMPLATE = app +TARGET = test-projectfile +DEPENDPATH += . +INCLUDEPATH += . ../../../externals/simplecpp ../../../externals/tinyxml2 ../../../externals/picojson +OBJECTS_DIR = ../../temp +MOC_DIR = ../../temp + +QT -= gui +QT += core +QT += testlib + +include(../common.pri) + +DEFINES += SRCDIR=\\\"$$PWD\\\" + +# tests +SOURCES += testprojectfile.cpp \ + ../../projectfile.cpp + +HEADERS += testprojectfile.h \ + ../../projectfile.h \ + ../../../externals/picojson/picojson.h diff --git a/cppcheck-2.14.0/gui/test/projectfile/testprojectfile.cpp b/cppcheck-2.14.0/gui/test/projectfile/testprojectfile.cpp new file mode 100644 index 00000000..5c69917e --- /dev/null +++ b/cppcheck-2.14.0/gui/test/projectfile/testprojectfile.cpp @@ -0,0 +1,173 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2021 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "testprojectfile.h" + +#include "importproject.h" +#include "platform.h" +#include "projectfile.h" +#include "settings.h" +#include "suppressions.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +// Mock... +const char Settings::SafeChecks::XmlRootName[] = "safe-checks"; +const char Settings::SafeChecks::XmlClasses[] = "class-public"; +const char Settings::SafeChecks::XmlExternalFunctions[] = "external-functions"; +const char Settings::SafeChecks::XmlInternalFunctions[] = "internal-functions"; +const char Settings::SafeChecks::XmlExternalVariables[] = "external-variables"; +Settings::Settings() : maxCtuDepth(10) {} +Platform::Platform() = default; +bool ImportProject::sourceFileExists(const std::string & /*file*/) { + return true; +} + +void TestProjectFile::loadInexisting() const +{ + const QString filepath(QString(SRCDIR) + "/../data/projectfiles/foo.cppcheck"); + ProjectFile pfile(filepath); + QCOMPARE(pfile.read(), false); +} + +void TestProjectFile::loadSimple() const +{ + 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 +{ + 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 +{ + 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")); +} + +void TestProjectFile::getAddonFilePath() const +{ + QTemporaryDir tempdir; + QVERIFY(tempdir.isValid()); + const QString filepath(tempdir.path() + "/addon.py"); + + QFile file(filepath); + QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text)); + file.close(); + + // Relative path to addon + QCOMPARE(ProjectFile::getAddonFilePath(tempdir.path(), "addon"), filepath); + QCOMPARE(ProjectFile::getAddonFilePath(tempdir.path(), "not exist"), QString()); + + // Absolute path to addon + QCOMPARE(ProjectFile::getAddonFilePath("/not/exist", filepath), filepath); + QCOMPARE(ProjectFile::getAddonFilePath(tempdir.path(), filepath), filepath); +} + +void TestProjectFile::getCheckingSuppressionsRelative() const +{ + const SuppressionList::Suppression suppression("*", "externals/*"); + const QList suppressions{suppression}; + ProjectFile projectFile; + projectFile.setFilename("/some/path/123.cppcheck"); + projectFile.setSuppressions(suppressions); + QCOMPARE(projectFile.getCheckingSuppressions()[0].fileName, "/some/path/externals/*"); +} + +void TestProjectFile::getCheckingSuppressionsAbsolute() const +{ + const SuppressionList::Suppression suppression("*", "/some/path/1.h"); + const QList suppressions{suppression}; + ProjectFile projectFile; + projectFile.setFilename("/other/123.cppcheck"); + projectFile.setSuppressions(suppressions); + QCOMPARE(projectFile.getCheckingSuppressions()[0].fileName, "/some/path/1.h"); +} + +void TestProjectFile::getCheckingSuppressionsStar() const +{ + const SuppressionList::Suppression suppression("*", "*.cpp"); + const QList suppressions{suppression}; + ProjectFile projectFile; + projectFile.setFilename("/some/path/123.cppcheck"); + projectFile.setSuppressions(suppressions); + QCOMPARE(projectFile.getCheckingSuppressions()[0].fileName, "*.cpp"); +} + +QTEST_MAIN(TestProjectFile) + diff --git a/cppcheck-2.14.0/gui/test/projectfile/testprojectfile.h b/cppcheck-2.14.0/gui/test/projectfile/testprojectfile.h new file mode 100644 index 00000000..7da59002 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/projectfile/testprojectfile.h @@ -0,0 +1,36 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2021 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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() const; + void loadSimple() const; + void loadSimpleWithIgnore() const; + void loadSimpleNoroot() const; + + void getAddonFilePath() const; + + void getCheckingSuppressionsRelative() const; + void getCheckingSuppressionsAbsolute() const; + void getCheckingSuppressionsStar() const; +}; diff --git a/cppcheck-2.14.0/gui/test/readme.txt b/cppcheck-2.14.0/gui/test/readme.txt new file mode 100644 index 00000000..b02dcbca --- /dev/null +++ b/cppcheck-2.14.0/gui/test/readme.txt @@ -0,0 +1,27 @@ +GUI 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. + diff --git a/cppcheck-2.14.0/gui/test/test.pro b/cppcheck-2.14.0/gui/test/test.pro new file mode 100644 index 00000000..7f75e371 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/test.pro @@ -0,0 +1,9 @@ +#lessThan(QT_MAJOR_VERSION, 5): error(requires >= Qt 5 (You used: $$QT_VERSION)) + +CONFIG += ordered +TEMPLATE = subdirs + +SUBDIRS = \ + filelist \ + projectfile \ + xmlreportv2 diff --git a/cppcheck-2.14.0/gui/test/translationhandler/CMakeLists.txt b/cppcheck-2.14.0/gui/test/translationhandler/CMakeLists.txt new file mode 100644 index 00000000..35cbf3c0 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/translationhandler/CMakeLists.txt @@ -0,0 +1,21 @@ +qt_wrap_cpp(test-translationhandler_SRC testtranslationhandler.h ${CMAKE_SOURCE_DIR}/gui/translationhandler.h) +add_custom_target(build-translationhandler-deps SOURCES ${test-translationhandler_SRC}) +add_dependencies(gui-build-deps build-translationhandler-deps) +add_executable(test-translationhandler + ${test-translationhandler_SRC} + testtranslationhandler.cpp + ${CMAKE_SOURCE_DIR}/gui/common.cpp + ${CMAKE_SOURCE_DIR}/gui/translationhandler.cpp + ) +target_include_directories(test-translationhandler PRIVATE ${CMAKE_SOURCE_DIR}/gui ${CMAKE_SOURCE_DIR}/lib) +target_link_libraries(test-translationhandler ${QT_CORE_LIB} ${QT_WIDGETS_LIB} ${QT_TEST_LIB}) + +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # Q_UNUSED() in generated code + target_compile_options_safe(test-translationhandler -Wno-extra-semi-stmt) +endif() + +if (REGISTER_GUI_TESTS) + # TODO: requires X session + #add_test(NAME test-translationhandler COMMAND $) +endif() \ No newline at end of file diff --git a/cppcheck-2.14.0/gui/test/translationhandler/testtranslationhandler.cpp b/cppcheck-2.14.0/gui/test/translationhandler/testtranslationhandler.cpp new file mode 100644 index 00000000..7abb1c8f --- /dev/null +++ b/cppcheck-2.14.0/gui/test/translationhandler/testtranslationhandler.cpp @@ -0,0 +1,43 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2021 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "testtranslationhandler.h" + +#include "translationhandler.h" + +#include +#include +#include + +static QStringList getTranslationNames(const TranslationHandler& handler) +{ + QStringList names; + for (const TranslationInfo& translation : handler.getTranslations()) { + names.append(translation.mName); + } + return names; +} + +void TestTranslationHandler::construct() const +{ + TranslationHandler handler; + QCOMPARE(getTranslationNames(handler).size(), 13); // 12 translations + english + QCOMPARE(handler.getCurrentLanguage(), QString("en")); +} + +QTEST_MAIN(TestTranslationHandler) diff --git a/cppcheck-2.14.0/gui/test/translationhandler/testtranslationhandler.h b/cppcheck-2.14.0/gui/test/translationhandler/testtranslationhandler.h new file mode 100644 index 00000000..45bd1ae5 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/translationhandler/testtranslationhandler.h @@ -0,0 +1,27 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2021 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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() const; +}; diff --git a/cppcheck-2.14.0/gui/test/translationhandler/translationhandler.pro b/cppcheck-2.14.0/gui/test/translationhandler/translationhandler.pro new file mode 100644 index 00000000..bc4468ef --- /dev/null +++ b/cppcheck-2.14.0/gui/test/translationhandler/translationhandler.pro @@ -0,0 +1,22 @@ +TEMPLATE = app +TARGET = test-translationhandler +DEPENDPATH += . +INCLUDEPATH += . +OBJECTS_DIR = ../../temp +MOC_DIR = ../../temp + +QT -= gui +QT += core +QT += widgets # TODO: get rid of this - causes X server dependency +QT += testlib + +include(../common.pri) + +# tests +SOURCES += testtranslationhandler.cpp \ + ../../translationhandler.cpp \ + ../../common.cpp + +HEADERS += testtranslationhandler.h \ + ../../translationhandler.h \ + ../../common.h diff --git a/cppcheck-2.14.0/gui/test/xmlreportv2/CMakeLists.txt b/cppcheck-2.14.0/gui/test/xmlreportv2/CMakeLists.txt new file mode 100644 index 00000000..b1c66660 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/xmlreportv2/CMakeLists.txt @@ -0,0 +1,37 @@ +qt_wrap_cpp(test-xmlreportv2_SRC testxmlreportv2.h) +add_custom_target(build-xmlreportv2-deps SOURCES ${test-xmlreportv2_SRC}) +add_dependencies(gui-build-deps build-xmlreportv2-deps) +if (NOT BUILD_CORE_DLL) + list(APPEND test-xmlreportv2_SRC $ $) + if(USE_BUNDLED_TINYXML2) + list(APPEND test-xmlreportv2_SRC $) + endif() +endif() +add_executable(test-xmlreportv2 + ${test-xmlreportv2_SRC} + testxmlreportv2.cpp + ${CMAKE_SOURCE_DIR}/gui/erroritem.cpp + ${CMAKE_SOURCE_DIR}/gui/report.cpp + ${CMAKE_SOURCE_DIR}/gui/xmlreport.cpp + ${CMAKE_SOURCE_DIR}/gui/xmlreportv2.cpp + ) +target_include_directories(test-xmlreportv2 PRIVATE ${CMAKE_SOURCE_DIR}/gui ${CMAKE_SOURCE_DIR}/lib) +target_compile_definitions(test-xmlreportv2 PRIVATE SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(test-xmlreportv2 ${QT_CORE_LIB} ${QT_TEST_LIB}) +if (HAVE_RULES) + target_link_libraries(test-xmlreportv2 ${PCRE_LIBRARY}) +endif() +if(tinyxml2_FOUND AND NOT USE_BUNDLED_TINYXML2) + target_link_libraries(test-xmlreportv2 ${tinyxml2_LIBRARIES}) +endif() +if (BUILD_CORE_DLL) + target_compile_definitions(test-xmlreportv2 PRIVATE CPPCHECKLIB_IMPORT TINYXML2_IMPORT) + target_link_libraries(test-xmlreportv2 cppcheck-core) +endif() +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options_safe(test-xmlreportv2 -Wno-extra-semi-stmt) +endif() + +if (REGISTER_GUI_TESTS) + add_test(NAME test-xmlreportv2 COMMAND $) +endif() diff --git a/cppcheck-2.14.0/gui/test/xmlreportv2/testxmlreportv2.cpp b/cppcheck-2.14.0/gui/test/xmlreportv2/testxmlreportv2.cpp new file mode 100644 index 00000000..cede5842 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/xmlreportv2/testxmlreportv2.cpp @@ -0,0 +1,56 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2021 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "testxmlreportv2.h" + +#include "erroritem.h" +#include "xmlreportv2.h" + +#include +#include + +void TestXmlReportV2::readXml() const +{ + const QString filepath(QString(SRCDIR) + "/../data/xmlfiles/xmlreport_v2.xml"); + XmlReportV2 report(filepath, QString()); + QVERIFY(report.open()); + QList errors = report.read(); + QCOMPARE(errors.size(), 6); + + const ErrorItem &item = errors[0]; + QCOMPARE(item.errorPath.size(), 1); + QCOMPARE(item.errorPath[0].file, QString("test.cxx")); + QCOMPARE(item.errorPath[0].line, 11); + QCOMPARE(item.errorId, QString("unreadVariable")); + QCOMPARE(GuiSeverity::toString(item.severity), QString("style")); + QCOMPARE(item.summary, QString("Variable 'a' is assigned a value that is never used")); + QCOMPARE(item.message, QString("Variable 'a' is assigned a value that is never used")); + + const ErrorItem &item2 = errors[3]; + QCOMPARE(item2.errorPath.size(), 2); + QCOMPARE(item2.errorPath[0].file, QString("test.cxx")); + QCOMPARE(item2.errorPath[0].line, 16); + QCOMPARE(item2.errorPath[1].file, QString("test.cxx")); + QCOMPARE(item2.errorPath[1].line, 32); + QCOMPARE(item2.errorId, QString("mismatchAllocDealloc")); + QCOMPARE(GuiSeverity::toString(item2.severity), QString("error")); + QCOMPARE(item2.summary, QString("Mismatching allocation and deallocation: k")); + QCOMPARE(item2.message, QString("Mismatching allocation and deallocation: k")); +} + +QTEST_MAIN(TestXmlReportV2) diff --git a/cppcheck-2.14.0/gui/test/xmlreportv2/testxmlreportv2.h b/cppcheck-2.14.0/gui/test/xmlreportv2/testxmlreportv2.h new file mode 100644 index 00000000..d5d0f659 --- /dev/null +++ b/cppcheck-2.14.0/gui/test/xmlreportv2/testxmlreportv2.h @@ -0,0 +1,27 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2021 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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() const; +}; diff --git a/cppcheck-2.14.0/gui/test/xmlreportv2/xmlreportv2.pro b/cppcheck-2.14.0/gui/test/xmlreportv2/xmlreportv2.pro new file mode 100644 index 00000000..5802dadb --- /dev/null +++ b/cppcheck-2.14.0/gui/test/xmlreportv2/xmlreportv2.pro @@ -0,0 +1,28 @@ +TEMPLATE = app +TARGET = test-xmlreportv2 +DEPENDPATH += . +INCLUDEPATH += . ../../../externals/simplecpp +OBJECTS_DIR = ../../temp +MOC_DIR = ../../temp + +QT -= gui +QT += core +QT += testlib + +include(../common.pri) +include(../../../lib/lib.pri) + +DEFINES += SRCDIR=\\\"$$PWD\\\" + +# tests +SOURCES += testxmlreportv2.cpp \ + ../../erroritem.cpp \ + ../../report.cpp \ + ../../xmlreport.cpp \ + ../../xmlreportv2.cpp + +HEADERS += testxmlreportv2.h \ + ../../erroritem.h \ + ../../report.h \ + ../../xmlreport.cpp \ + ../../xmlreportv2.h diff --git a/cppcheck-2.14.0/gui/threadhandler.cpp b/cppcheck-2.14.0/gui/threadhandler.cpp new file mode 100644 index 00000000..b6fd53d2 --- /dev/null +++ b/cppcheck-2.14.0/gui/threadhandler.cpp @@ -0,0 +1,301 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "threadhandler.h" + +#include "checkthread.h" +#include "common.h" +#include "resultsview.h" +#include "settings.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +ThreadHandler::ThreadHandler(QObject *parent) : + QObject(parent) +{ + setThreadCount(1); +} + +ThreadHandler::~ThreadHandler() +{ + removeThreads(); +} + +void ThreadHandler::clearFiles() +{ + mLastFiles.clear(); + mResults.clearFiles(); + mAnalyseWholeProgram = false; + mAddonsAndTools.clear(); + mSuppressions.clear(); +} + +void ThreadHandler::setFiles(const QStringList &files) +{ + mResults.setFiles(files); + mLastFiles = files; +} + +void ThreadHandler::setProject(const ImportProject &prj) +{ + mResults.setProject(prj); + mLastFiles.clear(); +} + +void ThreadHandler::setCheckFiles(bool all) +{ + if (mRunningThreadCount == 0) { + mResults.setFiles(getReCheckFiles(all)); + } +} + +void ThreadHandler::setCheckFiles(const QStringList& files) +{ + if (mRunningThreadCount == 0) { + mResults.setFiles(files); + } +} + +void ThreadHandler::check(const Settings &settings) +{ + if (mResults.getFileCount() == 0 || mRunningThreadCount > 0 || settings.jobs == 0) { + qDebug() << "Can't start checking if there's no files to check or if check is in progress."; + emit done(); + return; + } + + setThreadCount(settings.jobs); + + mRunningThreadCount = mThreads.size(); + + if (mResults.getFileCount() < mRunningThreadCount) { + mRunningThreadCount = mResults.getFileCount(); + } + + QStringList addonsAndTools = mAddonsAndTools; + for (const std::string& addon: settings.addons) { + QString s = QString::fromStdString(addon); + if (!addonsAndTools.contains(s)) + addonsAndTools << s; + } + + for (int i = 0; i < mRunningThreadCount; i++) { + mThreads[i]->setAddonsAndTools(addonsAndTools); + mThreads[i]->setSuppressions(mSuppressions); + mThreads[i]->setClangIncludePaths(mClangIncludePaths); + mThreads[i]->check(settings); + } + + // Date and time when checking starts.. + mCheckStartTime = QDateTime::currentDateTime(); + + mAnalyseWholeProgram = true; + + mTimer.start(); +} + +bool ThreadHandler::isChecking() const +{ + return mRunningThreadCount > 0; +} + +void ThreadHandler::setThreadCount(const int count) +{ + if (mRunningThreadCount > 0 || + count == mThreads.size() || + count <= 0) { + return; + } + + //Remove unused old threads + removeThreads(); + //Create new threads + for (int i = mThreads.size(); i < count; i++) { + mThreads << new CheckThread(mResults); + connect(mThreads.last(), &CheckThread::done, + this, &ThreadHandler::threadDone); + connect(mThreads.last(), &CheckThread::fileChecked, + &mResults, &ThreadResult::fileChecked); + } +} + + +void ThreadHandler::removeThreads() +{ + for (CheckThread* thread : mThreads) { + if (thread->isRunning()) { + thread->terminate(); + thread->wait(); + } + disconnect(thread, &CheckThread::done, + this, &ThreadHandler::threadDone); + disconnect(thread, &CheckThread::fileChecked, + &mResults, &ThreadResult::fileChecked); + delete thread; + } + + mThreads.clear(); + mAnalyseWholeProgram = false; +} + +void ThreadHandler::threadDone() +{ + if (mRunningThreadCount == 1 && mAnalyseWholeProgram) { + mThreads[0]->analyseWholeProgram(mLastFiles); + mAnalyseWholeProgram = false; + return; + } + + mRunningThreadCount--; + if (mRunningThreadCount == 0) { + emit done(); + + mScanDuration = mTimer.elapsed(); + + // Set date/time used by the recheck + if (!mCheckStartTime.isNull()) { + mLastCheckTime = mCheckStartTime; + mCheckStartTime = QDateTime(); + } + } +} + +void ThreadHandler::stop() +{ + mCheckStartTime = QDateTime(); + mAnalyseWholeProgram = false; + for (CheckThread* thread : mThreads) { + thread->stop(); + } +} + +void ThreadHandler::initialize(const ResultsView *view) +{ + connect(&mResults, &ThreadResult::progress, + view, &ResultsView::progress); + + connect(&mResults, &ThreadResult::error, + view, &ResultsView::error); + + connect(&mResults, &ThreadResult::log, + this, &ThreadHandler::log); + + connect(&mResults, &ThreadResult::debugError, + this, &ThreadHandler::debugError); +} + +void ThreadHandler::loadSettings(const QSettings &settings) +{ + setThreadCount(settings.value(SETTINGS_CHECK_THREADS, 1).toInt()); +} + +void ThreadHandler::saveSettings(QSettings &settings) const +{ + settings.setValue(SETTINGS_CHECK_THREADS, mThreads.size()); +} + +bool ThreadHandler::hasPreviousFiles() const +{ + return !mLastFiles.isEmpty(); +} + +int ThreadHandler::getPreviousFilesCount() const +{ + return mLastFiles.size(); +} + +int ThreadHandler::getPreviousScanDuration() const +{ + return mScanDuration; +} + +QStringList ThreadHandler::getReCheckFiles(bool all) const +{ + if (mLastCheckTime.isNull() || all) + return mLastFiles; + + std::set modified; + std::set unmodified; + + QStringList files; + for (int i = 0; i < mLastFiles.size(); ++i) { + if (needsReCheck(mLastFiles[i], modified, unmodified)) + files.push_back(mLastFiles[i]); + } + return files; +} + +bool ThreadHandler::needsReCheck(const QString &filename, std::set &modified, std::set &unmodified) const +{ + if (modified.find(filename) != modified.end()) + return true; + + if (unmodified.find(filename) != unmodified.end()) + return false; + + if (QFileInfo(filename).lastModified() > mLastCheckTime) { + return true; + } + + // Parse included files recursively + QFile f(filename); + if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) + return false; + + // prevent recursion.. + unmodified.insert(filename); + + QTextStream in(&f); + while (!in.atEnd()) { + QString line = in.readLine(); + if (line.startsWith("#include \"")) { + line.remove(0,10); + const int i = line.indexOf("\""); + if (i > 0) { + line.remove(i,line.length()); + line = QFileInfo(filename).absolutePath() + "/" + line; + if (needsReCheck(line, modified, unmodified)) { + modified.insert(std::move(line)); + return true; + } + } + } + } + + return false; +} + +QDateTime ThreadHandler::getCheckStartTime() const +{ + return mCheckStartTime; +} + +void ThreadHandler::setCheckStartTime(QDateTime checkStartTime) +{ + mCheckStartTime = std::move(checkStartTime); +} diff --git a/cppcheck-2.14.0/gui/threadhandler.h b/cppcheck-2.14.0/gui/threadhandler.h new file mode 100644 index 00000000..e2b06c3e --- /dev/null +++ b/cppcheck-2.14.0/gui/threadhandler.h @@ -0,0 +1,273 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "suppressions.h" +#include "threadresult.h" + +#include + +#include +#include +#include +#include +#include +#include + +class ResultsView; +class CheckThread; +class QSettings; +class Settings; +class ImportProject; +class ErrorItem; + +/// @addtogroup GUI +/// @{ + + +/** + * @brief This class handles creating threadresult and starting threads + * + */ +class ThreadHandler : public QObject { + Q_OBJECT +public: + explicit ThreadHandler(QObject *parent = nullptr); + ~ThreadHandler() override; + + /** + * @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(const ResultsView *view); + + /** + * @brief Load settings + * @param settings QSettings to load settings from + */ + void loadSettings(const QSettings &settings); + + /** + * @brief Save settings + * @param settings QSettings to save settings to + */ + void saveSettings(QSettings &settings) const; + + void setAddonsAndTools(const QStringList &addonsAndTools) { + mAddonsAndTools = addonsAndTools; + } + + void setSuppressions(const QList &s) { + mSuppressions = s; + } + + void setClangIncludePaths(const QStringList &s) { + mClangIncludePaths = s; + } + + /** + * @brief Clear all files from cppcheck + * + */ + void clearFiles(); + + /** + * @brief Set files to check + * + * @param files files to check + */ + void setFiles(const QStringList &files); + + /** + * @brief Set project to check + * + * @param prj project to check + */ + void setProject(const ImportProject &prj); + + /** + * @brief Start the threads to check the files + * + * @param settings Settings for checking + */ + void check(const Settings &settings); + + /** + * @brief Set files to check + * + * @param all true if all files, false if modified files + */ + void setCheckFiles(bool all); + + /** + * @brief Set selected files to check + * + * @param files list of files to be checked + */ + void setCheckFiles(const QStringList& files); + + /** + * @brief Is checking running? + * + * @return true if check is running, false otherwise. + */ + bool isChecking() const; + + /** + * @brief Have we checked files already? + * + * @return true check has been previously run and recheck can be done + */ + bool hasPreviousFiles() const; + + /** + * @brief Return count of files we checked last time. + * + * @return count of files that were checked last time. + */ + int getPreviousFilesCount() const; + + /** + * @brief Return the time elapsed while scanning the previous time. + * + * @return the time elapsed in milliseconds. + */ + int getPreviousScanDuration() const; + + /** + * @brief Get files that should be rechecked because they have been + * changed. + */ + QStringList getReCheckFiles(bool all) const; + + /** + * @brief Get start time of last check + * + * @return start time of last check + */ + QDateTime getCheckStartTime() const; + + /** + * @brief Set start time of check + * + * @param checkStartTime saved start time of the last check + */ + void setCheckStartTime(QDateTime checkStartTime); + +signals: + /** + * @brief Signal that all threads are done + * + */ + void done(); + + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void log(const QString &msg); + + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void debugError(const ErrorItem &item); + +public slots: + + /** + * @brief Slot to stop all threads + * + */ + void stop(); +protected slots: + /** + * @brief Slot that a single thread is done + * + */ + void threadDone(); +protected: + /** + * @brief List of files checked last time (used when rechecking) + * + */ + QStringList mLastFiles; + + /** @brief date and time when current checking started */ + QDateTime mCheckStartTime; + + /** + * @brief when was the files checked the last time (used when rechecking) + */ + QDateTime mLastCheckTime; + + /** + * @brief Timer used for measuring scan duration + * + */ + QElapsedTimer mTimer; + + /** + * @brief The previous scan duration in milliseconds. + * + */ + int mScanDuration{}; + + /** + * @brief Function to delete all threads + * + */ + void removeThreads(); + + /** + * @brief Thread results are stored here + * + */ + ThreadResult mResults; + + /** + * @brief List of threads currently in use + * + */ + QList mThreads; + + /** + * @brief The amount of threads currently running + * + */ + int mRunningThreadCount{}; + + bool mAnalyseWholeProgram{}; + + QStringList mAddonsAndTools; + QList mSuppressions; + QStringList mClangIncludePaths; +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 diff --git a/cppcheck-2.14.0/gui/threadresult.cpp b/cppcheck-2.14.0/gui/threadresult.cpp new file mode 100644 index 00000000..bc807e97 --- /dev/null +++ b/cppcheck-2.14.0/gui/threadresult.cpp @@ -0,0 +1,127 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "threadresult.h" + +#include "common.h" +#include "erroritem.h" +#include "errorlogger.h" +#include "errortypes.h" +#include "importproject.h" + +#include + +#include + +void ThreadResult::reportOut(const std::string &outmsg, Color /*c*/) +{ + emit log(QString::fromStdString(outmsg)); +} + +void ThreadResult::fileChecked(const QString &file) +{ + std::lock_guard 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 ErrorMessage &msg) +{ + std::lock_guard locker(mutex); + const ErrorItem item(msg); + if (msg.severity != Severity::debug) + emit error(item); + else + emit debugError(item); +} + +QString ThreadResult::getNextFile() +{ + std::lock_guard locker(mutex); + if (mFiles.isEmpty()) { + return QString(); + } + + return mFiles.takeFirst(); +} + +FileSettings ThreadResult::getNextFileSettings() +{ + std::lock_guard locker(mutex); + if (mFileSettings.empty()) { + return FileSettings(); + } + const FileSettings fs = mFileSettings.front(); + mFileSettings.pop_front(); + return fs; +} + +void ThreadResult::setFiles(const QStringList &files) +{ + std::lock_guard 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 = std::accumulate(files.begin(), files.end(), 0, [](quint64 total, const QString& file) { + return total + QFile(file).size(); + }); + mMaxProgress = sizeOfFiles; +} + +void ThreadResult::setProject(const ImportProject &prj) +{ + std::lock_guard locker(mutex); + mFiles.clear(); + mFileSettings = prj.fileSettings; + mProgress = 0; + mFilesChecked = 0; + mTotalFiles = prj.fileSettings.size(); + + // Determine the total size of all of the files to check, so that we can + // show an accurate progress estimate + mMaxProgress = std::accumulate(prj.fileSettings.begin(), prj.fileSettings.end(), quint64{ 0 }, [](quint64 v, const FileSettings& fs) { + return v + QFile(QString::fromStdString(fs.filename)).size(); + }); +} + +void ThreadResult::clearFiles() +{ + std::lock_guard locker(mutex); + mFiles.clear(); + mFileSettings.clear(); + mFilesChecked = 0; + mTotalFiles = 0; +} + +int ThreadResult::getFileCount() const +{ + std::lock_guard locker(mutex); + return mFiles.size() + mFileSettings.size(); +} diff --git a/cppcheck-2.14.0/gui/threadresult.h b/cppcheck-2.14.0/gui/threadresult.h new file mode 100644 index 00000000..faea466d --- /dev/null +++ b/cppcheck-2.14.0/gui/threadresult.h @@ -0,0 +1,166 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "color.h" +#include "errorlogger.h" +#include "filesettings.h" + +#include +#include +#include + +#include +#include +#include +#include + +class ErrorItem; +class ImportProject; + +/// @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() = default; + + /** + * @brief Get next unprocessed file + * @return File path + */ + QString getNextFile(); + + FileSettings getNextFileSettings(); + + /** + * @brief Set list of files to check + * @param files List of files to check + */ + void setFiles(const QStringList &files); + + void setProject(const ImportProject &prj); + + /** + * @brief Clear files to check + * + */ + void clearFiles(); + + /** + * @brief Get the number of files to check + * + */ + int getFileCount() const; + + /** + * ErrorLogger methods + */ + void reportOut(const std::string &outmsg, Color c = Color::Reset) override; + void reportErr(const ErrorMessage &msg) override; + +public slots: + + /** + * @brief Slot threads use to signal this class that a specific file is checked + * @param file File that is checked + */ + void fileChecked(const QString &file); +signals: + /** + * @brief Progress signal + * @param value Current progress + * @param description Description of the current stage + */ + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void progress(int value, const QString& description); + + /** + * @brief Signal of a new error + * + * @param item Error data + */ + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void error(const ErrorItem &item); + + /** + * @brief Signal of a new log message + * + * @param logline Log line + */ + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void log(const QString &logline); + + /** + * @brief Signal of a debug error + * + * @param item Error data + */ + // NOLINTNEXTLINE(readability-inconsistent-declaration-parameter-name) - caused by generated MOC code + void debugError(const ErrorItem &item); + +protected: + + /** + * @brief Mutex + * + */ + mutable std::mutex mutex; + + /** + * @brief List of files to check + * + */ + QStringList mFiles; + + std::list mFileSettings; + + /** + * @brief Max progress + * + */ + quint64 mMaxProgress{}; + + /** + * @brief Current progress + * + */ + quint64 mProgress{}; + + /** + * @brief Current number of files checked + * + */ + unsigned long mFilesChecked{}; + + /** + * @brief Total number of files + * + */ + unsigned long mTotalFiles{}; +}; +/// @} +#endif // THREADRESULT_H diff --git a/cppcheck-2.14.0/gui/translationhandler.cpp b/cppcheck-2.14.0/gui/translationhandler.cpp new file mode 100644 index 00000000..aae7e260 --- /dev/null +++ b/cppcheck-2.14.0/gui/translationhandler.cpp @@ -0,0 +1,187 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "translationhandler.h" + +#include "config.h" +#include "common.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +// Provide own translations for standard buttons. This (garbage) code is needed to enforce them to appear in .ts files even after "lupdate gui.pro" +UNUSED static void unused() +{ + Q_UNUSED(QT_TRANSLATE_NOOP("QPlatformTheme", "OK")) + Q_UNUSED(QT_TRANSLATE_NOOP("QPlatformTheme", "Cancel")) + Q_UNUSED(QT_TRANSLATE_NOOP("QPlatformTheme", "Close")) + Q_UNUSED(QT_TRANSLATE_NOOP("QPlatformTheme", "Save")) +} + +TranslationHandler::TranslationHandler(QObject *parent) : + QObject(parent), + mCurrentLanguage("en") +{ + // Add our available languages + // Keep this list sorted + addTranslation("Chinese (Simplified)", "cppcheck_zh_CN"); + addTranslation("Chinese (Traditional)", "cppcheck_zh_TW"); + addTranslation("Dutch", "cppcheck_nl"); + addTranslation("English", "cppcheck_en"); + addTranslation("Finnish", "cppcheck_fi"); + addTranslation("French", "cppcheck_fr"); + addTranslation("German", "cppcheck_de"); + addTranslation("Italian", "cppcheck_it"); + addTranslation("Japanese", "cppcheck_ja"); + addTranslation("Korean", "cppcheck_ko"); + addTranslation("Russian", "cppcheck_ru"); + addTranslation("Serbian", "cppcheck_sr"); + addTranslation("Spanish", "cppcheck_es"); + addTranslation("Swedish", "cppcheck_sv"); +} + +bool TranslationHandler::setLanguage(const QString &code) +{ + bool failure = false; + QString error; + + //If English is the language. Code can be e.g. en_US + if (code.indexOf("en") == 0) { + //Just remove all extra translators + if (mTranslator) { + qApp->removeTranslator(mTranslator); + delete mTranslator; + mTranslator = nullptr; + } + + mCurrentLanguage = code; + return true; + } + + //Make sure the translator is otherwise valid + const int index = getLanguageIndexByCode(code); + if (index == -1) { + error = QObject::tr("Unknown language specified!"); + failure = true; + } else { + // Make sure there is a translator + if (!mTranslator) + mTranslator = new QTranslator(this); + + //Load the new language + const QString appPath = QFileInfo(QCoreApplication::applicationFilePath()).canonicalPath(); + + QString datadir = getDataDir(); + + 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 = true; + //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); + } + else { + //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; +} + +const 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 + +class QTranslator; + +/// @addtogroup GUI +/// @{ + +/** + * @brief Information for one translation. + * + */ +struct TranslationInfo { + /** + * @brief Readable name for the translation (e.g. "English"). + * + */ + QString mName; + + /** + * @brief Filename for the translation. + * + */ + QString mFilename; + + /** + * @brief ISO 639 language code for the translation (e.g. "en"). + * + */ + QString mCode; +}; + +/** + * @brief A class handling the available translations. + * + * This class contains a list of available translations. The class also keeps + * track which translation is the currently active translation. + * + */ +class TranslationHandler : QObject { + Q_OBJECT +public: + explicit TranslationHandler(QObject *parent = nullptr); + + /** + * @brief Get a list of available translations. + * @return List of available translations. + * + */ + const 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. + * + */ + const 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 diff --git a/cppcheck-2.14.0/gui/txtreport.cpp b/cppcheck-2.14.0/gui/txtreport.cpp new file mode 100644 index 00000000..caa3833e --- /dev/null +++ b/cppcheck-2.14.0/gui/txtreport.cpp @@ -0,0 +1,85 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "txtreport.h" + +#include "erroritem.h" + +#include +#include +#include +#include + +TxtReport::TxtReport(const QString &filename) : + Report(filename) +{} + +bool TxtReport::create() +{ + if (Report::create()) { + mTxtWriter.setDevice(Report::getFile()); + return true; + } + return false; +} + +void TxtReport::writeHeader() +{ + // No header for txt report +} + +void TxtReport::writeFooter() +{ + // No footer for txt report +} + +void TxtReport::writeError(const ErrorItem &error) +{ + /* + Error example from the core program in text + [gui/test.cpp:23] -> [gui/test.cpp:14]: (error) Mismatching allocation and deallocation: k + */ + + QString line; + + for (int i = 0; i < error.errorPath.size(); i++) { + const QString file = QDir::toNativeSeparators(error.errorPath[i].file); + line += QString("[%1:%2]").arg(file).arg(error.errorPath[i].line); + if (i < error.errorPath.size() - 1) { + line += " -> "; + } + + if (i == error.errorPath.size() - 1) { + line += ": "; + } + } + QString temp = "(%1"; + if (error.inconclusive) { + temp += ", "; + temp += tr("inconclusive"); + } + temp += ") "; + line += temp.arg(GuiSeverity::toString(error.severity)); + line += error.summary; + +#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)) + mTxtWriter << line << Qt::endl; +#else + mTxtWriter << line << endl; +#endif +} diff --git a/cppcheck-2.14.0/gui/txtreport.h b/cppcheck-2.14.0/gui/txtreport.h new file mode 100644 index 00000000..d3e74f93 --- /dev/null +++ b/cppcheck-2.14.0/gui/txtreport.h @@ -0,0 +1,74 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "report.h" + +#include +#include +#include + +class ErrorItem; + +/// @addtogroup GUI +/// @{ + + +/** + * @brief Text file report. + * This report mimics the output of the command line cppcheck. + */ +class TxtReport : public Report { + Q_OBJECT + +public: + explicit TxtReport(const QString &filename); + + /** + * @brief Create the report (file). + * @return true if succeeded, false if file could not be created. + */ + bool create() override; + + /** + * @brief Write report header. + */ + void writeHeader() override; + + /** + * @brief Write report footer. + */ + void writeFooter() override; + + /** + * @brief Write error to report. + * @param error Error data. + */ + void writeError(const ErrorItem &error) override; + +private: + + /** + * @brief Text stream writer for writing the report in text format. + */ + QTextStream mTxtWriter; +}; +/// @} +#endif // TXT_REPORT_H diff --git a/cppcheck-2.14.0/gui/xmlreport.cpp b/cppcheck-2.14.0/gui/xmlreport.cpp new file mode 100644 index 00000000..3e432d62 --- /dev/null +++ b/cppcheck-2.14.0/gui/xmlreport.cpp @@ -0,0 +1,98 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "xmlreport.h" + +#include "report.h" + +#include +#include +#include +#include + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#include +#endif + +static constexpr char ResultElementName[] = "results"; +static constexpr 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); + const 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() == QString(ResultElementName)) { + QXmlStreamAttributes attribs = reader.attributes(); + if (attribs.hasAttribute(QString(VersionAttribute))) { + const int ver = attribs.value(QString(), VersionAttribute).toString().toInt(); + return ver; + } + 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; +} diff --git a/cppcheck-2.14.0/gui/xmlreport.h b/cppcheck-2.14.0/gui/xmlreport.h new file mode 100644 index 00000000..e2757493 --- /dev/null +++ b/cppcheck-2.14.0/gui/xmlreport.h @@ -0,0 +1,68 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "report.h" + +#include +#include + +class ErrorItem; + +/// @addtogroup GUI +/// @{ + + +/** + * @brief Base class for XML report classes. + */ +class XmlReport : public Report { +public: + explicit XmlReport(const QString &filename); + + /** + * @brief Read contents of the report file. + */ + virtual QList read() = 0; + + /** + * @brief Quote the message. + * @param message Message to quote. + * @return quoted message. + */ + static QString quoteMessage(const QString &message); + + /** + * @brief Unquote the message. + * @param message Message to quote. + * @return quoted message. + */ + static QString unquoteMessage(const QString &message); + + /** + * @brief Get the XML report format version from the file. + * @param filename Filename of the report file. + * @return XML report format version or 0 if error happened. + */ + static int determineVersion(const QString &filename); +}; +/// @} + +#endif // XML_REPORT_H diff --git a/cppcheck-2.14.0/gui/xmlreportv2.cpp b/cppcheck-2.14.0/gui/xmlreportv2.cpp new file mode 100644 index 00000000..46683774 --- /dev/null +++ b/cppcheck-2.14.0/gui/xmlreportv2.cpp @@ -0,0 +1,291 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "xmlreportv2.h" + +#include "cppcheck.h" +#include "erroritem.h" +#include "report.h" +#include "settings.h" +#include "xmlreport.h" + +#include + +#include +#include +#include +#include +#include +#include + +#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) +#include +#endif + +static const QString ResultElementName = "results"; +static const QString CppcheckElementName = "cppcheck"; +static const QString ErrorElementName = "error"; +static const QString ErrorsElementName = "errors"; +static const QString LocationElementName = "location"; +static const QString CWEAttribute = "cwe"; +static const QString HashAttribute = "hash"; +static const QString SinceDateAttribute = "sinceDate"; +static const QString TagsAttribute = "tag"; +static const QString FilenameAttribute = "file"; +static const QString IncludedFromFilenameAttribute = "file0"; +static const QString InconclusiveAttribute = "inconclusive"; +static const QString InfoAttribute = "info"; +static const QString LineAttribute = "line"; +static const QString ColumnAttribute = "column"; +static const QString IdAttribute = "id"; +static const QString SeverityAttribute = "severity"; +static const QString MsgAttribute = "msg"; +static const QString VersionAttribute = "version"; +static const QString ProductNameAttribute = "product-name"; +static const QString VerboseAttribute = "verbose"; + +XmlReportV2::XmlReportV2(const QString &filename, QString productName) : + XmlReport(filename), + mProductName(std::move(productName)), + mXmlReader(nullptr), + mXmlWriter(nullptr) +{} + +XmlReportV2::~XmlReportV2() +{ + delete mXmlReader; + delete mXmlWriter; +} + +bool XmlReportV2::create() +{ + if (Report::create()) { + mXmlWriter = new QXmlStreamWriter(Report::getFile()); + return true; + } + return false; +} + +bool XmlReportV2::open() +{ + if (Report::open()) { + mXmlReader = new QXmlStreamReader(Report::getFile()); + return true; + } + return false; +} + +void XmlReportV2::writeHeader() +{ + const auto nameAndVersion = Settings::getNameAndVersion(mProductName.toStdString()); + const QString name = QString::fromStdString(nameAndVersion.first); + const QString version = nameAndVersion.first.empty() ? CppCheck::version() : QString::fromStdString(nameAndVersion.second); + + mXmlWriter->setAutoFormatting(true); + mXmlWriter->writeStartDocument(); + mXmlWriter->writeStartElement(ResultElementName); + mXmlWriter->writeAttribute(VersionAttribute, QString::number(2)); + mXmlWriter->writeStartElement(CppcheckElementName); + if (!name.isEmpty()) + mXmlWriter->writeAttribute(ProductNameAttribute, name); + mXmlWriter->writeAttribute(VersionAttribute, version); + mXmlWriter->writeEndElement(); + mXmlWriter->writeStartElement(ErrorsElementName); +} + +void XmlReportV2::writeFooter() +{ + mXmlWriter->writeEndElement(); // errors + mXmlWriter->writeEndElement(); // results + mXmlWriter->writeEndDocument(); +} + +void XmlReportV2::writeError(const ErrorItem &error) +{ + /* + Error example from the core program in xml + + + + + */ + + mXmlWriter->writeStartElement(ErrorElementName); + mXmlWriter->writeAttribute(IdAttribute, error.errorId); + + // Don't localize severity so we can read these files + mXmlWriter->writeAttribute(SeverityAttribute, GuiSeverity::toString(error.severity)); + const QString summary = XmlReport::quoteMessage(error.summary); + mXmlWriter->writeAttribute(MsgAttribute, summary); + const QString message = XmlReport::quoteMessage(error.message); + mXmlWriter->writeAttribute(VerboseAttribute, message); + if (error.inconclusive) + mXmlWriter->writeAttribute(InconclusiveAttribute, "true"); + if (error.cwe > 0) + mXmlWriter->writeAttribute(CWEAttribute, QString::number(error.cwe)); + if (error.hash > 0) + mXmlWriter->writeAttribute(HashAttribute, QString::number(error.hash)); + if (!error.file0.isEmpty()) + mXmlWriter->writeAttribute(IncludedFromFilenameAttribute, quoteMessage(error.file0)); + if (!error.sinceDate.isEmpty()) + mXmlWriter->writeAttribute(SinceDateAttribute, error.sinceDate); + if (!error.tags.isEmpty()) + mXmlWriter->writeAttribute(TagsAttribute, error.tags); + + for (int i = error.errorPath.count() - 1; i >= 0; i--) { + mXmlWriter->writeStartElement(LocationElementName); + + QString file = QDir::toNativeSeparators(error.errorPath[i].file); + mXmlWriter->writeAttribute(FilenameAttribute, XmlReport::quoteMessage(file)); + mXmlWriter->writeAttribute(LineAttribute, QString::number(error.errorPath[i].line)); + if (error.errorPath[i].column > 0) + mXmlWriter->writeAttribute(ColumnAttribute, QString::number(error.errorPath[i].column)); + if (error.errorPath.count() > 1) + mXmlWriter->writeAttribute(InfoAttribute, XmlReport::quoteMessage(error.errorPath[i].info)); + + mXmlWriter->writeEndElement(); + } + + mXmlWriter->writeEndElement(); +} + +QList XmlReportV2::read() +{ + QList errors; + bool insideResults = false; + if (!mXmlReader) { + qDebug() << "You must Open() the file before reading it!"; + return errors; + } + while (!mXmlReader->atEnd()) { + switch (mXmlReader->readNext()) { + case QXmlStreamReader::StartElement: + if (mXmlReader->name() == ResultElementName) + insideResults = true; + + // Read error element from inside result element + if (insideResults && mXmlReader->name() == ErrorElementName) { + ErrorItem item = readError(mXmlReader); + errors.append(item); + } + break; + + case QXmlStreamReader::EndElement: + if (mXmlReader->name() == ResultElementName) + insideResults = false; + break; + + // Not handled + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Characters: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } + return errors; +} + +ErrorItem XmlReportV2::readError(const QXmlStreamReader *reader) +{ + /* + Error example from the core program in xml + + + + + */ + + ErrorItem item; + + // Read error element from inside errors element + if (mXmlReader->name() == ErrorElementName) { + QXmlStreamAttributes attribs = reader->attributes(); + item.errorId = attribs.value(QString(), IdAttribute).toString(); + item.severity = GuiSeverity::fromString(attribs.value(QString(), SeverityAttribute).toString()); + const QString summary = attribs.value(QString(), MsgAttribute).toString(); + item.summary = XmlReport::unquoteMessage(summary); + const QString message = attribs.value(QString(), VerboseAttribute).toString(); + item.message = XmlReport::unquoteMessage(message); + if (attribs.hasAttribute(QString(), InconclusiveAttribute)) + item.inconclusive = true; + if (attribs.hasAttribute(QString(), CWEAttribute)) + item.cwe = attribs.value(QString(), CWEAttribute).toInt(); + if (attribs.hasAttribute(QString(), HashAttribute)) + item.hash = attribs.value(QString(), HashAttribute).toULongLong(); + if (attribs.hasAttribute(QString(), IncludedFromFilenameAttribute)) + item.file0 = attribs.value(QString(), IncludedFromFilenameAttribute).toString(); + if (attribs.hasAttribute(QString(), SinceDateAttribute)) + item.sinceDate = attribs.value(QString(), SinceDateAttribute).toString(); + if (attribs.hasAttribute(QString(), TagsAttribute)) + item.tags = attribs.value(QString(), TagsAttribute).toString(); + } + + bool errorRead = false; + while (!errorRead && !mXmlReader->atEnd()) { + switch (mXmlReader->readNext()) { + case QXmlStreamReader::StartElement: + + // Read location element from inside error element + if (mXmlReader->name() == LocationElementName) { + QXmlStreamAttributes attribs = mXmlReader->attributes(); + QString file0 = attribs.value(QString(), IncludedFromFilenameAttribute).toString(); + if (!file0.isEmpty()) + item.file0 = XmlReport::unquoteMessage(file0); + QErrorPathItem loc; + loc.file = XmlReport::unquoteMessage(attribs.value(QString(), FilenameAttribute).toString()); + loc.line = attribs.value(QString(), LineAttribute).toString().toUInt(); + if (attribs.hasAttribute(QString(), ColumnAttribute)) + loc.column = attribs.value(QString(), ColumnAttribute).toString().toInt(); + if (attribs.hasAttribute(QString(), InfoAttribute)) + loc.info = XmlReport::unquoteMessage(attribs.value(QString(), InfoAttribute).toString()); + item.errorPath.push_front(loc); + } + break; + + case QXmlStreamReader::EndElement: + if (mXmlReader->name() == ErrorElementName) + errorRead = true; + break; + + // Not handled + case QXmlStreamReader::NoToken: + case QXmlStreamReader::Invalid: + case QXmlStreamReader::StartDocument: + case QXmlStreamReader::EndDocument: + case QXmlStreamReader::Characters: + case QXmlStreamReader::Comment: + case QXmlStreamReader::DTD: + case QXmlStreamReader::EntityReference: + case QXmlStreamReader::ProcessingInstruction: + break; + } + } + + if (item.errorPath.size() == 1 && item.errorPath[0].info.isEmpty()) + item.errorPath[0].info = item.message; + + return item; +} diff --git a/cppcheck-2.14.0/gui/xmlreportv2.h b/cppcheck-2.14.0/gui/xmlreportv2.h new file mode 100644 index 00000000..f32cd6eb --- /dev/null +++ b/cppcheck-2.14.0/gui/xmlreportv2.h @@ -0,0 +1,99 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "erroritem.h" +#include "xmlreport.h" + +#include +#include + +class QXmlStreamReader; +class QXmlStreamWriter; + +/// @addtogroup GUI +/// @{ + + +/** + * @brief XML file report version 2. + * This report outputs XML-formatted report. The XML format must match command + * line version's XML output. + */ +class XmlReportV2 : public XmlReport { +public: + explicit XmlReportV2(const QString &filename, QString productName); + ~XmlReportV2() override; + + /** + * @brief Create the report (file). + * @return true if succeeded, false if file could not be created. + */ + bool create() override; + + /** + * @brief Open existing report file. + */ + bool open() override; + + /** + * @brief Write report header. + */ + void writeHeader() override; + + /** + * @brief Write report footer. + */ + void writeFooter() override; + + /** + * @brief Write error to report. + * @param error Error data. + */ + void writeError(const ErrorItem &error) override; + + /** + * @brief Read contents of the report file. + */ + QList read() override; + +protected: + /** + * @brief Read and parse error item from XML stream. + * @param reader XML stream reader to use. + */ + ErrorItem readError(const QXmlStreamReader *reader); + +private: + /** Product name read from cppcheck.cfg */ + const QString mProductName; + + /** + * @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 diff --git a/cppcheck-2.14.0/htmlreport/README.txt b/cppcheck-2.14.0/htmlreport/README.txt new file mode 100644 index 00000000..88a84a16 --- /dev/null +++ b/cppcheck-2.14.0/htmlreport/README.txt @@ -0,0 +1,12 @@ +cppcheck-htmlreport + +This is a little utility to generate a html report of a XML file produced by +cppcheck. + +The utility is implemented in Python (2.7+) and requires the pygments module +to generate syntax highlighted source code. +If you are using a Debian based Linux system, the pygments package can be +installed by following command: +$ sudo apt-get install python-pygments + +For more information run './cppcheck-htmlreport --help' diff --git a/cppcheck-2.14.0/htmlreport/check.sh b/cppcheck-2.14.0/htmlreport/check.sh new file mode 100644 index 00000000..a4515ec0 --- /dev/null +++ b/cppcheck-2.14.0/htmlreport/check.sh @@ -0,0 +1,80 @@ +#!/bin/bash -ex + +# Command for checking HTML syntax with HTML Tidy, see http://www.html-tidy.org/ +tidy_version=$(tidy --version) + +if [[ "$tidy_version" == *"5.6.0"* ]] ;then + # newer tidy (5.6.0) command, if using this it is not necessary to ignore warnings: + tidy_cmd='tidy -o /dev/null -eq --drop-empty-elements no' +else + # older tidy from 2009 (Ubuntu 16.04 Xenial comes with this old version): + tidy_cmd='tidy -o /dev/null -eq' +fi + +function validate_html { + if [ ! -f "$1" ]; then + echo "File $1 does not exist!" + exit 1 + fi + if ! ${tidy_cmd} "$1"; then + echo "HTML validation failed!" + exit 1 + fi +} + +if [ -z "$PYTHON" ]; then + PYTHON=python +fi + +REPORT_DIR=$(mktemp -d -t htmlreport-XXXXXXXXXX) +INDEX_HTML="$REPORT_DIR/index.html" +STATS_HTML="$REPORT_DIR/stats.html" +GUI_TEST_XML="$REPORT_DIR/gui_test.xml" +ERRORLIST_XML="$REPORT_DIR/errorlist.xml" +UNMATCHEDSUPPR_XML="$REPORT_DIR/unmatchedSuppr.xml" + +$PYTHON cppcheck-htmlreport --file ../gui/test/data/xmlfiles/xmlreport_v2.xml --title "xml2 test" --report-dir "$REPORT_DIR" --source-dir ../test/ +echo -e "\n" +# Check HTML syntax +validate_html "$INDEX_HTML" +validate_html "$STATS_HTML" + + +../cppcheck ../samples --enable=all --inconclusive --xml-version=2 2> "$GUI_TEST_XML" +xmllint --noout "$GUI_TEST_XML" +$PYTHON cppcheck-htmlreport --file "$GUI_TEST_XML" --title "xml2 + inconclusive test" --report-dir "$REPORT_DIR" +echo "" +# Check HTML syntax +validate_html "$INDEX_HTML" +validate_html "$STATS_HTML" + + +../cppcheck ../samples --enable=all --inconclusive --verbose --xml-version=2 2> "$GUI_TEST_XML" +xmllint --noout "$GUI_TEST_XML" +$PYTHON cppcheck-htmlreport --file "$GUI_TEST_XML" --title "xml2 + inconclusive + verbose test" --report-dir "$REPORT_DIR" +echo -e "\n" +# Check HTML syntax +validate_html "$INDEX_HTML" +validate_html "$STATS_HTML" + + +../cppcheck --errorlist --inconclusive --xml-version=2 > "$ERRORLIST_XML" +xmllint --noout "$ERRORLIST_XML" +$PYTHON cppcheck-htmlreport --file "$ERRORLIST_XML" --title "errorlist" --report-dir "$REPORT_DIR" +# Check HTML syntax +validate_html "$INDEX_HTML" +validate_html "$STATS_HTML" + + +../cppcheck ../samples/memleak/good.c ../samples/resourceLeak/good.c --xml-version=2 --enable=information --suppressions-list=test_suppressions.txt --xml 2> "$UNMATCHEDSUPPR_XML" +xmllint --noout "$UNMATCHEDSUPPR_XML" +$PYTHON cppcheck-htmlreport --file "$UNMATCHEDSUPPR_XML" --title "unmatched Suppressions" --report-dir="$REPORT_DIR" +grep "unmatchedSuppression<.*>information<.*>Unmatched suppression: variableScope*<" "$INDEX_HTML" +grep ">unmatchedSuppressioninformation<.*>Unmatched suppression: uninitstring<" "$INDEX_HTML" +grep "notexisting" "$INDEX_HTML" +grep ">unmatchedSuppression<.*>information<.*>Unmatched suppression: \*<" "$INDEX_HTML" +# Check HTML syntax +validate_html "$INDEX_HTML" +validate_html "$STATS_HTML" + +rm -rf "$REPORT_DIR" diff --git a/cppcheck-2.14.0/htmlreport/cppcheck-htmlreport b/cppcheck-2.14.0/htmlreport/cppcheck-htmlreport new file mode 100644 index 00000000..8a3686b0 --- /dev/null +++ b/cppcheck-2.14.0/htmlreport/cppcheck-htmlreport @@ -0,0 +1,986 @@ +#!/usr/bin/env python3 + +from __future__ import unicode_literals + +from datetime import date +import io +import locale +import operator +import optparse +import os +import re +import sys +import subprocess + +from collections import Counter +from pygments import highlight +from pygments.lexers import guess_lexer, guess_lexer_for_filename +from pygments.formatters import HtmlFormatter # pylint: disable=no-name-in-module +from pygments.util import ClassNotFound +from xml.sax import parse as xml_parse +from xml.sax import SAXParseException as XmlParseException +from xml.sax.handler import ContentHandler as XmlContentHandler +from xml.sax.saxutils import escape +""" +Turns a cppcheck xml file into a browsable html report along +with syntax highlighted source code. +""" + +STYLE_FILE = """ +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif; + font-size: 13px; + line-height: 1.5; + height: 100%; + margin: 0; +} + +#wrapper { + position: fixed; + height: 100vh; + width: 100vw; + display: grid; + grid-template-rows: fit-content(8rem) auto fit-content(8rem); + grid-template-columns: fit-content(25%) 1fr; + grid-template-areas: + "header header" + "menu content" + "footer footer"; +} + +h1 { + margin: 0 0 8px -2px; + font-size: 175%; +} + +.header { + padding: 0 0 5px 15px; + grid-area: header; + border-bottom: thin solid #aaa; +} + +.footer { + grid-area: footer; + border-top: thin solid #aaa; + font-size: 85%; + +} + +.footer > p { + margin: 4px; +} + +#menu, +#menu_index { + grid-area: menu; + text-align: left; + overflow: auto; + padding: 0 23px 15px 15px; + border-right: thin solid #aaa; + min-width: 200px; +} + +#menu > a { + display: block; + margin-left: 10px; + font-size: 12px; +} + +#content, +#content_index { + grid-area: content; + padding: 0px 5px 15px 15px; + overflow: auto; +} + +label { + white-space: nowrap; +} + +label.checkBtn.disabled { + color: #606060; + background: #e0e0e0; + font-style: italic; +} + +label.checkBtn, input[type="text"] { + border: 1px solid grey; + border-radius: 4px; + box-shadow: 1px 1px inset; + padding: 1px 5px; +} + +label.checkBtn { + white-space: nowrap; + background: #ccddff; +} + +label.unchecked { + background: #eff8ff; + box-shadow: 1px 1px 1px; +} + +label.checkBtn:hover, label.unchecked:hover{ + box-shadow: 0 0 2px; +} + +label.disabled:hover { + box-shadow: 1px 1px inset; +} + +label.checkBtn > input { + display:none; +} + +.summaryTable { + width: 100%; +} + +table.summaryTable td { padding: 0 5px 0 5px; } + +.statHeader, .severityHeader { + font-weight: bold; +} + +.warning { + background-color: #ffffa7; +} + +.error { + background-color: #ffb7b7; +} + +.error2 { + background-color: #faa; + display: inline-block; + margin-left: 4px; +} + +.inconclusive { + background-color: #b6b6b4; +} + +.inconclusive2 { + background-color: #b6b6b4; + display: inline-block; + margin-left: 4px; +} + +.verbose { + display: inline-block; + vertical-align: top; + cursor: help; +} + +.verbose .content { + display: none; + position: absolute; + padding: 10px; + margin: 4px; + max-width: 40%; + white-space: pre-wrap; + border: 1px solid #000; + background-color: #ffffcc; + cursor: auto; +} + +.highlight .hll { + padding: 1px; +} + +.highlighttable { + background-color: #fff; + position: relative; + margin: -10px; +} + +.linenos { + border-right: thin solid #aaa; + color: #d3d3d3; + padding-right: 6px; +} + +.id-filtered, .severity-filtered, .file-filtered, .tool-filtered, .text-filtered { + visibility: collapse; +} +""" + +HTML_HEAD = """ + + + + + Cppcheck - HTML report - %s + + + + + +
+ +""" + +HTML_MENU = """ + +
+""" + +HTML_FOOTER = """ +
+ +
+ + +""" + +HTML_ERROR = "<--- %s\n" +HTML_INCONCLUSIVE = "<--- %s\n" + +HTML_EXPANDABLE_ERROR = "
<--- %s [+]
%s
\n""" +HTML_EXPANDABLE_INCONCLUSIVE = "
<--- %s [+]
%s
\n""" + +# escape() and unescape() takes care of &, < and >. +html_escape_table = { + '"': """, + "'": "'" +} +html_unescape_table = {v: k for k, v in html_escape_table.items()} + + +def html_escape(text): + return escape(text, html_escape_table) + +def filter_button(enabled_filters, id, function): + enabled = enabled_filters.get(id, False) + return '\n '\ + % (' disabled' if not enabled else '', function, id, 'checked' if enabled else 'disabled', id) + +def filter_bar(enabled): + return ''.join([ + '
\n' + ,''.join([filter_button(enabled, severity, 'toggleSeverity') for severity in ['error', 'warning', 'portability', 'performance', 'style', 'information']]) + ,'\n | ' + ,''.join([filter_button(enabled, tool, 'toggleTool') for tool in ['cppcheck', 'clang-tidy']]) + ,'\n | ' + ,'\n ' + ,'\n ' + ,'\n
\n']) + +def git_blame(errors, path, file, blame_options): + last_line= errors[-1]['line'] + if last_line == 0: + return {} + + first_line = next((error for error in errors if error['line'] > 0))['line'] + + full_path = os.path.join(path, file) + path, filename = os.path.split(full_path) + + if path: + os.chdir(path) + + cmd_args = ['git', 'blame', '-L %d,%d' % (first_line, last_line)] + if '-w' in blame_options: + cmd_args.append('-w') + if '-M' in blame_options: + cmd_args.append('-M') + cmd_args = cmd_args + ['--porcelain', '--incremental', '--', filename] + + try: + result = subprocess.check_output(cmd_args) + result = result.decode(locale.getpreferredencoding()) + except: + return [] + finally: + os.chdir(cwd) + + if result.startswith('fatal'): + return [] + + blame_data = [] + line_from = 0 + line_to = 0 + blame_info = dict() + + for line_str in result.splitlines(): + field, _, value = line_str.partition(' ') + if len(field) == 40: + line_nr, count = value.split(' ')[1:] + line_from = int(line_nr) + line_to = line_from + int(count) + elif field.startswith('author'): + blame_info[field] = value + if field == 'author-time': + author_date = date.fromtimestamp(int(blame_info['author-time'])) + blame_info['author-time'] = author_date.strftime("%d/%m/%Y") + elif field == 'filename': + blame_data.append([line_from, line_to, blame_info.copy()]) + + return blame_data + + +def blame_lookup(blame_data, line): + return next((data for start, end, data in blame_data if line >= start and line < end), {}) + + +def tr_str(td_th, line, id, cwe, severity, message, author, author_mail, date, add_author, tr_class=None, htmlfile=None, message_class=None): + ret = '' + if htmlfile: + ret += '<%s>%d' % (td_th, htmlfile, line, line, td_th) + for item in (id, cwe, severity): + ret += '<%s>%s' % (td_th, item, td_th) + else: + for item in (line, id, cwe, severity): + ret += '<%s>%s' % (td_th, item, td_th) + if message_class: + message_attribute = ' class="%s"' % message_class + else: + message_attribute = '' + ret += '<%s%s>%s' % (td_th, message_attribute, html_escape(message), td_th) + + for field in add_author: + if field == 'name': + ret += '<%s>%s' % (td_th, html_escape(author), td_th) + elif field == 'email': + ret += '<%s>%s' % (td_th, html_escape(author_mail), td_th) + elif field == 'date': + ret += '<%s>%s' % (td_th, date, td_th) + + if tr_class: + tr_attributes = ' class="%s"' % tr_class + else: + tr_attributes = '' + return '%s' % (tr_attributes, ret) + + +def to_css_selector(tag): + # https://www.w3.org/TR/CSS2/syndata.html#characters + # Note: for our usage, we don't consider escaped characters + invalid_css_chars = re.compile(r"[^-_a-zA-Z0-9\u00A0-\uFFFF]") + invalid_start = re.compile(r"^([0-9]|-[0-9]|--)") + # Replace forbidden characters by an hyphen + valid_chars = invalid_css_chars.sub("-", tag) + # Check that the start of the tag doesn't break the rules + start_invalid = invalid_start.match(valid_chars) + if start_invalid: + # otherwise, append a token to make it valid + valid_chars_valid_start = "cpp" + valid_chars + else: + valid_chars_valid_start = valid_chars + return valid_chars_valid_start + +class AnnotateCodeFormatter(HtmlFormatter): + errors = [] + + def wrap(self, *args, **kwargs): + line_no = 1 + for i, t in HtmlFormatter.wrap(self, *args, **kwargs): + # If this is a source code line we want to add a span tag at the + # end. + if i == 1: + for error in self.errors: + if error['line'] == line_no: + try: + if error['inconclusive'] == 'true': + # only print verbose msg if it really differs + # from actual message + if error.get('verbose') and (error['verbose'] != error['msg']): + index = t.rfind('\n') + t = t[:index] + HTML_EXPANDABLE_INCONCLUSIVE % (error['msg'], html_escape(error['verbose'].replace("\\012", '\n'))) + t[index + 1:] + else: + t = t.replace('\n', HTML_INCONCLUSIVE % error['msg']) + except KeyError: + if error.get('verbose') and (error['verbose'] != error['msg']): + index = t.rfind('\n') + t = t[:index] + HTML_EXPANDABLE_ERROR % (error['msg'], html_escape(error['verbose'].replace("\\012", '\n'))) + t[index + 1:] + else: + t = t.replace('\n', HTML_ERROR % error['msg']) + + line_no = line_no + 1 + yield i, t + + +class CppCheckHandler(XmlContentHandler): + + """Parses the cppcheck xml file and produces a list of all its errors.""" + + def __init__(self): + XmlContentHandler.__init__(self) + self.errors = [] + self.version = '1' + self.versionCppcheck = '' + + def startElement(self, name, attributes): + if name == 'results': + self.version = attributes.get('version', self.version) + + if self.version == '1': + self.handleVersion1(name, attributes) + else: + self.handleVersion2(name, attributes) + + def handleVersion1(self, name, attributes): + if name != 'error': + return + + self.errors.append({ + 'file': attributes.get('file', ''), + 'line': int(attributes.get('line', 0)), + 'locations': [{ + 'file': attributes.get('file', ''), + 'line': int(attributes.get('line', 0)), + }], + 'id': attributes['id'], + 'severity': attributes['severity'], + 'msg': attributes['msg'] + }) + + def handleVersion2(self, name, attributes): + if name == 'cppcheck': + self.versionCppcheck = attributes['version'] + if name == 'error': + error = { + 'locations': [], + 'file': '', + 'line': 0, + 'id': attributes['id'], + 'severity': attributes['severity'], + 'msg': attributes['msg'], + 'verbose': attributes.get('verbose') + } + + if 'inconclusive' in attributes: + error['inconclusive'] = attributes['inconclusive'] + if 'cwe' in attributes: + error['cwe'] = attributes['cwe'] + + self.errors.append(error) + elif name == 'location': + assert self.errors + error = self.errors[-1] + locations = error['locations'] + file = attributes['file'] + line = int(attributes['line']) + if not locations: + error['file'] = file + error['line'] = line + locations.append({ + 'file': file, + 'line': line, + 'info': attributes.get('info') + }) + +if __name__ == '__main__': + # Configure all the options this little utility is using. + parser = optparse.OptionParser() + parser.add_option('--title', dest='title', + help='The title of the project.', + default='[project name]') + parser.add_option('--file', dest='file', action="append", + help='The cppcheck xml output file to read defects ' + 'from. You can combine results from several ' + 'xml reports i.e. "--file file1.xml --file file2.xml ..". ' + '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('--add-author-information', dest='add_author_information', + help='Blame information to include. ' + 'Adds specified author information. ' + 'Specify as comma-separated list of either "name", "email", "date" or "n","e","d". ' + 'Default: "n,e,d"') + parser.add_option('--source-encoding', dest='source_encoding', + help='Encoding of source code.', default='utf-8') + parser.add_option('--blame-options', dest='blame_options', + help='[-w, -M] blame options which you can use to get author and author mail ' + '-w --> not including white spaces and returns original author of the line ' + '-M --> not including moving of lines and returns original author of the line') + + # 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. + cwd = os.getcwd() + source_dir = os.getcwd() + if options.source_dir: + source_dir = options.source_dir + + add_author_information = [] + if options.add_author_information: + fields = [x.strip() for x in options.add_author_information.split(',')] + for x in fields: + if x.lower() in ['n', 'name']: + add_author_information.append('name') + elif x.lower() in ['e', 'email']: + add_author_information.append('email') + elif x.lower() in ['d', 'date']: + add_author_information.append('date') + else: + print('Unrecognized value "%s" for author information, using default (name, email, date)' % x) + add_author_information = ['name', 'email', 'date'] + break + + blame_options = '' + if options.blame_options: + blame_options = options.blame_options + add_author_information = add_author_information or ['name', 'email', 'date'] + + # Parse the xml from all files defined in file argument + # or from stdin. If no input is provided, stdin is used + # Produce a simple list of errors. + print('Parsing xml report.') + try: + contentHandler = CppCheckHandler() + for fname in options.file or [sys.stdin]: + xml_parse(fname, contentHandler) + except (XmlParseException, ValueError) 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: + files[filename] = { + 'errors': [], 'htmlfile': str(file_no) + '.html'} + file_no = file_no + 1 + files[filename]['errors'].append(error) + + # Make sure that the report directory is created if it doesn't exist. + print('Creating %s directory' % options.report_dir) + if not os.path.exists(options.report_dir): + os.makedirs(options.report_dir) + + # Generate a HTML file with syntax highlighted source code for each + # file that contains one or more errors. + print('Processing errors') + + decode_errors = [] + for filename, data in sorted(files.items()): + htmlfile = data['htmlfile'] + errors = [] + + for error in data['errors']: + for location in error['locations']: + if filename == location['file']: + newError = dict(error) + + del newError['locations'] + newError['line'] = location['line'] + if location.get('info'): + newError['msg'] = location['info'] + newError['severity'] = 'information' + del newError['verbose'] + + errors.append(newError) + + lines = [] + for error in errors: + lines.append(error['line']) + + if filename == '': + continue + + source_filename = os.path.join(source_dir, filename) + try: + with io.open(source_filename, 'r', encoding=options.source_encoding) as input_file: + content = input_file.read() + except IOError: + if error['id'] == 'unmatchedSuppression': + continue # file not found, bail out + else: + sys.stderr.write("ERROR: Source file '%s' not found.\n" % + source_filename) + continue + except UnicodeDecodeError: + sys.stderr.write("WARNING: Unicode decode error in '%s'.\n" % + source_filename) + decode_errors.append(source_filename[2:]) # "[2:]" gets rid of "./" at beginning + continue + + htmlFormatter = AnnotateCodeFormatter(linenos=True, + style='colorful', + hl_lines=lines, + lineanchors='line', + encoding=options.source_encoding) + htmlFormatter.errors = errors + + with io.open(os.path.join(options.report_dir, htmlfile), 'w', encoding='utf-8') as output_file: + output_file.write(HTML_HEAD % + (options.title, + htmlFormatter.get_style_defs('.highlight'), + options.title, + ': ' + filename)) + output_file.write(HTML_HEAD_END) + + output_file.write(HTML_MENU % (filename.split('/')[-1])) + for error in sorted(errors, key=lambda k: k['line']): + output_file.write(" %s %s" % (data['htmlfile'], error['line'], error['id'], error['line'])) + output_file.write(HTML_MENU_END) + + try: + lexer = guess_lexer_for_filename(source_filename, '', stripnl=False) + except ClassNotFound: + try: + lexer = guess_lexer(content, stripnl=False) + except ClassNotFound: + sys.stderr.write("ERROR: Couldn't determine lexer for the file' " + source_filename + " '. Won't be able to syntax highlight this file.") + output_file.write("\n Could not generate content because pygments failed to determine the 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 = [] + filter_enabled = {} + for filename, data in sorted(files.items()): + for error in data['errors']: + stats.append(error['id']) # get the stats + filter_enabled[error['severity']] = True + filter_enabled['clang-tidy' if error['id'].startswith('clang-tidy-') else 'cppcheck'] = True + stats_count += 1 + + counter = Counter(stats) + + stat_html = [] + # the following lines sort the stat primary by value (occurrences), + # but if two IDs occur equally often, then we sort them alphabetically by warning ID + try: + cnt_max = counter.most_common()[0][1] + except IndexError: + cnt_max = 0 + + try: + cnt_min = counter.most_common()[-1][1] + except IndexError: + cnt_min = 0 + + stat_fmt = "\n {}{}" + for occurrences in reversed(range(cnt_min, cnt_max + 1)): + for _id in [k for k, v in sorted(counter.items()) if v == occurrences]: + stat_html.append(stat_fmt.format( + to_css_selector(_id), + to_css_selector(_id), + dict(counter.most_common())[_id], + _id + )) + + output_file.write(HTML_HEAD % (options.title, '', options.title, '')) + output_file.write(filter_bar(filter_enabled)) + output_file.write(HTML_HEAD_END) + output_file.write(HTML_MENU.replace('id="menu"', 'id="menu_index"', 1).replace("Defects:", "Defect summary", 1) % ('')) + output_file.write('\n ') + output_file.write('\n ') + output_file.write('\n ') + output_file.write(''.join(stat_html)) + output_file.write('\n ') + output_file.write('\n
Show#Defect ID
' + str(stats_count) + 'total
') + output_file.write('\n

Statistics

') + output_file.write(HTML_MENU_END.replace("content", "content_index", 1)) + + output_file.write('\n ') + output_file.write( + '\n %s' % + tr_str('th', 'Line', 'Id', 'CWE', 'Severity', 'Message', 'Author', 'Author mail', 'Date (DD/MM/YYYY)', add_author=add_author_information)) + + for filename, data in sorted(files.items()): + file_error = filename in decode_errors or filename.endswith('*') + is_file = filename != '' and not file_error + row_content = filename if file_error else "%s" % (data['htmlfile'], filename) + htmlfile = data.get('htmlfile') if is_file else None + + output_file.write("\n ") + output_file.write("\n " % row_content) + + if filename in decode_errors: + output_file.write("\n ") + + sorted_errors = sorted(data['errors'], key=lambda k: k['line']) + blame_data = git_blame(sorted_errors, source_dir, filename, blame_options) if add_author_information else [] + for error in sorted_errors: + git_blame_dict = blame_lookup(blame_data, error['line']) + message_class = None + try: + if error['inconclusive'] == 'true': + message_class = 'inconclusive' + error['severity'] += ", inconcl." + except KeyError: + pass + + try: + if error['cwe']: + cwe_url = "" + error['cwe'] + "" + except KeyError: + cwe_url = "" + + if error['severity'] in ['error', 'warning']: + message_class = error['severity'] + + line = error["line"] if is_file else "" + + output_file.write( + '\n %s' % + tr_str('td', line, error["id"], cwe_url, error["severity"], error["msg"], + git_blame_dict.get('author', 'Unknown'), git_blame_dict.get('author-mail', '---'), + git_blame_dict.get('author-time', '---'), + tr_class=to_css_selector(error["id"]) + ' sev_' + error["severity"] + ' issue', + message_class=message_class, + add_author=add_author_information, + htmlfile=htmlfile)) + output_file.write("\n ") + output_file.write('\n
%s
Could not generated due to UnicodeDecodeError
') + 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') + os.chdir(cwd) # going back to the cwd to find style.css + with io.open(os.path.join(options.report_dir, 'style.css'), 'w') as css_file: + css_file.write(STYLE_FILE) + + print("Creating stats.html (statistics)\n") + stats_countlist = {} + + for filename, data in sorted(files.items()): + if filename == '': + continue + stats_tmplist = [] + for error in sorted(data['errors'], key=lambda k: k['line']): + stats_tmplist.append(error['severity']) + + stats_countlist[filename] = dict(Counter(stats_tmplist)) + + # get top ten for each severity + SEVERITIES = "error", "warning", "portability", "performance", "style", "unusedFunction", "information", "missingInclude", "internal" + + with io.open(os.path.join(options.report_dir, 'stats.html'), 'w') as stats_file: + + stats_file.write(HTML_HEAD % (options.title, '', options.title, ': Statistics')) + stats_file.write(HTML_HEAD_END) + + stats_file.write(HTML_MENU.replace('id="menu"', 'id="menu_index"', 1).replace("Defects:", "Back to summary", 1) % ('')) + stats_file.write(HTML_MENU_END.replace("content", "content_index", 1)) + + + for sev in SEVERITIES: + _sum = 0 + stats_templist = {} + + # if the we have an style warning but we are checking for + # portability, we have to skip it to prevent KeyError + try: + for filename in stats_countlist: + try: # also bail out if we have a file with no sev-results + _sum += stats_countlist[filename][sev] + stats_templist[filename] = int(stats_countlist[filename][sev]) # file : amount, + except KeyError: + continue + # don't print "0 style" etc, if no style warnings were found + if _sum == 0: + continue + except KeyError: + continue + stats_file.write("

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

\n") + + stats_file.write(HTML_FOOTER % contentHandler.versionCppcheck) + + print("\nOpen '" + options.report_dir + "/index.html' to see the results.") diff --git a/cppcheck-2.14.0/htmlreport/example.cc b/cppcheck-2.14.0/htmlreport/example.cc new file mode 100644 index 00000000..9c010048 --- /dev/null +++ b/cppcheck-2.14.0/htmlreport/example.cc @@ -0,0 +1,7 @@ +#include "missing.h" + +int main() +{ + int x; + x++; +} diff --git a/cppcheck-2.14.0/htmlreport/example.xml b/cppcheck-2.14.0/htmlreport/example.xml new file mode 100644 index 00000000..4807c8be --- /dev/null +++ b/cppcheck-2.14.0/htmlreport/example.xml @@ -0,0 +1,8 @@ + + +Checking example.cc... + + +Checking usage of global functions.. + + diff --git a/cppcheck-2.14.0/htmlreport/requirements.txt b/cppcheck-2.14.0/htmlreport/requirements.txt new file mode 100644 index 00000000..b23d7742 --- /dev/null +++ b/cppcheck-2.14.0/htmlreport/requirements.txt @@ -0,0 +1 @@ +Pygments diff --git a/cppcheck-2.14.0/htmlreport/setup.py b/cppcheck-2.14.0/htmlreport/setup.py new file mode 100644 index 00000000..ac790fbe --- /dev/null +++ b/cppcheck-2.14.0/htmlreport/setup.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +from setuptools import setup + +with open('README.txt') as f: + readme = f.read() + +setup( + name="cppcheck", + description='Python script to parse the XML (version 2) output of ' + + 'cppcheck and generate an HTML report using Pygments for syntax ' + + 'highlighting.', + long_description=readme, + author='Henrik Nilsson', + url='https://github.com/danmar/cppcheck', + license='GPL', + scripts=[ + "cppcheck-htmlreport", + ], + install_requires=['Pygments'] +) diff --git a/cppcheck-2.14.0/htmlreport/test_htmlreport.py b/cppcheck-2.14.0/htmlreport/test_htmlreport.py new file mode 100644 index 00000000..4fd6cf6c --- /dev/null +++ b/cppcheck-2.14.0/htmlreport/test_htmlreport.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +"""Test cppcheck-htmlreport.""" + +import os +import contextlib +import shutil +import subprocess +import sys +import tempfile + +if sys.version_info < (2, 7): + # For TestCase.assertIn(). + import unittest2 as unittest +else: + import unittest + +ROOT_DIR = os.path.split(os.path.abspath(os.path.dirname(__file__)))[0] +CPPCHECK_BIN = os.path.join(ROOT_DIR, 'cppcheck') + +HTML_REPORT_BIN = os.path.join(os.path.abspath(os.path.dirname(__file__)), + 'cppcheck-htmlreport') + + +class TestHTMLReport(unittest.TestCase): + + def testReportError(self): + for xml_version in ['2']: + self.checkReportError(xml_version) + + def checkReportError(self, xml_version): + with runCheck( + os.path.join(ROOT_DIR, 'samples', 'memleak', 'bad.c'), + xml_version=xml_version + ) as (report, output_directory): + self.assertIn(' $ version.rc) + target_compile_definitions(cppcheck-core PRIVATE CPPCHECKLIB_EXPORT TINYXML2_EXPORT SIMPLECPP_EXPORT) +else() + add_library(cppcheck-core OBJECT ${srcs_lib} ${hdrs}) +endif() +target_externals_include_directories(cppcheck-core PRIVATE ${PROJECT_SOURCE_DIR}/externals/) +if(USE_BUNDLED_TINYXML2) + target_externals_include_directories(cppcheck-core PRIVATE ${PROJECT_SOURCE_DIR}/externals/tinyxml2/) +else() + target_include_directories(cppcheck-core SYSTEM PRIVATE ${tinyxml2_INCLUDE_DIRS}) +endif() +target_externals_include_directories(cppcheck-core PRIVATE ${PROJECT_SOURCE_DIR}/externals/picojson/) +target_externals_include_directories(cppcheck-core PRIVATE ${PROJECT_SOURCE_DIR}/externals/simplecpp/) +if (HAVE_RULES) + target_include_directories(cppcheck-core SYSTEM PRIVATE ${PCRE_INCLUDE}) +endif() +if (Boost_FOUND) + target_include_directories(cppcheck-core SYSTEM PRIVATE ${Boost_INCLUDE_DIRS}) +endif() + +if (NOT CMAKE_DISABLE_PRECOMPILE_HEADERS) + target_precompile_headers(cppcheck-core PRIVATE precompiled.h) +endif() diff --git a/cppcheck-2.14.0/lib/addoninfo.cpp b/cppcheck-2.14.0/lib/addoninfo.cpp new file mode 100644 index 00000000..f0b15eac --- /dev/null +++ b/cppcheck-2.14.0/lib/addoninfo.cpp @@ -0,0 +1,171 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "addoninfo.h" + +#include "path.h" +#include "utils.h" + +#include +#include +#include + +#include "json.h" + +static std::string getFullPath(const std::string &fileName, const std::string &exename) { + if (Path::isFile(fileName)) + return fileName; + + const std::string exepath = Path::getPathFromFilename(exename); + if (Path::isFile(exepath + fileName)) + return exepath + fileName; + if (Path::isFile(exepath + "addons/" + fileName)) + return exepath + "addons/" + fileName; + +#ifdef FILESDIR + if (Path::isFile(FILESDIR + ("/" + fileName))) + return FILESDIR + ("/" + fileName); + if (Path::isFile(FILESDIR + ("/addons/" + fileName))) + return FILESDIR + ("/addons/" + fileName); +#endif + return ""; +} + +static std::string parseAddonInfo(AddonInfo& addoninfo, const picojson::value &json, const std::string &fileName, const std::string &exename) { + const std::string& json_error = picojson::get_last_error(); + if (!json_error.empty()) { + return "Loading " + fileName + " failed. " + json_error; + } + if (!json.is()) + return "Loading " + fileName + " failed. JSON is not an object."; + + // TODO: remove/complete default value handling for missing fields + const picojson::object& obj = json.get(); + { + const auto it = obj.find("args"); + if (it != obj.cend()) { + const auto& val = it->second; + if (!val.is()) + return "Loading " + fileName + " failed. 'args' must be an array."; + for (const picojson::value &v : val.get()) { + if (!v.is()) + return "Loading " + fileName + " failed. 'args' entry is not a string."; + addoninfo.args += " " + v.get(); + } + } + } + + { + const auto it = obj.find("ctu"); + if (it != obj.cend()) { + const auto& val = it->second; + // ctu is specified in the config file + if (!val.is()) + return "Loading " + fileName + " failed. 'ctu' must be a boolean."; + addoninfo.ctu = val.get(); + } + else { + addoninfo.ctu = false; + } + } + + { + const auto it = obj.find("python"); + if (it != obj.cend()) { + const auto& val = it->second; + // Python was defined in the config file + if (!val.is()) { + return "Loading " + fileName +" failed. 'python' must be a string."; + } + addoninfo.python = val.get(); + } + else { + addoninfo.python = ""; + } + } + + { + const auto it = obj.find("executable"); + if (it != obj.cend()) { + const auto& val = it->second; + if (!val.is()) + return "Loading " + fileName + " failed. 'executable' must be a string."; + const std::string e = val.get(); + addoninfo.executable = getFullPath(e, fileName); + if (addoninfo.executable.empty()) + addoninfo.executable = e; + return ""; // <- do not load both "executable" and "script". + } + } + + const auto it = obj.find("script"); + if (it == obj.cend()) + return "Loading " + fileName + " failed. 'script' is missing."; + + const auto& val = it->second; + if (!val.is()) + return "Loading " + fileName + " failed. 'script' must be a string."; + + return addoninfo.getAddonInfo(val.get(), exename); +} + +std::string AddonInfo::getAddonInfo(const std::string &fileName, const std::string &exename) { + if (fileName[0] == '{') { + picojson::value json; + const std::string err = picojson::parse(json, fileName); + (void)err; // TODO: report + return parseAddonInfo(*this, json, fileName, exename); + } + if (fileName.find('.') == std::string::npos) + return getAddonInfo(fileName + ".py", exename); + + if (endsWith(fileName, ".py")) { + scriptFile = Path::fromNativeSeparators(getFullPath(fileName, exename)); + if (scriptFile.empty()) + return "Did not find addon " + fileName; + + std::string::size_type pos1 = scriptFile.rfind('/'); + if (pos1 == std::string::npos) + pos1 = 0; + else + pos1++; + std::string::size_type pos2 = scriptFile.rfind('.'); + if (pos2 < pos1) + pos2 = std::string::npos; + name = scriptFile.substr(pos1, pos2 - pos1); + + runScript = getFullPath("runaddon.py", exename); + + return ""; + } + + if (!endsWith(fileName, ".json")) + return "Failed to open addon " + fileName; + + std::ifstream fin(fileName); + if (!fin.is_open()) + return "Failed to open " + fileName; + if (name.empty()) { + name = Path::fromNativeSeparators(fileName); + if (name.find('/') != std::string::npos) + name = name.substr(name.rfind('/') + 1); + } + picojson::value json; + fin >> json; + return parseAddonInfo(*this, json, fileName, exename); +} diff --git a/cppcheck-2.14.0/lib/addoninfo.h b/cppcheck-2.14.0/lib/addoninfo.h new file mode 100644 index 00000000..5e4443eb --- /dev/null +++ b/cppcheck-2.14.0/lib/addoninfo.h @@ -0,0 +1,38 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef addonInfoH +#define addonInfoH + +#include "config.h" + +#include + +struct CPPCHECKLIB AddonInfo { + std::string name; + std::string scriptFile; // addon script + std::string executable; // addon executable + std::string args; // special extra arguments + std::string python; // script interpreter + bool ctu = false; + std::string runScript; + + std::string getAddonInfo(const std::string &fileName, const std::string &exename); +}; + +#endif // addonInfoH diff --git a/cppcheck-2.14.0/lib/analyzer.h b/cppcheck-2.14.0/lib/analyzer.h new file mode 100644 index 00000000..d6bcec72 --- /dev/null +++ b/cppcheck-2.14.0/lib/analyzer.h @@ -0,0 +1,197 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef analyzerH +#define analyzerH + +#include "config.h" +#include "mathlib.h" +#include +#include +#include + +class Token; +template +class ValuePtr; + +struct Analyzer { + struct Action { + + Action() = default; + Action(const Action&) = default; + Action& operator=(const Action& rhs) = default; + + template ), + REQUIRES("T must not be a bool", !std::is_same )> + // NOLINTNEXTLINE(google-explicit-constructor) + Action(T f) : mFlag(f) // cppcheck-suppress noExplicitConstructor + {} + + enum { + None = 0, + Read = (1 << 0), + Write = (1 << 1), + Invalid = (1 << 2), + Inconclusive = (1 << 3), + Match = (1 << 4), + Idempotent = (1 << 5), + Incremental = (1 << 6), + SymbolicMatch = (1 << 7), + Internal = (1 << 8), + }; + + void set(unsigned int f, bool state = true) { + mFlag = state ? mFlag | f : mFlag & ~f; + } + + bool get(unsigned int f) const { + return ((mFlag & f) != 0); + } + + bool isRead() const { + return get(Read); + } + + bool isWrite() const { + return get(Write); + } + + bool isInvalid() const { + return get(Invalid); + } + + bool isInconclusive() const { + return get(Inconclusive); + } + + bool isNone() const { + return mFlag == None; + } + + bool isModified() const { + return isWrite() || isInvalid(); + } + + bool isIdempotent() const { + return get(Idempotent); + } + + bool isIncremental() const { + return get(Incremental); + } + + bool isSymbolicMatch() const { + return get(SymbolicMatch); + } + + bool isInternal() const { + return get(Internal); + } + + bool matches() const { + return get(Match); + } + + Action& operator|=(Action a) { + set(a.mFlag); + return *this; + } + + friend Action operator|(Action a, Action b) { + a |= b; + return a; + } + + friend bool operator==(Action a, Action b) { + return a.mFlag == b.mFlag; + } + + friend bool operator!=(Action a, Action b) { + return a.mFlag != b.mFlag; + } + + private: + unsigned int mFlag{}; + }; + + enum class Terminate { None, Bail, Escape, Modified, Inconclusive, Conditional }; + + struct Result { + explicit Result(Action action = Action::None, Terminate terminate = Terminate::None) + : action(action), terminate(terminate) + {} + Action action; + Terminate terminate; + + void update(Result rhs) { + if (terminate == Terminate::None) + terminate = rhs.terminate; + action |= rhs.action; + } + }; + + enum class Direction { Forward, Reverse }; + + struct Assume { + enum Flags { + None = 0, + Quiet = (1 << 0), + Absolute = (1 << 1), + ContainerEmpty = (1 << 2), + }; + }; + + enum class Evaluate { Integral, ContainerEmpty }; + + /// Analyze a token + virtual Action analyze(const Token* tok, Direction d) const = 0; + /// Update the state of the value + virtual void update(Token* tok, Action a, Direction d) = 0; + /// Try to evaluate the value of a token(most likely a condition) + virtual std::vector evaluate(Evaluate e, const Token* tok, const Token* ctx = nullptr) const = 0; + std::vector evaluate(const Token* tok, const Token* ctx = nullptr) const + { + return evaluate(Evaluate::Integral, tok, ctx); + } + /// Lower any values to possible + virtual bool lowerToPossible() = 0; + /// Lower any values to inconclusive + virtual bool lowerToInconclusive() = 0; + /// If the analysis is unsure whether to update a scope, this will return true if the analysis should bifurcate the scope + virtual bool updateScope(const Token* endBlock, bool modified) const = 0; + /// If the value is conditional + virtual bool isConditional() const = 0; + /// If analysis should stop on the condition + virtual bool stopOnCondition(const Token* condTok) const = 0; + /// The condition that will be assumed during analysis + virtual void assume(const Token* tok, bool state, unsigned int flags = 0) = 0; + /// Update the state of the program at the token + virtual void updateState(const Token* tok) = 0; + /// Return analyzer for expression at token + virtual ValuePtr reanalyze(Token* tok, const std::string& msg = emptyString) const = 0; + virtual bool invalid() const { + return false; + } + virtual ~Analyzer() = default; + Analyzer(const Analyzer&) = default; +protected: + Analyzer() = default; +}; + +#endif diff --git a/cppcheck-2.14.0/lib/analyzerinfo.cpp b/cppcheck-2.14.0/lib/analyzerinfo.cpp new file mode 100644 index 00000000..9b5b06c2 --- /dev/null +++ b/cppcheck-2.14.0/lib/analyzerinfo.cpp @@ -0,0 +1,163 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "analyzerinfo.h" + +#include "errorlogger.h" +#include "filesettings.h" +#include "path.h" +#include "utils.h" + +#include +#include + +#include "xml.h" + +AnalyzerInformation::~AnalyzerInformation() +{ + close(); +} + +static std::string getFilename(const std::string &fullpath) +{ + std::string::size_type pos1 = fullpath.find_last_of("/\\"); + pos1 = (pos1 == std::string::npos) ? 0U : (pos1 + 1U); + std::string::size_type pos2 = fullpath.rfind('.'); + if (pos2 < pos1) + pos2 = std::string::npos; + if (pos2 != std::string::npos) + pos2 = pos2 - pos1; + return fullpath.substr(pos1,pos2); +} + +void AnalyzerInformation::writeFilesTxt(const std::string &buildDir, const std::list &sourcefiles, const std::string &userDefines, const std::list &fileSettings) +{ + std::map fileCount; + + const std::string filesTxt(buildDir + "/files.txt"); + std::ofstream fout(filesTxt); + for (const std::string &f : sourcefiles) { + const std::string afile = getFilename(f); + fout << afile << ".a" << (++fileCount[afile]) << "::" << Path::simplifyPath(Path::fromNativeSeparators(f)) << '\n'; + if (!userDefines.empty()) + fout << afile << ".a" << (++fileCount[afile]) << ":" << userDefines << ":" << Path::simplifyPath(Path::fromNativeSeparators(f)) << '\n'; + } + + for (const FileSettings &fs : fileSettings) { + const std::string afile = getFilename(fs.filename); + fout << afile << ".a" << (++fileCount[afile]) << ":" << fs.cfg << ":" << Path::simplifyPath(Path::fromNativeSeparators(fs.filename)) << std::endl; + } +} + +void AnalyzerInformation::close() +{ + mAnalyzerInfoFile.clear(); + if (mOutputStream.is_open()) { + mOutputStream << "\n"; + mOutputStream.close(); + } +} + +static bool skipAnalysis(const std::string &analyzerInfoFile, std::size_t hash, std::list &errors) +{ + tinyxml2::XMLDocument doc; + const tinyxml2::XMLError error = doc.LoadFile(analyzerInfoFile.c_str()); + if (error != tinyxml2::XML_SUCCESS) + return false; + + const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement(); + if (rootNode == nullptr) + return false; + + const char *attr = rootNode->Attribute("hash"); + if (!attr || attr != std::to_string(hash)) + return false; + + for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (std::strcmp(e->Name(), "error") == 0) + errors.emplace_back(e); + } + + return true; +} + +std::string AnalyzerInformation::getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfg) +{ + std::string line; + const std::string end(':' + cfg + ':' + Path::simplifyPath(sourcefile)); + while (std::getline(filesTxt,line)) { + if (line.size() <= end.size() + 2U) + continue; + if (!endsWith(line, end.c_str(), end.size())) + continue; + return line.substr(0,line.find(':')); + } + return ""; +} + +std::string AnalyzerInformation::getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg) +{ + std::ifstream fin(Path::join(buildDir, "files.txt")); + if (fin.is_open()) { + const std::string& ret = getAnalyzerInfoFileFromFilesTxt(fin, sourcefile, cfg); + if (!ret.empty()) + return Path::join(buildDir, ret); + } + + const std::string::size_type pos = sourcefile.rfind('/'); + std::string filename; + if (pos == std::string::npos) + filename = sourcefile; + else + filename = sourcefile.substr(pos + 1); + return Path::join(buildDir, filename) + ".analyzerinfo"; +} + +bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t hash, std::list &errors) +{ + if (buildDir.empty() || sourcefile.empty()) + return true; + close(); + + mAnalyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir,sourcefile,cfg); + + if (skipAnalysis(mAnalyzerInfoFile, hash, errors)) + return false; + + mOutputStream.open(mAnalyzerInfoFile); + if (mOutputStream.is_open()) { + mOutputStream << "\n"; + mOutputStream << "\n"; + } else { + mAnalyzerInfoFile.clear(); + } + + return true; +} + +void AnalyzerInformation::reportErr(const ErrorMessage &msg) +{ + if (mOutputStream.is_open()) + mOutputStream << msg.toXML() << '\n'; +} + +void AnalyzerInformation::setFileInfo(const std::string &check, const std::string &fileInfo) +{ + if (mOutputStream.is_open() && !fileInfo.empty()) + mOutputStream << " \n" << fileInfo << " \n"; +} diff --git a/cppcheck-2.14.0/lib/analyzerinfo.h b/cppcheck-2.14.0/lib/analyzerinfo.h new file mode 100644 index 00000000..1818fa22 --- /dev/null +++ b/cppcheck-2.14.0/lib/analyzerinfo.h @@ -0,0 +1,71 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef analyzerinfoH +#define analyzerinfoH +//--------------------------------------------------------------------------- + +#include "config.h" + +#include +#include +#include +#include + +class ErrorMessage; +struct FileSettings; + +/// @addtogroup Core +/// @{ + +/** + * @brief Analyzer information + * + * Store various analysis information: + * - checksum + * - error messages + * - whole program analysis data + * + * The information can be used for various purposes. It allows: + * - 'make' - only analyze TUs that are changed and generate full report + * - should be possible to add distributed analysis later + * - multi-threaded whole program analysis + */ +class CPPCHECKLIB AnalyzerInformation { +public: + ~AnalyzerInformation(); + + static void writeFilesTxt(const std::string &buildDir, const std::list &sourcefiles, const std::string &userDefines, const std::list &fileSettings); + + /** Close current TU.analyzerinfo file */ + void close(); + bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t hash, std::list &errors); + void reportErr(const ErrorMessage &msg); + void setFileInfo(const std::string &check, const std::string &fileInfo); + static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg); +protected: + static std::string getAnalyzerInfoFileFromFilesTxt(std::istream& filesTxt, const std::string &sourcefile, const std::string &cfg); +private: + std::ofstream mOutputStream; + std::string mAnalyzerInfoFile; +}; + +/// @} +//--------------------------------------------------------------------------- +#endif // analyzerinfoH diff --git a/cppcheck-2.14.0/lib/astutils.cpp b/cppcheck-2.14.0/lib/astutils.cpp new file mode 100644 index 00000000..e4c5f24d --- /dev/null +++ b/cppcheck-2.14.0/lib/astutils.cpp @@ -0,0 +1,3590 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#include "astutils.h" + +#include "config.h" +#include "errortypes.h" +#include "findtoken.h" +#include "infer.h" +#include "library.h" +#include "mathlib.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "utils.h" +#include "valueflow.h" +#include "valueptr.h" +#include "vfvalue.h" + +#include "checkclass.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const Token* findExpression(const nonneg int exprid, + const Token* start, + const Token* end, + const std::function& pred) +{ + if (exprid == 0) + return nullptr; + if (!precedes(start, end)) + return nullptr; + for (const Token* tok = start; tok != end; tok = tok->next()) { + if (tok->exprId() != exprid) + continue; + if (pred(tok)) + return tok; + } + return nullptr; +} + +static int findArgumentPosRecursive(const Token* tok, const Token* tokToFind, bool &found, nonneg int depth=0) +{ + ++depth; + if (!tok || depth >= 100) + return -1; + if (tok->str() == ",") { + int res = findArgumentPosRecursive(tok->astOperand1(), tokToFind, found, depth); + if (res == -1) + return -1; + if (found) + return res; + const int argn = res; + res = findArgumentPosRecursive(tok->astOperand2(), tokToFind, found, depth); + if (res == -1) + return -1; + return argn + res; + } + if (tokToFind == tok) + found = true; + return 1; +} + +static int findArgumentPos(const Token* tok, const Token* tokToFind){ + bool found = false; + const int argn = findArgumentPosRecursive(tok, tokToFind, found, 0); + if (found) + return argn - 1; + return -1; +} + +static int getArgumentPos(const Token* ftok, const Token* tokToFind){ + const Token* tok = ftok; + if (Token::Match(tok, "%name% (|{")) + tok = ftok->next(); + if (!Token::Match(tok, "(|{|[")) + return -1; + const Token* startTok = tok->astOperand2(); + if (!startTok && tok->next() != tok->link()) + startTok = tok->astOperand1(); + return findArgumentPos(startTok, tokToFind); +} + +std::vector astFlatten(const Token* tok, const char* op) +{ + std::vector result; + astFlattenCopy(tok, op, std::back_inserter(result)); + return result; +} + +std::vector astFlatten(Token* tok, const char* op) +{ + std::vector result; + astFlattenCopy(tok, op, std::back_inserter(result)); + return result; +} + +nonneg int astCount(const Token* tok, const char* op, int depth) +{ + --depth; + if (!tok || depth < 0) + return 0; + if (tok->str() == op) + return astCount(tok->astOperand1(), op, depth) + astCount(tok->astOperand2(), op, depth); + return 1; +} + +bool astHasToken(const Token* root, const Token * tok) +{ + if (!root) + return false; + while (tok->astParent() && tok != root) + tok = tok->astParent(); + return root == tok; +} + +bool astHasVar(const Token * tok, nonneg int varid) +{ + if (!tok) + return false; + if (tok->varId() == varid) + return true; + return astHasVar(tok->astOperand1(), varid) || astHasVar(tok->astOperand2(), varid); +} + +bool astHasExpr(const Token* tok, nonneg int exprid) +{ + if (!tok) + return false; + if (tok->exprId() == exprid) + return true; + return astHasExpr(tok->astOperand1(), exprid) || astHasExpr(tok->astOperand2(), exprid); +} + +static bool astIsCharWithSign(const Token *tok, ValueType::Sign sign) +{ + if (!tok) + return false; + const ValueType *valueType = tok->valueType(); + if (!valueType) + return false; + return valueType->type == ValueType::Type::CHAR && valueType->pointer == 0U && valueType->sign == sign; +} + +bool astIsSignedChar(const Token *tok) +{ + return astIsCharWithSign(tok, ValueType::Sign::SIGNED); +} + +bool astIsUnknownSignChar(const Token *tok) +{ + return astIsCharWithSign(tok, ValueType::Sign::UNKNOWN_SIGN); +} + +bool astIsGenericChar(const Token* tok) +{ + return !astIsPointer(tok) && tok && tok->valueType() && (tok->valueType()->type == ValueType::Type::CHAR || tok->valueType()->type == ValueType::Type::WCHAR_T); +} + +bool astIsPrimitive(const Token* tok) +{ + const ValueType* vt = tok ? tok->valueType() : nullptr; + if (!vt) + return false; + return vt->isPrimitive(); +} + +bool astIsIntegral(const Token *tok, bool unknown) +{ + const ValueType *vt = tok ? tok->valueType() : nullptr; + if (!vt) + return unknown; + return vt->isIntegral() && vt->pointer == 0U; +} + +bool astIsUnsigned(const Token* tok) +{ + return tok && tok->valueType() && tok->valueType()->sign == ValueType::UNSIGNED; +} + +bool astIsFloat(const Token *tok, bool unknown) +{ + const ValueType *vt = tok ? tok->valueType() : nullptr; + if (!vt) + return unknown; + return vt->type >= ValueType::Type::FLOAT && vt->pointer == 0U; +} + +bool astIsBool(const Token *tok) +{ + return tok && (tok->isBoolean() || (tok->valueType() && tok->valueType()->type == ValueType::Type::BOOL && !tok->valueType()->pointer)); +} + +bool astIsPointer(const Token *tok) +{ + return tok && tok->valueType() && tok->valueType()->pointer; +} + +bool astIsSmartPointer(const Token* tok) +{ + return tok && tok->valueType() && tok->valueType()->smartPointerTypeToken; +} + +bool astIsUniqueSmartPointer(const Token* tok) +{ + if (!astIsSmartPointer(tok)) + return false; + if (!tok->valueType()->smartPointer) + return false; + return tok->valueType()->smartPointer->unique; +} + +bool astIsIterator(const Token *tok) +{ + return tok && tok->valueType() && tok->valueType()->type == ValueType::Type::ITERATOR; +} + +bool astIsContainer(const Token* tok) { + return getLibraryContainer(tok) != nullptr && !astIsIterator(tok); +} + +bool astIsNonStringContainer(const Token* tok) +{ + const Library::Container* container = getLibraryContainer(tok); + return container && !container->stdStringLike && !astIsIterator(tok); +} + +bool astIsContainerView(const Token* tok) +{ + const Library::Container* container = getLibraryContainer(tok); + return container && !astIsIterator(tok) && container->view; +} + +bool astIsContainerOwned(const Token* tok) { + return astIsContainer(tok) && !astIsContainerView(tok); +} + +bool astIsContainerString(const Token* tok) +{ + if (!tok) + return false; + if (!tok->valueType()) + return false; + const Library::Container* container = tok->valueType()->container; + if (!container) + return false; + return container->stdStringLike; +} + +static const Token* getContainerFunction(const Token* tok) +{ + if (!tok || !tok->valueType() || !tok->valueType()->container) + return nullptr; + const Token* parent = tok->astParent(); + if (Token::Match(parent, ". %name% (") && astIsLHS(tok)) { + return parent->next(); + } + return nullptr; +} + +Library::Container::Action astContainerAction(const Token* tok, const Token** ftok) +{ + const Token* ftok2 = getContainerFunction(tok); + if (ftok) + *ftok = ftok2; + if (!ftok2) + return Library::Container::Action::NO_ACTION; + return tok->valueType()->container->getAction(ftok2->str()); +} + +Library::Container::Yield astContainerYield(const Token* tok, const Token** ftok) +{ + const Token* ftok2 = getContainerFunction(tok); + if (ftok) + *ftok = ftok2; + if (!ftok2) + return Library::Container::Yield::NO_YIELD; + return tok->valueType()->container->getYield(ftok2->str()); +} + +Library::Container::Yield astFunctionYield(const Token* tok, const Settings& settings, const Token** ftok) +{ + if (!tok) + return Library::Container::Yield::NO_YIELD; + + const auto* function = settings.library.getFunction(tok); + if (!function) + return Library::Container::Yield::NO_YIELD; + + if (ftok) + *ftok = tok; + return function->containerYield; +} + +bool astIsRangeBasedForDecl(const Token* tok) +{ + return Token::simpleMatch(tok->astParent(), ":") && Token::simpleMatch(tok->astParent()->astParent(), "("); +} + +std::string astCanonicalType(const Token *expr, bool pointedToType) +{ + if (!expr) + return ""; + std::pair decl = Token::typeDecl(expr, pointedToType); + if (decl.first && decl.second) { + std::string ret; + for (const Token *type = decl.first; Token::Match(type,"%name%|::") && type != decl.second; type = type->next()) { + if (!Token::Match(type, "const|static")) + ret += type->str(); + } + return ret; + } + return ""; +} + +static bool match(const Token *tok, const std::string &rhs) +{ + if (tok->str() == rhs) + return true; + if (!tok->varId() && tok->hasKnownIntValue() && std::to_string(tok->values().front().intvalue) == rhs) + return true; + return false; +} + +const Token * astIsVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok) +{ + if (!tok) + return nullptr; + + const Token *ret = nullptr; + if (tok->isComparisonOp()) { + if (tok->astOperand1() && match(tok->astOperand1(), rhs)) { + // Invert comparator + std::string s = tok->str(); + if (s[0] == '>') + s[0] = '<'; + else if (s[0] == '<') + s[0] = '>'; + if (s == comp) { + ret = tok->astOperand2(); + } + } else if (tok->str() == comp && tok->astOperand2() && match(tok->astOperand2(), rhs)) { + ret = tok->astOperand1(); + } + } else if (comp == "!=" && rhs == "0") { + if (tok->str() == "!") { + ret = tok->astOperand1(); + // handle (!(x==0)) as (x!=0) + astIsVariableComparison(ret, "==", "0", &ret); + } else + ret = tok; + } else if (comp == "==" && rhs == "0") { + if (tok->str() == "!") { + ret = tok->astOperand1(); + // handle (!(x!=0)) as (x==0) + astIsVariableComparison(ret, "!=", "0", &ret); + } + } + while (ret && ret->str() == ".") + ret = ret->astOperand2(); + if (ret && ret->str() == "=" && ret->astOperand1() && ret->astOperand1()->varId()) + ret = ret->astOperand1(); + else if (ret && ret->varId() == 0U) + ret = nullptr; + if (vartok) + *vartok = ret; + return ret; +} + +bool isVariableDecl(const Token* tok) +{ + if (!tok) + return false; + const Variable* var = tok->variable(); + if (!var) + return false; + if (var->nameToken() == tok) + return true; + const Token * const varDeclEndToken = var->declEndToken(); + return Token::Match(varDeclEndToken, "; %var%") && varDeclEndToken->next() == tok; +} + +bool isStlStringType(const Token* tok) +{ + return Token::Match(tok, "std :: string|wstring|u16string|u32string !!::") || + (Token::simpleMatch(tok, "std :: basic_string <") && !Token::simpleMatch(tok->linkAt(3), "> ::")); +} + +bool isTemporary(const Token* tok, const Library* library, bool unknown) +{ + if (!tok) + return false; + if (Token::simpleMatch(tok, ".")) + return (tok->originalName() != "->" && isTemporary(tok->astOperand1(), library)) || + isTemporary(tok->astOperand2(), library); + if (Token::Match(tok, ",|::")) + return isTemporary(tok->astOperand2(), library); + if (tok->isCast() || (tok->isCpp() && isCPPCast(tok))) + return isTemporary(tok->astOperand2(), library); + if (Token::Match(tok, ".|[|++|--|%name%|%assign%")) + return false; + if (tok->isUnaryOp("*")) + return false; + if (Token::Match(tok, "&|<<|>>") && isLikelyStream(tok->astOperand1())) + return false; + if (Token::simpleMatch(tok, "?")) { + const Token* branchTok = tok->astOperand2(); + if (!branchTok->astOperand1() || !branchTok->astOperand1()->valueType()) + return false; + if (!branchTok->astOperand2()->valueType()) + return false; + return !branchTok->astOperand1()->valueType()->isTypeEqual(branchTok->astOperand2()->valueType()); + } + if (Token::simpleMatch(tok, "(") && tok->astOperand1() && + (tok->astOperand2() || Token::simpleMatch(tok->next(), ")"))) { + if (Token::simpleMatch(tok->astOperand1(), "typeid")) + return false; + if (tok->valueType()) { + if (tok->valueType()->pointer > 0) { + const Token* const parent = tok->astParent(); + if (Token::simpleMatch(parent, "&")) + return true; + if (Token::simpleMatch(parent, "return") && parent->valueType()->reference != Reference::None && + parent->valueType()->container && parent->valueType()->container->stdStringLike) + return true; + } + return tok->valueType()->reference == Reference::None && tok->valueType()->pointer == 0; + } + const Token* ftok = nullptr; + if (Token::simpleMatch(tok->previous(), ">") && tok->previous()->link()) + ftok = tok->previous()->link()->previous(); + else + ftok = tok->previous(); + if (!ftok) + return false; + if (const Function * f = ftok->function()) + return !Function::returnsReference(f, true); + if (ftok->type()) + return true; + if (library) { + std::string returnType = library->returnValueType(ftok); + return !returnType.empty() && returnType.back() != '&'; + } + return unknown; + } + if (tok->isCast()) + return false; + // Currying a function is unknown in cppcheck + if (Token::simpleMatch(tok, "(") && Token::simpleMatch(tok->astOperand1(), "(")) + return unknown; + if (Token::simpleMatch(tok, "{") && Token::simpleMatch(tok->astParent(), "return") && tok->astOperand1() && + !tok->astOperand2()) + return isTemporary(tok->astOperand1(), library); + return true; +} + +static bool isFunctionCall(const Token* tok) +{ + if (Token::Match(tok, "%name% (")) + return true; + if (Token::Match(tok, "%name% <") && Token::simpleMatch(tok->next()->link(), "> (")) + return true; + if (Token::Match(tok, "%name% ::")) + return isFunctionCall(tok->tokAt(2)); + return false; +} + +static bool hasToken(const Token * startTok, const Token * stopTok, const Token * tok) +{ + for (const Token * tok2 = startTok; tok2 != stopTok; tok2 = tok2->next()) { + if (tok2 == tok) + return true; + } + return false; +} + +template )> +static T* previousBeforeAstLeftmostLeafGeneric(T* tok) +{ + if (!tok) + return nullptr; + T* leftmostLeaf = tok; + while (leftmostLeaf->astOperand1()) + leftmostLeaf = leftmostLeaf->astOperand1(); + return leftmostLeaf->previous(); +} + +const Token* previousBeforeAstLeftmostLeaf(const Token* tok) +{ + return previousBeforeAstLeftmostLeafGeneric(tok); +} +Token* previousBeforeAstLeftmostLeaf(Token* tok) +{ + return previousBeforeAstLeftmostLeafGeneric(tok); +} + +template )> +static T* nextAfterAstRightmostLeafGeneric(T* tok) +{ + T * rightmostLeaf = tok; + if (!rightmostLeaf || !rightmostLeaf->astOperand1()) + return nullptr; + do { + if (T* lam = findLambdaEndToken(rightmostLeaf)) { + rightmostLeaf = lam; + break; + } + if (rightmostLeaf->astOperand2() && precedes(rightmostLeaf, rightmostLeaf->astOperand2())) + rightmostLeaf = rightmostLeaf->astOperand2(); + else if (rightmostLeaf->astOperand1() && precedes(rightmostLeaf, rightmostLeaf->astOperand1())) + rightmostLeaf = rightmostLeaf->astOperand1(); + else + break; + } while (rightmostLeaf->astOperand1() || rightmostLeaf->astOperand2()); + while (Token::Match(rightmostLeaf->next(), "]|)") && !hasToken(rightmostLeaf->next()->link(), rightmostLeaf->next(), tok)) + rightmostLeaf = rightmostLeaf->next(); + if (Token::Match(rightmostLeaf, "{|(|[") && rightmostLeaf->link()) + rightmostLeaf = rightmostLeaf->link(); + return rightmostLeaf->next(); +} + +const Token* nextAfterAstRightmostLeaf(const Token* tok) +{ + return nextAfterAstRightmostLeafGeneric(tok); +} +Token* nextAfterAstRightmostLeaf(Token* tok) +{ + return nextAfterAstRightmostLeafGeneric(tok); +} + +const Token* astParentSkipParens(const Token* tok) +{ + return astParentSkipParens(const_cast(tok)); +} +Token* astParentSkipParens(Token* tok) +{ + if (!tok) + return nullptr; + Token * parent = tok->astParent(); + if (!Token::simpleMatch(parent, "(")) + return parent; + if (parent->link() != nextAfterAstRightmostLeaf(tok)) + return parent; + if (Token::Match(parent->previous(), "%name% (") || + (Token::simpleMatch(parent->previous(), "> (") && parent->previous()->link())) + return parent; + return astParentSkipParens(parent); +} + +const Token* getParentMember(const Token * tok) +{ + if (!tok) + return tok; + const Token * parent = tok->astParent(); + if (!Token::simpleMatch(parent, ".")) + return tok; + if (astIsRHS(tok)) { + if (Token::simpleMatch(parent->astOperand1(), ".")) + return parent->astOperand1()->astOperand2(); + return parent->astOperand1(); + } + const Token * gparent = parent->astParent(); + if (!Token::simpleMatch(gparent, ".") || gparent->astOperand2() != parent) + return tok; + if (gparent->astOperand1()) + return gparent->astOperand1(); + return tok; +} + +const Token* getParentLifetime(const Token* tok) +{ + if (!tok) + return tok; + // Skipping checking for variable if its a pointer-to-member + if (!Token::simpleMatch(tok->previous(), ". *")) { + const Variable* var = tok->variable(); + // TODO: Call getLifetimeVariable for deeper analysis + if (!var) + return tok; + if (var->isLocal() || var->isArgument()) + return tok; + } + const Token* parent = getParentMember(tok); + if (parent != tok) + return getParentLifetime(parent); + return tok; +} + +static std::vector getParentMembers(const Token* tok) +{ + if (!tok) + return {}; + if (!Token::simpleMatch(tok->astParent(), ".")) + return {tok}; + const Token* parent = tok->astParent(); + while (Token::simpleMatch(parent->astParent(), ".")) + parent = parent->astParent(); + std::vector result; + for (const Token* tok2 : astFlatten(parent, ".")) { + if (Token::simpleMatch(tok2, "(") && Token::simpleMatch(tok2->astOperand1(), ".")) { + std::vector sub = getParentMembers(tok2->astOperand1()); + result.insert(result.end(), sub.cbegin(), sub.cend()); + } + result.push_back(tok2); + } + return result; +} + +const Token* getParentLifetime(const Token* tok, const Library& library) +{ + std::vector members = getParentMembers(tok); + if (members.size() < 2) + return tok; + // Find the first local variable, temporary, or array + auto it = std::find_if(members.crbegin(), members.crend(), [&](const Token* tok2) { + const Variable* var = tok2->variable(); + if (var) + return var->isLocal() || var->isArgument(); + if (Token::simpleMatch(tok2, "[")) + return true; + return isTemporary(tok2, &library); + }); + if (it == members.rend()) + return tok; + // If any of the submembers are borrowed types then stop + if (std::any_of(it.base() - 1, members.cend() - 1, [&](const Token* tok2) { + const Token* obj = tok2; + if (Token::simpleMatch(obj, "[")) + obj = tok2->astOperand1(); + const Variable* var = obj->variable(); + // Check for arrays first since astIsPointer will return true, but an array is not a borrowed type + if (var && var->isArray()) + return false; + if (astIsPointer(obj) || astIsContainerView(obj) || astIsIterator(obj)) + return true; + if (!astIsUniqueSmartPointer(obj)) { + if (astIsSmartPointer(obj)) + return true; + const Token* dotTok = obj->next(); + if (!Token::simpleMatch(dotTok, ".")) { + const Token* endTok = nextAfterAstRightmostLeaf(obj); + if (!endTok) + dotTok = obj->next(); + else if (Token::simpleMatch(endTok, ".")) + dotTok = endTok; + else if (Token::simpleMatch(endTok->next(), ".")) + dotTok = endTok->next(); + } + // If we are dereferencing the member variable then treat it as borrowed + if (Token::simpleMatch(dotTok, ".") && dotTok->originalName() == "->") + return true; + } + return var && var->isReference(); + })) + return nullptr; + const Token* result = *it; + if (Token::simpleMatch(result, "[") && result->astOperand1()) + return getParentLifetime(result->astOperand1()); + return result; +} + +static bool isInConstructorList(const Token* tok) +{ + if (!tok) + return false; + if (!astIsRHS(tok)) + return false; + const Token* parent = tok->astParent(); + if (!Token::Match(parent, "{|(")) + return false; + if (!Token::Match(parent->previous(), "%var% {|(")) + return false; + if (!parent->astOperand1() || !parent->astOperand2()) + return false; + do { + parent = parent->astParent(); + } while (Token::simpleMatch(parent, ",")); + return Token::simpleMatch(parent, ":") && !Token::simpleMatch(parent->astParent(), "?"); +} + +std::vector getParentValueTypes(const Token* tok, const Settings* settings, const Token** parent) +{ + if (!tok) + return {}; + if (!tok->astParent()) + return {}; + if (isInConstructorList(tok)) { + if (parent) + *parent = tok->astParent()->astOperand1(); + if (tok->astParent()->astOperand1()->valueType()) + return {*tok->astParent()->astOperand1()->valueType()}; + return {}; + } + const Token* ftok = nullptr; + if (Token::Match(tok->astParent(), "(|{|,")) { + int argn = -1; + ftok = getTokenArgumentFunction(tok, argn); + const Token* typeTok = nullptr; + if (ftok && argn >= 0) { + if (ftok->function()) { + std::vector result; + const Token* nameTok = nullptr; + for (const Variable* var : getArgumentVars(ftok, argn)) { + if (!var) + continue; + if (!var->valueType()) + continue; + nameTok = var->nameToken(); + result.push_back(*var->valueType()); + } + if (result.size() == 1 && nameTok && parent) { + *parent = nameTok; + } + return result; + } + if (const Type* t = Token::typeOf(ftok, &typeTok)) { + if (astIsPointer(typeTok)) + return {*typeTok->valueType()}; + const Scope* scope = t->classScope; + // Check for aggregate constructors + if (scope && scope->numConstructors == 0 && t->derivedFrom.empty() && + (t->isClassType() || t->isStructType()) && numberOfArguments(ftok) <= scope->varlist.size() && + !scope->varlist.empty()) { + assert(argn < scope->varlist.size()); + auto it = std::next(scope->varlist.cbegin(), argn); + if (it->valueType()) + return {*it->valueType()}; + } + } + } + } + if (settings && Token::Match(tok->astParent()->tokAt(-2), ". push_back|push_front|insert|push (") && + astIsContainer(tok->astParent()->tokAt(-2)->astOperand1())) { + const Token* contTok = tok->astParent()->tokAt(-2)->astOperand1(); + const ValueType* vtCont = contTok->valueType(); + if (!vtCont->containerTypeToken) + return {}; + ValueType vtParent = ValueType::parseDecl(vtCont->containerTypeToken, *settings); + return {std::move(vtParent)}; + } + // The return type of a function is not the parent valuetype + if (Token::simpleMatch(tok->astParent(), "(") && ftok && !tok->astParent()->isCast() && + ftok->tokType() != Token::eType) + return {}; + if (parent && Token::Match(tok->astParent(), "return|(|{|%assign%")) { + *parent = tok->astParent(); + } + if (tok->astParent()->valueType()) + return {*tok->astParent()->valueType()}; + return {}; +} + +bool astIsLHS(const Token* tok) +{ + if (!tok) + return false; + const Token* parent = tok->astParent(); + if (!parent) + return false; + if (!parent->astOperand1()) + return false; + if (!parent->astOperand2()) + return false; + return parent->astOperand1() == tok; +} +bool astIsRHS(const Token* tok) +{ + if (!tok) + return false; + const Token* parent = tok->astParent(); + if (!parent) + return false; + if (!parent->astOperand1()) + return false; + if (!parent->astOperand2()) + return false; + return parent->astOperand2() == tok; +} + +template )> +static T* getCondTokImpl(T* tok) +{ + if (!tok) + return nullptr; + if (Token::simpleMatch(tok, "(")) + return getCondTok(tok->previous()); + if (Token::simpleMatch(tok, "for") && Token::simpleMatch(tok->next()->astOperand2(), ";") && + tok->next()->astOperand2()->astOperand2()) + return tok->next()->astOperand2()->astOperand2()->astOperand1(); + if (Token::simpleMatch(tok->next()->astOperand2(), ";")) + return tok->next()->astOperand2()->astOperand1(); + return tok->next()->astOperand2(); +} + +template )> +static T* getCondTokFromEndImpl(T* endBlock) +{ + if (!Token::simpleMatch(endBlock, "}")) + return nullptr; + T* startBlock = endBlock->link(); + if (!Token::simpleMatch(startBlock, "{")) + return nullptr; + if (Token::simpleMatch(startBlock->previous(), ")")) + return getCondTok(startBlock->previous()->link()); + if (Token::simpleMatch(startBlock->tokAt(-2), "} else {")) + return getCondTokFromEnd(startBlock->tokAt(-2)); + return nullptr; +} + +template )> +static T* getInitTokImpl(T* tok) +{ + if (!tok) + return nullptr; + if (Token::Match(tok, "%name% (")) + return getInitTokImpl(tok->next()); + if (tok->str() != "(") + return nullptr; + if (!Token::simpleMatch(tok->astOperand2(), ";")) + return nullptr; + if (Token::simpleMatch(tok->astOperand2()->astOperand1(), ";")) + return nullptr; + return tok->astOperand2()->astOperand1(); +} + +template )> +static T* getStepTokImpl(T* tok) +{ + if (!tok) + return nullptr; + if (Token::Match(tok, "%name% (")) + return getStepTokImpl(tok->next()); + if (tok->str() != "(") + return nullptr; + if (!Token::simpleMatch(tok->astOperand2(), ";")) + return nullptr; + if (!Token::simpleMatch(tok->astOperand2()->astOperand2(), ";")) + return nullptr; + return tok->astOperand2()->astOperand2()->astOperand2(); +} + +Token* getCondTok(Token* tok) +{ + return getCondTokImpl(tok); +} +const Token* getCondTok(const Token* tok) +{ + return getCondTokImpl(tok); +} + +Token* getCondTokFromEnd(Token* endBlock) +{ + return getCondTokFromEndImpl(endBlock); +} +const Token* getCondTokFromEnd(const Token* endBlock) +{ + return getCondTokFromEndImpl(endBlock); +} + +Token* getInitTok(Token* tok) { + return getInitTokImpl(tok); +} +const Token* getInitTok(const Token* tok) { + return getInitTokImpl(tok); +} + +Token* getStepTok(Token* tok) { + return getStepTokImpl(tok); +} +const Token* getStepTok(const Token* tok) { + return getStepTokImpl(tok); +} + +const Token *findNextTokenFromBreak(const Token *breakToken) +{ + const Scope *scope = breakToken->scope(); + while (scope) { + if (scope->isLoopScope() || scope->type == Scope::ScopeType::eSwitch) { + if (scope->type == Scope::ScopeType::eDo && Token::simpleMatch(scope->bodyEnd, "} while (")) + return scope->bodyEnd->linkAt(2)->next(); + return scope->bodyEnd; + } + scope = scope->nestedIn; + } + return nullptr; +} + +bool extractForLoopValues(const Token *forToken, + nonneg int &varid, + bool &knownInitValue, + MathLib::bigint &initValue, + bool &partialCond, + MathLib::bigint &stepValue, + MathLib::bigint &lastValue) +{ + if (!Token::simpleMatch(forToken, "for (") || !Token::simpleMatch(forToken->next()->astOperand2(), ";")) + return false; + const Token *initExpr = forToken->next()->astOperand2()->astOperand1(); + const Token *condExpr = forToken->next()->astOperand2()->astOperand2()->astOperand1(); + const Token *incExpr = forToken->next()->astOperand2()->astOperand2()->astOperand2(); + if (!initExpr || !initExpr->isBinaryOp() || initExpr->str() != "=" || !Token::Match(initExpr->astOperand1(), "%var%")) + return false; + std::vector minInitValue = getMinValue(ValueFlow::makeIntegralInferModel(), initExpr->astOperand2()->values()); + if (minInitValue.empty()) { + const ValueFlow::Value* v = initExpr->astOperand2()->getMinValue(true); + if (v) + minInitValue.push_back(v->intvalue); + } + if (minInitValue.empty()) + return false; + varid = initExpr->astOperand1()->varId(); + knownInitValue = initExpr->astOperand2()->hasKnownIntValue(); + initValue = minInitValue.front(); + partialCond = Token::Match(condExpr, "%oror%|&&"); + visitAstNodes(condExpr, [varid, &condExpr](const Token *tok) { + if (Token::Match(tok, "%oror%|&&")) + return ChildrenToVisit::op1_and_op2; + if (Token::Match(tok, "<|<=") && tok->isBinaryOp() && tok->astOperand1()->varId() == varid && tok->astOperand2()->hasKnownIntValue()) { + if (Token::Match(condExpr, "%oror%|&&") || tok->astOperand2()->getKnownIntValue() < condExpr->astOperand2()->getKnownIntValue()) + condExpr = tok; + } + return ChildrenToVisit::none; + }); + if (!Token::Match(condExpr, "<|<=") || !condExpr->isBinaryOp() || condExpr->astOperand1()->varId() != varid || !condExpr->astOperand2()->hasKnownIntValue()) + return false; + if (!incExpr || !incExpr->isUnaryOp("++") || incExpr->astOperand1()->varId() != varid) + return false; + stepValue = 1; + if (condExpr->str() == "<") + lastValue = condExpr->astOperand2()->getKnownIntValue() - 1; + else + lastValue = condExpr->astOperand2()->getKnownIntValue(); + return true; +} + + +static const Token * getVariableInitExpression(const Variable * var) +{ + if (!var) + return nullptr; + const Token *varDeclEndToken = var->declEndToken(); + if (!varDeclEndToken) + return nullptr; + if (Token::Match(varDeclEndToken, "; %varid% =", var->declarationId())) + return varDeclEndToken->tokAt(2)->astOperand2(); + return varDeclEndToken->astOperand2(); +} + +const Token* isInLoopCondition(const Token* tok) +{ + const Token* top = tok->astTop(); + return top && Token::Match(top->previous(), "for|while (") ? top : nullptr; +} + +/// If tok2 comes after tok1 +bool precedes(const Token * tok1, const Token * tok2) +{ + if (tok1 == tok2) + return false; + if (!tok1) + return false; + if (!tok2) + return true; + return tok1->index() < tok2->index(); +} + +/// If tok1 comes after tok2 +bool succeeds(const Token* tok1, const Token* tok2) +{ + if (tok1 == tok2) + return false; + if (!tok1) + return false; + if (!tok2) + return true; + return tok1->index() > tok2->index(); +} + +bool isAliasOf(const Token *tok, nonneg int varid, bool* inconclusive) +{ + if (tok->varId() == varid) + return false; + // NOLINTNEXTLINE(readability-use-anyofallof) - TODO: fix this / also Cppcheck false negative + for (const ValueFlow::Value &val : tok->values()) { + if (!val.isLocalLifetimeValue()) + continue; + if (val.tokvalue->varId() == varid) { + if (val.isInconclusive()) { + if (inconclusive) + *inconclusive = true; + else + continue; + } + return true; + } + } + return false; +} + +bool isAliasOf(const Token* tok, const Token* expr, int* indirect, bool* inconclusive) +{ + const ValueFlow::Value* value = nullptr; + const Token* r = nullptr; + if (indirect) + *indirect = 1; + for (const ReferenceToken& ref : followAllReferences(tok)) { + const bool pointer = astIsPointer(ref.token); + r = findAstNode(expr, [&](const Token* childTok) { + if (childTok->exprId() == 0) + return false; + if (ref.token != tok && expr->exprId() == childTok->exprId()) { + if (indirect) + *indirect = 0; + return true; + } + for (const ValueFlow::Value& val : ref.token->values()) { + if (val.isImpossible()) + continue; + if (val.isLocalLifetimeValue() || (pointer && val.isSymbolicValue() && val.intvalue == 0)) { + if (findAstNode(val.tokvalue, + [&](const Token* aliasTok) { + return aliasTok != childTok && aliasTok->exprId() == childTok->exprId(); + })) { + if (val.isInconclusive() && inconclusive != nullptr) { + value = &val; + } else { + return true; + } + } + } + } + return false; + }); + if (r) + break; + } + if (!r && value && inconclusive) + *inconclusive = true; + return r || value; +} + +static bool isAliased(const Token *startTok, const Token *endTok, nonneg int varid) +{ + if (!precedes(startTok, endTok)) + return false; + for (const Token *tok = startTok; tok != endTok; tok = tok->next()) { + if (Token::Match(tok, "= & %varid% ;", varid)) + return true; + if (isAliasOf(tok, varid)) + return true; + } + return false; +} + +bool isAliased(const Variable *var) +{ + if (!var) + return false; + if (!var->scope()) + return false; + const Token *start = var->declEndToken(); + if (!start) + return false; + return isAliased(start, var->scope()->bodyEnd, var->declarationId()); +} + +bool exprDependsOnThis(const Token* expr, bool onVar, nonneg int depth) +{ + if (!expr) + return false; + if (expr->str() == "this") + return true; + if (depth >= 1000) + // Abort recursion to avoid stack overflow + return true; + ++depth; + + // calling nonstatic method? + if (Token::Match(expr, "%name% (") && expr->function() && expr->function()->nestedIn && expr->function()->nestedIn->isClassOrStruct() && !expr->function()->isStatic()) { + // is it a method of this? + const Scope* fScope = expr->scope(); + while (!fScope->functionOf && fScope->nestedIn) + fScope = fScope->nestedIn; + + const Scope* classScope = fScope->functionOf; + if (classScope && classScope->function) + classScope = classScope->function->token->scope(); + + if (classScope && classScope->isClassOrStruct()) + return contains(classScope->findAssociatedScopes(), expr->function()->nestedIn); + return false; + } + if (onVar && expr->variable()) { + const Variable* var = expr->variable(); + return ((var->isPrivate() || var->isPublic() || var->isProtected()) && !var->isStatic()); + } + if (Token::simpleMatch(expr, ".")) + return exprDependsOnThis(expr->astOperand1(), onVar, depth); + return exprDependsOnThis(expr->astOperand1(), onVar, depth) || exprDependsOnThis(expr->astOperand2(), onVar, depth); +} + +static bool hasUnknownVars(const Token* startTok) +{ + bool result = false; + visitAstNodes(startTok, [&](const Token* tok) { + if (tok->varId() > 0 && !tok->variable()) { + result = true; + return ChildrenToVisit::done; + } + return ChildrenToVisit::op1_and_op2; + }); + return result; +} + +bool isStructuredBindingVariable(const Variable* var) +{ + if (!var) + return false; + const Token* tok = var->nameToken(); + while (tok && Token::Match(tok->astParent(), "[|,|:")) + tok = tok->astParent(); + return tok && (tok->str() == "[" || Token::simpleMatch(tok->previous(), "] :")); // TODO: remove workaround when #11105 is fixed +} + +/// This takes a token that refers to a variable and it will return the token +/// to the expression that the variable is assigned to. If its not valid to +/// make such substitution then it will return the original token. +static const Token * followVariableExpression(const Token * tok, const Token * end = nullptr) +{ + if (!tok) + return tok; + // Skip following variables that is across multiple files + if (end && end->fileIndex() != tok->fileIndex()) + return tok; + // Skip array access + if (Token::Match(tok, "%var% [")) + return tok; + // Skip pointer indirection + if (tok->astParent() && tok->isUnaryOp("*")) + return tok; + // Skip following variables if it is used in an assignment + if (Token::Match(tok->next(), "%assign%")) + return tok; + const Variable * var = tok->variable(); + const Token * varTok = getVariableInitExpression(var); + if (!varTok) + return tok; + if (hasUnknownVars(varTok)) + return tok; + if (var->isVolatile()) + return tok; + if (!var->isLocal() && !var->isConst()) + return tok; + if (var->isStatic() && !var->isConst()) + return tok; + if (var->isArgument()) + return tok; + if (isStructuredBindingVariable(var)) + return tok; + // assigning a floating point value to an integer does not preserve the value + if (var->valueType() && var->valueType()->isIntegral() && varTok->valueType() && varTok->valueType()->isFloat()) + return tok; + const Token * lastTok = precedes(tok, end) ? end : tok; + // If this is in a loop then check if variables are modified in the entire scope + const Token * endToken = (isInLoopCondition(tok) || isInLoopCondition(varTok) || var->scope() != tok->scope()) ? var->scope()->bodyEnd : lastTok; + if (!var->isConst() && (!precedes(varTok, endToken) || isVariableChanged(varTok, endToken, tok->varId(), false, nullptr))) + return tok; + if (precedes(varTok, endToken) && isAliased(varTok, endToken, tok->varId())) + return tok; + const Token* startToken = nextAfterAstRightmostLeaf(varTok); + if (!startToken) + startToken = varTok; + if (varTok->exprId() == 0) { + if (!varTok->isLiteral()) + return tok; + } else if (!precedes(startToken, endToken)) { + return tok; + } else if (findExpressionChanged(varTok, startToken, endToken, nullptr)) { + return tok; + } + return varTok; +} + +static void followVariableExpressionError(const Token *tok1, const Token *tok2, ErrorPath* errors) +{ + if (!errors) + return; + if (!tok1) + return; + if (!tok2) + return; + ErrorPathItem item = std::make_pair(tok2, "'" + tok1->str() + "' is assigned value '" + tok2->expressionString() + "' here."); + if (std::find(errors->cbegin(), errors->cend(), item) != errors->cend()) + return; + errors->push_back(std::move(item)); +} + +SmallVector followAllReferences(const Token* tok, + bool temporary, + bool inconclusive, + ErrorPath errors, + int depth) +{ + struct ReferenceTokenLess { + bool operator()(const ReferenceToken& x, const ReferenceToken& y) const { + return x.token < y.token; + } + }; + if (!tok) + return {}; + if (depth < 0) { + SmallVector refs_result; + refs_result.push_back({tok, std::move(errors)}); + return refs_result; + } + const Variable *var = tok->variable(); + if (var && var->declarationId() == tok->varId()) { + if (var->nameToken() == tok || isStructuredBindingVariable(var)) { + SmallVector refs_result; + refs_result.push_back({tok, std::move(errors)}); + return refs_result; + } + if (var->isReference() || var->isRValueReference()) { + const Token * const varDeclEndToken = var->declEndToken(); + if (!varDeclEndToken) { + SmallVector refs_result; + refs_result.push_back({tok, std::move(errors)}); + return refs_result; + } + if (var->isArgument()) { + errors.emplace_back(varDeclEndToken, "Passed to reference."); + SmallVector refs_result; + refs_result.push_back({tok, std::move(errors)}); + return refs_result; + } + if (Token::simpleMatch(varDeclEndToken, "=")) { + if (astHasToken(varDeclEndToken, tok)) + return {}; + errors.emplace_back(varDeclEndToken, "Assigned to reference."); + const Token *vartok = varDeclEndToken->astOperand2(); + if (vartok == tok || (!temporary && isTemporary(vartok, nullptr, true) && + (var->isConst() || var->isRValueReference()))) { + SmallVector refs_result; + refs_result.push_back({tok, std::move(errors)}); + return refs_result; + } + if (vartok) + return followAllReferences(vartok, temporary, inconclusive, std::move(errors), depth - 1); + } + } + } else if (Token::simpleMatch(tok, "?") && Token::simpleMatch(tok->astOperand2(), ":")) { + std::set result; + const Token* tok2 = tok->astOperand2(); + + auto refs = followAllReferences(tok2->astOperand1(), temporary, inconclusive, errors, depth - 1); + result.insert(refs.cbegin(), refs.cend()); + refs = followAllReferences(tok2->astOperand2(), temporary, inconclusive, errors, depth - 1); + result.insert(refs.cbegin(), refs.cend()); + + if (!inconclusive && result.size() != 1) { + SmallVector refs_result; + refs_result.push_back({tok, std::move(errors)}); + return refs_result; + } + + if (!result.empty()) { + SmallVector refs_result; + refs_result.insert(refs_result.end(), result.cbegin(), result.cend()); + return refs_result; + } + + } else if (tok->previous() && tok->previous()->function() && Token::Match(tok->previous(), "%name% (")) { + const Function *f = tok->previous()->function(); + if (!Function::returnsReference(f)) { + SmallVector refs_result; + refs_result.push_back({tok, std::move(errors)}); + return refs_result; + } + std::set result; + std::vector returns = Function::findReturns(f); + for (const Token* returnTok : returns) { + if (returnTok == tok) + continue; + for (const ReferenceToken& rt : + followAllReferences(returnTok, temporary, inconclusive, errors, depth - returns.size())) { + const Variable* argvar = rt.token->variable(); + if (!argvar) { + SmallVector refs_result; + refs_result.push_back({tok, std::move(errors)}); + return refs_result; + } + if (argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) { + const int n = getArgumentPos(argvar, f); + if (n < 0) { + SmallVector refs_result; + refs_result.push_back({tok, std::move(errors)}); + return refs_result; + } + std::vector args = getArguments(tok->previous()); + if (n >= args.size()) { + SmallVector refs_result; + refs_result.push_back({tok, std::move(errors)}); + return refs_result; + } + const Token* argTok = args[n]; + ErrorPath er = errors; + er.emplace_back(returnTok, "Return reference."); + er.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'."); + auto refs = + followAllReferences(argTok, temporary, inconclusive, std::move(er), depth - returns.size()); + result.insert(refs.cbegin(), refs.cend()); + if (!inconclusive && result.size() > 1) { + SmallVector refs_result; + refs_result.push_back({tok, std::move(errors)}); + return refs_result; + } + } + } + } + if (!result.empty()) { + SmallVector refs_result; + refs_result.insert(refs_result.end(), result.cbegin(), result.cend()); + return refs_result; + } + } + SmallVector refs_result; + refs_result.push_back({tok, std::move(errors)}); + return refs_result; +} + +const Token* followReferences(const Token* tok, ErrorPath* errors) +{ + if (!tok) + return nullptr; + auto refs = followAllReferences(tok, true, false); + if (refs.size() == 1) { + if (errors) + *errors = std::move(refs.front().errors); + return refs.front().token; + } + return nullptr; +} + +static bool isSameLifetime(const Token * const tok1, const Token * const tok2) +{ + ValueFlow::Value v1 = ValueFlow::getLifetimeObjValue(tok1); + if (!v1.isLifetimeValue()) + return false; + ValueFlow::Value v2 = ValueFlow::getLifetimeObjValue(tok2); + if (!v2.isLifetimeValue()) + return false; + return v1.tokvalue == v2.tokvalue; +} + +static bool compareKnownValue(const Token * const tok1, const Token * const tok2, const std::function &compare) +{ + static const auto isKnownFn = std::mem_fn(&ValueFlow::Value::isKnown); + + const auto v1 = std::find_if(tok1->values().cbegin(), tok1->values().cend(), isKnownFn); + if (v1 == tok1->values().end()) { + return false; + } + if (v1->isNonValue() || v1->isContainerSizeValue() || v1->isSymbolicValue()) + return false; + const auto v2 = std::find_if(tok2->values().cbegin(), tok2->values().cend(), isKnownFn); + if (v2 == tok2->values().end()) { + return false; + } + if (v1->valueType != v2->valueType) { + return false; + } + const bool sameLifetime = isSameLifetime(tok1, tok2); + return compare(*v1, *v2, sameLifetime); +} + +bool isEqualKnownValue(const Token * const tok1, const Token * const tok2) +{ + return compareKnownValue(tok1, tok2, [&](const ValueFlow::Value& v1, const ValueFlow::Value& v2, bool sameLifetime) { + bool r = v1.equalValue(v2); + if (v1.isIteratorValue()) { + r &= sameLifetime; + } + return r; + }); +} + +static inline bool isDifferentKnownValues(const Token * const tok1, const Token * const tok2) +{ + return compareKnownValue(tok1, tok2, [&](const ValueFlow::Value& v1, const ValueFlow::Value& v2, bool sameLifetime) { + bool r = v1.equalValue(v2); + if (v1.isIteratorValue()) { + r &= sameLifetime; + } + return !r; + }); +} + +static inline bool isSameConstantValue(bool macro, const Token* tok1, const Token* tok2) +{ + if (tok1 == nullptr || tok2 == nullptr) + return false; + + auto adjustForCast = [](const Token* tok) { + if (tok->astOperand2() && Token::Match(tok->previous(), "%type% (|{") && tok->previous()->isStandardType()) + return tok->astOperand2(); + return tok; + }; + + tok1 = adjustForCast(tok1); + if (!tok1->isNumber() && !tok1->enumerator()) + return false; + tok2 = adjustForCast(tok2); + if (!tok2->isNumber() && !tok2->enumerator()) + return false; + + if (macro && (tok1->isExpandedMacro() || tok2->isExpandedMacro() || tok1->isTemplateArg() || tok2->isTemplateArg())) + return false; + + const ValueType * v1 = tok1->valueType(); + const ValueType * v2 = tok2->valueType(); + + if (!v1 || !v2 || v1->sign != v2->sign || v1->type != v2->type || v1->pointer != v2->pointer) + return false; + + return isEqualKnownValue(tok1, tok2); +} + + +static bool isForLoopCondition(const Token * const tok) +{ + if (!tok) + return false; + const Token *const parent = tok->astParent(); + return Token::simpleMatch(parent, ";") && parent->astOperand1() == tok && + Token::simpleMatch(parent->astParent(), ";") && + Token::simpleMatch(parent->astParent()->astParent(), "(") && + parent->astParent()->astParent()->astOperand1()->str() == "for"; +} + +static bool isForLoopIncrement(const Token* const tok) +{ + if (!tok) + return false; + const Token *const parent = tok->astParent(); + return Token::simpleMatch(parent, ";") && parent->astOperand2() == tok && + Token::simpleMatch(parent->astParent(), ";") && + Token::simpleMatch(parent->astParent()->astParent(), "(") && + parent->astParent()->astParent()->astOperand1()->str() == "for"; +} + +bool isUsedAsBool(const Token* const tok, const Settings* settings) +{ + if (!tok) + return false; + if (isForLoopIncrement(tok)) + return false; + if (astIsBool(tok)) + return true; + if (Token::Match(tok, "!|&&|%oror%|%comp%")) + return true; + const Token* parent = tok->astParent(); + if (!parent) + return false; + if (Token::simpleMatch(parent, "[")) + return false; + if (parent->isUnaryOp("*")) + return false; + if (Token::simpleMatch(parent, ".")) { + if (astIsRHS(tok)) + return isUsedAsBool(parent, settings); + return false; + } + if (Token::Match(parent, "&&|!|%oror%")) + return true; + if (parent->isCast()) + return !Token::simpleMatch(parent->astOperand1(), "dynamic_cast") && isUsedAsBool(parent); + if (parent->isUnaryOp("*")) + return isUsedAsBool(parent); + if (Token::Match(parent, "==|!=") && (tok->astSibling()->isNumber() || tok->astSibling()->isKeyword()) && tok->astSibling()->hasKnownIntValue() && + tok->astSibling()->values().front().intvalue == 0) + return true; + if (parent->str() == "(" && astIsRHS(tok) && Token::Match(parent->astOperand1(), "if|while")) + return true; + if (Token::simpleMatch(parent, "?") && astIsLHS(tok)) + return true; + if (isForLoopCondition(tok)) + return true; + if (!Token::Match(parent, "%cop%") && !(parent->str() == "(" && tok == parent->astOperand1())) { + if (parent->str() == "," && parent->isInitComma()) + return false; + std::vector vtParents = getParentValueTypes(tok, settings); + return std::any_of(vtParents.cbegin(), vtParents.cend(), [&](const ValueType& vt) { + return vt.pointer == 0 && vt.type == ValueType::BOOL; + }); + } + return false; +} + +bool compareTokenFlags(const Token* tok1, const Token* tok2, bool macro) { + if (macro && (tok1->isExpandedMacro() || tok2->isExpandedMacro() || tok1->isTemplateArg() || tok2->isTemplateArg())) + return false; + if (tok1->isComplex() != tok2->isComplex()) + return false; + if (tok1->isLong() != tok2->isLong()) + return false; + if (tok1->isUnsigned() != tok2->isUnsigned()) + return false; + if (tok1->isSigned() != tok2->isSigned()) + return false; + return true; +} + +static bool astIsBoolLike(const Token* tok) +{ + return astIsBool(tok) || isUsedAsBool(tok); +} + +bool isSameExpression(bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors) +{ + if (tok1 == tok2) + return true; + if (tok1 == nullptr || tok2 == nullptr) + return false; + // tokens needs to be from the same TokenList so no need check standard on both of them + if (tok1->isCpp()) { + if (tok1->str() == "." && tok1->astOperand1() && tok1->astOperand1()->str() == "this") + tok1 = tok1->astOperand2(); + if (tok2->str() == "." && tok2->astOperand1() && tok2->astOperand1()->str() == "this") + tok2 = tok2->astOperand2(); + } + // Skip double not + if (Token::simpleMatch(tok1, "!") && Token::simpleMatch(tok1->astOperand1(), "!") && !Token::simpleMatch(tok1->astParent(), "=") && astIsBoolLike(tok2)) { + return isSameExpression(macro, tok1->astOperand1()->astOperand1(), tok2, library, pure, followVar, errors); + } + if (Token::simpleMatch(tok2, "!") && Token::simpleMatch(tok2->astOperand1(), "!") && !Token::simpleMatch(tok2->astParent(), "=") && astIsBoolLike(tok1)) { + return isSameExpression(macro, tok1, tok2->astOperand1()->astOperand1(), library, pure, followVar, errors); + } + const bool tok_str_eq = tok1->str() == tok2->str(); + if (!tok_str_eq && isDifferentKnownValues(tok1, tok2)) + return false; + + const Token *followTok1 = tok1, *followTok2 = tok2; + while (Token::simpleMatch(followTok1, "::") && followTok1->astOperand2()) + followTok1 = followTok1->astOperand2(); + while (Token::simpleMatch(followTok2, "::") && followTok2->astOperand2()) + followTok2 = followTok2->astOperand2(); + if (isSameConstantValue(macro, followTok1, followTok2)) + return true; + + // Follow variable + if (followVar && !tok_str_eq && (followTok1->varId() || followTok2->varId() || followTok1->enumerator() || followTok2->enumerator())) { + const Token * varTok1 = followVariableExpression(followTok1, followTok2); + if ((varTok1->str() == followTok2->str()) || isSameConstantValue(macro, varTok1, followTok2)) { + followVariableExpressionError(followTok1, varTok1, errors); + return isSameExpression(macro, varTok1, followTok2, library, true, followVar, errors); + } + const Token * varTok2 = followVariableExpression(followTok2, followTok1); + if ((followTok1->str() == varTok2->str()) || isSameConstantValue(macro, followTok1, varTok2)) { + followVariableExpressionError(followTok2, varTok2, errors); + return isSameExpression(macro, followTok1, varTok2, library, true, followVar, errors); + } + if ((varTok1->str() == varTok2->str()) || isSameConstantValue(macro, varTok1, varTok2)) { + followVariableExpressionError(tok1, varTok1, errors); + followVariableExpressionError(tok2, varTok2, errors); + return isSameExpression(macro, varTok1, varTok2, library, true, followVar, errors); + } + } + // Follow references + if (!tok_str_eq) { + const Token* refTok1 = followReferences(tok1, errors); + const Token* refTok2 = followReferences(tok2, errors); + if (refTok1 != tok1 || refTok2 != tok2) { + if (refTok1 && !refTok1->varId() && refTok2 && !refTok2->varId()) { // complex reference expression + const Token *start = refTok1, *end = refTok2; + if (!precedes(start, end)) + std::swap(start, end); + if (findExpressionChanged(start, start, end, nullptr)) + return false; + } + return isSameExpression(macro, refTok1, refTok2, library, pure, followVar, errors); + } + } + if (tok1->varId() != tok2->varId() || !tok_str_eq || tok1->originalName() != tok2->originalName()) { + if ((Token::Match(tok1,"<|>") && Token::Match(tok2,"<|>")) || + (Token::Match(tok1,"<=|>=") && Token::Match(tok2,"<=|>="))) { + return isSameExpression(macro, tok1->astOperand1(), tok2->astOperand2(), library, pure, followVar, errors) && + isSameExpression(macro, tok1->astOperand2(), tok2->astOperand1(), library, pure, followVar, errors); + } + const Token* condTok = nullptr; + const Token* exprTok = nullptr; + if (Token::Match(tok1, "==|!=")) { + condTok = tok1; + exprTok = tok2; + } else if (Token::Match(tok2, "==|!=")) { + condTok = tok2; + exprTok = tok1; + } + if (condTok && condTok->astOperand1() && condTok->astOperand2() && !Token::Match(exprTok, "%comp%")) { + const Token* varTok1 = nullptr; + const Token* varTok2 = exprTok; + const ValueFlow::Value* value = nullptr; + if (condTok->astOperand1()->hasKnownIntValue()) { + value = &condTok->astOperand1()->values().front(); + varTok1 = condTok->astOperand2(); + } else if (condTok->astOperand2()->hasKnownIntValue()) { + value = &condTok->astOperand2()->values().front(); + varTok1 = condTok->astOperand1(); + } + const bool exprIsNot = Token::simpleMatch(exprTok, "!"); + if (exprIsNot) + varTok2 = exprTok->astOperand1(); + bool compare = false; + if (value) { + if (value->intvalue == 0 && exprIsNot && Token::simpleMatch(condTok, "==")) { + compare = true; + } else if (value->intvalue == 0 && !exprIsNot && Token::simpleMatch(condTok, "!=")) { + compare = true; + } else if (value->intvalue != 0 && exprIsNot && Token::simpleMatch(condTok, "!=")) { + compare = true; + } else if (value->intvalue != 0 && !exprIsNot && Token::simpleMatch(condTok, "==")) { + compare = true; + } + } + if (compare && astIsBoolLike(varTok1) && astIsBoolLike(varTok2)) + return isSameExpression(macro, varTok1, varTok2, library, pure, followVar, errors); + + } + return false; + } + + if (!compareTokenFlags(tok1, tok2, macro)) + return false; + + if (pure && tok1->isName() && tok1->next()->str() == "(" && tok1->str() != "sizeof" && !(tok1->variable() && tok1 == tok1->variable()->nameToken())) { + if (!tok1->function()) { + if (Token::simpleMatch(tok1->previous(), ".")) { + const Token *lhs = tok1->previous(); + while (Token::Match(lhs, "(|.|[")) + lhs = lhs->astOperand1(); + if (!lhs) + return false; + const bool lhsIsConst = (lhs->variable() && lhs->variable()->isConst()) || + (lhs->valueType() && lhs->valueType()->constness > 0) || + (Token::Match(lhs, "%var% . %name% (") && library.isFunctionConst(lhs->tokAt(2))); + if (!lhsIsConst) + return false; + } else { + const Token * ftok = tok1; + if (Token::simpleMatch(tok1->previous(), "::")) + ftok = tok1->previous(); + if (!library.isFunctionConst(ftok) && !ftok->isAttributeConst() && !ftok->isAttributePure()) + return false; + } + } else { + if (!tok1->function()->isConst() && !tok1->function()->isAttributeConst() && + !tok1->function()->isAttributePure()) + return false; + } + } + // templates/casts + if ((tok1->next() && tok1->next()->link() && Token::Match(tok1, "%name% <")) || + (tok2->next() && tok2->next()->link() && Token::Match(tok2, "%name% <"))) { + + // non-const template function that is not a dynamic_cast => return false + if (pure && Token::simpleMatch(tok1->next()->link(), "> (") && + !(tok1->function() && tok1->function()->isConst()) && + tok1->str() != "dynamic_cast") + return false; + + // some template/cast stuff.. check that the template arguments are same + const Token *t1 = tok1->next(); + const Token *t2 = tok2->next(); + const Token *end1 = t1->link(); + const Token *end2 = t2->link(); + while (t1 && t2 && t1 != end1 && t2 != end2) { + if (t1->str() != t2->str() || !compareTokenFlags(t1, t2, macro)) + return false; + t1 = t1->next(); + t2 = t2->next(); + } + if (t1 != end1 || t2 != end2) + return false; + } + if (tok1->tokType() == Token::eIncDecOp || tok1->isAssignmentOp()) + return false; + // bailout when we see ({..}) + if (tok1->str() == "{") + return false; + // cast => assert that the casts are equal + if (tok1->str() == "(" && tok1->previous() && + !tok1->previous()->isName() && + !(tok1->previous()->str() == ">" && tok1->previous()->link())) { + const Token *t1 = tok1->next(); + const Token *t2 = tok2->next(); + while (t1 && t2 && + t1->str() == t2->str() && + compareTokenFlags(t1, t2, macro) && + (t1->isName() || t1->str() == "*")) { + t1 = t1->next(); + t2 = t2->next(); + } + if (!t1 || !t2 || t1->str() != ")" || t2->str() != ")") + return false; + } + bool noncommutativeEquals = + isSameExpression(macro, tok1->astOperand1(), tok2->astOperand1(), library, pure, followVar, errors); + noncommutativeEquals = noncommutativeEquals && + isSameExpression(macro, tok1->astOperand2(), tok2->astOperand2(), library, pure, followVar, errors); + + if (noncommutativeEquals) + return true; + + // in c++, a+b might be different to b+a, depending on the type of a and b + if (tok1->isCpp() && tok1->str() == "+" && tok1->isBinaryOp()) { + const ValueType* vt1 = tok1->astOperand1()->valueType(); + const ValueType* vt2 = tok1->astOperand2()->valueType(); + if (!(vt1 && (vt1->type >= ValueType::VOID || vt1->pointer) && vt2 && (vt2->type >= ValueType::VOID || vt2->pointer))) + return false; + } + + const bool commutative = tok1->isBinaryOp() && Token::Match(tok1, "%or%|%oror%|+|*|&|&&|^|==|!="); + bool commutativeEquals = commutative && + isSameExpression(macro, tok1->astOperand2(), tok2->astOperand1(), library, pure, followVar, errors); + commutativeEquals = commutativeEquals && + isSameExpression(macro, tok1->astOperand1(), tok2->astOperand2(), library, pure, followVar, errors); + + + return commutativeEquals; +} + +static bool isZeroBoundCond(const Token * const cond) +{ + if (cond == nullptr) + return false; + // Assume unsigned + // TODO: Handle reverse conditions + const bool isZero = cond->astOperand2()->getValue(0); + if (cond->str() == "==" || cond->str() == ">=") + return isZero; + if (cond->str() == "<=") + return true; + if (cond->str() == "<") + return !isZero; + if (cond->str() == ">") + return false; + return false; +} + +bool isOppositeCond(bool isNot, const Token * const cond1, const Token * const cond2, const Library& library, bool pure, bool followVar, ErrorPath* errors) +{ + if (!cond1 || !cond2) + return false; + + if (isSameExpression(true, cond1, cond2, library, pure, followVar, errors)) + return false; + + if (!isNot && cond1->str() == "&&" && cond2->str() == "&&") { + for (const Token* tok1: { + cond1->astOperand1(), cond1->astOperand2() + }) { + for (const Token* tok2: { + cond2->astOperand1(), cond2->astOperand2() + }) { + if (isSameExpression(true, tok1, tok2, library, pure, followVar, errors)) { + if (isOppositeCond(isNot, tok1->astSibling(), tok2->astSibling(), library, pure, followVar, errors)) + return true; + } + } + } + } + + if (cond1->str() != cond2->str() && (cond1->str() == "||" || cond2->str() == "||")) { + const Token* orCond = nullptr; + const Token* otherCond = nullptr; + if (cond1->str() == "||") { + orCond = cond1; + otherCond = cond2; + } + if (cond2->str() == "||") { + orCond = cond2; + otherCond = cond1; + } + return isOppositeCond(isNot, orCond->astOperand1(), otherCond, library, pure, followVar, errors) && + isOppositeCond(isNot, orCond->astOperand2(), otherCond, library, pure, followVar, errors); + } + + if (cond1->str() == "!") { + if (cond2->str() == "!=") { + if (cond2->astOperand1() && cond2->astOperand1()->str() == "0") + return isSameExpression(true, cond1->astOperand1(), cond2->astOperand2(), library, pure, followVar, errors); + if (cond2->astOperand2() && cond2->astOperand2()->str() == "0") + return isSameExpression(true, cond1->astOperand1(), cond2->astOperand1(), library, pure, followVar, errors); + } + if (!isUsedAsBool(cond2)) + return false; + return isSameExpression(true, cond1->astOperand1(), cond2, library, pure, followVar, errors); + } + + if (cond2->str() == "!") + return isOppositeCond(isNot, cond2, cond1, library, pure, followVar, errors); + + if (!isNot) { + if (cond1->str() == "==" && cond2->str() == "==") { + if (isSameExpression(true, cond1->astOperand1(), cond2->astOperand1(), library, pure, followVar, errors)) + return isDifferentKnownValues(cond1->astOperand2(), cond2->astOperand2()); + if (isSameExpression(true, cond1->astOperand2(), cond2->astOperand2(), library, pure, followVar, errors)) + return isDifferentKnownValues(cond1->astOperand1(), cond2->astOperand1()); + } + // TODO: Handle reverse conditions + if (Library::isContainerYield(cond1, Library::Container::Yield::EMPTY, "empty") && + Library::isContainerYield(cond2->astOperand1(), Library::Container::Yield::SIZE, "size") && + isSameExpression(true, + cond1->astOperand1()->astOperand1(), + cond2->astOperand1()->astOperand1()->astOperand1(), + library, + pure, + followVar, + errors)) { + return !isZeroBoundCond(cond2); + } + + if (Library::isContainerYield(cond2, Library::Container::Yield::EMPTY, "empty") && + Library::isContainerYield(cond1->astOperand1(), Library::Container::Yield::SIZE, "size") && + isSameExpression(true, + cond2->astOperand1()->astOperand1(), + cond1->astOperand1()->astOperand1()->astOperand1(), + library, + pure, + followVar, + errors)) { + return !isZeroBoundCond(cond1); + } + } + + + if (!cond1->isComparisonOp() || !cond2->isComparisonOp()) + return false; + + const std::string &comp1 = cond1->str(); + + // condition found .. get comparator + std::string comp2; + if (isSameExpression(true, cond1->astOperand1(), cond2->astOperand1(), library, pure, followVar, errors) && + isSameExpression(true, cond1->astOperand2(), cond2->astOperand2(), library, pure, followVar, errors)) { + comp2 = cond2->str(); + } else if (isSameExpression(true, cond1->astOperand1(), cond2->astOperand2(), library, pure, followVar, errors) && + isSameExpression(true, cond1->astOperand2(), cond2->astOperand1(), library, pure, followVar, errors)) { + comp2 = cond2->str(); + if (comp2[0] == '>') + comp2[0] = '<'; + else if (comp2[0] == '<') + comp2[0] = '>'; + } + + if (!isNot && comp2.empty()) { + const Token *expr1 = nullptr, *value1 = nullptr, *expr2 = nullptr, *value2 = nullptr; + std::string op1 = cond1->str(), op2 = cond2->str(); + if (cond1->astOperand2()->hasKnownIntValue()) { + expr1 = cond1->astOperand1(); + value1 = cond1->astOperand2(); + } else if (cond1->astOperand1()->hasKnownIntValue()) { + expr1 = cond1->astOperand2(); + value1 = cond1->astOperand1(); + if (op1[0] == '>') + op1[0] = '<'; + else if (op1[0] == '<') + op1[0] = '>'; + } + if (cond2->astOperand2()->hasKnownIntValue()) { + expr2 = cond2->astOperand1(); + value2 = cond2->astOperand2(); + } else if (cond2->astOperand1()->hasKnownIntValue()) { + expr2 = cond2->astOperand2(); + value2 = cond2->astOperand1(); + if (op2[0] == '>') + op2[0] = '<'; + else if (op2[0] == '<') + op2[0] = '>'; + } + if (!expr1 || !value1 || !expr2 || !value2) { + return false; + } + if (!isSameExpression(true, expr1, expr2, library, pure, followVar, errors)) + return false; + + const ValueFlow::Value &rhsValue1 = value1->values().front(); + const ValueFlow::Value &rhsValue2 = value2->values().front(); + + if (op1 == "<" || op1 == "<=") + return (op2 == "==" || op2 == ">" || op2 == ">=") && (rhsValue1.intvalue < rhsValue2.intvalue); + if (op1 == ">=" || op1 == ">") + return (op2 == "==" || op2 == "<" || op2 == "<=") && (rhsValue1.intvalue > rhsValue2.intvalue); + + return false; + } + + // is condition opposite? + return ((comp1 == "==" && comp2 == "!=") || + (comp1 == "!=" && comp2 == "==") || + (comp1 == "<" && comp2 == ">=") || + (comp1 == "<=" && comp2 == ">") || + (comp1 == ">" && comp2 == "<=") || + (comp1 == ">=" && comp2 == "<") || + (!isNot && ((comp1 == "<" && comp2 == ">") || + (comp1 == ">" && comp2 == "<") || + (comp1 == "==" && (comp2 == "!=" || comp2 == ">" || comp2 == "<")) || + ((comp1 == "!=" || comp1 == ">" || comp1 == "<") && comp2 == "==") + ))); +} + +bool isOppositeExpression(const Token * const tok1, const Token * const tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors) +{ + if (!tok1 || !tok2) + return false; + if (isOppositeCond(true, tok1, tok2, library, pure, followVar, errors)) + return true; + if (tok1->isUnaryOp("-") && !(tok2->astParent() && tok2->astParent()->tokType() == Token::eBitOp)) + return isSameExpression(true, tok1->astOperand1(), tok2, library, pure, followVar, errors); + if (tok2->isUnaryOp("-") && !(tok2->astParent() && tok2->astParent()->tokType() == Token::eBitOp)) + return isSameExpression(true, tok2->astOperand1(), tok1, library, pure, followVar, errors); + return false; +} + +static bool functionModifiesArguments(const Function* f) +{ + return std::any_of(f->argumentList.cbegin(), f->argumentList.cend(), [](const Variable& var) { + if (var.isReference() || var.isPointer()) + return !var.isConst(); + return true; + }); +} + +bool isConstFunctionCall(const Token* ftok, const Library& library) +{ + if (isUnevaluated(ftok)) + return true; + if (!Token::Match(ftok, "%name% (")) + return false; + if (const Function* f = ftok->function()) { + if (f->isAttributePure() || f->isAttributeConst()) + return true; + // Any modified arguments + if (functionModifiesArguments(f)) + return false; + if (Function::returnsVoid(f)) + return false; + // Member function call + if (Token::simpleMatch(ftok->previous(), ".") || exprDependsOnThis(ftok->next())) { + if (f->isConst()) + return true; + // Check for const overloaded function that just return the const version + if (!Function::returnsConst(f)) { + std::vector fs = f->getOverloadedFunctions(); + if (std::any_of(fs.cbegin(), fs.cend(), [&](const Function* g) { + if (f == g) + return false; + if (f->argumentList.size() != g->argumentList.size()) + return false; + if (functionModifiesArguments(g)) + return false; + if (g->isConst() && Function::returnsConst(g)) + return true; + return false; + })) + return true; + } + return false; + } + if (f->argumentList.empty()) + return f->isConstexpr(); + } else if (Token::Match(ftok->previous(), ". %name% (") && ftok->previous()->originalName() != "->" && + astIsSmartPointer(ftok->previous()->astOperand1())) { + return Token::Match(ftok, "get|get_deleter ( )"); + } else if (Token::Match(ftok->previous(), ". %name% (") && astIsContainer(ftok->previous()->astOperand1())) { + const Library::Container* container = ftok->previous()->astOperand1()->valueType()->container; + if (!container) + return false; + if (container->getYield(ftok->str()) != Library::Container::Yield::NO_YIELD) + return true; + if (container->getAction(ftok->str()) == Library::Container::Action::FIND_CONST) + return true; + return false; + } else if (const Library::Function* lf = library.getFunction(ftok)) { + if (lf->ispure) + return true; + if (lf->containerYield != Library::Container::Yield::NO_YIELD) + return true; + if (lf->containerAction == Library::Container::Action::FIND_CONST) + return true; + return false; + } else { + const bool memberFunction = Token::Match(ftok->previous(), ". %name% ("); + bool constMember = !memberFunction; + if (Token::Match(ftok->tokAt(-2), "%var% . %name% (")) { + const Variable* var = ftok->tokAt(-2)->variable(); + if (var) + constMember = var->isConst(); + } + // TODO: Only check const on lvalues + std::vector args = getArguments(ftok); + if (args.empty()) + return false; + return constMember && std::all_of(args.cbegin(), args.cend(), [](const Token* tok) { + const Variable* var = tok->variable(); + if (var) + return var->isConst(); + return false; + }); + } + return true; +} + +bool isConstExpression(const Token *tok, const Library& library) +{ + if (!tok) + return true; + if (tok->variable() && tok->variable()->isVolatile()) + return false; + if (tok->isName() && tok->next()->str() == "(") { + if (!isConstFunctionCall(tok, library)) + return false; + } + if (tok->tokType() == Token::eIncDecOp) + return false; + if (tok->isAssignmentOp()) + return false; + if (isLikelyStreamRead(tok)) + return false; + // bailout when we see ({..}) + if (tok->str() == "{") + return false; + return isConstExpression(tok->astOperand1(), library) && isConstExpression(tok->astOperand2(), library); +} + +bool isWithoutSideEffects(const Token* tok, bool checkArrayAccess, bool checkReference) +{ + if (!tok) + return true; + if (!tok->isCpp()) + return true; + + while (tok && tok->astOperand2() && tok->astOperand2()->str() != "(") + tok = tok->astOperand2(); + if (tok && tok->varId()) { + const Variable* var = tok->variable(); + return var && ((!var->isClass() && (checkReference || !var->isReference())) || var->isPointer() || (checkArrayAccess ? var->isStlType() && !var->isStlType(CheckClass::stl_containers_not_const) : var->isStlType())); + } + return true; +} + +bool isUniqueExpression(const Token* tok) +{ + if (!tok) + return true; + if (tok->function()) { + const Function * fun = tok->function(); + const Scope * scope = fun->nestedIn; + if (!scope) + return true; + const std::string returnType = fun->retType ? fun->retType->name() : fun->retDef->stringifyList(fun->tokenDef); + if (!std::all_of(scope->functionList.begin(), scope->functionList.end(), [&](const Function& f) { + if (f.type != Function::eFunction) + return true; + + const std::string freturnType = f.retType ? f.retType->name() : f.retDef->stringifyList(f.returnDefEnd()); + return f.argumentList.size() != fun->argumentList.size() || returnType != freturnType || f.name() == fun->name(); + })) + return false; + } else if (tok->variable()) { + const Variable * var = tok->variable(); + const Scope * scope = var->scope(); + if (!scope) + return true; + const Type * varType = var->type(); + // Iterate over the variables in scope and the parameters of the function if possible + const Function * fun = scope->function; + + auto pred = [=](const Variable& v) { + if (varType) + return v.type() && v.type()->name() == varType->name() && v.name() != var->name(); + return v.isFloatingType() == var->isFloatingType() && + v.isEnumType() == var->isEnumType() && + v.isClass() == var->isClass() && + v.isArray() == var->isArray() && + v.isPointer() == var->isPointer() && + v.name() != var->name(); + }; + if (std::any_of(scope->varlist.cbegin(), scope->varlist.cend(), pred)) + return false; + if (fun) { + if (std::any_of(fun->argumentList.cbegin(), fun->argumentList.cend(), pred)) + return false; + } + } else if (!isUniqueExpression(tok->astOperand1())) { + return false; + } + + return isUniqueExpression(tok->astOperand2()); +} + +static bool isEscaped(const Token* tok, bool functionsScope, const Library& library) +{ + if (library.isnoreturn(tok)) + return true; + if (functionsScope) + return Token::simpleMatch(tok, "throw"); + return Token::Match(tok, "return|throw"); +} + +static bool isEscapedOrJump(const Token* tok, bool functionsScope, const Library& library) +{ + if (library.isnoreturn(tok)) + return true; + if (functionsScope) + return Token::simpleMatch(tok, "throw"); + return Token::Match(tok, "return|goto|throw|continue|break"); +} + +bool isEscapeFunction(const Token* ftok, const Library* library) +{ + if (!Token::Match(ftok, "%name% (")) + return false; + const Function* function = ftok->function(); + if (function) { + if (function->isEscapeFunction()) + return true; + if (function->isAttributeNoreturn()) + return true; + } else if (library) { + if (library->isnoreturn(ftok)) + return true; + } + return false; +} + +static bool hasNoreturnFunction(const Token* tok, const Library& library, const Token** unknownFunc) +{ + if (!tok) + return false; + const Token* ftok = tok->str() == "(" ? tok->previous() : nullptr; + while (Token::simpleMatch(ftok, "(")) + ftok = ftok->astOperand1(); + if (ftok) { + const Function * function = ftok->function(); + if (function) { + if (function->isEscapeFunction()) + return true; + if (function->isAttributeNoreturn()) + return true; + } else if (library.isnoreturn(ftok)) { + return true; + } else if (Token::Match(ftok, "exit|abort")) { + return true; + } + if (unknownFunc && !function && library.functions.count(library.getFunctionName(ftok)) == 0) + *unknownFunc = ftok; + return false; + } + if (tok->isConstOp()) { + return hasNoreturnFunction(tok->astOperand1(), library, unknownFunc) || hasNoreturnFunction(tok->astOperand2(), library, unknownFunc); + } + + return false; +} + +bool isReturnScope(const Token* const endToken, const Library& library, const Token** unknownFunc, bool functionScope) +{ + if (!endToken || endToken->str() != "}") + return false; + + const Token *prev = endToken->previous(); + while (prev && Token::simpleMatch(prev->previous(), "; ;")) + prev = prev->previous(); + if (prev && Token::simpleMatch(prev->previous(), "} ;")) + prev = prev->previous(); + + if (Token::simpleMatch(prev, "}")) { + if (Token::simpleMatch(prev->link()->tokAt(-2), "} else {")) + return isReturnScope(prev, library, unknownFunc, functionScope) && + isReturnScope(prev->link()->tokAt(-2), library, unknownFunc, functionScope); + // TODO: Check all cases + if (!functionScope && Token::simpleMatch(prev->link()->previous(), ") {") && + Token::simpleMatch(prev->link()->linkAt(-1)->previous(), "switch (") && + !Token::findsimplematch(prev->link(), "break", prev)) { + return isReturnScope(prev, library, unknownFunc, functionScope); + } + if (isEscaped(prev->link()->astTop(), functionScope, library)) + return true; + if (Token::Match(prev->link()->previous(), "[;{}] {")) + return isReturnScope(prev, library, unknownFunc, functionScope); + } else if (Token::simpleMatch(prev, ";")) { + if (prev->tokAt(-2) && hasNoreturnFunction(prev->tokAt(-2)->astTop(), library, unknownFunc)) + return true; + // Unknown symbol + if (Token::Match(prev->tokAt(-2), ";|}|{ %name% ;") && prev->previous()->isIncompleteVar()) { + if (unknownFunc) + *unknownFunc = prev->previous(); + return false; + } + if (Token::simpleMatch(prev->previous(), ") ;") && prev->previous()->link() && + isEscaped(prev->previous()->link()->astTop(), functionScope, library)) + return true; + if (isEscaped(prev->previous()->astTop(), functionScope, library)) + return true; + // return/goto statement + prev = prev->previous(); + while (prev && !Token::Match(prev, ";|{|}") && !isEscapedOrJump(prev, functionScope, library)) + prev = prev->previous(); + return prev && prev->isName(); + } + return false; +} + +bool isWithinScope(const Token* tok, const Variable* var, Scope::ScopeType type) +{ + if (!tok || !var) + return false; + const Scope* scope = tok->scope(); + while (scope && scope != var->scope()) { + if (scope->type == type) + return true; + scope = scope->nestedIn; + } + return false; +} + +bool isVariableChangedByFunctionCall(const Token *tok, int indirect, nonneg int varid, const Settings *settings, bool *inconclusive) +{ + if (!tok) + return false; + if (tok->varId() == varid) + return isVariableChangedByFunctionCall(tok, indirect, settings, inconclusive); + return isVariableChangedByFunctionCall(tok->astOperand1(), indirect, varid, settings, inconclusive) || + isVariableChangedByFunctionCall(tok->astOperand2(), indirect, varid, settings, inconclusive); +} + +bool isScopeBracket(const Token* tok) +{ + if (!Token::Match(tok, "{|}")) + return false; + if (!tok->scope()) + return false; + if (tok->str() == "{") + return tok->scope()->bodyStart == tok; + if (tok->str() == "}") + return tok->scope()->bodyEnd == tok; + return false; +} + +template )> +static T* getTokenArgumentFunctionImpl(T* tok, int& argn) +{ + argn = -1; + { + T* parent = tok->astParent(); + if (parent && (parent->isUnaryOp("&") || parent->isIncDecOp())) + parent = parent->astParent(); + while (parent && parent->isCast()) + parent = parent->astParent(); + if (Token::Match(parent, "[+-]") && parent->valueType() && parent->valueType()->pointer) + parent = parent->astParent(); + + // passing variable to subfunction? + if (Token::Match(parent, "[*[(,{.]") || Token::Match(parent, "%oror%|&&")) + ; + else if (Token::simpleMatch(parent, ":")) { + while (Token::Match(parent, "[?:]")) + parent = parent->astParent(); + while (Token::simpleMatch(parent, ",")) + parent = parent->astParent(); + if (!parent || parent->str() != "(") + return nullptr; + } else + return nullptr; + } + + T* argtok = tok; + while (argtok && argtok->astParent() && (!Token::Match(argtok->astParent(), ",|(|{") || argtok->astParent()->isCast())) { + argtok = argtok->astParent(); + } + if (!argtok) + return nullptr; + if (Token::simpleMatch(argtok, ",")) + argtok = argtok->astOperand1(); + tok = argtok; + while (Token::Match(tok->astParent(), ",|(|{")) { + tok = tok->astParent(); + if (Token::Match(tok, "(|{")) + break; + } + argn = getArgumentPos(tok, argtok); + if (argn == -1) + return nullptr; + if (!Token::Match(tok, "{|(")) + return nullptr; + if (tok->astOperand2()) + tok = tok->astOperand1(); + while (tok && (tok->isUnaryOp("*") || tok->str() == "[")) + tok = tok->astOperand1(); + if (Token::Match(tok, ". * %name%")) // bailout for pointer to member + return tok->tokAt(2); + while (Token::simpleMatch(tok, ".")) + tok = tok->astOperand2(); + while (Token::simpleMatch(tok, "::")) { + // If there is only a op1 and not op2, then this is a global scope + if (!tok->astOperand2() && tok->astOperand1()) { + tok = tok->astOperand1(); + break; + } + tok = tok->astOperand2(); + if (Token::simpleMatch(tok, "<") && tok->link()) + tok = tok->astOperand1(); + } + if (tok && tok->link() && tok->str() == ">") + tok = tok->link()->previous(); + if (!Token::Match(tok, "%name%|(|{")) + return nullptr; + // Skip labels + if (Token::Match(tok, "%name% :")) + return nullptr; + return tok; +} + +const Token* getTokenArgumentFunction(const Token* tok, int& argn) { + return getTokenArgumentFunctionImpl(tok, argn); +} + +Token* getTokenArgumentFunction(Token* tok, int& argn) { + return getTokenArgumentFunctionImpl(tok, argn); +} + +std::vector getArgumentVars(const Token* tok, int argnr) +{ + std::vector result; + if (!tok) + return result; + if (tok->function()) { + const Variable* argvar = tok->function()->getArgumentVar(argnr); + if (argvar) + return {argvar}; + return result; + } + if (tok->variable() || Token::simpleMatch(tok, "{") || Token::Match(tok->previous(), "%type% (|{")) { + const Type* type = Token::typeOf(tok); + if (!type) + return result; + const Scope* typeScope = type->classScope; + if (!typeScope) + return result; + const bool tokIsBrace = Token::simpleMatch(tok, "{"); + // Aggregate constructor + if (tokIsBrace && typeScope->numConstructors == 0 && argnr < typeScope->varlist.size()) { + auto it = std::next(typeScope->varlist.cbegin(), argnr); + return {&*it}; + } + const int argCount = numberOfArguments(tok); + const bool constructor = tokIsBrace || (tok->variable() && tok->variable()->nameToken() == tok); + for (const Function &function : typeScope->functionList) { + if (function.argCount() < argCount) + continue; + if (constructor && !function.isConstructor()) + continue; + if (!constructor && !Token::simpleMatch(function.token, "operator()")) + continue; + const Variable* argvar = function.getArgumentVar(argnr); + if (argvar) + result.push_back(argvar); + } + } + return result; +} + +static bool isCPPCastKeyword(const Token* tok) +{ + if (!tok) + return false; + return endsWith(tok->str(), "_cast"); +} + +static bool isTrivialConstructor(const Token* tok) +{ + const Token* typeTok = nullptr; + const Type* t = Token::typeOf(tok, &typeTok); + if (t) + return false; + if (typeTok->valueType() && typeTok->valueType()->isPrimitive()) + return true; + return false; +} + +static bool isArray(const Token* tok) +{ + if (!tok) + return false; + if (tok->variable()) + return tok->variable()->isArray(); + if (Token::simpleMatch(tok, ".")) + return isArray(tok->astOperand2()); + return false; +} + +bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Settings *settings, bool *inconclusive) +{ + if (!tok) + return false; + + if (Token::simpleMatch(tok, ",")) + return false; + + const Token * const tok1 = tok; + + // address of variable + const bool addressOf = tok->astParent() && tok->astParent()->isUnaryOp("&"); + if (addressOf) + indirect++; + + const bool deref = tok->astParent() && tok->astParent()->isUnaryOp("*"); + if (deref && indirect > 0) + indirect--; + + if (indirect == 1 && tok->isCpp() && tok->tokAt(-1) && Token::simpleMatch(tok->tokAt(-2), "new (")) // placement new TODO: fix AST + return true; + + int argnr; + tok = getTokenArgumentFunction(tok, argnr); + if (!tok) + return false; // not a function => variable not changed + if (Token::simpleMatch(tok, "{") && isTrivialConstructor(tok)) + return false; + if (tok->isKeyword() && !isCPPCastKeyword(tok) && !startsWith(tok->str(),"operator")) + return false; + // A functional cast won't modify the variable + if (Token::Match(tok, "%type% (|{") && tok->tokType() == Token::eType && astIsPrimitive(tok->next())) + return false; + const Token * parenTok = tok->next(); + if (Token::simpleMatch(parenTok, "<") && parenTok->link()) + parenTok = parenTok->link()->next(); + const bool possiblyPassedByReference = (parenTok->next() == tok1 || Token::Match(tok1->previous(), ", %name% [,)}]")); + + if (!tok->function() && !tok->variable() && tok->isName()) { + if (settings) { + // Check if direction (in, out, inout) is specified in the library configuration and use that + const Library::ArgumentChecks::Direction argDirection = settings->library.getArgDirection(tok, 1 + argnr); + if (argDirection == Library::ArgumentChecks::Direction::DIR_IN) + return false; + + const bool requireNonNull = settings->library.isnullargbad(tok, 1 + argnr); + if (argDirection == Library::ArgumentChecks::Direction::DIR_OUT || + argDirection == Library::ArgumentChecks::Direction::DIR_INOUT) { + if (indirect == 0 && isArray(tok1)) + return true; + const bool requireInit = settings->library.isuninitargbad(tok, 1 + argnr); + // Assume that if the variable must be initialized then the indirection is 1 + if (indirect > 0 && requireInit && requireNonNull) + return true; + } + if (Token::simpleMatch(tok->tokAt(-2), "std :: tie")) + return true; + // if the library says 0 is invalid + // => it is assumed that parameter is an in parameter (TODO: this is a bad heuristic) + if (indirect == 0 && requireNonNull) + return false; + } + // possible pass-by-reference => inconclusive + if (possiblyPassedByReference) { + if (inconclusive != nullptr) + *inconclusive = true; + return false; + } + // Safe guess: Assume that parameter is changed by function call + return true; + } + + if (const Variable* var = tok->variable()) { + if (tok == var->nameToken() && (!var->isReference() || var->isConst()) && (!var->isClass() || (var->valueType() && var->valueType()->container))) // const ref or passed to (copy) ctor + return false; + } + + std::vector args = getArgumentVars(tok, argnr); + bool conclusive = false; + for (const Variable *arg:args) { + if (!arg) + continue; + conclusive = true; + if (indirect > 0) { + if (arg->isPointer() && !(arg->valueType() && arg->valueType()->isConst(indirect))) + return true; + if (indirect > 1 && addressOf && arg->isPointer() && (!arg->valueType() || !arg->valueType()->isConst(indirect-1))) + return true; + if (arg->isArray() || (!arg->isPointer() && (!arg->valueType() || arg->valueType()->type == ValueType::UNKNOWN_TYPE))) + return true; + } + if (!arg->isConst() && arg->isReference()) + return true; + } + if (addressOf && tok1->astParent()->isUnaryOp("&")) { + const Token* castToken = tok1->astParent(); + while (castToken->astParent()->isCast()) + castToken = castToken->astParent(); + if (Token::Match(castToken->astParent(), ",|(") && + castToken->valueType() && + castToken->valueType()->isIntegral() && + castToken->valueType()->pointer == 0) + return true; + } + if (!conclusive && inconclusive) { + *inconclusive = true; + } + return false; +} + +bool isVariableChanged(const Token *tok, int indirect, const Settings *settings, int depth) +{ + if (!tok) + return false; + + if (indirect == 0 && isConstVarExpression(tok)) + return false; + + const Token *tok2 = tok; + int derefs = 0; + while (Token::simpleMatch(tok2->astParent(), "*") || + (Token::simpleMatch(tok2->astParent(), ".") && !Token::simpleMatch(tok2->astParent()->astParent(), "(")) || + (tok2->astParent() && tok2->astParent()->isUnaryOp("&") && Token::simpleMatch(tok2->astParent()->astParent(), ".") && tok2->astParent()->astParent()->originalName()=="->") || + (Token::simpleMatch(tok2->astParent(), "[") && tok2 == tok2->astParent()->astOperand1())) { + if (tok2->astParent() && (tok2->astParent()->isUnaryOp("*") || (astIsLHS(tok2) && tok2->astParent()->originalName() == "->"))) + derefs++; + if (derefs > indirect) + break; + if (tok2->astParent() && tok2->astParent()->isUnaryOp("&") && Token::simpleMatch(tok2->astParent()->astParent(), ".") && tok2->astParent()->astParent()->originalName()=="->") + tok2 = tok2->astParent(); + tok2 = tok2->astParent(); + } + + if (tok2->astParent() && tok2->astParent()->isUnaryOp("&")) { + const Token* parent = tok2->astParent(); + while (parent->astParent() && parent->astParent()->isCast()) + parent = parent->astParent(); + if (parent->astParent() && parent->astParent()->isUnaryOp("*")) + tok2 = parent->astParent(); + } + + while ((Token::simpleMatch(tok2, ":") && Token::simpleMatch(tok2->astParent(), "?")) || + (Token::simpleMatch(tok2->astParent(), ":") && Token::simpleMatch(tok2->astParent()->astParent(), "?"))) + tok2 = tok2->astParent(); + + if (indirect == 0 && tok2->astParent() && tok2->astParent()->tokType() == Token::eIncDecOp) + return true; + + auto skipRedundantPtrOp = [](const Token* tok, const Token* parent) { + const Token* gparent = parent ? parent->astParent() : nullptr; + while (parent && gparent && ((parent->isUnaryOp("*") && gparent->isUnaryOp("&")) || (parent->isUnaryOp("&") && gparent->isUnaryOp("*")))) { + tok = gparent; + parent = gparent->astParent(); + if (parent) + gparent = parent->astParent(); + } + return tok; + }; + tok2 = skipRedundantPtrOp(tok2, tok2->astParent()); + + if (tok2->astParent() && tok2->astParent()->isAssignmentOp()) { + if (((indirect == 0 || tok2 != tok) || (indirect == 1 && tok2->str() == ".")) && tok2 == tok2->astParent()->astOperand1()) + return true; + // Check if assigning to a non-const lvalue + const Variable * var = getLHSVariable(tok2->astParent()); + if (var && var->isReference() && !var->isConst() && + ((var->nameToken() && var->nameToken()->next() == tok2->astParent()) || var->isPointer())) { + if (!var->isLocal() || isVariableChanged(var, settings, depth - 1)) + return true; + } + } + + const ValueType* vt = tok->variable() ? tok->variable()->valueType() : tok->valueType(); + + // Check addressof + if (tok2->astParent() && tok2->astParent()->isUnaryOp("&")) { + if (isVariableChanged(tok2->astParent(), indirect + 1, settings, depth - 1)) + return true; + } else { + // If its already const then it cant be modified + if (vt && vt->isConst(indirect)) + return false; + } + + if (tok2->isCpp() && Token::Match(tok2->astParent(), ">>|&") && astIsRHS(tok2) && isLikelyStreamRead(tok2->astParent())) + return true; + + if (isLikelyStream(tok2)) + return true; + + // Member function call + if (Token::Match(tok2->astParent(), ". %name%") && isFunctionCall(tok2->astParent()->next()) && + tok2->astParent()->astOperand1() == tok2) { + // Member function cannot change what `this` points to + if (indirect == 0 && astIsPointer(tok)) + return false; + + const Token *ftok = tok2->astParent()->astOperand2(); + if (astIsContainer(tok2->astParent()->astOperand1()) && vt && vt->container) { + const Library::Container* c = vt->container; + const Library::Container::Action action = c->getAction(ftok->str()); + if (contains({Library::Container::Action::INSERT, + Library::Container::Action::ERASE, + Library::Container::Action::CHANGE, + Library::Container::Action::CHANGE_CONTENT, + Library::Container::Action::CHANGE_INTERNAL, + Library::Container::Action::CLEAR, + Library::Container::Action::FIND, + Library::Container::Action::PUSH, + Library::Container::Action::POP, + Library::Container::Action::RESIZE}, + action)) + return true; + const Library::Container::Yield yield = c->getYield(ftok->str()); + // If accessing element check if the element is changed + if (contains({Library::Container::Yield::ITEM, Library::Container::Yield::AT_INDEX}, yield)) + return isVariableChanged(ftok->next(), indirect, settings, depth - 1); + + if (contains({Library::Container::Yield::BUFFER, + Library::Container::Yield::BUFFER_NT, + Library::Container::Yield::START_ITERATOR, + Library::Container::Yield::ITERATOR}, + yield)) { + return isVariableChanged(ftok->next(), indirect + 1, settings, depth - 1); + } + if (contains({Library::Container::Yield::SIZE, + Library::Container::Yield::EMPTY, + Library::Container::Yield::END_ITERATOR}, + yield)) { + return false; + } + } + if ((settings && settings->library.isFunctionConst(ftok)) || (astIsSmartPointer(tok) && ftok->str() == "get")) // TODO: replace with action/yield? + return false; + + const Function * fun = ftok->function(); + if (!fun) + return true; + return !fun->isConst(); + } + + // Member pointer + if (Token::Match(tok2->astParent(), ". * ( & %name% ::")) { + const Token* ftok = tok2->astParent()->linkAt(2)->previous(); + // TODO: Check for pointer to member variable + if (!ftok->function() || !ftok->function()->isConst()) + return true; + } + if (Token::Match(tok2->astParent(), ". * %name%")) // bailout + return true; + + if (Token::simpleMatch(tok2, "[") && astIsContainer(tok) && vt && vt->container && vt->container->stdAssociativeLike) + return true; + + const Token *ftok = tok2; + while (ftok && (!Token::Match(ftok, "[({]") || ftok->isCast())) + ftok = ftok->astParent(); + + if (ftok && Token::Match(ftok->link(), ")|} !!{")) { + if (ftok->str() == "(" && Token::simpleMatch(ftok->astOperand1(), "[")) // operator() on array element, bail out + return true; + const Token * ptok = tok2; + while (Token::Match(ptok->astParent(), ".|::|[")) + ptok = ptok->astParent(); + int pindirect = indirect; + if (indirect == 0 && astIsLHS(tok2) && Token::Match(ptok, ". %var%") && astIsPointer(ptok->next())) + pindirect = 1; + bool inconclusive = false; + bool isChanged = isVariableChangedByFunctionCall(ptok, pindirect, settings, &inconclusive); + isChanged |= inconclusive; + if (isChanged) + return true; + } + + const Token *parent = tok2->astParent(); + while (Token::Match(parent, ".|::")) + parent = parent->astParent(); + if (parent && parent->tokType() == Token::eIncDecOp && (indirect == 0 || tok2 != tok)) + return true; + + // structured binding, nonconst reference variable in lhs + if (Token::Match(tok2->astParent(), ":|=") && tok2 == tok2->astParent()->astOperand2() && Token::simpleMatch(tok2->astParent()->previous(), "]")) { + const Token *typeStart = tok2->astParent()->previous()->link()->previous(); + if (Token::simpleMatch(typeStart, "&")) + typeStart = typeStart->previous(); + if (typeStart && Token::Match(typeStart->previous(), "[;{}(] auto &| [")) { + for (const Token *vartok = typeStart->tokAt(2); vartok != tok2; vartok = vartok->next()) { + if (vartok->varId()) { + const Variable* refvar = vartok->variable(); + if (!refvar || (!refvar->isConst() && refvar->isReference())) + return true; + } + } + } + } + + if (Token::simpleMatch(tok2->astParent(), ":") && tok2->astParent()->astParent() && Token::simpleMatch(tok2->astParent()->astParent()->previous(), "for (")) { + // TODO: Check if container is empty or not + if (astIsLHS(tok2)) + return true; + const Token * varTok = tok2->astParent()->previous(); + if (!varTok) + return false; + const Variable * loopVar = varTok->variable(); + if (!loopVar) + return false; + if (!loopVar->isConst() && loopVar->isReference() && isVariableChanged(loopVar, settings, depth - 1)) + return true; + return false; + } + + if (indirect > 0) { + // check for `*(ptr + 1) = new_value` case + parent = tok2->astParent(); + while (parent && ((parent->isArithmeticalOp() && parent->isBinaryOp()) || parent->isIncDecOp())) { + parent = parent->astParent(); + } + if (Token::simpleMatch(parent, "*")) { + if (parent->astParent() && parent->astParent()->isAssignmentOp() && + (parent->astParent()->astOperand1() == parent)) { + return true; + } + } + } + + return false; +} + +bool isVariableChanged(const Token *start, const Token *end, const nonneg int exprid, bool globalvar, const Settings *settings, int depth) +{ + return findVariableChanged(start, end, 0, exprid, globalvar, settings, depth) != nullptr; +} + +bool isVariableChanged(const Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings *settings, int depth) +{ + return findVariableChanged(start, end, indirect, exprid, globalvar, settings, depth) != nullptr; +} + +const Token* findExpression(const Token* start, const nonneg int exprid) +{ + const Function* f = Scope::nestedInFunction(start->scope()); + if (!f) + return nullptr; + const Scope* scope = f->functionScope; + if (!scope) + return nullptr; + for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (tok->exprId() != exprid) + continue; + return tok; + } + return nullptr; +} + +// Thread-unsafe memoization +template()())> +static std::function memoize(F f) +{ + bool init = false; + R result{}; + return [=]() mutable -> R { + if (init) + return result; + result = f(); + init = true; + return result; + }; +} + +template()()), const Token*> )> +static bool isExpressionChangedAt(const F& getExprTok, + const Token* tok, + int indirect, + const nonneg int exprid, + bool globalvar, + const Settings* settings, + int depth) +{ + if (depth < 0) + return true; + if (tok->isLiteral() || tok->isKeyword() || tok->isStandardType() || Token::Match(tok, ",|;|:")) + return false; + if (tok->exprId() != exprid || (!tok->varId() && !tok->isName())) { + if (globalvar && Token::Match(tok, "%name% (") && !(tok->function() && tok->function()->isAttributePure())) + // TODO: Is global variable really changed by function call? + return true; + int i = 1; + bool aliased = false; + // If we can't find the expression then assume it is an alias + auto expr = getExprTok(); + if (!expr) + aliased = true; + if (!aliased) + aliased = isAliasOf(tok, expr, &i); + if (!aliased) + return false; + if (isVariableChanged(tok, indirect + i, settings, depth)) + return true; + // TODO: Try to traverse the lambda function + if (Token::Match(tok, "%var% (")) + return true; + return false; + } + return (isVariableChanged(tok, indirect, settings, depth)); +} + +bool isExpressionChangedAt(const Token* expr, + const Token* tok, + int indirect, + bool globalvar, + const Settings* settings, + int depth) +{ + return isExpressionChangedAt([&] { + return expr; + }, tok, indirect, expr->exprId(), globalvar, settings, depth); +} + +Token* findVariableChanged(Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings *settings, int depth) +{ + if (!precedes(start, end)) + return nullptr; + if (depth < 0) + return start; + auto getExprTok = memoize([&] { + return findExpression(start, exprid); + }); + for (Token *tok = start; tok != end; tok = tok->next()) { + if (isExpressionChangedAt(getExprTok, tok, indirect, exprid, globalvar, settings, depth)) + return tok; + } + return nullptr; +} + +const Token* findVariableChanged(const Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings *settings, int depth) +{ + return findVariableChanged(const_cast(start), end, indirect, exprid, globalvar, settings, depth); +} + +bool isVariableChanged(const Variable * var, const Settings *settings, int depth) +{ + if (!var) + return false; + if (!var->scope()) + return false; + const Token * start = var->declEndToken(); + if (!start) + return false; + if (Token::Match(start, "; %varid% =", var->declarationId())) + start = start->tokAt(2); + if (Token::simpleMatch(start, "=")) { + const Token* next = nextAfterAstRightmostLeafGeneric(start); + if (next) + start = next; + } + return findExpressionChanged(var->nameToken(), start->next(), var->scope()->bodyEnd, settings, depth); +} + +bool isVariablesChanged(const Token* start, + const Token* end, + int indirect, + const std::vector &vars, + const Settings* settings) +{ + std::set varids; + std::transform(vars.cbegin(), vars.cend(), std::inserter(varids, varids.begin()), [](const Variable* var) { + return var->declarationId(); + }); + const bool globalvar = std::any_of(vars.cbegin(), vars.cend(), [](const Variable* var) { + return var->isGlobal(); + }); + for (const Token* tok = start; tok != end; tok = tok->next()) { + if (tok->varId() == 0 || varids.count(tok->varId()) == 0) { + if (globalvar && Token::Match(tok, "%name% (")) + // TODO: Is global variable really changed by function call? + return true; + continue; + } + if (isVariableChanged(tok, indirect, settings)) + return true; + } + return false; +} + +bool isThisChanged(const Token* tok, int indirect, const Settings* settings) +{ + if ((Token::Match(tok->previous(), "%name% (") && !Token::simpleMatch(tok->astOperand1(), ".")) || + Token::Match(tok->tokAt(-3), "this . %name% (")) { + if (tok->previous()->function()) { + return (!tok->previous()->function()->isConst() && !tok->previous()->function()->isStatic()); + } + if (!tok->previous()->isKeyword()) { + return true; + } + } + if (isVariableChanged(tok, indirect, settings)) + return true; + return false; +} + +const Token* findThisChanged(const Token* start, const Token* end, int indirect, const Settings* settings) +{ + if (!precedes(start, end)) + return nullptr; + for (const Token* tok = start; tok != end; tok = tok->next()) { + if (!exprDependsOnThis(tok)) + continue; + if (isThisChanged(tok, indirect, settings)) + return tok; + } + return nullptr; +} + +template +static const Token* findExpressionChangedImpl(const Token* expr, + const Token* start, + const Token* end, + const Settings* settings, + int depth, + Find find) +{ + if (depth < 0) + return start; + if (!precedes(start, end)) + return nullptr; + const Token* result = nullptr; + findAstNode(expr, [&](const Token* tok) { + if (exprDependsOnThis(tok)) { + result = findThisChanged(start, end, /*indirect*/ 0, settings); + if (result) + return true; + } + bool global = false; + if (tok->variable()) { + if (tok->variable()->isConst()) + return false; + global = !tok->variable()->isLocal() && !tok->variable()->isArgument() && + !(tok->variable()->isMember() && !tok->variable()->isStatic()); + } else if (tok->isIncompleteVar() && !tok->isIncompleteConstant()) { + global = true; + } + + if (tok->exprId() > 0 || global) { + const Token* modifedTok = find(start, end, [&](const Token* tok2) { + int indirect = 0; + if (const ValueType* vt = tok->valueType()) { + indirect = vt->pointer; + if (vt->type == ValueType::ITERATOR) + ++indirect; + } + for (int i = 0; i <= indirect; ++i) + if (isExpressionChangedAt(tok, tok2, i, global, settings, depth)) + return true; + return false; + }); + if (modifedTok) { + result = modifedTok; + return true; + } + } + return false; + }); + return result; +} + +namespace { + struct ExpressionChangedSimpleFind { + template + const Token* operator()(const Token* start, const Token* end, F f) const + { + return findToken(start, end, f); + } + }; + + struct ExpressionChangedSkipDeadCode { + const Library& library; + const std::function(const Token* tok)>* evaluate; + ExpressionChangedSkipDeadCode(const Library& library, + const std::function(const Token* tok)>& evaluate) + : library(library), evaluate(&evaluate) + {} + template + const Token* operator()(const Token* start, const Token* end, F f) const + { + return findTokenSkipDeadCode(library, start, end, f, *evaluate); + } + }; +} + +const Token* findExpressionChanged(const Token* expr, + const Token* start, + const Token* end, + const Settings* settings, + int depth) +{ + return findExpressionChangedImpl(expr, start, end, settings, depth, ExpressionChangedSimpleFind{}); +} + +const Token* findExpressionChangedSkipDeadCode(const Token* expr, + const Token* start, + const Token* end, + const Settings* settings, + const std::function(const Token* tok)>& evaluate, + int depth) +{ + return findExpressionChangedImpl( + expr, start, end, settings, depth, ExpressionChangedSkipDeadCode{settings->library, evaluate}); +} + +const Token* getArgumentStart(const Token* ftok) +{ + const Token* tok = ftok; + if (Token::Match(tok, "%name% (|{|)")) + tok = ftok->next(); + while (Token::simpleMatch(tok, ")")) + tok = tok->next(); + if (!Token::Match(tok, "(|{|[")) + return nullptr; + const Token* startTok = tok->astOperand2(); + if (!startTok && tok->next() != tok->link()) + startTok = tok->astOperand1(); + return startTok; +} + +int numberOfArguments(const Token* ftok) { + return astCount(getArgumentStart(ftok), ","); +} + +int numberOfArgumentsWithoutAst(const Token* start) +{ + int arguments = 0; + const Token* openBracket = start->next(); + while (Token::simpleMatch(openBracket, ")")) + openBracket = openBracket->next(); + if (openBracket && openBracket->str()=="(" && openBracket->next() && openBracket->next()->str()!=")") { + const Token* argument=openBracket->next(); + while (argument) { + ++arguments; + argument = argument->nextArgument(); + } + } + return arguments; +} + +std::vector getArguments(const Token* ftok) { + return astFlatten(getArgumentStart(ftok), ","); +} + +int getArgumentPos(const Variable* var, const Function* f) +{ + auto arg_it = std::find_if(f->argumentList.cbegin(), f->argumentList.cend(), [&](const Variable& v) { + return v.nameToken() == var->nameToken(); + }); + if (arg_it == f->argumentList.end()) + return -1; + return std::distance(f->argumentList.cbegin(), arg_it); +} + +const Token* getIteratorExpression(const Token* tok) +{ + if (!tok) + return nullptr; + if (tok->isUnaryOp("*")) + return nullptr; + if (!tok->isName()) { + const Token* iter1 = getIteratorExpression(tok->astOperand1()); + if (iter1) + return iter1; + if (tok->str() == "(") + return nullptr; + const Token* iter2 = getIteratorExpression(tok->astOperand2()); + if (iter2) + return iter2; + } else if (Token::Match(tok, "begin|cbegin|rbegin|crbegin|end|cend|rend|crend (")) { + if (Token::Match(tok->previous(), ". %name% ( ) !!.")) + return tok->previous()->astOperand1(); + if (!Token::simpleMatch(tok->previous(), ".") && Token::Match(tok, "%name% ( !!)") && + !Token::simpleMatch(tok->linkAt(1), ") .")) + return tok->next()->astOperand2(); + } + return nullptr; +} + +bool isIteratorPair(const std::vector& args) +{ + if (args.size() != 2) + return false; + if (astIsPointer(args[0]) && astIsPointer(args[1])) + return true; + // Check if iterator is from same container + const Token* tok1 = nullptr; + const Token* tok2 = nullptr; + if (astIsIterator(args[0]) && astIsIterator(args[1])) { + tok1 = ValueFlow::getLifetimeObjValue(args[0]).tokvalue; + tok2 = ValueFlow::getLifetimeObjValue(args[1]).tokvalue; + if (!tok1 || !tok2) + return true; + } else { + tok1 = getIteratorExpression(args[0]); + tok2 = getIteratorExpression(args[1]); + } + if (tok1 && tok2) + return tok1->exprId() == tok2->exprId(); + return tok1 || tok2; +} + +const Token *findLambdaStartToken(const Token *last) +{ + if (!last || !last->isCpp() || last->str() != "}") + return nullptr; + const Token* tok = last->link(); + if (Token::simpleMatch(tok->astParent(), "(")) + tok = tok->astParent(); + if (Token::simpleMatch(tok->astParent(), "[")) + return tok->astParent(); + return nullptr; +} + +template )> +static T* findLambdaEndTokenGeneric(T* first) +{ + auto maybeLambda = [](T* tok) -> bool { + while (Token::Match(tok, "*|%name%|::|>")) { + if (tok->link()) + tok = tok->link()->previous(); + else { + if (tok->str() == ">") + return true; + if (tok->str() == "new") + return false; + tok = tok->previous(); + } + } + return true; + }; + + if (!first || !first->isCpp() || first->str() != "[") + return nullptr; + if (!maybeLambda(first->previous())) + return nullptr; + if (!Token::Match(first->link(), "] (|{|<")) + return nullptr; + const Token* roundOrCurly = first->link()->next(); + if (roundOrCurly->link() && roundOrCurly->str() == "<") + roundOrCurly = roundOrCurly->link()->next(); + if (first->astOperand1() != roundOrCurly) + return nullptr; + T * tok = first; + + if (tok->astOperand1() && tok->astOperand1()->str() == "(") + tok = tok->astOperand1(); + if (tok->astOperand1() && tok->astOperand1()->str() == "{") + return tok->astOperand1()->link(); + return nullptr; +} + +const Token* findLambdaEndToken(const Token* first) +{ + return findLambdaEndTokenGeneric(first); +} +Token* findLambdaEndToken(Token* first) +{ + return findLambdaEndTokenGeneric(first); +} + +bool isLikelyStream(const Token *stream) +{ + if (!stream) + return false; + + if (!stream->isCpp()) + return false; + + if (!Token::Match(stream->astParent(), "&|<<|>>") || !stream->astParent()->isBinaryOp()) + return false; + + if (stream->astParent()->astOperand1() != stream) + return false; + + return !astIsIntegral(stream, false); +} + +bool isLikelyStreamRead(const Token *op) +{ + if (!op) + return false; + + if (!op->isCpp()) + return false; + + if (!Token::Match(op, "&|>>") || !op->isBinaryOp()) + return false; + + if (!Token::Match(op->astOperand2(), "%name%|.|*|[") && op->str() != op->astOperand2()->str()) + return false; + + const Token *parent = op; + while (parent->astParent() && parent->astParent()->str() == op->str()) + parent = parent->astParent(); + if (parent->astParent() && !Token::Match(parent->astParent(), "%oror%|&&|(|,|.|!|;|return")) + return false; + if (op->str() == "&" && parent->astParent()) + return false; + if (!parent->astOperand1() || !parent->astOperand2()) + return false; + return (!parent->astOperand1()->valueType() || !parent->astOperand1()->valueType()->isIntegral()); +} + +bool isCPPCast(const Token* tok) +{ + return tok && Token::simpleMatch(tok->previous(), "> (") && tok->astOperand2() && tok->astOperand1() && isCPPCastKeyword(tok->astOperand1()); +} + +bool isConstVarExpression(const Token *tok, const std::function& skipPredicate) +{ + if (!tok) + return false; + if (tok->str() == "?" && tok->astOperand2() && tok->astOperand2()->str() == ":") // ternary operator + return isConstVarExpression(tok->astOperand2()->astOperand1()) && isConstVarExpression(tok->astOperand2()->astOperand2()); // left and right of ":" + if (skipPredicate && skipPredicate(tok)) + return false; + if (Token::simpleMatch(tok->previous(), "sizeof (")) + return true; + if (Token::Match(tok->previous(), "%name% (")) { + if (Token::simpleMatch(tok->astOperand1(), ".") && !isConstVarExpression(tok->astOperand1(), skipPredicate)) + return false; + std::vector args = getArguments(tok); + if (args.empty() && tok->previous()->function() && tok->previous()->function()->isConstexpr()) + return true; + return !args.empty() && std::all_of(args.cbegin(), args.cend(), [&](const Token* t) { + return isConstVarExpression(t, skipPredicate); + }); + } + if (isCPPCast(tok)) { + return isConstVarExpression(tok->astOperand2(), skipPredicate); + } + if (Token::Match(tok, "( %type%")) + return isConstVarExpression(tok->astOperand1(), skipPredicate); + if (tok->str() == "::" && tok->hasKnownValue()) + return isConstVarExpression(tok->astOperand2(), skipPredicate); + if (Token::Match(tok, "%cop%|[|.")) { + if (tok->astOperand1() && !isConstVarExpression(tok->astOperand1(), skipPredicate)) + return false; + if (tok->astOperand2() && !isConstVarExpression(tok->astOperand2(), skipPredicate)) + return false; + return true; + } + if (Token::Match(tok, "%bool%|%num%|%str%|%char%|nullptr|NULL")) + return true; + if (tok->isEnumerator()) + return true; + if (tok->variable()) + return tok->variable()->isConst() && tok->variable()->nameToken() && tok->variable()->nameToken()->hasKnownValue(); + return false; +} + +static ExprUsage getFunctionUsage(const Token* tok, int indirect, const Settings& settings) +{ + const bool addressOf = tok->astParent() && tok->astParent()->isUnaryOp("&"); + + int argnr; + const Token* ftok = getTokenArgumentFunction(tok, argnr); + if (!ftok) + return ExprUsage::None; + if (ftok->function()) { + std::vector args = getArgumentVars(ftok, argnr); + for (const Variable* arg : args) { + if (!arg) + continue; + if (arg->isReference() || (arg->isPointer() && indirect == 1)) { + if (!ftok->function()->hasBody()) + return ExprUsage::PassedByReference; + for (const Token* bodytok = ftok->function()->functionScope->bodyStart; bodytok != ftok->function()->functionScope->bodyEnd; bodytok = bodytok->next()) { + if (bodytok->variable() == arg) { + if (arg->isReference()) + return ExprUsage::PassedByReference; + if (Token::Match(bodytok->astParent(), "%comp%|!")) + return ExprUsage::NotUsed; + return ExprUsage::PassedByReference; + } + } + return ExprUsage::NotUsed; + } + } + if (!args.empty() && indirect == 0 && !addressOf) + return ExprUsage::Used; + } else if (ftok->isControlFlowKeyword()) { + return ExprUsage::Used; + } else if (ftok->str() == "{") { + return indirect == 0 ? ExprUsage::Used : ExprUsage::Inconclusive; + } else if (ftok->variable() && ftok == ftok->variable()->nameToken()) { // variable init/constructor call + if (ftok->variable()->type() && ftok->variable()->type()->classScope && ftok->variable()->type()->classScope->numConstructors == 0) + return ExprUsage::Used; + if (ftok->variable()->isStlType() || (ftok->variable()->valueType() && ftok->variable()->valueType()->container)) // STL types or containers don't initialize external variables + return ExprUsage::Used; + } else { + const bool isnullbad = settings.library.isnullargbad(ftok, argnr + 1); + if (indirect == 0 && astIsPointer(tok) && !addressOf && isnullbad) + return ExprUsage::Used; + bool hasIndirect = false; + const bool isuninitbad = settings.library.isuninitargbad(ftok, argnr + 1, indirect, &hasIndirect); + if (isuninitbad && (!addressOf || isnullbad)) + return ExprUsage::Used; + } + return ExprUsage::Inconclusive; +} + +bool isLeafDot(const Token* tok) +{ + if (!tok) + return false; + const Token * parent = tok->astParent(); + if (!Token::simpleMatch(parent, ".")) + return false; + if (parent->astOperand2() == tok && !Token::simpleMatch(parent->astParent(), ".")) + return true; + return isLeafDot(parent); +} + +ExprUsage getExprUsage(const Token* tok, int indirect, const Settings& settings) +{ + const Token* parent = tok->astParent(); + if (indirect > 0 && parent) { + while (Token::simpleMatch(parent, "[") && parent->astParent()) + parent = parent->astParent(); + if (Token::Match(parent, "%assign%") && (astIsRHS(tok) || astIsLHS(parent->astOperand1()))) + return ExprUsage::NotUsed; + if (Token::Match(parent, "++|--")) + return ExprUsage::NotUsed; + if (parent->isConstOp()) + return ExprUsage::NotUsed; + if (parent->isCast()) + return ExprUsage::NotUsed; + if (Token::simpleMatch(parent, ":") && Token::simpleMatch(parent->astParent(), "?")) + return getExprUsage(parent->astParent(), indirect, settings); + if (isUsedAsBool(tok, &settings)) + return ExprUsage::NotUsed; + } + if (indirect == 0) { + if (Token::Match(parent, "%cop%|%assign%|++|--") && parent->str() != "=" && + !parent->isUnaryOp("&") && + !(astIsRHS(tok) && isLikelyStreamRead(parent))) + return ExprUsage::Used; + if (isLeafDot(tok)) { + const Token* op = parent->astParent(); + while (Token::simpleMatch(op, ".")) + op = op->astParent(); + if (Token::Match(op, "%assign%|++|--")) { + if (op->str() == "=") { + if (precedes(tok, op)) + return ExprUsage::NotUsed; + } else + return ExprUsage::Used; + } + } + if (Token::simpleMatch(parent, "=") && astIsRHS(tok)) { + const Token* const lhs = parent->astOperand1(); + if (lhs && lhs->variable() && lhs->variable()->isReference() && lhs == lhs->variable()->nameToken()) + return ExprUsage::NotUsed; + return ExprUsage::Used; + } + // Function call or index + if (((Token::simpleMatch(parent, "(") && !parent->isCast()) || (Token::simpleMatch(parent, "[") && tok->valueType())) && + (astIsLHS(tok) || Token::simpleMatch(parent, "( )"))) + return ExprUsage::Used; + } + return getFunctionUsage(tok, indirect, settings); +} + +static void getLHSVariablesRecursive(std::vector& vars, const Token* tok) +{ + if (!tok) + return; + if (vars.empty() && Token::Match(tok, "*|&|&&|[")) { + getLHSVariablesRecursive(vars, tok->astOperand1()); + if (!vars.empty() || Token::simpleMatch(tok, "[")) + return; + getLHSVariablesRecursive(vars, tok->astOperand2()); + } else if (Token::Match(tok->previous(), "this . %var%")) { + getLHSVariablesRecursive(vars, tok->next()); + } else if (Token::simpleMatch(tok, ".")) { + getLHSVariablesRecursive(vars, tok->astOperand1()); + getLHSVariablesRecursive(vars, tok->astOperand2()); + } else if (Token::simpleMatch(tok, "::")) { + getLHSVariablesRecursive(vars, tok->astOperand2()); + } else if (tok->variable()) { + vars.push_back(tok->variable()); + } +} + +std::vector getLHSVariables(const Token* tok) +{ + std::vector result; + if (!Token::Match(tok, "%assign%|(|{")) + return result; + if (!tok->astOperand1()) + return result; + if (tok->astOperand1()->varId() > 0 && tok->astOperand1()->variable()) + return {tok->astOperand1()->variable()}; + getLHSVariablesRecursive(result, tok->astOperand1()); + return result; +} + +static const Token* getLHSVariableRecursive(const Token* tok) +{ + if (!tok) + return nullptr; + if (Token::Match(tok, "*|&|&&|[")) { + const Token* vartok = getLHSVariableRecursive(tok->astOperand1()); + if ((vartok && vartok->variable()) || Token::simpleMatch(tok, "[")) + return vartok; + return getLHSVariableRecursive(tok->astOperand2()); + } + if (Token::Match(tok->previous(), "this . %var%")) + return tok->next(); + return tok; +} + +const Variable *getLHSVariable(const Token *tok) +{ + if (!tok || !tok->isAssignmentOp()) + return nullptr; + if (!tok->astOperand1()) + return nullptr; + if (tok->astOperand1()->varId() > 0 && tok->astOperand1()->variable()) + return tok->astOperand1()->variable(); + const Token* vartok = getLHSVariableRecursive(tok->astOperand1()); + if (!vartok) + return nullptr; + return vartok->variable(); +} + +const Token* getLHSVariableToken(const Token* tok) +{ + if (!Token::Match(tok, "%assign%")) + return nullptr; + if (!tok->astOperand1()) + return nullptr; + if (tok->astOperand1()->varId() > 0) + return tok->astOperand1(); + const Token* vartok = getLHSVariableRecursive(tok->astOperand1()); + if (vartok && vartok->variable() && vartok->variable()->nameToken() == vartok) + return vartok; + return tok->astOperand1(); +} + +const Token* findAllocFuncCallToken(const Token *expr, const Library &library) +{ + if (!expr) + return nullptr; + if (Token::Match(expr, "[+-]")) { + const Token *tok1 = findAllocFuncCallToken(expr->astOperand1(), library); + return tok1 ? tok1 : findAllocFuncCallToken(expr->astOperand2(), library); + } + if (expr->isCast()) + return findAllocFuncCallToken(expr->astOperand2() ? expr->astOperand2() : expr->astOperand1(), library); + if (Token::Match(expr->previous(), "%name% (") && library.getAllocFuncInfo(expr->astOperand1())) + return expr->astOperand1(); + return (Token::simpleMatch(expr, "new") && expr->astOperand1()) ? expr : nullptr; +} + +bool isNullOperand(const Token *expr) +{ + if (!expr) + return false; + if (expr->isCpp() && Token::Match(expr, "static_cast|const_cast|dynamic_cast|reinterpret_cast <")) + expr = expr->astParent(); + else if (!expr->isCast()) + return Token::Match(expr, "NULL|nullptr"); + if (expr->valueType() && expr->valueType()->pointer == 0) + return false; + const Token *castOp = expr->astOperand2() ? expr->astOperand2() : expr->astOperand1(); + return Token::Match(castOp, "NULL|nullptr") || (MathLib::isInt(castOp->str()) && MathLib::isNullValue(castOp->str())); +} + +bool isGlobalData(const Token *expr) +{ + // function call that returns reference => assume global data + if (expr && expr->str() == "(" && expr->valueType() && expr->valueType()->reference != Reference::None) { + if (expr->isBinaryOp()) + return true; + if (expr->astOperand1() && precedes(expr->astOperand1(), expr)) + return true; + } + + bool globalData = false; + bool var = false; + visitAstNodes(expr, + [expr, &globalData, &var](const Token *tok) { + if (tok->varId()) + var = true; + if (tok->varId() && !tok->variable()) { + // Bailout, this is probably global + globalData = true; + return ChildrenToVisit::none; + } + if (tok->originalName() == "->") { + // TODO check if pointer points at local data + globalData = true; + return ChildrenToVisit::none; + } + if (Token::Match(tok, "[*[]") && tok->astOperand1() && tok->astOperand1()->variable()) { + // TODO check if pointer points at local data + const Variable *lhsvar = tok->astOperand1()->variable(); + const ValueType *lhstype = tok->astOperand1()->valueType(); + if (lhsvar->isPointer()) { + globalData = true; + return ChildrenToVisit::none; + } + if (lhsvar->isArgument() && lhsvar->isArray()) { + globalData = true; + return ChildrenToVisit::none; + } + if (lhsvar->isArgument() && (!lhstype || (lhstype->type <= ValueType::Type::VOID && !lhstype->container))) { + globalData = true; + return ChildrenToVisit::none; + } + } + if (tok->varId() == 0 && tok->isName() && tok->previous()->str() != ".") { + globalData = true; + return ChildrenToVisit::none; + } + if (tok->variable()) { + // TODO : Check references + if (tok->variable()->isReference() && tok != tok->variable()->nameToken()) { + globalData = true; + return ChildrenToVisit::none; + } + if (tok->variable()->isExtern()) { + globalData = true; + return ChildrenToVisit::none; + } + if (tok->previous()->str() != "." && !tok->variable()->isLocal() && !tok->variable()->isArgument()) { + globalData = true; + return ChildrenToVisit::none; + } + if (tok->variable()->isArgument() && tok->variable()->isPointer() && tok != expr) { + globalData = true; + return ChildrenToVisit::none; + } + if (tok->variable()->isPointerArray()) { + globalData = true; + return ChildrenToVisit::none; + } + } + // Unknown argument type => it might be some reference type.. + if (tok->isCpp() && tok->str() == "." && tok->astOperand1() && tok->astOperand1()->variable() && !tok->astOperand1()->valueType()) { + globalData = true; + return ChildrenToVisit::none; + } + if (Token::Match(tok, ".|[")) + return ChildrenToVisit::op1; + return ChildrenToVisit::op1_and_op2; + }); + return globalData || !var; +} + +bool isUnevaluated(const Token *tok) +{ + return Token::Match(tok, "alignof|_Alignof|_alignof|__alignof|__alignof__|decltype|offsetof|sizeof|typeid|typeof|__typeof__ ("); +} diff --git a/cppcheck-2.14.0/lib/astutils.h b/cppcheck-2.14.0/lib/astutils.h new file mode 100644 index 00000000..2b2bb736 --- /dev/null +++ b/cppcheck-2.14.0/lib/astutils.h @@ -0,0 +1,461 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef astutilsH +#define astutilsH +//--------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "errortypes.h" +#include "library.h" +#include "mathlib.h" +#include "smallvector.h" +#include "symboldatabase.h" +#include "token.h" + +class Settings; + +enum class ChildrenToVisit { + none, + op1, + op2, + op1_and_op2, + done // found what we looked for, don't visit any more children +}; + +/** + * Visit AST nodes recursively. The order is not "well defined" + */ +template )> +void visitAstNodes(T *ast, const TFunc &visitor) +{ + if (!ast) + return; + + // the size of 8 was determined in tests to be sufficient to avoid excess allocations. also add 1 as a buffer. + // we might need to increase that value in the future. + std::stack> tokens; + T *tok = ast; + do { + const ChildrenToVisit c = visitor(tok); + + if (c == ChildrenToVisit::done) + break; + if (c == ChildrenToVisit::op2 || c == ChildrenToVisit::op1_and_op2) { + T *t2 = tok->astOperand2(); + if (t2) + tokens.push(t2); + } + if (c == ChildrenToVisit::op1 || c == ChildrenToVisit::op1_and_op2) { + T *t1 = tok->astOperand1(); + if (t1) + tokens.push(t1); + } + + if (tokens.empty()) + break; + + tok = tokens.top(); + tokens.pop(); + } while (true); +} + +template +const Token* findAstNode(const Token* ast, const TFunc& pred) +{ + const Token* result = nullptr; + visitAstNodes(ast, [&](const Token* tok) { + if (pred(tok)) { + result = tok; + return ChildrenToVisit::done; + } + return ChildrenToVisit::op1_and_op2; + }); + return result; +} + +template +const Token* findParent(const Token* tok, const TFunc& pred) +{ + if (!tok) + return nullptr; + const Token* parent = tok->astParent(); + while (parent && !pred(parent)) { + parent = parent->astParent(); + } + return parent; +} + +const Token* findExpression(const nonneg int exprid, + const Token* start, + const Token* end, + const std::function& pred); +const Token* findExpression(const Token* start, const nonneg int exprid); + +template )> +void astFlattenCopy(T* tok, const char* op, OuputIterator out, nonneg int depth = 100) +{ + --depth; + if (!tok || depth < 0) + return; + if (tok->str() == op) { + astFlattenCopy(tok->astOperand1(), op, out, depth); + astFlattenCopy(tok->astOperand2(), op, out, depth); + } else { + *out = tok; + ++out; + } +} + +std::vector astFlatten(const Token* tok, const char* op); +std::vector astFlatten(Token* tok, const char* op); + +nonneg int astCount(const Token* tok, const char* op, int depth = 100); + +bool astHasToken(const Token* root, const Token * tok); + +bool astHasExpr(const Token* tok, nonneg int exprid); +bool astHasVar(const Token * tok, nonneg int varid); + +bool astIsPrimitive(const Token* tok); +/** Is expression a 'signed char' if no promotion is used */ +bool astIsSignedChar(const Token *tok); +/** Is expression a 'char' if no promotion is used? */ +bool astIsUnknownSignChar(const Token *tok); +/** Is expression a char according to valueType? */ +bool astIsGenericChar(const Token* tok); +/** Is expression of integral type? */ +bool astIsIntegral(const Token *tok, bool unknown); +bool astIsUnsigned(const Token* tok); +/** Is expression of floating point type? */ +bool astIsFloat(const Token *tok, bool unknown); +/** Is expression of boolean type? */ +bool astIsBool(const Token *tok); + +bool astIsPointer(const Token *tok); + +bool astIsSmartPointer(const Token* tok); +bool astIsUniqueSmartPointer(const Token* tok); + +bool astIsIterator(const Token *tok); + +bool astIsContainer(const Token *tok); +bool astIsNonStringContainer(const Token *tok); + +bool astIsContainerView(const Token* tok); +bool astIsContainerOwned(const Token* tok); +bool astIsContainerString(const Token* tok); + +Library::Container::Action astContainerAction(const Token* tok, const Token** ftok = nullptr); +Library::Container::Yield astContainerYield(const Token* tok, const Token** ftok = nullptr); + +Library::Container::Yield astFunctionYield(const Token* tok, const Settings& settings, const Token** ftok = nullptr); + +/** Is given token a range-declaration in a range-based for loop */ +bool astIsRangeBasedForDecl(const Token* tok); + +/** + * Get canonical type of expression. const/static/etc are not included and neither *&. + * For example: + * Expression type Return + * std::string std::string + * int * int + * static const int int + * std::vector std::vector + */ +std::string astCanonicalType(const Token *expr, bool pointedToType); + +/** Is given syntax tree a variable comparison against value */ +const Token * astIsVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok=nullptr); + +bool isVariableDecl(const Token* tok); +bool isStlStringType(const Token* tok); + +bool isTemporary(const Token* tok, const Library* library, bool unknown = false); + +const Token* previousBeforeAstLeftmostLeaf(const Token* tok); +Token* previousBeforeAstLeftmostLeaf(Token* tok); + +CPPCHECKLIB const Token * nextAfterAstRightmostLeaf(const Token * tok); +Token* nextAfterAstRightmostLeaf(Token* tok); + +Token* astParentSkipParens(Token* tok); +const Token* astParentSkipParens(const Token* tok); + +const Token* getParentMember(const Token * tok); + +const Token* getParentLifetime(const Token* tok); +const Token* getParentLifetime(const Token* tok, const Library& library); + +std::vector getParentValueTypes(const Token* tok, + const Settings* settings = nullptr, + const Token** parent = nullptr); + +bool astIsLHS(const Token* tok); +bool astIsRHS(const Token* tok); + +Token* getCondTok(Token* tok); +const Token* getCondTok(const Token* tok); + +Token* getInitTok(Token* tok); +const Token* getInitTok(const Token* tok); + +Token* getStepTok(Token* tok); +const Token* getStepTok(const Token* tok); + +Token* getCondTokFromEnd(Token* endBlock); +const Token* getCondTokFromEnd(const Token* endBlock); + +/// For a "break" token, locate the next token to execute. The token will +/// be either a "}" or a ";". +const Token *findNextTokenFromBreak(const Token *breakToken); + +/** + * Extract for loop values: loopvar varid, init value, step value, last value (inclusive) + */ +bool extractForLoopValues(const Token *forToken, + nonneg int &varid, + bool &knownInitValue, + long long &initValue, + bool &partialCond, + long long &stepValue, + long long &lastValue); + +bool precedes(const Token * tok1, const Token * tok2); +bool succeeds(const Token* tok1, const Token* tok2); + +bool exprDependsOnThis(const Token* expr, bool onVar = true, nonneg int depth = 0); + +struct ReferenceToken { + const Token* token; + ErrorPath errors; +}; + +SmallVector followAllReferences(const Token* tok, + bool temporary = true, + bool inconclusive = true, + ErrorPath errors = ErrorPath{}, + int depth = 20); +const Token* followReferences(const Token* tok, ErrorPath* errors = nullptr); + +CPPCHECKLIB bool isSameExpression(bool macro, const Token *tok1, const Token *tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors=nullptr); + +bool isEqualKnownValue(const Token * const tok1, const Token * const tok2); + +bool isStructuredBindingVariable(const Variable* var); + +const Token* isInLoopCondition(const Token* tok); + +/** + * Is token used as boolean, that is to say cast to a bool, or used as a condition in a if/while/for + */ +CPPCHECKLIB bool isUsedAsBool(const Token* const tok, const Settings* settings = nullptr); + +/** + * Are the tokens' flags equal? + */ +bool compareTokenFlags(const Token* tok1, const Token* tok2, bool macro); + +/** + * Are two conditions opposite + * @param isNot do you want to know if cond1 is !cond2 or if cond1 and cond2 are non-overlapping. true: cond1==!cond2 false: cond1==true => cond2==false + * @param cond1 condition1 + * @param cond2 condition2 + * @param library files data + * @param pure boolean + */ +bool isOppositeCond(bool isNot, const Token * const cond1, const Token * const cond2, const Library& library, bool pure, bool followVar, ErrorPath* errors=nullptr); + +bool isOppositeExpression(const Token * const tok1, const Token * const tok2, const Library& library, bool pure, bool followVar, ErrorPath* errors=nullptr); + +bool isConstFunctionCall(const Token* ftok, const Library& library); + +bool isConstExpression(const Token *tok, const Library& library); + +bool isWithoutSideEffects(const Token* tok, bool checkArrayAccess = false, bool checkReference = true); + +bool isUniqueExpression(const Token* tok); + +bool isEscapeFunction(const Token* ftok, const Library* library); + +/** Is scope a return scope (scope will unconditionally return) */ +CPPCHECKLIB bool isReturnScope(const Token* const endToken, + const Library& library, + const Token** unknownFunc = nullptr, + bool functionScope = false); + +/** Is tok within a scope of the given type, nested within var's scope? */ +bool isWithinScope(const Token* tok, + const Variable* var, + Scope::ScopeType type); + +/// Return the token to the function and the argument number +const Token * getTokenArgumentFunction(const Token * tok, int& argn); +Token* getTokenArgumentFunction(Token* tok, int& argn); + +std::vector getArgumentVars(const Token* tok, int argnr); + +/** Is variable changed by function call? + * In case the answer of the question is inconclusive, e.g. because the function declaration is not known + * the return value is false and the output parameter inconclusive is set to true + * + * @param tok ast tree + * @param varid Variable Id + * @param settings program settings + * @param inconclusive pointer to output variable which indicates that the answer of the question is inconclusive + */ +bool isVariableChangedByFunctionCall(const Token *tok, int indirect, nonneg int varid, const Settings *settings, bool *inconclusive); + +/** Is variable changed by function call? + * In case the answer of the question is inconclusive, e.g. because the function declaration is not known + * the return value is false and the output parameter inconclusive is set to true + * + * @param tok token of variable in function call + * @param settings program settings + * @param inconclusive pointer to output variable which indicates that the answer of the question is inconclusive + */ +CPPCHECKLIB bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Settings *settings, bool *inconclusive); + +/** Is variable changed in block of code? */ +CPPCHECKLIB bool isVariableChanged(const Token *start, const Token *end, const nonneg int exprid, bool globalvar, const Settings *settings, int depth = 20); +bool isVariableChanged(const Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings *settings, int depth = 20); + +bool isVariableChanged(const Token *tok, int indirect, const Settings *settings, int depth = 20); + +bool isVariableChanged(const Variable * var, const Settings *settings, int depth = 20); + +bool isVariablesChanged(const Token* start, + const Token* end, + int indirect, + const std::vector &vars, + const Settings* settings); + +bool isThisChanged(const Token* tok, int indirect, const Settings* settings); +const Token* findThisChanged(const Token* start, const Token* end, int indirect, const Settings* settings); + +const Token* findVariableChanged(const Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings *settings, int depth = 20); +Token* findVariableChanged(Token *start, const Token *end, int indirect, const nonneg int exprid, bool globalvar, const Settings *settings, int depth = 20); + +CPPCHECKLIB const Token* findExpressionChanged(const Token* expr, + const Token* start, + const Token* end, + const Settings* settings, + int depth = 20); + +const Token* findExpressionChangedSkipDeadCode(const Token* expr, + const Token* start, + const Token* end, + const Settings* settings, + const std::function(const Token* tok)>& evaluate, + int depth = 20); + +bool isExpressionChangedAt(const Token* expr, + const Token* tok, + int indirect, + bool globalvar, + const Settings* settings, + int depth = 20); + +/// If token is an alias if another variable +bool isAliasOf(const Token *tok, nonneg int varid, bool* inconclusive = nullptr); + +bool isAliasOf(const Token* tok, const Token* expr, int* indirect = nullptr, bool* inconclusive = nullptr); + +bool isAliased(const Variable *var); + +const Token* getArgumentStart(const Token* ftok); + +/** Determines the number of arguments - if token is a function call or macro + * @param ftok start token which is supposed to be the function/macro name. + * @return Number of arguments + */ +int numberOfArguments(const Token* ftok); + +/// Get number of arguments without using AST +int numberOfArgumentsWithoutAst(const Token* start); + +/** + * Get arguments (AST) + */ +std::vector getArguments(const Token *ftok); + +int getArgumentPos(const Variable* var, const Function* f); + +const Token* getIteratorExpression(const Token* tok); + +/** + * Are the arguments a pair of iterators/pointers? + */ +bool isIteratorPair(const std::vector& args); + +CPPCHECKLIB const Token *findLambdaStartToken(const Token *last); + +/** + * find lambda function end token + * \param first The [ token + * \return nullptr or the } + */ +CPPCHECKLIB const Token *findLambdaEndToken(const Token *first); +CPPCHECKLIB Token* findLambdaEndToken(Token* first); + +bool isLikelyStream(const Token *stream); + +/** + * do we see a likely write of rhs through overloaded operator + * s >> x; + * a & x; + */ +bool isLikelyStreamRead(const Token *op); + +bool isCPPCast(const Token* tok); + +bool isConstVarExpression(const Token* tok, const std::function& skipPredicate = nullptr); + +bool isLeafDot(const Token* tok); + +enum class ExprUsage { None, NotUsed, PassedByReference, Used, Inconclusive }; + +ExprUsage getExprUsage(const Token* tok, int indirect, const Settings& settings); + +const Variable *getLHSVariable(const Token *tok); + +const Token* getLHSVariableToken(const Token* tok); + +std::vector getLHSVariables(const Token* tok); + +/** Find a allocation function call in expression, so result of expression is allocated memory/resource. */ +const Token* findAllocFuncCallToken(const Token *expr, const Library &library); + +bool isScopeBracket(const Token* tok); + +CPPCHECKLIB bool isNullOperand(const Token *expr); + +bool isGlobalData(const Token *expr); + +bool isUnevaluated(const Token *tok); + +#endif // astutilsH diff --git a/cppcheck-2.14.0/lib/calculate.h b/cppcheck-2.14.0/lib/calculate.h new file mode 100644 index 00000000..b5ed6e3e --- /dev/null +++ b/cppcheck-2.14.0/lib/calculate.h @@ -0,0 +1,127 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef calculateH +#define calculateH + +#include "mathlib.h" +#include "errortypes.h" +#include +#include + +template +bool isEqual(T x, T y) +{ + return x == y; +} + +inline bool isEqual(double x, double y) +{ + const double diff = (x > y) ? x - y : y - x; + return !((diff / 2) < diff); +} +inline bool isEqual(float x, float y) +{ + return isEqual(double(x), double(y)); +} + +template +bool isZero(T x) +{ + return isEqual(x, T(0)); +} + +template +R calculate(const std::string& s, const T& x, const T& y, bool* error = nullptr) +{ + auto wrap = [](T z) { + return R{z}; + }; + constexpr MathLib::bigint maxBitsShift = sizeof(MathLib::bigint) * 8; + // For portability we cannot shift signed integers by 63 bits + constexpr MathLib::bigint maxBitsSignedShift = maxBitsShift - 1; + switch (MathLib::encodeMultiChar(s)) { + case '+': + return wrap(x + y); + case '-': + return wrap(x - y); + case '*': + return wrap(x * y); + case '/': + if (isZero(y) || (std::is_integral{} && std::is_signed{} && isEqual(y, T(-1)) && isEqual(x, std::numeric_limits::min()))) { + if (error) + *error = true; + return R{}; + } + return wrap(x / y); + case '%': + if (isZero(MathLib::bigint(y)) || (std::is_integral{} && std::is_signed{} && isEqual(y, T(-1)) && isEqual(x, std::numeric_limits::min()))) { + if (error) + *error = true; + return R{}; + } + return wrap(MathLib::bigint(x) % MathLib::bigint(y)); + case '&': + return wrap(MathLib::bigint(x) & MathLib::bigint(y)); + case '|': + return wrap(MathLib::bigint(x) | MathLib::bigint(y)); + case '^': + return wrap(MathLib::bigint(x) ^ MathLib::bigint(y)); + case '>': + return wrap(x > y); + case '<': + return wrap(x < y); + case '<<': + if (y >= maxBitsSignedShift || y < 0 || x < 0) { + if (error) + *error = true; + return R{}; + } + return wrap(MathLib::bigint(x) << MathLib::bigint(y)); + case '>>': + if (y >= maxBitsSignedShift || y < 0 || x < 0) { + if (error) + *error = true; + return R{}; + } + return wrap(MathLib::bigint(x) >> MathLib::bigint(y)); + case '&&': + return wrap(!isZero(x) && !isZero(y)); + case '||': + return wrap(!isZero(x) || !isZero(y)); + case '==': + return wrap(isEqual(x, y)); + case '!=': + return wrap(!isEqual(x, y)); + case '>=': + return wrap(x >= y); + case '<=': + return wrap(x <= y); + case '<=>': + return wrap(x - y); + } + throw InternalError(nullptr, "Unknown operator: " + s); +} + +template +T calculate(const std::string& s, const T& x, const T& y, bool* error = nullptr) +{ + return calculate(s, x, y, error); +} + +#endif diff --git a/cppcheck-2.14.0/lib/check.cpp b/cppcheck-2.14.0/lib/check.cpp new file mode 100644 index 00000000..c5e6368f --- /dev/null +++ b/cppcheck-2.14.0/lib/check.cpp @@ -0,0 +1,133 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- + +#include "check.h" + +#include "errorlogger.h" +#include "settings.h" +#include "token.h" +#include "tokenize.h" +#include "vfvalue.h" + +#include +#include +#include +#include +#include + +//--------------------------------------------------------------------------- + +Check::Check(const std::string &aname) + : mName(aname) +{ + { + const auto it = std::find_if(instances().begin(), instances().end(), [&](const Check *i) { + return i->name() == aname; + }); + if (it != instances().end()) + throw std::runtime_error("'" + aname + "' instance already exists"); + } + + // make sure the instances are sorted + const auto it = std::find_if(instances().begin(), instances().end(), [&](const Check* i) { + return i->name() > aname; + }); + if (it == instances().end()) + instances().push_back(this); + else + instances().insert(it, this); +} + +void Check::writeToErrorList(const ErrorMessage &errmsg) +{ + std::cout << errmsg.toXML() << std::endl; +} + + +void Check::reportError(const std::list &callstack, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe, Certainty certainty) +{ + const ErrorMessage errmsg(callstack, mTokenizer ? &mTokenizer->list : nullptr, severity, id, msg, cwe, certainty); + if (mErrorLogger) + mErrorLogger->reportErr(errmsg); + else + writeToErrorList(errmsg); +} + +void Check::reportError(const ErrorPath &errorPath, Severity severity, const char id[], const std::string &msg, const CWE &cwe, Certainty certainty) +{ + const ErrorMessage errmsg(errorPath, mTokenizer ? &mTokenizer->list : nullptr, severity, id, msg, cwe, certainty); + if (mErrorLogger) + mErrorLogger->reportErr(errmsg); + else + writeToErrorList(errmsg); +} + +bool Check::wrongData(const Token *tok, const char *str) +{ + if (mSettings->daca) + reportError(tok, Severity::debug, "DacaWrongData", "Wrong data detected by condition " + std::string(str)); + return true; +} + +std::list &Check::instances() +{ +#ifdef __SVR4 + // Under Solaris, destructors are called in wrong order which causes a segmentation fault. + // This fix ensures pointer remains valid and reachable until program terminates. + static std::list *_instances= new std::list; + return *_instances; +#else + static std::list _instances; + return _instances; +#endif +} + +std::string Check::getMessageId(const ValueFlow::Value &value, const char id[]) +{ + if (value.condition != nullptr) + return id + std::string("Cond"); + if (value.safe) + return std::string("safe") + (char)std::toupper(id[0]) + (id + 1); + return id; +} + +ErrorPath Check::getErrorPath(const Token* errtok, const ValueFlow::Value* value, std::string bug) const +{ + ErrorPath errorPath; + if (!value) { + errorPath.emplace_back(errtok, std::move(bug)); + } else if (mSettings->verbose || mSettings->xml || !mSettings->templateLocation.empty()) { + errorPath = value->errorPath; + errorPath.emplace_back(errtok, std::move(bug)); + } else { + if (value->condition) + errorPath.emplace_back(value->condition, "condition '" + value->condition->expressionString() + "'"); + //else if (!value->isKnown() || value->defaultArg) + // errorPath = value->callstack; + errorPath.emplace_back(errtok, std::move(bug)); + } + return errorPath; +} + +void Check::logChecker(const char id[]) +{ + reportError(nullptr, Severity::internal, "logChecker", id); +} + diff --git a/cppcheck-2.14.0/lib/check.h b/cppcheck-2.14.0/lib/check.h new file mode 100644 index 00000000..dc4a12d8 --- /dev/null +++ b/cppcheck-2.14.0/lib/check.h @@ -0,0 +1,175 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "errortypes.h" + +#include +#include +#include + +namespace tinyxml2 { + class XMLElement; +} + +namespace CTU { + class FileInfo; +} + +namespace ValueFlow { + class Value; +} + +class Settings; +class Token; +class ErrorLogger; +class ErrorMessage; +class Tokenizer; + +/** Use WRONG_DATA in checkers to mark conditions that check that data is correct */ +#define WRONG_DATA(COND, TOK) ((COND) && wrongData((TOK), #COND)) + +/// @addtogroup Core +/// @{ + +/** + * @brief Interface class that cppcheck uses to communicate with the checks. + * All checking classes must inherit from this class + */ +class CPPCHECKLIB Check { +public: + /** This constructor is used when registering the CheckClass */ + explicit Check(const std::string &aname); + +protected: + /** This constructor is used when running checks. */ + Check(std::string aname, const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : mTokenizer(tokenizer), mSettings(settings), mErrorLogger(errorLogger), mName(std::move(aname)) {} + +public: + virtual ~Check() { + if (!mTokenizer) + instances().remove(this); + } + + Check(const Check &) = delete; + Check& operator=(const Check &) = delete; + + /** List of registered check classes. This is used by Cppcheck to run checks and generate documentation */ + static std::list &instances(); + + /** run checks, the token list is not simplified */ + virtual void runChecks(const Tokenizer &, ErrorLogger *) = 0; + + /** get error messages */ + virtual void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const = 0; + + /** class name, used to generate documentation */ + const std::string& name() const { + return mName; + } + + /** get information about this class, used to generate documentation */ + virtual std::string classInfo() const = 0; + + /** + * Write given error to stdout in xml format. + * This is for for printout out the error list with --errorlist + * @param errmsg Error message to write + */ + static void writeToErrorList(const ErrorMessage &errmsg); + + /** Base class used for whole-program analysis */ + class CPPCHECKLIB FileInfo { + public: + FileInfo() = default; + virtual ~FileInfo() = default; + virtual std::string toString() const { + return std::string(); + } + }; + + virtual FileInfo * getFileInfo(const Tokenizer& /*tokenizer*/, const Settings& /*settings*/) const { + return nullptr; + } + + virtual FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const { + (void)xmlElement; + return nullptr; + } + + // Return true if an error is reported. + virtual bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& /*settings*/, ErrorLogger & /*errorLogger*/) { + (void)ctu; + (void)fileInfo; + //(void)settings; + //(void)errorLogger; + return false; + } + +protected: + static std::string getMessageId(const ValueFlow::Value &value, const char id[]); + + const Tokenizer* const mTokenizer{}; + const Settings* const mSettings{}; + ErrorLogger* const mErrorLogger{}; + + /** report an error */ + void reportError(const Token *tok, const Severity severity, const std::string &id, const std::string &msg) { + reportError(tok, severity, id, msg, CWE(0U), Certainty::normal); + } + + /** report an error */ + void reportError(const Token *tok, const Severity severity, const std::string &id, const std::string &msg, const CWE &cwe, Certainty certainty) { + const std::list callstack(1, tok); + reportError(callstack, severity, id, msg, cwe, certainty); + } + + /** report an error */ + void reportError(const std::list &callstack, Severity severity, const std::string &id, const std::string &msg) { + reportError(callstack, severity, id, msg, CWE(0U), Certainty::normal); + } + + /** report an error */ + void reportError(const std::list &callstack, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe, Certainty certainty); + + void reportError(const ErrorPath &errorPath, Severity severity, const char id[], const std::string &msg, const CWE &cwe, Certainty certainty); + + /** log checker */ + void logChecker(const char id[]); + + ErrorPath getErrorPath(const Token* errtok, const ValueFlow::Value* value, std::string bug) const; + + /** + * Use WRONG_DATA in checkers when you check for wrong data. That + * will call this method + */ + bool wrongData(const Token *tok, const char *str); + +private: + const std::string mName; +}; + +/// @} +//--------------------------------------------------------------------------- +#endif // checkH diff --git a/cppcheck-2.14.0/lib/check64bit.cpp b/cppcheck-2.14.0/lib/check64bit.cpp new file mode 100644 index 00000000..02196cfc --- /dev/null +++ b/cppcheck-2.14.0/lib/check64bit.cpp @@ -0,0 +1,163 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "errortypes.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" + +#include + +//--------------------------------------------------------------------------- + +// CWE ids used +static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior + +// Register this check class (by creating a static instance of it) +namespace { + Check64BitPortability instance; +} + +void Check64BitPortability::pointerassignment() +{ + if (!mSettings->severity.isEnabled(Severity::portability)) + return; + + logChecker("Check64BitPortability::pointerassignment"); // portability + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + // Check return values + for (const Scope * scope : symbolDatabase->functionScopes) { + if (scope->function == nullptr || !scope->function->hasBody()) // We only look for functions with a body + continue; + + bool retPointer = false; + if (scope->function->token->strAt(-1) == "*") // Function returns a pointer + retPointer = true; + else if (Token::Match(scope->function->token->previous(), "int|long|DWORD")) // Function returns an integer + ; + else + continue; + + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + // skip nested functions + if (tok->str() == "{") { + if (tok->scope()->type == Scope::ScopeType::eFunction || tok->scope()->type == Scope::ScopeType::eLambda) + tok = tok->link(); + } + + if (tok->str() != "return") + continue; + + if (!tok->astOperand1() || tok->astOperand1()->isNumber()) + continue; + + const ValueType * const returnType = tok->astOperand1()->valueType(); + if (!returnType) + continue; + + if (retPointer && !returnType->typeScope && returnType->pointer == 0U) + returnIntegerError(tok); + + if (!retPointer && returnType->pointer >= 1U) + returnPointerError(tok); + } + } + + // Check assignments + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { + if (tok->str() != "=") + continue; + + const ValueType *lhstype = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; + const ValueType *rhstype = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr; + if (!lhstype || !rhstype) + continue; + + // Assign integer to pointer.. + if (lhstype->pointer >= 1U && + !tok->astOperand2()->isNumber() && + rhstype->pointer == 0U && + rhstype->originalTypeName.empty() && + rhstype->type == ValueType::Type::INT) + assignmentIntegerToAddressError(tok); + + // Assign pointer to integer.. + if (rhstype->pointer >= 1U && + lhstype->pointer == 0U && + lhstype->originalTypeName.empty() && + lhstype->isIntegral() && + lhstype->type >= ValueType::Type::CHAR && + lhstype->type <= ValueType::Type::INT) + assignmentAddressToIntegerError(tok); + } + } +} + +void Check64BitPortability::assignmentAddressToIntegerError(const Token *tok) +{ + reportError(tok, Severity::portability, + "AssignmentAddressToInteger", + "Assigning a pointer to an integer is not portable.\n" + "Assigning a pointer to an integer (int/long/etc) is not portable across different platforms and " + "compilers. For example in 32-bit Windows and linux they are same width, but in 64-bit Windows and linux " + "they are of different width. In worst case you end up assigning 64-bit address to 32-bit integer. The safe " + "way is to store addresses only in pointer types (or typedefs like uintptr_t).", CWE758, Certainty::normal); +} + +void Check64BitPortability::assignmentIntegerToAddressError(const Token *tok) +{ + reportError(tok, Severity::portability, + "AssignmentIntegerToAddress", + "Assigning an integer to a pointer is not portable.\n" + "Assigning an integer (int/long/etc) to a pointer is not portable across different platforms and " + "compilers. For example in 32-bit Windows and linux they are same width, but in 64-bit Windows and linux " + "they are of different width. In worst case you end up assigning 64-bit integer to 32-bit pointer. The safe " + "way is to store addresses only in pointer types (or typedefs like uintptr_t).", CWE758, Certainty::normal); +} + +void Check64BitPortability::returnPointerError(const Token *tok) +{ + reportError(tok, Severity::portability, + "CastAddressToIntegerAtReturn", + "Returning an address value in a function with integer return type is not portable.\n" + "Returning an address value in a function with integer (int/long/etc) return type is not portable across " + "different platforms and compilers. For example in 32-bit Windows and Linux they are same width, but in " + "64-bit Windows and Linux they are of different width. In worst case you end up casting 64-bit address down " + "to 32-bit integer. The safe way is to always return an integer.", CWE758, Certainty::normal); +} + +void Check64BitPortability::returnIntegerError(const Token *tok) +{ + reportError(tok, Severity::portability, + "CastIntegerToAddressAtReturn", + "Returning an integer in a function with pointer return type is not portable.\n" + "Returning an integer (int/long/etc) in a function with pointer return type is not portable across different " + "platforms and compilers. For example in 32-bit Windows and Linux they are same width, but in 64-bit Windows " + "and Linux they are of different width. In worst case you end up casting 64-bit integer down to 32-bit pointer. " + "The safe way is to always return a pointer.", CWE758, Certainty::normal); +} diff --git a/cppcheck-2.14.0/lib/check64bit.h b/cppcheck-2.14.0/lib/check64bit.h new file mode 100644 index 00000000..34d26e81 --- /dev/null +++ b/cppcheck-2.14.0/lib/check64bit.h @@ -0,0 +1,89 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef check64bitH +#define check64bitH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "tokenize.h" + +#include + +class ErrorLogger; +class Settings; +class Token; + + +/// @addtogroup Checks +/// @{ + +/** + * @brief Check for 64-bit portability issues + */ + +class CPPCHECKLIB Check64BitPortability : public Check { + friend class Test64BitPortability; + +public: + /** This constructor is used when registering the Check64BitPortability */ + Check64BitPortability() : Check(myName()) {} + +private: + /** 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, ErrorLogger *errorLogger) override { + Check64BitPortability check64BitPortability(&tokenizer, &tokenizer.getSettings(), errorLogger); + check64BitPortability.pointerassignment(); + } + + /** Check for pointer assignment */ + void pointerassignment(); + + void assignmentAddressToIntegerError(const Token *tok); + void assignmentIntegerToAddressError(const Token *tok); + void returnIntegerError(const Token *tok); + void returnPointerError(const Token *tok); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + Check64BitPortability c(nullptr, settings, errorLogger); + c.assignmentAddressToIntegerError(nullptr); + c.assignmentIntegerToAddressError(nullptr); + c.returnIntegerError(nullptr); + c.returnPointerError(nullptr); + } + + static std::string myName() { + return "64-bit portability"; + } + + std::string classInfo() const override { + return "Check if there is 64-bit portability issues:\n" + "- assign address to/from int/long\n" + "- casting address from/to integer when returning from function\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // check64bitH diff --git a/cppcheck-2.14.0/lib/checkassert.cpp b/cppcheck-2.14.0/lib/checkassert.cpp new file mode 100644 index 00000000..a9619128 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkassert.cpp @@ -0,0 +1,159 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "errortypes.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "tokenlist.h" + +//--------------------------------------------------------------------------- + +// CWE ids used +static const CWE CWE398(398U); // Indicator of Poor Code Quality + +// Register this check class (by creating a static instance of it) +namespace { + CheckAssert instance; +} + +void CheckAssert::assertWithSideEffects() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckAssert::assertWithSideEffects"); // warning + + for (const Token* tok = mTokenizer->list.front(); tok; tok = tok->next()) { + if (!Token::simpleMatch(tok, "assert (")) + continue; + + const Token *endTok = tok->next()->link(); + for (const Token* tmp = tok->next(); tmp != endTok; tmp = tmp->next()) { + if (Token::simpleMatch(tmp, "sizeof (")) + tmp = tmp->linkAt(1); + + checkVariableAssignment(tmp, tok->scope()); + + if (tmp->tokType() != Token::eFunction) + continue; + + const Function* f = tmp->function(); + const Scope* scope = f->functionScope; + if (!scope) { + // guess that const method doesn't have side effects + if (f->nestedIn->isClassOrStruct() && !f->isConst() && !f->isStatic()) + sideEffectInAssertError(tmp, f->name()); // Non-const member function called, assume it has side effects + continue; + } + + for (const Token *tok2 = scope->bodyStart; tok2 != scope->bodyEnd; tok2 = tok2->next()) { + if (!tok2->isAssignmentOp() && tok2->tokType() != Token::eIncDecOp) + continue; + + const Variable* var = tok2->previous()->variable(); + if (!var || var->isLocal() || (var->isArgument() && !var->isReference() && !var->isPointer())) + continue; // See ticket #4937. Assigning function arguments not passed by reference is ok. + if (var->isArgument() && var->isPointer() && tok2->strAt(-2) != "*") + continue; // Pointers need to be dereferenced, otherwise there is no error + + bool noReturnInScope = true; + for (const Token *rt = scope->bodyStart; rt != scope->bodyEnd; rt = rt->next()) { + if (rt->str() != "return") continue; // find all return statements + if (inSameScope(rt, tok2)) { + noReturnInScope = false; + break; + } + } + if (noReturnInScope) continue; + + sideEffectInAssertError(tmp, f->name()); + break; + } + } + tok = endTok; + } +} +//--------------------------------------------------------------------------- + + +void CheckAssert::sideEffectInAssertError(const Token *tok, const std::string& functionName) +{ + reportError(tok, Severity::warning, + "assertWithSideEffect", + "$symbol:" + functionName + "\n" + "Assert statement calls a function which may have desired side effects: '$symbol'.\n" + "Non-pure function: '$symbol' is called inside assert statement. " + "Assert statements are removed from release builds so the code inside " + "assert statement is not executed. If the code is needed also in release " + "builds, this is a bug.", CWE398, Certainty::normal); +} + +void CheckAssert::assignmentInAssertError(const Token *tok, const std::string& varname) +{ + reportError(tok, Severity::warning, + "assignmentInAssert", + "$symbol:" + varname + "\n" + "Assert statement modifies '$symbol'.\n" + "Variable '$symbol' is modified inside assert statement. " + "Assert statements are removed from release builds so the code inside " + "assert statement is not executed. If the code is needed also in release " + "builds, this is a bug.", CWE398, Certainty::normal); +} + +// checks if side effects happen on the variable prior to tmp +void CheckAssert::checkVariableAssignment(const Token* assignTok, const Scope *assertionScope) +{ + if (!assignTok->isAssignmentOp() && assignTok->tokType() != Token::eIncDecOp) + return; + + const Variable* var = assignTok->astOperand1()->variable(); + if (!var) + return; + + // Variable declared in inner scope in assert => don't warn + if (assertionScope != var->scope()) { + const Scope *s = var->scope(); + while (s && s != assertionScope) + s = s->nestedIn; + if (s == assertionScope) + return; + } + + // assignment + if (assignTok->isAssignmentOp() || assignTok->tokType() == Token::eIncDecOp) { + if (var->isConst()) { + return; + } + assignmentInAssertError(assignTok, var->name()); + } + // TODO: function calls on var +} + +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(); +} diff --git a/cppcheck-2.14.0/lib/checkassert.h b/cppcheck-2.14.0/lib/checkassert.h new file mode 100644 index 00000000..7de7106f --- /dev/null +++ b/cppcheck-2.14.0/lib/checkassert.h @@ -0,0 +1,81 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checkassertH +#define checkassertH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "tokenize.h" + +#include + +class ErrorLogger; +class Scope; +class Settings; +class Token; + +/// @addtogroup Checks +/// @{ + +/** + * @brief Checking for side effects in assert statements + */ + +class CPPCHECKLIB CheckAssert : public Check { +public: + CheckAssert() : Check(myName()) {} + +private: + CheckAssert(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger) {} + + /** run checks, the token list is not simplified */ + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + CheckAssert checkAssert(&tokenizer, &tokenizer.getSettings(), errorLogger); + checkAssert.assertWithSideEffects(); + } + + void assertWithSideEffects(); + + void checkVariableAssignment(const Token* assignTok, const Scope *assertionScope); + static bool inSameScope(const Token* returnTok, const Token* assignTok); + + void sideEffectInAssertError(const Token *tok, const std::string& functionName); + void assignmentInAssertError(const Token *tok, const std::string &varname); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckAssert c(nullptr, settings, errorLogger); + c.sideEffectInAssertError(nullptr, "function"); + c.assignmentInAssertError(nullptr, "var"); + } + + static std::string myName() { + return "Assert"; + } + + std::string classInfo() const override { + return "Warn if there are side effects in assert statements (since this cause different behaviour in debug/release builds).\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkassertH diff --git a/cppcheck-2.14.0/lib/checkautovariables.cpp b/cppcheck-2.14.0/lib/checkautovariables.cpp new file mode 100644 index 00000000..fcf4c654 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkautovariables.cpp @@ -0,0 +1,789 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +// Auto variables checks +//--------------------------------------------------------------------------- + +#include "checkautovariables.h" + +#include "astutils.h" +#include "library.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "valueflow.h" +#include "vfvalue.h" + +#include +#include +#include +#include +#include + +//--------------------------------------------------------------------------- + + +// Register this check class into cppcheck by creating a static instance of it.. +namespace { + CheckAutoVariables instance; +} + +static const CWE CWE398(398U); // Indicator of Poor Code Quality +static const CWE CWE562(562U); // Return of Stack Variable Address +static const CWE CWE590(590U); // Free of Memory not on the Heap + +static bool isPtrArg(const Token *tok) +{ + const Variable *var = tok->variable(); + return (var && var->isArgument() && var->isPointer()); +} + +static bool isArrayArg(const Token *tok, const Settings* settings) +{ + const Variable *var = tok->variable(); + return (var && var->isArgument() && var->isArray() && !settings->library.isentrypoint(var->scope()->className)); +} + +static bool isArrayVar(const Token *tok) +{ + const Variable *var = tok->variable(); + return (var && var->isArray() && !var->isArgument()); +} + +static bool isRefPtrArg(const Token *tok) +{ + const Variable *var = tok->variable(); + return (var && var->isArgument() && var->isReference() && var->isPointer()); +} + +static bool isNonReferenceArg(const Token *tok) +{ + const Variable *var = tok->variable(); + return (var && var->isArgument() && !var->isReference() && (var->isPointer() || (var->valueType() && var->valueType()->type >= ValueType::Type::CONTAINER) || var->type())); +} + +static bool isAutoVar(const Token *tok) +{ + const Variable *var = tok->variable(); + + if (!var || !var->isLocal() || var->isStatic()) + return false; + + if (var->isReference()) { + // address of reference variable can be taken if the address + // of the variable it points at is not a auto-var + // TODO: check what the reference variable references. + return false; + } + + if (Token::Match(tok, "%name% .|::")) { + do { + tok = tok->tokAt(2); + } while (Token::Match(tok, "%name% .|::")); + if (Token::Match(tok, "%name% (")) + return false; + } + return true; +} + +static bool isAutoVarArray(const Token *tok) +{ + if (!tok) + return false; + + // &x[..] + if (tok->isUnaryOp("&") && Token::simpleMatch(tok->astOperand1(), "[")) + return isAutoVarArray(tok->astOperand1()->astOperand1()); + + // x+y + if (tok->str() == "+") + return isAutoVarArray(tok->astOperand1()) || isAutoVarArray(tok->astOperand2()); + + // x-intexpr + if (tok->str() == "-") + return isAutoVarArray(tok->astOperand1()) && + tok->astOperand2() && + tok->astOperand2()->valueType() && + tok->astOperand2()->valueType()->isIntegral(); + + const Variable *var = tok->variable(); + if (!var) + return false; + + // Variable + if (var->isLocal() && !var->isStatic() && var->isArray() && !var->isPointer()) + return true; + + // ValueFlow + if (var->isPointer() && !var->isArgument()) { + for (std::list::const_iterator it = tok->values().cbegin(); it != tok->values().cend(); ++it) { + const ValueFlow::Value &val = *it; + if (val.isTokValue() && isAutoVarArray(val.tokvalue)) + return true; + } + } + + return false; +} + +static bool isLocalContainerBuffer(const Token* tok) +{ + if (!tok) + return false; + + // x+y + if (tok->str() == "+") + return isLocalContainerBuffer(tok->astOperand1()) || isLocalContainerBuffer(tok->astOperand2()); + + if (tok->str() != "(" || !Token::simpleMatch(tok->astOperand1(), ".")) + return false; + + tok = tok->astOperand1()->astOperand1(); + + const Variable* var = tok->variable(); + if (!var || !var->isLocal() || var->isStatic()) + return false; + + const Library::Container::Yield yield = astContainerYield(tok); + + return yield == Library::Container::Yield::BUFFER || yield == Library::Container::Yield::BUFFER_NT; +} + +static bool isAddressOfLocalVariable(const Token *expr) +{ + if (!expr) + return false; + if (Token::Match(expr, "+|-")) + return isAddressOfLocalVariable(expr->astOperand1()) || isAddressOfLocalVariable(expr->astOperand2()); + if (expr->isCast()) + return isAddressOfLocalVariable(expr->astOperand2() ? expr->astOperand2() : expr->astOperand1()); + if (expr->isUnaryOp("&")) { + const Token *op = expr->astOperand1(); + bool deref = false; + while (Token::Match(op, ".|[")) { + if (op->originalName() == "->") + return false; + if (op->str() == "[") + deref = true; + op = op->astOperand1(); + } + return op && isAutoVar(op) && (!deref || !op->variable()->isPointer()); + } + return false; +} + +static bool variableIsUsedInScope(const Token* start, nonneg int varId, const Scope *scope) +{ + if (!start) // Ticket #5024 + return false; + + for (const Token *tok = start; tok && tok != scope->bodyEnd; tok = tok->next()) { + if (tok->varId() == varId) + return true; + const Scope::ScopeType scopeType = tok->scope()->type; + if (scopeType == Scope::eFor || scopeType == Scope::eDo || scopeType == Scope::eWhile) // In case of loops, better checking would be necessary + return true; + if (Token::simpleMatch(tok, "asm (")) + return true; + } + return false; +} + +void CheckAutoVariables::assignFunctionArg() +{ + const bool printStyle = mSettings->severity.isEnabled(Severity::style); + const bool printWarning = mSettings->severity.isEnabled(Severity::warning); + if (!printStyle && !printWarning && !mSettings->isPremiumEnabled("uselessAssignmentPtrArg")) + return; + + logChecker("CheckAutoVariables::assignFunctionArg"); // style,warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { + // TODO: What happens if this is removed? + if (tok->astParent()) + continue; + if (!(tok->isAssignmentOp() || tok->tokType() == Token::eIncDecOp) || !Token::Match(tok->astOperand1(), "%var%")) + continue; + const Token* const vartok = tok->astOperand1(); + if (isNonReferenceArg(vartok) && + !Token::Match(vartok->next(), "= %varid% ;", vartok->varId()) && + !variableIsUsedInScope(Token::findsimplematch(vartok->next(), ";"), vartok->varId(), scope) && + !Token::findsimplematch(vartok, "goto", scope->bodyEnd)) { + if (vartok->variable()->isPointer() && printWarning) + errorUselessAssignmentPtrArg(vartok); + else if (printStyle) + errorUselessAssignmentArg(vartok); + } + } + } +} + +static bool isAutoVariableRHS(const Token* tok) { + return isAddressOfLocalVariable(tok) || isAutoVarArray(tok) || isLocalContainerBuffer(tok); +} + +static bool hasOverloadedAssignment(const Token* tok, bool& inconclusive) +{ + inconclusive = false; + if (tok->isC()) + return false; + if (const ValueType* vt = tok->valueType()) { + if (vt->pointer && !Token::simpleMatch(tok->astParent(), "*")) + return false; + if (vt->container && vt->container->stdStringLike) + return true; + if (vt->typeScope) + return std::any_of(vt->typeScope->functionList.begin(), vt->typeScope->functionList.end(), [](const Function& f) { // TODO: compare argument type + return f.name() == "operator="; + }); + return false; + } + inconclusive = true; + return true; +} + +void CheckAutoVariables::autoVariables() +{ + logChecker("CheckAutoVariables::autoVariables"); + + const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { + // Skip lambda.. + if (const Token *lambdaEndToken = findLambdaEndToken(tok)) { + tok = lambdaEndToken; + continue; + } + // Critical assignment + if (Token::Match(tok, "[;{}] %var% =") && isRefPtrArg(tok->next()) && isAutoVariableRHS(tok->tokAt(2)->astOperand2())) { + checkAutoVariableAssignment(tok->next(), false); + } else if (Token::Match(tok, "[;{}] * %var% =") && isPtrArg(tok->tokAt(2)) && isAutoVariableRHS(tok->tokAt(3)->astOperand2())) { + const Token* lhs = tok->tokAt(2); + bool inconclusive = false; + if (!hasOverloadedAssignment(lhs, inconclusive) || (printInconclusive && inconclusive)) + checkAutoVariableAssignment(tok->next(), inconclusive); + tok = tok->tokAt(4); + } else if (Token::Match(tok, "[;{}] %var% . %var% =") && isPtrArg(tok->next()) && isAutoVariableRHS(tok->tokAt(4)->astOperand2())) { + const Token* lhs = tok->tokAt(3); + bool inconclusive = false; + if (!hasOverloadedAssignment(lhs, inconclusive) || (printInconclusive && inconclusive)) + checkAutoVariableAssignment(tok->next(), inconclusive); + tok = tok->tokAt(5); + } else if (Token::Match(tok, "[;{}] %var% [") && Token::simpleMatch(tok->linkAt(2), "] =") && + (isPtrArg(tok->next()) || isArrayArg(tok->next(), mSettings)) && + isAutoVariableRHS(tok->linkAt(2)->next()->astOperand2())) { + errorAutoVariableAssignment(tok->next(), false); + } + // Invalid pointer deallocation + else if ((Token::Match(tok, "%name% ( %var%|%str% ) ;") && mSettings->library.getDeallocFuncInfo(tok)) || + (tok->isCpp() && Token::Match(tok, "delete [| ]| (| %var%|%str% !!["))) { + tok = Token::findmatch(tok->next(), "%var%|%str%"); + if (Token::simpleMatch(tok->astParent(), ".")) + continue; + if (isArrayVar(tok) || tok->tokType() == Token::eString) + errorInvalidDeallocation(tok, nullptr); + else if (tok->variable() && tok->variable()->isPointer()) { + for (const ValueFlow::Value &v : tok->values()) { + if (v.isImpossible()) + continue; + if ((v.isTokValue() && (isArrayVar(v.tokvalue) || ((v.tokvalue->tokType() == Token::eString)))) || + (v.isLocalLifetimeValue() && v.lifetimeKind == ValueFlow::Value::LifetimeKind::Address && !Token::simpleMatch(v.tokvalue, "("))) { + errorInvalidDeallocation(tok, &v); + break; + } + } + } + } else if ((Token::Match(tok, "%name% ( & %var% ) ;") && mSettings->library.getDeallocFuncInfo(tok)) || + (tok->isCpp() && Token::Match(tok, "delete [| ]| (| & %var% !!["))) { + tok = Token::findmatch(tok->next(), "%var%"); + if (isAutoVar(tok)) + errorInvalidDeallocation(tok, nullptr); + } + } + } +} + +bool CheckAutoVariables::checkAutoVariableAssignment(const Token *expr, bool inconclusive, const Token *startToken) +{ + if (!startToken) + startToken = Token::findsimplematch(expr, "=")->next(); + for (const Token *tok = startToken; tok; tok = tok->next()) { + if (tok->str() == "}" && tok->scope()->type == Scope::ScopeType::eFunction) + errorAutoVariableAssignment(expr, inconclusive); + + if (Token::Match(tok, "return|throw|break|continue")) { + errorAutoVariableAssignment(expr, inconclusive); + return true; + } + if (Token::simpleMatch(tok, "=")) { + const Token *lhs = tok; + while (Token::Match(lhs->previous(), "%name%|.|*|]")) { + if (lhs->linkAt(-1)) + lhs = lhs->linkAt(-1); + else + lhs = lhs->previous(); + } + const Token *e = expr; + while (e->str() != "=" && lhs->str() == e->str()) { + e = e->next(); + lhs = lhs->next(); + } + if (lhs->str() == "=") + return false; + } + + if (Token::simpleMatch(tok, "if (")) { + const Token *ifStart = tok->linkAt(1)->next(); + return checkAutoVariableAssignment(expr, inconclusive, ifStart) || checkAutoVariableAssignment(expr, inconclusive, ifStart->link()->next()); + } + if (Token::simpleMatch(tok, "} else {")) + tok = tok->linkAt(2); + } + return false; +} + +//--------------------------------------------------------------------------- + +void CheckAutoVariables::errorAutoVariableAssignment(const Token *tok, bool inconclusive) +{ + if (!inconclusive) { + reportError(tok, Severity::error, "autoVariables", + "Address of local auto-variable assigned to a function parameter.\n" + "Dangerous assignment - the function parameter is assigned the address of a local " + "auto-variable. Local auto-variables are reserved from the stack which " + "is freed when the function ends. So the pointer to a local variable " + "is invalid after the function ends.", CWE562, Certainty::normal); + } else { + reportError(tok, Severity::error, "autoVariables", + "Address of local auto-variable assigned to a function parameter.\n" + "Function parameter is assigned the address of a local auto-variable. " + "Local auto-variables are reserved from the stack which is freed when " + "the function ends. The address is invalid after the function ends and it " + "might 'leak' from the function through the parameter.", + CWE562, + Certainty::inconclusive); + } +} + +void CheckAutoVariables::errorUselessAssignmentArg(const Token *tok) +{ + reportError(tok, + Severity::style, + "uselessAssignmentArg", + "Assignment of function parameter has no effect outside the function.", CWE398, Certainty::normal); +} + +void CheckAutoVariables::errorUselessAssignmentPtrArg(const Token *tok) +{ + reportError(tok, + Severity::warning, + "uselessAssignmentPtrArg", + "Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?", CWE398, Certainty::normal); +} + +bool CheckAutoVariables::diag(const Token* tokvalue) +{ + if (!tokvalue) + return true; + return !mDiagDanglingTemp.insert(tokvalue).second; +} + +//--------------------------------------------------------------------------- + +static bool isInScope(const Token * tok, const Scope * scope) +{ + if (!tok) + return false; + if (!scope) + return false; + const Variable * var = tok->variable(); + if (var && (var->isGlobal() || var->isStatic() || var->isExtern())) + return false; + if (tok->scope() && !tok->scope()->isClassOrStructOrUnion() && tok->scope()->isNestedIn(scope)) + return true; + if (!var) + return false; + if (var->isArgument() && !var->isReference()) { + const Scope * tokScope = tok->scope(); + if (!tokScope) + return false; + if (std::any_of(tokScope->nestedList.cbegin(), tokScope->nestedList.cend(), [&](const Scope* argScope) { + return argScope && argScope->isNestedIn(scope); + })) + return true; + } + return false; +} + +static bool isDeadScope(const Token * tok, const Scope * scope) +{ + if (!tok) + return false; + if (!scope) + return false; + const Variable * var = tok->variable(); + if (var && (!var->isLocal() || var->isStatic() || var->isExtern())) + return false; + if (tok->scope() && tok->scope()->bodyEnd != scope->bodyEnd && precedes(tok->scope()->bodyEnd, scope->bodyEnd)) + return true; + return false; +} + +static int getPointerDepth(const Token *tok) +{ + if (!tok) + return 0; + if (tok->valueType()) + return tok->valueType()->pointer; + int n = 0; + std::pair decl = Token::typeDecl(tok); + for (const Token* tok2 = decl.first; tok2 != decl.second; tok2 = tok2->next()) + if (Token::simpleMatch(tok2, "*")) + n++; + return n; +} + +static bool isDeadTemporary(const Token* tok, const Token* expr, const Library* library) +{ + if (!isTemporary(tok, library)) + return false; + if (expr) { + if (!precedes(nextAfterAstRightmostLeaf(tok->astTop()), nextAfterAstRightmostLeaf(expr->astTop()))) + return false; + const Token* parent = tok->astParent(); + if (Token::simpleMatch(parent, "{") && Token::simpleMatch(parent->astParent(), ":")) + parent = parent->astParent(); + // Is in a for loop + if (astIsRHS(tok) && Token::simpleMatch(parent, ":") && Token::simpleMatch(parent->astParent(), "(") && Token::simpleMatch(parent->astParent()->previous(), "for (")) { + const Token* braces = parent->astParent()->link()->next(); + if (precedes(braces, expr) && precedes(expr, braces->link())) + return false; + } + } + return true; +} + +static bool isEscapedReference(const Variable* var) +{ + if (!var) + return false; + if (!var->isReference()) + return false; + const Token * const varDeclEndToken = var->declEndToken(); + if (!varDeclEndToken) + return false; + if (!Token::simpleMatch(varDeclEndToken, "=")) + return false; + const Token* vartok = varDeclEndToken->astOperand2(); + return !isTemporary(vartok, nullptr, false); +} + +static bool isDanglingSubFunction(const Token* tokvalue, const Token* tok) +{ + if (!tokvalue) + return false; + const Variable* var = tokvalue->variable(); + if (!var->isLocal()) + return false; + const Function* f = Scope::nestedInFunction(tok->scope()); + if (!f) + return false; + const Token* parent = tokvalue->astParent(); + while (parent && !Token::Match(parent->previous(), "%name% (")) { + parent = parent->astParent(); + } + if (!Token::simpleMatch(parent, "(")) + return false; + return exprDependsOnThis(parent); +} + +static const Variable* getParentVar(const Token* tok) +{ + if (!tok) + return nullptr; + if (Token::simpleMatch(tok, ".")) + return getParentVar(tok->astOperand1()); + return tok->variable(); +} + +static bool isAssignedToNonLocal(const Token* tok) +{ + if (!Token::simpleMatch(tok->astParent(), "=")) + return false; + if (!astIsRHS(tok)) + return false; + const Variable* var = getParentVar(tok->astParent()->astOperand1()); + if (!var) + return false; + return !var->isLocal() || var->isStatic(); +} + +void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token * end) +{ + const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); + if (!start) + return; + const Scope * scope = start->scope(); + if (!scope) + return; + // If the scope is not set correctly then skip checking it + if (scope->bodyStart != start) + return; + const bool returnRef = Function::returnsReference(scope->function); + for (const Token *tok = start; tok && tok != end; tok = tok->next()) { + // Return reference from function + if (returnRef && Token::simpleMatch(tok->astParent(), "return")) { + for (const ValueFlow::LifetimeToken& lt : ValueFlow::getLifetimeTokens(tok, true)) { + if (!printInconclusive && lt.inconclusive) + continue; + const Variable* var = lt.token->variable(); + if (var && !var->isGlobal() && !var->isStatic() && !var->isReference() && !var->isRValueReference() && + isInScope(var->nameToken(), tok->scope())) { + errorReturnReference(tok, lt.errorPath, lt.inconclusive); + break; + } + if (isDeadTemporary(lt.token, nullptr, &mSettings->library)) { + errorReturnTempReference(tok, lt.errorPath, lt.inconclusive); + break; + } + } + // Assign reference to non-local variable + } else if (Token::Match(tok->previous(), "&|&& %var% =") && tok->astParent() == tok->next() && + tok->variable() && tok->variable()->nameToken() == tok && + tok->variable()->declarationId() == tok->varId() && tok->variable()->isStatic() && + !tok->variable()->isArgument()) { + ErrorPath errorPath; + const Variable *var = ValueFlow::getLifetimeVariable(tok, errorPath); + if (var && isInScope(var->nameToken(), tok->scope())) { + errorDanglingReference(tok, var, std::move(errorPath)); + continue; + } + // Reference to temporary + } else if (tok->variable() && (tok->variable()->isReference() || tok->variable()->isRValueReference())) { + for (const ValueFlow::LifetimeToken& lt : ValueFlow::getLifetimeTokens(getParentLifetime(tok))) { + if (!printInconclusive && lt.inconclusive) + continue; + const Token * tokvalue = lt.token; + if (isDeadTemporary(tokvalue, tok, &mSettings->library)) { + errorDanglingTempReference(tok, lt.errorPath, lt.inconclusive); + break; + } + } + } + const bool escape = Token::simpleMatch(tok->astParent(), "throw") || + (Token::simpleMatch(tok->astParent(), "return") && !Function::returnsStandardType(scope->function)); + std::unordered_set exprs; + for (const ValueFlow::Value& val:tok->values()) { + if (!val.isLocalLifetimeValue() && !val.isSubFunctionLifetimeValue()) + continue; + if (!printInconclusive && val.isInconclusive()) + continue; + const Token* parent = getParentLifetime(val.tokvalue, mSettings->library); + if (!exprs.insert(parent).second) + continue; + for (const ValueFlow::LifetimeToken& lt : ValueFlow::getLifetimeTokens(parent, escape || isAssignedToNonLocal(tok))) { + const Token * tokvalue = lt.token; + if (val.isLocalLifetimeValue()) { + if (escape) { + if (getPointerDepth(tok) < getPointerDepth(tokvalue)) + continue; + if (!ValueFlow::isLifetimeBorrowed(tok, *mSettings)) + continue; + if (tokvalue->exprId() == tok->exprId() && !(tok->variable() && tok->variable()->isArray()) && + !astIsContainerView(tok->astParent())) + continue; + if ((tokvalue->variable() && !isEscapedReference(tokvalue->variable()) && + isInScope(tokvalue->variable()->nameToken(), scope)) || + isDeadTemporary(tokvalue, nullptr, &mSettings->library)) { + errorReturnDanglingLifetime(tok, &val); + break; + } + } else if (tokvalue->variable() && isDeadScope(tokvalue->variable()->nameToken(), tok->scope())) { + errorInvalidLifetime(tok, &val); + break; + } else if (!tokvalue->variable() && + isDeadTemporary(tokvalue, tok, &mSettings->library)) { + if (!diag(tokvalue)) + errorDanglingTemporaryLifetime(tok, &val, tokvalue); + break; + } + } + if (tokvalue->variable() && (isInScope(tokvalue->variable()->nameToken(), tok->scope()) || + (val.isSubFunctionLifetimeValue() && isDanglingSubFunction(tokvalue, tok)))) { + const Variable * var = nullptr; + const Token * tok2 = tok; + if (Token::simpleMatch(tok->astParent(), "=")) { + if (astIsRHS(tok)) { + var = getParentVar(tok->astParent()->astOperand1()); + tok2 = tok->astParent()->astOperand1(); + } + } else if (tok->variable() && tok->variable()->declarationId() == tok->varId()) { + var = tok->variable(); + } + if (!ValueFlow::isLifetimeBorrowed(tok, *mSettings)) + continue; + const Token* nextTok = nextAfterAstRightmostLeaf(tok->astTop()); + if (!nextTok) + nextTok = tok->next(); + if (var && !var->isLocal() && !var->isArgument() && !(val.tokvalue && val.tokvalue->variable() && val.tokvalue->variable()->isStatic()) && + !isVariableChanged(nextTok, + tok->scope()->bodyEnd, + var->declarationId(), + var->isGlobal(), + mSettings)) { + errorDanglngLifetime(tok2, &val); + break; + } + } + } + } + const Token *lambdaEndToken = findLambdaEndToken(tok); + if (lambdaEndToken) { + checkVarLifetimeScope(lambdaEndToken->link(), lambdaEndToken); + tok = lambdaEndToken; + } + if (tok->str() == "{" && tok->scope()) { + // Check functions in local classes + if (tok->scope()->type == Scope::eClass || + tok->scope()->type == Scope::eStruct || + tok->scope()->type == Scope::eUnion) { + for (const Function& f:tok->scope()->functionList) { + if (f.functionScope) + checkVarLifetimeScope(f.functionScope->bodyStart, f.functionScope->bodyEnd); + } + tok = tok->link(); + } + } + } +} + +void CheckAutoVariables::checkVarLifetime() +{ + logChecker("CheckAutoVariables::checkVarLifetime"); + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + if (!scope->function) + continue; + checkVarLifetimeScope(scope->bodyStart, scope->bodyEnd); + } +} + +void CheckAutoVariables::errorReturnDanglingLifetime(const Token *tok, const ValueFlow::Value *val) +{ + const bool inconclusive = val ? val->isInconclusive() : false; + ErrorPath errorPath = val ? val->errorPath : ErrorPath(); + std::string msg = "Returning " + lifetimeMessage(tok, val, errorPath); + errorPath.emplace_back(tok, ""); + reportError(errorPath, Severity::error, "returnDanglingLifetime", msg + " that will be invalid when returning.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckAutoVariables::errorInvalidLifetime(const Token *tok, const ValueFlow::Value* val) +{ + const bool inconclusive = val ? val->isInconclusive() : false; + ErrorPath errorPath = val ? val->errorPath : ErrorPath(); + std::string msg = "Using " + lifetimeMessage(tok, val, errorPath); + errorPath.emplace_back(tok, ""); + reportError(errorPath, Severity::error, "invalidLifetime", msg + " that is out of scope.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckAutoVariables::errorDanglingTemporaryLifetime(const Token* tok, const ValueFlow::Value* val, const Token* tempTok) +{ + const bool inconclusive = val ? val->isInconclusive() : false; + ErrorPath errorPath = val ? val->errorPath : ErrorPath(); + std::string msg = "Using " + lifetimeMessage(tok, val, errorPath); + errorPath.emplace_back(tempTok, "Temporary created here."); + errorPath.emplace_back(tok, ""); + reportError(errorPath, + Severity::error, + "danglingTemporaryLifetime", + msg + " that is a temporary.", + CWE562, + inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckAutoVariables::errorDanglngLifetime(const Token *tok, const ValueFlow::Value *val) +{ + const bool inconclusive = val ? val->isInconclusive() : false; + ErrorPath errorPath = val ? val->errorPath : ErrorPath(); + std::string tokName = tok ? tok->expressionString() : "x"; + std::string msg = "Non-local variable '" + tokName + "' will use " + lifetimeMessage(tok, val, errorPath); + errorPath.emplace_back(tok, ""); + reportError(errorPath, Severity::error, "danglingLifetime", msg + ".", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckAutoVariables::errorDanglingTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive) +{ + errorPath.emplace_back(tok, ""); + reportError( + errorPath, Severity::error, "danglingTempReference", "Using reference to dangling temporary.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckAutoVariables::errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive) +{ + errorPath.emplace_back(tok, ""); + reportError( + errorPath, Severity::error, "returnReference", "Reference to local variable returned.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckAutoVariables::errorDanglingReference(const Token *tok, const Variable *var, ErrorPath errorPath) +{ + std::string tokName = tok ? tok->str() : "x"; + std::string varName = var ? var->name() : "y"; + std::string msg = "Non-local reference variable '" + tokName + "' to local variable '" + varName + "'"; + errorPath.emplace_back(tok, ""); + reportError(errorPath, Severity::error, "danglingReference", msg, CWE562, Certainty::normal); +} + +void CheckAutoVariables::errorReturnTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive) +{ + errorPath.emplace_back(tok, ""); + reportError( + errorPath, Severity::error, "returnTempReference", "Reference to temporary returned.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckAutoVariables::errorInvalidDeallocation(const Token *tok, const ValueFlow::Value *val) +{ + const Variable *var = val ? val->tokvalue->variable() : (tok ? tok->variable() : nullptr); + + std::string type = "an auto-variable"; + if (tok && tok->tokType() == Token::eString) + type = "a string literal"; + else if (val && val->tokvalue->tokType() == Token::eString) + type = "a pointer pointing to a string literal"; + else if (var) { + if (var->isGlobal()) + type = "a global variable"; + else if (var->isStatic()) + type = "a static variable"; + } + + if (val) + type += " (" + val->tokvalue->str() + ")"; + + reportError(getErrorPath(tok, val, "Deallocating memory that was not dynamically allocated"), + Severity::error, + "autovarInvalidDeallocation", + "Deallocation of " + type + " results in undefined behaviour.\n" + "The deallocation of " + type + " results in undefined behaviour. You should only free memory " + "that has been allocated dynamically.", CWE590, Certainty::normal); +} diff --git a/cppcheck-2.14.0/lib/checkautovariables.h b/cppcheck-2.14.0/lib/checkautovariables.h new file mode 100644 index 00000000..3351d8cd --- /dev/null +++ b/cppcheck-2.14.0/lib/checkautovariables.h @@ -0,0 +1,131 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checkautovariablesH +#define checkautovariablesH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "errortypes.h" +#include "tokenize.h" + +#include +#include + +class Settings; +class Token; +class ErrorLogger; +class Variable; + +namespace ValueFlow { + class Value; +} + +/// @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()) {} + +private: + /** 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, ErrorLogger *errorLogger) override { + CheckAutoVariables checkAutoVariables(&tokenizer, &tokenizer.getSettings(), errorLogger); + checkAutoVariables.assignFunctionArg(); + checkAutoVariables.checkVarLifetime(); + checkAutoVariables.autoVariables(); + } + + /** assign function argument */ + void assignFunctionArg(); + + /** Check auto variables */ + void autoVariables(); + + /** + * Check variable assignment.. value must be changed later or there will be a error reported + * @return true if error is reported */ + bool checkAutoVariableAssignment(const Token *expr, bool inconclusive, const Token *startToken = nullptr); + + void checkVarLifetime(); + + void checkVarLifetimeScope(const Token * start, const Token * end); + + void errorAutoVariableAssignment(const Token *tok, bool inconclusive); + void errorReturnDanglingLifetime(const Token *tok, const ValueFlow::Value* val); + void errorInvalidLifetime(const Token *tok, const ValueFlow::Value* val); + void errorDanglngLifetime(const Token *tok, const ValueFlow::Value *val); + void errorDanglingTemporaryLifetime(const Token* tok, const ValueFlow::Value* val, const Token* tempTok); + void errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive); + void errorDanglingReference(const Token *tok, const Variable *var, ErrorPath errorPath); + void errorDanglingTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive); + void errorReturnTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive); + void errorInvalidDeallocation(const Token *tok, const ValueFlow::Value *val); + void errorUselessAssignmentArg(const Token *tok); + void errorUselessAssignmentPtrArg(const Token *tok); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckAutoVariables c(nullptr,settings,errorLogger); + c.errorAutoVariableAssignment(nullptr, false); + c.errorReturnReference(nullptr, ErrorPath{}, false); + c.errorDanglingReference(nullptr, nullptr, ErrorPath{}); + c.errorReturnTempReference(nullptr, ErrorPath{}, false); + c.errorDanglingTempReference(nullptr, ErrorPath{}, false); + c.errorInvalidDeallocation(nullptr, nullptr); + c.errorUselessAssignmentArg(nullptr); + c.errorUselessAssignmentPtrArg(nullptr); + c.errorReturnDanglingLifetime(nullptr, nullptr); + c.errorInvalidLifetime(nullptr, nullptr); + c.errorDanglngLifetime(nullptr, nullptr); + c.errorDanglingTemporaryLifetime(nullptr, nullptr, nullptr); + } + + static std::string myName() { + return "Auto Variables"; + } + + std::string classInfo() const override { + return "A pointer to a variable is only valid as long as the variable is in scope.\n" + "Check:\n" + "- returning a pointer to auto or temporary variable\n" + "- assigning address of an variable to an effective parameter of a function\n" + "- returning reference to local/temporary variable\n" + "- returning address of function parameter\n" + "- suspicious assignment of pointer argument\n" + "- useless assignment of function argument\n"; + } + + /** returns true if tokvalue has already been diagnosed */ + bool diag(const Token* tokvalue); + + std::set mDiagDanglingTemp; +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkautovariablesH diff --git a/cppcheck-2.14.0/lib/checkbool.cpp b/cppcheck-2.14.0/lib/checkbool.cpp new file mode 100644 index 00000000..1cb44e47 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkbool.cpp @@ -0,0 +1,519 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#include "checkbool.h" + +#include "astutils.h" +#include "errortypes.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "vfvalue.h" + +#include +#include +//--------------------------------------------------------------------------- + +// Register this check class (by creating a static instance of it) +namespace { + CheckBool instance; +} + +static const CWE CWE398(398U); // Indicator of Poor Code Quality +static const CWE CWE571(571U); // Expression is Always True +static const CWE CWE587(587U); // Assignment of a Fixed Address to a Pointer +static const CWE CWE704(704U); // Incorrect Type Conversion or Cast + +static bool isBool(const Variable* var) +{ + return (var && Token::Match(var->typeEndToken(), "bool|_Bool")); +} + +//--------------------------------------------------------------------------- +void CheckBool::checkIncrementBoolean() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("incrementboolean")) + return; + + logChecker("CheckBool::checkIncrementBoolean"); // style + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (astIsBool(tok) && tok->astParent() && tok->astParent()->str() == "++") { + incrementBooleanError(tok); + } + } + } +} + +void CheckBool::incrementBooleanError(const Token *tok) +{ + reportError( + tok, + Severity::style, + "incrementboolean", + "Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n" + "The operand of a postfix increment operator may be of type bool but it is deprecated by C++ Standard (Annex D-1) and the operand is always set to true. You should assign it the value 'true' instead.", + CWE398, Certainty::normal + ); +} + +static bool isConvertedToBool(const Token* tok) +{ + if (!tok->astParent()) + return false; + return astIsBool(tok->astParent()) || Token::Match(tok->astParent()->previous(), "if|while ("); +} + +//--------------------------------------------------------------------------- +// if (bool & bool) -> if (bool && bool) +// if (bool | bool) -> if (bool || bool) +//--------------------------------------------------------------------------- +void CheckBool::checkBitwiseOnBoolean() +{ + if (!mSettings->severity.isEnabled(Severity::style)) + return; + + // danmar: this is inconclusive because I don't like that there are + // warnings for calculations. Example: set_flag(a & b); + if (!mSettings->certainty.isEnabled(Certainty::inconclusive)) + return; + + logChecker("CheckBool::checkBitwiseOnBoolean"); // style,inconclusive + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (tok->isBinaryOp()) { + bool isCompound{}; + if (tok->str() == "&" || tok->str() == "|") + isCompound = false; + else if (tok->str() == "&=" || tok->str() == "|=") + isCompound = true; + else + continue; + const bool isBoolOp1 = astIsBool(tok->astOperand1()); + const bool isBoolOp2 = astIsBool(tok->astOperand2()); + if (!tok->astOperand1()->valueType() || !tok->astOperand2()->valueType()) + continue; + if (!(isBoolOp1 || isBoolOp2)) + continue; + if (isCompound && (!isBoolOp1 || isBoolOp2)) + continue; + if (tok->str() == "|" && !isConvertedToBool(tok) && !(isBoolOp1 && isBoolOp2)) + continue; + // first operand will always be evaluated + if (!isConstExpression(tok->astOperand2(), mSettings->library)) + continue; + if (tok->astOperand2()->variable() && tok->astOperand2()->variable()->nameToken() == tok->astOperand2()) + continue; + const std::string expression = (isBoolOp1 ? tok->astOperand1() : tok->astOperand2())->expressionString(); + bitwiseOnBooleanError(tok, expression, tok->str() == "&" ? "&&" : "||", isCompound); + } + } + } +} + +void CheckBool::bitwiseOnBooleanError(const Token* tok, const std::string& expression, const std::string& op, bool isCompound) +{ + std::string msg = "Boolean expression '" + expression + "' is used in bitwise operation."; + if (!isCompound) + msg += " Did you mean '" + op + "'?"; + reportError(tok, + Severity::style, + "bitwiseOnBoolean", + msg, + CWE398, + Certainty::inconclusive); +} + +//--------------------------------------------------------------------------- +// if (!x==3) <- Probably meant to be "x!=3" +//--------------------------------------------------------------------------- + +void CheckBool::checkComparisonOfBoolWithInt() +{ + if (!mSettings->severity.isEnabled(Severity::warning) || !mTokenizer->isCPP()) + return; + + logChecker("CheckBool::checkComparisonOfBoolWithInt"); // warning,c++ + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!tok->isComparisonOp() || !tok->isBinaryOp()) + continue; + const Token* const left = tok->astOperand1(); + const Token* const right = tok->astOperand2(); + if (left->isBoolean() && right->varId()) { // Comparing boolean constant with variable + if (tok->str() != "==" && tok->str() != "!=") { + comparisonOfBoolWithInvalidComparator(right, left->str()); + } + } else if (left->varId() && right->isBoolean()) { // Comparing variable with boolean constant + if (tok->str() != "==" && tok->str() != "!=") { + comparisonOfBoolWithInvalidComparator(right, left->str()); + } + } + } + } +} + +void CheckBool::comparisonOfBoolWithInvalidComparator(const Token *tok, const std::string &expression) +{ + reportError(tok, Severity::warning, "comparisonOfBoolWithInvalidComparator", + "Comparison of a boolean value using relational operator (<, >, <= or >=).\n" + "The result of the expression '" + expression + "' is of type 'bool'. " + "Comparing 'bool' value using relational (<, >, <= or >=)" + " operator could cause unexpected results."); +} + +//------------------------------------------------------------------------------- +// Comparing functions which are returning value of type bool +//------------------------------------------------------------------------------- + +static bool tokenIsFunctionReturningBool(const Token* tok) +{ + const Function* func = tok ? tok->function() : nullptr; + if (func && Token::Match(tok, "%name% (")) { + if (func->tokenDef && Token::Match(func->tokenDef->previous(), "bool|_Bool")) { + return true; + } + } + return false; +} + +void CheckBool::checkComparisonOfFuncReturningBool() +{ + if (!mSettings->severity.isEnabled(Severity::style)) + return; + + if (!mTokenizer->isCPP()) + return; + + logChecker("CheckBool::checkComparisonOfFuncReturningBool"); // style,c++ + + const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); + auto getFunctionTok = [](const Token* tok) -> const Token* { + while (Token::simpleMatch(tok, "!") || (tok && tok->isCast() && !isCPPCast(tok))) + tok = tok->astOperand1(); + if (isCPPCast(tok)) + tok = tok->astOperand2(); + if (tok) + return tok->previous(); + return nullptr; + }; + + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!tok->isComparisonOp() || tok->str() == "==" || tok->str() == "!=") + continue; + + const Token* firstToken = getFunctionTok(tok->astOperand1()); + const Token* secondToken = getFunctionTok(tok->astOperand2()); + if (!firstToken || !secondToken) + continue; + + const bool firstIsFunctionReturningBool = tokenIsFunctionReturningBool(firstToken); + const bool secondIsFunctionReturningBool = tokenIsFunctionReturningBool(secondToken); + if (firstIsFunctionReturningBool && secondIsFunctionReturningBool) { + comparisonOfTwoFuncsReturningBoolError(firstToken->next(), firstToken->str(), secondToken->str()); + } else if (firstIsFunctionReturningBool) { + comparisonOfFuncReturningBoolError(firstToken->next(), firstToken->str()); + } else if (secondIsFunctionReturningBool) { + comparisonOfFuncReturningBoolError(secondToken->previous(), secondToken->str()); + } + } + } +} + +void CheckBool::comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression) +{ + reportError(tok, Severity::style, "comparisonOfFuncReturningBoolError", + "Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n" + "The return type of function '" + expression + "' is 'bool' " + "and result is of type 'bool'. Comparing 'bool' value using relational (<, >, <= or >=)" + " operator could cause unexpected results.", CWE398, Certainty::normal); +} + +void CheckBool::comparisonOfTwoFuncsReturningBoolError(const Token *tok, const std::string &expression1, const std::string &expression2) +{ + reportError(tok, Severity::style, "comparisonOfTwoFuncsReturningBoolError", + "Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n" + "The return type of function '" + expression1 + "' and function '" + expression2 + "' is 'bool' " + "and result is of type 'bool'. Comparing 'bool' value using relational (<, >, <= or >=)" + " operator could cause unexpected results.", CWE398, Certainty::normal); +} + +//------------------------------------------------------------------------------- +// Comparison of bool with bool +//------------------------------------------------------------------------------- + +void CheckBool::checkComparisonOfBoolWithBool() +{ + if (!mSettings->severity.isEnabled(Severity::style)) + return; + + if (!mTokenizer->isCPP()) + return; + + logChecker("CheckBool::checkComparisonOfBoolWithBool"); // style,c++ + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!tok->isComparisonOp() || tok->str() == "==" || tok->str() == "!=") + continue; + bool firstTokenBool = false; + + const Token *firstToken = tok->previous(); + if (firstToken->varId()) { + if (isBool(firstToken->variable())) { + firstTokenBool = true; + } + } + if (!firstTokenBool) + continue; + + bool secondTokenBool = false; + const Token *secondToken = tok->next(); + if (secondToken->varId()) { + if (isBool(secondToken->variable())) { + secondTokenBool = true; + } + } + if (secondTokenBool) { + comparisonOfBoolWithBoolError(firstToken->next(), secondToken->str()); + } + } + } +} + +void CheckBool::comparisonOfBoolWithBoolError(const Token *tok, const std::string &expression) +{ + reportError(tok, Severity::style, "comparisonOfBoolWithBoolError", + "Comparison of a variable having boolean value using relational (<, >, <= or >=) operator.\n" + "The variable '" + expression + "' is of type 'bool' " + "and comparing 'bool' value using relational (<, >, <= or >=)" + " operator could cause unexpected results.", CWE398, Certainty::normal); +} + +//----------------------------------------------------------------------------- +void CheckBool::checkAssignBoolToPointer() +{ + logChecker("CheckBool::checkAssignBoolToPointer"); + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (tok->str() == "=" && astIsPointer(tok->astOperand1()) && astIsBool(tok->astOperand2())) { + assignBoolToPointerError(tok); + } + } + } +} + +void CheckBool::assignBoolToPointerError(const Token *tok) +{ + reportError(tok, Severity::error, "assignBoolToPointer", + "Boolean value assigned to pointer.", CWE587, Certainty::normal); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CheckBool::checkComparisonOfBoolExpressionWithInt() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckBool::checkComparisonOfBoolExpressionWithInt"); // warning + + const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!tok->isComparisonOp()) + continue; + + const Token* numTok = nullptr; + const Token* boolExpr = nullptr; + bool numInRhs; + if (astIsBool(tok->astOperand1())) { + boolExpr = tok->astOperand1(); + numTok = tok->astOperand2(); + numInRhs = true; + } else if (astIsBool(tok->astOperand2())) { + boolExpr = tok->astOperand2(); + numTok = tok->astOperand1(); + numInRhs = false; + } else { + continue; + } + + if (!numTok || !boolExpr) + continue; + + if (boolExpr->isOp() && numTok->isName() && Token::Match(tok, "==|!=")) + // there is weird code such as: ((agetValueLE(0, mSettings); + if (minval && minval->intvalue == 0 && + (numInRhs ? Token::Match(tok, ">|==|!=") + : Token::Match(tok, "<|==|!="))) + minval = nullptr; + + const ValueFlow::Value *maxval = numTok->getValueGE(1, mSettings); + if (maxval && maxval->intvalue == 1 && + (numInRhs ? Token::Match(tok, "<|==|!=") + : Token::Match(tok, ">|==|!="))) + maxval = nullptr; + + if (minval || maxval) { + const bool not0or1 = (minval && minval->intvalue < 0) || (maxval && maxval->intvalue > 1); + comparisonOfBoolExpressionWithIntError(tok, not0or1); + } + } + } +} + +void CheckBool::comparisonOfBoolExpressionWithIntError(const Token *tok, bool not0or1) +{ + if (not0or1) + reportError(tok, Severity::warning, "compareBoolExpressionWithInt", + "Comparison of a boolean expression with an integer other than 0 or 1.", CWE398, Certainty::normal); + else + reportError(tok, Severity::warning, "compareBoolExpressionWithInt", + "Comparison of a boolean expression with an integer.", CWE398, Certainty::normal); +} + + +void CheckBool::pointerArithBool() +{ + logChecker("CheckBool::pointerArithBool"); + + const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope &scope : symbolDatabase->scopeList) { + if (scope.type != Scope::eIf && !scope.isLoopScope()) + continue; + const Token* tok = scope.classDef->next()->astOperand2(); + if (scope.type == Scope::eFor) { + tok = Token::findsimplematch(scope.classDef->tokAt(2), ";"); + if (tok) + tok = tok->astOperand2(); + if (tok) + tok = tok->astOperand1(); + } else if (scope.type == Scope::eDo) + tok = (scope.bodyEnd->tokAt(2)) ? scope.bodyEnd->tokAt(2)->astOperand2() : nullptr; + + pointerArithBoolCond(tok); + } +} + +void CheckBool::pointerArithBoolCond(const Token *tok) +{ + if (!tok) + return; + if (Token::Match(tok, "&&|%oror%")) { + pointerArithBoolCond(tok->astOperand1()); + pointerArithBoolCond(tok->astOperand2()); + return; + } + if (tok->str() != "+" && tok->str() != "-") + return; + + if (tok->isBinaryOp() && + tok->astOperand1()->isName() && + tok->astOperand1()->variable() && + tok->astOperand1()->variable()->isPointer() && + tok->astOperand2()->isNumber()) + pointerArithBoolError(tok); +} + +void CheckBool::pointerArithBoolError(const Token *tok) +{ + reportError(tok, + Severity::error, + "pointerArithBool", + "Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n" + "Converting pointer arithmetic result to bool. The boolean result is always true unless there is pointer arithmetic overflow, and overflow is undefined behaviour. Probably a dereference is forgotten.", CWE571, Certainty::normal); +} + +void CheckBool::checkAssignBoolToFloat() +{ + if (!mTokenizer->isCPP()) + return; + if (!mSettings->severity.isEnabled(Severity::style)) + return; + logChecker("CheckBool::checkAssignBoolToFloat"); // style,c++ + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (tok->str() == "=" && astIsFloat(tok->astOperand1(), false) && astIsBool(tok->astOperand2())) { + assignBoolToFloatError(tok); + } + } + } +} + +void CheckBool::assignBoolToFloatError(const Token *tok) +{ + reportError(tok, Severity::style, "assignBoolToFloat", + "Boolean value assigned to floating point variable.", CWE704, Certainty::normal); +} + +void CheckBool::returnValueOfFunctionReturningBool() +{ + if (!mSettings->severity.isEnabled(Severity::style)) + return; + + logChecker("CheckBool::returnValueOfFunctionReturningBool"); // style + + const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope * scope : symbolDatabase->functionScopes) { + if (!(scope->function && Token::Match(scope->function->retDef, "bool|_Bool"))) + continue; + + for (const Token* tok = scope->bodyStart->next(); tok && (tok != scope->bodyEnd); tok = tok->next()) { + // Skip lambdas + const Token* tok2 = findLambdaEndToken(tok); + if (tok2) + tok = tok2; + else if (tok->scope() && tok->scope()->isClassOrStruct()) + tok = tok->scope()->bodyEnd; + else if (Token::simpleMatch(tok, "return") && tok->astOperand1() && + (tok->astOperand1()->getValueGE(2, mSettings) || tok->astOperand1()->getValueLE(-1, mSettings)) && + !(tok->astOperand1()->astOperand1() && Token::Match(tok->astOperand1(), "&|%or%"))) + returnValueBoolError(tok); + } + } +} + +void CheckBool::returnValueBoolError(const Token *tok) +{ + reportError(tok, Severity::style, "returnNonBoolInBooleanFunction", "Non-boolean value returned from function returning bool"); +} diff --git a/cppcheck-2.14.0/lib/checkbool.h b/cppcheck-2.14.0/lib/checkbool.h new file mode 100644 index 00000000..cceb0540 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkbool.h @@ -0,0 +1,145 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checkboolH +#define checkboolH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "tokenize.h" + +#include + +class ErrorLogger; +class Settings; +class Token; + +/// @addtogroup Checks +/// @{ + + +/** @brief checks dealing with suspicious usage of boolean type (not for evaluating conditions) */ + +class CPPCHECKLIB CheckBool : public Check { +public: + /** @brief This constructor is used when registering the CheckClass */ + CheckBool() : Check(myName()) {} + +private: + /** @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, ErrorLogger *errorLogger) override { + CheckBool checkBool(&tokenizer, &tokenizer.getSettings(), errorLogger); + + // Checks + checkBool.checkComparisonOfBoolExpressionWithInt(); + checkBool.checkComparisonOfBoolWithInt(); + checkBool.checkAssignBoolToFloat(); + checkBool.pointerArithBool(); + checkBool.returnValueOfFunctionReturningBool(); + checkBool.checkComparisonOfFuncReturningBool(); + checkBool.checkComparisonOfBoolWithBool(); + checkBool.checkIncrementBoolean(); + checkBool.checkAssignBoolToPointer(); + checkBool.checkBitwiseOnBoolean(); + } + + /** @brief %Check for comparison of function returning bool*/ + void checkComparisonOfFuncReturningBool(); + + /** @brief %Check for comparison of variable of type bool*/ + void checkComparisonOfBoolWithBool(); + + /** @brief %Check for using postfix increment on bool */ + void checkIncrementBoolean(); + + /** @brief %Check for suspicious comparison of a bool and a non-zero (and non-one) value (e.g. "if (!x==4)") */ + void checkComparisonOfBoolWithInt(); + + /** @brief assigning bool to pointer */ + void checkAssignBoolToPointer(); + + /** @brief assigning bool to float */ + void checkAssignBoolToFloat(); + + /** @brief %Check for using bool in bitwise expression */ + void checkBitwiseOnBoolean(); + + /** @brief %Check for comparing a bool expression with an integer other than 0 or 1 */ + void checkComparisonOfBoolExpressionWithInt(); + + /** @brief %Check for 'if (p+1)' etc. either somebody forgot to dereference, or else somebody uses pointer overflow */ + void pointerArithBool(); + void pointerArithBoolCond(const Token *tok); + + /** @brief %Check if a function returning bool returns an integer other than 0 or 1 */ + void returnValueOfFunctionReturningBool(); + + // Error messages.. + void comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression); + void comparisonOfTwoFuncsReturningBoolError(const Token *tok, const std::string &expression1, const std::string &expression2); + void comparisonOfBoolWithBoolError(const Token *tok, const std::string &expression); + void incrementBooleanError(const Token *tok); + void comparisonOfBoolWithInvalidComparator(const Token *tok, const std::string &expression); + void assignBoolToPointerError(const Token *tok); + void assignBoolToFloatError(const Token *tok); + void bitwiseOnBooleanError(const Token* tok, const std::string& expression, const std::string& op, bool isCompound = false); + void comparisonOfBoolExpressionWithIntError(const Token *tok, bool not0or1); + void pointerArithBoolError(const Token *tok); + void returnValueBoolError(const Token *tok); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckBool c(nullptr, settings, errorLogger); + c.assignBoolToPointerError(nullptr); + c.assignBoolToFloatError(nullptr); + c.comparisonOfFuncReturningBoolError(nullptr, "func_name"); + c.comparisonOfTwoFuncsReturningBoolError(nullptr, "func_name1", "func_name2"); + c.comparisonOfBoolWithBoolError(nullptr, "var_name"); + c.incrementBooleanError(nullptr); + c.bitwiseOnBooleanError(nullptr, "expression", "&&"); + c.comparisonOfBoolExpressionWithIntError(nullptr, true); + c.pointerArithBoolError(nullptr); + c.comparisonOfBoolWithInvalidComparator(nullptr, "expression"); + c.returnValueBoolError(nullptr); + } + + static std::string myName() { + return "Boolean"; + } + + std::string classInfo() const override { + return "Boolean type checks\n" + "- using increment on boolean\n" + "- comparison of a boolean expression with an integer other than 0 or 1\n" + "- comparison of a function returning boolean value using relational operator\n" + "- comparison of a boolean value with boolean value using relational operator\n" + "- using bool in bitwise expression\n" + "- pointer addition in condition (either dereference is forgot or pointer overflow is required to make the condition false)\n" + "- Assigning bool value to pointer or float\n" + "- Returning an integer other than 0 or 1 from a function with boolean return value\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkboolH diff --git a/cppcheck-2.14.0/lib/checkboost.cpp b/cppcheck-2.14.0/lib/checkboost.cpp new file mode 100644 index 00000000..4913a691 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkboost.cpp @@ -0,0 +1,66 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "errortypes.h" +#include "symboldatabase.h" +#include "token.h" + +#include + +// Register this check class (by creating a static instance of it) +namespace { + CheckBoost instance; +} + +static const CWE CWE664(664); + +void CheckBoost::checkBoostForeachModification() +{ + logChecker("CheckBoost::checkBoostForeachModification"); + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart->next(); tok && tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::simpleMatch(tok, "BOOST_FOREACH (")) + continue; + + const Token *containerTok = tok->next()->link()->previous(); + if (!Token::Match(containerTok, "%var% ) {")) + continue; + + const Token *tok2 = containerTok->tokAt(2); + const Token *end = tok2->link(); + for (; tok2 != end; tok2 = tok2->next()) { + if (Token::Match(tok2, "%varid% . insert|erase|push_back|push_front|pop_front|pop_back|clear|swap|resize|assign|merge|remove|remove_if|reverse|sort|splice|unique|pop|push", containerTok->varId())) { + const Token* nextStatement = Token::findsimplematch(tok2->linkAt(3), ";", end); + if (!Token::Match(nextStatement, "; break|return|throw")) + boostForeachError(tok2); + break; + } + } + } + } +} + +void CheckBoost::boostForeachError(const Token *tok) +{ + reportError(tok, Severity::error, "boostForeachError", + "BOOST_FOREACH caches the end() iterator. It's undefined behavior if you modify the container inside.", CWE664, Certainty::normal + ); +} diff --git a/cppcheck-2.14.0/lib/checkboost.h b/cppcheck-2.14.0/lib/checkboost.h new file mode 100644 index 00000000..b8be5521 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkboost.h @@ -0,0 +1,80 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checkboostH +#define checkboostH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "tokenize.h" + +#include + +class ErrorLogger; +class Settings; +class Token; + +/// @addtogroup Checks +/// @{ + + +/** @brief %Check Boost usage */ +class CPPCHECKLIB CheckBoost : public Check { +public: + /** This constructor is used when registering the CheckClass */ + CheckBoost() : Check(myName()) {} + +private: + /** This constructor is used when running checks. */ + CheckBoost(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger) {} + + /** @brief Run checks against the normal token list */ + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + if (!tokenizer.isCPP()) + return; + + CheckBoost checkBoost(&tokenizer, &tokenizer.getSettings(), errorLogger); + checkBoost.checkBoostForeachModification(); + } + + /** @brief %Check for container modification while using the BOOST_FOREACH macro */ + void checkBoostForeachModification(); + + void boostForeachError(const Token *tok); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckBoost c(nullptr, settings, errorLogger); + c.boostForeachError(nullptr); + } + + static std::string myName() { + return "Boost usage"; + } + + std::string classInfo() const override { + return "Check for invalid usage of Boost:\n" + "- container modification during BOOST_FOREACH\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkboostH diff --git a/cppcheck-2.14.0/lib/checkbufferoverrun.cpp b/cppcheck-2.14.0/lib/checkbufferoverrun.cpp new file mode 100644 index 00000000..0b8a31df --- /dev/null +++ b/cppcheck-2.14.0/lib/checkbufferoverrun.cpp @@ -0,0 +1,1210 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +// Buffer overrun.. +//--------------------------------------------------------------------------- + +#include "checkbufferoverrun.h" + +#include "astutils.h" +#include "errorlogger.h" +#include "library.h" +#include "mathlib.h" +#include "platform.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "utils.h" +#include "valueflow.h" + +#include +#include +#include +#include +#include // std::accumulate +#include +#include + +#include "xml.h" + +//--------------------------------------------------------------------------- + +// Register this check class (by creating a static instance of it) +namespace { + CheckBufferOverrun instance; +} + +//--------------------------------------------------------------------------- + +// CWE ids used: +static const CWE CWE131(131U); // Incorrect Calculation of Buffer Size +static const CWE CWE170(170U); // Improper Null Termination +static const CWE CWE_ARGUMENT_SIZE(398U); // Indicator of Poor Code Quality +static const CWE CWE_ARRAY_INDEX_THEN_CHECK(398U); // Indicator of Poor Code Quality +static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior +static const CWE CWE_POINTER_ARITHMETIC_OVERFLOW(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior +static const CWE CWE_BUFFER_UNDERRUN(786U); // Access of Memory Location Before Start of Buffer +static const CWE CWE_BUFFER_OVERRUN(788U); // Access of Memory Location After End of Buffer + +//--------------------------------------------------------------------------- + +static const ValueFlow::Value *getBufferSizeValue(const Token *tok) +{ + const std::list &tokenValues = tok->values(); + const auto it = std::find_if(tokenValues.cbegin(), tokenValues.cend(), std::mem_fn(&ValueFlow::Value::isBufferSizeValue)); + return it == tokenValues.cend() ? nullptr : &*it; +} + +static int getMinFormatStringOutputLength(const std::vector ¶meters, nonneg int formatStringArgNr) +{ + if (formatStringArgNr <= 0 || formatStringArgNr > parameters.size()) + return 0; + if (parameters[formatStringArgNr - 1]->tokType() != Token::eString) + return 0; + const std::string &formatString = parameters[formatStringArgNr - 1]->str(); + bool percentCharFound = false; + int outputStringSize = 0; + bool handleNextParameter = false; + std::string digits_string; + bool i_d_x_f_found = false; + int parameterLength = 0; + int inputArgNr = formatStringArgNr; + for (int i = 1; i + 1 < formatString.length(); ++i) { + if (formatString[i] == '\\') { + if (i < formatString.length() - 1 && formatString[i + 1] == '0') + break; + + ++outputStringSize; + ++i; + continue; + } + + if (percentCharFound) { + switch (formatString[i]) { + case 'f': + case 'x': + case 'X': + case 'i': + i_d_x_f_found = true; + handleNextParameter = true; + parameterLength = 1; // TODO + break; + case 'c': + case 'e': + case 'E': + case 'g': + case 'o': + case 'u': + case 'p': + case 'n': + handleNextParameter = true; + parameterLength = 1; // TODO + break; + case 'd': + i_d_x_f_found = true; + parameterLength = 1; + if (inputArgNr < parameters.size() && parameters[inputArgNr]->hasKnownIntValue()) + parameterLength = std::to_string(parameters[inputArgNr]->getKnownIntValue()).length(); + + handleNextParameter = true; + break; + case 's': + parameterLength = 0; + if (inputArgNr < parameters.size() && parameters[inputArgNr]->tokType() == Token::eString) + parameterLength = Token::getStrLength(parameters[inputArgNr]); + + handleNextParameter = true; + break; + } + } + + if (formatString[i] == '%') + percentCharFound = !percentCharFound; + else if (percentCharFound) { + digits_string.append(1, formatString[i]); + } + + if (!percentCharFound) + outputStringSize++; + + if (handleNextParameter) { + // NOLINTNEXTLINE(cert-err34-c) - intentional use + int tempDigits = std::abs(std::atoi(digits_string.c_str())); + if (i_d_x_f_found) + tempDigits = std::max(tempDigits, 1); + + if (digits_string.find('.') != std::string::npos) { + const std::string endStr = digits_string.substr(digits_string.find('.') + 1); + // NOLINTNEXTLINE(cert-err34-c) - intentional use + const int maxLen = std::max(std::abs(std::atoi(endStr.c_str())), 1); + + if (formatString[i] == 's') { + // For strings, the length after the dot "%.2s" will limit + // the length of the string. + if (parameterLength > maxLen) + parameterLength = maxLen; + } else { + // For integers, the length after the dot "%.2d" can + // increase required length + if (tempDigits < maxLen) + tempDigits = maxLen; + } + } + + if (tempDigits < parameterLength) + outputStringSize += parameterLength; + else + outputStringSize += tempDigits; + + parameterLength = 0; + digits_string.clear(); + i_d_x_f_found = false; + percentCharFound = false; + handleNextParameter = false; + ++inputArgNr; + } + } + + return outputStringSize; +} + +//--------------------------------------------------------------------------- + +static bool getDimensionsEtc(const Token * const arrayToken, const Settings *settings, std::vector &dimensions, ErrorPath &errorPath, bool &mightBeLarger, MathLib::bigint &path) +{ + const Token *array = arrayToken; + while (Token::Match(array, ".|::")) + array = array->astOperand2(); + + if (array->variable() && array->variable()->isArray() && !array->variable()->dimensions().empty()) { + dimensions = array->variable()->dimensions(); + if (dimensions[0].num <= 1 || !dimensions[0].tok) { + visitAstNodes(arrayToken, + [&](const Token *child) { + if (child->originalName() == "->") { + mightBeLarger = true; + return ChildrenToVisit::none; + } + return ChildrenToVisit::op1_and_op2; + }); + } + } else if (const Token *stringLiteral = array->getValueTokenMinStrSize(*settings, &path)) { + Dimension dim; + dim.tok = nullptr; + dim.num = Token::getStrArraySize(stringLiteral); + dim.known = array->hasKnownValue(); + dimensions.emplace_back(dim); + } else if (array->valueType() && array->valueType()->pointer >= 1 && (array->valueType()->isIntegral() || array->valueType()->isFloat())) { + const ValueFlow::Value *value = getBufferSizeValue(array); + if (!value) + return false; + path = value->path; + errorPath = value->errorPath; + Dimension dim; + dim.known = value->isKnown(); + dim.tok = nullptr; + const int typeSize = array->valueType()->typeSize(settings->platform, array->valueType()->pointer > 1); + if (typeSize == 0) + return false; + dim.num = value->intvalue / typeSize; + dimensions.emplace_back(dim); + } + return !dimensions.empty(); +} + +static ValueFlow::Value makeSizeValue(MathLib::bigint size, MathLib::bigint path) +{ + ValueFlow::Value v(size); + v.path = path; + return v; +} + +static std::vector getOverrunIndexValues(const Token* tok, + const Token* arrayToken, + const std::vector& dimensions, + const std::vector& indexTokens, + MathLib::bigint path) +{ + const Token *array = arrayToken; + while (Token::Match(array, ".|::")) + array = array->astOperand2(); + + bool isArrayIndex = tok->str() == "["; + if (isArrayIndex) { + const Token* parent = tok; + while (Token::simpleMatch(parent, "[")) + parent = parent->astParent(); + if (!parent || parent->isUnaryOp("&")) + isArrayIndex = false; + } + + bool overflow = false; + std::vector indexValues; + for (int i = 0; i < dimensions.size() && i < indexTokens.size(); ++i) { + MathLib::bigint size = dimensions[i].num; + if (!isArrayIndex) + size++; + const bool zeroArray = array->variable() && array->variable()->isArray() && dimensions[i].num == 0; + std::vector values = !zeroArray + ? ValueFlow::isOutOfBounds(makeSizeValue(size, path), indexTokens[i]) + : std::vector{}; + if (values.empty()) { + if (indexTokens[i]->hasKnownIntValue()) + indexValues.push_back(indexTokens[i]->values().front()); + else + indexValues.push_back(ValueFlow::Value::unknown()); + continue; + } + overflow = true; + indexValues.push_back(values.front()); + } + if (overflow) + return indexValues; + return {}; +} + +void CheckBufferOverrun::arrayIndex() +{ + logChecker("CheckBufferOverrun::arrayIndex"); + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (tok->str() != "[") + continue; + const Token *array = tok->astOperand1(); + while (Token::Match(array, ".|::")) + array = array->astOperand2(); + if (!array || ((!array->variable() || array->variable()->nameToken() == array) && array->tokType() != Token::eString)) + continue; + if (!array->scope()->isExecutable()) { + // LHS in non-executable scope => This is just a definition + const Token *parent = tok; + while (parent && !Token::simpleMatch(parent->astParent(), "=")) + parent = parent->astParent(); + if (!parent || parent == parent->astParent()->astOperand1()) + continue; + } + + if (astIsContainer(array)) + continue; + + std::vector indexTokens; + for (const Token *tok2 = tok; tok2 && tok2->str() == "["; tok2 = tok2->link()->next()) { + if (!tok2->astOperand2()) { + indexTokens.clear(); + break; + } + indexTokens.emplace_back(tok2->astOperand2()); + } + if (indexTokens.empty()) + continue; + + std::vector dimensions; + ErrorPath errorPath; + bool mightBeLarger = false; + MathLib::bigint path = 0; + if (!getDimensionsEtc(tok->astOperand1(), mSettings, dimensions, errorPath, mightBeLarger, path)) + continue; + + const Variable* const var = array->variable(); + if (var && var->isArgument() && var->scope()) { + const Token* changeTok = var->scope()->bodyStart; + bool isChanged = false; + while ((changeTok = findVariableChanged(changeTok->next(), var->scope()->bodyEnd, /*indirect*/ 0, var->declarationId(), + /*globalvar*/ false, mSettings))) { + if (!Token::simpleMatch(changeTok->astParent(), "[")) { + isChanged = true; + break; + } + } + if (isChanged) + continue; + } + + // Positive index + if (!mightBeLarger) { // TODO check arrays with dim 1 also + const std::vector& indexValues = + getOverrunIndexValues(tok, tok->astOperand1(), dimensions, indexTokens, path); + if (!indexValues.empty()) + arrayIndexError(tok, dimensions, indexValues); + } + + // Negative index + bool neg = false; + std::vector negativeIndexes; + for (const Token * indexToken : indexTokens) { + const ValueFlow::Value *negativeValue = indexToken->getValueLE(-1, mSettings); + if (negativeValue) { + negativeIndexes.emplace_back(*negativeValue); + neg = true; + } else { + negativeIndexes.emplace_back(ValueFlow::Value::unknown()); + } + } + if (neg) { + negativeIndexError(tok, dimensions, negativeIndexes); + } + } +} + +static std::string stringifyIndexes(const std::string& array, const std::vector& indexValues) +{ + if (indexValues.size() == 1) + return std::to_string(indexValues[0].intvalue); + + std::ostringstream ret; + ret << array; + for (const ValueFlow::Value& index : indexValues) { + ret << "["; + if (index.isNonValue()) + ret << "*"; + else + ret << index.intvalue; + ret << "]"; + } + return ret.str(); +} + +static std::string arrayIndexMessage(const Token* tok, + const std::vector& dimensions, + const std::vector& indexValues, + const Token* condition) +{ + auto add_dim = [](const std::string &s, const Dimension &dim) { + return s + "[" + std::to_string(dim.num) + "]"; + }; + const std::string array = std::accumulate(dimensions.cbegin(), dimensions.cend(), tok->astOperand1()->expressionString(), std::move(add_dim)); + + std::ostringstream errmsg; + if (condition) + errmsg << ValueFlow::eitherTheConditionIsRedundant(condition) + << " or the array '" << array << "' is accessed at index " << stringifyIndexes(tok->astOperand1()->expressionString(), indexValues) << ", which is out of bounds."; + else + errmsg << "Array '" << array << "' accessed at index " << stringifyIndexes(tok->astOperand1()->expressionString(), indexValues) << ", which is out of bounds."; + + return errmsg.str(); +} + +void CheckBufferOverrun::arrayIndexError(const Token* tok, + const std::vector& dimensions, + const std::vector& indexes) +{ + if (!tok) { + reportError(tok, Severity::error, "arrayIndexOutOfBounds", "Array 'arr[16]' accessed at index 16, which is out of bounds.", CWE_BUFFER_OVERRUN, Certainty::normal); + reportError(tok, Severity::warning, "arrayIndexOutOfBoundsCond", "Array 'arr[16]' accessed at index 16, which is out of bounds.", CWE_BUFFER_OVERRUN, Certainty::normal); + return; + } + + const Token *condition = nullptr; + const ValueFlow::Value *index = nullptr; + for (const ValueFlow::Value& indexValue : indexes) { + if (!indexValue.errorSeverity() && !mSettings->severity.isEnabled(Severity::warning)) + return; + if (indexValue.condition) + condition = indexValue.condition; + if (!index || !indexValue.errorPath.empty()) + index = &indexValue; + } + + reportError(getErrorPath(tok, index, "Array index out of bounds"), + index->errorSeverity() ? Severity::error : Severity::warning, + index->condition ? "arrayIndexOutOfBoundsCond" : "arrayIndexOutOfBounds", + arrayIndexMessage(tok, dimensions, indexes, condition), + CWE_BUFFER_OVERRUN, + index->isInconclusive() ? Certainty::inconclusive : Certainty::normal); +} + +void CheckBufferOverrun::negativeIndexError(const Token* tok, + const std::vector& dimensions, + const std::vector& indexes) +{ + if (!tok) { + reportError(tok, Severity::error, "negativeIndex", "Negative array index", CWE_BUFFER_UNDERRUN, Certainty::normal); + return; + } + + const Token *condition = nullptr; + const ValueFlow::Value *negativeValue = nullptr; + for (const ValueFlow::Value& indexValue : indexes) { + if (!indexValue.errorSeverity() && !mSettings->severity.isEnabled(Severity::warning)) + return; + if (indexValue.condition) + condition = indexValue.condition; + if (!negativeValue || !indexValue.errorPath.empty()) + negativeValue = &indexValue; + } + + reportError(getErrorPath(tok, negativeValue, "Negative array index"), + negativeValue->errorSeverity() ? Severity::error : Severity::warning, + "negativeIndex", + arrayIndexMessage(tok, dimensions, indexes, condition), + CWE_BUFFER_UNDERRUN, + negativeValue->isInconclusive() ? Certainty::inconclusive : Certainty::normal); +} + +//--------------------------------------------------------------------------- + +void CheckBufferOverrun::pointerArithmetic() +{ + if (!mSettings->severity.isEnabled(Severity::portability) && !mSettings->isPremiumEnabled("pointerOutOfBounds")) + return; + + logChecker("CheckBufferOverrun::pointerArithmetic"); // portability + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (!Token::Match(tok, "+|-")) + continue; + if (!tok->valueType() || tok->valueType()->pointer == 0) + continue; + if (!tok->isBinaryOp()) + continue; + if (!tok->astOperand1()->valueType() || !tok->astOperand2()->valueType()) + continue; + + const Token *arrayToken, *indexToken; + if (tok->astOperand1()->valueType()->pointer > 0) { + arrayToken = tok->astOperand1(); + indexToken = tok->astOperand2(); + } else { + arrayToken = tok->astOperand2(); + indexToken = tok->astOperand1(); + } + + if (!indexToken || !indexToken->valueType() || indexToken->valueType()->pointer > 0 || !indexToken->valueType()->isIntegral()) + continue; + + std::vector dimensions; + ErrorPath errorPath; + bool mightBeLarger = false; + MathLib::bigint path = 0; + if (!getDimensionsEtc(arrayToken, mSettings, dimensions, errorPath, mightBeLarger, path)) + continue; + + if (tok->str() == "+") { + // Positive index + if (!mightBeLarger) { // TODO check arrays with dim 1 also + const std::vector indexTokens{indexToken}; + const std::vector& indexValues = + getOverrunIndexValues(tok, arrayToken, dimensions, indexTokens, path); + if (!indexValues.empty()) + pointerArithmeticError(tok, indexToken, &indexValues.front()); + } + + if (const ValueFlow::Value *neg = indexToken->getValueLE(-1, mSettings)) + pointerArithmeticError(tok, indexToken, neg); + } else if (tok->str() == "-") { + if (arrayToken->variable() && arrayToken->variable()->isArgument()) + continue; + + const Token *array = arrayToken; + while (Token::Match(array, ".|::")) + array = array->astOperand2(); + if (array->variable() && array->variable()->isArray()) { + const ValueFlow::Value *v = indexToken->getValueGE(1, mSettings); + if (v) + pointerArithmeticError(tok, indexToken, v); + } + } + } +} + +void CheckBufferOverrun::pointerArithmeticError(const Token *tok, const Token *indexToken, const ValueFlow::Value *indexValue) +{ + if (!tok) { + reportError(tok, Severity::portability, "pointerOutOfBounds", "Pointer arithmetic overflow.", CWE_POINTER_ARITHMETIC_OVERFLOW, Certainty::normal); + reportError(tok, Severity::portability, "pointerOutOfBoundsCond", "Pointer arithmetic overflow.", CWE_POINTER_ARITHMETIC_OVERFLOW, Certainty::normal); + return; + } + + std::string errmsg; + if (indexValue->condition) + errmsg = "Undefined behaviour, when '" + indexToken->expressionString() + "' is " + std::to_string(indexValue->intvalue) + " the pointer arithmetic '" + tok->expressionString() + "' is out of bounds."; + else + errmsg = "Undefined behaviour, pointer arithmetic '" + tok->expressionString() + "' is out of bounds."; + + reportError(getErrorPath(tok, indexValue, "Pointer arithmetic overflow"), + Severity::portability, + indexValue->condition ? "pointerOutOfBoundsCond" : "pointerOutOfBounds", + errmsg, + CWE_POINTER_ARITHMETIC_OVERFLOW, + indexValue->isInconclusive() ? Certainty::inconclusive : Certainty::normal); +} + +//--------------------------------------------------------------------------- + +ValueFlow::Value CheckBufferOverrun::getBufferSize(const Token *bufTok) const +{ + if (!bufTok->valueType()) + return ValueFlow::Value(-1); + const Variable *var = bufTok->variable(); + + if (!var || var->dimensions().empty()) { + const ValueFlow::Value *value = getBufferSizeValue(bufTok); + if (value) + return *value; + } + + if (!var) + return ValueFlow::Value(-1); + + const MathLib::bigint dim = std::accumulate(var->dimensions().cbegin(), var->dimensions().cend(), 1LL, [](MathLib::bigint i1, const Dimension &dim) { + return i1 * dim.num; + }); + + ValueFlow::Value v; + v.setKnown(); + v.valueType = ValueFlow::Value::ValueType::BUFFER_SIZE; + + if (var->isPointerArray()) + v.intvalue = dim * mSettings->platform.sizeof_pointer; + else if (var->isPointer()) + return ValueFlow::Value(-1); + else { + const MathLib::bigint typeSize = bufTok->valueType()->typeSize(mSettings->platform); + v.intvalue = dim * typeSize; + } + + return v; +} +//--------------------------------------------------------------------------- + +static bool checkBufferSize(const Token *ftok, const Library::ArgumentChecks::MinSize &minsize, const std::vector &args, const MathLib::bigint bufferSize, const Settings *settings, const Tokenizer* tokenizer) +{ + const Token * const arg = (minsize.arg > 0 && minsize.arg - 1 < args.size()) ? args[minsize.arg - 1] : nullptr; + const Token * const arg2 = (minsize.arg2 > 0 && minsize.arg2 - 1 < args.size()) ? args[minsize.arg2 - 1] : nullptr; + + switch (minsize.type) { + case Library::ArgumentChecks::MinSize::Type::STRLEN: + if (settings->library.isargformatstr(ftok, minsize.arg)) { + return getMinFormatStringOutputLength(args, minsize.arg) < bufferSize; + } else if (arg) { + const Token *strtoken = arg->getValueTokenMaxStrLength(); + if (strtoken) + return Token::getStrLength(strtoken) < bufferSize; + } + break; + case Library::ArgumentChecks::MinSize::Type::ARGVALUE: { + if (arg && arg->hasKnownIntValue()) { + MathLib::bigint myMinsize = arg->getKnownIntValue(); + const unsigned int baseSize = tokenizer->sizeOfType(minsize.baseType); + if (baseSize != 0) + myMinsize *= baseSize; + return myMinsize <= bufferSize; + } + break; + } + case Library::ArgumentChecks::MinSize::Type::SIZEOF: + // TODO + break; + case Library::ArgumentChecks::MinSize::Type::MUL: + if (arg && arg2 && arg->hasKnownIntValue() && arg2->hasKnownIntValue()) + return (arg->getKnownIntValue() * arg2->getKnownIntValue()) <= bufferSize; + break; + case Library::ArgumentChecks::MinSize::Type::VALUE: { + MathLib::bigint myMinsize = minsize.value; + const unsigned int baseSize = tokenizer->sizeOfType(minsize.baseType); + if (baseSize != 0) + myMinsize *= baseSize; + return myMinsize <= bufferSize; + } + case Library::ArgumentChecks::MinSize::Type::NONE: + break; + } + return true; +} + + +void CheckBufferOverrun::bufferOverflow() +{ + logChecker("CheckBufferOverrun::bufferOverflow"); + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::Match(tok, "%name% (") || Token::simpleMatch(tok, ") {")) + continue; + if (!mSettings->library.hasminsize(tok)) + continue; + const std::vector args = getArguments(tok); + for (int argnr = 0; argnr < args.size(); ++argnr) { + if (!args[argnr]->valueType() || args[argnr]->valueType()->pointer == 0) + continue; + const std::vector *minsizes = mSettings->library.argminsizes(tok, argnr + 1); + if (!minsizes || minsizes->empty()) + continue; + // Get buffer size.. + const Token *argtok = args[argnr]; + while (argtok && argtok->isCast()) + argtok = argtok->astOperand2() ? argtok->astOperand2() : argtok->astOperand1(); + while (Token::Match(argtok, ".|::")) + argtok = argtok->astOperand2(); + if (!argtok || !argtok->variable()) + continue; + if (argtok->valueType() && argtok->valueType()->pointer == 0) + continue; + // TODO: strcpy(buf+10, "hello"); + const ValueFlow::Value bufferSize = getBufferSize(argtok); + if (bufferSize.intvalue <= 0) + continue; + // buffer size == 1 => do not warn for dynamic memory + if (bufferSize.intvalue == 1 && Token::simpleMatch(argtok->astParent(), ".")) { // TODO: check if parent was allocated dynamically + const Token *tok2 = argtok; + while (Token::simpleMatch(tok2->astParent(), ".")) + tok2 = tok2->astParent(); + while (Token::Match(tok2, "[|.")) + tok2 = tok2->astOperand1(); + const Variable *var = tok2 ? tok2->variable() : nullptr; + if (var) { + if (var->isPointer()) + continue; + if (var->isArgument() && var->isReference()) + continue; + } + } + const bool error = std::none_of(minsizes->begin(), minsizes->end(), [=](const Library::ArgumentChecks::MinSize &minsize) { + return checkBufferSize(tok, minsize, args, bufferSize.intvalue, mSettings, mTokenizer); + }); + if (error) + bufferOverflowError(args[argnr], &bufferSize, Certainty::normal); + } + } + } +} + +void CheckBufferOverrun::bufferOverflowError(const Token *tok, const ValueFlow::Value *value, Certainty certainty) +{ + reportError(getErrorPath(tok, value, "Buffer overrun"), Severity::error, "bufferAccessOutOfBounds", "Buffer is accessed out of bounds: " + (tok ? tok->expressionString() : "buf"), CWE_BUFFER_OVERRUN, certainty); +} + +//--------------------------------------------------------------------------- + +void CheckBufferOverrun::arrayIndexThenCheck() +{ + if (!mSettings->severity.isEnabled(Severity::portability)) + return; + + logChecker("CheckBufferOverrun::arrayIndexThenCheck"); + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * const scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { + if (Token::simpleMatch(tok, "sizeof (")) { + tok = tok->linkAt(1); + continue; + } + + if (Token::Match(tok, "%name% [ %var% ]")) { + tok = tok->next(); + + const int indexID = tok->next()->varId(); + const std::string& indexName(tok->strAt(1)); + + // Iterate AST upwards + const Token* tok2 = tok; + const Token* tok3 = tok2; + while (tok2->astParent() && tok2->tokType() != Token::eLogicalOp && tok2->str() != "?") { + tok3 = tok2; + tok2 = tok2->astParent(); + } + + // Ensure that we ended at a logical operator and that we came from its left side + if (tok2->tokType() != Token::eLogicalOp || tok2->astOperand1() != tok3) + continue; + + // check if array index is ok + // statement can be closed in parentheses, so "(| " is using + if (Token::Match(tok2, "&& (| %varid% <|<=", indexID)) + arrayIndexThenCheckError(tok, indexName); + else if (Token::Match(tok2, "&& (| %any% >|>= %varid% !!+", indexID)) + arrayIndexThenCheckError(tok, indexName); + } + } + } +} + +void CheckBufferOverrun::arrayIndexThenCheckError(const Token *tok, const std::string &indexName) +{ + reportError(tok, Severity::style, "arrayIndexThenCheck", + "$symbol:" + indexName + "\n" + "Array index '$symbol' is used before limits check.\n" + "Defensive programming: The variable '$symbol' is used as an array index before it " + "is checked that is within limits. This can mean that the array might be accessed out of bounds. " + "Reorder conditions such as '(a[i] && i < 10)' to '(i < 10 && a[i])'. That way the array will " + "not be accessed if the index is out of limits.", CWE_ARRAY_INDEX_THEN_CHECK, Certainty::normal); +} + +//--------------------------------------------------------------------------- + +void CheckBufferOverrun::stringNotZeroTerminated() +{ + // this is currently 'inconclusive'. See TestBufferOverrun::terminateStrncpy3 + if (!mSettings->severity.isEnabled(Severity::warning) || !mSettings->certainty.isEnabled(Certainty::inconclusive)) + return; + + logChecker("CheckBufferOverrun::stringNotZeroTerminated"); // warning,inconclusive + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * const scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::simpleMatch(tok, "strncpy (")) + continue; + const std::vector args = getArguments(tok); + if (args.size() != 3) + continue; + const Token *sizeToken = args[2]; + if (!sizeToken->hasKnownIntValue()) + continue; + const ValueFlow::Value &bufferSize = getBufferSize(args[0]); + if (bufferSize.intvalue < 0 || sizeToken->getKnownIntValue() < bufferSize.intvalue) + continue; + if (Token::simpleMatch(args[1], "(") && Token::simpleMatch(args[1]->astOperand1(), ". c_str") && args[1]->astOperand1()->astOperand1()) { + const std::list& contValues = args[1]->astOperand1()->astOperand1()->values(); + auto it = std::find_if(contValues.cbegin(), contValues.cend(), [](const ValueFlow::Value& value) { + return value.isContainerSizeValue() && !value.isImpossible(); + }); + if (it != contValues.end() && it->intvalue < sizeToken->getKnownIntValue()) + continue; + } else { + const Token* srcValue = args[1]->getValueTokenMaxStrLength(); + if (srcValue && Token::getStrLength(srcValue) < sizeToken->getKnownIntValue()) + continue; + } + // Is the buffer zero terminated after the call? + bool isZeroTerminated = false; + for (const Token *tok2 = tok->next()->link(); tok2 != scope->bodyEnd; tok2 = tok2->next()) { + if (!Token::simpleMatch(tok2, "] =")) + continue; + const Token *rhs = tok2->next()->astOperand2(); + if (!rhs || !rhs->hasKnownIntValue() || rhs->getKnownIntValue() != 0) + continue; + if (isSameExpression(false, args[0], tok2->link()->astOperand1(), mSettings->library, false, false)) + isZeroTerminated = true; + } + if (isZeroTerminated) + continue; + // TODO: Locate unsafe string usage.. + terminateStrncpyError(tok, args[0]->expressionString()); + } + } +} + +void CheckBufferOverrun::terminateStrncpyError(const Token *tok, const std::string &varname) +{ + const std::string shortMessage = "The buffer '$symbol' may not be null-terminated after the call to strncpy()."; + reportError(tok, Severity::warning, "terminateStrncpy", + "$symbol:" + varname + '\n' + + shortMessage + '\n' + + shortMessage + ' ' + + "If the source string's size fits or exceeds the given size, strncpy() does not add a " + "zero at the end of the buffer. This causes bugs later in the code if the code " + "assumes buffer is null-terminated.", CWE170, Certainty::inconclusive); +} +//--------------------------------------------------------------------------- + +void CheckBufferOverrun::argumentSize() +{ + // Check '%type% x[10]' arguments + if (!mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("argumentSize")) + return; + + logChecker("CheckBufferOverrun::argumentSize"); // warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * const scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (!tok->function() || !Token::Match(tok, "%name% (")) + continue; + + // If argument is '%type% a[num]' then check bounds against num + const Function *callfunc = tok->function(); + const std::vector callargs = getArguments(tok); + for (nonneg int paramIndex = 0; paramIndex < callargs.size() && paramIndex < callfunc->argCount(); ++paramIndex) { + const Variable* const argument = callfunc->getArgumentVar(paramIndex); + if (!argument || !argument->nameToken() || !argument->isArray()) + continue; + if (!argument->valueType() || !callargs[paramIndex]->valueType()) + continue; + if (argument->valueType()->type != callargs[paramIndex]->valueType()->type) + continue; + const Token * calldata = callargs[paramIndex]; + while (Token::Match(calldata, "::|.")) + calldata = calldata->astOperand2(); + if (!calldata->variable() || !calldata->variable()->isArray()) + continue; + if (calldata->variable()->dimensions().size() != argument->dimensions().size()) + continue; + bool err = false; + for (int d = 0; d < argument->dimensions().size(); ++d) { + const auto& dim1 = calldata->variable()->dimensions()[d]; + const auto& dim2 = argument->dimensions()[d]; + if (!dim1.known || !dim2.known) + break; + if (dim1.num < dim2.num) + err = true; + } + if (err) + argumentSizeError(tok, tok->str(), paramIndex, callargs[paramIndex]->expressionString(), calldata->variable(), argument); + } + } + } +} + +void CheckBufferOverrun::argumentSizeError(const Token *tok, const std::string &functionName, nonneg int paramIndex, const std::string ¶mExpression, const Variable *paramVar, const Variable *functionArg) +{ + const std::string strParamNum = std::to_string(paramIndex + 1) + getOrdinalText(paramIndex + 1); + ErrorPath errorPath; + errorPath.emplace_back(tok, "Function '" + functionName + "' is called"); + if (functionArg) + errorPath.emplace_back(functionArg->nameToken(), "Declaration of " + strParamNum + " function argument."); + if (paramVar) + errorPath.emplace_back(paramVar->nameToken(), "Passing buffer '" + paramVar->name() + "' to function that is declared here"); + errorPath.emplace_back(tok, ""); + + reportError(errorPath, Severity::warning, "argumentSize", + "$symbol:" + functionName + '\n' + + "Buffer '" + paramExpression + "' is too small, the function '" + functionName + "' expects a bigger buffer in " + strParamNum + " argument", CWE_ARGUMENT_SIZE, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// CTU.. +//--------------------------------------------------------------------------- + +// a Clang-built executable will crash when using the anonymous MyFileInfo later on - so put it in a unique namespace for now +// see https://trac.cppcheck.net/ticket/12108 for more details +#ifdef __clang__ +inline namespace CheckBufferOverrun_internal +#else +namespace +#endif +{ + /** data for multifile checking */ + class MyFileInfo : public Check::FileInfo { + public: + /** unsafe array index usage */ + std::list unsafeArrayIndex; + + /** unsafe pointer arithmetics */ + std::list unsafePointerArith; + + /** Convert data into xml string */ + std::string toString() const override + { + std::string xml; + if (!unsafeArrayIndex.empty()) + xml = " \n" + CTU::toString(unsafeArrayIndex) + " \n"; + if (!unsafePointerArith.empty()) + xml += " \n" + CTU::toString(unsafePointerArith) + " \n"; + return xml; + } + }; +} + +bool CheckBufferOverrun::isCtuUnsafeBufferUsage(const Settings &settings, const Token *argtok, MathLib::bigint *offset, int type) +{ + if (!offset) + return false; + if (!argtok->valueType() || argtok->valueType()->typeSize(settings.platform) == 0) + return false; + const Token *indexTok = nullptr; + if (type == 1 && Token::Match(argtok, "%name% [") && argtok->astParent() == argtok->next() && !Token::simpleMatch(argtok->linkAt(1), "] [")) + indexTok = argtok->next()->astOperand2(); + else if (type == 2 && Token::simpleMatch(argtok->astParent(), "+")) + indexTok = (argtok == argtok->astParent()->astOperand1()) ? + argtok->astParent()->astOperand2() : + argtok->astParent()->astOperand1(); + if (!indexTok) + return false; + if (!indexTok->hasKnownIntValue()) + return false; + *offset = indexTok->getKnownIntValue() * argtok->valueType()->typeSize(settings.platform); + return true; +} + +bool CheckBufferOverrun::isCtuUnsafeArrayIndex(const Settings &settings, const Token *argtok, MathLib::bigint *offset) +{ + return isCtuUnsafeBufferUsage(settings, argtok, offset, 1); +} + +bool CheckBufferOverrun::isCtuUnsafePointerArith(const Settings &settings, const Token *argtok, MathLib::bigint *offset) +{ + return isCtuUnsafeBufferUsage(settings, argtok, offset, 2); +} + +/** @brief Parse current TU and extract file info */ +Check::FileInfo *CheckBufferOverrun::getFileInfo(const Tokenizer &tokenizer, const Settings &settings) const +{ + const std::list &unsafeArrayIndex = CTU::getUnsafeUsage(tokenizer, settings, isCtuUnsafeArrayIndex); + const std::list &unsafePointerArith = CTU::getUnsafeUsage(tokenizer, settings, isCtuUnsafePointerArith); + if (unsafeArrayIndex.empty() && unsafePointerArith.empty()) { + return nullptr; + } + auto *fileInfo = new MyFileInfo; + fileInfo->unsafeArrayIndex = unsafeArrayIndex; + fileInfo->unsafePointerArith = unsafePointerArith; + return fileInfo; +} + +Check::FileInfo * CheckBufferOverrun::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const +{ + // cppcheck-suppress shadowFunction - TODO: fix this + const std::string arrayIndex("array-index"); + const std::string pointerArith("pointer-arith"); + + auto *fileInfo = new MyFileInfo; + for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (e->Name() == arrayIndex) + fileInfo->unsafeArrayIndex = CTU::loadUnsafeUsageListFromXml(e); + else if (e->Name() == pointerArith) + fileInfo->unsafePointerArith = CTU::loadUnsafeUsageListFromXml(e); + } + + if (fileInfo->unsafeArrayIndex.empty() && fileInfo->unsafePointerArith.empty()) { + delete fileInfo; + return nullptr; + } + + return fileInfo; +} + +/** @brief Analyse all file infos for all TU */ +bool CheckBufferOverrun::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) +{ + if (!ctu) + return false; + bool foundErrors = false; + (void)settings; // This argument is unused + + CheckBufferOverrun dummy(nullptr, &settings, &errorLogger); + dummy. + logChecker("CheckBufferOverrun::analyseWholeProgram"); + + const std::map> callsMap = ctu->getCallsMap(); + + for (const Check::FileInfo* fi1 : fileInfo) { + const MyFileInfo *fi = dynamic_cast(fi1); + if (!fi) + continue; + for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafeArrayIndex) + foundErrors |= analyseWholeProgram1(callsMap, unsafeUsage, 1, errorLogger); + for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafePointerArith) + foundErrors |= analyseWholeProgram1(callsMap, unsafeUsage, 2, errorLogger); + } + return foundErrors; +} + +bool CheckBufferOverrun::analyseWholeProgram1(const std::map> &callsMap, const CTU::FileInfo::UnsafeUsage &unsafeUsage, int type, ErrorLogger &errorLogger) +{ + const CTU::FileInfo::FunctionCall *functionCall = nullptr; + + const std::list &locationList = + CTU::FileInfo::getErrorPath(CTU::FileInfo::InvalidValueType::bufferOverflow, + unsafeUsage, + callsMap, + "Using argument ARG", + &functionCall, + false); + if (locationList.empty()) + return false; + + const char *errorId = nullptr; + std::string errmsg; + CWE cwe(0); + + if (type == 1) { + errorId = "ctuArrayIndex"; + if (unsafeUsage.value > 0) + errmsg = "Array index out of bounds; '" + unsafeUsage.myArgumentName + "' buffer size is " + std::to_string(functionCall->callArgValue) + " and it is accessed at offset " + std::to_string(unsafeUsage.value) + "."; + else + errmsg = "Array index out of bounds; buffer '" + unsafeUsage.myArgumentName + "' is accessed at offset " + std::to_string(unsafeUsage.value) + "."; + cwe = (unsafeUsage.value > 0) ? CWE_BUFFER_OVERRUN : CWE_BUFFER_UNDERRUN; + } else { + errorId = "ctuPointerArith"; + errmsg = "Pointer arithmetic overflow; '" + unsafeUsage.myArgumentName + "' buffer size is " + std::to_string(functionCall->callArgValue); + cwe = CWE_POINTER_ARITHMETIC_OVERFLOW; + } + + const ErrorMessage errorMessage(locationList, + emptyString, + Severity::error, + errmsg, + errorId, + cwe, Certainty::normal); + errorLogger.reportErr(errorMessage); + + return true; +} + +void CheckBufferOverrun::objectIndex() +{ + logChecker("CheckBufferOverrun::objectIndex"); + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *functionScope : symbolDatabase->functionScopes) { + for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { + if (!Token::simpleMatch(tok, "[")) + continue; + const Token *obj = tok->astOperand1(); + const Token *idx = tok->astOperand2(); + if (!idx || !obj) + continue; + if (idx->hasKnownIntValue()) { + if (idx->getKnownIntValue() == 0) + continue; + } + if (idx->hasKnownIntValue() && idx->getKnownIntValue() == 0) + continue; + + std::vector values = ValueFlow::getLifetimeObjValues(obj, false, -1); + for (const ValueFlow::Value& v:values) { + if (v.lifetimeKind != ValueFlow::Value::LifetimeKind::Address) + continue; + const Variable *var = v.tokvalue->variable(); + if (!var) + continue; + if (var->isReference()) + continue; + if (var->isRValueReference()) + continue; + if (var->isArray()) + continue; + if (var->isPointer()) { + if (!var->valueType()) + continue; + if (!obj->valueType()) + continue; + if (var->valueType()->pointer > obj->valueType()->pointer) + continue; + } + if (obj->valueType() && var->valueType() && (obj->isCast() || (obj->isCpp() && isCPPCast(obj)) || obj->valueType()->pointer)) { // allow cast to a different type + const auto varSize = var->valueType()->typeSize(mSettings->platform); + if (varSize == 0) + continue; + if (obj->valueType()->type != var->valueType()->type) { + if (ValueFlow::isOutOfBounds(makeSizeValue(varSize, v.path), idx).empty()) + continue; + } + } + if (v.path != 0) { + std::vector idxValues; + std::copy_if(idx->values().cbegin(), + idx->values().cend(), + std::back_inserter(idxValues), + [&](const ValueFlow::Value& vidx) { + if (!vidx.isIntValue()) + return false; + return vidx.path == v.path || vidx.path == 0; + }); + if (std::any_of(idxValues.cbegin(), idxValues.cend(), [&](const ValueFlow::Value& vidx) { + if (vidx.isImpossible()) + return (vidx.intvalue == 0); + return (vidx.intvalue != 0); + })) { + objectIndexError(tok, &v, idx->hasKnownIntValue()); + } + } else { + objectIndexError(tok, &v, idx->hasKnownIntValue()); + } + } + } + } +} + +void CheckBufferOverrun::objectIndexError(const Token *tok, const ValueFlow::Value *v, bool known) +{ + ErrorPath errorPath; + std::string name; + if (v) { + const Token* expr = v->tokvalue; + while (Token::simpleMatch(expr->astParent(), ".")) + expr = expr->astParent(); + name = expr->expressionString(); + errorPath = v->errorPath; + } + errorPath.emplace_back(tok, ""); + std::string verb = known ? "is" : "might be"; + reportError(errorPath, + known ? Severity::error : Severity::warning, + "objectIndex", + "The address of variable '" + name + "' " + verb + " accessed at non-zero index.", + CWE758, + Certainty::normal); +} + +static bool isVLAIndex(const Token* tok) +{ + if (!tok) + return false; + if (tok->varId() != 0U) + return true; + if (tok->str() == "?") { + // this is a VLA index if both expressions around the ":" is VLA index + return tok->astOperand2() && + tok->astOperand2()->str() == ":" && + isVLAIndex(tok->astOperand2()->astOperand1()) && + isVLAIndex(tok->astOperand2()->astOperand2()); + } + return isVLAIndex(tok->astOperand1()) || isVLAIndex(tok->astOperand2()); +} + +void CheckBufferOverrun::negativeArraySize() +{ + logChecker("CheckBufferOverrun::negativeArraySize"); + const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Variable* var : symbolDatabase->variableList()) { + if (!var || !var->isArray()) + continue; + const Token* const nameToken = var->nameToken(); + if (!Token::Match(nameToken, "%var% [") || !nameToken->next()->astOperand2()) + continue; + const ValueFlow::Value* sz = nameToken->next()->astOperand2()->getValueLE(-1, mSettings); + // don't warn about constant negative index because that is a compiler error + if (sz && isVLAIndex(nameToken->next()->astOperand2())) + negativeArraySizeError(nameToken); + } + + for (const Scope* functionScope : symbolDatabase->functionScopes) { + for (const Token* tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { + if (!tok->isKeyword() || tok->str() != "new" || !tok->astOperand1() || tok->astOperand1()->str() != "[") + continue; + const Token* valOperand = tok->astOperand1()->astOperand2(); + if (!valOperand) + continue; + const ValueFlow::Value* sz = valOperand->getValueLE(-1, mSettings); + if (sz) + negativeMemoryAllocationSizeError(tok, sz); + } + } +} + +void CheckBufferOverrun::negativeArraySizeError(const Token* tok) +{ + const std::string arrayName = tok ? tok->expressionString() : std::string(); + const std::string line1 = arrayName.empty() ? std::string() : ("$symbol:" + arrayName + '\n'); + reportError(tok, Severity::error, "negativeArraySize", + line1 + + "Declaration of array '" + arrayName + "' with negative size is undefined behaviour", CWE758, Certainty::normal); +} + +void CheckBufferOverrun::negativeMemoryAllocationSizeError(const Token* tok, const ValueFlow::Value* value) +{ + const std::string msg = "Memory allocation size is negative."; + const ErrorPath errorPath = getErrorPath(tok, value, msg); + const bool inconclusive = value != nullptr && !value->isKnown(); + reportError(errorPath, inconclusive ? Severity::warning : Severity::error, "negativeMemoryAllocationSize", + msg, CWE131, inconclusive ? Certainty::inconclusive : Certainty::normal); +} diff --git a/cppcheck-2.14.0/lib/checkbufferoverrun.h b/cppcheck-2.14.0/lib/checkbufferoverrun.h new file mode 100644 index 00000000..6b99021f --- /dev/null +++ b/cppcheck-2.14.0/lib/checkbufferoverrun.h @@ -0,0 +1,159 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checkbufferoverrunH +#define checkbufferoverrunH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "ctu.h" +#include "errortypes.h" +#include "mathlib.h" +#include "symboldatabase.h" +#include "tokenize.h" +#include "vfvalue.h" + +#include +#include +#include +#include + +namespace tinyxml2 { + class XMLElement; +} + +class ErrorLogger; +class Settings; +class Token; + +/// @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()) {} + +private: + /** This constructor is used when running checks. */ + CheckBufferOverrun(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger) {} + + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + CheckBufferOverrun checkBufferOverrun(&tokenizer, &tokenizer.getSettings(), errorLogger); + checkBufferOverrun.arrayIndex(); + checkBufferOverrun.pointerArithmetic(); + checkBufferOverrun.bufferOverflow(); + checkBufferOverrun.arrayIndexThenCheck(); + checkBufferOverrun.stringNotZeroTerminated(); + checkBufferOverrun.objectIndex(); + checkBufferOverrun.argumentSize(); + checkBufferOverrun.negativeArraySize(); + } + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckBufferOverrun c(nullptr, settings, errorLogger); + c.arrayIndexError(nullptr, std::vector(), std::vector()); + c.pointerArithmeticError(nullptr, nullptr, nullptr); + c.negativeIndexError(nullptr, std::vector(), std::vector()); + c.arrayIndexThenCheckError(nullptr, "i"); + c.bufferOverflowError(nullptr, nullptr, Certainty::normal); + c.objectIndexError(nullptr, nullptr, true); + c.argumentSizeError(nullptr, "function", 1, "buffer", nullptr, nullptr); + c.negativeMemoryAllocationSizeError(nullptr, nullptr); + c.negativeArraySizeError(nullptr); + } + + /** @brief Parse current TU and extract file info */ + Check::FileInfo *getFileInfo(const Tokenizer &tokenizer, const Settings &settings) const override; + + /** @brief Analyse all file infos for all TU */ + bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) override; + + void arrayIndex(); + void arrayIndexError(const Token* tok, + const std::vector& dimensions, + const std::vector& indexes); + void negativeIndexError(const Token* tok, + const std::vector& dimensions, + const std::vector& indexes); + + void pointerArithmetic(); + void pointerArithmeticError(const Token *tok, const Token *indexToken, const ValueFlow::Value *indexValue); + + void bufferOverflow(); + void bufferOverflowError(const Token *tok, const ValueFlow::Value *value, Certainty certainty); + + void arrayIndexThenCheck(); + void arrayIndexThenCheckError(const Token *tok, const std::string &indexName); + + void stringNotZeroTerminated(); + void terminateStrncpyError(const Token *tok, const std::string &varname); + + void argumentSize(); + void argumentSizeError(const Token *tok, const std::string &functionName, nonneg int paramIndex, const std::string ¶mExpression, const Variable *paramVar, const Variable *functionArg); + + void negativeArraySize(); + void negativeArraySizeError(const Token* tok); + void negativeMemoryAllocationSizeError(const Token* tok, const ValueFlow::Value* value); // provide a negative value to memory allocation function + + void objectIndex(); + void objectIndexError(const Token *tok, const ValueFlow::Value *v, bool known); + + ValueFlow::Value getBufferSize(const Token *bufTok) const; + + // CTU + static bool isCtuUnsafeBufferUsage(const Settings &settings, const Token *argtok, MathLib::bigint *offset, int type); + static bool isCtuUnsafeArrayIndex(const Settings &settings, const Token *argtok, MathLib::bigint *offset); + static bool isCtuUnsafePointerArith(const Settings &settings, const Token *argtok, MathLib::bigint *offset); + + Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const override; + static bool analyseWholeProgram1(const std::map> &callsMap, const CTU::FileInfo::UnsafeUsage &unsafeUsage, int type, ErrorLogger &errorLogger); + + + static std::string myName() { + return "Bounds checking"; + } + + std::string classInfo() const override { + return "Out of bounds checking:\n" + "- Array index out of bounds\n" + "- Pointer arithmetic overflow\n" + "- Buffer overflow\n" + "- Dangerous usage of strncat()\n" + "- Using array index before checking it\n" + "- Partial string write that leads to buffer that is not zero terminated.\n" + "- Check for large enough arrays being passed to functions\n" + "- Allocating memory with a negative size\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkbufferoverrunH diff --git a/cppcheck-2.14.0/lib/checkclass.cpp b/cppcheck-2.14.0/lib/checkclass.cpp new file mode 100644 index 00000000..01aee5b2 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkclass.cpp @@ -0,0 +1,3681 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#include "checkclass.h" + +#include "astutils.h" +#include "library.h" +#include "settings.h" +#include "standards.h" +#include "symboldatabase.h" +#include "errorlogger.h" +#include "errortypes.h" +#include "platform.h" +#include "token.h" +#include "tokenize.h" +#include "tokenlist.h" +#include "utils.h" +#include "valueflow.h" + +#include +#include +#include +#include +#include +#include + +#include "xml.h" + +namespace CTU { + class FileInfo; +} + +//--------------------------------------------------------------------------- + +// Register CheckClass.. +namespace { + CheckClass instance; +} + +static const CWE CWE398(398U); // Indicator of Poor Code Quality +static const CWE CWE404(404U); // Improper Resource Shutdown or Release +static const CWE CWE665(665U); // Improper Initialization +static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior +static const CWE CWE762(762U); // Mismatched Memory Management Routines + +static const CWE CWE_ONE_DEFINITION_RULE(758U); + +static const char * getFunctionTypeName(Function::Type type) +{ + switch (type) { + case Function::eConstructor: + return "constructor"; + case Function::eCopyConstructor: + return "copy constructor"; + case Function::eMoveConstructor: + return "move constructor"; + case Function::eDestructor: + return "destructor"; + case Function::eFunction: + return "function"; + case Function::eOperatorEqual: + return "operator="; + case Function::eLambda: + return "lambda"; + } + return ""; +} + +static bool isVariableCopyNeeded(const Variable &var, Function::Type type) +{ + bool isOpEqual = false; + switch (type) { + case Function::eOperatorEqual: + isOpEqual = true; + break; + case Function::eCopyConstructor: + case Function::eMoveConstructor: + break; + default: + return true; + } + + return (!var.hasDefault() || isOpEqual) && // default init does not matter for operator= + (var.isPointer() || + (var.type() && var.type()->needInitialization == Type::NeedInitialization::True) || + (var.valueType() && var.valueType()->type >= ValueType::Type::CHAR)); +} + +static bool isVclTypeInit(const Type *type) +{ + if (!type) + return false; + return std::any_of(type->derivedFrom.begin(), type->derivedFrom.end(), [&](const Type::BaseInfo& baseInfo) { + if (!baseInfo.type) + return true; + if (isVclTypeInit(baseInfo.type)) + return true; + return false; + }); +} +//--------------------------------------------------------------------------- + +CheckClass::CheckClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger), + mSymbolDatabase(tokenizer?tokenizer->getSymbolDatabase():nullptr) +{} + +//--------------------------------------------------------------------------- +// ClassCheck: Check that all class constructors are ok. +//--------------------------------------------------------------------------- + +void CheckClass::constructors() +{ + const bool printStyle = mSettings->severity.isEnabled(Severity::style); + const bool printWarnings = mSettings->severity.isEnabled(Severity::warning); + if (!printStyle && !printWarnings && !mSettings->isPremiumEnabled("uninitMemberVar")) + return; + + logChecker("CheckClass::checkConstructors"); // style,warning + + const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); + for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { + if (mSettings->hasLib("vcl") && isVclTypeInit(scope->definedType)) + continue; + + const bool unusedTemplate = Token::simpleMatch(scope->classDef->previous(), ">"); + + const bool usedInUnion = std::any_of(mSymbolDatabase->scopeList.cbegin(), mSymbolDatabase->scopeList.cend(), [&](const Scope& unionScope) { + if (unionScope.type != Scope::eUnion) + return false; + return std::any_of(unionScope.varlist.cbegin(), unionScope.varlist.cend(), [&](const Variable& var) { + return var.type() && var.type()->classScope == scope; + }); + }); + + // There are no constructors. + if (scope->numConstructors == 0 && printStyle && !usedInUnion) { + // If there is a private variable, there should be a constructor.. + int needInit = 0, haveInit = 0; + std::vector uninitVars; + for (const Variable &var : scope->varlist) { + if (var.isPrivate() && !var.isStatic() && + (!var.isClass() || (var.type() && var.type()->needInitialization == Type::NeedInitialization::True))) { + ++needInit; + if (!var.isInit() && !var.hasDefault() && var.nameToken()->scope() == scope) // don't warn for anonymous union members + uninitVars.emplace_back(&var); + else + ++haveInit; + } + } + if (needInit > haveInit) { + if (haveInit == 0) + noConstructorError(scope->classDef, scope->className, scope->classDef->str() == "struct"); + else + for (const Variable* uv : uninitVars) + uninitVarError(uv->typeStartToken(), uv->scope()->className, uv->name()); + } + } + + if (!printWarnings) + continue; + + // #3196 => bailout if there are nested unions + // TODO: handle union variables better + { + const bool bailout = std::any_of(scope->nestedList.cbegin(), scope->nestedList.cend(), [](const Scope* nestedScope) { + return nestedScope->type == Scope::eUnion; + }); + if (bailout) + continue; + } + + + std::vector usageList = createUsageList(scope); + + for (const Function &func : scope->functionList) { + if (!(func.isConstructor() && (func.hasBody() || (func.isDefault() && func.type == Function::eConstructor))) && + !(func.type == Function::eOperatorEqual && func.hasBody())) + continue; // a defaulted constructor does not initialize primitive members + + // Bail: If initializer list is not recognized as a variable or type then skip since parsing is incomplete + if (unusedTemplate && func.type == Function::eConstructor) { + const Token *initList = func.constructorMemberInitialization(); + if (Token::Match(initList, ": %name% (") && initList->next()->tokType() == Token::eName) + break; + } + + // Mark all variables not used + clearAllVar(usageList); + + // Variables with default initializers + for (Usage &usage : usageList) { + const Variable& var = *usage.var; + + // check for C++11 initializer + if (var.hasDefault() && func.type != Function::eOperatorEqual && func.type != Function::eCopyConstructor) { // variable still needs to be copied + usage.init = true; + } + } + + std::list callstack; + initializeVarList(func, callstack, scope, usageList); + + // Assign 1 union member => assign all union members + for (const Usage &usage : usageList) { + const Variable& var = *usage.var; + if (!usage.assign && !usage.init) + continue; + const Scope* varScope1 = var.nameToken()->scope(); + while (varScope1->type == Scope::ScopeType::eStruct) + varScope1 = varScope1->nestedIn; + if (varScope1->type == Scope::ScopeType::eUnion) { + for (Usage &usage2 : usageList) { + const Variable& var2 = *usage2.var; + if (usage2.assign || usage2.init || var2.isStatic()) + continue; + const Scope* varScope2 = var2.nameToken()->scope(); + while (varScope2->type == Scope::ScopeType::eStruct) + varScope2 = varScope2->nestedIn; + if (varScope1 == varScope2) + usage2.assign = true; + } + } + } + + // Check if any variables are uninitialized + for (const Usage &usage : usageList) { + const Variable& var = *usage.var; + + if (usage.assign || usage.init || var.isStatic()) + continue; + + if (var.valueType() && var.valueType()->pointer == 0 && var.type() && var.type()->needInitialization == Type::NeedInitialization::False && var.type()->derivedFrom.empty()) + continue; + + if (var.isConst() && func.isOperator()) // We can't set const members in assignment operator + continue; + + // Check if this is a class constructor + if (!var.isPointer() && !var.isPointerArray() && var.isClass() && func.type == Function::eConstructor) { + // Unknown type so assume it is initialized + if (!var.type()) { + if (var.isStlType() && var.valueType() && var.valueType()->containerTypeToken && var.getTypeName() == "std::array") { + const Token* ctt = var.valueType()->containerTypeToken; + if (!ctt->isStandardType() && + (!ctt->type() || ctt->type()->needInitialization != Type::NeedInitialization::True) && + !mSettings->library.podtype(ctt->str())) // TODO: handle complex type expression + continue; + } + else + continue; + } + + // Known type that doesn't need initialization or + // known type that has member variables of an unknown type + else if (var.type()->needInitialization != Type::NeedInitialization::True) + continue; + } + + // Check if type can't be copied + if (!var.isPointer() && !var.isPointerArray() && var.typeScope()) { + if (func.type == Function::eMoveConstructor) { + if (canNotMove(var.typeScope())) + continue; + } else { + if (canNotCopy(var.typeScope())) + continue; + } + } + + // Is there missing member copy in copy/move constructor or assignment operator? + bool missingCopy = false; + + // Don't warn about unknown types in copy constructors since we + // don't know if they can be copied or not.. + if (!isVariableCopyNeeded(var, func.type)) { + if (!printInconclusive) + continue; + + missingCopy = true; + } + + // 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 && mSettings->library.getTypeCheck("operatorEqVarError", var.getTypeName()) != Library::TypeCheck::suppress) + operatorEqVarError(func.token, scope->className, var.name(), missingCopy); + } else if (func.access != AccessControl::Private || mSettings->standards.cpp >= Standards::CPP11) { + // If constructor is not in scope then we maybe using a constructor from a different template specialization + if (!precedes(scope->bodyStart, func.tokenDef)) + continue; + const Scope *varType = var.typeScope(); + if (!varType || varType->type != Scope::eUnion) { + const bool derived = scope != var.scope(); + if (func.type == Function::eConstructor && + func.nestedIn && (func.nestedIn->numConstructors - func.nestedIn->numCopyOrMoveConstructors) > 1 && + func.argCount() == 0 && func.functionScope && + func.arg && func.arg->link()->next() == func.functionScope->bodyStart && + func.functionScope->bodyStart->link() == func.functionScope->bodyStart->next()) { + // don't warn about user defined default constructor when there are other constructors + if (printInconclusive) + uninitVarError(func.token, func.access == AccessControl::Private, func.type, var.scope()->className, var.name(), derived, true); + } else if (missingCopy) + missingMemberCopyError(func.token, func.type, var.scope()->className, var.name()); + else + uninitVarError(func.token, func.access == AccessControl::Private, func.type, var.scope()->className, var.name(), derived, false); + } + } + } + } + } +} + +void CheckClass::checkExplicitConstructors() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("noExplicitConstructor")) + return; + + logChecker("CheckClass::checkExplicitConstructors"); // style + + for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { + // Do not perform check, if the class/struct has not any constructors + if (scope->numConstructors == 0) + continue; + + // Is class abstract? Maybe this test is over-simplification, but it will suffice for simple cases, + // and it will avoid false positives. + const bool isAbstractClass = std::any_of(scope->functionList.cbegin(), scope->functionList.cend(), [](const Function& func) { + return func.isPure(); + }); + + // Abstract classes can't be instantiated. But if there is C++11 + // "misuse" by derived classes then these constructors must be explicit. + if (isAbstractClass && mSettings->standards.cpp >= Standards::CPP11) + continue; + + for (const Function &func : scope->functionList) { + + // We are looking for constructors, which are meeting following criteria: + // 1) Constructor is declared with a single parameter + // 2) Constructor is not declared as explicit + // 3) It is not a copy/move constructor of non-abstract class + // 4) Constructor is not marked as delete (programmer can mark the default constructor as deleted, which is ok) + if (!func.isConstructor() || func.isDelete() || (!func.hasBody() && func.access == AccessControl::Private)) + continue; + + if (!func.isExplicit() && + func.argCount() > 0 && func.minArgCount() < 2 && + func.type != Function::eCopyConstructor && + func.type != Function::eMoveConstructor && + !(func.templateDef && Token::simpleMatch(func.argumentList.front().typeEndToken(), "...")) && + func.argumentList.front().getTypeName() != "std::initializer_list") { + noExplicitConstructorError(func.tokenDef, scope->className, scope->type == Scope::eStruct); + } + } + } +} + +static bool hasNonCopyableBase(const Scope *scope, bool *unknown) +{ + // check if there is base class that is not copyable + for (const Type::BaseInfo &baseInfo : scope->definedType->derivedFrom) { + if (!baseInfo.type || !baseInfo.type->classScope) { + *unknown = true; + continue; + } + + if (hasNonCopyableBase(baseInfo.type->classScope, unknown)) + return true; + + for (const Function &func : baseInfo.type->classScope->functionList) { + if (func.type != Function::eCopyConstructor) + continue; + if (func.access == AccessControl::Private || func.isDelete()) { + *unknown = false; + return true; + } + } + } + return false; +} + +void CheckClass::copyconstructors() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckClass::checkCopyConstructors"); // warning + + for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { + std::map allocatedVars; + + for (const Function &func : scope->functionList) { + if (func.type != Function::eConstructor || !func.functionScope) + continue; + const Token* tok = func.token->linkAt(1); + for (const Token* const end = func.functionScope->bodyStart; tok != end; tok = tok->next()) { + if (Token::Match(tok, "%var% ( new") || + (Token::Match(tok, "%var% ( %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)))) { + const Variable* var = tok->variable(); + if (var && var->isPointer() && var->scope() == scope) + allocatedVars[tok->varId()] = tok; + } + } + for (const Token* const end = func.functionScope->bodyEnd; tok != end; tok = tok->next()) { + if (Token::Match(tok, "%var% = new") || + (Token::Match(tok, "%var% = %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)))) { + const Variable* var = tok->variable(); + if (var && var->isPointer() && var->scope() == scope && !var->isStatic()) + allocatedVars[tok->varId()] = tok; + } + } + } + + if (!allocatedVars.empty()) { + const Function *funcCopyCtor = nullptr; + const Function *funcOperatorEq = nullptr; + const Function *funcDestructor = nullptr; + for (const Function &func : scope->functionList) { + if (func.type == Function::eCopyConstructor) + funcCopyCtor = &func; + else if (func.type == Function::eOperatorEqual) + funcOperatorEq = &func; + else if (func.type == Function::eDestructor) + funcDestructor = &func; + } + if (!funcCopyCtor || funcCopyCtor->isDefault()) { + bool unknown = false; + if (!hasNonCopyableBase(scope, &unknown) && !unknown) + noCopyConstructorError(scope, funcCopyCtor, allocatedVars.cbegin()->second, unknown); + } + if (!funcOperatorEq || funcOperatorEq->isDefault()) { + bool unknown = false; + if (!hasNonCopyableBase(scope, &unknown) && !unknown) + noOperatorEqError(scope, funcOperatorEq, allocatedVars.cbegin()->second, unknown); + } + if (!funcDestructor || funcDestructor->isDefault()) { + const Token * mustDealloc = nullptr; + for (std::map::const_iterator it = allocatedVars.cbegin(); it != allocatedVars.cend(); ++it) { + if (!Token::Match(it->second, "%var% [(=] new %type%")) { + mustDealloc = it->second; + break; + } + if (it->second->valueType() && it->second->valueType()->isIntegral()) { + mustDealloc = it->second; + break; + } + const Variable *var = it->second->variable(); + if (var && var->typeScope() && var->typeScope()->functionList.empty() && var->type()->derivedFrom.empty()) { + mustDealloc = it->second; + break; + } + } + if (mustDealloc) + noDestructorError(scope, funcDestructor, mustDealloc); + } + } + + std::set copiedVars; + const Token* copyCtor = nullptr; + for (const Function &func : scope->functionList) { + if (func.type != Function::eCopyConstructor) + continue; + copyCtor = func.tokenDef; + if (!func.functionScope) { + allocatedVars.clear(); + break; + } + const Token* tok = func.tokenDef->linkAt(1)->next(); + if (tok->str()==":") { + tok=tok->next(); + while (Token::Match(tok, "%name% (")) { + if (allocatedVars.find(tok->varId()) != allocatedVars.end()) { + if (tok->varId() && Token::Match(tok->tokAt(2), "%name% . %name% )")) + copiedVars.insert(tok); + else if (!Token::Match(tok->tokAt(2), "%any% )")) + allocatedVars.erase(tok->varId()); // Assume memory is allocated + } + tok = tok->linkAt(1)->tokAt(2); + } + } + for (tok = func.functionScope->bodyStart; tok != func.functionScope->bodyEnd; tok = tok->next()) { + if ((tok->isCpp() && Token::Match(tok, "%var% = new")) || + (Token::Match(tok, "%var% = %name% (") && (mSettings->library.getAllocFuncInfo(tok->tokAt(2)) || mSettings->library.getReallocFuncInfo(tok->tokAt(2))))) { + allocatedVars.erase(tok->varId()); + } else if (Token::Match(tok, "%var% = %name% . %name% ;") && allocatedVars.find(tok->varId()) != allocatedVars.end()) { + copiedVars.insert(tok); + } + } + break; + } + if (copyCtor && !copiedVars.empty()) { + for (const Token *cv : copiedVars) + copyConstructorShallowCopyError(cv, cv->str()); + // throw error if count mismatch + /* FIXME: This doesn't work. See #4154 + for (std::map::const_iterator i = allocatedVars.begin(); i != allocatedVars.end(); ++i) { + copyConstructorMallocError(copyCtor, i->second, i->second->str()); + } + */ + } + } +} + +/* This doesn't work. See #4154 + void CheckClass::copyConstructorMallocError(const Token *cctor, const Token *alloc, const std::string& varname) + { + std::list callstack; + callstack.push_back(cctor); + callstack.push_back(alloc); + reportError(callstack, Severity::warning, "copyCtorNoAllocation", "Copy constructor does not allocate memory for member '" + varname + "' although memory has been allocated in other constructors."); + } + */ + +void CheckClass::copyConstructorShallowCopyError(const Token *tok, const std::string& varname) +{ + reportError(tok, Severity::warning, "copyCtorPointerCopying", + "$symbol:" + varname + "\nValue of pointer '$symbol', which points to allocated memory, is copied in copy constructor instead of allocating new memory.", CWE398, Certainty::normal); +} + +static std::string noMemberErrorMessage(const Scope *scope, const char function[], bool isdefault) +{ + const std::string &classname = scope ? scope->className : "class"; + const std::string type = (scope && scope->type == Scope::eStruct) ? "Struct" : "Class"; + const bool isDestructor = (function[0] == 'd'); + std::string errmsg = "$symbol:" + classname + '\n'; + + if (isdefault) { + errmsg += type + " '$symbol' has dynamic memory/resource allocation(s). The " + function + " is explicitly defaulted but the default " + function + " does not work well."; + if (isDestructor) + errmsg += " It is recommended to define the " + std::string(function) + '.'; + else + errmsg += " It is recommended to define or delete the " + std::string(function) + '.'; + } else { + errmsg += type + " '$symbol' does not have a " + function + " which is recommended since it has dynamic memory/resource allocation(s)."; + } + + return errmsg; +} + +void CheckClass::noCopyConstructorError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive) +{ + reportError(alloc, Severity::warning, "noCopyConstructor", noMemberErrorMessage(scope, "copy constructor", isdefault), CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckClass::noOperatorEqError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive) +{ + reportError(alloc, Severity::warning, "noOperatorEq", noMemberErrorMessage(scope, "operator=", isdefault), CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckClass::noDestructorError(const Scope *scope, bool isdefault, const Token *alloc) +{ + reportError(alloc, Severity::warning, "noDestructor", noMemberErrorMessage(scope, "destructor", isdefault), CWE398, Certainty::normal); +} + +bool CheckClass::canNotCopy(const Scope *scope) +{ + bool constructor = false; + bool publicAssign = false; + bool publicCopy = false; + + for (const Function &func : scope->functionList) { + if (func.isConstructor()) + constructor = true; + if (func.access != AccessControl::Public) + continue; + if (func.type == Function::eCopyConstructor) { + publicCopy = true; + break; + } + if (func.type == Function::eOperatorEqual) { + publicAssign = true; + break; + } + } + + return constructor && !(publicAssign || publicCopy); +} + +bool CheckClass::canNotMove(const Scope *scope) +{ + bool constructor = false; + bool publicAssign = false; + bool publicCopy = false; + bool publicMove = false; + + for (const Function &func : scope->functionList) { + if (func.isConstructor()) + constructor = true; + if (func.access != AccessControl::Public) + continue; + if (func.type == Function::eCopyConstructor) { + publicCopy = true; + break; + } + if (func.type == Function::eMoveConstructor) { + publicMove = true; + break; + } + if (func.type == Function::eOperatorEqual) { + publicAssign = true; + break; + } + } + + return constructor && !(publicAssign || publicCopy || publicMove); +} + +static void getAllVariableMembers(const Scope *scope, std::vector& varList) +{ + std::transform(scope->varlist.cbegin(), scope->varlist.cend(), std::back_inserter(varList), [](const Variable& var) { + return &var; + }); + if (scope->definedType) { + for (const Type::BaseInfo& baseInfo: scope->definedType->derivedFrom) { + if (scope->definedType == baseInfo.type) + continue; + const Scope *baseClass = baseInfo.type ? baseInfo.type->classScope : nullptr; + if (baseClass && baseClass->isClassOrStruct() && baseClass->numConstructors == 0) + getAllVariableMembers(baseClass, varList); + } + } +} + +std::vector CheckClass::createUsageList(const Scope *scope) +{ + std::vector ret; + std::vector varlist; + getAllVariableMembers(scope, varlist); + ret.reserve(varlist.size()); + std::transform(varlist.cbegin(), varlist.cend(), std::back_inserter(ret), [](const Variable* var) { + return Usage(var); + }); + return ret; +} + +void CheckClass::assignVar(std::vector &usageList, nonneg int varid) +{ + auto it = std::find_if(usageList.begin(), usageList.end(), [varid](const Usage& usage) { + return usage.var->declarationId() == varid; + }); + if (it != usageList.end()) + it->assign = true; +} + +void CheckClass::assignVar(std::vector &usageList, const Token* vartok) +{ + if (vartok->varId() > 0) { + assignVar(usageList, vartok->varId()); + return; + } + auto it = std::find_if(usageList.begin(), usageList.end(), [vartok](const Usage& usage) { + // FIXME: This is a workaround when varid is not set for a derived member + return usage.var->name() == vartok->str(); + }); + if (it != usageList.end()) + it->assign = true; +} + +void CheckClass::initVar(std::vector &usageList, nonneg int varid) +{ + auto it = std::find_if(usageList.begin(), usageList.end(), [varid](const Usage& usage) { + return usage.var->declarationId() == varid; + }); + if (it != usageList.end()) + it->init = true; +} + +void CheckClass::assignAllVar(std::vector &usageList) +{ + for (Usage & i : usageList) + i.assign = true; +} + +void CheckClass::assignAllVarsVisibleFromScope(std::vector& usageList, const Scope* scope) +{ + for (Usage& usage : usageList) { + if (usage.var->scope() == scope) + usage.assign = true; + } + + // Iterate through each base class... + for (const Type::BaseInfo& i : scope->definedType->derivedFrom) { + const Type *derivedFrom = i.type; + + if (derivedFrom && derivedFrom->classScope) + assignAllVarsVisibleFromScope(usageList, derivedFrom->classScope); + } +} + +void CheckClass::clearAllVar(std::vector &usageList) +{ + for (Usage & i : usageList) { + i.assign = false; + i.init = false; + } +} + +bool CheckClass::isBaseClassMutableMemberFunc(const Token *tok, const Scope *scope) +{ + // Iterate through each base class... + for (const Type::BaseInfo & i : scope->definedType->derivedFrom) { + const Type *derivedFrom = i.type; + + // Check if base class exists in database + if (derivedFrom && derivedFrom->classScope) { + const std::list& functionList = derivedFrom->classScope->functionList; + + if (std::any_of(functionList.cbegin(), functionList.cend(), [&](const Function& func) { + return func.tokenDef->str() == tok->str() && !func.isStatic() && !func.isConst(); + })) + return true; + + if (isBaseClassMutableMemberFunc(tok, derivedFrom->classScope)) + 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) const +{ + if (!func.functionScope) + return; + + bool initList = func.isConstructor(); + const Token *ftok = func.arg->link()->next(); + int level = 0; + for (; ftok && ftok != func.functionScope->bodyEnd; ftok = ftok->next()) { + // Class constructor.. initializing variables like this + // clKalle::clKalle() : var(value) { } + if (initList) { + if (level == 0 && Token::Match(ftok, "%name% {|(") && Token::Match(ftok->linkAt(1), "}|) ,|{")) { + if (ftok->str() != func.name()) { + if (ftok->varId()) + initVar(usage, ftok->varId()); + else { // base class constructor + for (Usage& u : usage) { + if (u.var->scope() != scope) // assume that all variables are initialized in base class + u.init = true; + } + } + } else { // c++11 delegate constructor + const Function *member = ftok->function(); + // member function not found => assume it initializes all members + if (!member) { + assignAllVar(usage); + return; + } + + // recursive call + // assume that all variables are initialized + if (std::find(callstack.cbegin(), callstack.cend(), member) != callstack.cend()) { + /** @todo false negative: just bail */ + assignAllVar(usage); + return; + } + + // member function has implementation + if (member->hasBody()) { + // initialize variable use list using member function + callstack.push_back(member); + initializeVarList(*member, callstack, scope, usage); + callstack.pop_back(); + } + + // there is a called member function, but it has no implementation, so we assume it initializes everything + else { + assignAllVar(usage); + } + } + } else if (level != 0 && Token::Match(ftok, "%name% =")) // assignment in the initializer: var(value = x) + assignVar(usage, ftok->varId()); + + // Level handling + if (ftok->link() && Token::Match(ftok, "(|<")) + level++; + else if (ftok->str() == "{") { + if (level != 0 || + (Token::Match(ftok->previous(), "%name%|>") && Token::Match(ftok->link(), "} ,|{"))) + level++; + else + initList = false; + } else if (ftok->link() && Token::Match(ftok, ")|>|}")) + level--; + } + + if (initList) + continue; + + // Variable getting value from stream? + if (Token::Match(ftok, ">>|& %name%") && isLikelyStreamRead(ftok)) { + assignVar(usage, ftok->next()->varId()); + } + + // If assignment comes after an && or || this is really inconclusive because of short circuiting + if (Token::Match(ftok, "%oror%|&&")) + continue; + + if (Token::simpleMatch(ftok, "( !")) + ftok = ftok->next(); + + // Using the operator= function to initialize all variables.. + if (Token::Match(ftok->next(), "return| (| * this )| =")) { + assignAllVar(usage); + break; + } + + // Using swap to assign all variables.. + if (func.type == Function::eOperatorEqual && Token::Match(ftok, "[;{}] %name% (") && Token::Match(ftok->linkAt(2), ") . %name% ( *| this ) ;")) { + assignAllVar(usage); + break; + } + + // Calling member variable function? + if (Token::Match(ftok->next(), "%var% . %name% (") && !(ftok->next()->valueType() && ftok->next()->valueType()->pointer)) { + if (std::any_of(scope->varlist.cbegin(), scope->varlist.cend(), [&](const Variable& var) { + return var.declarationId() == ftok->next()->varId(); + })) + /** @todo false negative: we assume function changes variable state */ + assignVar(usage, ftok->next()->varId()); + + ftok = ftok->tokAt(2); + } + + if (!Token::Match(ftok->next(), "::| %name%") && + !Token::Match(ftok->next(), "*| this . %name%") && + !Token::Match(ftok->next(), "* %name% =") && + !Token::Match(ftok->next(), "( * this ) . %name%")) + continue; + + // Goto the first token in this statement.. + ftok = ftok->next(); + + // skip "return" + if (ftok->str() == "return") + ftok = ftok->next(); + + // Skip "( * this )" + if (Token::simpleMatch(ftok, "( * this ) .")) { + ftok = ftok->tokAt(5); + } + + // Skip "this->" + if (Token::simpleMatch(ftok, "this .")) + ftok = ftok->tokAt(2); + + // Skip "classname :: " + if (Token::Match(ftok, ":: %name%")) + ftok = ftok->next(); + while (Token::Match(ftok, "%name% ::")) + ftok = ftok->tokAt(2); + + // Clearing all variables.. + if (Token::Match(ftok, "::| memset ( this ,")) { + assignAllVar(usage); + return; + } + + // Ticket #7068 + if (Token::Match(ftok, "::| memset ( &| this . %name%")) { + if (ftok->str() == "::") + ftok = ftok->next(); + int offsetToMember = 4; + if (ftok->strAt(2) == "&") + ++offsetToMember; + assignVar(usage, ftok->tokAt(offsetToMember)->varId()); + ftok = ftok->linkAt(1); + continue; + } + + // Clearing array.. + if (Token::Match(ftok, "::| memset ( %name% ,")) { + if (ftok->str() == "::") + ftok = ftok->next(); + assignVar(usage, ftok->tokAt(2)->varId()); + ftok = ftok->linkAt(1); + continue; + } + + // Calling member function? + if (Token::simpleMatch(ftok, "operator= (")) { + if (ftok->function()) { + const Function *member = ftok->function(); + // recursive call + // assume that all variables are initialized + if (std::find(callstack.cbegin(), callstack.cend(), member) != callstack.cend()) { + /** @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(); + } + + // assume that a base class call to operator= assigns all its base members (but not more) + else if (func.tokenDef->str() == ftok->str() && isBaseClassMutableMemberFunc(ftok, scope)) { + if (member->nestedIn) + assignAllVarsVisibleFromScope(usage, member->nestedIn->definedType->classScope); + } + + // there is a called member function, but it has no implementation, so we assume it initializes everything + else { + assignAllVar(usage); + } + } + + // using default operator =, assume everything initialized + else { + assignAllVar(usage); + } + } else if (Token::Match(ftok, "::| %name% (") && !Token::Match(ftok, "if|while|for")) { + if (ftok->str() == "::") + ftok = ftok->next(); + + // Passing "this" => assume that everything is initialized + for (const Token *tok2 = ftok->next()->link(); tok2 && tok2 != ftok; tok2 = tok2->previous()) { + if (tok2->str() == "this") { + assignAllVar(usage); + return; + } + } + + // check if member function + if (ftok->function() && ftok->function()->nestedIn == scope && + !ftok->function()->isConstructor()) { + const Function *member = ftok->function(); + + // recursive call + // assume that all variables are initialized + if (std::find(callstack.cbegin(), callstack.cend(), member) != callstack.cend()) { + assignAllVar(usage); + return; + } + + // member function has implementation + if (member->hasBody()) { + // initialize variable use list using member function + callstack.push_back(member); + initializeVarList(*member, callstack, scope, usage); + callstack.pop_back(); + + // Assume that variables that are passed to it are initialized.. + for (const Token *tok2 = ftok; tok2; tok2 = tok2->next()) { + if (Token::Match(tok2, "[;{}]")) + break; + if (Token::Match(tok2, "[(,] &| %name% [,)]")) { + tok2 = tok2->next(); + if (tok2->str() == "&") + tok2 = tok2->next(); + if (isVariableChangedByFunctionCall(tok2, tok2->previous()->str() == "&", tok2->varId(), mSettings, nullptr)) + assignVar(usage, tok2->varId()); + } + } + } + + // there is a called member function, but it has no implementation, so we assume it initializes everything (if it can mutate state) + else if (!member->isConst() && !member->isStatic()) { + assignAllVar(usage); + } + + // const method, assume it assigns all mutable members + else if (member->isConst()) { + for (Usage& i: usage) { + if (i.var->isMutable()) + i.assign = true; + } + } + } + + // not member function + else { + // could be a base class virtual function, so we assume it initializes everything + if (!func.isConstructor() && isBaseClassMutableMemberFunc(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(usage, tok->varId()); + } + } + } + } + } + + // Assignment of member variable? + else if (Token::Match(ftok, "%name% =")) { + assignVar(usage, ftok); + bool bailout = ftok->variable() && ftok->variable()->isReference(); + const Token* tok2 = ftok->tokAt(2); + if (tok2->str() == "&") { + tok2 = tok2->next(); + bailout = true; + } + if (tok2->variable() && (bailout || tok2->variable()->isArray()) && tok2->strAt(1) != "[") + assignVar(usage, tok2->varId()); + } + + // Assignment of array item of member variable? + else if (Token::Match(ftok, "%name% [|.")) { + const Token *tok2 = ftok; + while (tok2) { + if (tok2->strAt(1) == "[") + tok2 = tok2->next()->link(); + else if (Token::Match(tok2->next(), ". %name%")) + tok2 = tok2->tokAt(2); + else + break; + } + if (tok2 && tok2->strAt(1) == "=") + assignVar(usage, ftok->varId()); + } + + // Assignment of array item of member variable? + else if (Token::Match(ftok, "* %name% =")) { + assignVar(usage, ftok->next()->varId()); + } else if (Token::Match(ftok, "* this . %name% =")) { + assignVar(usage, ftok->tokAt(3)->varId()); + } else if (astIsRangeBasedForDecl(ftok)) { + if (const Variable* rangeVar = ftok->astParent()->astOperand1()->variable()) { + if (rangeVar->isReference() && !rangeVar->isConst()) + assignVar(usage, ftok->varId()); + } + } + + // The functions 'clear' and 'Clear' are supposed to initialize variable. + if (Token::Match(ftok, "%name% . clear|Clear (")) { + assignVar(usage, ftok->varId()); + } + } +} + +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" + const std::string message {"The " + std::string(isStruct ? "struct" : "class") + " '$symbol' does not declare a constructor although it has private member variables which likely require initialization."}; + const std::string verbose {message + " Member variables of native types, pointers, or references are left uninitialized when the class is instantiated. That may cause bugs or undefined behavior."}; + reportError(tok, Severity::style, "noConstructor", "$symbol:" + classname + '\n' + message + '\n' + verbose, CWE398, Certainty::normal); +} + +void CheckClass::noExplicitConstructorError(const Token *tok, const std::string &classname, bool isStruct) +{ + const std::string message(std::string(isStruct ? "Struct" : "Class") + " '$symbol' has a constructor with 1 argument that is not explicit."); + const std::string verbose(message + " Such, so called \"Converting constructors\", should in general be explicit for type safety reasons as that prevents unintended implicit conversions."); + reportError(tok, Severity::style, "noExplicitConstructor", "$symbol:" + classname + '\n' + message + '\n' + verbose, CWE398, Certainty::normal); +} + +void CheckClass::uninitVarError(const Token *tok, bool isprivate, Function::Type functionType, const std::string &classname, const std::string &varname, bool derived, bool inconclusive) +{ + std::string ctor; + if (functionType == Function::eCopyConstructor) + ctor = "copy "; + else if (functionType == Function::eMoveConstructor) + ctor = "move "; + std::string message("Member variable '$symbol' is not initialized in the " + ctor + "constructor."); + if (derived) + message += " Maybe it should be initialized directly in the class " + classname + "?"; + std::string id = std::string("uninit") + (derived ? "Derived" : "") + "MemberVar" + (isprivate ? "Private" : ""); + const std::string verbose {message + " Member variables of native types, pointers, or references are left uninitialized when the class is instantiated. That may cause bugs or undefined behavior."}; + reportError(tok, Severity::warning, id, "$symbol:" + classname + "::" + varname + '\n' + message + '\n' + verbose, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckClass::uninitVarError(const Token *tok, const std::string &classname, const std::string &varname) +{ + const std::string message("Member variable '$symbol' is not initialized."); // report missing in-class initializer + const std::string verbose {message + " Member variables of native types, pointers, or references are left uninitialized when the class is instantiated. That may cause bugs or undefined behavior."}; + const std::string id = std::string("uninitMemberVarPrivate"); + reportError(tok, Severity::warning, id, "$symbol:" + classname + "::" + varname + '\n' + message + '\n' + verbose, CWE398, Certainty::normal); +} + +void CheckClass::missingMemberCopyError(const Token *tok, Function::Type functionType, const std::string& classname, const std::string& varname) +{ + const std::string ctor(functionType == Function::Type::eCopyConstructor ? "copy" : "move"); + const std::string action(functionType == Function::Type::eCopyConstructor ? "copied?" : "moved?"); + const std::string message = + "$symbol:" + classname + "::" + varname + "\n" + + "Member variable '$symbol' is not assigned in the " + ctor + " constructor. Should it be " + action; + reportError(tok, Severity::warning, "missingMemberCopy", message, CWE398, Certainty::inconclusive); +} + +void CheckClass::operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive) +{ + reportError(tok, Severity::warning, "operatorEqVarError", "$symbol:" + classname + "::" + varname + "\nMember variable '$symbol' is not assigned a value in '" + classname + "::operator='.", CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +//--------------------------------------------------------------------------- +// ClassCheck: Use initialization list instead of assignment +//--------------------------------------------------------------------------- + +void CheckClass::initializationListUsage() +{ + if (!mSettings->severity.isEnabled(Severity::performance)) + return; + + logChecker("CheckClass::initializationListUsage"); // performance + + for (const Scope *scope : mSymbolDatabase->functionScopes) { + // Check every constructor + if (!scope->function || !scope->function->isConstructor()) + continue; + + // Do not warn when a delegate constructor is called + if (const Token *initList = scope->function->constructorMemberInitialization()) { + if (Token::Match(initList, ": %name% {|(") && initList->strAt(1) == scope->className) + continue; + } + + const Scope* owner = scope->functionOf; + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "%name% (")) // Assignments might depend on this function call or if/for/while/switch statement from now on. + break; + if (Token::Match(tok, "try|do {")) + break; + if (!Token::Match(tok, "%var% =") || tok->strAt(-1) == "*" || tok->strAt(-1) == ".") + continue; + + const Variable* var = tok->variable(); + if (!var || var->scope() != owner || var->isStatic()) + continue; + if (var->isPointer() || var->isReference() || var->isEnumType()) + continue; + if (!WRONG_DATA(!var->valueType(), tok) && var->valueType()->type > ValueType::Type::ITERATOR) + continue; + + // bailout: multi line lambda in rhs => do not warn + if (findLambdaEndToken(tok->tokAt(2)) && tok->tokAt(2)->findExpressionStartEndTokens().second->linenr() > tok->tokAt(2)->linenr()) + continue; + + // Access local var member in rhs => do not warn + bool localmember = false; + visitAstNodes(tok->next()->astOperand2(), + [&](const Token *rhs) { + if (rhs->str() == "." && rhs->astOperand1() && rhs->astOperand1()->variable() && rhs->astOperand1()->variable()->isLocal()) + localmember = true; + return ChildrenToVisit::op1_and_op2; + }); + if (localmember) + continue; + + bool allowed = true; + visitAstNodes(tok->next()->astOperand2(), + [&](const Token *tok2) { + const Variable* var2 = tok2->variable(); + if (var2) { + if (var2->scope() == owner && tok2->strAt(-1)!=".") { // Is there a dependency between two member variables? + allowed = false; + return ChildrenToVisit::done; + } + if (var2->isArray() && var2->isLocal()) { // Can't initialize with a local array + allowed = false; + return ChildrenToVisit::done; + } + } else if (tok2->str() == "this") { // 'this' instance is not completely constructed in initialization list + allowed = false; + return ChildrenToVisit::done; + } else if (Token::Match(tok2, "%name% (") && tok2->strAt(-1) != "." && isMemberFunc(owner, tok2)) { // Member function called? + allowed = false; + return ChildrenToVisit::done; + } + return ChildrenToVisit::op1_and_op2; + }); + if (!allowed) + continue; + + suggestInitializationList(tok, tok->str()); + } + } +} + +void CheckClass::suggestInitializationList(const Token* tok, const std::string& varname) +{ + reportError(tok, Severity::performance, "useInitializationList", "$symbol:" + varname + "\nVariable '$symbol' is assigned in constructor body. Consider performing initialization in initialization list.\n" + "When an object of a class is created, the constructors of all member variables are called consecutively " + "in the order the variables are declared, even if you don't explicitly write them to the initialization list. You " + "could avoid assigning '$symbol' a value by passing the value to the constructor in the initialization list.", CWE398, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// ClassCheck: Unused private functions +//--------------------------------------------------------------------------- + +static bool checkFunctionUsage(const Function *privfunc, const Scope* scope) +{ + if (!scope) + return true; // Assume it is used, if scope is not seen + + for (std::list::const_iterator func = scope->functionList.cbegin(); func != scope->functionList.cend(); ++func) { + if (func->functionScope) { + if (Token::Match(func->tokenDef, "%name% (")) { + for (const Token *ftok = func->tokenDef->tokAt(2); ftok && ftok->str() != ")"; ftok = ftok->next()) { + if (Token::Match(ftok, "= %name% [(,)]") && ftok->strAt(1) == privfunc->name()) + return true; + if (ftok->str() == "(") + ftok = ftok->link(); + } + } + for (const Token *ftok = func->functionScope->classDef->linkAt(1); ftok != func->functionScope->bodyEnd; ftok = ftok->next()) { + if (ftok->function() == privfunc) + return true; + if (ftok->varId() == 0U && ftok->str() == privfunc->name()) // TODO: This condition should be redundant + return true; + } + } else if ((func->type != Function::eCopyConstructor && + func->type != Function::eOperatorEqual) || + func->access != AccessControl::Private) // Assume it is used, if a function implementation isn't seen, but empty private copy constructors and assignment operators are OK + return true; + } + + const std::map::const_iterator end = scope->definedTypesMap.cend(); + for (std::map::const_iterator iter = scope->definedTypesMap.cbegin(); iter != end; ++iter) { + const Type *type = iter->second; + if (type->enclosingScope == scope && checkFunctionUsage(privfunc, type->classScope)) + return true; + } + + for (const Variable &var : scope->varlist) { + if (var.isStatic()) { + const Token* tok = Token::findmatch(scope->bodyStart, "%varid% =|(|{", var.declarationId()); + if (tok) + tok = tok->tokAt(2); + while (tok && tok->str() != ";") { + if (tok->function() == privfunc) + return true; + tok = tok->next(); + } + } + } + + return false; // Unused in this scope +} + +void CheckClass::privateFunctions() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unusedPrivateFunction")) + return; + + logChecker("CheckClass::privateFunctions"); // style + + for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { + + // do not check borland classes with properties.. + if (Token::findsimplematch(scope->bodyStart, "; __property ;", scope->bodyEnd)) + continue; + + std::list privateFuncs; + for (const Function &func : scope->functionList) { + // Get private functions.. + if (func.type == Function::eFunction && func.access == AccessControl::Private && !func.isOperator()) // TODO: There are smarter ways to check private operator usage + privateFuncs.push_back(&func); + } + + // Bailout for overridden virtual functions of base classes + if (!scope->definedType->derivedFrom.empty()) { + // Check virtual functions + for (std::list::iterator it = privateFuncs.begin(); it != privateFuncs.end();) { + if ((*it)->isImplicitlyVirtual(true)) // Give true as default value to be returned if we don't see all base classes + it = privateFuncs.erase(it); + else + ++it; + } + } + + while (!privateFuncs.empty()) { + const auto& pf = privateFuncs.front(); + if (pf->retDef && pf->retDef->isAttributeMaybeUnused()) { + privateFuncs.pop_front(); + continue; + } + // Check that all private functions are used + bool used = checkFunctionUsage(pf, scope); // Usage in this class + // Check in friend classes + const std::vector& friendList = scope->definedType->friendList; + for (int i = 0; i < friendList.size() && !used; i++) { + if (friendList[i].type) + used = checkFunctionUsage(pf, friendList[i].type->classScope); + else + used = true; // Assume, it is used if we do not see friend class + } + + if (!used) + unusedPrivateFunctionError(pf->tokenDef, scope->className, pf->name()); + + privateFuncs.pop_front(); + } + } +} + +void CheckClass::unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname) +{ + reportError(tok, Severity::style, "unusedPrivateFunction", "$symbol:" + classname + "::" + funcname + "\nUnused private function: '$symbol'", CWE398, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// ClassCheck: Check that memset is not used on classes +//--------------------------------------------------------------------------- + +static const Scope* findFunctionOf(const Scope* scope) +{ + while (scope) { + if (scope->type == Scope::eFunction) + return scope->functionOf; + scope = scope->nestedIn; + } + return nullptr; +} + +void CheckClass::checkMemset() +{ + logChecker("CheckClass::checkMemset"); + const bool printWarnings = mSettings->severity.isEnabled(Severity::warning); + for (const Scope *scope : mSymbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "memset|memcpy|memmove (")) { + const Token* arg1 = tok->tokAt(2); + const Token* arg3 = arg1->nextArgument(); + if (arg3) + arg3 = arg3->nextArgument(); + if (!arg3) + // weird, shouldn't happen: memset etc should have + // 3 arguments. + continue; + + const Token *typeTok = nullptr; + const Scope *type = nullptr; + const Token* sizeofTok = arg3->previous()->astOperand2(); // try to find sizeof() in argument expression + if (sizeofTok && sizeofTok->astOperand1() && Token::simpleMatch(sizeofTok->astOperand1()->previous(), "sizeof (")) + sizeofTok = sizeofTok->astOperand1(); + else if (sizeofTok && sizeofTok->astOperand2() && Token::simpleMatch(sizeofTok->astOperand2()->previous(), "sizeof (")) + sizeofTok = sizeofTok->astOperand2(); + if (Token::simpleMatch(sizeofTok, "(")) + sizeofTok = sizeofTok->previous(); + if (Token::Match(sizeofTok, "sizeof ( %type% )")) + typeTok = sizeofTok->tokAt(2); + else if (Token::Match(sizeofTok, "sizeof ( %type% :: %type% )")) + typeTok = sizeofTok->tokAt(4); + else if (Token::Match(sizeofTok, "sizeof ( struct %type% )")) + typeTok = sizeofTok->tokAt(3); + else if (Token::simpleMatch(sizeofTok, "sizeof ( * this )") || Token::simpleMatch(arg1, "this ,")) { + type = findFunctionOf(sizeofTok->scope()); + } else if (Token::Match(arg1, "&|*|%var%")) { + int numIndirToVariableType = 0; // Offset to the actual type in terms of dereference/addressof + for (;; arg1 = arg1->next()) { + if (arg1->str() == "&") + ++numIndirToVariableType; + else if (arg1->str() == "*") + --numIndirToVariableType; + else + break; + } + + const Variable * const var = arg1->variable(); + if (var && arg1->strAt(1) == ",") { + if (var->isArrayOrPointer()) { + const Token *endTok = var->typeEndToken(); + while (Token::simpleMatch(endTok, "*")) { + ++numIndirToVariableType; + endTok = endTok->previous(); + } + } + + if (var->isArray()) + numIndirToVariableType += int(var->dimensions().size()); + + if (numIndirToVariableType == 1) + type = var->typeScope(); + + if (!type && !var->isPointer() && !Token::simpleMatch(var->typeStartToken(), "std :: array") && + mSettings->library.detectContainerOrIterator(var->typeStartToken())) { + memsetError(tok, tok->str(), var->getTypeName(), {}, /*isContainer*/ true); + } + } + } + + // No type defined => The tokens didn't match + if (!typeTok && !type) + continue; + + if (typeTok && typeTok->str() == "(") + typeTok = typeTok->next(); + + if (!type && typeTok->type()) + type = typeTok->type()->classScope; + + if (type) { + const std::set parsedTypes; + checkMemsetType(scope, tok, type, false, parsedTypes); + } + } else if (tok->variable() && tok->variable()->isPointer() && tok->variable()->typeScope() && Token::Match(tok, "%var% = %name% (")) { + const Library::AllocFunc* alloc = mSettings->library.getAllocFuncInfo(tok->tokAt(2)); + if (!alloc) + alloc = mSettings->library.getReallocFuncInfo(tok->tokAt(2)); + if (!alloc || alloc->bufferSize == Library::AllocFunc::BufferSize::none) + continue; + const std::set parsedTypes; + checkMemsetType(scope, tok->tokAt(2), tok->variable()->typeScope(), true, parsedTypes); + + if (printWarnings && tok->variable()->typeScope()->numConstructors > 0) + mallocOnClassWarning(tok, tok->strAt(2), tok->variable()->typeScope()->classDef); + } + } + } +} + +void CheckClass::checkMemsetType(const Scope *start, const Token *tok, const Scope *type, bool allocation, std::set parsedTypes) +{ + // If type has been checked there is no need to check it again + if (parsedTypes.find(type) != parsedTypes.end()) + return; + parsedTypes.insert(type); + + const bool printPortability = mSettings->severity.isEnabled(Severity::portability); + + // recursively check all parent classes + for (const Type::BaseInfo & i : type->definedType->derivedFrom) { + const Type* derivedFrom = i.type; + if (derivedFrom && derivedFrom->classScope) + checkMemsetType(start, tok, derivedFrom->classScope, allocation, parsedTypes); + } + + // Warn if type is a class that contains any virtual functions + for (const Function &func : type->functionList) { + if (func.hasVirtualSpecifier()) { + if (allocation) + mallocOnClassError(tok, tok->str(), type->classDef, "virtual function"); + else + memsetError(tok, tok->str(), "virtual function", type->classDef->str()); + } + } + + // Warn if type is a class or struct that contains any std::* variables + for (const Variable &var : type->varlist) { + if (var.isReference() && !var.isStatic()) { + memsetErrorReference(tok, tok->str(), type->classDef->str()); + continue; + } + // don't warn if variable static or const, pointer or array of pointers + if (!var.isStatic() && !var.isConst() && !var.isPointer() && (!var.isArray() || var.typeEndToken()->str() != "*")) { + const Token *tok1 = var.typeStartToken(); + const Scope *typeScope = var.typeScope(); + + std::string typeName; + if (Token::Match(tok1, "%type% ::")) { + const Token *typeTok = tok1; + while (Token::Match(typeTok, "%type% ::")) { + typeName += typeTok->str() + "::"; + typeTok = typeTok->tokAt(2); + } + typeName += typeTok->str(); + } + + // check for std:: type + if (var.isStlType() && typeName != "std::array" && !mSettings->library.podtype(typeName)) { + if (allocation) + mallocOnClassError(tok, tok->str(), type->classDef, "'" + typeName + "'"); + else + memsetError(tok, tok->str(), "'" + typeName + "'", type->classDef->str()); + } + + // check for known type + else if (typeScope && typeScope != type) + checkMemsetType(start, tok, typeScope, allocation, parsedTypes); + + // check for float + else if (printPortability && var.isFloatingType() && tok->str() == "memset") + memsetErrorFloat(tok, type->classDef->str()); + } + } +} + +void CheckClass::mallocOnClassWarning(const Token* tok, const std::string &memfunc, const Token* classTok) +{ + std::list toks = { tok, classTok }; + reportError(toks, Severity::warning, "mallocOnClassWarning", + "$symbol:" + memfunc +"\n" + "Memory for class instance allocated with $symbol(), but class provides constructors.\n" + "Memory for class instance allocated with $symbol(), but class provides constructors. This is unsafe, " + "since no constructor is called and class members remain uninitialized. Consider using 'new' instead.", CWE762, Certainty::normal); +} + +void CheckClass::mallocOnClassError(const Token* tok, const std::string &memfunc, const Token* classTok, const std::string &classname) +{ + std::list toks = { tok, classTok }; + reportError(toks, Severity::error, "mallocOnClassError", + "$symbol:" + memfunc +"\n" + "$symbol:" + classname +"\n" + "Memory for class instance allocated with " + memfunc + "(), but class contains a " + classname + ".\n" + "Memory for class instance allocated with " + memfunc + "(), but class a " + classname + ". This is unsafe, " + "since no constructor is called and class members remain uninitialized. Consider using 'new' instead.", CWE665, Certainty::normal); +} + +void CheckClass::memsetError(const Token *tok, const std::string &memfunc, const std::string &classname, const std::string &type, bool isContainer) +{ + const std::string typeStr = isContainer ? std::string() : (type + " that contains a "); + const std::string msg = "$symbol:" + memfunc + "\n" + "$symbol:" + classname + "\n" + "Using '" + memfunc + "' on " + typeStr + classname + ".\n" + "Using '" + memfunc + "' on " + typeStr + 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."; + reportError(tok, Severity::error, "memsetClass", msg, CWE762, Certainty::normal); +} + +void CheckClass::memsetErrorReference(const Token *tok, const std::string &memfunc, const std::string &type) +{ + reportError(tok, Severity::error, "memsetClassReference", + "$symbol:" + memfunc +"\n" + "Using '" + memfunc + "' on " + type + " that contains a reference.", CWE665, Certainty::normal); +} + +void CheckClass::memsetErrorFloat(const Token *tok, const std::string &type) +{ + reportError(tok, Severity::portability, "memsetClassFloat", "Using memset() on " + type + " which contains a floating point number.\n" + "Using memset() on " + type + " which contains a floating point number." + " This is not portable because memset() sets each byte of a block of memory to a specific value and" + " the actual representation of a floating-point value is implementation defined." + " Note: In case of an IEEE754-1985 compatible implementation setting all bits to zero results in the value 0.0.", CWE758, Certainty::normal); +} + + +//--------------------------------------------------------------------------- +// ClassCheck: "C& operator=(const C&) { ... return *this; }" +// operator= should return a reference to *this +//--------------------------------------------------------------------------- + +void CheckClass::operatorEqRetRefThis() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("operatorEqRetRefThis")) + return; + + logChecker("CheckClass::operatorEqRetRefThis"); // style + + for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { + for (std::list::const_iterator func = scope->functionList.cbegin(); func != scope->functionList.cend(); ++func) { + if (func->type == Function::eOperatorEqual && func->hasBody()) { + // make sure return signature is correct + if (func->retType == func->nestedIn->definedType && func->tokenDef->strAt(-1) == "&") { + checkReturnPtrThis(scope, &(*func), func->functionScope->bodyStart, func->functionScope->bodyEnd); + } + } + } + } +} + +void CheckClass::checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last) +{ + std::set analyzedFunctions; + checkReturnPtrThis(scope, func, tok, last, analyzedFunctions); +} + +void CheckClass::checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last, std::set& analyzedFunctions) +{ + bool foundReturn = false; + + const Token* const startTok = tok; + + for (; tok && tok != last; tok = tok->next()) { + // check for return of reference to this + + if (const Token* lScope = isLambdaCaptureList(tok)) // skip lambda + tok = lScope->link(); + + if (tok->str() != "return") + continue; + + foundReturn = true; + + const Token *retExpr = tok->astOperand1(); + if (retExpr && retExpr->str() == "=") + retExpr = retExpr->astOperand1(); + if (retExpr && retExpr->isUnaryOp("*") && Token::simpleMatch(retExpr->astOperand1(), "this")) + continue; + + std::string cast("( " + scope->className + " & )"); + if (Token::simpleMatch(tok->next(), cast.c_str(), cast.size())) + tok = tok->tokAt(4); + + // check if a function is called + if (tok->strAt(2) == "(" && + tok->linkAt(2)->next()->str() == ";") { + // check if it is a member function + for (std::list::const_iterator it = scope->functionList.cbegin(); it != scope->functionList.cend(); ++it) { + // check for a regular function with the same name and a body + if (it->type == Function::eFunction && it->hasBody() && + it->token->str() == tok->next()->str()) { + // check for the proper return type + if (it->tokenDef->previous()->str() == "&" && + it->tokenDef->strAt(-2) == scope->className) { + // make sure it's not a const function + if (!it->isConst()) { + /** @todo make sure argument types match */ + // avoid endless recursions + if (analyzedFunctions.find(&*it) == analyzedFunctions.end()) { + analyzedFunctions.insert(&*it); + checkReturnPtrThis(scope, &*it, it->arg->link()->next(), it->arg->link()->next()->link(), + analyzedFunctions); + } + // just bail for now + else + return; + } + } + } + } + } + + // check if *this is returned + else if (!(Token::simpleMatch(tok->next(), "operator= (") || + Token::simpleMatch(tok->next(), "this . operator= (") || + (Token::Match(tok->next(), "%type% :: operator= (") && + tok->next()->str() == scope->className))) + operatorEqRetRefThisError(func->token); + } + if (foundReturn) { + return; + } + if (startTok->next() == last) { + const std::string tmp("( const " + scope->className + " &"); + if (Token::simpleMatch(func->argDef, tmp.c_str(), tmp.size())) { + // Typical wrong way to suppress default assignment operator by declaring it and leaving empty + operatorEqMissingReturnStatementError(func->token, func->access == AccessControl::Public); + } else { + operatorEqMissingReturnStatementError(func->token, true); + } + return; + } + if (mSettings->library.isScopeNoReturn(last, nullptr)) { + // Typical wrong way to prohibit default assignment operator + // by always throwing an exception or calling a noreturn function + operatorEqShouldBeLeftUnimplementedError(func->token); + return; + } + + operatorEqMissingReturnStatementError(func->token, func->access == AccessControl::Public); +} + +void CheckClass::operatorEqRetRefThisError(const Token *tok) +{ + reportError(tok, Severity::style, "operatorEqRetRefThis", "'operator=' should return reference to 'this' instance.", CWE398, Certainty::normal); +} + +void CheckClass::operatorEqShouldBeLeftUnimplementedError(const Token *tok) +{ + reportError(tok, Severity::style, "operatorEqShouldBeLeftUnimplemented", "'operator=' should either return reference to 'this' instance or be declared private and left unimplemented.", CWE398, Certainty::normal); +} + +void CheckClass::operatorEqMissingReturnStatementError(const Token *tok, bool error) +{ + if (error) { + reportError(tok, Severity::error, "operatorEqMissingReturnStatement", "No 'return' statement in non-void function causes undefined behavior.", CWE398, Certainty::normal); + } else { + operatorEqRetRefThisError(tok); + } +} + +//--------------------------------------------------------------------------- +// ClassCheck: "C& operator=(const C& rhs) { if (this == &rhs) ... }" +// operator= should check for assignment to self +// +// For simple classes, an assignment to self check is only a potential optimization. +// +// For classes that allocate dynamic memory, assignment to self can be a real error +// if it is deallocated and allocated again without being checked for. +// +// This check is not valid for classes with multiple inheritance because a +// class can have multiple addresses so there is no trivial way to check for +// assignment to self. +//--------------------------------------------------------------------------- + +void CheckClass::operatorEqToSelf() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckClass::operatorEqToSelf"); // warning + + for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { + // skip classes with multiple inheritance + if (scope->definedType->derivedFrom.size() > 1) + continue; + + for (const Function &func : scope->functionList) { + if (func.type == Function::eOperatorEqual && func.hasBody()) { + // make sure that the operator takes an object of the same type as *this, otherwise we can't detect self-assignment checks + if (func.argumentList.empty()) + continue; + const Token* typeTok = func.argumentList.front().typeEndToken(); + while (typeTok->str() == "const" || typeTok->str() == "&" || typeTok->str() == "*") + typeTok = typeTok->previous(); + if (typeTok->str() != scope->className) + continue; + + // make sure return signature is correct + if (Token::Match(func.retDef, "%type% &") && func.retDef->str() == scope->className) { + // find the parameter name + const Token *rhs = func.argumentList.cbegin()->nameToken(); + const Token* out_ifStatementScopeStart = nullptr; + if (!hasAssignSelf(&func, rhs, out_ifStatementScopeStart)) { + if (hasAllocation(&func, scope)) + operatorEqToSelfError(func.token); + } else if (out_ifStatementScopeStart != nullptr) { + if (hasAllocationInIfScope(&func, scope, out_ifStatementScopeStart)) + operatorEqToSelfError(func.token); + } + } + } + } + } +} + +bool CheckClass::hasAllocationInIfScope(const Function *func, const Scope* scope, const Token *ifStatementScopeStart) const +{ + const Token *end; + if (ifStatementScopeStart->str() == "{") + end = ifStatementScopeStart->link(); + else + end = func->functionScope->bodyEnd; + return hasAllocation(func, scope, ifStatementScopeStart, end); +} + +bool CheckClass::hasAllocation(const Function *func, const Scope* scope) const +{ + return hasAllocation(func, scope, func->functionScope->bodyStart, func->functionScope->bodyEnd); +} + +bool CheckClass::hasAllocation(const Function *func, const Scope* scope, const Token *start, const Token *end) const +{ + if (!end) + end = func->functionScope->bodyEnd; + for (const Token *tok = start; tok && (tok != end); tok = tok->next()) { + if (((tok->isCpp() && Token::Match(tok, "%var% = new")) || + (Token::Match(tok, "%var% = %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)))) && + isMemberVar(scope, tok)) + return true; + + // check for deallocating memory + const Token *var; + if (Token::Match(tok, "%name% ( %var%") && mSettings->library.getDeallocFuncInfo(tok)) + var = tok->tokAt(2); + else if (tok->isCpp() && Token::Match(tok, "delete [ ] %var%")) + var = tok->tokAt(3); + else if (tok->isCpp() && Token::Match(tok, "delete %var%")) + var = tok->next(); + else + continue; + // Check for assignment to the deleted pointer (only if its a member of the class) + if (isMemberVar(scope, var)) { + for (const Token *tok1 = var->next(); tok1 && (tok1 != end); tok1 = tok1->next()) { + if (Token::Match(tok1, "%varid% =", var->varId())) + return true; + } + } + } + + return false; +} + +static bool isTrueKeyword(const Token* tok) +{ + return tok->hasKnownIntValue() && tok->getKnownIntValue() == 1; +} + +static bool isFalseKeyword(const Token* tok) +{ + return tok->hasKnownIntValue() && tok->getKnownIntValue() == 0; +} + +/* + * Checks if self-assignment test is inverse + * For example 'if (this == &rhs)' + */ +CheckClass::Bool CheckClass::isInverted(const Token *tok, const Token *rhs) +{ + bool res = true; + for (const Token *itr = tok; itr && itr->str()!="("; itr=itr->astParent()) { + if (Token::simpleMatch(itr, "!=") && (isTrueKeyword(itr->astOperand1()) || isTrueKeyword(itr->astOperand2()))) { + res = !res; + } else if (Token::simpleMatch(itr, "!=") && ((Token::simpleMatch(itr->astOperand1(), "this") && Token::simpleMatch(itr->astOperand2(), "&") && Token::simpleMatch(itr->astOperand2()->next(), rhs->str().c_str(), rhs->str().size())) + || (Token::simpleMatch(itr->astOperand2(), "this") && Token::simpleMatch(itr->astOperand1(), "&") && Token::simpleMatch(itr->astOperand1()->next(), rhs->str().c_str(), rhs->str().size())))) { + res = !res; + } else if (Token::simpleMatch(itr, "!=") && (isFalseKeyword(itr->astOperand1()) || isFalseKeyword(itr->astOperand2()))) { + //Do nothing + } else if (Token::simpleMatch(itr, "!")) { + res = !res; + } else if (Token::simpleMatch(itr, "==") && (isFalseKeyword(itr->astOperand1()) || isFalseKeyword(itr->astOperand2()))) { + res = !res; + } else if (Token::simpleMatch(itr, "==") && (isTrueKeyword(itr->astOperand1()) || isTrueKeyword(itr->astOperand2()))) { + //Do nothing + } else if (Token::simpleMatch(itr, "==") && ((Token::simpleMatch(itr->astOperand1(), "this") && Token::simpleMatch(itr->astOperand2(), "&") && Token::simpleMatch(itr->astOperand2()->next(), rhs->str().c_str(), rhs->str().size())) + || (Token::simpleMatch(itr->astOperand2(), "this") && Token::simpleMatch(itr->astOperand1(), "&") && Token::simpleMatch(itr->astOperand1()->next(), rhs->str().c_str(), rhs->str().size())))) { + //Do nothing + } else { + return Bool::BAILOUT; + } + } + if (res) + return Bool::TRUE; + return Bool::FALSE; +} + +const Token * CheckClass::getIfStmtBodyStart(const Token *tok, const Token *rhs) +{ + const Token *top = tok->astTop(); + if (Token::simpleMatch(top->link(), ") {")) { + switch (isInverted(tok->astParent(), rhs)) { + case Bool::BAILOUT: + return nullptr; + case Bool::TRUE: + return top->link()->next(); + case Bool::FALSE: + return top->link()->next()->link(); + } + } + return nullptr; +} + +bool CheckClass::hasAssignSelf(const Function *func, const Token *rhs, const Token *&out_ifStatementScopeStart) +{ + if (!rhs) + return false; + const Token *last = func->functionScope->bodyEnd; + for (const Token *tok = func->functionScope->bodyStart; tok && tok != last; tok = tok->next()) { + if (!Token::simpleMatch(tok, "if (")) + continue; + + bool ret = false; + visitAstNodes(tok->next()->astOperand2(), + [&](const Token *tok2) { + if (!Token::Match(tok2, "==|!=")) + return ChildrenToVisit::op1_and_op2; + if (Token::simpleMatch(tok2->astOperand1(), "this")) + tok2 = tok2->astOperand2(); + else if (Token::simpleMatch(tok2->astOperand2(), "this")) + tok2 = tok2->astOperand1(); + else + return ChildrenToVisit::op1_and_op2; + if (tok2 && tok2->isUnaryOp("&") && tok2->astOperand1()->str() == rhs->str()) + ret = true; + if (ret) { + out_ifStatementScopeStart = getIfStmtBodyStart(tok2, rhs); + } + return ret ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; + }); + if (ret) + return ret; + } + + return false; +} + +void CheckClass::operatorEqToSelfError(const Token *tok) +{ + reportError(tok, Severity::warning, "operatorEqToSelf", + "'operator=' should check for assignment to self to avoid problems with dynamic memory.\n" + "'operator=' should check for assignment to self to ensure that each block of dynamically " + "allocated memory is owned and managed by only one instance of the class.", CWE398, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// A destructor in a base class should be virtual +//--------------------------------------------------------------------------- + +void CheckClass::virtualDestructor() +{ + // This error should only be given if: + // * base class doesn't have virtual destructor + // * derived class has non-empty destructor (only c++03, in c++11 it's UB see paragraph 3 in [expr.delete]) + // * base class is deleted + // unless inconclusive in which case: + // * A class with any virtual functions should have a destructor that is either public and virtual or protected + const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); + + std::list inconclusiveErrors; + + logChecker("CheckClass::virtualDestructor"); + + for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { + + // Skip base classes (unless inconclusive) + if (scope->definedType->derivedFrom.empty()) { + if (printInconclusive) { + const Function *destructor = scope->getDestructor(); + if (destructor && !destructor->hasVirtualSpecifier() && destructor->access == AccessControl::Public) { + if (std::any_of(scope->functionList.cbegin(), scope->functionList.cend(), [](const Function& func) { + return func.hasVirtualSpecifier(); + })) + inconclusiveErrors.push_back(destructor); + } + } + continue; + } + + // Check if destructor is empty and non-empty .. + if (mSettings->standards.cpp <= Standards::CPP03) { + // Find the destructor + const Function *destructor = scope->getDestructor(); + + // Check for destructor with implementation + if (!destructor || !destructor->hasBody()) + continue; + + // Empty destructor + if (destructor->token->linkAt(3) == destructor->token->tokAt(4)) + continue; + } + + const Token *derived = scope->classDef; + const Token *derivedClass = derived->next(); + + // Iterate through each base class... + for (const Type::BaseInfo & j : scope->definedType->derivedFrom) { + // Check if base class is public and exists in database + if (j.access != AccessControl::Private && j.type) { + const Type *derivedFrom = j.type; + const Scope *derivedFromScope = derivedFrom->classScope; + if (!derivedFromScope) + continue; + + // Check for this pattern: + // 1. Base class pointer is given the address of derived class instance + // 2. Base class pointer is deleted + // + // If this pattern is not seen then bailout the checking of these base/derived classes + { + // pointer variables of type 'Base *' + std::set baseClassPointers; + + for (const Variable* var : mSymbolDatabase->variableList()) { + if (var && var->isPointer() && var->type() == derivedFrom) + baseClassPointers.insert(var->declarationId()); + } + + // pointer variables of type 'Base *' that should not be deleted + std::set dontDelete; + + // No deletion of derived class instance through base class pointer found => the code is ok + bool ok = true; + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (Token::Match(tok, "[;{}] %var% =") && + baseClassPointers.find(tok->next()->varId()) != baseClassPointers.end()) { + // new derived class.. + const std::string tmp("new " + derivedClass->str()); + if (Token::simpleMatch(tok->tokAt(3), tmp.c_str(), tmp.size())) { + dontDelete.insert(tok->next()->varId()); + } + } + + // Delete base class pointer that might point at derived class + else if (Token::Match(tok, "delete %var% ;") && + dontDelete.find(tok->next()->varId()) != dontDelete.end()) { + ok = false; + break; + } + } + + // No base class pointer that points at a derived class is deleted + if (ok) + continue; + } + + // Find the destructor declaration for the base class. + const Function *baseDestructor = derivedFromScope->getDestructor(); + + // Check that there is a destructor.. + if (!baseDestructor) { + if (derivedFrom->derivedFrom.empty()) { + virtualDestructorError(derivedFrom->classDef, derivedFrom->name(), derivedClass->str(), false); + } + } else if (!baseDestructor->hasVirtualSpecifier()) { + // TODO: This is just a temporary fix, better solution is needed. + // Skip situations where base class has base classes of its own, because + // some of the base classes might have virtual destructor. + // Proper solution is to check all of the base classes. If base class is not + // found or if one of the base classes has virtual destructor, error should not + // be printed. See TODO test case "virtualDestructorInherited" + if (derivedFrom->derivedFrom.empty()) { + // Make sure that the destructor is public (protected or private + // would not compile if inheritance is used in a way that would + // cause the bug we are trying to find here.) + if (baseDestructor->access == AccessControl::Public) { + virtualDestructorError(baseDestructor->token, derivedFrom->name(), derivedClass->str(), false); + // check for duplicate error and remove it if found + const std::list::iterator found = find(inconclusiveErrors.begin(), inconclusiveErrors.end(), baseDestructor); + if (found != inconclusiveErrors.end()) + inconclusiveErrors.erase(found); + } + } + } + } + } + } + + for (const Function *func : inconclusiveErrors) + virtualDestructorError(func->tokenDef, func->name(), emptyString, true); +} + +void CheckClass::virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive) +{ + if (inconclusive) { + if (mSettings->severity.isEnabled(Severity::warning)) + reportError(tok, Severity::warning, "virtualDestructor", "$symbol:" + Base + "\nClass '$symbol' which has virtual members does not have a virtual destructor.", CWE404, Certainty::inconclusive); + } else { + reportError(tok, Severity::error, "virtualDestructor", + "$symbol:" + Base +"\n" + "$symbol:" + Derived +"\n" + "Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor.\n" + "Class '" + Base + "' which is inherited by class '" + Derived + "' does not have a virtual destructor. " + "If you destroy instances of the derived class by deleting a pointer that points to the base class, only " + "the destructor of the base class is executed. Thus, dynamic memory that is managed by the derived class " + "could leak. This can be avoided by adding a virtual destructor to the base class.", CWE404, Certainty::normal); + } +} + +//--------------------------------------------------------------------------- +// warn for "this-x". The indented code may be "this->x" +//--------------------------------------------------------------------------- + +void CheckClass::thisSubtraction() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckClass::thisSubtraction"); // warning + + const Token *tok = mTokenizer->tokens(); + for (;;) { + tok = Token::findmatch(tok, "this - %name%"); + if (!tok) + break; + + if (tok->strAt(-1) != "*") + thisSubtractionError(tok); + + tok = tok->next(); + } +} + +void CheckClass::thisSubtractionError(const Token *tok) +{ + reportError(tok, Severity::warning, "thisSubtraction", "Suspicious pointer subtraction. Did you intend to write '->'?", CWE398, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// can member function be const? +//--------------------------------------------------------------------------- + +void CheckClass::checkConst() +{ + // This is an inconclusive check. False positives: #3322. + if (!mSettings->certainty.isEnabled(Certainty::inconclusive)) + return; + + if (!mSettings->severity.isEnabled(Severity::style) && + !mSettings->isPremiumEnabled("functionConst") && + !mSettings->isPremiumEnabled("functionStatic")) + return; + + logChecker("CheckClass::checkConst"); // style,inconclusive + + for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { + for (const Function &func : scope->functionList) { + // does the function have a body? + if (func.type != Function::eFunction || !func.hasBody()) + continue; + // don't warn for friend/static/virtual functions + if (func.isFriend() || func.isStatic() || func.hasVirtualSpecifier()) + continue; + + // don't suggest const when returning non-const pointer/reference, but still suggest static + auto isPointerOrReference = [this](const Token* start, const Token* end) -> bool { + bool inTemplArgList = false, isConstTemplArg = false; + for (const Token* tok = start; tok != end; tok = tok->next()) { + if (tok->str() == "{") // end of trailing return type + return false; + if (tok->str() == "<") { + if (!tok->link()) + mSymbolDatabase->debugMessage(tok, "debug", "CheckClass::checkConst found unlinked template argument list '" + tok->expressionString() + "'."); + inTemplArgList = true; + } + else if (tok->str() == ">") { + inTemplArgList = false; + isConstTemplArg = false; + } + else if (tok->str() == "const") { + if (!inTemplArgList) + return false; + isConstTemplArg = true; + } + else if (!isConstTemplArg && Token::Match(tok, "*|&")) + return true; + } + return false; + }; + + const bool returnsPtrOrRef = isPointerOrReference(func.retDef, func.tokenDef); + + if (Function::returnsPointer(&func, /*unknown*/ true) || Function::returnsReference(&func, /*unknown*/ true, /*includeRValueRef*/ true)) { // returns const/non-const depending on template arg + bool isTemplateArg = false; + for (const Token* tok2 = func.retDef; precedes(tok2, func.token); tok2 = tok2->next()) + if (tok2->isTemplateArg() && tok2->str() == "const") { + isTemplateArg = true; + break; + } + if (isTemplateArg) + continue; + } + + if (func.isOperator()) { // Operator without return type: conversion operator + const std::string& opName = func.tokenDef->str(); + if (opName.compare(8, 5, "const") != 0 && (endsWith(opName,'&') || endsWith(opName,'*'))) + continue; + } else if (mSettings->library.isSmartPointer(func.retDef)) { + // Don't warn if a std::shared_ptr etc is returned + continue; + } else { + // don't warn for unknown types.. + // LPVOID, HDC, etc + if (func.retDef->str().size() > 2 && !func.retDef->type() && func.retDef->isUpperCaseName()) + continue; + } + + // check if base class function is virtual + if (!scope->definedType->derivedFrom.empty() && func.isImplicitlyVirtual(true)) + continue; + + MemberAccess memberAccessed = MemberAccess::NONE; + // if nothing non-const was found. write error.. + if (!checkConstFunc(scope, &func, memberAccessed)) + continue; + + const bool suggestStatic = memberAccessed != MemberAccess::MEMBER && !func.isOperator(); + if ((returnsPtrOrRef || func.isConst()) && !suggestStatic) + continue; + + std::string classname = scope->className; + const Scope *nest = scope->nestedIn; + while (nest && nest->type != Scope::eGlobal) { + classname = std::string(nest->className + "::" + classname); + nest = nest->nestedIn; + } + + // get function name + std::string functionName = (func.tokenDef->isName() ? "" : "operator") + func.tokenDef->str(); + + if (func.tokenDef->str() == "(") + functionName += ")"; + else if (func.tokenDef->str() == "[") + functionName += "]"; + + if (func.isInline()) + checkConstError(func.token, classname, functionName, suggestStatic); + else // not inline + checkConstError2(func.token, func.tokenDef, classname, functionName, suggestStatic); + } + } +} + +// tok should point at "this" +static const Token* getFuncTokFromThis(const Token* tok) { + if (!Token::simpleMatch(tok->next(), ".")) + return nullptr; + tok = tok->tokAt(2); + while (Token::Match(tok, "%name% ::")) + tok = tok->tokAt(2); + return Token::Match(tok, "%name% (") ? tok : nullptr; +} + +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 !getFuncTokFromThis(tok); // function calls are handled elsewhere + if (Token::simpleMatch(tok->tokAt(-3), "( * this )")) + return true; + if (Token::Match(tok->tokAt(-3), "%name% ) . %name%")) { + tok = tok->tokAt(-3); + again = true; + } else if (Token::Match(tok->tokAt(-2), "%name% . %name%")) { + tok = tok->tokAt(-2); + again = true; + } else if (Token::Match(tok->tokAt(-2), "] . %name%")) { + tok = tok->linkAt(-2)->previous(); + again = true; + } else if (tok->str() == "]") { + tok = tok->link()->previous(); + again = true; + } + } while (again); + + if (tok->isKeyword() || tok->isStandardType()) + return false; + + for (const Variable& var : scope->varlist) { + if (var.name() == tok->str()) { + if (Token::Match(tok, "%name% ::")) + continue; + const Token* fqTok = tok; + while (Token::Match(fqTok->tokAt(-2), "%name% ::")) + fqTok = fqTok->tokAt(-2); + if (fqTok->strAt(-1) == "::") + fqTok = fqTok->previous(); + bool isMember = tok == fqTok; + std::string scopeStr; + const Scope* curScope = scope; + while (!isMember && curScope && curScope->type != Scope::ScopeType::eGlobal) { + scopeStr.insert(0, curScope->className + " :: "); + isMember = Token::Match(fqTok, scopeStr.c_str()); + + curScope = curScope->nestedIn; + } + if (isMember) { + if (tok->varId() == 0) + mSymbolDatabase->debugMessage(tok, "varid0", "CheckClass::isMemberVar found used member variable \'" + tok->str() + "\' with varid 0"); + + return !var.isStatic(); + } + } + } + + // not found in this class + if (!scope->definedType->derivedFrom.empty()) { + // check each base class + for (const Type::BaseInfo & i : scope->definedType->derivedFrom) { + // find the base class + const Type *derivedFrom = i.type; + + // find the function in the base class + if (derivedFrom && derivedFrom->classScope && derivedFrom->classScope != scope) { + if (isMemberVar(derivedFrom->classScope, tok)) + return true; + } + } + } + + return false; +} + +bool CheckClass::isMemberFunc(const Scope *scope, const Token *tok) +{ + if (!tok->function()) { + for (const Function &func : scope->functionList) { + if (func.name() == tok->str()) { + const Token* tok2 = tok->tokAt(2); + int argsPassed = tok2->str() == ")" ? 0 : 1; + for (;;) { + tok2 = tok2->nextArgument(); + if (tok2) + argsPassed++; + else + break; + } + if (argsPassed == func.argCount() || + (func.isVariadic() && argsPassed >= (func.argCount() - 1)) || + (argsPassed < func.argCount() && argsPassed >= func.minArgCount())) + return true; + } + } + } else if (tok->function()->nestedIn == scope) + return !tok->function()->isStatic(); + + // not found in this class + if (!scope->definedType->derivedFrom.empty()) { + // check each base class + for (const Type::BaseInfo & i : scope->definedType->derivedFrom) { + // find the base class + const Type *derivedFrom = i.type; + + // find the function in the base class + if (derivedFrom && derivedFrom->classScope && derivedFrom->classScope != scope) { + if (isMemberFunc(derivedFrom->classScope, tok)) + return true; + } + } + } + + return false; +} + +bool CheckClass::isConstMemberFunc(const Scope *scope, const Token *tok) +{ + if (!tok->function()) + return false; + if (tok->function()->nestedIn == scope) + return tok->function()->isConst(); + + // not found in this class + if (!scope->definedType->derivedFrom.empty()) { + // check each base class + for (const Type::BaseInfo & i : scope->definedType->derivedFrom) { + // find the base class + const Type *derivedFrom = i.type; + + // find the function in the base class + if (derivedFrom && derivedFrom->classScope) { + if (isConstMemberFunc(derivedFrom->classScope, tok)) + return true; + } + } + } + + return false; +} + +const std::set CheckClass::stl_containers_not_const = { "map", "unordered_map", "std :: map|unordered_map <" }; // start pattern + +bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, MemberAccess& memberAccessed) const +{ + if (mTokenizer->hasIfdef(func->functionScope->bodyStart, func->functionScope->bodyEnd)) + return false; + + auto getFuncTok = [](const Token* tok) -> const Token* { + if (Token::simpleMatch(tok, "this")) + tok = getFuncTokFromThis(tok); + bool isReturn = false; + if ((Token::Match(tok, "%name% (|{") || (isReturn = Token::simpleMatch(tok->astParent(), "return {"))) && !tok->isStandardType() && !tok->isKeyword()) { + if (isReturn) + tok = tok->astParent(); + return tok; + } + return nullptr; + }; + + auto checkFuncCall = [this, &memberAccessed](const Token* funcTok, const Scope* scope, const Function* func) { + if (isMemberFunc(scope, funcTok) && (funcTok->strAt(-1) != "." || Token::simpleMatch(funcTok->tokAt(-2), "this ."))) { + const bool isSelf = func == funcTok->function(); + if (!isConstMemberFunc(scope, funcTok) && !isSelf) + return false; + memberAccessed = (isSelf && memberAccessed != MemberAccess::MEMBER) ? MemberAccess::SELF : MemberAccess::MEMBER; + } + + if (const Function* f = funcTok->function()) { // check known function + const std::vector args = getArguments(funcTok); + const auto argMax = std::min(args.size(), f->argCount()); + + for (nonneg int argIndex = 0; argIndex < argMax; ++argIndex) { + const Variable* const argVar = f->getArgumentVar(argIndex); + if (!argVar || ((argVar->isArrayOrPointer() || argVar->isReference()) && + !(argVar->valueType() && argVar->valueType()->isConst(argVar->valueType()->pointer)))) { // argument might be modified + const Token* arg = args[argIndex]; + // Member variable given as parameter + const Token* varTok = previousBeforeAstLeftmostLeaf(arg); + if (!varTok) + return false; + varTok = varTok->next(); + if ((varTok->isName() && isMemberVar(scope, varTok)) || (varTok->isUnaryOp("&") && (varTok = varTok->astOperand1()) && isMemberVar(scope, varTok))) { + const Variable* var = varTok->variable(); + if (!var || (!var->isMutable() && !var->isConst())) + return false; + } + } + } + return true; + } + + // Member variable given as parameter to unknown function + const Token *lpar = funcTok->next(); + if (Token::simpleMatch(lpar, "( ) (")) + lpar = lpar->tokAt(2); + for (const Token* tok = lpar->next(); tok && tok != funcTok->next()->link(); tok = tok->next()) { + if (tok->str() == "(") + tok = tok->link(); + else if ((tok->isName() && isMemberVar(scope, tok)) || (tok->isUnaryOp("&") && (tok = tok->astOperand1()) && isMemberVar(scope, tok))) { + const Variable* var = tok->variable(); + if (!var || (!var->isMutable() && !var->isConst())) + return false; + } + } + return true; + }; + + // if the function doesn't have any assignment nor function call, + // it can be a const function.. + for (const Token *tok1 = func->functionScope->bodyStart; tok1 && tok1 != func->functionScope->bodyEnd; tok1 = tok1->next()) { + if (tok1->isName() && isMemberVar(scope, tok1)) { + memberAccessed = MemberAccess::MEMBER; + const Variable* v = tok1->variable(); + if (v && v->isMutable()) + continue; + + if (tok1->str() == "this") { + if (tok1->previous()->isAssignmentOp()) + return false; + if (Token::Match(tok1->previous(), "( this . * %var% )")) // call using ptr to member function TODO: check constness + return false; + if (Token::simpleMatch(tok1->astParent(), "*") && tok1->astParent()->astParent() && tok1->astParent()->astParent()->isIncDecOp()) + return false; + } + + // non const pointer cast + if (tok1->valueType() && tok1->valueType()->pointer > 0 && tok1->astParent() && tok1->astParent()->isCast() && + !(tok1->astParent()->valueType() && + (tok1->astParent()->valueType()->pointer == 0 || tok1->astParent()->valueType()->isConst(tok1->astParent()->valueType()->pointer)))) + return false; + + const Token* lhs = tok1->previous(); + if (lhs->str() == "(" && tok1->astParent() && tok1->astParent()->astParent()) + lhs = tok1->astParent()->astParent(); + else if (lhs->str() == "?" && lhs->astParent()) + lhs = lhs->astParent(); + else if (lhs->str() == ":" && lhs->astParent() && lhs->astParent()->astParent() && lhs->astParent()->str() == "?") + lhs = lhs->astParent()->astParent(); + if (lhs->str() == "&") { + const Token* const top = lhs->astTop(); + if (top->isAssignmentOp()) { + if (Token::simpleMatch(top->astOperand2(), "{") && !top->astOperand2()->previous()->function()) // TODO: check usage in init list + return false; + if (top->previous()->variable()) { + if (top->previous()->variable()->typeStartToken()->strAt(-1) != "const" && top->previous()->variable()->isPointer()) + return false; + } + } + } else if (lhs->str() == ":" && lhs->astParent() && lhs->astParent()->str() == "(") { // range-based for-loop (C++11) + // TODO: We could additionally check what is done with the elements to avoid false negatives. Here we just rely on "const" keyword being used. + if (lhs->astParent()->strAt(1) != "const") + return false; + } else { + if (lhs->isAssignmentOp()) { + const Variable* lhsVar = lhs->previous()->variable(); + if (lhsVar && !lhsVar->isConst() && lhsVar->isReference() && lhs == lhsVar->nameToken()->next()) + return false; + } + } + + const Token* jumpBackToken = nullptr; + const Token *lastVarTok = tok1; + const Token *end = tok1; + for (;;) { + if (Token::Match(end->next(), ". %name%")) { + end = end->tokAt(2); + if (end->varId()) + lastVarTok = end; + } else if (end->strAt(1) == "[") { + if (end->varId()) { + const Variable *var = end->variable(); + if (var && var->isStlType(stl_containers_not_const)) + return false; + const Token* assignTok = end->next()->astParent(); + if (var && assignTok && assignTok->isAssignmentOp() && assignTok->astOperand1() && assignTok->astOperand1()->variable()) { + // cppcheck-suppress shadowFunction - TODO: fix this + const Variable* assignVar = assignTok->astOperand1()->variable(); + if (assignVar->isPointer() && !assignVar->isConst() && var->typeScope()) { + const auto& funcMap = var->typeScope()->functionMap; + // if there is no operator that is const and returns a non-const pointer, func cannot be const + if (std::none_of(funcMap.cbegin(), funcMap.cend(), [](const std::pair& fm) { + return fm.second->isConst() && fm.first == "operator[]" && !Function::returnsConst(fm.second); + })) + 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; + } + + auto hasOverloadedMemberAccess = [](const Token* end, const Scope* scope) -> bool { + if (!end || !scope || !Token::simpleMatch(end->astParent(), ".")) + return false; + const std::string op = "operator" + end->astParent()->originalName(); + auto it = std::find_if(scope->functionList.begin(), scope->functionList.end(), [&op](const Function& f) { + return f.isConst() && f.name() == op; + }); + if (it == scope->functionList.end() || !it->retType || !it->retType->classScope) + return false; + const Function* func = it->retType->classScope->findFunction(end, /*requireConst*/ true); + return func && func->isConst(); + }; + + 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"))) || + + (lastVarTok->valueType() && lastVarTok->valueType()->container && + ((lastVarTok->valueType()->container->getYield(end->str()) == Library::Container::Yield::START_ITERATOR) || + (lastVarTok->valueType()->container->getYield(end->str()) == Library::Container::Yield::END_ITERATOR)) + && (tok1->previous()->isComparisonOp() || + (tok1->previous()->isAssignmentOp() && tok1->tokAt(-2)->variable() && Token::Match(tok1->tokAt(-2)->variable()->typeEndToken(), "const_iterator|const_reverse_iterator"))))) { + // empty body + } + else if (var->smartPointerType() && var->smartPointerType()->classScope && isConstMemberFunc(var->smartPointerType()->classScope, end)) { + // empty body + } else if (var->isSmartPointer() && Token::simpleMatch(tok1->next(), ".") && tok1->next()->originalName().empty() && mSettings->library.isFunctionConst(end)) { + // empty body + } else if (hasOverloadedMemberAccess(end, var->typeScope())) { + // empty body + } else if (!var->typeScope() || (end->function() != func && !isConstMemberFunc(var->typeScope(), end))) { + if (!mSettings->library.isFunctionConst(end)) + return false; + } + } + + // Assignment + else if (end->next()->isAssignmentOp()) + return false; + + // Streaming + else if (end->strAt(1) == "<<" && tok1->strAt(-1) != "<<") + return false; + else if (isLikelyStreamRead(tok1->previous())) + return false; + + // ++/-- + else if (end->next()->tokType() == Token::eIncDecOp || tok1->previous()->tokType() == Token::eIncDecOp) + return false; + + + const Token* start = tok1; + while (tok1->strAt(-1) == ")") + tok1 = tok1->linkAt(-1); + + if (start->strAt(-1) == "delete") + return false; + + tok1 = jumpBackToken?jumpBackToken:end; // Jump back to first [ to check inside, or jump to end of expression + if (tok1 == end && Token::Match(end->previous(), ". %name% ( !!)") && !checkFuncCall(tok1, scope, func)) // function call on member + return false; + } + + // streaming: << + else if (Token::simpleMatch(tok1->previous(), ") <<") && + isMemberVar(scope, tok1->tokAt(-2))) { + const Variable* var = tok1->tokAt(-2)->variable(); + if (!var || !var->isMutable()) + return false; + } + + // streaming: >> *this + else if (Token::simpleMatch(tok1, ">> * this") && isLikelyStreamRead(tok1)) { + return false; + } + + // function/constructor call, return init list + else if (const Token* funcTok = getFuncTok(tok1)) { + if (!checkFuncCall(funcTok, scope, func)) + return false; + } else if (Token::simpleMatch(tok1, "> (") && (!tok1->link() || !Token::Match(tok1->link()->previous(), "static_cast|const_cast|dynamic_cast|reinterpret_cast"))) { + return false; + } + } + + return true; +} + +void CheckClass::checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic) +{ + checkConstError2(tok, nullptr, classname, funcname, suggestStatic); +} + +void CheckClass::checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic) +{ + std::list toks{ tok1 }; + if (tok2) + toks.push_back(tok2); + if (!suggestStatic) + reportError(toks, Severity::style, "functionConst", + "$symbol:" + classname + "::" + funcname +"\n" + "Technically the member function '$symbol' can be const.\n" + "The member function '$symbol' can be made a const " + "function. Making this function 'const' should not cause compiler errors. " + "Even though the function can be made const function technically it may not make " + "sense conceptually. Think about your design and the task of the function first - is " + "it a function that must not change object internal state?", CWE398, Certainty::inconclusive); + else + reportError(toks, Severity::performance, "functionStatic", + "$symbol:" + classname + "::" + funcname +"\n" + "Technically the member function '$symbol' can be static (but you may consider moving to unnamed namespace).\n" + "The member function '$symbol' can be made a static " + "function. Making a function static can bring a performance benefit since no 'this' instance is " + "passed to the function. This change should not cause compiler errors but it does not " + "necessarily make sense conceptually. Think about your design and the task of the function first - " + "is it a function that must not access members of class instances? And maybe it is more appropriate " + "to move this function to an unnamed namespace.", CWE398, Certainty::inconclusive); +} + +//--------------------------------------------------------------------------- +// ClassCheck: Check that initializer list is in declared order. +//--------------------------------------------------------------------------- + +namespace { // avoid one-definition-rule violation + struct VarInfo { + VarInfo(const Variable *_var, const Token *_tok) + : var(_var), tok(_tok) {} + + const Variable *var; + const Token *tok; + std::vector initArgs; + }; +} + +void CheckClass::initializerListOrder() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("initializerList")) + return; + + // This check is not inconclusive. However it only determines if the initialization + // order is incorrect. It does not determine if being out of order causes + // a real error. Out of order is not necessarily an error but you can never + // have an error if the list is in order so this enforces defensive programming. + if (!mSettings->certainty.isEnabled(Certainty::inconclusive)) + return; + + logChecker("CheckClass::initializerListOrder"); // style,inconclusive + + for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { + + // iterate through all member functions looking for constructors + for (std::list::const_iterator func = scope->functionList.cbegin(); func != scope->functionList.cend(); ++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 + for (; tok && tok != func->functionScope->bodyStart; tok = tok->next()) { + if (Token::Match(tok, "%name% (|{")) { + const Token* const end = tok->linkAt(1); + const Variable *var = scope->getVariable(tok->str()); + if (var) + vars.emplace_back(var, tok); + else + tok = end; + + for (; tok != end; tok = tok->next()) { + if (const Variable* argVar = scope->getVariable(tok->str())) { + if (scope != argVar->scope()) + continue; + if (argVar->isStatic()) + continue; + if (tok->variable() && tok->variable()->isArgument()) + continue; + if (var->isPointer() && (argVar->isArray() || Token::simpleMatch(tok->astParent(), "&"))) + continue; + if (var->isReference()) + continue; + if (Token::simpleMatch(tok->astParent(), "=")) + continue; + vars.back().initArgs.emplace_back(argVar); + } + } + } + } + + for (int j = 0; j < vars.size(); j++) { + // check for use of uninitialized arguments + for (const auto& arg : vars[j].initArgs) + if (vars[j].var->index() < arg->index()) + initializerListError(vars[j].tok, vars[j].var->nameToken(), scope->className, vars[j].var->name(), arg->name()); + + // need at least 2 members to have out of order initialization + if (j == 0) + continue; + // check for out of order initialization + if (vars[j].var->index() < vars[j - 1].var->index()) + initializerListError(vars[j].tok,vars[j].var->nameToken(), scope->className, vars[j].var->name()); + } + } + } + } + } +} + +void CheckClass::initializerListError(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &varname, const std::string& argname) +{ + std::list toks = { tok1, tok2 }; + const std::string msg = argname.empty() ? + "Member variable '$symbol' is in the wrong place in the initializer list." : + "Member variable '$symbol' uses an uninitialized argument '" + argname + "' due to the order of declarations."; + reportError(toks, Severity::style, "initializerList", + "$symbol:" + classname + "::" + varname + '\n' + + msg + '\n' + + msg + ' ' + + "Members are initialized in the order they are declared, not in the " + "order they are in the initializer list. Keeping the initializer list " + "in the same order that the members were declared prevents order dependent " + "initialization errors.", CWE398, Certainty::inconclusive); +} + + +//--------------------------------------------------------------------------- +// Check for self initialization in initialization list +//--------------------------------------------------------------------------- + +void CheckClass::checkSelfInitialization() +{ + logChecker("CheckClass::checkSelfInitialization"); + + for (const Scope *scope : mSymbolDatabase->functionScopes) { + const Function* function = scope->function; + if (!function || !function->isConstructor()) + continue; + + const Token* tok = function->arg->link()->next(); + if (tok->str() != ":") + continue; + + for (; tok != scope->bodyStart; tok = tok->next()) { + if (Token::Match(tok, "[:,] %var% (|{")) { + const Token* varTok = tok->next(); + if (Token::Match(varTok->astParent(), "(|{")) { + if (const Token* initTok = varTok->astParent()->astOperand2()) { + if (initTok->varId() == varTok->varId()) + selfInitializationError(tok, varTok->str()); + else if (initTok->isCast() && ((initTok->astOperand1() && initTok->astOperand1()->varId() == varTok->varId()) || (initTok->astOperand2() && initTok->astOperand2()->varId() == varTok->varId()))) + selfInitializationError(tok, varTok->str()); + } + } + } + } + } +} + +void CheckClass::selfInitializationError(const Token* tok, const std::string& varname) +{ + reportError(tok, Severity::error, "selfInitialization", "$symbol:" + varname + "\nMember variable '$symbol' is initialized by itself.", CWE665, Certainty::normal); +} + + +//--------------------------------------------------------------------------- +// Check for virtual function calls in constructor/destructor +//--------------------------------------------------------------------------- + +void CheckClass::checkVirtualFunctionCallInConstructor() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + logChecker("CheckClass::checkVirtualFunctionCallInConstructor"); // warning + std::map> virtualFunctionCallsMap; + for (const Scope *scope : mSymbolDatabase->functionScopes) { + if (scope->function == nullptr || !scope->function->hasBody() || + !(scope->function->isConstructor() || + scope->function->isDestructor())) + continue; + + const std::list & virtualFunctionCalls = getVirtualFunctionCalls(*scope->function, virtualFunctionCallsMap); + for (const Token *callToken : virtualFunctionCalls) { + std::list callstack(1, callToken); + getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, callToken, callstack); + if (callstack.empty()) + continue; + const Function* const func = callstack.back()->function(); + if (!(func->hasVirtualSpecifier() || func->hasOverrideSpecifier())) + continue; + if (func->isPure()) + pureVirtualFunctionCallInConstructorError(scope->function, callstack, callstack.back()->str()); + else if (!func->hasFinalSpecifier() && + !(func->nestedIn && func->nestedIn->classDef && func->nestedIn->classDef->isFinalType())) + virtualFunctionCallInConstructorError(scope->function, callstack, callstack.back()->str()); + } + } +} + +const std::list & CheckClass::getVirtualFunctionCalls(const Function & function, + std::map> & virtualFunctionCallsMap) +{ + const std::map>::const_iterator found = virtualFunctionCallsMap.find(&function); + if (found != virtualFunctionCallsMap.end()) + return found->second; + + virtualFunctionCallsMap[&function] = std::list(); + std::list & virtualFunctionCalls = virtualFunctionCallsMap.find(&function)->second; + + if (!function.hasBody() || !function.functionScope) + return virtualFunctionCalls; + + for (const Token *tok = function.arg->link(); tok != function.functionScope->bodyEnd; tok = tok->next()) { + if (function.type != Function::eConstructor && + function.type != Function::eCopyConstructor && + function.type != Function::eMoveConstructor && + function.type != Function::eDestructor) { + if ((Token::simpleMatch(tok, ") {") && tok->link() && Token::Match(tok->link()->previous(), "if|switch")) || + Token::simpleMatch(tok, "else {")) { + // Assume pure virtual function call is prevented by "if|else|switch" condition + tok = tok->linkAt(1); + continue; + } + } + if (tok->scope()->type == Scope::eLambda) + tok = tok->scope()->bodyEnd->next(); + + const Function * callFunction = tok->function(); + if (!callFunction || + function.nestedIn != callFunction->nestedIn || + Token::simpleMatch(tok->previous(), ".") || + !(tok->astParent() && (tok->astParent()->str() == "(" || (tok->astParent()->str() == "::" && Token::simpleMatch(tok->astParent()->astParent(), "("))))) + continue; + + if (tok->previous() && + tok->previous()->str() == "(") { + const Token * prev = tok->previous(); + if (prev->previous() && + (mSettings->library.ignorefunction(tok->str()) + || mSettings->library.ignorefunction(prev->previous()->str()))) + continue; + } + + if (callFunction->isImplicitlyVirtual()) { + if (!callFunction->isPure() && Token::simpleMatch(tok->previous(), "::")) + continue; + virtualFunctionCalls.push_back(tok); + continue; + } + + const std::list & virtualFunctionCallsOfTok = getVirtualFunctionCalls(*callFunction, virtualFunctionCallsMap); + if (!virtualFunctionCallsOfTok.empty()) + virtualFunctionCalls.push_back(tok); + } + return virtualFunctionCalls; +} + +void CheckClass::getFirstVirtualFunctionCallStack( + std::map> & virtualFunctionCallsMap, + const Token * callToken, + std::list & pureFuncStack) +{ + const Function *callFunction = callToken->function(); + if (callFunction->isImplicitlyVirtual() && (!callFunction->isPure() || !callFunction->hasBody())) { + pureFuncStack.push_back(callFunction->tokenDef); + return; + } + std::map>::const_iterator found = virtualFunctionCallsMap.find(callFunction); + if (found == virtualFunctionCallsMap.cend() || found->second.empty()) { + pureFuncStack.clear(); + return; + } + const Token * firstCall = *found->second.cbegin(); + pureFuncStack.push_back(firstCall); + getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, firstCall, pureFuncStack); +} + +void CheckClass::virtualFunctionCallInConstructorError( + const Function * scopeFunction, + const std::list & tokStack, + const std::string &funcname) +{ + if (scopeFunction && !mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("virtualCallInConstructor")) + return; + + const char * scopeFunctionTypeName = scopeFunction ? getFunctionTypeName(scopeFunction->type) : "constructor"; + + ErrorPath errorPath; + std::transform(tokStack.cbegin(), tokStack.cend(), std::back_inserter(errorPath), [](const Token* tok) { + return ErrorPathItem(tok, "Calling " + tok->str()); + }); + int lineNumber = 1; + if (!errorPath.empty()) { + lineNumber = errorPath.front().first->linenr(); + errorPath.back().second = funcname + " is a virtual function"; + } + + std::string constructorName; + if (scopeFunction) { + const Token *endToken = scopeFunction->argDef->link()->next(); + if (scopeFunction->type == Function::Type::eDestructor) + constructorName = "~"; + for (const Token *tok = scopeFunction->tokenDef; tok != endToken; tok = tok->next()) { + if (!constructorName.empty() && Token::Match(tok->previous(), "%name%|%num% %name%|%num%")) + constructorName += ' '; + constructorName += tok->str(); + if (tok->str() == ")") + break; + } + } + + reportError(errorPath, Severity::style, "virtualCallInConstructor", + "Virtual function '" + funcname + "' is called from " + scopeFunctionTypeName + " '" + constructorName + "' at line " + std::to_string(lineNumber) + ". Dynamic binding is not used.", CWE(0U), Certainty::normal); +} + +void CheckClass::pureVirtualFunctionCallInConstructorError( + const Function * scopeFunction, + const std::list & tokStack, + const std::string &purefuncname) +{ + const char * scopeFunctionTypeName = scopeFunction ? getFunctionTypeName(scopeFunction->type) : "constructor"; + + ErrorPath errorPath; + std::transform(tokStack.cbegin(), tokStack.cend(), std::back_inserter(errorPath), [](const Token* tok) { + return ErrorPathItem(tok, "Calling " + tok->str()); + }); + if (!errorPath.empty()) + errorPath.back().second = purefuncname + " is a pure virtual function without body"; + + reportError(errorPath, Severity::warning, "pureVirtualCall", + "$symbol:" + purefuncname +"\n" + "Call of pure virtual function '$symbol' in " + scopeFunctionTypeName + ".\n" + "Call of pure virtual function '$symbol' in " + scopeFunctionTypeName + ". The call will fail during runtime.", CWE(0U), Certainty::normal); +} + + +//--------------------------------------------------------------------------- +// Check for members hiding inherited members with the same name +//--------------------------------------------------------------------------- + +void CheckClass::checkDuplInheritedMembers() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckClass::checkDuplInheritedMembers"); // warning + + // Iterate over all classes + for (const Type &classIt : mSymbolDatabase->typeList) { + // Iterate over the parent classes + checkDuplInheritedMembersRecursive(&classIt, &classIt); + } +} + +namespace { + struct DuplMemberInfo { + DuplMemberInfo(const Variable* cv, const Variable* pcv, const Type::BaseInfo* pc) : classVar(cv), parentClassVar(pcv), parentClass(pc) {} + const Variable* classVar; + const Variable* parentClassVar; + const Type::BaseInfo* parentClass; + }; + struct DuplMemberFuncInfo { + DuplMemberFuncInfo(const Function* cf, const Function* pcf, const Type::BaseInfo* pc) : classFunc(cf), parentClassFunc(pcf), parentClass(pc) {} + const Function* classFunc; + const Function* parentClassFunc; + const Type::BaseInfo* parentClass; + }; +} + +static std::vector getDuplInheritedMembersRecursive(const Type* typeCurrent, const Type* typeBase, bool skipPrivate = true) +{ + std::vector results; + for (const Type::BaseInfo &parentClassIt : typeBase->derivedFrom) { + // Check if there is info about the 'Base' class + if (!parentClassIt.type || !parentClassIt.type->classScope) + continue; + // Don't crash on recursive templates + if (parentClassIt.type == typeBase) + continue; + // Check if they have a member variable in common + for (const Variable &classVarIt : typeCurrent->classScope->varlist) { + for (const Variable &parentClassVarIt : parentClassIt.type->classScope->varlist) { + if (classVarIt.name() == parentClassVarIt.name() && (!parentClassVarIt.isPrivate() || !skipPrivate)) // Check if the class and its parent have a common variable + results.emplace_back(&classVarIt, &parentClassVarIt, &parentClassIt); + } + } + if (typeCurrent != parentClassIt.type) { + const auto recursive = getDuplInheritedMembersRecursive(typeCurrent, parentClassIt.type, skipPrivate); + results.insert(results.end(), recursive.begin(), recursive.end()); + } + } + return results; +} + +static std::vector getDuplInheritedMemberFunctionsRecursive(const Type* typeCurrent, const Type* typeBase, bool skipPrivate = true) +{ + std::vector results; + for (const Type::BaseInfo &parentClassIt : typeBase->derivedFrom) { + // Check if there is info about the 'Base' class + if (!parentClassIt.type || !parentClassIt.type->classScope) + continue; + // Don't crash on recursive templates + if (parentClassIt.type == typeBase) + continue; + for (const Function& classFuncIt : typeCurrent->classScope->functionList) { + if (classFuncIt.isImplicitlyVirtual()) + continue; + for (const Function& parentClassFuncIt : parentClassIt.type->classScope->functionList) { + if (classFuncIt.name() == parentClassFuncIt.name() && + (parentClassFuncIt.access != AccessControl::Private || !skipPrivate) && + !classFuncIt.isConstructor() && !classFuncIt.isDestructor() && + classFuncIt.argsMatch(parentClassIt.type->classScope, parentClassFuncIt.argDef, classFuncIt.argDef, emptyString, 0) && + (classFuncIt.isConst() == parentClassFuncIt.isConst() || Function::returnsConst(&classFuncIt) == Function::returnsConst(&parentClassFuncIt)) && + !(classFuncIt.isDelete() || parentClassFuncIt.isDelete())) + results.emplace_back(&classFuncIt, &parentClassFuncIt, &parentClassIt); + } + } + if (typeCurrent != parentClassIt.type) { + const auto recursive = getDuplInheritedMemberFunctionsRecursive(typeCurrent, parentClassIt.type); + results.insert(results.end(), recursive.begin(), recursive.end()); + } + } + return results; +} + +void CheckClass::checkDuplInheritedMembersRecursive(const Type* typeCurrent, const Type* typeBase) +{ + const auto resultsVar = getDuplInheritedMembersRecursive(typeCurrent, typeBase); + for (const auto& r : resultsVar) { + duplInheritedMembersError(r.classVar->nameToken(), r.parentClassVar->nameToken(), + typeCurrent->name(), r.parentClass->type->name(), r.classVar->name(), + typeCurrent->classScope->type == Scope::eStruct, + r.parentClass->type->classScope->type == Scope::eStruct); + } + + const auto resultsFunc = getDuplInheritedMemberFunctionsRecursive(typeCurrent, typeBase); + for (const auto& r : resultsFunc) { + duplInheritedMembersError(r.classFunc->token, r.parentClassFunc->token, + typeCurrent->name(), r.parentClass->type->name(), r.classFunc->name(), + typeCurrent->classScope->type == Scope::eStruct, + r.parentClass->type->classScope->type == Scope::eStruct, /*isFunction*/ true); + } +} + +void CheckClass::duplInheritedMembersError(const Token *tok1, const Token* tok2, + const std::string &derivedName, const std::string &baseName, + const std::string &memberName, bool derivedIsStruct, bool baseIsStruct, bool isFunction) +{ + ErrorPath errorPath; + const std::string member = isFunction ? "function" : "variable"; + errorPath.emplace_back(tok2, "Parent " + member + " '" + baseName + "::" + memberName + "'"); + errorPath.emplace_back(tok1, "Derived " + member + " '" + derivedName + "::" + memberName + "'"); + + const std::string symbols = "$symbol:" + derivedName + "\n$symbol:" + memberName + "\n$symbol:" + baseName; + + const std::string message = "The " + std::string(derivedIsStruct ? "struct" : "class") + " '" + derivedName + + "' defines member " + member + " with name '" + memberName + "' also defined in its parent " + + std::string(baseIsStruct ? "struct" : "class") + " '" + baseName + "'."; + reportError(errorPath, Severity::warning, "duplInheritedMember", symbols + '\n' + message, CWE398, Certainty::normal); +} + + +//--------------------------------------------------------------------------- +// Check that copy constructor and operator defined together +//--------------------------------------------------------------------------- + +enum class CtorType { + NO, + WITHOUT_BODY, + WITH_BODY +}; + +void CheckClass::checkCopyCtorAndEqOperator() +{ + // This is disabled because of #8388 + // The message must be clarified. How is the behaviour different? + // cppcheck-suppress unreachableCode - remove when code is enabled again + if ((true) || !mSettings->severity.isEnabled(Severity::warning)) // NOLINT(readability-simplify-boolean-expr) + return; + + // logChecker + + for (const Scope * scope : mSymbolDatabase->classAndStructScopes) { + + const bool hasNonStaticVars = std::any_of(scope->varlist.begin(), scope->varlist.end(), [](const Variable& var) { + return !var.isStatic(); + }); + if (!hasNonStaticVars) + continue; + + CtorType copyCtors = CtorType::NO; + bool moveCtor = false; + CtorType assignmentOperators = CtorType::NO; + + for (const Function &func : scope->functionList) { + if (copyCtors == CtorType::NO && func.type == Function::eCopyConstructor) { + copyCtors = func.hasBody() ? CtorType::WITH_BODY : CtorType::WITHOUT_BODY; + } + if (assignmentOperators == CtorType::NO && func.type == Function::eOperatorEqual) { + const Variable * variable = func.getArgumentVar(0); + if (variable && variable->type() && variable->type()->classScope == scope) { + assignmentOperators = func.hasBody() ? CtorType::WITH_BODY : CtorType::WITHOUT_BODY; + } + } + if (func.type == Function::eMoveConstructor) { + moveCtor = true; + break; + } + } + + if (moveCtor) + continue; + + // No method defined + if (copyCtors != CtorType::WITH_BODY && assignmentOperators != CtorType::WITH_BODY) + continue; + + // both methods are defined + if (copyCtors != CtorType::NO && assignmentOperators != CtorType::NO) + continue; + + copyCtorAndEqOperatorError(scope->classDef, scope->className, scope->type == Scope::eStruct, copyCtors == CtorType::WITH_BODY); + } +} + +void CheckClass::copyCtorAndEqOperatorError(const Token *tok, const std::string &classname, bool isStruct, bool hasCopyCtor) +{ + const std::string message = "$symbol:" + classname + "\n" + "The " + std::string(isStruct ? "struct" : "class") + " '$symbol' has '" + + getFunctionTypeName(hasCopyCtor ? Function::eCopyConstructor : Function::eOperatorEqual) + + "' but lack of '" + getFunctionTypeName(hasCopyCtor ? Function::eOperatorEqual : Function::eCopyConstructor) + + "'."; + reportError(tok, Severity::warning, "copyCtorAndEqOperator", message); +} + +void CheckClass::checkOverride() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("missingOverride")) + return; + if (mSettings->standards.cpp < Standards::CPP11) + return; + logChecker("CheckClass::checkMissingOverride"); // style,c++03 + for (const Scope * classScope : mSymbolDatabase->classAndStructScopes) { + if (!classScope->definedType || classScope->definedType->derivedFrom.empty()) + continue; + for (const Function &func : classScope->functionList) { + if (func.hasOverrideSpecifier() || func.hasFinalSpecifier()) + continue; + const Function *baseFunc = func.getOverriddenFunction(); + if (baseFunc) + overrideError(baseFunc, &func); + } + } +} + +void CheckClass::overrideError(const Function *funcInBase, const Function *funcInDerived) +{ + const std::string functionName = funcInDerived ? ((funcInDerived->isDestructor() ? "~" : "") + funcInDerived->name()) : ""; + const std::string funcType = (funcInDerived && funcInDerived->isDestructor()) ? "destructor" : "function"; + + ErrorPath errorPath; + if (funcInBase && funcInDerived) { + errorPath.emplace_back(funcInBase->tokenDef, "Virtual " + funcType + " in base class"); + errorPath.emplace_back(funcInDerived->tokenDef, char(std::toupper(funcType[0])) + funcType.substr(1) + " in derived class"); + } + + reportError(errorPath, Severity::style, "missingOverride", + "$symbol:" + functionName + "\n" + "The " + funcType + " '$symbol' overrides a " + funcType + " in a base class but is not marked with a 'override' specifier.", + CWE(0U) /* Unknown CWE! */, + Certainty::normal); +} + +void CheckClass::uselessOverrideError(const Function *funcInBase, const Function *funcInDerived, bool isSameCode) +{ + const std::string functionName = funcInDerived ? ((funcInDerived->isDestructor() ? "~" : "") + funcInDerived->name()) : ""; + const std::string funcType = (funcInDerived && funcInDerived->isDestructor()) ? "destructor" : "function"; + + ErrorPath errorPath; + if (funcInBase && funcInDerived) { + errorPath.emplace_back(funcInBase->tokenDef, "Virtual " + funcType + " in base class"); + errorPath.emplace_back(funcInDerived->tokenDef, char(std::toupper(funcType[0])) + funcType.substr(1) + " in derived class"); + } + + std::string errStr = "\nThe " + funcType + " '$symbol' overrides a " + funcType + " in a base class but "; + if (isSameCode) { + errStr += "is identical to the overridden function"; + } + else + errStr += "just delegates back to the base class."; + reportError(errorPath, Severity::style, "uselessOverride", + "$symbol:" + functionName + + errStr, + CWE(0U) /* Unknown CWE! */, + Certainty::normal); +} + +static const Token* getSingleFunctionCall(const Scope* scope) { + const Token* const start = scope->bodyStart->next(); + const Token* const end = Token::findsimplematch(start, ";", 1, scope->bodyEnd); + if (!end || end->next() != scope->bodyEnd) + return nullptr; + const Token* ftok = start; + if (ftok->str() == "return") + ftok = ftok->astOperand1(); + else { + while (Token::Match(ftok, "%name%|::")) + ftok = ftok->next(); + } + if (Token::simpleMatch(ftok, "(") && ftok->previous()->function()) + return ftok->previous(); + return nullptr; +} + +static bool compareTokenRanges(const Token* start1, const Token* end1, const Token* start2, const Token* end2) { + const Token* tok1 = start1; + const Token* tok2 = start2; + bool isEqual = false; + while (tok1 && tok2) { + if (tok1->str() != tok2->str()) + break; + if (tok1->str() == "this") + break; + if (tok1->isExpandedMacro() || tok2->isExpandedMacro()) + break; + if (tok1 == end1 && tok2 == end2) { + isEqual = true; + break; + } + tok1 = tok1->next(); + tok2 = tok2->next(); + } + return isEqual; +} + +void CheckClass::checkUselessOverride() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("uselessOverride")) + return; + + logChecker("CheckClass::checkUselessOverride"); // style + + for (const Scope* classScope : mSymbolDatabase->classAndStructScopes) { + if (!classScope->definedType || classScope->definedType->derivedFrom.size() != 1) + continue; + for (const Function& func : classScope->functionList) { + if (!func.functionScope) + continue; + if (func.hasFinalSpecifier()) + continue; + const Function* baseFunc = func.getOverriddenFunction(); + if (!baseFunc || baseFunc->isPure() || baseFunc->access != func.access) + continue; + if (std::any_of(classScope->functionList.begin(), classScope->functionList.end(), [&func](const Function& f) { // check for overloads + if (&f == &func) + return false; + return f.name() == func.name(); + })) + continue; + if (func.token->isExpandedMacro() || baseFunc->token->isExpandedMacro()) + continue; + if (baseFunc->functionScope) { + bool isSameCode = compareTokenRanges(baseFunc->argDef, baseFunc->argDef->link(), func.argDef, func.argDef->link()); // function arguments + if (isSameCode) { + isSameCode = compareTokenRanges(baseFunc->functionScope->bodyStart, baseFunc->functionScope->bodyEnd, // function body + func.functionScope->bodyStart, func.functionScope->bodyEnd); + + if (isSameCode) { + // bailout for shadowed members + if (!classScope->definedType || + !getDuplInheritedMembersRecursive(classScope->definedType, classScope->definedType, /*skipPrivate*/ false).empty() || + !getDuplInheritedMemberFunctionsRecursive(classScope->definedType, classScope->definedType, /*skipPrivate*/ false).empty()) + continue; + uselessOverrideError(baseFunc, &func, true); + continue; + } + } + } + if (const Token* const call = getSingleFunctionCall(func.functionScope)) { + if (call->function() != baseFunc) + continue; + std::vector funcArgs = getArguments(func.tokenDef); + std::vector callArgs = getArguments(call); + if (funcArgs.size() != callArgs.size() || + !std::equal(funcArgs.begin(), funcArgs.end(), callArgs.begin(), [](const Token* t1, const Token* t2) { + return t1->str() == t2->str(); + })) + continue; + uselessOverrideError(baseFunc, &func); + } + } + } +} + +static const Variable* getSingleReturnVar(const Scope* scope) { + if (!scope || !scope->bodyStart) + return nullptr; + const Token* const start = scope->bodyStart->next(); + const Token* const end = Token::findsimplematch(start, ";", 1, scope->bodyEnd); + if (!end || end->next() != scope->bodyEnd) + return nullptr; + if (!start->astOperand1() || start->str() != "return") + return nullptr; + return start->astOperand1()->variable(); +} + +void CheckClass::checkReturnByReference() +{ + if (!mSettings->severity.isEnabled(Severity::performance) && !mSettings->isPremiumEnabled("returnByReference")) + return; + + logChecker("CheckClass::checkReturnByReference"); // performance + + for (const Scope* classScope : mSymbolDatabase->classAndStructScopes) { + for (const Function& func : classScope->functionList) { + if (Function::returnsPointer(&func) || Function::returnsReference(&func) || Function::returnsStandardType(&func)) + continue; + if (func.isImplicitlyVirtual()) + continue; + if (const Variable* var = getSingleReturnVar(func.functionScope)) { + if (!var->valueType()) + continue; + if (var->isArgument()) + continue; + const bool isContainer = var->valueType()->type == ValueType::Type::CONTAINER && var->valueType()->container; + const bool isView = isContainer && var->valueType()->container->view; + bool warn = isContainer && !isView; + if (!warn && !isView) { + const std::size_t size = ValueFlow::getSizeOf(*var->valueType(), *mSettings); + if (size > 2 * mSettings->platform.sizeof_pointer) + warn = true; + } + if (warn) + returnByReferenceError(&func, var); + } + } + } +} + +void CheckClass::returnByReferenceError(const Function* func, const Variable* var) +{ + const Token* tok = func ? func->tokenDef : nullptr; + const std::string message = "Function '" + (func ? func->name() : "func") + "()' should return member '" + (var ? var->name() : "var") + "' by const reference."; + reportError(tok, Severity::performance, "returnByReference", message); +} + +void CheckClass::checkThisUseAfterFree() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckClass::checkThisUseAfterFree"); // warning + + for (const Scope * classScope : mSymbolDatabase->classAndStructScopes) { + + for (const Variable &var : classScope->varlist) { + // Find possible "self pointer".. pointer/smartpointer member variable of "self" type. + if (var.valueType() && var.valueType()->smartPointerType != classScope->definedType && var.valueType()->typeScope != classScope) { + const ValueType valueType = ValueType::parseDecl(var.typeStartToken(), *mSettings); + if (valueType.smartPointerType != classScope->definedType) + continue; + } + + // If variable is not static, check that "this" is assigned + if (!var.isStatic()) { + bool hasAssign = false; + for (const Function &func : classScope->functionList) { + if (func.type != Function::Type::eFunction || !func.hasBody()) + continue; + for (const Token *tok = func.functionScope->bodyStart; tok != func.functionScope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "%varid% = this|shared_from_this", var.declarationId())) { + hasAssign = true; + break; + } + } + if (hasAssign) + break; + } + if (!hasAssign) + continue; + } + + // Check usage of self pointer.. + for (const Function &func : classScope->functionList) { + if (func.type != Function::Type::eFunction || !func.hasBody()) + continue; + + const Token * freeToken = nullptr; + std::set callstack; + checkThisUseAfterFreeRecursive(classScope, &func, &var, std::move(callstack), freeToken); + } + } + } +} + +bool CheckClass::checkThisUseAfterFreeRecursive(const Scope *classScope, const Function *func, const Variable *selfPointer, std::set callstack, const Token *&freeToken) +{ + if (!func || !func->functionScope) + return false; + + // avoid recursion + if (callstack.count(func)) + return false; + callstack.insert(func); + + const Token * const bodyStart = func->functionScope->bodyStart; + const Token * const bodyEnd = func->functionScope->bodyEnd; + for (const Token *tok = bodyStart; tok != bodyEnd; tok = tok->next()) { + const bool isDestroyed = freeToken != nullptr && !func->isStatic(); + if (Token::Match(tok, "delete %var% ;") && selfPointer == tok->next()->variable()) { + freeToken = tok; + tok = tok->tokAt(2); + } else if (Token::Match(tok, "%var% . reset ( )") && selfPointer == tok->variable()) + freeToken = tok; + else if (Token::Match(tok->previous(), "!!. %name% (") && tok->function() && tok->function()->nestedIn == classScope) { + if (isDestroyed) { + thisUseAfterFree(selfPointer->nameToken(), freeToken, tok); + return true; + } + if (checkThisUseAfterFreeRecursive(classScope, tok->function(), selfPointer, callstack, freeToken)) + return true; + } else if (isDestroyed && Token::Match(tok->previous(), "!!. %name%") && tok->variable() && tok->variable()->scope() == classScope && !tok->variable()->isStatic() && !tok->variable()->isArgument()) { + thisUseAfterFree(selfPointer->nameToken(), freeToken, tok); + return true; + } else if (freeToken && Token::Match(tok, "return|throw")) { + // TODO + return tok->str() == "throw"; + } else if (tok->str() == "{" && tok->scope()->type == Scope::ScopeType::eLambda) { + tok = tok->link(); + } + } + return false; +} + +void CheckClass::thisUseAfterFree(const Token *self, const Token *free, const Token *use) +{ + std::string selfPointer = self ? self->str() : "ptr"; + const ErrorPath errorPath = { ErrorPathItem(self, "Assuming '" + selfPointer + "' is used as 'this'"), ErrorPathItem(free, "Delete '" + selfPointer + "', invalidating 'this'"), ErrorPathItem(use, "Call method when 'this' is invalid") }; + const std::string usestr = use ? use->str() : "x"; + const std::string usemsg = use && use->function() ? ("Calling method '" + usestr + "()'") : ("Using member '" + usestr + "'"); + reportError(errorPath, Severity::warning, "thisUseAfterFree", + "$symbol:" + selfPointer + "\n" + + usemsg + " when 'this' might be invalid", + CWE(0), Certainty::normal); +} + +void CheckClass::checkUnsafeClassRefMember() +{ + if (!mSettings->safeChecks.classes || !mSettings->severity.isEnabled(Severity::warning)) + return; + logChecker("CheckClass::checkUnsafeClassRefMember"); // warning,safeChecks + for (const Scope * classScope : mSymbolDatabase->classAndStructScopes) { + for (const Function &func : classScope->functionList) { + if (!func.hasBody() || !func.isConstructor()) + continue; + + const Token *initList = func.constructorMemberInitialization(); + while (Token::Match(initList, "[:,] %name% (")) { + if (Token::Match(initList->tokAt(2), "( %var% )")) { + const Variable * const memberVar = initList->next()->variable(); + const Variable * const argVar = initList->tokAt(3)->variable(); + if (memberVar && argVar && memberVar->isConst() && memberVar->isReference() && argVar->isArgument() && argVar->isConst() && argVar->isReference()) + unsafeClassRefMemberError(initList->next(), classScope->className + "::" + memberVar->name()); + } + initList = initList->linkAt(2)->next(); + } + } + } +} + +void CheckClass::unsafeClassRefMemberError(const Token *tok, const std::string &varname) +{ + reportError(tok, Severity::warning, "unsafeClassRefMember", + "$symbol:" + varname + "\n" + "Unsafe class: The const reference member '$symbol' is initialized by a const reference constructor argument. You need to be careful about lifetime issues.\n" + "Unsafe class checking: The const reference member '$symbol' is initialized by a const reference constructor argument. You need to be careful about lifetime issues. If you pass a local variable or temporary value in this constructor argument, be extra careful. If the argument is always some global object that is never destroyed then this is safe usage. However it would be defensive to make the member '$symbol' a non-reference variable or a smart pointer.", + CWE(0), Certainty::normal); +} + +// a Clang-built executable will crash when using the anonymous MyFileInfo later on - so put it in a unique namespace for now +// see https://trac.cppcheck.net/ticket/12108 for more details +#ifdef __clang__ +inline namespace CheckClass_internal +#else +namespace +#endif +{ + /* multifile checking; one definition rule violations */ + class MyFileInfo : public Check::FileInfo { + public: + struct NameLoc { + std::string className; + std::string fileName; + int lineNumber; + int column; + std::size_t hash; + + bool isSameLocation(const NameLoc& other) const { + return fileName == other.fileName && + lineNumber == other.lineNumber && + column == other.column; + } + }; + std::vector classDefinitions; + + /** Convert data into xml string */ + std::string toString() const override + { + std::string ret; + for (const NameLoc &nameLoc: classDefinitions) { + ret += "\n"; + } + return ret; + } + }; +} + +Check::FileInfo *CheckClass::getFileInfo(const Tokenizer &tokenizer, const Settings& /*settings*/) const +{ + if (!tokenizer.isCPP()) + return nullptr; + + // One definition rule + std::vector classDefinitions; + for (const Scope * classScope : tokenizer.getSymbolDatabase()->classAndStructScopes) { + if (classScope->isAnonymous()) + continue; + + if (classScope->classDef && Token::simpleMatch(classScope->classDef->previous(), ">")) + continue; + + // the full definition must be compared + const bool fullDefinition = std::all_of(classScope->functionList.cbegin(), + classScope->functionList.cend(), + [](const Function& f) { + return f.hasBody(); + }); + if (!fullDefinition) + continue; + + std::string name; + const Scope *scope = classScope; + while (scope->isClassOrStruct() && !classScope->className.empty()) { + if (Token::Match(scope->classDef, "struct|class %name% :: %name%")) { + // TODO handle such classnames + name.clear(); + break; + } + name = scope->className + "::" + name; + scope = scope->nestedIn; + } + if (name.empty()) + continue; + name.erase(name.size() - 2); + if (scope->type != Scope::ScopeType::eGlobal) + continue; + + MyFileInfo::NameLoc nameLoc; + nameLoc.className = std::move(name); + nameLoc.fileName = tokenizer.list.file(classScope->classDef); + nameLoc.lineNumber = classScope->classDef->linenr(); + nameLoc.column = classScope->classDef->column(); + + // Calculate hash from the full class/struct definition + std::string def; + for (const Token *tok = classScope->classDef; tok != classScope->bodyEnd; tok = tok->next()) + def += tok->str(); + for (const Function &f: classScope->functionList) { + if (f.functionScope && f.functionScope->nestedIn != classScope) { + for (const Token *tok = f.functionScope->bodyStart; tok != f.functionScope->bodyEnd; tok = tok->next()) + def += tok->str(); + } + } + nameLoc.hash = std::hash {}(def); + + classDefinitions.push_back(std::move(nameLoc)); + } + + if (classDefinitions.empty()) + return nullptr; + + auto *fileInfo = new MyFileInfo; + fileInfo->classDefinitions.swap(classDefinitions); + return fileInfo; +} + +Check::FileInfo * CheckClass::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const +{ + auto *fileInfo = new MyFileInfo; + for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (std::strcmp(e->Name(), "class") != 0) + continue; + const char *name = e->Attribute("name"); + const char *file = e->Attribute("file"); + const char *line = e->Attribute("line"); + const char *col = e->Attribute("col"); + const char *hash = e->Attribute("hash"); + if (name && file && line && col && hash) { + MyFileInfo::NameLoc nameLoc; + nameLoc.className = name; + nameLoc.fileName = file; + nameLoc.lineNumber = strToInt(line); + nameLoc.column = strToInt(col); + nameLoc.hash = strToInt(hash); + fileInfo->classDefinitions.push_back(std::move(nameLoc)); + } + } + if (fileInfo->classDefinitions.empty()) { + delete fileInfo; + fileInfo = nullptr; + } + return fileInfo; +} + +bool CheckClass::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) +{ + bool foundErrors = false; + (void)ctu; // This argument is unused + (void)settings; // This argument is unused + + std::unordered_map all; + + CheckClass dummy(nullptr, &settings, &errorLogger); + dummy. + logChecker("CheckClass::analyseWholeProgram"); + + for (const Check::FileInfo* fi1 : fileInfo) { + const MyFileInfo *fi = dynamic_cast(fi1); + if (!fi) + continue; + for (const MyFileInfo::NameLoc &nameLoc : fi->classDefinitions) { + auto it = all.find(nameLoc.className); + if (it == all.end()) { + all[nameLoc.className] = nameLoc; + continue; + } + if (it->second.hash == nameLoc.hash) + continue; + // Same location, sometimes the hash is different wrongly (possibly because of different token simplifications). + if (it->second.isSameLocation(nameLoc)) + continue; + + std::list locationList; + locationList.emplace_back(nameLoc.fileName, nameLoc.lineNumber, nameLoc.column); + locationList.emplace_back(it->second.fileName, it->second.lineNumber, it->second.column); + + const ErrorMessage errmsg(std::move(locationList), + emptyString, + Severity::error, + "$symbol:" + nameLoc.className + + "\nThe one definition rule is violated, different classes/structs have the same name '$symbol'", + "ctuOneDefinitionRuleViolation", + CWE_ONE_DEFINITION_RULE, + Certainty::normal); + errorLogger.reportErr(errmsg); + + foundErrors = true; + } + } + return foundErrors; +} + + diff --git a/cppcheck-2.14.0/lib/checkclass.h b/cppcheck-2.14.0/lib/checkclass.h new file mode 100644 index 00000000..bbbb1035 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkclass.h @@ -0,0 +1,418 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef checkclassH +#define checkclassH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "tokenize.h" +#include "symboldatabase.h" + +#include +#include +#include +#include +#include + +class ErrorLogger; +class Settings; +class Token; + +namespace CTU { + class FileInfo; +} + +namespace tinyxml2 { + class XMLElement; +} + +/// @addtogroup Checks +/// @{ + + +/** @brief %Check classes. Uninitialized member variables, non-conforming operators, missing virtual destructor, etc */ +class CPPCHECKLIB CheckClass : public Check { + friend class TestClass; + friend class TestConstructors; + friend class TestUnusedPrivateFunction; + +public: + /** @brief This constructor is used when registering the CheckClass */ + CheckClass() : Check(myName()) {} + + /** @brief Set of the STL types whose operator[] is not const */ + static const std::set stl_containers_not_const; + +private: + /** @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, ErrorLogger *errorLogger) override { + if (tokenizer.isC()) + return; + + CheckClass checkClass(&tokenizer, &tokenizer.getSettings(), errorLogger); + + // can't be a simplified check .. the 'sizeof' is used. + checkClass.checkMemset(); + checkClass.constructors(); + checkClass.privateFunctions(); + checkClass.operatorEqRetRefThis(); + checkClass.thisSubtraction(); + checkClass.operatorEqToSelf(); + checkClass.initializerListOrder(); + checkClass.initializationListUsage(); + checkClass.checkSelfInitialization(); + checkClass.virtualDestructor(); + checkClass.checkConst(); + checkClass.copyconstructors(); + checkClass.checkVirtualFunctionCallInConstructor(); + checkClass.checkDuplInheritedMembers(); + checkClass.checkExplicitConstructors(); + checkClass.checkCopyCtorAndEqOperator(); + checkClass.checkOverride(); + checkClass.checkUselessOverride(); + checkClass.checkReturnByReference(); + checkClass.checkThisUseAfterFree(); + checkClass.checkUnsafeClassRefMember(); + } + + /** @brief %Check that all class constructors are ok */ + void constructors(); + + /** @brief %Check that constructors with single parameter are explicit, + * if they has to be.*/ + void checkExplicitConstructors(); + + /** @brief %Check that all private functions are called */ + void privateFunctions(); + + /** + * @brief %Check that the memsets are valid. + * The 'memset' function can do dangerous things if used wrong. If it + * is used on STL containers for instance it will clear all its data + * and then the STL container may leak memory or worse have an invalid state. + * It can also overwrite the virtual table. + * Important: The checking doesn't work on simplified tokens list. + */ + void checkMemset(); + void checkMemsetType(const Scope *start, const Token *tok, const Scope *type, bool allocation, std::set parsedTypes); + + /** @brief 'operator=' should return reference to *this */ + void operatorEqRetRefThis(); // Warning upon no "return *this;" + + /** @brief 'operator=' should check for assignment to self */ + void operatorEqToSelf(); // Warning upon no check for assignment to self + + /** @brief The destructor in a base class should be virtual */ + void virtualDestructor(); + + /** @brief warn for "this-x". The indented code may be "this->x" */ + void thisSubtraction(); + + /** @brief can member function be const? */ + void checkConst(); + + /** @brief Check initializer list order */ + void initializerListOrder(); + + /** @brief Suggest using initialization list */ + void initializationListUsage(); + + /** @brief Check for initialization of a member with itself */ + void checkSelfInitialization(); + + void copyconstructors(); + + /** @brief call of virtual function in constructor/destructor */ + void checkVirtualFunctionCallInConstructor(); + + /** @brief Check duplicated inherited members */ + void checkDuplInheritedMembers(); + + /** @brief Check that copy constructor and operator defined together */ + void checkCopyCtorAndEqOperator(); + + /** @brief Check that the override keyword is used when overriding virtual functions */ + void checkOverride(); + + /** @brief Check that the overriden function is not identical to the base function */ + void checkUselessOverride(); + + /** @brief Check that large members are returned by reference from getter function */ + void checkReturnByReference(); + + /** @brief When "self pointer" is destroyed, 'this' might become invalid. */ + void checkThisUseAfterFree(); + + /** @brief Unsafe class check - const reference member */ + void checkUnsafeClassRefMember(); + + /** @brief Parse current TU and extract file info */ + Check::FileInfo *getFileInfo(const Tokenizer &tokenizer, const Settings &settings) const override; + + Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const override; + + /** @brief Analyse all file infos for all TU */ + bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) override; + + const SymbolDatabase* mSymbolDatabase{}; + + // Reporting errors.. + void noConstructorError(const Token *tok, const std::string &classname, bool isStruct); + void noExplicitConstructorError(const Token *tok, const std::string &classname, bool isStruct); + //void copyConstructorMallocError(const Token *cctor, const Token *alloc, const std::string& var_name); + void copyConstructorShallowCopyError(const Token *tok, const std::string& varname); + void noCopyConstructorError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive); + void noOperatorEqError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive); + void noDestructorError(const Scope *scope, bool isdefault, const Token *alloc); + void uninitVarError(const Token *tok, bool isprivate, Function::Type functionType, const std::string &classname, const std::string &varname, bool derived, bool inconclusive); + void uninitVarError(const Token *tok, const std::string &classname, const std::string &varname); + void missingMemberCopyError(const Token *tok, Function::Type functionType, const std::string& classname, const std::string& varname); + 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, bool isContainer = false); + void memsetErrorReference(const Token *tok, const std::string &memfunc, const std::string &type); + void memsetErrorFloat(const Token *tok, const std::string &type); + void mallocOnClassError(const Token* tok, const std::string &memfunc, const Token* classTok, const std::string &classname); + void mallocOnClassWarning(const Token* tok, const std::string &memfunc, const Token* classTok); + void virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive); + void thisSubtractionError(const Token *tok); + void operatorEqRetRefThisError(const Token *tok); + void operatorEqShouldBeLeftUnimplementedError(const Token *tok); + void operatorEqMissingReturnStatementError(const Token *tok, bool error); + void operatorEqToSelfError(const Token *tok); + void checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic); + void checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic); + void initializerListError(const Token *tok1,const Token *tok2, const std::string & classname, const std::string &varname, const std::string& argname = {}); + void suggestInitializationList(const Token *tok, const std::string& varname); + void selfInitializationError(const Token* tok, const std::string& varname); + void pureVirtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & tokStack, const std::string &purefuncname); + void virtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & tokStack, const std::string &funcname); + void duplInheritedMembersError(const Token* tok1, const Token* tok2, const std::string &derivedName, const std::string &baseName, const std::string &memberName, bool derivedIsStruct, bool baseIsStruct, bool isFunction = false); + void copyCtorAndEqOperatorError(const Token *tok, const std::string &classname, bool isStruct, bool hasCopyCtor); + void overrideError(const Function *funcInBase, const Function *funcInDerived); + void uselessOverrideError(const Function *funcInBase, const Function *funcInDerived, bool isSameCode = false); + void returnByReferenceError(const Function *func, const Variable* var); + void thisUseAfterFree(const Token *self, const Token *free, const Token *use); + void unsafeClassRefMemberError(const Token *tok, const std::string &varname); + void checkDuplInheritedMembersRecursive(const Type* typeCurrent, const Type* typeBase); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckClass c(nullptr, settings, errorLogger); + c.noConstructorError(nullptr, "classname", false); + c.noExplicitConstructorError(nullptr, "classname", false); + //c.copyConstructorMallocError(nullptr, 0, "var"); + c.copyConstructorShallowCopyError(nullptr, "var"); + c.noCopyConstructorError(nullptr, false, nullptr, false); + c.noOperatorEqError(nullptr, false, nullptr, false); + c.noDestructorError(nullptr, false, nullptr); + c.uninitVarError(nullptr, false, Function::eConstructor, "classname", "varname", false, false); + c.uninitVarError(nullptr, true, Function::eConstructor, "classname", "varnamepriv", false, false); + c.uninitVarError(nullptr, false, Function::eConstructor, "classname", "varname", true, false); + c.uninitVarError(nullptr, true, Function::eConstructor, "classname", "varnamepriv", true, false); + c.missingMemberCopyError(nullptr, Function::eConstructor, "classname", "varnamepriv"); + c.operatorEqVarError(nullptr, "classname", emptyString, false); + c.unusedPrivateFunctionError(nullptr, "classname", "funcname"); + c.memsetError(nullptr, "memfunc", "classname", "class"); + c.memsetErrorReference(nullptr, "memfunc", "class"); + c.memsetErrorFloat(nullptr, "class"); + c.mallocOnClassWarning(nullptr, "malloc", nullptr); + c.mallocOnClassError(nullptr, "malloc", nullptr, "std::string"); + c.virtualDestructorError(nullptr, "Base", "Derived", false); + c.thisSubtractionError(nullptr); + c.operatorEqRetRefThisError(nullptr); + c.operatorEqMissingReturnStatementError(nullptr, true); + c.operatorEqShouldBeLeftUnimplementedError(nullptr); + c.operatorEqToSelfError(nullptr); + c.checkConstError(nullptr, "class", "function", false); + c.checkConstError(nullptr, "class", "function", true); + c.initializerListError(nullptr, nullptr, "class", "variable"); + c.suggestInitializationList(nullptr, "variable"); + c.selfInitializationError(nullptr, "var"); + c.duplInheritedMembersError(nullptr, nullptr, "class", "class", "variable", false, false); + c.copyCtorAndEqOperatorError(nullptr, "class", false, false); + c.overrideError(nullptr, nullptr); + c.uselessOverrideError(nullptr, nullptr); + c.returnByReferenceError(nullptr, nullptr); + c.pureVirtualFunctionCallInConstructorError(nullptr, std::list(), "f"); + c.virtualFunctionCallInConstructorError(nullptr, std::list(), "f"); + c.thisUseAfterFree(nullptr, nullptr, nullptr); + c.unsafeClassRefMemberError(nullptr, "UnsafeClass::var"); + } + + static std::string myName() { + return "Class"; + } + + std::string classInfo() const override { + return "Check the code for each class.\n" + "- Missing constructors and copy constructors\n" + //"- Missing allocation of memory in copy constructor\n" + "- Constructors which should be explicit\n" + "- Are all variables initialized by the constructors?\n" + "- Are all variables assigned by 'operator='?\n" + "- Warn if memset, memcpy etc are used on a class\n" + "- Warn if memory for classes is allocated with malloc()\n" + "- If it's a base class, check that the destructor is virtual\n" + "- Are there unused private functions?\n" + "- 'operator=' should check for assignment to self\n" + "- Constness for member functions\n" + "- Order of initializations\n" + "- Suggest usage of initialization list\n" + "- Initialization of a member with itself\n" + "- Suspicious subtraction from 'this'\n" + "- Call of pure virtual function in constructor/destructor\n" + "- Duplicated inherited data members\n" + // disabled for now "- If 'copy constructor' defined, 'operator=' also should be defined and vice versa\n" + "- Check that arbitrary usage of public interface does not result in division by zero\n" + "- Delete \"self pointer\" and then access 'this'\n" + "- Check that the 'override' keyword is used when overriding virtual functions\n" + "- Check that the 'one definition rule' is not violated\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; + bool hasAllocation(const Function *func, const Scope* scope, const Token *start, const Token *end) const; + bool hasAllocationInIfScope(const Function *func, const Scope* scope, const Token *ifStatementScopeStart) const; + static bool hasAssignSelf(const Function *func, const Token *rhs, const Token *&out_ifStatementScopeStart); + enum class Bool { TRUE, FALSE, BAILOUT }; + static Bool isInverted(const Token *tok, const Token *rhs); + static const Token * getIfStmtBodyStart(const Token *tok, const Token *rhs); + + // checkConst helper functions + bool isMemberVar(const Scope *scope, const Token *tok) const; + static bool isMemberFunc(const Scope *scope, const Token *tok); + static bool isConstMemberFunc(const Scope *scope, const Token *tok); + enum class MemberAccess { NONE, SELF, MEMBER }; + bool checkConstFunc(const Scope *scope, const Function *func, MemberAccess& memberAccessed) const; + + // constructors helper function + /** @brief Information about a member variable. Used when checking for uninitialized variables */ + struct Usage { + explicit Usage(const Variable *var) : var(var) {} + + /** Variable that this usage is for */ + const Variable *var; + + /** @brief has this variable been assigned? */ + bool assign{}; + + /** @brief has this variable been initialized? */ + bool init{}; + }; + + static bool isBaseClassMutableMemberFunc(const Token *tok, const Scope *scope); + + /** + * @brief Create usage list that contains all scope members and also members + * of base classes without constructors. + * @param scope current class scope + */ + static std::vector createUsageList(const Scope *scope); + + /** + * @brief assign a variable in the varlist + * @param usageList reference to usage vector + * @param varid id of variable to mark assigned + */ + static void assignVar(std::vector &usageList, nonneg int varid); + + /** + * @brief assign a variable in the varlist + * @param usageList reference to usage vector + * @param vartok variable token + */ + static void assignVar(std::vector &usageList, const Token *vartok); + + /** + * @brief initialize a variable in the varlist + * @param usageList reference to usage vector + * @param varid id of variable to mark initialized + */ + static void initVar(std::vector &usageList, nonneg int varid); + + /** + * @brief set all variables in list assigned + * @param usageList reference to usage vector + */ + static void assignAllVar(std::vector &usageList); + + /** + * @brief set all variable in list assigned, if visible from given scope + * @param usageList reference to usage vector + * @param scope scope from which usages must be visible + */ + static void assignAllVarsVisibleFromScope(std::vector &usageList, const Scope *scope); + + /** + * @brief set all variables in list not assigned and not initialized + * @param usageList reference to usage vector + */ + static void clearAllVar(std::vector &usageList); + + /** + * @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) const; + + /** + * @brief gives a list of tokens where virtual functions are called directly or indirectly + * @param function function to be checked + * @param virtualFunctionCallsMap map of results for already checked functions + * @return list of tokens where pure virtual functions are called + */ + const std::list & getVirtualFunctionCalls( + const Function & function, + std::map> & virtualFunctionCallsMap); + + /** + * @brief looks for the first virtual function call stack + * @param virtualFunctionCallsMap map of results obtained from getVirtualFunctionCalls + * @param callToken token where pure virtual function is called directly or indirectly + * @param[in,out] pureFuncStack list to append the stack + */ + static void getFirstVirtualFunctionCallStack( + std::map> & virtualFunctionCallsMap, + const Token *callToken, + std::list & pureFuncStack); + + static bool canNotCopy(const Scope *scope); + + static bool canNotMove(const Scope *scope); + + /** + * @brief Helper for checkThisUseAfterFree + */ + bool checkThisUseAfterFreeRecursive(const Scope *classScope, const Function *func, const Variable *selfPointer, std::set callstack, const Token *&freeToken); +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkclassH diff --git a/cppcheck-2.14.0/lib/checkcondition.cpp b/cppcheck-2.14.0/lib/checkcondition.cpp new file mode 100644 index 00000000..fa30ae0f --- /dev/null +++ b/cppcheck-2.14.0/lib/checkcondition.cpp @@ -0,0 +1,2045 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +// Check for condition mismatches +//--------------------------------------------------------------------------- + +#include "checkcondition.h" + +#include "astutils.h" +#include "library.h" +#include "platform.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "utils.h" +#include "vfvalue.h" + +#include "checkother.h" // comparisonNonZeroExpressionLessThanZero and testIfNonZeroExpressionIsPositive + +#include +#include +#include +#include +#include +#include +#include + +// CWE ids used +static const CWE uncheckedErrorConditionCWE(391U); +static const CWE CWE398(398U); // Indicator of Poor Code Quality +static const CWE CWE570(570U); // Expression is Always False +static const CWE CWE571(571U); // Expression is Always True + +//--------------------------------------------------------------------------- + +// Register this check class (by creating a static instance of it) +namespace { + CheckCondition instance; +} + +bool CheckCondition::diag(const Token* tok, bool insert) +{ + if (!tok) + return false; + const Token* parent = tok->astParent(); + bool hasParent = false; + while (Token::Match(parent, "!|&&|%oror%")) { + if (mCondDiags.count(parent) != 0) { + hasParent = true; + break; + } + parent = parent->astParent(); + } + if (mCondDiags.count(tok) == 0 && !hasParent) { + if (insert) + mCondDiags.insert(tok); + return false; + } + return true; +} + +bool CheckCondition::isAliased(const std::set &vars) const +{ + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (Token::Match(tok, "= & %var% ;") && vars.find(tok->tokAt(2)->varId()) != vars.end()) + return true; + } + return false; +} + +void CheckCondition::assignIf() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("assignIf")) + return; + + logChecker("CheckCondition::assignIf"); // style + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (tok->str() != "=") + continue; + + if (Token::Match(tok->tokAt(-2), "[;{}] %var% =")) { + const Variable *var = tok->previous()->variable(); + if (var == nullptr) + continue; + + char bitop = '\0'; + MathLib::bigint num = 0; + + if (Token::Match(tok->next(), "%num% [&|]")) { + bitop = tok->strAt(2).at(0); + num = MathLib::toBigNumber(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::toBigNumber(endToken->previous()->str()); + } + } + + if (bitop == '\0') + continue; + + if (num < 0 && bitop == '|') + continue; + + assignIfParseScope(tok, tok->tokAt(4), var->declarationId(), var->isLocal(), bitop, num); + } + } +} + +static bool isParameterChanged(const Token *partok) +{ + bool addressOf = Token::Match(partok, "[(,] &"); + int argumentNumber = 0; + const Token *ftok; + for (ftok = partok; ftok && ftok->str() != "("; ftok = ftok->previous()) { + if (ftok->str() == ")") + ftok = ftok->link(); + else if (argumentNumber == 0U && ftok->str() == "&") + addressOf = true; + else if (ftok->str() == ",") + argumentNumber++; + } + ftok = ftok ? ftok->previous() : nullptr; + if (!(ftok && ftok->function())) + return true; + const Variable *par = ftok->function()->getArgumentVar(argumentNumber); + if (!par) + return true; + if (par->isConst()) + return false; + if (addressOf || par->isReference() || par->isPointer()) + return true; + return false; +} + +/** parse scopes recursively */ +bool CheckCondition::assignIfParseScope(const Token * const assignTok, + const Token * const startTok, + const nonneg int varid, + const bool islocal, + const char bitop, + const MathLib::bigint num) +{ + bool ret = false; + + for (const Token *tok2 = startTok; tok2; tok2 = tok2->next()) { + if ((bitop == '&') && Token::Match(tok2->tokAt(2), "%varid% %cop% %num% ;", varid) && tok2->strAt(3) == std::string(1U, bitop)) { + const MathLib::bigint num2 = MathLib::toBigNumber(tok2->strAt(4)); + if (0 == (num & num2)) + mismatchingBitAndError(assignTok, num, tok2, num2); + } + if (Token::Match(tok2, "%varid% =", varid)) { + return true; + } + if (bitop == '&' && Token::Match(tok2, "%varid% &= %num% ;", varid)) { + const MathLib::bigint num2 = MathLib::toBigNumber(tok2->strAt(2)); + if (0 == (num & num2)) + mismatchingBitAndError(assignTok, num, tok2, num2); + } + if (Token::Match(tok2, "++|-- %varid%", varid) || Token::Match(tok2, "%varid% ++|--", varid)) + return true; + if (Token::Match(tok2, "[(,] &| %varid% [,)]", varid) && isParameterChanged(tok2)) + return true; + if (tok2->str() == "}") + return false; + if (Token::Match(tok2, "break|continue|return")) + ret = true; + if (ret && tok2->str() == ";") + return false; + if (!islocal && Token::Match(tok2, "%name% (") && !Token::simpleMatch(tok2->next()->link(), ") {")) + return true; + if (Token::Match(tok2, "if|while (")) { + if (!islocal && tok2->str() == "while") + continue; + if (tok2->str() == "while") { + // is variable changed in loop? + const Token *bodyStart = tok2->linkAt(1)->next(); + const Token *bodyEnd = bodyStart ? bodyStart->link() : nullptr; + if (!bodyEnd || bodyEnd->str() != "}" || isVariableChanged(bodyStart, bodyEnd, varid, !islocal, mSettings)) + continue; + } + + // parse condition + const Token * const end = tok2->next()->link(); + for (; tok2 != end; tok2 = tok2->next()) { + if (Token::Match(tok2, "[(,] &| %varid% [,)]", varid)) { + return true; + } + if (Token::Match(tok2,"&&|%oror%|( %varid% ==|!= %num% &&|%oror%|)", varid)) { + const Token *vartok = tok2->next(); + const MathLib::bigint num2 = MathLib::toBigNumber(vartok->strAt(2)); + if ((num & num2) != ((bitop=='&') ? num2 : num)) { + const std::string& op(vartok->strAt(1)); + const bool alwaysTrue = op == "!="; + const std::string condition(vartok->str() + op + vartok->strAt(2)); + assignIfError(assignTok, tok2, condition, alwaysTrue); + } + } + if (Token::Match(tok2, "%varid% %op%", varid) && tok2->next()->isAssignmentOp()) { + return true; + } + } + + const bool ret1 = assignIfParseScope(assignTok, end->tokAt(2), varid, islocal, bitop, num); + bool ret2 = false; + if (Token::simpleMatch(end->next()->link(), "} else {")) + ret2 = assignIfParseScope(assignTok, end->next()->link()->tokAt(3), varid, islocal, bitop, num); + if (ret1 || ret2) + return true; + } + } + return false; +} + +void CheckCondition::assignIfError(const Token *tok1, const Token *tok2, const std::string &condition, bool result) +{ + if (tok2 && diag(tok2->tokAt(2))) + return; + std::list locations = { tok1, tok2 }; + reportError(locations, + Severity::style, + "assignIfError", + "Mismatching assignment and comparison, comparison '" + condition + "' is always " + std::string(bool_to_string(result)) + ".", CWE398, Certainty::normal); +} + + +void CheckCondition::mismatchingBitAndError(const Token *tok1, const MathLib::bigint num1, const Token *tok2, const MathLib::bigint num2) +{ + std::list locations = { tok1, tok2 }; + + std::ostringstream msg; + msg << "Mismatching bitmasks. Result is always 0 (" + << "X = Y & 0x" << std::hex << num1 << "; Z = X & 0x" << std::hex << num2 << "; => Z=0)."; + + reportError(locations, + Severity::style, + "mismatchingBitAnd", + msg.str(), CWE398, Certainty::normal); +} + + +static void getnumchildren(const Token *tok, std::list &numchildren) +{ + if (tok->astOperand1() && tok->astOperand1()->isNumber()) + numchildren.push_back(MathLib::toBigNumber(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::toBigNumber(tok->astOperand2()->str())); + else if (tok->astOperand2() && tok->str() == tok->astOperand2()->str()) + getnumchildren(tok->astOperand2(), numchildren); +} + +/* Return whether tok is in the body for a function returning a boolean. */ +static bool inBooleanFunction(const Token *tok) +{ + const Scope *scope = tok ? tok->scope() : nullptr; + while (scope && scope->isLocal()) + scope = scope->nestedIn; + if (scope && scope->type == Scope::eFunction) { + const Function *func = scope->function; + if (func) { + const Token *ret = func->retDef; + while (Token::Match(ret, "static|const")) + ret = ret->next(); + return Token::Match(ret, "bool|_Bool"); + } + } + return false; +} + +static bool isOperandExpanded(const Token *tok) +{ + if (tok->isExpandedMacro() || tok->isEnumerator()) + return true; + if (tok->astOperand1() && isOperandExpanded(tok->astOperand1())) + return true; + if (tok->astOperand2() && isOperandExpanded(tok->astOperand2())) + return true; + return false; +} + +void CheckCondition::checkBadBitmaskCheck() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("badBitmaskCheck")) + return; + + logChecker("CheckCondition::checkBadBitmaskCheck"); // style + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (tok->str() == "|" && tok->astOperand1() && tok->astOperand2() && tok->astParent()) { + const Token* parent = tok->astParent(); + const bool isBoolean = Token::Match(parent, "&&|%oror%") || + (parent->str() == "?" && parent->astOperand1() == tok) || + (parent->str() == "=" && parent->astOperand2() == tok && parent->astOperand1() && parent->astOperand1()->variable() && Token::Match(parent->astOperand1()->variable()->typeStartToken(), "bool|_Bool")) || + (parent->str() == "(" && Token::Match(parent->astOperand1(), "if|while")) || + (parent->str() == "return" && parent->astOperand1() == tok && inBooleanFunction(tok)); + + const bool isTrue = (tok->astOperand1()->hasKnownIntValue() && tok->astOperand1()->values().front().intvalue != 0) || + (tok->astOperand2()->hasKnownIntValue() && tok->astOperand2()->values().front().intvalue != 0); + + if (isBoolean && isTrue) + badBitmaskCheckError(tok); + + // If there are #ifdef in the expression don't warn about redundant | to avoid FP + const auto& startStop = tok->findExpressionStartEndTokens(); + if (mTokenizer->hasIfdef(startStop.first, startStop.second)) + continue; + + const bool isZero1 = (tok->astOperand1()->hasKnownIntValue() && tok->astOperand1()->values().front().intvalue == 0); + const bool isZero2 = (tok->astOperand2()->hasKnownIntValue() && tok->astOperand2()->values().front().intvalue == 0); + if (!isZero1 && !isZero2) + continue; + + if (!tok->isExpandedMacro() && + !(isZero1 && isOperandExpanded(tok->astOperand1())) && + !(isZero2 && isOperandExpanded(tok->astOperand2()))) + badBitmaskCheckError(tok, /*isNoOp*/ true); + } + } +} + +void CheckCondition::badBitmaskCheckError(const Token *tok, bool isNoOp) +{ + if (isNoOp) + reportError(tok, Severity::style, "badBitmaskCheck", "Operator '|' with one operand equal to zero is redundant.", CWE571, Certainty::normal); + else + reportError(tok, Severity::warning, "badBitmaskCheck", "Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?", CWE571, Certainty::normal); +} + +void CheckCondition::comparison() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("comparisonError")) + return; + + logChecker("CheckCondition::comparison"); // style + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (!tok->isComparisonOp()) + continue; + + const Token *expr1 = tok->astOperand1(); + const Token *expr2 = tok->astOperand2(); + if (!expr1 || !expr2) + continue; + if (expr1->isNumber()) + std::swap(expr1,expr2); + if (!expr2->isNumber()) + continue; + const MathLib::bigint num2 = MathLib::toBigNumber(expr2->str()); + if (num2 < 0) + continue; + if (!Token::Match(expr1,"[&|]")) + continue; + std::list numbers; + getnumchildren(expr1, numbers); + for (const MathLib::bigint num1 : numbers) { + if (num1 < 0) + continue; + if (Token::Match(tok, "==|!=")) { + if ((expr1->str() == "&" && (num1 & num2) != num2) || + (expr1->str() == "|" && (num1 | num2) != num2)) { + const std::string& op(tok->str()); + comparisonError(expr1, expr1->str(), num1, op, num2, op != "=="); + } + } else if (expr1->str() == "&") { + const bool or_equal = Token::Match(tok, ">=|<="); + const std::string& op(tok->str()); + if ((Token::Match(tok, ">=|<")) && (num1 < num2)) { + comparisonError(expr1, expr1->str(), num1, op, num2, !or_equal); + } else if ((Token::Match(tok, "<=|>")) && (num1 <= num2)) { + comparisonError(expr1, expr1->str(), num1, op, num2, or_equal); + } + } else if (expr1->str() == "|") { + if ((expr1->astOperand1()->valueType()) && + (expr1->astOperand1()->valueType()->sign == ValueType::Sign::UNSIGNED)) { + const bool or_equal = Token::Match(tok, ">=|<="); + const std::string& op(tok->str()); + if ((Token::Match(tok, ">=|<")) && (num1 >= num2)) { + //"(a | 0x07) >= 7U" is always true for unsigned a + //"(a | 0x07) < 7U" is always false for unsigned a + comparisonError(expr1, expr1->str(), num1, op, num2, or_equal); + } else if ((Token::Match(tok, "<=|>")) && (num1 > num2)) { + //"(a | 0x08) <= 7U" is always false for unsigned a + //"(a | 0x07) > 6U" is always true for unsigned a + comparisonError(expr1, expr1->str(), num1, op, num2, !or_equal); + } + } + } + } + } +} + +void CheckCondition::comparisonError(const Token *tok, const std::string &bitop, MathLib::bigint value1, const std::string &op, MathLib::bigint value2, bool result) +{ + std::ostringstream expression; + expression << std::hex << "(X " << bitop << " 0x" << value1 << ") " << op << " 0x" << value2; + + const std::string errmsg("Expression '" + expression.str() + "' is always " + bool_to_string(result) + ".\n" + "The expression '" + expression.str() + "' is always " + bool_to_string(result) + + ". Check carefully constants and operators used, these errors might be hard to " + "spot sometimes. In case of complex expression it might help to split it to " + "separate expressions."); + + reportError(tok, Severity::style, "comparisonError", errmsg, CWE398, Certainty::normal); +} + +bool CheckCondition::isOverlappingCond(const Token * const cond1, const Token * const cond2, bool pure) const +{ + if (!cond1 || !cond2) + return false; + + // same expressions + if (isSameExpression(true, cond1, cond2, mSettings->library, pure, false)) + return true; + + // bitwise overlap for example 'x&7' and 'x==1' + if (cond1->str() == "&" && cond1->astOperand1() && cond2->astOperand2()) { + const Token *expr1 = cond1->astOperand1(); + const Token *num1 = cond1->astOperand2(); + if (!num1) // unary operator& + return false; + if (!num1->isNumber()) + std::swap(expr1,num1); + if (!num1->isNumber() || MathLib::isNegative(num1->str())) + return false; + + if (!Token::Match(cond2, "&|==") || !cond2->astOperand1() || !cond2->astOperand2()) + return false; + const Token *expr2 = cond2->astOperand1(); + const Token *num2 = cond2->astOperand2(); + if (!num2->isNumber()) + std::swap(expr2,num2); + if (!num2->isNumber() || MathLib::isNegative(num2->str())) + return false; + + if (!isSameExpression(true, expr1, expr2, mSettings->library, pure, false)) + return false; + + const MathLib::bigint value1 = MathLib::toBigNumber(num1->str()); + const MathLib::bigint value2 = MathLib::toBigNumber(num2->str()); + if (cond2->str() == "&") + return ((value1 & value2) == value2); + return ((value1 & value2) > 0); + } + return false; +} + +void CheckCondition::duplicateCondition() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("duplicateCondition")) + return; + + logChecker("CheckCondition::duplicateCondition"); // style + + const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope &scope : symbolDatabase->scopeList) { + if (scope.type != Scope::eIf) + continue; + + const Token* tok2 = scope.classDef->next(); + if (!tok2) + continue; + const Token* cond1 = tok2->astOperand2(); + if (!cond1) + continue; + if (cond1->hasKnownIntValue()) + continue; + + tok2 = tok2->link(); + if (!Token::simpleMatch(tok2, ") {")) + continue; + tok2 = tok2->linkAt(1); + if (!Token::simpleMatch(tok2, "} if (")) + continue; + const Token *cond2 = tok2->tokAt(2)->astOperand2(); + if (!cond2) + continue; + + ErrorPath errorPath; + if (!findExpressionChanged(cond1, scope.classDef->next(), cond2, mSettings) && + isSameExpression(true, cond1, cond2, mSettings->library, true, true, &errorPath)) + duplicateConditionError(cond1, cond2, std::move(errorPath)); + } +} + +void CheckCondition::duplicateConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath) +{ + if (diag(tok1) & diag(tok2)) + return; + errorPath.emplace_back(tok1, "First condition"); + errorPath.emplace_back(tok2, "Second condition"); + + std::string msg = "The if condition is the same as the previous if condition"; + + reportError(errorPath, Severity::style, "duplicateCondition", msg, CWE398, Certainty::normal); +} + +void CheckCondition::multiCondition() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("multiCondition")) + return; + + logChecker("CheckCondition::multiCondition"); // style + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope &scope : symbolDatabase->scopeList) { + if (scope.type != Scope::eIf) + continue; + + const Token * const cond1 = scope.classDef->next()->astOperand2(); + if (!cond1) + continue; + + const Token * tok2 = scope.classDef->next(); + + // Check each 'else if' + for (;;) { + tok2 = tok2->link(); + if (!Token::simpleMatch(tok2, ") {")) + break; + tok2 = tok2->linkAt(1); + if (!Token::simpleMatch(tok2, "} else { if (")) + break; + tok2 = tok2->tokAt(4); + + if (tok2->astOperand2()) { + ErrorPath errorPath; + if (isOverlappingCond(cond1, tok2->astOperand2(), true) && + !findExpressionChanged(cond1, cond1, tok2->astOperand2(), mSettings)) + overlappingElseIfConditionError(tok2->astOperand2(), cond1->linenr()); + else if (isOppositeCond( + true, cond1, tok2->astOperand2(), mSettings->library, true, true, &errorPath) && + !findExpressionChanged(cond1, cond1, tok2->astOperand2(), mSettings)) + oppositeElseIfConditionError(cond1, tok2->astOperand2(), std::move(errorPath)); + } + } + } +} + +void CheckCondition::overlappingElseIfConditionError(const Token *tok, nonneg int line1) +{ + if (diag(tok)) + return; + std::ostringstream errmsg; + errmsg << "Expression is always false because 'else if' condition matches previous condition at line " + << line1 << "."; + + reportError(tok, Severity::style, "multiCondition", errmsg.str(), CWE398, Certainty::normal); +} + +void CheckCondition::oppositeElseIfConditionError(const Token *ifCond, const Token *elseIfCond, ErrorPath errorPath) +{ + if (diag(ifCond) & diag(elseIfCond)) + return; + std::ostringstream errmsg; + errmsg << "Expression is always true because 'else if' condition is opposite to previous condition at line " + << ifCond->linenr() << "."; + + errorPath.emplace_back(ifCond, "first condition"); + errorPath.emplace_back(elseIfCond, "else if condition is opposite to first condition"); + + reportError(errorPath, Severity::style, "multiCondition", errmsg.str(), CWE398, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// - Opposite inner conditions => always false +// - (TODO) Same/Overlapping inner condition => always true +// - same condition after early exit => always false +//--------------------------------------------------------------------------- + +static bool isNonConstFunctionCall(const Token *ftok, const Library &library) +{ + if (library.isFunctionConst(ftok)) + return false; + const Token *obj = ftok->next()->astOperand1(); + while (obj && obj->str() == ".") + obj = obj->astOperand1(); + if (!obj) + return true; + if (obj->variable() && obj->variable()->isConst()) + return false; + if (ftok->function() && ftok->function()->isConst()) + return false; + return true; +} + +void CheckCondition::multiCondition2() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckCondition::multiCondition2"); // warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope &scope : symbolDatabase->scopeList) { + const Token *condTok = nullptr; + if (scope.type == Scope::eIf || scope.type == Scope::eWhile) + condTok = scope.classDef->next()->astOperand2(); + else if (scope.type == Scope::eFor) { + condTok = scope.classDef->next()->astOperand2(); + if (!condTok || condTok->str() != ";") + continue; + condTok = condTok->astOperand2(); + if (!condTok || condTok->str() != ";") + continue; + condTok = condTok->astOperand1(); + } + if (!condTok) + continue; + const Token * const cond1 = condTok; + + if (!Token::simpleMatch(scope.classDef->linkAt(1), ") {")) + continue; + + bool functionCall = false; + bool nonConstFunctionCall = false; + bool nonlocal = false; // nonlocal variable used in condition + std::set vars; // variables used in condition + visitAstNodes(condTok, + [&](const Token *cond) { + if (Token::Match(cond, "%name% (")) { + functionCall = true; + nonConstFunctionCall = isNonConstFunctionCall(cond, mSettings->library); + if (nonConstFunctionCall) + return ChildrenToVisit::done; + } + + if (cond->varId()) { + vars.insert(cond->varId()); + const Variable *var = cond->variable(); + if (!nonlocal && var) { + if (!(var->isLocal() || var->isArgument())) + nonlocal = true; + else if ((var->isPointer() || var->isReference()) && !Token::Match(cond->astParent(), "%oror%|&&|!")) + // TODO: if var is pointer check what it points at + nonlocal = true; + } + } else if (!nonlocal && cond->isName()) { + // varid is 0. this is possibly a nonlocal variable.. + nonlocal = Token::Match(cond->astParent(), "%cop%|(|[") || Token::Match(cond, "%name% .") || (cond->isCpp() && cond->str() == "this"); + } else { + return ChildrenToVisit::op1_and_op2; + } + return ChildrenToVisit::none; + }); + + if (nonConstFunctionCall) + continue; + + std::vector varsInCond; + visitAstNodes(condTok, + [&varsInCond](const Token *cond) { + if (cond->variable()) { + const Variable *var = cond->variable(); + if (std::find(varsInCond.cbegin(), varsInCond.cend(), var) == varsInCond.cend()) + varsInCond.push_back(var); + } + return ChildrenToVisit::op1_and_op2; + }); + + // parse until second condition is reached.. + enum MULTICONDITIONTYPE { INNER, AFTER }; + const Token *tok; + + // Parse inner condition first and then early return condition + std::vector types = {MULTICONDITIONTYPE::INNER}; + if (Token::Match(scope.bodyStart, "{ return|throw|continue|break")) + types.push_back(MULTICONDITIONTYPE::AFTER); + for (const MULTICONDITIONTYPE type:types) { + if (type == MULTICONDITIONTYPE::AFTER) { + tok = scope.bodyEnd->next(); + } else { + tok = scope.bodyStart; + } + const Token * const endToken = tok->scope()->bodyEnd; + + for (; tok && tok != endToken; tok = tok->next()) { + if (isExpressionChangedAt(cond1, tok, 0, false, mSettings)) + break; + if (Token::Match(tok, "if|return")) { + const Token * condStartToken = tok->str() == "if" ? tok->next() : tok; + const Token * condEndToken = tok->str() == "if" ? condStartToken->link() : Token::findsimplematch(condStartToken, ";"); + // Does condition modify tracked variables? + if (findExpressionChanged(cond1, condStartToken, condEndToken, mSettings)) + break; + + // Condition.. + const Token *cond2 = tok->str() == "if" ? condStartToken->astOperand2() : condStartToken->astOperand1(); + const bool isReturnVar = (tok->str() == "return" && !Token::Match(cond2, "%cop%")); + + ErrorPath errorPath; + + if (type == MULTICONDITIONTYPE::INNER) { + visitAstNodes(cond1, [&](const Token* firstCondition) { + if (!firstCondition) + return ChildrenToVisit::none; + if (firstCondition->str() == "&&") { + if (!isOppositeCond(false, firstCondition, cond2, mSettings->library, true, true)) + return ChildrenToVisit::op1_and_op2; + } + if (!firstCondition->hasKnownIntValue()) { + if (!isReturnVar && isOppositeCond(false, firstCondition, cond2, mSettings->library, true, true, &errorPath)) { + if (!isAliased(vars)) + oppositeInnerConditionError(firstCondition, cond2, errorPath); + } else if (!isReturnVar && isSameExpression(true, firstCondition, cond2, mSettings->library, true, true, &errorPath)) { + identicalInnerConditionError(firstCondition, cond2, errorPath); + } + } + return ChildrenToVisit::none; + }); + } else { + visitAstNodes(cond2, [&](const Token *secondCondition) { + if (secondCondition->str() == "||" || secondCondition->str() == "&&") + return ChildrenToVisit::op1_and_op2; + + if ((!cond1->hasKnownIntValue() || !secondCondition->hasKnownIntValue()) && + isSameExpression(true, cond1, secondCondition, mSettings->library, true, true, &errorPath)) { + if (!isAliased(vars) && !mTokenizer->hasIfdef(cond1, secondCondition)) { + identicalConditionAfterEarlyExitError(cond1, secondCondition, errorPath); + return ChildrenToVisit::done; + } + } + return ChildrenToVisit::none; + }); + } + } + if (Token::Match(tok, "%name% (") && + isVariablesChanged(tok, tok->linkAt(1), 0, varsInCond, mSettings)) { + break; + } + if (Token::Match(tok, "%type% (") && nonlocal && isNonConstFunctionCall(tok, mSettings->library)) // non const function call -> bailout if there are nonlocal variables + break; + if (Token::Match(tok, "case|break|continue|return|throw") && tok->scope() == endToken->scope()) + break; + if (Token::Match(tok, "[;{}] %name% :")) + break; + // bailout if loop is seen. + // TODO: handle loops better. + if (Token::Match(tok, "for|while|do")) { + const Token *tok1 = tok->next(); + const Token *tok2; + if (Token::simpleMatch(tok, "do {")) { + if (!Token::simpleMatch(tok->linkAt(1), "} while (")) + break; + tok2 = tok->linkAt(1)->linkAt(2); + } else if (Token::Match(tok, "if|while (")) { + tok2 = tok->linkAt(1); + if (Token::simpleMatch(tok2, ") {")) + tok2 = tok2->linkAt(1); + if (!tok2) + break; + } else { + // Incomplete code + break; + } + const bool changed = std::any_of(vars.cbegin(), vars.cend(), [&](int varid) { + return isVariableChanged(tok1, tok2, varid, nonlocal, mSettings); + }); + if (changed) + break; + } + if ((tok->varId() && vars.find(tok->varId()) != vars.end()) || + (!tok->varId() && nonlocal) || + (functionCall && tok->variable() && !tok->variable()->isLocal())) { + if (Token::Match(tok, "%name% %assign%|++|--")) + break; + if (Token::Match(tok->astParent(), "*|.|[")) { + const Token *parent = tok; + while (Token::Match(parent->astParent(), ".|[") || (parent->astParent() && parent->astParent()->isUnaryOp("*"))) + parent = parent->astParent(); + if (Token::Match(parent->astParent(), "%assign%|++|--")) + break; + } + if (tok->isCpp() && Token::Match(tok, "%name% <<") && (!tok->valueType() || !tok->valueType()->isIntegral())) + break; + if (isLikelyStreamRead(tok->next()) || isLikelyStreamRead(tok->previous())) + break; + if (Token::Match(tok, "%name% [")) { + const Token *tok2 = tok->linkAt(1); + while (Token::simpleMatch(tok2, "] [")) + tok2 = tok2->linkAt(1); + if (Token::Match(tok2, "] %assign%|++|--")) + break; + } + if (Token::Match(tok->previous(), "++|--|& %name%")) + break; + if (tok->variable() && + !tok->variable()->isConst() && + Token::Match(tok, "%name% . %name% (")) { + const Function* function = tok->tokAt(2)->function(); + if (!function || !function->isConst()) + break; + } + if (Token::Match(tok->previous(), "[(,] *|& %name% [,)]") && isParameterChanged(tok)) + break; + } + } + } + } +} + +static std::string innerSmtString(const Token * tok) +{ + if (!tok) + return "if"; + if (!tok->astTop()) + return "if"; + const Token * top = tok->astTop(); + if (top->str() == "(" && top->astOperand1()) + return top->astOperand1()->str(); + return top->str(); +} + +void CheckCondition::oppositeInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath) +{ + if (diag(tok1) & diag(tok2)) + return; + const std::string s1(tok1 ? tok1->expressionString() : "x"); + const std::string s2(tok2 ? tok2->expressionString() : "!x"); + const std::string innerSmt = innerSmtString(tok2); + errorPath.emplace_back(tok1, "outer condition: " + s1); + errorPath.emplace_back(tok2, "opposite inner condition: " + s2); + + const std::string msg("Opposite inner '" + innerSmt + "' condition leads to a dead code block.\n" + "Opposite inner '" + innerSmt + "' condition leads to a dead code block (outer condition is '" + s1 + "' and inner condition is '" + s2 + "')."); + reportError(errorPath, Severity::warning, "oppositeInnerCondition", msg, CWE398, Certainty::normal); +} + +void CheckCondition::identicalInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath) +{ + if (diag(tok1) & diag(tok2)) + return; + const std::string s1(tok1 ? tok1->expressionString() : "x"); + const std::string s2(tok2 ? tok2->expressionString() : "x"); + const std::string innerSmt = innerSmtString(tok2); + errorPath.emplace_back(tok1, "outer condition: " + s1); + errorPath.emplace_back(tok2, "identical inner condition: " + s2); + + const std::string msg("Identical inner '" + innerSmt + "' condition is always true.\n" + "Identical inner '" + innerSmt + "' condition is always true (outer condition is '" + s1 + "' and inner condition is '" + s2 + "')."); + reportError(errorPath, Severity::warning, "identicalInnerCondition", msg, CWE398, Certainty::normal); +} + +void CheckCondition::identicalConditionAfterEarlyExitError(const Token *cond1, const Token* cond2, ErrorPath errorPath) +{ + if (diag(cond1) & diag(cond2)) + return; + + const bool isReturnValue = cond2 && Token::simpleMatch(cond2->astParent(), "return"); + + const std::string cond(cond1 ? cond1->expressionString() : "x"); + const std::string value = (cond2 && cond2->valueType() && cond2->valueType()->type == ValueType::Type::BOOL) ? "false" : "0"; + + errorPath.emplace_back(cond1, "If condition '" + cond + "' is true, the function will return/exit"); + errorPath.emplace_back(cond2, (isReturnValue ? "Returning identical expression '" : "Testing identical condition '") + cond + "'"); + + reportError(errorPath, + Severity::warning, + "identicalConditionAfterEarlyExit", + isReturnValue + ? ("Identical condition and return expression '" + cond + "', return value is always " + value) + : ("Identical condition '" + cond + "', second condition is always false"), + CWE398, + Certainty::normal); +} + +//--------------------------------------------------------------------------- +// if ((x != 1) || (x != 3)) // expression always true +// if ((x == 1) && (x == 3)) // expression always false +// if ((x < 1) && (x > 3)) // expression always false +// if ((x > 3) || (x < 10)) // expression always true +// if ((x > 5) && (x != 1)) // second comparison always true +// +// Check for suspect logic for an expression consisting of 2 comparison +// expressions with a shared variable and constants and a logical operator +// between them. +// +// Suggest a different logical operator when the logical operator between +// the comparisons is probably wrong. +// +// Inform that second comparison is always true when first comparison is true. +//--------------------------------------------------------------------------- + +static std::string invertOperatorForOperandSwap(std::string s) +{ + if (s[0] == '<') + s[0] = '>'; + else if (s[0] == '>') + s[0] = '<'; + return s; +} + +template +static int sign(const T v) { + return static_cast(v > 0) - static_cast(v < 0); +} + +// returns 1 (-1) if the first (second) condition is sufficient, 0 if indeterminate +template +static int sufficientCondition(std::string op1, const bool not1, const T value1, std::string op2, const bool not2, const T value2, const bool isAnd) { + auto transformOp = [](std::string& op, const bool invert) { + if (invert) { + if (op == "==") + op = "!="; + else if (op == "!=") + op = "=="; + else if (op == "<") + op = ">="; + else if (op == ">") + op = "<="; + else if (op == "<=") + op = ">"; + else if (op == ">=") + op = "<"; + } + }; + transformOp(op1, not1); + transformOp(op2, not2); + int res = 0; + bool equal = false; + if (op1 == op2) { + equal = true; + if (op1 == ">" || op1 == ">=") + res = sign(value1 - value2); + else if (op1 == "<" || op1 == "<=") + res = -sign(value1 - value2); + } else { // not equal + if (op1 == "!=") + res = 1; + else if (op2 == "!=") + res = -1; + else if (op1 == "==") + res = -1; + else if (op2 == "==") + res = 1; + else if (op1 == ">" && op2 == ">=") + res = sign(value1 - (value2 - 1)); + else if (op1 == ">=" && op2 == ">") + res = sign((value1 - 1) - value2); + else if (op1 == "<" && op2 == "<=") + res = -sign(value1 - (value2 + 1)); + else if (op1 == "<=" && op2 == "<") + res = -sign((value1 + 1) - value2); + } + return res * (isAnd == equal ? 1 : -1); +} + +template +static bool checkIntRelation(const std::string &op, const T value1, const T value2) +{ + return (op == "==" && value1 == value2) || + (op == "!=" && value1 != value2) || + (op == ">" && value1 > value2) || + (op == ">=" && value1 >= value2) || + (op == "<" && value1 < value2) || + (op == "<=" && value1 <= value2); +} + +static bool checkFloatRelation(const std::string &op, const double value1, const double value2) +{ + return (op == ">" && value1 > value2) || + (op == ">=" && value1 >= value2) || + (op == "<" && value1 < value2) || + (op == "<=" && value1 <= value2); +} + +template +static T getvalue3(const T value1, const T value2) +{ + const T min = std::min(value1, value2); + if (min== std::numeric_limits::max()) + return min; + return min + 1; // see #5895 +} + +template<> +double getvalue3(const double value1, const double value2) +{ + return (value1 + value2) / 2.0; +} + + +template +static inline T getvalue(const int test, const T value1, const T value2) +{ + // test: + // 1 => return value that is less than both value1 and value2 + // 2 => return value1 + // 3 => return value that is between value1 and value2 + // 4 => return value2 + // 5 => return value that is larger than both value1 and value2 + switch (test) { + case 1: + return std::numeric_limits::lowest(); + case 2: + return value1; + case 3: + return getvalue3(value1, value2); + case 4: + return value2; + case 5: + return std::numeric_limits::max(); + } + return 0; +} + +static bool parseComparison(const Token *comp, bool ¬1, std::string &op, std::string &value, const Token *&expr, bool &inconclusive) +{ + not1 = false; + while (comp && comp->str() == "!") { + not1 = !(not1); + comp = comp->astOperand1(); + } + + if (!comp) + return false; + + const Token* op1 = comp->astOperand1(); + const Token* op2 = comp->astOperand2(); + if (!comp->isComparisonOp() || !op1 || !op2) { + op = "!="; + value = "0"; + expr = comp; + } else if (op1->isLiteral()) { + if (op1->isExpandedMacro()) + return false; + op = invertOperatorForOperandSwap(comp->str()); + if (op1->enumerator() && op1->enumerator()->value_known) + value = std::to_string(op1->enumerator()->value); + else + value = op1->str(); + expr = op2; + } else if (comp->astOperand2()->isLiteral()) { + if (op2->isExpandedMacro()) + return false; + op = comp->str(); + if (op2->enumerator() && op2->enumerator()->value_known) + value = std::to_string(op2->enumerator()->value); + else + value = op2->str(); + expr = op1; + } else { + op = "!="; + value = "0"; + expr = comp; + } + + inconclusive = inconclusive || ((value)[0] == '\'' && !(op == "!=" || op == "==")); + + // Only float and int values are currently handled + return MathLib::isInt(value) || MathLib::isFloat(value) || (value[0] == '\''); +} + +static std::string conditionString(bool not1, const Token *expr1, const std::string &op, const std::string &value1) +{ + if (expr1->astParent()->isComparisonOp()) + return std::string(not1 ? "!(" : "") + expr1->expressionString() + + " " + + op + + " " + + value1 + + (not1 ? ")" : ""); + + return std::string(not1 ? "!" : "") + expr1->expressionString(); +} + +static std::string conditionString(const Token * tok) +{ + if (!tok) + return ""; + if (tok->isComparisonOp()) { + bool inconclusive = false; + bool not_; + std::string op, value; + const Token *expr; + if (parseComparison(tok, not_, op, value, expr, inconclusive) && expr->isName()) { + return conditionString(not_, expr, op, value); + } + } + if (Token::Match(tok, "%cop%|&&|%oror%")) { + if (tok->astOperand2()) + return conditionString(tok->astOperand1()) + " " + tok->str() + " " + conditionString(tok->astOperand2()); + return tok->str() + "(" + conditionString(tok->astOperand1()) + ")"; + + } + return tok->expressionString(); +} + +static bool isIfConstexpr(const Token* tok) { + const Token* const top = tok->astTop(); + return top && Token::simpleMatch(top->astOperand1(), "if") && top->astOperand1()->isConstexpr(); +} + +void CheckCondition::checkIncorrectLogicOperator() +{ + const bool printStyle = mSettings->severity.isEnabled(Severity::style); + const bool printWarning = mSettings->severity.isEnabled(Severity::warning); + if (!printWarning && !printStyle && !mSettings->isPremiumEnabled("incorrectLogicOperator")) + return; + const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); + + logChecker("CheckCondition::checkIncorrectLogicOperator"); // style,warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::Match(tok, "%oror%|&&") || !tok->astOperand1() || !tok->astOperand2()) + continue; + + + // 'A && (!A || B)' is equivalent to 'A && B' + // 'A || (!A && B)' is equivalent to 'A || B' + // 'A && (A || B)' is equivalent to 'A' + // 'A || (A && B)' is equivalent to 'A' + if (printStyle && + ((tok->str() == "||" && tok->astOperand2()->str() == "&&") || + (tok->str() == "&&" && tok->astOperand2()->str() == "||"))) { + const Token* tok2 = tok->astOperand2()->astOperand1(); + if (isOppositeCond(true, tok->astOperand1(), tok2, mSettings->library, true, false)) { + std::string expr1(tok->astOperand1()->expressionString()); + std::string expr2(tok->astOperand2()->astOperand1()->expressionString()); + std::string expr3(tok->astOperand2()->astOperand2()->expressionString()); + // make copy for later because the original string might get overwritten + const std::string expr1VerboseMsg = expr1; + const std::string expr2VerboseMsg = expr2; + const std::string expr3VerboseMsg = expr3; + + if (expr1.length() + expr2.length() + expr3.length() > 50U) { + if (expr1[0] == '!' && expr2[0] != '!') { + expr1 = "!A"; + expr2 = "A"; + } else { + expr1 = "A"; + expr2 = "!A"; + } + + expr3 = "B"; + } + + const std::string cond1 = expr1 + " " + tok->str() + " (" + expr2 + " " + tok->astOperand2()->str() + " " + expr3 + ")"; + const std::string cond2 = expr1 + " " + tok->str() + " " + expr3; + + const std::string cond1VerboseMsg = expr1VerboseMsg + " " + tok->str() + " " + expr2VerboseMsg + " " + tok->astOperand2()->str() + " " + expr3VerboseMsg; + const std::string cond2VerboseMsg = expr1VerboseMsg + " " + tok->str() + " " + expr3VerboseMsg; + // for the --verbose message, transform the actual condition and print it + const std::string msg = tok2->expressionString() + ". '" + cond1 + "' is equivalent to '" + cond2 + "'\n" + "The condition '" + cond1VerboseMsg + "' is equivalent to '" + cond2VerboseMsg + "'."; + redundantConditionError(tok, msg, false); + continue; + } + if (isSameExpression(false, tok->astOperand1(), tok2, mSettings->library, true, true)) { + std::string expr1(tok->astOperand1()->expressionString()); + std::string expr2(tok->astOperand2()->astOperand1()->expressionString()); + std::string expr3(tok->astOperand2()->astOperand2()->expressionString()); + // make copy for later because the original string might get overwritten + const std::string expr1VerboseMsg = expr1; + const std::string expr2VerboseMsg = expr2; + const std::string expr3VerboseMsg = expr3; + + if (expr1.length() + expr2.length() + expr3.length() > 50U) { + expr1 = "A"; + expr2 = "A"; + expr3 = "B"; + } + + const std::string cond1 = expr1 + " " + tok->str() + " (" + expr2 + " " + tok->astOperand2()->str() + " " + expr3 + ")"; + const std::string cond2 = std::move(expr1); + + const std::string cond1VerboseMsg = expr1VerboseMsg + " " + tok->str() + " " + expr2VerboseMsg + " " + tok->astOperand2()->str() + " " + expr3VerboseMsg; + const std::string& cond2VerboseMsg = expr1VerboseMsg; + // for the --verbose message, transform the actual condition and print it + const std::string msg = tok2->expressionString() + ". '" + cond1 + "' is equivalent to '" + cond2 + "'\n" + "The condition '" + cond1VerboseMsg + "' is equivalent to '" + cond2VerboseMsg + "'."; + redundantConditionError(tok, msg, false); + continue; + } + } + + // Comparison #1 (LHS) + const Token *comp1 = tok->astOperand1(); + if (comp1->str() == tok->str()) + comp1 = comp1->astOperand2(); + + // Comparison #2 (RHS) + const Token *comp2 = tok->astOperand2(); + + bool inconclusive = false; + bool parseable = true; + + // Parse LHS + bool not1; + std::string op1, value1; + const Token *expr1 = nullptr; + parseable &= (parseComparison(comp1, not1, op1, value1, expr1, inconclusive)); + + // Parse RHS + bool not2; + std::string op2, value2; + const Token *expr2 = nullptr; + parseable &= (parseComparison(comp2, not2, op2, value2, expr2, inconclusive)); + + if (inconclusive && !printInconclusive) + continue; + + const bool isUnknown = (expr1 && expr1->valueType() && expr1->valueType()->type == ValueType::UNKNOWN_TYPE) || + (expr2 && expr2->valueType() && expr2->valueType()->type == ValueType::UNKNOWN_TYPE); + if (isUnknown) + continue; + + const bool isfloat = astIsFloat(expr1, true) || MathLib::isFloat(value1) || astIsFloat(expr2, true) || MathLib::isFloat(value2); + + ErrorPath errorPath; + + // Opposite comparisons around || or && => always true or always false + const bool isLogicalOr(tok->str() == "||"); + if (!isfloat && isOppositeCond(isLogicalOr, tok->astOperand1(), tok->astOperand2(), mSettings->library, true, true, &errorPath)) { + if (!isIfConstexpr(tok)) { + const bool alwaysTrue(isLogicalOr); + incorrectLogicOperatorError(tok, conditionString(tok), alwaysTrue, inconclusive, errorPath); + } + continue; + } + + if (!parseable) + continue; + + if (isSameExpression(true, comp1, comp2, mSettings->library, true, true)) + continue; // same expressions => only report that there are same expressions + if (!isSameExpression(true, expr1, expr2, mSettings->library, true, true)) + continue; + + + // don't check floating point equality comparisons. that is bad + // and deserves different warnings. + if (isfloat && (op1 == "==" || op1 == "!=" || op2 == "==" || op2 == "!=")) + continue; + + + const double d1 = (isfloat) ? MathLib::toDoubleNumber(value1) : 0; + const double d2 = (isfloat) ? MathLib::toDoubleNumber(value2) : 0; + const MathLib::bigint i1 = (isfloat) ? 0 : MathLib::toBigNumber(value1); + const MathLib::bigint i2 = (isfloat) ? 0 : MathLib::toBigNumber(value2); + const bool useUnsignedInt = (std::numeric_limits::max()==i1) || (std::numeric_limits::max()==i2); + const MathLib::biguint u1 = (useUnsignedInt) ? MathLib::toBigNumber(value1) : 0; + const MathLib::biguint u2 = (useUnsignedInt) ? MathLib::toBigNumber(value2) : 0; + // evaluate if expression is always true/false + bool alwaysTrue = true, alwaysFalse = true; + bool firstTrue = true, secondTrue = true; + const bool isAnd = tok->str() == "&&"; + 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 auto testvalue = getvalue(test, d1, d2); + result1 = checkFloatRelation(op1, testvalue, d1); + result2 = checkFloatRelation(op2, testvalue, d2); + } else if (useUnsignedInt) { + const auto testvalue = getvalue(test, u1, u2); + result1 = checkIntRelation(op1, testvalue, u1); + result2 = checkIntRelation(op2, testvalue, u2); + } else { + const auto testvalue = getvalue(test, i1, i2); + result1 = checkIntRelation(op1, testvalue, i1); + result2 = checkIntRelation(op2, testvalue, i2); + } + if (not1) + result1 = !result1; + if (not2) + result2 = !result2; + if (isAnd) { + alwaysTrue &= (result1 && result2); + alwaysFalse &= !(result1 && result2); + } else { + alwaysTrue &= (result1 || result2); + alwaysFalse &= !(result1 || result2); + } + firstTrue &= !(!result1 && result2); + secondTrue &= !(result1 && !result2); + } + + const std::string cond1str = conditionString(not1, expr1, op1, value1); + const std::string cond2str = conditionString(not2, expr2, op2, value2); + if (printWarning && (alwaysTrue || alwaysFalse)) { + const std::string text = cond1str + " " + tok->str() + " " + cond2str; + incorrectLogicOperatorError(tok, text, alwaysTrue, inconclusive, std::move(errorPath)); + } else if (printStyle && (firstTrue || secondTrue)) { + // cppcheck-suppress accessMoved - TODO: FP - see #12174 + const int which = isfloat ? sufficientCondition(std::move(op1), not1, d1, std::move(op2), not2, d2, isAnd) : sufficientCondition(std::move(op1), not1, i1, std::move(op2), not2, i2, isAnd); + std::string text; + if (which != 0) { + text = "The condition '" + (which == 1 ? cond2str : cond1str) + "' is redundant since '" + (which == 1 ? cond1str : cond2str) + "' is sufficient."; + } else + text = "If '" + (secondTrue ? cond1str : cond2str) + "', the comparison '" + (secondTrue ? cond2str : cond1str) + "' is always true."; + redundantConditionError(tok, text, inconclusive); + } + } + } +} + +void CheckCondition::incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive, ErrorPath errors) +{ + if (diag(tok)) + return; + errors.emplace_back(tok, ""); + if (always) + reportError(errors, Severity::warning, "incorrectLogicOperator", + "Logical disjunction always evaluates to true: " + condition + ".\n" + "Logical disjunction always evaluates to true: " + condition + ". " + "Are these conditions necessary? Did you intend to use && instead? Are the numbers correct? Are you comparing the correct variables?", CWE571, inconclusive ? Certainty::inconclusive : Certainty::normal); + else + reportError(errors, Severity::warning, "incorrectLogicOperator", + "Logical conjunction always evaluates to false: " + condition + ".\n" + "Logical conjunction always evaluates to false: " + condition + ". " + "Are these conditions necessary? Did you intend to use || instead? Are the numbers correct? Are you comparing the correct variables?", CWE570, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckCondition::redundantConditionError(const Token *tok, const std::string &text, bool inconclusive) +{ + if (diag(tok)) + return; + reportError(tok, Severity::style, "redundantCondition", "Redundant condition: " + text, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +//----------------------------------------------------------------------------- +// Detect "(var % val1) > val2" where val2 is >= val1. +//----------------------------------------------------------------------------- +void CheckCondition::checkModuloAlwaysTrueFalse() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckCondition::checkModuloAlwaysTrueFalse"); // warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!tok->isComparisonOp()) + continue; + const Token *num, *modulo; + if (Token::simpleMatch(tok->astOperand1(), "%") && Token::Match(tok->astOperand2(), "%num%")) { + modulo = tok->astOperand1(); + num = tok->astOperand2(); + } else if (Token::Match(tok->astOperand1(), "%num%") && Token::simpleMatch(tok->astOperand2(), "%")) { + num = tok->astOperand1(); + modulo = tok->astOperand2(); + } else { + continue; + } + + if (Token::Match(modulo->astOperand2(), "%num%") && + MathLib::isLessEqual(modulo->astOperand2()->str(), num->str())) + moduloAlwaysTrueFalseError(tok, modulo->astOperand2()->str()); + } + } +} + +void CheckCondition::moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal) +{ + if (diag(tok)) + return; + reportError(tok, Severity::warning, "moduloAlwaysTrueFalse", + "Comparison of modulo result is predetermined, because it is always less than " + maxVal + ".", CWE398, Certainty::normal); +} + +static int countPar(const Token *tok1, const Token *tok2) +{ + int par = 0; + for (const Token *tok = tok1; tok && tok != tok2; tok = tok->next()) { + if (tok->str() == "(") + ++par; + else if (tok->str() == ")") + --par; + else if (tok->str() == ";") + return -1; + } + return par; +} + +//--------------------------------------------------------------------------- +// Clarify condition '(x = a < 0)' into '((x = a) < 0)' or '(x = (a < 0))' +// Clarify condition '(a & b == c)' into '((a & b) == c)' or '(a & (b == c))' +//--------------------------------------------------------------------------- +void CheckCondition::clarifyCondition() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("clarifyCondition")) + return; + + logChecker("CheckCondition::clarifyCondition"); // style + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "( %name% [=&|^]")) { + for (const Token *tok2 = tok->tokAt(3); tok2; tok2 = tok2->next()) { + if (tok2->str() == "(" || tok2->str() == "[") + tok2 = tok2->link(); + else if (tok2->isComparisonOp()) { + // This might be a template + if (!tok2->isC() && tok2->link()) + break; + if (Token::simpleMatch(tok2->astParent(), "?")) + break; + clarifyConditionError(tok, tok->strAt(2) == "=", false); + break; + } else if (!tok2->isName() && !tok2->isNumber() && tok2->str() != ".") + break; + } + } else if (tok->tokType() == Token::eBitOp && !tok->isUnaryOp("&")) { + if (tok->astOperand2() && tok->astOperand2()->variable() && tok->astOperand2()->variable()->nameToken() == tok->astOperand2()) + continue; + + // using boolean result in bitwise operation ! x [&|^] + const ValueType* vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; + const ValueType* vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr; + if (vt1 && vt1->type == ValueType::BOOL && !Token::Match(tok->astOperand1(), "%name%|(|[|::|.") && countPar(tok->astOperand1(), tok) == 0) + clarifyConditionError(tok, false, true); + else if (vt2 && vt2->type == ValueType::BOOL && !Token::Match(tok->astOperand2(), "%name%|(|[|::|.") && countPar(tok, tok->astOperand2()) == 0) + clarifyConditionError(tok, false, true); + } + } + } +} + +void CheckCondition::clarifyConditionError(const Token *tok, bool assign, bool boolop) +{ + std::string errmsg; + + if (assign) + errmsg = "Suspicious condition (assignment + comparison); Clarify expression with parentheses."; + + else if (boolop) + errmsg = "Boolean result is used in bitwise operation. Clarify expression with parentheses.\n" + "Suspicious expression. Boolean result is used in bitwise operation. The operator '!' " + "and the comparison operators have higher precedence than bitwise operators. " + "It is recommended that the expression is clarified with parentheses."; + else + errmsg = "Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses.\n" + "Suspicious condition. Comparison operators have higher precedence than bitwise operators. " + "Please clarify the condition with parentheses."; + + reportError(tok, + Severity::style, + "clarifyCondition", + errmsg, CWE398, Certainty::normal); +} + +void CheckCondition::alwaysTrueFalse() +{ + if (!mSettings->severity.isEnabled(Severity::style) && + !mSettings->isPremiumEnabled("alwaysTrue") && + !mSettings->isPremiumEnabled("alwaysFalse")) + return; + + logChecker("CheckCondition::alwaysTrueFalse"); // style + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + // don't write false positives when templates are used or inside of asserts or non-evaluated contexts + if (tok->link() && (Token::simpleMatch(tok, "<") || + Token::Match(tok->previous(), "static_assert|assert|ASSERT|sizeof|decltype ("))) { + tok = tok->link(); + continue; + } + if (!tok->hasKnownIntValue()) + continue; + if (Token::Match(tok->previous(), "%name% (") && tok->previous()->function()) { + const Function* f = tok->previous()->function(); + if (f->functionScope && Token::Match(f->functionScope->bodyStart, "{ return true|false ;")) + continue; + } + const Token* condition = nullptr; + { + // is this a condition.. + const Token *parent = tok->astParent(); + while (Token::Match(parent, "%oror%|&&")) + parent = parent->astParent(); + if (!parent) + continue; + if (parent->str() == "?" && precedes(tok, parent)) + condition = parent; + else if (Token::Match(parent->previous(), "if|while (")) + condition = parent->previous(); + else if (Token::simpleMatch(parent, "return")) + condition = parent; + else if (parent->str() == ";" && parent->astParent() && parent->astParent()->astParent() && + Token::simpleMatch(parent->astParent()->astParent()->previous(), "for (")) + condition = parent->astParent()->astParent()->previous(); + else if (Token::Match(tok, "%comp%")) + condition = tok; + else + continue; + } + // Skip already diagnosed values + if (diag(tok, false)) + continue; + if (condition->isConstexpr()) + continue; + if (!isUsedAsBool(tok)) + continue; + if (Token::simpleMatch(condition, "return") && Token::Match(tok, "%assign%")) + continue; + if (Token::simpleMatch(tok->astParent(), "return") && Token::Match(tok, ".|%var%")) + continue; + if (Token::Match(tok, "%num%|%bool%|%char%")) + continue; + if (Token::Match(tok, "! %num%|%bool%|%char%")) + continue; + if (Token::Match(tok, "%oror%|&&")) { + bool bail = false; + for (const Token* op : { tok->astOperand1(), tok->astOperand2() }) { + if (op->hasKnownIntValue() && (!op->isLiteral() || op->isBoolean())) { + bail = true; + break; + } + } + if (bail) + continue; + } + if (Token::simpleMatch(tok, ":")) + continue; + if (Token::Match(tok->astOperand1(), "%name% (") && Token::simpleMatch(tok->astParent(), "return")) + continue; + if (tok->isComparisonOp() && isWithoutSideEffects(tok->astOperand1()) && + isSameExpression(true, + tok->astOperand1(), + tok->astOperand2(), + mSettings->library, + true, + true)) + continue; + if (isConstVarExpression(tok, [](const Token* tok) { + return Token::Match(tok, "[|(|&|+|-|*|/|%|^|>>|<<") && !Token::simpleMatch(tok, "( )"); + })) + continue; + + // there are specific warnings about nonzero expressions (pointer/unsigned) + // do not write alwaysTrueFalse for these comparisons. + { + const ValueFlow::Value *zeroValue = nullptr; + const Token *nonZeroExpr = nullptr; + if (CheckOther::comparisonNonZeroExpressionLessThanZero(tok, zeroValue, nonZeroExpr, /*suppress*/ true) || + CheckOther::testIfNonZeroExpressionIsPositive(tok, zeroValue, nonZeroExpr)) + continue; + } + + // Don't warn when there are expanded macros.. + bool isExpandedMacro = false; + visitAstNodes(tok, [&](const Token * tok2) { + if (!tok2) + return ChildrenToVisit::none; + if (tok2->isExpandedMacro()) { + isExpandedMacro = true; + return ChildrenToVisit::done; + } + return ChildrenToVisit::op1_and_op2; + }); + if (isExpandedMacro) + continue; + for (const Token *parent = tok; parent; parent = parent->astParent()) { + if (parent->isExpandedMacro()) { + isExpandedMacro = true; + break; + } + } + if (isExpandedMacro) + continue; + + // don't warn when condition checks sizeof result + bool hasSizeof = false; + visitAstNodes(tok, [&](const Token * tok2) { + if (!tok2) + return ChildrenToVisit::none; + if (tok2->isNumber()) + return ChildrenToVisit::none; + if (Token::simpleMatch(tok2->previous(), "sizeof (")) { + hasSizeof = true; + return ChildrenToVisit::none; + } + if (tok2->isComparisonOp() || tok2->isArithmeticalOp()) { + return ChildrenToVisit::op1_and_op2; + } + return ChildrenToVisit::none; + }); + if (hasSizeof) + continue; + + alwaysTrueFalseError(tok, condition, &tok->values().front()); + } + } +} + +void CheckCondition::alwaysTrueFalseError(const Token* tok, const Token* condition, const ValueFlow::Value* value) +{ + const bool alwaysTrue = value && (value->intvalue != 0 || value->isImpossible()); + const std::string expr = tok ? tok->expressionString() : std::string("x"); + const std::string conditionStr = (Token::simpleMatch(condition, "return") ? "Return value" : "Condition"); + const std::string errmsg = conditionStr + " '" + expr + "' is always " + bool_to_string(alwaysTrue); + const ErrorPath errorPath = getErrorPath(tok, value, errmsg); + reportError(errorPath, + Severity::style, + "knownConditionTrueFalse", + errmsg, + (alwaysTrue ? CWE571 : CWE570), Certainty::normal); +} + +void CheckCondition::checkInvalidTestForOverflow() +{ + // Interesting blogs: + // https://www.airs.com/blog/archives/120 + // https://kristerw.blogspot.com/2016/02/how-undefined-signed-overflow-enables.html + // https://research.checkpoint.com/2020/optout-compiler-undefined-behavior-optimizations/ + + // x + c < x -> false + // x + c <= x -> false + // x + c > x -> true + // x + c >= x -> true + + // x + y < x -> y < 0 + + + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckCondition::checkInvalidTestForOverflow"); // warning + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (!Token::Match(tok, "<|<=|>=|>") || !tok->isBinaryOp()) + continue; + + const Token *lhsTokens[2] = {tok->astOperand1(), tok->astOperand2()}; + for (const Token *lhs: lhsTokens) { + std::string cmp = tok->str(); + if (lhs == tok->astOperand2()) + cmp[0] = (cmp[0] == '<') ? '>' : '<'; + + if (!Token::Match(lhs, "[+-]") || !lhs->isBinaryOp()) + continue; + + const bool isSignedInteger = lhs->valueType() && lhs->valueType()->isIntegral() && lhs->valueType()->sign == ValueType::Sign::SIGNED; + const bool isPointer = lhs->valueType() && lhs->valueType()->pointer > 0; + if (!isSignedInteger && !isPointer) + continue; + + const Token *exprTokens[2] = {lhs->astOperand1(), lhs->astOperand2()}; + for (const Token *expr: exprTokens) { + if (lhs->str() == "-" && expr == lhs->astOperand2()) + continue; // TODO? + + if (expr->hasKnownIntValue()) + continue; + + if (!isSameExpression(true, + expr, + lhs->astSibling(), + mSettings->library, + true, + false)) + continue; + + const Token * const other = expr->astSibling(); + + // x [+-] c cmp x + if ((other->isNumber() && other->getKnownIntValue() > 0) || + (!other->isNumber() && other->valueType() && other->valueType()->isIntegral() && other->valueType()->sign == ValueType::Sign::UNSIGNED)) { + bool result; + if (lhs->str() == "+") + result = (cmp == ">" || cmp == ">="); + else + result = (cmp == "<" || cmp == "<="); + invalidTestForOverflow(tok, lhs->valueType(), bool_to_string(result)); + continue; + } + + // x + y cmp x + if (lhs->str() == "+" && other->varId() > 0) { + const std::string result = other->str() + cmp + "0"; + invalidTestForOverflow(tok, lhs->valueType(), result); + continue; + } + + // x - y cmp x + if (lhs->str() == "-" && other->varId() > 0) { + std::string cmp2 = cmp; + cmp2[0] = (cmp[0] == '<') ? '>' : '<'; + const std::string result = other->str() + cmp2 + "0"; + invalidTestForOverflow(tok, lhs->valueType(), result); + continue; + } + } + } + } +} + +void CheckCondition::invalidTestForOverflow(const Token* tok, const ValueType *valueType, const std::string &replace) +{ + const std::string expr = (tok ? tok->expressionString() : std::string("x + c < x")); + const std::string overflow = (valueType && valueType->pointer) ? "pointer overflow" : "signed integer overflow"; + + std::string errmsg = + "Invalid test for overflow '" + expr + "'; " + overflow + " is undefined behavior."; + if (replace == "false" || replace == "true") + errmsg += " Some mainstream compilers remove such overflow tests when optimising the code and assume it's always " + replace + "."; + else + errmsg += " Some mainstream compilers removes handling of overflows when optimising the code and change the code to '" + replace + "'."; + reportError(tok, Severity::warning, "invalidTestForOverflow", errmsg, uncheckedErrorConditionCWE, Certainty::normal); +} + + +void CheckCondition::checkPointerAdditionResultNotNull() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckCondition::checkPointerAdditionResultNotNull"); // warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (!tok->isComparisonOp() || !tok->astOperand1() || !tok->astOperand2()) + continue; + + // Macros might have pointless safety checks + if (tok->isExpandedMacro()) + continue; + + const Token *calcToken, *exprToken; + if (tok->astOperand1()->str() == "+") { + calcToken = tok->astOperand1(); + exprToken = tok->astOperand2(); + } else if (tok->astOperand2()->str() == "+") { + calcToken = tok->astOperand2(); + exprToken = tok->astOperand1(); + } else + continue; + + // pointer comparison against NULL (ptr+12==0) + if (calcToken->hasKnownIntValue()) + continue; + if (!calcToken->valueType() || calcToken->valueType()->pointer==0) + continue; + if (!exprToken->hasKnownIntValue() || !exprToken->getValue(0)) + continue; + + pointerAdditionResultNotNullError(tok, calcToken); + } + } +} + +void CheckCondition::pointerAdditionResultNotNullError(const Token *tok, const Token *calc) +{ + const std::string s = calc ? calc->expressionString() : "ptr+1"; + reportError(tok, Severity::warning, "pointerAdditionResultNotNull", "Comparison is wrong. Result of '" + s + "' can't be 0 unless there is pointer overflow, and pointer overflow is undefined behaviour."); +} + +void CheckCondition::checkDuplicateConditionalAssign() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("duplicateConditionalAssign")) + return; + + logChecker("CheckCondition::checkDuplicateConditionalAssign"); // style + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::simpleMatch(tok, "if (")) + continue; + if (!Token::simpleMatch(tok->next()->link(), ") {")) + continue; + const Token *blockTok = tok->next()->link()->next(); + const Token *condTok = tok->next()->astOperand2(); + const bool isBoolVar = Token::Match(condTok, "!| %var%"); + if (!isBoolVar && !Token::Match(condTok, "==|!=")) + continue; + if ((isBoolVar || condTok->str() == "!=") && Token::simpleMatch(blockTok->link(), "} else {")) + continue; + if (!blockTok->next()) + continue; + const Token *assignTok = blockTok->next()->astTop(); + if (!Token::simpleMatch(assignTok, "=")) + continue; + if (nextAfterAstRightmostLeaf(assignTok) != blockTok->link()->previous()) + continue; + bool isRedundant = false; + if (isBoolVar) { + const bool isNegation = condTok->str() == "!"; + const Token* const varTok = isNegation ? condTok->next() : condTok; + const ValueType* vt = varTok->variable() ? varTok->variable()->valueType() : nullptr; + if (!(vt && vt->type == ValueType::Type::BOOL && !vt->pointer)) + continue; + + if (!(assignTok->astOperand1() && assignTok->astOperand1()->varId() == varTok->varId())) + continue; + if (!(assignTok->astOperand2() && assignTok->astOperand2()->hasKnownIntValue())) + continue; + const MathLib::bigint val = assignTok->astOperand2()->getKnownIntValue(); + if (val < 0 || val > 1) + continue; + isRedundant = (isNegation && val == 0) || (!isNegation && val == 1); + } else { // comparison + if (!isSameExpression( + true, condTok->astOperand1(), assignTok->astOperand1(), mSettings->library, true, true)) + continue; + if (!isSameExpression( + true, condTok->astOperand2(), assignTok->astOperand2(), mSettings->library, true, true)) + continue; + } + duplicateConditionalAssignError(condTok, assignTok, isRedundant); + } + } +} + +void CheckCondition::duplicateConditionalAssignError(const Token *condTok, const Token* assignTok, bool isRedundant) +{ + ErrorPath errors; + std::string msg = "Duplicate expression for the condition and assignment."; + if (condTok && assignTok) { + if (condTok->str() == "==") { + msg = "Assignment '" + assignTok->expressionString() + "' is redundant with condition '" + condTok->expressionString() + "'."; + errors.emplace_back(condTok, "Condition '" + condTok->expressionString() + "'"); + errors.emplace_back(assignTok, "Assignment '" + assignTok->expressionString() + "' is redundant"); + } else { + msg = "The statement 'if (" + condTok->expressionString() + ") " + assignTok->expressionString(); + msg += isRedundant ? "' is redundant." : "' is logically equivalent to '" + assignTok->expressionString() + "'."; + errors.emplace_back(assignTok, "Assignment '" + assignTok->expressionString() + "'"); + errors.emplace_back(condTok, "Condition '" + condTok->expressionString() + "' is redundant"); + } + } + + reportError( + errors, Severity::style, "duplicateConditionalAssign", msg, CWE398, Certainty::normal); +} + + +void CheckCondition::checkAssignmentInCondition() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("assignmentInCondition")) + return; + + logChecker("CheckCondition::checkAssignmentInCondition"); // style + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (tok->str() != "=") + continue; + if (!tok->astParent()) + continue; + + // Is this assignment of container/iterator? + if (!tok->valueType()) + continue; + if (tok->valueType()->pointer > 0) + continue; + if (tok->valueType()->type != ValueType::Type::CONTAINER && tok->valueType()->type != ValueType::Type::ITERATOR) + continue; + + // warn if this is a conditional expression.. + if (Token::Match(tok->astParent()->previous(), "if|while (")) + assignmentInCondition(tok); + else if (Token::Match(tok->astParent(), "%oror%|&&")) + assignmentInCondition(tok); + else if (Token::simpleMatch(tok->astParent(), "?") && tok == tok->astParent()->astOperand1()) + assignmentInCondition(tok); + } + } +} + +void CheckCondition::assignmentInCondition(const Token *eq) +{ + std::string expr = eq ? eq->expressionString() : "x=y"; + + reportError( + eq, + Severity::style, + "assignmentInCondition", + "Suspicious assignment in condition. Condition '" + expr + "' is always true.", + CWE571, + Certainty::normal); +} + +void CheckCondition::checkCompareValueOutOfTypeRange() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("compareValueOutOfTypeRange")) + return; + + if (mSettings->platform.type == Platform::Type::Native || + mSettings->platform.type == Platform::Type::Unspecified) + return; + + logChecker("CheckCondition::checkCompareValueOutOfTypeRange"); // style,platform + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (!tok->isComparisonOp() || !tok->isBinaryOp()) + continue; + + for (int i = 0; i < 2; ++i) { + const Token * const valueTok = (i == 0) ? tok->astOperand1() : tok->astOperand2(); + const Token * const typeTok = valueTok->astSibling(); + if (!valueTok->hasKnownIntValue() || !typeTok->valueType() || typeTok->valueType()->pointer) + continue; + if (valueTok->getKnownIntValue() < 0 && valueTok->valueType() && valueTok->valueType()->sign != ValueType::Sign::SIGNED) + continue; + if (valueTok->valueType() && valueTok->valueType()->isTypeEqual(typeTok->valueType())) + continue; + int bits = 0; + switch (typeTok->valueType()->type) { + case ValueType::Type::BOOL: + bits = 1; + break; + case ValueType::Type::CHAR: + bits = mSettings->platform.char_bit; + break; + case ValueType::Type::SHORT: + bits = mSettings->platform.short_bit; + break; + case ValueType::Type::INT: + bits = mSettings->platform.int_bit; + break; + case ValueType::Type::LONG: + bits = mSettings->platform.long_bit; + break; + case ValueType::Type::LONGLONG: + bits = mSettings->platform.long_long_bit; + break; + default: + break; + } + if (bits == 0 || bits >= 64) + continue; + + const auto typeMinValue = (typeTok->valueType()->sign == ValueType::Sign::UNSIGNED) ? 0 : (-(1LL << (bits-1))); + const auto unsignedTypeMaxValue = (1LL << bits) - 1LL; + long long typeMaxValue; + if (typeTok->valueType()->sign != ValueType::Sign::SIGNED) + typeMaxValue = unsignedTypeMaxValue; + else if (bits >= mSettings->platform.int_bit && (!valueTok->valueType() || valueTok->valueType()->sign != ValueType::Sign::SIGNED)) + typeMaxValue = unsignedTypeMaxValue; + else + typeMaxValue = unsignedTypeMaxValue / 2; + + bool result{}; + const auto kiv = valueTok->getKnownIntValue(); + if (tok->str() == "==") + result = false; + else if (tok->str() == "!=") + result = true; + else if (tok->str()[0] == '>' && i == 0) + // num > var + result = (kiv > 0); + else if (tok->str()[0] == '>' && i == 1) + // var > num + result = (kiv < 0); + else if (tok->str()[0] == '<' && i == 0) + // num < var + result = (kiv < 0); + else if (tok->str()[0] == '<' && i == 1) + // var < num + result = (kiv > 0); + + bool error = false; + if (kiv < typeMinValue || kiv > typeMaxValue) { + error = true; + } else { + switch (i) { + case 0: // num cmp var + if (kiv == typeMinValue) { + if (tok->str() == "<=") { + result = true; + error = true; + } else if (tok->str() == ">") + error = true; + } + else if (kiv == typeMaxValue && (tok->str() == ">=" || tok->str() == "<")) { + error = true; + } + break; + case 1: // var cmp num + if (kiv == typeMinValue) { + if (tok->str() == ">=") { + result = true; + error = true; + } else if (tok->str() == "<") + error = true; + } + else if (kiv == typeMaxValue && (tok->str() == "<=" || tok->str() == ">")) { + error = true; + } + break; + } + } + if (error) + compareValueOutOfTypeRangeError(valueTok, typeTok->valueType()->str(), kiv, result); + } + } + } +} + +void CheckCondition::compareValueOutOfTypeRangeError(const Token *comparison, const std::string &type, long long value, bool result) +{ + reportError( + comparison, + Severity::style, + "compareValueOutOfTypeRangeError", + "Comparing expression of type '" + type + "' against value " + std::to_string(value) + ". Condition is always " + bool_to_string(result) + ".", + CWE398, + Certainty::normal); +} diff --git a/cppcheck-2.14.0/lib/checkcondition.h b/cppcheck-2.14.0/lib/checkcondition.h new file mode 100644 index 00000000..c1edd6e9 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkcondition.h @@ -0,0 +1,223 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checkconditionH +#define checkconditionH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "mathlib.h" +#include "errortypes.h" +#include "tokenize.h" + +#include +#include + +class Settings; +class Token; +class ErrorLogger; +class ValueType; + +namespace ValueFlow { + class Value; +} + +/// @addtogroup Checks +/// @{ + +/** + * @brief Check for condition mismatches + */ + +class CPPCHECKLIB CheckCondition : public Check { +public: + /** This constructor is used when registering the CheckAssignIf */ + CheckCondition() : Check(myName()) {} + +private: + /** This constructor is used when running checks. */ + CheckCondition(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger) {} + + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + CheckCondition checkCondition(&tokenizer, &tokenizer.getSettings(), errorLogger); + checkCondition.multiCondition(); + checkCondition.clarifyCondition(); // not simplified because ifAssign + checkCondition.multiCondition2(); + checkCondition.checkIncorrectLogicOperator(); + checkCondition.checkInvalidTestForOverflow(); + checkCondition.duplicateCondition(); + checkCondition.checkPointerAdditionResultNotNull(); + checkCondition.checkDuplicateConditionalAssign(); + checkCondition.assignIf(); + checkCondition.checkBadBitmaskCheck(); + checkCondition.comparison(); + checkCondition.checkModuloAlwaysTrueFalse(); + checkCondition.checkAssignmentInCondition(); + checkCondition.checkCompareValueOutOfTypeRange(); + checkCondition.alwaysTrueFalse(); + } + + /** mismatching assignment / comparison */ + void assignIf(); + + /** parse scopes recursively */ + bool assignIfParseScope(const Token * const assignTok, + const Token * const startTok, + const nonneg int varid, + const bool islocal, + const char bitop, + const MathLib::bigint num); + + /** check bitmask using | instead of & */ + void checkBadBitmaskCheck(); + + /** mismatching lhs and rhs in comparison */ + void comparison(); + + void duplicateCondition(); + + /** match 'if' and 'else if' conditions */ + void multiCondition(); + + /** + * multiconditions #2 + * - Opposite inner conditions => always false + * - (TODO) Same/Overlapping inner condition => always true + * - same condition after early exit => always false + **/ + void multiCondition2(); + + /** @brief %Check for testing for mutual exclusion over ||*/ + void checkIncorrectLogicOperator(); + + /** @brief %Check for suspicious usage of modulo (e.g. "if(var % 4 == 4)") */ + void checkModuloAlwaysTrueFalse(); + + /** @brief Suspicious condition (assignment+comparison) */ + void clarifyCondition(); + + /** @brief Condition is always true/false */ + void alwaysTrueFalse(); + + /** @brief %Check for invalid test for overflow 'x+100 < x' */ + void checkInvalidTestForOverflow(); + + /** @brief Check if pointer addition result is NULL '(ptr + 1) == NULL' */ + void checkPointerAdditionResultNotNull(); + + void checkDuplicateConditionalAssign(); + + /** @brief Assignment in condition */ + void checkAssignmentInCondition(); + + // The conditions that have been diagnosed + std::set mCondDiags; + bool diag(const Token* tok, bool insert=true); + bool isAliased(const std::set &vars) const; + bool isOverlappingCond(const Token * const cond1, const Token * const cond2, bool pure) const; + void assignIfError(const Token *tok1, const Token *tok2, const std::string &condition, bool result); + void mismatchingBitAndError(const Token *tok1, const MathLib::bigint num1, const Token *tok2, const MathLib::bigint num2); + void badBitmaskCheckError(const Token *tok, bool isNoOp = false); + void comparisonError(const Token *tok, + const std::string &bitop, + MathLib::bigint value1, + const std::string &op, + MathLib::bigint value2, + bool result); + void duplicateConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath); + void overlappingElseIfConditionError(const Token *tok, nonneg int line1); + void oppositeElseIfConditionError(const Token *ifCond, const Token *elseIfCond, ErrorPath errorPath); + + void oppositeInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath); + + void identicalInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath); + + void identicalConditionAfterEarlyExitError(const Token *cond1, const Token *cond2, ErrorPath errorPath); + + void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive, ErrorPath errors); + void redundantConditionError(const Token *tok, const std::string &text, bool inconclusive); + + void moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal); + + void clarifyConditionError(const Token *tok, bool assign, bool boolop); + + void alwaysTrueFalseError(const Token* tok, const Token* condition, const ValueFlow::Value* value); + + void invalidTestForOverflow(const Token* tok, const ValueType *valueType, const std::string &replace); + void pointerAdditionResultNotNullError(const Token *tok, const Token *calc); + + void duplicateConditionalAssignError(const Token *condTok, const Token* assignTok, bool isRedundant = false); + + void assignmentInCondition(const Token *eq); + + void checkCompareValueOutOfTypeRange(); + void compareValueOutOfTypeRangeError(const Token *comparison, const std::string &type, long long value, bool result); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckCondition c(nullptr, settings, errorLogger); + + c.assignIfError(nullptr, nullptr, emptyString, false); + c.badBitmaskCheckError(nullptr); + c.comparisonError(nullptr, "&", 6, "==", 1, false); + c.duplicateConditionError(nullptr, nullptr, ErrorPath{}); + c.overlappingElseIfConditionError(nullptr, 1); + c.mismatchingBitAndError(nullptr, 0xf0, nullptr, 1); + c.oppositeInnerConditionError(nullptr, nullptr, ErrorPath{}); + c.identicalInnerConditionError(nullptr, nullptr, ErrorPath{}); + c.identicalConditionAfterEarlyExitError(nullptr, nullptr, ErrorPath{}); + c.incorrectLogicOperatorError(nullptr, "foo > 3 && foo < 4", true, false, ErrorPath{}); + c.redundantConditionError(nullptr, "If x > 11 the condition x > 10 is always true.", false); + c.moduloAlwaysTrueFalseError(nullptr, "1"); + c.clarifyConditionError(nullptr, true, false); + c.alwaysTrueFalseError(nullptr, nullptr, nullptr); + c.invalidTestForOverflow(nullptr, nullptr, "false"); + c.pointerAdditionResultNotNullError(nullptr, nullptr); + c.duplicateConditionalAssignError(nullptr, nullptr); + c.assignmentInCondition(nullptr); + c.compareValueOutOfTypeRangeError(nullptr, "unsigned char", 256, true); + } + + static std::string myName() { + return "Condition"; + } + + std::string classInfo() const override { + return "Match conditions with assignments and other conditions:\n" + "- Mismatching assignment and comparison => comparison is always true/false\n" + "- Mismatching lhs and rhs in comparison => comparison is always true/false\n" + "- Detect usage of | where & should be used\n" + "- Duplicate condition and assignment\n" + "- Detect matching 'if' and 'else if' conditions\n" + "- Mismatching bitand (a &= 0xf0; a &= 1; => a = 0)\n" + "- Opposite inner condition is always false\n" + "- Identical condition after early exit is always false\n" + "- Condition that is always true/false\n" + "- Mutual exclusion over || always evaluating to true\n" + "- Comparisons of modulo results that are always true/false.\n" + "- Known variable values => condition is always true/false\n" + "- Invalid test for overflow. Some mainstream compilers remove such overflow tests when optimising code.\n" + "- Suspicious assignment of container/iterator in condition => condition is always true.\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkconditionH diff --git a/cppcheck-2.14.0/lib/checkers.cpp b/cppcheck-2.14.0/lib/checkers.cpp new file mode 100644 index 00000000..d6f6be3b --- /dev/null +++ b/cppcheck-2.14.0/lib/checkers.cpp @@ -0,0 +1,983 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +// This file is auto-generated by tools/get_checkers.py: +// python3 tools/get_checkers.py > lib/checkers.cpp + +#include "checkers.h" + +namespace checkers { + const std::map allCheckers{ + {"CheckBool::checkIncrementBoolean","style"}, + {"CheckBool::checkBitwiseOnBoolean","style,inconclusive"}, + {"CheckBool::checkComparisonOfBoolWithInt","warning,c++"}, + {"CheckBool::checkComparisonOfFuncReturningBool","style,c++"}, + {"CheckBool::checkComparisonOfBoolWithBool","style,c++"}, + {"CheckBool::checkAssignBoolToPointer",""}, + {"CheckBool::checkComparisonOfBoolExpressionWithInt","warning"}, + {"CheckBool::pointerArithBool",""}, + {"CheckBool::checkAssignBoolToFloat","style,c++"}, + {"CheckBool::returnValueOfFunctionReturningBool","style"}, + {"CheckPostfixOperator::postfixOperator","performance"}, + {"CheckSizeof::checkSizeofForNumericParameter","warning"}, + {"CheckSizeof::checkSizeofForArrayParameter","warning"}, + {"CheckSizeof::checkSizeofForPointerSize","warning"}, + {"CheckSizeof::sizeofsizeof","warning"}, + {"CheckSizeof::sizeofCalculation","warning"}, + {"CheckSizeof::sizeofFunction","warning"}, + {"CheckSizeof::suspiciousSizeofCalculation","warning,inconclusive"}, + {"CheckSizeof::sizeofVoid","portability"}, + {"Check64BitPortability::pointerassignment","portability"}, + {"CheckStl::outOfBounds",""}, + {"CheckStl::outOfBoundsIndexExpression",""}, + {"CheckStl::iterators",""}, + {"CheckStl::misMatchingContainers",""}, + {"CheckStl::misMatchingContainerIterator",""}, + {"CheckStl::invalidContainer",""}, + {"CheckStl::stlOutOfBounds",""}, + {"CheckStl::negativeIndex",""}, + {"CheckStl::erase",""}, + {"CheckStl::stlBoundaries",""}, + {"CheckStl::if_find","warning,performance"}, + {"CheckStl::checkFindInsert","performance"}, + {"CheckStl::size","performance,c++03"}, + {"CheckStl::redundantCondition","style"}, + {"CheckStl::missingComparison","warning"}, + {"CheckStl::string_c_str",""}, + {"CheckStl::uselessCalls","performance,warning"}, + {"CheckStl::checkDereferenceInvalidIterator","warning"}, + {"CheckStl::checkDereferenceInvalidIterator2",""}, + {"CheckStl::useStlAlgorithm","style"}, + {"CheckStl::knownEmptyContainer","style"}, + {"CheckStl::eraseIteratorOutOfBounds",""}, + {"CheckStl::checkMutexes","warning"}, + {"CheckBoost::checkBoostForeachModification",""}, + {"CheckNullPointer::nullPointer",""}, + {"CheckNullPointer::nullConstantDereference",""}, + {"CheckNullPointer::arithmetic",""}, + {"CheckNullPointer::analyseWholeProgram","unusedfunctions"}, + {"CheckBufferOverrun::arrayIndex",""}, + {"CheckBufferOverrun::pointerArithmetic","portability"}, + {"CheckBufferOverrun::bufferOverflow",""}, + {"CheckBufferOverrun::arrayIndexThenCheck",""}, + {"CheckBufferOverrun::stringNotZeroTerminated","warning,inconclusive"}, + {"CheckBufferOverrun::argumentSize","warning"}, + {"CheckBufferOverrun::analyseWholeProgram",""}, + {"CheckBufferOverrun::objectIndex",""}, + {"CheckBufferOverrun::negativeArraySize",""}, + {"CheckUninitVar::check",""}, + {"CheckUninitVar::valueFlowUninit",""}, + {"CheckOther::checkCastIntToCharAndBack","warning"}, + {"CheckOther::clarifyCalculation","style"}, + {"CheckOther::clarifyStatement","warning"}, + {"CheckOther::checkSuspiciousSemicolon","warning,inconclusive"}, + {"CheckOther::warningOldStylePointerCast","style,c++"}, + {"CheckOther::invalidPointerCast","portability"}, + {"CheckOther::checkRedundantAssignment","style"}, + {"CheckOther::redundantBitwiseOperationInSwitch","warning"}, + {"CheckOther::checkSuspiciousCaseInSwitch","warning,inconclusive"}, + {"CheckOther::checkUnreachableCode","style"}, + {"CheckOther::checkVariableScope","style,notclang"}, + {"CheckOther::checkPassByReference","performance,c++"}, + {"CheckOther::checkConstPointer","style"}, + {"CheckOther::checkCharVariable","warning,portability"}, + {"CheckOther::checkIncompleteStatement","warning"}, + {"CheckOther::checkZeroDivision",""}, + {"CheckOther::checkNanInArithmeticExpression","style"}, + {"CheckOther::checkMisusedScopedObject","style,c++"}, + {"CheckOther::checkDuplicateBranch","style,inconclusive"}, + {"CheckOther::checkInvalidFree",""}, + {"CheckOther::checkDuplicateExpression","style,warning"}, + {"CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalse","warning"}, + {"CheckOther::checkSignOfUnsignedVariable","style"}, + {"CheckOther::checkRedundantCopy","c++,performance,inconclusive"}, + {"CheckOther::checkNegativeBitwiseShift",""}, + {"CheckOther::checkIncompleteArrayFill","warning,portability,inconclusive"}, + {"CheckOther::checkVarFuncNullUB","portability"}, + {"CheckOther::checkRedundantPointerOp","style"}, + {"CheckOther::checkInterlockedDecrement","windows-platform"}, + {"CheckOther::checkUnusedLabel","style,warning"}, + {"CheckOther::checkEvaluationOrder","C/C++03"}, + {"CheckOther::checkAccessOfMovedVariable","c++11,warning"}, + {"CheckOther::checkFuncArgNamesDifferent","style,warning,inconclusive"}, + {"CheckOther::checkShadowVariables","style"}, + {"CheckOther::checkKnownArgument","style"}, + {"CheckOther::checkKnownPointerToBool","style"}, + {"CheckOther::checkComparePointers",""}, + {"CheckOther::checkModuloOfOne","style"}, + {"CheckOther::checkOverlappingWrite",""}, + {"CheckClass::checkConstructors","style,warning"}, + {"CheckClass::checkExplicitConstructors","style"}, + {"CheckClass::checkCopyConstructors","warning"}, + {"CheckClass::initializationListUsage","performance"}, + {"CheckClass::privateFunctions","style"}, + {"CheckClass::checkMemset",""}, + {"CheckClass::operatorEqRetRefThis","style"}, + {"CheckClass::operatorEqToSelf","warning"}, + {"CheckClass::virtualDestructor",""}, + {"CheckClass::thisSubtraction","warning"}, + {"CheckClass::checkConst","style,inconclusive"}, + {"CheckClass::initializerListOrder","style,inconclusive"}, + {"CheckClass::checkSelfInitialization",""}, + {"CheckClass::checkVirtualFunctionCallInConstructor","warning"}, + {"CheckClass::checkDuplInheritedMembers","warning"}, + {"CheckClass::checkMissingOverride","style,c++03"}, + {"CheckClass::checkUselessOverride","style"}, + {"CheckClass::checkReturnByReference","performance"}, + {"CheckClass::checkThisUseAfterFree","warning"}, + {"CheckClass::checkUnsafeClassRefMember","warning,safeChecks"}, + {"CheckClass::analyseWholeProgram",""}, + {"CheckUnusedVar::checkFunctionVariableUsage","style"}, + {"CheckUnusedVar::checkStructMemberUsage","style"}, + {"CheckIO::checkCoutCerrMisusage","c"}, + {"CheckIO::checkFileUsage",""}, + {"CheckIO::checkWrongPrintfScanfArguments",""}, + {"CheckCondition::assignIf","style"}, + {"CheckCondition::checkBadBitmaskCheck","style"}, + {"CheckCondition::comparison","style"}, + {"CheckCondition::duplicateCondition","style"}, + {"CheckCondition::multiCondition","style"}, + {"CheckCondition::multiCondition2","warning"}, + {"CheckCondition::checkIncorrectLogicOperator","style,warning"}, + {"CheckCondition::checkModuloAlwaysTrueFalse","warning"}, + {"CheckCondition::clarifyCondition","style"}, + {"CheckCondition::alwaysTrueFalse","style"}, + {"CheckCondition::checkInvalidTestForOverflow","warning"}, + {"CheckCondition::checkPointerAdditionResultNotNull","warning"}, + {"CheckCondition::checkDuplicateConditionalAssign","style"}, + {"CheckCondition::checkAssignmentInCondition","style"}, + {"CheckCondition::checkCompareValueOutOfTypeRange","style,platform"}, + {"CheckFunctions::checkProhibitedFunctions",""}, + {"CheckFunctions::invalidFunctionUsage",""}, + {"CheckFunctions::checkIgnoredReturnValue","style,warning"}, + {"CheckFunctions::checkMissingReturn",""}, + {"CheckFunctions::checkMathFunctions","style,warning,c99,c++11"}, + {"CheckFunctions::memsetZeroBytes","warning"}, + {"CheckFunctions::memsetInvalid2ndParam","warning,portability"}, + {"CheckFunctions::returnLocalStdMove","performance,c++11"}, + {"CheckFunctions::useStandardLibrary","style"}, + {"CheckVaarg::va_start_argument",""}, + {"CheckVaarg::va_list_usage","notclang"}, + {"CheckUnusedFunctions::check","unusedFunction"}, + {"CheckType::checkTooBigBitwiseShift","platform"}, + {"CheckType::checkIntegerOverflow","platform"}, + {"CheckType::checkSignConversion","warning"}, + {"CheckType::checkLongCast","style"}, + {"CheckType::checkFloatToIntegerOverflow",""}, + {"CheckString::stringLiteralWrite",""}, + {"CheckString::checkAlwaysTrueOrFalseStringCompare","warning"}, + {"CheckString::checkSuspiciousStringCompare","warning"}, + {"CheckString::strPlusChar",""}, + {"CheckString::checkIncorrectStringCompare","warning"}, + {"CheckString::overlappingStrcmp","warning"}, + {"CheckString::sprintfOverlappingData",""}, + {"CheckAssert::assertWithSideEffects","warning"}, + {"CheckExceptionSafety::destructors","warning"}, + {"CheckExceptionSafety::deallocThrow","warning"}, + {"CheckExceptionSafety::checkRethrowCopy","style"}, + {"CheckExceptionSafety::checkCatchExceptionByValue","style"}, + {"CheckExceptionSafety::nothrowThrows",""}, + {"CheckExceptionSafety::unhandledExceptionSpecification","style,inconclusive"}, + {"CheckExceptionSafety::rethrowNoCurrentException",""}, + {"CheckAutoVariables::assignFunctionArg","style,warning"}, + {"CheckAutoVariables::autoVariables",""}, + {"CheckAutoVariables::checkVarLifetime",""}, + {"CheckLeakAutoVar::check","notclang"}, + {"CheckMemoryLeakInFunction::checkReallocUsage",""}, + {"CheckMemoryLeakInClass::check",""}, + {"CheckMemoryLeakStructMember::check",""}, + {"CheckMemoryLeakNoVar::check",""}, + }; + + const std::map premiumCheckers{ + {"Autosar: A0-1-3",""}, + {"Autosar: A0-1-6",""}, + {"Autosar: A0-4-2",""}, + {"Autosar: A0-4-4",""}, + {"Autosar: A10-1-1",""}, + {"Autosar: A11-0-2",""}, + {"Autosar: A11-3-1",""}, + {"Autosar: A13-2-1",""}, + {"Autosar: A13-2-3",""}, + {"Autosar: A13-5-2",""}, + {"Autosar: A13-5-5",""}, + {"Autosar: A15-1-2",""}, + {"Autosar: A15-3-5",""}, + {"Autosar: A16-6-1",""}, + {"Autosar: A16-7-1",""}, + {"Autosar: A18-0-3",""}, + {"Autosar: A18-1-1",""}, + {"Autosar: A18-1-2",""}, + {"Autosar: A18-1-3",""}, + {"Autosar: A18-5-1",""}, + {"Autosar: A18-9-1",""}, + {"Autosar: A2-11-1",""}, + {"Autosar: A2-13-1",""}, + {"Autosar: A2-13-3",""}, + {"Autosar: A2-13-5",""}, + {"Autosar: A2-13-6",""}, + {"Autosar: A2-5-2",""}, + {"Autosar: A20-8-2","warning"}, + {"Autosar: A20-8-3","warning"}, + {"Autosar: A20-8-4",""}, + {"Autosar: A20-8-5",""}, + {"Autosar: A20-8-6",""}, + {"Autosar: A21-8-1","warning"}, + {"Autosar: A23-0-1",""}, + {"Autosar: A25-1-1","warning"}, + {"Autosar: A25-4-1","warning"}, + {"Autosar: A26-5-1",""}, + {"Autosar: A26-5-2",""}, + {"Autosar: A27-0-1","warning"}, + {"Autosar: A27-0-2",""}, + {"Autosar: A27-0-4",""}, + {"Autosar: A3-1-3",""}, + {"Autosar: A3-1-4",""}, + {"Autosar: A3-3-1",""}, + {"Autosar: A4-10-1",""}, + {"Autosar: A4-7-1",""}, + {"Autosar: A5-0-2",""}, + {"Autosar: A5-0-3",""}, + {"Autosar: A5-0-4",""}, + {"Autosar: A5-1-1",""}, + {"Autosar: A5-1-2",""}, + {"Autosar: A5-1-3",""}, + {"Autosar: A5-1-6",""}, + {"Autosar: A5-1-7",""}, + {"Autosar: A5-16-1",""}, + {"Autosar: A5-2-1",""}, + {"Autosar: A5-2-4",""}, + {"Autosar: A6-5-3",""}, + {"Autosar: A7-1-4",""}, + {"Autosar: A7-1-6",""}, + {"Autosar: A7-1-7",""}, + {"Autosar: A8-4-1",""}, + {"Autosar: A8-5-3",""}, + {"Autosar: A9-3-1",""}, + {"Cert C++: CON51-CPP",""}, + {"Cert C++: CON52-CPP",""}, + {"Cert C++: CON53-CPP",""}, + {"Cert C++: CON54-CPP",""}, + {"Cert C++: CON55-CPP",""}, + {"Cert C++: CON56-CPP",""}, + {"Cert C++: CTR50-CPP",""}, + {"Cert C++: CTR52-CPP",""}, + {"Cert C++: CTR53-CPP",""}, + {"Cert C++: CTR56-CPP",""}, + {"Cert C++: CTR57-CPP","warning"}, + {"Cert C++: CTR58-CPP","warning"}, + {"Cert C++: DCL50-CPP",""}, + {"Cert C++: DCL51-CPP",""}, + {"Cert C++: DCL52-CPP",""}, + {"Cert C++: DCL53-CPP",""}, + {"Cert C++: DCL54-CPP",""}, + {"Cert C++: DCL56-CPP",""}, + {"Cert C++: DCL58-CPP",""}, + {"Cert C++: DCL59-CPP",""}, + {"Cert C++: ERR50-CPP",""}, + {"Cert C++: ERR51-CPP",""}, + {"Cert C++: ERR52-CPP",""}, + {"Cert C++: ERR53-CPP",""}, + {"Cert C++: ERR54-CPP",""}, + {"Cert C++: ERR55-CPP",""}, + {"Cert C++: ERR56-CPP",""}, + {"Cert C++: ERR58-CPP",""}, + {"Cert C++: ERR59-CPP","warning"}, + {"Cert C++: ERR60-CPP","warning"}, + {"Cert C++: ERR61-CPP",""}, + {"Cert C++: ERR62-CPP",""}, + {"Cert C++: EXP50-CPP",""}, + {"Cert C++: EXP51-CPP",""}, + {"Cert C++: EXP55-CPP",""}, + {"Cert C++: EXP56-CPP",""}, + {"Cert C++: EXP57-CPP",""}, + {"Cert C++: EXP58-CPP",""}, + {"Cert C++: EXP59-CPP",""}, + {"Cert C++: FIO51-CPP",""}, + {"Cert C++: INT50-CPP",""}, + {"Cert C++: MEM52-CPP",""}, + {"Cert C++: MEM53-CPP",""}, + {"Cert C++: MEM54-CPP",""}, + {"Cert C++: MEM55-CPP",""}, + {"Cert C++: MEM57-CPP",""}, + {"Cert C++: MSC50-CPP",""}, + {"Cert C++: MSC51-CPP",""}, + {"Cert C++: MSC53-CPP",""}, + {"Cert C++: MSC54-CPP",""}, + {"Cert C++: OOP51-CPP",""}, + {"Cert C++: OOP55-CPP",""}, + {"Cert C++: OOP56-CPP",""}, + {"Cert C++: OOP57-CPP",""}, + {"Cert C++: OOP58-CPP",""}, + {"Cert C++: STR50-CPP",""}, + {"Cert C++: STR53-CPP",""}, + {"Cert C: ARR30-C",""}, + {"Cert C: ARR32-C",""}, + {"Cert C: ARR37-C",""}, + {"Cert C: ARR38-C",""}, + {"Cert C: ARR39-C",""}, + {"Cert C: CON30-C",""}, + {"Cert C: CON31-C",""}, + {"Cert C: CON32-C",""}, + {"Cert C: CON33-C",""}, + {"Cert C: CON34-C",""}, + {"Cert C: CON35-C",""}, + {"Cert C: CON36-C",""}, + {"Cert C: CON37-C",""}, + {"Cert C: CON38-C",""}, + {"Cert C: CON39-C",""}, + {"Cert C: CON40-C",""}, + {"Cert C: CON41-C",""}, + {"Cert C: DCL31-C",""}, + {"Cert C: DCL36-C",""}, + {"Cert C: DCL37-C",""}, + {"Cert C: DCL38-C",""}, + {"Cert C: DCL39-C",""}, + {"Cert C: DCL40-C",""}, + {"Cert C: DCL41-C",""}, + {"Cert C: ENV30-C",""}, + {"Cert C: ENV31-C",""}, + {"Cert C: ENV32-C",""}, + {"Cert C: ENV33-C",""}, + {"Cert C: ENV34-C",""}, + {"Cert C: ERR30-C",""}, + {"Cert C: ERR32-C",""}, + {"Cert C: ERR33-C",""}, + {"Cert C: EXP32-C",""}, + {"Cert C: EXP35-C",""}, + {"Cert C: EXP36-C",""}, + {"Cert C: EXP37-C",""}, + {"Cert C: EXP39-C",""}, + {"Cert C: EXP40-C",""}, + {"Cert C: EXP42-C",""}, + {"Cert C: EXP43-C",""}, + {"Cert C: EXP45-C",""}, + {"Cert C: FIO30-C",""}, + {"Cert C: FIO32-C",""}, + {"Cert C: FIO34-C",""}, + {"Cert C: FIO37-C",""}, + {"Cert C: FIO38-C",""}, + {"Cert C: FIO40-C",""}, + {"Cert C: FIO41-C",""}, + {"Cert C: FIO44-C",""}, + {"Cert C: FIO45-C",""}, + {"Cert C: FLP30-C",""}, + {"Cert C: FLP36-C","portability"}, + {"Cert C: FLP37-C",""}, + {"Cert C: INT30-C",""}, + {"Cert C: INT31-C",""}, + {"Cert C: INT32-C",""}, + {"Cert C: INT33-C",""}, + {"Cert C: INT34-C",""}, + {"Cert C: INT35-C",""}, + {"Cert C: INT36-C",""}, + {"Cert C: MEM33-C",""}, + {"Cert C: MEM35-C",""}, + {"Cert C: MEM36-C",""}, + {"Cert C: MSC30-C",""}, + {"Cert C: MSC32-C",""}, + {"Cert C: MSC33-C",""}, + {"Cert C: MSC38-C",""}, + {"Cert C: MSC39-C",""}, + {"Cert C: MSC40-C",""}, + {"Cert C: PRE31-C",""}, + {"Cert C: SIG30-C",""}, + {"Cert C: SIG31-C",""}, + {"Cert C: SIG34-C",""}, + {"Cert C: SIG35-C",""}, + {"Cert C: STR31-C",""}, + {"Cert C: STR32-C",""}, + {"Cert C: STR34-C",""}, + {"Cert C: STR38-C",""}, + {"Misra C++ 2008: M0-1-11",""}, + {"Misra C++ 2008: M0-1-12",""}, + {"Misra C++ 2008: M0-1-4",""}, + {"Misra C++ 2008: M0-1-5",""}, + {"Misra C++ 2008: M0-1-7",""}, + {"Misra C++ 2008: M0-1-8",""}, + {"Misra C++ 2008: M0-3-2",""}, + {"Misra C++ 2008: M1-0-1","portability"}, + {"Misra C++ 2008: M10-1-1",""}, + {"Misra C++ 2008: M10-1-2",""}, + {"Misra C++ 2008: M10-1-3",""}, + {"Misra C++ 2008: M10-2-1",""}, + {"Misra C++ 2008: M10-3-1",""}, + {"Misra C++ 2008: M10-3-2",""}, + {"Misra C++ 2008: M10-3-3",""}, + {"Misra C++ 2008: M11-0-1",""}, + {"Misra C++ 2008: M12-1-2",""}, + {"Misra C++ 2008: M12-8-1",""}, + {"Misra C++ 2008: M12-8-2",""}, + {"Misra C++ 2008: M14-5-1","warning"}, + {"Misra C++ 2008: M14-5-2","warning"}, + {"Misra C++ 2008: M14-5-3","warning"}, + {"Misra C++ 2008: M14-6-1","warning"}, + {"Misra C++ 2008: M14-6-2","warning"}, + {"Misra C++ 2008: M14-7-1",""}, + {"Misra C++ 2008: M14-7-2",""}, + {"Misra C++ 2008: M14-7-3",""}, + {"Misra C++ 2008: M14-8-1",""}, + {"Misra C++ 2008: M14-8-2",""}, + {"Misra C++ 2008: M15-0-3",""}, + {"Misra C++ 2008: M15-1-1",""}, + {"Misra C++ 2008: M15-1-2",""}, + {"Misra C++ 2008: M15-1-3",""}, + {"Misra C++ 2008: M15-3-2","warning"}, + {"Misra C++ 2008: M15-3-3",""}, + {"Misra C++ 2008: M15-3-4",""}, + {"Misra C++ 2008: M15-3-6",""}, + {"Misra C++ 2008: M15-3-7",""}, + {"Misra C++ 2008: M15-4-1",""}, + {"Misra C++ 2008: M15-5-2",""}, + {"Misra C++ 2008: M16-0-1",""}, + {"Misra C++ 2008: M16-0-2",""}, + {"Misra C++ 2008: M16-0-3",""}, + {"Misra C++ 2008: M16-0-4",""}, + {"Misra C++ 2008: M16-0-6",""}, + {"Misra C++ 2008: M16-0-7",""}, + {"Misra C++ 2008: M16-0-8",""}, + {"Misra C++ 2008: M16-1-1",""}, + {"Misra C++ 2008: M16-1-2",""}, + {"Misra C++ 2008: M16-2-1",""}, + {"Misra C++ 2008: M16-2-2",""}, + {"Misra C++ 2008: M16-2-3",""}, + {"Misra C++ 2008: M16-2-4",""}, + {"Misra C++ 2008: M16-2-5",""}, + {"Misra C++ 2008: M16-2-6",""}, + {"Misra C++ 2008: M16-3-1",""}, + {"Misra C++ 2008: M16-3-2",""}, + {"Misra C++ 2008: M17-0-1",""}, + {"Misra C++ 2008: M17-0-2",""}, + {"Misra C++ 2008: M17-0-3",""}, + {"Misra C++ 2008: M17-0-5",""}, + {"Misra C++ 2008: M18-0-1",""}, + {"Misra C++ 2008: M18-0-2",""}, + {"Misra C++ 2008: M18-0-3",""}, + {"Misra C++ 2008: M18-0-4",""}, + {"Misra C++ 2008: M18-0-5",""}, + {"Misra C++ 2008: M18-2-1",""}, + {"Misra C++ 2008: M18-4-1",""}, + {"Misra C++ 2008: M18-7-1",""}, + {"Misra C++ 2008: M19-3-1",""}, + {"Misra C++ 2008: M2-10-1",""}, + {"Misra C++ 2008: M2-10-3",""}, + {"Misra C++ 2008: M2-10-4",""}, + {"Misra C++ 2008: M2-10-5",""}, + {"Misra C++ 2008: M2-10-6",""}, + {"Misra C++ 2008: M2-13-2",""}, + {"Misra C++ 2008: M2-13-3",""}, + {"Misra C++ 2008: M2-13-4",""}, + {"Misra C++ 2008: M2-13-5",""}, + {"Misra C++ 2008: M2-3-1",""}, + {"Misra C++ 2008: M2-7-1",""}, + {"Misra C++ 2008: M2-7-2",""}, + {"Misra C++ 2008: M2-7-3",""}, + {"Misra C++ 2008: M27-0-1",""}, + {"Misra C++ 2008: M3-1-1",""}, + {"Misra C++ 2008: M3-1-2",""}, + {"Misra C++ 2008: M3-1-3",""}, + {"Misra C++ 2008: M3-2-1",""}, + {"Misra C++ 2008: M3-3-1",""}, + {"Misra C++ 2008: M3-3-2",""}, + {"Misra C++ 2008: M3-9-1",""}, + {"Misra C++ 2008: M3-9-2",""}, + {"Misra C++ 2008: M3-9-3",""}, + {"Misra C++ 2008: M4-10-1",""}, + {"Misra C++ 2008: M4-10-2",""}, + {"Misra C++ 2008: M4-5-1",""}, + {"Misra C++ 2008: M4-5-2",""}, + {"Misra C++ 2008: M4-5-3",""}, + {"Misra C++ 2008: M5-0-10",""}, + {"Misra C++ 2008: M5-0-11",""}, + {"Misra C++ 2008: M5-0-12",""}, + {"Misra C++ 2008: M5-0-14",""}, + {"Misra C++ 2008: M5-0-15",""}, + {"Misra C++ 2008: M5-0-2",""}, + {"Misra C++ 2008: M5-0-20",""}, + {"Misra C++ 2008: M5-0-21",""}, + {"Misra C++ 2008: M5-0-3",""}, + {"Misra C++ 2008: M5-0-4",""}, + {"Misra C++ 2008: M5-0-5",""}, + {"Misra C++ 2008: M5-0-6",""}, + {"Misra C++ 2008: M5-0-7",""}, + {"Misra C++ 2008: M5-0-8",""}, + {"Misra C++ 2008: M5-0-9",""}, + {"Misra C++ 2008: M5-14-1",""}, + {"Misra C++ 2008: M5-17-1",""}, + {"Misra C++ 2008: M5-18-1",""}, + {"Misra C++ 2008: M5-19-1",""}, + {"Misra C++ 2008: M5-2-1",""}, + {"Misra C++ 2008: M5-2-10",""}, + {"Misra C++ 2008: M5-2-11",""}, + {"Misra C++ 2008: M5-2-12",""}, + {"Misra C++ 2008: M5-2-2",""}, + {"Misra C++ 2008: M5-2-3",""}, + {"Misra C++ 2008: M5-2-5",""}, + {"Misra C++ 2008: M5-2-6",""}, + {"Misra C++ 2008: M5-2-7",""}, + {"Misra C++ 2008: M5-2-8",""}, + {"Misra C++ 2008: M5-2-9",""}, + {"Misra C++ 2008: M5-3-1",""}, + {"Misra C++ 2008: M5-3-2",""}, + {"Misra C++ 2008: M5-3-3",""}, + {"Misra C++ 2008: M6-2-1",""}, + {"Misra C++ 2008: M6-2-2",""}, + {"Misra C++ 2008: M6-2-3",""}, + {"Misra C++ 2008: M6-3-1",""}, + {"Misra C++ 2008: M6-4-1",""}, + {"Misra C++ 2008: M6-4-2",""}, + {"Misra C++ 2008: M6-4-3",""}, + {"Misra C++ 2008: M6-4-4",""}, + {"Misra C++ 2008: M6-4-5",""}, + {"Misra C++ 2008: M6-4-6",""}, + {"Misra C++ 2008: M6-4-7",""}, + {"Misra C++ 2008: M6-4-8",""}, + {"Misra C++ 2008: M6-5-1",""}, + {"Misra C++ 2008: M6-5-2",""}, + {"Misra C++ 2008: M6-5-3",""}, + {"Misra C++ 2008: M6-5-4",""}, + {"Misra C++ 2008: M6-5-5",""}, + {"Misra C++ 2008: M6-5-6",""}, + {"Misra C++ 2008: M6-6-1",""}, + {"Misra C++ 2008: M6-6-2",""}, + {"Misra C++ 2008: M6-6-3",""}, + {"Misra C++ 2008: M6-6-4",""}, + {"Misra C++ 2008: M6-6-5",""}, + {"Misra C++ 2008: M7-2-1",""}, + {"Misra C++ 2008: M7-3-1",""}, + {"Misra C++ 2008: M7-3-2",""}, + {"Misra C++ 2008: M7-3-3",""}, + {"Misra C++ 2008: M7-3-4",""}, + {"Misra C++ 2008: M7-3-5",""}, + {"Misra C++ 2008: M7-3-6",""}, + {"Misra C++ 2008: M7-4-2",""}, + {"Misra C++ 2008: M7-4-3",""}, + {"Misra C++ 2008: M7-5-3",""}, + {"Misra C++ 2008: M8-0-1",""}, + {"Misra C++ 2008: M8-3-1",""}, + {"Misra C++ 2008: M8-4-4",""}, + {"Misra C++ 2008: M8-5-2",""}, + {"Misra C++ 2008: M8-5-3",""}, + {"Misra C++ 2008: M9-3-1",""}, + {"Misra C++ 2008: M9-5-1",""}, + {"Misra C++ 2008: M9-6-2",""}, + {"Misra C++ 2008: M9-6-3",""}, + {"Misra C++ 2008: M9-6-4",""}, + {"Misra C++ 2023: 0.2.1",""}, + {"Misra C++ 2023: 10.0.1",""}, + {"Misra C++ 2023: 10.1.2",""}, + {"Misra C++ 2023: 10.2.1",""}, + {"Misra C++ 2023: 10.2.2",""}, + {"Misra C++ 2023: 10.2.3",""}, + {"Misra C++ 2023: 10.4.1",""}, + {"Misra C++ 2023: 11.3.1",""}, + {"Misra C++ 2023: 11.6.1",""}, + {"Misra C++ 2023: 11.6.3",""}, + {"Misra C++ 2023: 12.2.1",""}, + {"Misra C++ 2023: 13.1.1",""}, + {"Misra C++ 2023: 13.3.1",""}, + {"Misra C++ 2023: 13.3.2",""}, + {"Misra C++ 2023: 13.3.3",""}, + {"Misra C++ 2023: 13.3.4",""}, + {"Misra C++ 2023: 14.1.1",""}, + {"Misra C++ 2023: 15.0.1",""}, + {"Misra C++ 2023: 15.0.2",""}, + {"Misra C++ 2023: 15.1.3",""}, + {"Misra C++ 2023: 15.1.5",""}, + {"Misra C++ 2023: 17.8.1",""}, + {"Misra C++ 2023: 18.4.1",""}, + {"Misra C++ 2023: 18.5.2",""}, + {"Misra C++ 2023: 19.3.2",""}, + {"Misra C++ 2023: 19.3.3",""}, + {"Misra C++ 2023: 21.20.1",""}, + {"Misra C++ 2023: 21.20.2",""}, + {"Misra C++ 2023: 21.6.3",""}, + {"Misra C++ 2023: 21.6.4",""}, + {"Misra C++ 2023: 21.6.5",""}, + {"Misra C++ 2023: 22.3.1",""}, + {"Misra C++ 2023: 22.4.1",""}, + {"Misra C++ 2023: 24.5.1",""}, + {"Misra C++ 2023: 24.5.2",""}, + {"Misra C++ 2023: 25.5.1",""}, + {"Misra C++ 2023: 25.5.2",""}, + {"Misra C++ 2023: 25.5.3",""}, + {"Misra C++ 2023: 28.6.1",""}, + {"Misra C++ 2023: 28.6.2",""}, + {"Misra C++ 2023: 30.0.2",""}, + {"Misra C++ 2023: 4.1.1",""}, + {"Misra C++ 2023: 4.1.2",""}, + {"Misra C++ 2023: 5.13.2",""}, + {"Misra C++ 2023: 5.13.5",""}, + {"Misra C++ 2023: 5.13.6",""}, + {"Misra C++ 2023: 5.7.3",""}, + {"Misra C++ 2023: 6.4.2",""}, + {"Misra C++ 2023: 6.4.3",""}, + {"Misra C++ 2023: 6.7.1",""}, + {"Misra C++ 2023: 6.7.2",""}, + {"Misra C++ 2023: 6.8.3",""}, + {"Misra C++ 2023: 6.8.4",""}, + {"Misra C++ 2023: 6.9.1",""}, + {"Misra C++ 2023: 6.9.2",""}, + {"Misra C++ 2023: 7.0.1",""}, + {"Misra C++ 2023: 7.0.2",""}, + {"Misra C++ 2023: 7.0.3",""}, + {"Misra C++ 2023: 7.0.5",""}, + {"Misra C++ 2023: 7.0.6",""}, + {"Misra C++ 2023: 8.1.1",""}, + {"Misra C++ 2023: 8.2.11",""}, + {"Misra C++ 2023: 8.2.2",""}, + {"Misra C++ 2023: 8.2.3",""}, + {"Misra C++ 2023: 8.2.4",""}, + {"Misra C++ 2023: 8.2.8",""}, + {"Misra C++ 2023: 8.2.9",""}, + {"Misra C++ 2023: 8.3.2",""}, + {"Misra C++ 2023: 9.2.1",""}, + {"Misra C++ 2023: 9.5.1",""}, + {"Misra C++ 2023: 9.5.2",""}, + {"Misra C++ 2023: 9.6.1",""}, + {"Misra C: 1.4",""}, + {"Misra C: 1.5",""}, + {"Misra C: 10.1",""}, + {"Misra C: 10.2",""}, + {"Misra C: 10.3",""}, + {"Misra C: 10.4",""}, + {"Misra C: 10.5",""}, + {"Misra C: 10.6",""}, + {"Misra C: 10.7",""}, + {"Misra C: 10.8",""}, + {"Misra C: 11.10",""}, + {"Misra C: 12.3",""}, + {"Misra C: 12.6",""}, + {"Misra C: 13.1",""}, + {"Misra C: 13.2",""}, + {"Misra C: 13.3",""}, + {"Misra C: 13.4",""}, + {"Misra C: 13.5",""}, + {"Misra C: 13.6",""}, + {"Misra C: 15.5",""}, + {"Misra C: 16.3",""}, + {"Misra C: 17.10",""}, + {"Misra C: 17.11",""}, + {"Misra C: 17.12",""}, + {"Misra C: 17.13",""}, + {"Misra C: 17.3",""}, + {"Misra C: 17.4",""}, + {"Misra C: 17.9",""}, + {"Misra C: 18.10",""}, + {"Misra C: 18.5",""}, + {"Misra C: 18.8",""}, + {"Misra C: 18.9",""}, + {"Misra C: 20.3",""}, + {"Misra C: 21.1",""}, + {"Misra C: 21.12",""}, + {"Misra C: 21.16",""}, + {"Misra C: 21.2",""}, + {"Misra C: 21.22",""}, + {"Misra C: 21.23",""}, + {"Misra C: 21.24",""}, + {"Misra C: 21.25","warning"}, + {"Misra C: 21.26","warning"}, + {"Misra C: 22.11",""}, + {"Misra C: 22.12",""}, + {"Misra C: 22.13",""}, + {"Misra C: 22.14",""}, + {"Misra C: 22.15",""}, + {"Misra C: 22.16","warning"}, + {"Misra C: 22.17","warning"}, + {"Misra C: 22.18","warning"}, + {"Misra C: 22.19","warning"}, + {"Misra C: 22.20",""}, + {"Misra C: 23.1",""}, + {"Misra C: 23.2",""}, + {"Misra C: 23.3",""}, + {"Misra C: 23.4",""}, + {"Misra C: 23.5",""}, + {"Misra C: 23.6",""}, + {"Misra C: 23.7",""}, + {"Misra C: 23.8",""}, + {"Misra C: 6.3",""}, + {"Misra C: 7.5",""}, + {"Misra C: 7.6",""}, + {"Misra C: 8.1",""}, + {"Misra C: 8.10",""}, + {"Misra C: 8.15",""}, + {"Misra C: 8.16",""}, + {"Misra C: 8.17",""}, + {"Misra C: 8.3",""}, + {"Misra C: 8.4",""}, + {"Misra C: 8.8",""}, + {"Misra C: 9.6",""}, + {"Misra C: 9.7",""}, + {"PremiumCheckBufferOverrun::addressOfPointerArithmetic","warning"}, + {"PremiumCheckBufferOverrun::negativeBufferSizeCheckedNonZero","warning"}, + {"PremiumCheckBufferOverrun::negativeBufferSizeCheckedNonZero","warning"}, + {"PremiumCheckHang::infiniteLoop",""}, + {"PremiumCheckHang::infiniteLoopContinue",""}, + {"PremiumCheckOther::arrayPointerComparison","style"}, + {"PremiumCheckOther::knownResult","style"}, + {"PremiumCheckOther::lossOfPrecision","style"}, + {"PremiumCheckOther::pointerCast","style"}, + {"PremiumCheckOther::reassignInLoop","style"}, + {"PremiumCheckOther::unreachableCode","style"}, + {"PremiumCheckStrictAlias::strictAliasCondition","warning"}, + {"PremiumCheckUninitVar::uninitmember",""}, + {"PremiumCheckUninitVar::uninitvar",""}, + {"PremiumCheckUnusedVar::unreadVariable","style"}, + {"PremiumCheckUnusedVar::unusedPrivateMember","style"} + }; + + const char Req[] = "Required"; + const char Adv[] = "Advisory"; + const char Man[] = "Mandatory"; + + const std::vector misraC2012Rules = + { + {1,1,Req,0}, + {1,2,Adv,0}, + {1,3,Req,0}, + {1,4,Req,2}, // amendment 2 + {1,5,Req,3}, // Amendment 3 + {2,1,Req,0}, + {2,2,Req,0}, + {2,3,Adv,0}, + {2,4,Adv,0}, + {2,5,Adv,0}, + {2,6,Adv,0}, + {2,7,Adv,0}, + {2,8,Adv,0}, + {3,1,Req,0}, + {3,2,Req,0}, + {4,1,Req,0}, + {4,2,Adv,0}, + {5,1,Req,0}, + {5,2,Req,0}, + {5,3,Req,0}, + {5,4,Req,0}, + {5,5,Req,0}, + {5,6,Req,0}, + {5,7,Req,0}, + {5,8,Req,0}, + {5,9,Adv,0}, + {6,1,Req,0}, + {6,2,Req,0}, + {6,3,Req,0}, + {7,1,Req,0}, + {7,2,Req,0}, + {7,3,Req,0}, + {7,4,Req,0}, + {7,5,Man,0}, + {7,6,Req,0}, + {8,1,Req,0}, + {8,2,Req,0}, + {8,3,Req,0}, + {8,4,Req,0}, + {8,5,Req,0}, + {8,6,Req,0}, + {8,7,Adv,0}, + {8,8,Req,0}, + {8,9,Adv,0}, + {8,10,Req,0}, + {8,11,Adv,0}, + {8,12,Req,0}, + {8,13,Adv,0}, + {8,14,Req,0}, + {8,15,Req,0}, + {8,16,Adv,0}, + {8,17,Adv,0}, + {9,1,Man,0}, + {9,2,Req,0}, + {9,3,Req,0}, + {9,4,Req,0}, + {9,5,Req,0}, + {9,6,Req,0}, + {9,7,Man,0}, + {10,1,Req,0}, + {10,2,Req,0}, + {10,3,Req,0}, + {10,4,Req,0}, + {10,5,Adv,0}, + {10,6,Req,0}, + {10,7,Req,0}, + {10,8,Req,0}, + {11,1,Req,0}, + {11,2,Req,0}, + {11,3,Req,0}, + {11,4,Adv,0}, + {11,5,Adv,0}, + {11,6,Req,0}, + {11,7,Req,0}, + {11,8,Req,0}, + {11,9,Req,0}, + {11,10,Req,0}, + {12,1,Adv,0}, + {12,2,Req,0}, + {12,3,Adv,0}, + {12,4,Adv,0}, + {12,5,Man,1}, // amendment 1 + {12,6,Req,4}, // amendment 4 + {13,1,Req,0}, + {13,2,Req,0}, + {13,3,Adv,0}, + {13,4,Adv,0}, + {13,5,Req,0}, + {13,6,Man,0}, + {14,1,Req,0}, + {14,2,Req,0}, + {14,3,Req,0}, + {14,4,Req,0}, + {15,1,Adv,0}, + {15,2,Req,0}, + {15,3,Req,0}, + {15,4,Adv,0}, + {15,5,Adv,0}, + {15,6,Req,0}, + {15,7,Req,0}, + {16,1,Req,0}, + {16,2,Req,0}, + {16,3,Req,0}, + {16,4,Req,0}, + {16,5,Req,0}, + {16,6,Req,0}, + {16,7,Req,0}, + {17,1,Req,0}, + {17,2,Req,0}, + {17,3,Man,0}, + {17,4,Man,0}, + {17,5,Adv,0}, + {17,6,Man,0}, + {17,7,Req,0}, + {17,8,Adv,0}, + {17,9,Man,0}, + {17,10,Req,0}, + {17,11,Adv,0}, + {17,12,Adv,0}, + {17,13,Req,0}, + {18,1,Req,0}, + {18,2,Req,0}, + {18,3,Req,0}, + {18,4,Adv,0}, + {18,5,Adv,0}, + {18,6,Req,0}, + {18,7,Req,0}, + {18,8,Req,0}, + {18,9,Req,0}, + {18,10,Man,0}, + {19,1,Man,0}, + {19,2,Adv,0}, + {20,1,Adv,0}, + {20,2,Req,0}, + {20,3,Req,0}, + {20,4,Req,0}, + {20,5,Adv,0}, + {20,6,Req,0}, + {20,7,Req,0}, + {20,8,Req,0}, + {20,9,Req,0}, + {20,10,Adv,0}, + {20,11,Req,0}, + {20,12,Req,0}, + {20,13,Req,0}, + {20,14,Req,0}, + {21,1,Req,0}, + {21,2,Req,0}, + {21,3,Req,0}, + {21,4,Req,0}, + {21,5,Req,0}, + {21,6,Req,0}, + {21,7,Req,0}, + {21,8,Req,0}, + {21,9,Req,0}, + {21,10,Req,0}, + {21,11,Req,0}, + {21,12,Adv,0}, + {21,13,Man,1}, // Amendment 1 + {21,14,Req,1}, // Amendment 1 + {21,15,Req,1}, // Amendment 1 + {21,16,Req,1}, // Amendment 1 + {21,17,Req,1}, // Amendment 1 + {21,18,Man,1}, // Amendment 1 + {21,19,Man,1}, // Amendment 1 + {21,20,Man,1}, // Amendment 1 + {21,21,Req,3}, // Amendment 3 + {21,22,Man,3}, // Amendment 3 + {21,23,Req,3}, // Amendment 3 + {21,24,Req,3}, // Amendment 3 + {21,25,Req,4}, // Amendment 4 + {21,26,Req,4}, // Amendment 4 + {22,1,Req,0}, + {22,2,Man,0}, + {22,3,Req,0}, + {22,4,Man,0}, + {22,5,Man,0}, + {22,6,Man,0}, + {22,7,Req,1}, // Amendment 1 + {22,8,Req,1}, // Amendment 1 + {22,9,Req,1}, // Amendment 1 + {22,10,Req,1}, // Amendment 1 + {22,11,Req,4}, // Amendment 4 + {22,12,Man,4}, // Amendment 4 + {22,13,Req,4}, // Amendment 4 + {22,14,Man,4}, // Amendment 4 + {22,15,Req,4}, // Amendment 4 + {22,16,Req,4}, // Amendment 4 + {22,17,Req,4}, // Amendment 4 + {22,18,Req,4}, // Amendment 4 + {22,19,Req,4}, // Amendment 4 + {22,20,Man,4}, // Amendment 4 + {23,1,Adv,3}, // Amendment 3 + {23,2,Req,3}, // Amendment 3 + {23,3,Adv,3}, // Amendment 3 + {23,4,Req,3}, // Amendment 3 + {23,5,Adv,3}, // Amendment 3 + {23,6,Req,3}, // Amendment 3 + {23,7,Adv,3}, // Amendment 3 + {23,8,Req,3}, // Amendment 3 + }; + + const std::map misraRuleSeverity{ + {"1.1", "error"}, //{"syntaxError", "unknownMacro"}}, + {"1.3", "error"}, //most "error" + {"2.1", "style"}, //{"alwaysFalse", "duplicateBreak"}}, + {"2.2", "style"}, //{"alwaysTrue", "redundantCondition", "redundantAssignment", "redundantAssignInSwitch", "unreadVariable"}}, + {"2.6", "style"}, //{"unusedLabel"}}, + {"2.8", "style"}, //{"unusedVariable"}}, + {"5.3", "style"}, //{"shadowVariable"}}, + {"8.3", "style"}, //{"funcArgNamesDifferent"}}, // inconclusive + {"8.13", "style"}, //{"constPointer"}}, + {"9.1", "error"}, //{"uninitvar"}}, + {"14.3", "style"}, //{"alwaysTrue", "alwaysFalse", "compareValueOutOfTypeRangeError", "knownConditionTrueFalse"}}, + {"13.2", "error"}, //{"unknownEvaluationOrder"}}, + {"13.6", "style"}, //{"sizeofCalculation"}}, + {"17.4", "error"}, //{"missingReturn"}}, + {"17.5", "warning"}, //{"argumentSize"}}, + {"18.1", "error"}, //{"pointerOutOfBounds"}}, + {"18.2", "error"}, //{"comparePointers"}}, + {"18.3", "error"}, //{"comparePointers"}}, + {"18.6", "error"}, //{"danglingLifetime"}}, + {"19.1", "error"}, //{"overlappingWriteUnion", "overlappingWriteFunction"}}, + {"20.6", "error"}, //{"preprocessorErrorDirective"}}, + {"21.13", "error"}, //{"invalidFunctionArg"}}, + {"21.17", "error"}, //{"bufferAccessOutOfBounds"}}, + {"21.18", "error"}, //{"bufferAccessOutOfBounds"}}, + {"22.1", "error"}, //{"memleak", "resourceLeak", "memleakOnRealloc", "leakReturnValNotUsed", "leakNoVarFunctionCall"}}, + {"22.2", "error"}, //{"autovarInvalidDeallocation"}}, + {"22.3", "error"}, //{"incompatibleFileOpen"}}, + {"22.4", "error"}, //{"writeReadOnlyFile"}}, + {"22.6", "error"}, //{"useClosedFile"}} + }; +} diff --git a/cppcheck-2.14.0/lib/checkers.h b/cppcheck-2.14.0/lib/checkers.h new file mode 100644 index 00000000..4297665b --- /dev/null +++ b/cppcheck-2.14.0/lib/checkers.h @@ -0,0 +1,48 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef checkersH +#define checkersH + +#include +#include +#include + +#include "config.h" + +namespace checkers { + extern CPPCHECKLIB const std::map allCheckers; + extern CPPCHECKLIB const std::map premiumCheckers; + + struct CPPCHECKLIB MisraInfo { + int a; + int b; + const char* str; + int amendment; + }; + + extern CPPCHECKLIB const char Req[]; // = "Required"; + extern CPPCHECKLIB const char Adv[]; // = "Advisory"; + extern CPPCHECKLIB const char Man[]; // = "Mandatory"; + + extern CPPCHECKLIB const std::vector misraC2012Rules; + + extern CPPCHECKLIB const std::map misraRuleSeverity; +} + +#endif diff --git a/cppcheck-2.14.0/lib/checkersreport.cpp b/cppcheck-2.14.0/lib/checkersreport.cpp new file mode 100644 index 00000000..f6b7c2e8 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkersreport.cpp @@ -0,0 +1,266 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "checkersreport.h" + +#include "checkers.h" +#include "errortypes.h" +#include "settings.h" + +#include +#include +#include +#include + +static bool isCppcheckPremium(const Settings& settings) { + return (settings.cppcheckCfgProductName.compare(0, 16, "Cppcheck Premium") == 0); +} + +static bool isMisraRuleActive(const std::set& activeCheckers, const std::string& rule) { + if (activeCheckers.count("Misra C: " + rule)) + return true; + if (rule == "1.1") + return true; // syntax error + if (rule == "1.3") + return true; // undefined behavior + if (rule == "2.1") + return activeCheckers.count("CheckCondition::alwaysTrueFalse") != 0; + if (rule == "2.6") + return activeCheckers.count("CheckOther::checkUnusedLabel") != 0; + if (rule == "2.8") + return activeCheckers.count("CheckUnusedVar::checkFunctionVariableUsage") != 0; + if (rule == "5.3") + return activeCheckers.count("CheckOther::checkShadowVariables") != 0; + if (rule == "8.13") + return activeCheckers.count("CheckOther::checkConstPointer") != 0; + if (rule == "9.1") + return true; // uninitvar + if (rule == "12.5") + return activeCheckers.count("CheckOther::checkConstPointer") != 0; + if (rule == "14.3") + return activeCheckers.count("CheckCondition::alwaysTrueFalse") != 0; + if (rule == "17.5") + return activeCheckers.count("CheckBufferOverrun::argumentSize") != 0; + if (rule == "18.1") + return activeCheckers.count("CheckBufferOverrun::pointerArithmetic") != 0; + if (rule == "18.2") + return activeCheckers.count("CheckOther::checkComparePointers") != 0; + if (rule == "18.3") + return activeCheckers.count("CheckOther::checkComparePointers") != 0; + if (rule == "18.6") + return true; // danlingLifetime => error + if (rule == "19.1") + return activeCheckers.count("CheckOther::checkOverlappingWrite") != 0; + if (rule == "20.6") + return true; // preprocessorErrorDirective + if (rule == "21.13") + return activeCheckers.count("CheckFunctions::invalidFunctionUsage") != 0; + if (rule == "21.17") + return activeCheckers.count("CheckBufferOverrun::bufferOverflow") != 0; + if (rule == "21.18") + return activeCheckers.count("CheckBufferOverrun::bufferOverflow") != 0; + if (rule == "22.1") + return true; // memleak => error + if (rule == "22.2") + return activeCheckers.count("CheckAutoVariables::autoVariables") != 0; + if (rule == "22.3") + return activeCheckers.count("CheckIO::checkFileUsage") != 0; + if (rule == "22.4") + return activeCheckers.count("CheckIO::checkFileUsage") != 0; + if (rule == "22.6") + return activeCheckers.count("CheckIO::checkFileUsage") != 0; + + return false; +} + +CheckersReport::CheckersReport(const Settings& settings, const std::set& activeCheckers) + : mSettings(settings), mActiveCheckers(activeCheckers) +{} + +int CheckersReport::getActiveCheckersCount() +{ + if (mAllCheckersCount == 0) { + countCheckers(); + } + return mActiveCheckersCount; +} + +int CheckersReport::getAllCheckersCount() +{ + if (mAllCheckersCount == 0) { + countCheckers(); + } + return mAllCheckersCount; +} + +void CheckersReport::countCheckers() +{ + mActiveCheckersCount = mAllCheckersCount = 0; + + for (const auto& checkReq: checkers::allCheckers) { + if (mActiveCheckers.count(checkReq.first) > 0) + ++mActiveCheckersCount; + ++mAllCheckersCount; + } + for (const auto& checkReq: checkers::premiumCheckers) { + if (mActiveCheckers.count(checkReq.first) > 0) + ++mActiveCheckersCount; + ++mAllCheckersCount; + } + if (mSettings.premiumArgs.find("misra-c-") != std::string::npos || mSettings.addons.count("misra")) { + for (const checkers::MisraInfo& info: checkers::misraC2012Rules) { + const std::string rule = std::to_string(info.a) + "." + std::to_string(info.b); + const bool active = isMisraRuleActive(mActiveCheckers, rule); + if (active) + ++mActiveCheckersCount; + ++mAllCheckersCount; + } + } +} + +std::string CheckersReport::getReport(const std::string& criticalErrors) const +{ + std::ostringstream fout; + + fout << "Critical errors" << std::endl; + fout << "---------------" << std::endl; + if (!criticalErrors.empty()) { + fout << "There was critical errors (" << criticalErrors << ")" << std::endl; + fout << "All checking is skipped for a file with such error" << std::endl; + } else { + fout << "No critical errors, all files were checked." << std::endl; + fout << "Important: Analysis is still not guaranteed to be 'complete' it is possible there are false negatives." << std::endl; + } + + fout << std::endl << std::endl; + fout << "Open source checkers" << std::endl; + fout << "--------------------" << std::endl; + + int maxCheckerSize = 0; + for (const auto& checkReq: checkers::allCheckers) { + const std::string& checker = checkReq.first; + if (checker.size() > maxCheckerSize) + maxCheckerSize = checker.size(); + } + for (const auto& checkReq: checkers::allCheckers) { + const std::string& checker = checkReq.first; + const bool active = mActiveCheckers.count(checkReq.first) > 0; + const std::string& req = checkReq.second; + fout << (active ? "Yes " : "No ") << checker; + if (!active && !req.empty()) + fout << std::string(maxCheckerSize + 4 - checker.size(), ' ') << "require:" + req; + fout << std::endl; + } + + const bool cppcheckPremium = isCppcheckPremium(mSettings); + + auto reportSection = [&fout, cppcheckPremium] + (const std::string& title, + const Settings& settings, + const std::set& activeCheckers, + const std::map& premiumCheckers, + const std::string& substring) { + fout << std::endl << std::endl; + fout << title << std::endl; + fout << std::string(title.size(), '-') << std::endl; + if (!cppcheckPremium) { + fout << "Not available, Cppcheck Premium is not used" << std::endl; + return; + } + int maxCheckerSize = 0; + for (const auto& checkReq: premiumCheckers) { + const std::string& checker = checkReq.first; + if (checker.find(substring) != std::string::npos && checker.size() > maxCheckerSize) + maxCheckerSize = checker.size(); + } + for (const auto& checkReq: premiumCheckers) { + const std::string& checker = checkReq.first; + if (checker.find(substring) == std::string::npos) + continue; + std::string req = checkReq.second; + bool active = cppcheckPremium && activeCheckers.count(checker) > 0; + if (substring == "::") { + if (req == "warning") + active &= settings.severity.isEnabled(Severity::warning); + else if (req == "style") + active &= settings.severity.isEnabled(Severity::style); + else if (req == "portability") + active &= settings.severity.isEnabled(Severity::portability); + else if (!req.empty()) + active = false; // FIXME: handle req + } + fout << (active ? "Yes " : "No ") << checker; + if (!cppcheckPremium) { + if (!req.empty()) + req = "premium," + req; + else + req = "premium"; + } + if (!req.empty()) + req = "require:" + req; + if (!active) + fout << std::string(maxCheckerSize + 4 - checker.size(), ' ') << req; + fout << std::endl; + } + }; + + reportSection("Premium checkers", mSettings, mActiveCheckers, checkers::premiumCheckers, "::"); + reportSection("Autosar", mSettings, mActiveCheckers, checkers::premiumCheckers, "Autosar: "); + reportSection("Cert C", mSettings, mActiveCheckers, checkers::premiumCheckers, "Cert C: "); + reportSection("Cert C++", mSettings, mActiveCheckers, checkers::premiumCheckers, "Cert C++: "); + + int misra = 0; + if (mSettings.premiumArgs.find("misra-c-2012") != std::string::npos) + misra = 2012; + else if (mSettings.premiumArgs.find("misra-c-2023") != std::string::npos) + misra = 2023; + else if (mSettings.addons.count("misra")) + misra = 2012; + + if (misra == 0) { + fout << std::endl << std::endl; + fout << "Misra C" << std::endl; + fout << "-------" << std::endl; + fout << "Misra is not enabled" << std::endl; + } else { + fout << std::endl << std::endl; + fout << "Misra C " << misra << std::endl; + fout << "------------" << std::endl; + for (const checkers::MisraInfo& info: checkers::misraC2012Rules) { + const std::string rule = std::to_string(info.a) + "." + std::to_string(info.b); + const bool active = isMisraRuleActive(mActiveCheckers, rule); + fout << (active ? "Yes " : "No ") << "Misra C " << misra << ": " << rule; + std::string extra; + if (misra == 2012 && info.amendment >= 1) + extra = " amendment:" + std::to_string(info.amendment); + std::string reqs; + if (info.amendment >= 3) + reqs += ",premium"; + if (!active && !reqs.empty()) + extra += " require:" + reqs.substr(1); + if (!extra.empty()) + fout << std::string(7 - rule.size(), ' ') << extra; + fout << '\n'; + } + } + + reportSection("Misra C++ 2008", mSettings, mActiveCheckers, checkers::premiumCheckers, "Misra C++ 2008: "); + reportSection("Misra C++ 2023", mSettings, mActiveCheckers, checkers::premiumCheckers, "Misra C++ 2023: "); + + return fout.str(); +} diff --git a/cppcheck-2.14.0/lib/checkersreport.h b/cppcheck-2.14.0/lib/checkersreport.h new file mode 100644 index 00000000..e0c8654e --- /dev/null +++ b/cppcheck-2.14.0/lib/checkersreport.h @@ -0,0 +1,48 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef checkersReportH +#define checkersReportH + +#include "config.h" + +#include +#include + +class Settings; + +class CPPCHECKLIB CheckersReport { +public: + CheckersReport(const Settings& settings, const std::set& activeCheckers); + + int getActiveCheckersCount(); + int getAllCheckersCount(); + + std::string getReport(const std::string& criticalErrors) const; + +private: + const Settings& mSettings; + const std::set& mActiveCheckers; + + void countCheckers(); + + int mActiveCheckersCount = 0; + int mAllCheckersCount = 0; +}; + +#endif diff --git a/cppcheck-2.14.0/lib/checkexceptionsafety.cpp b/cppcheck-2.14.0/lib/checkexceptionsafety.cpp new file mode 100644 index 00000000..c4e1e5c0 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkexceptionsafety.cpp @@ -0,0 +1,411 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "errortypes.h" +#include "library.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" + +#include +#include +#include +#include + +//--------------------------------------------------------------------------- + +// Register CheckExceptionSafety.. +namespace { + CheckExceptionSafety instance; +} + +static const CWE CWE398(398U); // Indicator of Poor Code Quality +static const CWE CWE703(703U); // Improper Check or Handling of Exceptional Conditions +static const CWE CWE480(480U); // Use of Incorrect Operator + +//--------------------------------------------------------------------------- + +void CheckExceptionSafety::destructors() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckExceptionSafety::destructors"); // warning + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + + // Perform check.. + for (const Scope * scope : symbolDatabase->functionScopes) { + const Function * function = scope->function; + if (!function) + continue; + // only looking for destructors + if (function->type == Function::eDestructor) { + // Inspect this destructor. + for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + // Skip try blocks + if (Token::simpleMatch(tok, "try {")) { + tok = tok->next()->link(); + } + + // Skip uncaught exceptions + else if (Token::simpleMatch(tok, "if ( ! std :: uncaught_exception ( ) ) {")) { + tok = tok->next()->link(); // end of if ( ... ) + tok = tok->next()->link(); // end of { ... } + } + + // throw found within a destructor + else if (tok->str() == "throw") { + destructorsError(tok, scope->className); + break; + } + } + } + } +} + +void CheckExceptionSafety::destructorsError(const Token * const tok, const std::string &className) +{ + reportError(tok, Severity::warning, "exceptThrowInDestructor", + "Class " + className + " is not safe, destructor throws exception\n" + "The class " + className + " is not safe because its destructor " + "throws an exception. If " + className + " is used and an exception " + "is thrown that is caught in an outer scope the program will terminate.", CWE398, Certainty::normal); +} + + +void CheckExceptionSafety::deallocThrow() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckExceptionSafety::deallocThrow"); // warning + + const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + + // Deallocate a global/member pointer and then throw exception + // the pointer will be a dead pointer + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + // only looking for delete now + if (tok->str() != "delete") + continue; + + // Check if this is something similar with: "delete p;" + tok = tok->next(); + if (Token::simpleMatch(tok, "[ ]")) + tok = tok->tokAt(2); + if (!tok || tok == scope->bodyEnd) + break; + if (!Token::Match(tok, "%var% ;")) + continue; + + // we only look for global variables + const Variable *var = tok->variable(); + if (!var || !(var->isGlobal() || var->isStatic())) + continue; + + const unsigned int varid(tok->varId()); + + // Token where throw occurs + const Token *throwToken = nullptr; + + // is there a throw after the deallocation? + const Token* const end2 = tok->scope()->bodyEnd; + for (const Token *tok2 = tok; tok2 != end2; tok2 = tok2->next()) { + // Throw after delete -> Dead pointer + if (tok2->str() == "throw") { + if (printInconclusive) { // For inconclusive checking, throw directly. + deallocThrowError(tok2, tok->str()); + break; + } + throwToken = tok2; + } + + // Variable is assigned -> Bail out + else if (Token::Match(tok2, "%varid% =", varid)) { + if (throwToken) // For non-inconclusive checking, wait until we find an assignment to it. Otherwise we assume it is safe to leave a dead pointer. + deallocThrowError(throwToken, tok2->str()); + break; + } + // Variable passed to function. Assume it becomes assigned -> Bail out + else if (Token::Match(tok2, "[,(] &| %varid% [,)]", varid)) // TODO: No bailout if passed by value or as const reference + break; + } + } + } +} + +void CheckExceptionSafety::deallocThrowError(const Token * const tok, const std::string &varname) +{ + reportError(tok, Severity::warning, "exceptDeallocThrow", "Exception thrown in invalid state, '" + + varname + "' points at deallocated memory.", CWE398, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// catch(const exception & err) +// { +// throw err; // <- should be just "throw;" +// } +//--------------------------------------------------------------------------- +void CheckExceptionSafety::checkRethrowCopy() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("rethrowcopy")) + return; + + logChecker("CheckExceptionSafety::checkRethrowCopy"); // style + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope &scope : symbolDatabase->scopeList) { + if (scope.type != Scope::eCatch) + continue; + + const unsigned int varid = scope.bodyStart->tokAt(-2)->varId(); + if (varid) { + for (const Token* tok = scope.bodyStart->next(); tok && tok != scope.bodyEnd; tok = tok->next()) { + if (Token::simpleMatch(tok, "catch (") && tok->next()->link() && tok->next()->link()->next()) { // Don't check inner catch - it is handled in another iteration of outer loop. + tok = tok->next()->link()->next()->link(); + if (!tok) + break; + } else if (Token::Match(tok, "%varid% .", varid)) { + const Token *parent = tok->astParent(); + while (Token::simpleMatch(parent->astParent(), ".")) + parent = parent->astParent(); + if (Token::Match(parent->astParent(), "%assign%|++|--|(") && parent == parent->astParent()->astOperand1()) + break; + } else if (Token::Match(tok, "throw %varid% ;", varid)) + rethrowCopyError(tok, tok->strAt(1)); + } + } + } +} + +void CheckExceptionSafety::rethrowCopyError(const Token * const tok, const std::string &varname) +{ + reportError(tok, Severity::style, "exceptRethrowCopy", + "Throwing a copy of the caught exception instead of rethrowing the original exception.\n" + "Rethrowing an exception with 'throw " + varname + ";' creates an unnecessary copy of '" + varname + "'. " + "To rethrow the caught exception without unnecessary copying or slicing, use a bare 'throw;'.", CWE398, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// try {} catch (std::exception err) {} <- Should be "std::exception& err" +//--------------------------------------------------------------------------- +void CheckExceptionSafety::checkCatchExceptionByValue() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("catchexceptionbyvalue")) + return; + + logChecker("CheckExceptionSafety::checkCatchExceptionByValue"); // style + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope &scope : symbolDatabase->scopeList) { + if (scope.type != Scope::eCatch) + continue; + + // Find a pass-by-value declaration in the catch(), excluding basic types + // e.g. catch (std::exception err) + const Variable *var = scope.bodyStart->tokAt(-2)->variable(); + if (var && var->isClass() && !var->isPointer() && !var->isReference()) + catchExceptionByValueError(scope.classDef); + } +} + +void CheckExceptionSafety::catchExceptionByValueError(const Token *tok) +{ + reportError(tok, Severity::style, + "catchExceptionByValue", "Exception should be caught by reference.\n" + "The exception is caught by value. It could be caught " + "as a (const) reference which is usually recommended in C++.", CWE398, Certainty::normal); +} + + +static const Token * functionThrowsRecursive(const Function * function, std::set & recursive) +{ + // check for recursion and bail if found + if (!recursive.insert(function).second) + return nullptr; + + if (!function->functionScope) + return nullptr; + + for (const Token *tok = function->functionScope->bodyStart->next(); + tok != function->functionScope->bodyEnd; tok = tok->next()) { + if (Token::simpleMatch(tok, "try {")) + tok = tok->linkAt(1); // skip till start of catch clauses + if (tok->str() == "throw") + return tok; + if (tok->function()) { + const Function * called = tok->function(); + // check if called function has an exception specification + if (called->isThrow() && called->throwArg) + return tok; + if (called->isNoExcept() && called->noexceptArg && + called->noexceptArg->str() != "true") + return tok; + 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() +{ + logChecker("CheckExceptionSafety::nothrowThrows"); + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope * scope : symbolDatabase->functionScopes) { + const Function* function = scope->function; + if (!function) + continue; + + // check noexcept and noexcept(true) functions + if (function->isNoExcept() && + (!function->noexceptArg || function->noexceptArg->str() == "true")) { + const Token *throws = functionThrows(function); + if (throws) + noexceptThrowError(throws); + } + + // check throw() functions + else if (function->isThrow() && !function->throwArg) { + const Token *throws = functionThrows(function); + if (throws) + noexceptThrowError(throws); + } + + // check __attribute__((nothrow)) or __declspec(nothrow) functions + else if (function->isAttributeNothrow()) { + const Token *throws = functionThrows(function); + if (throws) + noexceptThrowError(throws); + } + } +} + +void CheckExceptionSafety::noexceptThrowError(const Token * const tok) +{ + reportError(tok, Severity::error, "throwInNoexceptFunction", "Exception thrown in function declared not to throw exceptions.", CWE398, Certainty::normal); +} + +//-------------------------------------------------------------------------- +// void func() { functionWithExceptionSpecification(); } +//-------------------------------------------------------------------------- +void CheckExceptionSafety::unhandledExceptionSpecification() +{ + if ((!mSettings->severity.isEnabled(Severity::style) || !mSettings->certainty.isEnabled(Certainty::inconclusive)) && + !mSettings->isPremiumEnabled("unhandledexceptionspecification")) + return; + + logChecker("CheckExceptionSafety::unhandledExceptionSpecification"); // style,inconclusive + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope * scope : symbolDatabase->functionScopes) { + // only check functions without exception specification + if (scope->function && !scope->function->isThrow() && !mSettings->library.isentrypoint(scope->className)) { + for (const Token *tok = scope->function->functionScope->bodyStart->next(); + tok != scope->function->functionScope->bodyEnd; tok = tok->next()) { + if (tok->str() == "try") + break; + 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; + } + } + } + } + } +} + +void CheckExceptionSafety::unhandledExceptionSpecificationError(const Token * const tok1, const Token * const tok2, const std::string & funcname) +{ + const std::string str1(tok1 ? tok1->str() : "foo"); + const std::list locationList = { tok1, tok2 }; + reportError(locationList, Severity::style, "unhandledExceptionSpecification", + "Unhandled exception specification when calling function " + str1 + "().\n" + "Unhandled exception specification when calling function " + str1 + "(). " + "Either use a try/catch around the function call, or add a exception specification for " + funcname + "() also.", CWE703, Certainty::inconclusive); +} + +//-------------------------------------------------------------------------- +// 7.6.18.4 If no exception is presently being handled, evaluating a throw-expression with no operand calls std​::​​terminate(). +//-------------------------------------------------------------------------- +void CheckExceptionSafety::rethrowNoCurrentException() +{ + logChecker("CheckExceptionSafety::rethrowNoCurrentException"); + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + const Function* function = scope->function; + if (!function) + continue; + + // Rethrow can be used in 'exception dispatcher' idiom which is FP in such case + // https://isocpp.org/wiki/faq/exceptions#throw-without-an-object + // We check the beginning of the function with idiom pattern + if (Token::simpleMatch(function->functionScope->bodyStart->next(), "try { throw ; } catch (")) + continue; + + for (const Token *tok = function->functionScope->bodyStart->next(); + tok != function->functionScope->bodyEnd; tok = tok->next()) { + if (Token::simpleMatch(tok, "catch (")) { + tok = tok->linkAt(1); // skip catch argument + if (Token::simpleMatch(tok, ") {")) + tok = tok->linkAt(1); // skip catch scope + else + break; + } + if (Token::simpleMatch(tok, "throw ;")) { + rethrowNoCurrentExceptionError(tok); + } + } + } +} + +void CheckExceptionSafety::rethrowNoCurrentExceptionError(const Token *tok) +{ + reportError(tok, Severity::error, "rethrowNoCurrentException", + "Rethrowing current exception with 'throw;', it seems there is no current exception to rethrow." + " If there is no current exception this calls std::terminate()." + " More: https://isocpp.org/wiki/faq/exceptions#throw-without-an-object", + CWE480, Certainty::normal); +} diff --git a/cppcheck-2.14.0/lib/checkexceptionsafety.h b/cppcheck-2.14.0/lib/checkexceptionsafety.h new file mode 100644 index 00000000..dea6ee53 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkexceptionsafety.h @@ -0,0 +1,134 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef checkexceptionsafetyH +#define checkexceptionsafetyH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "tokenize.h" + +#include + +class Settings; +class ErrorLogger; +class Token; + + +/// @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()) {} + +private: + /** This constructor is used when running checks. */ + CheckExceptionSafety(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger) {} + + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + if (tokenizer.isC()) + return; + + CheckExceptionSafety checkExceptionSafety(&tokenizer, &tokenizer.getSettings(), errorLogger); + checkExceptionSafety.destructors(); + checkExceptionSafety.deallocThrow(); + checkExceptionSafety.checkRethrowCopy(); + checkExceptionSafety.checkCatchExceptionByValue(); + checkExceptionSafety.nothrowThrows(); + checkExceptionSafety.unhandledExceptionSpecification(); + checkExceptionSafety.rethrowNoCurrentException(); + } + + /** 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(); + + /** @brief %Check for rethrow not from catch scope */ + void rethrowNoCurrentException(); + + /** Don't throw exceptions in destructors */ + void destructorsError(const Token * const tok, const std::string &className); + void deallocThrowError(const Token * const tok, const std::string &varname); + void rethrowCopyError(const Token * const tok, const std::string &varname); + void catchExceptionByValueError(const Token *tok); + void noexceptThrowError(const Token * const tok); + /** Missing exception specification */ + void unhandledExceptionSpecificationError(const Token * const tok1, const Token * const tok2, const std::string & funcname); + /** Rethrow without currently handled exception */ + void rethrowNoCurrentExceptionError(const Token *tok); + + /** Generate all possible errors (for --errorlist) */ + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckExceptionSafety c(nullptr, settings, errorLogger); + c.destructorsError(nullptr, "Class"); + c.deallocThrowError(nullptr, "p"); + c.rethrowCopyError(nullptr, "varname"); + c.catchExceptionByValueError(nullptr); + c.noexceptThrowError(nullptr); + c.unhandledExceptionSpecificationError(nullptr, nullptr, "funcname"); + c.rethrowNoCurrentExceptionError(nullptr); + } + + /** Short description of class (for --doc) */ + static std::string myName() { + return "Exception Safety"; + } + + /** wiki formatted description of the class (for --doc) */ + std::string classInfo() const override { + return "Checking exception safety\n" + "- Throwing exceptions in destructors\n" + "- Throwing exception during invalid state\n" + "- Throwing a copy of a caught exception instead of rethrowing the original exception\n" + "- Exception caught by value instead of by reference\n" + "- Throwing exception in noexcept, nothrow(), __attribute__((nothrow)) or __declspec(nothrow) function\n" + "- Unhandled exception specification when calling function foo()\n" + "- Rethrow without currently handled exception\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkexceptionsafetyH diff --git a/cppcheck-2.14.0/lib/checkfunctions.cpp b/cppcheck-2.14.0/lib/checkfunctions.cpp new file mode 100644 index 00000000..82f7e796 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkfunctions.cpp @@ -0,0 +1,830 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +// Check functions +//--------------------------------------------------------------------------- + +#include "checkfunctions.h" + +#include "astutils.h" +#include "mathlib.h" +#include "platform.h" +#include "standards.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "valueflow.h" +#include "vfvalue.h" + +#include +#include +#include +#include +#include + +//--------------------------------------------------------------------------- + + +// Register this check class (by creating a static instance of it) +namespace { + CheckFunctions instance; +} + +static const CWE CWE252(252U); // Unchecked Return Value +static const CWE CWE477(477U); // Use of Obsolete Functions +static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior +static const CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments +static const CWE CWE686(686U); // Function Call With Incorrect Argument Type +static const CWE CWE687(687U); // Function Call With Incorrectly Specified Argument Value +static const CWE CWE688(688U); // Function Call With Incorrect Variable or Reference as Argument + +void CheckFunctions::checkProhibitedFunctions() +{ + const bool checkAlloca = mSettings->severity.isEnabled(Severity::warning) && ((mTokenizer->isC() && mSettings->standards.c >= Standards::C99) || mSettings->standards.cpp >= Standards::CPP11); + + logChecker("CheckFunctions::checkProhibitedFunctions"); + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::Match(tok, "%name% (") && tok->varId() == 0) + continue; + // alloca() is special as it depends on the code being C or C++, so it is not in Library + if (checkAlloca && Token::simpleMatch(tok, "alloca (") && (!tok->function() || tok->function()->nestedIn->type == Scope::eGlobal)) { + if (tok->isC()) { + if (mSettings->standards.c > Standards::C89) + reportError(tok, Severity::warning, "allocaCalled", + "$symbol:alloca\n" + "Obsolete function 'alloca' called. In C99 and later it is recommended to use a variable length array instead.\n" + "The obsolete function 'alloca' is called. In C99 and later it is recommended to use a variable length array or " + "a dynamically allocated array instead. The function 'alloca' is dangerous for many reasons " + "(http://stackoverflow.com/questions/1018853/why-is-alloca-not-considered-good-practice and http://linux.die.net/man/3/alloca)."); + } else + reportError(tok, Severity::warning, "allocaCalled", + "$symbol:alloca\n" + "Obsolete function 'alloca' called.\n" + "The obsolete function 'alloca' is called. In C++11 and later it is recommended to use std::array<> or " + "a dynamically allocated array instead. The function 'alloca' is dangerous for many reasons " + "(http://stackoverflow.com/questions/1018853/why-is-alloca-not-considered-good-practice and http://linux.die.net/man/3/alloca)."); + } else { + if (tok->function() && tok->function()->hasBody()) + continue; + + const Library::WarnInfo* wi = mSettings->library.getWarnInfo(tok); + if (wi) { + if (mSettings->severity.isEnabled(wi->severity) && ((tok->isC() && mSettings->standards.c >= wi->standards.c) || (tok->isCpp() && mSettings->standards.cpp >= wi->standards.cpp))) { + const std::string daca = mSettings->daca ? "prohibited" : ""; + reportError(tok, wi->severity, daca + tok->str() + "Called", wi->message, CWE477, Certainty::normal); + } + } + } + } + } +} + +//--------------------------------------------------------------------------- +// Check , and +//--------------------------------------------------------------------------- +void CheckFunctions::invalidFunctionUsage() +{ + logChecker("CheckFunctions::invalidFunctionUsage"); + const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::Match(tok, "%name% ( !!)")) + continue; + const Token * const functionToken = tok; + const std::vector arguments = getArguments(tok); + for (int argnr = 1; argnr <= arguments.size(); ++argnr) { + const Token * const argtok = arguments[argnr-1]; + + // check ... + const ValueFlow::Value *invalidValue = argtok->getInvalidValue(functionToken,argnr,mSettings); + if (invalidValue) { + invalidFunctionArgError(argtok, functionToken->next()->astOperand1()->expressionString(), argnr, invalidValue, mSettings->library.validarg(functionToken, argnr)); + } + + if (astIsBool(argtok)) { + // check + if (mSettings->library.isboolargbad(functionToken, argnr)) + invalidFunctionArgBoolError(argtok, functionToken->str(), argnr); + + // Are the values 0 and 1 valid? + else if (!mSettings->library.isIntArgValid(functionToken, argnr, 0)) + invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr)); + else if (!mSettings->library.isIntArgValid(functionToken, argnr, 1)) + invalidFunctionArgError(argtok, functionToken->str(), argnr, nullptr, mSettings->library.validarg(functionToken, argnr)); + } + // check + if (mSettings->library.isargstrz(functionToken, argnr)) { + if (Token::Match(argtok, "& %var% !![") && argtok->next() && argtok->next()->valueType()) { + const ValueType * valueType = argtok->next()->valueType(); + const Variable * variable = argtok->next()->variable(); + if ((valueType->type == ValueType::Type::CHAR || valueType->type == ValueType::Type::WCHAR_T || (valueType->type == ValueType::Type::RECORD && Token::Match(argtok, "& %var% . %var% ,|)"))) && + !variable->isArray() && + (variable->isConst() || !variable->isGlobal()) && + (!argtok->next()->hasKnownValue() || argtok->next()->getValue(0) == nullptr)) { + invalidFunctionArgStrError(argtok, functionToken->str(), argnr); + } + } + const ValueType* const valueType = argtok->valueType(); + const Variable* const variable = argtok->variable(); + // Is non-null terminated local variable of type char (e.g. char buf[] = {'x'};) ? + if (variable && variable->isLocal() + && valueType && (valueType->type == ValueType::Type::CHAR || valueType->type == ValueType::Type::WCHAR_T) + && !isVariablesChanged(variable->declEndToken(), functionToken, 0 /*indirect*/, { variable }, mSettings)) { + const Token* varTok = variable->declEndToken(); + auto count = -1; // Find out explicitly set count, e.g.: char buf[3] = {...}. Variable 'count' is set to 3 then. + if (varTok && Token::simpleMatch(varTok->astOperand1(), "[")) + { + const Token* const countTok = varTok->astOperand1()->astOperand2(); + if (countTok && countTok->hasKnownIntValue()) + count = countTok->getKnownIntValue(); + } + if (Token::simpleMatch(varTok, "= {")) { + varTok = varTok->tokAt(1); + auto charsUntilFirstZero = 0; + bool search = true; + while (search && varTok && !Token::simpleMatch(varTok->next(), "}")) { + varTok = varTok->next(); + if (!Token::simpleMatch(varTok, ",")) { + if (Token::Match(varTok, "%op%")) { + varTok = varTok->next(); + continue; + } + ++charsUntilFirstZero; + if (varTok && varTok->hasKnownIntValue() && varTok->getKnownIntValue() == 0) + search=false; // stop counting for cases like char buf[3] = {'x', '\0', 'y'}; + } + } + if (varTok && varTok->hasKnownIntValue() && varTok->getKnownIntValue() != 0 + && (count == -1 || (count > 0 && count <= charsUntilFirstZero))) { + invalidFunctionArgStrError(argtok, functionToken->str(), argnr); + } + } else if (count > -1 && Token::Match(varTok, "= %str%")) { + const Token* strTok = varTok->getValueTokenMinStrSize(*mSettings); + if (strTok) { + const int strSize = Token::getStrArraySize(strTok); + if (strSize > count && strTok->str().find('\0') == std::string::npos) + invalidFunctionArgStrError(argtok, functionToken->str(), argnr); + } + } + } + } + } + } + } +} + +void CheckFunctions::invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr) +{ + std::ostringstream errmsg; + errmsg << "$symbol:" << functionName << '\n'; + if (invalidValue && invalidValue->condition) + errmsg << ValueFlow::eitherTheConditionIsRedundant(invalidValue->condition) + << " or $symbol() argument nr " << argnr << " can have invalid value."; + else + errmsg << "Invalid $symbol() argument nr " << argnr << '.'; + if (invalidValue) + errmsg << " The value is " << std::setprecision(10) << (invalidValue->isIntValue() ? invalidValue->intvalue : invalidValue->floatValue) << " but the valid values are '" << validstr << "'."; + else + errmsg << " The value is 0 or 1 (boolean) but the valid values are '" << validstr << "'."; + if (invalidValue) + reportError(getErrorPath(tok, invalidValue, "Invalid argument"), + invalidValue->errorSeverity() && invalidValue->isKnown() ? Severity::error : Severity::warning, + "invalidFunctionArg", + errmsg.str(), + CWE628, + invalidValue->isInconclusive() ? Certainty::inconclusive : Certainty::normal); + else + reportError(tok, + Severity::error, + "invalidFunctionArg", + errmsg.str(), + CWE628, + Certainty::normal); +} + +void CheckFunctions::invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr) +{ + std::ostringstream errmsg; + errmsg << "$symbol:" << functionName << '\n'; + errmsg << "Invalid $symbol() argument nr " << argnr << ". A non-boolean value is required."; + reportError(tok, Severity::error, "invalidFunctionArgBool", errmsg.str(), CWE628, Certainty::normal); +} + +void CheckFunctions::invalidFunctionArgStrError(const Token *tok, const std::string &functionName, nonneg int argnr) +{ + std::ostringstream errmsg; + errmsg << "$symbol:" << functionName << '\n'; + errmsg << "Invalid $symbol() argument nr " << argnr << ". A nul-terminated string is required."; + reportError(tok, Severity::error, "invalidFunctionArgStr", errmsg.str(), CWE628, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Check for ignored return values. +//--------------------------------------------------------------------------- +void CheckFunctions::checkIgnoredReturnValue() +{ + if (!mSettings->severity.isEnabled(Severity::warning) && + !mSettings->severity.isEnabled(Severity::style) && + !mSettings->isPremiumEnabled("ignoredReturnValue")) + return; + + logChecker("CheckFunctions::checkIgnoredReturnValue"); // style,warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + // skip c++11 initialization, ({...}) + if (Token::Match(tok, "%var%|(|,|return {")) + tok = tok->linkAt(1); + else if (Token::Match(tok, "[(<]") && tok->link()) + tok = tok->link(); + + if (tok->varId() || tok->isKeyword() || tok->isStandardType() || !Token::Match(tok, "%name% (")) + continue; + + const Token *parent = tok->next()->astParent(); + while (Token::Match(parent, "%cop%")) { + if (Token::Match(parent, "<<|>>|*") && !parent->astParent()) + break; + parent = parent->astParent(); + } + if (parent) + continue; + + if (!tok->scope()->isExecutable()) { + tok = tok->scope()->bodyEnd; + continue; + } + + if ((!tok->function() || !Token::Match(tok->function()->retDef, "void %name%")) && + tok->next()->astOperand1()) { + const Library::UseRetValType retvalTy = mSettings->library.getUseRetValType(tok); + const bool warn = (tok->function() && tok->function()->isAttributeNodiscard()) || // avoid duplicate warnings for resource-allocating functions + (retvalTy == Library::UseRetValType::DEFAULT && mSettings->library.getAllocFuncInfo(tok) == nullptr); + if (mSettings->severity.isEnabled(Severity::warning) && warn) + ignoredReturnValueError(tok, tok->next()->astOperand1()->expressionString()); + else if (mSettings->severity.isEnabled(Severity::style) && + retvalTy == Library::UseRetValType::ERROR_CODE) + ignoredReturnErrorCode(tok, tok->next()->astOperand1()->expressionString()); + } + } + } +} + +void CheckFunctions::ignoredReturnValueError(const Token* tok, const std::string& function) +{ + reportError(tok, Severity::warning, "ignoredReturnValue", + "$symbol:" + function + "\nReturn value of function $symbol() is not used.", CWE252, Certainty::normal); +} + +void CheckFunctions::ignoredReturnErrorCode(const Token* tok, const std::string& function) +{ + reportError(tok, Severity::style, "ignoredReturnErrorCode", + "$symbol:" + function + "\nError code from the return value of function $symbol() is not used.", CWE252, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Check for ignored return values. +//--------------------------------------------------------------------------- +static const Token *checkMissingReturnScope(const Token *tok, const Library &library); + +void CheckFunctions::checkMissingReturn() +{ + logChecker("CheckFunctions::checkMissingReturn"); + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *scope : symbolDatabase->functionScopes) { + const Function *function = scope->function; + if (!function || !function->hasBody()) + continue; + if (function->name() == "main" && !(mTokenizer->isC() && mSettings->standards.c < Standards::C99)) + continue; + if (function->type != Function::Type::eFunction && function->type != Function::Type::eOperatorEqual) + continue; + if (Token::Match(function->retDef, "%name% (") && function->retDef->isUpperCaseName()) + continue; + if (Function::returnsVoid(function, true)) + continue; + const Token *errorToken = checkMissingReturnScope(scope->bodyEnd, mSettings->library); + if (errorToken) + missingReturnError(errorToken); + } +} + +static bool isForwardJump(const Token *gotoToken) +{ + if (!Token::Match(gotoToken, "goto %name% ;")) + return false; + for (const Token *prev = gotoToken; gotoToken; gotoToken = gotoToken->previous()) { + if (Token::Match(prev, "%name% :") && prev->str() == gotoToken->next()->str()) + return true; + if (prev->str() == "{" && prev->scope()->type == Scope::eFunction) + return false; + } + return false; +} + +static const Token *checkMissingReturnScope(const Token *tok, const Library &library) +{ + const Token *lastStatement = nullptr; + while ((tok = tok->previous()) != nullptr) { + if (tok->str() == ")") + tok = tok->link(); + if (tok->str() == "{") + return lastStatement ? lastStatement : tok->next(); + if (tok->str() == "}") { + for (const Token *prev = tok->link()->previous(); prev && prev->scope() == tok->scope() && !Token::Match(prev, "[;{}]"); prev = prev->previous()) { + if (prev->isKeyword() && Token::Match(prev, "return|throw")) + return nullptr; + if (prev->str() == "goto" && !isForwardJump(prev)) + return nullptr; + } + if (tok->scope()->type == Scope::ScopeType::eSwitch) { + // find reachable break / !default + bool hasDefault = false; + bool reachable = false; + for (const Token *switchToken = tok->link()->next(); switchToken != tok; switchToken = switchToken->next()) { + if (reachable && Token::simpleMatch(switchToken, "break ;")) { + if (Token::simpleMatch(switchToken->previous(), "}") && !checkMissingReturnScope(switchToken->previous(), library)) + reachable = false; + else + return switchToken; + } + if (switchToken->isKeyword() && Token::Match(switchToken, "return|throw")) + reachable = false; + if (Token::Match(switchToken, "%name% (") && library.isnoreturn(switchToken)) + reachable = false; + if (Token::Match(switchToken, "case|default")) + reachable = true; + if (Token::simpleMatch(switchToken, "default :")) + hasDefault = true; + else if (switchToken->str() == "{" && (switchToken->scope()->isLoopScope() || switchToken->scope()->type == Scope::ScopeType::eSwitch)) + switchToken = switchToken->link(); + } + if (!hasDefault) + return tok->link(); + } else if (tok->scope()->type == Scope::ScopeType::eIf) { + const Token *condition = tok->scope()->classDef->next()->astOperand2(); + if (condition && condition->hasKnownIntValue() && condition->getKnownIntValue() == 1) + return checkMissingReturnScope(tok, library); + return tok; + } else if (tok->scope()->type == Scope::ScopeType::eElse) { + const Token *errorToken = checkMissingReturnScope(tok, library); + if (errorToken) + return errorToken; + tok = tok->link(); + if (Token::simpleMatch(tok->tokAt(-2), "} else {")) + return checkMissingReturnScope(tok->tokAt(-2), library); + return tok; + } + // FIXME + return nullptr; + } + if (tok->isKeyword() && Token::Match(tok, "return|throw")) + return nullptr; + if (tok->str() == "goto" && !isForwardJump(tok)) + return nullptr; + if (Token::Match(tok, "%name% (") && !library.isnotnoreturn(tok)) { + return nullptr; + } + if (Token::Match(tok, "[;{}] %name% :")) + return tok; + if (Token::Match(tok, "; !!}") && !lastStatement) + lastStatement = tok->next(); + } + return nullptr; +} + +void CheckFunctions::missingReturnError(const Token* tok) +{ + reportError(tok, Severity::error, "missingReturn", + "Found an exit path from function with non-void return type that has missing return statement", CWE758, Certainty::normal); +} +//--------------------------------------------------------------------------- +// Detect passing wrong values to functions like atan(0, x); +//--------------------------------------------------------------------------- +void CheckFunctions::checkMathFunctions() +{ + const bool styleC99 = mSettings->severity.isEnabled(Severity::style) && ((mTokenizer->isC() && mSettings->standards.c != Standards::C89) || (mTokenizer->isCPP() && mSettings->standards.cpp != Standards::CPP03)); + const bool printWarnings = mSettings->severity.isEnabled(Severity::warning); + + if (!styleC99 && !printWarnings && !mSettings->isPremiumEnabled("wrongmathcall")) + return; + + logChecker("CheckFunctions::checkMathFunctions"); // style,warning,c99,c++11 + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (tok->varId()) + continue; + if (printWarnings && Token::Match(tok, "%name% ( !!)")) { + if (tok->strAt(-1) != "." + && Token::Match(tok, "log|logf|logl|log10|log10f|log10l|log2|log2f|log2l ( %num% )")) { + const std::string& number = tok->strAt(2); + if ((MathLib::isInt(number) && MathLib::toBigNumber(number) <= 0) || + (MathLib::isFloat(number) && MathLib::toDoubleNumber(number) <= 0.)) + mathfunctionCallWarning(tok); + } else if (Token::Match(tok, "log1p|log1pf|log1pl ( %num% )")) { + const std::string& number = tok->strAt(2); + if ((MathLib::isInt(number) && MathLib::toBigNumber(number) <= -1) || + (MathLib::isFloat(number) && MathLib::toDoubleNumber(number) <= -1.)) + mathfunctionCallWarning(tok); + } + // atan2 ( x , y): x and y can not be zero, because this is mathematically not defined + else if (Token::Match(tok, "atan2|atan2f|atan2l ( %num% , %num% )")) { + if (MathLib::isNullValue(tok->strAt(2)) && MathLib::isNullValue(tok->strAt(4))) + mathfunctionCallWarning(tok, 2); + } + // fmod ( x , y) If y is zero, then either a range error will occur or the function will return zero (implementation-defined). + else if (Token::Match(tok, "fmod|fmodf|fmodl (")) { + const Token* nextArg = tok->tokAt(2)->nextArgument(); + if (nextArg && MathLib::isNullValue(nextArg->str())) + mathfunctionCallWarning(tok, 2); + } + // pow ( x , y) If x is zero, and y is negative --> division by zero + else if (Token::Match(tok, "pow|powf|powl ( %num% , %num% )")) { + if (MathLib::isNullValue(tok->strAt(2)) && MathLib::isNegative(tok->strAt(4))) + mathfunctionCallWarning(tok, 2); + } + } + + if (styleC99) { + if (Token::Match(tok, "%num% - erf (") && Tokenizer::isOneNumber(tok->str()) && tok->next()->astOperand2() == tok->tokAt(3)) { + mathfunctionCallWarning(tok, "1 - erf(x)", "erfc(x)"); + } else if (Token::simpleMatch(tok, "exp (") && Token::Match(tok->linkAt(1), ") - %num%") && Tokenizer::isOneNumber(tok->linkAt(1)->strAt(2)) && tok->linkAt(1)->next()->astOperand1() == tok->next()) { + mathfunctionCallWarning(tok, "exp(x) - 1", "expm1(x)"); + } else if (Token::simpleMatch(tok, "log (") && tok->next()->astOperand2()) { + const Token* plus = tok->next()->astOperand2(); + if (plus->str() == "+" && ((plus->astOperand1() && Tokenizer::isOneNumber(plus->astOperand1()->str())) || (plus->astOperand2() && Tokenizer::isOneNumber(plus->astOperand2()->str())))) + mathfunctionCallWarning(tok, "log(1 + x)", "log1p(x)"); + } + } + } + } +} + +void CheckFunctions::mathfunctionCallWarning(const Token *tok, const nonneg int numParam) +{ + if (tok) { + if (numParam == 1) + reportError(tok, Severity::warning, "wrongmathcall", "$symbol:" + tok->str() + "\nPassing value " + tok->strAt(2) + " to $symbol() leads to implementation-defined result.", CWE758, Certainty::normal); + else if (numParam == 2) + reportError(tok, Severity::warning, "wrongmathcall", "$symbol:" + tok->str() + "\nPassing values " + tok->strAt(2) + " and " + tok->strAt(4) + " to $symbol() leads to implementation-defined result.", CWE758, Certainty::normal); + } else + reportError(tok, Severity::warning, "wrongmathcall", "Passing value '#' to #() leads to implementation-defined result.", CWE758, Certainty::normal); +} + +void CheckFunctions::mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp) +{ + reportError(tok, Severity::style, "unpreciseMathCall", "Expression '" + oldexp + "' can be replaced by '" + newexp + "' to avoid loss of precision.", CWE758, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// memset(p, y, 0 /* bytes to fill */) <- 2nd and 3rd arguments inverted +//--------------------------------------------------------------------------- +void CheckFunctions::memsetZeroBytes() +{ +// FIXME: +// Replace this with library configuration. +// For instance: +// +// +// + + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckFunctions::memsetZeroBytes"); // warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "memset|wmemset (") && (numberOfArguments(tok)==3)) { + const std::vector &arguments = getArguments(tok); + if (WRONG_DATA(arguments.size() != 3U, tok)) + continue; + const Token* lastParamTok = arguments[2]; + if (MathLib::isNullValue(lastParamTok->str())) + memsetZeroBytesError(tok); + } + } + } +} + +void CheckFunctions::memsetZeroBytesError(const Token *tok) +{ + const std::string summary("memset() called to fill 0 bytes."); + const std::string verbose(summary + " The second and third arguments might be inverted." + " The function memset ( void * ptr, int value, size_t num ) sets the" + " first num bytes of the block of memory pointed by ptr to the specified value."); + reportError(tok, Severity::warning, "memsetZeroBytes", summary + "\n" + verbose, CWE687, Certainty::normal); +} + +void CheckFunctions::memsetInvalid2ndParam() +{ +// FIXME: +// Replace this with library configuration. +// For instance: +// +// +// +// + + const bool printPortability = mSettings->severity.isEnabled(Severity::portability); + const bool printWarning = mSettings->severity.isEnabled(Severity::warning); + if (!printWarning && !printPortability) + return; + + logChecker("CheckFunctions::memsetInvalid2ndParam"); // warning,portability + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok && (tok != scope->bodyEnd); tok = tok->next()) { + if (!Token::simpleMatch(tok, "memset (")) + continue; + + const std::vector args = getArguments(tok); + if (args.size() != 3) + continue; + + // Second parameter is zero literal, i.e. 0.0f + const Token * const secondParamTok = args[1]; + if (Token::Match(secondParamTok, "%num% ,") && MathLib::isNullValue(secondParamTok->str())) + continue; + + // Check if second parameter is a float variable or a float literal != 0.0f + if (printPortability && astIsFloat(secondParamTok,false)) { + memsetFloatError(secondParamTok, secondParamTok->expressionString()); + } + + if (printWarning && secondParamTok->isNumber()) { // Check if the second parameter is a literal and is out of range + const long long int value = MathLib::toBigNumber(secondParamTok->str()); + const long long sCharMin = mSettings->platform.signedCharMin(); + const long long uCharMax = mSettings->platform.unsignedCharMax(); + if (value < sCharMin || value > uCharMax) + memsetValueOutOfRangeError(secondParamTok, secondParamTok->str()); + } + } + } +} + +void CheckFunctions::memsetFloatError(const Token *tok, const std::string &var_value) +{ + const std::string message("The 2nd memset() argument '" + var_value + + "' is a float, its representation is implementation defined."); + const std::string verbose(message + " memset() is used to set each byte of a block of memory to a specific value and" + " the actual representation of a floating-point value is implementation defined."); + reportError(tok, Severity::portability, "memsetFloat", message + "\n" + verbose, CWE688, Certainty::normal); +} + +void CheckFunctions::memsetValueOutOfRangeError(const Token *tok, const std::string &value) +{ + const std::string message("The 2nd memset() argument '" + value + "' doesn't fit into an 'unsigned char'."); + const std::string verbose(message + " The 2nd parameter is passed as an 'int', but the function fills the block of memory using the 'unsigned char' conversion of this value."); + reportError(tok, Severity::warning, "memsetValueOutOfRange", message + "\n" + verbose, CWE686, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// --check-library => warn for unconfigured functions +//--------------------------------------------------------------------------- + +void CheckFunctions::checkLibraryMatchFunctions() +{ + if (!mSettings->checkLibrary) + return; + + bool insideNew = false; + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (!tok->scope() || !tok->scope()->isExecutable()) + continue; + + if (tok->str() == "new") + insideNew = true; + else if (tok->str() == ";") + insideNew = false; + else if (insideNew) + continue; + + if (tok->isKeyword() || !Token::Match(tok, "%name% (")) + continue; + + if (tok->varId() != 0 || tok->type() || tok->isStandardType()) + continue; + + if (tok->linkAt(1)->strAt(1) == "(") + continue; + + if (tok->function()) + continue; + + if (Token::simpleMatch(tok->astTop(), "throw")) + continue; + + if (Token::simpleMatch(tok->astParent(), ".")) { + const Token* contTok = tok->astParent()->astOperand1(); + if (astContainerAction(contTok) != Library::Container::Action::NO_ACTION) + continue; + if (astContainerYield(contTok) != Library::Container::Yield::NO_YIELD) + continue; + } + + if (!mSettings->library.isNotLibraryFunction(tok)) + continue; + + const std::string &functionName = mSettings->library.getFunctionName(tok); + if (functionName.empty()) + continue; + + if (mSettings->library.functions.find(functionName) != mSettings->library.functions.end()) + continue; + + if (mSettings->library.podtype(tok->expressionString())) + continue; + + if (mSettings->library.getTypeCheck("unusedvar", functionName) != Library::TypeCheck::def) + continue; + + const Token* start = tok; + while (Token::Match(start->tokAt(-2), "%name% ::") && !start->tokAt(-2)->isKeyword()) + start = start->tokAt(-2); + if (mSettings->library.detectContainerOrIterator(start)) + continue; + + reportError(tok, + Severity::information, + "checkLibraryFunction", + "--check-library: There is no matching configuration for function " + functionName + "()"); + } +} + +// Check for problems to compiler apply (Named) Return Value Optimization for local variable +// Technically we have different guarantees between standard versions +// details: https://en.cppreference.com/w/cpp/language/copy_elision +void CheckFunctions::returnLocalStdMove() +{ + if (!mTokenizer->isCPP() || mSettings->standards.cpp < Standards::CPP11) + return; + + if (!mSettings->severity.isEnabled(Severity::performance)) + return; + + logChecker("CheckFunctions::returnLocalStdMove"); // performance,c++11 + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *scope : symbolDatabase->functionScopes) { + // Expect return by-value + if (Function::returnsReference(scope->function, /*unknown*/ true, /*includeRValueRef*/ true)) + continue; + const auto rets = Function::findReturns(scope->function); + for (const Token* ret : rets) { + if (!Token::simpleMatch(ret->tokAt(-3), "std :: move (")) + continue; + const Token* retval = ret->astOperand2(); + // NRVO + if (retval->variable() && retval->variable()->isLocal() && !retval->variable()->isVolatile()) + copyElisionError(retval); + // RVO + if (Token::Match(retval, "(|{") && !retval->isCast() && !(retval->valueType() && retval->valueType()->reference != Reference::None)) + copyElisionError(retval); + } + } +} + +void CheckFunctions::copyElisionError(const Token *tok) +{ + reportError(tok, + Severity::performance, + "returnStdMoveLocal", + "Using std::move for returning object by-value from function will affect copy elision optimization." + " More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local"); +} + +void CheckFunctions::useStandardLibrary() +{ + if (!mSettings->severity.isEnabled(Severity::style)) + return; + + logChecker("CheckFunctions::useStandardLibrary"); // style + + for (const Scope& scope: mTokenizer->getSymbolDatabase()->scopeList) { + if (scope.type != Scope::ScopeType::eFor) + continue; + + const Token *forToken = scope.classDef; + // for ( initToken ; condToken ; stepToken ) + const Token* initToken = getInitTok(forToken); + if (!initToken) + continue; + const Token* condToken = getCondTok(forToken); + if (!condToken) + continue; + const Token* stepToken = getStepTok(forToken); + if (!stepToken) + continue; + + // 1. we expect that idx variable will be initialized with 0 + const Token* idxToken = initToken->astOperand1(); + const Token* initVal = initToken->astOperand2(); + if (!idxToken || !initVal || !initVal->hasKnownIntValue() || initVal->getKnownIntValue() != 0) + continue; + const auto idxVarId = idxToken->varId(); + if (0 == idxVarId) + continue; + + // 2. we expect that idx will be less of some variable + if (!condToken->isComparisonOp()) + continue; + + const auto& secondOp = condToken->str(); + const bool isLess = "<" == secondOp && + isConstExpression(condToken->astOperand2(), mSettings->library) && + condToken->astOperand1()->varId() == idxVarId; + const bool isMore = ">" == secondOp && + isConstExpression(condToken->astOperand1(), mSettings->library) && + condToken->astOperand2()->varId() == idxVarId; + + if (!(isLess || isMore)) + continue; + + // 3. we expect idx incrementing by 1 + const bool inc = stepToken->str() == "++" && stepToken->astOperand1() && stepToken->astOperand1()->varId() == idxVarId; + const bool plusOne = stepToken->isBinaryOp() && stepToken->str() == "+=" && + stepToken->astOperand1()->varId() == idxVarId && + stepToken->astOperand2()->str() == "1"; + if (!inc && !plusOne) + continue; + + // technically using void* here is not correct but some compilers could allow it + + const Token *tok = scope.bodyStart; + const std::string memcpyName = tok->isCpp() ? "std::memcpy" : "memcpy"; + // (reinterpret_cast(dest))[i] = (reinterpret_cast(src))[i]; + if (Token::Match(tok, "{ (| reinterpret_cast < uint8_t|int8_t|char|void * > ( %var% ) )| [ %varid% ] = " + "(| reinterpret_cast < const| uint8_t|int8_t|char|void * > ( %var% ) )| [ %varid% ] ; }", idxVarId)) { + useStandardLibraryError(tok->next(), memcpyName); + continue; + } + + // ((char*)dst)[i] = ((const char*)src)[i]; + if (Token::Match(tok, "{ ( ( uint8_t|int8_t|char|void * ) (| %var% ) )| [ %varid% ] = " + "( ( const| uint8_t|int8_t|char|void * ) (| %var% ) )| [ %varid% ] ; }", idxVarId)) { + useStandardLibraryError(tok->next(), memcpyName); + continue; + } + + + const static std::string memsetName = tok->isCpp() ? "std::memset" : "memset"; + // ((char*)dst)[i] = 0; + if (Token::Match(tok, "{ ( ( uint8_t|int8_t|char|void * ) (| %var% ) )| [ %varid% ] = %char%|%num% ; }", idxVarId)) { + useStandardLibraryError(tok->next(), memsetName); + continue; + } + + // ((char*)dst)[i] = (const char*)0; + if (Token::Match(tok, "{ ( ( uint8_t|int8_t|char|void * ) (| %var% ) )| [ %varid% ] = " + "( const| uint8_t|int8_t|char ) (| %char%|%num% )| ; }", idxVarId)) { + useStandardLibraryError(tok->next(), memsetName); + continue; + } + + // (reinterpret_cast(dest))[i] = static_cast(0); + if (Token::Match(tok, "{ (| reinterpret_cast < uint8_t|int8_t|char|void * > ( %var% ) )| [ %varid% ] = " + "(| static_cast < const| uint8_t|int8_t|char > ( %char%|%num% ) )| ; }", idxVarId)) { + useStandardLibraryError(tok->next(), memsetName); + continue; + } + + // (reinterpret_cast(dest))[i] = 0; + if (Token::Match(tok, "{ (| reinterpret_cast < uint8_t|int8_t|char|void * > ( %var% ) )| [ %varid% ] = " + "%char%|%num% ; }", idxVarId)) { + useStandardLibraryError(tok->next(), memsetName); + continue; + } + } +} + +void CheckFunctions::useStandardLibraryError(const Token *tok, const std::string& expected) +{ + reportError(tok, Severity::style, + "useStandardLibrary", + "Consider using " + expected + " instead of loop."); +} diff --git a/cppcheck-2.14.0/lib/checkfunctions.h b/cppcheck-2.14.0/lib/checkfunctions.h new file mode 100644 index 00000000..2519c5ca --- /dev/null +++ b/cppcheck-2.14.0/lib/checkfunctions.h @@ -0,0 +1,169 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checkfunctionsH +#define checkfunctionsH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "errortypes.h" +#include "library.h" +#include "settings.h" +#include "tokenize.h" + +#include +#include + +class Token; +class ErrorLogger; + +namespace ValueFlow { + class Value; +} // namespace ValueFlow + + +/// @addtogroup Checks +/// @{ + +/** + * @brief Check for bad function usage + */ + +class CPPCHECKLIB CheckFunctions : public Check { +public: + /** This constructor is used when registering the CheckFunctions */ + CheckFunctions() : Check(myName()) {} + +private: + /** This constructor is used when running checks. */ + CheckFunctions(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger) {} + + /** @brief Run checks against the normal token list */ + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + CheckFunctions checkFunctions(&tokenizer, &tokenizer.getSettings(), errorLogger); + + checkFunctions.checkIgnoredReturnValue(); + checkFunctions.checkMissingReturn(); // Missing "return" in exit path + + // --check-library : functions with nonmatching configuration + checkFunctions.checkLibraryMatchFunctions(); + + checkFunctions.checkProhibitedFunctions(); + checkFunctions.invalidFunctionUsage(); + checkFunctions.checkMathFunctions(); + checkFunctions.memsetZeroBytes(); + checkFunctions.memsetInvalid2ndParam(); + checkFunctions.returnLocalStdMove(); + checkFunctions.useStandardLibrary(); + } + + /** Check for functions that should not be used */ + void checkProhibitedFunctions(); + + /** + * @brief Invalid function usage (invalid input value / overlapping data) + * + * %Check that given function parameters are valid according to the standard + * - wrong radix given for strtol/strtoul + * - overlapping data when using sprintf/snprintf + * - wrong input value according to library + */ + void invalidFunctionUsage(); + + /** @brief %Check for ignored return values. */ + void checkIgnoredReturnValue(); + + /** @brief %Check for parameters given to math function that do not make sense*/ + void checkMathFunctions(); + + /** @brief %Check for filling zero bytes with memset() */ + void memsetZeroBytes(); + + /** @brief %Check for invalid 2nd parameter of memset() */ + void memsetInvalid2ndParam(); + + /** @brief %Check for copy elision by RVO|NRVO */ + void returnLocalStdMove(); + + void useStandardLibrary(); + + /** @brief --check-library: warn for unconfigured function calls */ + void checkLibraryMatchFunctions(); + + /** @brief %Check for missing "return" */ + void checkMissingReturn(); + + void invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr); + void invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr); + void invalidFunctionArgStrError(const Token *tok, const std::string &functionName, nonneg int argnr); + void ignoredReturnValueError(const Token* tok, const std::string& function); + void ignoredReturnErrorCode(const Token* tok, const std::string& function); + void mathfunctionCallWarning(const Token *tok, const nonneg int numParam = 1); + void mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp); + void memsetZeroBytesError(const Token *tok); + void memsetFloatError(const Token *tok, const std::string &var_value); + void memsetValueOutOfRangeError(const Token *tok, const std::string &value); + void missingReturnError(const Token *tok); + void copyElisionError(const Token *tok); + void useStandardLibraryError(const Token *tok, const std::string& expected); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckFunctions c(nullptr, settings, errorLogger); + + for (std::map::const_iterator i = settings->library.functionwarn.cbegin(); i != settings->library.functionwarn.cend(); ++i) { + c.reportError(nullptr, Severity::style, i->first+"Called", i->second.message); + } + + c.invalidFunctionArgError(nullptr, "func_name", 1, nullptr,"1:4"); + c.invalidFunctionArgBoolError(nullptr, "func_name", 1); + c.invalidFunctionArgStrError(nullptr, "func_name", 1); + c.ignoredReturnValueError(nullptr, "malloc"); + c.mathfunctionCallWarning(nullptr); + c.mathfunctionCallWarning(nullptr, "1 - erf(x)", "erfc(x)"); + c.memsetZeroBytesError(nullptr); + c.memsetFloatError(nullptr, "varname"); + c.memsetValueOutOfRangeError(nullptr, "varname"); + c.missingReturnError(nullptr); + c.copyElisionError(nullptr); + c.useStandardLibraryError(nullptr, "memcpy"); + } + + static std::string myName() { + return "Check function usage"; + } + + std::string classInfo() const override { + return "Check function usage:\n" + "- missing 'return' in non-void function\n" + "- return value of certain functions not used\n" + "- invalid input values for functions\n" + "- Warn if a function is called whose usage is discouraged\n" + "- memset() third argument is zero\n" + "- memset() with a value out of range as the 2nd parameter\n" + "- memset() with a float as the 2nd parameter\n" + "- copy elision optimization for returning value affected by std::move\n" + "- use memcpy()/memset() instead of for loop\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkfunctionsH diff --git a/cppcheck-2.14.0/lib/checkinternal.cpp b/cppcheck-2.14.0/lib/checkinternal.cpp new file mode 100644 index 00000000..b152389b --- /dev/null +++ b/cppcheck-2.14.0/lib/checkinternal.cpp @@ -0,0 +1,400 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifdef CHECK_INTERNAL + +#include "checkinternal.h" + +#include "astutils.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" + +#include +#include +#include + +// Register this check class (by creating a static instance of it). +// Disabled in release builds +namespace { + CheckInternal instance; +} + +void CheckInternal::checkTokenMatchPatterns() +{ + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch (")) + continue; + + const std::string& funcname = tok->strAt(2); + + // Get pattern string + const Token *patternTok = tok->tokAt(4)->nextArgument(); + if (!patternTok || patternTok->tokType() != Token::eString) + continue; + + const std::string pattern = patternTok->strValue(); + if (pattern.empty()) { + simplePatternError(tok, pattern, funcname); + continue; + } + + if (pattern.find("||") != std::string::npos || pattern.find(" | ") != std::string::npos || pattern[0] == '|' || (pattern[pattern.length() - 1] == '|' && pattern[pattern.length() - 2] == ' ')) + orInComplexPattern(tok, pattern, funcname); + + // Check for signs of complex patterns + if (pattern.find_first_of("[|") != std::string::npos) + continue; + if (pattern.find("!!") != std::string::npos) + continue; + + bool complex = false; + size_t index = pattern.find('%'); + while (index != std::string::npos) { + if (pattern.length() <= index + 2) { + complex = true; + break; + } + if (pattern[index + 1] == 'o' && pattern[index + 2] == 'r') // %or% or %oror% + index = pattern.find('%', index + 1); + else { + complex = true; + break; + } + index = pattern.find('%', index + 1); + } + if (!complex) + simplePatternError(tok, pattern, funcname); + } + } +} + +void CheckInternal::checkRedundantTokCheck() +{ + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (Token::Match(tok, "&& Token :: simpleMatch|Match|findsimplematch|findmatch (")) { + // in code like + // if (tok->previous() && Token::match(tok->previous(), "bla")) {} + // the first tok->previous() check is redundant + const Token *astOp1 = tok->astOperand1(); + const Token *astOp2 = getArguments(tok->tokAt(3))[0]; + if (Token::simpleMatch(astOp1, "&&")) { + astOp1 = astOp1->astOperand2(); + } + if (astOp1->expressionString() == astOp2->expressionString()) { + checkRedundantTokCheckError(astOp2); + } + // if (!tok || !Token::match(tok, "foo")) + } else if (Token::Match(tok, "%oror% ! Token :: simpleMatch|Match|findsimplematch|findmatch (")) { + const Token *negTok = tok->next()->astParent()->astOperand1(); + if (Token::simpleMatch(negTok, "||")) { + negTok = negTok->astOperand2(); + } + // the first tok condition is negated + if (Token::simpleMatch(negTok, "!")) { + const Token *astOp1 = negTok->astOperand1(); + const Token *astOp2 = getArguments(tok->tokAt(4))[0]; + + if (astOp1->expressionString() == astOp2->expressionString()) { + checkRedundantTokCheckError(astOp2); + } + } + } + } +} + + +void CheckInternal::checkRedundantTokCheckError(const Token* tok) +{ + reportError(tok, Severity::style, "redundantTokCheck", + "Unnecessary check of \"" + (tok? tok->expressionString(): emptyString) + "\", match-function already checks if it is null."); +} + +void CheckInternal::checkTokenSimpleMatchPatterns() +{ + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope* scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::simpleMatch(tok, "Token :: simpleMatch (") && !Token::simpleMatch(tok, "Token :: findsimplematch (")) + continue; + + const std::string& funcname = tok->strAt(2); + + // Get pattern string + const Token *patternTok = tok->tokAt(4)->nextArgument(); + if (!patternTok || patternTok->tokType() != Token::eString) + continue; + + const std::string pattern = patternTok->strValue(); + if (pattern.empty()) { + complexPatternError(tok, pattern, funcname); + continue; + } + + // Check for [xyz] usage - but exclude standalone square brackets + unsigned int char_count = 0; + for (const char c : pattern) { + 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 (const char c : pattern) { + if (c == ' ') { + char_count = 0; + } else if (c == '|') { + if (char_count > 0) { + complexPatternError(tok, pattern, funcname); + continue; + } + } else { + ++char_count; + } + } + + // Check for real errors + if (pattern.length() > 1) { + for (size_t j = 0; j < pattern.length() - 1; j++) { + if (pattern[j] == '%' && pattern[j + 1] != ' ') + complexPatternError(tok, pattern, funcname); + else if (pattern[j] == '!' && pattern[j + 1] == '!') + complexPatternError(tok, pattern, funcname); + } + } + } + } +} + +namespace { + const std::set knownPatterns = { + "%any%" + , "%assign%" + , "%bool%" + , "%char%" + , "%comp%" + , "%num%" + , "%op%" + , "%cop%" + , "%or%" + , "%oror%" + , "%str%" + , "%type%" + , "%name%" + , "%var%" + , "%varid%" + }; +} + +void CheckInternal::checkMissingPercentCharacter() +{ + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope* scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch (")) + continue; + + const std::string& funcname = tok->strAt(2); + + // Get pattern string + const Token *patternTok = tok->tokAt(4)->nextArgument(); + if (!patternTok || patternTok->tokType() != Token::eString) + continue; + + const std::string pattern = patternTok->strValue(); + + std::set::const_iterator knownPattern, knownPatternsEnd = knownPatterns.cend(); + for (knownPattern = knownPatterns.cbegin(); knownPattern != knownPatternsEnd; ++knownPattern) { + const std::string brokenPattern = knownPattern->substr(0, knownPattern->size() - 1); + + std::string::size_type pos = 0; + while ((pos = pattern.find(brokenPattern, pos)) != std::string::npos) { + // Check if it's the full pattern + if (pattern.find(*knownPattern, pos) != pos) { + // Known whitelist of substrings + if ((brokenPattern == "%var" && pattern.find("%varid%", pos) == pos) || + (brokenPattern == "%or" && pattern.find("%oror%", pos) == pos)) { + ++pos; + continue; + } + + missingPercentCharacterError(tok, pattern, funcname); + } + + ++pos; + } + } + } + } +} + +void CheckInternal::checkUnknownPattern() +{ + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope* scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::simpleMatch(tok, "Token :: Match (") && !Token::simpleMatch(tok, "Token :: findmatch (")) + continue; + + // Get pattern string + const Token *patternTok = tok->tokAt(4)->nextArgument(); + if (!patternTok || patternTok->tokType() != Token::eString) + continue; + + const std::string pattern = patternTok->strValue(); + bool inBrackets = false; + + for (std::string::size_type j = 0; j < pattern.length() - 1; j++) { + if (pattern[j] == '[' && (j == 0 || pattern[j - 1] == ' ')) + inBrackets = true; + else if (pattern[j] == ']') + inBrackets = false; + else if (pattern[j] == '%' && pattern[j + 1] != ' ' && pattern[j + 1] != '|' && !inBrackets) { + const std::string::size_type end = pattern.find('%', j + 1); + if (end != std::string::npos) { + const std::string s = pattern.substr(j, end - j + 1); + if (knownPatterns.find(s) == knownPatterns.end()) + unknownPatternError(tok, s); + } + } + } + } + } +} + +void CheckInternal::checkRedundantNextPrevious() +{ + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope* scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (tok->str() != ".") + continue; + tok = tok->next(); + + if (Token::Match(tok, "previous ( ) . next|tokAt|strAt|linkAt (") || Token::Match(tok, "next ( ) . previous|tokAt|strAt|linkAt (") || + (Token::simpleMatch(tok, "tokAt (") && Token::Match(tok->linkAt(1), ") . previous|next|tokAt|strAt|linkAt|str|link ("))) { + const std::string& func1 = tok->str(); + const std::string& func2 = tok->linkAt(1)->strAt(2); + + if ((func2 == "previous" || func2 == "next" || func2 == "str" || func2 == "link") && tok->linkAt(1)->strAt(4) != ")") + continue; + + redundantNextPreviousError(tok, func1, func2); + } else if (Token::Match(tok, "next|previous ( ) . next|previous ( ) . next|previous|linkAt|strAt|link|str (")) { + const std::string& func1 = tok->str(); + const std::string& func2 = tok->strAt(8); + + if ((func2 == "previous" || func2 == "next" || func2 == "str" || func2 == "link") && tok->strAt(10) != ")") + continue; + + redundantNextPreviousError(tok, func1, func2); + } + } + } +} + +void CheckInternal::checkExtraWhitespace() +{ + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope* scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::Match(tok, "Token :: simpleMatch|findsimplematch|Match|findmatch (")) + continue; + + const std::string& funcname = tok->strAt(2); + + // Get pattern string + const Token *patternTok = tok->tokAt(4)->nextArgument(); + if (!patternTok || patternTok->tokType() != Token::eString) + continue; + + const std::string pattern = patternTok->strValue(); + if (!pattern.empty() && (pattern[0] == ' ' || *pattern.crbegin() == ' ')) + extraWhitespaceError(tok, pattern, funcname); + + // two whitespaces or more + if (pattern.find(" ") != std::string::npos) + extraWhitespaceError(tok, pattern, funcname); + } + } +} + +void CheckInternal::multiComparePatternError(const Token* tok, const std::string& pattern, const std::string &funcname) +{ + reportError(tok, Severity::error, "multiComparePatternError", + "Bad multicompare pattern (a %cmd% must be first unless it is %or%,%op%,%cop%,%name%,%oror%) inside Token::" + funcname + "() call: \"" + pattern + "\"" + ); +} + +void CheckInternal::simplePatternError(const Token* tok, const std::string& pattern, const std::string &funcname) +{ + reportError(tok, Severity::warning, "simplePatternError", + "Found simple pattern inside Token::" + funcname + "() call: \"" + pattern + "\"" + ); +} + +void CheckInternal::complexPatternError(const Token* tok, const std::string& pattern, const std::string &funcname) +{ + reportError(tok, Severity::error, "complexPatternError", + "Found complex pattern inside Token::" + funcname + "() call: \"" + pattern + "\"" + ); +} + +void CheckInternal::missingPercentCharacterError(const Token* tok, const std::string& pattern, const std::string& funcname) +{ + reportError(tok, Severity::error, "missingPercentCharacter", + "Missing percent end character in Token::" + funcname + "() pattern: \"" + pattern + "\"" + ); +} + +void CheckInternal::unknownPatternError(const Token* tok, const std::string& pattern) +{ + reportError(tok, Severity::error, "unknownPattern", + "Unknown pattern used: \"" + pattern + "\""); +} + +void CheckInternal::redundantNextPreviousError(const Token* tok, const std::string& func1, const std::string& func2) +{ + reportError(tok, Severity::style, "redundantNextPrevious", + "Call to 'Token::" + func1 + "()' followed by 'Token::" + func2 + "()' can be simplified."); +} + +void CheckInternal::orInComplexPattern(const Token* tok, const std::string& pattern, const std::string &funcname) +{ + reportError(tok, Severity::error, "orInComplexPattern", + "Token::" + funcname + "() pattern \"" + pattern + "\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\"."); +} + +void CheckInternal::extraWhitespaceError(const Token* tok, const std::string& pattern, const std::string &funcname) +{ + reportError(tok, Severity::warning, "extraWhitespaceError", + "Found extra whitespace inside Token::" + funcname + "() call: \"" + pattern + "\"" + ); +} + +#endif // #ifdef CHECK_INTERNAL diff --git a/cppcheck-2.14.0/lib/checkinternal.h b/cppcheck-2.14.0/lib/checkinternal.h new file mode 100644 index 00000000..87ef2323 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkinternal.h @@ -0,0 +1,122 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checkinternalH +#define checkinternalH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "errortypes.h" +#include "settings.h" +#include "tokenize.h" + +#include + +class ErrorLogger; +class Token; + +/// @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()) {} + +private: + /** This constructor is used when running checks. */ + CheckInternal(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger) {} + + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + if (!tokenizer.getSettings().checks.isEnabled(Checks::internalCheck)) + return; + + CheckInternal checkInternal(&tokenizer, &tokenizer.getSettings(), errorLogger); + + checkInternal.checkTokenMatchPatterns(); + checkInternal.checkTokenSimpleMatchPatterns(); + checkInternal.checkMissingPercentCharacter(); + checkInternal.checkUnknownPattern(); + checkInternal.checkRedundantNextPrevious(); + checkInternal.checkExtraWhitespace(); + checkInternal.checkRedundantTokCheck(); + } + + /** @brief %Check if a simple pattern is used inside Token::Match or Token::findmatch */ + void checkTokenMatchPatterns(); + + /** @brief %Check if a complex pattern is used inside Token::simpleMatch or Token::findsimplematch */ + void checkTokenSimpleMatchPatterns(); + + /** @brief %Check for missing % end character in Token::Match pattern */ + void checkMissingPercentCharacter(); + + /** @brief %Check for unknown (invalid) complex patterns like "%typ%" */ + void checkUnknownPattern(); + + /** @brief %Check for inefficient usage of Token::next(), Token::previous() and Token::tokAt() */ + void checkRedundantNextPrevious(); + + /** @brief %Check if there is whitespace at the beginning or at the end of a pattern */ + void checkExtraWhitespace(); + + /** @brief %Check if there is a redundant check for none-nullness of parameter before Match functions, such as (tok && Token::Match(tok, "foo")) */ + void checkRedundantTokCheck(); + + void multiComparePatternError(const Token *tok, const std::string &pattern, const std::string &funcname); + void simplePatternError(const Token *tok, const std::string &pattern, const std::string &funcname); + void complexPatternError(const Token *tok, const std::string &pattern, const std::string &funcname); + void missingPercentCharacterError(const Token *tok, const std::string &pattern, const std::string &funcname); + void unknownPatternError(const Token* tok, const std::string& pattern); + void redundantNextPreviousError(const Token* tok, const std::string& func1, const std::string& func2); + void orInComplexPattern(const Token *tok, const std::string &pattern, const std::string &funcname); + void extraWhitespaceError(const Token *tok, const std::string &pattern, const std::string &funcname); + void checkRedundantTokCheckError(const Token *tok); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckInternal c(nullptr, settings, errorLogger); + c.multiComparePatternError(nullptr, ";|%type%", "Match"); + c.simplePatternError(nullptr, "class {", "Match"); + c.complexPatternError(nullptr, "%type% ( )", "Match"); + c.missingPercentCharacterError(nullptr, "%num", "Match"); + c.unknownPatternError(nullptr, "%typ"); + c.redundantNextPreviousError(nullptr, "previous", "next"); + c.orInComplexPattern(nullptr, "||", "Match"); + c.extraWhitespaceError(nullptr, "%str% ", "Match"); + c.checkRedundantTokCheckError(nullptr); + } + + static std::string myName() { + return "cppcheck internal API usage"; + } + + std::string classInfo() const override { + // Don't include these checks on the WIKI where people can read what + // checks there are. These checks are not intended for users. + return ""; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkinternalH diff --git a/cppcheck-2.14.0/lib/checkio.cpp b/cppcheck-2.14.0/lib/checkio.cpp new file mode 100644 index 00000000..6c26967e --- /dev/null +++ b/cppcheck-2.14.0/lib/checkio.cpp @@ -0,0 +1,2024 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "astutils.h" +#include "errortypes.h" +#include "library.h" +#include "mathlib.h" +#include "platform.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "utils.h" +#include "vfvalue.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//--------------------------------------------------------------------------- + +// Register CheckIO.. +namespace { + CheckIO instance; +} + +// CVE ID used: +static const CWE CWE119(119U); // Improper Restriction of Operations within the Bounds of a Memory Buffer +static const CWE CWE398(398U); // Indicator of Poor Code Quality +static const CWE CWE664(664U); // Improper Control of a Resource Through its Lifetime +static const CWE CWE685(685U); // Function Call With Incorrect Number of Arguments +static const CWE CWE686(686U); // Function Call With Incorrect Argument Type +static const CWE CWE687(687U); // Function Call With Incorrectly Specified Argument Value +static const CWE CWE704(704U); // Incorrect Type Conversion or Cast +static const CWE CWE910(910U); // Use of Expired File Descriptor + +//--------------------------------------------------------------------------- +// std::cout << std::cout; +//--------------------------------------------------------------------------- +void CheckIO::checkCoutCerrMisusage() +{ + if (mTokenizer->isC()) + return; + + logChecker("CheckIO::checkCoutCerrMisusage"); // c + + const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "std :: cout|cerr !!.") && tok->next()->astParent() && tok->next()->astParent()->astOperand1() == tok->next()) { + const Token* tok2 = tok->next(); + while (tok2->astParent() && tok2->astParent()->str() == "<<") { + tok2 = tok2->astParent(); + if (tok2->astOperand2() && Token::Match(tok2->astOperand2()->previous(), "std :: cout|cerr")) + coutCerrMisusageError(tok, tok2->astOperand2()->strAt(1)); + } + } + } + } +} + +void CheckIO::coutCerrMisusageError(const Token* tok, const std::string& streamName) +{ + reportError(tok, Severity::error, "coutCerrMisusage", "Invalid usage of output stream: '<< std::" + streamName + "'.", CWE398, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// 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 class 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 OpenMode::RW_MODE; + if (str.find('w') != std::string::npos || str.find('a') != std::string::npos) + return OpenMode::WRITE_MODE; + if (str.find('r') != std::string::npos) + return OpenMode::READ_MODE; + return OpenMode::UNKNOWN_OM; +} + +namespace { + struct Filepointer { + OpenMode mode; + nonneg int mode_indent{}; + enum class Operation {NONE, UNIMPORTANT, READ, WRITE, POSITIONING, OPEN, CLOSE, UNKNOWN_OP} lastOperation = Operation::NONE; + nonneg int op_indent{}; + enum class AppendMode { UNKNOWN_AM, APPEND, APPEND_EX }; + AppendMode append_mode = AppendMode::UNKNOWN_AM; + std::string filename; + explicit Filepointer(OpenMode mode_ = OpenMode::UNKNOWN_OM) + : mode(mode_) {} + }; + + const std::unordered_set whitelist = { "clearerr", "feof", "ferror", "fgetpos", "ftell", "setbuf", "setvbuf", "ungetc", "ungetwc" }; +} + +void CheckIO::checkFileUsage() +{ + const bool windows = mSettings->platform.isWindows(); + const bool printPortability = mSettings->severity.isEnabled(Severity::portability); + const bool printWarnings = mSettings->severity.isEnabled(Severity::warning); + + std::map filepointers; + + logChecker("CheckIO::checkFileUsage"); + + const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Variable* var : symbolDatabase->variableList()) { + if (!var || !var->declarationId() || var->isArray() || !Token::simpleMatch(var->typeStartToken(), "FILE *")) + continue; + + if (var->isLocal()) { + if (var->nameToken()->strAt(1) == "(") // initialize by calling "ctor" + filepointers.insert(std::make_pair(var->declarationId(), Filepointer(OpenMode::UNKNOWN_OM))); + else + filepointers.insert(std::make_pair(var->declarationId(), Filepointer(OpenMode::CLOSED))); + } else { + filepointers.insert(std::make_pair(var->declarationId(), Filepointer(OpenMode::UNKNOWN_OM))); + // TODO: If all fopen calls we find open the file in the same type, we can set Filepointer::mode + } + } + + for (const Scope * scope : symbolDatabase->functionScopes) { + int indent = 0; + for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "%name% (") && isUnevaluated(tok)) { + tok = tok->linkAt(1); + continue; + } + if (tok->str() == "{") + indent++; + else if (tok->str() == "}") { + indent--; + for (std::pair& filepointer : filepointers) { + if (indent < filepointer.second.mode_indent) { + filepointer.second.mode_indent = 0; + filepointer.second.mode = OpenMode::UNKNOWN_OM; + } + if (indent < filepointer.second.op_indent) { + filepointer.second.op_indent = 0; + filepointer.second.lastOperation = Filepointer::Operation::UNKNOWN_OP; + } + } + } else if (tok->str() == "return" || tok->str() == "continue" || tok->str() == "break" || mSettings->library.isnoreturn(tok)) { // Reset upon return, continue or break + for (std::pair& filepointer : filepointers) { + filepointer.second.mode_indent = 0; + filepointer.second.mode = OpenMode::UNKNOWN_OM; + filepointer.second.op_indent = 0; + filepointer.second.lastOperation = Filepointer::Operation::UNKNOWN_OP; + } + } else if (Token::Match(tok, "%var% =") && + (tok->strAt(2) != "fopen" && tok->strAt(2) != "freopen" && tok->strAt(2) != "tmpfile" && + (windows ? (tok->str() != "_wfopen" && tok->str() != "_wfreopen") : true))) { + const std::map::iterator i = filepointers.find(tok->varId()); + if (i != filepointers.end()) { + i->second.mode = OpenMode::UNKNOWN_OM; + i->second.lastOperation = Filepointer::Operation::UNKNOWN_OP; + } + } else if (Token::Match(tok, "%name% (") && tok->previous() && (!tok->previous()->isName() || Token::Match(tok->previous(), "return|throw"))) { + std::string mode; + const Token* fileTok = nullptr; + const Token* fileNameTok = nullptr; + Filepointer::Operation operation = Filepointer::Operation::NONE; + + if ((tok->str() == "fopen" || tok->str() == "freopen" || tok->str() == "tmpfile" || + (windows && (tok->str() == "_wfopen" || tok->str() == "_wfreopen"))) && + tok->strAt(-1) == "=") { + if (tok->str() != "tmpfile") { + const Token* modeTok = tok->tokAt(2)->nextArgument(); + if (modeTok && modeTok->tokType() == Token::eString) + mode = modeTok->strValue(); + } else + mode = "wb+"; + fileTok = tok->tokAt(-2); + operation = Filepointer::Operation::OPEN; + if (Token::Match(tok, "fopen ( %str% ,")) + fileNameTok = tok->tokAt(2); + } else if (windows && Token::Match(tok, "fopen_s|freopen_s|_wfopen_s|_wfreopen_s ( & %name%")) { + const Token* modeTok = tok->tokAt(2)->nextArgument()->nextArgument(); + if (modeTok && modeTok->tokType() == Token::eString) + mode = modeTok->strValue(); + fileTok = tok->tokAt(3); + operation = Filepointer::Operation::OPEN; + } else if ((tok->str() == "rewind" || tok->str() == "fseek" || tok->str() == "fsetpos" || tok->str() == "fflush") || + (windows && tok->str() == "_fseeki64")) { + fileTok = tok->tokAt(2); + if (printPortability && fileTok && tok->str() == "fflush") { + if (fileTok->str() == "stdin") + fflushOnInputStreamError(tok, fileTok->str()); + else { + const Filepointer& f = filepointers[fileTok->varId()]; + if (f.mode == OpenMode::READ_MODE) + fflushOnInputStreamError(tok, fileTok->str()); + } + } + operation = Filepointer::Operation::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::Operation::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::Operation::WRITE; + } else if (tok->str() == "fclose") { + fileTok = tok->tokAt(2); + operation = Filepointer::Operation::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::Operation::UNIMPORTANT; + } else if (!Token::Match(tok, "if|for|while|catch|switch") && !mSettings->library.isFunctionConst(tok->str(), true)) { + const Token* const end2 = tok->linkAt(1); + if (scope->functionOf && scope->functionOf->isClassOrStruct() && !scope->function->isStatic() && ((tok->strAt(-1) != "::" && tok->strAt(-1) != ".") || tok->strAt(-2) == "this")) { + if (!tok->function() || (tok->function()->nestedIn && tok->function()->nestedIn->isClassOrStruct())) { + for (std::pair& filepointer : filepointers) { + const Variable* var = symbolDatabase->getVariableFromVarId(filepointer.first); + if (!var || !(var->isLocal() || var->isGlobal() || var->isStatic())) { + filepointer.second.mode = OpenMode::UNKNOWN_OM; + filepointer.second.mode_indent = 0; + filepointer.second.op_indent = indent; + filepointer.second.lastOperation = Filepointer::Operation::UNKNOWN_OP; + } + } + continue; + } + } + for (const Token* tok2 = tok->tokAt(2); tok2 != end2; tok2 = tok2->next()) { + if (tok2->varId() && filepointers.find(tok2->varId()) != filepointers.end()) { + fileTok = tok2; + operation = Filepointer::Operation::UNKNOWN_OP; // Assume that repositioning was last operation and that the file is opened now + break; + } + } + } + + while (Token::Match(fileTok, "%name% .")) + fileTok = fileTok->tokAt(2); + + if (!fileTok || !fileTok->varId() || fileTok->strAt(1) == "[") + continue; + + if (filepointers.find(fileTok->varId()) == filepointers.end()) { // function call indicates: Its a File + filepointers.insert(std::make_pair(fileTok->varId(), Filepointer(OpenMode::UNKNOWN_OM))); + } + + Filepointer& f = filepointers[fileTok->varId()]; + + switch (operation) { + case Filepointer::Operation::OPEN: + if (fileNameTok) { + for (std::map::const_iterator it = filepointers.cbegin(); it != filepointers.cend(); ++it) { + const Filepointer &fptr = it->second; + if (fptr.filename == fileNameTok->str() && (fptr.mode == OpenMode::RW_MODE || fptr.mode == OpenMode::WRITE_MODE)) + incompatibleFileOpenError(tok, fileNameTok->str()); + } + + f.filename = fileNameTok->str(); + } + + f.mode = getMode(mode); + if (mode.find('a') != std::string::npos) { + if (f.mode == OpenMode::RW_MODE) + f.append_mode = Filepointer::AppendMode::APPEND_EX; + else + f.append_mode = Filepointer::AppendMode::APPEND; + } else + f.append_mode = Filepointer::AppendMode::UNKNOWN_AM; + f.mode_indent = indent; + break; + case Filepointer::Operation::POSITIONING: + if (f.mode == OpenMode::CLOSED) + useClosedFileError(tok); + else if (f.append_mode == Filepointer::AppendMode::APPEND && tok->str() != "fflush" && printWarnings) + seekOnAppendedFileError(tok); + break; + case Filepointer::Operation::READ: + if (f.mode == OpenMode::CLOSED) + useClosedFileError(tok); + else if (f.mode == OpenMode::WRITE_MODE) + readWriteOnlyFileError(tok); + else if (f.lastOperation == Filepointer::Operation::WRITE) + ioWithoutPositioningError(tok); + break; + case Filepointer::Operation::WRITE: + if (f.mode == OpenMode::CLOSED) + useClosedFileError(tok); + else if (f.mode == OpenMode::READ_MODE) + writeReadOnlyFileError(tok); + else if (f.lastOperation == Filepointer::Operation::READ) + ioWithoutPositioningError(tok); + break; + case Filepointer::Operation::CLOSE: + if (f.mode == OpenMode::CLOSED) + useClosedFileError(tok); + else + f.mode = OpenMode::CLOSED; + f.mode_indent = indent; + break; + case Filepointer::Operation::UNIMPORTANT: + if (f.mode == OpenMode::CLOSED) + useClosedFileError(tok); + break; + case Filepointer::Operation::UNKNOWN_OP: + f.mode = OpenMode::UNKNOWN_OM; + f.mode_indent = 0; + break; + default: + break; + } + if (operation != Filepointer::Operation::NONE && operation != Filepointer::Operation::UNIMPORTANT) { + f.op_indent = indent; + f.lastOperation = operation; + } + } + } + for (std::pair& filepointer : filepointers) { + filepointer.second.op_indent = 0; + filepointer.second.mode = OpenMode::UNKNOWN_OM; + filepointer.second.lastOperation = Filepointer::Operation::UNKNOWN_OP; + } + } +} + +void CheckIO::fflushOnInputStreamError(const Token *tok, const std::string &varname) +{ + reportError(tok, Severity::portability, + "fflushOnInputStream", "fflush() called on input stream '" + varname + "' may result in undefined behaviour on non-linux systems.", CWE398, Certainty::normal); +} + +void CheckIO::ioWithoutPositioningError(const Token *tok) +{ + reportError(tok, Severity::error, + "IOWithoutPositioning", "Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.", CWE664, Certainty::normal); +} + +void CheckIO::readWriteOnlyFileError(const Token *tok) +{ + reportError(tok, Severity::error, + "readWriteOnlyFile", "Read operation on a file that was opened only for writing.", CWE664, Certainty::normal); +} + +void CheckIO::writeReadOnlyFileError(const Token *tok) +{ + reportError(tok, Severity::error, + "writeReadOnlyFile", "Write operation on a file that was opened only for reading.", CWE664, Certainty::normal); +} + +void CheckIO::useClosedFileError(const Token *tok) +{ + reportError(tok, Severity::error, + "useClosedFile", "Used file that is not opened.", CWE910, Certainty::normal); +} + +void CheckIO::seekOnAppendedFileError(const Token *tok) +{ + reportError(tok, Severity::warning, + "seekOnAppendedFile", "Repositioning operation performed on a file opened in append mode has no effect.", CWE398, Certainty::normal); +} + +void CheckIO::incompatibleFileOpenError(const Token *tok, const std::string &filename) +{ + reportError(tok, Severity::warning, + "incompatibleFileOpen", "The file '" + filename + "' is opened for read and write access at the same time on different streams", CWE664, Certainty::normal); +} + + +//--------------------------------------------------------------------------- +// scanf without field width limits can crash with huge input data +//--------------------------------------------------------------------------- +void CheckIO::invalidScanf() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + const Token *formatToken = nullptr; + if (Token::Match(tok, "scanf|vscanf ( %str% ,")) + formatToken = tok->tokAt(2); + else if (Token::Match(tok, "sscanf|vsscanf|fscanf|vfscanf (")) { + const Token* nextArg = tok->tokAt(2)->nextArgument(); + if (nextArg && nextArg->tokType() == Token::eString) + formatToken = nextArg; + else + continue; + } else + continue; + + bool format = false; + + // scan the string backwards, so we do not need to keep states + const std::string &formatstr(formatToken->str()); + for (std::size_t i = 1; i < formatstr.length(); i++) { + if (formatstr[i] == '%') + format = !format; + + else if (!format) + continue; + + else if (std::isdigit(formatstr[i]) || formatstr[i] == '*') { + format = false; + } + + else if (std::isalpha((unsigned char)formatstr[i]) || formatstr[i] == '[') { + if (formatstr[i] == 's' || formatstr[i] == '[' || formatstr[i] == 'S' || (formatstr[i] == 'l' && formatstr[i+1] == 's')) // #3490 - field width limits are only necessary for string input + invalidScanfError(tok); + format = false; + } + } + } + } +} + +void CheckIO::invalidScanfError(const Token *tok) +{ + const std::string fname = (tok ? tok->str() : std::string("scanf")); + reportError(tok, Severity::warning, + "invalidscanf", fname + "() without field width limits can crash with huge input data.\n" + + fname + "() without field width limits can crash with huge input data. Add a field width " + "specifier to fix this problem.\n" + "\n" + "Sample program that can crash:\n" + "\n" + "#include \n" + "int main()\n" + "{\n" + " char c[5];\n" + " scanf(\"%s\", c);\n" + " return 0;\n" + "}\n" + "\n" + "Typing in 5 or more characters may make the program crash. The correct usage " + "here is 'scanf(\"%4s\", c);', as the maximum field width does not include the " + "terminating null byte.\n" + "Source: http://linux.die.net/man/3/scanf\n" + "Source: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/libkern/stdio/scanf.c", + CWE119, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// printf("%u", "xyz"); // Wrong argument type +// printf("%u%s", 1); // Too few arguments +// printf("", 1); // Too much arguments +//--------------------------------------------------------------------------- + +static bool findFormat(nonneg int arg, const Token *firstArg, + const Token *&formatStringTok, const Token *&formatArgTok) +{ + const Token* argTok = firstArg; + + for (int i = 0; i < arg && argTok; ++i) + argTok = argTok->nextArgument(); + + if (Token::Match(argTok, "%str% [,)]")) { + formatArgTok = argTok->nextArgument(); + formatStringTok = argTok; + return true; + } + if (Token::Match(argTok, "%var% [,)]") && + argTok->variable() && + Token::Match(argTok->variable()->typeStartToken(), "char|wchar_t") && + (argTok->variable()->isPointer() || + (argTok->variable()->dimensions().size() == 1 && + argTok->variable()->dimensionKnown(0) && + argTok->variable()->dimension(0) != 0))) { + formatArgTok = argTok->nextArgument(); + if (!argTok->values().empty()) { + const std::list::const_iterator value = std::find_if( + argTok->values().cbegin(), argTok->values().cend(), std::mem_fn(&ValueFlow::Value::isTokValue)); + if (value != argTok->values().cend() && value->isTokValue() && value->tokvalue && + value->tokvalue->tokType() == Token::eString) { + formatStringTok = value->tokvalue; + } + } + return true; + } + return false; +} + +// Utility function returning whether iToTest equals iTypename or iOptionalPrefix+iTypename +static inline bool typesMatch(const std::string& iToTest, const std::string& iTypename, const std::string& iOptionalPrefix = "std::") +{ + return (iToTest == iTypename) || (iToTest == iOptionalPrefix + iTypename); +} + +void CheckIO::checkWrongPrintfScanfArguments() +{ + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + const bool isWindows = mSettings->platform.isWindows(); + + logChecker("CheckIO::checkWrongPrintfScanfArguments"); + + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!tok->isName()) continue; + + const Token* argListTok = nullptr; // Points to first va_list argument + const Token* formatStringTok = nullptr; // Points to format string token + + bool scan = false; + bool scanf_s = false; + int formatStringArgNo = -1; + + if (tok->strAt(1) == "(" && mSettings->library.formatstr_function(tok)) { + formatStringArgNo = mSettings->library.formatstr_argno(tok); + scan = mSettings->library.formatstr_scan(tok); + scanf_s = mSettings->library.formatstr_secure(tok); + } + + if (formatStringArgNo >= 0) { + // formatstring found in library. Find format string and first argument belonging to format string. + if (!findFormat(formatStringArgNo, tok->tokAt(2), formatStringTok, argListTok)) + continue; + } else if (Token::simpleMatch(tok, "swprintf (")) { + if (Token::Match(tok->tokAt(2)->nextArgument(), "%str%")) { + // Find third parameter and format string + if (!findFormat(1, tok->tokAt(2), formatStringTok, argListTok)) + continue; + } else { + // Find fourth parameter and format string + if (!findFormat(2, tok->tokAt(2), formatStringTok, argListTok)) + continue; + } + } else if (isWindows && Token::Match(tok, "sprintf_s|swprintf_s (")) { + // template int sprintf_s(char (&buffer)[size], const char *format, ...); + if (findFormat(1, tok->tokAt(2), formatStringTok, argListTok)) { + if (!formatStringTok) + continue; + } + // int sprintf_s(char *buffer, size_t sizeOfBuffer, const char *format, ...); + else if (findFormat(2, tok->tokAt(2), formatStringTok, argListTok)) { + if (!formatStringTok) + continue; + } + } else if (isWindows && Token::Match(tok, "_snprintf_s|_snwprintf_s (")) { + // template int _snprintf_s(char (&buffer)[size], size_t count, const char *format, ...); + if (findFormat(2, tok->tokAt(2), formatStringTok, argListTok)) { + if (!formatStringTok) + continue; + } + // int _snprintf_s(char *buffer, size_t sizeOfBuffer, size_t count, const char *format, ...); + else if (findFormat(3, tok->tokAt(2), formatStringTok, argListTok)) { + if (!formatStringTok) + continue; + } + } else { + continue; + } + + if (!formatStringTok) + continue; + + checkFormatString(tok, formatStringTok, argListTok, scan, scanf_s); + } + } +} + +void CheckIO::checkFormatString(const Token * const tok, + const Token * const formatStringTok, + const Token * argListTok, + const bool scan, + const bool scanf_s) +{ + const bool isWindows = mSettings->platform.isWindows(); + const bool printWarning = mSettings->severity.isEnabled(Severity::warning); + const std::string &formatString = formatStringTok->str(); + + // Count format string parameters.. + int numFormat = 0; + int numSecure = 0; + bool percent = false; + const Token* argListTok2 = argListTok; + std::set parameterPositionsUsed; + for (std::string::const_iterator i = formatString.cbegin(); i != formatString.cend(); ++i) { + if (*i == '%') { + percent = !percent; + } else if (percent && *i == '[') { + while (i != formatString.cend()) { + if (*i == ']') { + numFormat++; + if (argListTok) + argListTok = argListTok->nextArgument(); + percent = false; + break; + } + ++i; + } + if (scanf_s) { + numSecure++; + if (argListTok) { + argListTok = argListTok->nextArgument(); + } + } + if (i == formatString.cend()) + break; + } else if (percent) { + percent = false; + + bool _continue = false; + bool skip = false; + std::string width; + int parameterPosition = 0; + bool hasParameterPosition = false; + while (i != formatString.cend() && *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 = strToInt(width); + hasParameterPosition = true; + width.clear(); + } + ++i; + } + auto bracketBeg = formatString.cend(); + if (i != formatString.cend() && *i == '[') { + bracketBeg = i; + while (i != formatString.cend()) { + if (*i == ']') + break; + + ++i; + } + if (scanf_s && !skip) { + numSecure++; + if (argListTok) { + argListTok = argListTok->nextArgument(); + } + } + } + if (i == formatString.cend()) + break; + if (_continue) + continue; + + if (scan || *i != 'm') { // %m is a non-standard extension that requires no parameter on print functions. + ++numFormat; + + // Handle parameter positions (POSIX extension) - Ticket #4900 + if (hasParameterPosition) { + if (parameterPositionsUsed.find(parameterPosition) == parameterPositionsUsed.end()) + parameterPositionsUsed.insert(parameterPosition); + else // Parameter already referenced, hence don't consider it a new format + --numFormat; + } + + // Perform type checks + ArgumentInfo argInfo(argListTok, mSettings, mTokenizer->isCPP()); + + if ((argInfo.typeToken && !argInfo.isLibraryType(mSettings)) || *i == ']') { + if (scan) { + std::string specifier; + bool done = false; + while (!done) { + switch (*i) { + case 's': + case ']': // charset + specifier += (*i == 's' || bracketBeg == formatString.end()) ? std::string{ 's' } : std::string{ bracketBeg, i + 1 }; + if (argInfo.variableInfo && argInfo.isKnownType() && argInfo.variableInfo->isArray() && (argInfo.variableInfo->dimensions().size() == 1) && argInfo.variableInfo->dimensions()[0].known) { + if (!width.empty()) { + const int numWidth = strToInt(width); + if (numWidth != (argInfo.variableInfo->dimension(0) - 1)) + invalidScanfFormatWidthError(tok, numFormat, numWidth, argInfo.variableInfo, specifier); + } + } + if (argListTok && argListTok->tokType() != Token::eString && argInfo.typeToken && + 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 && argInfo.typeToken) { + numSecure++; + if (argListTok) { + argListTok = argListTok->nextArgument(); + } + } + done = true; + break; + case 'c': + if (argInfo.variableInfo && argInfo.isKnownType() && argInfo.variableInfo->isArray() && (argInfo.variableInfo->dimensions().size() == 1) && argInfo.variableInfo->dimensions()[0].known) { + if (!width.empty()) { + const int numWidth = strToInt(width); + if (numWidth > argInfo.variableInfo->dimension(0)) + invalidScanfFormatWidthError(tok, numFormat, numWidth, argInfo.variableInfo, std::string(1, *i)); + } + } + if (scanf_s) { + numSecure++; + if (argListTok) { + argListTok = argListTok->nextArgument(); + } + } + done = true; + break; + case 'x': + case 'X': + case 'u': + case 'o': + specifier += *i; + if (argInfo.typeToken->tokType() == Token::eString) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + else if (argInfo.isKnownType()) { + if (!Token::Match(argInfo.typeToken, "char|short|int|long")) { + if (argInfo.typeToken->isStandardType() || !argInfo.element) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + } else if (!argInfo.typeToken->isUnsigned() || + !argInfo.isArrayOrPointer() || + argInfo.typeToken->strAt(-1) == "const") { + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + } else { + switch (specifier[0]) { + case 'h': + if (specifier[1] == 'h') { + if (argInfo.typeToken->str() != "char") + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + } else if (argInfo.typeToken->str() != "short") + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + break; + case 'l': + if (specifier[1] == 'l') { + if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || + typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || + typesMatch(argInfo.typeToken->originalName(), "uintmax_t")) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong()) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || + typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || + typesMatch(argInfo.typeToken->originalName(), "uintmax_t")) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + break; + case 'I': + if (specifier.find("I64") != std::string::npos) { + if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + } else if (specifier.find("I32") != std::string::npos) { + if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong()) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + } else if (!typesMatch(argInfo.typeToken->originalName(), "size_t")) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + break; + case 'j': + if (!typesMatch(argInfo.typeToken->originalName(), "uintmax_t")) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + break; + case 'z': + if (!typesMatch(argInfo.typeToken->originalName(), "size_t")) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + break; + case 't': + if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + break; + case 'L': + if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || + typesMatch(argInfo.typeToken->originalName(), "uintmax_t")) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + break; + default: + if (argInfo.typeToken->str() != "int") + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || + typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || + typesMatch(argInfo.typeToken->originalName(), "uintmax_t")) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, true); + break; + } + } + } + done = true; + break; + case 'n': + case 'd': + case 'i': + specifier += *i; + if (argInfo.typeToken->tokType() == Token::eString) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + else if (argInfo.isKnownType()) { + if (!Token::Match(argInfo.typeToken, "char|short|int|long")) { + if (argInfo.typeToken->isStandardType() || !argInfo.element) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + } else if (argInfo.typeToken->isUnsigned() || + !argInfo.isArrayOrPointer() || + argInfo.typeToken->strAt(-1) == "const") { + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + } else { + switch (specifier[0]) { + case 'h': + if (specifier[1] == 'h') { + if (argInfo.typeToken->str() != "char") + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + } else if (argInfo.typeToken->str() != "short") + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + break; + case 'l': + if (specifier[1] == 'l') { + if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || + typesMatch(argInfo.typeToken->originalName(), "intmax_t")) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong()) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || + typesMatch(argInfo.typeToken->originalName(), "intmax_t")) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + break; + case 'I': + if (specifier.find("I64") != std::string::npos) { + if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + } else if (specifier.find("I32") != std::string::npos) { + if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong()) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + } else if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + break; + case 'j': + if (!typesMatch(argInfo.typeToken->originalName(), "intmax_t")) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + break; + case 'z': + if (!(typesMatch(argInfo.typeToken->originalName(), "ssize_t") || + (isWindows && typesMatch(argInfo.typeToken->originalName(), "SSIZE_T")))) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + break; + case 't': + if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + break; + case 'L': + if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + break; + default: + if (argInfo.typeToken->str() != "int") + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || + argInfo.typeToken->originalName() == "intmax_t") + invalidScanfArgTypeError_int(tok, numFormat, specifier, &argInfo, false); + break; + } + } + } + done = true; + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + case 'a': + specifier += *i; + if (argInfo.typeToken->tokType() == Token::eString) + invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); + else if (argInfo.isKnownType()) { + if (!Token::Match(argInfo.typeToken, "float|double")) { + if (argInfo.typeToken->isStandardType()) + invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); + } else if (!argInfo.isArrayOrPointer() || + argInfo.typeToken->strAt(-1) == "const") { + invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); + } else { + switch (specifier[0]) { + case 'l': + if (argInfo.typeToken->str() != "double" || argInfo.typeToken->isLong()) + invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); + break; + case 'L': + if (argInfo.typeToken->str() != "double" || !argInfo.typeToken->isLong()) + invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); + break; + default: + if (argInfo.typeToken->str() != "float") + invalidScanfArgTypeError_float(tok, numFormat, specifier, &argInfo); + break; + } + } + } + done = true; + break; + case 'I': + if ((i+1 != formatString.cend() && *(i+1) == '6' && + i+2 != formatString.cend() && *(i+2) == '4') || + (i+1 != formatString.cend() && *(i+1) == '3' && + i+2 != formatString.cend() && *(i+2) == '2')) { + specifier += *i++; + specifier += *i++; + if ((i+1) != formatString.cend() && !isalpha(*(i+1))) { + specifier += *i; + invalidLengthModifierError(tok, numFormat, specifier); + done = true; + } else { + specifier += *i++; + } + } else { + if ((i+1) != formatString.cend() && !isalpha(*(i+1))) { + specifier += *i; + invalidLengthModifierError(tok, numFormat, specifier); + done = true; + } else { + specifier += *i++; + } + } + break; + case 'h': + case 'l': + if (i+1 != formatString.cend() && *(i+1) == *i) + specifier += *i++; + FALLTHROUGH; + case 'j': + case 'q': + case 't': + case 'z': + case 'L': + // Expect an alphabetical character after these specifiers + if ((i + 1) != formatString.end() && !isalpha(*(i+1))) { + specifier += *i; + invalidLengthModifierError(tok, numFormat, specifier); + done = true; + } else { + specifier += *i++; + } + break; + default: + done = true; + break; + } + } + } else if (printWarning) { + std::string specifier; + bool done = false; + while (!done) { + if (i == formatString.end()) { + break; + } + switch (*i) { + case 's': + if (argListTok->tokType() != Token::eString && + argInfo.isKnownType() && !argInfo.isArrayOrPointer()) { + if (!Token::Match(argInfo.typeToken, "char|wchar_t")) { + if (!argInfo.element) + invalidPrintfArgTypeError_s(tok, numFormat, &argInfo); + } + } + done = true; + break; + case 'n': + if ((argInfo.isKnownType() && (!argInfo.isArrayOrPointer() || argInfo.typeToken->strAt(-1) == "const")) || argListTok->tokType() == Token::eString) + invalidPrintfArgTypeError_n(tok, numFormat, &argInfo); + done = true; + break; + case 'c': + case 'x': + case 'X': + case 'o': + specifier += *i; + if (argInfo.typeToken->tokType() == Token::eString) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + else if (argInfo.isArrayOrPointer() && !argInfo.element) { + // use %p on pointers and arrays + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else if (argInfo.isKnownType()) { + if (!Token::Match(argInfo.typeToken, "bool|short|long|int|char|wchar_t")) { + if (!(!argInfo.isArrayOrPointer() && argInfo.element)) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else { + switch (specifier[0]) { + case 'h': + if (specifier[1] == 'h') { + if (!(argInfo.typeToken->str() == "char" && argInfo.typeToken->isUnsigned())) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else if (!(argInfo.typeToken->str() == "short" && argInfo.typeToken->isUnsigned())) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + break; + case 'l': + if (specifier[1] == 'l') { + if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || + argInfo.typeToken->originalName() == "uintmax_t") + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong()) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || + argInfo.typeToken->originalName() == "uintmax_t") + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + break; + case 'j': + if (argInfo.typeToken->originalName() != "uintmax_t") + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + break; + case 'z': + if (!typesMatch(argInfo.typeToken->originalName(), "size_t")) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + break; + case 't': + if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + break; + case 'I': + if (specifier.find("I64") != std::string::npos) { + if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else if (specifier.find("I32") != std::string::npos) { + if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong()) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else if (!(typesMatch(argInfo.typeToken->originalName(), "size_t") || + argInfo.typeToken->originalName() == "WPARAM" || + argInfo.typeToken->originalName() == "UINT_PTR" || + argInfo.typeToken->originalName() == "LONG_PTR" || + argInfo.typeToken->originalName() == "LPARAM" || + argInfo.typeToken->originalName() == "LRESULT")) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + break; + default: + if (!Token::Match(argInfo.typeToken, "bool|char|short|wchar_t|int")) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + break; + } + } + } + done = true; + break; + case 'd': + case 'i': + specifier += *i; + if (argInfo.typeToken->tokType() == Token::eString) { + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + } else if (argInfo.isArrayOrPointer() && !argInfo.element) { + // use %p on pointers and arrays + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + } else if (argInfo.isKnownType()) { + if (argInfo.typeToken->isUnsigned() && !Token::Match(argInfo.typeToken, "char|short")) { + if (!(!argInfo.isArrayOrPointer() && argInfo.element)) + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + } else if (!Token::Match(argInfo.typeToken, "bool|char|short|int|long")) { + if (!(!argInfo.isArrayOrPointer() && argInfo.element)) + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + } else { + switch (specifier[0]) { + case 'h': + if (specifier[1] == 'h') { + if (!(argInfo.typeToken->str() == "char" && !argInfo.typeToken->isUnsigned())) + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + } else if (!(argInfo.typeToken->str() == "short" && !argInfo.typeToken->isUnsigned())) + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + break; + case 'l': + if (specifier[1] == 'l') { + if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || + argInfo.typeToken->originalName() == "intmax_t") + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong()) + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || + argInfo.typeToken->originalName() == "intmax_t") + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + break; + case 'j': + if (argInfo.typeToken->originalName() != "intmax_t") + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + break; + case 't': + if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + break; + case 'I': + if (specifier.find("I64") != std::string::npos) { + if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + } else if (specifier.find("I32") != std::string::npos) { + if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong()) + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + } else if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + break; + case 'z': + if (!(typesMatch(argInfo.typeToken->originalName(), "ssize_t") || + (isWindows && typesMatch(argInfo.typeToken->originalName(), "SSIZE_T")))) + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + break; + case 'L': + if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + break; + default: + if (!Token::Match(argInfo.typeToken, "bool|char|short|int")) + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + else if (typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t") || + argInfo.typeToken->originalName() == "intmax_t") + invalidPrintfArgTypeError_sint(tok, numFormat, specifier, &argInfo); + break; + } + } + } + done = true; + break; + case 'u': + specifier += *i; + if (argInfo.typeToken->tokType() == Token::eString) { + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else if (argInfo.isArrayOrPointer() && !argInfo.element) { + // use %p on pointers and arrays + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else if (argInfo.isKnownType()) { + if (!argInfo.typeToken->isUnsigned() && !Token::Match(argInfo.typeToken, "bool|_Bool")) { + if (!(!argInfo.isArrayOrPointer() && argInfo.element)) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else if (!Token::Match(argInfo.typeToken, "bool|char|short|long|int")) { + if (!(!argInfo.isArrayOrPointer() && argInfo.element)) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else { + switch (specifier[0]) { + case 'h': + if (specifier[1] == 'h') { + if (!(argInfo.typeToken->str() == "char" && argInfo.typeToken->isUnsigned())) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else if (!(argInfo.typeToken->str() == "short" && argInfo.typeToken->isUnsigned())) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + break; + case 'l': + if (specifier[1] == 'l') { + if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || + argInfo.typeToken->originalName() == "uintmax_t") + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else if (argInfo.typeToken->str() != "long" || argInfo.typeToken->isLong()) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || + argInfo.typeToken->originalName() == "uintmax_t") + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + break; + case 'j': + if (argInfo.typeToken->originalName() != "uintmax_t") + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + break; + case 'z': + if (!typesMatch(argInfo.typeToken->originalName(), "size_t")) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + break; + case 't': + if (!typesMatch(argInfo.typeToken->originalName(), "ptrdiff_t")) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + break; + case 'I': + if (specifier.find("I64") != std::string::npos) { + if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else if (specifier.find("I32") != std::string::npos) { + if (argInfo.typeToken->str() != "int" || argInfo.typeToken->isLong()) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + } else if (!typesMatch(argInfo.typeToken->originalName(), "size_t")) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + break; + case 'L': + if (argInfo.typeToken->str() != "long" || !argInfo.typeToken->isLong()) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + break; + default: + if (!Token::Match(argInfo.typeToken, "bool|char|short|int")) + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + else if (typesMatch(argInfo.typeToken->originalName(), "size_t") || + argInfo.typeToken->originalName() == "intmax_t") + invalidPrintfArgTypeError_uint(tok, numFormat, specifier, &argInfo); + break; + } + } + } + done = true; + break; + case 'p': + if (argInfo.typeToken->tokType() == Token::eString) + ; // string literals are passed as pointers to literal start, okay + else if (argInfo.isKnownType() && !argInfo.isArrayOrPointer()) + invalidPrintfArgTypeError_p(tok, numFormat, &argInfo); + done = true; + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + specifier += *i; + if (argInfo.typeToken->tokType() == Token::eString) + invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); + else if (argInfo.isArrayOrPointer() && !argInfo.element) { + // use %p on pointers and arrays + invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); + } else if (argInfo.isKnownType()) { + if (!Token::Match(argInfo.typeToken, "float|double")) { + if (!(!argInfo.isArrayOrPointer() && argInfo.element)) + invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); + } else if ((specifier[0] == 'L' && (!argInfo.typeToken->isLong() || argInfo.typeToken->str() != "double")) || + (specifier[0] != 'L' && argInfo.typeToken->isLong())) + invalidPrintfArgTypeError_float(tok, numFormat, specifier, &argInfo); + } + done = true; + break; + case 'h': // Can be 'hh' (signed char or unsigned char) or 'h' (short int or unsigned short int) + case 'l': { // Can be 'll' (long long int or unsigned long long int) or 'l' (long int or unsigned long int) + // If the next character is the same (which makes 'hh' or 'll') then expect another alphabetical character + if ((i + 1) != formatString.end() && *(i + 1) == *i) { + if ((i + 2) != formatString.end() && !isalpha(*(i + 2))) { + std::string modifier; + modifier += *i; + modifier += *(i + 1); + invalidLengthModifierError(tok, numFormat, modifier); + done = true; + } else { + specifier = *i++; + specifier += *i++; + } + } else { + if ((i + 1) != formatString.end() && !isalpha(*(i + 1))) { + std::string modifier; + modifier += *i; + invalidLengthModifierError(tok, numFormat, modifier); + done = true; + } else { + specifier = *i++; + } + } + } + break; + case 'I': // Microsoft extension: I for size_t and ptrdiff_t, I32 for __int32, and I64 for __int64 + if ((*(i+1) == '3' && *(i+2) == '2') || + (*(i+1) == '6' && *(i+2) == '4')) { + specifier += *i++; + specifier += *i++; + } + FALLTHROUGH; + case 'j': // intmax_t or uintmax_t + case 'z': // size_t + case 't': // ptrdiff_t + case 'L': // long double + // Expect an alphabetical character after these specifiers + if ((i + 1) != formatString.end() && !isalpha(*(i+1))) { + specifier += *i; + invalidLengthModifierError(tok, numFormat, specifier); + done = true; + } else { + specifier += *i++; + } + break; + default: + done = true; + break; + } + } + } + } + + if (argListTok) + argListTok = argListTok->nextArgument(); // Find next argument + } + } + } + + // Count printf/scanf parameters.. + int numFunction = 0; + while (argListTok2) { + if (Token::Match(argListTok2, "%name% ...")) // bailout for parameter pack + return; + numFunction++; + argListTok2 = argListTok2->nextArgument(); // Find next argument + } + + if (printWarning) { + // Check that all parameter positions reference an actual parameter + for (const int i : parameterPositionsUsed) { + if ((i == 0) || (i > numFormat)) + wrongPrintfScanfPosixParameterPositionError(tok, tok->str(), i, numFormat); + } + } + + // Mismatching number of parameters => warning + if ((numFormat + numSecure) != numFunction) + wrongPrintfScanfArgumentsError(tok, tok->originalName().empty() ? tok->str() : tok->originalName(), numFormat + numSecure, numFunction); +} + +// We currently only support string literals, variables, and functions. +/// @todo add non-string literals, and generic expressions + +CheckIO::ArgumentInfo::ArgumentInfo(const Token * arg, const Settings *settings, bool _isCPP) + : isCPP(_isCPP) +{ + if (!arg) + return; + + // Use AST type info + // TODO: This is a bailout so that old code is used in simple cases. Remove the old code and always use the AST type. + if (!Token::Match(arg, "%str% ,|)") && !(arg->variable() && arg->variable()->isArray())) { + const Token *top = arg; + while (top->str() == "(" && !top->isCast()) + top = top->next(); + while (top->astParent() && top->astParent()->str() != "," && top->astParent() != arg->previous()) + top = top->astParent(); + const ValueType *valuetype = top->argumentType(); + if (valuetype && valuetype->type >= ValueType::Type::BOOL) { + typeToken = tempToken = new Token(top); + if (valuetype->pointer && valuetype->constness & 1) { + tempToken->str("const"); + tempToken->insertToken("a"); + tempToken = tempToken->next(); + } + if (valuetype->type == ValueType::BOOL) + tempToken->str("bool"); + else if (valuetype->type == ValueType::CHAR) + tempToken->str("char"); + else if (valuetype->type == ValueType::SHORT) + tempToken->str("short"); + else if (valuetype->type == ValueType::WCHAR_T) + tempToken->str("wchar_t"); + else if (valuetype->type == ValueType::INT) + tempToken->str("int"); + else if (valuetype->type == ValueType::LONG) + tempToken->str("long"); + else if (valuetype->type == ValueType::LONGLONG) { + tempToken->str("long"); + tempToken->isLong(true); + } else if (valuetype->type == ValueType::FLOAT) + tempToken->str("float"); + else if (valuetype->type == ValueType::DOUBLE) + tempToken->str("double"); + else if (valuetype->type == ValueType::LONGDOUBLE) { + tempToken->str("double"); + tempToken->isLong(true); + } + if (valuetype->isIntegral()) { + if (valuetype->sign == ValueType::Sign::UNSIGNED) + tempToken->isUnsigned(true); + else if (valuetype->sign == ValueType::Sign::SIGNED) + tempToken->isSigned(true); + } + if (!valuetype->originalTypeName.empty()) + tempToken->originalName(valuetype->originalTypeName); + for (int p = 0; p < valuetype->pointer; p++) + tempToken->insertToken("*"); + tempToken = const_cast(typeToken); + if (top->isBinaryOp() && valuetype->pointer == 1 && (valuetype->type == ValueType::CHAR || valuetype->type == ValueType::WCHAR_T)) + tempToken->tokType(Token::eString); + return; + } + } + + + if (arg->tokType() == Token::eString) { + typeToken = arg; + return; + } + if (arg->str() == "&" || arg->tokType() == Token::eVariable || + arg->tokType() == Token::eFunction || Token::Match(arg, "%type% ::") || + (Token::Match(arg, "static_cast|reinterpret_cast|const_cast <") && + Token::simpleMatch(arg->linkAt(1), "> (") && + Token::Match(arg->linkAt(1)->linkAt(1), ") ,|)"))) { + if (Token::Match(arg, "static_cast|reinterpret_cast|const_cast")) { + typeToken = arg->tokAt(2); + while (typeToken->str() == "const" || typeToken->str() == "extern") + typeToken = typeToken->next(); + return; + } + if (arg->str() == "&") { + address = true; + arg = arg->next(); + } + while (Token::Match(arg, "%type% ::")) + arg = arg->tokAt(2); + if (!arg || !(arg->tokType() == Token::eVariable || arg->tokType() == Token::eFunction)) + return; + const Token *varTok = nullptr; + const Token *tok1 = arg->next(); + for (; tok1; tok1 = tok1->next()) { + if (tok1->str() == "," || tok1->str() == ")") { + if (tok1->previous()->str() == "]") { + varTok = tok1->linkAt(-1)->previous(); + if (varTok->str() == ")" && varTok->link()->previous()->tokType() == Token::eFunction) { + const Function * function = varTok->link()->previous()->function(); + if (function && function->retType && function->retType->isEnumType()) { + if (function->retType->classScope->enumType) + typeToken = function->retType->classScope->enumType; + else { + tempToken = new Token(tok1); + tempToken->str("int"); + typeToken = tempToken; + } + } else if (function && function->retDef) { + typeToken = function->retDef; + while (typeToken->str() == "const" || typeToken->str() == "extern") + typeToken = typeToken->next(); + functionInfo = function; + element = true; + } + return; + } + } else if (tok1->previous()->str() == ")" && tok1->linkAt(-1)->previous()->tokType() == Token::eFunction) { + const Function * function = tok1->linkAt(-1)->previous()->function(); + if (function && function->retType && function->retType->isEnumType()) { + if (function->retType->classScope->enumType) + typeToken = function->retType->classScope->enumType; + else { + tempToken = new Token(tok1); + tempToken->str("int"); + typeToken = tempToken; + } + } else if (function && function->retDef) { + typeToken = function->retDef; + while (typeToken->str() == "const" || typeToken->str() == "extern") + typeToken = typeToken->next(); + functionInfo = function; + element = false; + } + return; + } else + varTok = tok1->previous(); + break; + } + if (tok1->str() == "(" || tok1->str() == "{" || tok1->str() == "[") + tok1 = tok1->link(); + else if (tok1->link() && tok1->str() == "<") + tok1 = tok1->link(); + + // check for some common well known functions + else if (isCPP && ((Token::Match(tok1->previous(), "%var% . size|empty|c_str ( ) [,)]") && isStdContainer(tok1->previous())) || + (Token::Match(tok1->previous(), "] . size|empty|c_str ( ) [,)]") && isStdContainer(tok1->previous()->link()->previous())))) { + tempToken = new Token(tok1); + if (tok1->next()->str() == "size") { + // size_t is platform dependent + if (settings->platform.sizeof_size_t == 8) { + tempToken->str("long"); + if (settings->platform.sizeof_long != 8) + tempToken->isLong(true); + } else if (settings->platform.sizeof_size_t == 4) { + if (settings->platform.sizeof_long == 4) { + tempToken->str("long"); + } else { + tempToken->str("int"); + } + } + + tempToken->originalName("size_t"); + tempToken->isUnsigned(true); + } else if (tok1->next()->str() == "empty") { + tempToken->str("bool"); + } else if (tok1->next()->str() == "c_str") { + tempToken->str("const"); + tempToken->insertToken("*"); + if (typeToken->strAt(2) == "string") + tempToken->insertToken("char"); + else + tempToken->insertToken("wchar_t"); + } + typeToken = tempToken; + return; + } + + // check for std::vector::at() and std::string::at() + else if (Token::Match(tok1->previous(), "%var% . at (") && + Token::Match(tok1->linkAt(2), ") [,)]")) { + varTok = tok1->previous(); + variableInfo = varTok->variable(); + + if (!variableInfo || !isStdVectorOrString()) { + variableInfo = nullptr; + typeToken = nullptr; + } + + return; + } else if (!(tok1->str() == "." || tok1->tokType() == Token::eVariable || tok1->tokType() == Token::eFunction)) + return; + } + + if (varTok) { + variableInfo = varTok->variable(); + element = tok1->previous()->str() == "]"; + + // look for std::vector operator [] and use template type as return type + if (variableInfo) { + if (element && isStdVectorOrString()) { // isStdVectorOrString sets type token if true + element = false; // not really an array element + } else if (variableInfo->isEnumType()) { + if (variableInfo->type() && variableInfo->type()->classScope && variableInfo->type()->classScope->enumType) + typeToken = variableInfo->type()->classScope->enumType; + else { + tempToken = new Token(tok1); + tempToken->str("int"); + typeToken = tempToken; + } + } else + typeToken = variableInfo->typeStartToken(); + } + + return; + } + } +} + +CheckIO::ArgumentInfo::~ArgumentInfo() +{ + if (tempToken) { + while (tempToken->next()) + tempToken->deleteNext(); + + delete tempToken; + } +} + +namespace { + const std::set stl_vector = { "array", "vector" }; + const std::set stl_string = { "string", "u16string", "u32string", "wstring" }; +} + +bool CheckIO::ArgumentInfo::isStdVectorOrString() +{ + if (!isCPP) + return false; + if (variableInfo->isStlType(stl_vector)) { + typeToken = variableInfo->typeStartToken()->tokAt(4); + _template = true; + return true; + } + if (variableInfo->isStlType(stl_string)) { + tempToken = new Token(variableInfo->typeStartToken()); + if (variableInfo->typeStartToken()->strAt(2) == "string") + tempToken->str("char"); + else + tempToken->str("wchar_t"); + typeToken = tempToken; + return true; + } + if (variableInfo->type() && !variableInfo->type()->derivedFrom.empty()) { + const std::vector& derivedFrom = variableInfo->type()->derivedFrom; + for (const Type::BaseInfo & i : derivedFrom) { + const Token* nameTok = i.nameTok; + if (Token::Match(nameTok, "std :: vector|array <")) { + typeToken = nameTok->tokAt(4); + _template = true; + return true; + } + if (Token::Match(nameTok, "std :: string|wstring")) { + tempToken = new Token(variableInfo->typeStartToken()); + if (nameTok->strAt(2) == "string") + tempToken->str("char"); + else + tempToken->str("wchar_t"); + typeToken = tempToken; + return true; + } + } + } else if (variableInfo->type()) { + const Scope * classScope = variableInfo->type()->classScope; + if (classScope) { + for (const Function &func : classScope->functionList) { + if (func.name() == "operator[]") { + if (Token::Match(func.retDef, "%type% &")) { + typeToken = func.retDef; + return true; + } + } + } + } + } + + return false; +} + +static const std::set stl_container = { + "array", "bitset", "deque", "forward_list", + "hash_map", "hash_multimap", "hash_set", + "list", "map", "multimap", "multiset", + "priority_queue", "queue", "set", "stack", + "unordered_map", "unordered_multimap", "unordered_multiset", "unordered_set", "vector" +}; + +bool CheckIO::ArgumentInfo::isStdContainer(const Token *tok) +{ + if (!isCPP) + return false; + if (tok && tok->variable()) { + const Variable* variable = tok->variable(); + if (variable->isStlType(stl_container)) { + typeToken = variable->typeStartToken()->tokAt(4); + return true; + } + if (variable->isStlType(stl_string)) { + typeToken = variable->typeStartToken(); + return true; + } + if (variable->type() && !variable->type()->derivedFrom.empty()) { + for (const Type::BaseInfo &baseInfo : variable->type()->derivedFrom) { + const Token* nameTok = baseInfo.nameTok; + if (Token::Match(nameTok, "std :: vector|array|bitset|deque|list|forward_list|map|multimap|multiset|priority_queue|queue|set|stack|hash_map|hash_multimap|hash_set|unordered_map|unordered_multimap|unordered_set|unordered_multiset <")) { + typeToken = nameTok->tokAt(4); + return true; + } + if (Token::Match(nameTok, "std :: string|wstring")) { + typeToken = nameTok; + return true; + } + } + } + } + + return false; +} + +bool CheckIO::ArgumentInfo::isArrayOrPointer() const +{ + if (address) + return true; + if (variableInfo && !_template) + return variableInfo->isArrayOrPointer(); + + const Token *tok = typeToken; + while (Token::Match(tok, "const|struct")) + tok = tok->next(); + return tok && tok->strAt(1) == "*"; +} + +bool CheckIO::ArgumentInfo::isComplexType() const +{ + if (variableInfo->type()) + return (true); + + const Token* varTypeTok = typeToken; + if (varTypeTok->str() == "std") + varTypeTok = varTypeTok->tokAt(2); + + return ((variableInfo->isStlStringType() || (varTypeTok->strAt(1) == "<" && varTypeTok->linkAt(1) && varTypeTok->linkAt(1)->strAt(1) != "::")) && !variableInfo->isArrayOrPointer()); +} + +bool CheckIO::ArgumentInfo::isKnownType() const +{ + if (variableInfo) + return (typeToken->isStandardType() || typeToken->next()->isStandardType() || isComplexType()); + if (functionInfo) + return (typeToken->isStandardType() || functionInfo->retType || Token::Match(typeToken, "std :: string|wstring")); + + return typeToken->isStandardType() || Token::Match(typeToken, "std :: string|wstring"); +} + +bool CheckIO::ArgumentInfo::isLibraryType(const Settings *settings) const +{ + return typeToken && typeToken->isStandardType() && settings->library.podtype(typeToken->str()); +} + +void CheckIO::wrongPrintfScanfArgumentsError(const Token* tok, + const std::string &functionName, + nonneg int numFormat, + nonneg int numFunction) +{ + const Severity severity = numFormat > numFunction ? Severity::error : Severity::warning; + if (severity != Severity::error && !mSettings->severity.isEnabled(Severity::warning)) + return; + + std::ostringstream errmsg; + errmsg << functionName + << " format string requires " + << numFormat + << " parameter" << (numFormat != 1 ? "s" : "") << " but " + << (numFormat > numFunction ? "only " : "") + << numFunction + << (numFunction != 1 ? " are" : " is") + << " given."; + + reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str(), CWE685, Certainty::normal); +} + +void CheckIO::wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName, + nonneg int index, nonneg int numFunction) +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + std::ostringstream errmsg; + errmsg << functionName << ": "; + if (index == 0) { + errmsg << "parameter positions start at 1, not 0"; + } else { + errmsg << "referencing parameter " << index << " while " << numFunction << " arguments given"; + } + reportError(tok, Severity::warning, "wrongPrintfScanfParameterPositionError", errmsg.str(), CWE685, Certainty::normal); +} + +void CheckIO::invalidScanfArgTypeError_s(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) +{ + const Severity severity = getSeverity(argInfo); + if (!mSettings->severity.isEnabled(severity)) + return; + std::ostringstream errmsg; + errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires a \'"; + if (specifier[0] == 's') + errmsg << "char"; + else if (specifier[0] == 'S') + errmsg << "wchar_t"; + errmsg << " *\' but the argument type is "; + argumentType(errmsg, argInfo); + errmsg << "."; + reportError(tok, severity, "invalidScanfArgType_s", errmsg.str(), CWE686, Certainty::normal); +} +void CheckIO::invalidScanfArgTypeError_int(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned) +{ + const Severity severity = getSeverity(argInfo); + if (!mSettings->severity.isEnabled(severity)) + return; + std::ostringstream errmsg; + errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'"; + if (specifier[0] == 'h') { + if (specifier[1] == 'h') + errmsg << (isUnsigned ? "unsigned " : "") << "char"; + else + errmsg << (isUnsigned ? "unsigned " : "") << "short"; + } else if (specifier[0] == 'l') { + if (specifier[1] == 'l') + errmsg << (isUnsigned ? "unsigned " : "") << "long long"; + else + errmsg << (isUnsigned ? "unsigned " : "") << "long"; + } else if (specifier.find("I32") != std::string::npos) { + errmsg << (isUnsigned ? "unsigned " : "") << "__int32"; + } else if (specifier.find("I64") != std::string::npos) { + errmsg << (isUnsigned ? "unsigned " : "") << "__int64"; + } else if (specifier[0] == 'I') { + errmsg << (isUnsigned ? "size_t" : "ptrdiff_t"); + } else if (specifier[0] == 'j') { + if (isUnsigned) + errmsg << "uintmax_t"; + else + errmsg << "intmax_t"; + } else if (specifier[0] == 'z') { + if (specifier[1] == 'd' || specifier[1] == 'i') + errmsg << "ssize_t"; + else + errmsg << "size_t"; + } else if (specifier[0] == 't') { + errmsg << (isUnsigned ? "unsigned " : "") << "ptrdiff_t"; + } else if (specifier[0] == 'L') { + errmsg << (isUnsigned ? "unsigned " : "") << "long long"; + } else { + errmsg << (isUnsigned ? "unsigned " : "") << "int"; + } + errmsg << " *\' but the argument type is "; + argumentType(errmsg, argInfo); + errmsg << "."; + reportError(tok, severity, "invalidScanfArgType_int", errmsg.str(), CWE686, Certainty::normal); +} +void CheckIO::invalidScanfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) +{ + const Severity severity = getSeverity(argInfo); + if (!mSettings->severity.isEnabled(severity)) + return; + std::ostringstream errmsg; + errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'"; + if (specifier[0] == 'l' && specifier[1] != 'l') + errmsg << "double"; + else if (specifier[0] == 'L') + errmsg << "long double"; + else + errmsg << "float"; + errmsg << " *\' but the argument type is "; + argumentType(errmsg, argInfo); + errmsg << "."; + reportError(tok, severity, "invalidScanfArgType_float", errmsg.str(), CWE686, Certainty::normal); +} + +void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo) +{ + const Severity severity = getSeverity(argInfo); + if (!mSettings->severity.isEnabled(severity)) + return; + std::ostringstream errmsg; + errmsg << "%s in format string (no. " << numFormat << ") requires \'char *\' but the argument type is "; + argumentType(errmsg, argInfo); + errmsg << "."; + reportError(tok, severity, "invalidPrintfArgType_s", errmsg.str(), CWE686, Certainty::normal); +} +void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo) +{ + const Severity severity = getSeverity(argInfo); + if (!mSettings->severity.isEnabled(severity)) + return; + std::ostringstream errmsg; + errmsg << "%n in format string (no. " << numFormat << ") requires \'int *\' but the argument type is "; + argumentType(errmsg, argInfo); + errmsg << "."; + reportError(tok, severity, "invalidPrintfArgType_n", errmsg.str(), CWE686, Certainty::normal); +} +void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo) +{ + const Severity severity = getSeverity(argInfo); + if (!mSettings->severity.isEnabled(severity)) + return; + std::ostringstream errmsg; + errmsg << "%p in format string (no. " << numFormat << ") requires an address but the argument type is "; + argumentType(errmsg, argInfo); + errmsg << "."; + reportError(tok, severity, "invalidPrintfArgType_p", errmsg.str(), CWE686, Certainty::normal); +} +static void printfFormatType(std::ostream& os, const std::string& specifier, bool isUnsigned) +{ + os << "\'"; + if (specifier[0] == 'l') { + if (specifier[1] == 'l') + os << (isUnsigned ? "unsigned " : "") << "long long"; + else + os << (isUnsigned ? "unsigned " : "") << "long"; + } else if (specifier[0] == 'h') { + if (specifier[1] == 'h') + os << (isUnsigned ? "unsigned " : "") << "char"; + else + os << (isUnsigned ? "unsigned " : "") << "short"; + } else if (specifier.find("I32") != std::string::npos) { + os << (isUnsigned ? "unsigned " : "") << "__int32"; + } else if (specifier.find("I64") != std::string::npos) { + os << (isUnsigned ? "unsigned " : "") << "__int64"; + } else if (specifier[0] == 'I') { + os << (isUnsigned ? "size_t" : "ptrdiff_t"); + } else if (specifier[0] == 'j') { + if (isUnsigned) + os << "uintmax_t"; + else + os << "intmax_t"; + } else if (specifier[0] == 'z') { + if (specifier[1] == 'd' || specifier[1] == 'i') + os << "ssize_t"; + else + os << "size_t"; + } else if (specifier[0] == 't') { + os << (isUnsigned ? "unsigned " : "") << "ptrdiff_t"; + } else if (specifier[0] == 'L') { + os << (isUnsigned ? "unsigned " : "") << "long long"; + } else { + os << (isUnsigned ? "unsigned " : "") << "int"; + } + os << "\'"; +} + +void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) +{ + const Severity severity = getSeverity(argInfo); + if (!mSettings->severity.isEnabled(severity)) + return; + std::ostringstream errmsg; + errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires "; + printfFormatType(errmsg, specifier, true); + errmsg << " but the argument type is "; + argumentType(errmsg, argInfo); + errmsg << "."; + reportError(tok, severity, "invalidPrintfArgType_uint", errmsg.str(), CWE686, Certainty::normal); +} + +void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) +{ + const Severity severity = getSeverity(argInfo); + if (!mSettings->severity.isEnabled(severity)) + return; + std::ostringstream errmsg; + errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires "; + printfFormatType(errmsg, specifier, false); + errmsg << " but the argument type is "; + argumentType(errmsg, argInfo); + errmsg << "."; + reportError(tok, severity, "invalidPrintfArgType_sint", errmsg.str(), CWE686, Certainty::normal); +} +void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) +{ + const Severity severity = getSeverity(argInfo); + if (!mSettings->severity.isEnabled(severity)) + return; + std::ostringstream errmsg; + errmsg << "%" << specifier << " in format string (no. " << numFormat << ") requires \'"; + if (specifier[0] == 'L') + errmsg << "long "; + errmsg << "double\' but the argument type is "; + argumentType(errmsg, argInfo); + errmsg << "."; + reportError(tok, severity, "invalidPrintfArgType_float", errmsg.str(), CWE686, Certainty::normal); +} + +Severity CheckIO::getSeverity(const CheckIO::ArgumentInfo *argInfo) +{ + return (argInfo && argInfo->typeToken && !argInfo->typeToken->originalName().empty()) ? Severity::portability : Severity::warning; +} + +void CheckIO::argumentType(std::ostream& os, const ArgumentInfo * argInfo) +{ + if (argInfo) { + os << "\'"; + const Token *type = argInfo->typeToken; + if (type->tokType() == Token::eString) { + if (type->isLong()) + os << "const wchar_t *"; + else + os << "const char *"; + } else { + if (type->originalName().empty()) { + if (type->strAt(-1) == "const") + os << "const "; + while (Token::Match(type, "const|struct")) { + os << type->str() << " "; + type = type->next(); + } + while (Token::Match(type, "%any% ::")) { + os << type->str() << "::"; + type = type->tokAt(2); + } + os << type->stringify(false, true, false); + if (type->strAt(1) == "*" && !argInfo->element) + os << " *"; + else if (argInfo->variableInfo && !argInfo->element && argInfo->variableInfo->isArray()) + os << " *"; + else if (type->strAt(1) == "*" && argInfo->variableInfo && argInfo->element && argInfo->variableInfo->isArray()) + os << " *"; + if (argInfo->address) + os << " *"; + } else { + if (type->isUnsigned()) { + if (type->originalName() == "__int64" || type->originalName() == "__int32" || type->originalName() == "ptrdiff_t") + os << "unsigned "; + } + os << type->originalName(); + if (type->strAt(1) == "*" || argInfo->address) + os << " *"; + os << " {aka " << type->stringify(false, true, false); + if (type->strAt(1) == "*" || argInfo->address) + os << " *"; + os << "}"; + } + } + os << "\'"; + } else + os << "Unknown"; +} + +void CheckIO::invalidLengthModifierError(const Token* tok, nonneg int numFormat, const std::string& modifier) +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + std::ostringstream errmsg; + errmsg << "'" << modifier << "' in format string (no. " << numFormat << ") is a length modifier and cannot be used without a conversion specifier."; + reportError(tok, Severity::warning, "invalidLengthModifierError", errmsg.str(), CWE704, Certainty::normal); +} + +void CheckIO::invalidScanfFormatWidthError(const Token* tok, nonneg int numFormat, int width, const Variable *var, const std::string& specifier) +{ + MathLib::bigint arrlen = 0; + std::string varname; + + if (var) { + arrlen = var->dimension(0); + varname = var->name(); + } + + std::ostringstream errmsg; + if (arrlen > width) { + if (tok != nullptr && (!mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning))) + return; + errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is smaller than destination buffer" + << " '" << varname << "[" << arrlen << "]'."; + reportError(tok, Severity::warning, "invalidScanfFormatWidth_smaller", errmsg.str(), CWE(0U), Certainty::inconclusive); + } else { + errmsg << "Width " << width << " given in format string (no. " << numFormat << ") is larger than destination buffer '" + << varname << "[" << arrlen << "]', use %" << (specifier == "c" ? arrlen : (arrlen - 1)) << specifier << " to prevent overflowing it."; + reportError(tok, Severity::error, "invalidScanfFormatWidth", errmsg.str(), CWE687, Certainty::normal); + } +} diff --git a/cppcheck-2.14.0/lib/checkio.h b/cppcheck-2.14.0/lib/checkio.h new file mode 100644 index 00000000..e3f1dd6f --- /dev/null +++ b/cppcheck-2.14.0/lib/checkio.h @@ -0,0 +1,184 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef checkioH +#define checkioH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "tokenize.h" + +#include +#include + +class Function; +class Settings; +class Token; +class Variable; +class ErrorLogger; +enum class Severity; + +/// @addtogroup Checks +/// @{ + +/** @brief %Check input output operations. */ +class CPPCHECKLIB CheckIO : public Check { + friend class TestIO; + +public: + /** @brief This constructor is used when registering CheckIO */ + CheckIO() : Check(myName()) {} + +private: + /** @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, ErrorLogger *errorLogger) override { + CheckIO checkIO(&tokenizer, &tokenizer.getSettings(), errorLogger); + + checkIO.checkWrongPrintfScanfArguments(); + checkIO.checkCoutCerrMisusage(); + checkIO.checkFileUsage(); + checkIO.invalidScanf(); + } + + /** @brief %Check for missusage of std::cout */ + void checkCoutCerrMisusage(); + + /** @brief %Check usage of files*/ + void checkFileUsage(); + + /** @brief scanf can crash if width specifiers are not used */ + void invalidScanf(); + + /** @brief %Checks type and number of arguments given to functions like printf or scanf*/ + void checkWrongPrintfScanfArguments(); + + class ArgumentInfo { + public: + ArgumentInfo(const Token *arg, const Settings *settings, bool _isCPP); + ~ArgumentInfo(); + + ArgumentInfo(const ArgumentInfo &) = delete; + ArgumentInfo& operator= (const ArgumentInfo &) = delete; + + bool isArrayOrPointer() const; + bool isComplexType() const; + bool isKnownType() const; + bool isStdVectorOrString(); + bool isStdContainer(const Token *tok); + bool isLibraryType(const Settings *settings) const; + + const Variable* variableInfo{}; + const Token* typeToken{}; + const Function* functionInfo{}; + Token* tempToken{}; + bool element{}; + bool _template{}; + bool address{}; + bool isCPP{}; + }; + + void checkFormatString(const Token * const tok, + const Token * const formatStringTok, + const Token * argListTok, + const bool scan, + const bool scanf_s); + + // Reporting errors.. + void coutCerrMisusageError(const Token* tok, const std::string& streamName); + void fflushOnInputStreamError(const Token *tok, const std::string &varname); + void ioWithoutPositioningError(const Token *tok); + void readWriteOnlyFileError(const Token *tok); + void writeReadOnlyFileError(const Token *tok); + void useClosedFileError(const Token *tok); + void seekOnAppendedFileError(const Token *tok); + void incompatibleFileOpenError(const Token *tok, const std::string &filename); + void invalidScanfError(const Token *tok); + void wrongPrintfScanfArgumentsError(const Token* tok, + const std::string &functionName, + nonneg int numFormat, + nonneg int numFunction); + void wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName, + nonneg int index, nonneg int numFunction); + void invalidScanfArgTypeError_s(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); + void invalidScanfArgTypeError_int(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned); + void invalidScanfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); + void invalidPrintfArgTypeError_s(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo); + void invalidPrintfArgTypeError_n(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo); + void invalidPrintfArgTypeError_p(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo); + void invalidPrintfArgTypeError_uint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); + void invalidPrintfArgTypeError_sint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); + void invalidPrintfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); + void invalidLengthModifierError(const Token* tok, nonneg int numFormat, const std::string& modifier); + void invalidScanfFormatWidthError(const Token* tok, nonneg int numFormat, int width, const Variable *var, const std::string& specifier); + static void argumentType(std::ostream & os, const ArgumentInfo * argInfo); + static Severity getSeverity(const ArgumentInfo *argInfo); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckIO c(nullptr, settings, errorLogger); + c.coutCerrMisusageError(nullptr, "cout"); + c.fflushOnInputStreamError(nullptr, "stdin"); + c.ioWithoutPositioningError(nullptr); + c.readWriteOnlyFileError(nullptr); + c.writeReadOnlyFileError(nullptr); + c.useClosedFileError(nullptr); + c.seekOnAppendedFileError(nullptr); + c.incompatibleFileOpenError(nullptr, "tmp"); + c.invalidScanfError(nullptr); + c.wrongPrintfScanfArgumentsError(nullptr, "printf",3,2); + c.invalidScanfArgTypeError_s(nullptr, 1, "s", nullptr); + c.invalidScanfArgTypeError_int(nullptr, 1, "d", nullptr, false); + c.invalidScanfArgTypeError_float(nullptr, 1, "f", nullptr); + c.invalidPrintfArgTypeError_s(nullptr, 1, nullptr); + c.invalidPrintfArgTypeError_n(nullptr, 1, nullptr); + c.invalidPrintfArgTypeError_p(nullptr, 1, nullptr); + c.invalidPrintfArgTypeError_uint(nullptr, 1, "u", nullptr); + c.invalidPrintfArgTypeError_sint(nullptr, 1, "i", nullptr); + c.invalidPrintfArgTypeError_float(nullptr, 1, "f", nullptr); + c.invalidLengthModifierError(nullptr, 1, "I"); + c.invalidScanfFormatWidthError(nullptr, 10, 5, nullptr, "s"); + c.invalidScanfFormatWidthError(nullptr, 99, -1, nullptr, "s"); + c.wrongPrintfScanfPosixParameterPositionError(nullptr, "printf", 2, 1); + } + + static std::string myName() { + return "IO using format string"; + } + + std::string classInfo() const override { + return "Check format string input/output operations.\n" + "- Bad usage of the function 'sprintf' (overlapping data)\n" + "- Missing or wrong width specifiers in 'scanf' format string\n" + "- Use a file that has been closed\n" + "- File input/output without positioning results in undefined behaviour\n" + "- Read to a file that has only been opened for writing (or vice versa)\n" + "- Repositioning operation on a file opened in append mode\n" + "- The same file can't be open for read and write at the same time on different streams\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 diff --git a/cppcheck-2.14.0/lib/checkleakautovar.cpp b/cppcheck-2.14.0/lib/checkleakautovar.cpp new file mode 100644 index 00000000..4d37446a --- /dev/null +++ b/cppcheck-2.14.0/lib/checkleakautovar.cpp @@ -0,0 +1,1215 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +// Leaks when using auto variables +//--------------------------------------------------------------------------- + +#include "checkleakautovar.h" + +#include "astutils.h" +#include "checkmemoryleak.h" // <- CheckMemoryLeak::memoryLeak +#include "checknullpointer.h" // <- CheckNullPointer::isPointerDeRef +#include "mathlib.h" +#include "platform.h" +#include "settings.h" +#include "errortypes.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "tokenlist.h" +#include "utils.h" +#include "vfvalue.h" + +#include +#include +#include +#include +#include +#include + +//--------------------------------------------------------------------------- + +// Register this check class (by creating a static instance of it) +namespace { + CheckLeakAutoVar instance; +} + +static const CWE CWE672(672U); +static const CWE CWE415(415U); + +// Hardcoded allocation types (not from library) +static constexpr int NEW_ARRAY = -2; +static constexpr int NEW = -1; + +static const std::array, 4> alloc_failed_conds {{{"==", "0"}, {"<", "0"}, {"==", "-1"}, {"<=", "-1"}}}; +static const std::array, 4> alloc_success_conds {{{"!=", "0"}, {">", "0"}, {"!=", "-1"}, {">=", "0"}}}; + +static bool isAutoDeallocType(const Type* type) { + if (!type || !type->classScope) + return true; + if (type->classScope->numConstructors > 0) + return true; + const std::list& varlist = type->classScope->varlist; + if (std::any_of(varlist.begin(), varlist.end(), [](const Variable& v) { + return !v.valueType() || (!v.valueType()->isPrimitive() && !v.valueType()->container); + })) + return true; + if (std::none_of(type->derivedFrom.cbegin(), type->derivedFrom.cend(), [](const Type::BaseInfo& bi) { + return isAutoDeallocType(bi.type); + })) + return false; + return true; +} + +/** + * @brief Is variable type some class with automatic deallocation? + * @param var variable token + * @return true unless it can be seen there is no automatic deallocation + */ +static bool isAutoDealloc(const Variable *var) +{ + if (var->valueType() && var->valueType()->type != ValueType::Type::RECORD && var->valueType()->type != ValueType::Type::UNKNOWN_TYPE) + return false; + + // return false if the type is a simple record type without side effects + // a type that has no side effects (no constructors and no members with constructors) + /** @todo false negative: check constructors for side effects */ + return isAutoDeallocType(var->type()); +} + +template +static bool isVarTokComparison(const Token * tok, const Token ** vartok, + const std::array, N>& ops) +{ + return std::any_of(ops.cbegin(), ops.cend(), [&](const std::pair& op) { + return astIsVariableComparison(tok, op.first, op.second, vartok); + }); +} + +//--------------------------------------------------------------------------- + +void VarInfo::print() +{ + std::cout << "size=" << alloctype.size() << std::endl; + for (std::map::const_iterator it = alloctype.cbegin(); it != alloctype.cend(); ++it) { + std::string strusage; + const auto use = possibleUsage.find(it->first); + if (use != possibleUsage.end()) + strusage = use->second.first->str(); + + std::string status; + switch (it->second.status) { + case OWNED: + status = "owned"; + break; + case DEALLOC: + status = "dealloc"; + break; + case ALLOC: + status = "alloc"; + break; + case NOALLOC: + status = "noalloc"; + break; + case REALLOC: + status = "realloc"; + break; + default: + status = "?"; + break; + } + + std::cout << "status=" << status << " " + << "alloctype='" << it->second.type << "' " + << "possibleUsage='" << strusage << "' " + << "conditionalAlloc=" << (conditionalAlloc.find(it->first) != conditionalAlloc.end() ? "yes" : "no") << " " + << "referenced=" << (referenced.find(it->first) != referenced.end() ? "yes" : "no") << " " + << "reallocedFrom=" << it->second.reallocedFromType + << std::endl; + } +} + +void VarInfo::possibleUsageAll(const std::pair& functionUsage) +{ + possibleUsage.clear(); + for (std::map::const_iterator it = alloctype.cbegin(); it != alloctype.cend(); ++it) + possibleUsage[it->first] = functionUsage; +} + + +void CheckLeakAutoVar::leakError(const Token *tok, const std::string &varname, int type) const +{ + const CheckMemoryLeak checkmemleak(mTokenizer, mErrorLogger, mSettings); + if (Library::isresource(type)) + checkmemleak.resourceLeakError(tok, varname); + else + checkmemleak.memleakError(tok, varname); +} + +void CheckLeakAutoVar::mismatchError(const Token *deallocTok, const Token *allocTok, const std::string &varname) const +{ + const CheckMemoryLeak c(mTokenizer, mErrorLogger, mSettings); + const std::list callstack = { allocTok, deallocTok }; + c.mismatchAllocDealloc(callstack, varname); +} + +void CheckLeakAutoVar::deallocUseError(const Token *tok, const std::string &varname) const +{ + const CheckMemoryLeak c(mTokenizer, mErrorLogger, mSettings); + c.deallocuseError(tok, varname); +} + +void CheckLeakAutoVar::deallocReturnError(const Token *tok, const Token *deallocTok, const std::string &varname) +{ + const std::list locations = { deallocTok, tok }; + reportError(locations, Severity::error, "deallocret", "$symbol:" + varname + "\nReturning/dereferencing '$symbol' after it is deallocated / released", CWE672, Certainty::normal); +} + +void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::pair& functionUsage) +{ + if (mSettings->checkLibrary && functionUsage.second == VarInfo::USED && + (!functionUsage.first || !functionUsage.first->function() || !functionUsage.first->function()->hasBody())) { + std::string funcStr = functionUsage.first ? mSettings->library.getFunctionName(functionUsage.first) : "f"; + if (funcStr.empty()) + funcStr = "unknown::" + functionUsage.first->str(); + reportError(tok, + Severity::information, + "checkLibraryUseIgnore", + "--check-library: Function " + funcStr + "() should have / configuration"); + } +} + +void CheckLeakAutoVar::doubleFreeError(const Token *tok, const Token *prevFreeTok, const std::string &varname, int type) +{ + const std::list locations = { prevFreeTok, tok }; + + if (Library::isresource(type)) + reportError(locations, Severity::error, "doubleFree", "$symbol:" + varname + "\nResource handle '$symbol' freed twice.", CWE415, Certainty::normal); + else + reportError(locations, Severity::error, "doubleFree", "$symbol:" + varname + "\nMemory pointed to by '$symbol' is freed twice.", CWE415, Certainty::normal); +} + + +void CheckLeakAutoVar::check() +{ + if (mSettings->clang) + return; + + logChecker("CheckLeakAutoVar::check"); // notclang + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + // Local variables that are known to be non-zero. + const std::set notzero; + + // Check function scopes + for (const Scope * scope : symbolDatabase->functionScopes) { + if (scope->hasInlineOrLambdaFunction()) + continue; + + // Empty variable info + VarInfo varInfo; + + checkScope(scope->bodyStart, varInfo, notzero, 0); + } +} + +static bool isVarUsedInTree(const Token *tok, nonneg int varid) +{ + if (!tok) + return false; + if (tok->varId() == varid) + return true; + if (tok->str() == "(" && Token::simpleMatch(tok->astOperand1(), "sizeof")) + return false; + return isVarUsedInTree(tok->astOperand1(), varid) || isVarUsedInTree(tok->astOperand2(), varid); +} + +static bool isPointerReleased(const Token *startToken, const Token *endToken, nonneg int varid) +{ + for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) { + if (tok->varId() != varid) + continue; + if (Token::Match(tok, "%var% . release ( )")) + return true; + if (Token::Match(tok, "%var% =")) + return false; + } + return false; +} + +static bool isLocalVarNoAutoDealloc(const Token *varTok) +{ + // not a local variable nor argument? + const Variable *var = varTok->variable(); + if (!var) + return true; + if (!var->isArgument() && (!var->isLocal() || var->isStatic())) + return false; + + // Don't check reference variables + if (var->isReference() && !var->isArgument()) + return false; + + // non-pod variable + if (varTok->isCpp()) { + // Possibly automatically deallocated memory + if (isAutoDealloc(var) && Token::Match(varTok, "%var% [=({] new")) + return false; + if (!var->isPointer() && !var->typeStartToken()->isStandardType()) + return false; + } + return true; +} + +/** checks if nameToken is a name of a function in a function call: + * func(arg) + * or + * func(arg) + * @param nameToken Function name token + * @return opening parenthesis token or NULL if not a function call + */ + +static const Token * isFunctionCall(const Token * nameToken) +{ + if (!nameToken->isStandardType() && nameToken->isName()) { + nameToken = nameToken->next(); + // check if function is a template + if (nameToken && nameToken->link() && nameToken->str() == "<") { + // skip template arguments + nameToken = nameToken->link()->next(); + } + // check for '(' + if (nameToken && nameToken->link() && !nameToken->isCast() && nameToken->str() == "(") { + // returning opening parenthesis pointer + return nameToken; + } + } + return nullptr; +} + +bool CheckLeakAutoVar::checkScope(const Token * const startToken, + VarInfo &varInfo, + std::set notzero, + nonneg int recursiveCount) +{ +#if ASAN + static const nonneg int recursiveLimit = 300; +#elif defined(__MINGW32__) + // testrunner crashes with stack overflow in CI + static constexpr nonneg int recursiveLimit = 600; +#else + static constexpr nonneg int recursiveLimit = 1000; +#endif + if (++recursiveCount > recursiveLimit) // maximum number of "else if ()" + throw InternalError(startToken, "Internal limit: CheckLeakAutoVar::checkScope() Maximum recursive count of 1000 reached.", InternalError::LIMIT); + + std::map &alloctype = varInfo.alloctype; + auto& possibleUsage = varInfo.possibleUsage; + const std::set conditionalAlloc(varInfo.conditionalAlloc); + + // Parse all tokens + const Token * const endToken = startToken->link(); + for (const Token *tok = startToken; tok && tok != endToken; tok = tok->next()) { + if (!tok->scope()->isExecutable()) { + tok = tok->scope()->bodyEnd; + if (!tok) // Ticket #6666 (crash upon invalid code) + break; + } + + // check each token + { + const bool isInit = Token::Match(tok, "%var% {|(") && tok->variable() && tok == tok->variable()->nameToken() && tok->variable()->isPointer(); + const Token * nextTok = isInit ? nullptr : checkTokenInsideExpression(tok, varInfo); + if (nextTok) { + tok = nextTok; + continue; + } + } + + + // look for end of statement + const bool isInit = Token::Match(tok->tokAt(-1), "%var% {|(") && tok->tokAt(-1)->variable() && tok->tokAt(-1) == tok->tokAt(-1)->variable()->nameToken(); + if ((!Token::Match(tok, "[;{},]") || Token::Match(tok->next(), "[;{},]")) && !(isInit && tok->str() == "(")) + continue; + + if (Token::Match(tok, "[;{},] %var% [")) + continue; + + if (!isInit) + tok = tok->next(); + if (!tok || tok == endToken) + break; + + if (Token::Match(tok, "%name% (") && isUnevaluated(tok)) { + tok = tok->linkAt(1); + continue; + } + + if (Token::Match(tok, "const %type%")) + tok = tok->tokAt(2); + + while (!isInit && tok->str() == "(") + tok = tok->next(); + while (tok->isUnaryOp("*") && tok->astOperand1()->isUnaryOp("&")) + tok = tok->astOperand1()->astOperand1(); + + // parse statement, skip to last member + const Token* varTok = isInit ? tok->tokAt(-1) : tok; + while (Token::Match(varTok, "%name% ::|. %name% !!(")) + varTok = varTok->tokAt(2); + + const Token *ftok = tok; + if (ftok->str() == "::") + ftok = ftok->next(); + while (Token::Match(ftok, "%name% :: %name%")) + ftok = ftok->tokAt(2); + + auto isAssignment = [](const Token* varTok) -> const Token* { + if (varTok->varId()) { + const Token* top = varTok; + while (top->astParent()) { + top = top->astParent(); + if (!Token::Match(top, "(|*|&|.")) + break; + } + if (top->str() == "=" && succeeds(top, varTok)) + return top; + } + return nullptr; + }; + + // assignment.. + if (const Token* const tokAssignOp = isInit ? varTok : isAssignment(varTok)) { + + if (Token::simpleMatch(tokAssignOp->astOperand1(), ".")) + continue; + // taking address of another variable.. + if (Token::Match(tokAssignOp, "= %var% [+;]")) { + if (varTok->tokAt(2)->varId() != varTok->varId()) { + // If variable points at allocated memory => error + leakIfAllocated(varTok, varInfo); + + // no multivariable checking currently => bail out for rhs variables + for (const Token *tok2 = varTok; tok2; tok2 = tok2->next()) { + if (tok2->str() == ";") { + break; + } + if (tok2->varId()) { + varInfo.erase(tok2->varId()); + } + } + } + } + + // right ast part (after `=` operator) + const Token* tokRightAstOperand = tokAssignOp->astOperand2(); + while (tokRightAstOperand && tokRightAstOperand->isCast()) + tokRightAstOperand = tokRightAstOperand->astOperand2() ? tokRightAstOperand->astOperand2() : tokRightAstOperand->astOperand1(); + + // is variable used in rhs? + if (isVarUsedInTree(tokRightAstOperand, varTok->varId())) + continue; + + // Variable has already been allocated => error + if (conditionalAlloc.find(varTok->varId()) == conditionalAlloc.end()) + leakIfAllocated(varTok, varInfo); + varInfo.erase(varTok->varId()); + + if (!isLocalVarNoAutoDealloc(varTok)) + continue; + + // allocation? + const Token *const fTok = tokRightAstOperand ? tokRightAstOperand->previous() : nullptr; + if (Token::Match(fTok, "%type% (")) { + const Library::AllocFunc* f = mSettings->library.getAllocFuncInfo(fTok); + if (f && f->arg == -1) { + VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()]; + varAlloc.type = f->groupId; + varAlloc.status = VarInfo::ALLOC; + varAlloc.allocTok = fTok; + } + + changeAllocStatusIfRealloc(alloctype, fTok, varTok); + } else if (varTok->isCpp() && Token::Match(varTok->tokAt(2), "new !!(")) { + const Token* tok2 = varTok->tokAt(2)->astOperand1(); + const bool arrayNew = (tok2 && (tok2->str() == "[" || (Token::Match(tok2, "(|{") && tok2->astOperand1() && tok2->astOperand1()->str() == "["))); + VarInfo::AllocInfo& varAlloc = alloctype[varTok->varId()]; + varAlloc.type = arrayNew ? NEW_ARRAY : NEW; + varAlloc.status = VarInfo::ALLOC; + varAlloc.allocTok = varTok->tokAt(2); + } + + // Assigning non-zero value variable. It might be used to + // track the execution for a later if condition. + if (Token::Match(varTok->tokAt(2), "%num% ;") && MathLib::toBigNumber(varTok->strAt(2)) != 0) + notzero.insert(varTok->varId()); + else if (Token::Match(varTok->tokAt(2), "- %type% ;") && varTok->tokAt(3)->isUpperCaseName()) + notzero.insert(varTok->varId()); + else + notzero.erase(varTok->varId()); + } + + // if/else + else if (Token::simpleMatch(tok, "if (")) { + // Parse function calls inside the condition + + const Token * closingParenthesis = tok->linkAt(1); + for (const Token *innerTok = tok->tokAt(2); innerTok && innerTok != closingParenthesis; innerTok = innerTok->next()) { + if (isUnevaluated(innerTok)) { + innerTok = innerTok->linkAt(1); + continue; + } + // TODO: replace with checkTokenInsideExpression() + const Token* const openingPar = isFunctionCall(innerTok); + if (!openingPar) + checkTokenInsideExpression(innerTok, varInfo); + + if (!isLocalVarNoAutoDealloc(innerTok)) + continue; + + // Check assignments in the if-statement. Skip multiple assignments since we don't track those + if (Token::Match(innerTok, "%var% =") && innerTok->astParent() == innerTok->next() && + !(innerTok->next()->astParent() && innerTok->next()->astParent()->isAssignmentOp())) { + // allocation? + // right ast part (after `=` operator) + const Token* tokRightAstOperand = innerTok->next()->astOperand2(); + while (tokRightAstOperand && tokRightAstOperand->isCast()) + tokRightAstOperand = tokRightAstOperand->astOperand2() ? tokRightAstOperand->astOperand2() : tokRightAstOperand->astOperand1(); + if (tokRightAstOperand && Token::Match(tokRightAstOperand->previous(), "%type% (")) { + const Token * fTok = tokRightAstOperand->previous(); + const Library::AllocFunc* f = mSettings->library.getAllocFuncInfo(fTok); + if (f && f->arg == -1) { + VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()]; + varAlloc.type = f->groupId; + varAlloc.status = VarInfo::ALLOC; + varAlloc.allocTok = fTok; + } else { + // Fixme: warn about leak + alloctype.erase(innerTok->varId()); + } + changeAllocStatusIfRealloc(alloctype, fTok, varTok); + } else if (innerTok->isCpp() && Token::Match(innerTok->tokAt(2), "new !!(")) { + const Token* tok2 = innerTok->tokAt(2)->astOperand1(); + const bool arrayNew = (tok2 && (tok2->str() == "[" || (tok2->str() == "(" && tok2->astOperand1() && tok2->astOperand1()->str() == "["))); + VarInfo::AllocInfo& varAlloc = alloctype[innerTok->varId()]; + varAlloc.type = arrayNew ? NEW_ARRAY : NEW; + varAlloc.status = VarInfo::ALLOC; + varAlloc.allocTok = innerTok->tokAt(2); + } + } + + // check for function call + if (openingPar) { + const Library::AllocFunc* allocFunc = mSettings->library.getDeallocFuncInfo(innerTok); + // innerTok is a function name + const VarInfo::AllocInfo allocation(0, VarInfo::NOALLOC); + functionCall(innerTok, openingPar, varInfo, allocation, allocFunc); + innerTok = openingPar->link(); + } + } + + if (Token::simpleMatch(closingParenthesis, ") {")) { + VarInfo varInfo1(varInfo); // VarInfo for if code + VarInfo varInfo2(varInfo); // VarInfo for else code + + // Skip expressions before commas + const Token * astOperand2AfterCommas = tok->next()->astOperand2(); + while (Token::simpleMatch(astOperand2AfterCommas, ",")) + astOperand2AfterCommas = astOperand2AfterCommas->astOperand2(); + + // Recursively scan variable comparisons in condition + visitAstNodes(astOperand2AfterCommas, [&](const Token *tok3) { + if (!tok3) + return ChildrenToVisit::none; + if (tok3->str() == "&&" || tok3->str() == "||") { + // FIXME: handle && ! || better + return ChildrenToVisit::op1_and_op2; + } + if (tok3->str() == "(" && Token::Match(tok3->astOperand1(), "UNLIKELY|LIKELY")) { + return ChildrenToVisit::op2; + } + if (tok3->str() == "(" && tok3->previous()->isName()) { + const std::vector params = getArguments(tok3->previous()); + for (const Token *par : params) { + if (!par->isComparisonOp()) + continue; + const Token *vartok = nullptr; + if (isVarTokComparison(par, &vartok, alloc_success_conds) || + (isVarTokComparison(par, &vartok, alloc_failed_conds))) { + varInfo1.erase(vartok->varId()); + varInfo2.erase(vartok->varId()); + } + } + return ChildrenToVisit::none; + } + + const Token *vartok = nullptr; + if (isVarTokComparison(tok3, &vartok, alloc_success_conds)) { + varInfo2.reallocToAlloc(vartok->varId()); + varInfo2.erase(vartok->varId()); + if (astIsVariableComparison(tok3, "!=", "0", &vartok) && + (notzero.find(vartok->varId()) != notzero.end())) + varInfo2.clear(); + } else if (isVarTokComparison(tok3, &vartok, alloc_failed_conds)) { + varInfo1.reallocToAlloc(vartok->varId()); + varInfo1.erase(vartok->varId()); + } + return ChildrenToVisit::none; + }); + + if (!checkScope(closingParenthesis->next(), varInfo1, notzero, recursiveCount)) { + varInfo.clear(); + continue; + } + closingParenthesis = closingParenthesis->linkAt(1); + if (Token::simpleMatch(closingParenthesis, "} else {")) { + if (!checkScope(closingParenthesis->tokAt(2), varInfo2, notzero, recursiveCount)) { + varInfo.clear(); + return false; + } + tok = closingParenthesis->linkAt(2)->previous(); + } else { + tok = closingParenthesis->previous(); + } + + VarInfo old; + old.swap(varInfo); + + std::map::const_iterator it; + + for (it = old.alloctype.cbegin(); it != old.alloctype.cend(); ++it) { + const int varId = it->first; + if (old.conditionalAlloc.find(varId) == old.conditionalAlloc.end()) + continue; + if (varInfo1.alloctype.find(varId) == varInfo1.alloctype.end() || + varInfo2.alloctype.find(varId) == varInfo2.alloctype.end()) { + varInfo1.erase(varId); + varInfo2.erase(varId); + } + } + + // Conditional allocation in varInfo1 + for (it = varInfo1.alloctype.cbegin(); it != varInfo1.alloctype.cend(); ++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.cbegin(); it != varInfo2.alloctype.cend(); ++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.cbegin(); it != varInfo1.alloctype.cend(); ++it) { + if (it->second.managed() && conditionalAlloc.find(it->first) != conditionalAlloc.end()) { + varInfo.conditionalAlloc.erase(it->first); + varInfo2.erase(it->first); + } + } + for (it = varInfo2.alloctype.cbegin(); it != varInfo2.alloctype.cend(); ++it) { + if (it->second.managed() && conditionalAlloc.find(it->first) != conditionalAlloc.end()) { + varInfo.conditionalAlloc.erase(it->first); + varInfo1.erase(it->first); + } + } + + alloctype.insert(varInfo1.alloctype.cbegin(), varInfo1.alloctype.cend()); + alloctype.insert(varInfo2.alloctype.cbegin(), varInfo2.alloctype.cend()); + + possibleUsage.insert(varInfo1.possibleUsage.cbegin(), varInfo1.possibleUsage.cend()); + possibleUsage.insert(varInfo2.possibleUsage.cbegin(), varInfo2.possibleUsage.cend()); + } + } + + // unknown control.. (TODO: handle loops) + else if ((Token::Match(tok, "%type% (") && Token::simpleMatch(tok->linkAt(1), ") {")) || Token::simpleMatch(tok, "do {")) { + varInfo.clear(); + return false; + } + + // return + else if (tok->str() == "return") { + ret(tok, varInfo); + varInfo.clear(); + } + + // throw + else if (tok->isCpp() && tok->str() == "throw") { + bool tryFound = false; + const Scope* scope = tok->scope(); + while (scope && scope->isExecutable()) { + if (scope->type == Scope::eTry) + tryFound = true; + scope = scope->nestedIn; + } + // If the execution leaves the function then treat it as return + if (!tryFound) + ret(tok, varInfo); + varInfo.clear(); + } + + // delete + else if (tok->isCpp() && tok->str() == "delete") { + const Token * delTok = tok; + if (Token::simpleMatch(delTok->astOperand1(), ".")) + continue; + const bool arrayDelete = Token::simpleMatch(tok->next(), "[ ]"); + if (arrayDelete) + tok = tok->tokAt(3); + else + tok = tok->next(); + if (tok->str() == "(") + tok = tok->next(); + while (Token::Match(tok, "%name% ::|.")) + tok = tok->tokAt(2); + const bool isnull = tok->hasKnownIntValue() && tok->values().front().intvalue == 0; + if (!isnull && tok->varId() && tok->strAt(1) != "[") { + const VarInfo::AllocInfo allocation(arrayDelete ? NEW_ARRAY : NEW, VarInfo::DEALLOC, delTok); + changeAllocStatus(varInfo, allocation, tok, tok); + } + } + + // Function call.. + else if (const Token* openingPar = isFunctionCall(ftok)) { + const Library::AllocFunc* af = mSettings->library.getDeallocFuncInfo(ftok); + VarInfo::AllocInfo allocation(af ? af->groupId : 0, VarInfo::DEALLOC, ftok); + if (allocation.type == 0) + allocation.status = VarInfo::NOALLOC; + if (Token::simpleMatch(ftok->astParent(), "(") && Token::simpleMatch(ftok->astParent()->astOperand2(), ".")) + continue; + functionCall(ftok, openingPar, varInfo, allocation, af); + + tok = ftok->next()->link(); + + // Handle scopes that might be noreturn + if (allocation.status == VarInfo::NOALLOC && Token::simpleMatch(tok, ") ; }")) { + if (ftok->isKeyword()) + continue; + bool unknown = false; + if (mTokenizer->isScopeNoReturn(tok->tokAt(2), &unknown)) { + if (!unknown) + varInfo.clear(); + else { + if (ftok->function() && !ftok->function()->isAttributeNoreturn() && + !(ftok->function()->functionScope && mTokenizer->isScopeNoReturn(ftok->function()->functionScope->bodyEnd))) // check function scope + continue; + const std::string functionName(mSettings->library.getFunctionName(ftok)); + if (!mSettings->library.isLeakIgnore(functionName) && !mSettings->library.isUse(functionName)) { + const VarInfo::Usage usage = Token::simpleMatch(openingPar, "( )") ? VarInfo::NORET : VarInfo::USED; // TODO: check parameters passed to function + varInfo.possibleUsageAll({ ftok, usage }); + } + } + } + } + + continue; + } + + // goto => weird execution path + else if (tok->str() == "goto") { + varInfo.clear(); + return false; + } + + // continue/break + else if (Token::Match(tok, "continue|break ;")) { + varInfo.clear(); + } + + // Check smart pointer + else if (Token::Match(ftok, "%name% <") && mSettings->library.isSmartPointer(tok)) { + const Token * typeEndTok = ftok->linkAt(1); + if (!Token::Match(typeEndTok, "> %var% {|( %var% ,|)|}")) + continue; + + tok = typeEndTok->linkAt(2); + + const int varid = typeEndTok->next()->varId(); + if (isPointerReleased(typeEndTok->tokAt(2), endToken, varid)) + continue; + + bool arrayDelete = false; + if (Token::findsimplematch(ftok->next(), "[ ]", typeEndTok)) + arrayDelete = true; + + // Check deleter + const Token * deleterToken = nullptr; + const Token * endDeleterToken = nullptr; + const Library::AllocFunc* af = nullptr; + if (Token::Match(ftok, "unique_ptr < %type% ,")) { + deleterToken = ftok->tokAt(4); + endDeleterToken = typeEndTok; + } else if (Token::Match(typeEndTok, "> %var% {|( %var% ,")) { + deleterToken = typeEndTok->tokAt(5); + endDeleterToken = typeEndTok->linkAt(2); + } + if (deleterToken) { + // Skip the decaying plus in expressions like +[](T*){} + if (deleterToken->str() == "+") { + deleterToken = deleterToken->next(); + } + // Check if its a pointer to a function + const Token * dtok = Token::findmatch(deleterToken, "& %name%", endDeleterToken); + if (dtok) { + dtok = dtok->next(); + af = mSettings->library.getDeallocFuncInfo(dtok); + } + if (!dtok || !af) { + const Token * tscopeStart = nullptr; + const Token * tscopeEnd = nullptr; + // If the deleter is a lambda, check if it calls the dealloc function + if (deleterToken->str() == "[" && + Token::simpleMatch(deleterToken->link(), "] (") && + // TODO: Check for mutable keyword + Token::simpleMatch(deleterToken->link()->linkAt(1), ") {")) { + tscopeStart = deleterToken->link()->linkAt(1)->tokAt(1); + tscopeEnd = tscopeStart->link(); + // check user-defined deleter function + } else if (dtok && dtok->function()) { + const Scope* tscope = dtok->function()->functionScope; + if (tscope) { + tscopeStart = tscope->bodyStart; + tscopeEnd = tscope->bodyEnd; + } + // If the deleter is a class, check if class calls the dealloc function + } else if ((dtok = Token::findmatch(deleterToken, "%type%", endDeleterToken)) && dtok->type()) { + const Scope * tscope = dtok->type()->classScope; + if (tscope) { + tscopeStart = tscope->bodyStart; + tscopeEnd = tscope->bodyEnd; + } + } + + if (tscopeStart && tscopeEnd) { + for (const Token *tok2 = tscopeStart; tok2 != tscopeEnd; tok2 = tok2->next()) { + af = mSettings->library.getDeallocFuncInfo(tok2); + if (af) + break; + } + } else { // there is a deleter, but we can't check it -> assume that it deallocates correctly + varInfo.clear(); + continue; + } + } + } + + const Token * vtok = typeEndTok->tokAt(3); + const VarInfo::AllocInfo allocation(af ? af->groupId : (arrayDelete ? NEW_ARRAY : NEW), VarInfo::OWNED, ftok); + changeAllocStatus(varInfo, allocation, vtok, vtok); + } else if (Token::Match(tok, "%var% .")) + checkTokenInsideExpression(tok, varInfo); + } + ret(endToken, varInfo, true); + return true; +} + + +const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const tok, VarInfo &varInfo, bool inFuncCall) +{ + // Deallocation and then dereferencing pointer.. + if (tok->varId() > 0) { + // TODO : Write a separate checker for this that uses valueFlowForward. + const std::map::const_iterator var = varInfo.alloctype.find(tok->varId()); + if (var != varInfo.alloctype.end()) { + bool unknown = false; + if (var->second.status == VarInfo::DEALLOC && CheckNullPointer::isPointerDeRef(tok, unknown, *mSettings) && !unknown) { + deallocUseError(tok, tok->str()); + } else if (Token::simpleMatch(tok->tokAt(-2), "= &")) { + varInfo.erase(tok->varId()); + } else { + // check if tok is assigned into another variable + const Token *rhs = tok; + bool isAssignment = false; + while (rhs->astParent()) { + if (rhs->astParent()->str() == "=") { + isAssignment = true; + break; + } + rhs = rhs->astParent(); + } + while (rhs->isCast()) { + rhs = rhs->astOperand2() ? rhs->astOperand2() : rhs->astOperand1(); + } + if (rhs->varId() == tok->varId() && isAssignment) { + // simple assignment + varInfo.erase(tok->varId()); + } else if (rhs->astParent() && rhs->str() == "(" && !mSettings->library.returnValue(rhs->astOperand1()).empty()) { + // #9298, assignment through return value of a function + const std::string &returnValue = mSettings->library.returnValue(rhs->astOperand1()); + if (startsWith(returnValue, "arg")) { + int argn; + const Token *func = getTokenArgumentFunction(tok, argn); + if (func) { + const std::string arg = "arg" + std::to_string(argn + 1); + if (returnValue == arg) { + varInfo.erase(tok->varId()); + } + } + } + } + } + } else if (Token::Match(tok->previous(), "& %name% = %var% ;")) { + varInfo.referenced.insert(tok->tokAt(2)->varId()); + } + } + + // check for function call + const Token * const openingPar = inFuncCall ? nullptr : isFunctionCall(tok); + if (openingPar) { + const Library::AllocFunc* allocFunc = mSettings->library.getDeallocFuncInfo(tok); + VarInfo::AllocInfo alloc(allocFunc ? allocFunc->groupId : 0, VarInfo::DEALLOC, tok); + if (alloc.type == 0) + alloc.status = VarInfo::NOALLOC; + functionCall(tok, openingPar, varInfo, alloc, nullptr); + const std::string &returnValue = mSettings->library.returnValue(tok); + if (startsWith(returnValue, "arg")) + // the function returns one of its argument, we need to process a potential assignment + return openingPar; + return isCPPCast(tok->astParent()) ? openingPar : openingPar->link(); + } + + return nullptr; +} + + +void CheckLeakAutoVar::changeAllocStatusIfRealloc(std::map &alloctype, const Token *fTok, const Token *retTok) const +{ + const Library::AllocFunc* f = mSettings->library.getReallocFuncInfo(fTok); + if (f && f->arg == -1 && f->reallocArg > 0 && f->reallocArg <= numberOfArguments(fTok)) { + const Token* argTok = getArguments(fTok).at(f->reallocArg - 1); + if (alloctype.find(argTok->varId()) != alloctype.end()) { + VarInfo::AllocInfo& argAlloc = alloctype[argTok->varId()]; + if (argAlloc.type != 0 && argAlloc.type != f->groupId) + mismatchError(fTok, argAlloc.allocTok, argTok->str()); + argAlloc.status = VarInfo::REALLOC; + argAlloc.allocTok = fTok; + } + VarInfo::AllocInfo& retAlloc = alloctype[retTok->varId()]; + retAlloc.type = f->groupId; + retAlloc.status = VarInfo::ALLOC; + retAlloc.allocTok = fTok; + retAlloc.reallocedFromType = argTok->varId(); + } +} + + +void CheckLeakAutoVar::changeAllocStatus(VarInfo &varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg) +{ + std::map &alloctype = varInfo.alloctype; + const std::map::iterator var = alloctype.find(arg->varId()); + if (var != alloctype.end()) { + if (allocation.status == VarInfo::NOALLOC) { + // possible usage + varInfo.possibleUsage[arg->varId()] = { tok, VarInfo::USED }; + if (var->second.status == VarInfo::DEALLOC && arg->previous()->str() == "&") + varInfo.erase(arg->varId()); + } else if (var->second.managed()) { + doubleFreeError(tok, var->second.allocTok, arg->str(), allocation.type); + var->second.status = allocation.status; + } else if (var->second.type != allocation.type && var->second.type != 0) { + // mismatching allocation and deallocation + mismatchError(tok, var->second.allocTok, arg->str()); + varInfo.erase(arg->varId()); + } else { + // deallocation + var->second.status = allocation.status; + var->second.type = allocation.type; + var->second.allocTok = allocation.allocTok; + } + } else if (allocation.status != VarInfo::NOALLOC && allocation.status != VarInfo::OWNED && !Token::simpleMatch(tok->astTop(), "return")) { + alloctype[arg->varId()].status = VarInfo::DEALLOC; + alloctype[arg->varId()].allocTok = tok; + } +} + +void CheckLeakAutoVar::functionCall(const Token *tokName, const Token *tokOpeningPar, VarInfo &varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af) +{ + // Ignore function call? + const bool isLeakIgnore = mSettings->library.isLeakIgnore(mSettings->library.getFunctionName(tokName)); + if (mSettings->library.getReallocFuncInfo(tokName)) + return; + if (tokName->next()->valueType() && tokName->next()->valueType()->container && tokName->next()->valueType()->container->stdStringLike) + return; + + const Token * const tokFirstArg = tokOpeningPar->next(); + if (!tokFirstArg || tokFirstArg->str() == ")") { + // no arguments + return; + } + + int argNr = 1; + for (const Token *funcArg = tokFirstArg; funcArg; funcArg = funcArg->nextArgument()) { + const Token* arg = funcArg; + if (arg->isCpp()) { + int tokAdvance = 0; + if (arg->str() == "new") + tokAdvance = 1; + else if (Token::simpleMatch(arg, "* new")) + tokAdvance = 2; + if (tokAdvance > 0) { + arg = arg->tokAt(tokAdvance); + if (Token::simpleMatch(arg, "( std :: nothrow )")) + arg = arg->tokAt(5); + } + } + + // Skip casts + if (arg->isKeyword() && arg->astParent() && arg->astParent()->isCast()) + arg = arg->astParent(); + while (arg && arg->isCast()) + arg = arg->astOperand2() ? arg->astOperand2() : arg->astOperand1(); + const Token * const argTypeStartTok = arg; + + while (Token::Match(arg, "%name% .|:: %name%")) + arg = arg->tokAt(2); + + if ((Token::Match(arg, "%var% [-,)] !!.") && !(arg->variable() && arg->variable()->isArray())) || + (Token::Match(arg, "& %var% !!.") && !(arg->next()->variable() && arg->next()->variable()->isArray()))) { + // goto variable + const bool isAddressOf = arg->str() == "&"; + if (isAddressOf) + arg = arg->next(); + + const bool isnull = !isAddressOf && (arg->hasKnownIntValue() && arg->values().front().intvalue == 0); + + // Is variable allocated? + if (!isnull && (!af || af->arg == argNr)) { + const Library::AllocFunc* deallocFunc = mSettings->library.getDeallocFuncInfo(tokName); + VarInfo::AllocInfo dealloc(deallocFunc ? deallocFunc->groupId : 0, VarInfo::DEALLOC, tokName); + if (const Library::AllocFunc* allocFunc = mSettings->library.getAllocFuncInfo(tokName)) { + if (mSettings->library.getDeallocFuncInfo(tokName)) { + changeAllocStatus(varInfo, dealloc.type == 0 ? allocation : dealloc, tokName, arg); + } + if (allocFunc->arg == argNr && + !(arg->variable() && arg->variable()->isArgument() && arg->valueType() && arg->valueType()->pointer > 1) && + (isAddressOf || (arg->valueType() && arg->valueType()->pointer == 2))) { + leakIfAllocated(arg, varInfo); + VarInfo::AllocInfo& varAlloc = varInfo.alloctype[arg->varId()]; + varAlloc.type = allocFunc->groupId; + varAlloc.status = VarInfo::ALLOC; + varAlloc.allocTok = arg; + } + } + else if (isLeakIgnore) + checkTokenInsideExpression(arg, varInfo); + else + changeAllocStatus(varInfo, dealloc.type == 0 ? allocation : dealloc, tokName, arg); + } + } + // Check smart pointer + else if (Token::Match(arg, "%name% < %type%") && mSettings->library.isSmartPointer(argTypeStartTok)) { + const Token * typeEndTok = arg->linkAt(1); + const Token * allocTok = nullptr; + if (!Token::Match(typeEndTok, "> {|( %var% ,|)|}")) + continue; + + bool arrayDelete = false; + if (Token::findsimplematch(arg->next(), "[ ]", typeEndTok)) + arrayDelete = true; + + // Check deleter + const Token * deleterToken = nullptr; + const Token * endDeleterToken = nullptr; + const Library::AllocFunc* sp_af = nullptr; + if (Token::Match(arg, "unique_ptr < %type% ,")) { + deleterToken = arg->tokAt(4); + endDeleterToken = typeEndTok; + } else if (Token::Match(typeEndTok, "> {|( %var% ,")) { + deleterToken = typeEndTok->tokAt(4); + endDeleterToken = typeEndTok->linkAt(1); + } + if (deleterToken) { + // Check if its a pointer to a function + const Token * dtok = Token::findmatch(deleterToken, "& %name%", endDeleterToken); + if (dtok) { + sp_af = mSettings->library.getDeallocFuncInfo(dtok->tokAt(1)); + } else { + // If the deleter is a class, check if class calls the dealloc function + dtok = Token::findmatch(deleterToken, "%type%", endDeleterToken); + if (dtok && dtok->type()) { + const Scope * tscope = dtok->type()->classScope; + for (const Token *tok2 = tscope->bodyStart; tok2 != tscope->bodyEnd; tok2 = tok2->next()) { + sp_af = mSettings->library.getDeallocFuncInfo(tok2); + if (sp_af) { + allocTok = tok2; + break; + } + } + } + } + } + + const Token * vtok = typeEndTok->tokAt(2); + const VarInfo::AllocInfo sp_allocation(sp_af ? sp_af->groupId : (arrayDelete ? NEW_ARRAY : NEW), VarInfo::OWNED, allocTok); + changeAllocStatus(varInfo, sp_allocation, vtok, vtok); + } else { + const Token* const nextArg = funcArg->nextArgument(); + while (arg && ((nextArg && arg != nextArg) || (!nextArg && arg != tokOpeningPar->link()))) { + checkTokenInsideExpression(arg, varInfo, /*inFuncCall*/ isLeakIgnore); + + if (isLambdaCaptureList(arg)) + break; + arg = arg->next(); + } + } + // TODO: check each token in argument expression (could contain multiple variables) + argNr++; + } +} + + +void CheckLeakAutoVar::leakIfAllocated(const Token *vartok, + const VarInfo &varInfo) +{ + const std::map &alloctype = varInfo.alloctype; + const auto& possibleUsage = varInfo.possibleUsage; + + const std::map::const_iterator var = alloctype.find(vartok->varId()); + if (var != alloctype.cend() && var->second.status == VarInfo::ALLOC) { + const auto use = possibleUsage.find(vartok->varId()); + if (use == possibleUsage.end()) { + leakError(vartok, vartok->str(), var->second.type); + } else { + configurationInfo(vartok, use->second); + } + } +} + +static const Token* getOutparamAllocation(const Token* tok, const Settings* settings) +{ + if (!tok) + return nullptr; + int argn{}; + const Token* ftok = getTokenArgumentFunction(tok, argn); + if (!ftok) + return nullptr; + if (const Library::AllocFunc* allocFunc = settings->library.getAllocFuncInfo(ftok)) { + if (allocFunc->arg == argn + 1) + return ftok; + } + return nullptr; +} + +void CheckLeakAutoVar::ret(const Token *tok, VarInfo &varInfo, const bool isEndOfScope) +{ + const std::map &alloctype = varInfo.alloctype; + const auto& possibleUsage = varInfo.possibleUsage; + std::vector toRemove; + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (std::map::const_iterator it = alloctype.cbegin(); it != alloctype.cend(); ++it) { + // don't warn if variable is conditionally allocated, unless it leaves the scope + if (!isEndOfScope && !it->second.managed() && varInfo.conditionalAlloc.find(it->first) != varInfo.conditionalAlloc.end()) + continue; + + // don't warn if there is a reference of the variable + if (varInfo.referenced.find(it->first) != varInfo.referenced.end()) + continue; + + const int varid = it->first; + const Variable *var = symbolDatabase->getVariableFromVarId(varid); + if (var) { + // don't warn if we leave an inner scope + if (isEndOfScope && var->scope() && tok != var->scope()->bodyEnd) + continue; + enum class PtrUsage { NONE, DEREF, PTR } used = PtrUsage::NONE; + for (const Token *tok2 = tok; tok2; tok2 = tok2->next()) { + if (tok2->str() == ";") + break; + if (!Token::Match(tok2, "return|(|{|,|*")) + continue; + + const Token* tok3 = tok2->next(); + while (tok3 && tok3->isCast() && tok3->valueType() && + (tok3->valueType()->pointer || + (tok3->valueType()->typeSize(mSettings->platform) == 0) || + (tok3->valueType()->typeSize(mSettings->platform) >= mSettings->platform.sizeof_pointer))) + tok3 = tok3->astOperand2() ? tok3->astOperand2() : tok3->astOperand1(); + if (tok3 && tok3->varId() == varid) + tok2 = tok3->next(); + else if (Token::Match(tok3, "& %varid% . %name%", varid)) + tok2 = tok3->tokAt(4); + else if (Token::simpleMatch(tok3, "*") && tok3->next()->varId() == varid) + tok2 = tok3; + else + continue; + if (Token::Match(tok2, "[});,+]") && (!astIsBool(tok) || tok2->str() != ";")) { + used = PtrUsage::PTR; + break; + } + if (Token::Match(tok2, "[|.|*")) { + used = PtrUsage::DEREF; + break; + } + } + + // don't warn when returning after checking return value of outparam allocation + const Token* outparamFunc{}; + if ((tok->scope()->type == Scope::ScopeType::eIf || tok->scope()->type== Scope::ScopeType::eElse) && + (outparamFunc = getOutparamAllocation(it->second.allocTok, mSettings))) { + const Scope* scope = tok->scope(); + if (scope->type == Scope::ScopeType::eElse) { + scope = scope->bodyStart->tokAt(-2)->scope(); + } + const Token* const ifEnd = scope->bodyStart->previous(); + const Token* const ifStart = ifEnd->link(); + const Token* const alloc = it->second.allocTok; + if (precedes(ifStart, alloc) && succeeds(ifEnd, alloc)) { // allocation and check in if + if (Token::Match(outparamFunc->next()->astParent(), "%comp%")) + continue; + } else { // allocation result assigned to variable + const Token* const retAssign = outparamFunc->next()->astParent(); + if (Token::simpleMatch(retAssign, "=") && retAssign->astOperand1()->varId()) { + bool isRetComp = false; + for (const Token* tok2 = ifStart; tok2 != ifEnd; tok2 = tok2->next()) { + if (tok2->varId() == retAssign->astOperand1()->varId()) { + isRetComp = true; + break; + } + } + if (isRetComp) + continue; + } + } + } + + // return deallocated pointer + if (used != PtrUsage::NONE && it->second.status == VarInfo::DEALLOC) + deallocReturnError(tok, it->second.allocTok, var->name()); + + else if (used != PtrUsage::PTR && !it->second.managed() && !var->isReference()) { + const auto use = possibleUsage.find(varid); + if (use == possibleUsage.end()) { + leakError(tok, var->name(), it->second.type); + } else if (!use->second.first->variable()) { // TODO: handle constructors + configurationInfo(tok, use->second); + } + } + toRemove.push_back(varid); + } + } + for (const int varId : toRemove) + varInfo.erase(varId); +} diff --git a/cppcheck-2.14.0/lib/checkleakautovar.h b/cppcheck-2.14.0/lib/checkleakautovar.h new file mode 100644 index 00000000..3c3bd262 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkleakautovar.h @@ -0,0 +1,180 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checkleakautovarH +#define checkleakautovarH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "library.h" +#include "tokenize.h" + +#include +#include +#include + +class ErrorLogger; +class Settings; +class Token; + + +class CPPCHECKLIB VarInfo { +public: + enum AllocStatus { REALLOC = -3, OWNED = -2, DEALLOC = -1, NOALLOC = 0, ALLOC = 1 }; + struct AllocInfo { + AllocStatus status; + /** Allocation type. If it is a positive value then it corresponds to + * a Library allocation id. A negative value is a builtin + * checkleakautovar allocation type. + */ + int type; + int reallocedFromType = -1; + const Token * allocTok; + explicit AllocInfo(int type_ = 0, AllocStatus status_ = NOALLOC, const Token* allocTok_ = nullptr) : status(status_), type(type_), allocTok(allocTok_) {} + + bool managed() const { + return status < 0; + } + }; + enum Usage { USED, NORET }; + std::map alloctype; + std::map> possibleUsage; + std::set conditionalAlloc; + std::set referenced; + + void clear() { + alloctype.clear(); + possibleUsage.clear(); + conditionalAlloc.clear(); + referenced.clear(); + } + + void erase(nonneg int varid) { + alloctype.erase(varid); + possibleUsage.erase(varid); + conditionalAlloc.erase(varid); + referenced.erase(varid); + } + + void swap(VarInfo &other) { + alloctype.swap(other.alloctype); + possibleUsage.swap(other.possibleUsage); + conditionalAlloc.swap(other.conditionalAlloc); + referenced.swap(other.referenced); + } + + void reallocToAlloc(nonneg int varid) { + const AllocInfo& alloc = alloctype[varid]; + if (alloc.reallocedFromType >= 0) { + const std::map::iterator it = alloctype.find(alloc.reallocedFromType); + if (it != alloctype.end() && it->second.status == REALLOC) { + it->second.status = ALLOC; + } + } + } + + /** set possible usage for all variables */ + void possibleUsageAll(const std::pair& functionUsage); + + 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()) {} + +private: + /** This constructor is used when running checks. */ + CheckLeakAutoVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger) {} + + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + CheckLeakAutoVar checkLeakAutoVar(&tokenizer, &tokenizer.getSettings(), errorLogger); + checkLeakAutoVar.check(); + } + + /** check for leaks in all scopes */ + void check(); + + /** check for leaks in a function scope */ + bool checkScope(const Token * const startToken, + VarInfo &varInfo, + std::set notzero, + nonneg int recursiveCount); + + /** Check token inside expression. + * @param tok token inside expression. + * @param varInfo Variable info + * @return next token to process (if no other checks needed for this token). NULL if other checks could be performed. + */ + const Token * checkTokenInsideExpression(const Token * const tok, VarInfo &varInfo, bool inFuncCall = false); + + /** parse function call */ + void functionCall(const Token *tokName, const Token *tokOpeningPar, VarInfo &varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af); + + /** parse changes in allocation status */ + void changeAllocStatus(VarInfo &varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg); + + /** update allocation status if reallocation function */ + void changeAllocStatusIfRealloc(std::map &alloctype, const Token *fTok, const Token *retTok) const; + + /** return. either "return" or end of variable scope is seen */ + void ret(const Token *tok, VarInfo &varInfo, const bool isEndOfScope = false); + + /** 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) const; + void mismatchError(const Token* deallocTok, const Token* allocTok, const std::string &varname) const; + void deallocUseError(const Token *tok, const std::string &varname) const; + void deallocReturnError(const Token *tok, const Token *deallocTok, const std::string &varname); + void doubleFreeError(const Token *tok, const Token *prevFreeTok, const std::string &varname, int type); + + /** message: user configuration is needed to complete analysis */ + void configurationInfo(const Token* tok, const std::pair& functionUsage); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckLeakAutoVar c(nullptr, settings, errorLogger); + c.deallocReturnError(nullptr, nullptr, "p"); + c.configurationInfo(nullptr, { nullptr, VarInfo::USED }); // user configuration is needed to complete analysis + c.doubleFreeError(nullptr, nullptr, "varname", 0); + } + + static std::string myName() { + return "Leaks (auto variables)"; + } + + std::string classInfo() const override { + return "Detect when a auto variable is allocated but not deallocated or deallocated twice.\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkleakautovarH diff --git a/cppcheck-2.14.0/lib/checkmemoryleak.cpp b/cppcheck-2.14.0/lib/checkmemoryleak.cpp new file mode 100644 index 00000000..733e6053 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkmemoryleak.cpp @@ -0,0 +1,1156 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "checkmemoryleak.h" + +#include "astutils.h" +#include "errorlogger.h" +#include "errortypes.h" +#include "library.h" +#include "mathlib.h" +#include "platform.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" + +#include +#include +#include + +//--------------------------------------------------------------------------- + +// Register this check class (by creating a static instance of it) +namespace { + CheckMemoryLeakInFunction instance1; + CheckMemoryLeakInClass instance2; + CheckMemoryLeakStructMember instance3; + CheckMemoryLeakNoVar instance4; +} + +// CWE ID used: +static const CWE CWE398(398U); // Indicator of Poor Code Quality +static const CWE CWE401(401U); // Improper Release of Memory Before Removing Last Reference ('Memory Leak') +static const CWE CWE771(771U); // Missing Reference to Active Allocated Resource +static const CWE CWE772(772U); // Missing Release of Resource after Effective Lifetime + +//--------------------------------------------------------------------------- + +CheckMemoryLeak::AllocType CheckMemoryLeak::getAllocationType(const Token *tok2, nonneg int varid, std::list *callstack) const +{ + // What we may have... + // * var = (char *)malloc(10); + // * var = new char[10]; + // * var = strdup("hello"); + // * var = strndup("hello", 3); + if (tok2 && tok2->str() == "(") { + tok2 = tok2->link(); + tok2 = tok2 ? tok2->next() : nullptr; + } + if (!tok2) + return No; + if (tok2->str() == "::") + tok2 = tok2->next(); + if (!tok2->isName()) + return No; + + if (!Token::Match(tok2, "%name% ::|. %type%")) { + // Using realloc.. + AllocType reallocType = getReallocationType(tok2, varid); + if (reallocType != No) + return reallocType; + + if (tok2->isCpp() && tok2->str() == "new") { + if (tok2->strAt(1) == "(" && !Token::Match(tok2->next(),"( std| ::| nothrow )")) + return No; + if (tok2->astOperand1() && (tok2->astOperand1()->str() == "[" || (tok2->astOperand1()->astOperand1() && tok2->astOperand1()->astOperand1()->str() == "["))) + return NewArray; + const Token *typeTok = tok2->next(); + while (Token::Match(typeTok, "%name% :: %name%")) + typeTok = typeTok->tokAt(2); + const Scope* classScope = nullptr; + if (typeTok->type() && typeTok->type()->isClassType()) { + classScope = typeTok->type()->classScope; + } else if (typeTok->function() && typeTok->function()->isConstructor()) { + classScope = typeTok->function()->nestedIn; + } + if (classScope && classScope->numConstructors > 0) + return No; + return New; + } + + if (mSettings_->hasLib("posix")) { + if (Token::Match(tok2, "open|openat|creat|mkstemp|mkostemp|socket (")) { + // simple sanity check of function parameters.. + // TODO: Make such check for all these functions + const int num = numberOfArguments(tok2); + if (tok2->str() == "open" && num != 2 && num != 3) + return No; + + // is there a user function with this name? + if (tok2->function()) + return No; + return Fd; + } + + if (Token::simpleMatch(tok2, "popen (")) + return Pipe; + } + + // Does tok2 point on a Library allocation function? + const int alloctype = mSettings_->library.getAllocId(tok2, -1); + if (alloctype > 0) { + if (alloctype == mSettings_->library.deallocId("free")) + return Malloc; + if (alloctype == mSettings_->library.deallocId("fclose")) + return File; + return Library::ismemory(alloctype) ? OtherMem : OtherRes; + } + } + + while (Token::Match(tok2,"%name% ::|. %type%")) + tok2 = tok2->tokAt(2); + + // User function + const Function* func = tok2->function(); + if (func == nullptr) + return No; + + // Prevent recursion + if (callstack && std::find(callstack->cbegin(), callstack->cend(), func) != callstack->cend()) + return No; + + std::list cs; + if (!callstack) + callstack = &cs; + + callstack->push_back(func); + return functionReturnType(func, callstack); +} + + +CheckMemoryLeak::AllocType CheckMemoryLeak::getReallocationType(const Token *tok2, nonneg int varid) const +{ + // What we may have... + // * var = (char *)realloc(..; + if (tok2 && tok2->str() == "(") { + tok2 = tok2->link(); + tok2 = tok2 ? tok2->next() : nullptr; + } + if (!tok2) + return No; + + if (!Token::Match(tok2, "%name% (")) + return No; + + const Library::AllocFunc *f = mSettings_->library.getReallocFuncInfo(tok2); + if (!(f && f->reallocArg > 0 && f->reallocArg <= numberOfArguments(tok2))) + return No; + const auto args = getArguments(tok2); + if (args.size() < (f->reallocArg)) + return No; + const Token* arg = args.at(f->reallocArg - 1); + while (arg && arg->isCast()) + arg = arg->astOperand1(); + while (arg && arg->isUnaryOp("*")) + arg = arg->astOperand1(); + if (varid > 0 && !Token::Match(arg, "%varid% [,)]", varid)) + return No; + + const int realloctype = mSettings_->library.getReallocId(tok2, -1); + if (realloctype > 0) { + if (realloctype == mSettings_->library.deallocId("free")) + return Malloc; + if (realloctype == mSettings_->library.deallocId("fclose")) + return File; + return Library::ismemory(realloctype) ? OtherMem : OtherRes; + } + return No; +} + + +CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok, nonneg int varid) const +{ + if (tok->isCpp() && tok->str() == "delete" && tok->astOperand1()) { + const Token* vartok = tok->astOperand1(); + if (Token::Match(vartok, ".|::")) + vartok = vartok->astOperand2(); + + if (vartok && vartok->varId() == varid) { + if (tok->strAt(1) == "[") + return NewArray; + return New; + } + } + + if (tok->str() == "::") + tok = tok->next(); + + if (Token::Match(tok, "%name% (")) { + if (Token::simpleMatch(tok, "fcloseall ( )")) + return File; + + int argNr = 1; + for (const Token* tok2 = tok->tokAt(2); tok2; tok2 = tok2->nextArgument()) { + const Token* vartok = tok2; + while (Token::Match(vartok, "%name% .|::")) + vartok = vartok->tokAt(2); + + if (Token::Match(vartok, "%varid% )|,|-", varid)) { + if (tok->str() == "realloc" && Token::simpleMatch(vartok->next(), ", 0 )")) + return Malloc; + + if (mSettings_->hasLib("posix")) { + if (tok->str() == "close") + return Fd; + if (tok->str() == "pclose") + return Pipe; + } + + // Does tok point on a Library deallocation function? + const int dealloctype = mSettings_->library.getDeallocId(tok, argNr); + if (dealloctype > 0) { + if (dealloctype == mSettings_->library.deallocId("free")) + return Malloc; + if (dealloctype == mSettings_->library.deallocId("fclose")) + return File; + return Library::ismemory(dealloctype) ? OtherMem : OtherRes; + } + } + argNr++; + } + } + + return No; +} + +bool CheckMemoryLeak::isReopenStandardStream(const Token *tok) const +{ + if (getReallocationType(tok, 0) == File) { + const Library::AllocFunc *f = mSettings_->library.getReallocFuncInfo(tok); + if (f && f->reallocArg > 0 && f->reallocArg <= numberOfArguments(tok)) { + const Token* arg = getArguments(tok).at(f->reallocArg - 1); + if (Token::Match(arg, "stdin|stdout|stderr")) + return true; + } + } + return false; +} + +bool CheckMemoryLeak::isOpenDevNull(const Token *tok) const +{ + if (mSettings_->hasLib("posix") && tok->str() == "open" && numberOfArguments(tok) == 2) { + const Token* arg = getArguments(tok).at(0); + if (Token::simpleMatch(arg, "\"/dev/null\"")) + return true; + } + return false; +} + +//-------------------------------------------------------------------------- + + +//-------------------------------------------------------------------------- + +void CheckMemoryLeak::memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype) const +{ + if (alloctype == CheckMemoryLeak::File || + alloctype == CheckMemoryLeak::Pipe || + alloctype == CheckMemoryLeak::Fd || + alloctype == CheckMemoryLeak::OtherRes) + resourceLeakError(tok, varname); + else + memleakError(tok, varname); +} +//--------------------------------------------------------------------------- + +void CheckMemoryLeak::reportErr(const Token *tok, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe) const +{ + std::list callstack; + + if (tok) + callstack.push_back(tok); + + reportErr(callstack, severity, id, msg, cwe); +} + +void CheckMemoryLeak::reportErr(const std::list &callstack, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe) const +{ + const ErrorMessage errmsg(callstack, mTokenizer_ ? &mTokenizer_->list : nullptr, severity, id, msg, cwe, Certainty::normal); + if (mErrorLogger_) + mErrorLogger_->reportErr(errmsg); + else + Check::writeToErrorList(errmsg); +} + +void CheckMemoryLeak::memleakError(const Token *tok, const std::string &varname) const +{ + reportErr(tok, Severity::error, "memleak", "$symbol:" + varname + "\nMemory leak: $symbol", CWE(401U)); +} + +void CheckMemoryLeak::memleakUponReallocFailureError(const Token *tok, const std::string &reallocfunction, const std::string &varname) const +{ + reportErr(tok, Severity::error, "memleakOnRealloc", "$symbol:" + varname + "\nCommon " + reallocfunction + " mistake: \'$symbol\' nulled but not freed upon failure", CWE(401U)); +} + +void CheckMemoryLeak::resourceLeakError(const Token *tok, const std::string &varname) const +{ + std::string errmsg("Resource leak"); + if (!varname.empty()) + errmsg = "$symbol:" + varname + '\n' + errmsg + ": $symbol"; + reportErr(tok, Severity::error, "resourceLeak", errmsg, CWE(775U)); +} + +void CheckMemoryLeak::deallocuseError(const Token *tok, const std::string &varname) const +{ + reportErr(tok, Severity::error, "deallocuse", "$symbol:" + varname + "\nDereferencing '$symbol' after it is deallocated / released", CWE(416U)); +} + +void CheckMemoryLeak::mismatchAllocDealloc(const std::list &callstack, const std::string &varname) const +{ + reportErr(callstack, Severity::error, "mismatchAllocDealloc", "$symbol:" + varname + "\nMismatching allocation and deallocation: $symbol", CWE(762U)); +} + +CheckMemoryLeak::AllocType CheckMemoryLeak::functionReturnType(const Function* func, std::list *callstack) const +{ + if (!func || !func->hasBody() || !func->functionScope) + return No; + + // Get return pointer.. + const Variable* var = nullptr; + for (const Token *tok2 = func->functionScope->bodyStart; tok2 != func->functionScope->bodyEnd; tok2 = tok2->next()) { + if (const Token *endOfLambda = findLambdaEndToken(tok2)) + tok2 = endOfLambda; + if (tok2->str() == "{" && !tok2->scope()->isExecutable()) + tok2 = tok2->link(); + if (tok2->str() == "return") { + const AllocType allocType = getAllocationType(tok2->next(), 0, callstack); + if (allocType != No) + return allocType; + + if (tok2->scope() != func->functionScope || !tok2->astOperand1()) + return No; + const Token* tok = tok2->astOperand1(); + if (Token::Match(tok, ".|::")) + tok = tok->astOperand2() ? tok->astOperand2() : tok->astOperand1(); + if (tok) + var = tok->variable(); + break; + } + } + + // Not returning pointer value.. + if (!var) + 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? + if (!var->isLocal() || var->isStatic()) + return No; + + // Check if return pointer is allocated.. + AllocType allocType = No; + nonneg int const varid = var->declarationId(); + for (const Token* tok = func->functionScope->bodyStart; tok != func->functionScope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "%varid% =", varid)) { + allocType = getAllocationType(tok->tokAt(2), varid, callstack); + } + if (Token::Match(tok, "= %varid% ;", varid)) { + return No; + } + if (!tok->isC() && Token::Match(tok, "[(,] %varid% [,)]", varid)) { + return No; + } + if (Token::Match(tok, "[(,] & %varid% [.,)]", varid)) { + return No; + } + if (Token::Match(tok, "[;{}] %varid% .", varid)) { + return No; + } + if (allocType == No && tok->str() == "return") + return No; + } + + return allocType; +} + + +static bool notvar(const Token *tok, nonneg int varid) +{ + if (!tok) + return false; + if (Token::Match(tok, "&&|;")) + return notvar(tok->astOperand1(),varid) || notvar(tok->astOperand2(),varid); + if (tok->str() == "(" && Token::Match(tok->astOperand1(), "UNLIKELY|LIKELY")) + return notvar(tok->astOperand2(), varid); + const Token *vartok = astIsVariableComparison(tok, "==", "0"); + return vartok && (vartok->varId() == varid); +} + +static bool ifvar(const Token *tok, nonneg int varid, const std::string &comp, const std::string &rhs) +{ + if (!Token::simpleMatch(tok, "if (")) + return false; + const Token *condition = tok->next()->astOperand2(); + if (condition && condition->str() == "(" && Token::Match(condition->astOperand1(), "UNLIKELY|LIKELY")) + condition = condition->astOperand2(); + if (!condition || condition->str() == "&&") + return false; + + const Token *vartok = astIsVariableComparison(condition, comp, rhs); + return (vartok && vartok->varId() == varid); +} + +//--------------------------------------------------------------------------- +// 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); +//--------------------------------------------------------------------------- + +void CheckMemoryLeakInFunction::checkReallocUsage() +{ + logChecker("CheckMemoryLeakInFunction::checkReallocUsage"); + + // only check functions + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + + // Search for the "var = realloc(var, 100" pattern within this function + for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (tok->varId() > 0 && Token::Match(tok, "%name% =")) { + // Get the parenthesis in "realloc(" + const Token* parTok = tok->next()->astOperand2(); + // Skip casts + while (parTok && parTok->isCast()) + parTok = parTok->astOperand1(); + if (!parTok) + continue; + + const Token *const reallocTok = parTok->astOperand1(); + if (!reallocTok) + continue; + const Library::AllocFunc* f = mSettings->library.getReallocFuncInfo(reallocTok); + if (!(f && f->arg == -1 && mSettings->library.isnotnoreturn(reallocTok))) + continue; + + const AllocType allocType = getReallocationType(reallocTok, tok->varId()); + if (!((allocType == Malloc || allocType == OtherMem))) + continue; + const Token* arg = getArguments(reallocTok).at(f->reallocArg - 1); + while (arg && arg->isCast()) + arg = arg->astOperand1(); + const Token* tok2 = tok; + while (arg && arg->isUnaryOp("*") && tok2 && tok2->astParent() && tok2->astParent()->isUnaryOp("*")) { + arg = arg->astOperand1(); + tok2 = tok2->astParent(); + } + + if (!arg || !tok2) + continue; + + if (!(tok->varId() == arg->varId() && tok->variable() && !tok->variable()->isArgument())) + continue; + + // Check that another copy of the pointer wasn't saved earlier in the function + if (Token::findmatch(scope->bodyStart, "%name% = %varid% ;", tok, tok->varId()) || + Token::findmatch(scope->bodyStart, "[{};] %varid% = *| %var% .| %var%| [;=]", tok, tok->varId())) + continue; + + // Check if the argument is known to be null, which means it is not a memory leak + if (arg->hasKnownIntValue() && arg->getKnownIntValue() == 0) { + continue; + } + + const Token* tokEndRealloc = reallocTok->linkAt(1); + // Check that the allocation isn't followed immediately by an 'if (!var) { error(); }' that might handle failure + if (Token::simpleMatch(tokEndRealloc->next(), "; if (") && + notvar(tokEndRealloc->tokAt(3)->astOperand2(), tok->varId())) { + const Token* tokEndBrace = tokEndRealloc->linkAt(3)->linkAt(1); + if (tokEndBrace && mTokenizer->isScopeNoReturn(tokEndBrace)) + continue; + } + + memleakUponReallocFailureError(tok, reallocTok->str(), tok->str()); + } + } + } +} +//--------------------------------------------------------------------------- + + +//--------------------------------------------------------------------------- +// Checks for memory leaks in classes.. +//--------------------------------------------------------------------------- + + +void CheckMemoryLeakInClass::check() +{ + logChecker("CheckMemoryLeakInClass::check"); + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + // only check classes and structures + for (const Scope * scope : symbolDatabase->classAndStructScopes) { + for (const Variable &var : scope->varlist) { + if (!var.isStatic() && (var.isPointer() || var.isPointerArray())) { + // allocation but no deallocation of private variables in public function.. + const Token *tok = var.typeStartToken(); + // Either it is of standard type or a non-derived type + if (tok->isStandardType() || (var.type() && var.type()->derivedFrom.empty())) { + if (var.isPrivate()) + checkPublicFunctions(scope, var.nameToken()); + + variable(scope, var.nameToken()); + } + } + } + } +} + + +void CheckMemoryLeakInClass::variable(const Scope *scope, const Token *tokVarname) +{ + const std::string& varname = tokVarname->str(); + const int varid = tokVarname->varId(); + const std::string& classname = scope->className; + + // Check if member variable has been allocated and deallocated.. + CheckMemoryLeak::AllocType memberAlloc = CheckMemoryLeak::No; + CheckMemoryLeak::AllocType memberDealloc = CheckMemoryLeak::No; + + bool allocInConstructor = false; + bool deallocInDestructor = false; + + // Inspect member functions + for (const Function &func : scope->functionList) { + const bool constructor = func.isConstructor(); + const bool destructor = func.isDestructor(); + if (!func.hasBody()) { + if (destructor && !func.isDefault()) { // implementation for destructor is not seen and not defaulted => assume it deallocates all variables properly + deallocInDestructor = true; + memberDealloc = CheckMemoryLeak::Many; + } + continue; + } + bool body = false; + const Token *end = func.functionScope->bodyEnd; + for (const Token *tok = func.arg->link(); tok != end; tok = tok->next()) { + if (tok == func.functionScope->bodyStart) + body = true; + else { + if (!body) { + if (!Token::Match(tok, ":|, %varid% (", varid)) + continue; + } + + // Allocate.. + if (!body || Token::Match(tok, "%varid% =|[", varid)) { + // var1 = var2 = ... + // bail out + if (tok->strAt(-1) == "=") + return; + + // Foo::var1 = .. + // bail out when not same class + if (tok->strAt(-1) == "::" && + tok->strAt(-2) != scope->className) + return; + + const Token* allocTok = tok->tokAt(body ? 2 : 3); + if (tok->astParent() && tok->astParent()->str() == "[" && tok->astParent()->astParent()) + allocTok = tok->astParent()->astParent()->astOperand2(); + + AllocType alloc = getAllocationType(allocTok, 0); + if (alloc != CheckMemoryLeak::No) { + if (constructor) + allocInConstructor = true; + + if (memberAlloc != No && memberAlloc != alloc) + alloc = CheckMemoryLeak::Many; + + if (alloc != CheckMemoryLeak::Many && memberDealloc != CheckMemoryLeak::No && memberDealloc != CheckMemoryLeak::Many && memberDealloc != alloc) { + mismatchAllocDealloc({tok}, classname + "::" + varname); + } + + memberAlloc = alloc; + } + } + + if (!body) + continue; + + // Deallocate.. + AllocType dealloc = getDeallocationType(tok, varid); + // some usage in the destructor => assume it's related + // to deallocation + if (destructor && tok->str() == varname) + dealloc = CheckMemoryLeak::Many; + if (dealloc != CheckMemoryLeak::No) { + if (destructor) + deallocInDestructor = true; + + if (dealloc != CheckMemoryLeak::Many && memberAlloc != CheckMemoryLeak::No && memberAlloc != Many && memberAlloc != dealloc) { + mismatchAllocDealloc({tok}, classname + "::" + varname); + } + + // several types of allocation/deallocation? + if (memberDealloc != CheckMemoryLeak::No && memberDealloc != dealloc) + dealloc = CheckMemoryLeak::Many; + + memberDealloc = dealloc; + } + + // Function call .. possible deallocation + else if (Token::Match(tok->previous(), "[{};] %name% (") && !tok->isKeyword() && !mSettings->library.isLeakIgnore(tok->str())) { + return; + } + } + } + } + + if (allocInConstructor && !deallocInDestructor) { + unsafeClassError(tokVarname, classname, classname + "::" + varname /*, memberAlloc*/); + } else if (memberAlloc != CheckMemoryLeak::No && memberDealloc == CheckMemoryLeak::No) { + unsafeClassError(tokVarname, classname, classname + "::" + varname /*, memberAlloc*/); + } +} + +void CheckMemoryLeakInClass::unsafeClassError(const Token *tok, const std::string &classname, const std::string &varname) +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unsafeClassCanLeak")) + return; + + reportError(tok, Severity::style, "unsafeClassCanLeak", + "$symbol:" + classname + "\n" + "$symbol:" + varname + "\n" + "Class '" + classname + "' is unsafe, '" + varname + "' can leak by wrong usage.\n" + "The class '" + classname + "' is unsafe, wrong usage can cause memory/resource leaks for '" + varname + "'. This can for instance be fixed by adding proper cleanup in the destructor.", CWE398, Certainty::normal); +} + + +void CheckMemoryLeakInClass::checkPublicFunctions(const Scope *scope, const Token *classtok) +{ + // Check that public functions deallocate the pointers that they allocate. + // There is no checking how these functions are used and therefore it + // isn't established if there is real leaks or not. + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + const int varid = classtok->varId(); + + // Parse public functions.. + // If they allocate member variables, they should also deallocate + for (const Function &func : scope->functionList) { + if ((func.type == Function::eFunction || func.type == Function::eOperatorEqual) && + func.access == AccessControl::Public && func.hasBody()) { + const Token *tok2 = func.functionScope->bodyStart->next(); + if (Token::Match(tok2, "%varid% =", varid)) { + const CheckMemoryLeak::AllocType alloc = getAllocationType(tok2->tokAt(2), varid); + if (alloc != CheckMemoryLeak::No) + publicAllocationError(tok2, tok2->str()); + } else if (Token::Match(tok2, "%type% :: %varid% =", varid) && + tok2->str() == scope->className) { + const CheckMemoryLeak::AllocType alloc = getAllocationType(tok2->tokAt(4), varid); + if (alloc != CheckMemoryLeak::No) + publicAllocationError(tok2, tok2->strAt(2)); + } + } + } +} + +void CheckMemoryLeakInClass::publicAllocationError(const Token *tok, const std::string &varname) +{ + reportError(tok, Severity::warning, "publicAllocationError", "$symbol:" + varname + "\nPossible leak in public function. The pointer '$symbol' is not deallocated before it is allocated.", CWE398, Certainty::normal); +} + + +void CheckMemoryLeakStructMember::check() +{ + if (mSettings->clang) + return; + + logChecker("CheckMemoryLeakStructMember::check"); + + const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Variable* var : symbolDatabase->variableList()) { + if (!var || (!var->isLocal() && !(var->isArgument() && var->scope())) || var->isStatic()) + continue; + if (var->isReference() || (var->valueType() && var->valueType()->pointer > 1)) + continue; + if (var->typeEndToken()->isStandardType()) + continue; + checkStructVariable(var); + } +} + +bool CheckMemoryLeakStructMember::isMalloc(const Variable *variable) const +{ + if (!variable) + return false; + const int declarationId(variable->declarationId()); + bool alloc = false; + for (const Token *tok2 = variable->nameToken(); tok2 && tok2 != variable->scope()->bodyEnd; tok2 = tok2->next()) { + if (Token::Match(tok2, "= %varid% [;=]", declarationId)) + return false; + if (Token::Match(tok2, "%varid% = %name% (", declarationId) && mSettings->library.getAllocFuncInfo(tok2->tokAt(2))) + alloc = true; + } + return alloc; +} + +void CheckMemoryLeakStructMember::checkStructVariable(const Variable* const variable) const +{ + if (!variable) + return; + // Is struct variable a pointer? + if (variable->isArrayOrPointer()) { + // Check that variable is allocated with malloc + if (!isMalloc(variable)) + return; + } else if (!mTokenizer->isC() && (!variable->typeScope() || variable->typeScope()->getDestructor())) { + // For non-C code a destructor might cleanup members + return; + } + + // Check struct.. + int indentlevel2 = 0; + + auto deallocInFunction = [this](const Token* tok, int structid) -> bool { + // Calling non-function / function that doesn't deallocate? + if (tok->isKeyword() || mSettings->library.isLeakIgnore(tok->str())) + return false; + + // Check if the struct is used.. + bool deallocated = false; + const Token* const end = tok->linkAt(1); + for (const Token* tok2 = tok; tok2 != end; tok2 = tok2->next()) { + if (Token::Match(tok2, "[(,] &| %varid% [,)]", structid)) { + /** @todo check if the function deallocates the memory */ + deallocated = true; + break; + } + + if (Token::Match(tok2, "[(,] &| %varid% . %name% [,)]", structid)) { + /** @todo check if the function deallocates the memory */ + deallocated = true; + break; + } + } + + return deallocated; + }; + + // return { memberTok, rhsTok } + auto isMemberAssignment = [](const Token* varTok, int varId) -> std::pair { + if (varTok->varId() != varId) + return {}; + const Token* top = varTok; + while (top->astParent()) { + if (Token::Match(top->astParent(), "(|[")) + return {}; + top = top->astParent(); + } + if (!Token::simpleMatch(top, "=") || !precedes(varTok, top)) + return {}; + const Token* dot = top->astOperand1(); + while (dot && dot->str() != ".") + dot = dot->astOperand1(); + if (!dot) + return {}; + return { dot->astOperand2(), top->next() }; + }; + std::pair assignToks; + + const Token* tokStart = variable->nameToken(); + if (variable->isArgument() && variable->scope()) + tokStart = variable->scope()->bodyStart->next(); + for (const Token *tok2 = tokStart; tok2 && tok2 != variable->scope()->bodyEnd; tok2 = tok2->next()) { + if (tok2->str() == "{") + ++indentlevel2; + + else if (tok2->str() == "}") { + if (indentlevel2 == 0) + break; + --indentlevel2; + } + + // Unknown usage of struct + /** @todo Check how the struct is used. Only bail out if necessary */ + else if (Token::Match(tok2, "[(,] %varid% [,)]", variable->declarationId())) + break; + + // Struct member is allocated => check if it is also properly deallocated.. + else if ((assignToks = isMemberAssignment(tok2, variable->declarationId())).first && assignToks.first->varId()) { + if (getAllocationType(assignToks.second, assignToks.first->varId()) == AllocType::No) + continue; + + if (variable->isArgument() && variable->valueType() && variable->valueType()->type == ValueType::UNKNOWN_TYPE && assignToks.first->astParent()) { + const Token* accessTok = assignToks.first->astParent(); + while (Token::simpleMatch(accessTok->astOperand1(), ".")) + accessTok = accessTok->astOperand1(); + if (Token::simpleMatch(accessTok, ".") && accessTok->originalName() == "->") + continue; + } + + const int structid(variable->declarationId()); + const int structmemberid(assignToks.first->varId()); + + // This struct member is allocated.. check that it is deallocated + int indentlevel3 = indentlevel2; + for (const Token *tok3 = tok2; tok3; tok3 = tok3->next()) { + if (tok3->str() == "{") + ++indentlevel3; + + else if (tok3->str() == "}") { + if (indentlevel3 == 0) { + memoryLeak(tok3, variable->name() + "." + tok2->strAt(2), Malloc); + break; + } + --indentlevel3; + } + + // Deallocating the struct member.. + else if (getDeallocationType(tok3, structmemberid) != AllocType::No) { + // If the deallocation happens at the base level, don't check this member anymore + if (indentlevel3 == 0) + break; + + // deallocating and then returning from function in a conditional block => + // skip ahead out of the block + bool ret = false; + while (tok3) { + if (tok3->str() == "return") + ret = true; + else if (tok3->str() == "{" || tok3->str() == "}") + break; + tok3 = tok3->next(); + } + if (!ret || !tok3 || tok3->str() != "}") + break; + --indentlevel3; + continue; + } + + // Deallocating the struct.. + else if (Token::Match(tok3, "%name% ( %varid% )", structid) && mSettings->library.getDeallocFuncInfo(tok3)) { + if (indentlevel2 == 0) + memoryLeak(tok3, variable->name() + "." + tok2->strAt(2), Malloc); + break; + } + + // failed allocation => skip code.. + else if (Token::simpleMatch(tok3, "if (") && + notvar(tok3->next()->astOperand2(), structmemberid)) { + // Goto the ")" + tok3 = tok3->next()->link(); + + // make sure we have ") {".. it should be + if (!Token::simpleMatch(tok3, ") {")) + break; + + // Goto the "}" + tok3 = tok3->next()->link(); + } + + // succeeded allocation + else if (ifvar(tok3, structmemberid, "!=", "0")) { + // goto the ")" + tok3 = tok3->next()->link(); + + // check if the variable is deallocated or returned.. + int indentlevel4 = 0; + for (const Token *tok4 = tok3; tok4; tok4 = tok4->next()) { + if (tok4->str() == "{") + ++indentlevel4; + else if (tok4->str() == "}") { + --indentlevel4; + if (indentlevel4 == 0) + break; + } else if (Token::Match(tok4, "%name% ( %var% . %varid% )", structmemberid) && mSettings->library.getDeallocFuncInfo(tok4)) { + break; + } + } + + // was there a proper deallocation? + if (indentlevel4 > 0) + break; + } + + // Returning from function.. + else if ((tok3->scope()->type != Scope::ScopeType::eLambda || tok3->scope() == variable->scope()) && tok3->str() == "return") { + // Returning from function without deallocating struct member? + if (!Token::Match(tok3, "return %varid% ;", structid) && + !Token::Match(tok3, "return & %varid%", structid) && + !(Token::Match(tok3, "return %varid% . %var%", structid) && tok3->tokAt(3)->varId() == structmemberid) && + !(Token::Match(tok3, "return %name% (") && tok3->astOperand1() && deallocInFunction(tok3->astOperand1(), 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, "%name% (")) { + if (deallocInFunction(tok3, structid)) + break; + } + } + } + } +} + + + +void CheckMemoryLeakNoVar::check() +{ + logChecker("CheckMemoryLeakNoVar::check"); + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + // only check functions + for (const Scope * scope : symbolDatabase->functionScopes) { + + // Checks if a call to an allocation function like malloc() is made and its return value is not assigned. + checkForUnusedReturnValue(scope); + + // Checks to see if a function is called with memory allocated for an argument that + // could be leaked if a function called for another argument throws. + checkForUnsafeArgAlloc(scope); + + // Check for leaks where a the return value of an allocation function like malloc() is an input argument, + // for example f(malloc(1)), where f is known to not release the input argument. + checkForUnreleasedInputArgument(scope); + } +} + +//--------------------------------------------------------------------------- +// Checks if an input argument to a function is the return value of an allocation function +// like malloc(), and the function does not release it. +//--------------------------------------------------------------------------- +void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope) +{ + // parse the executable scope until tok is reached... + for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + // allocating memory in parameter for function call.. + if (tok->varId() || !Token::Match(tok, "%name% (")) + continue; + + // check if the output of the function is assigned + const Token* tok2 = tok->next()->astParent(); + while (tok2 && (tok2->isCast() || Token::Match(tok2, "?|:"))) + tok2 = tok2->astParent(); + if (Token::Match(tok2, "%assign%")) // TODO: check if function returns allocated resource + continue; + if (Token::simpleMatch(tok->astTop(), "return")) + continue; + + const std::string& functionName = tok->str(); + if ((tok->isCpp() && functionName == "delete") || + functionName == "return") + continue; + + if (Token::simpleMatch(tok->next()->astParent(), "(")) // passed to another function + continue; + if (!tok->isKeyword() && !tok->function() && !mSettings->library.isLeakIgnore(functionName)) + continue; + + const std::vector args = getArguments(tok); + int argnr = -1; + for (const Token* arg : args) { + ++argnr; + if (arg->isOp() && !(tok->isKeyword() && arg->str() == "*")) // e.g. switch (*new int) + continue; + while (arg->astOperand1()) { + if (arg->isCpp() && Token::simpleMatch(arg, "new")) + break; + arg = arg->astOperand1(); + } + const AllocType alloc = getAllocationType(arg, 0); + if (alloc == No) + continue; + if (alloc == New || alloc == NewArray) { + const Token* typeTok = arg->next(); + bool bail = !typeTok->isStandardType() && + !mSettings->library.detectContainerOrIterator(typeTok) && + !mSettings->library.podtype(typeTok->expressionString()); + if (bail && typeTok->type() && typeTok->type()->classScope && + typeTok->type()->classScope->numConstructors == 0 && + typeTok->type()->classScope->getDestructor() == nullptr) { + bail = false; + } + if (bail) + continue; + } + if (isReopenStandardStream(arg)) + continue; + if (tok->function()) { + const Variable* argvar = tok->function()->getArgumentVar(argnr); + if (!argvar || !argvar->valueType()) + continue; + const MathLib::bigint argSize = argvar->valueType()->typeSize(mSettings->platform, /*p*/ true); + if (argSize <= 0 || argSize >= mSettings->platform.sizeof_pointer) + continue; + } + functionCallLeak(arg, arg->str(), functionName); + } + + } +} + +//--------------------------------------------------------------------------- +// Checks if a call to an allocation function like malloc() is made and its return value is not assigned. +//--------------------------------------------------------------------------- +void CheckMemoryLeakNoVar::checkForUnusedReturnValue(const Scope *scope) +{ + for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + const bool isNew = tok->isCpp() && tok->str() == "new"; + if (!isNew && !Token::Match(tok, "%name% (")) + continue; + + if (tok->varId()) + continue; + + const AllocType allocType = getAllocationType(tok, 0); + if (allocType == No) + continue; + + if (tok != tok->next()->astOperand1() && !isNew) + continue; + + if (isReopenStandardStream(tok)) + continue; + if (isOpenDevNull(tok)) + continue; + + // get ast parent, skip casts + const Token *parent = isNew ? tok->astParent() : tok->next()->astParent(); + while (parent && parent->isCast()) + parent = parent->astParent(); + + bool warn = true; + if (isNew) { + const Token* typeTok = tok->next(); + warn = typeTok && (typeTok->isStandardType() || mSettings->library.detectContainer(typeTok)); + } + + if (!parent && warn) { + // Check if we are in a C++11 constructor + const Token * closingBrace = Token::findmatch(tok, "}|;"); + if (closingBrace->str() == "}" && Token::Match(closingBrace->link()->tokAt(-1), "%name%") && (!isNew && precedes(tok, closingBrace->link()))) + continue; + returnValueNotUsedError(tok, tok->str()); + } else if (Token::Match(parent, "%comp%|!|,|%oror%|&&|:")) { + if (parent->astParent() && parent->str() == ",") + continue; + if (parent->str() == ":") { + if (!(Token::simpleMatch(parent->astParent(), "?") && !parent->astParent()->astParent())) + continue; + } + returnValueNotUsedError(tok, tok->str()); + } + } +} + +//--------------------------------------------------------------------------- +// Check if an exception could cause a leak in an argument constructed with +// shared_ptr/unique_ptr. For example, in the following code, it is possible +// that if g() throws an exception, the memory allocated by "new int(42)" +// could be leaked. See stackoverflow.com/questions/19034538/ +// why-is-there-memory-leak-while-using-shared-ptr-as-a-function-parameter +// +// void x() { +// f(shared_ptr(new int(42)), g()); +// } +//--------------------------------------------------------------------------- +void CheckMemoryLeakNoVar::checkForUnsafeArgAlloc(const Scope *scope) +{ + // This test only applies to C++ source + if (!mTokenizer->isCPP() || !mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning)) + return; + + for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "%name% (")) { + const Token *endParamToken = tok->next()->link(); + const Token* pointerType = nullptr; + const Token* functionCalled = nullptr; + + // Scan through the arguments to the function call + for (const Token *tok2 = tok->tokAt(2); tok2 && tok2 != endParamToken; tok2 = tok2->nextArgument()) { + const Function *func = tok2->function(); + const bool isNothrow = func && (func->isAttributeNothrow() || func->isThrow()); + + if (Token::Match(tok2, "shared_ptr|unique_ptr <") && Token::Match(tok2->next()->link(), "> ( new %name%")) { + pointerType = tok2; + } else if (!isNothrow) { + if (Token::Match(tok2, "%name% (")) + functionCalled = tok2; + else if (tok2->isName() && Token::simpleMatch(tok2->next()->link(), "> (")) + functionCalled = tok2; + } + } + + if (pointerType && functionCalled) { + std::string functionName = functionCalled->str(); + if (functionCalled->strAt(1) == "<") { + functionName += '<'; + for (const Token* tok2 = functionCalled->tokAt(2); tok2 != functionCalled->next()->link(); tok2 = tok2->next()) + functionName += tok2->str(); + functionName += '>'; + } + std::string objectTypeName; + for (const Token* tok2 = pointerType->tokAt(2); tok2 != pointerType->next()->link(); tok2 = tok2->next()) + objectTypeName += tok2->str(); + + unsafeArgAllocError(tok, functionName, pointerType->str(), objectTypeName); + } + } + } +} + +void CheckMemoryLeakNoVar::functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall) +{ + reportError(loc, Severity::error, "leakNoVarFunctionCall", "Allocation with " + alloc + ", " + functionCall + " doesn't release it.", CWE772, Certainty::normal); +} + +void CheckMemoryLeakNoVar::returnValueNotUsedError(const Token *tok, const std::string &alloc) +{ + reportError(tok, Severity::error, "leakReturnValNotUsed", "$symbol:" + alloc + "\nReturn value of allocation function '$symbol' is not stored.", CWE771, Certainty::normal); +} + +void CheckMemoryLeakNoVar::unsafeArgAllocError(const Token *tok, const std::string &funcName, const std::string &ptrType, const std::string& objType) +{ + const std::string factoryFunc = ptrType == "shared_ptr" ? "make_shared" : "make_unique"; + reportError(tok, Severity::warning, "leakUnsafeArgAlloc", + "$symbol:" + funcName + "\n" + "Unsafe allocation. If $symbol() throws, memory could be leaked. Use " + factoryFunc + "<" + objType + ">() instead.", + CWE401, + Certainty::inconclusive); // Inconclusive because funcName may never throw +} diff --git a/cppcheck-2.14.0/lib/checkmemoryleak.h b/cppcheck-2.14.0/lib/checkmemoryleak.h new file mode 100644 index 00000000..de13039c --- /dev/null +++ b/cppcheck-2.14.0/lib/checkmemoryleak.h @@ -0,0 +1,364 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef checkmemoryleakH +#define checkmemoryleakH +//--------------------------------------------------------------------------- + +/** + * @file + * + * %Check for memory leaks + * + * The checking is split up into three specialized classes. + * - CheckMemoryLeakInFunction can detect when a function variable is allocated but not deallocated properly. + * - CheckMemoryLeakInClass can detect when a class variable is allocated but not deallocated properly. + * - CheckMemoryLeakStructMember checks allocation/deallocation of structs and struct members + */ + +#include "check.h" +#include "config.h" +#include "tokenize.h" + +#include +#include + +class Function; +class Scope; +class Settings; +class Token; +class Variable; +class ErrorLogger; +struct CWE; +enum class Severity; + +/// @addtogroup Core +/// @{ + +/** @brief Base class for memory leaks checking */ +class CPPCHECKLIB CheckMemoryLeak { +private: + /** For access to the tokens */ + const Tokenizer * const mTokenizer_; + + /** ErrorLogger used to report errors */ + ErrorLogger * const mErrorLogger_; + + /** Enabled standards */ + const Settings * const mSettings_; + + /** + * Report error. Similar with the function Check::reportError + * @param tok the token where the error occurs + * @param severity the severity of the bug + * @param id type of message + * @param msg text + * @param cwe cwe number + */ + void reportErr(const Token *tok, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe) const; + + /** + * Report error. Similar with the function Check::reportError + * @param callstack callstack of error + * @param severity the severity of the bug + * @param id type of message + * @param msg text + * @param cwe cwe number + */ + void reportErr(const std::list &callstack, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe) const; + +public: + CheckMemoryLeak() = delete; + CheckMemoryLeak(const CheckMemoryLeak &) = delete; + CheckMemoryLeak& operator=(const CheckMemoryLeak &) = delete; + + CheckMemoryLeak(const Tokenizer *t, ErrorLogger *e, const Settings *s) + : mTokenizer_(t), mErrorLogger_(e), mSettings_(s) {} + + /** @brief What type of allocation are used.. the "Many" means that several types of allocation and deallocation are used */ + enum AllocType { No, Malloc, New, NewArray, File, Fd, Pipe, OtherMem, OtherRes, Many }; + + void memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype) const; + + /** + * @brief Get type of deallocation at given position + * @param tok position + * @param varid variable id + * @return type of deallocation + */ + AllocType getDeallocationType(const Token *tok, nonneg int varid) const; + + /** + * @brief Get type of allocation at given position + */ + AllocType getAllocationType(const Token *tok2, nonneg int varid, std::list *callstack = nullptr) const; + + /** + * @brief Get type of reallocation at given position + */ + AllocType getReallocationType(const Token *tok2, nonneg int varid) const; + + /** + * Check if token reopens a standard stream + * @param tok token to check + */ + bool isReopenStandardStream(const Token *tok) const; + /** + * Check if token opens /dev/null + * @param tok token to check + */ + bool isOpenDevNull(const Token *tok) const; + /** + * Report that there is a memory leak (new/malloc/etc) + * @param tok token where memory is leaked + * @param varname name of variable + */ + void memleakError(const Token *tok, const std::string &varname) const; + + /** + * Report that there is a resource leak (fopen/popen/etc) + * @param tok token where resource is leaked + * @param varname name of variable + */ + void resourceLeakError(const Token *tok, const std::string &varname) const; + + void deallocuseError(const Token *tok, const std::string &varname) const; + void mismatchAllocDealloc(const std::list &callstack, const std::string &varname) const; + void memleakUponReallocFailureError(const Token *tok, const std::string &reallocfunction, const std::string &varname) const; + + /** What type of allocated memory does the given function return? */ + AllocType functionReturnType(const Function* func, std::list *callstack = nullptr) const; +}; + +/// @} + + + +/// @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 : public Check, public CheckMemoryLeak { + friend class TestMemleakInFunction; + +public: + /** @brief This constructor is used when registering this class */ + CheckMemoryLeakInFunction() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {} + +private: + /** @brief This constructor is used when running checks */ + CheckMemoryLeakInFunction(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {} + + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, &tokenizer.getSettings(), errorLogger); + checkMemoryLeak.checkReallocUsage(); + } + + /** + * Checking for a memory leak caused by improper realloc usage. + */ + void checkReallocUsage(); + + /** Report all possible errors (for the --errorlist) */ + void getErrorMessages(ErrorLogger *e, const Settings *settings) const override { + CheckMemoryLeakInFunction c(nullptr, settings, e); + c.memleakError(nullptr, "varname"); + c.resourceLeakError(nullptr, "varname"); + c.deallocuseError(nullptr, "varname"); + const std::list callstack; + c.mismatchAllocDealloc(callstack, "varname"); + c.memleakUponReallocFailureError(nullptr, "realloc", "varname"); + } + + /** + * Get name of class (--doc) + * @return name of class + */ + static std::string myName() { + return "Memory leaks (function variables)"; + } + + /** + * Get class information (--doc) + * @return Wiki formatted information about this class + */ + std::string classInfo() const override { + return "Is there any allocated memory when a function goes out of scope\n"; + } +}; + + + +/** + * @brief %Check class variables, variables that are allocated in the constructor should be deallocated in the destructor + */ + +class CPPCHECKLIB CheckMemoryLeakInClass : public Check, private CheckMemoryLeak { + friend class TestMemleakInClass; + +public: + CheckMemoryLeakInClass() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {} + +private: + CheckMemoryLeakInClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {} + + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + if (!tokenizer.isCPP()) + return; + + CheckMemoryLeakInClass checkMemoryLeak(&tokenizer, &tokenizer.getSettings(), errorLogger); + checkMemoryLeak.check(); + } + + void check(); + + void variable(const Scope *scope, const Token *tokVarname); + + /** Public functions: possible double-allocation */ + void checkPublicFunctions(const Scope *scope, const Token *classtok); + void publicAllocationError(const Token *tok, const std::string &varname); + + void unsafeClassError(const Token *tok, const std::string &classname, const std::string &varname); + + void getErrorMessages(ErrorLogger *e, const Settings *settings) const override { + CheckMemoryLeakInClass c(nullptr, settings, e); + c.publicAllocationError(nullptr, "varname"); + c.unsafeClassError(nullptr, "class", "class::varname"); + } + + static std::string myName() { + return "Memory leaks (class variables)"; + } + + std::string classInfo() const override { + return "If the constructor allocate memory then the destructor must deallocate it.\n"; + } +}; + + + +/** @brief detect simple memory leaks for struct members */ + +class CPPCHECKLIB CheckMemoryLeakStructMember : public Check, private CheckMemoryLeak { + friend class TestMemleakStructMember; + +public: + CheckMemoryLeakStructMember() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {} + +private: + CheckMemoryLeakStructMember(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {} + + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + CheckMemoryLeakStructMember checkMemoryLeak(&tokenizer, &tokenizer.getSettings(), errorLogger); + checkMemoryLeak.check(); + } + + void check(); + + /** Is local variable allocated with malloc? */ + bool isMalloc(const Variable *variable) const; + + void checkStructVariable(const Variable* const variable) const; + + void getErrorMessages(ErrorLogger * /*errorLogger*/, const Settings * /*settings*/) const override {} + + static std::string myName() { + return "Memory leaks (struct members)"; + } + + std::string classInfo() const override { + return "Don't forget to deallocate struct members\n"; + } +}; + + + +/** @brief detect simple memory leaks (address not taken) */ + +class CPPCHECKLIB CheckMemoryLeakNoVar : public Check, private CheckMemoryLeak { + friend class TestMemleakNoVar; + +public: + CheckMemoryLeakNoVar() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {} + +private: + CheckMemoryLeakNoVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {} + + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + CheckMemoryLeakNoVar checkMemoryLeak(&tokenizer, &tokenizer.getSettings(), errorLogger); + checkMemoryLeak.check(); + } + + void check(); + + /** + * @brief %Check if an input argument to a function is the return value of an allocation function + * like malloc(), and the function does not release it. + * @param scope The scope of the function to check. + */ + void checkForUnreleasedInputArgument(const Scope *scope); + + /** + * @brief %Check if a call to an allocation function like malloc() is made and its return value is not assigned. + * @param scope The scope of the function to check. + */ + void checkForUnusedReturnValue(const Scope *scope); + + /** + * @brief %Check if an exception could cause a leak in an argument constructed with shared_ptr/unique_ptr. + * @param scope The scope of the function to check. + */ + void checkForUnsafeArgAlloc(const Scope *scope); + + void functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall); + void returnValueNotUsedError(const Token* tok, const std::string &alloc); + void unsafeArgAllocError(const Token *tok, const std::string &funcName, const std::string &ptrType, const std::string &objType); + + void getErrorMessages(ErrorLogger *e, const Settings *settings) const override { + CheckMemoryLeakNoVar c(nullptr, settings, e); + c.functionCallLeak(nullptr, "funcName", "funcName"); + c.returnValueNotUsedError(nullptr, "funcName"); + c.unsafeArgAllocError(nullptr, "funcName", "shared_ptr", "int"); + } + + static std::string myName() { + return "Memory leaks (address not taken)"; + } + + std::string classInfo() const override { + return "Not taking the address to allocated memory\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkmemoryleakH diff --git a/cppcheck-2.14.0/lib/checknullpointer.cpp b/cppcheck-2.14.0/lib/checknullpointer.cpp new file mode 100644 index 00000000..9c5d2509 --- /dev/null +++ b/cppcheck-2.14.0/lib/checknullpointer.cpp @@ -0,0 +1,650 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#include "checknullpointer.h" + +#include "astutils.h" +#include "ctu.h" +#include "errorlogger.h" +#include "errortypes.h" +#include "library.h" +#include "mathlib.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "valueflow.h" + +#include +#include +#include +#include +#include + +//--------------------------------------------------------------------------- + +// CWE ids used: +static const CWE CWE_NULL_POINTER_DEREFERENCE(476U); +static const CWE CWE_INCORRECT_CALCULATION(682U); + +// Register this check class (by creating a static instance of it) +namespace { + CheckNullPointer instance; +} + +//--------------------------------------------------------------------------- + +static bool checkNullpointerFunctionCallPlausibility(const Function* func, unsigned int arg) +{ + return !func || (func->argCount() >= arg && func->getArgumentVar(arg - 1) && func->getArgumentVar(arg - 1)->isPointer()); +} + +/** + * @brief parse a function call and extract information about variable usage + * @param tok first token + * @param var variables that the function read / write. + * @param library --library files data + */ +void CheckNullPointer::parseFunctionCall(const Token &tok, std::list &var, const Library &library) +{ + if (Token::Match(&tok, "%name% ( )") || !tok.tokAt(2)) + return; + + const std::vector args = getArguments(&tok); + + for (int argnr = 1; argnr <= args.size(); ++argnr) { + const Token *param = args[argnr - 1]; + if (library.isnullargbad(&tok, argnr) && checkNullpointerFunctionCallPlausibility(tok.function(), argnr)) + var.push_back(param); + else if (tok.function()) { + const Variable* argVar = tok.function()->getArgumentVar(argnr-1); + if (argVar && argVar->isStlStringType() && !argVar->isArrayOrPointer()) + var.push_back(param); + } + } + + if (library.formatstr_function(&tok)) { + const int formatStringArgNr = library.formatstr_argno(&tok); + if (formatStringArgNr < 0 || formatStringArgNr >= args.size()) + return; + + // 1st parameter.. + if (Token::Match(&tok, "snprintf|vsnprintf|fnprintf|vfnprintf") && args.size() > 1 && !(args[1] && args[1]->hasKnownIntValue() && args[1]->getKnownIntValue() == 0)) // Only if length (second parameter) is not zero + var.push_back(args[0]); + + if (args[formatStringArgNr]->tokType() != Token::eString) + return; + const std::string &formatString = args[formatStringArgNr]->strValue(); + int argnr = formatStringArgNr + 1; + const bool scan = library.formatstr_scan(&tok); + + bool percent = false; + for (std::string::const_iterator i = formatString.cbegin(); i != formatString.cend(); ++i) { + if (*i == '%') { + percent = !percent; + } else if (percent) { + percent = false; + + bool _continue = false; + while (!std::isalpha((unsigned char)*i)) { + if (*i == '*') { + if (scan) + _continue = true; + else + argnr++; + } + ++i; + if (i == formatString.end()) + return; + } + if (_continue) + continue; + + if (argnr < args.size() && (*i == 'n' || *i == 's' || scan)) + var.push_back(args[argnr]); + + if (*i != 'm') // %m is a non-standard glibc extension that requires no parameter + argnr++; + } + } + } +} + +namespace { + const std::set stl_stream = { + "fstream", "ifstream", "iostream", "istream", + "istringstream", "ofstream", "ostream", "ostringstream", + "stringstream", "wistringstream", "wostringstream", "wstringstream" + }; +} + +/** + * Is there a pointer dereference? Everything that should result in + * a nullpointer dereference error message will result in a true + * return value. If it's unknown if the pointer is dereferenced false + * is returned. + * @param tok token for the pointer + * @param unknown it is not known if there is a pointer dereference (could be reported as a debug message) + * @return true => there is a dereference + */ +bool CheckNullPointer::isPointerDeRef(const Token *tok, bool &unknown) const +{ + return isPointerDeRef(tok, unknown, *mSettings); +} + +bool CheckNullPointer::isPointerDeRef(const Token *tok, bool &unknown, const Settings &settings) +{ + unknown = false; + + // Is pointer used as function parameter? + if (Token::Match(tok->previous(), "[(,] %name% [,)]")) { + const Token *ftok = tok->previous(); + while (ftok && ftok->str() != "(") { + if (ftok->str() == ")") + ftok = ftok->link(); + ftok = ftok->previous(); + } + if (ftok && ftok->previous()) { + std::list varlist; + parseFunctionCall(*ftok->previous(), varlist, settings.library); + if (std::find(varlist.cbegin(), varlist.cend(), tok) != varlist.cend()) { + return true; + } + } + } + + if (tok->str() == "(" && !tok->scope()->isExecutable()) + return false; + + const Token* parent = tok->astParent(); + if (!parent) + return false; + const bool addressOf = parent->astParent() && parent->astParent()->str() == "&"; + if (parent->str() == "." && astIsRHS(tok)) + return isPointerDeRef(parent, unknown, settings); + const bool firstOperand = parent->astOperand1() == tok; + parent = astParentSkipParens(tok); + if (!parent) + return false; + + // Dereferencing pointer.. + const Token* grandParent = parent->astParent(); + if (parent->isUnaryOp("*") && !(grandParent && isUnevaluated(grandParent->previous()))) { + // declaration of function pointer + if (tok->variable() && tok->variable()->nameToken() == tok) + return false; + if (!addressOf) + return true; + } + + // array access + if (firstOperand && parent->str() == "[" && !addressOf) + return true; + + // address of member variable / array element + const Token *parent2 = parent; + while (Token::Match(parent2, "[|.")) + parent2 = parent2->astParent(); + if (parent2 != parent && parent2 && parent2->isUnaryOp("&")) + return false; + + // read/write member variable + if (firstOperand && parent->originalName() == "->" && !addressOf) + return true; + + // If its a function pointer then check if its called + if (tok->variable() && tok->variable()->isPointer() && Token::Match(tok->variable()->nameToken(), "%name% ) (") && + Token::Match(tok, "%name% (")) + return true; + + if (Token::Match(tok, "%var% = %var% .") && + tok->varId() == tok->tokAt(2)->varId()) + return true; + + // std::string dereferences nullpointers + if (Token::Match(parent->tokAt(-3), "std :: string|wstring (|{ %name% )|}")) + return true; + if (Token::Match(parent->previous(), "%name% (|{ %name% )|}")) { + const Variable* var = tok->tokAt(-2)->variable(); + if (var && !var->isPointer() && !var->isArray() && var->isStlStringType()) + return true; + } + + // streams dereference nullpointers + if (Token::Match(parent, "<<|>>") && !firstOperand) { + const Variable* var = tok->variable(); + if (var && var->isPointer() && Token::Match(var->typeStartToken(), "char|wchar_t")) { // Only outputting or reading to char* can cause problems + const Token* tok2 = parent; // Find start of statement + for (; tok2; tok2 = tok2->previous()) { + if (Token::Match(tok2->previous(), ";|{|}|:")) + break; + } + if (Token::Match(tok2, "std :: cout|cin|cerr")) + return true; + if (tok2 && tok2->varId() != 0) { + const Variable* var2 = tok2->variable(); + if (var2 && var2->isStlType(stl_stream)) + return true; + } + } + } + + const Variable *ovar = nullptr; + if (Token::Match(parent, "+|==|!=") || (parent->str() == "=" && !firstOperand)) { + if (parent->astOperand1() == tok && parent->astOperand2()) + ovar = parent->astOperand2()->variable(); + else if (parent->astOperand1() && parent->astOperand2() == tok) + ovar = parent->astOperand1()->variable(); + } + if (ovar && !ovar->isPointer() && !ovar->isArray() && ovar->isStlStringType()) + return true; + + // assume that it's not a dereference (no false positives) + return false; +} + + +static bool isNullablePointer(const Token* tok) +{ + if (!tok) + return false; + if (Token::simpleMatch(tok, "new") && tok->varId() == 0) + return false; + if (astIsPointer(tok)) + return true; + if (astIsSmartPointer(tok)) + return true; + if (Token::simpleMatch(tok, ".")) + return isNullablePointer(tok->astOperand2()); + if (const Variable* var = tok->variable()) { + return (var->isPointer() || var->isSmartPointer()); + } + return false; +} + +void CheckNullPointer::nullPointerByDeRefAndChec() +{ + const bool printInconclusive = (mSettings->certainty.isEnabled(Certainty::inconclusive)); + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (isUnevaluated(tok)) { + tok = tok->next()->link(); + continue; + } + + if (Token::Match(tok, "%num%|%char%|%str%")) + continue; + + if (!isNullablePointer(tok) || + (tok->str() == "." && isNullablePointer(tok->astOperand2()) && tok->astOperand2()->getValue(0))) // avoid duplicate warning + continue; + + // Can pointer be NULL? + const ValueFlow::Value *value = tok->getValue(0); + if (!value) + continue; + + if (!printInconclusive && value->isInconclusive()) + continue; + + // Pointer dereference. + bool unknown = false; + if (!isPointerDeRef(tok,unknown)) { + if (unknown) + nullPointerError(tok, tok->expressionString(), value, true); + continue; + } + + nullPointerError(tok, tok->expressionString(), value, value->isInconclusive()); + } +} + +void CheckNullPointer::nullPointer() +{ + logChecker("CheckNullPointer::nullPointer"); + nullPointerByDeRefAndChec(); +} + +namespace { + const std::set stl_istream = { + "fstream", "ifstream", "iostream", "istream", + "istringstream", "stringstream", "wistringstream", "wstringstream" + }; +} + +/** Dereferencing null constant (simplified token list) */ +void CheckNullPointer::nullConstantDereference() +{ + logChecker("CheckNullPointer::nullConstantDereference"); + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope * scope : symbolDatabase->functionScopes) { + if (scope->function == nullptr || !scope->function->hasBody()) // We only look for functions with a body + continue; + + const Token *tok = scope->bodyStart; + + if (scope->function->isConstructor()) + tok = scope->function->token; // Check initialization list + + for (; tok != scope->bodyEnd; tok = tok->next()) { + if (isUnevaluated(tok)) + tok = tok->next()->link(); + + else if (Token::simpleMatch(tok, "* 0")) { + if (Token::Match(tok->previous(), "return|throw|;|{|}|:|[|(|,") || tok->previous()->isOp()) { + nullPointerError(tok); + } + } + + else if (Token::Match(tok, "0 [") && (tok->previous()->str() != "&" || !Token::Match(tok->next()->link()->next(), "[.(]"))) + nullPointerError(tok); + + else if (Token::Match(tok->previous(), "!!. %name% (|{") && (tok->previous()->str() != "::" || tok->strAt(-2) == "std")) { + if (Token::Match(tok->tokAt(2), "0|NULL|nullptr )|}") && tok->varId()) { // constructor call + const Variable *var = tok->variable(); + if (var && !var->isPointer() && !var->isArray() && var->isStlStringType()) + nullPointerError(tok); + } else { // function call + std::list var; + parseFunctionCall(*tok, var, mSettings->library); + + // is one of the var items a NULL pointer? + for (const Token *vartok : var) { + if (vartok->hasKnownIntValue() && vartok->getKnownIntValue() == 0) + nullPointerError(vartok); + } + } + } else if (Token::Match(tok, "std :: string|wstring ( 0|NULL|nullptr )")) + nullPointerError(tok); + + else if (Token::Match(tok->previous(), "::|. %name% (")) { + const std::vector &args = getArguments(tok); + for (int argnr = 0; argnr < args.size(); ++argnr) { + const Token *argtok = args[argnr]; + if (!argtok->hasKnownIntValue()) + continue; + if (argtok->values().front().intvalue != 0) + continue; + if (mSettings->library.isnullargbad(tok, argnr+1)) + nullPointerError(argtok); + } + } + + else if (Token::Match(tok->previous(), ">> 0|NULL|nullptr")) { // Only checking input stream operations is safe here, because otherwise 0 can be an integer as well + const Token* tok2 = tok->previous(); // Find start of statement + for (; tok2; tok2 = tok2->previous()) { + if (Token::Match(tok2->previous(), ";|{|}|:|(")) + break; + } + if (tok2 && tok2->previous() && tok2->previous()->str()=="(") + continue; + if (Token::simpleMatch(tok2, "std :: cin")) + nullPointerError(tok); + if (tok2 && tok2->varId() != 0) { + const Variable *var = tok2->variable(); + if (var && var->isStlType(stl_istream)) + nullPointerError(tok); + } + } + + const Variable *ovar = nullptr; + const Token *tokNull = nullptr; + if (Token::Match(tok, "0|NULL|nullptr ==|!=|>|>=|<|<= %var%")) { + if (!Token::Match(tok->tokAt(3),".|[")) { + ovar = tok->tokAt(2)->variable(); + tokNull = tok; + } + } else if (Token::Match(tok, "%var% ==|!=|>|>=|<|<= 0|NULL|nullptr") || + Token::Match(tok, "%var% =|+ 0|NULL|nullptr )|]|,|;|+")) { + ovar = tok->variable(); + tokNull = tok->tokAt(2); + } + if (ovar && !ovar->isPointer() && !ovar->isArray() && ovar->isStlStringType() && tokNull && tokNull->originalName() != "'\\0'") + nullPointerError(tokNull); + } + } +} + +void CheckNullPointer::nullPointerError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive) +{ + const std::string errmsgcond("$symbol:" + varname + '\n' + ValueFlow::eitherTheConditionIsRedundant(value ? value->condition : nullptr) + " or there is possible null pointer dereference: $symbol."); + const std::string errmsgdefarg("$symbol:" + varname + "\nPossible null pointer dereference if the default parameter value is used: $symbol"); + + if (!tok) { + reportError(tok, Severity::error, "nullPointer", "Null pointer dereference", CWE_NULL_POINTER_DEREFERENCE, Certainty::normal); + reportError(tok, Severity::warning, "nullPointerDefaultArg", errmsgdefarg, CWE_NULL_POINTER_DEREFERENCE, Certainty::normal); + reportError(tok, Severity::warning, "nullPointerRedundantCheck", errmsgcond, CWE_NULL_POINTER_DEREFERENCE, Certainty::normal); + return; + } + + if (!value) { + reportError(tok, Severity::error, "nullPointer", "Null pointer dereference", CWE_NULL_POINTER_DEREFERENCE, inconclusive ? Certainty::inconclusive : Certainty::normal); + return; + } + + if (!mSettings->isEnabled(value, inconclusive)) + return; + + const ErrorPath errorPath = getErrorPath(tok, value, "Null pointer dereference"); + + if (value->condition) { + reportError(errorPath, Severity::warning, "nullPointerRedundantCheck", errmsgcond, CWE_NULL_POINTER_DEREFERENCE, inconclusive || value->isInconclusive() ? Certainty::inconclusive : Certainty::normal); + } else if (value->defaultArg) { + reportError(errorPath, Severity::warning, "nullPointerDefaultArg", errmsgdefarg, CWE_NULL_POINTER_DEREFERENCE, inconclusive || value->isInconclusive() ? Certainty::inconclusive : Certainty::normal); + } else { + std::string errmsg = std::string(value->isKnown() ? "Null" : "Possible null") + " pointer dereference"; + if (!varname.empty()) + errmsg = "$symbol:" + varname + '\n' + errmsg + ": $symbol"; + + reportError(errorPath, + value->isKnown() ? Severity::error : Severity::warning, + "nullPointer", + errmsg, + CWE_NULL_POINTER_DEREFERENCE, inconclusive || value->isInconclusive() ? Certainty::inconclusive : Certainty::normal); + } +} + +void CheckNullPointer::arithmetic() +{ + logChecker("CheckNullPointer::arithmetic"); + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::Match(tok, "-|+|+=|-=|++|--")) + continue; + const Token *pointerOperand; + const Token *numericOperand; + if (tok->astOperand1() && tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->pointer != 0) { + pointerOperand = tok->astOperand1(); + numericOperand = tok->astOperand2(); + } else if (tok->astOperand2() && tok->astOperand2()->valueType() && tok->astOperand2()->valueType()->pointer != 0) { + pointerOperand = tok->astOperand2(); + numericOperand = tok->astOperand1(); + } else + continue; + if (numericOperand && numericOperand->valueType() && !numericOperand->valueType()->isIntegral()) + continue; + const ValueFlow::Value* numValue = numericOperand ? numericOperand->getValue(0) : nullptr; + if (numValue && numValue->intvalue == 0) // don't warn for arithmetic with 0 + continue; + const ValueFlow::Value* value = pointerOperand->getValue(0); + if (!value) + continue; + if (!mSettings->certainty.isEnabled(Certainty::inconclusive) && value->isInconclusive()) + continue; + if (value->condition && !mSettings->severity.isEnabled(Severity::warning)) + continue; + if (value->condition) + redundantConditionWarning(tok, value, value->condition, value->isInconclusive()); + else + pointerArithmeticError(tok, value, value->isInconclusive()); + } + } +} + +static std::string arithmeticTypeString(const Token *tok) +{ + if (tok && tok->str()[0] == '-') + return "subtraction"; + if (tok && tok->str()[0] == '+') + return "addition"; + return "arithmetic"; +} + +void CheckNullPointer::pointerArithmeticError(const Token* tok, const ValueFlow::Value *value, bool inconclusive) +{ + // cppcheck-suppress shadowFunction - TODO: fix this + std::string arithmetic = arithmeticTypeString(tok); + std::string errmsg; + if (tok && tok->str()[0] == '-') { + errmsg = "Overflow in pointer arithmetic, NULL pointer is subtracted."; + } else { + errmsg = "Pointer " + arithmetic + " with NULL pointer."; + } + const ErrorPath errorPath = getErrorPath(tok, value, "Null pointer " + arithmetic); + reportError(errorPath, + Severity::error, + "nullPointerArithmetic", + errmsg, + CWE_INCORRECT_CALCULATION, + inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckNullPointer::redundantConditionWarning(const Token* tok, const ValueFlow::Value *value, const Token *condition, bool inconclusive) +{ + // cppcheck-suppress shadowFunction - TODO: fix this + std::string arithmetic = arithmeticTypeString(tok); + std::string errmsg; + if (tok && tok->str()[0] == '-') { + errmsg = ValueFlow::eitherTheConditionIsRedundant(condition) + " or there is overflow in pointer " + arithmetic + "."; + } else { + errmsg = ValueFlow::eitherTheConditionIsRedundant(condition) + " or there is pointer arithmetic with NULL pointer."; + } + const ErrorPath errorPath = getErrorPath(tok, value, "Null pointer " + arithmetic); + reportError(errorPath, + Severity::warning, + "nullPointerArithmeticRedundantCheck", + errmsg, + CWE_INCORRECT_CALCULATION, + inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +// NOLINTNEXTLINE(readability-non-const-parameter) - used as callback so we need to preserve the signature +static bool isUnsafeUsage(const Settings &settings, const Token *vartok, MathLib::bigint *value) +{ + (void)value; + bool unknown = false; + return CheckNullPointer::isPointerDeRef(vartok, unknown, settings); +} + +// a Clang-built executable will crash when using the anonymous MyFileInfo later on - so put it in a unique namespace for now +// see https://trac.cppcheck.net/ticket/12108 for more details +#ifdef __clang__ +inline namespace CheckNullPointer_internal +#else +namespace +#endif +{ + /* data for multifile checking */ + class MyFileInfo : public Check::FileInfo { + public: + /** function arguments that are dereferenced without checking if they are null */ + std::list unsafeUsage; + + /** Convert data into xml string */ + std::string toString() const override + { + return CTU::toString(unsafeUsage); + } + }; +} + +Check::FileInfo *CheckNullPointer::getFileInfo(const Tokenizer &tokenizer, const Settings &settings) const +{ + const std::list &unsafeUsage = CTU::getUnsafeUsage(tokenizer, settings, isUnsafeUsage); + if (unsafeUsage.empty()) + return nullptr; + + auto *fileInfo = new MyFileInfo; + fileInfo->unsafeUsage = unsafeUsage; + return fileInfo; +} + +Check::FileInfo * CheckNullPointer::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const +{ + const std::list &unsafeUsage = CTU::loadUnsafeUsageListFromXml(xmlElement); + if (unsafeUsage.empty()) + return nullptr; + + auto *fileInfo = new MyFileInfo; + fileInfo->unsafeUsage = unsafeUsage; + return fileInfo; +} + +bool CheckNullPointer::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) +{ + if (!ctu) + return false; + bool foundErrors = false; + (void)settings; // This argument is unused + + CheckNullPointer dummy(nullptr, &settings, &errorLogger); + dummy. + logChecker("CheckNullPointer::analyseWholeProgram"); // unusedfunctions + + const std::map> callsMap = ctu->getCallsMap(); + + for (const Check::FileInfo* fi1 : fileInfo) { + const MyFileInfo *fi = dynamic_cast(fi1); + if (!fi) + continue; + for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafeUsage) { + for (int warning = 0; warning <= 1; warning++) { + if (warning == 1 && !settings.severity.isEnabled(Severity::warning)) + break; + + const std::list &locationList = + CTU::FileInfo::getErrorPath(CTU::FileInfo::InvalidValueType::null, + unsafeUsage, + callsMap, + "Dereferencing argument ARG that is null", + nullptr, + warning); + if (locationList.empty()) + continue; + + const ErrorMessage errmsg(locationList, + emptyString, + warning ? Severity::warning : Severity::error, + "Null pointer dereference: " + unsafeUsage.myArgumentName, + "ctunullpointer", + CWE_NULL_POINTER_DEREFERENCE, Certainty::normal); + errorLogger.reportErr(errmsg); + + foundErrors = true; + break; + } + } + } + + return foundErrors; +} diff --git a/cppcheck-2.14.0/lib/checknullpointer.h b/cppcheck-2.14.0/lib/checknullpointer.h new file mode 100644 index 00000000..f797a8f6 --- /dev/null +++ b/cppcheck-2.14.0/lib/checknullpointer.h @@ -0,0 +1,149 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checknullpointerH +#define checknullpointerH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "tokenize.h" +#include "vfvalue.h" + +#include +#include + +class ErrorLogger; +class Library; +class Settings; +class Token; + +namespace CTU { + class FileInfo; +} + +namespace tinyxml2 { + class XMLElement; +} + +/// @addtogroup Checks +/// @{ + + +/** @brief check for null pointer dereferencing */ + +class CPPCHECKLIB CheckNullPointer : public Check { + friend class TestNullPointer; + +public: + /** @brief This constructor is used when registering the CheckNullPointer */ + CheckNullPointer() : Check(myName()) {} + + /** + * Is there a pointer dereference? Everything that should result in + * a nullpointer dereference error message will result in a true + * return value. If it's unknown if the pointer is dereferenced false + * is returned. + * @param tok token for the pointer + * @param unknown it is not known if there is a pointer dereference (could be reported as a debug message) + * @return true => there is a dereference + */ + bool isPointerDeRef(const Token *tok, bool &unknown) const; + + static bool isPointerDeRef(const Token *tok, bool &unknown, const Settings &settings); + +private: + /** + * @brief parse a function call and extract information about variable usage + * @param tok first token + * @param var variables that the function read / write. + * @param library --library files data + */ + static void parseFunctionCall(const Token &tok, + std::list &var, + const Library &library); + + /** @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, ErrorLogger *errorLogger) override { + CheckNullPointer checkNullPointer(&tokenizer, &tokenizer.getSettings(), errorLogger); + checkNullPointer.nullPointer(); + checkNullPointer.arithmetic(); + checkNullPointer.nullConstantDereference(); + } + + /** @brief possible null pointer dereference */ + void nullPointer(); + + /** @brief dereferencing null constant (after Tokenizer::simplifyKnownVariables) */ + void nullConstantDereference(); + + void nullPointerError(const Token *tok) { + ValueFlow::Value v(0); + v.setKnown(); + nullPointerError(tok, emptyString, &v, false); + } + void nullPointerError(const Token *tok, const std::string &varname, const ValueFlow::Value* value, bool inconclusive); + + /** @brief Parse current TU and extract file info */ + Check::FileInfo *getFileInfo(const Tokenizer &tokenizer, const Settings &settings) const override; + + Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const override; + + /** @brief Analyse all file infos for all TU */ + bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) override; + + /** Get error messages. Used by --errorlist */ + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckNullPointer c(nullptr, settings, errorLogger); + c.nullPointerError(nullptr, "pointer", nullptr, false); + c.pointerArithmeticError(nullptr, nullptr, false); + c.redundantConditionWarning(nullptr, nullptr, nullptr, false); + } + + /** Name of check */ + static std::string myName() { + return "Null pointer"; + } + + /** class info in WIKI format. Used by --doc */ + std::string classInfo() const override { + return "Null pointers\n" + "- null pointer dereferencing\n" + "- undefined null pointer arithmetic\n"; + } + + /** + * @brief Does one part of the check for nullPointer(). + * Dereferencing a pointer and then checking if it's NULL.. + */ + void nullPointerByDeRefAndChec(); + + /** undefined null pointer arithmetic */ + void arithmetic(); + void pointerArithmeticError(const Token* tok, const ValueFlow::Value *value, bool inconclusive); + void redundantConditionWarning(const Token* tok, const ValueFlow::Value *value, const Token *condition, bool inconclusive); +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checknullpointerH diff --git a/cppcheck-2.14.0/lib/checkother.cpp b/cppcheck-2.14.0/lib/checkother.cpp new file mode 100644 index 00000000..76364d83 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkother.cpp @@ -0,0 +1,4017 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#include "checkother.h" + +#include "astutils.h" +#include "fwdanalysis.h" +#include "library.h" +#include "mathlib.h" +#include "platform.h" +#include "settings.h" +#include "standards.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "tokenlist.h" +#include "utils.h" +#include "valueflow.h" +#include "vfvalue.h" + +#include // find_if() +#include +#include +#include +#include +#include +#include + +//--------------------------------------------------------------------------- + +// Register this check class (by creating a static instance of it) +namespace { + CheckOther instance; +} + +static const CWE CWE128(128U); // Wrap-around Error +static const CWE CWE131(131U); // Incorrect Calculation of Buffer Size +static const CWE CWE197(197U); // Numeric Truncation Error +static const CWE CWE362(362U); // Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') +static const CWE CWE369(369U); // Divide By Zero +static const CWE CWE398(398U); // Indicator of Poor Code Quality +static const CWE CWE475(475U); // Undefined Behavior for Input to API +static const CWE CWE561(561U); // Dead Code +static const CWE CWE563(563U); // Assignment to Variable without Use ('Unused Variable') +static const CWE CWE570(570U); // Expression is Always False +static const CWE CWE571(571U); // Expression is Always True +static const CWE CWE672(672U); // Operation on a Resource after Expiration or Release +static const CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments +static const CWE CWE683(683U); // Function Call With Incorrect Order of Arguments +static const CWE CWE704(704U); // Incorrect Type Conversion or Cast +static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior +static const CWE CWE768(768U); // Incorrect Short Circuit Evaluation +static const CWE CWE783(783U); // Operator Precedence Logic Error + +//---------------------------------------------------------------------------------- +// The return value of fgetc(), getc(), ungetc(), getchar() etc. is an integer value. +// If this return value is stored in a character variable and then compared +// to EOF, which is an integer, the comparison maybe be false. +// +// Reference: +// - Ticket #160 +// - http://www.cplusplus.com/reference/cstdio/fgetc/ +// - http://www.cplusplus.com/reference/cstdio/getc/ +// - http://www.cplusplus.com/reference/cstdio/getchar/ +// - http://www.cplusplus.com/reference/cstdio/ungetc/ ... +//---------------------------------------------------------------------------------- +void CheckOther::checkCastIntToCharAndBack() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckOther::checkCastIntToCharAndBack"); // warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + std::map vars; + for (const Token* tok = scope->bodyStart->next(); tok && tok != scope->bodyEnd; tok = tok->next()) { + // Quick check to see if any of the matches below have any chances + if (!Token::Match(tok, "%var%|EOF %comp%|=")) + continue; + if (Token::Match(tok, "%var% = fclose|fflush|fputc|fputs|fscanf|getchar|getc|fgetc|putchar|putc|puts|scanf|sscanf|ungetc (")) { + const Variable *var = tok->variable(); + if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) { + vars[tok->varId()] = tok->strAt(2); + } + } else if (Token::Match(tok, "EOF %comp% ( %var% = fclose|fflush|fputc|fputs|fscanf|getchar|getc|fgetc|putchar|putc|puts|scanf|sscanf|ungetc (")) { + tok = tok->tokAt(3); + const Variable *var = tok->variable(); + if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) { + checkCastIntToCharAndBackError(tok, tok->strAt(2)); + } + } else if (tok->isCpp() && (Token::Match(tok, "EOF %comp% ( %var% = std :: cin . get (") || Token::Match(tok, "EOF %comp% ( %var% = cin . get ("))) { + tok = tok->tokAt(3); + const Variable *var = tok->variable(); + if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) { + checkCastIntToCharAndBackError(tok, "cin.get"); + } + } else if (tok->isCpp() && (Token::Match(tok, "%var% = std :: cin . get (") || Token::Match(tok, "%var% = cin . get ("))) { + const Variable *var = tok->variable(); + if (var && var->typeEndToken()->str() == "char" && !var->typeEndToken()->isSigned()) { + vars[tok->varId()] = "cin.get"; + } + } else if (Token::Match(tok, "%var% %comp% EOF")) { + if (vars.find(tok->varId()) != vars.end()) { + checkCastIntToCharAndBackError(tok, vars[tok->varId()]); + } + } else if (Token::Match(tok, "EOF %comp% %var%")) { + tok = tok->tokAt(2); + if (vars.find(tok->varId()) != vars.end()) { + checkCastIntToCharAndBackError(tok, vars[tok->varId()]); + } + } + } + } +} + +void CheckOther::checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName) +{ + reportError( + tok, + Severity::warning, + "checkCastIntToCharAndBack", + "$symbol:" + strFunctionName + "\n" + "Storing $symbol() return value in char variable and then comparing with EOF.\n" + "When saving $symbol() return value in char variable there is loss of precision. " + " When $symbol() returns EOF this value is truncated. Comparing the char " + "variable with EOF can have unexpected results. For instance a loop \"while (EOF != (c = $symbol());\" " + "loops forever on some compilers/platforms and on other compilers/platforms it will stop " + "when the file contains a matching character.", CWE197, Certainty::normal + ); +} + + +//--------------------------------------------------------------------------- +// Clarify calculation precedence for ternary operators. +//--------------------------------------------------------------------------- +void CheckOther::clarifyCalculation() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("clarifyCalculation")) + return; + + logChecker("CheckOther::clarifyCalculation"); // style + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + // ? operator where lhs is arithmetical expression + if (tok->str() != "?" || !tok->astOperand1() || !tok->astOperand1()->isCalculation()) + continue; + if (!tok->astOperand1()->isArithmeticalOp() && tok->astOperand1()->tokType() != Token::eBitOp) + continue; + + // non-pointer calculation in lhs and pointer in rhs => no clarification is needed + if (tok->astOperand1()->isBinaryOp() && Token::Match(tok->astOperand1(), "%or%|&|%|*|/") && tok->astOperand2()->valueType() && tok->astOperand2()->valueType()->pointer > 0) + continue; + + // bit operation in lhs and char literals in rhs => probably no mistake + if (tok->astOperand1()->tokType() == Token::eBitOp && Token::Match(tok->astOperand2()->astOperand1(), "%char%") && Token::Match(tok->astOperand2()->astOperand2(), "%char%")) + continue; + + // 2nd operand in lhs has known integer value => probably no mistake + if (tok->astOperand1()->isBinaryOp() && tok->astOperand1()->astOperand2()->hasKnownIntValue()) { + const Token *op = tok->astOperand1()->astOperand2(); + if (op->isNumber()) + continue; + if (op->valueType() && op->valueType()->isEnum()) + continue; + } + + // Is code clarified by parentheses already? + const Token *tok2 = tok->astOperand1(); + for (; tok2; tok2 = tok2->next()) { + if (tok2->str() == "(") + tok2 = tok2->link(); + else if (tok2->str() == ")") + break; + else if (tok2->str() == "?") { + clarifyCalculationError(tok, tok->astOperand1()->str()); + break; + } + } + } + } +} + +void CheckOther::clarifyCalculationError(const Token *tok, const std::string &op) +{ + // suspicious calculation + const std::string calc("'a" + op + "b?c:d'"); + + // recommended calculation #1 + const std::string s1("'(a" + op + "b)?c:d'"); + + // recommended calculation #2 + const std::string s2("'a" + op + "(b?c:d)'"); + + reportError(tok, + Severity::style, + "clarifyCalculation", + "Clarify calculation precedence for '" + op + "' and '?'.\n" + "Suspicious calculation. Please use parentheses to clarify the code. " + "The code '" + calc + "' should be written as either '" + s1 + "' or '" + s2 + "'.", CWE783, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Clarify (meaningless) statements like *foo++; with parentheses. +//--------------------------------------------------------------------------- +void CheckOther::clarifyStatement() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckOther::clarifyStatement"); // warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { + if (tok->astOperand1() && Token::Match(tok, "* %name%")) { + const Token *tok2 = tok->previous(); + + while (tok2 && tok2->str() == "*") + tok2 = tok2->previous(); + + if (tok2 && !tok2->astParent() && Token::Match(tok2, "[{};]")) { + tok2 = tok->astOperand1(); + if (Token::Match(tok2, "++|-- [;,]")) + clarifyStatementError(tok2); + } + } + } + } +} + +void CheckOther::clarifyStatementError(const Token *tok) +{ + reportError(tok, Severity::warning, "clarifyStatement", "In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n" + "A statement like '*A++;' might not do what you intended. Postfix 'operator++' is executed before 'operator*'. " + "Thus, the dereference is meaningless. Did you intend to write '(*A)++;'?", CWE783, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Check for suspicious occurrences of 'if(); {}'. +//--------------------------------------------------------------------------- +void CheckOther::checkSuspiciousSemicolon() +{ + if (!mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning)) + return; + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + + logChecker("CheckOther::checkSuspiciousSemicolon"); // warning,inconclusive + + // Look for "if(); {}", "for(); {}" or "while(); {}" + for (const Scope &scope : symbolDatabase->scopeList) { + if (scope.type == Scope::eIf || scope.type == Scope::eElse || scope.type == Scope::eWhile || scope.type == Scope::eFor) { + // Ensure the semicolon is at the same line number as the if/for/while statement + // and the {..} block follows it without an extra empty line. + if (Token::simpleMatch(scope.bodyStart, "{ ; } {") && + scope.bodyStart->previous()->linenr() == scope.bodyStart->tokAt(2)->linenr() && + scope.bodyStart->linenr()+1 >= scope.bodyStart->tokAt(3)->linenr() && + !scope.bodyStart->tokAt(3)->isExpandedMacro()) { + suspiciousSemicolonError(scope.classDef); + } + } + } +} + +void CheckOther::suspiciousSemicolonError(const Token* tok) +{ + reportError(tok, Severity::warning, "suspiciousSemicolon", + "Suspicious use of ; at the end of '" + (tok ? tok->str() : std::string()) + "' statement.", CWE398, Certainty::normal); +} + + +//--------------------------------------------------------------------------- +// For C++ code, warn if C-style casts are used on pointer types +//--------------------------------------------------------------------------- +void CheckOther::warningOldStylePointerCast() +{ + // Only valid on C++ code + if (!mTokenizer->isCPP()) + return; + + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("cstyleCast")) + return; + + logChecker("CheckOther::warningOldStylePointerCast"); // style,c++ + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + const Token* tok; + if (scope->function && scope->function->isConstructor()) + tok = scope->classDef; + else + tok = scope->bodyStart; + for (; tok && tok != scope->bodyEnd; tok = tok->next()) { + // Old style pointer casting.. + if (tok->str() != "(") + continue; + const Token* castTok = tok->next(); + while (Token::Match(castTok, "const|volatile|class|struct|union|%type%|::")) { + castTok = castTok->next(); + if (Token::simpleMatch(castTok, "<") && castTok->link()) + castTok = castTok->link()->next(); + } + if (castTok == tok->next()) + continue; + bool isPtr = false, isRef = false; + while (Token::Match(castTok, "*|const|&")) { + if (castTok->str() == "*") + isPtr = true; + else if (castTok->str() == "&") + isRef = true; + castTok = castTok->next(); + } + if ((!isPtr && !isRef) || !Token::Match(castTok, ") (| %name%|%num%|%bool%|%char%|%str%|&")) + continue; + + if (Token::Match(tok->previous(), "%type%")) + continue; + + // skip first "const" in "const Type* const" + while (Token::Match(tok->next(), "const|volatile|class|struct|union")) + tok = tok->next(); + const Token* typeTok = tok->next(); + // skip second "const" in "const Type* const" + if (tok->strAt(3) == "const") + tok = tok->next(); + + const Token *p = tok->tokAt(4); + if (p->hasKnownIntValue() && p->values().front().intvalue==0) // Casting nullpointers is safe + continue; + + if (typeTok->tokType() == Token::eType || typeTok->tokType() == Token::eName) + cstyleCastError(tok, isPtr); + } + } +} + +void CheckOther::cstyleCastError(const Token *tok, bool isPtr) +{ + const std::string type = isPtr ? "pointer" : "reference"; + reportError(tok, Severity::style, "cstyleCast", + "C-style " + type + " casting\n" + "C-style " + type + " 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.", CWE398, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// float* f; double* d = (double*)f; <-- Pointer cast to a type with an incompatible binary data representation +//--------------------------------------------------------------------------- + +void CheckOther::invalidPointerCast() +{ + if (!mSettings->severity.isEnabled(Severity::portability)) + return; + + logChecker("CheckOther::invalidPointerCast"); // portability + + const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + const Token* toTok = nullptr; + const Token* fromTok = nullptr; + // Find cast + if (Token::Match(tok, "( const|volatile| const|volatile| %type% %type%| const| * )")) { + toTok = tok; + fromTok = tok->astOperand1(); + } else if (Token::simpleMatch(tok, "reinterpret_cast <") && tok->linkAt(1)) { + toTok = tok->linkAt(1)->next(); + fromTok = toTok->astOperand2(); + } + if (!fromTok) + continue; + + const ValueType* fromType = fromTok->valueType(); + const ValueType* toType = toTok->valueType(); + if (!fromType || !toType || !fromType->pointer || !toType->pointer) + continue; + + if (fromType->type != toType->type && fromType->type >= ValueType::Type::BOOL && toType->type >= ValueType::Type::BOOL && (toType->type != ValueType::Type::CHAR || printInconclusive)) { + if (toType->isIntegral() && fromType->isIntegral()) + continue; + + invalidPointerCastError(tok, fromType->str(), toType->str(), toType->type == ValueType::Type::CHAR, toType->isIntegral()); + } + } + } +} + + +void CheckOther::invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt) +{ + if (toIsInt) { // If we cast something to int*, this can be useful to play with its binary data representation + reportError(tok, Severity::portability, "invalidPointerCast", "Casting from " + from + " to " + to + " is not portable due to different binary data representations on different platforms.", CWE704, inconclusive ? Certainty::inconclusive : Certainty::normal); + } else + reportError(tok, Severity::portability, "invalidPointerCast", "Casting between " + from + " and " + to + " which have an incompatible binary data representation.", CWE704, Certainty::normal); +} + + +//--------------------------------------------------------------------------- +// Detect redundant assignments: x = 0; x = 4; +//--------------------------------------------------------------------------- + +void CheckOther::checkRedundantAssignment() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("redundantAssignment")) + return; + + logChecker("CheckOther::checkRedundantAssignment"); // style + + const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *scope : symbolDatabase->functionScopes) { + if (!scope->bodyStart) + continue; + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (Token::simpleMatch(tok, "] (")) + // todo: handle lambdas + break; + if (Token::simpleMatch(tok, "try {")) + // todo: check try blocks + tok = tok->linkAt(1); + if ((tok->isAssignmentOp() || tok->tokType() == Token::eIncDecOp) && tok->astOperand1()) { + if (tok->astParent()) + continue; + + // Do not warn about redundant initialization when rhs is trivial + // TODO : do not simplify the variable declarations + bool isInitialization = false; + if (Token::Match(tok->tokAt(-2), "; %var% =") && tok->tokAt(-2)->isSplittedVarDeclEq()) { + isInitialization = true; + bool trivial = true; + visitAstNodes(tok->astOperand2(), + [&](const Token *rhs) { + if (Token::simpleMatch(rhs, "{ 0 }")) + return ChildrenToVisit::none; + if (Token::Match(rhs, "%str%|%num%|%name%") && !rhs->varId()) + return ChildrenToVisit::none; + if (Token::Match(rhs, ":: %name%") && rhs->hasKnownIntValue()) + return ChildrenToVisit::none; + if (rhs->isCast()) + return ChildrenToVisit::op2; + trivial = false; + return ChildrenToVisit::done; + }); + if (trivial) + continue; + } + + const Token* rhs = tok->astOperand2(); + // Do not warn about assignment with 0 / NULL + if ((rhs && MathLib::isNullValue(rhs->str())) || isNullOperand(rhs)) + continue; + + if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isReference()) + // todo: check references + continue; + + if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isStatic()) + // todo: check static variables + continue; + + bool inconclusive = false; + if (tok->isCpp() && tok->astOperand1()->valueType()) { + // If there is a custom assignment operator => this is inconclusive + if (tok->astOperand1()->valueType()->typeScope) { + const std::string op = "operator" + tok->str(); + const std::list& fList = tok->astOperand1()->valueType()->typeScope->functionList; + inconclusive = std::any_of(fList.cbegin(), fList.cend(), [&](const Function& f) { + return f.name() == op; + }); + } + // assigning a smart pointer has side effects + if (tok->astOperand1()->valueType()->type == ValueType::SMART_POINTER) + break; + } + if (inconclusive && !mSettings->certainty.isEnabled(Certainty::inconclusive)) + continue; + + FwdAnalysis fwdAnalysis(mSettings->library); + if (fwdAnalysis.hasOperand(tok->astOperand2(), tok->astOperand1())) + continue; + + // Is there a redundant assignment? + const Token *start; + if (tok->isAssignmentOp()) + start = tok->next(); + else + start = tok->findExpressionStartEndTokens().second->next(); + + // Get next assignment.. + const Token *nextAssign = fwdAnalysis.reassign(tok->astOperand1(), start, scope->bodyEnd); + + if (!nextAssign) + continue; + + // there is redundant assignment. Is there a case between the assignments? + bool hasCase = false; + for (const Token *tok2 = tok; tok2 != nextAssign; tok2 = tok2->next()) { + if (tok2->str() == "break" || tok2->str() == "return") + break; + if (tok2->str() == "case") { + hasCase = true; + break; + } + } + + // warn + if (hasCase) + redundantAssignmentInSwitchError(tok, nextAssign, tok->astOperand1()->expressionString()); + else if (isInitialization) + redundantInitializationError(tok, nextAssign, tok->astOperand1()->expressionString(), inconclusive); + else + redundantAssignmentError(tok, nextAssign, tok->astOperand1()->expressionString(), inconclusive); + } + } + } +} + +void CheckOther::redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var) +{ + const std::list callstack = { tok1, tok2 }; + reportError(callstack, Severity::performance, "redundantCopy", + "$symbol:" + var + "\n" + "Buffer '$symbol' is being written before its old content has been used.", CWE563, Certainty::normal); +} + +void CheckOther::redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive) +{ + const ErrorPath errorPath = { ErrorPathItem(tok1, var + " is assigned"), ErrorPathItem(tok2, var + " is overwritten") }; + if (inconclusive) + reportError(errorPath, Severity::style, "redundantAssignment", + "$symbol:" + var + "\n" + "Variable '$symbol' is reassigned a value before the old one has been used if variable is no semaphore variable.\n" + "Variable '$symbol' is reassigned a value before the old one has been used. Make sure that this variable is not used like a semaphore in a threading environment before simplifying this code.", CWE563, Certainty::inconclusive); + else + reportError(errorPath, Severity::style, "redundantAssignment", + "$symbol:" + var + "\n" + "Variable '$symbol' is reassigned a value before the old one has been used.", CWE563, Certainty::normal); +} + +void CheckOther::redundantInitializationError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive) +{ + const ErrorPath errorPath = { ErrorPathItem(tok1, var + " is initialized"), ErrorPathItem(tok2, var + " is overwritten") }; + reportError(errorPath, Severity::style, "redundantInitialization", + "$symbol:" + var + "\nRedundant initialization for '$symbol'. The initialized value is overwritten before it is read.", + CWE563, + inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckOther::redundantAssignmentInSwitchError(const Token *tok1, const Token* tok2, const std::string &var) +{ + const ErrorPath errorPath = { ErrorPathItem(tok1, "$symbol is assigned"), ErrorPathItem(tok2, "$symbol is overwritten") }; + reportError(errorPath, Severity::style, "redundantAssignInSwitch", + "$symbol:" + var + "\n" + "Variable '$symbol' is reassigned a value before the old one has been used. 'break;' missing?", CWE563, Certainty::normal); +} + + +//--------------------------------------------------------------------------- +// 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) +{ + return Token::Match(tok, "%name% (") || Token::Match(tok, "break|continue|return|exit|goto|throw"); +} + +void CheckOther::redundantBitwiseOperationInSwitchError() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckOther::redundantBitwiseOperationInSwitch"); // warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + // Find the beginning of a switch. E.g.: + // switch (var) { ... + for (const Scope &switchScope : symbolDatabase->scopeList) { + if (switchScope.type != Scope::eSwitch || !switchScope.bodyStart) + continue; + + // Check the contents of the switch statement + std::map varsWithBitsSet; + std::map bitOperations; + + for (const Token *tok2 = switchScope.bodyStart->next(); tok2 != switchScope.bodyEnd; tok2 = tok2->next()) { + if (tok2->str() == "{") { + // Inside a conditional or loop. Don't mark variable accesses as being redundant. E.g.: + // case 3: b = 1; + // case 4: if (a) { b = 2; } // Doesn't make the b=1 redundant because it's conditional + if (Token::Match(tok2->previous(), ")|else {") && tok2->link()) { + const Token* endOfConditional = tok2->link(); + for (const Token* tok3 = tok2; tok3 != endOfConditional; tok3 = tok3->next()) { + if (tok3->varId() != 0) { + varsWithBitsSet.erase(tok3->varId()); + bitOperations.erase(tok3->varId()); + } else if (isFunctionOrBreakPattern(tok3)) { + varsWithBitsSet.clear(); + bitOperations.clear(); + } + } + tok2 = endOfConditional; + } + } + + // Variable assignment. Report an error if it's assigned to twice before a break. E.g.: + // case 3: b = 1; // <== redundant + // case 4: b = 2; + + if (Token::Match(tok2->previous(), ";|{|}|: %var% = %any% ;")) { + varsWithBitsSet.erase(tok2->varId()); + bitOperations.erase(tok2->varId()); + } + + // Bitwise operation. Report an error if it's performed twice before a break. E.g.: + // case 3: b |= 1; // <== redundant + // case 4: b |= 1; + else if (Token::Match(tok2->previous(), ";|{|}|: %var% %assign% %num% ;") && + (tok2->strAt(1) == "|=" || tok2->strAt(1) == "&=") && + Token::Match(tok2->next()->astOperand2(), "%num%")) { + const std::string bitOp = tok2->strAt(1)[0] + tok2->strAt(2); + const std::map::const_iterator i2 = varsWithBitsSet.find(tok2->varId()); + + // This variable has not had a bit operation performed on it yet, so just make a note of it + if (i2 == varsWithBitsSet.end()) { + varsWithBitsSet[tok2->varId()] = tok2; + bitOperations[tok2->varId()] = bitOp; + } + + // The same bit operation has been performed on the same variable twice, so report an error + else if (bitOperations[tok2->varId()] == bitOp) + redundantBitwiseOperationInSwitchError(i2->second, i2->second->str()); + + // A different bit operation was performed on the variable, so clear it + else { + varsWithBitsSet.erase(tok2->varId()); + bitOperations.erase(tok2->varId()); + } + } + + // Bitwise operation. Report an error if it's performed twice before a break. E.g.: + // case 3: b = b | 1; // <== redundant + // case 4: b = b | 1; + else if (Token::Match(tok2->previous(), ";|{|}|: %var% = %name% %or%|& %num% ;") && + tok2->varId() == tok2->tokAt(2)->varId()) { + const std::string bitOp = tok2->strAt(3) + tok2->strAt(4); + const std::map::const_iterator i2 = varsWithBitsSet.find(tok2->varId()); + + // This variable has not had a bit operation performed on it yet, so just make a note of it + if (i2 == varsWithBitsSet.end()) { + varsWithBitsSet[tok2->varId()] = tok2; + bitOperations[tok2->varId()] = bitOp; + } + + // The same bit operation has been performed on the same variable twice, so report an error + else if (bitOperations[tok2->varId()] == bitOp) + redundantBitwiseOperationInSwitchError(i2->second, i2->second->str()); + + // A different bit operation was performed on the variable, so clear it + else { + varsWithBitsSet.erase(tok2->varId()); + bitOperations.erase(tok2->varId()); + } + } + + // Not a simple assignment so there may be good reason if this variable is assigned to twice. E.g.: + // case 3: b = 1; + // case 4: b++; + else if (tok2->varId() != 0 && tok2->strAt(1) != "|" && tok2->strAt(1) != "&") { + varsWithBitsSet.erase(tok2->varId()); + bitOperations.erase(tok2->varId()); + } + + // Reset our record of assignments if there is a break or function call. E.g.: + // case 3: b = 1; break; + if (isFunctionOrBreakPattern(tok2)) { + varsWithBitsSet.clear(); + bitOperations.clear(); + } + } + } +} + +void CheckOther::redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname) +{ + reportError(tok, Severity::style, + "redundantBitwiseOperationInSwitch", + "$symbol:" + varname + "\n" + "Redundant bitwise operation on '$symbol' in 'switch' statement. 'break;' missing?"); +} + + +//--------------------------------------------------------------------------- +// Check for statements like case A||B: in switch() +//--------------------------------------------------------------------------- +void CheckOther::checkSuspiciousCaseInSwitch() +{ + if (!mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckOther::checkSuspiciousCaseInSwitch"); // warning,inconclusive + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope & scope : symbolDatabase->scopeList) { + if (scope.type != Scope::eSwitch) + continue; + + for (const Token* tok = scope.bodyStart->next(); tok != scope.bodyEnd; tok = tok->next()) { + if (tok->str() == "case") { + const Token* finding = nullptr; + for (const Token* tok2 = tok->next(); tok2; tok2 = tok2->next()) { + if (tok2->str() == ":") + break; + if (Token::Match(tok2, "[;}{]")) + break; + + if (tok2->str() == "?") + finding = nullptr; + else if (Token::Match(tok2, "&&|%oror%")) + finding = tok2; + } + if (finding) + suspiciousCaseInSwitchError(finding, finding->str()); + } + } + } +} + +void CheckOther::suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString) +{ + reportError(tok, Severity::warning, "suspiciousCase", + "Found suspicious case label in switch(). Operator '" + operatorString + "' probably doesn't work as intended.\n" + "Using an operator like '" + operatorString + "' in a case label is suspicious. Did you intend to use a bitwise operator, multiple case labels or if/else instead?", CWE398, Certainty::inconclusive); +} + +//--------------------------------------------------------------------------- +// 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() +{ + // misra-c-2012-2.1 + // misra-c-2023-2.1 + // misra-cpp-2008-0-1-1 + // autosar + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unreachableCode")) + return; + + logChecker("CheckOther::checkUnreachableCode"); // style + + const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); + const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { + const Token* secondBreak = nullptr; + const Token* labelName = nullptr; + if (tok->link() && Token::Match(tok, "(|[|<")) + tok = tok->link(); + else if (Token::Match(tok, "break|continue ;")) + secondBreak = tok->tokAt(2); + else if (Token::Match(tok, "[;{}:] return|throw") && tok->next()->isKeyword()) { + if (Token::simpleMatch(tok->astParent(), "?")) + continue; + tok = tok->next(); // tok should point to return or throw + for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { + if (tok2->str() == "(" || tok2->str() == "{") + tok2 = tok2->link(); + if (tok2->str() == ";") { + secondBreak = tok2->next(); + break; + } + } + } else if (Token::Match(tok, "goto %any% ;")) { + secondBreak = tok->tokAt(3); + labelName = tok->next(); + } else if (Token::Match(tok, "%name% (") && mSettings->library.isnoreturn(tok) && !Token::Match(tok->next()->astParent(), "?|:")) { + if ((!tok->function() || (tok->function()->token != tok && tok->function()->tokenDef != tok)) && tok->linkAt(1)->strAt(1) != "{") + secondBreak = tok->linkAt(1)->tokAt(2); + if (Token::simpleMatch(secondBreak, "return")) { + // clarification for tools that function returns + continue; + } + } + + // Statements follow directly, no line between them. (#3383) + // TODO: Try to find a better way to avoid false positives due to preprocessor configurations. + const bool inconclusive = secondBreak && (secondBreak->linenr() - 1 > secondBreak->previous()->linenr()); + + if (secondBreak && (printInconclusive || !inconclusive)) { + if (Token::Match(secondBreak, "continue|goto|throw|return") && secondBreak->isKeyword()) { + duplicateBreakError(secondBreak, inconclusive); + tok = Token::findmatch(secondBreak, "[}:]"); + } else if (secondBreak->str() == "break") { // break inside switch as second break statement should not issue a warning + if (tok->str() == "break") // If the previous was a break, too: Issue warning + duplicateBreakError(secondBreak, inconclusive); + else { + if (tok->scope()->type != Scope::eSwitch) // Check, if the enclosing scope is a switch + duplicateBreakError(secondBreak, inconclusive); + } + tok = Token::findmatch(secondBreak, "[}:]"); + } else if (!Token::Match(secondBreak, "return|}|case|default") && secondBreak->strAt(1) != ":") { // TODO: No bailout for unconditional scopes + // If the goto label is followed by a loop construct in which the label is defined it's quite likely + // that the goto jump was intended to skip some code on the first loop iteration. + bool labelInFollowingLoop = false; + if (labelName && Token::Match(secondBreak, "while|do|for")) { + const Token *scope2 = Token::findsimplematch(secondBreak, "{"); + if (scope2) { + for (const Token *tokIter = scope2; tokIter != scope2->link() && tokIter; tokIter = tokIter->next()) { + if (Token::Match(tokIter, "[;{}] %any% :") && labelName->str() == tokIter->strAt(1)) { + labelInFollowingLoop = true; + break; + } + } + } + } + + // hide FP for statements that just hide compiler warnings about unused function arguments + bool silencedCompilerWarningOnly = false; + const Token *silencedWarning = secondBreak; + for (;;) { + if (Token::Match(silencedWarning, "( void ) %name% ;")) { + silencedWarning = silencedWarning->tokAt(5); + continue; + } + if (silencedWarning && silencedWarning == scope->bodyEnd) + silencedCompilerWarningOnly = true; + break; + } + if (silencedWarning) + secondBreak = silencedWarning; + + if (!labelInFollowingLoop && !silencedCompilerWarningOnly) + unreachableCodeError(secondBreak, tok, inconclusive); + tok = Token::findmatch(secondBreak, "[}:]"); + } else if (secondBreak->scope() && secondBreak->scope()->isLoopScope() && secondBreak->str() == "}" && tok->str() == "continue") { + redundantContinueError(tok); + tok = secondBreak; + } else + tok = secondBreak; + + if (!tok) + break; + tok = tok->previous(); // Will be advanced again by for loop + } + } + } +} + +void CheckOther::duplicateBreakError(const Token *tok, bool inconclusive) +{ + reportError(tok, Severity::style, "duplicateBreak", + "Consecutive return, break, continue, goto or throw statements are unnecessary.\n" + "Consecutive return, break, continue, goto or throw statements are unnecessary. " + "The second statement can never be executed, and so should be removed.", CWE561, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckOther::unreachableCodeError(const Token *tok, const Token* noreturn, bool inconclusive) +{ + std::string msg = "Statements following "; + if (noreturn && (noreturn->function() || mSettings->library.isnoreturn(noreturn))) + msg += "noreturn function '" + noreturn->str() + "()'"; + else if (noreturn && noreturn->isKeyword()) + msg += "'" + noreturn->str() + "'"; + else + msg += "return, break, continue, goto or throw"; + msg += " will never be executed."; + reportError(tok, Severity::style, "unreachableCode", + msg, CWE561, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckOther::redundantContinueError(const Token *tok) +{ + reportError(tok, Severity::style, "redundantContinue", + "'continue' is redundant since it is the last statement in a loop.", CWE561, Certainty::normal); +} + +static bool isSimpleExpr(const Token* tok, const Variable* var, const Settings* settings) { + if (!tok) + return false; + if (tok->isNumber() || tok->tokType() == Token::eString || tok->tokType() == Token::eChar || tok->isBoolean()) + return true; + bool needsCheck = tok->varId() > 0; + if (!needsCheck) { + if (tok->isArithmeticalOp()) + return isSimpleExpr(tok->astOperand1(), var, settings) && (!tok->astOperand2() || isSimpleExpr(tok->astOperand2(), var, settings)); + const Token* ftok = tok->previous(); + if (Token::Match(ftok, "%name% (") && + ((ftok->function() && ftok->function()->isConst()) || settings->library.isFunctionConst(ftok->str(), /*pure*/ true))) + needsCheck = true; + else if (tok->str() == "[") { + needsCheck = tok->astOperand1() && tok->astOperand1()->varId() > 0; + tok = tok->astOperand1(); + } + else if (isLeafDot(tok->astOperand2())) { + needsCheck = tok->astOperand2()->varId() > 0; + tok = tok->astOperand2(); + } + } + return (needsCheck && !findExpressionChanged(tok, tok->astParent(), var->scope()->bodyEnd, settings)); +} + +//--------------------------------------------------------------------------- +// Check scope of variables.. +//--------------------------------------------------------------------------- +void CheckOther::checkVariableScope() +{ + if (mSettings->clang) + return; + + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("variableScope")) + return; + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + // In C it is common practice to declare local variables at the + // start of functions. + if (mSettings->daca && mTokenizer->isC()) + return; + + logChecker("CheckOther::checkVariableScope"); // style,notclang + + for (const Variable* var : symbolDatabase->variableList()) { + if (!var || !var->isLocal() || var->isConst()) + continue; + + if (var->nameToken()->isExpandedMacro()) + continue; + + const bool isPtrOrRef = var->isPointer() || var->isReference(); + const bool isSimpleType = var->typeStartToken()->isStandardType() || var->typeStartToken()->isEnumType() || (var->typeStartToken()->isC() && var->type() && var->type()->isStructType()); + if (!isPtrOrRef && !isSimpleType && !astIsContainer(var->nameToken())) + continue; + + if (mTokenizer->hasIfdef(var->nameToken(), var->scope()->bodyEnd)) + continue; + + // reference of range for loop variable.. + if (Token::Match(var->nameToken()->previous(), "& %var% = %var% .")) { + const Token *otherVarToken = var->nameToken()->tokAt(2); + const Variable *otherVar = otherVarToken->variable(); + if (otherVar && Token::Match(otherVar->nameToken(), "%var% :") && + otherVar->nameToken()->next()->astParent() && + Token::simpleMatch(otherVar->nameToken()->next()->astParent()->previous(), "for (")) + continue; + } + + bool forHead = false; // Don't check variables declared in header of a for loop + for (const Token* tok = var->typeStartToken(); tok; tok = tok->previous()) { + if (tok->str() == "(") { + forHead = true; + break; + } + if (Token::Match(tok, "[;{}]")) + break; + } + if (forHead) + continue; + + const Token* tok = var->nameToken()->next(); + bool isConstructor = false; + if (Token::Match(tok, "; %varid% =", var->declarationId())) { // bailout for assignment + tok = tok->tokAt(2)->astOperand2(); + if (!isSimpleExpr(tok, var, mSettings)) + continue; + } + else if (Token::Match(tok, "{|(")) { // bailout for constructor + isConstructor = true; + const Token* argTok = tok->astOperand2(); + bool bail = false; + while (argTok) { + if (Token::simpleMatch(argTok, ",")) { + if (!isSimpleExpr(argTok->astOperand2(), var, mSettings)) { + bail = true; + break; + } + } else if (argTok->str() != "." && !isSimpleExpr(argTok, var, mSettings)) { + bail = true; + break; + } + argTok = argTok->astOperand1(); + } + if (bail) + continue; + } + // bailout if initialized with function call that has possible side effects + if (!isConstructor && Token::Match(tok, "[(=]") && Token::simpleMatch(tok->astOperand2(), "(")) + continue; + bool reduce = true; + bool used = false; // Don't warn about unused variables + for (; tok && tok != var->scope()->bodyEnd; tok = tok->next()) { + if (tok->str() == "{" && tok->scope() != tok->previous()->scope() && !tok->isExpandedMacro() && !isWithinScope(tok, var, Scope::ScopeType::eLambda)) { + if (used) { + bool used2 = false; + if (!checkInnerScope(tok, var, used2) || used2) { + reduce = false; + break; + } + } else if (!checkInnerScope(tok, var, used)) { + reduce = false; + break; + } + + tok = tok->link(); + + // parse else if blocks.. + } else if (Token::simpleMatch(tok, "else { if (") && Token::simpleMatch(tok->linkAt(3), ") {")) { + tok = tok->next(); + } 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 +{ + const Scope* scope = tok->next()->scope(); + bool loopVariable = scope->isLoopScope(); + bool noContinue = true; + const Token* forHeadEnd = nullptr; + const Token* end = tok->link(); + if (scope->type == Scope::eUnconditional && (tok->strAt(-1) == ")" || tok->previous()->isName())) // Might be an unknown macro like BOOST_FOREACH + loopVariable = true; + + if (scope->type == Scope::eDo) { + end = end->linkAt(2); + } else if (loopVariable && tok->strAt(-1) == ")") { + tok = tok->linkAt(-1); // Jump to opening ( of for/while statement + } else if (scope->type == Scope::eSwitch) { + for (const Scope* innerScope : scope->nestedList) { + if (used) { + bool used2 = false; + if (!checkInnerScope(innerScope->bodyStart, var, used2) || used2) { + return false; + } + } else if (!checkInnerScope(innerScope->bodyStart, var, used)) { + return false; + } + } + } + + bool bFirstAssignment=false; + for (; tok && tok != end; tok = tok->next()) { + if (tok->str() == "goto") + return false; + if (tok->str() == "continue") + noContinue = false; + + if (Token::simpleMatch(tok, "for (")) + forHeadEnd = tok->linkAt(1); + if (tok == forHeadEnd) + forHeadEnd = nullptr; + + if (loopVariable && noContinue && tok->scope() == scope && !forHeadEnd && scope->type != Scope::eSwitch && Token::Match(tok, "%varid% =", var->declarationId())) { // Assigned in outer scope. + loopVariable = false; + std::pair range = tok->next()->findExpressionStartEndTokens(); + if (range.first) + range.first = range.first->next(); + const Token* exprTok = findExpression(var->nameToken()->exprId(), range.first, range.second, [&](const Token* tok2) { + return tok2->varId() == var->declarationId(); + }); + if (exprTok) { + tok = exprTok; + loopVariable = true; + } + } + + if (loopVariable && Token::Match(tok, "%varid% !!=", var->declarationId())) // Variable used in loop + return false; + + if (Token::Match(tok, "& %varid%", var->declarationId())) // Taking address of variable + return false; + + if (Token::Match(tok, "%varid% =", var->declarationId())) { + if (!bFirstAssignment && var->isInit() && Token::findmatch(tok->tokAt(2), "%varid%", Token::findsimplematch(tok->tokAt(3), ";"), var->declarationId())) + return false; + 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() || (var->valueType() && var->valueType()->container))) // 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 == tok->scope()) { + if (scope->type == Scope::eSwitch) + return false; // Used in outer switch scope - unsafe or impossible to reduce scope + + if (scope->bodyStart && scope->bodyStart->isSimplifiedScope()) + return false; // simplified if/for/switch init statement + } + if (var->isArrayOrPointer()) { + int argn{}; + if (const Token* ftok = getTokenArgumentFunction(tok, argn)) { // var passed to function? + if (ftok->next()->astParent()) { // return value used? + if (ftok->function() && Function::returnsPointer(ftok->function())) + return false; + const std::string ret = mSettings->library.returnValueType(ftok); // assume that var is returned + if (!ret.empty() && ret.back() == '*') + return false; + } + } + } + } + } + + return true; +} + +void CheckOther::variableScopeError(const Token *tok, const std::string &varname) +{ + reportError(tok, + Severity::style, + "variableScope", + "$symbol:" + varname + "\n" + "The scope of the variable '$symbol' can be reduced.\n" + "The scope of the variable '$symbol' can be reduced. Warning: Be careful " + "when fixing this message, especially when there are inner loops. Here is an " + "example where cppcheck will write that the scope for 'i' can be reduced:\n" + "void f(int x)\n" + "{\n" + " int i = 0;\n" + " if (x) {\n" + " // it's safe to move 'int i = 0;' here\n" + " for (int n = 0; n < 10; ++n) {\n" + " // it is possible but not safe to move 'int i = 0;' here\n" + " do_something(&i);\n" + " }\n" + " }\n" + "}\n" + "When you see this message it is always safe to reduce the variable scope 1 level.", CWE398, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Comma in return statement: return a+1, b++;. (experimental) +//--------------------------------------------------------------------------- +void CheckOther::checkCommaSeparatedReturn() +{ + // This is experimental for now. See #5076 + if ((true) || !mSettings->severity.isEnabled(Severity::style)) // NOLINT(readability-simplify-boolean-expr) + return; + + // logChecker + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (tok->str() == "return") { + tok = tok->next(); + while (tok && tok->str() != ";") { + if (tok->link() && Token::Match(tok, "[([{<]")) + tok = tok->link(); + + if (!tok->isExpandedMacro() && tok->str() == "," && tok->linenr() != tok->next()->linenr()) + commaSeparatedReturnError(tok); + + tok = tok->next(); + } + // bailout: missing semicolon (invalid code / bad tokenizer) + if (!tok) + break; + } + } +} + +void CheckOther::commaSeparatedReturnError(const Token *tok) +{ + reportError(tok, + Severity::style, + "commaSeparatedReturn", + "Comma is used in return statement. The comma can easily be misread as a ';'.\n" + "Comma is used in return statement. When comma is used in a return statement it can " + "easily be misread as a semicolon. For example in the code below the value " + "of 'b' is returned if the condition is true, but it is easy to think that 'a+1' is " + "returned:\n" + " if (x)\n" + " return a + 1,\n" + " b++;\n" + "However it can be useful to use comma in macros. Cppcheck does not warn when such a " + "macro is then used in a return statement, it is less likely such code is misunderstood.", CWE398, Certainty::normal); +} + +void CheckOther::checkPassByReference() +{ + if (!mSettings->severity.isEnabled(Severity::performance) || mTokenizer->isC()) + return; + + logChecker("CheckOther::checkPassByReference"); // performance,c++ + + const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Variable* var : symbolDatabase->variableList()) { + if (!var || !var->isClass() || var->isPointer() || var->isArray() || var->isReference() || var->isEnumType()) + continue; + + const bool isRangeBasedFor = astIsRangeBasedForDecl(var->nameToken()); + if (!var->isArgument() && !isRangeBasedFor) + continue; + + if (!isRangeBasedFor && var->scope() && var->scope()->function->arg->link()->strAt(-1) == "...") + continue; // references could not be used as va_start parameters (#5824) + + const Token * const varDeclEndToken = var->declEndToken(); + if ((varDeclEndToken && varDeclEndToken->isExternC()) || + (var->scope() && var->scope()->function && var->scope()->function->tokenDef && var->scope()->function->tokenDef->isExternC())) + continue; // references cannot be used in functions in extern "C" blocks + + bool inconclusive = false; + + const bool isContainer = var->valueType() && var->valueType()->type == ValueType::Type::CONTAINER && var->valueType()->container && !var->valueType()->container->view; + if (!isContainer) { + if (var->type() && !var->type()->isEnumType()) { // Check if type is a struct or class. + // Ensure that it is a large object. + if (!var->type()->classScope) + inconclusive = true; + else if (!var->valueType() || ValueFlow::getSizeOf(*var->valueType(), *mSettings) <= 2 * mSettings->platform.sizeof_pointer) + continue; + } + else + continue; + } + + if (inconclusive && !mSettings->certainty.isEnabled(Certainty::inconclusive)) + continue; + + const bool isConst = var->isConst(); + if (isConst) { + passedByValueError(var, inconclusive, isRangeBasedFor); + continue; + } + + // Check if variable could be const + if (!isRangeBasedFor && (!var->scope() || var->scope()->function->isImplicitlyVirtual())) + continue; + + if (!isVariableChanged(var, mSettings)) { + passedByValueError(var, inconclusive, isRangeBasedFor); + } + } +} + +void CheckOther::passedByValueError(const Variable* var, bool inconclusive, bool isRangeBasedFor) +{ + std::string id = isRangeBasedFor ? "iterateByValue" : "passedByValue"; + const std::string action = isRangeBasedFor ? "declared as": "passed by"; + const std::string type = isRangeBasedFor ? "Range variable" : "Function parameter"; + std::string msg = "$symbol:" + (var ? var->name() : "") + "\n" + + type + " '$symbol' should be " + action + " const reference."; + ErrorPath errorPath; + if (var && var->scope() && var->scope()->function && var->scope()->function->functionPointerUsage) { + id += "Callback"; + errorPath.emplace_front(var->scope()->function->functionPointerUsage, "Function pointer used here."); + msg += " However it seems that '" + var->scope()->function->name() + "' is a callback function."; + } + if (var) + errorPath.emplace_back(var->nameToken(), msg); + if (isRangeBasedFor) + msg += "\nVariable '$symbol' is used to iterate by value. It could be declared as a const reference which is usually faster and recommended in C++."; + else + msg += "\nParameter '$symbol' is passed by value. It could be passed as a const reference which is usually faster and recommended in C++."; + reportError(errorPath, Severity::performance, id.c_str(), msg, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +static bool isVariableMutableInInitializer(const Token* start, const Token * end, nonneg int varid) +{ + if (!start) + return false; + if (!end) + return false; + for (const Token *tok = start; tok != end; tok = tok->next()) { + if (tok->varId() != varid) + continue; + if (tok->astParent()) { + const Token * memberTok = tok->astParent()->previous(); + if (Token::Match(memberTok, "%var% (") && memberTok->variable()) { + const Variable * memberVar = memberTok->variable(); + if (memberVar->isClass()) + //TODO: check if the called constructor could live with a const variable + // pending that, assume the worst (that it can't) + return true; + if (!memberVar->isReference()) + continue; + if (memberVar->isConst()) + continue; + } + } + return true; + } + return false; +} + +void CheckOther::checkConstVariable() +{ + if (!mSettings->severity.isEnabled(Severity::style) || mTokenizer->isC()) + return; + + const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Variable *var : symbolDatabase->variableList()) { + if (!var) + continue; + if (!var->isReference()) + continue; + if (var->isRValueReference()) + continue; + if (var->isPointer()) + continue; + if (var->isConst()) + continue; + const Scope* scope = var->scope(); + if (!scope) + continue; + const Function* function = scope->function; + if (!function && !scope->isLocal()) + continue; + if (function && var->isArgument()) { + if (function->isImplicitlyVirtual() || function->templateDef) + continue; + if (function->isConstructor() && isVariableMutableInInitializer(function->constructorMemberInitialization(), scope->bodyStart, var->declarationId())) + continue; + } + if (var->isGlobal()) + continue; + if (var->isStatic()) + continue; + if (var->isArray() && !var->isStlType()) + continue; + if (var->isEnumType()) + continue; + if (var->isVolatile()) + continue; + if (var->isMaybeUnused()) + continue; + if (var->nameToken()->isExpandedMacro()) + continue; + if (isStructuredBindingVariable(var)) // TODO: check all bound variables + continue; + if (isVariableChanged(var, mSettings)) + continue; + const bool hasFunction = function != nullptr; + if (!hasFunction) { + const Scope* functionScope = scope; + do { + functionScope = functionScope->nestedIn; + } while (functionScope && !(function = functionScope->function)); + } + if (function && (Function::returnsReference(function) || Function::returnsPointer(function)) && !Function::returnsConst(function)) { + std::vector returns = Function::findReturns(function); + if (std::any_of(returns.cbegin(), returns.cend(), [&](const Token* retTok) { + if (retTok->varId() == var->declarationId()) + return true; + while (retTok && retTok->isCast()) + retTok = retTok->astOperand2(); + while (Token::simpleMatch(retTok, ".")) + retTok = retTok->astOperand2(); + if (Token::simpleMatch(retTok, "&")) + retTok = retTok->astOperand1(); + return ValueFlow::hasLifetimeToken(getParentLifetime(retTok), var->nameToken()); + })) + continue; + } + // Skip if another non-const variable is initialized with this variable + { + //Is it the right side of an initialization of a non-const reference + bool usedInAssignment = false; + for (const Token* tok = var->nameToken(); tok != scope->bodyEnd && tok != nullptr; tok = tok->next()) { + if (Token::Match(tok, "& %var% = %varid%", var->declarationId())) { + const Variable* refvar = tok->next()->variable(); + if (refvar && !refvar->isConst() && refvar->nameToken() == tok->next()) { + usedInAssignment = true; + break; + } + } + if (tok->isUnaryOp("&") && Token::Match(tok, "& %varid%", var->declarationId())) { + const Token* opTok = tok->astParent(); + int argn = -1; + if (opTok && (opTok->isUnaryOp("!") || opTok->isComparisonOp())) + continue; + if (opTok && (opTok->isAssignmentOp() || opTok->isCalculation())) { + if (opTok->isCalculation()) { + if (opTok->astOperand1() != tok) + opTok = opTok->astOperand1(); + else + opTok = opTok->astOperand2(); + } + if (opTok && opTok->valueType() && var->valueType() && opTok->valueType()->isConst(var->valueType()->pointer)) + continue; + } else if (const Token* ftok = getTokenArgumentFunction(tok, argn)) { + bool inconclusive{}; + if (var->valueType() && !isVariableChangedByFunctionCall(ftok, var->valueType()->pointer, var->declarationId(), mSettings, &inconclusive) && !inconclusive) + continue; + } + usedInAssignment = true; + break; + } + if (astIsRangeBasedForDecl(tok) && Token::Match(tok->astParent()->astOperand2(), "%varid%", var->declarationId())) { + const Variable* refvar = tok->astParent()->astOperand1()->variable(); + if (refvar && refvar->isReference() && !refvar->isConst()) { + usedInAssignment = true; + break; + } + } + } + if (usedInAssignment) + continue; + } + + constVariableError(var, hasFunction ? function : nullptr); + } +} + +static const Token* getVariableChangedStart(const Variable* p) +{ + if (p->isArgument()) + return p->scope()->bodyStart; + const Token* start = p->nameToken()->next(); + if (start->isSplittedVarDeclEq()) + start = start->tokAt(3); + return start; +} + +namespace { + struct CompareVariables { + bool operator()(const Variable* a, const Variable* b) const { + const int fileA = a->nameToken()->fileIndex(); + const int fileB = b->nameToken()->fileIndex(); + if (fileA != fileB) + return fileA < fileB; + const int lineA = a->nameToken()->linenr(); + const int lineB = b->nameToken()->linenr(); + if (lineA != lineB) + return lineA < lineB; + const int columnA = a->nameToken()->column(); + const int columnB = b->nameToken()->column(); + return columnA < columnB; + } + }; +} + +void CheckOther::checkConstPointer() +{ + if (!mSettings->severity.isEnabled(Severity::style) && + !mSettings->isPremiumEnabled("constParameter") && + !mSettings->isPremiumEnabled("constPointer")) + return; + + logChecker("CheckOther::checkConstPointer"); // style + + std::set pointers, nonConstPointers; + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + const Variable* const var = tok->variable(); + if (!var) + continue; + if (!var->isLocal() && !var->isArgument()) + continue; + const Token* const nameTok = var->nameToken(); + if (tok == nameTok) { + // declarations of (static) pointers are (not) split up, array declarations are never split up + if (var->isLocal() && (!var->isStatic() || Token::simpleMatch(nameTok->next(), "[")) && + !astIsRangeBasedForDecl(nameTok)) + continue; + } + const ValueType* const vt = tok->valueType(); + if (!vt) + continue; + if ((vt->pointer != 1 && !(vt->pointer == 2 && var->isArray())) || (vt->constness & 1)) + continue; + if (var->typeStartToken()->isTemplateArg()) + continue; + if (std::find(nonConstPointers.cbegin(), nonConstPointers.cend(), var) != nonConstPointers.cend()) + continue; + pointers.emplace(var); + const Token* parent = tok->astParent(); + enum Deref { NONE, DEREF, MEMBER } deref = NONE; + bool hasIncDec = false; + if (parent && (parent->isUnaryOp("*") || (hasIncDec = parent->isIncDecOp() && parent->astParent() && parent->astParent()->isUnaryOp("*")))) + deref = DEREF; + else if (Token::simpleMatch(parent, "[") && parent->astOperand1() == tok && tok != nameTok) + deref = DEREF; + else if (Token::Match(parent, "%op%") && Token::simpleMatch(parent->astParent(), ".")) + deref = DEREF; + else if (Token::simpleMatch(parent, ".")) + deref = MEMBER; + else if (astIsRangeBasedForDecl(tok)) + continue; + if (deref != NONE) { + const Token* gparent = parent->astParent(); + if (deref == MEMBER) { + if (!gparent) + continue; + if (parent->astOperand2()) { + if (parent->astOperand2()->function() && parent->astOperand2()->function()->isConst()) + continue; + if (mSettings->library.isFunctionConst(parent->astOperand2())) + continue; + } + } + if (Token::Match(gparent, "%cop%") && !gparent->isUnaryOp("&") && !gparent->isUnaryOp("*")) + continue; + if (hasIncDec) { + parent = gparent; + gparent = gparent ? gparent->astParent() : nullptr; + } + int argn = -1; + if (Token::simpleMatch(gparent, "return")) { + const Function* function = gparent->scope()->function; + if (function && (!Function::returnsReference(function) || Function::returnsConst(function))) + continue; + } + else if (Token::Match(gparent, "%assign%") && parent == gparent->astOperand2()) { + bool takingRef = false, nonConstPtrAssignment = false; + const Token *lhs = gparent->astOperand1(); + if (lhs && lhs->variable() && lhs->variable()->isReference() && lhs->variable()->nameToken() == lhs && !lhs->variable()->isConst()) + takingRef = true; + if (lhs && lhs->valueType() && lhs->valueType()->pointer && (lhs->valueType()->constness & 1) == 0 && + parent->valueType() && parent->valueType()->pointer) + nonConstPtrAssignment = true; + if (!takingRef && !nonConstPtrAssignment) + continue; + } else if (Token::simpleMatch(gparent, "[") && gparent->astOperand2() == parent) + continue; + else if (gparent && gparent->isCast() && gparent->valueType() && + ((gparent->valueType()->pointer == 0 && gparent->valueType()->reference == Reference::None) || + (var->valueType() && gparent->valueType()->isConst(var->valueType()->pointer)))) + continue; + else if (const Token* ftok = getTokenArgumentFunction(parent, argn)) { + bool inconclusive{}; + if (!isVariableChangedByFunctionCall(ftok->next(), vt->pointer, var->declarationId(), mSettings, &inconclusive) && !inconclusive) + continue; + } + } else { + int argn = -1; + if (Token::Match(parent, "%oror%|%comp%|&&|?|!|-|<<")) + continue; + if (Token::simpleMatch(parent, "(") && Token::Match(parent->astOperand1(), "if|while")) + continue; + if (Token::simpleMatch(parent, "=") && parent->astOperand1() == tok) + continue; + if (const Token* ftok = getTokenArgumentFunction(tok, argn)) { + if (ftok->function()) { + const bool isCastArg = parent->isCast() && !ftok->function()->getOverloadedFunctions().empty(); // assume that cast changes the called function + if (!isCastArg) { + const Variable* argVar = ftok->function()->getArgumentVar(argn); + if (argVar && argVar->valueType() && argVar->valueType()->isConst(vt->pointer)) { + bool inconclusive{}; + if (!isVariableChangedByFunctionCall(ftok, vt->pointer, var->declarationId(), mSettings, &inconclusive) && !inconclusive) + continue; + } + } + } else { + const auto dir = mSettings->library.getArgDirection(ftok, argn + 1); + if (dir == Library::ArgumentChecks::Direction::DIR_IN) + continue; + } + } + else if (Token::simpleMatch(parent, "(")) { + if (parent->isCast() && parent->valueType() && var->valueType() && parent->valueType()->isConst(var->valueType()->pointer)) + continue; + } + } + if (tok != nameTok) + nonConstPointers.emplace(var); + } + for (const Variable *p: pointers) { + if (p->isArgument()) { + if (!p->scope() || !p->scope()->function || p->scope()->function->isImplicitlyVirtual(true) || p->scope()->function->hasVirtualSpecifier()) + continue; + if (p->isMaybeUnused()) + continue; + } + if (std::find(nonConstPointers.cbegin(), nonConstPointers.cend(), p) == nonConstPointers.cend()) { + const Token *start = getVariableChangedStart(p); + const int indirect = p->isArray() ? p->dimensions().size() : 1; + if (isVariableChanged(start, p->scope()->bodyEnd, indirect, p->declarationId(), false, mSettings)) + continue; + if (p->typeStartToken() && p->typeStartToken()->isSimplifiedTypedef() && !(Token::simpleMatch(p->typeEndToken(), "*") && !p->typeEndToken()->isSimplifiedTypedef())) + continue; + constVariableError(p, p->isArgument() ? p->scope()->function : nullptr); + } + } +} + +void CheckOther::constVariableError(const Variable *var, const Function *function) +{ + if (!var) { + reportError(nullptr, Severity::style, "constParameter", "Parameter 'x' can be declared with const"); + reportError(nullptr, Severity::style, "constVariable", "Variable 'x' can be declared with const"); + reportError(nullptr, Severity::style, "constParameterReference", "Parameter 'x' can be declared with const"); + reportError(nullptr, Severity::style, "constVariableReference", "Variable 'x' can be declared with const"); + reportError(nullptr, Severity::style, "constParameterPointer", "Parameter 'x' can be declared with const"); + reportError(nullptr, Severity::style, "constVariablePointer", "Variable 'x' can be declared with const"); + reportError(nullptr, Severity::style, "constParameterCallback", "Parameter 'x' can be declared with const, however it seems that 'f' is a callback function."); + return; + } + + const std::string vartype(var->isArgument() ? "Parameter" : "Variable"); + const std::string varname(var->name()); + const std::string ptrRefArray = var->isArray() ? "const array" : (var->isPointer() ? "pointer to const" : "reference to const"); + + ErrorPath errorPath; + std::string id = "const" + vartype; + std::string message = "$symbol:" + varname + "\n" + vartype + " '$symbol' can be declared as " + ptrRefArray; + errorPath.emplace_back(var->nameToken(), message); + if (var->isArgument() && function && function->functionPointerUsage) { + errorPath.emplace_front(function->functionPointerUsage, "You might need to cast the function pointer here"); + id += "Callback"; + message += ". However it seems that '" + function->name() + "' is a callback function, if '$symbol' is declared with const you might also need to cast function pointer(s)."; + } else if (var->isReference()) { + id += "Reference"; + } else if (var->isPointer() && !var->isArray()) { + id += "Pointer"; + } + + reportError(errorPath, Severity::style, id.c_str(), message, CWE398, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Check usage of char variables.. +//--------------------------------------------------------------------------- + +void CheckOther::checkCharVariable() +{ + const bool warning = mSettings->severity.isEnabled(Severity::warning); + const bool portability = mSettings->severity.isEnabled(Severity::portability); + if (!warning && !portability) + return; + + logChecker("CheckOther::checkCharVariable"); // warning,portability + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "%var% [")) { + if (!tok->variable()) + continue; + if (!tok->variable()->isArray() && !tok->variable()->isPointer()) + continue; + const Token *index = tok->next()->astOperand2(); + if (warning && tok->variable()->isArray() && astIsSignedChar(index) && index->getValueGE(0x80, mSettings)) + signedCharArrayIndexError(tok); + if (portability && astIsUnknownSignChar(index) && index->getValueGE(0x80, mSettings)) + unknownSignCharArrayIndexError(tok); + } else if (warning && Token::Match(tok, "[&|^]") && tok->isBinaryOp()) { + bool warn = false; + if (astIsSignedChar(tok->astOperand1())) { + const ValueFlow::Value *v1 = tok->astOperand1()->getValueLE(-1, mSettings); + const ValueFlow::Value *v2 = tok->astOperand2()->getMaxValue(false); + if (!v1) + v1 = tok->astOperand1()->getValueGE(0x80, mSettings); + if (v1 && !(tok->str() == "&" && v2 && v2->isKnown() && v2->intvalue >= 0 && v2->intvalue < 0x100)) + warn = true; + } else if (astIsSignedChar(tok->astOperand2())) { + const ValueFlow::Value *v1 = tok->astOperand2()->getValueLE(-1, mSettings); + const ValueFlow::Value *v2 = tok->astOperand1()->getMaxValue(false); + if (!v1) + v1 = tok->astOperand2()->getValueGE(0x80, mSettings); + if (v1 && !(tok->str() == "&" && v2 && v2->isKnown() && v2->intvalue >= 0 && v2->intvalue < 0x100)) + warn = true; + } + + // is the result stored in a short|int|long? + if (warn && Token::simpleMatch(tok->astParent(), "=")) { + const Token *lhs = tok->astParent()->astOperand1(); + if (lhs && lhs->valueType() && lhs->valueType()->type >= ValueType::Type::SHORT) + charBitOpError(tok); // This is an error.. + } + } + } + } +} + +void CheckOther::signedCharArrayIndexError(const Token *tok) +{ + reportError(tok, + Severity::warning, + "signedCharArrayIndex", + "Signed 'char' type used as array index.\n" + "Signed 'char' type used as array index. If the value " + "can be greater than 127 there will be a buffer underflow " + "because of sign extension.", CWE128, Certainty::normal); +} + +void CheckOther::unknownSignCharArrayIndexError(const Token *tok) +{ + reportError(tok, + Severity::portability, + "unknownSignCharArrayIndex", + "'char' type used as array index.\n" + "'char' type used as array index. Values greater than 127 will be " + "treated depending on whether 'char' is signed or unsigned on target platform.", CWE758, Certainty::normal); +} + +void CheckOther::charBitOpError(const Token *tok) +{ + reportError(tok, + Severity::warning, + "charBitOp", + "When using 'char' variables in bit operations, sign extension can generate unexpected results.\n" + "When using 'char' variables in bit operations, sign extension can generate unexpected results. For example:\n" + " char c = 0x80;\n" + " int i = 0 | c;\n" + " if (i & 0x8000)\n" + " printf(\"not expected\");\n" + "The \"not expected\" will be printed on the screen.", CWE398, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Incomplete statement.. +//--------------------------------------------------------------------------- + +static bool isType(const Token * tok, bool unknown) +{ + if (tok && (tok->isStandardType() || (!tok->isKeyword() && Token::Match(tok, "%type%")) || tok->str() == "auto")) + return true; + if (tok && tok->varId()) + return false; + if (Token::simpleMatch(tok, "::")) + return isType(tok->astOperand2(), unknown); + if (Token::simpleMatch(tok, "<") && tok->link()) + return true; + if (unknown && Token::Match(tok, "%name% !!(")) + return true; + return false; +} + +static bool isVarDeclOp(const Token* tok) +{ + if (!tok) + return false; + const Token * vartok = tok->astOperand2(); + if (vartok && vartok->variable() && vartok->variable()->nameToken() == vartok) + return true; + const Token * typetok = tok->astOperand1(); + return isType(typetok, vartok && vartok->varId() != 0); +} + +static bool isBracketAccess(const Token* tok) +{ + if (!Token::simpleMatch(tok, "[") || !tok->astOperand1()) + return false; + tok = tok->astOperand1(); + if (tok->str() == ".") + tok = tok->astOperand2(); + while (Token::simpleMatch(tok, "[")) + tok = tok->astOperand1(); + if (!tok || !tok->variable()) + return false; + return tok->variable()->nameToken() != tok; +} + +static bool isConstant(const Token* tok) { + return tok && (tok->isEnumerator() || Token::Match(tok, "%bool%|%num%|%str%|%char%|nullptr|NULL")); +} + +static bool isConstStatement(const Token *tok, bool isNestedBracket = false) +{ + if (!tok) + return false; + if (tok->isExpandedMacro()) + return false; + if (tok->varId() != 0) + return true; + if (isConstant(tok)) + return true; + if (Token::Match(tok, "*|&|&&") && + (Token::Match(tok->previous(), "::|.|const|volatile|restrict") || isVarDeclOp(tok))) + return false; + if (Token::Match(tok, "<<|>>") && !astIsIntegral(tok, false)) + return false; + const Token* tok2 = tok; + while (tok2) { + if (Token::simpleMatch(tok2->astOperand1(), "delete")) + return false; + tok2 = tok2->astParent(); + } + if (Token::Match(tok, "&&|%oror%")) + return isConstStatement(tok->astOperand1()) && isConstStatement(tok->astOperand2()); + if (Token::Match(tok, "!|~|%cop%") && (tok->astOperand1() || tok->astOperand2())) + return true; + if (Token::simpleMatch(tok->previous(), "sizeof (")) + return true; + if (isCPPCast(tok)) { + if (Token::simpleMatch(tok->astOperand1(), "dynamic_cast") && Token::simpleMatch(tok->astOperand1()->linkAt(1)->previous(), "& >")) + return false; + return isWithoutSideEffects(tok) && isConstStatement(tok->astOperand2()); + } + if (tok->isCast() && tok->next() && tok->next()->isStandardType()) + return isWithoutSideEffects(tok->astOperand1()) && isConstStatement(tok->astOperand1()); + if (Token::simpleMatch(tok, ".")) + return isConstStatement(tok->astOperand2()); + if (Token::simpleMatch(tok, ",")) { + if (tok->astParent()) // warn about const statement on rhs at the top level + return isConstStatement(tok->astOperand1()) && isConstStatement(tok->astOperand2()); + + const Token* lml = previousBeforeAstLeftmostLeaf(tok); // don't warn about matrix/vector assignment (e.g. Eigen) + if (lml) + lml = lml->next(); + const Token* stream = lml; + while (stream && Token::Match(stream->astParent(), ".|[|(|*")) + stream = stream->astParent(); + return (!stream || !isLikelyStream(stream)) && isConstStatement(tok->astOperand2()); + } + if (Token::simpleMatch(tok, "?") && Token::simpleMatch(tok->astOperand2(), ":")) // ternary operator + return isConstStatement(tok->astOperand1()) && isConstStatement(tok->astOperand2()->astOperand1()) && isConstStatement(tok->astOperand2()->astOperand2()); + if (isBracketAccess(tok) && isWithoutSideEffects(tok->astOperand1(), /*checkArrayAccess*/ true, /*checkReference*/ false)) { + const bool isChained = succeeds(tok->astParent(), tok); + if (Token::simpleMatch(tok->astParent(), "[")) { + if (isChained) + return isConstStatement(tok->astOperand2()) && isConstStatement(tok->astParent()); + return isNestedBracket && isConstStatement(tok->astOperand2()); + } + return isConstStatement(tok->astOperand2(), /*isNestedBracket*/ !isChained); + } + return false; +} + +static bool isVoidStmt(const Token *tok) +{ + if (Token::simpleMatch(tok, "( void")) + return true; + if (isCPPCast(tok) && tok->astOperand1() && Token::Match(tok->astOperand1()->next(), "< void *| >")) + return true; + const Token *tok2 = tok; + while (tok2->astOperand1()) + tok2 = tok2->astOperand1(); + if (Token::simpleMatch(tok2->previous(), ")") && Token::simpleMatch(tok2->previous()->link(), "( void")) + return true; + if (Token::simpleMatch(tok2, "( void")) + return true; + return Token::Match(tok2->previous(), "delete|throw|return"); +} + +static bool isConstTop(const Token *tok) +{ + if (!tok) + return false; + if (!tok->astParent()) + return true; + if (Token::simpleMatch(tok->astParent(), ";") && + Token::Match(tok->astTop()->previous(), "for|if (") && Token::simpleMatch(tok->astTop()->astOperand2(), ";")) { + if (Token::simpleMatch(tok->astParent()->astParent(), ";")) + return tok->astParent()->astOperand2() == tok; + return tok->astParent()->astOperand1() == tok; + } + if (Token::simpleMatch(tok, "[")) { + const Token* bracTok = tok; + while (Token::simpleMatch(bracTok->astParent(), "[")) + bracTok = bracTok->astParent(); + if (!bracTok->astParent()) + return true; + } + if (tok->str() == "," && tok->astParent()->isAssignmentOp()) + return true; + return false; +} + +void CheckOther::checkIncompleteStatement() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckOther::checkIncompleteStatement"); // warning + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + const Scope *scope = tok->scope(); + if (scope && !scope->isExecutable()) + continue; + if (!isConstTop(tok)) + continue; + if (tok->str() == "," && Token::simpleMatch(tok->astTop()->previous(), "for (")) + continue; + + // Do not warn for statement when both lhs and rhs has side effects: + // dostuff() || x=213; + if (Token::Match(tok, "%oror%|&&")) { + bool warn = false; + visitAstNodes(tok, [&warn](const Token *child) { + if (Token::Match(child, "%oror%|&&")) + return ChildrenToVisit::op1_and_op2; + if (child->isAssignmentOp()) + return ChildrenToVisit::none; + if (child->tokType() == Token::Type::eIncDecOp) + return ChildrenToVisit::none; + if (Token::Match(child->previous(), "%name% (")) + return ChildrenToVisit::none; + warn = true; + return ChildrenToVisit::done; + }); + if (!warn) + continue; + } + + const Token *rtok = nextAfterAstRightmostLeaf(tok); + if (!Token::simpleMatch(tok->astParent(), ";") && !Token::simpleMatch(rtok, ";") && + !Token::Match(tok->previous(), ";|}|{ %any% ;") && + !(tok->isCpp() && tok->isCast() && !tok->astParent()) && + !Token::simpleMatch(tok->tokAt(-2), "for (") && + !Token::Match(tok->tokAt(-1), "%var% [") && + !(tok->str() == "," && tok->astParent() && tok->astParent()->isAssignmentOp())) + continue; + // Skip statement expressions + if (Token::simpleMatch(rtok, "; } )")) + continue; + if (!isConstStatement(tok)) + continue; + if (isVoidStmt(tok)) + continue; + if (tok->isCpp() && tok->str() == "&" && !(tok->astOperand1() && tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->isIntegral())) + // Possible archive + continue; + const bool inconclusive = tok->isConstOp(); + if (mSettings->certainty.isEnabled(Certainty::inconclusive) || !inconclusive) + constStatementError(tok, tok->isNumber() ? "numeric" : "string", inconclusive); + } +} + +void CheckOther::constStatementError(const Token *tok, const std::string &type, bool inconclusive) +{ + const Token *valueTok = tok; + while (valueTok && valueTok->isCast()) + valueTok = valueTok->astOperand2() ? valueTok->astOperand2() : valueTok->astOperand1(); + + std::string msg; + if (Token::simpleMatch(tok, "==")) + msg = "Found suspicious equality comparison. Did you intend to assign a value instead?"; + else if (Token::Match(tok, ",|!|~|%cop%")) + msg = "Found suspicious operator '" + tok->str() + "', result is not used."; + else if (Token::Match(tok, "%var%")) + msg = "Unused variable value '" + tok->str() + "'"; + else if (isConstant(valueTok)) { + std::string typeStr("string"); + if (valueTok->isNumber()) + typeStr = "numeric"; + else if (valueTok->isBoolean()) + typeStr = "bool"; + else if (valueTok->tokType() == Token::eChar) + typeStr = "character"; + else if (isNullOperand(valueTok)) + typeStr = "NULL"; + else if (valueTok->isEnumerator()) + typeStr = "enumerator"; + msg = "Redundant code: Found a statement that begins with " + typeStr + " constant."; + } + else if (!tok) + msg = "Redundant code: Found a statement that begins with " + type + " constant."; + else if (tok->isCast() && tok->tokType() == Token::Type::eExtendedOp) { + msg = "Redundant code: Found unused cast "; + msg += valueTok ? "of expression '" + valueTok->expressionString() + "'." : "expression."; + } + else if (tok->str() == "?" && tok->tokType() == Token::Type::eExtendedOp) + msg = "Redundant code: Found unused result of ternary operator."; + else if (tok->str() == "." && tok->tokType() == Token::Type::eOther) + msg = "Redundant code: Found unused member access."; + else if (tok->str() == "[" && tok->tokType() == Token::Type::eExtendedOp) + msg = "Redundant code: Found unused array access."; + else if (mSettings->debugwarnings) { + reportError(tok, Severity::debug, "debug", "constStatementError not handled."); + return; + } + reportError(tok, Severity::warning, "constStatement", msg, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Detect division by zero. +//--------------------------------------------------------------------------- +void CheckOther::checkZeroDivision() +{ + logChecker("CheckOther::checkZeroDivision"); + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (!tok->astOperand2() || !tok->astOperand1()) + continue; + if (tok->str() != "%" && tok->str() != "/" && tok->str() != "%=" && tok->str() != "/=") + continue; + if (!tok->valueType() || !tok->valueType()->isIntegral()) + continue; + if (tok->scope() && tok->scope()->type == Scope::eEnum) // don't warn for compile-time error + continue; + + // Value flow.. + const ValueFlow::Value *value = tok->astOperand2()->getValue(0LL); + if (value && mSettings->isEnabled(value, false)) + zerodivError(tok, value); + } +} + +void CheckOther::zerodivError(const Token *tok, const ValueFlow::Value *value) +{ + if (!tok && !value) { + reportError(tok, Severity::error, "zerodiv", "Division by zero.", CWE369, Certainty::normal); + reportError(tok, Severity::warning, "zerodivcond", ValueFlow::eitherTheConditionIsRedundant(nullptr) + " or there is division by zero.", CWE369, Certainty::normal); + return; + } + + const ErrorPath errorPath = getErrorPath(tok, value, "Division by zero"); + + std::ostringstream errmsg; + if (value->condition) { + const int line = tok ? tok->linenr() : 0; + errmsg << ValueFlow::eitherTheConditionIsRedundant(value->condition) + << " or there is division by zero at line " << line << "."; + } else + errmsg << "Division by zero."; + + reportError(errorPath, + value->errorSeverity() ? Severity::error : Severity::warning, + value->condition ? "zerodivcond" : "zerodiv", + errmsg.str(), CWE369, value->isInconclusive() ? Certainty::inconclusive : Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Check for NaN (not-a-number) in an arithmetic expression, e.g. +// double d = 1.0 / 0.0 + 100.0; +//--------------------------------------------------------------------------- + +void CheckOther::checkNanInArithmeticExpression() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("nanInArithmeticExpression")) + return; + logChecker("CheckOther::checkNanInArithmeticExpression"); // style + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (tok->str() != "/") + continue; + if (!Token::Match(tok->astParent(), "[+-]")) + continue; + if (Token::simpleMatch(tok->astOperand2(), "0.0")) + nanInArithmeticExpressionError(tok); + } +} + +void CheckOther::nanInArithmeticExpressionError(const Token *tok) +{ + reportError(tok, Severity::style, "nanInArithmeticExpression", + "Using NaN/Inf in a computation.\n" + "Using NaN/Inf in a computation. " + "Although nothing bad really happens, it is suspicious.", CWE369, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Creating instance of classes which are destroyed immediately +//--------------------------------------------------------------------------- +void CheckOther::checkMisusedScopedObject() +{ + // Skip this check for .c files + if (mTokenizer->isC()) + return; + + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unusedScopedObject")) + return; + + logChecker("CheckOther::checkMisusedScopedObject"); // style,c++ + + auto getConstructorTok = [](const Token* tok, std::string& typeStr) -> const Token* { + if (!Token::Match(tok, "[;{}] %name%") || tok->next()->isKeyword()) + return nullptr; + tok = tok->next(); + typeStr.clear(); + while (Token::Match(tok, "%name% ::")) { + typeStr += tok->str(); + typeStr += "::"; + tok = tok->tokAt(2); + } + typeStr += tok->str(); + const Token* endTok = tok; + if (Token::Match(endTok, "%name% <")) + endTok = endTok->linkAt(1); + if (Token::Match(endTok, "%name%|> (|{") && Token::Match(endTok->linkAt(1), ")|} ;") && + !Token::simpleMatch(endTok->next()->astParent(), ";")) { // for loop condition + return tok; + } + return nullptr; + }; + + auto isLibraryConstructor = [&](const Token* tok, const std::string& typeStr) -> bool { + const Library::TypeCheck typeCheck = mSettings->library.getTypeCheck("unusedvar", typeStr); + if (typeCheck == Library::TypeCheck::check || typeCheck == Library::TypeCheck::checkFiniteLifetime) + return true; + return mSettings->library.detectContainerOrIterator(tok); + }; + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + std::string typeStr; + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { + const Token* ctorTok = getConstructorTok(tok, typeStr); + if (ctorTok && (((ctorTok->type() || ctorTok->isStandardType() || (ctorTok->function() && ctorTok->function()->isConstructor())) // TODO: The rhs of || should be removed; It is a workaround for a symboldatabase bug + && (!ctorTok->function() || ctorTok->function()->isConstructor()) // // is not a function on this scope or is function in this scope and it's a ctor + && ctorTok->str() != "void") || isLibraryConstructor(tok->next(), typeStr))) { + const Token* parTok = ctorTok->next(); + if (Token::simpleMatch(parTok, "<") && parTok->link()) + parTok = parTok->link()->next(); + if (const Token* arg = parTok->astOperand2()) { + if (!isConstStatement(arg)) + continue; + if (parTok->str() == "(") { + if (arg->varId() && !(arg->variable() && arg->variable()->nameToken() != arg)) + continue; + const Token* rml = nextAfterAstRightmostLeaf(arg); + if (rml && rml->previous() && rml->previous()->varId()) + continue; + } + } + tok = tok->next(); + misusedScopeObjectError(ctorTok, typeStr); + tok = tok->next(); + } + if (tok->isAssignmentOp() && Token::simpleMatch(tok->astOperand1(), "(") && tok->astOperand1()->astOperand1()) { + if (const Function* ftok = tok->astOperand1()->astOperand1()->function()) { + if (ftok->retType && Token::Match(ftok->retType->classDef, "class|struct|union") && !Function::returnsReference(ftok, /*unknown*/ false, /*includeRValueRef*/ true)) + misusedScopeObjectError(tok->next(), ftok->retType->name(), /*isAssignment*/ true); + } + } + } + } +} + +void CheckOther::misusedScopeObjectError(const Token *tok, const std::string& varname, bool isAssignment) +{ + std::string msg = "Instance of '$symbol' object is destroyed immediately"; + msg += isAssignment ? ", assignment has no effect." : "."; + reportError(tok, Severity::style, + "unusedScopedObject", + "$symbol:" + varname + "\n" + + msg, CWE563, Certainty::normal); +} + +static const Token * getSingleExpressionInBlock(const Token * tok) +{ + if (!tok) + return nullptr; + const Token * top = tok->astTop(); + if (!top) + return nullptr; + const Token * nextExpression = nextAfterAstRightmostLeaf(top); + if (!Token::simpleMatch(nextExpression, "; }")) + return nullptr; + return top; +} + +//----------------------------------------------------------------------------- +// check for duplicate code in if and else branches +// if (a) { b = true; } else { b = true; } +//----------------------------------------------------------------------------- +void CheckOther::checkDuplicateBranch() +{ + // This is inconclusive since in practice most warnings are noise: + // * There can be unfixed low-priority todos. The code is fine as it + // is but it could be possible to enhance it. Writing a warning + // here is noise since the code is fine (see cppcheck, abiword, ..) + // * There can be overspecified code so some conditions can't be true + // and their conditional code is a duplicate of the condition that + // is always true just in case it would be false. See for instance + // abiword. + if (!mSettings->severity.isEnabled(Severity::style) || !mSettings->certainty.isEnabled(Certainty::inconclusive)) + return; + + logChecker("CheckOther::checkDuplicateBranch"); // style,inconclusive + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope & scope : symbolDatabase->scopeList) { + if (scope.type != Scope::eIf) + continue; + + // check all the code in the function for if (..) else + if (Token::simpleMatch(scope.bodyEnd, "} else {")) { + // Make sure there are no macros (different macros might be expanded + // to the same code) + bool macro = false; + for (const Token *tok = scope.bodyStart; tok != scope.bodyEnd->linkAt(2); tok = tok->next()) { + if (tok->isExpandedMacro()) { + macro = true; + break; + } + } + if (macro) + continue; + + const Token * const tokIf = scope.bodyStart->next(); + const Token * const tokElse = scope.bodyEnd->tokAt(3); + + // compare first tok before stringifying the whole blocks + const std::string tokIfStr = tokIf->stringify(false, true, false); + if (tokIfStr.empty()) + continue; + + const std::string tokElseStr = tokElse->stringify(false, true, false); + + if (tokIfStr == tokElseStr) { + // save if branch code + const std::string branch1 = tokIf->stringifyList(scope.bodyEnd); + + if (branch1.empty()) + continue; + + // save else branch code + const std::string branch2 = tokElse->stringifyList(scope.bodyEnd->linkAt(2)); + + // check for duplicates + if (branch1 == branch2) { + duplicateBranchError(scope.classDef, scope.bodyEnd->next(), ErrorPath{}); + continue; + } + } + + // check for duplicates using isSameExpression + const Token * branchTop1 = getSingleExpressionInBlock(tokIf); + if (!branchTop1) + continue; + const Token * branchTop2 = getSingleExpressionInBlock(tokElse); + if (!branchTop2) + continue; + if (branchTop1->str() != branchTop2->str()) + continue; + ErrorPath errorPath; + if (isSameExpression(false, branchTop1->astOperand1(), branchTop2->astOperand1(), mSettings->library, true, true, &errorPath) && + isSameExpression(false, branchTop1->astOperand2(), branchTop2->astOperand2(), mSettings->library, true, true, &errorPath)) + duplicateBranchError(scope.classDef, scope.bodyEnd->next(), std::move(errorPath)); + } + } +} + +void CheckOther::duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors) +{ + errors.emplace_back(tok2, ""); + errors.emplace_back(tok1, ""); + + reportError(errors, Severity::style, "duplicateBranch", "Found duplicate branches for 'if' and 'else'.\n" + "Finding the same code in an 'if' and related 'else' branch is suspicious and " + "might indicate a cut and paste or logic error. Please examine this code " + "carefully to determine if it is correct.", CWE398, Certainty::inconclusive); +} + + +//----------------------------------------------------------------------------- +// Check for a free() of an invalid address +// char* p = malloc(100); +// free(p + 10); +//----------------------------------------------------------------------------- +void CheckOther::checkInvalidFree() +{ + std::map inconclusive; + std::map allocation; + + logChecker("CheckOther::checkInvalidFree"); + + const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); + const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok && tok != scope->bodyEnd; tok = tok->next()) { + + // Keep track of which variables were assigned addresses to newly-allocated memory + if ((tok->isCpp() && Token::Match(tok, "%var% = new")) || + (Token::Match(tok, "%var% = %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)))) { + allocation.insert(std::make_pair(tok->varId(), tok->strAt(2))); + inconclusive.insert(std::make_pair(tok->varId(), false)); + } + + // If a previously-allocated pointer is incremented or decremented, any subsequent + // free involving pointer arithmetic may or may not be invalid, so we should only + // report an inconclusive result. + else if (Token::Match(tok, "%var% = %name% +|-") && + tok->varId() == tok->tokAt(2)->varId() && + allocation.find(tok->varId()) != allocation.end()) { + if (printInconclusive) + inconclusive[tok->varId()] = true; + else { + allocation.erase(tok->varId()); + inconclusive.erase(tok->varId()); + } + } + + // If a previously-allocated pointer is assigned a completely new value, + // we can't know if any subsequent free() on that pointer is valid or not. + else if (Token::Match(tok, "%var% =")) { + allocation.erase(tok->varId()); + inconclusive.erase(tok->varId()); + } + + // If a variable that was previously assigned a newly-allocated memory location is + // added or subtracted from when used to free the memory, report an error. + else if ((Token::Match(tok, "%name% ( %any% +|-") && mSettings->library.getDeallocFuncInfo(tok)) || + Token::Match(tok, "delete [ ] ( %any% +|-") || + Token::Match(tok, "delete %any% +|- %any%")) { + + const int varIndex = tok->strAt(1) == "(" ? 2 : + tok->strAt(3) == "(" ? 4 : 1; + const int var1 = tok->tokAt(varIndex)->varId(); + const int var2 = tok->tokAt(varIndex + 2)->varId(); + const std::map::const_iterator alloc1 = inconclusive.find(var1); + const std::map::const_iterator alloc2 = inconclusive.find(var2); + if (alloc1 != inconclusive.end()) { + invalidFreeError(tok, allocation[var1], alloc1->second); + } else if (alloc2 != inconclusive.end()) { + invalidFreeError(tok, allocation[var2], alloc2->second); + } + } + + // If the previously-allocated variable is passed in to another function + // as a parameter, it might be modified, so we shouldn't report an error + // if it is later used to free memory + else if (Token::Match(tok, "%name% (") && !mSettings->library.isFunctionConst(tok->str(), true)) { + const Token* tok2 = Token::findmatch(tok->next(), "%var%", tok->linkAt(1)); + while (tok2 != nullptr) { + allocation.erase(tok->varId()); + inconclusive.erase(tok2->varId()); + tok2 = Token::findmatch(tok2->next(), "%var%", tok->linkAt(1)); + } + } + } + } +} + +void CheckOther::invalidFreeError(const Token *tok, const std::string &allocation, bool inconclusive) +{ + std::string alloc = allocation; + if (alloc != "new") + alloc += "()"; + std::string deallocated = (alloc == "new") ? "deleted" : "freed"; + reportError(tok, Severity::error, "invalidFree", "Mismatching address is " + deallocated + ". The address you get from " + alloc + " must be " + deallocated + " without offset.", CWE(0U), inconclusive ? Certainty::inconclusive : Certainty::normal); +} + + +//--------------------------------------------------------------------------- +// check for the same expression on both sides of an operator +// (x == x), (x && x), (x || x) +// (x.y == x.y), (x.y && x.y), (x.y || x.y) +//--------------------------------------------------------------------------- + +namespace { + bool notconst(const Function* func) + { + return !func->isConst(); + } + + void getConstFunctions(const SymbolDatabase *symbolDatabase, std::list &constFunctions) + { + for (const Scope &scope : symbolDatabase->scopeList) { + // only add const functions that do not have a non-const overloaded version + // since it is pretty much impossible to tell which is being called. + using StringFunctionMap = std::map>; + StringFunctionMap functionsByName; + for (const Function &func : scope.functionList) { + functionsByName[func.tokenDef->str()].push_back(&func); + } + for (std::pair>& it : functionsByName) { + const std::list::const_iterator nc = std::find_if(it.second.cbegin(), it.second.cend(), notconst); + if (nc == it.second.cend()) { + // ok to add all of them + constFunctions.splice(constFunctions.end(), it.second); + } + } + } + } +} + +void CheckOther::checkDuplicateExpression() +{ + { + const bool styleEnabled = mSettings->severity.isEnabled(Severity::style); + const bool premiumEnabled = mSettings->isPremiumEnabled("oppositeExpression") || + mSettings->isPremiumEnabled("duplicateExpression") || + mSettings->isPremiumEnabled("duplicateAssignExpression") || + mSettings->isPremiumEnabled("duplicateExpressionTernary") || + mSettings->isPremiumEnabled("duplicateValueTernary") || + mSettings->isPremiumEnabled("selfAssignment") || + mSettings->isPremiumEnabled("knownConditionTrueFalse"); + + if (!styleEnabled && !premiumEnabled) + return; + } + + logChecker("CheckOther::checkDuplicateExpression"); // style,warning + + // Parse all executing scopes.. + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + std::list constFunctions; + getConstFunctions(symbolDatabase, constFunctions); + + for (const Scope *scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (tok->str() == "=" && Token::Match(tok->astOperand1(), "%var%")) { + const Token * endStatement = Token::findsimplematch(tok, ";"); + if (Token::Match(endStatement, "; %type% %var% ;")) { + endStatement = endStatement->tokAt(4); + } + if (Token::Match(endStatement, "%var% %assign%")) { + const Token * nextAssign = endStatement->tokAt(1); + const Token * var1 = tok->astOperand1(); + const Token * var2 = nextAssign->astOperand1(); + if (var1 && var2 && + Token::Match(var1->previous(), ";|{|} %var%") && + Token::Match(var2->previous(), ";|{|} %var%") && + var2->valueType() && var1->valueType() && + var2->valueType()->originalTypeName == var1->valueType()->originalTypeName && + var2->valueType()->pointer == var1->valueType()->pointer && + var2->valueType()->constness == var1->valueType()->constness && + var2->varId() != var1->varId() && ( + tok->astOperand2()->isArithmeticalOp() || + tok->astOperand2()->str() == "." || + Token::Match(tok->astOperand2()->previous(), "%name% (") + ) && + tok->next()->tokType() != Token::eType && + isSameExpression(true, tok->next(), nextAssign->next(), mSettings->library, true, false) && + isSameExpression(true, tok->astOperand2(), nextAssign->astOperand2(), mSettings->library, true, false) && + tok->astOperand2()->expressionString() == nextAssign->astOperand2()->expressionString()) { + bool differentDomain = false; + const Scope * varScope = var1->scope() ? var1->scope() : scope; + for (const Token *assignTok = Token::findsimplematch(var2, ";"); assignTok && assignTok != varScope->bodyEnd; assignTok = assignTok->next()) { + if (!Token::Match(assignTok, "%assign%|%comp%")) + continue; + if (!assignTok->astOperand1()) + continue; + if (!assignTok->astOperand2()) + continue; + + if (assignTok->astOperand1()->varId() != var1->varId() && + assignTok->astOperand1()->varId() != var2->varId() && + !isSameExpression(true, + tok->astOperand2(), + assignTok->astOperand1(), + mSettings->library, + true, + true)) + continue; + if (assignTok->astOperand2()->varId() != var1->varId() && + assignTok->astOperand2()->varId() != var2->varId() && + !isSameExpression(true, + tok->astOperand2(), + assignTok->astOperand2(), + mSettings->library, + true, + true)) + continue; + differentDomain = true; + break; + } + if (!differentDomain && !isUniqueExpression(tok->astOperand2())) + duplicateAssignExpressionError(var1, var2, false); + else if (mSettings->certainty.isEnabled(Certainty::inconclusive)) + duplicateAssignExpressionError(var1, var2, true); + } + } + } + auto isInsideLambdaCaptureList = [](const Token* tok) { + const Token* parent = tok->astParent(); + while (Token::simpleMatch(parent, ",")) + parent = parent->astParent(); + return isLambdaCaptureList(parent); + }; + ErrorPath errorPath; + if (tok->isOp() && tok->astOperand1() && !Token::Match(tok, "+|*|<<|>>|+=|*=|<<=|>>=") && !isInsideLambdaCaptureList(tok)) { + if (Token::Match(tok, "==|!=|-") && astIsFloat(tok->astOperand1(), true)) + continue; + const bool pointerDereference = (tok->astOperand1() && tok->astOperand1()->isUnaryOp("*")) || + (tok->astOperand2() && tok->astOperand2()->isUnaryOp("*")); + const bool followVar = (!isConstVarExpression(tok) || Token::Match(tok, "%comp%|%oror%|&&")) && !pointerDereference; + if (isSameExpression(true, + tok->astOperand1(), + tok->astOperand2(), + mSettings->library, + true, + followVar, + &errorPath)) { + if (isWithoutSideEffects(tok->astOperand1())) { + const Token* loopTok = isInLoopCondition(tok); + if (!loopTok || + !findExpressionChanged(tok, tok, loopTok->link()->next()->link(), mSettings)) { + const bool isEnum = tok->scope()->type == Scope::eEnum; + const bool assignment = !isEnum && tok->str() == "="; + if (assignment) + selfAssignmentError(tok, tok->astOperand1()->expressionString()); + else if (!isEnum) { + if (tok->isCpp() && mSettings->standards.cpp >= Standards::CPP11 && tok->str() == "==") { + const Token* parent = tok->astParent(); + while (parent && parent->astParent()) { + parent = parent->astParent(); + } + if (parent && parent->previous() && parent->previous()->str() == "static_assert") { + continue; + } + } + duplicateExpressionError(tok->astOperand1(), tok->astOperand2(), tok, std::move(errorPath)); + } + } + } + } else if (tok->str() == "=" && Token::simpleMatch(tok->astOperand2(), "=") && + isSameExpression(false, + tok->astOperand1(), + tok->astOperand2()->astOperand1(), + mSettings->library, + true, + false)) { + if (isWithoutSideEffects(tok->astOperand1())) { + selfAssignmentError(tok, tok->astOperand1()->expressionString()); + } + } else if (isOppositeExpression(tok->astOperand1(), + tok->astOperand2(), + mSettings->library, + false, + true, + &errorPath) && + !Token::Match(tok, "=|-|-=|/|/=") && + isWithoutSideEffects(tok->astOperand1())) { + oppositeExpressionError(tok, std::move(errorPath)); + } else if (!Token::Match(tok, "[-/%]")) { // These operators are not associative + if (tok->astOperand2() && tok->str() == tok->astOperand1()->str() && + isSameExpression(true, + tok->astOperand2(), + tok->astOperand1()->astOperand2(), + mSettings->library, + true, + followVar, + &errorPath) && + isWithoutSideEffects(tok->astOperand2())) + duplicateExpressionError(tok->astOperand2(), tok->astOperand1()->astOperand2(), tok, errorPath); + else if (tok->astOperand2() && isConstExpression(tok->astOperand1(), mSettings->library)) { + auto checkDuplicate = [&](const Token* exp1, const Token* exp2, const Token* ast1) { + if (isSameExpression(true, exp1, exp2, mSettings->library, true, true, &errorPath) && + isWithoutSideEffects(exp1) && + isWithoutSideEffects(ast1->astOperand2())) + duplicateExpressionError(exp1, exp2, tok, errorPath, /*hasMultipleExpr*/ true); + }; + const Token *ast1 = tok->astOperand1(); + while (ast1 && tok->str() == ast1->str()) { // chain of identical operators + checkDuplicate(ast1->astOperand2(), tok->astOperand2(), ast1); + if (ast1->astOperand1() && ast1->astOperand1()->str() != tok->str()) // check first condition in the chain + checkDuplicate(ast1->astOperand1(), tok->astOperand2(), ast1); + ast1 = ast1->astOperand1(); + } + } + } + } else if (tok->astOperand1() && tok->astOperand2() && tok->str() == ":" && tok->astParent() && tok->astParent()->str() == "?") { + if (!tok->astOperand1()->values().empty() && !tok->astOperand2()->values().empty() && isEqualKnownValue(tok->astOperand1(), tok->astOperand2()) && + !isVariableChanged(tok->astParent(), /*indirect*/ 0, mSettings) && + isConstStatement(tok->astOperand1()) && isConstStatement(tok->astOperand2())) + duplicateValueTernaryError(tok); + else if (isSameExpression(true, tok->astOperand1(), tok->astOperand2(), mSettings->library, false, true, &errorPath)) + duplicateExpressionTernaryError(tok, std::move(errorPath)); + } + } + } +} + +void CheckOther::oppositeExpressionError(const Token *opTok, ErrorPath errors) +{ + errors.emplace_back(opTok, ""); + + const std::string& op = opTok ? opTok->str() : "&&"; + + reportError(errors, Severity::style, "oppositeExpression", "Opposite expression on both sides of \'" + op + "\'.\n" + "Finding the opposite expression on both sides of an operator is suspicious and might " + "indicate a cut and paste or logic error. Please examine this code carefully to " + "determine if it is correct.", CWE398, Certainty::normal); +} + +void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors, bool hasMultipleExpr) +{ + errors.emplace_back(opTok, ""); + + const std::string& expr1 = tok1 ? tok1->expressionString() : "x"; + const std::string& expr2 = tok2 ? tok2->expressionString() : "x"; + + const std::string& op = opTok ? opTok->str() : "&&"; + std::string msg = "Same expression " + (hasMultipleExpr ? "\'" + expr1 + "\'" + " found multiple times in chain of \'" + op + "\' operators" : "on both sides of \'" + op + "\'"); + const char *id = "duplicateExpression"; + if (expr1 != expr2 && (!opTok || Token::Match(opTok, "%oror%|%comp%|&&|?|!"))) { + id = "knownConditionTrueFalse"; + std::string exprMsg = "The comparison \'" + expr1 + " " + op + " " + expr2 + "\' is always "; + if (Token::Match(opTok, "==|>=|<=")) + msg = exprMsg + "true"; + else if (Token::Match(opTok, "!=|>|<")) + msg = exprMsg + "false"; + } + + if (expr1 != expr2 && !Token::Match(tok1, "%num%|NULL|nullptr") && !Token::Match(tok2, "%num%|NULL|nullptr")) + msg += " because '" + expr1 + "' and '" + expr2 + "' represent the same value"; + + reportError(errors, Severity::style, id, msg + + (std::string(".\nFinding the same expression ") + (hasMultipleExpr ? "more than once in a condition" : "on both sides of an operator")) + + " is suspicious and might indicate a cut and paste or logic error. Please examine this code carefully to " + "determine if it is correct.", CWE398, Certainty::normal); +} + +void CheckOther::duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive) +{ + const std::list toks = { tok2, tok1 }; + + const std::string& var1 = tok1 ? tok1->str() : "x"; + const std::string& var2 = tok2 ? tok2->str() : "x"; + + reportError(toks, Severity::style, "duplicateAssignExpression", + "Same expression used in consecutive assignments of '" + var1 + "' and '" + var2 + "'.\n" + "Finding variables '" + var1 + "' and '" + var2 + "' that are assigned the same expression " + "is suspicious and might indicate a cut and paste or logic error. Please examine this code carefully to " + "determine if it is correct.", CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckOther::duplicateExpressionTernaryError(const Token *tok, ErrorPath errors) +{ + errors.emplace_back(tok, ""); + reportError(errors, Severity::style, "duplicateExpressionTernary", "Same expression in both branches of ternary operator.\n" + "Finding the same expression in both branches of ternary operator is suspicious as " + "the same code is executed regardless of the condition.", CWE398, Certainty::normal); +} + +void CheckOther::duplicateValueTernaryError(const Token *tok) +{ + reportError(tok, Severity::style, "duplicateValueTernary", "Same value in both branches of ternary operator.\n" + "Finding the same value in both branches of ternary operator is suspicious as " + "the same code is executed regardless of the condition.", CWE398, Certainty::normal); +} + +void CheckOther::selfAssignmentError(const Token *tok, const std::string &varname) +{ + reportError(tok, Severity::style, + "selfAssignment", + "$symbol:" + varname + "\n" + "Redundant assignment of '$symbol' to itself.", CWE398, Certainty::normal); +} + +//----------------------------------------------------------------------------- +// Check is a comparison of two variables leads to condition, which is +// always true or false. +// For instance: int a = 1; if(isless(a,a)){...} +// In this case isless(a,a) always evaluates to false. +// +// Reference: +// - http://www.cplusplus.com/reference/cmath/ +//----------------------------------------------------------------------------- +void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalse() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalse"); // warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (tok->isName() && Token::Match(tok, "isgreater|isless|islessgreater|isgreaterequal|islessequal ( %var% , %var% )")) { + const int varidLeft = tok->tokAt(2)->varId();// get the left varid + const int varidRight = tok->tokAt(4)->varId();// get the right varid + // compare varids: if they are not zero but equal + // --> the comparison function is called with the same variables + if (varidLeft == varidRight) { + const std::string& functionName = tok->str(); // store function name + const std::string& varNameLeft = tok->strAt(2); // get the left variable name + if (functionName == "isgreater" || functionName == "isless" || functionName == "islessgreater") { + // e.g.: isgreater(x,x) --> (x)>(x) --> false + checkComparisonFunctionIsAlwaysTrueOrFalseError(tok, functionName, varNameLeft, false); + } else { // functionName == "isgreaterequal" || functionName == "islessequal" + // e.g.: isgreaterequal(x,x) --> (x)>=(x) --> true + checkComparisonFunctionIsAlwaysTrueOrFalseError(tok, functionName, varNameLeft, true); + } + } + } + } + } +} +void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &functionName, const std::string &varName, const bool result) +{ + const std::string strResult = bool_to_string(result); + const CWE cweResult = result ? CWE571 : CWE570; + + reportError(tok, Severity::warning, "comparisonFunctionIsAlwaysTrueOrFalse", + "$symbol:" + functionName + "\n" + "Comparison of two identical variables with $symbol(" + varName + "," + varName + ") always evaluates to " + strResult + ".\n" + "The function $symbol is designed to compare two variables. Calling this function with one variable (" + varName + ") " + "for both parameters leads to a statement which is always " + strResult + ".", cweResult, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Check testing sign of unsigned variables and pointers. +//--------------------------------------------------------------------------- +void CheckOther::checkSignOfUnsignedVariable() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unsignedLessThanZero")) + return; + + logChecker("CheckOther::checkSignOfUnsignedVariable"); // style + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope * scope : symbolDatabase->functionScopes) { + // check all the code in the function + for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + const ValueFlow::Value *zeroValue = nullptr; + const Token *nonZeroExpr = nullptr; + if (comparisonNonZeroExpressionLessThanZero(tok, zeroValue, nonZeroExpr)) { + const ValueType* vt = nonZeroExpr->valueType(); + if (vt->pointer) + pointerLessThanZeroError(tok, zeroValue); + else + unsignedLessThanZeroError(tok, zeroValue, nonZeroExpr->expressionString()); + } else if (testIfNonZeroExpressionIsPositive(tok, zeroValue, nonZeroExpr)) { + const ValueType* vt = nonZeroExpr->valueType(); + if (vt->pointer) + pointerPositiveError(tok, zeroValue); + else + unsignedPositiveError(tok, zeroValue, nonZeroExpr->expressionString()); + } + } + } +} + +bool CheckOther::comparisonNonZeroExpressionLessThanZero(const Token *tok, const ValueFlow::Value *&zeroValue, const Token *&nonZeroExpr, bool suppress) +{ + if (!tok->isComparisonOp() || !tok->astOperand1() || !tok->astOperand2()) + return false; + + const ValueFlow::Value *v1 = tok->astOperand1()->getValue(0); + const ValueFlow::Value *v2 = tok->astOperand2()->getValue(0); + + if (Token::Match(tok, "<|<=") && v2 && v2->isKnown()) { + zeroValue = v2; + nonZeroExpr = tok->astOperand1(); + } else if (Token::Match(tok, ">|>=") && v1 && v1->isKnown()) { + zeroValue = v1; + nonZeroExpr = tok->astOperand2(); + } else { + return false; + } + + if (const Variable* var = nonZeroExpr->variable()) + if (var->typeStartToken()->isTemplateArg()) + return suppress; + + const ValueType* vt = nonZeroExpr->valueType(); + return vt && (vt->pointer || vt->sign == ValueType::UNSIGNED); +} + +bool CheckOther::testIfNonZeroExpressionIsPositive(const Token *tok, const ValueFlow::Value *&zeroValue, const Token *&nonZeroExpr) +{ + if (!tok->isComparisonOp() || !tok->astOperand1() || !tok->astOperand2()) + return false; + + const ValueFlow::Value *v1 = tok->astOperand1()->getValue(0); + const ValueFlow::Value *v2 = tok->astOperand2()->getValue(0); + + if (Token::simpleMatch(tok, ">=") && v2 && v2->isKnown()) { + zeroValue = v2; + nonZeroExpr = tok->astOperand1(); + } else if (Token::simpleMatch(tok, "<=") && v1 && v1->isKnown()) { + zeroValue = v1; + nonZeroExpr = tok->astOperand2(); + } else { + return false; + } + + const ValueType* vt = nonZeroExpr->valueType(); + return vt && (vt->pointer || vt->sign == ValueType::UNSIGNED); +} + +void CheckOther::unsignedLessThanZeroError(const Token *tok, const ValueFlow::Value * v, const std::string &varname) +{ + reportError(getErrorPath(tok, v, "Unsigned less than zero"), Severity::style, "unsignedLessThanZero", + "$symbol:" + varname + "\n" + "Checking if unsigned expression '$symbol' is less than zero.\n" + "The unsigned expression '$symbol' will never be negative so it " + "is either pointless or an error to check if it is.", CWE570, Certainty::normal); +} + +void CheckOther::pointerLessThanZeroError(const Token *tok, const ValueFlow::Value *v) +{ + reportError(getErrorPath(tok, v, "Pointer less than zero"), Severity::style, "pointerLessThanZero", + "A pointer can not be negative so it is either pointless or an error to check if it is.", CWE570, Certainty::normal); +} + +void CheckOther::unsignedPositiveError(const Token *tok, const ValueFlow::Value * v, const std::string &varname) +{ + reportError(getErrorPath(tok, v, "Unsigned positive"), Severity::style, "unsignedPositive", + "$symbol:" + varname + "\n" + "Unsigned expression '$symbol' can't be negative so it is unnecessary to test it.", CWE570, Certainty::normal); +} + +void CheckOther::pointerPositiveError(const Token *tok, const ValueFlow::Value * v) +{ + reportError(getErrorPath(tok, v, "Pointer positive"), Severity::style, "pointerPositive", + "A pointer can not be negative so it is either pointless or an error to check if it is not.", CWE570, Certainty::normal); +} + +/* check if a constructor in given class scope takes a reference */ +static bool constructorTakesReference(const Scope * const classScope) +{ + return std::any_of(classScope->functionList.begin(), classScope->functionList.end(), [&](const Function& constructor) { + if (constructor.isConstructor()) { + for (int argnr = 0U; argnr < constructor.argCount(); argnr++) { + const Variable * const argVar = constructor.getArgumentVar(argnr); + if (argVar && argVar->isReference()) { + return true; + } + } + } + return false; + }); +} + +//--------------------------------------------------------------------------- +// This check rule works for checking the "const A a = getA()" usage when getA() returns "const A &" or "A &". +// In most scenarios, "const A & a = getA()" will be more efficient. +//--------------------------------------------------------------------------- +void CheckOther::checkRedundantCopy() +{ + if (!mSettings->severity.isEnabled(Severity::performance) || mTokenizer->isC() || !mSettings->certainty.isEnabled(Certainty::inconclusive)) + return; + + logChecker("CheckOther::checkRedundantCopy"); // c++,performance,inconclusive + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Variable* var : symbolDatabase->variableList()) { + if (!var || var->isReference() || var->isPointer() || + (!var->type() && !var->isStlType()) || // bailout if var is of standard type, if it is a pointer or non-const + (!var->isConst() && isVariableChanged(var, mSettings))) + continue; + + const Token* startTok = var->nameToken(); + if (startTok->strAt(1) == "=") // %type% %name% = ... ; + ; + else if (Token::Match(startTok->next(), "(|{") && var->isClass() && var->typeScope()) { + // Object is instantiated. Warn if constructor takes arguments by value. + if (constructorTakesReference(var->typeScope())) + continue; + } else if (Token::simpleMatch(startTok->next(), ";") && startTok->next()->isSplittedVarDeclEq()) { + startTok = startTok->tokAt(2); + } else + continue; + + const Token* tok = startTok->next()->astOperand2(); + if (!tok) + continue; + if (!Token::Match(tok->previous(), "%name% (")) + continue; + if (!Token::Match(tok->link(), ") )| ;")) // bailout for usage like "const A a = getA()+3" + continue; + + const Token* dot = tok->astOperand1(); + if (Token::simpleMatch(dot, ".")) { + if (dot->astOperand1() && isVariableChanged(dot->astOperand1()->variable(), mSettings)) + continue; + if (isTemporary(dot, &mSettings->library, /*unknown*/ true)) + continue; + } + if (exprDependsOnThis(tok->previous())) + continue; + + const Function* func = tok->previous()->function(); + if (func && func->tokenDef->strAt(-1) == "&") { + const Scope* fScope = func->functionScope; + if (fScope && fScope->bodyEnd && Token::Match(fScope->bodyEnd->tokAt(-3), "return %var% ;")) { + const Token* varTok = fScope->bodyEnd->tokAt(-2); + if (varTok->variable() && !varTok->variable()->isGlobal() && + (!varTok->variable()->type() || !varTok->variable()->type()->classScope || + (varTok->variable()->valueType() && ValueFlow::getSizeOf(*varTok->variable()->valueType(), *mSettings) > 2 * mSettings->platform.sizeof_pointer))) + redundantCopyError(startTok, startTok->str()); + } + } + } +} + +void CheckOther::redundantCopyError(const Token *tok,const std::string& varname) +{ + reportError(tok, Severity::performance, "redundantCopyLocalConst", + "$symbol:" + varname + "\n" + "Use const reference for '$symbol' to avoid unnecessary data copying.\n" + "The const variable '$symbol' is assigned a copy of the data. You can avoid " + "the unnecessary data copying by converting '$symbol' to const reference.", + CWE398, + Certainty::inconclusive); // since #5618 that check became inconclusive +} + +//--------------------------------------------------------------------------- +// Checking for shift by negative values +//--------------------------------------------------------------------------- + +static bool isNegative(const Token *tok, const Settings *settings) +{ + return tok->valueType() && tok->valueType()->sign == ValueType::SIGNED && tok->getValueLE(-1LL, settings); +} + +void CheckOther::checkNegativeBitwiseShift() +{ + const bool portability = mSettings->severity.isEnabled(Severity::portability); + + logChecker("CheckOther::checkNegativeBitwiseShift"); + + for (const Token* tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (!tok->astOperand1() || !tok->astOperand2()) + continue; + + if (!Token::Match(tok, "<<|>>|<<=|>>=")) + continue; + + // don't warn if lhs is a class. this is an overloaded operator then + if (tok->isCpp()) { + const ValueType * lhsType = tok->astOperand1()->valueType(); + if (!lhsType || !lhsType->isIntegral()) + continue; + } + + // bailout if operation is protected by ?: + bool ternary = false; + for (const Token *parent = tok; parent; parent = parent->astParent()) { + if (Token::Match(parent, "?|:")) { + ternary = true; + break; + } + } + if (ternary) + continue; + + // Get negative rhs value. preferably a value which doesn't have 'condition'. + if (portability && isNegative(tok->astOperand1(), mSettings)) + negativeBitwiseShiftError(tok, 1); + else if (isNegative(tok->astOperand2(), mSettings)) + negativeBitwiseShiftError(tok, 2); + } +} + + +void CheckOther::negativeBitwiseShiftError(const Token *tok, int op) +{ + if (op == 1) + // LHS - this is used by intention in various software, if it + // is used often in a project and works as expected then this is + // a portability issue + reportError(tok, Severity::portability, "shiftNegativeLHS", "Shifting a negative value is technically undefined behaviour", CWE758, Certainty::normal); + else // RHS + reportError(tok, Severity::error, "shiftNegative", "Shifting by a negative value is undefined behaviour", CWE758, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Check for incompletely filled buffers. +//--------------------------------------------------------------------------- +void CheckOther::checkIncompleteArrayFill() +{ + if (!mSettings->certainty.isEnabled(Certainty::inconclusive)) + return; + const bool printWarning = mSettings->severity.isEnabled(Severity::warning); + const bool printPortability = mSettings->severity.isEnabled(Severity::portability); + if (!printPortability && !printWarning) + return; + + logChecker("CheckOther::checkIncompleteArrayFill"); // warning,portability,inconclusive + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "memset|memcpy|memmove (") && Token::Match(tok->linkAt(1)->tokAt(-2), ", %num% )")) { + const Token* tok2 = tok->tokAt(2); + if (tok2->str() == "::") + tok2 = tok2->next(); + while (Token::Match(tok2, "%name% ::|.")) + tok2 = tok2->tokAt(2); + if (!Token::Match(tok2, "%var% ,")) + continue; + + const Variable *var = tok2->variable(); + if (!var || !var->isArray() || var->dimensions().empty() || !var->dimension(0)) + continue; + + if (MathLib::toBigNumber(tok->linkAt(1)->strAt(-1)) == var->dimension(0)) { + int size = mTokenizer->sizeOfType(var->typeStartToken()); + if (size == 0 && var->valueType()->pointer) + size = mSettings->platform.sizeof_pointer; + else if (size == 0 && var->valueType()) + size = ValueFlow::getSizeOf(*var->valueType(), *mSettings); + const Token* tok3 = tok->next()->astOperand2()->astOperand1()->astOperand1(); + if ((size != 1 && size != 100 && size != 0) || var->isPointer()) { + if (printWarning) + incompleteArrayFillError(tok, tok3->expressionString(), tok->str(), false); + } else if (var->valueType()->type == ValueType::Type::BOOL && printPortability) // sizeof(bool) is not 1 on all platforms + incompleteArrayFillError(tok, tok3->expressionString(), tok->str(), true); + } + } + } + } +} + +void CheckOther::incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean) +{ + if (boolean) + reportError(tok, Severity::portability, "incompleteArrayFill", + "$symbol:" + buffer + "\n" + "$symbol:" + function + "\n" + "Array '" + buffer + "' might be filled incompletely. Did you forget to multiply the size given to '" + function + "()' with 'sizeof(*" + buffer + ")'?\n" + "The array '" + buffer + "' is filled incompletely. The function '" + function + "()' needs the size given in bytes, but the type 'bool' is larger than 1 on some platforms. Did you forget to multiply the size with 'sizeof(*" + buffer + ")'?", CWE131, Certainty::inconclusive); + else + reportError(tok, Severity::warning, "incompleteArrayFill", + "$symbol:" + buffer + "\n" + "$symbol:" + function + "\n" + "Array '" + buffer + "' is filled incompletely. Did you forget to multiply the size given to '" + function + "()' with 'sizeof(*" + buffer + ")'?\n" + "The array '" + buffer + "' is filled incompletely. The function '" + function + "()' needs the size given in bytes, but an element of the given array is larger than one byte. Did you forget to multiply the size with 'sizeof(*" + buffer + ")'?", CWE131, Certainty::inconclusive); +} + +//--------------------------------------------------------------------------- +// Detect NULL being passed to variadic function. +//--------------------------------------------------------------------------- + +void CheckOther::checkVarFuncNullUB() +{ + if (!mSettings->severity.isEnabled(Severity::portability)) + return; + + logChecker("CheckOther::checkVarFuncNullUB"); // portability + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + // Is NULL passed to a function? + if (Token::Match(tok,"[(,] NULL [,)]")) { + // Locate function name in this function call. + const Token *ftok = tok; + int argnr = 1; + while (ftok && ftok->str() != "(") { + if (ftok->str() == ")") + ftok = ftok->link(); + else if (ftok->str() == ",") + ++argnr; + ftok = ftok->previous(); + } + ftok = ftok ? ftok->previous() : nullptr; + if (ftok && ftok->isName()) { + // If this is a variadic function then report error + const Function *f = ftok->function(); + if (f && f->argCount() <= argnr) { + const Token *tok2 = f->argDef; + tok2 = tok2 ? tok2->link() : nullptr; // goto ')' + if (tok2 && Token::simpleMatch(tok2->tokAt(-1), "...")) + varFuncNullUBError(tok); + } + } + } + } + } +} + +void CheckOther::varFuncNullUBError(const Token *tok) +{ + reportError(tok, + Severity::portability, + "varFuncNullUB", + "Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n" + "Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n" + "The C99 standard, in section 7.15.1.1, states that if the type used by va_arg() is not compatible with the type of the actual next argument (as promoted according to the default argument promotions), the behavior is undefined.\n" + "The value of the NULL macro is an implementation-defined null pointer constant (7.17), which can be any integer constant expression with the value 0, or such an expression casted to (void*) (6.3.2.3). This includes values like 0, 0L, or even 0LL.\n" + "In practice on common architectures, this will cause real crashes if sizeof(int) != sizeof(void*), and NULL is defined to 0 or any other null pointer constant that promotes to int.\n" + "To reproduce you might be able to use this little code example on 64bit platforms. If the output includes \"ERROR\", the sentinel had only 4 out of 8 bytes initialized to zero and was not detected as the final argument to stop argument processing via va_arg(). Changing the 0 to (void*)0 or 0L will make the \"ERROR\" output go away.\n" + "#include \n" + "#include \n" + "\n" + "void f(char *s, ...) {\n" + " va_list ap;\n" + " va_start(ap,s);\n" + " for (;;) {\n" + " char *p = va_arg(ap,char*);\n" + " printf(\"%018p, %s\\n\", p, (long)p & 255 ? p : \"\");\n" + " if(!p) break;\n" + " }\n" + " va_end(ap);\n" + "}\n" + "\n" + "void g() {\n" + " char *s2 = \"x\";\n" + " char *s3 = \"ERROR\";\n" + "\n" + " // changing 0 to 0L for the 7th argument (which is intended to act as sentinel) makes the error go away on x86_64\n" + " f(\"first\", s2, s2, s2, s2, s2, 0, s3, (char*)0);\n" + "}\n" + "\n" + "void h() {\n" + " int i;\n" + " volatile unsigned char a[1000];\n" + " for (i = 0; iseverity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("redundantPointerOp")) + return; + + logChecker("CheckOther::checkRedundantPointerOp"); // style + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (tok->isExpandedMacro() && tok->str() == "(") + tok = tok->link(); + + bool addressOfDeref{}; + if (tok->isUnaryOp("&") && tok->astOperand1()->isUnaryOp("*")) + addressOfDeref = true; + else if (tok->isUnaryOp("*") && tok->astOperand1()->isUnaryOp("&")) + addressOfDeref = false; + else + continue; + + // variable + const Token *varTok = tok->astOperand1()->astOperand1(); + if (!varTok || varTok->isExpandedMacro()) + continue; + + if (!addressOfDeref) { // dereference of address + if (tok->isExpandedMacro()) + continue; + if (varTok->valueType() && varTok->valueType()->pointer && varTok->valueType()->reference == Reference::LValue) + continue; + } + + const Variable *var = varTok->variable(); + if (!var || (addressOfDeref && !var->isPointer())) + continue; + + redundantPointerOpError(tok, var->name(), false, addressOfDeref); + } +} + +void CheckOther::redundantPointerOpError(const Token* tok, const std::string &varname, bool inconclusive, bool addressOfDeref) +{ + std::string msg = "$symbol:" + varname + "\nRedundant pointer operation on '$symbol' - it's already a "; + msg += addressOfDeref ? "pointer." : "variable."; + reportError(tok, Severity::style, "redundantPointerOp", msg, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckOther::checkInterlockedDecrement() +{ + if (!mSettings->platform.isWindows()) { + return; + } + + logChecker("CheckOther::checkInterlockedDecrement"); // windows-platform + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (tok->isName() && Token::Match(tok, "InterlockedDecrement ( & %name% ) ; if ( %name%|!|0")) { + const Token* interlockedVarTok = tok->tokAt(3); + const Token* checkStartTok = interlockedVarTok->tokAt(5); + if ((Token::Match(checkStartTok, "0 %comp% %name% )") && checkStartTok->strAt(2) == interlockedVarTok->str()) || + (Token::Match(checkStartTok, "! %name% )") && checkStartTok->strAt(1) == interlockedVarTok->str()) || + (Token::Match(checkStartTok, "%name% )") && checkStartTok->str() == interlockedVarTok->str()) || + (Token::Match(checkStartTok, "%name% %comp% 0 )") && checkStartTok->str() == interlockedVarTok->str())) { + raceAfterInterlockedDecrementError(checkStartTok); + } + } else if (Token::Match(tok, "if ( ::| InterlockedDecrement ( & %name%")) { + const Token* condEnd = tok->next()->link(); + const Token* funcTok = tok->tokAt(2); + const Token* firstAccessTok = funcTok->str() == "::" ? funcTok->tokAt(4) : funcTok->tokAt(3); + if (condEnd && condEnd->next() && condEnd->next()->link()) { + const Token* ifEndTok = condEnd->next()->link(); + if (Token::Match(ifEndTok, "} return %name%")) { + const Token* secondAccessTok = ifEndTok->tokAt(2); + if (secondAccessTok->str() == firstAccessTok->str()) { + raceAfterInterlockedDecrementError(secondAccessTok); + } + } else if (Token::Match(ifEndTok, "} else { return %name%")) { + const Token* secondAccessTok = ifEndTok->tokAt(4); + if (secondAccessTok->str() == firstAccessTok->str()) { + raceAfterInterlockedDecrementError(secondAccessTok); + } + } + } + } + } +} + +void CheckOther::raceAfterInterlockedDecrementError(const Token* tok) +{ + reportError(tok, Severity::error, "raceAfterInterlockedDecrement", + "Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.", CWE362, Certainty::normal); +} + +void CheckOther::checkUnusedLabel() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("unusedLabel")) + return; + + logChecker("CheckOther::checkUnusedLabel"); // style,warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + const bool hasIfdef = mTokenizer->hasIfdef(scope->bodyStart, scope->bodyEnd); + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (!tok->scope()->isExecutable()) + tok = tok->scope()->bodyEnd; + + if (Token::Match(tok, "{|}|; %name% :") && !tok->tokAt(1)->isKeyword()) { + const std::string tmp("goto " + tok->strAt(1)); + if (!Token::findsimplematch(scope->bodyStart->next(), tmp.c_str(), tmp.size(), scope->bodyEnd->previous())) + unusedLabelError(tok->next(), tok->next()->scope()->type == Scope::eSwitch, hasIfdef); + } + } + } +} + +void CheckOther::unusedLabelError(const Token* tok, bool inSwitch, bool hasIfdef) +{ + if (tok && !mSettings->severity.isEnabled(inSwitch ? Severity::warning : Severity::style)) + return; + + std::string id = "unusedLabel"; + if (inSwitch) + id += "Switch"; + if (hasIfdef) + id += "Configuration"; + + std::string msg = "$symbol:" + (tok ? tok->str() : emptyString) + "\nLabel '$symbol' is not used."; + if (hasIfdef) + msg += " There is #if in function body so the label might be used in code that is removed by the preprocessor."; + if (inSwitch) + msg += " Should this be a 'case' of the enclosing switch()?"; + + reportError(tok, + inSwitch ? Severity::warning : Severity::style, + id, + msg, + CWE398, + Certainty::normal); +} + + +void CheckOther::checkEvaluationOrder() +{ + // This checker is not written according to C++11 sequencing rules + if (mTokenizer->isCPP() && mSettings->standards.cpp >= Standards::CPP11) + return; + + logChecker("CheckOther::checkEvaluationOrder"); // C/C++03 + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * functionScope : symbolDatabase->functionScopes) { + for (const Token* tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { + if (tok->tokType() != Token::eIncDecOp && !tok->isAssignmentOp()) + continue; + if (!tok->astOperand1()) + continue; + for (const Token *tok2 = tok;; tok2 = tok2->astParent()) { + // If ast parent is a sequence point then break + const Token * const parent = tok2->astParent(); + if (!parent) + break; + if (Token::Match(parent, "%oror%|&&|?|:|;")) + break; + if (parent->str() == ",") { + const Token *par = parent; + while (Token::simpleMatch(par,",")) + par = par->astParent(); + // not function or in a while clause => break + if (!(par && par->str() == "(" && par->astOperand2() && par->strAt(-1) != "while")) + break; + // control flow (if|while|etc) => break + if (Token::simpleMatch(par->link(),") {")) + break; + // sequence point in function argument: dostuff((1,2),3) => break + par = par->next(); + while (par && (par->previous() != parent)) + par = par->nextArgument(); + if (!par) + break; + } + if (parent->str() == "(" && parent->astOperand2()) + break; + + // self assignment.. + if (tok2 == tok && + tok->str() == "=" && + parent->str() == "=" && + isSameExpression(false, tok->astOperand1(), parent->astOperand1(), mSettings->library, true, false)) { + if (mSettings->severity.isEnabled(Severity::warning) && + isSameExpression(true, tok->astOperand1(), parent->astOperand1(), mSettings->library, true, false)) + selfAssignmentError(parent, tok->astOperand1()->expressionString()); + break; + } + + // Is expression used? + bool foundError = false; + visitAstNodes((parent->astOperand1() != tok2) ? parent->astOperand1() : parent->astOperand2(), + [&](const Token *tok3) { + if (tok3->str() == "&" && !tok3->astOperand2()) + return ChildrenToVisit::none; // don't handle address-of for now + if (tok3->str() == "(" && Token::simpleMatch(tok3->previous(), "sizeof")) + return ChildrenToVisit::none; // don't care about sizeof usage + if (isSameExpression(false, tok->astOperand1(), tok3, mSettings->library, true, false)) + foundError = true; + return foundError ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; + }); + + if (foundError) { + unknownEvaluationOrder(parent); + break; + } + } + } + } +} + +void CheckOther::unknownEvaluationOrder(const Token* tok) +{ + reportError(tok, Severity::error, "unknownEvaluationOrder", + "Expression '" + (tok ? tok->expressionString() : std::string("x = x++;")) + "' depends on order of evaluation of side effects", CWE768, Certainty::normal); +} + +void CheckOther::checkAccessOfMovedVariable() +{ + if (!mTokenizer->isCPP() || mSettings->standards.cpp < Standards::CPP11 || !mSettings->severity.isEnabled(Severity::warning)) + return; + logChecker("CheckOther::checkAccessOfMovedVariable"); // c++11,warning + const bool reportInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + const Token * scopeStart = scope->bodyStart; + if (scope->function) { + const Token * memberInitializationStart = scope->function->constructorMemberInitialization(); + if (memberInitializationStart) + scopeStart = memberInitializationStart; + } + for (const Token* tok = scopeStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!tok->astParent()) + continue; + const ValueFlow::Value * movedValue = tok->getMovedValue(); + if (!movedValue || movedValue->moveKind == ValueFlow::Value::MoveKind::NonMovedVariable) + continue; + if (movedValue->isInconclusive() && !reportInconclusive) + continue; + + bool inconclusive = false; + bool accessOfMoved = false; + if (tok->strAt(1) == ".") { + if (tok->next()->originalName() == "->") + accessOfMoved = true; + else + inconclusive = true; + } else { + const ExprUsage usage = getExprUsage(tok, 0, *mSettings); + if (usage == ExprUsage::Used) + accessOfMoved = true; + if (usage == ExprUsage::PassedByReference) + accessOfMoved = !isVariableChangedByFunctionCall(tok, 0, mSettings, &inconclusive); + else if (usage == ExprUsage::Inconclusive) + inconclusive = true; + } + if (accessOfMoved || (inconclusive && reportInconclusive)) + accessMovedError(tok, tok->str(), movedValue, inconclusive || movedValue->isInconclusive()); + } + } +} + +void CheckOther::accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive) +{ + if (!tok) { + reportError(tok, Severity::warning, "accessMoved", "Access of moved variable 'v'.", CWE672, Certainty::normal); + reportError(tok, Severity::warning, "accessForwarded", "Access of forwarded variable 'v'.", CWE672, Certainty::normal); + return; + } + + const char * errorId = nullptr; + std::string kindString; + switch (value->moveKind) { + case ValueFlow::Value::MoveKind::MovedVariable: + errorId = "accessMoved"; + kindString = "moved"; + break; + case ValueFlow::Value::MoveKind::ForwardedVariable: + errorId = "accessForwarded"; + kindString = "forwarded"; + break; + default: + return; + } + const std::string errmsg("$symbol:" + varname + "\nAccess of " + kindString + " variable '$symbol'."); + const ErrorPath errorPath = getErrorPath(tok, value, errmsg); + reportError(errorPath, Severity::warning, errorId, errmsg, CWE672, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + + + +void CheckOther::checkFuncArgNamesDifferent() +{ + const bool style = mSettings->severity.isEnabled(Severity::style); + const bool inconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); + const bool warning = mSettings->severity.isEnabled(Severity::warning); + + if (!(warning || (style && inconclusive)) && !mSettings->isPremiumEnabled("funcArgNamesDifferent")) + return; + + logChecker("CheckOther::checkFuncArgNamesDifferent"); // style,warning,inconclusive + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + // check every function + for (const Scope *scope : symbolDatabase->functionScopes) { + const Function * function = scope->function; + // only check functions with arguments + if (!function || function->argCount() == 0) + continue; + + // only check functions with separate declarations and definitions + if (function->argDef == function->arg) + continue; + + // get the function argument name tokens + std::vector declarations(function->argCount()); + std::vector definitions(function->argCount()); + const Token * decl = function->argDef->next(); + for (int j = 0; j < function->argCount(); ++j) { + declarations[j] = nullptr; + definitions[j] = nullptr; + // get the definition + const Variable * variable = function->getArgumentVar(j); + if (variable) { + definitions[j] = variable->nameToken(); + } + // get the declaration (search for first token with varId) + while (decl && !Token::Match(decl, ",|)|;")) { + // skip everything after the assignment because + // it could also have a varId or be the first + // token with a varId if there is no name token + if (decl->str() == "=") { + decl = decl->nextArgument(); + break; + } + // skip over template + if (decl->link()) + decl = decl->link(); + else if (decl->varId()) + declarations[j] = decl; + decl = decl->next(); + } + if (Token::simpleMatch(decl, ",")) + decl = decl->next(); + } + // check for different argument order + if (warning) { + bool order_different = false; + for (int j = 0; j < function->argCount(); ++j) { + if (!declarations[j] || !definitions[j] || declarations[j]->str() == definitions[j]->str()) + continue; + + for (int k = 0; k < function->argCount(); ++k) { + if (j != k && definitions[k] && declarations[j]->str() == definitions[k]->str()) { + order_different = true; + break; + } + } + } + if (order_different) { + funcArgOrderDifferent(function->name(), function->argDef->next(), function->arg->next(), declarations, definitions); + continue; + } + } + // check for different argument names + if (style && inconclusive) { + for (int j = 0; j < function->argCount(); ++j) { + if (declarations[j] && definitions[j] && declarations[j]->str() != definitions[j]->str()) + funcArgNamesDifferent(function->name(), j, declarations[j], definitions[j]); + } + } + } +} + +void CheckOther::funcArgNamesDifferent(const std::string & functionName, nonneg int index, + const Token* declaration, const Token* definition) +{ + std::list tokens = { declaration,definition }; + reportError(tokens, Severity::style, "funcArgNamesDifferent", + "$symbol:" + functionName + "\n" + "Function '$symbol' argument " + std::to_string(index + 1) + " names different: declaration '" + + (declaration ? declaration->str() : std::string("A")) + "' definition '" + + (definition ? definition->str() : std::string("B")) + "'.", CWE628, Certainty::inconclusive); +} + +void CheckOther::funcArgOrderDifferent(const std::string & functionName, + const Token* declaration, const Token* definition, + const std::vector & declarations, + const std::vector & definitions) +{ + std::list tokens = { + !declarations.empty() ? declarations[0] ? declarations[0] : declaration : nullptr, + !definitions.empty() ? definitions[0] ? definitions[0] : definition : nullptr + }; + std::string msg = "$symbol:" + functionName + "\nFunction '$symbol' argument order different: declaration '"; + for (int i = 0; i < declarations.size(); ++i) { + if (i != 0) + msg += ", "; + if (declarations[i]) + msg += declarations[i]->str(); + } + msg += "' definition '"; + for (int i = 0; i < definitions.size(); ++i) { + if (i != 0) + msg += ", "; + if (definitions[i]) + msg += definitions[i]->str(); + } + msg += "'"; + reportError(tokens, Severity::warning, "funcArgOrderDifferent", msg, CWE683, Certainty::normal); +} + +static const Token *findShadowed(const Scope *scope, const Variable& var, int linenr) +{ + if (!scope) + return nullptr; + for (const Variable &v : scope->varlist) { + if (scope->isExecutable() && v.nameToken()->linenr() > linenr) + continue; + if (v.name() == var.name()) + return v.nameToken(); + } + auto it = std::find_if(scope->functionList.cbegin(), scope->functionList.cend(), [&](const Function& f) { + return f.type == Function::Type::eFunction && f.name() == var.name() && precedes(f.tokenDef, var.nameToken()); + }); + if (it != scope->functionList.end()) + return it->tokenDef; + + if (scope->type == Scope::eLambda) + return nullptr; + const Token* shadowed = findShadowed(scope->nestedIn, var, linenr); + if (!shadowed) + shadowed = findShadowed(scope->functionOf, var, linenr); + return shadowed; +} + +void CheckOther::checkShadowVariables() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("shadowVariable")) + return; + logChecker("CheckOther::checkShadowVariables"); // style + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope & scope : symbolDatabase->scopeList) { + if (!scope.isExecutable() || scope.type == Scope::eLambda) + continue; + const Scope *functionScope = &scope; + while (functionScope && functionScope->type != Scope::ScopeType::eFunction && functionScope->type != Scope::ScopeType::eLambda) + functionScope = functionScope->nestedIn; + for (const Variable &var : scope.varlist) { + if (var.nameToken() && var.nameToken()->isExpandedMacro()) // #8903 + continue; + + if (functionScope && functionScope->type == Scope::ScopeType::eFunction && functionScope->function) { + const auto argList = functionScope->function->argumentList; + auto it = std::find_if(argList.cbegin(), argList.cend(), [&](const Variable& arg) { + return arg.nameToken() && var.name() == arg.name(); + }); + if (it != argList.end()) { + shadowError(var.nameToken(), it->nameToken(), "argument"); + continue; + } + } + + const Token *shadowed = findShadowed(scope.nestedIn, var, var.nameToken()->linenr()); + if (!shadowed) + shadowed = findShadowed(scope.functionOf, var, var.nameToken()->linenr()); + if (!shadowed) + continue; + if (scope.type == Scope::eFunction && scope.className == var.name()) + continue; + if (functionScope->functionOf && functionScope->functionOf->isClassOrStructOrUnion() && functionScope->function && functionScope->function->isStatic() && + shadowed->variable() && !shadowed->variable()->isLocal()) + continue; + shadowError(var.nameToken(), shadowed, (shadowed->varId() != 0) ? "variable" : "function"); + } + } +} + +void CheckOther::shadowError(const Token *var, const Token *shadowed, const std::string& type) +{ + ErrorPath errorPath; + errorPath.emplace_back(shadowed, "Shadowed declaration"); + errorPath.emplace_back(var, "Shadow variable"); + const std::string &varname = var ? var->str() : type; + const std::string Type = char(std::toupper(type[0])) + type.substr(1); + const std::string id = "shadow" + Type; + const std::string message = "$symbol:" + varname + "\nLocal variable \'$symbol\' shadows outer " + type; + reportError(errorPath, Severity::style, id.c_str(), message, CWE398, Certainty::normal); +} + +static bool isVariableExpression(const Token* tok) +{ + if (tok->varId() != 0) + return true; + if (Token::simpleMatch(tok, ".")) + return isVariableExpression(tok->astOperand1()) && + isVariableExpression(tok->astOperand2()); + if (Token::simpleMatch(tok, "[")) + return isVariableExpression(tok->astOperand1()); + return false; +} + +static bool isVariableExprHidden(const Token* tok) +{ + if (!tok) + return false; + if (!tok->astParent()) + return false; + if (Token::simpleMatch(tok->astParent(), "*") && Token::simpleMatch(tok->astSibling(), "0")) + return true; + if (Token::simpleMatch(tok->astParent(), "&&") && Token::simpleMatch(tok->astSibling(), "false")) + return true; + if (Token::simpleMatch(tok->astParent(), "||") && Token::simpleMatch(tok->astSibling(), "true")) + return true; + return false; +} + +void CheckOther::checkKnownArgument() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("knownArgument")) + return; + logChecker("CheckOther::checkKnownArgument"); // style + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *functionScope : symbolDatabase->functionScopes) { + for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { + if (!tok->hasKnownIntValue()) + continue; + if (Token::Match(tok, "++|--|%assign%")) + continue; + if (!Token::Match(tok->astParent(), "(|{|,")) + continue; + if (tok->astParent()->isCast() || (tok->isCast() && Token::Match(tok->astOperand2(), "++|--|%assign%"))) + continue; + int argn = -1; + const Token* ftok = getTokenArgumentFunction(tok, argn); + if (!ftok) + continue; + if (ftok->isCast()) + continue; + if (Token::Match(ftok, "if|while|switch|sizeof")) + continue; + if (tok == tok->astParent()->previous()) + continue; + if (isConstVarExpression(tok)) + continue; + if (Token::Match(tok->astOperand1(), "%name% (")) + continue; + const Token * tok2 = tok; + if (isCPPCast(tok2)) + tok2 = tok2->astOperand2(); + if (isVariableExpression(tok2)) + continue; + if (tok->isComparisonOp() && + isSameExpression( + true, tok->astOperand1(), tok->astOperand2(), mSettings->library, true, true)) + continue; + // ensure that there is a integer variable in expression with unknown value + const Token* vartok = nullptr; + visitAstNodes(tok, [&](const Token* child) { + if (Token::Match(child, "%var%|.|[")) { + if (child->hasKnownIntValue()) + return ChildrenToVisit::none; + if (astIsIntegral(child, false) && !astIsPointer(child) && child->values().empty()) { + vartok = child; + return ChildrenToVisit::done; + } + } + return ChildrenToVisit::op1_and_op2; + }); + if (!vartok) + continue; + if (vartok->astSibling() && + findAstNode(vartok->astSibling(), [](const Token* child) { + return Token::simpleMatch(child, "sizeof"); + })) + continue; + // ensure that function name does not contain "assert" + std::string funcname = ftok->str(); + strTolower(funcname); + if (funcname.find("assert") != std::string::npos) + continue; + knownArgumentError(tok, ftok, &tok->values().front(), vartok->expressionString(), isVariableExprHidden(vartok)); + } + } +} + +void CheckOther::knownArgumentError(const Token *tok, const Token *ftok, const ValueFlow::Value *value, const std::string &varexpr, bool isVariableExpressionHidden) +{ + if (!tok) { + reportError(tok, Severity::style, "knownArgument", "Argument 'x-x' to function 'func' is always 0. It does not matter what value 'x' has."); + reportError(tok, Severity::style, "knownArgumentHiddenVariableExpression", "Argument 'x*0' to function 'func' is always 0. Constant literal calculation disable/hide variable expression 'x'."); + return; + } + + const MathLib::bigint intvalue = value->intvalue; + const std::string &expr = tok->expressionString(); + const std::string &fun = ftok->str(); + + std::string ftype = "function "; + if (ftok->type()) + ftype = "constructor "; + else if (fun == "{") + ftype = "init list "; + + const char *id; + std::string errmsg = "Argument '" + expr + "' to " + ftype + fun + " is always " + std::to_string(intvalue) + ". "; + if (!isVariableExpressionHidden) { + id = "knownArgument"; + errmsg += "It does not matter what value '" + varexpr + "' has."; + } else { + id = "knownArgumentHiddenVariableExpression"; + errmsg += "Constant literal calculation disable/hide variable expression '" + varexpr + "'."; + } + + const ErrorPath errorPath = getErrorPath(tok, value, errmsg); + reportError(errorPath, Severity::style, id, errmsg, CWE570, Certainty::normal); +} + +void CheckOther::checkKnownPointerToBool() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("knownPointerToBool")) + return; + logChecker("CheckOther::checkKnownPointerToBool"); // style + const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope* functionScope : symbolDatabase->functionScopes) { + for (const Token* tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { + if (!tok->hasKnownIntValue()) + continue; + if (!astIsPointer(tok)) + continue; + if (Token::Match(tok->astParent(), "?|!|&&|%oror%|%comp%")) + continue; + if (tok->astParent() && Token::Match(tok->astParent()->previous(), "if|while|switch|sizeof (")) + continue; + if (tok->isExpandedMacro()) + continue; + if (findParent(tok, [](const Token* parent) { + return parent->isExpandedMacro(); + })) + continue; + if (!isUsedAsBool(tok, mSettings)) + continue; + const ValueFlow::Value& value = tok->values().front(); + knownPointerToBoolError(tok, &value); + } + } +} + +void CheckOther::knownPointerToBoolError(const Token* tok, const ValueFlow::Value* value) +{ + if (!tok) { + reportError(tok, Severity::style, "knownPointerToBool", "Pointer expression 'p' converted to bool is always true."); + return; + } + std::string cond = bool_to_string(value->intvalue); + const std::string& expr = tok->expressionString(); + std::string errmsg = "Pointer expression '" + expr + "' converted to bool is always " + cond + "."; + const ErrorPath errorPath = getErrorPath(tok, value, errmsg); + reportError(errorPath, Severity::style, "knownPointerToBool", errmsg, CWE570, Certainty::normal); +} + +void CheckOther::checkComparePointers() +{ + logChecker("CheckOther::checkComparePointers"); + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *functionScope : symbolDatabase->functionScopes) { + for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { + if (!Token::Match(tok, "<|>|<=|>=|-")) + continue; + const Token *tok1 = tok->astOperand1(); + const Token *tok2 = tok->astOperand2(); + if (!astIsPointer(tok1) || !astIsPointer(tok2)) + continue; + ValueFlow::Value v1 = ValueFlow::getLifetimeObjValue(tok1); + ValueFlow::Value v2 = ValueFlow::getLifetimeObjValue(tok2); + if (!v1.isLocalLifetimeValue() || !v2.isLocalLifetimeValue()) + continue; + const Variable *var1 = v1.tokvalue->variable(); + const Variable *var2 = v2.tokvalue->variable(); + if (!var1 || !var2) + continue; + if (v1.tokvalue->varId() == v2.tokvalue->varId()) + continue; + if (var1->isReference() || var2->isReference()) + continue; + if (var1->isRValueReference() || var2->isRValueReference()) + continue; + if (const Token* parent2 = getParentLifetime(v2.tokvalue, mSettings->library)) + if (var1 == parent2->variable()) + continue; + if (const Token* parent1 = getParentLifetime(v1.tokvalue, mSettings->library)) + if (var2 == parent1->variable()) + continue; + comparePointersError(tok, &v1, &v2); + } + } +} + +void CheckOther::comparePointersError(const Token *tok, const ValueFlow::Value *v1, const ValueFlow::Value *v2) +{ + ErrorPath errorPath; + std::string verb = "Comparing"; + if (Token::simpleMatch(tok, "-")) + verb = "Subtracting"; + const char * const id = (verb[0] == 'C') ? "comparePointers" : "subtractPointers"; + if (v1) { + errorPath.emplace_back(v1->tokvalue->variable()->nameToken(), "Variable declared here."); + errorPath.insert(errorPath.end(), v1->errorPath.cbegin(), v1->errorPath.cend()); + } + if (v2) { + errorPath.emplace_back(v2->tokvalue->variable()->nameToken(), "Variable declared here."); + errorPath.insert(errorPath.end(), v2->errorPath.cbegin(), v2->errorPath.cend()); + } + errorPath.emplace_back(tok, ""); + reportError( + errorPath, Severity::error, id, verb + " pointers that point to different objects", CWE570, Certainty::normal); +} + +void CheckOther::checkModuloOfOne() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("moduloofone")) + return; + + logChecker("CheckOther::checkModuloOfOne"); // style + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (!tok->astOperand2() || !tok->astOperand1()) + continue; + if (tok->str() != "%") + continue; + if (!tok->valueType() || !tok->valueType()->isIntegral()) + continue; + + // Value flow.. + const ValueFlow::Value *value = tok->astOperand2()->getValue(1LL); + if (value && value->isKnown()) + checkModuloOfOneError(tok); + } +} + +void CheckOther::checkModuloOfOneError(const Token *tok) +{ + reportError(tok, Severity::style, "moduloofone", "Modulo of one is always equal to zero"); +} + +//----------------------------------------------------------------------------- +// Overlapping write (undefined behavior) +//----------------------------------------------------------------------------- +static bool getBufAndOffset(const Token *expr, const Token *&buf, MathLib::bigint *offset) +{ + if (!expr) + return false; + const Token *bufToken, *offsetToken; + if (expr->isUnaryOp("&") && Token::simpleMatch(expr->astOperand1(), "[")) { + bufToken = expr->astOperand1()->astOperand1(); + offsetToken = expr->astOperand1()->astOperand2(); + } else if (Token::Match(expr, "+|-") && expr->isBinaryOp()) { + const bool pointer1 = (expr->astOperand1()->valueType() && expr->astOperand1()->valueType()->pointer > 0); + const bool pointer2 = (expr->astOperand2()->valueType() && expr->astOperand2()->valueType()->pointer > 0); + if (pointer1 && !pointer2) { + bufToken = expr->astOperand1(); + offsetToken = expr->astOperand2(); + } else if (!pointer1 && pointer2) { + bufToken = expr->astOperand2(); + offsetToken = expr->astOperand1(); + } else { + return false; + } + } else if (expr->valueType() && expr->valueType()->pointer > 0) { + buf = expr; + *offset = 0; + return true; + } else { + return false; + } + if (!bufToken->valueType() || !bufToken->valueType()->pointer) + return false; + if (!offsetToken->hasKnownIntValue()) + return false; + buf = bufToken; + *offset = offsetToken->getKnownIntValue(); + return true; +} + +void CheckOther::checkOverlappingWrite() +{ + logChecker("CheckOther::checkOverlappingWrite"); + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *functionScope : symbolDatabase->functionScopes) { + for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { + if (tok->isAssignmentOp()) { + // check if LHS is a union member.. + const Token * const lhs = tok->astOperand1(); + if (!Token::simpleMatch(lhs, ".") || !lhs->isBinaryOp()) + continue; + const Variable * const lhsvar = lhs->astOperand1()->variable(); + if (!lhsvar || !lhsvar->typeScope() || lhsvar->typeScope()->type != Scope::ScopeType::eUnion) + continue; + const Token* const lhsmember = lhs->astOperand2(); + if (!lhsmember) + continue; + + // Is other union member used in RHS? + const Token *errorToken = nullptr; + visitAstNodes(tok->astOperand2(), [lhsvar, lhsmember, &errorToken](const Token *rhs) { + if (!Token::simpleMatch(rhs, ".")) + return ChildrenToVisit::op1_and_op2; + if (!rhs->isBinaryOp() || rhs->astOperand1()->variable() != lhsvar) + return ChildrenToVisit::none; + if (lhsmember->str() == rhs->astOperand2()->str()) + return ChildrenToVisit::none; + const Variable* rhsmembervar = rhs->astOperand2()->variable(); + const Scope* varscope1 = lhsmember->variable() ? lhsmember->variable()->typeStartToken()->scope() : nullptr; + const Scope* varscope2 = rhsmembervar ? rhsmembervar->typeStartToken()->scope() : nullptr; + if (varscope1 && varscope1 == varscope2 && varscope1 != lhsvar->typeScope()) + // lhsmember and rhsmember are declared in same anonymous scope inside union + return ChildrenToVisit::none; + errorToken = rhs->astOperand2(); + return ChildrenToVisit::done; + }); + if (errorToken) + overlappingWriteUnion(tok); + } else if (Token::Match(tok, "%name% (")) { + const Library::NonOverlappingData *nonOverlappingData = mSettings->library.getNonOverlappingData(tok); + if (!nonOverlappingData) + continue; + const std::vector args = getArguments(tok); + if (nonOverlappingData->ptr1Arg <= 0 || nonOverlappingData->ptr1Arg > args.size()) + continue; + if (nonOverlappingData->ptr2Arg <= 0 || nonOverlappingData->ptr2Arg > args.size()) + continue; + + const Token *ptr1 = args[nonOverlappingData->ptr1Arg - 1]; + if (ptr1->hasKnownIntValue() && ptr1->getKnownIntValue() == 0) + continue; + + const Token *ptr2 = args[nonOverlappingData->ptr2Arg - 1]; + if (ptr2->hasKnownIntValue() && ptr2->getKnownIntValue() == 0) + continue; + + // TODO: nonOverlappingData->strlenArg + if (nonOverlappingData->sizeArg <= 0 || nonOverlappingData->sizeArg > args.size()) { + if (nonOverlappingData->sizeArg == -1) { + ErrorPath errorPath; + constexpr bool macro = true; + constexpr bool pure = true; + constexpr bool follow = true; + if (!isSameExpression(macro, ptr1, ptr2, mSettings->library, pure, follow, &errorPath)) + continue; + overlappingWriteFunction(tok); + } + continue; + } + if (!args[nonOverlappingData->sizeArg-1]->hasKnownIntValue()) + continue; + const MathLib::bigint sizeValue = args[nonOverlappingData->sizeArg-1]->getKnownIntValue(); + const Token *buf1, *buf2; + MathLib::bigint offset1, offset2; + if (!getBufAndOffset(ptr1, buf1, &offset1)) + continue; + if (!getBufAndOffset(ptr2, buf2, &offset2)) + continue; + + if (offset1 < offset2 && offset1 + sizeValue <= offset2) + continue; + if (offset2 < offset1 && offset2 + sizeValue <= offset1) + continue; + + ErrorPath errorPath; + constexpr bool macro = true; + constexpr bool pure = true; + constexpr bool follow = true; + if (!isSameExpression(macro, buf1, buf2, mSettings->library, pure, follow, &errorPath)) + continue; + overlappingWriteFunction(tok); + } + } + } +} + +void CheckOther::overlappingWriteUnion(const Token *tok) +{ + reportError(tok, Severity::error, "overlappingWriteUnion", "Overlapping read/write of union is undefined behavior"); +} + +void CheckOther::overlappingWriteFunction(const Token *tok) +{ + const std::string &funcname = tok ? tok->str() : emptyString; + reportError(tok, Severity::error, "overlappingWriteFunction", "Overlapping read/write in " + funcname + "() is undefined behavior"); +} diff --git a/cppcheck-2.14.0/lib/checkother.h b/cppcheck-2.14.0/lib/checkother.h new file mode 100644 index 00000000..79b62e96 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkother.h @@ -0,0 +1,432 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checkotherH +#define checkotherH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "errortypes.h" +#include "tokenize.h" + +#include +#include + +namespace ValueFlow { + class Value; +} + +class Settings; +class Token; +class Function; +class Variable; +class ErrorLogger; + +/// @addtogroup Checks +/// @{ + + +/** @brief Various small checks */ + +class CPPCHECKLIB CheckOther : public Check { + friend class TestCharVar; + friend class TestIncompleteStatement; + friend class TestOther; + +public: + /** @brief This constructor is used when registering the CheckClass */ + CheckOther() : Check(myName()) {} + + /** Is expression a comparison that checks if a nonzero (unsigned/pointer) expression is less than zero? */ + static bool comparisonNonZeroExpressionLessThanZero(const Token *tok, const ValueFlow::Value *&zeroValue, const Token *&nonZeroExpr, bool suppress = false); + + /** Is expression a comparison that checks if a nonzero (unsigned/pointer) expression is positive? */ + static bool testIfNonZeroExpressionIsPositive(const Token *tok, const ValueFlow::Value *&zeroValue, const Token *&nonZeroExpr); + +private: + /** @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, ErrorLogger *errorLogger) override { + CheckOther checkOther(&tokenizer, &tokenizer.getSettings(), errorLogger); + + // Checks + checkOther.warningOldStylePointerCast(); + checkOther.invalidPointerCast(); + checkOther.checkCharVariable(); + checkOther.checkRedundantAssignment(); + checkOther.redundantBitwiseOperationInSwitchError(); + checkOther.checkSuspiciousCaseInSwitch(); + checkOther.checkDuplicateBranch(); + checkOther.checkDuplicateExpression(); + checkOther.checkUnreachableCode(); + checkOther.checkSuspiciousSemicolon(); + checkOther.checkVariableScope(); + checkOther.checkSignOfUnsignedVariable(); // don't ignore casts (#3574) + checkOther.checkIncompleteArrayFill(); + checkOther.checkVarFuncNullUB(); + checkOther.checkNanInArithmeticExpression(); + checkOther.checkCommaSeparatedReturn(); + checkOther.checkRedundantPointerOp(); + checkOther.checkZeroDivision(); + checkOther.checkNegativeBitwiseShift(); + checkOther.checkInterlockedDecrement(); + checkOther.checkUnusedLabel(); + checkOther.checkEvaluationOrder(); + checkOther.checkFuncArgNamesDifferent(); + checkOther.checkShadowVariables(); + checkOther.checkKnownArgument(); + checkOther.checkKnownPointerToBool(); + checkOther.checkComparePointers(); + checkOther.checkIncompleteStatement(); + checkOther.checkRedundantCopy(); + checkOther.clarifyCalculation(); + checkOther.checkPassByReference(); + checkOther.checkConstVariable(); + checkOther.checkConstPointer(); + checkOther.checkComparisonFunctionIsAlwaysTrueOrFalse(); + checkOther.checkInvalidFree(); + checkOther.clarifyStatement(); + checkOther.checkCastIntToCharAndBack(); + checkOther.checkMisusedScopedObject(); + checkOther.checkAccessOfMovedVariable(); + checkOther.checkModuloOfOne(); + checkOther.checkOverlappingWrite(); + } + + /** @brief Clarify calculation for ".. a * b ? .." */ + void clarifyCalculation(); + + /** @brief Suspicious statement like '*A++;' */ + void clarifyStatement(); + + /** @brief Are there C-style pointer casts in a c++ file? */ + void warningOldStylePointerCast(); + + /** @brief Check for pointer casts to a type with an incompatible binary data representation */ + void invalidPointerCast(); + + /** @brief %Check scope of variables */ + void checkVariableScope(); + bool checkInnerScope(const Token *tok, const Variable* var, bool& used) const; + + /** @brief %Check for comma separated statements in return */ + void checkCommaSeparatedReturn(); + + /** @brief %Check for function parameters that should be passed by reference */ + void checkPassByReference(); + + void checkConstVariable(); + void checkConstPointer(); + + /** @brief Using char variable as array index / as operand in bit operation */ + void checkCharVariable(); + + /** @brief Incomplete statement. A statement that only contains a constant or variable */ + void checkIncompleteStatement(); + + /** @brief %Check zero division*/ + void checkZeroDivision(); + + /** @brief Check for NaN (not-a-number) in an arithmetic expression */ + void checkNanInArithmeticExpression(); + + /** @brief copying to memory or assigning to a variable twice */ + void checkRedundantAssignment(); + + /** @brief %Check for redundant bitwise operation in switch statement*/ + void redundantBitwiseOperationInSwitchError(); + + /** @brief %Check for code like 'case A||B:'*/ + void checkSuspiciousCaseInSwitch(); + + /** @brief %Check for objects that are destroyed immediately */ + void checkMisusedScopedObject(); + + /** @brief %Check for suspicious code where if and else branch are the same (e.g "if (a) b = true; else b = true;") */ + void checkDuplicateBranch(); + + /** @brief %Check for suspicious code with the same expression on both sides of operator (e.g "if (a && a)") */ + void checkDuplicateExpression(); + + /** @brief %Check for code that gets never executed, such as duplicate break statements */ + void checkUnreachableCode(); + + /** @brief %Check for testing sign of unsigned variable */ + void checkSignOfUnsignedVariable(); + + /** @brief %Check for suspicious use of semicolon */ + void checkSuspiciousSemicolon(); + + /** @brief %Check for free() operations on invalid memory locations */ + void checkInvalidFree(); + void invalidFreeError(const Token *tok, const std::string &allocation, bool inconclusive); + + /** @brief %Check for code creating redundant copies */ + void checkRedundantCopy(); + + /** @brief %Check for bitwise shift with negative right operand */ + void checkNegativeBitwiseShift(); + + /** @brief %Check for buffers that are filled incompletely with memset and similar functions */ + void checkIncompleteArrayFill(); + + /** @brief %Check that variadic function calls don't use NULL. If NULL is \#defined as 0 and the function expects a pointer, the behaviour is undefined. */ + void checkVarFuncNullUB(); + + /** @brief %Check to avoid casting a return value to unsigned char and then back to integer type. */ + void checkCastIntToCharAndBack(); + + /** @brief %Check for using of comparison functions evaluating always to true or false. */ + void checkComparisonFunctionIsAlwaysTrueOrFalse(); + + /** @brief %Check for redundant pointer operations */ + void checkRedundantPointerOp(); + + /** @brief %Check for race condition with non-interlocked access after InterlockedDecrement() */ + void checkInterlockedDecrement(); + + /** @brief %Check for unused labels */ + void checkUnusedLabel(); + + /** @brief %Check for expression that depends on order of evaluation of side effects */ + void checkEvaluationOrder(); + + /** @brief %Check for access of moved or forwarded variable */ + void checkAccessOfMovedVariable(); + + /** @brief %Check if function declaration and definition argument names different */ + void checkFuncArgNamesDifferent(); + + /** @brief %Check for shadow variables. Less noisy than gcc/clang -Wshadow. */ + void checkShadowVariables(); + + void checkKnownArgument(); + + void checkKnownPointerToBool(); + + void checkComparePointers(); + + void checkModuloOfOne(); + + void checkOverlappingWrite(); + void overlappingWriteUnion(const Token *tok); + void overlappingWriteFunction(const Token *tok); + + // Error messages.. + void checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &functionName, const std::string &varName, const bool result); + void checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName); + void clarifyCalculationError(const Token *tok, const std::string &op); + void clarifyStatementError(const Token* tok); + void cstyleCastError(const Token *tok, bool isPtr = true); + void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt); + void passedByValueError(const Variable* var, bool inconclusive, bool isRangeBasedFor = false); + void constVariableError(const Variable *var, const Function *function); + void constStatementError(const Token *tok, const std::string &type, bool inconclusive); + void signedCharArrayIndexError(const Token *tok); + void unknownSignCharArrayIndexError(const Token *tok); + void charBitOpError(const Token *tok); + void variableScopeError(const Token *tok, const std::string &varname); + void zerodivError(const Token *tok, const ValueFlow::Value *value); + void nanInArithmeticExpressionError(const Token *tok); + void redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive); + void redundantInitializationError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive); + void redundantAssignmentInSwitchError(const Token *tok1, const Token *tok2, const std::string &var); + void redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var); + void redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname); + void suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString); + void selfAssignmentError(const Token *tok, const std::string &varname); + void misusedScopeObjectError(const Token *tok, const std::string &varname, bool isAssignment = false); + void duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors); + void duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive); + void oppositeExpressionError(const Token *opTok, ErrorPath errors); + void duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors, bool hasMultipleExpr = false); + void duplicateValueTernaryError(const Token *tok); + void duplicateExpressionTernaryError(const Token *tok, ErrorPath errors); + void duplicateBreakError(const Token *tok, bool inconclusive); + void unreachableCodeError(const Token* tok, const Token* noreturn, bool inconclusive); + void redundantContinueError(const Token* tok); + void unsignedLessThanZeroError(const Token *tok, const ValueFlow::Value *v, const std::string &varname); + void pointerLessThanZeroError(const Token *tok, const ValueFlow::Value *v); + void unsignedPositiveError(const Token *tok, const ValueFlow::Value *v, const std::string &varname); + void pointerPositiveError(const Token *tok, const ValueFlow::Value *v); + void suspiciousSemicolonError(const Token *tok); + void negativeBitwiseShiftError(const Token *tok, int op); + void redundantCopyError(const Token *tok, const std::string &varname); + void incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean); + void varFuncNullUBError(const Token *tok); + void commaSeparatedReturnError(const Token *tok); + void redundantPointerOpError(const Token* tok, const std::string& varname, bool inconclusive, bool addressOfDeref); + void raceAfterInterlockedDecrementError(const Token* tok); + void unusedLabelError(const Token* tok, bool inSwitch, bool hasIfdef); + void unknownEvaluationOrder(const Token* tok); + void accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive); + void funcArgNamesDifferent(const std::string & functionName, nonneg int index, const Token* declaration, const Token* definition); + void funcArgOrderDifferent(const std::string & functionName, const Token * declaration, const Token * definition, const std::vector & declarations, const std::vector & definitions); + void shadowError(const Token *var, const Token *shadowed, const std::string& type); + void knownArgumentError(const Token *tok, const Token *ftok, const ValueFlow::Value *value, const std::string &varexpr, bool isVariableExpressionHidden); + void knownPointerToBoolError(const Token* tok, const ValueFlow::Value* value); + void comparePointersError(const Token *tok, const ValueFlow::Value *v1, const ValueFlow::Value *v2); + void checkModuloOfOneError(const Token *tok); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckOther c(nullptr, settings, errorLogger); + + // error + c.zerodivError(nullptr, nullptr); + c.misusedScopeObjectError(nullptr, "varname"); + c.invalidPointerCastError(nullptr, "float *", "double *", false, false); + c.negativeBitwiseShiftError(nullptr, 1); + c.negativeBitwiseShiftError(nullptr, 2); + c.raceAfterInterlockedDecrementError(nullptr); + c.invalidFreeError(nullptr, "malloc", false); + c.overlappingWriteUnion(nullptr); + c.overlappingWriteFunction(nullptr); + + //performance + c.redundantCopyError(nullptr, "varname"); + c.redundantCopyError(nullptr, nullptr, "var"); + + // style/warning + c.checkComparisonFunctionIsAlwaysTrueOrFalseError(nullptr, "isless","varName",false); + c.checkCastIntToCharAndBackError(nullptr, "func_name"); + c.cstyleCastError(nullptr); + c.passedByValueError(nullptr, false); + c.constVariableError(nullptr, nullptr); + c.constStatementError(nullptr, "type", false); + c.signedCharArrayIndexError(nullptr); + c.unknownSignCharArrayIndexError(nullptr); + c.charBitOpError(nullptr); + c.variableScopeError(nullptr, "varname"); + c.redundantAssignmentInSwitchError(nullptr, nullptr, "var"); + c.suspiciousCaseInSwitchError(nullptr, "||"); + c.selfAssignmentError(nullptr, "varname"); + c.clarifyCalculationError(nullptr, "+"); + c.clarifyStatementError(nullptr); + c.duplicateBranchError(nullptr, nullptr, ErrorPath{}); + c.duplicateAssignExpressionError(nullptr, nullptr, true); + c.oppositeExpressionError(nullptr, ErrorPath{}); + c.duplicateExpressionError(nullptr, nullptr, nullptr, ErrorPath{}); + c.duplicateValueTernaryError(nullptr); + c.duplicateExpressionTernaryError(nullptr, ErrorPath{}); + c.duplicateBreakError(nullptr, false); + c.unreachableCodeError(nullptr, nullptr, false); + c.unsignedLessThanZeroError(nullptr, nullptr, "varname"); + c.unsignedPositiveError(nullptr, nullptr, "varname"); + c.pointerLessThanZeroError(nullptr, nullptr); + c.pointerPositiveError(nullptr, nullptr); + c.suspiciousSemicolonError(nullptr); + c.incompleteArrayFillError(nullptr, "buffer", "memset", false); + c.varFuncNullUBError(nullptr); + c.nanInArithmeticExpressionError(nullptr); + c.commaSeparatedReturnError(nullptr); + c.redundantPointerOpError(nullptr, "varname", false, /*addressOfDeref*/ true); + c.unusedLabelError(nullptr, false, false); + c.unusedLabelError(nullptr, false, true); + c.unusedLabelError(nullptr, true, false); + c.unusedLabelError(nullptr, true, true); + c.unknownEvaluationOrder(nullptr); + c.accessMovedError(nullptr, "v", nullptr, false); + c.funcArgNamesDifferent("function", 1, nullptr, nullptr); + c.redundantBitwiseOperationInSwitchError(nullptr, "varname"); + c.shadowError(nullptr, nullptr, "variable"); + c.shadowError(nullptr, nullptr, "function"); + c.shadowError(nullptr, nullptr, "argument"); + c.knownArgumentError(nullptr, nullptr, nullptr, "x", false); + c.knownPointerToBoolError(nullptr, nullptr); + c.comparePointersError(nullptr, nullptr, nullptr); + c.redundantAssignmentError(nullptr, nullptr, "var", false); + c.redundantInitializationError(nullptr, nullptr, "var", false); + + const std::vector nullvec; + c.funcArgOrderDifferent("function", nullptr, nullptr, nullvec, nullvec); + c.checkModuloOfOneError(nullptr); + } + + static std::string myName() { + return "Other"; + } + + std::string classInfo() const override { + return "Other checks\n" + + // error + "- division with zero\n" + "- scoped object destroyed immediately after construction\n" + "- assignment in an assert statement\n" + "- free() or delete of an invalid memory location\n" + "- bitwise operation with negative right operand\n" + "- cast the return values of getc(),fgetc() and getchar() to character and compare it to EOF\n" + "- race condition with non-interlocked access after InterlockedDecrement() call\n" + "- expression 'x = x++;' depends on order of evaluation of side effects\n" + "- overlapping write of union\n" + + // warning + "- either division by zero or useless condition\n" + "- access of moved or forwarded variable.\n" + + // performance + "- redundant data copying for const variable\n" + "- subsequent assignment or copying to a variable or buffer\n" + "- passing parameter by value\n" + + // portability + "- Passing NULL pointer to function with variable number of arguments leads to UB.\n" + + // style + "- C-style pointer cast in C++ code\n" + "- casting between incompatible pointer types\n" + "- [Incomplete statement](IncompleteStatement)\n" + "- [check how signed char variables are used](CharVar)\n" + "- variable scope can be limited\n" + "- unusual pointer arithmetic. For example: \"abc\" + 'd'\n" + "- redundant assignment, increment, or bitwise operation in a switch statement\n" + "- redundant strcpy in a switch statement\n" + "- Suspicious case labels in switch()\n" + "- assignment of a variable to itself\n" + "- Comparison of values leading always to true or false\n" + "- Clarify calculation with parentheses\n" + "- suspicious comparison of '\\0' with a char\\* variable\n" + "- duplicate break statement\n" + "- unreachable code\n" + "- testing if unsigned variable is negative/positive\n" + "- Suspicious use of ; at the end of 'if/for/while' statement.\n" + "- Array filled incompletely using memset/memcpy/memmove.\n" + "- NaN (not a number) value used in arithmetic expression.\n" + "- comma in return statement (the comma can easily be misread as a semicolon).\n" + "- prefer erfc, expm1 or log1p to avoid loss of precision.\n" + "- identical code in both branches of if/else or ternary operator.\n" + "- redundant pointer operation on pointer like &\\*some_ptr.\n" + "- find unused 'goto' labels.\n" + "- function declaration and definition argument names different.\n" + "- function declaration and definition argument order different.\n" + "- shadow variable.\n" + "- variable can be declared const.\n" + "- calculating modulo of one.\n" + "- known function argument, suspicious calculation.\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkotherH diff --git a/cppcheck-2.14.0/lib/checkpostfixoperator.cpp b/cppcheck-2.14.0/lib/checkpostfixoperator.cpp new file mode 100644 index 00000000..eb8d57a3 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkpostfixoperator.cpp @@ -0,0 +1,89 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "errortypes.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" + +#include + +//--------------------------------------------------------------------------- + + +// Register this check class (by creating a static instance of it) +namespace { + CheckPostfixOperator instance; +} + + +// CWE ids used +static const CWE CWE398(398U); // Indicator of Poor Code Quality + + +void CheckPostfixOperator::postfixOperator() +{ + if (!mSettings->severity.isEnabled(Severity::performance)) + return; + + logChecker("CheckPostfixOperator::postfixOperator"); // performance + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + const Variable *var = tok->variable(); + if (!var || !Token::Match(tok, "%var% ++|--")) + continue; + + const Token* parent = tok->next()->astParent(); + if (!parent || parent->str() == ";" || (parent->str() == "," && (!parent->astParent() || parent->astParent()->str() != "("))) { + if (var->isPointer() || var->isArray()) + continue; + + if (Token::Match(var->nameToken()->previous(), "iterator|const_iterator|reverse_iterator|const_reverse_iterator")) { + // the variable is an iterator + postfixOperatorError(tok); + } else if (var->type()) { + // the variable is an instance of class + postfixOperatorError(tok); + } + } + } + } +} +//--------------------------------------------------------------------------- + + +void CheckPostfixOperator::postfixOperatorError(const Token *tok) +{ + reportError(tok, Severity::performance, "postfixOperator", + "Prefer prefix ++/-- operators for non-primitive types.\n" + "Prefix ++/-- operators should be preferred for non-primitive types. " + "Pre-increment/decrement can be more efficient than " + "post-increment/decrement. Post-increment/decrement usually " + "involves keeping a copy of the previous value around and " + "adds a little extra code.", CWE398, Certainty::normal); +} diff --git a/cppcheck-2.14.0/lib/checkpostfixoperator.h b/cppcheck-2.14.0/lib/checkpostfixoperator.h new file mode 100644 index 00000000..b1f64188 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkpostfixoperator.h @@ -0,0 +1,83 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checkpostfixoperatorH +#define checkpostfixoperatorH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "tokenize.h" + +#include + +class ErrorLogger; +class Settings; +class Token; + +/// @addtogroup Checks +/// @{ + +/** + * @brief Using postfix operators ++ or -- rather than postfix operator. + */ + +class CPPCHECKLIB CheckPostfixOperator : public Check { + friend class TestPostfixOperator; + +public: + /** This constructor is used when registering the CheckPostfixOperator */ + CheckPostfixOperator() : Check(myName()) {} + +private: + /** This constructor is used when running checks. */ + CheckPostfixOperator(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger) {} + + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + if (tokenizer.isC()) + return; + + CheckPostfixOperator checkPostfixOperator(&tokenizer, &tokenizer.getSettings(), errorLogger); + checkPostfixOperator.postfixOperator(); + } + + /** Check postfix operators */ + void postfixOperator(); + + /** Report Error */ + void postfixOperatorError(const Token *tok); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckPostfixOperator c(nullptr, settings, errorLogger); + c.postfixOperatorError(nullptr); + } + + static std::string myName() { + return "Using postfix operators"; + } + + std::string classInfo() const override { + return "Warn if using postfix operators ++ or -- rather than prefix operator\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkpostfixoperatorH diff --git a/cppcheck-2.14.0/lib/checksizeof.cpp b/cppcheck-2.14.0/lib/checksizeof.cpp new file mode 100644 index 00000000..50ddd100 --- /dev/null +++ b/cppcheck-2.14.0/lib/checksizeof.cpp @@ -0,0 +1,503 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "errortypes.h" +#include "library.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" + +#include +#include +#include +#include + +//--------------------------------------------------------------------------- + +// Register this check class (by creating a static instance of it) +namespace { + CheckSizeof instance; +} + +// CWE IDs used: +static const CWE CWE467(467U); // Use of sizeof() on a Pointer Type +static const CWE CWE682(682U); // Incorrect Calculation +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +void CheckSizeof::checkSizeofForNumericParameter() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckSizeof::checkSizeofForNumericParameter"); // warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "sizeof ( %num% )") || + Token::Match(tok, "sizeof %num%")) { + sizeofForNumericParameterError(tok); + } + } + } +} + +void CheckSizeof::sizeofForNumericParameterError(const Token *tok) +{ + reportError(tok, Severity::warning, + "sizeofwithnumericparameter", "Suspicious usage of 'sizeof' with a numeric constant as parameter.\n" + "It is unusual to use a constant value with sizeof. For example, 'sizeof(10)'" + " returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 10. 'sizeof('A')'" + " and 'sizeof(char)' can return different results.", CWE682, Certainty::normal); +} + + +//--------------------------------------------------------------------------- +//--------------------------------------------------------------------------- +void CheckSizeof::checkSizeofForArrayParameter() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckSizeof::checkSizeofForArrayParameter"); // warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "sizeof ( %var% )") || + Token::Match(tok, "sizeof %var% !![")) { + const Token* varTok = tok->next(); + if (varTok->str() == "(") { + varTok = varTok->next(); + } + + const Variable *var = varTok->variable(); + if (var && var->isArray() && var->isArgument() && !var->isReference()) + sizeofForArrayParameterError(tok); + } + } + } +} + +void CheckSizeof::sizeofForArrayParameterError(const Token *tok) +{ + reportError(tok, Severity::warning, + "sizeofwithsilentarraypointer", "Using 'sizeof' on array given as function argument " + "returns size of a pointer.\n" + "Using 'sizeof' for array given as function argument returns the size of a pointer. " + "It does not return the size of the whole array in bytes as might be " + "expected. For example, this code:\n" + " int f(char a[100]) {\n" + " return sizeof(a);\n" + " }\n" + "returns 4 (in 32-bit systems) or 8 (in 64-bit systems) instead of 100 (the " + "size of the array in bytes).", CWE467, Certainty::normal + ); +} + +void CheckSizeof::checkSizeofForPointerSize() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckSizeof::checkSizeofForPointerSize"); // warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + const Token* tokSize; + const Token* tokFunc; + const Token *variable = nullptr; + const Token *variable2 = nullptr; + + // Find any function that may use sizeof on a pointer + // Once leaving those tests, it is mandatory to have: + // - variable matching the used pointer + // - tokVar pointing on the argument where sizeof may be used + if (Token::Match(tok->tokAt(2), "%name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2))) { + if (Token::Match(tok, "%var% =")) + variable = tok; + else if (tok->strAt(1) == ")" && Token::Match(tok->linkAt(1)->tokAt(-2), "%var% =")) + variable = tok->linkAt(1)->tokAt(-2); + else if (tok->link() && Token::Match(tok, "> ( %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2)) && Token::Match(tok->link()->tokAt(-3), "%var% =")) + variable = tok->link()->tokAt(-3); + tokSize = tok->tokAt(4); + tokFunc = tok->tokAt(2); + } else if (Token::simpleMatch(tok, "memset (") && tok->strAt(-1) != ".") { + variable = tok->tokAt(2); + tokSize = variable->nextArgument(); + if (tokSize) + tokSize = tokSize->nextArgument(); + tokFunc = tok; + } else if (Token::Match(tok, "memcpy|memcmp|memmove|strncpy|strncmp|strncat (") && tok->strAt(-1) != ".") { + variable = tok->tokAt(2); + variable2 = variable->nextArgument(); + if (!variable2) + continue; + tokSize = variable2->nextArgument(); + tokFunc = tok; + } else { + continue; + } + + if (tokSize && tokFunc->str() == "calloc") + tokSize = tokSize->nextArgument(); + + if (tokSize) { + const Token * const paramsListEndTok = tokFunc->linkAt(1); + for (const Token* tok2 = tokSize; tok2 != paramsListEndTok; tok2 = tok2->next()) { + if (Token::simpleMatch(tok2, "/ sizeof")) { + // Allow division with sizeof(char) + if (Token::simpleMatch(tok2->next(), "sizeof (")) { + const Token *sztok = tok2->tokAt(2)->astOperand2(); + const ValueType *vt = ((sztok != nullptr) ? sztok->valueType() : nullptr); + if (vt && vt->type == ValueType::CHAR && vt->pointer == 0) + continue; + } + auto hasMultiplication = [](const Token* parTok) -> bool { + while (parTok) { // Allow division if followed by multiplication + if (parTok->isArithmeticalOp() && parTok->str() == "*") { + const Token* szToks[] = { parTok->astOperand1(), parTok->astOperand2() }; + if (std::any_of(std::begin(szToks), std::end(szToks), [](const Token* szTok) { + return Token::simpleMatch(szTok, "(") && Token::simpleMatch(szTok->previous(), "sizeof"); + })) + return true; + } + parTok = parTok->astParent(); + } + return false; + }; + if (hasMultiplication(tok2->astParent())) + continue; + + divideBySizeofError(tok2, tokFunc->str()); + } + } + } + + if (!variable || !tokSize) + continue; + + while (Token::Match(variable, "%var% ::|.")) + variable = variable->tokAt(2); + + while (Token::Match(variable2, "%var% ::|.")) + variable2 = variable2->tokAt(2); + + if (!variable) + 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 = nullptr; + } + + if (variable2) { + var = variable2->variable(); + if (!var || !var->isPointer() || var->isArray()) { + variable2 = nullptr; + } + } + + // If there are no pointer variable at this point, there is + // no need to continue + if (variable == nullptr && variable2 == nullptr) { + continue; + } + + // Jump to the next sizeof token in the function and in the parameter + // This is to allow generic operations with sizeof + for (; tokSize && tokSize->str() != ")" && tokSize->str() != "," && tokSize->str() != "sizeof"; tokSize = tokSize->next()) {} + + if (tokSize->str() != "sizeof") + continue; + + // Now check for the sizeof usage: Does the level of pointer indirection match? + const Token * const tokLink = tokSize->linkAt(1); + if (tokLink && tokLink->strAt(-1) == "*") { + if (variable && variable->valueType() && variable->valueType()->pointer == 1 && variable->valueType()->type != ValueType::VOID) + sizeofForPointerError(variable, variable->str()); + else if (variable2 && variable2->valueType() && variable2->valueType()->pointer == 1 && variable2->valueType()->type != ValueType::VOID) + sizeofForPointerError(variable2, variable2->str()); + } + + if (Token::simpleMatch(tokSize, "sizeof ( &")) + tokSize = tokSize->tokAt(3); + else if (Token::Match(tokSize, "sizeof (|&")) + tokSize = tokSize->tokAt(2); + else + tokSize = tokSize->next(); + + while (Token::Match(tokSize, "%var% ::|.")) + tokSize = tokSize->tokAt(2); + + if (Token::Match(tokSize, "%var% [|(")) + continue; + + // Now check for the sizeof usage again. Once here, everything using sizeof(varid) or sizeof(&varid) + // looks suspicious + if (variable && tokSize->varId() == variable->varId()) + sizeofForPointerError(variable, variable->str()); + if (variable2 && tokSize->varId() == variable2->varId()) + sizeofForPointerError(variable2, variable2->str()); + } + } +} + +void CheckSizeof::sizeofForPointerError(const Token *tok, const std::string &varname) +{ + reportError(tok, Severity::warning, "pointerSize", + "Size of pointer '" + varname + "' used instead of size of its data.\n" + "Size of pointer '" + varname + "' used instead of size of its data. " + "This is likely to lead to a buffer overflow. You probably intend to " + "write 'sizeof(*" + varname + ")'.", CWE467, Certainty::normal); +} + +void CheckSizeof::divideBySizeofError(const Token *tok, const std::string &memfunc) +{ + reportError(tok, Severity::warning, "sizeofDivisionMemfunc", + "Division by result of sizeof(). " + memfunc + "() expects a size in bytes, did you intend to multiply instead?", CWE682, Certainty::normal); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CheckSizeof::sizeofsizeof() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckSizeof::sizeofsizeof"); // warning + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (Token::Match(tok, "sizeof (| sizeof")) { + sizeofsizeofError(tok); + tok = tok->next(); + } + } +} + +void CheckSizeof::sizeofsizeofError(const Token *tok) +{ + reportError(tok, Severity::warning, + "sizeofsizeof", "Calling 'sizeof' on 'sizeof'.\n" + "Calling sizeof for 'sizeof looks like a suspicious code and " + "most likely there should be just one 'sizeof'. The current " + "code is equivalent to 'sizeof(size_t)'", CWE682, Certainty::normal); +} + +//----------------------------------------------------------------------------- + +void CheckSizeof::sizeofCalculation() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckSizeof::sizeofCalculation"); // warning + + const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (!Token::simpleMatch(tok, "sizeof (")) + continue; + + // ignore if the `sizeof` result is cast to void inside a macro, i.e. the calculation is + // expected to be parsed but skipped, such as in a disabled custom ASSERT() macro + if (tok->isExpandedMacro() && tok->previous()) { + const Token *cast_end = (tok->previous()->str() == "(") ? tok->previous() : tok; + if (Token::simpleMatch(cast_end->tokAt(-3), "( void )") || + Token::simpleMatch(cast_end->tokAt(-4), "static_cast < void >")) { + continue; + } + } + + const Token *argument = tok->next()->astOperand2(); + if (!argument || !argument->isCalculation()) + continue; + + bool inconclusive = false; + if (argument->isExpandedMacro()) + inconclusive = true; + else if (tok->next()->isExpandedMacro()) + inconclusive = true; + + if (!inconclusive || printInconclusive) + sizeofCalculationError(argument, inconclusive); + } +} + +void CheckSizeof::sizeofCalculationError(const Token *tok, bool inconclusive) +{ + reportError(tok, Severity::warning, + "sizeofCalculation", "Found calculation inside sizeof().", CWE682, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +//----------------------------------------------------------------------------- + +void CheckSizeof::sizeofFunction() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckSizeof::sizeofFunction"); // warning + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "sizeof (")) { + + // ignore if the `sizeof` result is cast to void inside a macro, i.e. the calculation is + // expected to be parsed but skipped, such as in a disabled custom ASSERT() macro + if (tok->isExpandedMacro() && tok->previous()) { + const Token *cast_end = (tok->previous()->str() == "(") ? tok->previous() : tok; + if (Token::simpleMatch(cast_end->tokAt(-3), "( void )") || + Token::simpleMatch(cast_end->tokAt(-4), "static_cast < void >")) { + continue; + } + } + + if (const Token *argument = tok->next()->astOperand2()) { + const Token *checkToken = argument->previous(); + if (checkToken->tokType() == Token::eName) + break; + const Function * fun = checkToken->function(); + // Don't report error if the function is overloaded + if (fun && fun->nestedIn->functionMap.count(checkToken->str()) == 1) { + sizeofFunctionError(tok); + } + } + } + } +} + +void CheckSizeof::sizeofFunctionError(const Token *tok) +{ + reportError(tok, Severity::warning, + "sizeofFunctionCall", "Found function call inside sizeof().", CWE682, Certainty::normal); +} + +//----------------------------------------------------------------------------- +// Check for code like sizeof()*sizeof() or sizeof(ptr)/value +//----------------------------------------------------------------------------- +void CheckSizeof::suspiciousSizeofCalculation() +{ + if (!mSettings->severity.isEnabled(Severity::warning) || !mSettings->certainty.isEnabled(Certainty::inconclusive)) + return; + + logChecker("CheckSizeof::suspiciousSizeofCalculation"); // warning,inconclusive + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "sizeof (")) { + const Token* lPar = tok->astParent(); + if (lPar && lPar->str() == "(") { + const Token* const rPar = lPar->link(); + const Token* varTok = lPar->astOperand2(); + int derefCount = 0; + while (Token::Match(varTok, "[|*")) { + ++derefCount; + varTok = varTok->astOperand1(); + } + if (lPar->astParent() && lPar->astParent()->str() == "/") { + const Variable* var = varTok ? varTok->variable() : nullptr; + if (var && var->isPointer() && !var->isArray() && !(var->valueType() && var->valueType()->pointer <= derefCount)) + divideSizeofError(tok); + } + else if (Token::simpleMatch(rPar, ") * sizeof") && rPar->next()->astOperand1() == tok->next()) + multiplySizeofError(tok); + } + } + } +} + +void CheckSizeof::multiplySizeofError(const Token *tok) +{ + reportError(tok, Severity::warning, + "multiplySizeof", "Multiplying sizeof() with sizeof() indicates a logic error.", CWE682, Certainty::inconclusive); +} + +void CheckSizeof::divideSizeofError(const Token *tok) +{ + reportError(tok, Severity::warning, + "divideSizeof", "Division of result of sizeof() on pointer type.\n" + "Division of result of sizeof() on pointer type. sizeof() returns the size of the pointer, " + "not the size of the memory area it points to.", CWE682, Certainty::inconclusive); +} + +void CheckSizeof::sizeofVoid() +{ + if (!mSettings->severity.isEnabled(Severity::portability)) + return; + + logChecker("CheckSizeof::sizeofVoid"); // portability + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "sizeof ( void )")) { + sizeofVoidError(tok); + } else if (Token::simpleMatch(tok, "sizeof (") && tok->next()->astOperand2()) { + const ValueType *vt = tok->next()->astOperand2()->valueType(); + if (vt && vt->type == ValueType::Type::VOID && vt->pointer == 0U) + sizeofDereferencedVoidPointerError(tok, tok->strAt(3)); + } else if (tok->str() == "-") { + // only warn for: 'void *' - 'integral' + const ValueType *vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; + const ValueType *vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr; + const bool op1IsvoidPointer = (vt1 && vt1->type == ValueType::Type::VOID && vt1->pointer == 1U); + const bool op2IsIntegral = (vt2 && vt2->isIntegral() && vt2->pointer == 0U); + if (op1IsvoidPointer && op2IsIntegral) + arithOperationsOnVoidPointerError(tok, tok->astOperand1()->expressionString(), vt1->str()); + } else if (Token::Match(tok, "+|++|--|+=|-=")) { // Arithmetic operations on variable of type "void*" + const ValueType *vt1 = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; + const ValueType *vt2 = tok->astOperand2() ? tok->astOperand2()->valueType() : nullptr; + + const bool voidpointer1 = (vt1 && vt1->type == ValueType::Type::VOID && vt1->pointer == 1U); + const bool voidpointer2 = (vt2 && vt2->type == ValueType::Type::VOID && vt2->pointer == 1U); + + if (voidpointer1) + arithOperationsOnVoidPointerError(tok, tok->astOperand1()->expressionString(), vt1->str()); + + if (!tok->isAssignmentOp() && voidpointer2) + arithOperationsOnVoidPointerError(tok, tok->astOperand2()->expressionString(), vt2->str()); + } + } +} + +void CheckSizeof::sizeofVoidError(const Token *tok) +{ + const std::string message = "Behaviour of 'sizeof(void)' is not covered by the ISO C standard."; + const std::string verbose = message + " A value for 'sizeof(void)' is defined only as part of a GNU C extension, which defines 'sizeof(void)' to be 1."; + reportError(tok, Severity::portability, "sizeofVoid", message + "\n" + verbose, CWE682, Certainty::normal); +} + +void CheckSizeof::sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname) +{ + const std::string message = "'*" + varname + "' is of type 'void', the behaviour of 'sizeof(void)' is not covered by the ISO C standard."; + const std::string verbose = message + " A value for 'sizeof(void)' is defined only as part of a GNU C extension, which defines 'sizeof(void)' to be 1."; + reportError(tok, Severity::portability, "sizeofDereferencedVoidPointer", message + "\n" + verbose, CWE682, Certainty::normal); +} + +void CheckSizeof::arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname, const std::string &vartype) +{ + const std::string message = "'$symbol' is of type '" + vartype + "'. When using void pointers in calculations, the behaviour is undefined."; + const std::string verbose = message + " Arithmetic operations on 'void *' is a GNU C extension, which defines the 'sizeof(void)' to be 1."; + reportError(tok, Severity::portability, "arithOperationsOnVoidPointer", "$symbol:" + varname + '\n' + message + '\n' + verbose, CWE467, Certainty::normal); +} diff --git a/cppcheck-2.14.0/lib/checksizeof.h b/cppcheck-2.14.0/lib/checksizeof.h new file mode 100644 index 00000000..6924a1d2 --- /dev/null +++ b/cppcheck-2.14.0/lib/checksizeof.h @@ -0,0 +1,138 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checksizeofH +#define checksizeofH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "tokenize.h" + +#include + +class ErrorLogger; +class Settings; +class Token; + +/// @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()) {} + +private: + /** @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, ErrorLogger* errorLogger) override { + CheckSizeof checkSizeof(&tokenizer, &tokenizer.getSettings(), errorLogger); + + // Checks + checkSizeof.sizeofsizeof(); + checkSizeof.sizeofCalculation(); + checkSizeof.sizeofFunction(); + checkSizeof.suspiciousSizeofCalculation(); + checkSizeof.checkSizeofForArrayParameter(); + checkSizeof.checkSizeofForPointerSize(); + checkSizeof.checkSizeofForNumericParameter(); + checkSizeof.sizeofVoid(); + } + + /** @brief %Check for 'sizeof sizeof ..' */ + void sizeofsizeof(); + + /** @brief %Check for calculations inside sizeof */ + void sizeofCalculation(); + + /** @brief %Check for function call inside sizeof */ + void sizeofFunction(); + + /** @brief %Check for suspicious calculations with sizeof results */ + void suspiciousSizeofCalculation(); + + /** @brief %Check for using sizeof with array given as function argument */ + void checkSizeofForArrayParameter(); + + /** @brief %Check for using sizeof of a variable when allocating it */ + void checkSizeofForPointerSize(); + + /** @brief %Check for using sizeof with numeric given as function argument */ + void checkSizeofForNumericParameter(); + + /** @brief %Check for using sizeof(void) */ + void sizeofVoid(); + + // Error messages.. + void sizeofsizeofError(const Token* tok); + void sizeofCalculationError(const Token* tok, bool inconclusive); + void sizeofFunctionError(const Token* tok); + void multiplySizeofError(const Token* tok); + void divideSizeofError(const Token* tok); + void sizeofForArrayParameterError(const Token* tok); + void sizeofForPointerError(const Token* tok, const std::string &varname); + void divideBySizeofError(const Token* tok, const std::string &memfunc); + void sizeofForNumericParameterError(const Token* tok); + void sizeofVoidError(const Token *tok); + void sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname); + void arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname, const std::string &vartype); + + void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override { + CheckSizeof c(nullptr, settings, errorLogger); + c.sizeofForArrayParameterError(nullptr); + c.sizeofForPointerError(nullptr, "varname"); + c.divideBySizeofError(nullptr, "memset"); + c.sizeofForNumericParameterError(nullptr); + c.sizeofsizeofError(nullptr); + c.sizeofCalculationError(nullptr, false); + c.sizeofFunctionError(nullptr); + c.multiplySizeofError(nullptr); + c.divideSizeofError(nullptr); + c.sizeofVoidError(nullptr); + c.sizeofDereferencedVoidPointerError(nullptr, "varname"); + c.arithOperationsOnVoidPointerError(nullptr, "varname", "vartype"); + } + + static std::string myName() { + return "Sizeof"; + } + + std::string classInfo() const override { + return "sizeof() usage checks\n" + "- sizeof for array given as function argument\n" + "- sizeof for numeric given as function argument\n" + "- using sizeof(pointer) instead of the size of pointed data\n" + "- look for 'sizeof sizeof ..'\n" + "- look for calculations inside sizeof()\n" + "- look for function calls inside sizeof()\n" + "- look for suspicious calculations with sizeof()\n" + "- using 'sizeof(void)' which is undefined\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checksizeofH diff --git a/cppcheck-2.14.0/lib/checkstl.cpp b/cppcheck-2.14.0/lib/checkstl.cpp new file mode 100644 index 00000000..5a4a5403 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkstl.cpp @@ -0,0 +1,3285 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "astutils.h" +#include "errortypes.h" +#include "library.h" +#include "mathlib.h" +#include "pathanalysis.h" +#include "settings.h" +#include "standards.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "utils.h" +#include "valueflow.h" + +#include "checknullpointer.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Register this check class (by creating a static instance of it) +namespace { + CheckStl instance; +} + +// CWE IDs used: +static const CWE CWE398(398U); // Indicator of Poor Code Quality +static const CWE CWE597(597U); // Use of Wrong Operator in String Comparison +static const CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments +static const CWE CWE664(664U); // Improper Control of a Resource Through its Lifetime +static const CWE CWE667(667U); // Improper Locking +static const CWE CWE704(704U); // Incorrect Type Conversion or Cast +static const CWE CWE762(762U); // Mismatched Memory Management Routines +static const CWE CWE786(786U); // Access of Memory Location Before Start of Buffer +static const CWE CWE788(788U); // Access of Memory Location After End of Buffer +static const CWE CWE825(825U); // Expired Pointer Dereference +static const CWE CWE833(833U); // Deadlock +static const CWE CWE834(834U); // Excessive Iteration + +static bool isElementAccessYield(Library::Container::Yield yield) +{ + return contains({Library::Container::Yield::ITEM, Library::Container::Yield::AT_INDEX}, yield); +} + +static bool containerAppendsElement(const Library::Container* container, const Token* parent) +{ + if (Token::Match(parent, ". %name% (")) { + const Library::Container::Action action = container->getAction(parent->strAt(1)); + if (contains({Library::Container::Action::INSERT, + Library::Container::Action::CHANGE, + Library::Container::Action::CHANGE_INTERNAL, + Library::Container::Action::PUSH, + Library::Container::Action::RESIZE}, + action)) + return true; + } + return false; +} + +static bool containerYieldsElement(const Library::Container* container, const Token* parent) +{ + if (Token::Match(parent, ". %name% (")) { + const Library::Container::Yield yield = container->getYield(parent->strAt(1)); + if (isElementAccessYield(yield)) + return true; + } + return false; +} + +static bool containerPopsElement(const Library::Container* container, const Token* parent) +{ + if (Token::Match(parent, ". %name% (")) { + const Library::Container::Action action = container->getAction(parent->strAt(1)); + if (contains({ Library::Container::Action::POP }, action)) + return true; + } + return false; +} + +static const Token* getContainerIndex(const Library::Container* container, const Token* parent) +{ + if (Token::Match(parent, ". %name% (")) { + const Library::Container::Yield yield = container->getYield(parent->strAt(1)); + if (yield == Library::Container::Yield::AT_INDEX && !Token::simpleMatch(parent->tokAt(2), "( )")) + return parent->tokAt(2)->astOperand2(); + } + if (!container->arrayLike_indexOp && !container->stdStringLike) + return nullptr; + if (Token::simpleMatch(parent, "[")) + return parent->astOperand2(); + return nullptr; +} + +static const Token* getContainerFromSize(const Library::Container* container, const Token* tok) +{ + if (!tok) + return nullptr; + if (Token::Match(tok->tokAt(-2), ". %name% (")) { + const Library::Container::Yield yield = container->getYield(tok->strAt(-1)); + if (yield == Library::Container::Yield::SIZE) + return tok->tokAt(-2)->astOperand1(); + } + return nullptr; +} + +void CheckStl::outOfBounds() +{ + logChecker("CheckStl::outOfBounds"); + + for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { + for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { + const Library::Container *container = getLibraryContainer(tok); + if (!container || container->stdAssociativeLike) + continue; + const Token * parent = astParentSkipParens(tok); + const Token* accessTok = parent; + if (Token::simpleMatch(accessTok, ".") && Token::simpleMatch(accessTok->astParent(), "(")) + accessTok = accessTok->astParent(); + if (astIsIterator(accessTok) && Token::simpleMatch(accessTok->astParent(), "+")) + accessTok = accessTok->astParent(); + const Token* indexTok = getContainerIndex(container, parent); + if (indexTok == tok) + continue; + for (const ValueFlow::Value &value : tok->values()) { + if (!value.isContainerSizeValue()) + continue; + if (value.isImpossible()) + continue; + if (value.isInconclusive() && !mSettings->certainty.isEnabled(Certainty::inconclusive)) + continue; + if (!value.errorSeverity() && !mSettings->severity.isEnabled(Severity::warning)) + continue; + if (value.intvalue == 0 && (indexTok || + (containerYieldsElement(container, parent) && !containerAppendsElement(container, parent)) || + containerPopsElement(container, parent))) { + std::string indexExpr; + if (indexTok && !indexTok->hasKnownValue()) + indexExpr = indexTok->expressionString(); + outOfBoundsError(accessTok, tok->expressionString(), &value, indexExpr, nullptr); + continue; + } + if (indexTok) { + std::vector indexValues = + ValueFlow::isOutOfBounds(value, indexTok, mSettings->severity.isEnabled(Severity::warning)); + if (!indexValues.empty()) { + outOfBoundsError( + accessTok, tok->expressionString(), &value, indexTok->expressionString(), &indexValues.front()); + continue; + } + } + } + if (indexTok && !indexTok->hasKnownIntValue()) { + const ValueFlow::Value* value = + ValueFlow::findValue(indexTok->values(), mSettings, [&](const ValueFlow::Value& v) { + if (!v.isSymbolicValue()) + return false; + if (v.isImpossible()) + return false; + if (v.intvalue < 0) + return false; + const Token* sizeTok = v.tokvalue; + if (sizeTok && sizeTok->isCast()) + sizeTok = sizeTok->astOperand2() ? sizeTok->astOperand2() : sizeTok->astOperand1(); + const Token* containerTok = getContainerFromSize(container, sizeTok); + if (!containerTok) + return false; + return containerTok->exprId() == tok->exprId(); + }); + if (!value) + continue; + outOfBoundsError(accessTok, tok->expressionString(), nullptr, indexTok->expressionString(), value); + } + } + } +} + +static std::string indexValueString(const ValueFlow::Value& indexValue, const std::string& containerName = emptyString) +{ + if (indexValue.isIteratorStartValue()) + return "at position " + std::to_string(indexValue.intvalue) + " from the beginning"; + if (indexValue.isIteratorEndValue()) + return "at position " + std::to_string(-indexValue.intvalue) + " from the end"; + std::string indexString = std::to_string(indexValue.intvalue); + if (indexValue.isSymbolicValue()) { + indexString = containerName + ".size()"; + if (indexValue.intvalue != 0) + indexString += "+" + std::to_string(indexValue.intvalue); + } + if (indexValue.bound == ValueFlow::Value::Bound::Lower) + return "greater or equal to " + indexString; + return indexString; +} + +void CheckStl::outOfBoundsError(const Token *tok, const std::string &containerName, const ValueFlow::Value *containerSize, const std::string &index, const ValueFlow::Value *indexValue) +{ + // Do not warn if both the container size and index value are possible + if (containerSize && indexValue && containerSize->isPossible() && indexValue->isPossible()) + return; + + const std::string expression = tok ? tok->expressionString() : (containerName+"[x]"); + + std::string errmsg; + if (!containerSize) { + if (indexValue && indexValue->condition) + errmsg = ValueFlow::eitherTheConditionIsRedundant(indexValue->condition) + " or '" + index + + "' can have the value " + indexValueString(*indexValue, containerName) + ". Expression '" + + expression + "' causes access out of bounds."; + else + errmsg = "Out of bounds access in expression '" + expression + "'"; + } else if (containerSize->intvalue == 0) { + if (containerSize->condition) + errmsg = ValueFlow::eitherTheConditionIsRedundant(containerSize->condition) + " or expression '" + expression + "' causes access out of bounds."; + else if (indexValue == nullptr && !index.empty() && tok->valueType() && tok->valueType()->type == ValueType::ITERATOR) + errmsg = "Out of bounds access in expression '" + expression + "' because '$symbol' is empty and '" + index + "' may be non-zero."; + else + errmsg = "Out of bounds access in expression '" + expression + "' because '$symbol' is empty."; + } else if (indexValue) { + if (containerSize->condition) + errmsg = ValueFlow::eitherTheConditionIsRedundant(containerSize->condition) + " or size of '$symbol' can be " + std::to_string(containerSize->intvalue) + ". Expression '" + expression + "' causes access out of bounds."; + else if (indexValue->condition) + errmsg = ValueFlow::eitherTheConditionIsRedundant(indexValue->condition) + " or '" + index + "' can have the value " + indexValueString(*indexValue) + ". Expression '" + expression + "' causes access out of bounds."; + else + errmsg = "Out of bounds access in '" + expression + "', if '$symbol' size is " + std::to_string(containerSize->intvalue) + " and '" + index + "' is " + indexValueString(*indexValue); + } else { + // should not happen + return; + } + + ErrorPath errorPath; + if (!indexValue) + errorPath = getErrorPath(tok, containerSize, "Access out of bounds"); + else { + ErrorPath errorPath1 = getErrorPath(tok, containerSize, "Access out of bounds"); + ErrorPath errorPath2 = getErrorPath(tok, indexValue, "Access out of bounds"); + if (errorPath1.size() <= 1) + errorPath = std::move(errorPath2); + else if (errorPath2.size() <= 1) + errorPath = std::move(errorPath1); + else { + errorPath = std::move(errorPath1); + errorPath.splice(errorPath.end(), errorPath2); + } + } + + reportError(errorPath, + (containerSize && !containerSize->errorSeverity()) || (indexValue && !indexValue->errorSeverity()) ? Severity::warning : Severity::error, + "containerOutOfBounds", + "$symbol:" + containerName +"\n" + errmsg, + CWE398, + (containerSize && containerSize->isInconclusive()) || (indexValue && indexValue->isInconclusive()) ? Certainty::inconclusive : Certainty::normal); +} + +bool CheckStl::isContainerSize(const Token *containerToken, const Token *expr) const +{ + if (!Token::simpleMatch(expr, "( )")) + return false; + if (!Token::Match(expr->astOperand1(), ". %name% (")) + return false; + if (!isSameExpression(false, containerToken, expr->astOperand1()->astOperand1(), mSettings->library, false, false)) + return false; + return containerToken->valueType()->container->getYield(expr->previous()->str()) == Library::Container::Yield::SIZE; +} + +bool CheckStl::isContainerSizeGE(const Token * containerToken, const Token *expr) const +{ + if (!expr) + return false; + if (isContainerSize(containerToken, expr)) + return true; + if (expr->str() == "*") { + const Token *mul; + if (isContainerSize(containerToken, expr->astOperand1())) + mul = expr->astOperand2(); + else if (isContainerSize(containerToken, expr->astOperand2())) + mul = expr->astOperand1(); + else + return false; + return mul && (!mul->hasKnownIntValue() || mul->values().front().intvalue != 0); + } + if (expr->str() == "+") { + const Token *op; + if (isContainerSize(containerToken, expr->astOperand1())) + op = expr->astOperand2(); + else if (isContainerSize(containerToken, expr->astOperand2())) + op = expr->astOperand1(); + else + return false; + return op && op->getValueGE(0, mSettings); + } + return false; +} + +void CheckStl::outOfBoundsIndexExpression() +{ + logChecker("CheckStl::outOfBoundsIndexExpression"); + for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { + for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { + if (!tok->isName() || !tok->valueType()) + continue; + const Library::Container *container = tok->valueType()->container; + if (!container) + continue; + if (!container->arrayLike_indexOp && !container->stdStringLike) + continue; + if (!Token::Match(tok, "%name% [")) + continue; + if (isContainerSizeGE(tok, tok->next()->astOperand2())) + outOfBoundsIndexExpressionError(tok, tok->next()->astOperand2()); + } + } +} + +void CheckStl::outOfBoundsIndexExpressionError(const Token *tok, const Token *index) +{ + const std::string varname = tok ? tok->str() : std::string("var"); + const std::string i = index ? index->expressionString() : std::string(varname + ".size()"); + + std::string errmsg = "Out of bounds access of $symbol, index '" + i + "' is out of bounds."; + + reportError(tok, + Severity::error, + "containerOutOfBoundsIndexExpression", + "$symbol:" + varname +"\n" + errmsg, + CWE398, + Certainty::normal); +} + + + +// Error message for bad iterator usage.. +void CheckStl::invalidIteratorError(const Token *tok, const std::string &iteratorName) +{ + reportError(tok, Severity::error, "invalidIterator1", "$symbol:"+iteratorName+"\nInvalid iterator: $symbol", CWE664, Certainty::normal); +} + +void CheckStl::iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2) +{ + reportError(tok, Severity::error, "iterators1", + "$symbol:" + containerName1 + "\n" + "$symbol:" + containerName2 + "\n" + "Same iterator is used with different containers '" + containerName1 + "' and '" + containerName2 + "'.", CWE664, Certainty::normal); +} + +void CheckStl::iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2) +{ + std::list callstack = { tok, containerTok }; + reportError(callstack, Severity::error, "iterators2", + "$symbol:" + containerName1 + "\n" + "$symbol:" + containerName2 + "\n" + "Same iterator is used with different containers '" + containerName1 + "' and '" + containerName2 + "'.", CWE664, Certainty::normal); +} + +void CheckStl::iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName) +{ + std::list callstack = { tok, containerTok }; + reportError(callstack, + Severity::error, + "iterators3", + "$symbol:" + containerName + + "\n" + "Same iterator is used with containers '$symbol' that are temporaries or defined in different scopes.", + CWE664, + Certainty::normal); +} + +// Error message used when dereferencing an iterator that has been erased.. +void CheckStl::dereferenceErasedError(const Token *erased, const Token* deref, const std::string &itername, bool inconclusive) +{ + if (erased) { + std::list callstack = { deref, erased }; + reportError(callstack, Severity::error, "eraseDereference", + "$symbol:" + itername + "\n" + "Iterator '$symbol' used after element has been erased.\n" + "The iterator '$symbol' is invalid after the element it pointed to has been erased. " + "Dereferencing or comparing it with another iterator is invalid operation.", CWE664, inconclusive ? Certainty::inconclusive : Certainty::normal); + } else { + reportError(deref, Severity::error, "eraseDereference", + "$symbol:" + itername + "\n" + "Invalid iterator '$symbol' used.\n" + "The iterator '$symbol' is invalid before being assigned. " + "Dereferencing or comparing it with another iterator is invalid operation.", CWE664, inconclusive ? Certainty::inconclusive : Certainty::normal); + } +} + +static const Token *skipMembers(const Token *tok) +{ + while (Token::Match(tok, "%name% .")) + tok = tok->tokAt(2); + return tok; +} + +static bool isIterator(const Variable *var, bool& inconclusiveType) +{ + // Check that its an iterator + if (!var || !var->isLocal() || !Token::Match(var->typeEndToken(), "iterator|const_iterator|reverse_iterator|const_reverse_iterator|auto")) + return false; + + inconclusiveType = false; + if (var->typeEndToken()->str() == "auto") + return (var->nameToken()->valueType() && var->nameToken()->valueType()->type == ValueType::Type::ITERATOR); + + if (var->type()) { // If it is defined, ensure that it is defined like an iterator + // look for operator* and operator++ + const Function* end = var->type()->getFunction("operator*"); + const Function* incOperator = var->type()->getFunction("operator++"); + if (!end || end->argCount() > 0 || !incOperator) + return false; + + inconclusiveType = true; // heuristics only + } + + return true; +} + +static std::string getContainerName(const Token *containerToken) +{ + if (!containerToken) + return std::string(); + std::string ret(containerToken->str()); + for (const Token *nametok = containerToken; nametok; nametok = nametok->tokAt(-2)) { + if (!Token::Match(nametok->tokAt(-2), "%name% .")) + break; + ret = nametok->strAt(-2) + '.' + ret; + } + return ret; +} + +static bool isVector(const Token* tok) +{ + if (!tok) + return false; + const Variable *var = tok->variable(); + const Token *decltok = var ? var->typeStartToken() : nullptr; + return Token::simpleMatch(decltok, "std :: vector"); +} + +void CheckStl::iterators() +{ + logChecker("CheckStl::iterators"); + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + // Filling map of iterators id and their scope begin + std::map iteratorScopeBeginInfo; + for (const Variable* var : symbolDatabase->variableList()) { + bool inconclusiveType=false; + if (!isIterator(var, inconclusiveType)) + continue; + const int iteratorId = var->declarationId(); + if (iteratorId != 0) + iteratorScopeBeginInfo[iteratorId] = var->nameToken(); + } + + for (const Variable* var : symbolDatabase->variableList()) { + bool inconclusiveType=false; + if (!isIterator(var, inconclusiveType)) + continue; + if (inconclusiveType && !mSettings->certainty.isEnabled(Certainty::inconclusive)) + continue; + + const int iteratorId = var->declarationId(); + + // the validIterator flag says if the iterator has a valid value or not + bool validIterator = Token::Match(var->nameToken()->next(), "[(=:{]"); + const Scope* invalidationScope = nullptr; + + // The container this iterator can be used with + const Token* containerToken = nullptr; + const Scope* containerAssignScope = nullptr; + + // When "validatingToken" is reached the validIterator is set to true + const Token* validatingToken = nullptr; + + const Token* eraseToken = nullptr; + + // Scan through the rest of the code and see if the iterator is + // used against other containers. + for (const Token *tok2 = var->nameToken(); tok2 && tok2 != var->scope()->bodyEnd; tok2 = tok2->next()) { + if (invalidationScope && tok2 == invalidationScope->bodyEnd) + validIterator = true; // Assume that the iterator becomes valid again + if (containerAssignScope && tok2 == containerAssignScope->bodyEnd) + containerToken = nullptr; // We don't know which containers might be used with the iterator + + if (tok2 == validatingToken) { + validIterator = true; + eraseToken = nullptr; + invalidationScope = nullptr; + } + + // Is the iterator used in a insert/erase operation? + if (Token::Match(tok2, "%name% . insert|erase ( *| %varid% )|,", iteratorId) && !isVector(tok2)) { + const Token* itTok = tok2->tokAt(4); + if (itTok->str() == "*") { + if (tok2->strAt(2) == "insert") + continue; + + itTok = itTok->next(); + } + // It is bad to insert/erase an invalid iterator + if (!validIterator) + invalidIteratorError(tok2, itTok->str()); + + // If insert/erase is used on different container then + // report an error + if (containerToken && tok2->varId() != containerToken->varId()) { + // skip error message if container is a set.. + const Variable *variableInfo = tok2->variable(); + const Token *decltok = variableInfo ? variableInfo->typeStartToken() : nullptr; + + if (Token::simpleMatch(decltok, "std :: set")) + continue; // No warning + + // skip error message if the iterator is erased/inserted by value + if (itTok->previous()->str() == "*") + continue; + + // inserting iterator range.. + if (tok2->strAt(2) == "insert") { + const Token *par2 = itTok->nextArgument(); + if (!par2 || par2->nextArgument()) + continue; + while (par2->str() != ")") { + if (par2->varId() == containerToken->varId()) + break; + bool inconclusiveType2=false; + if (isIterator(par2->variable(), inconclusiveType2)) + break; // TODO: check if iterator points at same container + if (par2->str() == "(") + par2 = par2->link(); + par2 = par2->next(); + } + if (par2->str() != ")") + continue; + } + + // Not different containers if a reference is used.. + if (containerToken && containerToken->variable() && containerToken->variable()->isReference()) { + const Token *nameToken = containerToken->variable()->nameToken(); + if (Token::Match(nameToken, "%name% =")) { + const Token *name1 = nameToken->tokAt(2); + const Token *name2 = tok2; + while (Token::Match(name1, "%name%|.|::") && name2 && name1->str() == name2->str()) { + name1 = name1->next(); + name2 = name2->next(); + } + if (!Token::simpleMatch(name1, ";") || !Token::Match(name2, "[;,()=]")) + continue; + } + } + + // Show error message, mismatching iterator is used. + iteratorsError(tok2, getContainerName(containerToken), getContainerName(tok2)); + } + + // invalidate the iterator if it is erased + else if (tok2->strAt(2) == "erase" && (tok2->strAt(4) != "*" || (containerToken && tok2->varId() == containerToken->varId()))) { + validIterator = false; + eraseToken = tok2; + invalidationScope = tok2->scope(); + } + + // skip the operation + tok2 = itTok->next(); + } + + // it = foo.erase(.. + // taking the result of an erase is ok + else if (Token::Match(tok2, "%varid% = %name% .", iteratorId) && + Token::simpleMatch(skipMembers(tok2->tokAt(2)), "erase (")) { + // the returned iterator is valid + validatingToken = skipMembers(tok2->tokAt(2))->linkAt(1); + tok2 = validatingToken->link(); + } + + // Reassign the iterator + else if (Token::Match(tok2, "%varid% = %name% .", iteratorId) && + Token::Match(skipMembers(tok2->tokAt(2)), "begin|rbegin|cbegin|crbegin|find (")) { + validatingToken = skipMembers(tok2->tokAt(2))->linkAt(1); + containerToken = skipMembers(tok2->tokAt(2))->tokAt(-2); + if (containerToken->varId() == 0 || Token::simpleMatch(validatingToken, ") .")) + containerToken = nullptr; + containerAssignScope = tok2->scope(); + + // skip ahead + tok2 = validatingToken->link(); + } + + // Reassign the iterator + else if (Token::Match(tok2, "%varid% =", iteratorId)) { + break; + } + + // Passing iterator to function. Iterator might be initialized + else if (Token::Match(tok2, "%varid% ,|)", iteratorId)) { + validIterator = true; + } + + // Dereferencing invalid iterator? + else if (!validIterator && Token::Match(tok2, "* %varid%", iteratorId)) { + dereferenceErasedError(eraseToken, tok2, tok2->strAt(1), inconclusiveType); + tok2 = tok2->next(); + } else if (!validIterator && Token::Match(tok2, "%varid% . %name%", iteratorId)) { + dereferenceErasedError(eraseToken, tok2, tok2->str(), inconclusiveType); + tok2 = tok2->tokAt(2); + } + + // bailout handling. Assume that the iterator becomes valid if we see return/break. + // TODO: better handling + else if (tok2->scope() == invalidationScope && Token::Match(tok2, "return|break|continue")) { + validatingToken = Token::findsimplematch(tok2->next(), ";"); + } + + // bailout handling. Assume that the iterator becomes valid if we see else. + // TODO: better handling + else if (tok2->str() == "else") { + validIterator = true; + } + } + } +} + +void CheckStl::mismatchingContainerIteratorError(const Token* containerTok, const Token* iterTok, const Token* containerTok2) +{ + const std::string container(containerTok ? containerTok->expressionString() : std::string("v1")); + const std::string container2(containerTok2 ? containerTok2->expressionString() : std::string("v2")); + const std::string iter(iterTok ? iterTok->expressionString() : std::string("it")); + reportError(containerTok, + Severity::error, + "mismatchingContainerIterator", + "Iterator '" + iter + "' referring to container '" + container2 + "' is used with container '" + container + "'.", + CWE664, + Certainty::normal); +} + +// Error message for bad iterator usage.. +void CheckStl::mismatchingContainersError(const Token* tok1, const Token* tok2) +{ + const std::string expr1(tok1 ? tok1->expressionString() : std::string("v1")); + const std::string expr2(tok2 ? tok2->expressionString() : std::string("v2")); + reportError(tok1, + Severity::error, + "mismatchingContainers", + "Iterators of different containers '" + expr1 + "' and '" + expr2 + "' are used together.", + CWE664, + Certainty::normal); +} + +void CheckStl::mismatchingContainerExpressionError(const Token *tok1, const Token *tok2) +{ + const std::string expr1(tok1 ? tok1->expressionString() : std::string("v1")); + const std::string expr2(tok2 ? tok2->expressionString() : std::string("v2")); + reportError(tok1, Severity::warning, "mismatchingContainerExpression", + "Iterators to containers from different expressions '" + + expr1 + "' and '" + expr2 + "' are used together.", CWE664, Certainty::normal); +} + +void CheckStl::sameIteratorExpressionError(const Token *tok) +{ + reportError(tok, Severity::style, "sameIteratorExpression", "Same iterators expression are used for algorithm.", CWE664, Certainty::normal); +} + +static std::vector getAddressContainer(const Token* tok) +{ + if (Token::simpleMatch(tok, "[") && tok->astOperand1()) + return { tok->astOperand1() }; + while (Token::simpleMatch(tok, "::") && tok->astOperand2()) + tok = tok->astOperand2(); + std::vector values = ValueFlow::getLifetimeObjValues(tok, /*inconclusive*/ false); + std::vector res; + for (const auto& v : values) { + if (v.tokvalue) + res.emplace_back(v.tokvalue); + } + if (res.empty()) + res.emplace_back(tok); + return res; +} + +static bool isSameIteratorContainerExpression(const Token* tok1, + const Token* tok2, + const Library& library, + ValueFlow::Value::LifetimeKind kind = ValueFlow::Value::LifetimeKind::Iterator) +{ + if (isSameExpression(false, tok1, tok2, library, false, false)) { + return !astIsContainerOwned(tok1) || !isTemporary(tok1, &library); + } + if (astContainerYield(tok2) == Library::Container::Yield::ITEM) + return true; + if (kind == ValueFlow::Value::LifetimeKind::Address || kind == ValueFlow::Value::LifetimeKind::Iterator) { + const auto address1 = getAddressContainer(tok1); + const auto address2 = getAddressContainer(tok2); + return std::any_of(address1.begin(), address1.end(), [&](const Token* tok1) { + return std::any_of(address2.begin(), address2.end(), [&](const Token* tok2) { + return isSameExpression(false, tok1, tok2, library, false, false); + }); + }); + } + return false; +} + +static ValueFlow::Value getLifetimeIteratorValue(const Token* tok, MathLib::bigint path = 0) +{ + std::vector values = ValueFlow::getLifetimeObjValues(tok, false, path); + auto it = std::find_if(values.cbegin(), values.cend(), [](const ValueFlow::Value& v) { + return v.lifetimeKind == ValueFlow::Value::LifetimeKind::Iterator; + }); + if (it != values.end()) + return *it; + if (values.size() == 1) + return values.front(); + return ValueFlow::Value{}; +} + +bool CheckStl::checkIteratorPair(const Token* tok1, const Token* tok2) +{ + if (!tok1) + return false; + if (!tok2) + return false; + ValueFlow::Value val1 = getLifetimeIteratorValue(tok1); + ValueFlow::Value val2 = getLifetimeIteratorValue(tok2); + if (val1.tokvalue && val2.tokvalue && val1.lifetimeKind == val2.lifetimeKind) { + if (val1.lifetimeKind == ValueFlow::Value::LifetimeKind::Lambda) + return false; + if (tok1->astParent() == tok2->astParent() && Token::Match(tok1->astParent(), "%comp%|-")) { + if (val1.lifetimeKind == ValueFlow::Value::LifetimeKind::Address) + return false; + if (val1.lifetimeKind == ValueFlow::Value::LifetimeKind::Object && + (!astIsContainer(val1.tokvalue) || !astIsContainer(val2.tokvalue))) + return false; + } + if (isSameIteratorContainerExpression(val1.tokvalue, val2.tokvalue, mSettings->library, val1.lifetimeKind)) + return false; + if (val1.tokvalue->expressionString() == val2.tokvalue->expressionString()) + iteratorsError(tok1, val1.tokvalue, val1.tokvalue->expressionString()); + else + mismatchingContainersError(val1.tokvalue, val2.tokvalue); + return true; + } + + if (Token::Match(tok1->astParent(), "%comp%|-")) { + if (astIsIntegral(tok1, true) || astIsIntegral(tok2, true) || + astIsFloat(tok1, true) || astIsFloat(tok2, true)) + return false; + } + const Token* iter1 = getIteratorExpression(tok1); + const Token* iter2 = getIteratorExpression(tok2); + if (iter1 && iter2 && !isSameIteratorContainerExpression(iter1, iter2, mSettings->library)) { + mismatchingContainerExpressionError(iter1, iter2); + return true; + } + return false; +} + +namespace { + struct ArgIteratorInfo { + const Token* tok; + const Library::ArgumentChecks::IteratorInfo* info; + }; +} + +void CheckStl::mismatchingContainers() +{ + logChecker("CheckStl::misMatchingContainers"); + + // Check if different containers are used in various calls of standard functions + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "%comp%|-")) { + if (checkIteratorPair(tok->astOperand1(), tok->astOperand2())) + continue; + } + if (!Token::Match(tok, "%name% ( !!)")) + continue; + const Token * const ftok = tok; + + const std::vector args = getArguments(ftok); + if (args.size() < 2) + continue; + + // Group args together by container + std::map> containers; + for (int argnr = 1; argnr <= args.size(); ++argnr) { + const Library::ArgumentChecks::IteratorInfo *i = mSettings->library.getArgIteratorInfo(ftok, argnr); + if (!i) + continue; + const Token * const argTok = args[argnr - 1]; + containers[i->container].emplace_back(ArgIteratorInfo{argTok, i}); + } + + // Lambda is used to escape the nested loops + [&] { + for (const auto& p : containers) + { + const std::vector& cargs = p.second; + for (ArgIteratorInfo iter1 : cargs) { + for (ArgIteratorInfo iter2 : cargs) { + if (iter1.tok == iter2.tok) + continue; + if (iter1.info->first && iter2.info->last && + isSameExpression(false, iter1.tok, iter2.tok, mSettings->library, false, false)) + sameIteratorExpressionError(iter1.tok); + if (checkIteratorPair(iter1.tok, iter2.tok)) + return; + } + } + } + }(); + } + } + for (const Variable *var : symbolDatabase->variableList()) { + if (var && var->isStlStringType() && Token::Match(var->nameToken(), "%var% (") && + Token::Match(var->nameToken()->tokAt(2), "%name% . begin|cbegin|rbegin|crbegin ( ) , %name% . end|cend|rend|crend ( ) ,|)")) { + if (var->nameToken()->strAt(2) != var->nameToken()->strAt(8)) { + mismatchingContainersError(var->nameToken(), var->nameToken()->tokAt(2)); + } + } + } +} + +void CheckStl::mismatchingContainerIterator() +{ + logChecker("CheckStl::misMatchingContainerIterator"); + + // Check if different containers are used in various calls of standard functions + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!astIsContainer(tok)) + continue; + if (!astIsLHS(tok)) + continue; + if (!Token::Match(tok->astParent(), ". %name% ( !!)")) + continue; + const Token* const ftok = tok->astParent()->next(); + const std::vector args = getArguments(ftok); + + const Library::Container * c = tok->valueType()->container; + const Library::Container::Action action = c->getAction(tok->strAt(2)); + const Token* iterTok = nullptr; + if (action == Library::Container::Action::INSERT && args.size() == 2) { + // Skip if iterator pair + if (astIsIterator(args.back())) + continue; + if (!astIsIterator(args.front())) + continue; + iterTok = args.front(); + } else if (action == Library::Container::Action::ERASE) { + if (!astIsIterator(args.front())) + continue; + iterTok = args.front(); + } else { + continue; + } + + ValueFlow::Value val = getLifetimeIteratorValue(iterTok); + if (!val.tokvalue) + continue; + if (!val.isKnown() && Token::simpleMatch(val.tokvalue->astParent(), ":")) + continue; + if (val.lifetimeKind != ValueFlow::Value::LifetimeKind::Iterator) + continue; + if (iterTok->str() == "*" && iterTok->astOperand1()->valueType() && iterTok->astOperand1()->valueType()->type == ValueType::ITERATOR) + continue; + if (isSameIteratorContainerExpression(tok, val.tokvalue, mSettings->library)) + continue; + mismatchingContainerIteratorError(tok, iterTok, val.tokvalue); + } + } +} + +static const Token* getInvalidMethod(const Token* tok) +{ + if (!astIsLHS(tok)) + return nullptr; + if (Token::Match(tok->astParent(), ". assign|clear|swap")) + return tok->astParent()->next(); + if (Token::Match(tok->astParent(), "%assign%")) + return tok->astParent(); + const Token* ftok = nullptr; + if (Token::Match(tok->astParent(), ". %name% (")) + ftok = tok->astParent()->next(); + if (!ftok) + return nullptr; + if (const Library::Container * c = tok->valueType()->container) { + const Library::Container::Action action = c->getAction(ftok->str()); + if (c->unstableErase) { + if (action == Library::Container::Action::ERASE) + return ftok; + } + if (c->unstableInsert) { + if (action == Library::Container::Action::RESIZE) + return ftok; + if (action == Library::Container::Action::CLEAR) + return ftok; + if (action == Library::Container::Action::PUSH) + return ftok; + if (action == Library::Container::Action::POP) + return ftok; + if (action == Library::Container::Action::INSERT) + return ftok; + if (action == Library::Container::Action::CHANGE) + return ftok; + if (action == Library::Container::Action::CHANGE_INTERNAL) + return ftok; + if (Token::Match(ftok, "insert|emplace")) + return ftok; + } + } + return nullptr; +} + +namespace { + struct InvalidContainerAnalyzer { + struct Info { + struct Reference { + const Token* tok; + ErrorPath errorPath; + const Token* ftok; + }; + std::unordered_map expressions; + + void add(const std::vector& refs) { + for (const Reference& r : refs) { + add(r); + } + } + void add(const Reference& r) { + if (!r.tok) + return; + expressions.insert(std::make_pair(r.tok->exprId(), r)); + } + + std::vector invalidTokens() const { + std::vector result; + std::transform(expressions.cbegin(), expressions.cend(), std::back_inserter(result), SelectMapValues{}); + return result; + } + }; + std::unordered_map invalidMethods; + + std::vector invalidatesContainer(const Token* tok) const { + std::vector result; + if (Token::Match(tok, "%name% (")) { + const Function* f = tok->function(); + if (!f) + return result; + ErrorPathItem epi = std::make_pair(tok, "Calling function " + tok->str()); + const bool dependsOnThis = exprDependsOnThis(tok->next()); + auto it = invalidMethods.find(f); + if (it != invalidMethods.end()) { + std::vector refs = it->second.invalidTokens(); + std::copy_if(refs.cbegin(), refs.cend(), std::back_inserter(result), [&](const Info::Reference& r) { + const Variable* var = r.tok->variable(); + if (!var) + return false; + if (dependsOnThis && !var->isLocal() && !var->isGlobal() && !var->isStatic()) + return true; + if (!var->isArgument()) + return false; + if (!var->isReference()) + return false; + return true; + }); + std::vector args = getArguments(tok); + for (Info::Reference& r : result) { + r.errorPath.push_front(epi); + r.ftok = tok; + const Variable* var = r.tok->variable(); + if (!var) + continue; + if (var->isArgument()) { + const int n = getArgumentPos(var, f); + const Token* tok2 = nullptr; + if (n >= 0 && n < args.size()) + tok2 = args[n]; + r.tok = tok2; + } + } + } + } else if (astIsContainer(tok)) { + const Token* ftok = getInvalidMethod(tok); + if (ftok) { + ErrorPath ep; + ep.emplace_front(ftok, + "After calling '" + ftok->expressionString() + + "', iterators or references to the container's data may be invalid ."); + result.emplace_back(Info::Reference{tok, std::move(ep), ftok}); + } + } + return result; + } + + void analyze(const SymbolDatabase* symboldatabase) { + for (const Scope* scope : symboldatabase->functionScopes) { + const Function* f = scope->function; + if (!f) + continue; + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "if|while|for|goto|return")) + break; + std::vector c = invalidatesContainer(tok); + if (c.empty()) + continue; + invalidMethods[f].add(c); + } + } + } + }; +} + +static const Token* getLoopContainer(const Token* tok) +{ + if (!Token::simpleMatch(tok, "for (")) + return nullptr; + const Token* sepTok = tok->next()->astOperand2(); + if (!Token::simpleMatch(sepTok, ":")) + return nullptr; + return sepTok->astOperand2(); +} + +static const ValueFlow::Value* getInnerLifetime(const Token* tok, + nonneg int id, + ErrorPath* errorPath = nullptr, + int depth = 4) +{ + if (depth < 0) + return nullptr; + if (!tok) + return nullptr; + for (const ValueFlow::Value& val : tok->values()) { + if (!val.isLocalLifetimeValue()) + continue; + if (contains({ValueFlow::Value::LifetimeKind::Address, + ValueFlow::Value::LifetimeKind::SubObject, + ValueFlow::Value::LifetimeKind::Lambda}, + val.lifetimeKind)) { + if (val.isInconclusive()) + return nullptr; + if (val.capturetok) + return getInnerLifetime(val.capturetok, id, errorPath, depth - 1); + if (errorPath) + errorPath->insert(errorPath->end(), val.errorPath.cbegin(), val.errorPath.cend()); + return getInnerLifetime(val.tokvalue, id, errorPath, depth - 1); + } + if (!val.tokvalue->variable()) + continue; + if (val.tokvalue->varId() != id) + continue; + return &val; + } + return nullptr; +} + +static const Token* endOfExpression(const Token* tok) +{ + if (!tok) + return nullptr; + const Token* parent = tok->astParent(); + while (Token::simpleMatch(parent, ".")) + parent = parent->astParent(); + if (!parent) + return tok->next(); + const Token* endToken = nextAfterAstRightmostLeaf(parent); + if (!endToken) + return parent->next(); + return endToken; +} + +void CheckStl::invalidContainer() +{ + logChecker("CheckStl::invalidContainer"); + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + const Library& library = mSettings->library; + InvalidContainerAnalyzer analyzer; + analyzer.analyze(symbolDatabase); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (const Token* contTok = getLoopContainer(tok)) { + const Token* blockStart = tok->next()->link()->next(); + const Token* blockEnd = blockStart->link(); + if (contTok->exprId() == 0) + continue; + if (!astIsContainer(contTok)) + continue; + for (const Token* tok2 = blockStart; tok2 != blockEnd; tok2 = tok2->next()) { + bool bail = false; + for (const InvalidContainerAnalyzer::Info::Reference& r : analyzer.invalidatesContainer(tok2)) { + if (!astIsContainer(r.tok)) + continue; + if (r.tok->exprId() != contTok->exprId()) + continue; + const Scope* s = tok2->scope(); + if (!s) + continue; + if (isReturnScope(s->bodyEnd, mSettings->library)) + continue; + invalidContainerLoopError(r.ftok, tok, r.errorPath); + bail = true; + break; + } + if (bail) + break; + } + } else { + for (const InvalidContainerAnalyzer::Info::Reference& r : analyzer.invalidatesContainer(tok)) { + if (!astIsContainer(r.tok)) + continue; + std::set skipVarIds; + // Skip if the variable is assigned to + const Token* assignExpr = tok; + while (assignExpr->astParent()) { + const bool isRHS = astIsRHS(assignExpr); + assignExpr = assignExpr->astParent(); + if (Token::Match(assignExpr, "%assign%")) { + if (!isRHS) + assignExpr = nullptr; + break; + } + } + if (Token::Match(assignExpr, "%assign%") && Token::Match(assignExpr->astOperand1(), "%var%")) + skipVarIds.insert(assignExpr->astOperand1()->varId()); + const Token* endToken = endOfExpression(tok); + const ValueFlow::Value* v = nullptr; + ErrorPath errorPath; + PathAnalysis::Info info = + PathAnalysis{endToken, library}.forwardFind([&](const PathAnalysis::Info& info) { + if (!info.tok->variable()) + return false; + if (info.tok->varId() == 0) + return false; + if (skipVarIds.count(info.tok->varId()) > 0) + return false; + // if (Token::simpleMatch(info.tok->next(), ".")) + // return false; + if (Token::Match(info.tok->astParent(), "%assign%") && astIsLHS(info.tok)) + skipVarIds.insert(info.tok->varId()); + if (info.tok->variable()->isReference() && !isVariableDecl(info.tok) && + reaches(info.tok->variable()->nameToken(), tok, library, nullptr)) { + + ErrorPath ep; + bool addressOf = false; + const Variable* var = ValueFlow::getLifetimeVariable(info.tok, ep, &addressOf); + // Check the reference is created before the change + if (var && var->declarationId() == r.tok->varId() && !addressOf) { + // An argument always reaches + if (var->isArgument() || + (!var->isReference() && !var->isRValueReference() && !isVariableDecl(tok) && + reaches(var->nameToken(), tok, library, &ep))) { + errorPath = std::move(ep); + return true; + } + } + } + ErrorPath ep; + const ValueFlow::Value* val = getInnerLifetime(info.tok, r.tok->varId(), &ep); + // Check the iterator is created before the change + if (val && val->tokvalue != tok && reaches(val->tokvalue, tok, library, &ep)) { + v = val; + errorPath = std::move(ep); + return true; + } + return false; + }); + if (!info.tok) + continue; + errorPath.insert(errorPath.end(), info.errorPath.cbegin(), info.errorPath.cend()); + errorPath.insert(errorPath.end(), r.errorPath.cbegin(), r.errorPath.cend()); + if (v) { + invalidContainerError(info.tok, r.tok, v, std::move(errorPath)); + } else { + invalidContainerReferenceError(info.tok, r.tok, std::move(errorPath)); + } + } + } + } + } +} + +void CheckStl::invalidContainerLoopError(const Token* tok, const Token* loopTok, ErrorPath errorPath) +{ + const std::string method = tok ? tok->str() : "erase"; + errorPath.emplace_back(loopTok, "Iterating container here."); + + // Remove duplicate entries from error path + errorPath.remove_if([&](const ErrorPathItem& epi) { + return epi.first == tok; + }); + + const std::string msg = "Calling '" + method + "' while iterating the container is invalid."; + errorPath.emplace_back(tok, ""); + reportError(errorPath, Severity::error, "invalidContainerLoop", msg, CWE664, Certainty::normal); +} + +void CheckStl::invalidContainerError(const Token *tok, const Token * /*contTok*/, const ValueFlow::Value *val, ErrorPath errorPath) +{ + const bool inconclusive = val ? val->isInconclusive() : false; + if (val) + errorPath.insert(errorPath.begin(), val->errorPath.cbegin(), val->errorPath.cend()); + std::string msg = "Using " + lifetimeMessage(tok, val, errorPath); + errorPath.emplace_back(tok, ""); + reportError(errorPath, Severity::error, "invalidContainer", msg + " that may be invalid.", CWE664, inconclusive ? Certainty::inconclusive : Certainty::normal); +} + +void CheckStl::invalidContainerReferenceError(const Token* tok, const Token* contTok, ErrorPath errorPath) +{ + std::string name = contTok ? contTok->expressionString() : "x"; + std::string msg = "Reference to " + name; + errorPath.emplace_back(tok, ""); + reportError(errorPath, Severity::error, "invalidContainerReference", msg + " that may be invalid.", CWE664, Certainty::normal); +} + +void CheckStl::stlOutOfBounds() +{ + logChecker("CheckStl::stlOutOfBounds"); + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + + // Scan through all scopes.. + for (const Scope &scope : symbolDatabase->scopeList) { + const Token* tok = scope.classDef; + // only interested in conditions + if ((!scope.isLoopScope() && scope.type != Scope::eIf) || !tok) + continue; + + const Token *condition = nullptr; + if (scope.type == Scope::eFor) { + if (Token::simpleMatch(tok->next()->astOperand2(), ";") && Token::simpleMatch(tok->next()->astOperand2()->astOperand2(), ";")) + condition = tok->next()->astOperand2()->astOperand2()->astOperand1(); + } else if (Token::simpleMatch(tok, "do {") && Token::simpleMatch(tok->linkAt(1), "} while (")) + condition = tok->linkAt(1)->tokAt(2)->astOperand2(); + else + condition = tok->next()->astOperand2(); + + if (!condition) + continue; + + std::vector conds; + + visitAstNodes(condition, + [&](const Token *cond) { + if (Token::Match(cond, "%oror%|&&")) + return ChildrenToVisit::op1_and_op2; + if (cond->isComparisonOp()) + conds.emplace_back(cond); + return ChildrenToVisit::none; + }); + + for (const Token *cond : conds) { + const Token *vartok; + const Token *containerToken; + // check in the ast that cond is of the form "%var% <= %var% . %name% ( )" + if (cond->str() == "<=" && Token::Match(cond->astOperand1(), "%var%") && + cond->astOperand2()->str() == "(" && cond->astOperand2()->astOperand1()->str() == "." && + Token::Match(cond->astOperand2()->astOperand1()->astOperand1(), "%var%") && + Token::Match(cond->astOperand2()->astOperand1()->astOperand2(), "%name%")) { + vartok = cond->astOperand1(); + containerToken = cond->next(); + } else { + continue; + } + + if (containerToken->hasKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE)) + continue; + + // Is it a array like container? + const Library::Container* container = containerToken->valueType() ? containerToken->valueType()->container : nullptr; + if (!container) + continue; + if (container->getYield(containerToken->strAt(2)) != Library::Container::Yield::SIZE) + continue; + + // variable id for loop variable. + const int numId = vartok->varId(); + + // variable id for the container variable + const int declarationId = containerToken->varId(); + const std::string &containerName = containerToken->str(); + + for (const Token *tok3 = scope.bodyStart; tok3 && tok3 != scope.bodyEnd; tok3 = tok3->next()) { + if (tok3->varId() == declarationId) { + tok3 = tok3->next(); + if (Token::Match(tok3, ". %name% ( )")) { + if (container->getYield(tok3->strAt(1)) == Library::Container::Yield::SIZE) + break; + } else if (container->arrayLike_indexOp && Token::Match(tok3, "[ %varid% ]", numId)) + stlOutOfBoundsError(tok3, tok3->strAt(1), containerName, false); + else if (Token::Match(tok3, ". %name% ( %varid% )", numId)) { + const Library::Container::Yield yield = container->getYield(tok3->strAt(1)); + if (yield == Library::Container::Yield::AT_INDEX) + stlOutOfBoundsError(tok3, tok3->strAt(3), containerName, true); + } + } + } + } + } +} + +void CheckStl::stlOutOfBoundsError(const Token *tok, const std::string &num, const std::string &var, bool at) +{ + if (at) + reportError(tok, Severity::error, "stlOutOfBounds", "$symbol:" + var + "\nWhen " + num + "==$symbol.size(), $symbol.at(" + num + ") is out of bounds.", CWE788, Certainty::normal); + else + reportError(tok, Severity::error, "stlOutOfBounds", "$symbol:" + var + "\nWhen " + num + "==$symbol.size(), $symbol[" + num + "] is out of bounds.", CWE788, Certainty::normal); +} + +void CheckStl::negativeIndex() +{ + logChecker("CheckStl::negativeIndex"); + + // Negative index is out of bounds.. + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::Match(tok, "%var% [") || !tok->next()->astOperand2()) + continue; + const Variable * const var = tok->variable(); + if (!var || tok == var->nameToken()) + continue; + const Library::Container * const container = mSettings->library.detectContainer(var->typeStartToken()); + if (!container || !container->arrayLike_indexOp) + continue; + const ValueFlow::Value *index = tok->next()->astOperand2()->getValueLE(-1, mSettings); + if (!index) + continue; + negativeIndexError(tok, *index); + } + } +} + +void CheckStl::negativeIndexError(const Token *tok, const ValueFlow::Value &index) +{ + const ErrorPath errorPath = getErrorPath(tok, &index, "Negative array index"); + std::ostringstream errmsg; + if (index.condition) + errmsg << ValueFlow::eitherTheConditionIsRedundant(index.condition) + << ", otherwise there is negative array index " << index.intvalue << "."; + else + errmsg << "Array index " << index.intvalue << " is out of bounds."; + const auto severity = index.errorSeverity() && index.isKnown() ? Severity::error : Severity::warning; + const auto certainty = index.isInconclusive() ? Certainty::inconclusive : Certainty::normal; + reportError(errorPath, severity, "negativeContainerIndex", errmsg.str(), CWE786, certainty); +} + +void CheckStl::erase() +{ + logChecker("CheckStl::erase"); + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope &scope : symbolDatabase->scopeList) { + if (scope.type == Scope::eFor && Token::simpleMatch(scope.classDef, "for (")) { + const Token *tok = scope.classDef->linkAt(1); + if (!Token::Match(tok->tokAt(-3), "; ++| %var% ++| ) {")) + continue; + tok = tok->previous(); + if (!tok->isName()) + tok = tok->previous(); + eraseCheckLoopVar(scope, tok->variable()); + } else if (scope.type == Scope::eWhile && Token::Match(scope.classDef, "while ( %var% !=")) { + eraseCheckLoopVar(scope, scope.classDef->tokAt(2)->variable()); + } + } +} + +void CheckStl::eraseCheckLoopVar(const Scope &scope, const Variable *var) +{ + bool inconclusiveType=false; + if (!isIterator(var, inconclusiveType)) + return; + for (const Token *tok = scope.bodyStart; tok != scope.bodyEnd; tok = tok->next()) { + if (tok->str() != "(") + continue; + if (!Token::Match(tok->tokAt(-2), ". erase ( ++| %varid% )", var->declarationId())) + continue; + // Vector erases are handled by invalidContainer check + if (isVector(tok->tokAt(-3))) + continue; + if (Token::Match(tok->astParent(), "=|return")) + continue; + // Iterator is invalid.. + int indentlevel = 0U; + const Token *tok2 = tok->link(); + for (; tok2 != scope.bodyEnd; tok2 = tok2->next()) { + if (tok2->str() == "{") { + ++indentlevel; + continue; + } + if (tok2->str() == "}") { + if (indentlevel > 0U) + --indentlevel; + else if (Token::simpleMatch(tok2, "} else {")) + tok2 = tok2->linkAt(2); + continue; + } + if (tok2->varId() == var->declarationId()) { + if (Token::simpleMatch(tok2->next(), "=")) + break; + dereferenceErasedError(tok, tok2, tok2->str(), inconclusiveType); + break; + } + if (indentlevel == 0U && Token::Match(tok2, "break|return|goto")) + break; + } + if (tok2 == scope.bodyEnd) + dereferenceErasedError(tok, scope.classDef, var->nameToken()->str(), inconclusiveType); + } +} + +void CheckStl::stlBoundaries() +{ + logChecker("CheckStl::stlBoundaries"); + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Variable* var : symbolDatabase->variableList()) { + if (!var || !var->scope() || !var->scope()->isExecutable()) + continue; + + const Library::Container* container = mSettings->library.detectIterator(var->typeStartToken()); + if (!container || container->opLessAllowed) + continue; + + const Token* const end = var->scope()->bodyEnd; + for (const Token *tok = var->nameToken(); tok != end; tok = tok->next()) { + if (Token::Match(tok, "!!* %varid% <", var->declarationId())) { + stlBoundariesError(tok); + } else if (Token::Match(tok, "> %varid% !!.", var->declarationId())) { + stlBoundariesError(tok); + } + } + } +} + +// Error message for bad boundary usage.. +void CheckStl::stlBoundariesError(const Token *tok) +{ + reportError(tok, Severity::error, "stlBoundaries", + "Dangerous comparison using operator< on iterator.\n" + "Iterator compared with operator<. This is dangerous since the order of items in the " + "container is not guaranteed. One should use operator!= instead to compare iterators.", CWE664, Certainty::normal); +} + +static bool if_findCompare(const Token * const tokBack, bool stdStringLike) +{ + const Token *tok = tokBack->astParent(); + if (!tok) + return true; + if (tok->isComparisonOp()) { + if (stdStringLike) { + const Token * const tokOther = tokBack->astSibling(); + return !tokOther || !tokOther->hasKnownIntValue() || tokOther->getKnownIntValue() != 0; + } + return (!tok->astOperand1()->isNumber() && !tok->astOperand2()->isNumber()); + } + if (tok->isArithmeticalOp()) // result is used in some calculation + return true; // TODO: check if there is a comparison of the result somewhere + if (tok->str() == ".") + return true; // Dereferencing is OK, the programmer might know that the element exists - TODO: An inconclusive warning might be appropriate + if (tok->isAssignmentOp()) + return if_findCompare(tok, stdStringLike); // Go one step upwards in the AST + return false; +} + +void CheckStl::if_find() +{ + const bool printWarning = mSettings->severity.isEnabled(Severity::warning); + const bool printPerformance = mSettings->severity.isEnabled(Severity::performance); + if (!printWarning && !printPerformance) + return; + + logChecker("CheckStl::if_find"); // warning,performance + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope &scope : symbolDatabase->scopeList) { + if ((scope.type != Scope::eIf && scope.type != Scope::eWhile) || !scope.classDef) + continue; + + const Token *conditionStart = scope.classDef->next(); + if (Token::simpleMatch(conditionStart->astOperand2(), ";")) + conditionStart = conditionStart->astOperand2(); + + for (const Token *tok = conditionStart; tok->str() != "{"; tok = tok->next()) { + const Token* funcTok = nullptr; + const Library::Container* container = nullptr; + + if (Token::Match(tok, "%name% (")) + tok = tok->linkAt(1); + + else if (tok->variable() && Token::Match(tok, "%var% . %name% (")) { + container = mSettings->library.detectContainer(tok->variable()->typeStartToken()); + funcTok = tok->tokAt(2); + } + + // check also for vector-like or pointer containers + else if (tok->variable() && tok->astParent() && (tok->astParent()->str() == "*" || tok->astParent()->str() == "[")) { + const Token *tok2 = tok->astParent(); + + if (!Token::Match(tok2->astParent(), ". %name% (")) + continue; + + funcTok = tok2->astParent()->next(); + + if (tok->variable()->isArrayOrPointer()) + container = mSettings->library.detectContainer(tok->variable()->typeStartToken()); + else { // Container of container - find the inner container + container = mSettings->library.detectContainer(tok->variable()->typeStartToken()); // outer container + tok2 = Token::findsimplematch(tok->variable()->typeStartToken(), "<", tok->variable()->typeEndToken()); + if (container && container->type_templateArgNo >= 0 && tok2) { + tok2 = tok2->next(); + for (int j = 0; j < container->type_templateArgNo; j++) + tok2 = tok2->nextTemplateArgument(); + + container = mSettings->library.detectContainer(tok2); // inner container + } else + container = nullptr; + } + } + + Library::Container::Action action{}; + if (container && + ((action = container->getAction(funcTok->str())) == Library::Container::Action::FIND || action == Library::Container::Action::FIND_CONST)) { + if (if_findCompare(funcTok->next(), container->stdStringLike)) + continue; + + if (printWarning && container->getYield(funcTok->str()) == Library::Container::Yield::ITERATOR) + if_findError(tok, false); + else if (printPerformance && container->stdStringLike && funcTok->str() == "find") + if_findError(tok, true); + } else if (printWarning && Token::Match(tok, "std :: find|find_if (")) { + // check that result is checked properly + if (!if_findCompare(tok->tokAt(3), false)) { + if_findError(tok, false); + } + } + } + } +} + + +void CheckStl::if_findError(const Token *tok, bool str) +{ + if (str && mSettings->standards.cpp >= Standards::CPP20) + reportError(tok, Severity::performance, "stlIfStrFind", + "Inefficient usage of string::find() in condition; string::starts_with() could be faster.\n" + "Either inefficient or wrong usage of string::find(). string::starts_with() will be faster if " + "string::find's result is compared with 0, because it will not scan the whole " + "string. If your intention is to check that there are no findings in the string, " + "you should compare with std::string::npos.", CWE597, Certainty::normal); + if (!str) + reportError(tok, Severity::warning, "stlIfFind", "Suspicious condition. The result of find() is an iterator, but it is not properly checked.", CWE398, Certainty::normal); +} + +static std::pair isMapFind(const Token *tok) +{ + if (!Token::simpleMatch(tok, "(")) + return {}; + if (!Token::simpleMatch(tok->astOperand1(), ".")) + return {}; + if (!astIsContainer(tok->astOperand1()->astOperand1())) + return {}; + const Token * contTok = tok->astOperand1()->astOperand1(); + const Library::Container * container = contTok->valueType()->container; + if (!container) + return {}; + if (!container->stdAssociativeLike) + return {}; + if (!Token::Match(tok->astOperand1(), ". find|count (")) + return {}; + if (!tok->astOperand2()) + return {}; + return {contTok, tok->astOperand2()}; +} + +static const Token* skipLocalVars(const Token* const tok) +{ + if (!tok) + return tok; + if (Token::simpleMatch(tok, "{")) + return skipLocalVars(tok->next()); + + const Token *top = tok->astTop(); + if (!top) { + const Token *semi = Token::findsimplematch(tok, ";"); + if (!semi) + return tok; + if (!Token::Match(semi->previous(), "%var% ;")) + return tok; + const Token *varTok = semi->previous(); + const Variable *var = varTok->variable(); + if (!var) + return tok; + if (var->nameToken() != varTok) + return tok; + return skipLocalVars(semi->next()); + } + if (tok->isAssignmentOp()) { + const Token *varTok = top->astOperand1(); + const Variable *var = varTok->variable(); + if (!var) + return tok; + if (var->scope() != tok->scope()) + return tok; + const Token *endTok = nextAfterAstRightmostLeaf(top); + if (!endTok) + return tok; + return skipLocalVars(endTok->next()); + } + return tok; +} + +static const Token *findInsertValue(const Token *tok, const Token *containerTok, const Token *keyTok, const Library &library) +{ + const Token *startTok = skipLocalVars(tok); + const Token *top = startTok->astTop(); + + const Token *icontainerTok = nullptr; + const Token *ikeyTok = nullptr; + const Token *ivalueTok = nullptr; + if (Token::simpleMatch(top, "=") && Token::simpleMatch(top->astOperand1(), "[")) { + icontainerTok = top->astOperand1()->astOperand1(); + ikeyTok = top->astOperand1()->astOperand2(); + ivalueTok = top->astOperand2(); + } + if (Token::simpleMatch(top, "(") && Token::Match(top->astOperand1(), ". insert|emplace (") && !astIsIterator(top->astOperand1()->tokAt(2))) { + icontainerTok = top->astOperand1()->astOperand1(); + const Token *itok = top->astOperand1()->tokAt(2)->astOperand2(); + if (Token::simpleMatch(itok, ",")) { + ikeyTok = itok->astOperand1(); + ivalueTok = itok->astOperand2(); + } else { + ikeyTok = itok; + } + } + if (!ikeyTok || !icontainerTok) + return nullptr; + if (isSameExpression(true, containerTok, icontainerTok, library, true, false) && + isSameExpression(true, keyTok, ikeyTok, library, true, true)) { + if (ivalueTok) + return ivalueTok; + return ikeyTok; + } + return nullptr; +} + +void CheckStl::checkFindInsert() +{ + if (!mSettings->severity.isEnabled(Severity::performance)) + return; + + logChecker("CheckStl::checkFindInsert"); // performance + + const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *scope : symbolDatabase->functionScopes) { + for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::simpleMatch(tok, "if (")) + continue; + if (!Token::simpleMatch(tok->next()->link(), ") {")) + continue; + if (!Token::Match(tok->next()->astOperand2(), "%comp%")) + continue; + const Token *condTok = tok->next()->astOperand2(); + const Token *containerTok; + const Token *keyTok; + std::tie(containerTok, keyTok) = isMapFind(condTok->astOperand1()); + if (!containerTok) + continue; + // In < C++17 we only warn for small simple types + if (mSettings->standards.cpp < Standards::CPP17 && !(keyTok && keyTok->valueType() && (keyTok->valueType()->isIntegral() || keyTok->valueType()->pointer > 0))) + continue; + + const Token *thenTok = tok->next()->link()->next(); + const Token *valueTok = findInsertValue(thenTok, containerTok, keyTok, mSettings->library); + if (!valueTok) + continue; + + if (Token::simpleMatch(thenTok->link(), "} else {")) { + const Token *valueTok2 = + findInsertValue(thenTok->link()->tokAt(2), containerTok, keyTok, mSettings->library); + if (!valueTok2) + continue; + if (isSameExpression(true, valueTok, valueTok2, mSettings->library, true, true)) { + checkFindInsertError(valueTok); + } + } else { + checkFindInsertError(valueTok); + } + } + } +} + +void CheckStl::checkFindInsertError(const Token *tok) +{ + std::string replaceExpr; + if (tok && Token::simpleMatch(tok->astParent(), "=") && tok == tok->astParent()->astOperand2() && Token::simpleMatch(tok->astParent()->astOperand1(), "[")) { + if (mSettings->standards.cpp < Standards::CPP11) + // We will recommend using emplace/try_emplace instead + return; + const std::string f = (mSettings->standards.cpp < Standards::CPP17) ? "emplace" : "try_emplace"; + replaceExpr = " Instead of '" + tok->astParent()->expressionString() + "' consider using '" + + tok->astParent()->astOperand1()->astOperand1()->expressionString() + + "." + f + "(" + + tok->astParent()->astOperand1()->astOperand2()->expressionString() + + ", " + + tok->expressionString() + + ");'."; + } + + reportError( + tok, Severity::performance, "stlFindInsert", "Searching before insertion is not necessary." + replaceExpr, CWE398, Certainty::normal); +} + +/** + * Is container.size() slow? + */ +static bool isCpp03ContainerSizeSlow(const Token *tok) +{ + if (!tok) + return false; + const Variable* var = tok->variable(); + return var && var->isStlType("list"); +} + +void CheckStl::size() +{ + if (!mSettings->severity.isEnabled(Severity::performance)) + return; + + if (mSettings->standards.cpp >= Standards::CPP11) + return; + + logChecker("CheckStl::size"); // performance,c++03 + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "%var% . size ( )") || + Token::Match(tok, "%name% . %var% . size ( )")) { + // get the variable + const Token *varTok = tok; + if (tok->strAt(2) != "size") + varTok = varTok->tokAt(2); + + const Token* const end = varTok->tokAt(5); + + // check for comparison to zero + if ((!tok->previous()->isArithmeticalOp() && Token::Match(end, "==|<=|!=|> 0")) || + (end->next() && !end->next()->isArithmeticalOp() && Token::Match(tok->tokAt(-2), "0 ==|>=|!=|<"))) { + if (isCpp03ContainerSizeSlow(varTok)) { + sizeError(varTok); + continue; + } + } + + // check for comparison to one + if ((!tok->previous()->isArithmeticalOp() && Token::Match(end, ">=|< 1") && !end->tokAt(2)->isArithmeticalOp()) || + (end->next() && !end->next()->isArithmeticalOp() && Token::Match(tok->tokAt(-2), "1 <=|>") && !tok->tokAt(-3)->isArithmeticalOp())) { + if (isCpp03ContainerSizeSlow(varTok)) + sizeError(varTok); + } + + // check for using as boolean expression + else if ((Token::Match(tok->tokAt(-2), "if|while (") && end->str() == ")") || + (tok->previous()->tokType() == Token::eLogicalOp && Token::Match(end, "&&|)|,|;|%oror%"))) { + if (isCpp03ContainerSizeSlow(varTok)) + sizeError(varTok); + } + } + } + } +} + +void CheckStl::sizeError(const Token *tok) +{ + const std::string varname = tok ? tok->str() : std::string("list"); + reportError(tok, Severity::performance, "stlSize", + "$symbol:" + varname + "\n" + "Possible inefficient checking for '$symbol' emptiness.\n" + "Checking for '$symbol' emptiness might be inefficient. " + "Using $symbol.empty() instead of $symbol.size() can be faster. " + "$symbol.size() can take linear time but $symbol.empty() is " + "guaranteed to take constant time.", CWE398, Certainty::normal); +} + +void CheckStl::redundantCondition() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("redundantIfRemove")) + return; + + logChecker("CheckStl::redundantCondition"); // style + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope &scope : symbolDatabase->scopeList) { + if (scope.type != Scope::eIf) + continue; + + const Token* tok = scope.classDef->tokAt(2); + if (!Token::Match(tok, "%name% . find ( %any% ) != %name% . end|rend|cend|crend ( ) ) { %name% . remove|erase ( %any% ) ;")) + continue; + + // Get tokens for the fields %name% and %any% + const Token *var1 = tok; + const Token *any1 = var1->tokAt(4); + const Token *var2 = any1->tokAt(3); + const Token *var3 = var2->tokAt(7); + const Token *any2 = var3->tokAt(4); + + // Check if all the "%name%" fields are the same and if all the "%any%" are the same.. + if (var1->str() == var2->str() && + var2->str() == var3->str() && + any1->str() == any2->str()) { + redundantIfRemoveError(tok); + } + } +} + +void CheckStl::redundantIfRemoveError(const Token *tok) +{ + reportError(tok, Severity::style, "redundantIfRemove", + "Redundant checking of STL container element existence before removing it.\n" + "Redundant checking of STL container element existence before removing it. " + "It is safe to call the remove method on a non-existing element.", CWE398, Certainty::normal); +} + +void CheckStl::missingComparison() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckStl::missingComparison"); // warning + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope &scope : symbolDatabase->scopeList) { + if (scope.type != Scope::eFor || !scope.classDef) + continue; + + for (const Token *tok2 = scope.classDef->tokAt(2); tok2 != scope.bodyStart; tok2 = tok2->next()) { + if (tok2->str() == ";") + break; + + if (!Token::Match(tok2, "%var% = %name% . begin|rbegin|cbegin|crbegin ( ) ; %name% != %name% . end|rend|cend|crend ( ) ; ++| %name% ++| ) {")) + continue; + + // same container + if (tok2->strAt(2) != tok2->strAt(10)) + break; + + const int iteratorId(tok2->varId()); + + // same iterator + if (iteratorId == tok2->tokAt(10)->varId()) + break; + + // increment iterator + if (!Token::Match(tok2->tokAt(16), "++ %varid% )", iteratorId) && + !Token::Match(tok2->tokAt(16), "%varid% ++ )", iteratorId)) { + break; + } + + const Token *incrementToken = nullptr; + // Parse loop.. + for (const Token *tok3 = scope.bodyStart; tok3 != scope.bodyEnd; tok3 = tok3->next()) { + if (tok3->varId() == iteratorId) { + if (Token::Match(tok3, "%varid% = %name% . insert ( ++| %varid% ++| ,", iteratorId)) { + // skip insertion.. + tok3 = tok3->linkAt(6); + if (!tok3) + break; + } else if (Token::simpleMatch(tok3->astParent(), "++")) + incrementToken = tok3; + else if (Token::simpleMatch(tok3->astParent(), "+")) { + if (Token::Match(tok3->astSibling(), "%num%")) { + const Token* tokenGrandParent = tok3->astParent()->astParent(); + if (Token::Match(tokenGrandParent, "==|!=")) + break; + } + } else if (Token::Match(tok3->astParent(), "==|!=")) + incrementToken = nullptr; + } else if (tok3->str() == "break" || tok3->str() == "return") + incrementToken = nullptr; + } + if (incrementToken) + missingComparisonError(incrementToken, tok2->tokAt(16)); + } + } +} + +void CheckStl::missingComparisonError(const Token *incrementToken1, const Token *incrementToken2) +{ + std::list callstack = { incrementToken1,incrementToken2 }; + + std::ostringstream errmsg; + errmsg << "Missing bounds check for extra iterator increment in loop.\n" + << "The iterator incrementing is suspicious - it is incremented at line "; + if (incrementToken1) + errmsg << incrementToken1->linenr(); + errmsg << " and then at line "; + if (incrementToken2) + errmsg << incrementToken2->linenr(); + errmsg << ". The loop might unintentionally skip an element in the container. " + << "There is no comparison between these increments to prevent that the iterator is " + << "incremented beyond the end."; + + reportError(callstack, Severity::warning, "StlMissingComparison", errmsg.str(), CWE834, Certainty::normal); +} + + +static bool isLocal(const Token *tok) +{ + const Variable *var = tok->variable(); + return var && !var->isStatic() && var->isLocal(); +} + +namespace { + const std::set stl_string_stream = { + "istringstream", "ostringstream", "stringstream", "wstringstream" + }; +} + +void CheckStl::string_c_str() +{ + const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); + const bool printPerformance = mSettings->severity.isEnabled(Severity::performance); + + const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); + + logChecker("CheckStl::string_c_str"); + + // Find all functions that take std::string as argument + struct StrArg { + nonneg int n; + std::string argtype; + }; + std::multimap c_strFuncParam; + if (printPerformance) { + for (const Scope &scope : symbolDatabase->scopeList) { + for (const Function &func : scope.functionList) { + nonneg int numpar = 0; + for (const Variable &var : func.argumentList) { + numpar++; + if ((var.isStlStringType() || var.isStlStringViewType()) && (!var.isReference() || var.isConst())) + c_strFuncParam.emplace(&func, StrArg{ numpar, var.getTypeName() }); + } + } + } + } + + auto isString = [](const Token* str) -> bool { + while (Token::Match(str, "::|.")) + str = str->astOperand2(); + if (Token::Match(str, "(|[") && !(str->valueType() && str->valueType()->type == ValueType::ITERATOR)) + str = str->previous(); + return str && ((str->variable() && str->variable()->isStlStringType()) || // variable + (str->function() && isStlStringType(str->function()->retDef)) || // function returning string + (str->valueType() && str->valueType()->type == ValueType::ITERATOR && isStlStringType(str->valueType()->containerTypeToken))); // iterator pointing to string + }; + + // Try to detect common problems when using string::c_str() + for (const Scope &scope : symbolDatabase->scopeList) { + if (scope.type != Scope::eFunction || !scope.function) + continue; + + enum {charPtr, stdString, stdStringConstRef, Other} returnType = Other; + if (Token::Match(scope.function->tokenDef->tokAt(-2), "char|wchar_t *")) + returnType = charPtr; + else if (Token::Match(scope.function->tokenDef->tokAt(-5), "const std :: string|wstring &")) + returnType = stdStringConstRef; + else if (Token::Match(scope.function->tokenDef->tokAt(-3), "std :: string|wstring !!&")) + returnType = stdString; + + for (const Token *tok = scope.bodyStart; tok && tok != scope.bodyEnd; tok = tok->next()) { + // Invalid usage.. + if (Token::Match(tok, "throw %var% . c_str|data ( ) ;") && isLocal(tok->next()) && + tok->next()->variable() && tok->next()->variable()->isStlStringType()) { + string_c_strThrowError(tok); + } else if (tok->variable() && tok->strAt(1) == "=") { + if (Token::Match(tok->tokAt(2), "%var% . str ( ) . c_str|data ( ) ;")) { + const Variable* var = tok->variable(); + const Variable* var2 = tok->tokAt(2)->variable(); + if (var->isPointer() && var2 && var2->isStlType(stl_string_stream)) + string_c_strError(tok); + } else if (Token::Match(tok->tokAt(2), "%name% (") && + Token::Match(tok->linkAt(3), ") . c_str|data ( ) ;") && + tok->tokAt(2)->function() && Token::Match(tok->tokAt(2)->function()->retDef, "std :: string|wstring %name%")) { + const Variable* var = tok->variable(); + if (var->isPointer()) + string_c_strError(tok); + } else if (printPerformance && tok->tokAt(1)->astOperand2() && Token::Match(tok->tokAt(1)->astOperand2()->tokAt(-3), "%var% . c_str|data ( ) ;")) { + const Token* vartok = tok->tokAt(1)->astOperand2()->tokAt(-3); + if ((tok->variable()->isStlStringType() || tok->variable()->isStlStringViewType()) && vartok->variable() && vartok->variable()->isStlStringType()) + string_c_strAssignment(tok, tok->variable()->getTypeName()); + } + } else if (printPerformance && tok->function() && Token::Match(tok, "%name% ( !!)") && tok->str() != scope.className) { + const auto range = c_strFuncParam.equal_range(tok->function()); + for (std::multimap::const_iterator i = range.first; i != range.second; ++i) { + if (i->second.n == 0) + continue; + + const Token* tok2 = tok->tokAt(2); + int j; + for (j = 0; tok2 && j < i->second.n - 1; j++) + tok2 = tok2->nextArgument(); + if (tok2) + tok2 = tok2->nextArgument(); + else + break; + if (!tok2 && j == i->second.n - 1) + tok2 = tok->next()->link(); + else if (tok2) + tok2 = tok2->previous(); + else + break; + if (tok2 && Token::Match(tok2->tokAt(-4), ". c_str|data ( )")) { + if (isString(tok2->tokAt(-4)->astOperand1())) { + string_c_strParam(tok, i->second.n, i->second.argtype); + } else if (Token::Match(tok2->tokAt(-9), "%name% . str ( )")) { // Check ss.str().c_str() as parameter + const Variable* ssVar = tok2->tokAt(-9)->variable(); + if (ssVar && ssVar->isStlType(stl_string_stream)) + string_c_strParam(tok, i->second.n, i->second.argtype); + } + } + } + } else if (printPerformance && Token::Match(tok, "%var% (|{ %var% . c_str|data ( ) !!,") && + tok->variable() && (tok->variable()->isStlStringType() || tok->variable()->isStlStringViewType()) && + tok->tokAt(2)->variable() && tok->tokAt(2)->variable()->isStlStringType()) { + string_c_strConstructor(tok, tok->variable()->getTypeName()); + } else if (printPerformance && tok->next() && tok->next()->variable() && tok->next()->variable()->isStlStringType() && tok->valueType() && tok->valueType()->type == ValueType::CONTAINER && + ((Token::Match(tok->previous(), "%var% + %var% . c_str|data ( )") && tok->previous()->variable() && tok->previous()->variable()->isStlStringType()) || + (Token::Match(tok->tokAt(-5), "%var% . c_str|data ( ) + %var%") && tok->tokAt(-5)->variable() && tok->tokAt(-5)->variable()->isStlStringType()))) { + string_c_strConcat(tok); + } else if (printPerformance && Token::simpleMatch(tok, "<<") && tok->astOperand2() && Token::Match(tok->astOperand2()->astOperand1(), ". c_str|data ( )")) { + const Token* str = tok->astOperand2()->astOperand1()->astOperand1(); + if (isString(str)) { + const Token* strm = tok; + while (Token::simpleMatch(strm, "<<")) + strm = strm->astOperand1(); + if (strm && strm->variable() && strm->variable()->isStlType()) + string_c_strStream(tok); + } + } + + // Using c_str() to get the return value is only dangerous if the function returns a char* + else if ((returnType == charPtr || (printPerformance && (returnType == stdString || returnType == stdStringConstRef))) && tok->str() == "return") { + bool err = false; + + const Token* tok2 = tok->next(); + if (Token::Match(tok2, "std :: string|wstring (") && + Token::Match(tok2->linkAt(3), ") . c_str|data ( ) ;")) { + err = true; + } else if (Token::simpleMatch(tok2, "(") && + Token::Match(tok2->link(), ") . c_str|data ( ) ;")) { + // Check for "+ localvar" or "+ std::string(" inside the bracket + bool is_implicit_std_string = printInconclusive; + const Token *search_end = tok2->link(); + for (const Token *search_tok = tok2->next(); search_tok != search_end; search_tok = search_tok->next()) { + if (Token::Match(search_tok, "+ %var%") && isLocal(search_tok->next()) && + search_tok->next()->variable() && search_tok->next()->variable()->isStlStringType()) { + is_implicit_std_string = true; + break; + } + if (Token::Match(search_tok, "+ std :: string|wstring (")) { + is_implicit_std_string = true; + break; + } + } + + if (is_implicit_std_string) + err = true; + } + + bool local = false; + bool ptrOrRef = false; + const Variable* lastVar = nullptr; + const Function* lastFunc = nullptr; + bool funcStr = false; + if (Token::Match(tok2, "%var% .")) { + local = isLocal(tok2); + bool refToNonLocal = false; + if (tok2->variable() && tok2->variable()->isReference()) { + const Token *refTok = tok2->variable()->nameToken(); + refToNonLocal = true; // safe assumption is default to avoid FPs + if (Token::Match(refTok, "%var% = %var% .|;|[")) + refToNonLocal = !isLocal(refTok->tokAt(2)); + } + ptrOrRef = refToNonLocal || (tok2->variable() && (tok2->variable()->isPointer() || tok2->variable()->isSmartPointer())); + } + while (tok2) { + if (Token::Match(tok2, "%var% .|::")) { + if (ptrOrRef) + local = false; + lastVar = tok2->variable(); + tok2 = tok2->tokAt(2); + } else if (Token::Match(tok2, "%name% (") && Token::simpleMatch(tok2->linkAt(1), ") .")) { + lastFunc = tok2->function(); + local = false; + funcStr = tok2->str() == "str"; + tok2 = tok2->linkAt(1)->tokAt(2); + } else + break; + } + + if (Token::Match(tok2, "c_str|data ( ) ;")) { + if ((local || returnType != charPtr) && lastVar && lastVar->isStlStringType()) + err = true; + else if (funcStr && lastVar && lastVar->isStlType(stl_string_stream)) + err = true; + else if (lastFunc && Token::Match(lastFunc->tokenDef->tokAt(-3), "std :: string|wstring")) + err = true; + } + + if (err) { + if (returnType == charPtr) + string_c_strError(tok); + else + string_c_strReturn(tok); + } + } + } + } +} + +void CheckStl::string_c_strThrowError(const Token* tok) +{ + reportError(tok, Severity::error, "stlcstrthrow", "Dangerous usage of c_str(). The value returned by c_str() is invalid after throwing exception.\n" + "Dangerous usage of c_str(). The string is destroyed after the c_str() call so the thrown pointer is invalid."); +} + +void CheckStl::string_c_strError(const Token* tok) +{ + reportError(tok, Severity::error, "stlcstr", "Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n" + "Dangerous usage of c_str(). The c_str() return value is only valid until its string is deleted.", CWE664, Certainty::normal); +} + +void CheckStl::string_c_strReturn(const Token* tok) +{ + reportError(tok, Severity::performance, "stlcstrReturn", "Returning the result of c_str() in a function that returns std::string is slow and redundant.\n" + "The conversion from const char* as returned by c_str() to std::string creates an unnecessary string copy. Solve that by directly returning the string.", CWE704, Certainty::normal); +} + +void CheckStl::string_c_strParam(const Token* tok, nonneg int number, const std::string& argtype) +{ + std::ostringstream oss; + oss << "Passing the result of c_str() to a function that takes " << argtype << " as argument no. " << number << " is slow and redundant.\n" + "The conversion from const char* as returned by c_str() to " << argtype << " creates an unnecessary string copy or length calculation. Solve that by directly passing the string."; + reportError(tok, Severity::performance, "stlcstrParam", oss.str(), CWE704, Certainty::normal); +} + +void CheckStl::string_c_strConstructor(const Token* tok, const std::string& argtype) +{ + std::string msg = "Constructing a " + argtype + " from the result of c_str() is slow and redundant.\n" + "Constructing a " + argtype + " from const char* requires a call to strlen(). Solve that by directly passing the string."; + reportError(tok, Severity::performance, "stlcstrConstructor", msg, CWE704, Certainty::normal); +} + +void CheckStl::string_c_strAssignment(const Token* tok, const std::string& argtype) +{ + std::string msg = "Assigning the result of c_str() to a " + argtype + " is slow and redundant.\n" + "Assigning a const char* to a " + argtype + " requires a call to strlen(). Solve that by directly assigning the string."; + reportError(tok, Severity::performance, "stlcstrAssignment", msg, CWE704, Certainty::normal); +} + +void CheckStl::string_c_strConcat(const Token* tok) +{ + std::string msg = "Concatenating the result of c_str() and a std::string is slow and redundant.\n" + "Concatenating a const char* with a std::string requires a call to strlen(). Solve that by directly concatenating the strings."; + reportError(tok, Severity::performance, "stlcstrConcat", msg, CWE704, Certainty::normal); +} + +void CheckStl::string_c_strStream(const Token* tok) +{ + std::string msg = "Passing the result of c_str() to a stream is slow and redundant.\n" + "Passing a const char* to a stream requires a call to strlen(). Solve that by directly passing the string."; + reportError(tok, Severity::performance, "stlcstrStream", msg, CWE704, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// +//--------------------------------------------------------------------------- + +namespace { + const std::set stl_containers_with_empty_and_clear = { + "deque", "forward_list", "list", + "map", "multimap", "multiset", "set", "string", + "unordered_map", "unordered_multimap", "unordered_multiset", + "unordered_set", "vector", "wstring" + }; + +} + +void CheckStl::uselessCalls() +{ + const bool printPerformance = mSettings->severity.isEnabled(Severity::performance); + const bool printWarning = mSettings->severity.isEnabled(Severity::warning); + if (!printPerformance && !printWarning) + return; + + logChecker("CheckStl::uselessCalls"); // performance,warning + + const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (printWarning && Token::Match(tok, "%var% . compare|find|rfind|find_first_not_of|find_first_of|find_last_not_of|find_last_of ( %name% [,)]") && + tok->varId() == tok->tokAt(4)->varId()) { + const Variable* var = tok->variable(); + if (!var || !var->isStlType()) + continue; + uselessCallsReturnValueError(tok->tokAt(4), tok->str(), tok->strAt(2)); + } else if (printPerformance && Token::Match(tok, "%var% . swap ( %name% )") && + tok->varId() == tok->tokAt(4)->varId()) { + const Variable* var = tok->variable(); + if (!var || !var->isStlType()) + continue; + uselessCallsSwapError(tok, tok->str()); + } else if (printPerformance && Token::Match(tok, "%var% . substr (") && tok->variable() && tok->variable()->isStlStringType()) { + const Token* funcTok = tok->tokAt(3); + const std::vector args = getArguments(funcTok); + if (Token::Match(tok->tokAt(-2), "%var% =") && tok->varId() == tok->tokAt(-2)->varId() && + !args.empty() && args[0]->hasKnownIntValue() && args[0]->getKnownIntValue() == 0) { + uselessCallsSubstrError(tok, Token::simpleMatch(funcTok->astParent(), "=") ? SubstrErrorType::PREFIX : SubstrErrorType::PREFIX_CONCAT); + } else if (args.empty() || (args[0]->hasKnownIntValue() && args[0]->getKnownIntValue() == 0 && + (args.size() == 1 || (args.size() == 2 && tok->linkAt(3)->strAt(-1) == "npos" && !tok->linkAt(3)->previous()->variable())))) { + uselessCallsSubstrError(tok, SubstrErrorType::COPY); + } else if (args.size() == 2 && args[1]->hasKnownIntValue() && args[1]->getKnownIntValue() == 0) { + uselessCallsSubstrError(tok, SubstrErrorType::EMPTY); + } + } else if (printWarning && Token::Match(tok, "[{};] %var% . empty ( ) ;") && + !tok->tokAt(4)->astParent() && + tok->next()->variable() && tok->next()->variable()->isStlType(stl_containers_with_empty_and_clear)) + uselessCallsEmptyError(tok->next()); + else if (Token::Match(tok, "[{};] std :: remove|remove_if|unique (") && tok->tokAt(5)->nextArgument()) + uselessCallsRemoveError(tok->next(), tok->strAt(3)); + else if (printPerformance && tok->valueType() && tok->valueType()->type == ValueType::CONTAINER) { + if (Token::Match(tok, "%var% = { %var% . begin ( ) ,") && tok->varId() == tok->tokAt(3)->varId()) + uselessCallsConstructorError(tok); + else if (const Variable* var = tok->variable()) { + std::string pattern = "%var% = "; + for (const Token* t = var->typeStartToken(); t != var->typeEndToken()->next(); t = t->next()) { + pattern += t->str(); + pattern += ' '; + } + pattern += "{|( %varid% . begin ( ) ,"; + if (Token::Match(tok, pattern.c_str(), tok->varId())) + uselessCallsConstructorError(tok); + } + } + } + } +} + + +void CheckStl::uselessCallsReturnValueError(const Token *tok, const std::string &varname, const std::string &function) +{ + std::ostringstream errmsg; + errmsg << "$symbol:" << varname << '\n'; + errmsg << "$symbol:" << function << '\n'; + errmsg << "It is inefficient to call '" << varname << "." << function << "(" << varname << ")' as it always returns 0.\n" + << "'std::string::" << function << "()' returns zero when given itself as parameter " + << "(" << varname << "." << function << "(" << varname << ")). As it is currently the " + << "code is inefficient. It is possible either the string searched ('" + << varname << "') or searched for ('" << varname << "') is wrong."; + reportError(tok, Severity::warning, "uselessCallsCompare", errmsg.str(), CWE628, Certainty::normal); +} + +void CheckStl::uselessCallsSwapError(const Token *tok, const std::string &varname) +{ + reportError(tok, Severity::performance, "uselessCallsSwap", + "$symbol:" + varname + "\n" + "It is inefficient to swap a object with itself by calling '$symbol.swap($symbol)'\n" + "The 'swap()' function has no logical effect when given itself as parameter " + "($symbol.swap($symbol)). As it is currently the " + "code is inefficient. Is the object or the parameter wrong here?", CWE628, Certainty::normal); +} + +void CheckStl::uselessCallsSubstrError(const Token *tok, SubstrErrorType type) +{ + std::string msg = "Ineffective call of function 'substr' because "; + switch (type) { + case SubstrErrorType::EMPTY: + msg += "it returns an empty string."; + break; + case SubstrErrorType::COPY: + msg += "it returns a copy of the object. Use operator= instead."; + break; + case SubstrErrorType::PREFIX: + msg += "a prefix of the string is assigned to itself. Use resize() or pop_back() instead."; + break; + case SubstrErrorType::PREFIX_CONCAT: + msg += "a prefix of the string is assigned to itself. Use replace() instead."; + break; + } + reportError(tok, Severity::performance, "uselessCallsSubstr", msg, CWE398, Certainty::normal); +} + +void CheckStl::uselessCallsConstructorError(const Token *tok) +{ + const std::string msg = "Inefficient constructor call: container '" + tok->str() + "' is assigned a partial copy of itself. Use erase() or resize() instead."; + reportError(tok, Severity::performance, "uselessCallsConstructor", msg, CWE398, Certainty::normal); +} + +void CheckStl::uselessCallsEmptyError(const Token *tok) +{ + reportError(tok, Severity::warning, "uselessCallsEmpty", "Ineffective call of function 'empty()'. Did you intend to call 'clear()' instead?", CWE398, Certainty::normal); +} + +void CheckStl::uselessCallsRemoveError(const Token *tok, const std::string& function) +{ + reportError(tok, Severity::warning, "uselessCallsRemove", + "$symbol:" + function + "\n" + "Return value of std::$symbol() ignored. Elements remain in container.\n" + "The return value of std::$symbol() is ignored. This function returns an iterator to the end of the range containing those elements that should be kept. " + "Elements past new end remain valid but with unspecified values. Use the erase method of the container to delete them.", CWE762, Certainty::normal); +} + +// Check for iterators being dereferenced before being checked for validity. +// E.g. if (*i && i != str.end()) { } +void CheckStl::checkDereferenceInvalidIterator() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckStl::checkDereferenceInvalidIterator"); // warning + + // Iterate over "if", "while", and "for" conditions where there may + // be an iterator that is dereferenced before being checked for validity. + for (const Scope &scope : mTokenizer->getSymbolDatabase()->scopeList) { + if (!(scope.type == Scope::eIf || scope.isLoopScope())) + continue; + + const Token* const tok = scope.classDef; + const Token* startOfCondition = tok->next(); + if (scope.type == Scope::eDo) + startOfCondition = startOfCondition->link()->tokAt(2); + if (!startOfCondition) // ticket #6626 invalid code + continue; + const Token* endOfCondition = startOfCondition->link(); + if (!endOfCondition) + continue; + + // For "for" loops, only search between the two semicolons + if (scope.type == Scope::eFor) { + startOfCondition = Token::findsimplematch(tok->tokAt(2), ";", endOfCondition); + if (!startOfCondition) + continue; + endOfCondition = Token::findsimplematch(startOfCondition->next(), ";", endOfCondition); + if (!endOfCondition) + continue; + } + + // Only consider conditions composed of all "&&" terms and + // conditions composed of all "||" terms + const bool isOrExpression = + Token::findsimplematch(startOfCondition, "||", endOfCondition) != nullptr; + const bool isAndExpression = + Token::findsimplematch(startOfCondition, "&&", endOfCondition) != nullptr; + + // Look for a check of the validity of an iterator + const Token* validityCheckTok = nullptr; + if (!isOrExpression && isAndExpression) { + validityCheckTok = + Token::findmatch(startOfCondition, "&& %var% != %name% . end|rend|cend|crend ( )", endOfCondition); + } else if (isOrExpression && !isAndExpression) { + validityCheckTok = + Token::findmatch(startOfCondition, "%oror% %var% == %name% . end|rend|cend|crend ( )", endOfCondition); + } + + if (!validityCheckTok) + continue; + const int iteratorVarId = validityCheckTok->next()->varId(); + + // If the iterator dereference is to the left of the check for + // the iterator's validity, report an error. + const Token* const dereferenceTok = + Token::findmatch(startOfCondition, "* %varid%", validityCheckTok, iteratorVarId); + if (dereferenceTok) + dereferenceInvalidIteratorError(dereferenceTok, dereferenceTok->strAt(1)); + } +} + + +void CheckStl::checkDereferenceInvalidIterator2() +{ + const bool printInconclusive = (mSettings->certainty.isEnabled(Certainty::inconclusive)); + + logChecker("CheckStl::checkDereferenceInvalidIterator2"); + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (Token::Match(tok, "sizeof|decltype|typeid|typeof (")) { + tok = tok->next()->link(); + continue; + } + + if (Token::Match(tok, "%assign%")) + continue; + + std::vector contValues; + std::copy_if(tok->values().cbegin(), tok->values().cend(), std::back_inserter(contValues), [&](const ValueFlow::Value& value) { + if (value.isImpossible()) + return false; + if (!printInconclusive && value.isInconclusive()) + return false; + return value.isContainerSizeValue(); + }); + + + // Can iterator point to END or before START? + for (const ValueFlow::Value& value:tok->values()) { + if (value.isImpossible()) + continue; + if (!printInconclusive && value.isInconclusive()) + continue; + if (!value.isIteratorValue()) + continue; + bool isInvalidIterator = false; + const ValueFlow::Value* cValue = nullptr; + if (value.isIteratorEndValue() && value.intvalue >= 0) { + isInvalidIterator = value.intvalue > 0; + } else if (value.isIteratorStartValue() && value.intvalue < 0) { + isInvalidIterator = true; + } else { + auto it = std::find_if(contValues.cbegin(), contValues.cend(), [&](const ValueFlow::Value& c) { + if (value.path != c.path) + return false; + if (value.isIteratorStartValue() && value.intvalue >= c.intvalue) + return true; + if (value.isIteratorEndValue() && -value.intvalue > c.intvalue) + return true; + return false; + }); + if (it == contValues.end()) + continue; + cValue = &*it; + if (value.isIteratorStartValue() && value.intvalue > cValue->intvalue) + isInvalidIterator = true; + } + bool inconclusive = false; + bool unknown = false; + const Token* emptyAdvance = nullptr; + const Token* advanceIndex = nullptr; + if (cValue && cValue->intvalue == 0) { + if (Token::Match(tok->astParent(), "+|-") && astIsIntegral(tok->astSibling(), false)) { + if (tok->astSibling() && tok->astSibling()->hasKnownIntValue()) { + if (tok->astSibling()->values().front().intvalue == 0) + continue; + } else { + advanceIndex = tok->astSibling(); + } + emptyAdvance = tok->astParent(); + } else if (Token::Match(tok->astParent(), "++|--")) { + emptyAdvance = tok->astParent(); + } + } + if (!CheckNullPointer::isPointerDeRef(tok, unknown, *mSettings) && !isInvalidIterator && !emptyAdvance) { + if (!unknown) + continue; + inconclusive = true; + } + if (cValue) { + const ValueFlow::Value& lValue = getLifetimeIteratorValue(tok, cValue->path); + assert(cValue->isInconclusive() || value.isInconclusive() || lValue.isLifetimeValue()); + if (!lValue.isLifetimeValue()) + continue; + if (emptyAdvance) + outOfBoundsError(emptyAdvance, + lValue.tokvalue->expressionString(), + cValue, + advanceIndex ? advanceIndex->expressionString() : emptyString, + nullptr); + else + outOfBoundsError(tok, lValue.tokvalue->expressionString(), cValue, tok->expressionString(), &value); + } else { + dereferenceInvalidIteratorError(tok, &value, inconclusive); + } + } + } +} + +void CheckStl::dereferenceInvalidIteratorError(const Token* tok, const ValueFlow::Value *value, bool inconclusive) +{ + const std::string& varname = tok ? tok->expressionString() : "var"; + const std::string errmsgcond("$symbol:" + varname + '\n' + ValueFlow::eitherTheConditionIsRedundant(value ? value->condition : nullptr) + " or there is possible dereference of an invalid iterator: $symbol."); + if (!tok || !value) { + reportError(tok, Severity::error, "derefInvalidIterator", "Dereference of an invalid iterator", CWE825, Certainty::normal); + reportError(tok, Severity::warning, "derefInvalidIteratorRedundantCheck", errmsgcond, CWE825, Certainty::normal); + return; + } + if (!mSettings->isEnabled(value, inconclusive)) + return; + + const ErrorPath errorPath = getErrorPath(tok, value, "Dereference of an invalid iterator"); + + if (value->condition) { + reportError(errorPath, Severity::warning, "derefInvalidIteratorRedundantCheck", errmsgcond, CWE825, (inconclusive || value->isInconclusive()) ? Certainty::inconclusive : Certainty::normal); + } else { + std::string errmsg = std::string(value->isKnown() ? "Dereference" : "Possible dereference") + " of an invalid iterator"; + if (!varname.empty()) + errmsg = "$symbol:" + varname + '\n' + errmsg + ": $symbol"; + + reportError(errorPath, + value->isKnown() ? Severity::error : Severity::warning, + "derefInvalidIterator", + errmsg, + CWE825, (inconclusive || value->isInconclusive()) ? Certainty::inconclusive : Certainty::normal); + } +} + +void CheckStl::dereferenceInvalidIteratorError(const Token* deref, const std::string &iterName) +{ + reportError(deref, Severity::warning, + "derefInvalidIterator", + "$symbol:" + iterName + "\n" + "Possible dereference of an invalid iterator: $symbol\n" + "Possible dereference of an invalid iterator: $symbol. Make sure to check that the iterator is valid before dereferencing it - not after.", CWE825, Certainty::normal); +} + +void CheckStl::useStlAlgorithmError(const Token *tok, const std::string &algoName) +{ + reportError(tok, Severity::style, "useStlAlgorithm", + "Consider using " + algoName + " algorithm instead of a raw loop.", CWE398, Certainty::normal); +} + +static bool isEarlyExit(const Token *start) +{ + if (start->str() != "{") + return false; + const Token *endToken = start->link(); + const Token *tok = Token::findmatch(start, "return|throw|break", endToken); + if (!tok) + return false; + const Token *endStatement = Token::findsimplematch(tok, "; }", endToken); + if (!endStatement) + return false; + if (endStatement->next() != endToken) + return false; + return true; +} + +static const Token *singleStatement(const Token *start) +{ + if (start->str() != "{") + return nullptr; + const Token *endToken = start->link(); + const Token *endStatement = Token::findsimplematch(start->next(), ";"); + if (!Token::simpleMatch(endStatement, "; }")) + return nullptr; + if (endStatement->next() != endToken) + return nullptr; + return endStatement; +} + +static const Token *singleAssignInScope(const Token *start, nonneg int varid, bool &input, bool &hasBreak, const Settings* settings) +{ + const Token *endStatement = singleStatement(start); + if (!endStatement) + return nullptr; + if (!Token::Match(start->next(), "%var% %assign%")) + return nullptr; + const Token *assignTok = start->tokAt(2); + if (isVariableChanged(assignTok->next(), endStatement, assignTok->astOperand1()->varId(), /*globalvar*/ false, settings)) + return nullptr; + if (isVariableChanged(assignTok->next(), endStatement, varid, /*globalvar*/ false, settings)) + return nullptr; + input = Token::findmatch(assignTok->next(), "%varid%", endStatement, varid) || !Token::Match(start->next(), "%var% ="); + hasBreak = Token::simpleMatch(endStatement->previous(), "break"); + return assignTok; +} + +static const Token *singleMemberCallInScope(const Token *start, nonneg int varid, bool &input, const Settings* settings) +{ + if (start->str() != "{") + return nullptr; + const Token *endToken = start->link(); + if (!Token::Match(start->next(), "%var% . %name% (")) + return nullptr; + if (!Token::simpleMatch(start->linkAt(4), ") ; }")) + return nullptr; + const Token *endStatement = start->linkAt(4)->next(); + if (endStatement->next() != endToken) + return nullptr; + + const Token *dotTok = start->tokAt(2); + if (!Token::findmatch(dotTok->tokAt(2), "%varid%", endStatement, varid)) + return nullptr; + input = Token::Match(start->next(), "%var% . %name% ( %varid% )", varid); + if (isVariableChanged(dotTok->next(), endStatement, dotTok->astOperand1()->varId(), /*globalvar*/ false, settings)) + return nullptr; + return dotTok; +} + +static const Token *singleIncrementInScope(const Token *start, nonneg int varid, bool &input) +{ + if (start->str() != "{") + return nullptr; + const Token *varTok = nullptr; + if (Token::Match(start->next(), "++ %var% ; }")) + varTok = start->tokAt(2); + else if (Token::Match(start->next(), "%var% ++ ; }")) + varTok = start->tokAt(1); + if (!varTok) + return nullptr; + input = varTok->varId() == varid; + return varTok; +} + +static const Token *singleConditionalInScope(const Token *start, nonneg int varid, const Settings* settings) +{ + if (start->str() != "{") + return nullptr; + const Token *endToken = start->link(); + if (!Token::simpleMatch(start->next(), "if (")) + return nullptr; + if (!Token::simpleMatch(start->linkAt(2), ") {")) + return nullptr; + const Token *bodyTok = start->linkAt(2)->next(); + const Token *endBodyTok = bodyTok->link(); + if (!Token::simpleMatch(endBodyTok, "} }")) + return nullptr; + if (endBodyTok->next() != endToken) + return nullptr; + if (!Token::findmatch(start, "%varid%", bodyTok, varid)) + return nullptr; + if (isVariableChanged(start, bodyTok, varid, /*globalvar*/ false, settings)) + return nullptr; + return bodyTok; +} + +static bool addByOne(const Token *tok, nonneg int varid) +{ + if (Token::Match(tok, "+= %any% ;") && + tok->tokAt(1)->hasKnownIntValue() && + tok->tokAt(1)->getValue(1)) { + return true; + } + if (Token::Match(tok, "= %varid% + %any% ;", varid) && + tok->tokAt(3)->hasKnownIntValue() && + tok->tokAt(3)->getValue(1)) { + return true; + } + return false; +} + +static bool accumulateBoolLiteral(const Token *tok, nonneg int varid) +{ + if (Token::Match(tok, "%assign% %bool% ;") && + tok->tokAt(1)->hasKnownIntValue()) { + return true; + } + if (Token::Match(tok, "= %varid% %oror%|%or%|&&|& %bool% ;", varid) && + tok->tokAt(3)->hasKnownIntValue()) { + return true; + } + return false; +} + +static bool accumulateBool(const Token *tok, nonneg int varid) +{ + // Missing %oreq% so we have to check both manually + if (Token::simpleMatch(tok, "&=") || Token::simpleMatch(tok, "|=")) { + return true; + } + if (Token::Match(tok, "= %varid% %oror%|%or%|&&|&", varid)) { + return true; + } + return false; +} + +static bool hasVarIds(const Token *tok, nonneg int var1, nonneg int var2) +{ + if (tok->astOperand1()->varId() == tok->astOperand2()->varId()) + return false; + if (tok->astOperand1()->varId() == var1 || tok->astOperand1()->varId() == var2) { + if (tok->astOperand2()->varId() == var1 || tok->astOperand2()->varId() == var2) { + return true; + } + } + return false; +} + +static std::string flipMinMax(const std::string &algo) +{ + if (algo == "std::max_element") + return "std::min_element"; + if (algo == "std::min_element") + return "std::max_element"; + return algo; +} + +static std::string minmaxCompare(const Token *condTok, nonneg int loopVar, nonneg int assignVar, bool invert = false) +{ + if (!Token::Match(condTok, "<|<=|>=|>")) + return "std::accumulate"; + if (!hasVarIds(condTok, loopVar, assignVar)) + return "std::accumulate"; + std::string algo = "std::max_element"; + if (Token::Match(condTok, "<|<=")) + algo = "std::min_element"; + if (condTok->astOperand1()->varId() == assignVar) + algo = flipMinMax(algo); + if (invert) + algo = flipMinMax(algo); + return algo; +} + +namespace { + struct LoopAnalyzer { + const Token* bodyTok = nullptr; + const Token* loopVar = nullptr; + const Settings* settings = nullptr; + std::set varsChanged; + + explicit LoopAnalyzer(const Token* tok, const Settings* psettings) + : bodyTok(tok->next()->link()->next()), settings(psettings) + { + const Token* splitTok = tok->next()->astOperand2(); + if (Token::simpleMatch(splitTok, ":") && splitTok->previous()->varId() != 0) { + loopVar = splitTok->previous(); + } + if (valid()) { + findChangedVariables(); + } + } + bool isLoopVarChanged() const { + return varsChanged.count(loopVar->varId()) > 0; + } + + bool isModified(const Token* tok) const + { + if (tok->variable() && tok->variable()->isConst()) + return false; + int n = 1 + (astIsPointer(tok) ? 1 : 0); + for (int i = 0; i < n; i++) { + bool inconclusive = false; + if (isVariableChangedByFunctionCall(tok, i, settings, &inconclusive)) + return true; + if (inconclusive) + return true; + if (isVariableChanged(tok, i, settings)) + return true; + } + return false; + } + + template + void findTokens(Predicate pred, F f) const + { + for (const Token* tok = bodyTok; precedes(tok, bodyTok->link()); tok = tok->next()) { + if (pred(tok)) + f(tok); + } + } + + template + const Token* findToken(Predicate pred) const + { + for (const Token* tok = bodyTok; precedes(tok, bodyTok->link()); tok = tok->next()) { + if (pred(tok)) + return tok; + } + return nullptr; + } + + bool hasGotoOrBreak() const + { + return findToken([](const Token* tok) { + return Token::Match(tok, "goto|break"); + }); + } + + bool valid() const { + return bodyTok && loopVar; + } + + std::string findAlgo() const + { + if (!valid()) + return ""; + bool loopVarChanged = isLoopVarChanged(); + if (!loopVarChanged && varsChanged.empty()) { + if (hasGotoOrBreak()) + return ""; + bool alwaysTrue = true; + bool alwaysFalse = true; + auto hasReturn = [](const Token* tok) { + return Token::simpleMatch(tok, "return"); + }; + findTokens(hasReturn, [&](const Token* tok) { + const Token* returnTok = tok->astOperand1(); + if (!returnTok || !returnTok->hasKnownIntValue() || !astIsBool(returnTok)) { + alwaysTrue = false; + alwaysFalse = false; + return; + } + (returnTok->values().front().intvalue ? alwaysTrue : alwaysFalse) &= true; + (returnTok->values().front().intvalue ? alwaysFalse : alwaysTrue) &= false; + }); + if (alwaysTrue == alwaysFalse) + return ""; + if (alwaysTrue) + return "std::any_of"; + return "std::all_of or std::none_of"; + } + return ""; + } + + bool isLocalVar(const Variable* var) const + { + if (!var) + return false; + if (var->isPointer() || var->isReference()) + return false; + if (var->declarationId() == loopVar->varId()) + return false; + const Scope* scope = var->scope(); + return scope && scope->isNestedIn(bodyTok->scope()); + } + + private: + void findChangedVariables() + { + std::set vars; + for (const Token* tok = bodyTok; precedes(tok, bodyTok->link()); tok = tok->next()) { + if (tok->varId() == 0) + continue; + if (vars.count(tok->varId()) > 0) + continue; + if (isLocalVar(tok->variable())) { + vars.insert(tok->varId()); + continue; + } + if (!isModified(tok)) + continue; + varsChanged.insert(tok->varId()); + vars.insert(tok->varId()); + } + } + }; +} // namespace + +void CheckStl::useStlAlgorithm() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("useStlAlgorithm")) + return; + + logChecker("CheckStl::useStlAlgorithm"); // style + + auto checkAssignee = [](const Token* tok) { + if (astIsBool(tok)) // std::accumulate is not a good fit for bool values, std::all/any/none_of return early + return false; + return !astIsContainer(tok); // don't warn for containers, where overloaded operators can be costly + }; + + auto isConditionWithoutSideEffects = [this](const Token* tok) -> bool { + if (!Token::simpleMatch(tok, "{") || !Token::simpleMatch(tok->previous(), ")")) + return false; + return isConstExpression(tok->previous()->link()->astOperand2(), mSettings->library); + }; + + for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { + for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { + // Parse range-based for loop + if (!Token::simpleMatch(tok, "for (")) + continue; + if (!Token::simpleMatch(tok->next()->link(), ") {")) + continue; + LoopAnalyzer a{tok, mSettings}; + std::string algoName = a.findAlgo(); + if (!algoName.empty()) { + useStlAlgorithmError(tok, algoName); + continue; + } + + const Token *bodyTok = tok->next()->link()->next(); + const Token *splitTok = tok->next()->astOperand2(); + const Token* loopVar{}; + bool isIteratorLoop = false; + if (Token::simpleMatch(splitTok, ":")) { + loopVar = splitTok->previous(); + if (loopVar->varId() == 0) + continue; + if (Token::simpleMatch(splitTok->astOperand2(), "{")) + continue; + } + else { // iterator-based loop? + const Token* initTok = getInitTok(tok); + const Token* condTok = getCondTok(tok); + const Token* stepTok = getStepTok(tok); + if (!initTok || !condTok || !stepTok) + continue; + loopVar = Token::Match(condTok, "%comp%") ? condTok->astOperand1() : nullptr; + if (!Token::Match(loopVar, "%var%") || !loopVar->valueType() || loopVar->valueType()->type != ValueType::Type::ITERATOR) + continue; + if (!Token::simpleMatch(initTok, "=") || !Token::Match(initTok->astOperand1(), "%varid%", loopVar->varId())) + continue; + if (!stepTok->isIncDecOp()) + continue; + isIteratorLoop = true; + } + + // Check for single assignment + bool useLoopVarInAssign{}, hasBreak{}; + const Token *assignTok = singleAssignInScope(bodyTok, loopVar->varId(), useLoopVarInAssign, hasBreak, mSettings); + if (assignTok) { + if (!checkAssignee(assignTok->astOperand1())) + continue; + const int assignVarId = assignTok->astOperand1()->varId(); + std::string algo; + if (assignVarId == loopVar->varId()) { + if (useLoopVarInAssign) + algo = "std::transform"; + else if (Token::Match(assignTok->next(), "%var%|%bool%|%num%|%char% ;")) + algo = "std::fill"; + else if (Token::Match(assignTok->next(), "%name% ( )")) + algo = "std::generate"; + else + algo = "std::fill or std::generate"; + } else { + if (addByOne(assignTok, assignVarId)) + algo = "std::distance"; + else if (accumulateBool(assignTok, assignVarId)) + algo = "std::any_of, std::all_of, std::none_of, or std::accumulate"; + else if (Token::Match(assignTok, "= %var% <|<=|>=|> %var% ? %var% : %var%") && hasVarIds(assignTok->tokAt(6), loopVar->varId(), assignVarId)) + algo = minmaxCompare(assignTok->tokAt(2), loopVar->varId(), assignVarId, assignTok->tokAt(5)->varId() == assignVarId); + else + algo = "std::accumulate"; + } + useStlAlgorithmError(assignTok, algo); + continue; + } + // Check for container calls + bool useLoopVarInMemCall; + const Token *memberAccessTok = singleMemberCallInScope(bodyTok, loopVar->varId(), useLoopVarInMemCall, mSettings); + if (memberAccessTok && !isIteratorLoop) { + const Token *memberCallTok = memberAccessTok->astOperand2(); + const int contVarId = memberAccessTok->astOperand1()->varId(); + if (contVarId == loopVar->varId()) + continue; + if (memberCallTok->str() == "push_back" || + memberCallTok->str() == "push_front" || + memberCallTok->str() == "emplace_back") { + std::string algo; + if (useLoopVarInMemCall) + algo = "std::copy"; + else + algo = "std::transform"; + useStlAlgorithmError(memberCallTok, algo); + } + continue; + } + + // Check for increment in loop + bool useLoopVarInIncrement; + const Token *incrementTok = singleIncrementInScope(bodyTok, loopVar->varId(), useLoopVarInIncrement); + if (incrementTok) { + std::string algo; + if (useLoopVarInIncrement) + algo = "std::transform"; + else + algo = "std::distance"; + useStlAlgorithmError(incrementTok, algo); + continue; + } + + // Check for conditionals + const Token *condBodyTok = singleConditionalInScope(bodyTok, loopVar->varId(), mSettings); + if (condBodyTok) { + // Check for single assign + assignTok = singleAssignInScope(condBodyTok, loopVar->varId(), useLoopVarInAssign, hasBreak, mSettings); + if (assignTok) { + if (!checkAssignee(assignTok->astOperand1())) + continue; + const int assignVarId = assignTok->astOperand1()->varId(); + std::string algo; + if (assignVarId == loopVar->varId()) { + if (useLoopVarInAssign) + algo = "std::transform"; + else + algo = "std::replace_if"; + } else { + if (addByOne(assignTok, assignVarId)) + algo = "std::count_if"; + else if (accumulateBoolLiteral(assignTok, assignVarId)) + algo = "std::any_of, std::all_of, std::none_of, or std::accumulate"; + else if (assignTok->str() != "=") + algo = "std::accumulate"; + else if (hasBreak && isConditionWithoutSideEffects(condBodyTok)) + algo = "std::any_of, std::all_of, std::none_of"; + else + continue; + } + useStlAlgorithmError(assignTok, algo); + continue; + } + + // Check for container call + memberAccessTok = singleMemberCallInScope(condBodyTok, loopVar->varId(), useLoopVarInMemCall, mSettings); + if (memberAccessTok) { + const Token *memberCallTok = memberAccessTok->astOperand2(); + const int contVarId = memberAccessTok->astOperand1()->varId(); + if (contVarId == loopVar->varId()) + continue; + if (memberCallTok->str() == "push_back" || + memberCallTok->str() == "push_front" || + memberCallTok->str() == "emplace_back") { + if (useLoopVarInMemCall) + useStlAlgorithmError(memberAccessTok, "std::copy_if"); + // There is no transform_if to suggest + } + continue; + } + + // Check for increment in loop + incrementTok = singleIncrementInScope(condBodyTok, loopVar->varId(), useLoopVarInIncrement); + if (incrementTok) { + std::string algo; + if (useLoopVarInIncrement) + algo = "std::transform"; + else + algo = "std::count_if"; + useStlAlgorithmError(incrementTok, algo); + continue; + } + + // Check early return + if (isEarlyExit(condBodyTok)) { + const Token *loopVar2 = Token::findmatch(condBodyTok, "%varid%", condBodyTok->link(), loopVar->varId()); + std::string algo; + if (loopVar2 || + (isIteratorLoop && loopVar->variable() && precedes(loopVar->variable()->nameToken(), tok))) // iterator declared outside the loop + algo = "std::find_if"; + else + algo = "std::any_of"; + useStlAlgorithmError(condBodyTok, algo); + continue; + } + } + } + } +} + +void CheckStl::knownEmptyContainerError(const Token *tok, const std::string& algo) +{ + const std::string var = tok ? tok->expressionString() : std::string("var"); + + std::string msg; + if (astIsIterator(tok)) { + msg = "Using " + algo + " with iterator '" + var + "' that is always empty."; + } else { + msg = "Iterating over container '" + var + "' that is always empty."; + } + + reportError(tok, Severity::style, + "knownEmptyContainer", + msg, CWE398, Certainty::normal); +} + +static bool isKnownEmptyContainer(const Token* tok) +{ + if (!tok) + return false; + return std::any_of(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& v) { + if (!v.isKnown()) + return false; + if (!v.isContainerSizeValue()) + return false; + if (v.intvalue != 0) + return false; + return true; + }); +} + +void CheckStl::knownEmptyContainer() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("knownEmptyContainer")) + return; + logChecker("CheckStl::knownEmptyContainer"); // style + for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { + for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { + + if (!Token::Match(tok, "%name% ( !!)")) + continue; + + // Parse range-based for loop + if (tok->str() == "for") { + if (!Token::simpleMatch(tok->next()->link(), ") {")) + continue; + const Token *splitTok = tok->next()->astOperand2(); + if (!Token::simpleMatch(splitTok, ":")) + continue; + const Token* contTok = splitTok->astOperand2(); + if (!isKnownEmptyContainer(contTok)) + continue; + knownEmptyContainerError(contTok, emptyString); + } else { + const std::vector args = getArguments(tok); + if (args.empty()) + continue; + + for (int argnr = 1; argnr <= args.size(); ++argnr) { + const Library::ArgumentChecks::IteratorInfo *i = mSettings->library.getArgIteratorInfo(tok, argnr); + if (!i) + continue; + const Token * const argTok = args[argnr - 1]; + if (!isKnownEmptyContainer(argTok)) + continue; + knownEmptyContainerError(argTok, tok->str()); + break; + + } + } + } + } +} + +void CheckStl::eraseIteratorOutOfBoundsError(const Token *ftok, const Token* itertok, const ValueFlow::Value* val) +{ + if (!ftok || !itertok || !val) { + reportError(ftok, Severity::error, "eraseIteratorOutOfBounds", + "Calling function 'erase()' on the iterator 'iter' which is out of bounds.", CWE628, Certainty::normal); + reportError(ftok, Severity::warning, "eraseIteratorOutOfBoundsCond", + "Either the condition 'x' is redundant or function 'erase()' is called on the iterator 'iter' which is out of bounds.", CWE628, Certainty::normal); + return; + } + const std::string& func = ftok->str(); + const std::string iter = itertok->expressionString(); + + const bool isConditional = val->isPossible(); + std::string msg; + if (isConditional) { + msg = ValueFlow::eitherTheConditionIsRedundant(val->condition) + " or function '" + func + "()' is called on the iterator '" + iter + "' which is out of bounds."; + } else { + msg = "Calling function '" + func + "()' on the iterator '" + iter + "' which is out of bounds."; + } + + const Severity severity = isConditional ? Severity::warning : Severity::error; + const std::string id = isConditional ? "eraseIteratorOutOfBoundsCond" : "eraseIteratorOutOfBounds"; + reportError(ftok, severity, + id, + msg, CWE628, Certainty::normal); +} + +static const ValueFlow::Value* getOOBIterValue(const Token* tok, const ValueFlow::Value* sizeVal) +{ + auto it = std::find_if(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& v) { + if (v.isPossible() || v.isKnown()) { + switch (v.valueType) { + case ValueFlow::Value::ValueType::ITERATOR_END: + return v.intvalue >= 0; + case ValueFlow::Value::ValueType::ITERATOR_START: + return (v.intvalue < 0) || (sizeVal && v.intvalue >= sizeVal->intvalue); + default: + break; + } + } + return false; + }); + return it != tok->values().end() ? &*it : nullptr; +} + +void CheckStl::eraseIteratorOutOfBounds() +{ + logChecker("CheckStl::eraseIteratorOutOfBounds"); + for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { + for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { + + if (!tok->valueType()) + continue; + const Library::Container* container = tok->valueType()->container; + if (!container || !astIsLHS(tok) || !Token::simpleMatch(tok->astParent(), ".")) + continue; + const Token* const ftok = tok->astParent()->astOperand2(); + const Library::Container::Action action = container->getAction(ftok->str()); + if (action != Library::Container::Action::ERASE) + continue; + const std::vector args = getArguments(ftok); + if (args.size() != 1) // TODO: check range overload + continue; + + const ValueFlow::Value* sizeVal = tok->getKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE); + if (const ValueFlow::Value* errVal = getOOBIterValue(args[0], sizeVal)) + eraseIteratorOutOfBoundsError(ftok, args[0], errVal); + } + } +} + +static bool isMutex(const Variable* var) +{ + const Token* tok = Token::typeDecl(var->nameToken()).first; + return Token::Match(tok, "std :: mutex|recursive_mutex|timed_mutex|recursive_timed_mutex|shared_mutex"); +} + +static bool isLockGuard(const Variable* var) +{ + const Token* tok = Token::typeDecl(var->nameToken()).first; + return Token::Match(tok, "std :: lock_guard|unique_lock|scoped_lock|shared_lock"); +} + +static bool isLocalMutex(const Variable* var, const Scope* scope) +{ + if (!var) + return false; + if (isLockGuard(var)) + return false; + return !var->isReference() && !var->isRValueReference() && !var->isStatic() && var->scope() == scope; +} + +void CheckStl::globalLockGuardError(const Token* tok) +{ + reportError(tok, Severity::warning, + "globalLockGuard", + "Lock guard is defined globally. Lock guards are intended to be local. A global lock guard could lead to a deadlock since it won't unlock until the end of the program.", CWE833, Certainty::normal); +} + +void CheckStl::localMutexError(const Token* tok) +{ + reportError(tok, Severity::warning, + "localMutex", + "The lock is ineffective because the mutex is locked at the same scope as the mutex itself.", CWE667, Certainty::normal); +} + +void CheckStl::checkMutexes() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + logChecker("CheckStl::checkMutexes"); // warning + for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { + std::set checkedVars; + for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) { + if (!Token::Match(tok, "%var%")) + continue; + const Variable* var = tok->variable(); + if (!var) + continue; + if (Token::Match(tok, "%var% . lock ( )")) { + if (!isMutex(var)) + continue; + if (!checkedVars.insert(var->declarationId()).second) + continue; + if (isLocalMutex(var, tok->scope())) + localMutexError(tok); + } else if (Token::Match(tok, "%var% (|{ %var% )|}|,")) { + if (!isLockGuard(var)) + continue; + const Variable* mvar = tok->tokAt(2)->variable(); + if (!mvar) + continue; + if (!checkedVars.insert(mvar->declarationId()).second) + continue; + if (var->isStatic() || var->isGlobal()) + globalLockGuardError(tok); + else if (isLocalMutex(mvar, tok->scope())) + localMutexError(tok); + } + } + } +} + diff --git a/cppcheck-2.14.0/lib/checkstl.h b/cppcheck-2.14.0/lib/checkstl.h new file mode 100644 index 00000000..e1a31622 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkstl.h @@ -0,0 +1,313 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checkstlH +#define checkstlH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "errortypes.h" +#include "tokenize.h" +#include "vfvalue.h" + +#include + +class Scope; +class Settings; +class Token; +class Variable; +class ErrorLogger; + + +/// @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()) {} + +private: + /** This constructor is used when running checks. */ + CheckStl(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger) + : Check(myName(), tokenizer, settings, errorLogger) {} + + /** run checks, the token list is not simplified */ + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + if (!tokenizer.isCPP()) { + return; + } + + CheckStl checkStl(&tokenizer, &tokenizer.getSettings(), errorLogger); + checkStl.erase(); + checkStl.if_find(); + checkStl.checkFindInsert(); + checkStl.iterators(); + checkStl.missingComparison(); + checkStl.outOfBounds(); + checkStl.outOfBoundsIndexExpression(); + checkStl.redundantCondition(); + checkStl.string_c_str(); + checkStl.uselessCalls(); + checkStl.useStlAlgorithm(); + + checkStl.stlOutOfBounds(); + checkStl.negativeIndex(); + + checkStl.invalidContainer(); + checkStl.mismatchingContainers(); + checkStl.mismatchingContainerIterator(); + checkStl.knownEmptyContainer(); + checkStl.eraseIteratorOutOfBounds(); + + checkStl.stlBoundaries(); + checkStl.checkDereferenceInvalidIterator(); + checkStl.checkDereferenceInvalidIterator2(); + checkStl.checkMutexes(); + + // Style check + checkStl.size(); + } + + /** Accessing container out of bounds using ValueFlow */ + void outOfBounds(); + + /** Accessing container out of bounds, following index expression */ + void outOfBoundsIndexExpression(); + + /** + * Finds errors like this: + * for (unsigned ii = 0; ii <= foo.size(); ++ii) + */ + void stlOutOfBounds(); + + /** + * negative index for array like containers + */ + void negativeIndex(); + + /** + * Finds errors like this: + * for (it = foo.begin(); it != bar.end(); ++it) + */ + void iterators(); + + void invalidContainer(); + + bool checkIteratorPair(const Token* tok1, const Token* tok2); + + /** + * Mismatching containers: + * std::find(foo.begin(), bar.end(), x) + */ + void mismatchingContainers(); + + void mismatchingContainerIterator(); + + /** + * Dangerous usage of erase. The iterator is invalidated by erase so + * it is bad to dereference it after the erase. + */ + void erase(); + void eraseCheckLoopVar(const Scope& scope, const Variable* var); + + /** + * bad condition.. "it < alist.end()" + */ + void stlBoundaries(); + + /** if (a.find(x)) - possibly incorrect condition */ + void if_find(); + + void checkFindInsert(); + + /** + * Suggest using empty() instead of checking size() against zero for containers. + * Item 4 from Scott Meyers book "Effective STL". + */ + void size(); + + /** + * Check for redundant condition 'if (ints.find(1) != ints.end()) ints.remove(123);' + * */ + void redundantCondition(); + + /** + * @brief Missing inner comparison, when incrementing iterator inside loop + * Dangers: + * - may increment iterator beyond end + * - may unintentionally skip elements in list/set etc + */ + void missingComparison(); + + /** Check for common mistakes when using the function string::c_str() */ + void string_c_str(); + + /** @brief %Check calls that using them is useless */ + void uselessCalls(); + + /** @brief %Check for dereferencing an iterator that is invalid */ + void checkDereferenceInvalidIterator(); + void checkDereferenceInvalidIterator2(); + + /** + * Dereferencing an erased iterator + * @param erased token where the erase occurs + * @param deref token where the dereference occurs + * @param itername iterator name + * @param inconclusive inconclusive flag + */ + void dereferenceErasedError(const Token* erased, const Token* deref, const std::string& itername, bool inconclusive); + + /** @brief Look for loops that can replaced with std algorithms */ + void useStlAlgorithm(); + + void knownEmptyContainer(); + + void eraseIteratorOutOfBounds(); + + void checkMutexes(); + + bool isContainerSize(const Token *containerToken, const Token *expr) const; + bool isContainerSizeGE(const Token * containerToken, const Token *expr) const; + + void missingComparisonError(const Token* incrementToken1, const Token* incrementToken2); + void string_c_strThrowError(const Token* tok); + void string_c_strError(const Token* tok); + void string_c_strReturn(const Token* tok); + void string_c_strParam(const Token* tok, nonneg int number, const std::string& argtype = "std::string"); + void string_c_strConstructor(const Token* tok, const std::string& argtype = "std::string"); + void string_c_strAssignment(const Token* tok, const std::string& argtype = "std::string"); + void string_c_strConcat(const Token* tok); + void string_c_strStream(const Token* tok); + + void outOfBoundsError(const Token *tok, const std::string &containerName, const ValueFlow::Value *containerSize, const std::string &index, const ValueFlow::Value *indexValue); + void outOfBoundsIndexExpressionError(const Token *tok, const Token *index); + void stlOutOfBoundsError(const Token* tok, const std::string& num, const std::string& var, bool at); + void negativeIndexError(const Token* tok, const ValueFlow::Value& index); + void invalidIteratorError(const Token* tok, const std::string& iteratorName); + void iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2); + void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2); + void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName); + void mismatchingContainerIteratorError(const Token* containerTok, const Token* iterTok, const Token* containerTok2); + void mismatchingContainersError(const Token* tok1, const Token* tok2); + void mismatchingContainerExpressionError(const Token *tok1, const Token *tok2); + void sameIteratorExpressionError(const Token *tok); + void stlBoundariesError(const Token* tok); + void if_findError(const Token* tok, bool str); + void checkFindInsertError(const Token *tok); + void sizeError(const Token* tok); + void redundantIfRemoveError(const Token* tok); + void invalidContainerLoopError(const Token* tok, const Token* loopTok, ErrorPath errorPath); + void invalidContainerError(const Token *tok, const Token * contTok, const ValueFlow::Value *val, ErrorPath errorPath); + void invalidContainerReferenceError(const Token* tok, const Token* contTok, ErrorPath errorPath); + + void uselessCallsReturnValueError(const Token* tok, const std::string& varname, const std::string& function); + void uselessCallsSwapError(const Token* tok, const std::string& varname); + enum class SubstrErrorType { EMPTY, COPY, PREFIX, PREFIX_CONCAT }; + void uselessCallsSubstrError(const Token* tok, SubstrErrorType type); + void uselessCallsEmptyError(const Token* tok); + void uselessCallsRemoveError(const Token* tok, const std::string& function); + void uselessCallsConstructorError(const Token* tok); + + void dereferenceInvalidIteratorError(const Token* deref, const std::string& iterName); + void dereferenceInvalidIteratorError(const Token* tok, const ValueFlow::Value *value, bool inconclusive); + + void useStlAlgorithmError(const Token *tok, const std::string &algoName); + + void knownEmptyContainerError(const Token *tok, const std::string& algo); + + void eraseIteratorOutOfBoundsError(const Token* ftok, const Token* itertok, const ValueFlow::Value* val = nullptr); + + void globalLockGuardError(const Token *tok); + void localMutexError(const Token *tok); + + void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override { + CheckStl c(nullptr, settings, errorLogger); + c.outOfBoundsError(nullptr, "container", nullptr, "x", nullptr); + c.invalidIteratorError(nullptr, "iterator"); + c.iteratorsError(nullptr, "container1", "container2"); + c.iteratorsError(nullptr, nullptr, "container0", "container1"); + c.iteratorsError(nullptr, nullptr, "container"); + c.invalidContainerLoopError(nullptr, nullptr, ErrorPath{}); + c.invalidContainerError(nullptr, nullptr, nullptr, ErrorPath{}); + c.mismatchingContainerIteratorError(nullptr, nullptr, nullptr); + c.mismatchingContainersError(nullptr, nullptr); + c.mismatchingContainerExpressionError(nullptr, nullptr); + c.sameIteratorExpressionError(nullptr); + c.dereferenceErasedError(nullptr, nullptr, "iter", false); + c.stlOutOfBoundsError(nullptr, "i", "foo", false); + c.negativeIndexError(nullptr, ValueFlow::Value(-1)); + c.stlBoundariesError(nullptr); + c.if_findError(nullptr, false); + c.if_findError(nullptr, true); + c.checkFindInsertError(nullptr); + c.string_c_strError(nullptr); + c.string_c_strReturn(nullptr); + c.string_c_strParam(nullptr, 0); + c.string_c_strThrowError(nullptr); + c.sizeError(nullptr); + c.missingComparisonError(nullptr, nullptr); + c.redundantIfRemoveError(nullptr); + c.uselessCallsReturnValueError(nullptr, "str", "find"); + c.uselessCallsSwapError(nullptr, "str"); + c.uselessCallsSubstrError(nullptr, SubstrErrorType::COPY); + c.uselessCallsEmptyError(nullptr); + c.uselessCallsRemoveError(nullptr, "remove"); + c.dereferenceInvalidIteratorError(nullptr, "i"); + c.eraseIteratorOutOfBoundsError(nullptr, nullptr); + c.useStlAlgorithmError(nullptr, emptyString); + c.knownEmptyContainerError(nullptr, emptyString); + c.globalLockGuardError(nullptr); + c.localMutexError(nullptr); + } + + static std::string myName() { + return "STL usage"; + } + + std::string classInfo() const override { + return "Check for invalid usage of STL:\n" + "- out of bounds errors\n" + "- misuse of iterators when iterating through a container\n" + "- mismatching containers in calls\n" + "- same iterators in calls\n" + "- dereferencing an erased iterator\n" + "- for vectors: using iterator/pointer after push_back has been used\n" + "- optimisation: use empty() instead of size() to guarantee fast code\n" + "- suspicious condition when using find\n" + "- unnecessary searching in associative containers\n" + "- redundant condition\n" + "- common mistakes when using string::c_str()\n" + "- useless calls of string and STL functions\n" + "- dereferencing an invalid iterator\n" + "- erasing an iterator that is out of bounds\n" + "- reading from empty STL container\n" + "- iterating over an empty STL container\n" + "- consider using an STL algorithm instead of raw loop\n" + "- incorrect locking with mutex\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkstlH diff --git a/cppcheck-2.14.0/lib/checkstring.cpp b/cppcheck-2.14.0/lib/checkstring.cpp new file mode 100644 index 00000000..ed8f43d9 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkstring.cpp @@ -0,0 +1,473 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#include "checkstring.h" + +#include "astutils.h" +#include "errortypes.h" +#include "mathlib.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "utils.h" + +#include +#include +#include +#include + +//--------------------------------------------------------------------------- + +// Register this check class (by creating a static instance of it) +namespace { + CheckString instance; +} + +// CWE ids used: +static const CWE CWE570(570U); // Expression is Always False +static const CWE CWE571(571U); // Expression is Always True +static const CWE CWE595(595U); // Comparison of Object References Instead of Object Contents +static const CWE CWE628(628U); // Function Call with Incorrectly Specified Arguments +static const CWE CWE665(665U); // Improper Initialization +static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior + +//--------------------------------------------------------------------------- +// Writing string literal is UB +//--------------------------------------------------------------------------- +void CheckString::stringLiteralWrite() +{ + logChecker("CheckString::stringLiteralWrite"); + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!tok->variable() || !tok->variable()->isPointer()) + continue; + const Token *str = tok->getValueTokenMinStrSize(*mSettings); + if (!str) + continue; + if (Token::Match(tok, "%var% [") && Token::simpleMatch(tok->linkAt(1), "] =")) + stringLiteralWriteError(tok, str); + else if (Token::Match(tok->previous(), "* %var% =")) + stringLiteralWriteError(tok, str); + } + } +} + +void CheckString::stringLiteralWriteError(const Token *tok, const Token *strValue) +{ + std::list callstack{ tok }; + if (strValue) + callstack.push_back(strValue); + + std::string errmsg("Modifying string literal"); + if (strValue) { + std::string s = strValue->str(); + // 20 is an arbitrary value, the max string length shown in a warning message + if (s.size() > 20U) + s.replace(17, std::string::npos, "..\""); + errmsg += " " + s; + } + errmsg += " directly or indirectly is undefined behaviour."; + + reportError(callstack, Severity::error, "stringLiteralWrite", errmsg, CWE758, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Check for string comparison involving two static strings. +// if(strcmp("00FF00","00FF00")==0) // <- statement is always true +//--------------------------------------------------------------------------- +void CheckString::checkAlwaysTrueOrFalseStringCompare() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckString::checkAlwaysTrueOrFalseStringCompare"); // warning + + for (const Token* tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (tok->isName() && tok->strAt(1) == "(" && Token::Match(tok, "memcmp|strncmp|strcmp|stricmp|strverscmp|bcmp|strcmpi|strcasecmp|strncasecmp|strncasecmp_l|strcasecmp_l|wcsncasecmp|wcscasecmp|wmemcmp|wcscmp|wcscasecmp_l|wcsncasecmp_l|wcsncmp|_mbscmp|_mbscmp_l|_memicmp|_memicmp_l|_stricmp|_wcsicmp|_mbsicmp|_stricmp_l|_wcsicmp_l|_mbsicmp_l")) { + if (Token::Match(tok->tokAt(2), "%str% , %str% ,|)")) { + const std::string &str1 = tok->strAt(2); + const std::string &str2 = tok->strAt(4); + if (!tok->isExpandedMacro() && !tok->tokAt(2)->isExpandedMacro() && !tok->tokAt(4)->isExpandedMacro()) + alwaysTrueFalseStringCompareError(tok, str1, str2); + tok = tok->tokAt(5); + } else if (Token::Match(tok->tokAt(2), "%name% , %name% ,|)")) { + const std::string &str1 = tok->strAt(2); + const std::string &str2 = tok->strAt(4); + if (str1 == str2) + alwaysTrueStringVariableCompareError(tok, str1, str2); + tok = tok->tokAt(5); + } else if (Token::Match(tok->tokAt(2), "%name% . c_str ( ) , %name% . c_str ( ) ,|)")) { + const std::string &str1 = tok->strAt(2); + const std::string &str2 = tok->strAt(8); + if (str1 == str2) + alwaysTrueStringVariableCompareError(tok, str1, str2); + tok = tok->tokAt(13); + } + } else if (tok->isName() && Token::Match(tok, "QString :: compare ( %str% , %str% )")) { + const std::string &str1 = tok->strAt(4); + const std::string &str2 = tok->strAt(6); + alwaysTrueFalseStringCompareError(tok, str1, str2); + tok = tok->tokAt(7); + } else if (Token::Match(tok, "!!+ %str% ==|!= %str% !!+")) { + const std::string &str1 = tok->strAt(1); + const std::string &str2 = tok->strAt(3); + alwaysTrueFalseStringCompareError(tok, str1, str2); + tok = tok->tokAt(5); + } + if (!tok) + break; + } +} + +void CheckString::alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2) +{ + constexpr std::size_t stringLen = 10; + const std::string string1 = (str1.size() < stringLen) ? str1 : (str1.substr(0, stringLen-2) + ".."); + const std::string string2 = (str2.size() < stringLen) ? str2 : (str2.substr(0, stringLen-2) + ".."); + + reportError(tok, Severity::warning, "staticStringCompare", + "Unnecessary comparison of static strings.\n" + "The compared strings, '" + string1 + "' and '" + string2 + "', are always " + (str1==str2?"identical":"unequal") + ". " + "Therefore the comparison is unnecessary and looks suspicious.", (str1==str2)?CWE571:CWE570, Certainty::normal); +} + +void CheckString::alwaysTrueStringVariableCompareError(const Token *tok, const std::string& str1, const std::string& str2) +{ + reportError(tok, Severity::warning, "stringCompare", + "Comparison of identical string variables.\n" + "The compared strings, '" + str1 + "' and '" + str2 + "', are identical. " + "This could be a logic bug.", CWE571, Certainty::normal); +} + + +//----------------------------------------------------------------------------- +// Detect "str == '\0'" where "*str == '\0'" is correct. +// Comparing char* with each other instead of using strcmp() +//----------------------------------------------------------------------------- +void CheckString::checkSuspiciousStringCompare() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckString::checkSuspiciousStringCompare"); // warning + + const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!tok->isComparisonOp()) + continue; + + const Token* varTok = tok->astOperand1(); + const Token* litTok = tok->astOperand2(); + if (!varTok || !litTok) // <- failed to create AST for comparison + continue; + if (Token::Match(varTok, "%char%|%num%|%str%")) + std::swap(varTok, litTok); + else if (!Token::Match(litTok, "%char%|%num%|%str%")) + continue; + + if (varTok->isLiteral()) + continue; + + const ValueType* varType = varTok->valueType(); + if (varTok->isCpp() && (!varType || !varType->isIntegral())) + continue; + + if (litTok->tokType() == Token::eString) { + if (varTok->isC() || (varType && varType->pointer)) + suspiciousStringCompareError(tok, varTok->expressionString(), litTok->isLong()); + } else if (litTok->tokType() == Token::eChar && varType && varType->pointer) { + suspiciousStringCompareError_char(tok, varTok->expressionString()); + } + } + } +} + +void CheckString::suspiciousStringCompareError(const Token* tok, const std::string& var, bool isLong) +{ + const std::string cmpFunc = isLong ? "wcscmp" : "strcmp"; + reportError(tok, Severity::warning, "literalWithCharPtrCompare", + "$symbol:" + var + "\nString literal compared with variable '$symbol'. Did you intend to use " + cmpFunc + "() instead?", CWE595, Certainty::normal); +} + +void CheckString::suspiciousStringCompareError_char(const Token* tok, const std::string& var) +{ + reportError(tok, Severity::warning, "charLiteralWithCharPtrCompare", + "$symbol:" + var + "\nChar literal compared with pointer '$symbol'. Did you intend to dereference it?", CWE595, Certainty::normal); +} + + +//--------------------------------------------------------------------------- +// Adding C-string and char with operator+ +//--------------------------------------------------------------------------- + +static bool isChar(const Variable* var) +{ + return (var && !var->isPointer() && !var->isArray() && (var->typeStartToken()->str() == "char" || var->typeStartToken()->str() == "wchar_t")); +} + +void CheckString::strPlusChar() +{ + logChecker("CheckString::strPlusChar"); + const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (tok->str() == "+") { + if (tok->astOperand1() && (tok->astOperand1()->tokType() == Token::eString)) { // string literal... + if (tok->astOperand2() && (tok->astOperand2()->tokType() == Token::eChar || isChar(tok->astOperand2()->variable()))) // added to char variable or char constant + strPlusCharError(tok); + } + } + } + } +} + +void CheckString::strPlusCharError(const Token *tok) +{ + std::string charType = "char"; + if (tok && tok->astOperand2() && tok->astOperand2()->variable()) + charType = tok->astOperand2()->variable()->typeStartToken()->str(); + else if (tok && tok->astOperand2() && tok->astOperand2()->tokType() == Token::eChar && tok->astOperand2()->isLong()) + charType = "wchar_t"; + reportError(tok, Severity::error, "strPlusChar", "Unusual pointer arithmetic. A value of type '" + charType +"' is added to a string literal.", CWE665, Certainty::normal); +} + +static bool isMacroUsage(const Token* tok) +{ + if (const Token* parent = tok->astParent()) { + while (parent && parent->isCast()) + parent = parent->astParent(); + if (!parent) + return false; + if (parent->isExpandedMacro()) + return true; + if (parent->isUnaryOp("!") || parent->isComparisonOp()) { + int argn{}; + const Token* ftok = getTokenArgumentFunction(parent, argn); + if (ftok && !ftok->function()) + return true; + } + } + return false; +} + +//--------------------------------------------------------------------------- +// Implicit casts of string literals to bool +// Comparing string literal with strlen() with wrong length +//--------------------------------------------------------------------------- +void CheckString::checkIncorrectStringCompare() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckString::checkIncorrectStringCompare"); // warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + // skip "assert(str && ..)" and "assert(.. && str)" + if ((endsWith(tok->str(), "assert") || endsWith(tok->str(), "ASSERT")) && + Token::Match(tok, "%name% (") && + (Token::Match(tok->tokAt(2), "%str% &&") || Token::Match(tok->next()->link()->tokAt(-2), "&& %str% )"))) + tok = tok->next()->link(); + + if (Token::simpleMatch(tok, ". substr (") && Token::Match(tok->tokAt(3)->nextArgument(), "%num% )")) { + const MathLib::biguint clen = MathLib::toBigUNumber(tok->linkAt(2)->strAt(-1)); + const Token* begin = tok->previous(); + for (;;) { // Find start of statement + while (begin->link() && Token::Match(begin, "]|)|>")) + begin = begin->link()->previous(); + if (Token::Match(begin->previous(), ".|::")) + begin = begin->tokAt(-2); + else + break; + } + begin = begin->previous(); + const Token* end = tok->linkAt(2)->next(); + if (Token::Match(begin->previous(), "%str% ==|!=") && begin->strAt(-2) != "+") { + const std::size_t slen = Token::getStrLength(begin->previous()); + if (clen != slen) { + incorrectStringCompareError(tok->next(), "substr", begin->strAt(-1)); + } + } else if (Token::Match(end, "==|!= %str% !!+")) { + const std::size_t slen = Token::getStrLength(end->next()); + if (clen != slen) { + incorrectStringCompareError(tok->next(), "substr", end->strAt(1)); + } + } + } else if (Token::Match(tok, "%str%|%char%") && + isUsedAsBool(tok) && + !isMacroUsage(tok)) + incorrectStringBooleanError(tok, tok->str()); + } + } +} + +void CheckString::incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string) +{ + reportError(tok, Severity::warning, "incorrectStringCompare", "$symbol:" + func + "\nString literal " + string + " doesn't match length argument for $symbol().", CWE570, Certainty::normal); +} + +void CheckString::incorrectStringBooleanError(const Token *tok, const std::string& string) +{ + const bool charLiteral = isCharLiteral(string); + const std::string literalType = charLiteral ? "char" : "string"; + const std::string result = bool_to_string(getCharLiteral(string) != "\\0"); + reportError(tok, + Severity::warning, + charLiteral ? "incorrectCharBooleanError" : "incorrectStringBooleanError", + "Conversion of " + literalType + " literal " + string + " to bool always evaluates to " + result + '.', CWE571, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// always true: strcmp(str,"a")==0 || strcmp(str,"b") +// TODO: Library configuration for string comparison functions +//--------------------------------------------------------------------------- +void CheckString::overlappingStrcmp() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckString::overlappingStrcmp"); // warning + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (tok->str() != "||") + continue; + std::list equals0; + std::list notEquals0; + visitAstNodes(tok, [&](const Token * t) { + if (!t) + return ChildrenToVisit::none; + if (t->str() == "||") { + return ChildrenToVisit::op1_and_op2; + } + if (t->str() == "==") { + if (Token::simpleMatch(t->astOperand1(), "(") && Token::simpleMatch(t->astOperand2(), "0")) + equals0.push_back(t->astOperand1()); + else if (Token::simpleMatch(t->astOperand2(), "(") && Token::simpleMatch(t->astOperand1(), "0")) + equals0.push_back(t->astOperand2()); + return ChildrenToVisit::none; + } + if (t->str() == "!=") { + if (Token::simpleMatch(t->astOperand1(), "(") && Token::simpleMatch(t->astOperand2(), "0")) + notEquals0.push_back(t->astOperand1()); + else if (Token::simpleMatch(t->astOperand2(), "(") && Token::simpleMatch(t->astOperand1(), "0")) + notEquals0.push_back(t->astOperand2()); + return ChildrenToVisit::none; + } + if (t->str() == "!" && Token::simpleMatch(t->astOperand1(), "(")) + equals0.push_back(t->astOperand1()); + else if (t->str() == "(") + notEquals0.push_back(t); + return ChildrenToVisit::none; + }); + + for (const Token *eq0 : equals0) { + for (const Token * ne0 : notEquals0) { + if (!Token::Match(eq0->previous(), "strcmp|wcscmp (")) + continue; + if (!Token::Match(ne0->previous(), "strcmp|wcscmp (")) + continue; + const std::vector args1 = getArguments(eq0->previous()); + const std::vector args2 = getArguments(ne0->previous()); + if (args1.size() != 2 || args2.size() != 2) + continue; + if (args1[1]->isLiteral() && + args2[1]->isLiteral() && + args1[1]->str() != args2[1]->str() && + isSameExpression(true, args1[0], args2[0], mSettings->library, true, false)) + overlappingStrcmpError(eq0, ne0); + } + } + } + } +} + +void CheckString::overlappingStrcmpError(const Token *eq0, const Token *ne0) +{ + std::string eq0Expr(eq0 ? eq0->expressionString() : std::string("strcmp(x,\"abc\")")); + if (eq0 && eq0->astParent()->str() == "!") + eq0Expr = "!" + eq0Expr; + else + eq0Expr += " == 0"; + + const std::string ne0Expr = (ne0 ? ne0->expressionString() : std::string("strcmp(x,\"def\")")) + " != 0"; + + reportError(ne0, Severity::warning, "overlappingStrcmp", "The expression '" + ne0Expr + "' is suspicious. It overlaps '" + eq0Expr + "'."); +} + +//--------------------------------------------------------------------------- +// Overlapping source and destination passed to sprintf(). +// TODO: Library configuration for overlapping arguments +//--------------------------------------------------------------------------- +void CheckString::sprintfOverlappingData() +{ + logChecker("CheckString::sprintfOverlappingData"); + + const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::Match(tok, "sprintf|snprintf|swprintf (")) + continue; + + const std::vector args = getArguments(tok); + + const int formatString = Token::simpleMatch(tok, "sprintf") ? 1 : 2; + for (unsigned int argnr = formatString + 1; argnr < args.size(); ++argnr) { + const Token *dest = args[0]; + while (dest->isCast()) + dest = dest->astOperand2() ? dest->astOperand2() : dest->astOperand1(); + const Token *arg = args[argnr]; + if (!arg->valueType() || arg->valueType()->pointer != 1) + continue; + while (arg->isCast()) + arg = arg->astOperand2() ? arg->astOperand2() : arg->astOperand1(); + + const bool same = isSameExpression(false, + dest, + arg, + mSettings->library, + true, + false); + if (same) { + sprintfOverlappingDataError(tok, args[argnr], arg->expressionString()); + } + } + } + } +} + +void CheckString::sprintfOverlappingDataError(const Token *funcTok, const Token *tok, const std::string &varname) +{ + const std::string func = funcTok ? funcTok->str() : "s[n]printf"; + + reportError(tok, Severity::error, "sprintfOverlappingData", + "$symbol:" + varname + "\n" + "Undefined behavior: Variable '$symbol' is used as parameter and destination in " + func + "().\n" + + "The variable '$symbol' is used both as a parameter and as destination in " + + func + "(). The origin and destination buffers overlap. Quote from glibc (C-library) " + "documentation (http://www.gnu.org/software/libc/manual/html_mono/libc.html#Formatted-Output-Functions): " + "\"If copying takes place between objects that overlap as a result of a call " + "to sprintf() or snprintf(), the results are undefined.\"", CWE628, Certainty::normal); +} diff --git a/cppcheck-2.14.0/lib/checkstring.h b/cppcheck-2.14.0/lib/checkstring.h new file mode 100644 index 00000000..5a2b6050 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkstring.h @@ -0,0 +1,129 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checkstringH +#define checkstringH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "tokenize.h" + +#include + +class ErrorLogger; +class Settings; +class Token; + +/// @addtogroup Checks +/// @{ + + +/** @brief Detect misusage of C-style strings and related standard functions */ + +class CPPCHECKLIB CheckString : public Check { +public: + /** @brief This constructor is used when registering the CheckClass */ + CheckString() : Check(myName()) {} + +private: + /** @brief This constructor is used when running checks. */ + CheckString(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger) {} + + /** @brief Run checks against the normal token list */ + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + CheckString checkString(&tokenizer, &tokenizer.getSettings(), errorLogger); + + // Checks + checkString.strPlusChar(); + checkString.checkSuspiciousStringCompare(); + checkString.stringLiteralWrite(); + checkString.overlappingStrcmp(); + checkString.checkIncorrectStringCompare(); + checkString.sprintfOverlappingData(); + checkString.checkAlwaysTrueOrFalseStringCompare(); + } + + /** @brief undefined behaviour, writing string literal */ + void stringLiteralWrite(); + + /** @brief str plus char (unusual pointer arithmetic) */ + void strPlusChar(); + + /** @brief %Check for using bad usage of strncmp and substr */ + void checkIncorrectStringCompare(); + + /** @brief %Check for comparison of a string literal with a char* variable */ + void checkSuspiciousStringCompare(); + + /** @brief %Check for suspicious code that compares string literals for equality */ + void checkAlwaysTrueOrFalseStringCompare(); + + /** @brief %Check for overlapping strcmp() */ + void overlappingStrcmp(); + + /** @brief %Check for overlapping source and destination passed to sprintf() */ + void sprintfOverlappingData(); + + void stringLiteralWriteError(const Token *tok, const Token *strValue); + void sprintfOverlappingDataError(const Token *funcTok, const Token *tok, const std::string &varname); + void strPlusCharError(const Token *tok); + void incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string); + void incorrectStringBooleanError(const Token *tok, const std::string& string); + void alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2); + void alwaysTrueStringVariableCompareError(const Token *tok, const std::string& str1, const std::string& str2); + void suspiciousStringCompareError(const Token* tok, const std::string& var, bool isLong); + void suspiciousStringCompareError_char(const Token* tok, const std::string& var); + void overlappingStrcmpError(const Token* eq0, const Token *ne0); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckString c(nullptr, settings, errorLogger); + c.stringLiteralWriteError(nullptr, nullptr); + c.sprintfOverlappingDataError(nullptr, nullptr, "varname"); + c.strPlusCharError(nullptr); + c.incorrectStringCompareError(nullptr, "substr", "\"Hello World\""); + c.suspiciousStringCompareError(nullptr, "foo", false); + c.suspiciousStringCompareError_char(nullptr, "foo"); + c.incorrectStringBooleanError(nullptr, "\"Hello World\""); + c.incorrectStringBooleanError(nullptr, "\'x\'"); + c.alwaysTrueFalseStringCompareError(nullptr, "str1", "str2"); + c.alwaysTrueStringVariableCompareError(nullptr, "varname1", "varname2"); + c.overlappingStrcmpError(nullptr, nullptr); + } + + static std::string myName() { + return "String"; + } + + std::string classInfo() const override { + return "Detect misusage of C-style strings:\n" + "- overlapping buffers passed to sprintf as source and destination\n" + "- incorrect length arguments for 'substr' and 'strncmp'\n" + "- suspicious condition (runtime comparison of string literals)\n" + "- suspicious condition (string/char literals as boolean)\n" + "- suspicious comparison of a string literal with a char\\* variable\n" + "- suspicious comparison of '\\0' with a char\\* variable\n" + "- overlapping strcmp() expression\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkstringH diff --git a/cppcheck-2.14.0/lib/checktype.cpp b/cppcheck-2.14.0/lib/checktype.cpp new file mode 100644 index 00000000..38df8896 --- /dev/null +++ b/cppcheck-2.14.0/lib/checktype.cpp @@ -0,0 +1,510 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#include "checktype.h" + +#include "errortypes.h" +#include "mathlib.h" +#include "platform.h" +#include "settings.h" +#include "standards.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "valueflow.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +//--------------------------------------------------------------------------- + +// Register this check class (by creating a static instance of it) +namespace { + CheckType instance; +} + +//--------------------------------------------------------------------------- +// Checking for shift by too many bits +//--------------------------------------------------------------------------- +// + +// CWE ids used: +static const CWE CWE195(195U); // Signed to Unsigned Conversion Error +static const CWE CWE197(197U); // Numeric Truncation Error +static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior +static const CWE CWE190(190U); // Integer Overflow or Wraparound + + +void CheckType::checkTooBigBitwiseShift() +{ + // unknown sizeof(int) => can't run this checker + if (mSettings->platform.type == Platform::Type::Unspecified) + return; + + logChecker("CheckType::checkTooBigBitwiseShift"); // platform + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + // C++ and macro: OUT(x<isCpp() && Token::Match(tok, "[;{}] %name% (") && Token::simpleMatch(tok->linkAt(2), ") ;") && tok->next()->isUpperCaseName() && !tok->next()->function()) + tok = tok->linkAt(2); + + if (!tok->astOperand1() || !tok->astOperand2()) + continue; + + if (!Token::Match(tok, "<<|>>|<<=|>>=")) + continue; + + // get number of bits of lhs + const ValueType * const lhstype = tok->astOperand1()->valueType(); + if (!lhstype || !lhstype->isIntegral() || lhstype->pointer >= 1) + continue; + // C11 Standard, section 6.5.7 Bitwise shift operators, states: + // The integer promotions are performed on each of the operands. + // The type of the result is that of the promoted left operand. + int lhsbits; + if ((lhstype->type == ValueType::Type::CHAR) || + (lhstype->type == ValueType::Type::SHORT) || + (lhstype->type == ValueType::Type::WCHAR_T) || + (lhstype->type == ValueType::Type::BOOL) || + (lhstype->type == ValueType::Type::INT)) + lhsbits = mSettings->platform.int_bit; + else if (lhstype->type == ValueType::Type::LONG) + lhsbits = mSettings->platform.long_bit; + else if (lhstype->type == ValueType::Type::LONGLONG) + lhsbits = mSettings->platform.long_long_bit; + else + continue; + + // Get biggest rhs value. preferably a value which doesn't have 'condition'. + const ValueFlow::Value * value = tok->astOperand2()->getValueGE(lhsbits, mSettings); + if (value && mSettings->isEnabled(value, false)) + tooBigBitwiseShiftError(tok, lhsbits, *value); + else if (lhstype->sign == ValueType::Sign::SIGNED) { + value = tok->astOperand2()->getValueGE(lhsbits-1, mSettings); + if (value && mSettings->isEnabled(value, false)) + tooBigSignedBitwiseShiftError(tok, lhsbits, *value); + } + } +} + +void CheckType::tooBigBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits) +{ + constexpr char id[] = "shiftTooManyBits"; + + if (!tok) { + reportError(tok, Severity::error, id, "Shifting 32-bit value by 40 bits is undefined behaviour", CWE758, Certainty::normal); + return; + } + + const ErrorPath errorPath = getErrorPath(tok, &rhsbits, "Shift"); + + std::ostringstream errmsg; + errmsg << "Shifting " << lhsbits << "-bit value by " << rhsbits.intvalue << " bits is undefined behaviour"; + if (rhsbits.condition) + errmsg << ". See condition at line " << rhsbits.condition->linenr() << "."; + + reportError(errorPath, rhsbits.errorSeverity() ? Severity::error : Severity::warning, id, errmsg.str(), CWE758, rhsbits.isInconclusive() ? Certainty::inconclusive : Certainty::normal); +} + +void CheckType::tooBigSignedBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits) +{ + constexpr char id[] = "shiftTooManyBitsSigned"; + + const bool cpp14 = ((tok && tok->isCpp()) || (mTokenizer && mTokenizer->isCPP())) && (mSettings->standards.cpp >= Standards::CPP14); + + std::string behaviour = "undefined"; + if (cpp14) + behaviour = "implementation-defined"; + if (!tok) { + reportError(tok, Severity::error, id, "Shifting signed 32-bit value by 31 bits is " + behaviour + " behaviour", CWE758, Certainty::normal); + return; + } + + + Severity severity = rhsbits.errorSeverity() ? Severity::error : Severity::warning; + if (cpp14) + severity = Severity::portability; + + if ((severity == Severity::portability) && !mSettings->severity.isEnabled(Severity::portability)) + return; + const ErrorPath errorPath = getErrorPath(tok, &rhsbits, "Shift"); + + std::ostringstream errmsg; + errmsg << "Shifting signed " << lhsbits << "-bit value by " << rhsbits.intvalue << " bits is " + behaviour + " behaviour"; + if (rhsbits.condition) + errmsg << ". See condition at line " << rhsbits.condition->linenr() << "."; + + reportError(errorPath, severity, id, errmsg.str(), CWE758, rhsbits.isInconclusive() ? Certainty::inconclusive : Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Checking for integer overflow +//--------------------------------------------------------------------------- + +void CheckType::checkIntegerOverflow() +{ + // unknown sizeof(int) => can't run this checker + if (mSettings->platform.type == Platform::Type::Unspecified || mSettings->platform.int_bit >= MathLib::bigint_bits) + return; + + logChecker("CheckType::checkIntegerOverflow"); // platform + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (!tok->isArithmeticalOp()) + continue; + + // is result signed integer? + const ValueType *vt = tok->valueType(); + if (!vt || !vt->isIntegral() || vt->sign != ValueType::Sign::SIGNED) + continue; + + unsigned int bits; + if (vt->type == ValueType::Type::INT) + bits = mSettings->platform.int_bit; + else if (vt->type == ValueType::Type::LONG) + bits = mSettings->platform.long_bit; + else if (vt->type == ValueType::Type::LONGLONG) + bits = mSettings->platform.long_long_bit; + else + continue; + + if (bits >= MathLib::bigint_bits) + continue; + + // max value according to platform settings. + const MathLib::bigint maxvalue = (((MathLib::biguint)1) << (bits - 1)) - 1; + + // is there a overflow result value + const ValueFlow::Value *value = tok->getValueGE(maxvalue + 1, mSettings); + if (!value) + value = tok->getValueLE(-maxvalue - 2, mSettings); + if (!value || !mSettings->isEnabled(value,false)) + continue; + + // For left shift, it's common practice to shift into the sign bit + if (tok->str() == "<<" && value->intvalue > 0 && value->intvalue < (((MathLib::bigint)1) << bits)) + continue; + + integerOverflowError(tok, *value); + } +} + +void CheckType::integerOverflowError(const Token *tok, const ValueFlow::Value &value) +{ + const std::string expr(tok ? tok->expressionString() : ""); + + std::string msg; + if (value.condition) + msg = ValueFlow::eitherTheConditionIsRedundant(value.condition) + + " or there is signed integer overflow for expression '" + expr + "'."; + else + msg = "Signed integer overflow for expression '" + expr + "'."; + + if (value.safe) + msg = "Safe checks: " + msg; + + reportError(getErrorPath(tok, &value, "Integer overflow"), + value.errorSeverity() ? Severity::error : Severity::warning, + getMessageId(value, "integerOverflow").c_str(), + msg, + CWE190, + value.isInconclusive() ? Certainty::inconclusive : Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Checking for sign conversion when operand can be negative +//--------------------------------------------------------------------------- + +void CheckType::checkSignConversion() +{ + if (!mSettings->severity.isEnabled(Severity::warning)) + return; + + logChecker("CheckType::checkSignConversion"); // warning + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (!tok->isArithmeticalOp() || Token::Match(tok,"+|-")) + continue; + + // Is result unsigned? + if (!(tok->valueType() && tok->valueType()->sign == ValueType::Sign::UNSIGNED)) + continue; + + // Check if an operand can be negative.. + const Token * astOperands[] = { tok->astOperand1(), tok->astOperand2() }; + for (const Token * tok1 : astOperands) { + if (!tok1) + continue; + const ValueFlow::Value* negativeValue = + ValueFlow::findValue(tok1->values(), mSettings, [&](const ValueFlow::Value& v) { + return !v.isImpossible() && v.isIntValue() && (v.intvalue <= -1 || v.wideintvalue <= -1); + }); + if (!negativeValue) + continue; + if (tok1->valueType() && tok1->valueType()->sign != ValueType::Sign::UNSIGNED) + signConversionError(tok1, negativeValue, tok1->isNumber()); + } + } +} + +void CheckType::signConversionError(const Token *tok, const ValueFlow::Value *negativeValue, const bool constvalue) +{ + const std::string expr(tok ? tok->expressionString() : "var"); + + std::ostringstream msg; + if (tok && tok->isName()) + msg << "$symbol:" << expr << "\n"; + if (constvalue) + msg << "Expression '" << expr << "' has a negative value. That is converted to an unsigned value and used in an unsigned calculation."; + else + msg << "Expression '" << expr << "' can have a negative value. That is converted to an unsigned value and used in an unsigned calculation."; + + if (!negativeValue) + reportError(tok, Severity::warning, "signConversion", msg.str(), CWE195, Certainty::normal); + else { + const ErrorPath &errorPath = getErrorPath(tok,negativeValue,"Negative value is converted to an unsigned value"); + reportError(errorPath, + Severity::warning, + Check::getMessageId(*negativeValue, "signConversion").c_str(), + msg.str(), + CWE195, + negativeValue->isInconclusive() ? Certainty::inconclusive : Certainty::normal); + } +} + + +//--------------------------------------------------------------------------- +// Checking for long cast of int result const long x = var1 * var2; +//--------------------------------------------------------------------------- +static bool checkTypeCombination(ValueType src, ValueType tgt, const Settings& settings) +{ + static const std::pair typeCombinations[] = { + { ValueType::Type::INT, ValueType::Type::LONG }, + { ValueType::Type::INT, ValueType::Type::LONGLONG }, + { ValueType::Type::LONG, ValueType::Type::LONGLONG }, + { ValueType::Type::FLOAT, ValueType::Type::DOUBLE }, + { ValueType::Type::FLOAT, ValueType::Type::LONGDOUBLE }, + { ValueType::Type::DOUBLE, ValueType::Type::LONGDOUBLE }, + }; + + src.reference = Reference::None; + tgt.reference = Reference::None; + + const std::size_t sizeSrc = ValueFlow::getSizeOf(src, settings); + const std::size_t sizeTgt = ValueFlow::getSizeOf(tgt, settings); + if (!(sizeSrc > 0 && sizeTgt > 0 && sizeSrc < sizeTgt)) + return false; + + return std::any_of(std::begin(typeCombinations), std::end(typeCombinations), [&](const std::pair& p) { + return src.type == p.first && tgt.type == p.second; + }); +} + +void CheckType::checkLongCast() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("truncLongCastAssignment")) + return; + + logChecker("CheckType::checkLongCast"); // style + + // Assignments.. + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (tok->str() != "=" || !Token::Match(tok->astOperand2(), "*|<<") || tok->astOperand2()->isUnaryOp("*")) + continue; + + if (tok->astOperand2()->hasKnownIntValue()) { + const ValueFlow::Value &v = tok->astOperand2()->values().front(); + if (mSettings->platform.isIntValue(v.intvalue)) + continue; + } + + const ValueType *lhstype = tok->astOperand1() ? tok->astOperand1()->valueType() : nullptr; + const ValueType *rhstype = tok->astOperand2()->valueType(); + + if (!lhstype || !rhstype) + continue; + if (!checkTypeCombination(*rhstype, *lhstype, *mSettings)) + continue; + + // assign int result to long/longlong const nonpointer? + if (rhstype->pointer == 0U && + rhstype->originalTypeName.empty() && + lhstype->pointer == 0U && + lhstype->originalTypeName.empty()) + longCastAssignError(tok, rhstype, lhstype); + } + + // Return.. + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope * scope : symbolDatabase->functionScopes) { + + // function must return long data + const Token * def = scope->classDef; + if (!Token::Match(def, "%name% (") || !def->next()->valueType()) + continue; + const ValueType* retVt = def->next()->valueType(); + + // return statements + const Token *ret = nullptr; + for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (tok->str() == "return") { + if (Token::Match(tok->astOperand1(), "<<|*")) { + const ValueType *type = tok->astOperand1()->valueType(); + if (type && checkTypeCombination(*type, *retVt, *mSettings) && + type->pointer == 0U && + type->originalTypeName.empty()) + ret = tok; + } + // All return statements must have problem otherwise no warning + if (ret != tok) { + ret = nullptr; + break; + } + } + } + + if (ret) + longCastReturnError(ret, ret->astOperand1()->valueType(), retVt); + } +} + +static void makeBaseTypeString(std::string& typeStr) +{ + const auto pos = typeStr.find("signed"); + if (pos != std::string::npos) + typeStr.erase(typeStr.begin(), typeStr.begin() + pos + 6 + 1); +} + +void CheckType::longCastAssignError(const Token *tok, const ValueType* src, const ValueType* tgt) +{ + std::string srcStr = src ? src->str() : "int"; + makeBaseTypeString(srcStr); + std::string tgtStr = tgt ? tgt->str() : "long"; + makeBaseTypeString(tgtStr); + reportError(tok, + Severity::style, + "truncLongCastAssignment", + srcStr + " result is assigned to " + tgtStr + " variable. If the variable is " + tgtStr + " to avoid loss of information, then you have loss of information.\n" + + srcStr + " result is assigned to " + tgtStr + " variable. If the variable is " + tgtStr + " to avoid loss of information, then there is loss of information. To avoid loss of information you must cast a calculation operand to " + tgtStr + ", for example 'l = a * b;' => 'l = (" + tgtStr + ")a * b;'.", CWE197, Certainty::normal); +} + +void CheckType::longCastReturnError(const Token *tok, const ValueType* src, const ValueType* tgt) +{ + std::string srcStr = src ? src->str() : "int"; + makeBaseTypeString(srcStr); + std::string tgtStr = tgt ? tgt->str() : "long"; + makeBaseTypeString(tgtStr); + reportError(tok, + Severity::style, + "truncLongCastReturn", + srcStr +" result is returned as " + tgtStr + " value. If the return value is " + tgtStr + " to avoid loss of information, then you have loss of information.\n" + + srcStr +" result is returned as " + tgtStr + " value. If the return value is " + tgtStr + " to avoid loss of information, then there is loss of information. To avoid loss of information you must cast a calculation operand to long, for example 'return a*b;' => 'return (long)a*b'.", CWE197, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Checking for float to integer overflow +//--------------------------------------------------------------------------- + +void CheckType::checkFloatToIntegerOverflow() +{ + logChecker("CheckType::checkFloatToIntegerOverflow"); + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + const ValueType *vtint, *vtfloat; + + // Explicit cast + if (Token::Match(tok, "( %name%") && tok->astOperand1() && !tok->astOperand2()) { + vtint = tok->valueType(); + vtfloat = tok->astOperand1()->valueType(); + checkFloatToIntegerOverflow(tok, vtint, vtfloat, tok->astOperand1()->values()); + } + + // Assignment + else if (tok->str() == "=" && tok->astOperand1() && tok->astOperand2()) { + vtint = tok->astOperand1()->valueType(); + vtfloat = tok->astOperand2()->valueType(); + checkFloatToIntegerOverflow(tok, vtint, vtfloat, tok->astOperand2()->values()); + } + + else if (tok->str() == "return" && tok->astOperand1() && tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->isFloat()) { + const Scope *scope = tok->scope(); + while (scope && scope->type != Scope::ScopeType::eLambda && scope->type != Scope::ScopeType::eFunction) + scope = scope->nestedIn; + if (scope && scope->type == Scope::ScopeType::eFunction && scope->function && scope->function->retDef) { + const ValueType &valueType = ValueType::parseDecl(scope->function->retDef, *mSettings); + vtfloat = tok->astOperand1()->valueType(); + checkFloatToIntegerOverflow(tok, &valueType, vtfloat, tok->astOperand1()->values()); + } + } + } +} + +void CheckType::checkFloatToIntegerOverflow(const Token *tok, const ValueType *vtint, const ValueType *vtfloat, const std::list &floatValues) +{ + // Conversion of float to integer? + if (!vtint || !vtint->isIntegral()) + return; + if (!vtfloat || !vtfloat->isFloat()) + return; + + for (const ValueFlow::Value &f : floatValues) { + if (f.valueType != ValueFlow::Value::ValueType::FLOAT) + continue; + if (!mSettings->isEnabled(&f, false)) + continue; + if (f.floatValue >= std::exp2(mSettings->platform.long_long_bit)) + floatToIntegerOverflowError(tok, f); + else if ((-f.floatValue) > std::exp2(mSettings->platform.long_long_bit - 1)) + floatToIntegerOverflowError(tok, f); + else if (mSettings->platform.type != Platform::Type::Unspecified) { + int bits = 0; + if (vtint->type == ValueType::Type::CHAR) + bits = mSettings->platform.char_bit; + else if (vtint->type == ValueType::Type::SHORT) + bits = mSettings->platform.short_bit; + else if (vtint->type == ValueType::Type::INT) + bits = mSettings->platform.int_bit; + else if (vtint->type == ValueType::Type::LONG) + bits = mSettings->platform.long_bit; + else if (vtint->type == ValueType::Type::LONGLONG) + bits = mSettings->platform.long_long_bit; + else + continue; + if (bits < MathLib::bigint_bits && f.floatValue >= (((MathLib::biguint)1) << bits)) + floatToIntegerOverflowError(tok, f); + } + } +} + +void CheckType::floatToIntegerOverflowError(const Token *tok, const ValueFlow::Value &value) +{ + std::ostringstream errmsg; + errmsg << "Undefined behaviour: float (" << value.floatValue << ") to integer conversion overflow."; + reportError(getErrorPath(tok, &value, "float to integer conversion"), + value.errorSeverity() ? Severity::error : Severity::warning, + "floatConversionOverflow", + errmsg.str(), CWE190, value.isInconclusive() ? Certainty::inconclusive : Certainty::normal); +} diff --git a/cppcheck-2.14.0/lib/checktype.h b/cppcheck-2.14.0/lib/checktype.h new file mode 100644 index 00000000..43f1340c --- /dev/null +++ b/cppcheck-2.14.0/lib/checktype.h @@ -0,0 +1,120 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checktypeH +#define checktypeH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "tokenize.h" +#include "vfvalue.h" + +#include +#include + +class ErrorLogger; +class Settings; +class Token; +class ValueType; + +/// @addtogroup Checks +/// @{ + + +/** @brief Various small checks */ + +class CPPCHECKLIB CheckType : public Check { +public: + /** @brief This constructor is used when registering the CheckClass */ + CheckType() : Check(myName()) {} + +private: + /** @brief This constructor is used when running checks. */ + CheckType(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger) {} + + /** @brief Run checks against the normal token list */ + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + // These are not "simplified" because casts can't be ignored + CheckType checkType(&tokenizer, &tokenizer.getSettings(), errorLogger); + checkType.checkTooBigBitwiseShift(); + checkType.checkIntegerOverflow(); + checkType.checkSignConversion(); + checkType.checkLongCast(); + checkType.checkFloatToIntegerOverflow(); + } + + /** @brief %Check for bitwise shift with too big right operand */ + void checkTooBigBitwiseShift(); + + /** @brief %Check for integer overflow */ + void checkIntegerOverflow(); + + /** @brief %Check for dangerous sign conversion */ + void checkSignConversion(); + + /** @brief %Check for implicit long cast of int result */ + void checkLongCast(); + + /** @brief %Check for float to integer overflow */ + void checkFloatToIntegerOverflow(); + void checkFloatToIntegerOverflow(const Token *tok, const ValueType *vtint, const ValueType *vtfloat, const std::list &floatValues); + + // Error messages.. + void tooBigBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits); + void tooBigSignedBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits); + void integerOverflowError(const Token *tok, const ValueFlow::Value &value); + void signConversionError(const Token *tok, const ValueFlow::Value *negativeValue, const bool constvalue); + void longCastAssignError(const Token *tok, const ValueType* src = nullptr, const ValueType* tgt = nullptr); + void longCastReturnError(const Token *tok, const ValueType* src = nullptr, const ValueType* tgt = nullptr); + void floatToIntegerOverflowError(const Token *tok, const ValueFlow::Value &value); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckType c(nullptr, settings, errorLogger); + c.tooBigBitwiseShiftError(nullptr, 32, ValueFlow::Value(64)); + c.tooBigSignedBitwiseShiftError(nullptr, 31, ValueFlow::Value(31)); + c.integerOverflowError(nullptr, ValueFlow::Value(1LL<<32)); + c.signConversionError(nullptr, nullptr, false); + c.longCastAssignError(nullptr); + c.longCastReturnError(nullptr); + ValueFlow::Value f; + f.valueType = ValueFlow::Value::ValueType::FLOAT; + f.floatValue = 1E100; + c.floatToIntegerOverflowError(nullptr, f); + } + + static std::string myName() { + return "Type"; + } + + std::string classInfo() const override { + return "Type checks\n" + "- bitwise shift by too many bits (only enabled when --platform is used)\n" + "- signed integer overflow (only enabled when --platform is used)\n" + "- dangerous sign conversion, when signed value can be negative\n" + "- possible loss of information when assigning int result to long variable\n" + "- possible loss of information when returning int result as long return value\n" + "- float conversion overflow\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checktypeH diff --git a/cppcheck-2.14.0/lib/checkuninitvar.cpp b/cppcheck-2.14.0/lib/checkuninitvar.cpp new file mode 100644 index 00000000..86d169e3 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkuninitvar.cpp @@ -0,0 +1,1762 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#include "checkuninitvar.h" + +#include "astutils.h" +#include "ctu.h" +#include "errorlogger.h" +#include "library.h" +#include "mathlib.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" + +#include "checknullpointer.h" // CheckNullPointer::isPointerDeref + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace tinyxml2 { + class XMLElement; +} + +//--------------------------------------------------------------------------- + +// CWE ids used: +static const CWE CWE_USE_OF_UNINITIALIZED_VARIABLE(457U); + +// Register this check class (by creating a static instance of it) +namespace { + CheckUninitVar instance; +} + +//--------------------------------------------------------------------------- + +// get ast parent, skip possible address-of and casts +static const Token *getAstParentSkipPossibleCastAndAddressOf(const Token *vartok, bool *unknown) +{ + if (unknown) + *unknown = false; + if (!vartok) + return nullptr; + const Token *parent = vartok->astParent(); + while (Token::Match(parent, ".|::")) + parent = parent->astParent(); + if (!parent) + return nullptr; + if (parent->isUnaryOp("&")) + parent = parent->astParent(); + else if (parent->str() == "&" && vartok == parent->astOperand2() && Token::Match(parent->astOperand1()->previous(), "( %type% )")) { + parent = parent->astParent(); + if (unknown) + *unknown = true; + } + while (parent && parent->isCast()) + parent = parent->astParent(); + return parent; +} + +static std::map getVariableValues(const Token* tok) { + std::map ret; + if (!tok || !tok->scope()->isExecutable()) + return ret; + while (tok && tok->str() != "{") { + if (tok->str() == "}") { + if (tok->link()->isBinaryOp()) + tok = tok->link()->previous(); + else + break; + } + if (Token::Match(tok, "%var% =|{") && tok->next()->isBinaryOp() && tok->varId() && ret.count(tok->varId()) == 0) { + const Token* rhs = tok->next()->astOperand2(); + if (rhs && rhs->hasKnownIntValue()) + ret[tok->varId()] = VariableValue(rhs->getKnownIntValue()); + } + tok = tok->previous(); + } + return ret; +} + +bool CheckUninitVar::diag(const Token* tok) +{ + if (!tok) + return true; + while (Token::Match(tok->astParent(), "*|&|.")) + tok = tok->astParent(); + return !mUninitDiags.insert(tok).second; +} + +void CheckUninitVar::check() +{ + logChecker("CheckUninitVar::check"); + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + std::set arrayTypeDefs; + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (Token::Match(tok, "%name% [") && tok->variable() && Token::Match(tok->variable()->typeStartToken(), "%type% %var% ;")) + arrayTypeDefs.insert(tok->variable()->typeStartToken()->str()); + } + + // check every executable scope + for (const Scope &scope : symbolDatabase->scopeList) { + if (scope.isExecutable()) { + checkScope(&scope, arrayTypeDefs); + } + } +} + +void CheckUninitVar::checkScope(const Scope* scope, const std::set &arrayTypeDefs) +{ + for (const Variable &var : scope->varlist) { + if ((mTokenizer->isCPP() && var.type() && !var.isPointer() && var.type()->needInitialization != Type::NeedInitialization::True) || + var.isStatic() || var.isExtern() || var.isReference()) + continue; + + // don't warn for try/catch exception variable + if (var.isThrow()) + continue; + + if (Token::Match(var.nameToken()->next(), "[({:]")) + continue; + + if (Token::Match(var.nameToken(), "%name% =")) { // Variable is initialized, but Rhs might be not + checkRhs(var.nameToken(), var, NO_ALLOC, 0U, emptyString); + continue; + } + if (Token::Match(var.nameToken(), "%name% ) (") && Token::simpleMatch(var.nameToken()->linkAt(2), ") =")) { // Function pointer is initialized, but Rhs might be not + checkRhs(var.nameToken()->linkAt(2)->next(), var, NO_ALLOC, 0U, emptyString); + continue; + } + + if (var.isArray() || var.isPointerToArray()) { + const Token *tok = var.nameToken()->next(); + if (var.isPointerToArray()) + tok = tok->next(); + while (Token::simpleMatch(tok->link(), "] [")) + tok = tok->link()->next(); + if (Token::Match(tok->link(), "] =|{|(")) + continue; + } + + bool stdtype = var.typeStartToken()->isC() && arrayTypeDefs.find(var.typeStartToken()->str()) == arrayTypeDefs.end(); + const Token* tok = var.typeStartToken(); + for (; tok != var.nameToken() && tok->str() != "<"; tok = tok->next()) { + if (tok->isStandardType() || tok->isEnumType()) + stdtype = true; + } + if (var.isArray() && !stdtype) { // std::array + if (!(var.isStlType() && Token::simpleMatch(var.typeStartToken(), "std :: array") && var.valueType() && + var.valueType()->containerTypeToken && var.valueType()->containerTypeToken->isStandardType())) + continue; + } + + while (tok && tok->str() != ";") + tok = tok->next(); + if (!tok) + continue; + + if (tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "for (") && Token::simpleMatch(tok->astParent()->link()->next(), "{") && + checkLoopBody(tok->astParent()->link()->next(), var, var.isArray() ? ARRAY : NO_ALLOC, emptyString, true)) + continue; + + if (var.isArray()) { + bool init = false; + for (const Token *parent = var.nameToken(); parent; parent = parent->astParent()) { + if (parent->str() == "=") { + init = true; + break; + } + } + if (!init) { + Alloc alloc = ARRAY; + std::map variableValue = getVariableValues(var.typeStartToken()); + checkScopeForVariable(tok, var, nullptr, nullptr, &alloc, emptyString, variableValue); + } + continue; + } + if (stdtype || var.isPointer()) { + Alloc alloc = NO_ALLOC; + std::map variableValue = getVariableValues(var.typeStartToken()); + checkScopeForVariable(tok, var, nullptr, nullptr, &alloc, emptyString, variableValue); + } + if (var.type()) + checkStruct(tok, var); + } + + if (scope->function) { + for (const Variable &arg : scope->function->argumentList) { + if (arg.declarationId() && Token::Match(arg.typeStartToken(), "%type% * %name% [,)]")) { + // Treat the pointer as initialized until it is assigned by malloc + for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::Match(tok, "[;{}] %varid% =", arg.declarationId())) + continue; + const Token *allocFuncCallToken = findAllocFuncCallToken(tok->tokAt(2)->astOperand2(), mSettings->library); + if (!allocFuncCallToken) + continue; + const Library::AllocFunc *allocFunc = mSettings->library.getAllocFuncInfo(allocFuncCallToken); + if (!allocFunc || allocFunc->initData) + continue; + + if (arg.typeStartToken()->strAt(-1) == "struct" || (arg.type() && arg.type()->isStructType())) + checkStruct(tok, arg); + else if (arg.typeStartToken()->isStandardType() || arg.typeStartToken()->isEnumType()) { + Alloc alloc = NO_ALLOC; + std::map variableValue; + checkScopeForVariable(tok->next(), arg, nullptr, nullptr, &alloc, emptyString, variableValue); + } + } + } + } + } +} + +void CheckUninitVar::checkStruct(const Token *tok, const Variable &structvar) +{ + const Token *typeToken = structvar.typeStartToken(); + while (Token::Match(typeToken, "%name% ::")) + typeToken = typeToken->tokAt(2); + const SymbolDatabase * symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Scope *scope2 : symbolDatabase->classAndStructScopes) { + if (scope2->className == typeToken->str() && scope2->numConstructors == 0U) { + for (const Variable &var : scope2->varlist) { + if (var.isStatic() || var.hasDefault() || var.isArray() || + (!mTokenizer->isC() && var.isClass() && (!var.type() || var.type()->needInitialization != Type::NeedInitialization::True))) + continue; + + // is the variable declared in a inner union? + bool innerunion = false; + for (const Scope *innerScope : scope2->nestedList) { + if (innerScope->type == Scope::eUnion) { + if (var.typeStartToken()->linenr() >= innerScope->bodyStart->linenr() && + var.typeStartToken()->linenr() <= innerScope->bodyEnd->linenr()) { + innerunion = true; + break; + } + } + } + + if (!innerunion) { + Alloc alloc = NO_ALLOC; + const Token *tok2 = tok; + if (tok->str() == "}") + tok2 = tok2->next(); + std::map variableValue = getVariableValues(structvar.typeStartToken()); + checkScopeForVariable(tok2, structvar, nullptr, nullptr, &alloc, var.name(), variableValue); + } + } + } + } +} + +static VariableValue operator!(VariableValue v) +{ + v.notEqual = !v.notEqual; + return v; +} +static bool operator==(const VariableValue & v, MathLib::bigint i) +{ + return v.notEqual ? (i != v.value) : (i == v.value); +} +static bool operator!=(const VariableValue & v, MathLib::bigint i) +{ + return v.notEqual ? (i == v.value) : (i != v.value); +} + +static void conditionAlwaysTrueOrFalse(const Token *tok, const std::map &variableValue, bool *alwaysTrue, bool *alwaysFalse) +{ + if (!tok) + return; + + if (tok->hasKnownIntValue()) { + if (tok->getKnownIntValue() == 0) + *alwaysFalse = true; + else + *alwaysTrue = true; + return; + } + + if (tok->isName() || tok->str() == ".") { + while (tok && tok->str() == ".") + tok = tok->astOperand2(); + const std::map::const_iterator it = variableValue.find(tok ? tok->varId() : ~0U); + if (it != variableValue.end()) { + *alwaysTrue = (it->second != 0LL); + *alwaysFalse = (it->second == 0LL); + } + } + + else if (tok->isComparisonOp()) { + if (variableValue.empty()) { + return; + } + + const Token *vartok, *numtok; + if (tok->astOperand2() && tok->astOperand2()->isNumber()) { + vartok = tok->astOperand1(); + numtok = tok->astOperand2(); + } else if (tok->astOperand1() && tok->astOperand1()->isNumber()) { + vartok = tok->astOperand2(); + numtok = tok->astOperand1(); + } else { + return; + } + + while (vartok && vartok->str() == ".") + vartok = vartok->astOperand2(); + + const std::map::const_iterator it = variableValue.find(vartok ? vartok->varId() : ~0U); + if (it == variableValue.end()) + return; + + if (tok->str() == "==") + *alwaysTrue = (it->second == MathLib::toBigNumber(numtok->str())); + else if (tok->str() == "!=") + *alwaysTrue = (it->second != MathLib::toBigNumber(numtok->str())); + else + return; + *alwaysFalse = !(*alwaysTrue); + } + + else if (tok->str() == "!") { + bool t=false,f=false; + conditionAlwaysTrueOrFalse(tok->astOperand1(), variableValue, &t, &f); + if (t || f) { + *alwaysTrue = !t; + *alwaysFalse = !f; + } + } + + else if (tok->str() == "||") { + bool t1=false, f1=false; + conditionAlwaysTrueOrFalse(tok->astOperand1(), variableValue, &t1, &f1); + bool t2=false, f2=false; + if (!t1) + conditionAlwaysTrueOrFalse(tok->astOperand2(), variableValue, &t2, &f2); + *alwaysTrue = (t1 || t2); + *alwaysFalse = (f1 && f2); + } + + else if (tok->str() == "&&") { + bool t1=false, f1=false; + conditionAlwaysTrueOrFalse(tok->astOperand1(), variableValue, &t1, &f1); + bool t2=false, f2=false; + if (!f1) + conditionAlwaysTrueOrFalse(tok->astOperand2(), variableValue, &t2, &f2); + *alwaysTrue = (t1 && t2); + *alwaysFalse = (f1 || f2); + } +} + +static bool isVariableUsed(const Token *tok, const Variable& var) +{ + if (!tok) + return false; + if (tok->str() == "&" && !tok->astOperand2()) + return false; + if (tok->isConstOp()) + return isVariableUsed(tok->astOperand1(),var) || isVariableUsed(tok->astOperand2(),var); + if (tok->varId() != var.declarationId()) + return false; + if (!var.isArray()) + return true; + + const Token *parent = tok->astParent(); + while (Token::Match(parent, "[?:]")) + parent = parent->astParent(); + // no dereference, then array is not "used" + if (!Token::Match(parent, "*|[")) + return false; + const Token *parent2 = parent->astParent(); + // TODO: handle function calls. There is a TODO assertion in TestUninitVar::uninitvar_arrays + return !parent2 || parent2->isConstOp() || (parent2->str() == "=" && parent2->astOperand2() == parent); +} + +bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var, bool * const possibleInit, bool * const noreturn, Alloc* const alloc, const std::string &membervar, std::map& variableValue) +{ + const bool suppressErrors(possibleInit && *possibleInit); // Assume that this is a variable declaration, rather than a fundef + const bool printDebug = mSettings->debugwarnings; + + if (possibleInit) + *possibleInit = false; + + int number_of_if = 0; + + if (var.declarationId() == 0U) + return true; + + for (; tok; tok = tok->next()) { + // End of scope.. + if (tok->str() == "}") { + if (number_of_if && possibleInit) + *possibleInit = true; + + // might be a noreturn function.. + if (mTokenizer->isScopeNoReturn(tok)) { + if (noreturn) + *noreturn = true; + return false; + } + + break; + } + + // Unconditional inner scope, try, lambda, init list + if (tok->str() == "{" && Token::Match(tok->previous(), ",|;|{|}|]|try")) { + bool possibleInitInner = false; + if (checkScopeForVariable(tok->next(), var, &possibleInitInner, noreturn, alloc, membervar, variableValue)) + return true; + tok = tok->link(); + if (possibleInitInner) { + number_of_if = 1; + if (possibleInit) + *possibleInit = true; + } + continue; + } + + // track values of other variables.. + if (Token::Match(tok->previous(), "[;{}.] %var% =")) { + if (tok->next()->astOperand2() && tok->next()->astOperand2()->hasKnownIntValue()) + variableValue[tok->varId()] = VariableValue(tok->next()->astOperand2()->getKnownIntValue()); + else if (Token::Match(tok->previous(), "[;{}] %var% = - %name% ;")) + variableValue[tok->varId()] = !VariableValue(0); + else + variableValue.erase(tok->varId()); + } + + // Inner scope.. + else if (Token::simpleMatch(tok, "if (")) { + bool alwaysTrue = false; + bool alwaysFalse = false; + + // Is variable assigned in condition? + if (!membervar.empty()) { + for (const Token *cond = tok->linkAt(1); cond != tok; cond = cond->previous()) { + if (cond->varId() == var.declarationId() && isMemberVariableAssignment(cond, membervar)) + return true; + } + } + + conditionAlwaysTrueOrFalse(tok->next()->astOperand2(), variableValue, &alwaysTrue, &alwaysFalse); + + // initialization / usage in condition.. + if (!alwaysTrue && checkIfForWhileHead(tok->next(), var, suppressErrors, bool(number_of_if == 0), *alloc, membervar)) + return true; + + // checking if a not-zero variable is zero => bail out + nonneg int condVarId = 0; + VariableValue condVarValue(0); + const Token *condVarTok = nullptr; + if (alwaysFalse) + ; + else if (astIsVariableComparison(tok->next()->astOperand2(), "!=", "0", &condVarTok)) { + const std::map::const_iterator it = variableValue.find(condVarTok->varId()); + if (it != variableValue.cend() && it->second != 0) + return true; // this scope is not fully analysed => return true + + condVarId = condVarTok->varId(); + condVarValue = !VariableValue(0); + } else if (Token::Match(tok->next()->astOperand2(), "==|!=")) { + const Token *condition = tok->next()->astOperand2(); + const Token *lhs = condition->astOperand1(); + const Token *rhs = condition->astOperand2(); + const Token *vartok = (lhs && lhs->hasKnownIntValue()) ? rhs : lhs; + const Token *numtok = (lhs == vartok) ? rhs : lhs; + while (Token::simpleMatch(vartok, ".")) + vartok = vartok->astOperand2(); + if (vartok && vartok->varId() && numtok && numtok->hasKnownIntValue()) { + const std::map::const_iterator it = variableValue.find(vartok->varId()); + if (it != variableValue.cend() && it->second != numtok->getKnownIntValue()) + return true; // this scope is not fully analysed => return true + condVarId = vartok->varId(); + condVarValue = VariableValue(numtok->getKnownIntValue()); + if (condition->str() == "!=") + condVarValue = !condVarValue; + } + } + + // goto the { + tok = tok->next()->link()->next(); + + if (!tok) + break; + if (tok->str() == "{") { + bool possibleInitIf((!alwaysTrue && number_of_if > 0) || suppressErrors); + bool noreturnIf = false; + std::map varValueIf(variableValue); + const bool initif = !alwaysFalse && checkScopeForVariable(tok->next(), var, &possibleInitIf, &noreturnIf, alloc, membervar, varValueIf); + + // bail out for such code: + // if (a) x=0; // conditional initialization + // if (b) return; // cppcheck doesn't know if b can be false when a is false. + // x++; // it's possible x is always initialized + if (!alwaysTrue && noreturnIf && number_of_if > 0) { + if (printDebug) { + std::string condition; + for (const Token *tok2 = tok->linkAt(-1); tok2 != tok; tok2 = tok2->next()) { + condition += tok2->str(); + if (tok2->isName() && tok2->next()->isName()) + condition += ' '; + } + reportError(tok, Severity::debug, "bailoutUninitVar", "bailout uninitialized variable checking for '" + var.name() + "'. can't determine if this condition can be false when previous condition is false: " + condition); + } + return true; + } + + if (alwaysTrue && (initif || noreturnIf)) + return true; + + if (!alwaysFalse && !initif && !noreturnIf) + variableValue = varValueIf; + + if (initif && condVarId > 0) + variableValue[condVarId] = !condVarValue; + + // goto the } + tok = tok->link(); + + if (!Token::simpleMatch(tok, "} else {")) { + if (initif || possibleInitIf) { + ++number_of_if; + if (number_of_if >= 2) + return true; + } + } else { + // goto the { + tok = tok->tokAt(2); + + bool possibleInitElse((!alwaysFalse && number_of_if > 0) || suppressErrors); + bool noreturnElse = false; + std::map varValueElse(variableValue); + const bool initelse = !alwaysTrue && checkScopeForVariable(tok->next(), var, &possibleInitElse, &noreturnElse, alloc, membervar, varValueElse); + + if (!alwaysTrue && !initelse && !noreturnElse) + variableValue = varValueElse; + + if (initelse && condVarId > 0 && !noreturnIf && !noreturnElse) + variableValue[condVarId] = condVarValue; + + // goto the } + tok = tok->link(); + + if ((alwaysFalse || initif || noreturnIf) && + (alwaysTrue || initelse || noreturnElse)) + return true; + + if (initif || initelse || possibleInitElse) + ++number_of_if; + if (!initif && !noreturnIf) + variableValue.insert(varValueIf.cbegin(), varValueIf.cend()); + if (!initelse && !noreturnElse) + variableValue.insert(varValueElse.cbegin(), varValueElse.cend()); + } + } + } + + // = { .. } + else if (Token::simpleMatch(tok, "= {") || (Token::Match(tok, "%name% {") && tok->variable() && tok == tok->variable()->nameToken())) { + // end token + const Token *end = tok->next()->link(); + + // If address of variable is taken in the block then bail out + if (var.isPointer() || var.isArray()) { + if (Token::findmatch(tok->tokAt(2), "%varid%", end, var.declarationId())) + return true; + } else if (Token::findmatch(tok->tokAt(2), "& %varid%", end, var.declarationId())) { + return true; + } + + const Token *errorToken = nullptr; + visitAstNodes(tok->next(), + [&](const Token *child) { + if (child->isUnaryOp("&")) + return ChildrenToVisit::none; + if (child->str() == "," || child->str() == "{" || child->isConstOp()) + return ChildrenToVisit::op1_and_op2; + if (child->str() == "." && Token::Match(child->astOperand1(), "%varid%", var.declarationId()) && child->astOperand2() && child->astOperand2()->str() == membervar) { + errorToken = child; + return ChildrenToVisit::done; + } + return ChildrenToVisit::none; + }); + + if (errorToken) { + uninitStructMemberError(errorToken->astOperand2(), errorToken->astOperand1()->str() + "." + membervar); + return true; + } + + // Skip block + tok = end; + continue; + } + + // skip sizeof / offsetof + if (isUnevaluated(tok)) + tok = tok->linkAt(1); + + // for/while.. + else if (Token::Match(tok, "for|while (") || Token::simpleMatch(tok, "do {")) { + const bool forwhile = Token::Match(tok, "for|while ("); + + // is variable initialized in for-head? + if (forwhile && checkIfForWhileHead(tok->next(), var, tok->str() == "for", false, *alloc, membervar)) + return true; + + // goto the { + const Token *tok2 = forwhile ? tok->next()->link()->next() : tok->next(); + + if (tok2 && tok2->str() == "{") { + const bool init = checkLoopBody(tok2, var, *alloc, membervar, (number_of_if > 0) || suppressErrors); + + // variable is initialized in the loop.. + if (init) + return true; + + // is variable used in for-head? + bool initcond = false; + if (!suppressErrors) { + const Token *startCond = forwhile ? tok->next() : tok->next()->link()->tokAt(2); + initcond = checkIfForWhileHead(startCond, var, false, bool(number_of_if == 0), *alloc, membervar); + } + + // goto "}" + tok = tok2->link(); + + // do-while => goto ")" + if (!forwhile) { + // Assert that the tokens are '} while (' + if (!Token::simpleMatch(tok, "} while (")) { + if (printDebug) + reportError(tok,Severity::debug,emptyString,"assertion failed '} while ('"); + break; + } + + // Goto ')' + tok = tok->linkAt(2); + + if (!tok) + // bailout : invalid code / bad tokenizer + break; + + if (initcond) + // variable is initialized in while-condition + return true; + } + } + } + + // Unknown or unhandled inner scope + else if (Token::simpleMatch(tok, ") {") || (Token::Match(tok, "%name% {") && tok->str() != "try" && !(tok->variable() && tok == tok->variable()->nameToken()))) { + if (tok->str() == "struct" || tok->str() == "union") { + tok = tok->linkAt(1); + continue; + } + return true; + } + + // bailout if there is ({ + if (Token::simpleMatch(tok, "( {")) { + return true; + } + + // bailout if there is assembler code or setjmp + if (Token::Match(tok, "asm|setjmp (")) { + return true; + } + + // bailout if there is a goto label + if (Token::Match(tok, "[;{}] %name% :")) { + return true; + } + + // bailout if there is a pointer to member + if (Token::Match(tok, "%varid% . *", var.declarationId())) { + return true; + } + + if (tok->str() == "?") { + if (!tok->astOperand2()) + return true; + const bool used1 = isVariableUsed(tok->astOperand2()->astOperand1(), var); + const bool used0 = isVariableUsed(tok->astOperand2()->astOperand2(), var); + const bool err = (number_of_if == 0) ? (used1 || used0) : (used1 && used0); + if (err) + uninitvarError(tok, var.nameToken()->str(), *alloc); + + // Todo: skip expression if there is no error + return true; + } + + if (Token::Match(tok, "return|break|continue|throw|goto")) { + if (noreturn) + *noreturn = true; + + tok = tok->next(); + while (tok && tok->str() != ";") { + // variable is seen.. + if (tok->varId() == var.declarationId()) { + if (!membervar.empty()) { + if (!suppressErrors && Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar && Token::Match(tok->next()->astParent(), "%cop%|return|throw|?")) + uninitStructMemberError(tok, tok->str() + "." + membervar); + else if (tok->isCpp() && !suppressErrors && Token::Match(tok, "%name%") && Token::Match(tok->astParent(), "return|throw|?")) { + if (std::any_of(tok->values().cbegin(), tok->values().cend(), [](const ValueFlow::Value& v) { + return v.isUninitValue() && !v.isInconclusive(); + })) + uninitStructMemberError(tok, tok->str() + "." + membervar); + } + } + + // Use variable + else if (!suppressErrors && isVariableUsage(tok, var.isPointer(), *alloc)) + uninitvarError(tok, tok->str(), *alloc); + + return true; + } + + if (isUnevaluated(tok)) + tok = tok->linkAt(1); + + else if (tok->str() == "?") { + if (!tok->astOperand2()) + return true; + const bool used1 = isVariableUsed(tok->astOperand2()->astOperand1(), var); + const bool used0 = isVariableUsed(tok->astOperand2()->astOperand2(), var); + const bool err = (number_of_if == 0) ? (used1 || used0) : (used1 && used0); + if (err) + uninitvarError(tok, var.nameToken()->str(), *alloc); + return true; + } + + tok = tok->next(); + } + + return (noreturn == nullptr); + } + + // variable is seen.. + if (tok->varId() == var.declarationId()) { + // calling function that returns uninit data through pointer.. + if (var.isPointer() && Token::simpleMatch(tok->next(), "=")) { + const Token *rhs = tok->next()->astOperand2(); + while (rhs && rhs->isCast()) + rhs = rhs->astOperand2() ? rhs->astOperand2() : rhs->astOperand1(); + if (rhs && Token::Match(rhs->previous(), "%name% (")) { + const Library::AllocFunc *allocFunc = mSettings->library.getAllocFuncInfo(rhs->astOperand1()); + if (allocFunc && !allocFunc->initData) { + *alloc = NO_CTOR_CALL; + continue; + } + } + } + if (mTokenizer->isCPP() && var.isPointer() && (var.typeStartToken()->isStandardType() || var.typeStartToken()->isEnumType() || (var.type() && var.type()->needInitialization == Type::NeedInitialization::True)) && Token::simpleMatch(tok->next(), "= new")) { + *alloc = CTOR_CALL; + + // type has constructor(s) + if (var.typeScope() && var.typeScope()->numConstructors > 0) + return true; + + // standard or enum type: check if new initializes the allocated memory + if (var.typeStartToken()->isStandardType() || var.typeStartToken()->isEnumType()) { + // scalar new with initialization + if (Token::Match(tok->next(), "= new %type% (")) + return true; + + // array new + if (Token::Match(tok->next(), "= new %type% [") && Token::simpleMatch(tok->linkAt(4), "] (")) + return true; + } + + continue; + } + + + if (!membervar.empty()) { + if (isMemberVariableAssignment(tok, membervar)) { + checkRhs(tok, var, *alloc, number_of_if, membervar); + return true; + } + + if (isMemberVariableUsage(tok, var.isPointer(), *alloc, membervar)) { + uninitStructMemberError(tok, tok->str() + "." + membervar); + return true; + } + + if (Token::Match(tok->previous(), "[(,] %name% [,)]")) + return true; + + if (Token::Match(tok->previous(), "= %var% . %var% ;") && membervar == tok->strAt(2)) + return true; + + } else { + // Use variable + if (!suppressErrors && isVariableUsage(tok, var.isPointer(), *alloc)) { + uninitvarError(tok, tok->str(), *alloc); + return true; + } + + const Token *parent = tok; + while (parent->astParent() && ((astIsLHS(parent) && parent->astParent()->str() == "[") || parent->astParent()->isUnaryOp("*"))) { + parent = parent->astParent(); + if (parent->str() == "[") { + if (const Token *errorToken = checkExpr(parent->astOperand2(), var, *alloc, number_of_if==0)) { + if (!suppressErrors) + uninitvarError(errorToken, errorToken->expressionString(), *alloc); + return true; + } + } + } + if (Token::simpleMatch(parent->astParent(), "=") && astIsLHS(parent)) { + const Token *eq = parent->astParent(); + if (const Token *errorToken = checkExpr(eq->astOperand2(), var, *alloc, number_of_if==0)) { + if (!suppressErrors) + uninitvarError(errorToken, errorToken->expressionString(), *alloc); + return true; + } + } + + // assume that variable is assigned + return true; + } + } + } + + return false; +} + +const Token* CheckUninitVar::checkExpr(const Token* tok, const Variable& var, const Alloc alloc, bool known, bool* bailout) const +{ + if (!tok) + return nullptr; + if (isUnevaluated(tok->previous())) + return nullptr; + + if (tok->astOperand1()) { + bool bailout1 = false; + const Token *errorToken = checkExpr(tok->astOperand1(), var, alloc, known, &bailout1); + if (bailout && bailout1) + *bailout = true; + if (errorToken) + return errorToken; + if ((bailout1 || !known) && Token::Match(tok, "%oror%|&&|?")) + return nullptr; + } + if (tok->astOperand2()) + return checkExpr(tok->astOperand2(), var, alloc, known, bailout); + if (tok->varId() == var.declarationId()) { + const Token *errorToken = isVariableUsage(tok, var.isPointer(), alloc); + if (errorToken) + return errorToken; + if (bailout) + *bailout = true; + } + return nullptr; +} + +bool CheckUninitVar::checkIfForWhileHead(const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit, Alloc alloc, const std::string &membervar) +{ + const Token * const endpar = startparentheses->link(); + if (Token::Match(startparentheses, "( ! %name% %oror%") && startparentheses->tokAt(2)->getValue(0)) + suppressErrors = true; + for (const Token *tok = startparentheses->next(); tok && tok != endpar; tok = tok->next()) { + if (tok->varId() == var.declarationId()) { + if (Token::Match(tok, "%name% . %name%")) { + if (membervar.empty()) + return true; + if (tok->strAt(2) == membervar) { + if (isMemberVariableAssignment(tok, membervar)) + return true; + + if (!suppressErrors && isMemberVariableUsage(tok, var.isPointer(), alloc, membervar)) + uninitStructMemberError(tok, tok->str() + "." + membervar); + } + continue; + } + + if (const Token *errorToken = isVariableUsage(tok, var.isPointer(), alloc)) { + if (suppressErrors) + continue; + uninitvarError(errorToken, errorToken->expressionString(), alloc); + } + return true; + } + // skip sizeof / offsetof + if (isUnevaluated(tok)) + tok = tok->linkAt(1); + if ((!isuninit || !membervar.empty()) && tok->str() == "&&") + suppressErrors = true; + } + return false; +} + +/** recursively check loop, return error token */ +const Token* CheckUninitVar::checkLoopBodyRecursive(const Token *start, const Variable& var, const Alloc alloc, const std::string &membervar, bool &bailout) const +{ + assert(start->str() == "{"); + + const Token *errorToken = nullptr; + + const Token *const end = start->link(); + for (const Token *tok = start->next(); tok != end; tok = tok->next()) { + // skip sizeof / offsetof + if (isUnevaluated(tok)) { + tok = tok->linkAt(1); + continue; + } + + if (Token::Match(tok, "asm ( %str% ) ;")) { + bailout = true; + return nullptr; + } + + // for loop; skip third expression until loop body has been analyzed.. + if (tok->str() == ";" && Token::simpleMatch(tok->astParent(), ";") && Token::simpleMatch(tok->astParent()->astParent(), "(")) { + const Token *top = tok->astParent()->astParent(); + if (!Token::simpleMatch(top->previous(), "for (") || !Token::simpleMatch(top->link(), ") {")) + continue; + const Token *bodyStart = top->link()->next(); + const Token *errorToken1 = checkLoopBodyRecursive(bodyStart, var, alloc, membervar, bailout); + if (!errorToken) + errorToken = errorToken1; + if (bailout) + return nullptr; + } + // for loop; skip loop body if there is third expression + if (Token::simpleMatch(tok, ") {") && + Token::simpleMatch(tok->link()->previous(), "for (") && + Token::simpleMatch(tok->link()->astOperand2(), ";") && + Token::simpleMatch(tok->link()->astOperand2()->astOperand2(), ";")) { + tok = tok->linkAt(1); + } + + if (tok->str() == "{") { + // switch => bailout + if (tok->scope() && tok->scope()->type == Scope::ScopeType::eSwitch) { + bailout = true; + return nullptr; + } + + const Token *errorToken1 = checkLoopBodyRecursive(tok, var, alloc, membervar, bailout); + tok = tok->link(); + if (Token::simpleMatch(tok, "} else {")) { + const Token *elseBody = tok->tokAt(2); + const Token *errorToken2 = checkLoopBodyRecursive(elseBody, var, alloc, membervar, bailout); + tok = elseBody->link(); + if (errorToken1 && errorToken2) + return errorToken1; + if (errorToken2) + errorToken = errorToken2; + } + if (bailout) + return nullptr; + if (!errorToken) + errorToken = errorToken1; + } + + if (tok->varId() != var.declarationId()) + continue; + + bool conditionalUsage = false; + for (const Token* parent = tok; parent; parent = parent->astParent()) { + if (Token::Match(parent->astParent(), "%oror%|&&|?") && astIsRHS(parent)) { + conditionalUsage = true; + break; + } + } + + if (!membervar.empty()) { + if (isMemberVariableAssignment(tok, membervar)) { + bool assign = true; + bool rhs = false; + // Used for tracking if an ")" is inner or outer + const Token *rpar = nullptr; + for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { + if (tok2->str() == "=") + rhs = true; + + // Look at inner expressions but not outer expressions + if (!rpar && tok2->str() == "(") + rpar = tok2->link(); + else if (tok2->str() == ")") { + // No rpar => this is an outer right parenthesis + if (!rpar) + break; + if (rpar == tok2) + rpar = nullptr; + } + + if (tok2->str() == ";" || (!rpar && tok2->str() == ",")) + break; + if (rhs && tok2->varId() == var.declarationId() && isMemberVariableUsage(tok2, var.isPointer(), alloc, membervar)) { + assign = false; + break; + } + } + if (assign) { + bailout = true; + return nullptr; + } + } + if (isMemberVariableUsage(tok, var.isPointer(), alloc, membervar)) { + if (!conditionalUsage) + return tok; + if (!errorToken) + errorToken = tok; + } else if (Token::Match(tok->previous(), "[(,] %name% [,)]")) { + bailout = true; + return nullptr; + } + } else { + if (const Token *errtok = isVariableUsage(tok, var.isPointer(), alloc)) { + if (!conditionalUsage) + return errtok; + if (!errorToken) + errorToken = errtok; + } else if (tok->strAt(1) == "=") { + bool varIsUsedInRhs = false; + visitAstNodes(tok->next()->astOperand2(), [&](const Token * t) { + if (!t) + return ChildrenToVisit::none; + if (t->varId() == var.declarationId()) { + varIsUsedInRhs = true; + return ChildrenToVisit::done; + } + if (isUnevaluated(t->previous())) + return ChildrenToVisit::none; + return ChildrenToVisit::op1_and_op2; + }); + if (!varIsUsedInRhs) { + bailout = true; + return nullptr; + } + } else { + bailout = true; + return nullptr; + } + } + } + + return errorToken; +} + +bool CheckUninitVar::checkLoopBody(const Token *tok, const Variable& var, const Alloc alloc, const std::string &membervar, const bool suppressErrors) +{ + bool bailout = false; + const Token *errorToken = checkLoopBodyRecursive(tok, var, alloc, membervar, bailout); + + if (!suppressErrors && !bailout && errorToken) { + if (membervar.empty()) + uninitvarError(errorToken, errorToken->expressionString(), alloc); + else + uninitStructMemberError(errorToken, errorToken->expressionString() + "." + membervar); + return true; + } + + return bailout; +} + +void CheckUninitVar::checkRhs(const Token *tok, const Variable &var, Alloc alloc, nonneg int number_of_if, const std::string &membervar) +{ + bool rhs = false; + int indent = 0; + while (nullptr != (tok = tok->next())) { + if (tok->str() == "=") + rhs = true; + else if (rhs && tok->varId() == var.declarationId()) { + if (membervar.empty() && isVariableUsage(tok, var.isPointer(), alloc)) + uninitvarError(tok, tok->str(), alloc); + else if (!membervar.empty() && isMemberVariableUsage(tok, var.isPointer(), alloc, membervar)) + uninitStructMemberError(tok, tok->str() + "." + membervar); + else if (Token::Match(tok, "%var% =")) + break; + else if (Token::Match(tok->previous(), "[(,&]")) + break; + } else if (tok->str() == ";" || (indent==0 && tok->str() == ",")) + break; + else if (tok->str() == "(") + ++indent; + else if (tok->str() == ")") { + if (indent == 0) + break; + --indent; + } else if (tok->str() == "?" && tok->astOperand2()) { + const bool used1 = isVariableUsed(tok->astOperand2()->astOperand1(), var); + const bool used0 = isVariableUsed(tok->astOperand2()->astOperand2(), var); + const bool err = (number_of_if == 0) ? (used1 || used0) : (used1 && used0); + if (err) + uninitvarError(tok, var.nameToken()->str(), alloc); + break; + } else if (isUnevaluated(tok)) + tok = tok->linkAt(1); + + } +} + +static bool astIsLhs(const Token *tok) +{ + return tok && tok->astParent() && tok == tok->astParent()->astOperand1(); +} + +static bool astIsRhs(const Token *tok) +{ + return tok && tok->astParent() && tok == tok->astParent()->astOperand2(); +} + +static bool isVoidCast(const Token *tok) +{ + return Token::simpleMatch(tok, "(") && tok->isCast() && tok->valueType() && tok->valueType()->type == ValueType::Type::VOID && tok->valueType()->pointer == 0; +} + +const Token* CheckUninitVar::isVariableUsage(const Token *vartok, const Library& library, bool pointer, Alloc alloc, int indirect) +{ + const bool cpp = vartok->isCpp(); + const Token *valueExpr = vartok; // non-dereferenced , no address of value as variable + while (Token::Match(valueExpr->astParent(), ".|::") && astIsRhs(valueExpr)) + valueExpr = valueExpr->astParent(); + // stuff we ignore.. + while (valueExpr->astParent()) { + // *&x + if (valueExpr->astParent()->isUnaryOp("&") && valueExpr->astParent()->astParent() && valueExpr->astParent()->astParent()->isUnaryOp("*")) + valueExpr = valueExpr->astParent()->astParent(); + // (type &)x + else if (valueExpr->astParent()->isCast() && valueExpr->astParent()->isUnaryOp("(") && Token::simpleMatch(valueExpr->astParent()->link()->previous(), "& )")) + valueExpr = valueExpr->astParent(); + // designated initializers: {.x | { ... , .x + else if (Token::simpleMatch(valueExpr->astParent(), ".") && + Token::Match(valueExpr->astParent()->previous(), ",|{")) + valueExpr = valueExpr->astParent(); + else + break; + } + if (!pointer) { + if (Token::Match(vartok, "%name% [.(]") && vartok->variable() && !vartok->variable()->isPointer()) + return nullptr; + while (Token::simpleMatch(valueExpr->astParent(), ".") && astIsLhs(valueExpr) && valueExpr->astParent()->valueType() && valueExpr->astParent()->valueType()->pointer == 0) + valueExpr = valueExpr->astParent(); + } + const Token *derefValue = nullptr; // dereferenced value expression + if (alloc != NO_ALLOC) { + const int arrayDim = (vartok->variable() && vartok->variable()->isArray()) ? vartok->variable()->dimensions().size() : 1; + int deref = 0; + derefValue = valueExpr; + while (Token::Match(derefValue->astParent(), "+|-|*|[|.") || + (derefValue->astParent() && derefValue->astParent()->isCast()) || + (deref < arrayDim && Token::simpleMatch(derefValue->astParent(), "&") && derefValue->astParent()->isBinaryOp())) { + const Token * const derefValueParent = derefValue->astParent(); + if (derefValueParent->str() == "*") { + if (derefValueParent->isUnaryOp("*")) + ++deref; + else + break; + } else if (derefValueParent->str() == "[") { + if (astIsLhs(derefValue)) + ++deref; + else + break; + } else if (Token::Match(derefValueParent, "[+-]")) { + if (deref >= arrayDim) + break; + } else if (derefValueParent->str() == ".") + ++deref; + derefValue = derefValueParent; + if (deref < arrayDim) + valueExpr = derefValue; + } + if (deref < arrayDim) { + // todo compare deref with array dimensions + derefValue = nullptr; + } + } else if (vartok->astParent() && vartok->astParent()->isUnaryOp("&")) { + const Token *child = vartok->astParent(); + const Token *parent = child->astParent(); + while (parent && (parent->isCast() || parent->str() == "+")) { + child = parent; + parent = child->astParent(); + } + if (parent && (parent->isUnaryOp("*") || (parent->str() == "[" && astIsLhs(child)))) + derefValue = parent; + } + + if (!valueExpr->astParent()) + return nullptr; + + // FIXME handle address of!! + if (derefValue && derefValue->astParent() && derefValue->astParent()->isUnaryOp("&")) + return nullptr; + + // BAILOUT for binary & without parent + if (Token::simpleMatch(valueExpr->astParent(), "&") && astIsRhs(valueExpr) && Token::Match(valueExpr->astParent()->tokAt(-3), "( %name% ) &")) + return nullptr; + + // safe operations + if (isVoidCast(valueExpr->astParent())) + return nullptr; + if (Token::simpleMatch(valueExpr->astParent(), ".")) { + const Token *parent = valueExpr->astParent(); + while (Token::simpleMatch(parent, ".")) + parent = parent->astParent(); + if (isVoidCast(parent)) + return nullptr; + } + if (alloc != NO_ALLOC) { + if (Token::Match(valueExpr->astParent(), "%comp%|%oror%|&&|?|!")) + return nullptr; + if (Token::Match(valueExpr->astParent(), "%or%|&") && valueExpr->astParent()->isBinaryOp()) + return nullptr; + if (alloc == CTOR_CALL && derefValue && Token::simpleMatch(derefValue->astParent(), "(") && astIsLhs(derefValue)) + return nullptr; + if (Token::simpleMatch(valueExpr->astParent(), "return")) + return nullptr; + } + + // Passing variable to function.. + if (Token::Match(valueExpr->astParent(), "[(,]") && (valueExpr->astParent()->str() == "," || astIsRhs(valueExpr))) { + const Token *parent = valueExpr->astParent(); + while (Token::simpleMatch(parent, ",")) + parent = parent->astParent(); + if (Token::simpleMatch(parent, "{")) + return valueExpr; + const int use = isFunctionParUsage(valueExpr, library, pointer, alloc, indirect); + return (use>0) ? valueExpr : nullptr; + } + if (derefValue && Token::Match(derefValue->astParent(), "[(,]") && (derefValue->astParent()->str() == "," || astIsRhs(derefValue))) { + const int use = isFunctionParUsage(derefValue, library, false, NO_ALLOC, indirect); + return (use>0) ? derefValue : nullptr; + } + if (valueExpr->astParent()->isUnaryOp("&")) { + const Token *parent = valueExpr->astParent(); + if (Token::Match(parent->astParent(), "[(,]") && (parent->astParent()->str() == "," || astIsRhs(parent))) { + const int use = isFunctionParUsage(valueExpr, library, pointer, alloc, indirect); + return (use>0) ? valueExpr : nullptr; + } + return nullptr; + } + + // Assignments; + // * Is this LHS in assignment + // * Passing address in RHS to pointer variable + { + const Token *tok = derefValue ? derefValue : valueExpr; + if (alloc == NO_ALLOC) { + while (tok->valueType() && tok->valueType()->pointer == 0 && Token::simpleMatch(tok->astParent(), ".")) + tok = tok->astParent(); + } + if (Token::simpleMatch(tok->astParent(), "=")) { + if (astIsLhs(tok)) { + if (alloc == ARRAY || !derefValue || !derefValue->isUnaryOp("*") || !pointer) + return nullptr; + const Token* deref = derefValue->astOperand1(); + while (deref && deref->isCast()) + deref = deref->astOperand1(); + if (deref == vartok || Token::simpleMatch(deref, "+")) + return nullptr; + } + if (alloc != NO_ALLOC && astIsRhs(valueExpr)) + return nullptr; + } + } + + // Initialize reference variable + if (Token::Match((derefValue ? derefValue : vartok)->astParent(), "(|=") && astIsRhs(derefValue ? derefValue : vartok)) { + const Token *rhstok = derefValue ? derefValue : vartok; + const Token *lhstok = rhstok->astParent()->astOperand1(); + const Variable *lhsvar = lhstok ? lhstok->variable() : nullptr; + if (lhsvar && lhsvar->isReference() && lhsvar->nameToken() == lhstok) + return nullptr; + } + + // range for loop + if (Token::simpleMatch(valueExpr->astParent(), ":") && + valueExpr->astParent()->astParent() && + Token::simpleMatch(valueExpr->astParent()->astParent()->previous(), "for (")) { + if (astIsLhs(valueExpr)) + return nullptr; + // Taking value by reference? + const Token *lhs = valueExpr->astParent()->astOperand1(); + if (lhs && lhs->variable() && lhs->variable()->nameToken() == lhs && lhs->variable()->isReference()) + return nullptr; + } + + // Stream read/write + // FIXME this code is a hack!! + if (cpp && Token::Match(valueExpr->astParent(), "<<|>>")) { + if (isLikelyStreamRead(vartok->previous())) + return nullptr; + + if (const auto* vt = valueExpr->valueType()) { + if (vt->type == ValueType::Type::VOID) + return nullptr; + // passing a char* to a stream will dereference it + if ((alloc == CTOR_CALL || alloc == ARRAY) && vt->pointer && vt->type != ValueType::Type::CHAR && vt->type != ValueType::Type::WCHAR_T) + return nullptr; + } + } + if (astIsRhs(derefValue) && isLikelyStreamRead(derefValue->astParent())) + return nullptr; + + // Assignment with overloaded & + if (cpp && Token::simpleMatch(valueExpr->astParent(), "&") && astIsRhs(valueExpr)) { + const Token *parent = valueExpr->astParent(); + while (Token::simpleMatch(parent, "&") && parent->isBinaryOp()) + parent = parent->astParent(); + if (!parent) { + const Token *lhs = valueExpr->astParent(); + while (Token::simpleMatch(lhs, "&") && lhs->isBinaryOp()) + lhs = lhs->astOperand1(); + if (lhs && lhs->isName() && (!lhs->valueType() || lhs->valueType()->type <= ValueType::Type::CONTAINER)) + return nullptr; // <- possible assignment + } + } + + return derefValue ? derefValue : valueExpr; +} + +const Token* CheckUninitVar::isVariableUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect) const +{ + return isVariableUsage(vartok, mSettings->library, pointer, alloc, indirect); +} + +/*** + * Is function parameter "used" so a "usage of uninitialized variable" can + * be written? If parameter is passed "by value" then it is "used". If it + * is passed "by reference" then it is not necessarily "used". + * @return -1 => unknown 0 => not used 1 => used + */ +int CheckUninitVar::isFunctionParUsage(const Token *vartok, const Library& library, bool pointer, Alloc alloc, int indirect) +{ + bool unknown = false; + const Token *parent = getAstParentSkipPossibleCastAndAddressOf(vartok, &unknown); + if (unknown || !Token::Match(parent, "[(,]")) + return -1; + + // locate start parentheses in function call.. + int argumentNumber = 0; + const Token *start = vartok; + while (start && !Token::Match(start, "[;{}(]")) { + if (start->str() == ")") + start = start->link(); + else if (start->str() == ",") + ++argumentNumber; + start = start->previous(); + } + if (!start) + return -1; + + if (Token::simpleMatch(start->link(), ") {") && Token::Match(start->previous(), "if|for|while|switch")) + return (!pointer || alloc == NO_ALLOC); + + // is this a function call? + if (Token::Match(start->previous(), "%name% (")) { + const bool address(vartok->previous()->str() == "&"); + const bool array(vartok->variable() && vartok->variable()->isArray()); + // check how function handle uninitialized data arguments.. + const Function *func = start->previous()->function(); + if (func) { + const Variable *arg = func->getArgumentVar(argumentNumber); + if (arg) { + const Token *argStart = arg->typeStartToken(); + if (!address && !array && Token::Match(argStart, "%type% %name%| [,)]")) + return 1; + if (pointer && !address && alloc == NO_ALLOC && Token::Match(argStart, "%type% * %name% [,)]")) + return 1; + while (argStart->previous() && argStart->previous()->isName()) + argStart = argStart->previous(); + if (Token::Match(argStart, "const %type% & %name% [,)]")) { + // If it's a record it's ok to pass a partially uninitialized struct. + if (vartok->variable() && vartok->variable()->valueType() && vartok->variable()->valueType()->type == ValueType::Type::RECORD) + return -1; + return 1; + } + if ((pointer || address) && Token::Match(argStart, "const %type% %name% [") && Token::Match(argStart->linkAt(3), "] [,)]")) + return 1; + } + + } else if (Token::Match(start->previous(), "if|while|for")) { + // control-flow statement reading the variable "by value" + return alloc == NO_ALLOC; + } else { + const bool isnullbad = library.isnullargbad(start->previous(), argumentNumber + 1); + if (indirect == 0 && pointer && !address && isnullbad && alloc == NO_ALLOC) + return 1; + bool hasIndirect = false; + const bool isuninitbad = library.isuninitargbad(start->previous(), argumentNumber + 1, indirect, &hasIndirect); + if (alloc != NO_ALLOC) + return (isnullbad || hasIndirect) && isuninitbad; + return isuninitbad && (!address || isnullbad); + } + } + + // unknown + return -1; +} + +int CheckUninitVar::isFunctionParUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect) const +{ + return CheckUninitVar::isFunctionParUsage(vartok, mSettings->library, pointer, alloc, indirect); +} + +bool CheckUninitVar::isMemberVariableAssignment(const Token *tok, const std::string &membervar) const +{ + if (Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar) { + if (Token::Match(tok->tokAt(3), "[=.[]")) + return true; + if (Token::Match(tok->tokAt(-2), "[(,=] &")) + return true; + if (isLikelyStreamRead(tok->previous())) + return true; + if ((tok->previous() && tok->previous()->isConstOp()) || Token::Match(tok->previous(), "[|=")) + ; // member variable usage + else if (tok->tokAt(3)->isConstOp()) + ; // member variable usage + else if (Token::Match(tok->previous(), "[(,] %name% . %name% [,)]") && + 1 == isFunctionParUsage(tok, false, NO_ALLOC)) { + return false; + } else + return true; + } else if (tok->strAt(1) == "=") + return true; + else if (Token::Match(tok, "%var% . %name% (")) { + const Token *ftok = tok->tokAt(2); + if (!ftok->function() || !ftok->function()->isConst()) + // TODO: Try to determine if membervar is assigned in method + return true; + } else if (tok->strAt(-1) == "&") { + if (Token::Match(tok->tokAt(-2), "[(,] & %name%")) { + // locate start parentheses in function call.. + int argumentNumber = 0; + const Token *ftok = tok; + while (ftok && !Token::Match(ftok, "[;{}(]")) { + if (ftok->str() == ")") + ftok = ftok->link(); + else if (ftok->str() == ",") + ++argumentNumber; + ftok = ftok->previous(); + } + + // is this a function call? + ftok = ftok ? ftok->previous() : nullptr; + if (Token::Match(ftok, "%name% (")) { + // check how function handle uninitialized data arguments.. + const Function *function = ftok->function(); + + if (!function && mSettings) { + // Function definition not seen, check if direction is specified in the library configuration + const Library::ArgumentChecks::Direction argDirection = mSettings->library.getArgDirection(ftok, 1 + argumentNumber); + if (argDirection == Library::ArgumentChecks::Direction::DIR_IN) + return false; + if (argDirection == Library::ArgumentChecks::Direction::DIR_OUT) + return true; + } + + const Variable *arg = function ? function->getArgumentVar(argumentNumber) : nullptr; + const Token *argStart = arg ? arg->typeStartToken() : nullptr; + while (argStart && argStart->previous() && argStart->previous()->isName()) + argStart = argStart->previous(); + if (Token::Match(argStart, "const struct| %type% * const| %name% [,)]")) + return false; + } + + else if (ftok && Token::simpleMatch(ftok->previous(), "= * (")) + return false; + } + return true; + } + return false; +} + +bool CheckUninitVar::isMemberVariableUsage(const Token *tok, bool isPointer, Alloc alloc, const std::string &membervar) const +{ + if (Token::Match(tok->previous(), "[(,] %name% . %name% [,)]") && + tok->strAt(2) == membervar) { + const int use = isFunctionParUsage(tok, isPointer, alloc); + if (use == 1) + return true; + } + + if (isMemberVariableAssignment(tok, membervar)) + return false; + + if (Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar && !(tok->tokAt(-2)->variable() && tok->tokAt(-2)->variable()->isReference())) { + const Token *parent = tok->next()->astParent(); + return !parent || !parent->isUnaryOp("&"); + } + if (!isPointer && !Token::simpleMatch(tok->astParent(), ".") && Token::Match(tok->previous(), "[(,] %name% [,)]") && isVariableUsage(tok, isPointer, alloc)) + return true; + + if (!isPointer && Token::Match(tok->previous(), "= %name% ;")) { + const Token* lhs = tok->previous()->astOperand1(); + return !(lhs && lhs->variable() && lhs->variable()->isReference() && lhs == lhs->variable()->nameToken()); + } + + // = *(&var); + 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; + + // TODO: this used to be experimental - enable or remove see #5586 + if ((false) && // NOLINT(readability-simplify-boolean-expr) + !isPointer && + Token::Match(tok->tokAt(-2), "[(,] & %name% [,)]") && + isVariableUsage(tok, isPointer, alloc)) + return true; + + return false; +} + +void CheckUninitVar::uninitdataError(const Token *tok, const std::string &varname) +{ + reportError(tok, Severity::error, "uninitdata", "$symbol:" + varname + "\nMemory is allocated but not initialized: $symbol", CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal); +} + +void CheckUninitVar::uninitvarError(const Token *tok, const std::string &varname, ErrorPath errorPath) +{ + if (diag(tok)) + return; + errorPath.emplace_back(tok, ""); + reportError(errorPath, + Severity::error, + "legacyUninitvar", + "$symbol:" + varname + "\nUninitialized variable: $symbol", + CWE_USE_OF_UNINITIALIZED_VARIABLE, + Certainty::normal); +} + +void CheckUninitVar::uninitvarError(const Token* tok, const ValueFlow::Value& v) +{ + if (!mSettings->isEnabled(&v)) + return; + if (diag(tok)) + return; + const Token* ltok = tok; + if (tok && Token::simpleMatch(tok->astParent(), ".") && astIsRHS(tok)) + ltok = tok->astParent(); + const std::string& varname = ltok ? ltok->expressionString() : "x"; + ErrorPath errorPath = v.errorPath; + errorPath.emplace_back(tok, ""); + auto severity = v.isKnown() ? Severity::error : Severity::warning; + auto certainty = v.isInconclusive() ? Certainty::inconclusive : Certainty::normal; + if (v.subexpressions.empty()) { + reportError(errorPath, + severity, + "uninitvar", + "$symbol:" + varname + "\nUninitialized variable: $symbol", + CWE_USE_OF_UNINITIALIZED_VARIABLE, + certainty); + return; + } + std::string vars = v.subexpressions.size() == 1 ? "variable: " : "variables: "; + std::string prefix; + for (const std::string& var : v.subexpressions) { + vars += prefix + varname + "." + var; + prefix = ", "; + } + reportError(errorPath, + severity, + "uninitvar", + "$symbol:" + varname + "\nUninitialized " + vars, + CWE_USE_OF_UNINITIALIZED_VARIABLE, + certainty); +} + +void CheckUninitVar::uninitStructMemberError(const Token *tok, const std::string &membername) +{ + reportError(tok, + Severity::error, + "uninitStructMember", + "$symbol:" + membername + "\nUninitialized struct member: $symbol", CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal); +} + +void CheckUninitVar::valueFlowUninit() +{ + logChecker("CheckUninitVar::valueFlowUninit"); + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + std::unordered_set ids; + for (const bool subfunction : {false, true}) { + // check every executable scope + for (const Scope* scope : symbolDatabase->functionScopes) { + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (isUnevaluated(tok)) { + tok = tok->linkAt(1); + continue; + } + if (ids.count(tok->exprId()) > 0) + continue; + if (!tok->variable() && !tok->isUnaryOp("*") && !tok->isUnaryOp("&")) + continue; + if (Token::Match(tok, "%name% (")) + continue; + const Token* parent = tok->astParent(); + while (Token::simpleMatch(parent, ".")) + parent = parent->astParent(); + if (parent && parent->isUnaryOp("&")) + continue; + if (isVoidCast(parent)) + continue; + auto v = std::find_if( + tok->values().cbegin(), tok->values().cend(), std::mem_fn(&ValueFlow::Value::isUninitValue)); + if (v == tok->values().cend()) + continue; + if (v->tokvalue && ids.count(v->tokvalue->exprId()) > 0) + continue; + if (subfunction == (v->path == 0)) + continue; + if (v->isInconclusive()) + continue; + if (v->indirect > 1 || v->indirect < 0) + continue; + bool uninitderef = false; + if (tok->variable()) { + bool unknown; + const bool isarray = tok->variable()->isArray(); + if (isarray && tok->variable()->isMember()) + continue; // Todo: this is a bailout + const bool deref = CheckNullPointer::isPointerDeRef(tok, unknown, *mSettings); + uninitderef = deref && v->indirect == 0; + const bool isleaf = isLeafDot(tok) || uninitderef; + if (!isleaf && Token::Match(tok->astParent(), ". %name%") && (tok->astParent()->next()->varId() || tok->astParent()->next()->isEnumerator())) + continue; + } + const ExprUsage usage = getExprUsage(tok, v->indirect, *mSettings); + if (usage == ExprUsage::NotUsed || usage == ExprUsage::Inconclusive) + continue; + if (!v->subexpressions.empty() && usage == ExprUsage::PassedByReference) + continue; + if (usage != ExprUsage::Used) { + if (!(Token::Match(tok->astParent(), ". %name% (|[") && uninitderef) && + isVariableChanged(tok, v->indirect, mSettings)) + continue; + bool inconclusive = false; + if (isVariableChangedByFunctionCall(tok, v->indirect, mSettings, &inconclusive) || inconclusive) + continue; + } + uninitvarError(tok, *v); + ids.insert(tok->exprId()); + if (v->tokvalue) + ids.insert(v->tokvalue->exprId()); + } + } + } +} + +// NOLINTNEXTLINE(readability-non-const-parameter) - used as callback so we need to preserve the signature +static bool isVariableUsage(const Settings &settings, const Token *vartok, MathLib::bigint *value) +{ + (void)value; + return CheckUninitVar::isVariableUsage(vartok, settings.library, true, CheckUninitVar::Alloc::ARRAY); +} + +// a Clang-built executable will crash when using the anonymous MyFileInfo later on - so put it in a unique namespace for now +// see https://trac.cppcheck.net/ticket/12108 for more details +#ifdef __clang__ +inline namespace CheckUninitVar_internal +#else +namespace +#endif +{ + /* data for multifile checking */ + class MyFileInfo : public Check::FileInfo { + public: + /** function arguments that data are unconditionally read */ + std::list unsafeUsage; + + /** Convert data into xml string */ + std::string toString() const override + { + return CTU::toString(unsafeUsage); + } + }; +} + +Check::FileInfo *CheckUninitVar::getFileInfo(const Tokenizer &tokenizer, const Settings &settings) const +{ + const std::list &unsafeUsage = CTU::getUnsafeUsage(tokenizer, settings, ::isVariableUsage); + if (unsafeUsage.empty()) + return nullptr; + + auto *fileInfo = new MyFileInfo; + fileInfo->unsafeUsage = unsafeUsage; + return fileInfo; +} + +Check::FileInfo * CheckUninitVar::loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const +{ + const std::list &unsafeUsage = CTU::loadUnsafeUsageListFromXml(xmlElement); + if (unsafeUsage.empty()) + return nullptr; + + auto *fileInfo = new MyFileInfo; + fileInfo->unsafeUsage = unsafeUsage; + return fileInfo; +} + +bool CheckUninitVar::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) +{ + if (!ctu) + return false; + bool foundErrors = false; + (void)settings; // This argument is unused + + const std::map> callsMap = ctu->getCallsMap(); + + for (const Check::FileInfo* fi1 : fileInfo) { + const MyFileInfo *fi = dynamic_cast(fi1); + if (!fi) + continue; + for (const CTU::FileInfo::UnsafeUsage &unsafeUsage : fi->unsafeUsage) { + const CTU::FileInfo::FunctionCall *functionCall = nullptr; + + const std::list &locationList = + CTU::FileInfo::getErrorPath(CTU::FileInfo::InvalidValueType::uninit, + unsafeUsage, + callsMap, + "Using argument ARG", + &functionCall, + false); + if (locationList.empty()) + continue; + + const ErrorMessage errmsg(locationList, + emptyString, + Severity::error, + "Using argument " + unsafeUsage.myArgumentName + " that points at uninitialized variable " + functionCall->callArgumentExpression, + "ctuuninitvar", + CWE_USE_OF_UNINITIALIZED_VARIABLE, + Certainty::normal); + errorLogger.reportErr(errmsg); + + foundErrors = true; + } + } + return foundErrors; +} diff --git a/cppcheck-2.14.0/lib/checkuninitvar.h b/cppcheck-2.14.0/lib/checkuninitvar.h new file mode 100644 index 00000000..ed6f1734 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkuninitvar.h @@ -0,0 +1,155 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checkuninitvarH +#define checkuninitvarH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "mathlib.h" +#include "errortypes.h" +#include "tokenize.h" +#include "vfvalue.h" + +#include +#include +#include +#include + +class Scope; +class Token; +class Variable; +class ErrorLogger; +class Settings; +class Library; + +namespace CTU { + class FileInfo; +} + +namespace tinyxml2 { + class XMLElement; +} + + +struct VariableValue { + explicit VariableValue(MathLib::bigint val = 0) : value(val) {} + MathLib::bigint value; + bool notEqual{}; +}; + +/// @addtogroup Checks +/// @{ + + +/** @brief Checking for uninitialized variables */ + +class CPPCHECKLIB CheckUninitVar : public Check { + friend class TestUninitVar; + +public: + /** @brief This constructor is used when registering the CheckUninitVar */ + CheckUninitVar() : Check(myName()) {} + + enum Alloc { NO_ALLOC, NO_CTOR_CALL, CTOR_CALL, ARRAY }; + + static const Token *isVariableUsage(const Token *vartok, const Library &library, bool pointer, Alloc alloc, int indirect = 0); + const Token *isVariableUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect = 0) const; + +private: + /** @brief This constructor is used when running checks. */ + CheckUninitVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger) {} + + /** @brief Run checks against the normal token list */ + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + CheckUninitVar checkUninitVar(&tokenizer, &tokenizer.getSettings(), errorLogger); + checkUninitVar.valueFlowUninit(); + checkUninitVar.check(); + } + + bool diag(const Token* tok); + /** Check for uninitialized variables */ + void check(); + void checkScope(const Scope* scope, const std::set &arrayTypeDefs); + void checkStruct(const Token *tok, const Variable &structvar); + bool checkScopeForVariable(const Token *tok, const Variable& var, bool* const possibleInit, bool* const noreturn, Alloc* const alloc, const std::string &membervar, std::map& variableValue); + const Token* checkExpr(const Token* tok, const Variable& var, const Alloc alloc, bool known, bool* bailout = nullptr) const; + bool checkIfForWhileHead(const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit, Alloc alloc, const std::string &membervar); + bool checkLoopBody(const Token *tok, const Variable& var, const Alloc alloc, const std::string &membervar, const bool suppressErrors); + const Token* checkLoopBodyRecursive(const Token *start, const Variable& var, const Alloc alloc, const std::string &membervar, bool &bailout) const; + void checkRhs(const Token *tok, const Variable &var, Alloc alloc, nonneg int number_of_if, const std::string &membervar); + static int isFunctionParUsage(const Token *vartok, const Library &library, bool pointer, Alloc alloc, int indirect = 0); + int isFunctionParUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect = 0) const; + bool isMemberVariableAssignment(const Token *tok, const std::string &membervar) const; + bool isMemberVariableUsage(const Token *tok, bool isPointer, Alloc alloc, const std::string &membervar) const; + + /** ValueFlow-based checking for uninitialized variables */ + void valueFlowUninit(); + + /** @brief Parse current TU and extract file info */ + Check::FileInfo *getFileInfo(const Tokenizer &tokenizer, const Settings &settings) const override; + + Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const override; + + /** @brief Analyse all file infos for all TU */ + bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) override; + + void uninitvarError(const Token* tok, const ValueFlow::Value& v); + void uninitdataError(const Token *tok, const std::string &varname); + void uninitvarError(const Token *tok, const std::string &varname, ErrorPath errorPath); + void uninitvarError(const Token *tok, const std::string &varname) { + uninitvarError(tok, varname, ErrorPath{}); + } + void uninitvarError(const Token *tok, const std::string &varname, Alloc alloc) { + if (alloc == NO_CTOR_CALL || alloc == CTOR_CALL) + uninitdataError(tok, varname); + else + uninitvarError(tok, varname); + } + void uninitStructMemberError(const Token *tok, const std::string &membername); + + std::set mUninitDiags; + + void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override + { + CheckUninitVar c(nullptr, settings, errorLogger); + + ValueFlow::Value v{}; + + c.uninitvarError(nullptr, v); + c.uninitdataError(nullptr, "varname"); + c.uninitStructMemberError(nullptr, "a.b"); + } + + static std::string myName() { + return "Uninitialized variables"; + } + + std::string classInfo() const override { + return "Uninitialized variables\n" + "- using uninitialized local variables\n" + "- using allocated data before it has been initialized\n"; + } +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkuninitvarH diff --git a/cppcheck-2.14.0/lib/checkunusedfunctions.cpp b/cppcheck-2.14.0/lib/checkunusedfunctions.cpp new file mode 100644 index 00000000..67f0f0c5 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkunusedfunctions.cpp @@ -0,0 +1,476 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#include "checkunusedfunctions.h" + +#include "astutils.h" +#include "errorlogger.h" +#include "errortypes.h" +#include "library.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "tokenlist.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xml.h" + +//--------------------------------------------------------------------------- + +static const CWE CWE561(561U); // Dead Code + +static std::string stripTemplateParameters(const std::string& funcName) { + std::string name = funcName; + const auto pos = name.find('<'); + if (pos > 0 && pos != std::string::npos) + name.erase(pos - 1); + return name; +} + +//--------------------------------------------------------------------------- +// FUNCTION USAGE - Check for unused functions etc +//--------------------------------------------------------------------------- + +void CheckUnusedFunctions::parseTokens(const Tokenizer &tokenizer, const Settings &settings) +{ + const char * const FileName = tokenizer.list.getFiles().front().c_str(); + + const bool doMarkup = settings.library.markupFile(FileName); + + // Function declarations.. + if (!doMarkup) { + const SymbolDatabase* symbolDatabase = tokenizer.getSymbolDatabase(); + for (const Scope* scope : symbolDatabase->functionScopes) { + const Function* func = scope->function; + if (!func || !func->token) + continue; + + // Don't warn about functions that are marked by __attribute__((constructor)) or __attribute__((destructor)) + if (func->isAttributeConstructor() || func->isAttributeDestructor() || func->type != Function::eFunction || func->isOperator()) + continue; + + if (func->isExtern()) + continue; + + mFunctionDecl.emplace_back(func); + + FunctionUsage &usage = mFunctions[stripTemplateParameters(func->name())]; + + if (!usage.lineNumber) + usage.lineNumber = func->token->linenr(); + + // TODO: why always overwrite this but not the filename and line? + usage.fileIndex = func->token->fileIndex(); + const std::string& fileName = tokenizer.list.file(func->token); + + // No filename set yet.. + if (usage.filename.empty()) { + usage.filename = fileName; + } + // Multiple files => filename = "+" + else if (usage.filename != fileName) { + //func.filename = "+"; + usage.usedOtherFile |= usage.usedSameFile; + } + } + } + + // Function usage.. + const Token *lambdaEndToken = nullptr; + for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { + + if (tok == lambdaEndToken) + lambdaEndToken = nullptr; + else if (!lambdaEndToken && tok->str() == "[") + lambdaEndToken = findLambdaEndToken(tok); + + // parsing of library code to find called functions + if (settings.library.isexecutableblock(FileName, tok->str())) { + const Token * markupVarToken = tok->tokAt(settings.library.blockstartoffset(FileName)); + // not found + if (!markupVarToken) + continue; + int scope = 0; + bool start = true; + // find all function calls in library code (starts with '(', not if or while etc) + while ((scope || start) && markupVarToken) { + if (markupVarToken->str() == settings.library.blockstart(FileName)) { + scope++; + start = false; + } else if (markupVarToken->str() == settings.library.blockend(FileName)) + scope--; + else if (!settings.library.iskeyword(FileName, markupVarToken->str())) { + mFunctionCalls.insert(markupVarToken->str()); + if (mFunctions.find(markupVarToken->str()) != mFunctions.end()) + mFunctions[markupVarToken->str()].usedOtherFile = true; + else if (markupVarToken->next()->str() == "(") { + FunctionUsage &func = mFunctions[markupVarToken->str()]; + func.filename = tokenizer.list.getFiles()[markupVarToken->fileIndex()]; + if (func.filename.empty() || func.filename == "+") + func.usedOtherFile = true; + else + func.usedSameFile = true; + } + } + markupVarToken = markupVarToken->next(); + } + } + + if (!doMarkup // only check source files + && settings.library.isexporter(tok->str()) && tok->next() != nullptr) { + const Token * propToken = tok->next(); + while (propToken && propToken->str() != ")") { + if (settings.library.isexportedprefix(tok->str(), propToken->str())) { + const Token* nextPropToken = propToken->next(); + const std::string& value = nextPropToken->str(); + if (mFunctions.find(value) != mFunctions.end()) { + mFunctions[value].usedOtherFile = true; + } + mFunctionCalls.insert(value); + } + if (settings.library.isexportedsuffix(tok->str(), propToken->str())) { + const Token* prevPropToken = propToken->previous(); + const std::string& value = prevPropToken->str(); + if (value != ")" && mFunctions.find(value) != mFunctions.end()) { + mFunctions[value].usedOtherFile = true; + } + mFunctionCalls.insert(value); + } + propToken = propToken->next(); + } + } + + if (doMarkup && settings.library.isimporter(FileName, tok->str()) && tok->next()) { + const Token * propToken = tok->next(); + if (propToken->next()) { + propToken = propToken->next(); + while (propToken && propToken->str() != ")") { + const std::string& value = propToken->str(); + if (!value.empty()) { + mFunctions[value].usedOtherFile = true; + mFunctionCalls.insert(value); + break; + } + propToken = propToken->next(); + } + } + } + + if (settings.library.isreflection(tok->str())) { + const int argIndex = settings.library.reflectionArgument(tok->str()); + if (argIndex >= 0) { + const Token * funcToken = tok->next(); + int index = 0; + std::string value; + while (funcToken) { + if (funcToken->str()==",") { + if (++index == argIndex) + break; + value.clear(); + } else + value += funcToken->str(); + funcToken = funcToken->next(); + } + if (index == argIndex) { + value = value.substr(1, value.length() - 2); + mFunctions[value].usedOtherFile = true; + mFunctionCalls.insert(std::move(value)); + } + } + } + + const Token *funcname = nullptr; + + if (doMarkup) + funcname = Token::Match(tok, "%name% (") ? tok : nullptr; + else if ((lambdaEndToken || tok->scope()->isExecutable()) && Token::Match(tok, "%name% (")) { + funcname = tok; + } else if ((lambdaEndToken || tok->scope()->isExecutable()) && Token::Match(tok, "%name% <") && Token::simpleMatch(tok->linkAt(1), "> (")) { + funcname = tok; + } else if (Token::Match(tok, "< %name%") && tok->link()) { + funcname = tok->next(); + while (Token::Match(funcname, "%name% :: %name%")) + funcname = funcname->tokAt(2); + } else if (tok->scope()->type != Scope::ScopeType::eEnum && (Token::Match(tok, "[;{}.,()[=+-/|!?:]") || Token::Match(tok, "return|throw"))) { + funcname = tok->next(); + if (funcname && funcname->str() == "&") + funcname = funcname->next(); + if (funcname && funcname->str() == "::") + funcname = funcname->next(); + while (Token::Match(funcname, "%name% :: %name%")) + funcname = funcname->tokAt(2); + + if (!Token::Match(funcname, "%name% [(),;]:}>]") || funcname->varId()) + continue; + } + + if (!funcname || funcname->isKeyword() || funcname->isStandardType()) + continue; + + // funcname ( => Assert that the end parentheses isn't followed by { + if (Token::Match(funcname, "%name% (|<") && funcname->linkAt(1)) { + const Token *ftok = funcname->next(); + if (ftok->str() == "<") + ftok = ftok->link(); + if (Token::Match(ftok->linkAt(1), ") const|throw|{")) + funcname = nullptr; + } + + if (funcname) { + const auto baseName = stripTemplateParameters(funcname->str()); + FunctionUsage &func = mFunctions[baseName]; + const std::string& called_from_file = tokenizer.list.getFiles()[funcname->fileIndex()]; + + if (func.filename.empty() || func.filename == "+" || func.filename != called_from_file) + func.usedOtherFile = true; + else + func.usedSameFile = true; + + mFunctionCalls.insert(baseName); + } + } +} + + +static bool isOperatorFunction(const std::string & funcName) +{ + /* Operator functions are invalid function names for C, so no need to check + * this in here. As result the returned error function might be incorrect. + * + * List of valid operators can be found at: + * http://en.cppreference.com/w/cpp/language/operators + * + * Conversion functions must be a member function (at least for gcc), so no + * need to cover them for unused functions. + * + * To speed up the comparison, not the whole list of operators is used. + * Instead only the character after the operator prefix is checked to be a + * none alpa numeric value, but the '_', to cover function names like + * "operator_unused". In addition the following valid operators are checked: + * - new + * - new[] + * - delete + * - delete[] + */ + const std::string operatorPrefix = "operator"; + if (funcName.compare(0, operatorPrefix.length(), operatorPrefix) != 0) { + return false; + } + + // Taking care of funcName == "operator", which is no valid operator + if (funcName.length() == operatorPrefix.length()) { + return false; + } + + const char firstOperatorChar = funcName[operatorPrefix.length()]; + if (firstOperatorChar == '_') { + return false; + } + + if (!std::isalnum(firstOperatorChar)) { + return true; + } + + const std::vector additionalOperators = { + "new", "new[]", "delete", "delete[]" + }; + + + return std::find(additionalOperators.cbegin(), additionalOperators.cend(), funcName.substr(operatorPrefix.length())) != additionalOperators.cend(); +} + +#define logChecker(id) \ + do { \ + const ErrorMessage errmsg({}, nullptr, Severity::internal, "logChecker", (id), CWE(0U), Certainty::normal); \ + errorLogger.reportErr(errmsg); \ + } while (false) + +bool CheckUnusedFunctions::check(const Settings& settings, ErrorLogger& errorLogger) const +{ + logChecker("CheckUnusedFunctions::check"); // unusedFunction + + using ErrorParams = std::tuple; + std::vector errors; // ensure well-defined order + + for (std::unordered_map::const_iterator it = mFunctions.cbegin(); it != mFunctions.cend(); ++it) { + const FunctionUsage &func = it->second; + if (func.usedOtherFile || func.filename.empty()) + continue; + if (settings.library.isentrypoint(it->first)) + continue; + if (!func.usedSameFile) { + if (isOperatorFunction(it->first)) + continue; + std::string filename; + if (func.filename != "+") + filename = func.filename; + errors.emplace_back(filename, func.fileIndex, 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."; + mErrorLogger->reportErr( errmsg.str() ); + errors = true; + */ + } + } + std::sort(errors.begin(), errors.end()); + for (const auto& e : errors) + unusedFunctionError(errorLogger, std::get<0>(e), std::get<1>(e), std::get<2>(e), std::get<3>(e)); + return !errors.empty(); +} + +void CheckUnusedFunctions::unusedFunctionError(ErrorLogger& errorLogger, + const std::string &filename, unsigned int fileIndex, unsigned int lineNumber, + const std::string &funcname) +{ + std::list locationList; + if (!filename.empty()) { + locationList.emplace_back(filename, lineNumber, 0); + locationList.back().fileIndex = fileIndex; + } + + const ErrorMessage errmsg(std::move(locationList), emptyString, Severity::style, "$symbol:" + funcname + "\nThe function '$symbol' is never used.", "unusedFunction", CWE561, Certainty::normal); + errorLogger.reportErr(errmsg); +} + +CheckUnusedFunctions::FunctionDecl::FunctionDecl(const Function *f) + : functionName(f->name()), lineNumber(f->token->linenr()) +{} + +std::string CheckUnusedFunctions::analyzerInfo() const +{ + std::ostringstream ret; + for (const FunctionDecl &functionDecl : mFunctionDecl) { + ret << " \n"; + } + for (const std::string &fc : mFunctionCalls) { + ret << " \n"; + } + return ret.str(); +} + +namespace { + struct Location { + Location() : lineNumber(0) {} + Location(std::string f, const int l) : fileName(std::move(f)), lineNumber(l) {} + std::string fileName; + int lineNumber; + }; +} + +void CheckUnusedFunctions::analyseWholeProgram(const Settings &settings, ErrorLogger &errorLogger, const std::string &buildDir) +{ + std::map decls; + std::set calls; + + const std::string filesTxt(buildDir + "/files.txt"); + std::ifstream fin(filesTxt.c_str()); + std::string filesTxtLine; + while (std::getline(fin, filesTxtLine)) { + const std::string::size_type firstColon = filesTxtLine.find(':'); + if (firstColon == std::string::npos) + continue; + const std::string::size_type secondColon = filesTxtLine.find(':', firstColon+1); + if (secondColon == std::string::npos) + continue; + const std::string xmlfile = buildDir + '/' + filesTxtLine.substr(0,firstColon); + const std::string sourcefile = filesTxtLine.substr(secondColon+1); + + tinyxml2::XMLDocument doc; + const tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str()); + if (error != tinyxml2::XML_SUCCESS) + continue; + + const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement(); + if (rootNode == nullptr) + continue; + + for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (std::strcmp(e->Name(), "FileInfo") != 0) + continue; + const char *checkattr = e->Attribute("check"); + if (checkattr == nullptr || std::strcmp(checkattr,"CheckUnusedFunctions") != 0) + continue; + for (const tinyxml2::XMLElement *e2 = e->FirstChildElement(); e2; e2 = e2->NextSiblingElement()) { + const char* functionName = e2->Attribute("functionName"); + if (functionName == nullptr) + continue; + if (std::strcmp(e2->Name(),"functioncall") == 0) { + calls.insert(functionName); + continue; + } + if (std::strcmp(e2->Name(),"functiondecl") == 0) { + const char* lineNumber = e2->Attribute("lineNumber"); + if (lineNumber) { + // cppcheck-suppress templateInstantiation - TODO: fix this - see #11631 + decls[functionName] = Location(sourcefile, strToInt(lineNumber)); + } + } + } + } + } + + for (std::map::const_iterator decl = decls.cbegin(); decl != decls.cend(); ++decl) { + const std::string &functionName = stripTemplateParameters(decl->first); + + if (settings.library.isentrypoint(functionName)) + continue; + + if (calls.find(functionName) == calls.end() && !isOperatorFunction(functionName)) { + const Location &loc = decl->second; + unusedFunctionError(errorLogger, loc.fileName, /*fileIndex*/ 0, loc.lineNumber, functionName); + } + } +} + +void CheckUnusedFunctions::updateFunctionData(const CheckUnusedFunctions& check) +{ + for (const auto& entry : check.mFunctions) + { + FunctionUsage &usage = mFunctions[entry.first]; + if (!usage.lineNumber) + usage.lineNumber = entry.second.lineNumber; + // TODO: why always overwrite this but not the filename and line? + usage.fileIndex = entry.second.fileIndex; + if (usage.filename.empty()) + usage.filename = entry.second.filename; + // cppcheck-suppress bitwiseOnBoolean - TODO: FP + usage.usedOtherFile |= entry.second.usedOtherFile; + // cppcheck-suppress bitwiseOnBoolean - TODO: FP + usage.usedSameFile |= entry.second.usedSameFile; + } + mFunctionDecl.insert(mFunctionDecl.cend(), check.mFunctionDecl.cbegin(), check.mFunctionDecl.cend()); + mFunctionCalls.insert(check.mFunctionCalls.cbegin(), check.mFunctionCalls.cend()); +} diff --git a/cppcheck-2.14.0/lib/checkunusedfunctions.h b/cppcheck-2.14.0/lib/checkunusedfunctions.h new file mode 100644 index 00000000..65b22eb0 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkunusedfunctions.h @@ -0,0 +1,94 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 +#include +#include +#include + +class ErrorLogger; +class Function; +class Settings; +class Tokenizer; + +/** @brief Check for functions never called */ +/// @{ + +class CPPCHECKLIB CheckUnusedFunctions { + friend class TestSuppressions; + friend class TestSingleExecutorBase; + friend class TestProcessExecutorBase; + friend class TestThreadExecutorBase; + friend class TestUnusedFunctions; + +public: + CheckUnusedFunctions() = default; + + // Parse current tokens and determine.. + // * Check what functions are used + // * What functions are declared + void parseTokens(const Tokenizer &tokenizer, const Settings &settings); + + std::string analyzerInfo() const; + + static void analyseWholeProgram(const Settings &settings, ErrorLogger& errorLogger, const std::string &buildDir); + + static void getErrorMessages(ErrorLogger &errorLogger) { + unusedFunctionError(errorLogger, emptyString, 0, 0, "funcName"); + } + + // Return true if an error is reported. + bool check(const Settings& settings, ErrorLogger& errorLogger) const; + + void updateFunctionData(const CheckUnusedFunctions& check); + +private: + static void unusedFunctionError(ErrorLogger& errorLogger, + const std::string &filename, unsigned int fileIndex, unsigned int lineNumber, + const std::string &funcname); + + struct CPPCHECKLIB FunctionUsage { + std::string filename; + unsigned int lineNumber{}; + unsigned int fileIndex{}; + bool usedSameFile{}; + bool usedOtherFile{}; + }; + + std::unordered_map mFunctions; + + class CPPCHECKLIB FunctionDecl { + public: + explicit FunctionDecl(const Function *f); + std::string functionName; + unsigned int lineNumber; + }; + std::list mFunctionDecl; + std::set mFunctionCalls; +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkunusedfunctionsH diff --git a/cppcheck-2.14.0/lib/checkunusedvar.cpp b/cppcheck-2.14.0/lib/checkunusedvar.cpp new file mode 100644 index 00000000..6732c450 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkunusedvar.cpp @@ -0,0 +1,1747 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#include "checkunusedvar.h" + +#include "astutils.h" +#include "errortypes.h" +#include "fwdanalysis.h" +#include "library.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "utils.h" +#include "valueflow.h" + +#include +#include +#include +#include +#include +//--------------------------------------------------------------------------- + +// Register this check class (by creating a static instance of it) +namespace { + CheckUnusedVar instance; +} + +static const CWE CWE563(563U); // Assignment to Variable without Use ('Unused Variable') +static const CWE CWE665(665U); // Improper Initialization + +/** Is scope a raii class scope */ +static bool isRaiiClassScope(const Scope *classScope) +{ + return classScope && classScope->getDestructor() != nullptr; +} + +/** Is ValueType a raii class? */ +static bool isRaiiClass(const ValueType *valueType, bool cpp, bool defaultReturn = true) +{ + if (!cpp) + return false; + + if (!valueType) + return defaultReturn; + + if ((valueType->smartPointerType && isRaiiClassScope(valueType->smartPointerType->classScope)) || (!valueType->smartPointerType && valueType->type == ValueType::Type::SMART_POINTER)) + return true; + + switch (valueType->type) { + case ValueType::Type::UNKNOWN_TYPE: + case ValueType::Type::NONSTD: + return defaultReturn; + + case ValueType::Type::RECORD: + if (isRaiiClassScope(valueType->typeScope)) + return true; + return defaultReturn; + + case ValueType::Type::POD: + case ValueType::Type::SMART_POINTER: + case ValueType::Type::CONTAINER: + case ValueType::Type::ITERATOR: + case ValueType::Type::VOID: + case ValueType::Type::BOOL: + case ValueType::Type::CHAR: + case ValueType::Type::SHORT: + case ValueType::Type::WCHAR_T: + case ValueType::Type::INT: + case ValueType::Type::LONG: + case ValueType::Type::LONGLONG: + case ValueType::Type::UNKNOWN_INT: + case ValueType::Type::FLOAT: + case ValueType::Type::DOUBLE: + case ValueType::Type::LONGDOUBLE: + return false; + } + + return defaultReturn; +} + +/** + * @brief This class is used create a list of variables within a function. + */ +class Variables { +public: + enum VariableType { standard, array, pointer, reference, pointerArray, referenceArray, pointerPointer, none }; + + /** Store information about variable usage */ + class VariableUsage { + public: + explicit VariableUsage(const Variable *var = nullptr, + VariableType type = standard, + bool read = false, + bool write = false, + bool modified = false, + bool allocateMemory = false) : + _var(var), + _lastAccess(var ? var->nameToken() : nullptr), + mType(type), + _read(read), + _write(write), + _modified(modified), + _allocateMemory(allocateMemory) {} + + /** variable is used.. set both read+write */ + void use() { + _read = true; + _write = true; + } + + /** is variable unused? */ + bool unused() const { + return (!_read && !_write); + } + + std::set _aliases; + std::set _assignments; + + const Variable* _var; + const Token* _lastAccess; + VariableType mType; + bool _read; + bool _write; + bool _modified; // read/modify/write + bool _allocateMemory; + }; + + void clear() { + mVarUsage.clear(); + } + const std::map &varUsage() const { + return mVarUsage; + } + void addVar(const Variable *var, VariableType type, bool write_); + void allocateMemory(nonneg int varid, const Token* tok); + void read(nonneg int varid, const Token* tok); + void readAliases(nonneg int varid, const Token* tok); + void readAll(nonneg int varid, const Token* tok); + void write(nonneg int varid, const Token* tok); + void writeAliases(nonneg int varid, const Token* tok); + void writeAll(nonneg int varid, const Token* tok); + void use(nonneg int varid, const Token* tok); + void modified(nonneg int varid, const Token* tok); + VariableUsage *find(nonneg int varid); + void alias(nonneg int varid1, nonneg int varid2, bool replace); + void erase(nonneg int varid) { + mVarUsage.erase(varid); + } + void eraseAliases(nonneg int varid); + void eraseAll(nonneg int varid); + void clearAliases(nonneg int varid); + +private: + + std::map mVarUsage; +}; + + +/** + * Alias the 2 given variables. Either replace the existing aliases if + * they exist or merge them. You would replace an existing alias when this + * assignment is in the same scope as the previous assignment. You might + * merge the aliases when this assignment is in a different scope from the + * previous assignment depending on the relationship of the 2 scopes. + */ +void Variables::alias(nonneg int varid1, nonneg int varid2, bool replace) +{ + VariableUsage *var1 = find(varid1); + VariableUsage *var2 = find(varid2); + + if (!var1 || !var2) + return; + + // alias to self + if (varid1 == varid2) { + var1->use(); + return; + } + + if (replace) { + // remove var1 from all aliases + for (std::set::const_iterator i = var1->_aliases.cbegin(); i != var1->_aliases.cend(); ++i) { + VariableUsage *temp = find(*i); + + if (temp) + temp->_aliases.erase(var1->_var->declarationId()); + } + + // remove all aliases from var1 + var1->_aliases.clear(); + } + + // var1 gets all var2s aliases + for (std::set::const_iterator i = var2->_aliases.cbegin(); i != var2->_aliases.cend(); ++i) { + if (*i != varid1) + var1->_aliases.insert(*i); + } + + // var2 is an alias of var1 + var2->_aliases.insert(varid1); + var1->_aliases.insert(varid2); + + if (var2->mType == Variables::pointer) { + var2->_read = true; + } +} + +void Variables::clearAliases(nonneg int varid) +{ + VariableUsage *usage = find(varid); + + if (usage) { + // remove usage from all aliases + for (std::set::const_iterator i = usage->_aliases.cbegin(); i != usage->_aliases.cend(); ++i) { + VariableUsage *temp = find(*i); + + if (temp) + temp->_aliases.erase(usage->_var->declarationId()); + } + + // remove all aliases from usage + usage->_aliases.clear(); + } +} + +void Variables::eraseAliases(nonneg int varid) +{ + VariableUsage *usage = find(varid); + + if (usage) { + for (std::set::const_iterator aliases = usage->_aliases.cbegin(); aliases != usage->_aliases.cend(); ++aliases) + erase(*aliases); + } +} + +void Variables::eraseAll(nonneg int varid) +{ + eraseAliases(varid); + erase(varid); +} + +void Variables::addVar(const Variable *var, + VariableType type, + bool write_) +{ + if (var->declarationId() > 0) { + mVarUsage.insert(std::make_pair(var->declarationId(), VariableUsage(var, type, false, write_, false))); + } +} + +void Variables::allocateMemory(nonneg int varid, const Token* tok) +{ + VariableUsage *usage = find(varid); + + if (usage) { + usage->_allocateMemory = true; + usage->_lastAccess = tok; + } +} + +void Variables::read(nonneg int varid, const Token* tok) +{ + VariableUsage *usage = find(varid); + + if (usage) { + usage->_read = true; + if (tok) + usage->_lastAccess = tok; + } +} + +void Variables::readAliases(nonneg int varid, const Token* tok) +{ + const VariableUsage *usage = find(varid); + + if (usage) { + for (nonneg int const aliases : usage->_aliases) { + VariableUsage *aliased = find(aliases); + + if (aliased) { + aliased->_read = true; + aliased->_lastAccess = tok; + } + } + } +} + +void Variables::readAll(nonneg int varid, const Token* tok) +{ + read(varid, tok); + readAliases(varid, tok); +} + +void Variables::write(nonneg 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(nonneg int varid, const Token* tok) +{ + VariableUsage *usage = find(varid); + + if (usage) { + for (std::set::const_iterator aliases = usage->_aliases.cbegin(); aliases != usage->_aliases.cend(); ++aliases) { + VariableUsage *aliased = find(*aliases); + + if (aliased) { + aliased->_write = true; + aliased->_lastAccess = tok; + } + } + } +} + +void Variables::writeAll(nonneg int varid, const Token* tok) +{ + write(varid, tok); + writeAliases(varid, tok); +} + +void Variables::use(nonneg int varid, const Token* tok) +{ + VariableUsage *usage = find(varid); + + if (usage) { + usage->use(); + usage->_lastAccess = tok; + + for (std::set::const_iterator aliases = usage->_aliases.cbegin(); aliases != usage->_aliases.cend(); ++aliases) { + VariableUsage *aliased = find(*aliases); + + if (aliased) { + aliased->use(); + aliased->_lastAccess = tok; + } + } + } +} + +void Variables::modified(nonneg int varid, const Token* tok) +{ + VariableUsage *usage = find(varid); + + if (usage) { + if (!usage->_var->isStatic()) + usage->_read = false; + usage->_modified = true; + usage->_lastAccess = tok; + + for (std::set::const_iterator aliases = usage->_aliases.cbegin(); aliases != usage->_aliases.cend(); ++aliases) { + VariableUsage *aliased = find(*aliases); + + if (aliased) { + aliased->_modified = true; + aliased->_lastAccess = tok; + } + } + } +} + +Variables::VariableUsage *Variables::find(nonneg int varid) +{ + if (varid) { + const std::map::iterator i = mVarUsage.find(varid); + if (i != mVarUsage.end()) + return &i->second; + } + return nullptr; +} + +static const Token* doAssignment(Variables &variables, const Token *tok, bool dereference, const Scope *scope) +{ + // a = a + b; + if (Token::Match(tok, "%var% = %var% !!;")) { + const Token* rhsVarTok = tok->tokAt(2); + if (tok->varId() == rhsVarTok->varId()) { + return rhsVarTok; + } + } + + if (Token::Match(tok, "%var% %assign%") && tok->strAt(1) != "=") + return tok->next(); + + const Token* const tokOld = tok; + + // check for aliased variable + const nonneg int varid1 = tok->varId(); + Variables::VariableUsage *var1 = variables.find(varid1); + + if (var1) { + // jump behind '=' + tok = tok->next(); + while (!tok->isAssignmentOp()) { + if (tok->varId()) + variables.read(tok->varId(), tok); + tok = tok->next(); + } + tok = tok->next(); + + if (Token::Match(tok, "( const| struct|union| %type% * ) ( (")) + tok = tok->link()->next(); + + if (Token::Match(tok, "( [(<] const| struct|union| %type% *| [>)]")) + tok = tok->next(); + + if (Token::Match(tok, "(| &| %name%") || + (Token::Match(tok->next(), "< const| struct|union| %type% *| > ( &| %name%"))) { + bool addressOf = false; + + if (Token::Match(tok, "%var% .")) + variables.use(tok->varId(), tok); // use = read + write + + // check for C style cast + if (tok->str() == "(") { + tok = tok->next(); + if (tok->str() == "const") + tok = tok->next(); + + if (Token::Match(tok, "struct|union")) + tok = tok->next(); + + while ((tok->isName() && tok->varId() == 0) || (tok->str() == "*") || (tok->str() == ")")) + tok = tok->next(); + + if (tok->str() == "&") { + addressOf = true; + tok = tok->next(); + } else if (tok->str() == "(") { + tok = tok->next(); + if (tok->str() == "&") { + addressOf = true; + tok = tok->next(); + } + } else if (Token::Match(tok, "%cop% %var%")) { + variables.read(tok->next()->varId(), tok); + } + } + + // check for C++ style cast + else if (tok->str().find("cast") != std::string::npos && + tok->strAt(1) == "<") { + tok = tok->tokAt(2); + if (tok->str() == "const") + tok = tok->next(); + + if (Token::Match(tok, "struct|union")) + tok = tok->next(); + + tok = tok->next(); + if (!tok) + return tokOld; + if (tok->str() == "*") + tok = tok->next(); + + tok = tok->tokAt(2); + if (!tok) + return tokOld; + if (tok->str() == "&") { + addressOf = true; + tok = tok->next(); + } + } + + // no cast, no ? + else if (!Token::Match(tok, "%name% ?")) { + if (tok->str() == "&") { + addressOf = true; + tok = tok->next(); + } else if (tok->str() == "new") + return tokOld; + } + + // check if variable is local + const nonneg int varid2 = tok->varId(); + const Variables::VariableUsage* var2 = variables.find(varid2); + + if (var2) { // local variable (alias or read it) + if (var1->mType == Variables::pointer || var1->mType == Variables::pointerArray) { + if (dereference) + variables.read(varid2, tok); + else { + if (addressOf || + var2->mType == Variables::array || + var2->mType == Variables::pointer) { + bool replace = true; + + // pointerArray => don't replace + if (var1->mType == Variables::pointerArray) + replace = false; + + // check if variable declared in same scope + else if (scope == var1->_var->scope()) + replace = true; + + // not in same scope as declaration + else { + // no other assignment in this scope + if (var1->_assignments.find(scope) == var1->_assignments.end() || + scope->type == Scope::eSwitch) { + // nothing to replace + // cppcheck-suppress duplicateBranch - remove when TODO below is address + if (var1->_assignments.empty()) + replace = false; + + // this variable has previous assignments + else { + // TODO: determine if existing aliases should be replaced or merged + replace = false; + } + } + + // assignment in this scope + else { + // replace when only one other assignment, merge them otherwise + replace = (var1->_assignments.size() == 1); + } + } + + variables.alias(varid1, varid2, replace); + } else if (tok->strAt(1) == "?") { + if (var2->mType == Variables::reference) + variables.readAliases(varid2, tok); + else + variables.read(varid2, tok); + } else { + variables.readAll(varid2, tok); + } + } + } else if (var1->mType == Variables::reference) { + variables.alias(varid1, varid2, true); + } else if (var1->mType == Variables::standard && addressOf) { + variables.alias(varid1, varid2, true); + } else { + if ((var2->mType == Variables::pointer || var2->mType == Variables::pointerArray) && tok->strAt(1) == "[") + variables.readAliases(varid2, tok); + + variables.read(varid2, tok); + } + } else { // not a local variable (or an unsupported local variable) + if (var1->mType == Variables::pointer && !dereference) { + // check if variable declaration is in this scope + if (var1->_var->scope() == scope) { + // If variable is used in RHS then "use" variable + for (const Token *rhs = tok; rhs && rhs->str() != ";"; rhs = rhs->next()) { + if (rhs->varId() == varid1) { + variables.use(varid1, tok); + break; + } + } + variables.clearAliases(varid1); + } else { + // no other assignment in this scope + if (var1->_assignments.find(scope) == var1->_assignments.end()) { + /** + * @todo determine if existing aliases should be discarded + */ + } + + // this assignment replaces the last assignment in this scope + else { + // aliased variables in a larger scope are not supported + // remove all aliases + variables.clearAliases(varid1); + } + } + } + } + } else + tok = tokOld; + + var1->_assignments.insert(scope); + } + + // check for alias to struct member + // char c[10]; a.b = c; + else if (Token::Match(tok->tokAt(-2), "%name% .")) { + const Token *rhsVarTok = tok->tokAt(2); + if (rhsVarTok && rhsVarTok->varId()) { + const nonneg int varid2 = rhsVarTok->varId(); + const Variables::VariableUsage *var2 = variables.find(varid2); + + // struct member aliased to local variable + if (var2 && (var2->mType == Variables::array || + var2->mType == Variables::pointer)) { + // erase aliased variable and all variables that alias it + // to prevent false positives + variables.eraseAll(varid2); + } + } + } + + // Possible pointer alias + else if (Token::Match(tok, "%name% = %name% ;")) { + const nonneg int varid2 = tok->tokAt(2)->varId(); + const Variables::VariableUsage *var2 = variables.find(varid2); + if (var2 && (var2->mType == Variables::array || + var2->mType == Variables::pointer)) { + variables.use(varid2,tok); + } + } + + return tok; +} + +static bool isPartOfClassStructUnion(const Token* tok) +{ + for (; tok; tok = tok->previous()) { + if (tok->str() == "}" || tok->str() == ")") + tok = tok->link(); + else if (tok->str() == "(") + return false; + else if (tok->str() == "{") { + return (tok->strAt(-1) == "struct" || tok->strAt(-2) == "struct" || tok->strAt(-1) == "class" || tok->strAt(-2) == "class" || tok->strAt(-1) == "union" || tok->strAt(-2) == "union"); + } + } + return false; +} + +static bool isVarDecl(const Token *tok) +{ + return tok && tok->variable() && tok->variable()->nameToken() == tok; +} + +// Skip [ .. ] +static const Token * skipBrackets(const Token *tok) +{ + while (tok && tok->str() == "[") + tok = tok->link()->next(); + return tok; +} + + +// Skip [ .. ] . x +static const Token * skipBracketsAndMembers(const Token *tok) +{ + while (tok) { + if (tok->str() == "[") + tok = tok->link()->next(); + else if (Token::Match(tok, ". %name%")) + tok = tok->tokAt(2); + else + break; + } + return tok; +} + +static void useFunctionArgs(const Token *tok, Variables& variables) +{ + // TODO: Match function args to see if they are const or not. Assume that const data is not written. + if (!tok) + return; + if (tok->str() == ",") { + useFunctionArgs(tok->astOperand1(), variables); + useFunctionArgs(tok->astOperand2(), variables); + } else if (Token::Match(tok, "[+:]") && (!tok->valueType() || tok->valueType()->pointer)) { + useFunctionArgs(tok->astOperand1(), variables); + useFunctionArgs(tok->astOperand2(), variables); + } else if (tok->variable() && tok->variable()->isArray()) { + variables.use(tok->varId(), tok); + } +} + +//--------------------------------------------------------------------------- +// Usage of function variables +//--------------------------------------------------------------------------- +void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables) +{ + // Find declarations if the scope is executable.. + if (scope->isExecutable()) { + // Find declarations + for (std::list::const_iterator i = scope->varlist.cbegin(); i != scope->varlist.cend(); ++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() && !(i->valueType() && i->valueType()->type == ValueType::UNKNOWN_TYPE && Token::simpleMatch(i->typeStartToken(), "auto"))) + type = Variables::reference; + else if (i->nameToken()->previous()->str() == "*" && i->nameToken()->strAt(-2) == "*") + type = Variables::pointerPointer; + else if (i->isPointerToArray()) + type = Variables::pointerPointer; + else if (i->isPointer()) + type = Variables::pointer; + else if (mTokenizer->isC() || + i->typeEndToken()->isStandardType() || + i->isStlType() || + isRecordTypeWithoutSideEffects(i->type()) || + mSettings->library.detectContainer(i->typeStartToken()) || + mSettings->library.getTypeCheck("unusedvar", i->typeStartToken()->str()) == Library::TypeCheck::check) + type = Variables::standard; + + if (type == Variables::none || isPartOfClassStructUnion(i->typeStartToken())) + continue; + const Token* defValTok = i->nameToken()->next(); + if (Token::Match(i->nameToken()->previous(), "* %var% ) (")) // function pointer. Jump behind parameter list. + defValTok = defValTok->linkAt(1)->next(); + for (; defValTok; defValTok = defValTok->next()) { + if (defValTok->str() == "[") + defValTok = defValTok->link(); + else if (defValTok->str() == "(" || defValTok->str() == "{" || defValTok->str() == "=" || defValTok->str() == ":") { + variables.addVar(&*i, type, true); + break; + } else if (defValTok->str() == ";" || defValTok->str() == "," || defValTok->str() == ")") { + variables.addVar(&*i, type, i->isStatic() && i->scope()->type != Scope::eFunction); + break; + } + } + if (i->isArray() && i->isClass() && // Array of class/struct members. Initialized by ctor except for std::array + !(i->isStlType() && i->valueType() && i->valueType()->containerTypeToken && i->valueType()->containerTypeToken->isStandardType())) + variables.write(i->declarationId(), i->nameToken()); + if (i->isArray() && Token::Match(i->nameToken(), "%name% [ %var% ]")) // Array index variable read. + variables.read(i->nameToken()->tokAt(2)->varId(), i->nameToken()); + + if (defValTok && defValTok->next()) { + // simple assignment "var = 123" + if (defValTok->str() == "=" && defValTok->next()->str() != "{") { + doAssignment(variables, i->nameToken(), false, scope); + } else { + // could be "var = {...}" OR "var{...}" (since C++11) + const Token* tokBraceStart = nullptr; + if (Token::simpleMatch(defValTok, "= {")) { + // "var = {...}" + tokBraceStart = defValTok->next(); + } else if (defValTok->str() == "{") { + // "var{...}" + tokBraceStart = defValTok; + } + if (tokBraceStart) { + for (const Token* tok = tokBraceStart->next(); tok && tok != tokBraceStart->link(); tok = tok->next()) { + if (tok->varId()) { + // Variables used to initialize the array read. + variables.read(tok->varId(), i->nameToken()); + } + } + } + } + } + } + } + + // Check variable usage + const Token *tok; + if (scope->type == Scope::eFunction) + tok = scope->bodyStart->next(); + else + tok = scope->classDef->next(); + for (; tok && tok != scope->bodyEnd; tok = tok->next()) { + if (tok->str() == "{" && tok != scope->bodyStart && !tok->previous()->varId()) { + if (std::any_of(scope->nestedList.cbegin(), scope->nestedList.cend(), [&](const Scope* s) { + return s->bodyStart == tok; + })) { + checkFunctionVariableUsage_iterateScopes(tok->scope(), variables); // Scan child scope + tok = tok->link(); + } + if (!tok) + break; + } + + if (Token::Match(tok, "asm ( %str% )")) { + variables.clear(); + break; + } + + // templates + if (tok->isName() && endsWith(tok->str(), '>')) { + // TODO: This is a quick fix to handle when constants are used + // as template parameters. Try to handle this better, perhaps + // only remove constants. + variables.clear(); + } + + else if (Token::Match(tok->previous(), "[;{}]")) { + for (const Token* tok2 = tok->next(); tok2; tok2 = tok2->next()) { + if (tok2->varId()) { + // Is this a variable declaration? + const Variable *var = tok2->variable(); + if (!var || var->nameToken() != tok2) + continue; + + // Mark template parameters used in declaration as use.. + if (tok2->strAt(-1) == ">") { + for (const Token *tok3 = tok; tok3 != tok2; tok3 = tok3->next()) { + if (tok3->varId()) + variables.use(tok3->varId(), tok3); + } + } + + // Skip variable declaration.. + tok = tok2->next(); + if (Token::Match(tok, "( %name% )")) // Simple initialization through copy ctor + tok = tok->next(); + else if (Token::Match(tok, "= %var% ;")) { // Simple initialization + tok = tok->next(); + if (!var->isReference()) + variables.read(tok->varId(), tok); + } else if (tok->str() == "[" && Token::simpleMatch(skipBrackets(tok),"= {")) { + const Token * const rhs1 = skipBrackets(tok)->next(); + for (const Token *rhs = rhs1->link(); rhs != rhs1; rhs = rhs->previous()) { + if (rhs->varId()) + variables.readAll(rhs->varId(), rhs); + } + } else if (var->typeEndToken()->str() == ">") // Be careful with types like std::vector + tok = tok->previous(); + break; + } + if (Token::Match(tok2, "[;({=]")) + break; + } + } + // Freeing memory (not considered "using" the pointer if it was also allocated in this function) + if ((Token::Match(tok, "%name% ( %var% )") && mSettings->library.getDeallocFuncInfo(tok)) || + (tok->isCpp() && (Token::Match(tok, "delete %var% ;") || Token::Match(tok, "delete [ ] %var% ;")))) { + nonneg int varid = 0; + if (tok->str() != "delete") { + const Token *varTok = tok->tokAt(2); + varid = varTok->varId(); + tok = varTok->next(); + } else if (tok->strAt(1) == "[") { + const Token *varTok = tok->tokAt(3); + varid = varTok->varId(); + tok = varTok; + } else { + varid = tok->next()->varId(); + tok = tok->next(); + } + + const Variables::VariableUsage *const var = variables.find(varid); + if (var) { + if (!var->_aliases.empty()) + variables.use(varid, tok); + else if (!var->_allocateMemory) + variables.readAll(varid, tok); + } + } + + else if (Token::Match(tok, "return|throw")) { + for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { + if (tok2->varId()) + variables.readAll(tok2->varId(), tok); + else if (tok2->str() == ";") + break; + } + } + + // assignment + else if (Token::Match(tok, "*| ++|--| %name% ++|--| %assign%") || + Token::Match(tok, "*| ( const| %type% *| ) %name% %assign%")) { + bool dereference = false; + bool pre = false; + bool post = false; + + if (tok->str() == "*") { + dereference = true; + tok = tok->next(); + } + + if (Token::Match(tok, "( const| %type% *| ) %name% %assign%")) + tok = tok->link()->next(); + + else if (tok->str() == "(") + tok = tok->next(); + + if (tok->tokType() == Token::eIncDecOp) { + pre = true; + tok = tok->next(); + } + + if (tok->next()->tokType() == Token::eIncDecOp) + post = true; + + const nonneg int varid1 = tok->varId(); + const Token * const start = tok; + + // assignment in while head.. + bool inwhile = false; + { + const Token *parent = tok->astParent(); + while (parent) { + if (Token::simpleMatch(parent->previous(), "while (")) { + inwhile = true; + break; + } + parent = parent->astParent(); + } + } + + tok = doAssignment(variables, tok, dereference, scope); + + if (tok && tok->isAssignmentOp() && tok->str() != "=") { + variables.use(varid1, tok); + if (Token::Match(tok, "%assign% %name%")) { + tok = tok->next(); + variables.read(tok->varId(), tok); + } + } + + if (pre || post) + variables.use(varid1, tok); + + if (dereference) { + const Variables::VariableUsage *const var = variables.find(varid1); + if (var && var->mType == Variables::array) + variables.write(varid1, tok); + variables.writeAliases(varid1, tok); + variables.read(varid1, tok); + } else { + const Variables::VariableUsage *const var = variables.find(varid1); + if (var && (inwhile || start->strAt(-1) == ",")) { + variables.use(varid1, tok); + } else if (var && var->mType == Variables::reference) { + variables.writeAliases(varid1, tok); + variables.read(varid1, tok); + } + // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable + else if (var && var->mType == Variables::pointer && + Token::Match(start, "%name% =") && + findAllocFuncCallToken(start->next()->astOperand2(), mSettings->library)) { + + const Token *allocFuncCallToken = findAllocFuncCallToken(start->next()->astOperand2(), mSettings->library); + const Library::AllocFunc *allocFunc = mSettings->library.getAllocFuncInfo(allocFuncCallToken); + + bool allocateMemory = !allocFunc || Library::ismemory(allocFunc->groupId); + + if (allocFuncCallToken->str() == "new") { + const Token *type = allocFuncCallToken->next(); + + // skip nothrow + if (type->isCpp() && (Token::simpleMatch(type, "( nothrow )") || + Token::simpleMatch(type, "( std :: nothrow )"))) + type = type->link()->next(); + + // is it a user defined type? + if (!type->isStandardType()) { + const Variable *variable = start->variable(); + if (!variable || !isRecordTypeWithoutSideEffects(variable->type())) + allocateMemory = false; + } + } + + if (allocateMemory) + variables.allocateMemory(varid1, tok); + else + variables.write(varid1, tok); + } else if (varid1 && Token::Match(tok, "%varid% .", varid1)) { + variables.read(varid1, tok); + variables.write(varid1, start); + } else { + variables.write(varid1, tok); + } + } + + const Variables::VariableUsage * const var2 = variables.find(tok->varId()); + if (var2) { + if (var2->mType == Variables::reference) { + variables.writeAliases(tok->varId(), tok); + variables.read(tok->varId(), tok); + } else if (tok->varId() != varid1 && Token::Match(tok, "%name% .|[")) + variables.read(tok->varId(), tok); + else if (tok->varId() != varid1 && + var2->mType == Variables::standard && + tok->strAt(-1) != "&") + variables.use(tok->varId(), tok); + } + + const Token * const equal = skipBracketsAndMembers(tok->next()); + + // checked for chained assignments + if (tok != start && equal && equal->str() == "=") { + const nonneg int varId = tok->varId(); + const Variables::VariableUsage * const var = variables.find(varId); + + if (var && var->mType != Variables::reference) { + variables.read(varId,tok); + } + + tok = tok->previous(); + } + } + + // assignment + else if ((Token::Match(tok, "%name% [") && Token::simpleMatch(skipBracketsAndMembers(tok->next()), "=")) || + (tok->isUnaryOp("*") && astIsLHS(tok) && Token::simpleMatch(tok->astParent(), "=") && Token::simpleMatch(tok->astOperand1(), "+"))) { + const Token *eq = tok; + while (eq && !eq->isAssignmentOp()) + eq = eq->astParent(); + + const bool deref = eq && eq->astOperand1() && eq->astOperand1()->valueType() && eq->astOperand1()->valueType()->pointer == 0U; + + if (tok->str() == "*") { + tok = tok->tokAt(2); + if (tok->str() == "(") + tok = tok->link()->next(); + } + + const nonneg int varid = tok->varId(); + const Variables::VariableUsage *var = variables.find(varid); + + if (var) { + // Consider allocating memory separately because allocating/freeing alone does not constitute using the variable + if (var->mType == Variables::pointer && + ((tok->isCpp() && Token::simpleMatch(skipBrackets(tok->next()), "= new")) || + (Token::Match(skipBrackets(tok->next()), "= %name% (") && mSettings->library.getAllocFuncInfo(tok->tokAt(2))))) { + variables.allocateMemory(varid, tok); + } else if (var->mType == Variables::pointer || var->mType == Variables::reference) { + variables.read(varid, tok); + variables.writeAliases(varid, tok); + } else if (var->mType == Variables::pointerArray) { + tok = doAssignment(variables, tok, deref, scope); + } else + variables.writeAll(varid, tok); + } + } + + else if (tok->isCpp() && Token::Match(tok, "[;{}] %var% <<")) { + variables.erase(tok->next()->varId()); + } + + else if (Token::Match(tok, "& %var%")) { + if (tok->astOperand2()) { // bitop + variables.read(tok->next()->varId(), tok); + } else // addressof + variables.use(tok->next()->varId(), tok); // use = read + write + } else if (Token::Match(tok, ">>|>>= %name%")) { + if (isLikelyStreamRead(tok)) + variables.use(tok->next()->varId(), tok); // use = read + write + else + variables.read(tok->next()->varId(), tok); + } else if (Token::Match(tok, "%var% >>|&") && Token::Match(tok->previous(), "[{};:]")) { + variables.read(tok->varId(), tok); + } else if (isLikelyStreamRead(tok->previous())) { + variables.use(tok->varId(), tok); + } + + // function parameter + else if (Token::Match(tok, "[(,] %var% [")) { + variables.use(tok->next()->varId(), tok); // use = read + write + } else if (Token::Match(tok, "[(,] %var% [,)]") && tok->previous()->str() != "*") { + variables.use(tok->next()->varId(), tok); // use = read + write + } else if (Token::Match(tok, "[(,] & %var% [,)]")) { + variables.eraseAll(tok->tokAt(2)->varId()); + } else if (Token::Match(tok, "[(,] (") && + Token::Match(tok->next()->link(), ") %var% [,)[]")) { + variables.use(tok->next()->link()->next()->varId(), tok); // use = read + write + } else if (Token::Match(tok, "[(,] *| *| %var%")) { + const Token* vartok = tok->next(); + while (vartok->str() == "*") + vartok = vartok->next(); + if (!(vartok->variable() && vartok == vartok->variable()->nameToken()) && + !(tok->str() == "(" && !Token::Match(tok->previous(), "%name%"))) + variables.use(vartok->varId(), vartok); + } + + // function + else if (Token::Match(tok, "%name% (")) { + if (tok->varId() && !tok->function()) // operator() + variables.use(tok->varId(), tok); + else + variables.read(tok->varId(), tok); + useFunctionArgs(tok->next()->astOperand2(), variables); + } else if (Token::Match(tok, "std :: ref ( %var% )")) { + variables.eraseAll(tok->tokAt(4)->varId()); + } + + else if (Token::Match(tok->previous(), "[{,] %var% [,}]")) { + variables.use(tok->varId(), tok); + } + + else if (tok->varId() && Token::Match(tok, "%var% .")) { + variables.use(tok->varId(), tok); // use = read + write + } + + else if (tok->str() == ":" && (!tok->valueType() || tok->valueType()->pointer)) { + if (tok->astOperand1()) + variables.use(tok->astOperand1()->varId(), tok->astOperand1()); + if (tok->astOperand2()) + variables.use(tok->astOperand2()->varId(), tok->astOperand2()); + } + + else if (tok->isExtendedOp() && tok->next() && tok->next()->varId() && tok->strAt(2) != "=" && !isVarDecl(tok->next())) { + variables.readAll(tok->next()->varId(), tok); + } + + else if (tok->varId() && !isVarDecl(tok) && tok->next() && (tok->next()->str() == ")" || tok->next()->isExtendedOp())) { + if (Token::Match(tok->tokAt(-2), "%name% ( %var% [,)]") && + !(tok->tokAt(-2)->variable() && tok->tokAt(-2)->variable()->isReference())) + variables.use(tok->varId(), tok); + else + variables.readAll(tok->varId(), tok); + } + + else if (Token::Match(tok, "%var% ;") && Token::Match(tok->previous(), "[;{}:]")) { + variables.readAll(tok->varId(), tok); + } + + // ++|-- + else if (tok->next() && tok->next()->tokType() == Token::eIncDecOp && tok->next()->astOperand1() && tok->next()->astOperand1()->varId()) { + if (tok->next()->astParent()) + variables.use(tok->next()->astOperand1()->varId(), tok); + else + variables.modified(tok->next()->astOperand1()->varId(), tok); + } + + else if (tok->isAssignmentOp()) { + for (const Token *tok2 = tok->next(); tok2 && tok2->str() != ";"; tok2 = tok2->next()) { + if (tok2->varId()) { + if (tok2->strAt(1) == "=") + variables.write(tok2->varId(), tok); + else if (tok2->next() && tok2->next()->isAssignmentOp()) + variables.use(tok2->varId(), tok); + else + variables.read(tok2->varId(), tok); + } + } + } else if (tok->variable() && tok->variable()->isClass() && tok->variable()->type() && + (tok->variable()->type()->needInitialization == Type::NeedInitialization::False) && + tok->next()->str() == ";") { + variables.write(tok->varId(), tok); + } + } +} + +void CheckUnusedVar::checkFunctionVariableUsage() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->checkLibrary && !mSettings->isPremiumEnabled("unusedVariable")) + return; + + logChecker("CheckUnusedVar::checkFunctionVariableUsage"); // style + + // Parse all executing scopes.. + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + auto reportLibraryCfgError = [this](const Token* tok, const std::string& typeName) { + if (mSettings->checkLibrary) { + reportError(tok, + Severity::information, + "checkLibraryCheckType", + "--check-library: Provide configuration for " + typeName); + } + }; + + // only check functions + for (const Scope * scope : symbolDatabase->functionScopes) { + // Bailout when there are lambdas or inline functions + // TODO: Handle lambdas and inline functions properly + if (scope->hasInlineOrLambdaFunction()) + continue; + + for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (findLambdaEndToken(tok)) + // todo: handle lambdas + break; + if (Token::simpleMatch(tok, "try {")) + // todo: check try blocks + tok = tok->linkAt(1); + const Token *varDecl = nullptr; + if (tok->variable() && tok->variable()->nameToken() == tok) { + const Token * eq = tok->next(); + while (Token::simpleMatch(eq, "[")) + eq = eq->link()->next(); + if (Token::simpleMatch(eq, ") (") && Token::simpleMatch(eq->linkAt(1), ") =")) + eq = eq->linkAt(1)->next(); + if (Token::simpleMatch(eq, "=")) { + varDecl = tok; + tok = eq; + } + } + // not assignment/initialization/increment => continue + const bool isAssignment = tok->isAssignmentOp() && tok->astOperand1(); + const bool isInitialization = (Token::Match(tok, "%var% (|{") && tok->variable() && tok->variable()->nameToken() == tok); + const bool isIncrementOrDecrement = (tok->tokType() == Token::Type::eIncDecOp); + if (!isAssignment && !isInitialization && !isIncrementOrDecrement) + continue; + + bool isTrivialInit = false; + if (isInitialization && Token::Match(tok, "%var% { }")) // don't warn for trivial initialization + isTrivialInit = true; + + if (isIncrementOrDecrement && tok->astParent() && precedes(tok, tok->astOperand1())) + continue; + + if (tok->str() == "=" && !(tok->valueType() && tok->valueType()->pointer) && isRaiiClass(tok->valueType(), tok->isCpp(), false)) + continue; + + const bool isPointer = tok->valueType() && (tok->valueType()->pointer || tok->valueType()->type == ValueType::SMART_POINTER); + + if (tok->isName()) { + if (isRaiiClass(tok->valueType(), tok->isCpp(), false) || + (tok->valueType() && tok->valueType()->type == ValueType::RECORD && + (!tok->valueType()->typeScope || !isRecordTypeWithoutSideEffects(tok->valueType()->typeScope->definedType)))) + continue; + tok = tok->next(); + } + if (!isInitialization && tok->astParent() && !tok->astParent()->isAssignmentOp() && tok->str() != "(") { + const Token *parent = tok->astParent(); + while (Token::Match(parent, "%oror%|%comp%|!|&&")) + parent = parent->astParent(); + if (!parent) + continue; + if (!Token::simpleMatch(parent->previous(), "if (")) + continue; + } + // Do not warn about assignment with NULL + if (isPointer && isNullOperand(tok->astOperand2())) + continue; + + if (!tok->astOperand1()) + continue; + + const Token *iteratorToken = tok->astOperand1(); + while (Token::Match(iteratorToken, "[.*]")) + iteratorToken = iteratorToken->astOperand1(); + if (iteratorToken && iteratorToken->variable() && iteratorToken->variable()->typeEndToken()->str().find("iterator") != std::string::npos) + continue; + + const Token *op1tok = tok->astOperand1(); + while (Token::Match(op1tok, ".|[|*")) + op1tok = op1tok->astOperand1(); + + // Assignment in macro => do not warn + if (isAssignment && tok->isExpandedMacro() && op1tok && op1tok->isExpandedMacro()) + continue; + + const Variable *op1Var = op1tok ? op1tok->variable() : nullptr; + if (!op1Var && Token::Match(tok, "(|{") && tok->previous() && tok->previous()->variable()) + op1Var = tok->previous()->variable(); + std::string bailoutTypeName; + if (op1Var) { + if (op1Var->isReference() && op1Var->nameToken() != tok->astOperand1()) + // todo: check references + continue; + + if (op1Var->isStatic()) + // todo: check static variables + continue; + + if (op1Var->nameToken()->isAttributeUnused()) + continue; + + // Avoid FP for union.. + if (op1Var->type() && op1Var->type()->isUnionType()) + continue; + + // Bailout for unknown template classes, we have no idea what side effects such assignments have + if (mTokenizer->isCPP() && + op1Var->isClass() && + (!op1Var->valueType() || op1Var->valueType()->type == ValueType::Type::UNKNOWN_TYPE)) { + // Check in the library if we should bailout or not.. + std::string typeName = op1Var->getTypeName(); + if (startsWith(typeName, "::")) + typeName.erase(typeName.begin(), typeName.begin() + 2); + switch (mSettings->library.getTypeCheck("unusedvar", typeName)) { + case Library::TypeCheck::def: + bailoutTypeName = typeName; + break; + case Library::TypeCheck::check: + break; + case Library::TypeCheck::suppress: + case Library::TypeCheck::checkFiniteLifetime: + continue; + } + } + } + + // Is there a redundant assignment? + const Token *start = tok->findExpressionStartEndTokens().second->next(); + + const Token *expr = varDecl ? varDecl : tok->astOperand1(); + + if (isInitialization) + expr = tok->previous(); + + // Is variable in lhs a union member? + if (tok->previous() && tok->previous()->variable() && tok->previous()->variable()->nameToken()->scope()->type == Scope::eUnion) + continue; + + FwdAnalysis fwdAnalysis(mSettings->library); + const Token* scopeEnd = ValueFlow::getEndOfExprScope(expr, scope, /*smallest*/ false); + if (fwdAnalysis.unusedValue(expr, start, scopeEnd)) { + if (!bailoutTypeName.empty()) { + if (bailoutTypeName != "auto") + reportLibraryCfgError(tok, bailoutTypeName); + continue; + } + if (isTrivialInit && findExpressionChanged(expr, start, scopeEnd, mSettings)) + continue; + + // warn + if (!expr->variable() || !expr->variable()->isMaybeUnused()) + unreadVariableError(tok, expr->expressionString(), false); + } + } + + // varId, usage {read, write, modified} + Variables variables; + + checkFunctionVariableUsage_iterateScopes(scope, variables); + + + // Check usage of all variables in the current scope.. + for (std::map::const_iterator it = variables.varUsage().cbegin(); + it != variables.varUsage().cend(); + ++it) { + const Variables::VariableUsage &usage = it->second; + + // variable has been marked as unused so ignore it + if (usage._var->nameToken()->isAttributeUnused() || usage._var->nameToken()->isAttributeUsed()) + continue; + + // skip things that are only partially implemented to prevent false positives + if (usage.mType == Variables::pointerPointer || + usage.mType == Variables::pointerArray || + usage.mType == Variables::referenceArray) + continue; + + const std::string &varname = usage._var->name(); + const Variable* var = symbolDatabase->getVariableFromVarId(it->first); + + // variable has had memory allocated for it, but hasn't done + // anything with that memory other than, perhaps, freeing it + if (usage.unused() && !usage._modified && usage._allocateMemory) + allocatedButUnusedVariableError(usage._lastAccess, varname); + + // variable has not been written, read, or modified + else if (usage.unused() && !usage._modified) { + if (!usage._var->isMaybeUnused()) { + unusedVariableError(usage._var->nameToken(), varname); + } + } + // variable has not been written but has been modified + else if (usage._modified && !usage._write && !usage._allocateMemory && var && !var->isStlType()) { + if (var->isStatic()) // static variables are initialized by default + continue; + unassignedVariableError(usage._var->nameToken(), varname); + } + // variable has been read but not written + else if (!usage._write && !usage._allocateMemory && var && !var->isStlType() && !isEmptyType(var->type()) && + !(var->type() && var->type()->needInitialization == Type::NeedInitialization::False)) + unassignedVariableError(usage._var->nameToken(), varname); + else if (!usage._var->isMaybeUnused() && !usage._modified && !usage._read && var) { + const Token* vnt = var->nameToken(); + bool error = false; + if (vnt->next()->isSplittedVarDeclEq() || (!var->isReference() && vnt->next()->str() == "=")) { + const Token* nextStmt = vnt->tokAt(2); + if (nextStmt->isExpandedMacro()) { + const Token* parent = nextStmt; + while (parent->astParent() && parent == parent->astParent()->astOperand1()) + parent = parent->astParent(); + if (parent->isAssignmentOp() && parent->isExpandedMacro()) + continue; + } + while (nextStmt && nextStmt->str() != ";") + nextStmt = nextStmt->next(); + error = precedes(usage._lastAccess, nextStmt); + } + if (error) { + if (mTokenizer->isCPP() && var->isClass() && + (!var->valueType() || var->valueType()->type == ValueType::Type::UNKNOWN_TYPE)) { + const std::string typeName = var->getTypeName(); + switch (mSettings->library.getTypeCheck("unusedvar", typeName)) { + case Library::TypeCheck::def: + reportLibraryCfgError(vnt, typeName); + break; + case Library::TypeCheck::check: + break; + case Library::TypeCheck::suppress: + case Library::TypeCheck::checkFiniteLifetime: + error = false; + } + } + if (error) + unreadVariableError(vnt, varname, false); + } + } + } + } +} + +void CheckUnusedVar::unusedVariableError(const Token *tok, const std::string &varname) +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unusedVariable")) + return; + + reportError(tok, Severity::style, "unusedVariable", "$symbol:" + varname + "\nUnused variable: $symbol", CWE563, Certainty::normal); +} + +void CheckUnusedVar::allocatedButUnusedVariableError(const Token *tok, const std::string &varname) +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unusedVariable")) + return; + + reportError(tok, Severity::style, "unusedAllocatedMemory", "$symbol:" + varname + "\nVariable '$symbol' is allocated memory that is never used.", CWE563, Certainty::normal); +} + +void CheckUnusedVar::unreadVariableError(const Token *tok, const std::string &varname, bool modified) +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unusedVariable")) + return; + + if (modified) + reportError(tok, Severity::style, "unreadVariable", "$symbol:" + varname + "\nVariable '$symbol' is modified but its new value is never used.", CWE563, Certainty::normal); + else + reportError(tok, Severity::style, "unreadVariable", "$symbol:" + varname + "\nVariable '$symbol' is assigned a value that is never used.", CWE563, Certainty::normal); +} + +void CheckUnusedVar::unassignedVariableError(const Token *tok, const std::string &varname) +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unusedVariable")) + return; + + reportError(tok, Severity::style, "unassignedVariable", "$symbol:" + varname + "\nVariable '$symbol' is not assigned a value.", CWE665, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Check that all struct members are used +//--------------------------------------------------------------------------- +void CheckUnusedVar::checkStructMemberUsage() +{ + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unusedVariable")) + return; + + logChecker("CheckUnusedVar::checkStructMemberUsage"); // style + + const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); + + for (const Scope &scope : symbolDatabase->scopeList) { + if (scope.type != Scope::eStruct && scope.type != Scope::eClass && scope.type != Scope::eUnion) + continue; + + if (scope.bodyStart->fileIndex() != 0 || scope.className.empty()) + continue; + + if (scope.classDef->isExpandedMacro()) + continue; + + // Packed struct => possibly used by lowlevel code. Struct members might be required by hardware. + if (scope.bodyEnd->isAttributePacked()) + continue; + if (mTokenizer->isPacked(scope.bodyStart)) + continue; + + // Bail out for template struct, members might be used in non-matching instantiations + if (scope.className.find('<') != std::string::npos) + continue; + + // bail out if struct is inherited + const bool isInherited = std::any_of(symbolDatabase->scopeList.cbegin(), symbolDatabase->scopeList.cend(), [&](const Scope& derivedScope) { + const Type* dType = derivedScope.definedType; + return dType && std::any_of(dType->derivedFrom.cbegin(), dType->derivedFrom.cend(), [&](const Type::BaseInfo& derivedFrom) { + return derivedFrom.type == scope.definedType && derivedFrom.access != AccessControl::Private; + }); + }); + + // bail out for extern/global struct + bool bailout = false; + for (const Variable* var : symbolDatabase->variableList()) { + if (var && (var->isExtern() || (var->isGlobal() && !var->isStatic())) && var->typeEndToken()->str() == scope.className) { + bailout = true; + break; + } + if (bailout) + break; + } + if (bailout) + continue; + + // Bail out if some data is casted to struct.. + const std::string castPattern("( struct| " + scope.className + " * ) &| %name%"); + if (Token::findmatch(scope.bodyEnd, castPattern.c_str())) + continue; + + // (struct S){..} + const std::string initPattern("( struct| " + scope.className + " ) {"); + if (Token::findmatch(scope.bodyEnd, initPattern.c_str())) + continue; + + // Bail out if struct is used in sizeof.. + for (const Token *tok = scope.bodyEnd; nullptr != (tok = Token::findsimplematch(tok, "sizeof ("));) { + tok = tok->tokAt(2); + if (Token::Match(tok, ("struct| " + scope.className).c_str())) { + bailout = true; + break; + } + } + if (bailout) + continue; + + for (const Variable &var : scope.varlist) { + // only warn for variables without side effects + if (!var.typeStartToken()->isStandardType() && !var.isPointer() && !astIsContainer(var.nameToken()) && !isRecordTypeWithoutSideEffects(var.type())) + continue; + if (isInherited && !var.isPrivate()) + continue; + + // Check if the struct member variable is used anywhere in the file + bool use = false; + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (Token::Match(tok, ". %name%") && !tok->next()->variable() && !tok->next()->function() && tok->next()->str() == var.name()) { + // not known => assume variable is used + use = true; + break; + } + if (tok->variable() != &var) + continue; + if (tok != var.nameToken()) { + use = true; + break; + } + } + if (!use) { + std::string prefix = "struct"; + if (scope.type == Scope::ScopeType::eClass) + prefix = "class"; + else if (scope.type == Scope::ScopeType::eUnion) + prefix = "union"; + unusedStructMemberError(var.nameToken(), scope.className, var.name(), prefix); + } + } + } +} + +void CheckUnusedVar::unusedStructMemberError(const Token* tok, const std::string& structname, const std::string& varname, const std::string& prefix) +{ + reportError(tok, Severity::style, "unusedStructMember", "$symbol:" + structname + "::" + varname + '\n' + prefix + " member '$symbol' is never used.", CWE563, Certainty::normal); +} + +bool CheckUnusedVar::isRecordTypeWithoutSideEffects(const Type* type) +{ + // a type that has no side effects (no constructors and no members with constructors) + /** @todo false negative: check constructors for side effects */ + const std::pair::iterator,bool> found=mIsRecordTypeWithoutSideEffectsMap.insert( + std::pair(type,false)); //Initialize with side effects for possible recursions + bool & withoutSideEffects = found.first->second; + if (!found.second) + return withoutSideEffects; + + // unknown types are assumed to have side effects + if (!type || !type->classScope) + return (withoutSideEffects = false); + + // Non-empty constructors => possible side effects + for (const Function& f : type->classScope->functionList) { + if (!f.isConstructor() && !f.isDestructor()) + continue; + if (f.argDef && Token::simpleMatch(f.argDef->link(), ") =")) + continue; // ignore default/deleted constructors + const bool emptyBody = (f.functionScope && Token::simpleMatch(f.functionScope->bodyStart, "{ }")); + + const Token* nextToken = f.argDef->link(); + if (Token::simpleMatch(nextToken, ") :")) { + // validating initialization list + nextToken = nextToken->next(); // goto ":" + + for (const Token *initListToken = nextToken; Token::Match(initListToken, "[:,] %var% [({]"); initListToken = initListToken->linkAt(2)->next()) { + const Token* varToken = initListToken->next(); + const Variable* variable = varToken->variable(); + if (variable && !isVariableWithoutSideEffects(*variable)) { + return withoutSideEffects = false; + } + + const Token* valueEnd = initListToken->linkAt(2); + for (const Token* valueToken = initListToken->tokAt(3); valueToken != valueEnd; valueToken = valueToken->next()) { + const Variable* initValueVar = valueToken->variable(); + if (initValueVar && !isVariableWithoutSideEffects(*initValueVar)) { + return withoutSideEffects = false; + } + if ((valueToken->tokType() == Token::Type::eName) || + (valueToken->tokType() == Token::Type::eLambda) || + (valueToken->tokType() == Token::Type::eOther)) { + return withoutSideEffects = false; + } + const Function* initValueFunc = valueToken->function(); + if (initValueFunc && !isFunctionWithoutSideEffects(*initValueFunc, valueToken, + std::list {})) { + return withoutSideEffects = false; + } + } + } + } + + if (!emptyBody) + return (withoutSideEffects = false); + } + + // Derived from type that has side effects? + if (std::any_of(type->derivedFrom.cbegin(), type->derivedFrom.cend(), [this](const Type::BaseInfo& derivedFrom) { + return !isRecordTypeWithoutSideEffects(derivedFrom.type); + })) + return (withoutSideEffects = false); + + // Is there a member variable with possible side effects + for (const Variable& var : type->classScope->varlist) { + withoutSideEffects = isVariableWithoutSideEffects(var, type); + if (!withoutSideEffects) { + return withoutSideEffects; + } + } + + + return (withoutSideEffects = true); +} + +bool CheckUnusedVar::isVariableWithoutSideEffects(const Variable& var, const Type* type) +{ + const Type* variableType = var.type(); + if (variableType && variableType != type) { + if (!isRecordTypeWithoutSideEffects(variableType)) + return false; + } else { + if (WRONG_DATA(!var.valueType(), var.typeStartToken())) + return false; + const ValueType::Type valueType = var.valueType()->type; + if ((valueType == ValueType::Type::UNKNOWN_TYPE) || (valueType == ValueType::Type::NONSTD)) + return false; + } + + return true; +} + +bool CheckUnusedVar::isEmptyType(const Type* type) +{ + // a type that has no variables and no constructor + + const std::pair::iterator,bool> found=mIsEmptyTypeMap.insert( + std::pair(type,false)); + bool & emptyType=found.first->second; + if (!found.second) + return emptyType; + + if (type && type->classScope && type->classScope->numConstructors == 0 && + (type->classScope->varlist.empty())) { + return (emptyType = std::all_of(type->derivedFrom.cbegin(), type->derivedFrom.cend(), [this](const Type::BaseInfo& bi) { + return isEmptyType(bi.type); + })); + } + + // unknown types are assumed to be nonempty + return (emptyType = false); +} + +bool CheckUnusedVar::isFunctionWithoutSideEffects(const Function& func, const Token* functionUsageToken, + std::list checkedFuncs) +{ + // no body to analyze + if (!func.hasBody()) { + return false; + } + + for (const Token* argsToken = functionUsageToken->next(); !Token::simpleMatch(argsToken, ")"); argsToken = argsToken->next()) { + const Variable* argVar = argsToken->variable(); + if (argVar && argVar->isGlobal()) { + return false; // TODO: analyze global variable usage + } + } + + bool sideEffectReturnFound = false; + std::set pointersToGlobals; + for (const Token* bodyToken = func.functionScope->bodyStart->next(); bodyToken != func.functionScope->bodyEnd; + bodyToken = bodyToken->next()) { + // check variable inside function body + const Variable* bodyVariable = bodyToken->variable(); + if (bodyVariable) { + if (!isVariableWithoutSideEffects(*bodyVariable)) { + return false; + } + // check if global variable is changed + if (bodyVariable->isGlobal() || (pointersToGlobals.find(bodyVariable) != pointersToGlobals.end())) { + const int indirect = bodyVariable->isArray() ? bodyVariable->dimensions().size() : bodyVariable->isPointer(); + if (isVariableChanged(bodyToken, indirect, mSettings)) { + return false; + } + // check if pointer to global variable assigned to another variable (another_var = &global_var) + if (Token::simpleMatch(bodyToken->tokAt(-1), "&") && Token::simpleMatch(bodyToken->tokAt(-2), "=")) { + const Token* assigned_var_token = bodyToken->tokAt(-3); + if (assigned_var_token && assigned_var_token->variable()) { + pointersToGlobals.insert(assigned_var_token->variable()); + } + } + } + } + + // check nested function + const Function* bodyFunction = bodyToken->function(); + if (bodyFunction) { + if (std::find(checkedFuncs.cbegin(), checkedFuncs.cend(), bodyFunction) != checkedFuncs.cend()) { // recursion found + continue; + } + checkedFuncs.push_back(bodyFunction); + if (!isFunctionWithoutSideEffects(*bodyFunction, bodyToken, checkedFuncs)) { + return false; + } + } + + // check returned value + if (Token::simpleMatch(bodyToken, "return")) { + const Token* returnValueToken = bodyToken->next(); + // TODO: handle complex return expressions + if (!Token::simpleMatch(returnValueToken->next(), ";")) { + sideEffectReturnFound = true; + continue; + } + // simple one-token return + const Variable* returnVariable = returnValueToken->variable(); + if (returnValueToken->isLiteral() || + (returnVariable && isVariableWithoutSideEffects(*returnVariable))) { + continue; + } + sideEffectReturnFound = true; + } + + // unknown name + if (bodyToken->isNameOnly()) { + return false; + } + } + + return !sideEffectReturnFound; +} diff --git a/cppcheck-2.14.0/lib/checkunusedvar.h b/cppcheck-2.14.0/lib/checkunusedvar.h new file mode 100644 index 00000000..9c697dcf --- /dev/null +++ b/cppcheck-2.14.0/lib/checkunusedvar.h @@ -0,0 +1,119 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef checkunusedvarH +#define checkunusedvarH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "tokenize.h" + +#include +#include +#include + +class ErrorLogger; +class Scope; +class Settings; +class Token; +class Type; +class Variables; +class Variable; +class Function; + +/// @addtogroup Checks +/// @{ + + +/** @brief Various small checks */ + +class CPPCHECKLIB CheckUnusedVar : public Check { + friend class TestUnusedVar; + +public: + /** @brief This constructor is used when registering the CheckClass */ + CheckUnusedVar() : Check(myName()) {} + +private: + /** @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, ErrorLogger *errorLogger) override { + CheckUnusedVar checkUnusedVar(&tokenizer, &tokenizer.getSettings(), errorLogger); + + // Coding style checks + checkUnusedVar.checkStructMemberUsage(); + checkUnusedVar.checkFunctionVariableUsage(); + } + + /** @brief %Check for unused function variables */ + void checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables); + void checkFunctionVariableUsage(); + + /** @brief %Check that all struct members are used */ + void checkStructMemberUsage(); + + bool isRecordTypeWithoutSideEffects(const Type* type); + bool isVariableWithoutSideEffects(const Variable& var, const Type* type = nullptr); + bool isEmptyType(const Type* type); + bool isFunctionWithoutSideEffects(const Function& func, const Token* functionUsageToken, + std::list checkedFuncs); + + // Error messages.. + void unusedStructMemberError(const Token *tok, const std::string &structname, const std::string &varname, const std::string& prefix = "struct"); + void unusedVariableError(const Token *tok, const std::string &varname); + void allocatedButUnusedVariableError(const Token *tok, const std::string &varname); + void unreadVariableError(const Token *tok, const std::string &varname, bool modified); + void unassignedVariableError(const Token *tok, const std::string &varname); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckUnusedVar c(nullptr, settings, errorLogger); + c.unusedVariableError(nullptr, "varname"); + c.allocatedButUnusedVariableError(nullptr, "varname"); + c.unreadVariableError(nullptr, "varname", false); + c.unassignedVariableError(nullptr, "varname"); + c.unusedStructMemberError(nullptr, "structname", "variable"); + } + + static std::string myName() { + return "UnusedVar"; + } + + std::string classInfo() const override { + return "UnusedVar checks\n" + + // style + "- unused variable\n" + "- allocated but unused variable\n" + "- unread variable\n" + "- unassigned variable\n" + "- unused struct member\n"; + } + + std::map mIsRecordTypeWithoutSideEffectsMap; + + std::map mIsEmptyTypeMap; + +}; +/// @} +//--------------------------------------------------------------------------- +#endif // checkunusedvarH diff --git a/cppcheck-2.14.0/lib/checkvaarg.cpp b/cppcheck-2.14.0/lib/checkvaarg.cpp new file mode 100644 index 00000000..7ad381e1 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkvaarg.cpp @@ -0,0 +1,182 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "checkvaarg.h" + +#include "astutils.h" +#include "errortypes.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" + +#include +#include +#include +#include + +//--------------------------------------------------------------------------- + +// Register this check class (by creating a static instance of it) +namespace { + CheckVaarg instance; +} + + +//--------------------------------------------------------------------------- +// Ensure that correct parameter is passed to va_start() +//--------------------------------------------------------------------------- + +// CWE ids used: +static const CWE CWE664(664U); // Improper Control of a Resource Through its Lifetime +static const CWE CWE688(688U); // Function Call With Incorrect Variable or Reference as Argument +static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior + +void CheckVaarg::va_start_argument() +{ + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + const std::size_t functions = symbolDatabase->functionScopes.size(); + const bool printWarnings = mSettings->severity.isEnabled(Severity::warning); + + logChecker("CheckVaarg::va_start_argument"); + + for (std::size_t i = 0; i < functions; ++i) { + const Scope* scope = symbolDatabase->functionScopes[i]; + const Function* function = scope->function; + if (!function) + continue; + for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (!tok->scope()->isExecutable()) + tok = tok->scope()->bodyEnd; + else if (Token::simpleMatch(tok, "va_start (")) { + const Token* param2 = tok->tokAt(2)->nextArgument(); + if (!param2) + continue; + const Variable* var = param2->variable(); + if (var && var->isReference()) + referenceAs_va_start_error(param2, var->name()); + if (var && var->index() + 2 < function->argCount() && printWarnings) { + auto it = function->argumentList.end(); + std::advance(it, -2); + wrongParameterTo_va_start_error(tok, var->name(), it->name()); // cppcheck-suppress derefInvalidIterator // FP due to isVariableChangedByFunctionCall() + } + tok = tok->linkAt(1); + } + } + } +} + +void CheckVaarg::wrongParameterTo_va_start_error(const Token *tok, const std::string& paramIsName, const std::string& paramShouldName) +{ + reportError(tok, Severity::warning, + "va_start_wrongParameter", "'" + paramIsName + "' given to va_start() is not last named argument of the function. Did you intend to pass '" + paramShouldName + "'?", CWE688, Certainty::normal); +} + +void CheckVaarg::referenceAs_va_start_error(const Token *tok, const std::string& paramName) +{ + reportError(tok, Severity::error, + "va_start_referencePassed", "Using reference '" + paramName + "' as parameter for va_start() results in undefined behaviour.", CWE758, Certainty::normal); +} + +//--------------------------------------------------------------------------- +// Detect missing va_end() if va_start() was used +// Detect va_list usage after va_end() +//--------------------------------------------------------------------------- + +void CheckVaarg::va_list_usage() +{ + if (mSettings->clang) + return; + + logChecker("CheckVaarg::va_list_usage"); // notclang + + const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); + for (const Variable* var : symbolDatabase->variableList()) { + if (!var || var->isPointer() || var->isReference() || var->isArray() || !var->scope() || var->typeStartToken()->str() != "va_list") + continue; + if (!var->isLocal() && !var->isArgument()) // Check only local variables and arguments + continue; + + bool open = var->isArgument(); // va_list passed as argument are opened + bool exitOnEndOfStatement = false; + + const Token* tok = var->nameToken()->next(); + for (; tok && tok != var->scope()->bodyEnd; tok = tok->next()) { + // Skip lambdas + const Token* tok2 = findLambdaEndToken(tok); + if (tok2) + tok = tok2; + if (Token::Match(tok, "va_start ( %varid%", var->declarationId())) { + if (open) + va_start_subsequentCallsError(tok, var->name()); + open = true; + tok = tok->linkAt(1); + } else if (Token::Match(tok, "va_end ( %varid%", var->declarationId())) { + if (!open) + va_list_usedBeforeStartedError(tok, var->name()); + open = false; + tok = tok->linkAt(1); + } else if (Token::simpleMatch(tok, "va_copy (")) { + bool nopen = open; + if (tok->linkAt(1)->previous()->varId() == var->declarationId()) { // Source + if (!open) + va_list_usedBeforeStartedError(tok, var->name()); + } + if (tok->tokAt(2)->varId() == var->declarationId()) { // Destination + if (open) + va_start_subsequentCallsError(tok, var->name()); + nopen = true; + } + open = nopen; + tok = tok->linkAt(1); + } else if (Token::Match(tok, "throw|return")) + exitOnEndOfStatement = true; + else if (tok->str() == "break") { + tok = findNextTokenFromBreak(tok); + if (!tok) + return; + } else if (tok->str() == "goto" || (tok->isCpp() && tok->str() == "try")) { + open = false; + break; + } else if (!open && tok->varId() == var->declarationId()) + va_list_usedBeforeStartedError(tok, var->name()); + else if (exitOnEndOfStatement && tok->str() == ";") + break; + } + if (open && !var->isArgument()) + va_end_missingError(tok, var->name()); + } +} + +void CheckVaarg::va_end_missingError(const Token *tok, const std::string& varname) +{ + reportError(tok, Severity::error, + "va_end_missing", "va_list '" + varname + "' was opened but not closed by va_end().", CWE664, Certainty::normal); +} + +void CheckVaarg::va_list_usedBeforeStartedError(const Token *tok, const std::string& varname) +{ + reportError(tok, Severity::error, + "va_list_usedBeforeStarted", "va_list '" + varname + "' used before va_start() was called.", CWE664, Certainty::normal); +} + +void CheckVaarg::va_start_subsequentCallsError(const Token *tok, const std::string& varname) +{ + reportError(tok, Severity::error, + "va_start_subsequentCalls", "va_start() or va_copy() called subsequently on '" + varname + "' without va_end() in between.", CWE664, Certainty::normal); +} diff --git a/cppcheck-2.14.0/lib/checkvaarg.h b/cppcheck-2.14.0/lib/checkvaarg.h new file mode 100644 index 00000000..f4d5c214 --- /dev/null +++ b/cppcheck-2.14.0/lib/checkvaarg.h @@ -0,0 +1,91 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef checkvaargtH +#define checkvaargtH +//--------------------------------------------------------------------------- + +#include "check.h" +#include "config.h" +#include "tokenize.h" + +#include + +class ErrorLogger; +class Settings; +class Token; + +/// @addtogroup Checks +/// @{ + +/** + * @brief Checking for misusage of variable argument lists + */ + +class CPPCHECKLIB CheckVaarg : public Check { +public: + CheckVaarg() : Check(myName()) {} + +private: + CheckVaarg(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : Check(myName(), tokenizer, settings, errorLogger) {} + + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { + CheckVaarg check(&tokenizer, &tokenizer.getSettings(), errorLogger); + check.va_start_argument(); + check.va_list_usage(); + } + + void va_start_argument(); + void va_list_usage(); + + void wrongParameterTo_va_start_error(const Token *tok, const std::string& paramIsName, const std::string& paramShouldName); + void referenceAs_va_start_error(const Token *tok, const std::string& paramName); + void va_end_missingError(const Token *tok, const std::string& varname); + void va_list_usedBeforeStartedError(const Token *tok, const std::string& varname); + void va_start_subsequentCallsError(const Token *tok, const std::string& varname); + + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { + CheckVaarg c(nullptr, settings, errorLogger); + c.wrongParameterTo_va_start_error(nullptr, "arg1", "arg2"); + c.referenceAs_va_start_error(nullptr, "arg1"); + c.va_end_missingError(nullptr, "vl"); + c.va_list_usedBeforeStartedError(nullptr, "vl"); + c.va_start_subsequentCallsError(nullptr, "vl"); + } + + static std::string myName() { + return "Vaarg"; + } + + std::string classInfo() const override { + return "Check for misusage of variable argument lists:\n" + "- Wrong parameter passed to va_start()\n" + "- Reference passed to va_start()\n" + "- Missing va_end()\n" + "- Using va_list before it is opened\n" + "- Subsequent calls to va_start/va_copy()\n"; + } +}; + +/// @} + +//--------------------------------------------------------------------------- +#endif // checkvaargtH diff --git a/cppcheck-2.14.0/lib/clangimport.cpp b/cppcheck-2.14.0/lib/clangimport.cpp new file mode 100644 index 00000000..fc021758 --- /dev/null +++ b/cppcheck-2.14.0/lib/clangimport.cpp @@ -0,0 +1,1639 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "clangimport.h" + +#include "errortypes.h" +#include "mathlib.h" +#include "settings.h" +#include "standards.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "tokenlist.h" +#include "utils.h" +#include "vfvalue.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const std::string AccessSpecDecl = "AccessSpecDecl"; +static const std::string ArraySubscriptExpr = "ArraySubscriptExpr"; +static const std::string BinaryOperator = "BinaryOperator"; +static const std::string BreakStmt = "BreakStmt"; +static const std::string CallExpr = "CallExpr"; +static const std::string CaseStmt = "CaseStmt"; +static const std::string CharacterLiteral = "CharacterLiteral"; +static const std::string ClassTemplateDecl = "ClassTemplateDecl"; +static const std::string ClassTemplateSpecializationDecl = "ClassTemplateSpecializationDecl"; +static const std::string ConditionalOperator = "ConditionalOperator"; +static const std::string ConstantExpr = "ConstantExpr"; +static const std::string CompoundAssignOperator = "CompoundAssignOperator"; +static const std::string CompoundStmt = "CompoundStmt"; +static const std::string ContinueStmt = "ContinueStmt"; +static const std::string CStyleCastExpr = "CStyleCastExpr"; +static const std::string CXXBindTemporaryExpr = "CXXBindTemporaryExpr"; +static const std::string CXXBoolLiteralExpr = "CXXBoolLiteralExpr"; +static const std::string CXXConstructorDecl = "CXXConstructorDecl"; +static const std::string CXXConstructExpr = "CXXConstructExpr"; +static const std::string CXXDefaultArgExpr = "CXXDefaultArgExpr"; +static const std::string CXXDeleteExpr = "CXXDeleteExpr"; +static const std::string CXXDestructorDecl = "CXXDestructorDecl"; +static const std::string CXXForRangeStmt = "CXXForRangeStmt"; +static const std::string CXXFunctionalCastExpr = "CXXFunctionalCastExpr"; +static const std::string CXXMemberCallExpr = "CXXMemberCallExpr"; +static const std::string CXXMethodDecl = "CXXMethodDecl"; +static const std::string CXXNewExpr = "CXXNewExpr"; +static const std::string CXXNullPtrLiteralExpr = "CXXNullPtrLiteralExpr"; +static const std::string CXXOperatorCallExpr = "CXXOperatorCallExpr"; +static const std::string CXXRecordDecl = "CXXRecordDecl"; +static const std::string CXXStaticCastExpr = "CXXStaticCastExpr"; +static const std::string CXXStdInitializerListExpr = "CXXStdInitializerListExpr"; +static const std::string CXXTemporaryObjectExpr = "CXXTemporaryObjectExpr"; +static const std::string CXXThisExpr = "CXXThisExpr"; +static const std::string CXXThrowExpr = "CXXThrowExpr"; +static const std::string DeclRefExpr = "DeclRefExpr"; +static const std::string DeclStmt = "DeclStmt"; +static const std::string DefaultStmt = "DefaultStmt"; +static const std::string DoStmt = "DoStmt"; +static const std::string EnumConstantDecl = "EnumConstantDecl"; +static const std::string EnumDecl = "EnumDecl"; +static const std::string ExprWithCleanups = "ExprWithCleanups"; +static const std::string FieldDecl = "FieldDecl"; +static const std::string FloatingLiteral = "FloatingLiteral"; +static const std::string ForStmt = "ForStmt"; +static const std::string FunctionDecl = "FunctionDecl"; +static const std::string FunctionTemplateDecl = "FunctionTemplateDecl"; +static const std::string GotoStmt = "GotoStmt"; +static const std::string IfStmt = "IfStmt"; +static const std::string ImplicitCastExpr = "ImplicitCastExpr"; +static const std::string InitListExpr = "InitListExpr"; +static const std::string IntegerLiteral = "IntegerLiteral"; +static const std::string LabelStmt = "LabelStmt"; +static const std::string LinkageSpecDecl = "LinkageSpecDecl"; +static const std::string MaterializeTemporaryExpr = "MaterializeTemporaryExpr"; +static const std::string MemberExpr = "MemberExpr"; +static const std::string NamespaceDecl = "NamespaceDecl"; +static const std::string NullStmt = "NullStmt"; +static const std::string ParenExpr = "ParenExpr"; +static const std::string ParmVarDecl = "ParmVarDecl"; +static const std::string RecordDecl = "RecordDecl"; +static const std::string ReturnStmt = "ReturnStmt"; +static const std::string StringLiteral = "StringLiteral"; +static const std::string SwitchStmt = "SwitchStmt"; +static const std::string TemplateArgument = "TemplateArgument"; +static const std::string TypedefDecl = "TypedefDecl"; +static const std::string UnaryOperator = "UnaryOperator"; +static const std::string UnaryExprOrTypeTraitExpr = "UnaryExprOrTypeTraitExpr"; +static const std::string VarDecl = "VarDecl"; +static const std::string WhileStmt = "WhileStmt"; + +static std::string unquote(const std::string &s) +{ + return (s[0] == '\'') ? s.substr(1, s.size() - 2) : s; +} + + +static std::vector splitString(const std::string &line) +{ + std::vector ret; + std::string::size_type pos1 = line.find_first_not_of(' '); + while (pos1 < line.size()) { + std::string::size_type pos2; + if (std::strchr("*()", line[pos1])) { + ret.push_back(line.substr(pos1,1)); + pos1 = line.find_first_not_of(' ', pos1 + 1); + continue; + } + if (line[pos1] == '<') + pos2 = line.find('>', pos1); + else if (line[pos1] == '\"') + pos2 = line.find('\"', pos1+1); + else if (line[pos1] == '\'') { + pos2 = line.find('\'', pos1+1); + if (pos2 < (int)line.size() - 3 && line.compare(pos2, 3, "\':\'", 0, 3) == 0) + pos2 = line.find('\'', pos2 + 3); + } else { + pos2 = pos1; + while (pos2 < line.size() && (line[pos2] == '_' || line[pos2] == ':' || std::isalnum((unsigned char)line[pos2]))) + ++pos2; + if (pos2 > pos1 && pos2 < line.size() && line[pos2] == '<' && std::isalpha(line[pos1])) { + int tlevel = 1; + while (++pos2 < line.size() && tlevel > 0) { + if (line[pos2] == '<') + ++tlevel; + else if (line[pos2] == '>') + --tlevel; + } + if (tlevel == 0 && pos2 < line.size() && line[pos2] == ' ') { + ret.push_back(line.substr(pos1, pos2-pos1)); + pos1 = pos2 + 1; + continue; + } + } + + pos2 = line.find(' ', pos1) - 1; + if ((std::isalpha(line[pos1]) || line[pos1] == '_') && + line.find("::", pos1) < pos2 && + line.find("::", pos1) < line.find('<', pos1)) { + pos2 = line.find("::", pos1); + ret.push_back(line.substr(pos1, pos2-pos1)); + ret.emplace_back("::"); + pos1 = pos2 + 2; + continue; + } + if ((std::isalpha(line[pos1]) || line[pos1] == '_') && + line.find('<', pos1) < pos2 && + line.find("<<",pos1) != line.find('<',pos1) && + line.find('>', pos1) != std::string::npos && + line.find('>', pos1) > pos2) { + int level = 0; + for (pos2 = pos1; pos2 < line.size(); ++pos2) { + if (line[pos2] == '<') + ++level; + else if (line[pos2] == '>') { + if (level <= 1) + break; + --level; + } + } + if (level > 1 && pos2 + 1 >= line.size()) + return std::vector {}; + pos2 = line.find(' ', pos2); + if (pos2 != std::string::npos) + --pos2; + } + } + if (pos2 == std::string::npos) { + ret.push_back(line.substr(pos1)); + break; + } + ret.push_back(line.substr(pos1, pos2+1-pos1)); + pos1 = line.find_first_not_of(' ', pos2 + 1); + } + return ret; +} + + +namespace clangimport { + struct Data { + struct Decl { + explicit Decl(Scope *scope) : scope(scope) {} + Decl(Token *def, Variable *var) : def(def), var(var) {} + Decl(Token *def, Function *function) : def(def), function(function) {} + Decl(Token *def, Enumerator *enumerator) : def(def), enumerator(enumerator) {} + void ref(Token *tok) const { + if (enumerator) + tok->enumerator(enumerator); + if (function) + tok->function(function); + if (var) { + tok->variable(var); + tok->varId(var->declarationId()); + } + } + Token* def{}; + Enumerator* enumerator{}; + Function* function{}; + Scope* scope{}; + Variable* var{}; + }; + + const Settings *mSettings = nullptr; + SymbolDatabase *mSymbolDatabase = nullptr; + + int enumValue = 0; + + void enumDecl(const std::string &addr, Token *nameToken, Enumerator *enumerator) { + Decl decl(nameToken, enumerator); + mDeclMap.insert(std::pair(addr, decl)); + nameToken->enumerator(enumerator); + notFound(addr); + } + + void funcDecl(const std::string &addr, Token *nameToken, Function *function) { + Decl decl(nameToken, function); + mDeclMap.insert(std::pair(addr, decl)); + nameToken->function(function); + notFound(addr); + } + + void scopeDecl(const std::string &addr, Scope *scope) { + Decl decl(scope); + mDeclMap.insert(std::pair(addr, decl)); + } + + void varDecl(const std::string &addr, Token *def, Variable *var) { + Decl decl(def, var); + mDeclMap.insert(std::pair(addr, decl)); + def->varId(++mVarId); + def->variable(var); + if (def->valueType()) + var->setValueType(*def->valueType()); + notFound(addr); + } + + void replaceVarDecl(const Variable *from, Variable *to) { + for (auto &it: mDeclMap) { + Decl &decl = it.second; + if (decl.var == from) + decl.var = to; + } + } + + void ref(const std::string &addr, Token *tok) { + auto it = mDeclMap.find(addr); + if (it != mDeclMap.end()) + it->second.ref(tok); + else + mNotFound[addr].push_back(tok); + } + + std::vector getVariableList() const { + std::vector ret; + ret.resize(mVarId + 1, nullptr); + for (const auto& it: mDeclMap) { + if (it.second.var) + ret[it.second.var->declarationId()] = it.second.var; + } + return ret; + } + + bool hasDecl(const std::string &addr) const { + return mDeclMap.find(addr) != mDeclMap.end(); + } + + const Scope *getScope(const std::string &addr) { + auto it = mDeclMap.find(addr); + return (it == mDeclMap.end() ? nullptr : it->second.scope); + } + + // "}" tokens that are not end-of-scope + std::set mNotScope; + + std::map scopeAccessControl; + private: + void notFound(const std::string &addr) { + auto it = mNotFound.find(addr); + if (it != mNotFound.end()) { + for (Token *reftok: it->second) + ref(addr, reftok); + mNotFound.erase(it); + } + } + + std::map mDeclMap; + std::map> mNotFound; + int mVarId = 0; + }; + + class AstNode; + using AstNodePtr = std::shared_ptr; + + class AstNode { + public: + AstNode(std::string nodeType, const std::string &ext, Data *data) + : nodeType(std::move(nodeType)), mExtTokens(splitString(ext)), mData(data) + {} + std::string nodeType; + std::vector children; + + void setLocations(TokenList &tokenList, int file, int line, int col); + + void dumpAst(int num = 0, int indent = 0) const; + void createTokens1(TokenList &tokenList) { + //dumpAst(); + if (!tokenList.back()) { + setLocations(tokenList, 0, 1, 1); + // FIXME: treat as C++ if no filename (i.e. no lang) is specified for now + if (tokenList.getSourceFilePath().empty()) + tokenList.setLang(Standards::Language::CPP); + } + else + setLocations(tokenList, tokenList.back()->fileIndex(), tokenList.back()->linenr(), 1); + createTokens(tokenList); + if (nodeType == VarDecl || nodeType == RecordDecl || nodeType == TypedefDecl) + addtoken(tokenList, ";"); + mData->mNotScope.clear(); + } + + AstNodePtr getChild(int c) { + if (c >= children.size()) { + std::ostringstream err; + err << "ClangImport: AstNodePtr::getChild(" << c << ") out of bounds. children.size=" << children.size() << " " << nodeType; + for (const std::string &s: mExtTokens) + err << " " << s; + throw InternalError(nullptr, err.str()); + } + return children[c]; + } + private: + Token *createTokens(TokenList &tokenList); + Token *addtoken(TokenList &tokenList, const std::string &str, bool valueType=true); + const ::Type *addTypeTokens(TokenList &tokenList, const std::string &str, const Scope *scope = nullptr); + void addFullScopeNameTokens(TokenList &tokenList, const Scope *recordScope); + Scope *createScope(TokenList &tokenList, Scope::ScopeType scopeType, AstNodePtr astNode, const Token *def); + Scope *createScope(TokenList &tokenList, Scope::ScopeType scopeType, const std::vector &children2, const Token *def); + Token *createTokensCall(TokenList &tokenList); + void createTokensFunctionDecl(TokenList &tokenList); + void createTokensForCXXRecord(TokenList &tokenList); + Token *createTokensVarDecl(TokenList &tokenList); + std::string getSpelling() const; + std::string getType(int index = 0) const; + std::string getFullType(int index = 0) const; + bool isDefinition() const; + std::string getTemplateParameters() const; + const Scope *getNestedInScope(TokenList &tokenList); + void setValueType(Token *tok); + + int mFile = 0; + int mLine = 1; + int mCol = 1; + std::vector mExtTokens; + Data *mData; + }; +} + +std::string clangimport::AstNode::getSpelling() const +{ + if (nodeType == CompoundAssignOperator) { + int typeIndex = 1; + while (typeIndex < mExtTokens.size() && mExtTokens[typeIndex][0] != '\'') + typeIndex++; + // name is next quoted token + int nameIndex = typeIndex + 1; + while (nameIndex < mExtTokens.size() && mExtTokens[nameIndex][0] != '\'') + nameIndex++; + return (nameIndex < mExtTokens.size()) ? unquote(mExtTokens[nameIndex]) : ""; + } + + if (nodeType == UnaryExprOrTypeTraitExpr) { + int typeIndex = 1; + while (typeIndex < mExtTokens.size() && mExtTokens[typeIndex][0] != '\'') + typeIndex++; + const int nameIndex = typeIndex + 1; + return (nameIndex < mExtTokens.size()) ? unquote(mExtTokens[nameIndex]) : ""; + } + + int typeIndex = mExtTokens.size() - 1; + if (nodeType == FunctionDecl || nodeType == CXXConstructorDecl || nodeType == CXXMethodDecl) { + while (typeIndex >= 0 && mExtTokens[typeIndex][0] != '\'') + typeIndex--; + if (typeIndex <= 0) + return ""; + } + if (nodeType == DeclRefExpr) { + while (typeIndex > 0 && std::isalpha(mExtTokens[typeIndex][0])) + typeIndex--; + if (typeIndex <= 0) + return ""; + } + const std::string &str = mExtTokens[typeIndex - 1]; + if (startsWith(str,"col:")) + return ""; + if (startsWith(str,"= mExtTokens.size()) + return ""; + std::string type = mExtTokens[typeIndex]; + if (type.find("\':\'") != std::string::npos) { + if (index == 0) + type.erase(type.find("\':\'") + 1); + else + type.erase(0, type.find("\':\'") + 2); + } + return type; +} + +bool clangimport::AstNode::isDefinition() const +{ + return contains(mExtTokens, "definition"); +} + +std::string clangimport::AstNode::getTemplateParameters() const +{ + if (children.empty() || children[0]->nodeType != TemplateArgument) + return ""; + std::string templateParameters; + for (const AstNodePtr& child: children) { + if (child->nodeType == TemplateArgument) { + if (templateParameters.empty()) + templateParameters = "<"; + else + templateParameters += ","; + templateParameters += unquote(child->mExtTokens.back()); + } + } + return templateParameters + ">"; +} + +void clangimport::AstNode::dumpAst(int num, int indent) const +{ + (void)num; + std::cout << std::string(indent, ' ') << nodeType; + for (const auto& tok: mExtTokens) + std::cout << " " << tok; + std::cout << std::endl; + for (int c = 0; c < children.size(); ++c) { + if (children[c]) + children[c]->dumpAst(c, indent + 2); + else + std::cout << std::string(indent + 2, ' ') << "<<<>>>>" << std::endl; + } +} + +void clangimport::AstNode::setLocations(TokenList &tokenList, int file, int line, int col) +{ + for (const std::string &ext: mExtTokens) { + if (startsWith(ext, "(ext.substr(5, ext.find_first_of(",>", 5) - 5)); + else if (startsWith(ext, "(ext.substr(6, ext.find_first_of(":,>", 6) - 6)); + const auto pos = ext.find(", col:"); + if (pos != std::string::npos) + col = strToInt(ext.substr(pos+6, ext.find_first_of(":,>", pos+6) - (pos+6))); + } else if (ext[0] == '<') { + const std::string::size_type colon = ext.find(':'); + if (colon != std::string::npos) { + const bool windowsPath = colon == 2 && ext.size() > 3 && ext[2] == ':'; + const std::string::size_type sep1 = windowsPath ? ext.find(':', 4) : colon; + const std::string::size_type sep2 = ext.find(':', sep1 + 1); + file = tokenList.appendFileIfNew(ext.substr(1, sep1 - 1)); + line = strToInt(ext.substr(sep1 + 1, sep2 - sep1 - 1)); + } + } + } + mFile = file; + mLine = line; + mCol = col; + for (const auto& child: children) { + if (child) + child->setLocations(tokenList, file, line, col); + } +} + +Token *clangimport::AstNode::addtoken(TokenList &tokenList, const std::string &str, bool valueType) +{ + const Scope *scope = getNestedInScope(tokenList); + tokenList.addtoken(str, mLine, mCol, mFile); + tokenList.back()->scope(scope); + if (valueType) + setValueType(tokenList.back()); + return tokenList.back(); +} + +const ::Type * clangimport::AstNode::addTypeTokens(TokenList &tokenList, const std::string &str, const Scope *scope) +{ + if (str.find("\':\'") != std::string::npos) { + return addTypeTokens(tokenList, str.substr(0, str.find("\':\'") + 1), scope); + } + + if (startsWith(str, "'enum (anonymous")) + return nullptr; + + std::string type; + if (str.find(" (") != std::string::npos) { + if (str.find('<') != std::string::npos) + type = str.substr(1, str.find('<')) + "...>"; + else + type = str.substr(1,str.find(" (")-1); + } else + type = unquote(str); + + if (type.find("(*)(") != std::string::npos) { + type.erase(type.find("(*)(")); + type += "*"; + } + if (type.find('(') != std::string::npos) + type.erase(type.find('(')); + + std::stack lpar; + for (const std::string &s: splitString(type)) { + Token *tok = addtoken(tokenList, s, false); + if (tok->str() == "(") + lpar.push(tok); + else if (tok->str() == ")") { + Token::createMutualLinks(tok, lpar.top()); + lpar.pop(); + } + } + + // Set Type + if (!scope) { + scope = tokenList.back() ? tokenList.back()->scope() : nullptr; + if (!scope) + return nullptr; + } + for (const Token *typeToken = tokenList.back(); Token::Match(typeToken, "&|*|%name%"); typeToken = typeToken->previous()) { + if (!typeToken->isName()) + continue; + const ::Type *recordType = scope->check->findVariableType(scope, typeToken); + if (recordType) { + const_cast(typeToken)->type(recordType); + return recordType; + } + } + return nullptr; +} + +void clangimport::AstNode::addFullScopeNameTokens(TokenList &tokenList, const Scope *recordScope) +{ + if (!recordScope) + return; + std::list scopes; + while (recordScope && recordScope != tokenList.back()->scope() && !recordScope->isExecutable()) { + scopes.push_front(recordScope); + recordScope = recordScope->nestedIn; + } + for (const Scope *s: scopes) { + if (!s->className.empty()) { + addtoken(tokenList, s->className); + addtoken(tokenList, "::"); + } + } +} + +const Scope *clangimport::AstNode::getNestedInScope(TokenList &tokenList) +{ + if (!tokenList.back()) + return &mData->mSymbolDatabase->scopeList.front(); + if (tokenList.back()->str() == "}" && mData->mNotScope.find(tokenList.back()) == mData->mNotScope.end()) + return tokenList.back()->scope()->nestedIn; + return tokenList.back()->scope(); +} + +void clangimport::AstNode::setValueType(Token *tok) +{ + for (int i = 0; i < 2; i++) { + const std::string &type = getType(i); + + if (type.find('<') != std::string::npos) + // TODO + continue; + + TokenList decl(nullptr); + decl.setLang(tok->isCpp() ? Standards::Language::CPP : Standards::Language::C); + addTypeTokens(decl, type, tok->scope()); + if (!decl.front()) + break; + + const ValueType valueType = ValueType::parseDecl(decl.front(), *mData->mSettings); + if (valueType.type != ValueType::Type::UNKNOWN_TYPE) { + tok->setValueType(new ValueType(valueType)); + break; + } + } +} + +Scope *clangimport::AstNode::createScope(TokenList &tokenList, Scope::ScopeType scopeType, AstNodePtr astNode, const Token *def) +{ + std::vector children2{std::move(astNode)}; + return createScope(tokenList, scopeType, children2, def); +} + +Scope *clangimport::AstNode::createScope(TokenList &tokenList, Scope::ScopeType scopeType, const std::vector & children2, const Token *def) +{ + SymbolDatabase *symbolDatabase = mData->mSymbolDatabase; + + auto *nestedIn = const_cast(getNestedInScope(tokenList)); + + symbolDatabase->scopeList.emplace_back(nullptr, nullptr, nestedIn); + Scope *scope = &symbolDatabase->scopeList.back(); + if (scopeType == Scope::ScopeType::eEnum) + scope->enumeratorList.reserve(children2.size()); + nestedIn->nestedList.push_back(scope); + scope->type = scopeType; + scope->classDef = def; + scope->check = nestedIn->check; + if (Token::Match(def, "if|for|while (")) { + std::map replaceVar; + for (const Token *vartok = def->tokAt(2); vartok; vartok = vartok->next()) { + if (!vartok->variable()) + continue; + if (vartok->variable()->nameToken() == vartok) { + const Variable *from = vartok->variable(); + scope->varlist.emplace_back(*from, scope); + Variable *to = &scope->varlist.back(); + replaceVar[from] = to; + mData->replaceVarDecl(from, to); + } + if (replaceVar.find(vartok->variable()) != replaceVar.end()) + const_cast(vartok)->variable(replaceVar[vartok->variable()]); + } + std::list &varlist = const_cast(def->scope())->varlist; + for (std::list::iterator var = varlist.begin(); var != varlist.end();) { + if (replaceVar.find(&(*var)) != replaceVar.end()) + var = varlist.erase(var); + else + ++var; + } + } + scope->bodyStart = addtoken(tokenList, "{"); + tokenList.back()->scope(scope); + mData->scopeAccessControl[scope] = scope->defaultAccess(); + if (!children2.empty()) { + for (const AstNodePtr &astNode: children2) { + if (astNode->nodeType == "VisibilityAttr") + continue; + if (astNode->nodeType == AccessSpecDecl) { + if (contains(astNode->mExtTokens, "private")) + mData->scopeAccessControl[scope] = AccessControl::Private; + else if (contains(astNode->mExtTokens, "protected")) + mData->scopeAccessControl[scope] = AccessControl::Protected; + else if (contains(astNode->mExtTokens, "public")) + mData->scopeAccessControl[scope] = AccessControl::Public; + continue; + } + astNode->createTokens(tokenList); + if (scopeType == Scope::ScopeType::eEnum) + astNode->addtoken(tokenList, ","); + else if (!Token::Match(tokenList.back(), "[;{}]")) + astNode->addtoken(tokenList, ";"); + } + } + scope->bodyEnd = addtoken(tokenList, "}"); + Token::createMutualLinks(const_cast(scope->bodyStart), const_cast(scope->bodyEnd)); + mData->scopeAccessControl.erase(scope); + return scope; +} + +Token *clangimport::AstNode::createTokens(TokenList &tokenList) +{ + if (nodeType == ArraySubscriptExpr) { + Token *array = getChild(0)->createTokens(tokenList); + Token *bracket1 = addtoken(tokenList, "["); + Token *index = children[1]->createTokens(tokenList); + Token *bracket2 = addtoken(tokenList, "]"); + bracket1->astOperand1(array); + bracket1->astOperand2(index); + bracket1->link(bracket2); + bracket2->link(bracket1); + return bracket1; + } + if (nodeType == BinaryOperator) { + Token *tok1 = getChild(0)->createTokens(tokenList); + Token *binop = addtoken(tokenList, unquote(mExtTokens.back())); + Token *tok2 = children[1]->createTokens(tokenList); + binop->astOperand1(tok1); + binop->astOperand2(tok2); + return binop; + } + if (nodeType == BreakStmt) + return addtoken(tokenList, "break"); + if (nodeType == CharacterLiteral) { + const int c = MathLib::toBigNumber(mExtTokens.back()); + if (c == 0) + return addtoken(tokenList, "\'\\0\'"); + if (c == '\r') + return addtoken(tokenList, "\'\\r\'"); + if (c == '\n') + return addtoken(tokenList, "\'\\n\'"); + if (c == '\t') + return addtoken(tokenList, "\'\\t\'"); + if (c == '\\') + return addtoken(tokenList, "\'\\\\\'"); + if (c < ' ' || c >= 0x80) { + std::ostringstream hex; + hex << std::hex << ((c>>4) & 0xf) << (c&0xf); + return addtoken(tokenList, "\'\\x" + hex.str() + "\'"); + } + return addtoken(tokenList, std::string("\'") + char(c) + std::string("\'")); + } + if (nodeType == CallExpr) + return createTokensCall(tokenList); + if (nodeType == CaseStmt) { + Token *caseToken = addtoken(tokenList, "case"); + Token *exprToken = getChild(0)->createTokens(tokenList); + caseToken->astOperand1(exprToken); + addtoken(tokenList, ":"); + children.back()->createTokens(tokenList); + return nullptr; + } + if (nodeType == ClassTemplateDecl) { + for (const AstNodePtr& child: children) { + if (child->nodeType == ClassTemplateSpecializationDecl) + child->createTokens(tokenList); + } + return nullptr; + } + if (nodeType == ClassTemplateSpecializationDecl) { + createTokensForCXXRecord(tokenList); + return nullptr; + } + if (nodeType == ConditionalOperator) { + Token *expr1 = getChild(0)->createTokens(tokenList); + Token *tok1 = addtoken(tokenList, "?"); + Token *expr2 = children[1]->createTokens(tokenList); + Token *tok2 = addtoken(tokenList, ":"); + Token *expr3 = children[2]->createTokens(tokenList); + tok2->astOperand1(expr2); + tok2->astOperand2(expr3); + tok1->astOperand1(expr1); + tok1->astOperand2(tok2); + return tok1; + } + if (nodeType == CompoundAssignOperator) { + Token *lhs = getChild(0)->createTokens(tokenList); + Token *assign = addtoken(tokenList, getSpelling()); + Token *rhs = children[1]->createTokens(tokenList); + assign->astOperand1(lhs); + assign->astOperand2(rhs); + return assign; + } + if (nodeType == CompoundStmt) { + for (const AstNodePtr& child: children) { + child->createTokens(tokenList); + if (!Token::Match(tokenList.back(), "[;{}]")) + child->addtoken(tokenList, ";"); + } + return nullptr; + } + if (nodeType == ConstantExpr) + return children.back()->createTokens(tokenList); + if (nodeType == ContinueStmt) + return addtoken(tokenList, "continue"); + if (nodeType == CStyleCastExpr) { + Token *par1 = addtoken(tokenList, "("); + addTypeTokens(tokenList, '\'' + getType() + '\''); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + par1->astOperand1(getChild(0)->createTokens(tokenList)); + return par1; + } + if (nodeType == CXXBindTemporaryExpr) + return getChild(0)->createTokens(tokenList); + if (nodeType == CXXBoolLiteralExpr) { + addtoken(tokenList, mExtTokens.back()); + tokenList.back()->setValueType(new ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0)); + return tokenList.back(); + } + if (nodeType == CXXConstructExpr) { + if (!children.empty()) + return getChild(0)->createTokens(tokenList); + addTypeTokens(tokenList, '\'' + getType() + '\''); + Token *type = tokenList.back(); + Token *par1 = addtoken(tokenList, "("); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + par1->astOperand1(type); + return par1; + } + if (nodeType == CXXConstructorDecl) { + createTokensFunctionDecl(tokenList); + return nullptr; + } + if (nodeType == CXXDeleteExpr) { + addtoken(tokenList, "delete"); + getChild(0)->createTokens(tokenList); + return nullptr; + } + if (nodeType == CXXDestructorDecl) { + createTokensFunctionDecl(tokenList); + return nullptr; + } + if (nodeType == CXXForRangeStmt) { + Token *forToken = addtoken(tokenList, "for"); + Token *par1 = addtoken(tokenList, "("); + AstNodePtr varDecl; + if (children[6]->nodeType == DeclStmt) + varDecl = getChild(6)->getChild(0); + else + varDecl = getChild(5)->getChild(0); + varDecl->mExtTokens.pop_back(); + varDecl->children.clear(); + Token *expr1 = varDecl->createTokens(tokenList); + Token *colon = addtoken(tokenList, ":"); + AstNodePtr range; + for (int i = 0; i < 2; i++) { + if (children[i] && children[i]->nodeType == DeclStmt && children[i]->getChild(0)->nodeType == VarDecl) { + range = children[i]->getChild(0)->getChild(0); + break; + } + } + if (!range) + throw InternalError(tokenList.back(), "Failed to import CXXForRangeStmt. Range?"); + Token *expr2 = range->createTokens(tokenList); + Token *par2 = addtoken(tokenList, ")"); + + par1->link(par2); + par2->link(par1); + + colon->astOperand1(expr1); + colon->astOperand2(expr2); + par1->astOperand1(forToken); + par1->astOperand2(colon); + + createScope(tokenList, Scope::ScopeType::eFor, children.back(), forToken); + return nullptr; + } + if (nodeType == CXXMethodDecl) { + for (int i = 0; i+1 < mExtTokens.size(); ++i) { + if (mExtTokens[i] == "prev" && !mData->hasDecl(mExtTokens[i+1])) + return nullptr; + } + createTokensFunctionDecl(tokenList); + return nullptr; + } + if (nodeType == CXXMemberCallExpr) + return createTokensCall(tokenList); + if (nodeType == CXXNewExpr) { + Token *newtok = addtoken(tokenList, "new"); + if (children.size() == 1 && getChild(0)->nodeType == CXXConstructExpr) { + newtok->astOperand1(getChild(0)->createTokens(tokenList)); + return newtok; + } + std::string type = getType(); + if (type.find('*') != std::string::npos) + type = type.erase(type.rfind('*')); + addTypeTokens(tokenList, type); + if (!children.empty()) { + Token *bracket1 = addtoken(tokenList, "["); + getChild(0)->createTokens(tokenList); + Token *bracket2 = addtoken(tokenList, "]"); + bracket1->link(bracket2); + bracket2->link(bracket1); + } + return newtok; + } + if (nodeType == CXXNullPtrLiteralExpr) + return addtoken(tokenList, "nullptr"); + if (nodeType == CXXOperatorCallExpr) + return createTokensCall(tokenList); + if (nodeType == CXXRecordDecl) { + createTokensForCXXRecord(tokenList); + return nullptr; + } + if (nodeType == CXXStaticCastExpr || nodeType == CXXFunctionalCastExpr) { + Token *cast = addtoken(tokenList, getSpelling()); + Token *par1 = addtoken(tokenList, "("); + Token *expr = getChild(0)->createTokens(tokenList); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + par1->astOperand1(cast); + par1->astOperand2(expr); + setValueType(par1); + return par1; + } + if (nodeType == CXXStdInitializerListExpr) + return getChild(0)->createTokens(tokenList); + if (nodeType == CXXTemporaryObjectExpr && !children.empty()) + return getChild(0)->createTokens(tokenList); + if (nodeType == CXXThisExpr) + return addtoken(tokenList, "this"); + if (nodeType == CXXThrowExpr) { + Token *t = addtoken(tokenList, "throw"); + t->astOperand1(getChild(0)->createTokens(tokenList)); + return t; + } + if (nodeType == DeclRefExpr) { + int addrIndex = mExtTokens.size() - 1; + while (addrIndex > 1 && !startsWith(mExtTokens[addrIndex],"0x")) + --addrIndex; + const std::string addr = mExtTokens[addrIndex]; + std::string name = unquote(getSpelling()); + Token *reftok = addtoken(tokenList, name.empty() ? "" : std::move(name)); + mData->ref(addr, reftok); + return reftok; + } + if (nodeType == DeclStmt) + return getChild(0)->createTokens(tokenList); + if (nodeType == DefaultStmt) { + addtoken(tokenList, "default"); + addtoken(tokenList, ":"); + children.back()->createTokens(tokenList); + return nullptr; + } + if (nodeType == DoStmt) { + addtoken(tokenList, "do"); + createScope(tokenList, Scope::ScopeType::eDo, getChild(0), tokenList.back()); + Token *tok1 = addtoken(tokenList, "while"); + Token *par1 = addtoken(tokenList, "("); + Token *expr = children[1]->createTokens(tokenList); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + par1->astOperand1(tok1); + par1->astOperand2(expr); + return nullptr; + } + if (nodeType == EnumConstantDecl) { + Token *nameToken = addtoken(tokenList, getSpelling()); + auto *scope = const_cast(nameToken->scope()); + scope->enumeratorList.emplace_back(nameToken->scope()); + Enumerator *e = &scope->enumeratorList.back(); + e->name = nameToken; + e->value = mData->enumValue++; + e->value_known = true; + mData->enumDecl(mExtTokens.front(), nameToken, e); + return nameToken; + } + if (nodeType == EnumDecl) { + int colIndex = mExtTokens.size() - 1; + while (colIndex > 0 && !startsWith(mExtTokens[colIndex],"col:") && !startsWith(mExtTokens[colIndex],"line:")) + --colIndex; + if (colIndex == 0) + return nullptr; + + mData->enumValue = 0; + Token *enumtok = addtoken(tokenList, "enum"); + const Token *nametok = nullptr; + { + int nameIndex = mExtTokens.size() - 1; + while (nameIndex > colIndex && mExtTokens[nameIndex][0] == '\'') + --nameIndex; + if (nameIndex > colIndex) + nametok = addtoken(tokenList, mExtTokens[nameIndex]); + if (mExtTokens.back()[0] == '\'') { + addtoken(tokenList, ":"); + addTypeTokens(tokenList, mExtTokens.back()); + } + } + Scope *enumscope = createScope(tokenList, Scope::ScopeType::eEnum, children, enumtok); + if (nametok) + enumscope->className = nametok->str(); + if (enumscope->bodyEnd && Token::simpleMatch(enumscope->bodyEnd->previous(), ", }")) + const_cast(enumscope->bodyEnd)->deletePrevious(); + + // Create enum type + mData->mSymbolDatabase->typeList.emplace_back(enumtok, enumscope, enumtok->scope()); + enumscope->definedType = &mData->mSymbolDatabase->typeList.back(); + if (nametok) + const_cast(enumtok->scope())->definedTypesMap[nametok->str()] = enumscope->definedType; + + return nullptr; + } + if (nodeType == ExprWithCleanups) + return getChild(0)->createTokens(tokenList); + if (nodeType == FieldDecl) + return createTokensVarDecl(tokenList); + if (nodeType == FloatingLiteral) + return addtoken(tokenList, mExtTokens.back()); + if (nodeType == ForStmt) { + Token *forToken = addtoken(tokenList, "for"); + Token *par1 = addtoken(tokenList, "("); + Token *expr1 = getChild(0) ? children[0]->createTokens(tokenList) : nullptr; + Token *sep1 = addtoken(tokenList, ";"); + Token *expr2 = children[2] ? children[2]->createTokens(tokenList) : nullptr; + Token *sep2 = addtoken(tokenList, ";"); + Token *expr3 = children[3] ? children[3]->createTokens(tokenList) : nullptr; + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + par1->astOperand1(forToken); + par1->astOperand2(sep1); + sep1->astOperand1(expr1); + sep1->astOperand2(sep2); + sep2->astOperand1(expr2); + sep2->astOperand2(expr3); + createScope(tokenList, Scope::ScopeType::eFor, children[4], forToken); + return nullptr; + } + if (nodeType == FunctionDecl) { + createTokensFunctionDecl(tokenList); + return nullptr; + } + if (nodeType == FunctionTemplateDecl) { + bool first = true; + for (const AstNodePtr& child: children) { + if (child->nodeType == FunctionDecl) { + if (!first) + child->createTokens(tokenList); + first = false; + } + } + return nullptr; + } + if (nodeType == GotoStmt) { + addtoken(tokenList, "goto"); + addtoken(tokenList, unquote(mExtTokens[mExtTokens.size() - 2])); + addtoken(tokenList, ";"); + return nullptr; + } + if (nodeType == IfStmt) { + AstNodePtr cond; + AstNodePtr thenCode; + AstNodePtr elseCode; + if (children.size() == 2) { + cond = children[children.size() - 2]; + thenCode = children[children.size() - 1]; + } else { + cond = children[children.size() - 3]; + thenCode = children[children.size() - 2]; + elseCode = children[children.size() - 1]; + } + + Token *iftok = addtoken(tokenList, "if"); + Token *par1 = addtoken(tokenList, "("); + par1->astOperand1(iftok); + par1->astOperand2(cond->createTokens(tokenList)); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + createScope(tokenList, Scope::ScopeType::eIf, std::move(thenCode), iftok); + if (elseCode) { + elseCode->addtoken(tokenList, "else"); + createScope(tokenList, Scope::ScopeType::eElse, std::move(elseCode), tokenList.back()); + } + return nullptr; + } + if (nodeType == ImplicitCastExpr) { + Token *expr = getChild(0)->createTokens(tokenList); + if (!expr->valueType() || contains(mExtTokens, "")) + setValueType(expr); + return expr; + } + if (nodeType == InitListExpr) { + const Scope *scope = tokenList.back()->scope(); + Token *start = addtoken(tokenList, "{"); + start->scope(scope); + for (const AstNodePtr& child: children) { + if (tokenList.back()->str() != "{") + addtoken(tokenList, ","); + child->createTokens(tokenList); + } + Token *end = addtoken(tokenList, "}"); + end->scope(scope); + start->link(end); + end->link(start); + mData->mNotScope.insert(end); + return start; + } + if (nodeType == IntegerLiteral) + return addtoken(tokenList, mExtTokens.back()); + if (nodeType == LabelStmt) { + addtoken(tokenList, unquote(mExtTokens.back())); + addtoken(tokenList, ":"); + for (const auto& child: children) + child->createTokens(tokenList); + return nullptr; + } + if (nodeType == LinkageSpecDecl) + return nullptr; + if (nodeType == MaterializeTemporaryExpr) + return getChild(0)->createTokens(tokenList); + if (nodeType == MemberExpr) { + Token *s = getChild(0)->createTokens(tokenList); + Token *dot = addtoken(tokenList, "."); + std::string memberName = getSpelling(); + if (startsWith(memberName, "->")) { + dot->originalName("->"); + memberName = memberName.substr(2); + } else if (startsWith(memberName, ".")) { + memberName = memberName.substr(1); + } + if (memberName.empty()) + memberName = ""; + Token *member = addtoken(tokenList, memberName); + mData->ref(mExtTokens.back(), member); + dot->astOperand1(s); + dot->astOperand2(member); + return dot; + } + if (nodeType == NamespaceDecl) { + if (children.empty()) + return nullptr; + const Token *defToken = addtoken(tokenList, "namespace"); + const std::string &s = mExtTokens[mExtTokens.size() - 2]; + const Token* nameToken = (startsWith(s, "col:") || startsWith(s, "line:")) ? + addtoken(tokenList, mExtTokens.back()) : nullptr; + Scope *scope = createScope(tokenList, Scope::ScopeType::eNamespace, children, defToken); + if (nameToken) + scope->className = nameToken->str(); + return nullptr; + } + if (nodeType == NullStmt) + return addtoken(tokenList, ";"); + if (nodeType == ParenExpr) { + Token *par1 = addtoken(tokenList, "("); + Token *expr = getChild(0)->createTokens(tokenList); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + return expr; + } + if (nodeType == RecordDecl) { + const Token *classDef = addtoken(tokenList, "struct"); + const std::string &recordName = getSpelling(); + if (!recordName.empty()) + addtoken(tokenList, getSpelling()); + if (!isDefinition()) { + addtoken(tokenList, ";"); + return nullptr; + } + + Scope *recordScope = createScope(tokenList, Scope::ScopeType::eStruct, children, classDef); + mData->mSymbolDatabase->typeList.emplace_back(classDef, recordScope, classDef->scope()); + recordScope->definedType = &mData->mSymbolDatabase->typeList.back(); + if (!recordName.empty()) { + recordScope->className = recordName; + const_cast(classDef->scope())->definedTypesMap[recordName] = recordScope->definedType; + } + + return nullptr; + } + if (nodeType == ReturnStmt) { + Token *tok1 = addtoken(tokenList, "return"); + if (!children.empty()) { + getChild(0)->setValueType(tok1); + tok1->astOperand1(getChild(0)->createTokens(tokenList)); + } + return tok1; + } + if (nodeType == StringLiteral) + return addtoken(tokenList, mExtTokens.back()); + if (nodeType == SwitchStmt) { + Token *tok1 = addtoken(tokenList, "switch"); + Token *par1 = addtoken(tokenList, "("); + Token *expr = children[children.size() - 2]->createTokens(tokenList); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + par1->astOperand1(tok1); + par1->astOperand2(expr); + createScope(tokenList, Scope::ScopeType::eSwitch, children.back(), tok1); + return nullptr; + } + if (nodeType == TypedefDecl) { + addtoken(tokenList, "typedef"); + addTypeTokens(tokenList, getType()); + return addtoken(tokenList, getSpelling()); + } + if (nodeType == UnaryOperator) { + int index = (int)mExtTokens.size() - 1; + while (index > 0 && mExtTokens[index][0] != '\'') + --index; + Token *unop = addtoken(tokenList, unquote(mExtTokens[index])); + unop->astOperand1(getChild(0)->createTokens(tokenList)); + return unop; + } + if (nodeType == UnaryExprOrTypeTraitExpr) { + Token *tok1 = addtoken(tokenList, getSpelling()); + Token *par1 = addtoken(tokenList, "("); + if (children.empty()) + addTypeTokens(tokenList, mExtTokens.back()); + else { + AstNodePtr child = getChild(0); + if (child && child->nodeType == ParenExpr) + child = child->getChild(0); + Token *expr = child->createTokens(tokenList); + child->setValueType(expr); + par1->astOperand2(expr); + } + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + par1->astOperand1(tok1); + par1->astOperand2(par1->next()); + setValueType(par1); + return par1; + } + if (nodeType == VarDecl) + return createTokensVarDecl(tokenList); + if (nodeType == WhileStmt) { + AstNodePtr cond = children[children.size() - 2]; + AstNodePtr body = children.back(); + Token *whiletok = addtoken(tokenList, "while"); + Token *par1 = addtoken(tokenList, "("); + par1->astOperand1(whiletok); + par1->astOperand2(cond->createTokens(tokenList)); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + createScope(tokenList, Scope::ScopeType::eWhile, std::move(body), whiletok); + return nullptr; + } + return addtoken(tokenList, "?" + nodeType + "?"); +} + +Token * clangimport::AstNode::createTokensCall(TokenList &tokenList) +{ + int firstParam; + Token *f; + if (nodeType == CXXOperatorCallExpr) { + firstParam = 2; + Token *obj = getChild(1)->createTokens(tokenList); + Token *dot = addtoken(tokenList, "."); + Token *op = getChild(0)->createTokens(tokenList); + dot->astOperand1(obj); + dot->astOperand2(op); + f = dot; + } else { + firstParam = 1; + f = getChild(0)->createTokens(tokenList); + } + f->setValueType(nullptr); + Token *par1 = addtoken(tokenList, "("); + par1->astOperand1(f); + int args = 0; + while (args < children.size() && children[args]->nodeType != CXXDefaultArgExpr) + args++; + Token *child = nullptr; + for (int c = firstParam; c < args; ++c) { + if (child) { + Token *comma = addtoken(tokenList, ","); + comma->setValueType(nullptr); + comma->astOperand1(child); + comma->astOperand2(children[c]->createTokens(tokenList)); + child = comma; + } else { + child = children[c]->createTokens(tokenList); + } + } + par1->astOperand2(child); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + return par1; +} + +void clangimport::AstNode::createTokensFunctionDecl(TokenList &tokenList) +{ + const bool prev = contains(mExtTokens, "prev"); + const bool hasBody = !children.empty() && children.back()->nodeType == CompoundStmt; + const bool isStatic = contains(mExtTokens, "static"); + const bool isInline = contains(mExtTokens, "inline"); + + const Token *startToken = nullptr; + + SymbolDatabase *symbolDatabase = mData->mSymbolDatabase; + if (nodeType != CXXConstructorDecl && nodeType != CXXDestructorDecl) { + if (isStatic) + addtoken(tokenList, "static"); + if (isInline) + addtoken(tokenList, "inline"); + const Token * const before = tokenList.back(); + addTypeTokens(tokenList, '\'' + getType() + '\''); + startToken = before ? before->next() : tokenList.front(); + } + + if (mExtTokens.size() > 4 && mExtTokens[1] == "parent") + addFullScopeNameTokens(tokenList, mData->getScope(mExtTokens[2])); + + Token *nameToken = addtoken(tokenList, getSpelling() + getTemplateParameters()); + auto *nestedIn = const_cast(nameToken->scope()); + + if (prev) { + const std::string addr = *(std::find(mExtTokens.cbegin(), mExtTokens.cend(), "prev") + 1); + mData->ref(addr, nameToken); + } + if (!nameToken->function()) { + nestedIn->functionList.emplace_back(nameToken, unquote(getFullType())); + mData->funcDecl(mExtTokens.front(), nameToken, &nestedIn->functionList.back()); + if (nodeType == CXXConstructorDecl) + nestedIn->functionList.back().type = Function::Type::eConstructor; + else if (nodeType == CXXDestructorDecl) + nestedIn->functionList.back().type = Function::Type::eDestructor; + else + nestedIn->functionList.back().retDef = startToken; + } + + auto * const function = const_cast(nameToken->function()); + + if (!prev) { + auto accessControl = mData->scopeAccessControl.find(tokenList.back()->scope()); + if (accessControl != mData->scopeAccessControl.end()) + function->access = accessControl->second; + } + + Scope *scope = nullptr; + if (hasBody) { + symbolDatabase->scopeList.emplace_back(nullptr, nullptr, nestedIn); + scope = &symbolDatabase->scopeList.back(); + scope->check = symbolDatabase; + scope->function = function; + scope->classDef = nameToken; + scope->type = Scope::ScopeType::eFunction; + scope->className = nameToken->str(); + nestedIn->nestedList.push_back(scope); + function->hasBody(true); + function->functionScope = scope; + } + + Token *par1 = addtoken(tokenList, "("); + if (!function->arg) + function->arg = par1; + function->token = nameToken; + if (!function->nestedIn) + function->nestedIn = nestedIn; + function->argDef = par1; + // Function arguments + for (int i = 0; i < children.size(); ++i) { + AstNodePtr child = children[i]; + if (child->nodeType != ParmVarDecl) + continue; + if (tokenList.back() != par1) + addtoken(tokenList, ","); + const Type *recordType = addTypeTokens(tokenList, child->mExtTokens.back(), nestedIn); + const Token *typeEndToken = tokenList.back(); + const std::string spelling = child->getSpelling(); + Token *vartok = nullptr; + if (!spelling.empty()) + vartok = child->addtoken(tokenList, spelling); + if (!prev) { + function->argumentList.emplace_back(vartok, child->getType(), nullptr, typeEndToken, i, AccessControl::Argument, recordType, scope); + if (vartok) { + const std::string addr = child->mExtTokens[0]; + mData->varDecl(addr, vartok, &function->argumentList.back()); + } + } else if (vartok) { + const std::string addr = child->mExtTokens[0]; + mData->ref(addr, vartok); + } + } + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + + if (function->isConst()) + addtoken(tokenList, "const"); + + // Function body + if (hasBody) { + symbolDatabase->functionScopes.push_back(scope); + Token *bodyStart = addtoken(tokenList, "{"); + bodyStart->scope(scope); + children.back()->createTokens(tokenList); + Token *bodyEnd = addtoken(tokenList, "}"); + scope->bodyStart = bodyStart; + scope->bodyEnd = bodyEnd; + bodyStart->link(bodyEnd); + bodyEnd->link(bodyStart); + } else { + if (nodeType == CXXConstructorDecl && contains(mExtTokens, "default")) { + addtoken(tokenList, "="); + addtoken(tokenList, "default"); + } + + addtoken(tokenList, ";"); + } +} + +void clangimport::AstNode::createTokensForCXXRecord(TokenList &tokenList) +{ + const bool isStruct = contains(mExtTokens, "struct"); + Token * const classToken = addtoken(tokenList, isStruct ? "struct" : "class"); + std::string className; + if (mExtTokens[mExtTokens.size() - 2] == (isStruct?"struct":"class")) + className = mExtTokens.back(); + else + className = mExtTokens[mExtTokens.size() - 2]; + className += getTemplateParameters(); + /*Token *nameToken =*/ addtoken(tokenList, className); + // base classes + bool firstBase = true; + for (const AstNodePtr &child: children) { + if (child->nodeType == "public" || child->nodeType == "protected" || child->nodeType == "private") { + addtoken(tokenList, firstBase ? ":" : ","); + addtoken(tokenList, child->nodeType); + addtoken(tokenList, unquote(child->mExtTokens.back())); + firstBase = false; + } + } + // definition + if (isDefinition()) { + std::vector children2; + std::copy_if(children.cbegin(), children.cend(), std::back_inserter(children2), [](const AstNodePtr& child) { + return child->nodeType == CXXConstructorDecl || + child->nodeType == CXXDestructorDecl || + child->nodeType == CXXMethodDecl || + child->nodeType == FieldDecl || + child->nodeType == VarDecl || + child->nodeType == AccessSpecDecl || + child->nodeType == TypedefDecl; + }); + Scope *scope = createScope(tokenList, isStruct ? Scope::ScopeType::eStruct : Scope::ScopeType::eClass, children2, classToken); + const std::string addr = mExtTokens[0]; + mData->scopeDecl(addr, scope); + scope->className = className; + mData->mSymbolDatabase->typeList.emplace_back(classToken, scope, classToken->scope()); + scope->definedType = &mData->mSymbolDatabase->typeList.back(); + const_cast(classToken->scope())->definedTypesMap[className] = scope->definedType; + } + addtoken(tokenList, ";"); + tokenList.back()->scope(classToken->scope()); +} + +Token * clangimport::AstNode::createTokensVarDecl(TokenList &tokenList) +{ + const std::string addr = mExtTokens.front(); + if (contains(mExtTokens, "static")) + addtoken(tokenList, "static"); + int typeIndex = mExtTokens.size() - 1; + while (typeIndex > 1 && std::isalpha(mExtTokens[typeIndex][0])) + typeIndex--; + const std::string type = mExtTokens[typeIndex]; + const std::string name = mExtTokens[typeIndex - 1]; + const Token *startToken = tokenList.back(); + const ::Type *recordType = addTypeTokens(tokenList, type); + if (!startToken) + startToken = tokenList.front(); + else if (startToken->str() != "static") + startToken = startToken->next(); + Token *vartok1 = addtoken(tokenList, name); + auto *scope = const_cast(tokenList.back()->scope()); + scope->varlist.emplace_back(vartok1, unquote(type), startToken, vartok1->previous(), 0, scope->defaultAccess(), recordType, scope); + mData->varDecl(addr, vartok1, &scope->varlist.back()); + if (mExtTokens.back() == "cinit" && !children.empty()) { + Token *eq = addtoken(tokenList, "="); + eq->astOperand1(vartok1); + eq->astOperand2(children.back()->createTokens(tokenList)); + return eq; + } + if (mExtTokens.back() == "callinit") { + Token *par1 = addtoken(tokenList, "("); + par1->astOperand1(vartok1); + par1->astOperand2(getChild(0)->createTokens(tokenList)); + Token *par2 = addtoken(tokenList, ")"); + par1->link(par2); + par2->link(par1); + return par1; + } + if (mExtTokens.back() == "listinit") { + return getChild(0)->createTokens(tokenList); + } + return vartok1; +} + +static void setTypes(TokenList &tokenList) +{ + for (Token *tok = tokenList.front(); tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "sizeof (")) { + for (Token *typeToken = tok->tokAt(2); typeToken->str() != ")"; typeToken = typeToken->next()) { + if (typeToken->type()) + continue; + typeToken->type(typeToken->scope()->findType(typeToken->str())); + } + } + } +} + +static void setValues(const Tokenizer &tokenizer, const SymbolDatabase *symbolDatabase) +{ + const Settings & settings = tokenizer.getSettings(); + + for (const Scope& scope : symbolDatabase->scopeList) { + if (!scope.definedType) + continue; + + int typeSize = 0; + for (const Variable &var: scope.varlist) { + const int mul = std::accumulate(var.dimensions().cbegin(), var.dimensions().cend(), 1, [](int v, const Dimension& dim) { + return v * dim.num; + }); + if (var.valueType()) + typeSize += mul * var.valueType()->typeSize(settings.platform, true); + } + scope.definedType->sizeOf = typeSize; + } + + for (auto *tok = const_cast(tokenizer.tokens()); tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "sizeof (")) { + ValueType vt = ValueType::parseDecl(tok->tokAt(2), settings); + const int sz = vt.typeSize(settings.platform, true); + if (sz <= 0) + continue; + long long mul = 1; + for (const Token *arrtok = tok->linkAt(1)->previous(); arrtok; arrtok = arrtok->previous()) { + const std::string &a = arrtok->str(); + if (a.size() > 2 && a[0] == '[' && a.back() == ']') + mul *= strToInt(a.substr(1)); + else + break; + } + ValueFlow::Value v(mul * sz); + v.setKnown(); + tok->next()->addValue(v); + } + } +} + +void clangimport::parseClangAstDump(Tokenizer &tokenizer, std::istream &f) +{ + TokenList &tokenList = tokenizer.list; + + tokenizer.createSymbolDatabase(); + auto *symbolDatabase = const_cast(tokenizer.getSymbolDatabase()); + symbolDatabase->scopeList.emplace_back(nullptr, nullptr, nullptr); + symbolDatabase->scopeList.back().type = Scope::ScopeType::eGlobal; + symbolDatabase->scopeList.back().check = symbolDatabase; + + clangimport::Data data; + data.mSettings = &tokenizer.getSettings(); + data.mSymbolDatabase = symbolDatabase; + std::string line; + std::vector tree; + while (std::getline(f,line)) { + const std::string::size_type pos1 = line.find('-'); + if (pos1 == std::string::npos) + continue; + if (!tree.empty() && line.substr(pos1) == "-<<>>") { + const int level = (pos1 - 1) / 2; + tree[level - 1]->children.push_back(nullptr); + continue; + } + const std::string::size_type pos2 = line.find(' ', pos1); + if (pos2 < pos1 + 4 || pos2 == std::string::npos) + continue; + const std::string nodeType = line.substr(pos1+1, pos2 - pos1 - 1); + const std::string ext = line.substr(pos2); + + if (pos1 == 1 && endsWith(nodeType, "Decl")) { + if (!tree.empty()) + tree[0]->createTokens1(tokenList); + tree.clear(); + tree.push_back(std::make_shared(nodeType, ext, &data)); + continue; + } + + const int level = (pos1 - 1) / 2; + if (level == 0 || level > tree.size()) + continue; + + AstNodePtr newNode = std::make_shared(nodeType, ext, &data); + tree[level - 1]->children.push_back(newNode); + if (level >= tree.size()) + tree.push_back(std::move(newNode)); + else + tree[level] = std::move(newNode); + } + + if (!tree.empty()) + tree[0]->createTokens1(tokenList); + + // Validation + for (const Token *tok = tokenList.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "(|)|[|]|{|}") && !tok->link()) + throw InternalError(tok, "Token::link() is not set properly"); + } + + if (tokenList.front()) + tokenList.front()->assignIndexes(); + symbolDatabase->clangSetVariables(data.getVariableList()); + symbolDatabase->createSymbolDatabaseExprIds(); + tokenList.clangSetOrigFiles(); + setTypes(tokenList); + setValues(tokenizer, symbolDatabase); +} + diff --git a/cppcheck-2.14.0/lib/clangimport.h b/cppcheck-2.14.0/lib/clangimport.h new file mode 100644 index 00000000..d3ceb105 --- /dev/null +++ b/cppcheck-2.14.0/lib/clangimport.h @@ -0,0 +1,35 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef clangimportH +#define clangimportH +//--------------------------------------------------------------------------- + +#include "config.h" + +#include + +class Tokenizer; + +namespace clangimport { + void CPPCHECKLIB parseClangAstDump(Tokenizer &tokenizer, std::istream &f); +} + +#endif diff --git a/cppcheck-2.14.0/lib/color.cpp b/cppcheck-2.14.0/lib/color.cpp new file mode 100644 index 00000000..fc0a6281 --- /dev/null +++ b/cppcheck-2.14.0/lib/color.cpp @@ -0,0 +1,65 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "color.h" + +#ifndef _WIN32 +#include +#include +#include +#include +#endif + +bool gDisableColors = false; + +#ifndef _WIN32 +static bool isStreamATty(const std::ostream & os) +{ + static const bool stdout_tty = isatty(STDOUT_FILENO); + static const bool stderr_tty = isatty(STDERR_FILENO); + if (&os == &std::cout) + return stdout_tty; + if (&os == &std::cerr) + return stderr_tty; + return (stdout_tty && stderr_tty); +} +#endif + +std::ostream& operator<<(std::ostream & os, Color c) +{ +#ifndef _WIN32 + if (!gDisableColors && isStreamATty(os)) + return os << "\033[" << static_cast(c) << "m"; +#else + (void)c; +#endif + return os; +} + +std::string toString(Color c) +{ +#ifndef _WIN32 + std::stringstream ss; + ss << c; + return ss.str(); +#else + (void)c; + return ""; +#endif +} + diff --git a/cppcheck-2.14.0/lib/color.h b/cppcheck-2.14.0/lib/color.h new file mode 100644 index 00000000..247de299 --- /dev/null +++ b/cppcheck-2.14.0/lib/color.h @@ -0,0 +1,43 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef colorH +#define colorH + +#include "config.h" + +#include +#include + +enum class Color { + Reset = 0, + Bold = 1, + Dim = 2, + FgRed = 31, + FgGreen = 32, + FgBlue = 34, + FgMagenta = 35, + FgDefault = 39 +}; +CPPCHECKLIB std::ostream& operator<<(std::ostream& os, Color c); + +CPPCHECKLIB std::string toString(Color c); + +extern CPPCHECKLIB bool gDisableColors; // for testing + +#endif diff --git a/cppcheck-2.14.0/lib/config.h b/cppcheck-2.14.0/lib/config.h new file mode 100644 index 00000000..3674f778 --- /dev/null +++ b/cppcheck-2.14.0/lib/config.h @@ -0,0 +1,204 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef configH +#define configH + +#ifdef MAXTIME +#error "MAXTIME is no longer supported - please use command-line options --checks-max-time=, --template-max-time= and --typedef-max-time= instead" +#endif + +#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(DISABLE_CRTDBG_MAP_ALLOC) && defined(_MSC_VER) && defined(_DEBUG) +# define _CRTDBG_MAP_ALLOC +# include +#endif + +// compatibility macros +#ifndef __has_builtin +#define __has_builtin(x) 0 +#endif + +#ifndef __has_include +#define __has_include(x) 0 +#endif + +#ifndef __has_cpp_attribute +#define __has_cpp_attribute(x) 0 +#endif + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +// C++11 noexcept +#if defined(__cpp_noexcept_function_type) || \ + (defined(__GNUC__) && (__GNUC__ >= 5)) \ + || defined(__clang__) \ + || defined(__CPPCHECK__) +# define NOEXCEPT noexcept +#else +# define NOEXCEPT +#endif + +// C++11 noreturn +#if __has_cpp_attribute (noreturn) \ + || (defined(__GNUC__) && (__GNUC__ >= 5)) \ + || defined(__clang__) \ + || defined(__CPPCHECK__) +# define NORETURN [[noreturn]] +#elif defined(__GNUC__) +# define NORETURN __attribute__((noreturn)) +#else +# define NORETURN +#endif + +// fallthrough +#if __cplusplus >= 201703L && __has_cpp_attribute (fallthrough) +# define FALLTHROUGH [[fallthrough]] +#elif defined(__clang__) +# define FALLTHROUGH [[clang::fallthrough]] +#elif (defined(__GNUC__) && (__GNUC__ >= 7)) +# define FALLTHROUGH __attribute__((fallthrough)) +#else +# define FALLTHROUGH +#endif + +// unused +#if __cplusplus >= 201703L && __has_cpp_attribute (maybe_unused) +# define UNUSED [[maybe_unused]] +#elif defined(__GNUC__) \ + || defined(__clang__) \ + || defined(__CPPCHECK__) +# define UNUSED __attribute__((unused)) +#else +# define UNUSED +#endif + +// warn_unused +#if __has_cpp_attribute (gnu::warn_unused) || \ + (defined(__clang__) && (__clang_major__ >= 15)) +# define WARN_UNUSED [[gnu::warn_unused]] +#else +# define WARN_UNUSED +#endif + +// deprecated +#if defined(__GNUC__) \ + || defined(__clang__) \ + || defined(__CPPCHECK__) +# define DEPRECATED __attribute__((deprecated)) +#elif defined(_MSC_VER) +# define DEPRECATED __declspec(deprecated) +#else +# define DEPRECATED +#endif + +#define REQUIRES(msg, ...) class=typename std::enable_if<__VA_ARGS__::value>::type + +#include +static const std::string emptyString; + +// Use the nonneg macro when you want to assert that a variable/argument is not negative +#ifdef __CPPCHECK__ +#define nonneg __cppcheck_low__(0) +#elif defined(NONNEG) +// Enable non-negative values checking +// TODO : investigate using annotations/contracts for stronger value checking +#define nonneg unsigned +#else +// Disable non-negative values checking +#define nonneg +#endif + +#if __has_feature(address_sanitizer) +#define ASAN 1 +#endif + +#ifndef ASAN +#ifdef __SANITIZE_ADDRESS__ +#define ASAN 1 +#else +#define ASAN 0 +#endif +#endif + +#if defined(_WIN32) +#define HAS_THREADING_MODEL_THREAD +#define STDCALL __stdcall +#elif ((defined(__GNUC__) || defined(__sun)) && !defined(__MINGW32__)) || defined(__CPPCHECK__) +#define HAS_THREADING_MODEL_FORK +#if !defined(DISALLOW_THREAD_EXECUTOR) +#define HAS_THREADING_MODEL_THREAD +#endif +#define STDCALL +#else +#error "No threading model defined" +#endif + +#define STRINGISIZE(...) #__VA_ARGS__ + +#ifdef __clang__ +#define SUPPRESS_WARNING_PUSH(warning) _Pragma("clang diagnostic push") _Pragma(STRINGISIZE(clang diagnostic ignored warning)) +#define SUPPRESS_WARNING_POP _Pragma("clang diagnostic pop") +#define SUPPRESS_WARNING_GCC_PUSH(warning) +#define SUPPRESS_WARNING_GCC_POP +#define SUPPRESS_WARNING_CLANG_PUSH(warning) SUPPRESS_WARNING_PUSH(warning) +#define SUPPRESS_WARNING_CLANG_POP SUPPRESS_WARNING_POP +#elif defined(__GNUC__) +#define SUPPRESS_WARNING_PUSH(warning) _Pragma("GCC diagnostic push") _Pragma(STRINGISIZE(GCC diagnostic ignored warning)) +#define SUPPRESS_WARNING_POP _Pragma("GCC diagnostic pop") +#define SUPPRESS_WARNING_GCC_PUSH(warning) SUPPRESS_WARNING_PUSH(warning) +#define SUPPRESS_WARNING_GCC_POP SUPPRESS_WARNING_POP +#define SUPPRESS_WARNING_CLANG_PUSH(warning) +#define SUPPRESS_WARNING_CLANG_POP +#else +#define SUPPRESS_WARNING_PUSH(warning) +#define SUPPRESS_WARNING_POP +#define SUPPRESS_WARNING_GCC_PUSH(warning) +#define SUPPRESS_WARNING_GCC_POP +#define SUPPRESS_WARNING_CLANG_PUSH(warning) +#define SUPPRESS_WARNING_CLANG_POP +#endif + +#if !defined(NO_WINDOWS_SEH) && defined(_WIN32) && defined(_MSC_VER) +#define USE_WINDOWS_SEH +#endif + +// TODO: __GLIBC__ is dependent on the features.h include and not a built-in compiler define, so it might be problematic to depend on it +#if !defined(NO_UNIX_BACKTRACE_SUPPORT) && defined(__GNUC__) && defined(__GLIBC__) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__NetBSD__) && !defined(__SVR4) && !defined(__QNX__) +#define USE_UNIX_BACKTRACE_SUPPORT +#endif + +#if !defined(NO_UNIX_SIGNAL_HANDLING) && defined(__GNUC__) && !defined(__MINGW32__) && !defined(__OS2__) +#define USE_UNIX_SIGNAL_HANDLING +#endif + +#endif // configH diff --git a/cppcheck-2.14.0/lib/cppcheck.cpp b/cppcheck-2.14.0/lib/cppcheck.cpp new file mode 100644 index 00000000..17948db6 --- /dev/null +++ b/cppcheck-2.14.0/lib/cppcheck.cpp @@ -0,0 +1,1900 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "addoninfo.h" +#include "check.h" +#include "checkunusedfunctions.h" +#include "clangimport.h" +#include "color.h" +#include "ctu.h" +#include "errortypes.h" +#include "filesettings.h" +#include "library.h" +#include "path.h" +#include "platform.h" +#include "preprocessor.h" +#include "standards.h" +#include "suppressions.h" +#include "timer.h" +#include "token.h" +#include "tokenize.h" +#include "tokenlist.h" +#include "utils.h" +#include "valueflow.h" +#include "version.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include // IWYU pragma: keep +#include +#include // <- TEMPORARY +#include +#include +#include +#include +#include +#include +#include +#include + +#include "json.h" + +#include + +#include "xml.h" + +#ifdef HAVE_RULES +#ifdef _WIN32 +#define PCRE_STATIC +#endif +#include +#endif + +class SymbolDatabase; + +static constexpr char Version[] = CPPCHECK_VERSION_STRING; +static constexpr char ExtraVersion[] = ""; + +static constexpr char FILELIST[] = "cppcheck-addon-ctu-file-list"; + +static TimerResults s_timerResults; + +// CWE ids used +static const CWE CWE398(398U); // Indicator of Poor Code Quality + +// File deleter +namespace { + class FilesDeleter { + public: + FilesDeleter() = default; + ~FilesDeleter() { + for (const std::string& fileName: mFilenames) + std::remove(fileName.c_str()); + } + void addFile(const std::string& fileName) { + mFilenames.push_back(fileName); + } + private: + std::vector mFilenames; + }; +} + +static std::string cmdFileName(std::string f) +{ + f = Path::toNativeSeparators(std::move(f)); + if (f.find(' ') != std::string::npos) + return "\"" + f + "\""; + return f; +} + +static std::vector split(const std::string &str, const std::string &sep=" ") +{ + std::vector ret; + for (std::string::size_type startPos = 0U; startPos < str.size();) { + startPos = str.find_first_not_of(sep, startPos); + if (startPos == std::string::npos) + break; + + if (str[startPos] == '\"') { + const std::string::size_type endPos = str.find('\"', startPos + 1); + ret.push_back(str.substr(startPos + 1, endPos - startPos - 1)); + startPos = (endPos < str.size()) ? (endPos + 1) : endPos; + continue; + } + + const std::string::size_type endPos = str.find(sep, startPos + 1); + ret.push_back(str.substr(startPos, endPos - startPos)); + startPos = endPos; + } + + return ret; +} + +static std::string getDumpFileName(const Settings& settings, const std::string& filename) +{ + if (!settings.dumpFile.empty()) + return settings.dumpFile; + + std::string extension; + if (settings.dump) + extension = ".dump"; + else + extension = "." + std::to_string(settings.pid) + ".dump"; + + if (!settings.dump && !settings.buildDir.empty()) + return AnalyzerInformation::getAnalyzerInfoFile(settings.buildDir, filename, emptyString) + extension; + return filename + extension; +} + +static std::string getCtuInfoFileName(const std::string &dumpFile) +{ + return dumpFile.substr(0, dumpFile.size()-4) + "ctu-info"; +} + +static void createDumpFile(const Settings& settings, + const std::string& filename, + std::ofstream& fdump, + std::string& dumpFile) +{ + if (!settings.dump && settings.addons.empty()) + return; + dumpFile = getDumpFileName(settings, filename); + + fdump.open(dumpFile); + if (!fdump.is_open()) + return; + + { + std::ofstream fout(getCtuInfoFileName(dumpFile)); + } + + std::string language; + switch (settings.enforcedLang) { + case Standards::Language::C: + language = " language=\"c\""; + break; + case Standards::Language::CPP: + language = " language=\"cpp\""; + break; + case Standards::Language::None: + { + // TODO: error out on unknown language? + const Standards::Language lang = Path::identify(filename); + if (lang == Standards::Language::CPP) + language = " language=\"cpp\""; + else if (lang == Standards::Language::C) + language = " language=\"c\""; + break; + } + } + + fdump << "\n"; + fdump << "\n"; + fdump << " " << '\n'; +} + +static std::string detectPython(const CppCheck::ExecuteCmdFn &executeCommand) +{ +#ifdef _WIN32 + const char *py_exes[] = { "python3.exe", "python.exe" }; +#else + const char *py_exes[] = { "python3", "python" }; +#endif + for (const char* py_exe : py_exes) { + std::string out; +#ifdef _MSC_VER + // FIXME: hack to avoid debug assertion with _popen() in executeCommand() for non-existing commands + const std::string cmd = std::string(py_exe) + " --version >NUL 2>&1"; + if (system(cmd.c_str()) != 0) { + // TODO: get more detailed error? + continue; + } +#endif + if (executeCommand(py_exe, split("--version"), "2>&1", out) == EXIT_SUCCESS && startsWith(out, "Python ") && std::isdigit(out[7])) { + return py_exe; + } + } + return ""; +} + +static std::vector executeAddon(const AddonInfo &addonInfo, + const std::string &defaultPythonExe, + const std::string &file, + const std::string &premiumArgs, + const CppCheck::ExecuteCmdFn &executeCommand) +{ + const std::string redirect = "2>&1"; + + std::string pythonExe; + + if (!addonInfo.executable.empty()) + pythonExe = addonInfo.executable; + else if (!addonInfo.python.empty()) + pythonExe = cmdFileName(addonInfo.python); + else if (!defaultPythonExe.empty()) + pythonExe = cmdFileName(defaultPythonExe); + else { + // store in static variable so we only look this up once + static const std::string detectedPythonExe = detectPython(executeCommand); + if (detectedPythonExe.empty()) + throw InternalError(nullptr, "Failed to auto detect python"); + pythonExe = detectedPythonExe; + } + + std::string args; + if (addonInfo.executable.empty()) + args = cmdFileName(addonInfo.runScript) + " " + cmdFileName(addonInfo.scriptFile); + args += std::string(args.empty() ? "" : " ") + "--cli" + addonInfo.args; + if (!premiumArgs.empty() && !addonInfo.executable.empty()) + args += " " + premiumArgs; + + const bool is_file_list = (file.find(FILELIST) != std::string::npos); + const std::string fileArg = (is_file_list ? " --file-list " : " ") + cmdFileName(file); + args += fileArg; + + std::string result; + if (const int exitcode = executeCommand(pythonExe, split(args), redirect, result)) { + std::string message("Failed to execute addon '" + addonInfo.name + "' - exitcode is " + std::to_string(exitcode)); + std::string details = pythonExe + " " + args; + if (result.size() > 2) { + details += "\nOutput:\n"; + details += result; + const auto pos = details.find_last_not_of("\n\r"); + if (pos != std::string::npos) + details.resize(pos + 1); + } + throw InternalError(nullptr, std::move(message), std::move(details)); + } + + std::vector addonResult; + + // Validate output.. + std::istringstream istr(result); + std::string line; + while (std::getline(istr, line)) { + // TODO: also bail out? + if (line.empty()) { + //std::cout << "addon '" << addonInfo.name << "' result contains empty line" << std::endl; + continue; + } + + // TODO: get rid of this + if (startsWith(line,"Checking ")) { + //std::cout << "addon '" << addonInfo.name << "' result contains 'Checking ' line" << std::endl; + continue; + } + + if (line[0] != '{') { + //std::cout << "addon '" << addonInfo.name << "' result is not a JSON" << std::endl; + + result.erase(result.find_last_not_of('\n') + 1, std::string::npos); // Remove trailing newlines + throw InternalError(nullptr, "Failed to execute '" + pythonExe + " " + args + "'. " + result); + } + + //std::cout << "addon '" << addonInfo.name << "' result is " << line << std::endl; + + // TODO: make these failures? + picojson::value res; + const std::string err = picojson::parse(res, line); + if (!err.empty()) { + //std::cout << "addon '" << addonInfo.name << "' result is not a valid JSON (" << err << ")" << std::endl; + continue; + } + if (!res.is()) { + //std::cout << "addon '" << addonInfo.name << "' result is not a JSON object" << std::endl; + continue; + } + addonResult.emplace_back(std::move(res)); + } + + // Valid results + return addonResult; +} + +static std::string getDefinesFlags(const std::string &semicolonSeparatedString) +{ + std::string flags; + for (const std::string &d: split(semicolonSeparatedString, ";")) + flags += "-D" + d + " "; + return flags; +} + +CppCheck::CppCheck(ErrorLogger &errorLogger, + bool useGlobalSuppressions, + ExecuteCmdFn executeCommand) + : mErrorLogger(errorLogger) + , mUseGlobalSuppressions(useGlobalSuppressions) + , mExecuteCommand(std::move(executeCommand)) +{} + +CppCheck::~CppCheck() +{ + while (!mFileInfo.empty()) { + delete mFileInfo.back(); + mFileInfo.pop_back(); + } + + if (mPlistFile.is_open()) { + mPlistFile << ErrorLogger::plistFooter(); + mPlistFile.close(); + } +} + +const char * CppCheck::version() +{ + return Version; +} + +const char * CppCheck::extraVersion() +{ + return ExtraVersion; +} + +static bool reportClangErrors(std::istream &is, const std::function& reportErr, std::vector &warnings) +{ + std::string line; + while (std::getline(is, line)) { + if (line.empty() || line[0] == ' ' || line[0] == '`' || line[0] == '-') + continue; + + std::string::size_type pos3 = line.find(": error: "); + if (pos3 == std::string::npos) + pos3 = line.find(": fatal error:"); + if (pos3 == std::string::npos) + pos3 = line.find(": warning:"); + if (pos3 == std::string::npos) + continue; + + // file:line:column: error: .... + const std::string::size_type pos2 = line.rfind(':', pos3 - 1); + const std::string::size_type pos1 = line.rfind(':', pos2 - 1); + + if (pos1 >= pos2 || pos2 >= pos3) + continue; + + const std::string filename = line.substr(0, pos1); + const std::string linenr = line.substr(pos1+1, pos2-pos1-1); + const std::string colnr = line.substr(pos2+1, pos3-pos2-1); + const std::string msg = line.substr(line.find(':', pos3+1) + 2); + + const std::string locFile = Path::toNativeSeparators(filename); + const int line_i = strToInt(linenr); + const int column = strToInt(colnr); + ErrorMessage::FileLocation loc(locFile, line_i, column); + ErrorMessage errmsg({std::move(loc)}, + locFile, + Severity::error, + msg, + "syntaxError", + Certainty::normal); + + if (line.compare(pos3, 10, ": warning:") == 0) { + warnings.push_back(std::move(errmsg)); + continue; + } + + reportErr(errmsg); + + return true; + } + return false; +} + +unsigned int CppCheck::checkClang(const std::string &path) +{ + if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mUnusedFunctionsCheck) + mUnusedFunctionsCheck.reset(new CheckUnusedFunctions()); + + if (!mSettings.quiet) + mErrorLogger.reportOut(std::string("Checking ") + path + " ...", Color::FgGreen); + + // TODO: this ignores the configured language + const bool isCpp = Path::identify(path) == Standards::Language::CPP; + const std::string langOpt = isCpp ? "-x c++" : "-x c"; + const std::string analyzerInfo = mSettings.buildDir.empty() ? std::string() : AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, path, emptyString); + const std::string clangcmd = analyzerInfo + ".clang-cmd"; + const std::string clangStderr = analyzerInfo + ".clang-stderr"; + const std::string clangAst = analyzerInfo + ".clang-ast"; + std::string exe = mSettings.clangExecutable; +#ifdef _WIN32 + // append .exe if it is not a path + if (Path::fromNativeSeparators(mSettings.clangExecutable).find('/') == std::string::npos) { + exe += ".exe"; + } +#endif + + std::string flags(langOpt + " "); + // TODO: does not apply C standard + if (isCpp && !mSettings.standards.stdValue.empty()) + flags += "-std=" + mSettings.standards.stdValue + " "; + + for (const std::string &i: mSettings.includePaths) + flags += "-I" + i + " "; + + flags += getDefinesFlags(mSettings.userDefines); + + const std::string args2 = "-fsyntax-only -Xclang -ast-dump -fno-color-diagnostics " + flags + path; + const std::string redirect2 = analyzerInfo.empty() ? std::string("2>&1") : ("2> " + clangStderr); + if (!mSettings.buildDir.empty()) { + std::ofstream fout(clangcmd); + fout << exe << " " << args2 << " " << redirect2 << std::endl; + } else if (mSettings.verbose && !mSettings.quiet) { + mErrorLogger.reportOut(exe + " " + args2); + } + + std::string output2; + const int exitcode = mExecuteCommand(exe,split(args2),redirect2,output2); + if (exitcode != EXIT_SUCCESS) { + // TODO: report as proper error + std::cerr << "Failed to execute '" << exe << " " << args2 << " " << redirect2 << "' - (exitcode: " << exitcode << " / output: " << output2 << ")" << std::endl; + return 0; // TODO: report as failure? + } + + if (output2.find("TranslationUnitDecl") == std::string::npos) { + // TODO: report as proper error + std::cerr << "Failed to execute '" << exe << " " << args2 << " " << redirect2 << "' - (no TranslationUnitDecl in output)" << std::endl; + return 0; // TODO: report as failure? + } + + // Ensure there are not syntax errors... + std::vector compilerWarnings; + if (!mSettings.buildDir.empty()) { + std::ifstream fin(clangStderr); + auto reportError = [this](const ErrorMessage& errorMessage) { + reportErr(errorMessage); + }; + if (reportClangErrors(fin, reportError, compilerWarnings)) + return 0; + } else { + std::istringstream istr(output2); + auto reportError = [this](const ErrorMessage& errorMessage) { + reportErr(errorMessage); + }; + if (reportClangErrors(istr, reportError, compilerWarnings)) + return 0; + } + + if (!mSettings.buildDir.empty()) { + std::ofstream fout(clangAst); + fout << output2 << std::endl; + } + + try { + Tokenizer tokenizer(mSettings, *this); + tokenizer.list.appendFileIfNew(path); + std::istringstream ast(output2); + clangimport::parseClangAstDump(tokenizer, ast); + ValueFlow::setValues(tokenizer.list, + const_cast(*tokenizer.getSymbolDatabase()), + *this, + mSettings, + &s_timerResults); + if (mSettings.debugnormal) + tokenizer.printDebugOutput(1); + checkNormalTokens(tokenizer); + + // create dumpfile + std::ofstream fdump; + std::string dumpFile; + createDumpFile(mSettings, path, fdump, dumpFile); + if (fdump.is_open()) { + // TODO: use tinyxml2 to create XML + fdump << "\n"; + for (const ErrorMessage& errmsg: compilerWarnings) + fdump << " \n"; + fdump << " \n"; + fdump << " \n"; + fdump << " \n"; + fdump << " \n"; + tokenizer.dump(fdump); + fdump << "\n"; + fdump << "\n"; + fdump.close(); + } + + // run addons + executeAddons(dumpFile, path); + + } catch (const InternalError &e) { + const ErrorMessage errmsg = ErrorMessage::fromInternalError(e, nullptr, path, "Bailing out from analysis: Processing Clang AST dump failed"); + reportErr(errmsg); + } catch (const TerminateException &) { + // Analysis is terminated + return mExitCode; + } catch (const std::exception &e) { + internalError(path, std::string("Processing Clang AST dump failed: ") + e.what()); + } + + return mExitCode; +} + +unsigned int CppCheck::check(const std::string &path) +{ + if (mSettings.clang) + return checkClang(Path::simplifyPath(path)); + + return checkFile(Path::simplifyPath(path), emptyString); +} + +unsigned int CppCheck::check(const std::string &path, const std::string &content) +{ + std::istringstream iss(content); + return checkFile(Path::simplifyPath(path), emptyString, &iss); +} + +unsigned int CppCheck::check(const FileSettings &fs) +{ + // TODO: move to constructor when CppCheck no longer owns the settings + if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mUnusedFunctionsCheck) + mUnusedFunctionsCheck.reset(new CheckUnusedFunctions()); + + CppCheck temp(mErrorLogger, mUseGlobalSuppressions, mExecuteCommand); + temp.mSettings = mSettings; + if (!temp.mSettings.userDefines.empty()) + temp.mSettings.userDefines += ';'; + if (mSettings.clang) + temp.mSettings.userDefines += fs.defines; + else + temp.mSettings.userDefines += fs.cppcheckDefines(); + temp.mSettings.includePaths = fs.includePaths; + temp.mSettings.userUndefs.insert(fs.undefs.cbegin(), fs.undefs.cend()); + if (fs.standard.find("++") != std::string::npos) + temp.mSettings.standards.setCPP(fs.standard); + else if (!fs.standard.empty()) + temp.mSettings.standards.setC(fs.standard); + if (fs.platformType != Platform::Type::Unspecified) + temp.mSettings.platform.set(fs.platformType); + if (mSettings.clang) { + temp.mSettings.includePaths.insert(temp.mSettings.includePaths.end(), fs.systemIncludePaths.cbegin(), fs.systemIncludePaths.cend()); + // TODO: propagate back suppressions + const unsigned int returnValue = temp.check(Path::simplifyPath(fs.filename)); + if (mUnusedFunctionsCheck) + mUnusedFunctionsCheck->updateFunctionData(*temp.mUnusedFunctionsCheck); + return returnValue; + } + const unsigned int returnValue = temp.checkFile(Path::simplifyPath(fs.filename), fs.cfg); + mSettings.supprs.nomsg.addSuppressions(temp.mSettings.supprs.nomsg.getSuppressions()); + if (mUnusedFunctionsCheck) + mUnusedFunctionsCheck->updateFunctionData(*temp.mUnusedFunctionsCheck); + return returnValue; +} + +static simplecpp::TokenList createTokenList(const std::string& filename, std::vector& files, simplecpp::OutputList* outputList, std::istream* fileStream) +{ + if (fileStream) + return {*fileStream, files, filename, outputList}; + + return {filename, files, outputList}; +} + +unsigned int CppCheck::checkFile(const std::string& filename, const std::string &cfgname, std::istream* fileStream) +{ + // TODO: move to constructor when CppCheck no longer owns the settings + if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mUnusedFunctionsCheck) + mUnusedFunctionsCheck.reset(new CheckUnusedFunctions()); + + mExitCode = 0; + + if (Settings::terminated()) + return mExitCode; + + const Timer fileTotalTimer(mSettings.showtime == SHOWTIME_MODES::SHOWTIME_FILE_TOTAL, filename); + + if (!mSettings.quiet) { + std::string fixedpath = Path::simplifyPath(filename); + fixedpath = Path::toNativeSeparators(std::move(fixedpath)); + mErrorLogger.reportOut(std::string("Checking ") + fixedpath + ' ' + cfgname + std::string("..."), Color::FgGreen); + + if (mSettings.verbose) { + mErrorLogger.reportOut("Defines:" + mSettings.userDefines); + std::string undefs; + for (const std::string& U : mSettings.userUndefs) { + if (!undefs.empty()) + undefs += ';'; + undefs += ' ' + U; + } + mErrorLogger.reportOut("Undefines:" + undefs); + std::string includePaths; + for (const std::string &I : mSettings.includePaths) + includePaths += " -I" + I; + mErrorLogger.reportOut("Includes:" + includePaths); + mErrorLogger.reportOut(std::string("Platform:") + mSettings.platform.toString()); + } + } + + if (mPlistFile.is_open()) { + mPlistFile << ErrorLogger::plistFooter(); + mPlistFile.close(); + } + + try { + if (mSettings.library.markupFile(filename)) { + if (mUnusedFunctionsCheck && mSettings.useSingleJob() && mSettings.buildDir.empty()) { + // this is not a real source file - we just want to tokenize it. treat it as C anyways as the language needs to be determined. + Tokenizer tokenizer(mSettings, *this); + tokenizer.list.setLang(Standards::Language::C); + if (fileStream) { + tokenizer.list.createTokens(*fileStream, filename); + } + else { + std::ifstream in(filename); + tokenizer.list.createTokens(in, filename); + } + mUnusedFunctionsCheck->parseTokens(tokenizer, mSettings); + } + return EXIT_SUCCESS; + } + + simplecpp::OutputList outputList; + std::vector files; + simplecpp::TokenList tokens1 = createTokenList(filename, files, &outputList, fileStream); + + // If there is a syntax error, report it and stop + const auto output_it = std::find_if(outputList.cbegin(), outputList.cend(), [](const simplecpp::Output &output){ + return Preprocessor::hasErrors(output); + }); + if (output_it != outputList.cend()) { + const simplecpp::Output &output = *output_it; + std::string file = Path::fromNativeSeparators(output.location.file()); + if (mSettings.relativePaths) + file = Path::getRelativePath(file, mSettings.basePaths); + + ErrorMessage::FileLocation loc1(file, output.location.line, output.location.col); + + ErrorMessage errmsg({std::move(loc1)}, + "", + Severity::error, + output.msg, + "syntaxError", + Certainty::normal); + reportErr(errmsg); + return mExitCode; + } + + Preprocessor preprocessor(mSettings, *this); + + if (!preprocessor.loadFiles(tokens1, files)) + return mExitCode; + + if (!mSettings.plistOutput.empty()) { + std::string filename2; + if (filename.find('/') != std::string::npos) + filename2 = filename.substr(filename.rfind('/') + 1); + else + filename2 = filename; + const std::size_t fileNameHash = std::hash {}(filename); + filename2 = mSettings.plistOutput + filename2.substr(0, filename2.find('.')) + "_" + std::to_string(fileNameHash) + ".plist"; + mPlistFile.open(filename2); + mPlistFile << ErrorLogger::plistHeader(version(), files); + } + + std::string dumpProlog; + if (mSettings.dump || !mSettings.addons.empty()) { + dumpProlog += getDumpFileContentsRawTokens(files, tokens1); + } + + // Parse comments and then remove them + preprocessor.inlineSuppressions(tokens1, mSettings.supprs.nomsg); + if (mSettings.dump || !mSettings.addons.empty()) { + std::ostringstream oss; + mSettings.supprs.nomsg.dump(oss); + dumpProlog += oss.str(); + } + tokens1.removeComments(); + preprocessor.removeComments(); + + if (!mSettings.buildDir.empty()) { + // Get toolinfo + std::ostringstream toolinfo; + toolinfo << CPPCHECK_VERSION_STRING; + toolinfo << (mSettings.severity.isEnabled(Severity::warning) ? 'w' : ' '); + toolinfo << (mSettings.severity.isEnabled(Severity::style) ? 's' : ' '); + toolinfo << (mSettings.severity.isEnabled(Severity::performance) ? 'p' : ' '); + toolinfo << (mSettings.severity.isEnabled(Severity::portability) ? 'p' : ' '); + toolinfo << (mSettings.severity.isEnabled(Severity::information) ? 'i' : ' '); + toolinfo << mSettings.userDefines; + mSettings.supprs.nomsg.dump(toolinfo); + + // Calculate hash so it can be compared with old hash / future hashes + const std::size_t hash = preprocessor.calculateHash(tokens1, toolinfo.str()); + std::list errors; + if (!mAnalyzerInformation.analyzeFile(mSettings.buildDir, filename, cfgname, hash, errors)) { + while (!errors.empty()) { + reportErr(errors.front()); + errors.pop_front(); + } + return mExitCode; // known results => no need to reanalyze file + } + } + + FilesDeleter filesDeleter; + + // write dump file xml prolog + std::ofstream fdump; + std::string dumpFile; + createDumpFile(mSettings, filename, fdump, dumpFile); + if (fdump.is_open()) { + fdump << dumpProlog; + if (!mSettings.dump) + filesDeleter.addFile(dumpFile); + } + + // Get directives + std::list directives = preprocessor.createDirectives(tokens1); + preprocessor.simplifyPragmaAsm(&tokens1); + + preprocessor.setPlatformInfo(&tokens1); + + // Get configurations.. + std::set configurations; + if ((mSettings.checkAllConfigurations && mSettings.userDefines.empty()) || mSettings.force) { + Timer t("Preprocessor::getConfigs", mSettings.showtime, &s_timerResults); + configurations = preprocessor.getConfigs(tokens1); + } else { + configurations.insert(mSettings.userDefines); + } + + if (mSettings.checkConfiguration) { + for (const std::string &config : configurations) + (void)preprocessor.getcode(tokens1, config, files, true); + + return 0; + } + +#ifdef HAVE_RULES + // Run define rules on raw code + if (hasRule("define")) { + std::string code; + for (const Directive &dir : directives) { + if (startsWith(dir.str,"#define ") || startsWith(dir.str,"#include ")) + code += "#line " + std::to_string(dir.linenr) + " \"" + dir.file + "\"\n" + dir.str + '\n'; + } + TokenList tokenlist(&mSettings); + std::istringstream istr2(code); + // TODO: asserts when file has unknown extension + tokenlist.createTokens(istr2, Path::identify(*files.begin())); // TODO: check result? + executeRules("define", tokenlist); + } +#endif + + if (!mSettings.force && configurations.size() > mSettings.maxConfigs) { + if (mSettings.severity.isEnabled(Severity::information)) { + tooManyConfigsError(Path::toNativeSeparators(filename),configurations.size()); + } else { + mTooManyConfigs = true; + } + } + + std::set hashes; + int checkCount = 0; + bool hasValidConfig = false; + std::list configurationError; + for (const std::string &currCfg : configurations) { + // bail out if terminated + if (Settings::terminated()) + break; + + // Check only a few configurations (default 12), after that bail out, unless --force + // was used. + if (!mSettings.force && ++checkCount > mSettings.maxConfigs) + break; + + if (!mSettings.userDefines.empty()) { + mCurrentConfig = mSettings.userDefines; + const std::vector v1(split(mSettings.userDefines, ";")); + for (const std::string &cfg: split(currCfg, ";")) { + if (std::find(v1.cbegin(), v1.cend(), cfg) == v1.cend()) { + mCurrentConfig += ";" + cfg; + } + } + } else { + mCurrentConfig = currCfg; + } + + if (mSettings.preprocessOnly) { + Timer t("Preprocessor::getcode", mSettings.showtime, &s_timerResults); + std::string codeWithoutCfg = preprocessor.getcode(tokens1, mCurrentConfig, files, true); + t.stop(); + + if (startsWith(codeWithoutCfg,"#file")) + codeWithoutCfg.insert(0U, "//"); + std::string::size_type pos = 0; + while ((pos = codeWithoutCfg.find("\n#file",pos)) != std::string::npos) + codeWithoutCfg.insert(pos+1U, "//"); + pos = 0; + while ((pos = codeWithoutCfg.find("\n#endfile",pos)) != std::string::npos) + codeWithoutCfg.insert(pos+1U, "//"); + pos = 0; + while ((pos = codeWithoutCfg.find(Preprocessor::macroChar,pos)) != std::string::npos) + codeWithoutCfg[pos] = ' '; + reportOut(codeWithoutCfg); + continue; + } + + Tokenizer tokenizer(mSettings, *this); + if (mSettings.showtime != SHOWTIME_MODES::SHOWTIME_NONE) + tokenizer.setTimerResults(&s_timerResults); + tokenizer.setDirectives(directives); // TODO: how to avoid repeated copies? + + try { + // Create tokens, skip rest of iteration if failed + { + Timer timer("Tokenizer::createTokens", mSettings.showtime, &s_timerResults); + simplecpp::TokenList tokensP = preprocessor.preprocess(tokens1, mCurrentConfig, files, true); + tokenizer.list.createTokens(std::move(tokensP)); + } + hasValidConfig = true; + + // locations macros + mLocationMacros.clear(); + for (const Token* tok = tokenizer.tokens(); tok; tok = tok->next()) { + if (!tok->getMacroName().empty()) + mLocationMacros[Location(files[tok->fileIndex()], tok->linenr())].emplace(tok->getMacroName()); + } + + // If only errors are printed, print filename after the check + if (!mSettings.quiet && (!mCurrentConfig.empty() || checkCount > 1)) { + std::string fixedpath = Path::simplifyPath(filename); + fixedpath = Path::toNativeSeparators(std::move(fixedpath)); + mErrorLogger.reportOut("Checking " + fixedpath + ": " + mCurrentConfig + "...", Color::FgGreen); + } + + if (!tokenizer.tokens()) + continue; + + // skip rest of iteration if just checking configuration + if (mSettings.checkConfiguration) + continue; + +#ifdef HAVE_RULES + // Execute rules for "raw" code + executeRules("raw", tokenizer.list); +#endif + + // Simplify tokens into normal form, skip rest of iteration if failed + if (!tokenizer.simplifyTokens1(mCurrentConfig)) + continue; + + // dump xml if --dump + if ((mSettings.dump || !mSettings.addons.empty()) && fdump.is_open()) { + fdump << "" << std::endl; + fdump << " " << std::endl; + fdump << " " << std::endl; + fdump << " " << std::endl; + fdump << " " << std::endl; + preprocessor.dump(fdump); + tokenizer.dump(fdump); + fdump << "" << std::endl; + } + + // Need to call this even if the hash will skip this configuration + mSettings.supprs.nomsg.markUnmatchedInlineSuppressionsAsChecked(tokenizer); + + // Skip if we already met the same simplified token list + if (mSettings.force || mSettings.maxConfigs > 1) { + const std::size_t hash = tokenizer.list.calculateHash(); + if (hashes.find(hash) != hashes.end()) { + if (mSettings.debugwarnings) + purgedConfigurationMessage(filename, mCurrentConfig); + continue; + } + hashes.insert(hash); + } + + // Check normal tokens + checkNormalTokens(tokenizer); + } catch (const simplecpp::Output &o) { + // #error etc during preprocessing + configurationError.push_back((mCurrentConfig.empty() ? "\'\'" : mCurrentConfig) + " : [" + o.location.file() + ':' + std::to_string(o.location.line) + "] " + o.msg); + --checkCount; // don't count invalid configurations + + if (!hasValidConfig && currCfg == *configurations.rbegin()) { + // If there is no valid configuration then report error.. + std::string file = Path::fromNativeSeparators(o.location.file()); + if (mSettings.relativePaths) + file = Path::getRelativePath(file, mSettings.basePaths); + + ErrorMessage::FileLocation loc1(file, o.location.line, o.location.col); + + ErrorMessage errmsg({std::move(loc1)}, + filename, + Severity::error, + o.msg, + "preprocessorErrorDirective", + Certainty::normal); + reportErr(errmsg); + } + continue; + + } catch (const TerminateException &) { + // Analysis is terminated + return mExitCode; + + } catch (const InternalError &e) { + ErrorMessage errmsg = ErrorMessage::fromInternalError(e, &tokenizer.list, filename); + reportErr(errmsg); + } + } + + if (!hasValidConfig && configurations.size() > 1 && mSettings.severity.isEnabled(Severity::information)) { + std::string msg; + msg = "This file is not analyzed. Cppcheck failed to extract a valid configuration. Use -v for more details."; + msg += "\nThis file is not analyzed. Cppcheck failed to extract a valid configuration. The tested configurations have these preprocessor errors:"; + for (const std::string &s : configurationError) + msg += '\n' + s; + + const std::string locFile = Path::toNativeSeparators(filename); + ErrorMessage::FileLocation loc(locFile, 0, 0); + ErrorMessage errmsg({std::move(loc)}, + locFile, + Severity::information, + msg, + "noValidConfiguration", + Certainty::normal); + reportErr(errmsg); + } + + // dumped all configs, close root element now + if (fdump.is_open()) { + fdump << "" << std::endl; + fdump.close(); + } + + executeAddons(dumpFile, Path::simplifyPath(filename)); + + } catch (const TerminateException &) { + // Analysis is terminated + return mExitCode; + } catch (const std::runtime_error &e) { + internalError(filename, std::string("Checking file failed: ") + e.what()); + } catch (const std::bad_alloc &) { + internalError(filename, "Checking file failed: out of memory"); + } catch (const InternalError &e) { + const ErrorMessage errmsg = ErrorMessage::fromInternalError(e, nullptr, filename, "Bailing out from analysis: Checking file failed"); + reportErr(errmsg); + } + + if (!mSettings.buildDir.empty()) { + mAnalyzerInformation.close(); + } + + // In jointSuppressionReport mode, unmatched suppressions are + // collected after all files are processed + if (!mSettings.useSingleJob() && (mSettings.severity.isEnabled(Severity::information) || mSettings.checkConfiguration)) { + SuppressionList::reportUnmatchedSuppressions(mSettings.supprs.nomsg.getUnmatchedLocalSuppressions(filename, (bool)mUnusedFunctionsCheck), *this); + } + + mErrorList.clear(); + + if (mSettings.showtime == SHOWTIME_MODES::SHOWTIME_FILE || mSettings.showtime == SHOWTIME_MODES::SHOWTIME_TOP5_FILE) + printTimerResults(mSettings.showtime); + + return mExitCode; +} + +// TODO: replace with ErrorMessage::fromInternalError() +void CppCheck::internalError(const std::string &filename, const std::string &msg) +{ + const std::string fullmsg("Bailing out from analysis: " + msg); + + ErrorMessage::FileLocation loc1(filename, 0, 0); + + ErrorMessage errmsg({std::move(loc1)}, + emptyString, + Severity::error, + fullmsg, + "internalError", + Certainty::normal); + + mErrorLogger.reportErr(errmsg); +} + +//--------------------------------------------------------------------------- +// CppCheck - A function that checks a normal token list +//--------------------------------------------------------------------------- + +void CppCheck::checkNormalTokens(const Tokenizer &tokenizer) +{ + CheckUnusedFunctions unusedFunctionsChecker; + + // TODO: this should actually be the behavior if only "--enable=unusedFunction" is specified - see #10648 + const char* unusedFunctionOnly = std::getenv("UNUSEDFUNCTION_ONLY"); + const bool doUnusedFunctionOnly = unusedFunctionOnly && (std::strcmp(unusedFunctionOnly, "1") == 0); + + if (!doUnusedFunctionOnly) { + const std::time_t maxTime = mSettings.checksMaxTime > 0 ? std::time(nullptr) + mSettings.checksMaxTime : 0; + + // call all "runChecks" in all registered Check classes + // cppcheck-suppress shadowFunction - TODO: fix this + for (Check *check : Check::instances()) { + if (Settings::terminated()) + return; + + if (maxTime > 0 && std::time(nullptr) > maxTime) { + if (mSettings.debugwarnings) { + ErrorMessage::FileLocation loc(tokenizer.list.getFiles()[0], 0, 0); + ErrorMessage errmsg({std::move(loc)}, + emptyString, + Severity::debug, + "Checks maximum time exceeded", + "checksMaxTime", + Certainty::normal); + reportErr(errmsg); + } + return; + } + + Timer timerRunChecks(check->name() + "::runChecks", mSettings.showtime, &s_timerResults); + check->runChecks(tokenizer, this); + } + } + + if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mSettings.buildDir.empty()) { + unusedFunctionsChecker.parseTokens(tokenizer, mSettings); + } + if (mUnusedFunctionsCheck && mSettings.useSingleJob() && mSettings.buildDir.empty()) { + mUnusedFunctionsCheck->parseTokens(tokenizer, mSettings); + } + + if (mSettings.clang) { + // TODO: Use CTU for Clang analysis + return; + } + + if (mSettings.useSingleJob() || !mSettings.buildDir.empty()) { + // Analyse the tokens.. + + if (CTU::FileInfo * const fi1 = CTU::getFileInfo(tokenizer)) { + if (!mSettings.buildDir.empty()) + mAnalyzerInformation.setFileInfo("ctu", fi1->toString()); + if (mSettings.useSingleJob()) + mFileInfo.push_back(fi1); + else + delete fi1; + } + + if (!doUnusedFunctionOnly) { + // cppcheck-suppress shadowFunction - TODO: fix this + for (const Check *check : Check::instances()) { + if (Check::FileInfo * const fi = check->getFileInfo(tokenizer, mSettings)) { + if (!mSettings.buildDir.empty()) + mAnalyzerInformation.setFileInfo(check->name(), fi->toString()); + if (mSettings.useSingleJob()) + mFileInfo.push_back(fi); + else + delete fi; + } + } + } + } + + if (mSettings.checks.isEnabled(Checks::unusedFunction) && !mSettings.buildDir.empty()) { + mAnalyzerInformation.setFileInfo("CheckUnusedFunctions", unusedFunctionsChecker.analyzerInfo()); + } + +#ifdef HAVE_RULES + executeRules("normal", tokenizer.list); +#endif +} + +//--------------------------------------------------------------------------- + +#ifdef HAVE_RULES +bool CppCheck::hasRule(const std::string &tokenlist) const +{ + return std::any_of(mSettings.rules.cbegin(), mSettings.rules.cend(), [&](const Settings::Rule& rule) { + return rule.tokenlist == tokenlist; + }); +} + +static const char * pcreErrorCodeToString(const int pcreExecRet) +{ + switch (pcreExecRet) { + case PCRE_ERROR_NULL: + return "Either code or subject was passed as NULL, or ovector was NULL " + "and ovecsize was not zero (PCRE_ERROR_NULL)"; + case PCRE_ERROR_BADOPTION: + return "An unrecognized bit was set in the options argument (PCRE_ERROR_BADOPTION)"; + case PCRE_ERROR_BADMAGIC: + return "PCRE stores a 4-byte \"magic number\" at the start of the compiled code, " + "to catch the case when it is passed a junk pointer and to detect when a " + "pattern that was compiled in an environment of one endianness is run in " + "an environment with the other endianness. This is the error that PCRE " + "gives when the magic number is not present (PCRE_ERROR_BADMAGIC)"; + case PCRE_ERROR_UNKNOWN_NODE: + return "While running the pattern match, an unknown item was encountered in the " + "compiled pattern. This error could be caused by a bug in PCRE or by " + "overwriting of the compiled pattern (PCRE_ERROR_UNKNOWN_NODE)"; + case PCRE_ERROR_NOMEMORY: + return "If a pattern contains back references, but the ovector that is passed " + "to pcre_exec() is not big enough to remember the referenced substrings, " + "PCRE gets a block of memory at the start of matching to use for this purpose. " + "If the call via pcre_malloc() fails, this error is given. The memory is " + "automatically freed at the end of matching. This error is also given if " + "pcre_stack_malloc() fails in pcre_exec(). " + "This can happen only when PCRE has been compiled with " + "--disable-stack-for-recursion (PCRE_ERROR_NOMEMORY)"; + case PCRE_ERROR_NOSUBSTRING: + return "This error is used by the pcre_copy_substring(), pcre_get_substring(), " + "and pcre_get_substring_list() functions (see below). " + "It is never returned by pcre_exec() (PCRE_ERROR_NOSUBSTRING)"; + case PCRE_ERROR_MATCHLIMIT: + return "The backtracking limit, as specified by the match_limit field in a pcre_extra " + "structure (or defaulted) was reached. " + "See the description above (PCRE_ERROR_MATCHLIMIT)"; + case PCRE_ERROR_CALLOUT: + return "This error is never generated by pcre_exec() itself. " + "It is provided for use by callout functions that want to yield a distinctive " + "error code. See the pcrecallout documentation for details (PCRE_ERROR_CALLOUT)"; + case PCRE_ERROR_BADUTF8: + return "A string that contains an invalid UTF-8 byte sequence was passed as a subject, " + "and the PCRE_NO_UTF8_CHECK option was not set. If the size of the output vector " + "(ovecsize) is at least 2, the byte offset to the start of the the invalid UTF-8 " + "character is placed in the first element, and a reason code is placed in the " + "second element. The reason codes are listed in the following section. For " + "backward compatibility, if PCRE_PARTIAL_HARD is set and the problem is a truncated " + "UTF-8 character at the end of the subject (reason codes 1 to 5), " + "PCRE_ERROR_SHORTUTF8 is returned instead of PCRE_ERROR_BADUTF8"; + case PCRE_ERROR_BADUTF8_OFFSET: + return "The UTF-8 byte sequence that was passed as a subject was checked and found to " + "be valid (the PCRE_NO_UTF8_CHECK option was not set), but the value of " + "startoffset did not point to the beginning of a UTF-8 character or the end of " + "the subject (PCRE_ERROR_BADUTF8_OFFSET)"; + case PCRE_ERROR_PARTIAL: + return "The subject string did not match, but it did match partially. See the " + "pcrepartial documentation for details of partial matching (PCRE_ERROR_PARTIAL)"; + case PCRE_ERROR_BADPARTIAL: + return "This code is no longer in use. It was formerly returned when the PCRE_PARTIAL " + "option was used with a compiled pattern containing items that were not supported " + "for partial matching. From release 8.00 onwards, there are no restrictions on " + "partial matching (PCRE_ERROR_BADPARTIAL)"; + case PCRE_ERROR_INTERNAL: + return "An unexpected internal error has occurred. This error could be caused by a bug " + "in PCRE or by overwriting of the compiled pattern (PCRE_ERROR_INTERNAL)"; + case PCRE_ERROR_BADCOUNT: + return "This error is given if the value of the ovecsize argument is negative " + "(PCRE_ERROR_BADCOUNT)"; + case PCRE_ERROR_RECURSIONLIMIT: + return "The internal recursion limit, as specified by the match_limit_recursion " + "field in a pcre_extra structure (or defaulted) was reached. " + "See the description above (PCRE_ERROR_RECURSIONLIMIT)"; + case PCRE_ERROR_DFA_UITEM: + return "PCRE_ERROR_DFA_UITEM"; + case PCRE_ERROR_DFA_UCOND: + return "PCRE_ERROR_DFA_UCOND"; + case PCRE_ERROR_DFA_WSSIZE: + return "PCRE_ERROR_DFA_WSSIZE"; + case PCRE_ERROR_DFA_RECURSE: + return "PCRE_ERROR_DFA_RECURSE"; + case PCRE_ERROR_NULLWSLIMIT: + return "PCRE_ERROR_NULLWSLIMIT"; + case PCRE_ERROR_BADNEWLINE: + return "An invalid combination of PCRE_NEWLINE_xxx options was " + "given (PCRE_ERROR_BADNEWLINE)"; + case PCRE_ERROR_BADOFFSET: + return "The value of startoffset was negative or greater than the length " + "of the subject, that is, the value in length (PCRE_ERROR_BADOFFSET)"; + case PCRE_ERROR_SHORTUTF8: + return "This error is returned instead of PCRE_ERROR_BADUTF8 when the subject " + "string ends with a truncated UTF-8 character and the PCRE_PARTIAL_HARD option is set. " + "Information about the failure is returned as for PCRE_ERROR_BADUTF8. " + "It is in fact sufficient to detect this case, but this special error code for " + "PCRE_PARTIAL_HARD precedes the implementation of returned information; " + "it is retained for backwards compatibility (PCRE_ERROR_SHORTUTF8)"; + case PCRE_ERROR_RECURSELOOP: + return "This error is returned when pcre_exec() detects a recursion loop " + "within the pattern. Specifically, it means that either the whole pattern " + "or a subpattern has been called recursively for the second time at the same " + "position in the subject string. Some simple patterns that might do this " + "are detected and faulted at compile time, but more complicated cases, " + "in particular mutual recursions between two different subpatterns, " + "cannot be detected until run time (PCRE_ERROR_RECURSELOOP)"; + case PCRE_ERROR_JIT_STACKLIMIT: + return "This error is returned when a pattern that was successfully studied " + "using a JIT compile option is being matched, but the memory available " + "for the just-in-time processing stack is not large enough. See the pcrejit " + "documentation for more details (PCRE_ERROR_JIT_STACKLIMIT)"; + case PCRE_ERROR_BADMODE: + return "This error is given if a pattern that was compiled by the 8-bit library " + "is passed to a 16-bit or 32-bit library function, or vice versa (PCRE_ERROR_BADMODE)"; + case PCRE_ERROR_BADENDIANNESS: + return "This error is given if a pattern that was compiled and saved is reloaded on a " + "host with different endianness. The utility function pcre_pattern_to_host_byte_order() " + "can be used to convert such a pattern so that it runs on the new host (PCRE_ERROR_BADENDIANNESS)"; + case PCRE_ERROR_DFA_BADRESTART: + return "PCRE_ERROR_DFA_BADRESTART"; +#if PCRE_MAJOR >= 8 && PCRE_MINOR >= 32 + case PCRE_ERROR_BADLENGTH: + return "This error is given if pcre_exec() is called with a negative value for the length argument (PCRE_ERROR_BADLENGTH)"; + case PCRE_ERROR_JIT_BADOPTION: + return "This error is returned when a pattern that was successfully studied using a JIT compile " + "option is being matched, but the matching mode (partial or complete match) does not correspond " + "to any JIT compilation mode. When the JIT fast path function is used, this error may be " + "also given for invalid options. See the pcrejit documentation for more details (PCRE_ERROR_JIT_BADOPTION)"; +#endif + } + return ""; +} + +void CppCheck::executeRules(const std::string &tokenlist, const TokenList &list) +{ + // There is no rule to execute + if (!hasRule(tokenlist)) + return; + + // Write all tokens in a string that can be parsed by pcre + std::string str; + for (const Token *tok = list.front(); tok; tok = tok->next()) { + str += " "; + str += tok->str(); + } + + for (const Settings::Rule &rule : mSettings.rules) { + if (rule.tokenlist != tokenlist) + continue; + + if (!mSettings.quiet) { + reportOut("Processing rule: " + rule.pattern, Color::FgGreen); + } + + const char *pcreCompileErrorStr = nullptr; + int erroffset = 0; + pcre * const re = pcre_compile(rule.pattern.c_str(),0,&pcreCompileErrorStr,&erroffset,nullptr); + if (!re) { + if (pcreCompileErrorStr) { + const std::string msg = "pcre_compile failed: " + std::string(pcreCompileErrorStr); + const ErrorMessage errmsg(std::list(), + emptyString, + Severity::error, + msg, + "pcre_compile", + Certainty::normal); + + reportErr(errmsg); + } + continue; + } + + // Optimize the regex, but only if PCRE_CONFIG_JIT is available +#ifdef PCRE_CONFIG_JIT + const char *pcreStudyErrorStr = nullptr; + pcre_extra * const pcreExtra = pcre_study(re, PCRE_STUDY_JIT_COMPILE, &pcreStudyErrorStr); + // pcre_study() returns NULL for both errors and when it can not optimize the regex. + // The last argument is how one checks for errors. + // It is NULL if everything works, and points to an error string otherwise. + if (pcreStudyErrorStr) { + const std::string msg = "pcre_study failed: " + std::string(pcreStudyErrorStr); + const ErrorMessage errmsg(std::list(), + emptyString, + Severity::error, + msg, + "pcre_study", + Certainty::normal); + + reportErr(errmsg); + // pcre_compile() worked, but pcre_study() returned an error. Free the resources allocated by pcre_compile(). + pcre_free(re); + continue; + } +#else + const pcre_extra * const pcreExtra = nullptr; +#endif + + int pos = 0; + int ovector[30]= {0}; + while (pos < (int)str.size()) { + const int pcreExecRet = pcre_exec(re, pcreExtra, str.c_str(), (int)str.size(), pos, 0, ovector, 30); + if (pcreExecRet < 0) { + const std::string errorMessage = pcreErrorCodeToString(pcreExecRet); + if (!errorMessage.empty()) { + const ErrorMessage errmsg(std::list(), + emptyString, + Severity::error, + std::string("pcre_exec failed: ") + errorMessage, + "pcre_exec", + Certainty::normal); + + reportErr(errmsg); + } + break; + } + const auto pos1 = (unsigned int)ovector[0]; + const auto pos2 = (unsigned int)ovector[1]; + + // jump to the end of the match for the next pcre_exec + pos = (int)pos2; + + // determine location.. + int fileIndex = 0; + int line = 0; + + std::size_t len = 0; + for (const Token *tok = list.front(); tok; tok = tok->next()) { + len = len + 1U + tok->str().size(); + if (len > pos1) { + fileIndex = tok->fileIndex(); + line = tok->linenr(); + break; + } + } + + const std::string& file = list.getFiles()[fileIndex]; + + ErrorMessage::FileLocation loc(file, line, 0); + + // Create error message + const ErrorMessage errmsg({std::move(loc)}, + list.getSourceFilePath(), + rule.severity, + !rule.summary.empty() ? rule.summary : "found '" + str.substr(pos1, pos2 - pos1) + "'", + rule.id, + Certainty::normal); + + // Report error + reportErr(errmsg); + } + + pcre_free(re); +#ifdef PCRE_CONFIG_JIT + // Free up the EXTRA PCRE value (may be NULL at this point) + if (pcreExtra) { + pcre_free_study(pcreExtra); + } +#endif + } +} +#endif + +void CppCheck::executeAddons(const std::string& dumpFile, const std::string& file0) +{ + if (!dumpFile.empty()) { + std::vector f{dumpFile}; + executeAddons(f, file0); + } +} + +void CppCheck::executeAddons(const std::vector& files, const std::string& file0) +{ + if (mSettings.addons.empty() || files.empty()) + return; + + FilesDeleter filesDeleter; + + std::string fileList; + + if (files.size() >= 2 || endsWith(files[0], ".ctu-info")) { + fileList = Path::getPathFromFilename(files[0]) + FILELIST + std::to_string(mSettings.pid); + filesDeleter.addFile(fileList); + std::ofstream fout(fileList); + for (const std::string& f: files) + fout << f << std::endl; + } + + // ensure all addons have already been resolved - TODO: remove when settings are const after creation + assert(mSettings.addonInfos.size() == mSettings.addons.size()); + + for (const AddonInfo &addonInfo : mSettings.addonInfos) { + if (addonInfo.name != "misra" && !addonInfo.ctu && endsWith(files.back(), ".ctu-info")) + continue; + + const std::vector results = + executeAddon(addonInfo, mSettings.addonPython, fileList.empty() ? files[0] : fileList, mSettings.premiumArgs, mExecuteCommand); + + const bool misraC2023 = mSettings.premiumArgs.find("--misra-c-2023") != std::string::npos; + + for (const picojson::value& res : results) { + // TODO: get rid of copy? + // this is a copy so we can access missing fields and get a default value + picojson::object obj = res.get(); + + ErrorMessage errmsg; + + if (obj.count("file") > 0) { + std::string fileName = obj["file"].get(); + const int64_t lineNumber = obj["linenr"].get(); + const int64_t column = obj["column"].get(); + errmsg.callStack.emplace_back(std::move(fileName), lineNumber, column); + } else if (obj.count("loc") > 0) { + for (const picojson::value &locvalue: obj["loc"].get()) { + picojson::object loc = locvalue.get(); + std::string fileName = loc["file"].get(); + const int64_t lineNumber = loc["linenr"].get(); + const int64_t column = loc["column"].get(); + std::string info = loc["info"].get(); + errmsg.callStack.emplace_back(std::move(fileName), std::move(info), lineNumber, column); + } + } + + errmsg.id = obj["addon"].get() + "-" + obj["errorId"].get(); + if (misraC2023 && startsWith(errmsg.id, "misra-c2012-")) + errmsg.id = "misra-c2023-" + errmsg.id.substr(12); + errmsg.setmsg(mSettings.getMisraRuleText(errmsg.id, obj["message"].get())); + const std::string severity = obj["severity"].get(); + errmsg.severity = severityFromString(severity); + if (errmsg.severity == Severity::none || errmsg.severity == Severity::internal) { + if (!endsWith(errmsg.id, "-logChecker")) + continue; + errmsg.severity = Severity::internal; + } + else if (!mSettings.severity.isEnabled(errmsg.severity)) { + // Do not filter out premium misra/cert/autosar messages that has been + // explicitly enabled with a --premium option + if (!isPremiumCodingStandardId(errmsg.id)) + continue; + } + errmsg.file0 = file0; + + reportErr(errmsg); + } + } +} + +void CppCheck::executeAddonsWholeProgram(const std::list> &files) +{ + if (mSettings.addons.empty()) + return; + + std::vector ctuInfoFiles; + for (const auto &f: files) { + const std::string &dumpFileName = getDumpFileName(mSettings, f.first); + ctuInfoFiles.push_back(getCtuInfoFileName(dumpFileName)); + } + + try { + executeAddons(ctuInfoFiles, ""); + } catch (const InternalError& e) { + const ErrorMessage errmsg = ErrorMessage::fromInternalError(e, nullptr, "", "Bailing out from analysis: Whole program analysis failed"); + reportErr(errmsg); + } + + if (mSettings.buildDir.empty()) { + for (const std::string &f: ctuInfoFiles) + std::remove(f.c_str()); + } +} + +Settings &CppCheck::settings() +{ + return mSettings; +} + +void CppCheck::tooManyConfigsError(const std::string &file, const int numberOfConfigurations) +{ + if (!mSettings.severity.isEnabled(Severity::information) && !mTooManyConfigs) + return; + + mTooManyConfigs = false; + + if (mSettings.severity.isEnabled(Severity::information) && file.empty()) + return; + + std::list loclist; + if (!file.empty()) { + loclist.emplace_back(file, 0, 0); + } + + std::ostringstream msg; + msg << "Too many #ifdef configurations - cppcheck only checks " << mSettings.maxConfigs; + if (numberOfConfigurations > mSettings.maxConfigs) + msg << " of " << numberOfConfigurations << " configurations. Use --force to check all configurations.\n"; + if (file.empty()) + msg << " configurations. Use --force to check all configurations. For more details, use --enable=information.\n"; + msg << "The checking of the file will be interrupted because there are too many " + "#ifdef configurations. Checking of all #ifdef configurations can be forced " + "by --force command line option or from GUI preferences. However that may " + "increase the checking time."; + if (file.empty()) + msg << " For more details, use --enable=information."; + + + ErrorMessage errmsg(std::move(loclist), + emptyString, + Severity::information, + msg.str(), + "toomanyconfigs", CWE398, + Certainty::normal); + + reportErr(errmsg); +} + +void CppCheck::purgedConfigurationMessage(const std::string &file, const std::string& configuration) +{ + mTooManyConfigs = false; + + if (mSettings.severity.isEnabled(Severity::information) && file.empty()) + return; + + std::list loclist; + if (!file.empty()) { + loclist.emplace_back(file, 0, 0); + } + + ErrorMessage errmsg(std::move(loclist), + emptyString, + Severity::information, + "The configuration '" + configuration + "' was not checked because its code equals another one.", + "purgedConfiguration", + Certainty::normal); + + reportErr(errmsg); +} + +//--------------------------------------------------------------------------- + +// TODO: part of this logic is duplicated in Executor::hasToLog() +void CppCheck::reportErr(const ErrorMessage &msg) +{ + if (msg.severity == Severity::internal) { + mErrorLogger.reportErr(msg); + return; + } + + if (!mSettings.library.reportErrors(msg.file0)) + return; + + std::set macroNames; + if (!msg.callStack.empty()) { + const std::string &file = msg.callStack.back().getfile(false); + int lineNumber = msg.callStack.back().line; + const auto it = mLocationMacros.find(Location(file, lineNumber)); + if (it != mLocationMacros.cend()) + macroNames = it->second; + } + + // TODO: only convert if necessary + const auto errorMessage = SuppressionList::ErrorMessage::fromErrorMessage(msg, macroNames); + + if (mSettings.supprs.nomsg.isSuppressed(errorMessage, mUseGlobalSuppressions)) { + // Safety: Report critical errors to ErrorLogger + if (mSettings.safety && ErrorLogger::isCriticalErrorId(msg.id)) { + mExitCode = 1; + + if (mSettings.supprs.nomsg.isSuppressedExplicitly(errorMessage, mUseGlobalSuppressions)) { + // Report with internal severity to signal that there is this critical error but + // it is suppressed + ErrorMessage temp(msg); + temp.severity = Severity::internal; + mErrorLogger.reportErr(temp); + } else { + // Report critical error that is not explicitly suppressed + mErrorLogger.reportErr(msg); + } + } + return; + } + + // TODO: there should be no need for the verbose and default messages here + std::string errmsg = msg.toString(mSettings.verbose); + if (errmsg.empty()) + return; + + // Alert only about unique errors. + // This makes sure the errors of a single check() call are unique. + // TODO: get rid of this? This is forwarded to another ErrorLogger which is also doing this + if (!mErrorList.emplace(std::move(errmsg)).second) + return; + + if (!mSettings.buildDir.empty()) + mAnalyzerInformation.reportErr(msg); + + if (!mSettings.supprs.nofail.isSuppressed(errorMessage) && !mSettings.supprs.nomsg.isSuppressed(errorMessage)) { + mExitCode = 1; + } + + mErrorLogger.reportErr(msg); + // check if plistOutput should be populated and the current output file is open and the error is not suppressed + if (!mSettings.plistOutput.empty() && mPlistFile.is_open() && !mSettings.supprs.nomsg.isSuppressed(errorMessage)) { + // add error to plist output file + mPlistFile << ErrorLogger::plistData(msg); + } +} + +void CppCheck::reportOut(const std::string &outmsg, Color c) +{ + mErrorLogger.reportOut(outmsg, c); +} + +void CppCheck::reportProgress(const std::string &filename, const char stage[], const std::size_t value) +{ + mErrorLogger.reportProgress(filename, stage, value); +} + +void CppCheck::getErrorMessages(ErrorLogger &errorlogger) +{ + Settings s; + s.severity.enable(Severity::warning); + s.severity.enable(Severity::style); + s.severity.enable(Severity::portability); + s.severity.enable(Severity::performance); + s.severity.enable(Severity::information); + + CppCheck cppcheck(errorlogger, true, nullptr); + cppcheck.purgedConfigurationMessage(emptyString,emptyString); + cppcheck.mTooManyConfigs = true; + cppcheck.tooManyConfigsError(emptyString,0U); + // TODO: add functions to get remaining error messages + + // call all "getErrorMessages" in all registered Check classes + for (std::list::const_iterator it = Check::instances().cbegin(); it != Check::instances().cend(); ++it) + (*it)->getErrorMessages(&errorlogger, &s); + + CheckUnusedFunctions::getErrorMessages(errorlogger); + Preprocessor::getErrorMessages(errorlogger, s); +} + +void CppCheck::analyseClangTidy(const FileSettings &fileSettings) +{ + std::string allIncludes; + for (const std::string &inc : fileSettings.includePaths) { + allIncludes = allIncludes + "-I\"" + inc + "\" "; + } + + const std::string allDefines = getDefinesFlags(fileSettings.defines); + +#ifdef _WIN32 + constexpr char exe[] = "clang-tidy.exe"; +#else + constexpr char exe[] = "clang-tidy"; +#endif + + const std::string args = "-quiet -checks=*,-clang-analyzer-*,-llvm* \"" + fileSettings.filename + "\" -- " + allIncludes + allDefines; + std::string output; + if (const int exitcode = mExecuteCommand(exe, split(args), emptyString, output)) { + std::cerr << "Failed to execute '" << exe << "' (exitcode: " << std::to_string(exitcode) << ")" << std::endl; + return; + } + + // parse output and create error messages + std::istringstream istr(output); + std::string line; + + if (!mSettings.buildDir.empty()) { + const std::string analyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(mSettings.buildDir, fileSettings.filename, emptyString); + std::ofstream fcmd(analyzerInfoFile + ".clang-tidy-cmd"); + fcmd << istr.str(); + } + + while (std::getline(istr, line)) { + if (line.find("error") == std::string::npos && line.find("warning") == std::string::npos) + continue; + + std::size_t endColumnPos = line.find(": error:"); + if (endColumnPos == std::string::npos) { + endColumnPos = line.find(": warning:"); + } + + const std::size_t endLinePos = line.rfind(':', endColumnPos-1); + const std::size_t endNamePos = line.rfind(':', endLinePos - 1); + const std::size_t endMsgTypePos = line.find(':', endColumnPos + 2); + const std::size_t endErrorPos = line.rfind('[', std::string::npos); + if (endLinePos==std::string::npos || endNamePos==std::string::npos || endMsgTypePos==std::string::npos || endErrorPos==std::string::npos) + continue; + + const std::string lineNumString = line.substr(endNamePos + 1, endLinePos - endNamePos - 1); + const std::string columnNumString = line.substr(endLinePos + 1, endColumnPos - endLinePos - 1); + const std::string messageString = line.substr(endMsgTypePos + 1, endErrorPos - endMsgTypePos - 1); + const std::string errorString = line.substr(endErrorPos, line.length()); + + std::string fixedpath = Path::simplifyPath(line.substr(0, endNamePos)); + const int64_t lineNumber = strToInt(lineNumString); + const int64_t column = strToInt(columnNumString); + fixedpath = Path::toNativeSeparators(std::move(fixedpath)); + + ErrorMessage errmsg; + errmsg.callStack.emplace_back(fixedpath, lineNumber, column); + + errmsg.id = "clang-tidy-" + errorString.substr(1, errorString.length() - 2); + if (errmsg.id.find("performance") != std::string::npos) + errmsg.severity = Severity::performance; + else if (errmsg.id.find("portability") != std::string::npos) + errmsg.severity = Severity::portability; + else if (errmsg.id.find("cert") != std::string::npos || errmsg.id.find("misc") != std::string::npos || errmsg.id.find("unused") != std::string::npos) + errmsg.severity = Severity::warning; + else + errmsg.severity = Severity::style; + + errmsg.file0 = std::move(fixedpath); + errmsg.setmsg(messageString); + reportErr(errmsg); + } +} + +bool CppCheck::analyseWholeProgram() +{ + bool errors = false; + // Init CTU + CTU::maxCtuDepth = mSettings.maxCtuDepth; + // Analyse the tokens + CTU::FileInfo ctu; + for (const Check::FileInfo *fi : mFileInfo) { + const auto *fi2 = dynamic_cast(fi); + if (fi2) { + ctu.functionCalls.insert(ctu.functionCalls.end(), fi2->functionCalls.cbegin(), fi2->functionCalls.cend()); + ctu.nestedCalls.insert(ctu.nestedCalls.end(), fi2->nestedCalls.cbegin(), fi2->nestedCalls.cend()); + } + } + + // cppcheck-suppress shadowFunction - TODO: fix this + for (Check *check : Check::instances()) + errors |= check->analyseWholeProgram(&ctu, mFileInfo, mSettings, *this); // TODO: ctu + + if (mUnusedFunctionsCheck) + errors |= mUnusedFunctionsCheck->check(mSettings, *this); + + return errors && (mExitCode > 0); +} + +unsigned int CppCheck::analyseWholeProgram(const std::string &buildDir, const std::list> &files, const std::list& fileSettings) +{ + executeAddonsWholeProgram(files); // TODO: pass FileSettings + if (buildDir.empty()) { + removeCtuInfoFiles(files, fileSettings); + return mExitCode; + } + if (mSettings.checks.isEnabled(Checks::unusedFunction)) + CheckUnusedFunctions::analyseWholeProgram(mSettings, *this, buildDir); + std::list fileInfoList; + CTU::FileInfo ctuFileInfo; + + // Load all analyzer info data.. + const std::string filesTxt(buildDir + "/files.txt"); + std::ifstream fin(filesTxt); + std::string filesTxtLine; + while (std::getline(fin, filesTxtLine)) { + const std::string::size_type firstColon = filesTxtLine.find(':'); + if (firstColon == std::string::npos) + continue; + const std::string::size_type lastColon = filesTxtLine.rfind(':'); + if (firstColon == lastColon) + continue; + const std::string xmlfile = buildDir + '/' + filesTxtLine.substr(0,firstColon); + //const std::string sourcefile = filesTxtLine.substr(lastColon+1); + + tinyxml2::XMLDocument doc; + const tinyxml2::XMLError error = doc.LoadFile(xmlfile.c_str()); + if (error != tinyxml2::XML_SUCCESS) + continue; + + const tinyxml2::XMLElement * const rootNode = doc.FirstChildElement(); + if (rootNode == nullptr) + continue; + + for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (std::strcmp(e->Name(), "FileInfo") != 0) + continue; + const char *checkClassAttr = e->Attribute("check"); + if (!checkClassAttr) + continue; + if (std::strcmp(checkClassAttr, "ctu") == 0) { + ctuFileInfo.loadFromXml(e); + continue; + } + // cppcheck-suppress shadowFunction - TODO: fix this + for (const Check *check : Check::instances()) { + if (checkClassAttr == check->name()) + fileInfoList.push_back(check->loadFileInfoFromXml(e)); + } + } + } + + // Set CTU max depth + CTU::maxCtuDepth = mSettings.maxCtuDepth; + + // Analyse the tokens + // cppcheck-suppress shadowFunction - TODO: fix this + for (Check *check : Check::instances()) + check->analyseWholeProgram(&ctuFileInfo, fileInfoList, mSettings, *this); + + if (mUnusedFunctionsCheck) + mUnusedFunctionsCheck->check(mSettings, *this); + + for (Check::FileInfo *fi : fileInfoList) + delete fi; + + return mExitCode; +} + +void CppCheck::removeCtuInfoFiles(const std::list> &files, const std::list& fileSettings) +{ + if (mSettings.buildDir.empty()) { + for (const auto& f: files) { + const std::string &dumpFileName = getDumpFileName(mSettings, f.first); + const std::string &ctuInfoFileName = getCtuInfoFileName(dumpFileName); + std::remove(ctuInfoFileName.c_str()); + } + for (const auto& fs: fileSettings) { + const std::string &dumpFileName = getDumpFileName(mSettings, fs.filename); + const std::string &ctuInfoFileName = getCtuInfoFileName(dumpFileName); + std::remove(ctuInfoFileName.c_str()); + } + } +} + +// cppcheck-suppress unusedFunction - only used in tests +void CppCheck::resetTimerResults() +{ + s_timerResults.reset(); +} + +void CppCheck::printTimerResults(SHOWTIME_MODES mode) +{ + s_timerResults.showResults(mode); +} + +bool CppCheck::isPremiumCodingStandardId(const std::string& id) const { + if (mSettings.premiumArgs.find("--misra") != std::string::npos) { + if (startsWith(id, "misra-") || startsWith(id, "premium-misra-")) + return true; + } + if (mSettings.premiumArgs.find("--cert") != std::string::npos && startsWith(id, "premium-cert-")) + return true; + if (mSettings.premiumArgs.find("--autosar") != std::string::npos && startsWith(id, "premium-autosar-")) + return true; + return false; +} + +std::string CppCheck::getDumpFileContentsRawTokens(const std::vector& files, const simplecpp::TokenList& tokens1) const { + std::string dumpProlog; + dumpProlog += " \n"; + for (unsigned int i = 0; i < files.size(); ++i) { + dumpProlog += " \n"; + } + for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) { + dumpProlog += " location.fileIndex); + dumpProlog += "\" "; + + dumpProlog += "linenr=\""; + dumpProlog += std::to_string(tok->location.line); + dumpProlog += "\" "; + + dumpProlog +="column=\""; + dumpProlog += std::to_string(tok->location.col); + dumpProlog += "\" "; + + dumpProlog += "str=\""; + dumpProlog += ErrorLogger::toxml(tok->str()); + dumpProlog += "\""; + + dumpProlog += "/>\n"; + } + dumpProlog += " \n"; + return dumpProlog; +} diff --git a/cppcheck-2.14.0/lib/cppcheck.h b/cppcheck-2.14.0/lib/cppcheck.h new file mode 100644 index 00000000..486f77bc --- /dev/null +++ b/cppcheck-2.14.0/lib/cppcheck.h @@ -0,0 +1,260 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef cppcheckH +#define cppcheckH +//--------------------------------------------------------------------------- + +#include "analyzerinfo.h" +#include "check.h" +#include "color.h" +#include "config.h" +#include "errorlogger.h" +#include "settings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class TokenList; +enum class SHOWTIME_MODES; +struct FileSettings; +class CheckUnusedFunctions; +class Tokenizer; + +namespace simplecpp { class TokenList; } + +/// @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: + using ExecuteCmdFn = std::function,std::string,std::string&)>; + + /** + * @brief Constructor. + */ + CppCheck(ErrorLogger &errorLogger, + bool useGlobalSuppressions, + ExecuteCmdFn executeCommand); + + /** + * @brief Destructor. + */ + ~CppCheck() override; + + /** + * @brief This starts the actual checking. Note that you must call + * parseFromArgs() or settings() and addFile() before calling this. + * @return amount of errors found or 0 if none were found. + */ + + /** + * @brief Check the file. + * This function checks one given file for errors. + * @param path Path to the file to check. + * @return amount of errors found or 0 if none were found. + * @note You must set settings before calling this function (by calling + * settings()). + */ + unsigned int check(const std::string &path); + unsigned int check(const FileSettings &fs); + + /** + * @brief Check the file. + * This function checks one "virtual" file. The file is not read from + * the disk but the content is given in @p content. In errors the @p path + * is used as a filename. + * @param path Path to the file to check. + * @param content File content as a string. + * @return amount of errors found or 0 if none were found. + * @note You must set settings before calling this function (by calling + * settings()). + */ + unsigned int check(const std::string &path, const std::string &content); + + /** + * @brief Get reference to current settings. + * @return a reference to current settings + */ + Settings &settings(); + + /** + * @brief Returns current version number as a string. + * @return version, e.g. "1.38" + */ + static const char * version(); + + /** + * @brief Returns extra version info as a string. + * This is for returning extra version info, like Git commit id, build + * time/date etc. + * @return extra version info, e.g. "04d42151" (Git commit id). + */ + static const char * extraVersion(); + + /** + * @brief Call all "getErrorMessages" in all registered Check classes. + * Also print out XML header and footer. + */ + static void getErrorMessages(ErrorLogger &errorlogger); + + void tooManyConfigsError(const std::string &file, const int numberOfConfigurations); + void purgedConfigurationMessage(const std::string &file, const std::string& configuration); + + /** Analyse whole program, run this after all TUs has been scanned. + * This is deprecated and the plan is to remove this when + * .analyzeinfo is good enough. + * Return true if an error is reported. + */ + bool analyseWholeProgram(); + + /** Analyze all files using clang-tidy */ + void analyseClangTidy(const FileSettings &fileSettings); + + /** analyse whole program use .analyzeinfo files */ + unsigned int analyseWholeProgram(const std::string &buildDir, const std::list> &files, const std::list& fileSettings); + + /** Remove *.ctu-info files */ + void removeCtuInfoFiles(const std::list>& files, const std::list& fileSettings); // cppcheck-suppress functionConst // has side effects + + static void resetTimerResults(); + static void printTimerResults(SHOWTIME_MODES mode); + + bool isPremiumCodingStandardId(const std::string& id) const; + + std::string getAddonMessage(const std::string& id, const std::string& text) const; + + /** + * @brief Get dumpfile contents, this is only public for testing purposes + */ + std::string getDumpFileContentsRawTokens(const std::vector& files, const simplecpp::TokenList& tokens1) const; + +private: +#ifdef HAVE_RULES + /** Are there "simple" rules */ + bool hasRule(const std::string &tokenlist) const; +#endif + + /** @brief There has been an internal error => Report information message */ + void internalError(const std::string &filename, const std::string &msg); + + /** + * @brief Check a file using stream + * @param filename file name + * @param cfgname cfg name + * @param fileStream stream the file content can be read from + * @return number of errors found + */ + unsigned int checkFile(const std::string& filename, const std::string &cfgname, std::istream* fileStream = nullptr); + + /** + * @brief Check normal tokens + * @param tokenizer tokenizer instance + */ + void checkNormalTokens(const Tokenizer &tokenizer); + + /** + * Execute addons + */ + void executeAddons(const std::vector& files, const std::string& file0); + void executeAddons(const std::string &dumpFile, const std::string& file0); + + /** + * Execute addons + */ + void executeAddonsWholeProgram(const std::list> &files); + +#ifdef HAVE_RULES + /** + * @brief Execute rules, if any + * @param tokenlist token list to use (define / normal / raw) + * @param list token list + */ + void executeRules(const std::string &tokenlist, const TokenList &list); +#endif + + unsigned int checkClang(const std::string &path); + + /** + * @brief Errors and warnings are directed here. + * + * @param msg Errors messages are normally in format + * "[filepath:line number] Message", e.g. + * "[main.cpp:4] Uninitialized member variable" + */ + void reportErr(const ErrorMessage &msg) override; + + /** + * @brief Information about progress is directed here. + * + * @param outmsg Message to show, e.g. "Checking main.cpp..." + */ + void reportOut(const std::string &outmsg, Color c = Color::Reset) override; + + // TODO: store hashes instead of the full messages + std::unordered_set mErrorList; + Settings mSettings; + + void reportProgress(const std::string &filename, const char stage[], const std::size_t value) override; + + ErrorLogger &mErrorLogger; + + /** @brief Current preprocessor configuration */ + std::string mCurrentConfig; + + using Location = std::pair; + std::map> mLocationMacros; // What macros are used on a location? + + unsigned int mExitCode{}; + + bool mUseGlobalSuppressions; + + /** Are there too many configs? */ + bool mTooManyConfigs{}; + + /** File info used for whole program analysis */ + std::list mFileInfo; + + AnalyzerInformation mAnalyzerInformation; + + /** Callback for executing a shell command (exe, args, output) */ + ExecuteCmdFn mExecuteCommand; + + std::ofstream mPlistFile; + + std::unique_ptr mUnusedFunctionsCheck; +}; + +/// @} +//--------------------------------------------------------------------------- +#endif // cppcheckH diff --git a/cppcheck-2.14.0/lib/cppcheck.natvis b/cppcheck-2.14.0/lib/cppcheck.natvis new file mode 100644 index 00000000..4b3b2d0a --- /dev/null +++ b/cppcheck-2.14.0/lib/cppcheck.natvis @@ -0,0 +1,71 @@ + + + + {mStr} + + + + {front->mStr} - {back->mStr} + + + + + {mTokensFrontBack.front->mStr} - {mTokensFrontBack.back->mStr} + + mFiles + + + + + + + pCurr + pCurr = pCurr->mNext + + + + + + + + {mNameToken->mStr} + + + + {tokenDef->mStr} + + + + {num} + ? + + + + {type}: {className} + + + + {platformType} + + + + {files[fileIndex]} : {line} [col={col}] + + + + + {intvalue} (INT) + {tokvalue} (TOK) + {floatValue} (FLOAT) + + + + + + [UNKNOWN] + {name} = {value} (value_known=true) + {name} = ? + + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/lib/cppcheck.vcxproj b/cppcheck-2.14.0/lib/cppcheck.vcxproj new file mode 100644 index 00000000..e53ce4d8 --- /dev/null +++ b/cppcheck-2.14.0/lib/cppcheck.vcxproj @@ -0,0 +1,389 @@ + + + + Debug-PCRE + x64 + + + Debug + x64 + + + Release-PCRE + x64 + + + Release + x64 + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {C183DB5B-AD6C-423D-80CA-1F9549555A1A} + cppcheck_lib + 10.0 + + + + DynamicLibrary + Unicode + false + v142 + + + DynamicLibrary + Unicode + false + v142 + + + DynamicLibrary + Unicode + false + v142 + + + DynamicLibrary + Unicode + false + v142 + + + + + + + + + + + + + + + + + + $(SolutionDir)bin\debug\ + $(SolutionDir)bin\debug\ + temp\$(Configuration)_$(PlatformName)\ + temp\$(Configuration)_$(PlatformName)\ + cppcheck-core + cppcheck-core + false + false + $(SolutionDir)bin\ + $(SolutionDir)bin\ + temp\$(Configuration)_$(PlatformName)\ + temp\$(Configuration)_$(PlatformName)\ + cppcheck-core + cppcheck-core + false + false + true + true + + + + ProgramDatabase + true + Disabled + CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) + Level4 + ..\externals;..\externals\picojson;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) + 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 + MultiThreadedDebugDLL + Use + precompiled.h + precompiled.h + true + stdcpp14 + /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) + + + ../externals;%(AdditionalLibraryDirectories) + true + true + 8000000 + 8000000 + true + + + xcopy "$(SolutionDir)addons" "$(OutDir)addons" /E /I /D /Y +xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y +xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y + + + + + ProgramDatabase + true + Disabled + CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) + Level4 + ..\externals;..\externals\picojson;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) + 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 + MultiThreadedDebugDLL + Use + precompiled.h + precompiled.h + true + stdcpp14 + /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) + + + pcre64.lib;%(AdditionalDependencies) + ../externals;%(AdditionalLibraryDirectories) + true + true + 8000000 + 8000000 + true + + + xcopy "$(SolutionDir)addons" "$(OutDir)addons" /E /I /D /Y +xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y +xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y + + + + + MaxSpeed + Level4 + AnySuitable + true + Speed + true + true + true + ..\externals;..\externals\picojson;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) + 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 + CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) + MultiThreadedDLL + ProgramDatabase + true + false + Use + precompiled.h + precompiled.h + /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) + true + stdcpp14 + + + ../externals;%(AdditionalLibraryDirectories) + true + true + true + true + 8000000 + 8000000 + true + + + xcopy "$(SolutionDir)addons" "$(OutDir)addons" /E /I /D /Y +xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y +xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y + + + + + MaxSpeed + Level4 + AnySuitable + true + Speed + true + true + true + ..\externals;..\externals\picojson;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) + 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 + CPPCHECKLIB_EXPORT;TINYXML2_EXPORT;SIMPLECPP_EXPORT;NDEBUG;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) + MultiThreadedDLL + ProgramDatabase + true + false + Use + precompiled.h + precompiled.h + /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) + true + stdcpp14 + + + pcre64.lib;%(AdditionalDependencies) + ../externals;%(AdditionalLibraryDirectories) + true + true + true + true + 8000000 + 8000000 + true + + + xcopy "$(SolutionDir)addons" "$(OutDir)addons" /E /I /D /Y +xcopy "$(SolutionDir)cfg" "$(OutDir)cfg" /E /I /D /Y +xcopy "$(SolutionDir)platforms" "$(OutDir)platforms" /E /I /D /Y + + + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/lib/cppcheck.vcxproj.filters b/cppcheck-2.14.0/lib/cppcheck.vcxproj.filters new file mode 100644 index 00000000..c3e5d346 --- /dev/null +++ b/cppcheck-2.14.0/lib/cppcheck.vcxproj.filters @@ -0,0 +1,431 @@ + + + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + 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 + + + + + + + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/lib/ctu.cpp b/cppcheck-2.14.0/lib/ctu.cpp new file mode 100644 index 00000000..20c621c7 --- /dev/null +++ b/cppcheck-2.14.0/lib/ctu.cpp @@ -0,0 +1,592 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#include "ctu.h" + +#include "astutils.h" +#include "errortypes.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "tokenlist.h" +#include "utils.h" + +#include +#include +#include +#include // back_inserter +#include +#include + +#include "xml.h" +//--------------------------------------------------------------------------- + +static constexpr char ATTR_CALL_ID[] = "call-id"; +static constexpr char ATTR_CALL_FUNCNAME[] = "call-funcname"; +static constexpr char ATTR_CALL_ARGNR[] = "call-argnr"; +static constexpr char ATTR_CALL_ARGEXPR[] = "call-argexpr"; +static constexpr char ATTR_CALL_ARGVALUETYPE[] = "call-argvaluetype"; +static constexpr char ATTR_CALL_ARGVALUE[] = "call-argvalue"; +static constexpr char ATTR_WARNING[] = "warning"; +static constexpr char ATTR_LOC_FILENAME[] = "file"; +static constexpr char ATTR_LOC_LINENR[] = "line"; +static constexpr char ATTR_LOC_COLUMN[] = "col"; +static constexpr char ATTR_INFO[] = "info"; +static constexpr char ATTR_MY_ID[] = "my-id"; +static constexpr char ATTR_MY_ARGNR[] = "my-argnr"; +static constexpr char ATTR_MY_ARGNAME[] = "my-argname"; +static constexpr char ATTR_VALUE[] = "value"; + +int CTU::maxCtuDepth = 2; + +std::string CTU::getFunctionId(const Tokenizer &tokenizer, const Function *function) +{ + return tokenizer.list.file(function->tokenDef) + ':' + std::to_string(function->tokenDef->linenr()) + ':' + std::to_string(function->tokenDef->column()); +} + +CTU::FileInfo::Location::Location(const Tokenizer &tokenizer, const Token *tok) + : fileName(tokenizer.list.file(tok)) + , lineNumber(tok->linenr()) + , column(tok->column()) +{} + +std::string CTU::FileInfo::toString() const +{ + std::ostringstream out; + + // Function calls.. + for (const CTU::FileInfo::FunctionCall &functionCall : functionCalls) { + out << functionCall.toXmlString(); + } + + // Nested calls.. + for (const CTU::FileInfo::NestedCall &nestedCall : nestedCalls) { + out << nestedCall.toXmlString() << "\n"; + } + + return out.str(); +} + +std::string CTU::FileInfo::CallBase::toBaseXmlString() const +{ + std::ostringstream out; + out << " " << ATTR_CALL_ID << "=\"" << callId << "\"" + << " " << ATTR_CALL_FUNCNAME << "=\"" << ErrorLogger::toxml(callFunctionName) << "\"" + << " " << ATTR_CALL_ARGNR << "=\"" << callArgNr << "\"" + << " " << ATTR_LOC_FILENAME << "=\"" << ErrorLogger::toxml(location.fileName) << "\"" + << " " << ATTR_LOC_LINENR << "=\"" << location.lineNumber << "\"" + << " " << ATTR_LOC_COLUMN << "=\"" << location.column << "\""; + return out.str(); +} + +std::string CTU::FileInfo::FunctionCall::toXmlString() const +{ + std::ostringstream out; + out << "(callValueType) << "\"" + << " " << ATTR_CALL_ARGVALUE << "=\"" << callArgValue << "\""; + if (warning) + out << " " << ATTR_WARNING << "=\"true\""; + if (callValuePath.empty()) + out << "/>"; + else { + out << ">\n"; + for (const ErrorMessage::FileLocation &loc : callValuePath) + out << " \n"; + out << ""; + } + return out.str(); +} + +std::string CTU::FileInfo::NestedCall::toXmlString() const +{ + std::ostringstream out; + out << ""; + return out.str(); +} + +std::string CTU::FileInfo::UnsafeUsage::toString() const +{ + std::ostringstream out; + out << " \n"; + return out.str(); +} + +std::string CTU::toString(const std::list &unsafeUsage) +{ + std::ostringstream ret; + for (const CTU::FileInfo::UnsafeUsage &u : unsafeUsage) + ret << u.toString(); + return ret.str(); +} + +CTU::FileInfo::CallBase::CallBase(const Tokenizer &tokenizer, const Token *callToken) + : callId(getFunctionId(tokenizer, callToken->function())) + , callFunctionName(callToken->next()->astOperand1()->expressionString()) + , location(CTU::FileInfo::Location(tokenizer, callToken)) +{} + +CTU::FileInfo::NestedCall::NestedCall(const Tokenizer &tokenizer, const Function *myFunction, const Token *callToken) + : CallBase(tokenizer, callToken) + , myId(getFunctionId(tokenizer, myFunction)) +{} + +static std::string readAttrString(const tinyxml2::XMLElement *e, const char *attr, bool *error) +{ + const char *value = e->Attribute(attr); + if (!value && error) + *error = true; + return value ? value : ""; +} + +static long long readAttrInt(const tinyxml2::XMLElement *e, const char *attr, bool *error) +{ + int64_t value = 0; + const bool err = (e->QueryInt64Attribute(attr, &value) != tinyxml2::XML_SUCCESS); + if (error) + *error = err; + return value; +} + +bool CTU::FileInfo::CallBase::loadBaseFromXml(const tinyxml2::XMLElement *xmlElement) +{ + bool error = false; + callId = readAttrString(xmlElement, ATTR_CALL_ID, &error); + callFunctionName = readAttrString(xmlElement, ATTR_CALL_FUNCNAME, &error); + callArgNr = readAttrInt(xmlElement, ATTR_CALL_ARGNR, &error); + location.fileName = readAttrString(xmlElement, ATTR_LOC_FILENAME, &error); + location.lineNumber = readAttrInt(xmlElement, ATTR_LOC_LINENR, &error); + location.column = readAttrInt(xmlElement, ATTR_LOC_COLUMN, &error); + return !error; +} + +bool CTU::FileInfo::FunctionCall::loadFromXml(const tinyxml2::XMLElement *xmlElement) +{ + if (!loadBaseFromXml(xmlElement)) + return false; + bool error=false; + callArgumentExpression = readAttrString(xmlElement, ATTR_CALL_ARGEXPR, &error); + callValueType = (ValueFlow::Value::ValueType)readAttrInt(xmlElement, ATTR_CALL_ARGVALUETYPE, &error); + callArgValue = readAttrInt(xmlElement, ATTR_CALL_ARGVALUE, &error); + const char *w = xmlElement->Attribute(ATTR_WARNING); + warning = w && std::strcmp(w, "true") == 0; + for (const tinyxml2::XMLElement *e2 = xmlElement->FirstChildElement(); !error && e2; e2 = e2->NextSiblingElement()) { + if (std::strcmp(e2->Name(), "path") != 0) + continue; + std::string file = readAttrString(e2, ATTR_LOC_FILENAME, &error); + std::string info = readAttrString(e2, ATTR_INFO, &error); + const int line = readAttrInt(e2, ATTR_LOC_LINENR, &error); + const int column = readAttrInt(e2, ATTR_LOC_COLUMN, &error); + ErrorMessage::FileLocation loc(file, std::move(info), line, column); + (void)loc; // TODO: loc is unused + } + return !error; +} + +bool CTU::FileInfo::NestedCall::loadFromXml(const tinyxml2::XMLElement *xmlElement) +{ + if (!loadBaseFromXml(xmlElement)) + return false; + bool error = false; + myId = readAttrString(xmlElement, ATTR_MY_ID, &error); + myArgNr = readAttrInt(xmlElement, ATTR_MY_ARGNR, &error); + return !error; +} + +void CTU::FileInfo::loadFromXml(const tinyxml2::XMLElement *xmlElement) +{ + for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (std::strcmp(e->Name(), "function-call") == 0) { + FunctionCall functionCall; + if (functionCall.loadFromXml(e)) + functionCalls.push_back(std::move(functionCall)); + } else if (std::strcmp(e->Name(), "nested-call") == 0) { + NestedCall nestedCall; + if (nestedCall.loadFromXml(e)) + nestedCalls.push_back(std::move(nestedCall)); + } + } +} + +std::map> CTU::FileInfo::getCallsMap() const +{ + std::map> ret; + for (const CTU::FileInfo::NestedCall &nc : nestedCalls) + ret[nc.callId].push_back(&nc); + for (const CTU::FileInfo::FunctionCall &fc : functionCalls) + ret[fc.callId].push_back(&fc); + return ret; +} + +std::list CTU::loadUnsafeUsageListFromXml(const tinyxml2::XMLElement *xmlElement) +{ + std::list ret; + for (const tinyxml2::XMLElement *e = xmlElement->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (std::strcmp(e->Name(), "unsafe-usage") != 0) + continue; + bool error = false; + FileInfo::UnsafeUsage unsafeUsage; + unsafeUsage.myId = readAttrString(e, ATTR_MY_ID, &error); + unsafeUsage.myArgNr = readAttrInt(e, ATTR_MY_ARGNR, &error); + unsafeUsage.myArgumentName = readAttrString(e, ATTR_MY_ARGNAME, &error); + unsafeUsage.location.fileName = readAttrString(e, ATTR_LOC_FILENAME, &error); + unsafeUsage.location.lineNumber = readAttrInt(e, ATTR_LOC_LINENR, &error); + unsafeUsage.location.column = readAttrInt(e, ATTR_LOC_COLUMN, &error); + unsafeUsage.value = readAttrInt(e, ATTR_VALUE, &error); + + if (!error) + ret.push_back(std::move(unsafeUsage)); + } + return ret; +} + +static int isCallFunction(const Scope *scope, int argnr, const Token *&tok) +{ + const Variable * const argvar = scope->function->getArgumentVar(argnr); + if (!argvar->isPointer()) + return -1; + for (const Token *tok2 = scope->bodyStart; tok2 != scope->bodyEnd; tok2 = tok2->next()) { + if (tok2->variable() != argvar) + continue; + if (!Token::Match(tok2->previous(), "[(,] %var% [,)]")) + break; + int argnr2 = 1; + const Token *prev = tok2; + while (prev && prev->str() != "(") { + if (Token::Match(prev,"]|)")) + prev = prev->link(); + else if (prev->str() == ",") + ++argnr2; + prev = prev->previous(); + } + if (!prev || !Token::Match(prev->previous(), "%name% (")) + break; + if (!prev->astOperand1() || !prev->astOperand1()->function()) + break; + tok = prev->previous(); + return argnr2; + } + return -1; +} + + +CTU::FileInfo *CTU::getFileInfo(const Tokenizer &tokenizer) +{ + const SymbolDatabase * const symbolDatabase = tokenizer.getSymbolDatabase(); + + auto *fileInfo = new FileInfo; + + // Parse all functions in TU + for (const Scope &scope : symbolDatabase->scopeList) { + if (!scope.isExecutable() || scope.type != Scope::eFunction || !scope.function) + continue; + const Function *const scopeFunction = scope.function; + + // source function calls + for (const Token *tok = scope.bodyStart; tok != scope.bodyEnd; tok = tok->next()) { + if (tok->str() != "(" || !tok->astOperand1() || !tok->astOperand2()) + continue; + const Function* tokFunction = tok->astOperand1()->function(); + if (!tokFunction) + continue; + const std::vector args(getArguments(tok->previous())); + for (int argnr = 0; argnr < args.size(); ++argnr) { + const Token *argtok = args[argnr]; + if (!argtok) + continue; + for (const ValueFlow::Value &value : argtok->values()) { + if ((!value.isIntValue() || value.intvalue != 0 || value.isInconclusive()) && !value.isBufferSizeValue()) + continue; + // Skip impossible values since they cannot be represented + if (value.isImpossible()) + continue; + FileInfo::FunctionCall functionCall; + functionCall.callValueType = value.valueType; + functionCall.callId = getFunctionId(tokenizer, tokFunction); + functionCall.callFunctionName = tok->astOperand1()->expressionString(); + functionCall.location = FileInfo::Location(tokenizer,tok); + functionCall.callArgNr = argnr + 1; + functionCall.callArgumentExpression = argtok->expressionString(); + functionCall.callArgValue = value.intvalue; + functionCall.warning = !value.errorSeverity(); + for (const ErrorPathItem &i : value.errorPath) { + const std::string& file = tokenizer.list.file(i.first); + const std::string& info = i.second; + const int line = i.first->linenr(); + const int column = i.first->column(); + ErrorMessage::FileLocation loc(file, info, line, column); + functionCall.callValuePath.push_back(std::move(loc)); + } + fileInfo->functionCalls.push_back(std::move(functionCall)); + } + // array + if (argtok->variable() && argtok->variable()->isArray() && argtok->variable()->dimensions().size() == 1 && argtok->variable()->dimensionKnown(0)) { + FileInfo::FunctionCall functionCall; + functionCall.callValueType = ValueFlow::Value::ValueType::BUFFER_SIZE; + functionCall.callId = getFunctionId(tokenizer, tokFunction); + functionCall.callFunctionName = tok->astOperand1()->expressionString(); + functionCall.location = FileInfo::Location(tokenizer, tok); + functionCall.callArgNr = argnr + 1; + functionCall.callArgumentExpression = argtok->expressionString(); + const auto typeSize = argtok->valueType()->typeSize(tokenizer.getSettings().platform); + functionCall.callArgValue = typeSize > 0 ? argtok->variable()->dimension(0) * typeSize : -1; + functionCall.warning = false; + fileInfo->functionCalls.push_back(std::move(functionCall)); + } + // &var => buffer + if (argtok->isUnaryOp("&") && argtok->astOperand1()->variable() && argtok->astOperand1()->valueType() && !argtok->astOperand1()->variable()->isArray()) { + FileInfo::FunctionCall functionCall; + functionCall.callValueType = ValueFlow::Value::ValueType::BUFFER_SIZE; + functionCall.callId = getFunctionId(tokenizer, tokFunction); + functionCall.callFunctionName = tok->astOperand1()->expressionString(); + functionCall.location = FileInfo::Location(tokenizer, tok); + functionCall.callArgNr = argnr + 1; + functionCall.callArgumentExpression = argtok->expressionString(); + functionCall.callArgValue = argtok->astOperand1()->valueType()->typeSize(tokenizer.getSettings().platform); + functionCall.warning = false; + fileInfo->functionCalls.push_back(std::move(functionCall)); + } + // pointer/reference to uninitialized data + auto isAddressOfArg = [](const Token* argtok) -> const Token* { + if (!argtok->isUnaryOp("&")) + return nullptr; + argtok = argtok->astOperand1(); + if (!argtok || !argtok->valueType() || argtok->valueType()->pointer != 0) + return nullptr; + return argtok; + }; + auto isReferenceArg = [&](const Token* argtok) -> const Token* { + const Variable* argvar = tokFunction->getArgumentVar(argnr); + if (!argvar || !argvar->valueType() || argvar->valueType()->reference == Reference::None) + return nullptr; + return argtok; + }; + const Token* addr = isAddressOfArg(argtok); + argtok = addr ? addr : isReferenceArg(argtok); + if (!argtok || argtok->values().size() != 1U) + continue; + if (argtok->variable() && argtok->variable()->isClass()) + continue; + + const ValueFlow::Value &v = argtok->values().front(); + if (v.valueType == ValueFlow::Value::ValueType::UNINIT && !v.isInconclusive()) { + FileInfo::FunctionCall functionCall; + functionCall.callValueType = ValueFlow::Value::ValueType::UNINIT; + functionCall.callId = getFunctionId(tokenizer, tokFunction); + functionCall.callFunctionName = tok->astOperand1()->expressionString(); + functionCall.location = FileInfo::Location(tokenizer, tok); + functionCall.callArgNr = argnr + 1; + functionCall.callArgValue = 0; + functionCall.callArgumentExpression = argtok->expressionString(); + functionCall.warning = false; + fileInfo->functionCalls.push_back(std::move(functionCall)); + continue; + } + } + } + + // Nested function calls + for (int argnr = 0; argnr < scopeFunction->argCount(); ++argnr) { + const Token *tok; + const int argnr2 = isCallFunction(&scope, argnr, tok); + if (argnr2 > 0) { + FileInfo::NestedCall nestedCall(tokenizer, scopeFunction, tok); + nestedCall.myArgNr = argnr + 1; + nestedCall.callArgNr = argnr2; + fileInfo->nestedCalls.push_back(std::move(nestedCall)); + } + } + } + + return fileInfo; +} + +static std::list> getUnsafeFunction(const Settings &settings, const Scope *scope, int argnr, bool (*isUnsafeUsage)(const Settings &settings, const Token *argtok, MathLib::bigint *value)) +{ + std::list> ret; + const Variable * const argvar = scope->function->getArgumentVar(argnr); + if (!argvar->isArrayOrPointer() && !argvar->isReference()) + return ret; + for (const Token *tok2 = scope->bodyStart; tok2 != scope->bodyEnd; tok2 = tok2->next()) { + if (Token::Match(tok2, ")|else {")) { + tok2 = tok2->linkAt(1); + if (Token::findmatch(tok2->link(), "return|throw", tok2)) + return ret; + int indirect = 0; + if (argvar->valueType()) + indirect = argvar->valueType()->pointer; + if (isVariableChanged(tok2->link(), tok2, indirect, argvar->declarationId(), false, &settings)) + return ret; + } + if (Token::Match(tok2, "%oror%|&&|?")) { + tok2 = tok2->findExpressionStartEndTokens().second; + continue; + } + if (tok2->variable() != argvar) + continue; + MathLib::bigint value = 0; + if (!isUnsafeUsage(settings, tok2, &value)) + return ret; // TODO: Is this a read? then continue.. + ret.emplace_back(tok2, value); + return ret; + } + return ret; +} + +std::list CTU::getUnsafeUsage(const Tokenizer &tokenizer, const Settings &settings, bool (*isUnsafeUsage)(const Settings &settings, const Token *argtok, MathLib::bigint *value)) +{ + std::list unsafeUsage; + + // Parse all functions in TU + const SymbolDatabase * const symbolDatabase = tokenizer.getSymbolDatabase(); + + for (const Scope &scope : symbolDatabase->scopeList) { + if (!scope.isExecutable() || scope.type != Scope::eFunction || !scope.function) + continue; + const Function *const function = scope.function; + + // "Unsafe" functions unconditionally reads data before it is written.. + for (int argnr = 0; argnr < function->argCount(); ++argnr) { + for (const std::pair &v : getUnsafeFunction(settings, &scope, argnr, isUnsafeUsage)) { + const Token *tok = v.first; + const MathLib::bigint val = v.second; + unsafeUsage.emplace_back(CTU::getFunctionId(tokenizer, function), argnr+1, tok->str(), CTU::FileInfo::Location(tokenizer,tok), val); + } + } + } + + return unsafeUsage; +} + +static bool findPath(const std::string &callId, + nonneg int callArgNr, + MathLib::bigint unsafeValue, + CTU::FileInfo::InvalidValueType invalidValue, + const std::map> &callsMap, + const CTU::FileInfo::CallBase *path[10], + int index, + bool warning) +{ + if (index >= CTU::maxCtuDepth || index >= 10) + return false; + + const std::map>::const_iterator it = callsMap.find(callId); + if (it == callsMap.end()) + return false; + + for (const CTU::FileInfo::CallBase *c : it->second) { + if (c->callArgNr != callArgNr) + continue; + + const auto *functionCall = dynamic_cast(c); + if (functionCall) { + if (!warning && functionCall->warning) + continue; + switch (invalidValue) { + case CTU::FileInfo::InvalidValueType::null: + if (functionCall->callValueType != ValueFlow::Value::ValueType::INT || functionCall->callArgValue != 0) + continue; + break; + case CTU::FileInfo::InvalidValueType::uninit: + if (functionCall->callValueType != ValueFlow::Value::ValueType::UNINIT) + continue; + break; + case CTU::FileInfo::InvalidValueType::bufferOverflow: + if (functionCall->callValueType != ValueFlow::Value::ValueType::BUFFER_SIZE) + continue; + if (unsafeValue < 0 || (unsafeValue >= functionCall->callArgValue && functionCall->callArgValue >= 0)) + break; + continue; + } + path[index] = functionCall; + return true; + } + + const auto *nestedCall = dynamic_cast(c); + if (!nestedCall) + continue; + + if (findPath(nestedCall->myId, nestedCall->myArgNr, unsafeValue, invalidValue, callsMap, path, index + 1, warning)) { + path[index] = nestedCall; + return true; + } + } + + return false; +} + +std::list CTU::FileInfo::getErrorPath(InvalidValueType invalidValue, + const CTU::FileInfo::UnsafeUsage &unsafeUsage, + const std::map> &callsMap, + const char info[], + const FunctionCall ** const functionCallPtr, + bool warning) +{ + std::list locationList; + + const CTU::FileInfo::CallBase *path[10] = {nullptr}; + + if (!findPath(unsafeUsage.myId, unsafeUsage.myArgNr, unsafeUsage.value, invalidValue, callsMap, path, 0, warning)) + return locationList; + + const std::string value1 = (invalidValue == InvalidValueType::null) ? "null" : "uninitialized"; + + for (int index = 9; index >= 0; index--) { + if (!path[index]) + continue; + + const auto *functionCall = dynamic_cast(path[index]); + + if (functionCall) { + if (functionCallPtr) + *functionCallPtr = functionCall; + std::copy(functionCall->callValuePath.cbegin(), functionCall->callValuePath.cend(), std::back_inserter(locationList)); + } + + std::string info_s = "Calling function " + path[index]->callFunctionName + ", " + std::to_string(path[index]->callArgNr) + getOrdinalText(path[index]->callArgNr) + " argument is " + value1; + ErrorMessage::FileLocation fileLoc(path[index]->location.fileName, std::move(info_s), path[index]->location.lineNumber, path[index]->location.column); + locationList.push_back(std::move(fileLoc)); + } + + std::string info_s = replaceStr(info, "ARG", unsafeUsage.myArgumentName); + ErrorMessage::FileLocation fileLoc2(unsafeUsage.location.fileName, std::move(info_s), unsafeUsage.location.lineNumber, unsafeUsage.location.column); + locationList.push_back(std::move(fileLoc2)); + + return locationList; +} diff --git a/cppcheck-2.14.0/lib/ctu.h b/cppcheck-2.14.0/lib/ctu.h new file mode 100644 index 00000000..7dd4a4c8 --- /dev/null +++ b/cppcheck-2.14.0/lib/ctu.h @@ -0,0 +1,156 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef ctuH +#define ctuH +//--------------------------------------------------------------------------- + +#include "config.h" +#include "check.h" +#include "errorlogger.h" +#include "mathlib.h" +#include "vfvalue.h" + +#include +#include +#include +#include +#include + +class Function; +class Settings; +class Token; +class Tokenizer; + +namespace tinyxml2 { + class XMLElement; +} + +/// @addtogroup Core +/// @{ + + +/** @brief Whole program analysis (ctu=Cross Translation Unit) */ +namespace CTU { + class CPPCHECKLIB FileInfo : public Check::FileInfo { + public: + enum class InvalidValueType { null, uninit, bufferOverflow }; + + std::string toString() const override; + + struct Location { + Location() = default; + Location(const Tokenizer &tokenizer, const Token *tok); + Location(std::string fileName, nonneg int lineNumber, nonneg int column) : fileName(std::move(fileName)), lineNumber(lineNumber), column(column) {} + std::string fileName; + nonneg int lineNumber{}; + nonneg int column{}; + }; + + struct UnsafeUsage { + UnsafeUsage() = default; + UnsafeUsage(std::string myId, nonneg int myArgNr, std::string myArgumentName, Location location, MathLib::bigint value) : myId(std::move(myId)), myArgNr(myArgNr), myArgumentName(std::move(myArgumentName)), location(std::move(location)), value(value) {} + std::string myId; + nonneg int myArgNr{}; + std::string myArgumentName; + Location location; + MathLib::bigint value{}; + std::string toString() const; + }; + + class CallBase { + public: + CallBase() = default; + CallBase(std::string callId, int callArgNr, std::string callFunctionName, Location loc) + : callId(std::move(callId)), callArgNr(callArgNr), callFunctionName(std::move(callFunctionName)), location(std::move(loc)) + {} + CallBase(const Tokenizer &tokenizer, const Token *callToken); + virtual ~CallBase() = default; + CallBase(const CallBase&) = default; + std::string callId; + int callArgNr{}; + std::string callFunctionName; + Location location; + protected: + std::string toBaseXmlString() const; + bool loadBaseFromXml(const tinyxml2::XMLElement *xmlElement); + }; + + class FunctionCall : public CallBase { + public: + std::string callArgumentExpression; + MathLib::bigint callArgValue; + ValueFlow::Value::ValueType callValueType; + std::vector callValuePath; + bool warning; + + std::string toXmlString() const; + bool loadFromXml(const tinyxml2::XMLElement *xmlElement); + }; + + class NestedCall : public CallBase { + public: + NestedCall() = default; + + NestedCall(std::string myId, nonneg int myArgNr, const std::string &callId, nonneg int callArgnr, const std::string &callFunctionName, const Location &location) + : CallBase(callId, callArgnr, callFunctionName, location), + myId(std::move(myId)), + myArgNr(myArgNr) {} + + NestedCall(const Tokenizer &tokenizer, const Function *myFunction, const Token *callToken); + + std::string toXmlString() const; + bool loadFromXml(const tinyxml2::XMLElement *xmlElement); + + std::string myId; + nonneg int myArgNr{}; + }; + + std::list functionCalls; + std::list nestedCalls; + + void loadFromXml(const tinyxml2::XMLElement *xmlElement); + std::map> getCallsMap() const; + + static std::list getErrorPath(InvalidValueType invalidValue, + const UnsafeUsage &unsafeUsage, + const std::map> &callsMap, + const char info[], + const FunctionCall ** const functionCallPtr, + bool warning); + }; + + extern int maxCtuDepth; + + CPPCHECKLIB std::string toString(const std::list &unsafeUsage); + + CPPCHECKLIB std::string getFunctionId(const Tokenizer &tokenizer, const Function *function); + + /** @brief Parse current TU and extract file info */ + CPPCHECKLIB FileInfo *getFileInfo(const Tokenizer &tokenizer); + + CPPCHECKLIB std::list getUnsafeUsage(const Tokenizer &tokenizer, const Settings &settings, bool (*isUnsafeUsage)(const Settings &settings, const Token *argtok, MathLib::bigint *value)); + + CPPCHECKLIB std::list loadUnsafeUsageListFromXml(const tinyxml2::XMLElement *xmlElement); +} + +/// @} +//--------------------------------------------------------------------------- +#endif // ctuH diff --git a/cppcheck-2.14.0/lib/errorlogger.cpp b/cppcheck-2.14.0/lib/errorlogger.cpp new file mode 100644 index 00000000..752af2b3 --- /dev/null +++ b/cppcheck-2.14.0/lib/errorlogger.cpp @@ -0,0 +1,937 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "color.h" +#include "cppcheck.h" +#include "path.h" +#include "settings.h" +#include "suppressions.h" +#include "token.h" +#include "tokenlist.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xml.h" + +const std::set ErrorLogger::mCriticalErrorIds{ + "cppcheckError", + "cppcheckLimit", + "internalAstError", + "instantiationError", + "internalError", + "premium-internalError", + "premium-invalidArgument", + "premium-invalidLicense", + "preprocessorErrorDirective", + "syntaxError", + "unknownMacro" +}; + +ErrorMessage::ErrorMessage() + : severity(Severity::none), cwe(0U), certainty(Certainty::normal), hash(0) +{} + +// TODO: id and msg are swapped compared to other calls +ErrorMessage::ErrorMessage(std::list callStack, std::string file1, Severity severity, const std::string &msg, std::string id, Certainty certainty) : + callStack(std::move(callStack)), // locations for this error message + id(std::move(id)), // set the message id + file0(std::move(file1)), + severity(severity), // severity for this error message + cwe(0U), + certainty(certainty), + hash(0) +{ + // set the summary and verbose messages + setmsg(msg); +} + + +// TODO: id and msg are swapped compared to other calls +ErrorMessage::ErrorMessage(std::list callStack, std::string file1, Severity severity, const std::string &msg, std::string id, const CWE &cwe, Certainty certainty) : + callStack(std::move(callStack)), // locations for this error message + id(std::move(id)), // set the message id + file0(std::move(file1)), + severity(severity), // severity for this error message + cwe(cwe.id), + certainty(certainty), + hash(0) +{ + // set the summary and verbose messages + setmsg(msg); +} + +ErrorMessage::ErrorMessage(const std::list& callstack, const TokenList* list, Severity severity, std::string id, const std::string& msg, Certainty certainty) + : id(std::move(id)), severity(severity), cwe(0U), certainty(certainty), hash(0) +{ + // Format callstack + for (std::list::const_iterator it = callstack.cbegin(); it != callstack.cend(); ++it) { + // --errorlist can provide null values here + if (!(*it)) + continue; + + callStack.emplace_back(*it, list); + } + + if (list && !list->getFiles().empty()) + file0 = list->getFiles()[0]; + + setmsg(msg); +} + + +ErrorMessage::ErrorMessage(const std::list& callstack, const TokenList* list, Severity severity, std::string id, const std::string& msg, const CWE &cwe, Certainty certainty) + : id(std::move(id)), severity(severity), cwe(cwe.id), certainty(certainty) +{ + // Format callstack + for (const Token *tok: callstack) { + // --errorlist can provide null values here + if (!tok) + continue; + + callStack.emplace_back(tok, list); + } + + if (list && !list->getFiles().empty()) + file0 = list->getFiles()[0]; + + setmsg(msg); + + hash = 0; // calculateWarningHash(list, hashWarning.str()); +} + +ErrorMessage::ErrorMessage(const ErrorPath &errorPath, const TokenList *tokenList, Severity severity, const char id[], const std::string &msg, const CWE &cwe, Certainty certainty) + : id(id), severity(severity), cwe(cwe.id), certainty(certainty) +{ + // Format callstack + for (const ErrorPathItem& e: errorPath) { + const Token *tok = e.first; + // --errorlist can provide null values here + if (!tok) + continue; + + const std::string& path_info = e.second; + + std::string info; + if (startsWith(path_info,"$symbol:") && path_info.find('\n') < path_info.size()) { + const std::string::size_type pos = path_info.find('\n'); + const std::string symbolName = path_info.substr(8, pos - 8); + info = replaceStr(path_info.substr(pos+1), "$symbol", symbolName); + } + else { + info = path_info; + } + + callStack.emplace_back(tok, std::move(info), tokenList); + } + + if (tokenList && !tokenList->getFiles().empty()) + file0 = tokenList->getFiles()[0]; + + setmsg(msg); + + hash = 0; // calculateWarningHash(tokenList, hashWarning.str()); +} + +ErrorMessage::ErrorMessage(const tinyxml2::XMLElement * const errmsg) + : severity(Severity::none), + cwe(0U), + certainty(Certainty::normal) +{ + const char * const unknown = ""; + + const char *attr = errmsg->Attribute("id"); + id = attr ? attr : unknown; + + attr = errmsg->Attribute("severity"); + severity = attr ? severityFromString(attr) : Severity::none; + + attr = errmsg->Attribute("cwe"); + // cppcheck-suppress templateInstantiation - TODO: fix this - see #11631 + cwe.id = attr ? strToInt(attr) : 0; + + attr = errmsg->Attribute("inconclusive"); + certainty = (attr && (std::strcmp(attr, "true") == 0)) ? Certainty::inconclusive : Certainty::normal; + + attr = errmsg->Attribute("msg"); + mShortMessage = attr ? attr : ""; + + attr = errmsg->Attribute("verbose"); + mVerboseMessage = attr ? attr : ""; + + attr = errmsg->Attribute("hash"); + hash = attr ? strToInt(attr) : 0; + + for (const tinyxml2::XMLElement *e = errmsg->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (std::strcmp(e->Name(),"location")==0) { + const char *strfile = e->Attribute("file"); + const char *strinfo = e->Attribute("info"); + const char *strline = e->Attribute("line"); + const char *strcolumn = e->Attribute("column"); + + const char *file = strfile ? strfile : unknown; + const char *info = strinfo ? strinfo : ""; + const int line = strline ? strToInt(strline) : 0; + const int column = strcolumn ? strToInt(strcolumn) : 0; + callStack.emplace_front(file, info, line, column); + } else if (std::strcmp(e->Name(),"symbol")==0) { + mSymbolNames += e->GetText(); + } + } +} + +void ErrorMessage::setmsg(const std::string &msg) +{ + // If a message ends to a '\n' and contains only a one '\n' + // it will cause the mVerboseMessage to be empty which will show + // as an empty message to the user if --verbose is used. + // Even this doesn't cause problems with messages that have multiple + // lines, none of the error messages should end into it. + assert(!endsWith(msg,'\n')); + + // The summary and verbose message are separated by a newline + // If there is no newline then both the summary and verbose messages + // are the given message + const std::string::size_type pos = msg.find('\n'); + const std::string symbolName = mSymbolNames.empty() ? std::string() : mSymbolNames.substr(0, mSymbolNames.find('\n')); + if (pos == std::string::npos) { + mShortMessage = replaceStr(msg, "$symbol", symbolName); + mVerboseMessage = replaceStr(msg, "$symbol", symbolName); + } else if (startsWith(msg,"$symbol:")) { + mSymbolNames += msg.substr(8, pos-7); + setmsg(msg.substr(pos + 1)); + } else { + mShortMessage = replaceStr(msg.substr(0, pos), "$symbol", symbolName); + mVerboseMessage = replaceStr(msg.substr(pos + 1), "$symbol", symbolName); + } +} + +static void serializeString(std::string &oss, const std::string & str) +{ + oss += std::to_string(str.length()); + oss += " "; + oss += str; +} + +ErrorMessage ErrorMessage::fromInternalError(const InternalError &internalError, const TokenList *tokenList, const std::string &filename, const std::string& msg) +{ + if (internalError.token) + assert(tokenList != nullptr); // we need to make sure we can look up the provided token + + std::list locationList; + if (tokenList && internalError.token) { + locationList.emplace_back(internalError.token, tokenList); + } else { + locationList.emplace_back(filename, 0, 0); + if (tokenList && (filename != tokenList->getSourceFilePath())) { + locationList.emplace_back(tokenList->getSourceFilePath(), 0, 0); + } + } + ErrorMessage errmsg(std::move(locationList), + tokenList ? tokenList->getSourceFilePath() : filename, + Severity::error, + (msg.empty() ? "" : (msg + ": ")) + internalError.errorMessage, + internalError.id, + Certainty::normal); + // TODO: find a better way + if (!internalError.details.empty()) + errmsg.mVerboseMessage = errmsg.mVerboseMessage + ": " + internalError.details; + return errmsg; +} + +std::string ErrorMessage::serialize() const +{ + // Serialize this message into a simple string + std::string oss; + serializeString(oss, id); + serializeString(oss, severityToString(severity)); + serializeString(oss, std::to_string(cwe.id)); + serializeString(oss, std::to_string(hash)); + serializeString(oss, file0); + if (certainty == Certainty::inconclusive) { + const std::string text("inconclusive"); + serializeString(oss, text); + } + + const std::string saneShortMessage = fixInvalidChars(mShortMessage); + const std::string saneVerboseMessage = fixInvalidChars(mVerboseMessage); + + serializeString(oss, saneShortMessage); + serializeString(oss, saneVerboseMessage); + oss += std::to_string(callStack.size()); + oss += " "; + + for (std::list::const_iterator loc = callStack.cbegin(); loc != callStack.cend(); ++loc) { + std::string frame; + frame += std::to_string(loc->line); + frame += '\t'; + frame += std::to_string(loc->column); + frame += '\t'; + frame += loc->getfile(false); + frame += '\t'; + frame += loc->getOrigFile(false); + frame += '\t'; + frame += loc->getinfo(); + serializeString(oss, frame); + } + + return oss; +} + +void ErrorMessage::deserialize(const std::string &data) +{ + // TODO: clear all fields + certainty = Certainty::normal; + callStack.clear(); + + std::istringstream iss(data); + std::array results; + std::size_t elem = 0; + while (iss.good() && elem < 7) { + unsigned int len = 0; + if (!(iss >> len)) + throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - invalid length"); + + if (iss.get() != ' ') + throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - invalid separator"); + + if (!iss.good()) + throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - premature end of data"); + + std::string temp; + if (len > 0) { + temp.resize(len); + iss.read(&temp[0], len); + + if (!iss.good()) + throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - premature end of data"); + + if (temp == "inconclusive") { + certainty = Certainty::inconclusive; + continue; + } + } + + results[elem++] = std::move(temp); + } + + if (!iss.good()) + throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - premature end of data"); + + if (elem != 7) + throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - insufficient elements"); + + id = std::move(results[0]); + severity = severityFromString(results[1]); + cwe.id = 0; + if (!results[2].empty()) { + std::string err; + if (!strToInt(results[2], cwe.id, &err)) + throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - invalid CWE ID - " + err); + } + hash = 0; + if (!results[3].empty()) { + std::string err; + if (!strToInt(results[3], hash, &err)) + throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - invalid hash - " + err); + } + file0 = std::move(results[4]); + mShortMessage = std::move(results[5]); + mVerboseMessage = std::move(results[6]); + + unsigned int stackSize = 0; + if (!(iss >> stackSize)) + throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - invalid stack size"); + + if (iss.get() != ' ') + throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - invalid separator"); + + if (stackSize == 0) + return; + + while (iss.good()) { + unsigned int len = 0; + if (!(iss >> len)) + throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - invalid length (stack)"); + + if (iss.get() != ' ') + throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - invalid separator (stack)"); + + std::string temp; + if (len > 0) { + temp.resize(len); + iss.read(&temp[0], len); + + if (!iss.good()) + throw InternalError(nullptr, "Internal Error: Deserialization of error message failed - premature end of data (stack)"); + } + + std::vector substrings; + substrings.reserve(5); + for (std::string::size_type pos = 0; pos < temp.size() && substrings.size() < 5; ++pos) { + if (substrings.size() == 4) { + substrings.push_back(temp.substr(pos)); + break; + } + const std::string::size_type start = pos; + pos = temp.find('\t', pos); + if (pos == std::string::npos) { + substrings.push_back(temp.substr(start)); + break; + } + substrings.push_back(temp.substr(start, pos - start)); + } + if (substrings.size() < 4) + throw InternalError(nullptr, "Internal Error: Deserializing of error message failed"); + + // (*loc).line << '\t' << (*loc).column << '\t' << (*loc).getfile(false) << '\t' << loc->getOrigFile(false) << '\t' << loc->getinfo(); + + std::string info; + if (substrings.size() == 5) + info = std::move(substrings[4]); + ErrorMessage::FileLocation loc(substrings[3], std::move(info), strToInt(substrings[0]), strToInt(substrings[1])); + loc.setfile(std::move(substrings[2])); + + callStack.push_back(std::move(loc)); + + if (callStack.size() >= stackSize) + break; + } +} + +std::string ErrorMessage::getXMLHeader(std::string productName) +{ + const auto nameAndVersion = Settings::getNameAndVersion(productName); + productName = nameAndVersion.first; + const std::string version = nameAndVersion.first.empty() ? CppCheck::version() : nameAndVersion.second; + + tinyxml2::XMLPrinter printer; + + // standard xml header + printer.PushDeclaration("xml version=\"1.0\" encoding=\"UTF-8\""); + + // header + printer.OpenElement("results", false); + + printer.PushAttribute("version", 2); + printer.OpenElement("cppcheck", false); + if (!productName.empty()) + printer.PushAttribute("product-name", productName.c_str()); + printer.PushAttribute("version", version.c_str()); + printer.CloseElement(false); + printer.OpenElement("errors", false); + + return std::string(printer.CStr()) + '>'; +} + +std::string ErrorMessage::getXMLFooter() +{ + return " \n"; +} + +// There is no utf-8 support around but the strings should at least be safe for to tinyxml2. +// See #5300 "Invalid encoding in XML output" and #6431 "Invalid XML created - Invalid encoding of string literal " +std::string ErrorMessage::fixInvalidChars(const std::string& raw) +{ + std::string result; + result.reserve(raw.length()); + std::string::const_iterator from=raw.cbegin(); + while (from!=raw.cend()) { + if (std::isprint(static_cast(*from))) { + result.push_back(*from); + } else { + std::ostringstream es; + // straight cast to (unsigned) doesn't work out. + const unsigned uFrom = (unsigned char)*from; + es << '\\' << std::setbase(8) << std::setw(3) << std::setfill('0') << uFrom; + result += es.str(); + } + ++from; + } + return result; +} + +std::string ErrorMessage::toXML() const +{ + tinyxml2::XMLPrinter printer(nullptr, false, 2); + printer.OpenElement("error", false); + printer.PushAttribute("id", id.c_str()); + printer.PushAttribute("severity", severityToString(severity).c_str()); + printer.PushAttribute("msg", fixInvalidChars(mShortMessage).c_str()); + printer.PushAttribute("verbose", fixInvalidChars(mVerboseMessage).c_str()); + if (cwe.id) + printer.PushAttribute("cwe", cwe.id); + if (hash) + printer.PushAttribute("hash", std::to_string(hash).c_str()); + if (certainty == Certainty::inconclusive) + printer.PushAttribute("inconclusive", "true"); + + if (!file0.empty()) + printer.PushAttribute("file0", file0.c_str()); + + for (std::list::const_reverse_iterator it = callStack.crbegin(); it != callStack.crend(); ++it) { + printer.OpenElement("location", false); + printer.PushAttribute("file", it->getfile().c_str()); + printer.PushAttribute("line", std::max(it->line,0)); + printer.PushAttribute("column", it->column); + if (!it->getinfo().empty()) + printer.PushAttribute("info", fixInvalidChars(it->getinfo()).c_str()); + printer.CloseElement(false); + } + for (std::string::size_type pos = 0; pos < mSymbolNames.size();) { + const std::string::size_type pos2 = mSymbolNames.find('\n', pos); + std::string symbolName; + if (pos2 == std::string::npos) { + symbolName = mSymbolNames.substr(pos); + pos = pos2; + } else { + symbolName = mSymbolNames.substr(pos, pos2-pos); + pos = pos2 + 1; + } + printer.OpenElement("symbol", false); + printer.PushText(symbolName.c_str()); + printer.CloseElement(false); + } + printer.CloseElement(false); + return printer.CStr(); +} + +/** + * 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) +{ + std::string::size_type index = 0; + while ((index = source.find(searchFor, index)) != std::string::npos) { + source.replace(index, searchFor.length(), replaceWith); + index += replaceWith.length(); + } +} + +// TODO: read info from some shared resource instead? +static std::string readCode(const std::string &file, int linenr, int column, const char endl[]) +{ + std::ifstream fin(file); + std::string line; + while (linenr > 0 && std::getline(fin,line)) { + linenr--; + } + const std::string::size_type endPos = line.find_last_not_of("\r\n\t "); + if (endPos + 1 < line.size()) + line.erase(endPos + 1); + std::string::size_type pos = 0; + while ((pos = line.find('\t', pos)) != std::string::npos) + line[pos] = ' '; + return line + endl + std::string((column>0 ? column-1 : 0), ' ') + '^'; +} + +static void replaceSpecialChars(std::string& source) +{ + // 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. + static const std::unordered_map substitutionMap = { + {'b', "\b"}, + {'n', "\n"}, + {'r', "\r"}, + {'t', "\t"} + }; + + std::string::size_type index = 0; + while ((index = source.find('\\', index)) != std::string::npos) { + const char searchFor = source[index+1]; + const auto it = substitutionMap.find(searchFor); + if (it == substitutionMap.end()) { + index += 1; + continue; + } + const std::string& replaceWith = it->second; + source.replace(index, 2, replaceWith); + index += replaceWith.length(); + } +} + +static void replace(std::string& source, const std::unordered_map &substitutionMap) +{ + std::string::size_type index = 0; + while ((index = source.find('{', index)) != std::string::npos) { + const std::string::size_type end = source.find('}', index); + if (end == std::string::npos) + break; + const std::string searchFor = source.substr(index, end-index+1); + const auto it = substitutionMap.find(searchFor); + if (it == substitutionMap.end()) { + index += 1; + continue; + } + const std::string& replaceWith = it->second; + source.replace(index, searchFor.length(), replaceWith); + index += replaceWith.length(); + } +} + +static void replaceColors(std::string& source) { + // TODO: colors are not applied when either stdout or stderr is not a TTY because we resolve them before the stream usage + static const std::unordered_map substitutionMap = + { + {"{reset}", ::toString(Color::Reset)}, + {"{bold}", ::toString(Color::Bold)}, + {"{dim}", ::toString(Color::Dim)}, + {"{red}", ::toString(Color::FgRed)}, + {"{green}", ::toString(Color::FgGreen)}, + {"{blue}", ::toString(Color::FgBlue)}, + {"{magenta}", ::toString(Color::FgMagenta)}, + {"{default}", ::toString(Color::FgDefault)}, + }; + replace(source, substitutionMap); +} + +// TODO: remove default parameters +std::string ErrorMessage::toString(bool verbose, const std::string &templateFormat, const std::string &templateLocation) const +{ + // Save this ErrorMessage in plain text. + + // TODO: should never happen - remove this + // No template is given + // (not 100%) equivalent templateFormat: {callstack} ({severity}{inconclusive:, inconclusive}) {message} + if (templateFormat.empty()) { + std::string text; + if (!callStack.empty()) { + text += ErrorLogger::callStackToString(callStack); + text += ": "; + } + if (severity != Severity::none) { + text += '('; + text += severityToString(severity); + if (certainty == Certainty::inconclusive) + text += ", inconclusive"; + text += ") "; + } + text += (verbose ? mVerboseMessage : mShortMessage); + return text; + } + + // template is given. Reformat the output according to it + std::string result = templateFormat; + + findAndReplace(result, "{id}", id); + + std::string::size_type pos1 = result.find("{inconclusive:"); + while (pos1 != std::string::npos) { + const std::string::size_type pos2 = result.find('}', pos1+1); + const std::string replaceFrom = result.substr(pos1,pos2-pos1+1); + const std::string replaceWith = (certainty == Certainty::inconclusive) ? result.substr(pos1+14, pos2-pos1-14) : std::string(); + findAndReplace(result, replaceFrom, replaceWith); + pos1 = result.find("{inconclusive:", pos1); + } + findAndReplace(result, "{severity}", severityToString(severity)); + findAndReplace(result, "{cwe}", std::to_string(cwe.id)); + findAndReplace(result, "{message}", verbose ? mVerboseMessage : mShortMessage); + if (!callStack.empty()) { + if (result.find("{callstack}") != std::string::npos) + findAndReplace(result, "{callstack}", ErrorLogger::callStackToString(callStack)); + findAndReplace(result, "{file}", callStack.back().getfile()); + findAndReplace(result, "{line}", std::to_string(callStack.back().line)); + findAndReplace(result, "{column}", std::to_string(callStack.back().column)); + if (result.find("{code}") != std::string::npos) { + const std::string::size_type pos = result.find('\r'); + const char *endl; + if (pos == std::string::npos) + endl = "\n"; + else if (pos+1 < result.size() && result[pos+1] == '\n') + endl = "\r\n"; + else + endl = "\r"; + findAndReplace(result, "{code}", readCode(callStack.back().getOrigFile(), callStack.back().line, callStack.back().column, endl)); + } + } else { + static const std::unordered_map callStackSubstitutionMap = + { + {"{callstack}", ""}, + {"{file}", "nofile"}, + {"{line}", "0"}, + {"{column}", "0"}, + {"{code}", ""} + }; + replace(result, callStackSubstitutionMap); + } + + if (!templateLocation.empty() && callStack.size() >= 2U) { + for (const FileLocation &fileLocation : callStack) { + std::string text = templateLocation; + + findAndReplace(text, "{file}", fileLocation.getfile()); + findAndReplace(text, "{line}", std::to_string(fileLocation.line)); + findAndReplace(text, "{column}", std::to_string(fileLocation.column)); + findAndReplace(text, "{info}", fileLocation.getinfo().empty() ? mShortMessage : fileLocation.getinfo()); + if (text.find("{code}") != std::string::npos) { + const std::string::size_type pos = text.find('\r'); + const char *endl; + if (pos == std::string::npos) + endl = "\n"; + else if (pos+1 < text.size() && text[pos+1] == '\n') + endl = "\r\n"; + else + endl = "\r"; + findAndReplace(text, "{code}", readCode(fileLocation.getOrigFile(), fileLocation.line, fileLocation.column, endl)); + } + result += '\n' + text; + } + } + + return result; +} + +std::string ErrorLogger::callStackToString(const std::list &callStack) +{ + std::string str; + for (std::list::const_iterator tok = callStack.cbegin(); tok != callStack.cend(); ++tok) { + str += (tok == callStack.cbegin() ? "" : " -> "); + str += tok->stringify(); + } + return str; +} + + +ErrorMessage::FileLocation::FileLocation(const Token* tok, const TokenList* tokenList) + : fileIndex(tok->fileIndex()), line(tok->linenr()), column(tok->column()), mOrigFileName(tokenList->getOrigFile(tok)), mFileName(tokenList->file(tok)) +{} + +ErrorMessage::FileLocation::FileLocation(const Token* tok, std::string info, const TokenList* tokenList) + : fileIndex(tok->fileIndex()), line(tok->linenr()), column(tok->column()), mOrigFileName(tokenList->getOrigFile(tok)), mFileName(tokenList->file(tok)), mInfo(std::move(info)) +{} + +std::string ErrorMessage::FileLocation::getfile(bool convert) const +{ + if (convert) + return Path::toNativeSeparators(mFileName); + return mFileName; +} + +std::string ErrorMessage::FileLocation::getOrigFile(bool convert) const +{ + if (convert) + return Path::toNativeSeparators(mOrigFileName); + return mOrigFileName; +} + +void ErrorMessage::FileLocation::setfile(std::string file) +{ + mFileName = Path::fromNativeSeparators(std::move(file)); + mFileName = Path::simplifyPath(std::move(mFileName)); +} + +std::string ErrorMessage::FileLocation::stringify() const +{ + std::string str; + str += '['; + str += Path::toNativeSeparators(mFileName); + if (line != SuppressionList::Suppression::NO_LINE) { + str += ':'; + str += std::to_string(line); + } + str += ']'; + return str; +} + +std::string ErrorLogger::toxml(const std::string &str) +{ + std::string xml; + for (const unsigned char c : str) { + switch (c) { + case '<': + xml += "<"; + break; + case '>': + xml += ">"; + break; + case '&': + xml += "&"; + break; + case '\"': + xml += """; + break; + case '\'': + xml += "'"; + break; + case '\0': + xml += "\\0"; + break; + default: + if (c >= ' ' && c <= 0x7f) + xml += c; + else + xml += 'x'; + break; + } + } + return xml; +} + +std::string ErrorLogger::plistHeader(const std::string &version, const std::vector &files) +{ + std::ostringstream ostr; + ostr << "\r\n" + << "\r\n" + << "\r\n" + << "\r\n" + << " clang_version\r\n" + << "cppcheck version " << version << "\r\n" + << " files\r\n" + << " \r\n"; + for (const std::string & file : files) + ostr << " " << ErrorLogger::toxml(file) << "\r\n"; + ostr << " \r\n" + << " diagnostics\r\n" + << " \r\n"; + return ostr.str(); +} + +static std::string plistLoc(const char indent[], const ErrorMessage::FileLocation &loc) +{ + std::ostringstream ostr; + ostr << indent << "\r\n" + << indent << ' ' << "line" << loc.line << "\r\n" + << indent << ' ' << "col" << loc.column << "\r\n" + << indent << ' ' << "file" << loc.fileIndex << "\r\n" + << indent << "\r\n"; + return ostr.str(); +} + +std::string ErrorLogger::plistData(const ErrorMessage &msg) +{ + std::ostringstream plist; + plist << " \r\n" + << " path\r\n" + << " \r\n"; + + std::list::const_iterator prev = msg.callStack.cbegin(); + + for (std::list::const_iterator it = msg.callStack.cbegin(); it != msg.callStack.cend(); ++it) { + if (prev != it) { + plist << " \r\n" + << " kindcontrol\r\n" + << " edges\r\n" + << " \r\n" + << " \r\n" + << " start\r\n" + << " \r\n" + << plistLoc(" ", *prev) + << plistLoc(" ", *prev) + << " \r\n" + << " end\r\n" + << " \r\n" + << plistLoc(" ", *it) + << plistLoc(" ", *it) + << " \r\n" + << " \r\n" + << " \r\n" + << " \r\n"; + prev = it; + } + + std::list::const_iterator next = it; + ++next; + const std::string message = (it->getinfo().empty() && next == msg.callStack.cend() ? msg.shortMessage() : it->getinfo()); + + plist << " \r\n" + << " kindevent\r\n" + << " location\r\n" + << plistLoc(" ", *it) + << " ranges\r\n" + << " \r\n" + << " \r\n" + << plistLoc(" ", *it) + << plistLoc(" ", *it) + << " \r\n" + << " \r\n" + << " depth0\r\n" + << " extended_message\r\n" + << " " << ErrorLogger::toxml(message) << "\r\n" + << " message\r\n" + << " " << ErrorLogger::toxml(message) << "\r\n" + << " \r\n"; + } + + plist << " \r\n" + << " description" << ErrorLogger::toxml(msg.shortMessage()) << "\r\n" + << " category" << severityToString(msg.severity) << "\r\n" + << " type" << ErrorLogger::toxml(msg.shortMessage()) << "\r\n" + << " check_name" << msg.id << "\r\n" + << " \r\n" + << " issue_hash_content_of_line_in_context" << 0 << "\r\n" + << " issue_context_kind\r\n" + << " issue_context\r\n" + << " issue_hash_function_offset\r\n" + << " location\r\n" + << plistLoc(" ", msg.callStack.back()) + << " \r\n"; + return plist.str(); +} + + +std::string replaceStr(std::string s, const std::string &from, const std::string &to) +{ + std::string::size_type pos1 = 0; + while (pos1 < s.size()) { + pos1 = s.find(from, pos1); + if (pos1 == std::string::npos) + return s; + if (pos1 > 0 && (s[pos1-1] == '_' || std::isalnum(s[pos1-1]))) { + pos1++; + continue; + } + const std::string::size_type pos2 = pos1 + from.size(); + if (pos2 >= s.size()) + return s.substr(0,pos1) + to; + if (s[pos2] == '_' || std::isalnum(s[pos2])) { + pos1++; + continue; + } + s.replace(pos1, from.size(), to); + pos1 += to.size(); + } + return s; +} + +void substituteTemplateFormatStatic(std::string& templateFormat) +{ + replaceSpecialChars(templateFormat); + replaceColors(templateFormat); +} + +void substituteTemplateLocationStatic(std::string& templateLocation) +{ + replaceSpecialChars(templateLocation); + replaceColors(templateLocation); +} diff --git a/cppcheck-2.14.0/lib/errorlogger.h b/cppcheck-2.14.0/lib/errorlogger.h new file mode 100644 index 00000000..c396781a --- /dev/null +++ b/cppcheck-2.14.0/lib/errorlogger.h @@ -0,0 +1,283 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef errorloggerH +#define errorloggerH +//--------------------------------------------------------------------------- + +#include "config.h" +#include "errortypes.h" +#include "color.h" + +#include +#include +#include +#include +#include +#include + +class Token; +class TokenList; + +namespace tinyxml2 { + class XMLElement; +} + +/// @addtogroup Core +/// @{ + +/** + * 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 WARN_UNUSED FileLocation { + public: + FileLocation(const std::string &file, int line, unsigned int column) + : fileIndex(0), line(line), column(column), mOrigFileName(file), mFileName(file) {} + + FileLocation(const std::string &file, std::string info, int line, unsigned int column) + : fileIndex(0), line(line), column(column), mOrigFileName(file), mFileName(file), mInfo(std::move(info)) {} + + FileLocation(const Token* tok, const TokenList* tokenList); + FileLocation(const Token* tok, std::string info, const TokenList* tokenList); + + /** + * Return the filename. + * @param convert If true convert path to native separators. + * @return filename. + */ + std::string getfile(bool convert = true) const; + + /** + * Filename with the whole path (no --rp) + * @param convert If true convert path to native separators. + * @return filename. + */ + std::string getOrigFile(bool convert = true) const; + + /** + * Set the filename. + * @param file Filename to set. + */ + void setfile(std::string file); + + /** + * @return the location as a string. Format: [file:line] + */ + std::string stringify() const; + + unsigned int fileIndex; + int line; // negative value means "no line" + unsigned int column; + + const std::string& getinfo() const { + return mInfo; + } + + private: + std::string mOrigFileName; + std::string mFileName; + std::string mInfo; + }; + + ErrorMessage(std::list callStack, + std::string file1, + Severity severity, + const std::string &msg, + std::string id, Certainty certainty); + ErrorMessage(std::list callStack, + std::string file1, + Severity severity, + const std::string &msg, + std::string id, + const CWE &cwe, + Certainty certainty); + ErrorMessage(const std::list& callstack, + const TokenList* list, + Severity severity, + std::string id, + const std::string& msg, + Certainty certainty); + ErrorMessage(const std::list& callstack, + const TokenList* list, + Severity severity, + std::string id, + const std::string& msg, + const CWE &cwe, + Certainty certainty); + ErrorMessage(const ErrorPath &errorPath, + const TokenList *tokenList, + Severity severity, + const char id[], + const std::string &msg, + const CWE &cwe, + Certainty certainty); + ErrorMessage(); + explicit ErrorMessage(const tinyxml2::XMLElement * const errmsg); + + /** + * Format the error message in XML format + */ + std::string toXML() const; + + static std::string getXMLHeader(std::string productName); + static std::string getXMLFooter(); + + /** + * Format the error message into a string. + * @param verbose use verbose message + * @param templateFormat Empty string to use default output format + * or template to be used. E.g. "{file}:{line},{severity},{id},{message}" + * @param templateLocation Format Empty string to use default output format + * or template to be used. E.g. "{file}:{line},{info}" + * @return formatted string + */ + std::string toString(bool verbose, + const std::string &templateFormat = emptyString, + const std::string &templateLocation = emptyString) const; + + std::string serialize() const; + void deserialize(const std::string &data); + + std::list callStack; + std::string id; + + /** For GUI rechecking; source file (not header) */ + std::string file0; + + Severity severity; + CWE cwe; + Certainty certainty; + + /** Warning hash */ + std::size_t hash; + + /** set short and verbose messages */ + void setmsg(const std::string &msg); + + /** Short message (single line short message) */ + const std::string &shortMessage() const { + return mShortMessage; + } + + /** Verbose message (may be the same as the short message) */ + const std::string &verboseMessage() const { + return mVerboseMessage; + } + + /** Symbol names */ + const std::string &symbolNames() const { + return mSymbolNames; + } + + static ErrorMessage fromInternalError(const InternalError &internalError, const TokenList *tokenList, const std::string &filename, const std::string& msg = emptyString); + +private: + static std::string fixInvalidChars(const std::string& raw); + + /** Short message */ + std::string mShortMessage; + + /** Verbose message */ + std::string mVerboseMessage; + + /** symbol names */ + std::string mSymbolNames; +}; + +/** + * @brief This is an interface, which the class responsible of error logging + * should implement. + */ +class CPPCHECKLIB ErrorLogger { +public: + ErrorLogger() = default; + virtual ~ErrorLogger() = default; + + /** + * 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, Color c = Color::Reset) = 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 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; + } + + static std::string callStackToString(const std::list &callStack); + + /** + * Convert XML-sensitive characters into XML entities + * @param str The input string containing XML-sensitive characters + * @return The output string containing XML entities + */ + static std::string toxml(const std::string &str); + + static std::string plistHeader(const std::string &version, const std::vector &files); + static std::string plistData(const ErrorMessage &msg); + static const char *plistFooter() { + return " \r\n" + "\r\n" + ""; + } + + static bool isCriticalErrorId(const std::string& id) { + return mCriticalErrorIds.count(id) != 0; + } + +private: + static const std::set mCriticalErrorIds; +}; + +/** Replace substring. Example replaceStr("1,NR,3", "NR", "2") => "1,2,3" */ +std::string replaceStr(std::string s, const std::string &from, const std::string &to); + +/** replaces the static parts of the location template **/ +CPPCHECKLIB void substituteTemplateFormatStatic(std::string& templateFormat); + +/** replaces the static parts of the location template **/ +CPPCHECKLIB void substituteTemplateLocationStatic(std::string& templateLocation); + +/// @} +//--------------------------------------------------------------------------- +#endif // errorloggerH diff --git a/cppcheck-2.14.0/lib/errortypes.cpp b/cppcheck-2.14.0/lib/errortypes.cpp new file mode 100644 index 00000000..2c8824c9 --- /dev/null +++ b/cppcheck-2.14.0/lib/errortypes.cpp @@ -0,0 +1,99 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "errortypes.h" + +#include "utils.h" + +static std::string typeToString(InternalError::Type type) +{ + switch (type) { + case InternalError::Type::AST: + return "internalAstError"; + case InternalError::Type::SYNTAX: + return "syntaxError"; + case InternalError::Type::UNKNOWN_MACRO: + return "unknownMacro"; + case InternalError::Type::INTERNAL: + return "internalError"; + case InternalError::Type::LIMIT: + return "cppcheckLimit"; + case InternalError::Type::INSTANTIATION: + return "instantiationError"; + } + cppcheck::unreachable(); +} + +InternalError::InternalError(const Token *tok, std::string errorMsg, Type type) : + InternalError(tok, std::move(errorMsg), "", type) +{} + +InternalError::InternalError(const Token *tok, std::string errorMsg, std::string details, Type type) : + token(tok), errorMessage(std::move(errorMsg)), details(std::move(details)), type(type), id(typeToString(type)) +{} + +std::string severityToString(Severity severity) +{ + switch (severity) { + case Severity::none: + return ""; + case Severity::error: + return "error"; + case Severity::warning: + return "warning"; + case Severity::style: + return "style"; + case Severity::performance: + return "performance"; + case Severity::portability: + return "portability"; + case Severity::information: + return "information"; + case Severity::debug: + return "debug"; + case Severity::internal: + return "internal"; + } + throw InternalError(nullptr, "Unknown severity"); +} + +// TODO: bail out on invalid severity +Severity severityFromString(const std::string& severity) +{ + if (severity.empty()) + return Severity::none; + if (severity == "none") + return Severity::none; + if (severity == "error") + return Severity::error; + if (severity == "warning") + return Severity::warning; + if (severity == "style") + return Severity::style; + if (severity == "performance") + return Severity::performance; + if (severity == "portability") + return Severity::portability; + if (severity == "information") + return Severity::information; + if (severity == "debug") + return Severity::debug; + if (severity == "internal") + return Severity::internal; + return Severity::none; +} diff --git a/cppcheck-2.14.0/lib/errortypes.h b/cppcheck-2.14.0/lib/errortypes.h new file mode 100644 index 00000000..3e9a6a41 --- /dev/null +++ b/cppcheck-2.14.0/lib/errortypes.h @@ -0,0 +1,134 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef errortypesH +#define errortypesH +//--------------------------------------------------------------------------- + +#include "config.h" + +#include +#include +#include +#include + +/// @addtogroup Core +/// @{ +class Token; + +/** @brief Simple container to be thrown when internal error is detected. */ +struct CPPCHECKLIB InternalError { + enum Type {AST, SYNTAX, UNKNOWN_MACRO, INTERNAL, LIMIT, INSTANTIATION}; + + InternalError(const Token *tok, std::string errorMsg, Type type = INTERNAL); + InternalError(const Token *tok, std::string errorMsg, std::string details, Type type = INTERNAL); + + const Token *token; + std::string errorMessage; + std::string details; + Type type; + std::string id; +}; + +class TerminateException : public std::runtime_error { +public: + TerminateException() : std::runtime_error("terminate") {} +}; + +enum class Certainty { + normal, inconclusive +}; + +enum class Checks { + unusedFunction, missingInclude, internalCheck +}; + +/** @brief enum class for severity. Used when reporting errors. */ +enum class Severity { + /** + * 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, + /** + * Internal message. + * Message will not be shown to the user. + * Tracking what checkers is executed, tracking suppressed critical errors, etc. + */ + internal +}; + +CPPCHECKLIB std::string severityToString(Severity severity); +CPPCHECKLIB Severity severityFromString(const std::string &severity); + +struct CWE { + explicit CWE(unsigned short cweId) : id(cweId) {} + unsigned short id; +}; + +using ErrorPathItem = std::pair; +using ErrorPath = std::list; + +/// @} +//--------------------------------------------------------------------------- +#endif // errortypesH diff --git a/cppcheck-2.14.0/lib/filesettings.h b/cppcheck-2.14.0/lib/filesettings.h new file mode 100644 index 00000000..11ac5b5c --- /dev/null +++ b/cppcheck-2.14.0/lib/filesettings.h @@ -0,0 +1,49 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef fileSettingsH +#define fileSettingsH + +#include "config.h" +#include "platform.h" + +#include +#include +#include + +/** File settings. Multiple configurations for a file is allowed. */ +struct CPPCHECKLIB FileSettings { + std::string cfg; + std::string filename; + std::string defines; + // TODO: handle differently + std::string cppcheckDefines() const { + return defines + (msc ? ";_MSC_VER=1900" : "") + (useMfc ? ";__AFXWIN_H__=1" : ""); + } + std::set undefs; + std::list includePaths; + // only used by clang mode + std::list systemIncludePaths; + std::string standard; + Platform::Type platformType = Platform::Type::Unspecified; + // TODO: get rid of these + bool msc{}; + bool useMfc{}; +}; + +#endif // fileSettingsH diff --git a/cppcheck-2.14.0/lib/findtoken.h b/cppcheck-2.14.0/lib/findtoken.h new file mode 100644 index 00000000..647cbda7 --- /dev/null +++ b/cppcheck-2.14.0/lib/findtoken.h @@ -0,0 +1,221 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef findtokenH +#define findtokenH +//--------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "config.h" +#include "errortypes.h" +#include "library.h" +#include "smallvector.h" +#include "symboldatabase.h" +#include "token.h" + +inline std::vector evaluateKnownValues(const Token* tok) +{ + if (!tok->hasKnownIntValue()) + return {}; + return {tok->getKnownIntValue()}; +} + +template )> +void findTokensImpl(T* start, const Token* end, const Predicate& pred, Found found) +{ + for (T* tok = start; precedes(tok, end); tok = tok->next()) { + if (pred(tok)) { + if (found(tok)) + break; + } + } +} + +template )> +std::vector findTokens(T* start, const Token* end, const Predicate& pred) +{ + std::vector result; + findTokensImpl(start, end, pred, [&](T* tok) { + result.push_back(tok); + return false; + }); + return result; +} + +template )> +T* findToken(T* start, const Token* end, const Predicate& pred) +{ + T* result = nullptr; + findTokensImpl(start, end, pred, [&](T* tok) { + result = tok; + return true; + }); + return result; +} + +template )> +bool findTokensSkipDeadCodeImpl(const Library& library, + T* start, + const Token* end, + const Predicate& pred, + Found found, + const Evaluate& evaluate) +{ + for (T* tok = start; precedes(tok, end); tok = tok->next()) { + if (pred(tok)) { + if (found(tok)) + return true; + } + if (Token::Match(tok, "if|for|while (") && Token::simpleMatch(tok->next()->link(), ") {")) { + const Token* condTok = getCondTok(tok); + if (!condTok) + continue; + auto result = evaluate(condTok); + if (result.empty()) + continue; + if (findTokensSkipDeadCodeImpl(library, tok->next(), tok->linkAt(1), pred, found, evaluate)) + return true; + T* thenStart = tok->linkAt(1)->next(); + T* elseStart = nullptr; + if (Token::simpleMatch(thenStart->link(), "} else {")) + elseStart = thenStart->link()->tokAt(2); + + int r = result.front(); + if (r == 0) { + if (elseStart) { + if (findTokensSkipDeadCodeImpl(library, elseStart, elseStart->link(), pred, found, evaluate)) + return true; + if (isReturnScope(elseStart->link(), library)) + return true; + tok = elseStart->link(); + } else { + tok = thenStart->link(); + } + } else { + if (findTokensSkipDeadCodeImpl(library, thenStart, thenStart->link(), pred, found, evaluate)) + return true; + if (isReturnScope(thenStart->link(), library)) + return true; + tok = thenStart->link(); + } + } else if (Token::Match(tok->astParent(), "&&|?|%oror%") && astIsLHS(tok)) { + auto result = evaluate(tok); + if (result.empty()) + continue; + const bool cond = result.front() != 0; + T* next = nullptr; + if ((cond && Token::simpleMatch(tok->astParent(), "||")) || + (!cond && Token::simpleMatch(tok->astParent(), "&&"))) { + next = nextAfterAstRightmostLeaf(tok->astParent()); + } else if (Token::simpleMatch(tok->astParent(), "?")) { + T* colon = tok->astParent()->astOperand2(); + if (!cond) { + next = colon; + } else { + if (findTokensSkipDeadCodeImpl(library, tok->astParent()->next(), colon, pred, found, evaluate)) + return true; + next = nextAfterAstRightmostLeaf(colon); + } + } + if (next) + tok = next; + } else if (Token::simpleMatch(tok, "} else {")) { + const Token* condTok = getCondTokFromEnd(tok); + if (!condTok) + continue; + auto result = evaluate(condTok); + if (result.empty()) + continue; + if (isReturnScope(tok->link(), library)) + return true; + int r = result.front(); + if (r != 0) { + tok = tok->linkAt(2); + } + } else if (Token::simpleMatch(tok, "[") && Token::Match(tok->link(), "] (|{")) { + T* afterCapture = tok->link()->next(); + if (Token::simpleMatch(afterCapture, "(") && afterCapture->link()) + tok = afterCapture->link()->next(); + else + tok = afterCapture; + } + } + return false; +} + +template )> +std::vector findTokensSkipDeadCode(const Library& library, + T* start, + const Token* end, + const Predicate& pred, + const Evaluate& evaluate) +{ + std::vector result; + (void)findTokensSkipDeadCodeImpl( + library, + start, + end, + pred, + [&](T* tok) { + result.push_back(tok); + return false; + }, + evaluate); + return result; +} + +template )> +std::vector findTokensSkipDeadCode(const Library& library, T* start, const Token* end, const Predicate& pred) +{ + return findTokensSkipDeadCode(library, start, end, pred, &evaluateKnownValues); +} + +template )> +T* findTokenSkipDeadCode(const Library& library, T* start, const Token* end, const Predicate& pred, const Evaluate& evaluate) +{ + T* result = nullptr; + (void)findTokensSkipDeadCodeImpl( + library, + start, + end, + pred, + [&](T* tok) { + result = tok; + return true; + }, + evaluate); + return result; +} + +template )> +T* findTokenSkipDeadCode(const Library& library, T* start, const Token* end, const Predicate& pred) +{ + return findTokenSkipDeadCode(library, start, end, pred, &evaluateKnownValues); +} + +#endif // findtokenH diff --git a/cppcheck-2.14.0/lib/forwardanalyzer.cpp b/cppcheck-2.14.0/lib/forwardanalyzer.cpp new file mode 100644 index 00000000..89411fba --- /dev/null +++ b/cppcheck-2.14.0/lib/forwardanalyzer.cpp @@ -0,0 +1,925 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "forwardanalyzer.h" + +#include "analyzer.h" +#include "astutils.h" +#include "config.h" +#include "errorlogger.h" +#include "errortypes.h" +#include "mathlib.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenlist.h" +#include "utils.h" +#include "valueptr.h" +#include "vfvalue.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + struct ForwardTraversal { + enum class Progress { Continue, Break, Skip }; + enum class Terminate { None, Bail, Inconclusive }; + ForwardTraversal(const ValuePtr& analyzer, const TokenList& tokenList, ErrorLogger& errorLogger, const Settings& settings) + : analyzer(analyzer), tokenList(tokenList), errorLogger(errorLogger), settings(settings) + {} + ValuePtr analyzer; + const TokenList& tokenList; + ErrorLogger& errorLogger; + const Settings& settings; + Analyzer::Action actions; + bool analyzeOnly{}; + bool analyzeTerminate{}; + Analyzer::Terminate terminate = Analyzer::Terminate::None; + std::vector loopEnds; + int branchCount = 0; + + Progress Break(Analyzer::Terminate t = Analyzer::Terminate::None) { + if ((!analyzeOnly || analyzeTerminate) && t != Analyzer::Terminate::None) + terminate = t; + return Progress::Break; + } + + struct Branch { + explicit Branch(Token* tok = nullptr) : endBlock(tok) {} + Token* endBlock = nullptr; + Analyzer::Action action = Analyzer::Action::None; + bool check = false; + bool escape = false; + bool escapeUnknown = false; + bool active = false; + bool isEscape() const { + return escape || escapeUnknown; + } + bool isConclusiveEscape() const { + return escape && !escapeUnknown; + } + bool isModified() const { + return action.isModified() && !isConclusiveEscape(); + } + bool isInconclusive() const { + return action.isInconclusive() && !isConclusiveEscape(); + } + bool isDead() const { + return action.isModified() || action.isInconclusive() || isEscape(); + } + }; + + bool stopUpdates() { + analyzeOnly = true; + return actions.isModified(); + } + + std::pair evalCond(const Token* tok, const Token* ctx = nullptr) const { + if (!tok) + return std::make_pair(false, false); + std::vector result = analyzer->evaluate(tok, ctx); + // TODO: We should convert to bool + const bool checkThen = std::any_of(result.cbegin(), result.cend(), [](int x) { + return x != 0; + }); + const bool checkElse = std::any_of(result.cbegin(), result.cend(), [](int x) { + return x == 0; + }); + return std::make_pair(checkThen, checkElse); + } + + bool isConditionTrue(const Token* tok, const Token* ctx = nullptr) const { + return evalCond(tok, ctx).first; + } + + template )> + Progress traverseTok(T* tok, const F &f, bool traverseUnknown, T** out = nullptr) { + if (Token::Match(tok, "asm|goto")) + return Break(Analyzer::Terminate::Bail); + if (Token::Match(tok, "setjmp|longjmp (")) { + // Traverse the parameters of the function before escaping + traverseRecursive(tok->next()->astOperand2(), f, traverseUnknown); + return Break(Analyzer::Terminate::Bail); + } + if (Token::simpleMatch(tok, "continue")) { + if (loopEnds.empty()) + return Break(Analyzer::Terminate::Escape); + // If we are in a loop then jump to the end + if (out) + *out = loopEnds.back(); + } else if (Token::Match(tok, "return|throw")) { + traverseRecursive(tok->astOperand2(), f, traverseUnknown); + traverseRecursive(tok->astOperand1(), f, traverseUnknown); + return Break(Analyzer::Terminate::Escape); + } else if (Token::Match(tok, "%name% (") && isEscapeFunction(tok, &settings.library)) { + // Traverse the parameters of the function before escaping + traverseRecursive(tok->next()->astOperand2(), f, traverseUnknown); + return Break(Analyzer::Terminate::Escape); + } else if (isUnevaluated(tok->previous())) { + if (out) + *out = tok->link(); + return Progress::Skip; + } else if (tok->astOperand1() && tok->astOperand2() && Token::Match(tok, "?|&&|%oror%")) { + if (traverseConditional(tok, f, traverseUnknown) == Progress::Break) + return Break(); + if (out) + *out = nextAfterAstRightmostLeaf(tok); + return Progress::Skip; + // Skip lambdas + } else if (T* lambdaEndToken = findLambdaEndToken(tok)) { + if (checkScope(lambdaEndToken).isModified()) + return Break(Analyzer::Terminate::Bail); + if (out) + *out = lambdaEndToken->next(); + // Skip class scope + } else if (tok->str() == "{" && tok->scope() && tok->scope()->isClassOrStruct()) { + if (out) + *out = tok->link(); + } else { + if (f(tok) == Progress::Break) + return Break(); + } + return Progress::Continue; + } + + template )> + Progress traverseRecursive(T* tok, const F &f, bool traverseUnknown, unsigned int recursion=0) { + if (!tok) + return Progress::Continue; + if (recursion > 10000) + return Progress::Skip; + T* firstOp = tok->astOperand1(); + T* secondOp = tok->astOperand2(); + // Evaluate: + // 1. RHS of assignment before LHS + // 2. Unary op before operand + // 3. Function arguments before function call + if (tok->isAssignmentOp() || !secondOp || isFunctionCall(tok)) + std::swap(firstOp, secondOp); + if (firstOp && traverseRecursive(firstOp, f, traverseUnknown, recursion+1) == Progress::Break) + return Break(); + const Progress p = tok->isAssignmentOp() ? Progress::Continue : traverseTok(tok, f, traverseUnknown); + if (p == Progress::Break) + return Break(); + if (p == Progress::Continue && secondOp && traverseRecursive(secondOp, f, traverseUnknown, recursion+1) == Progress::Break) + return Break(); + if (tok->isAssignmentOp() && traverseTok(tok, f, traverseUnknown) == Progress::Break) + return Break(); + return Progress::Continue; + } + + template )> + Progress traverseConditional(T* tok, F f, bool traverseUnknown) { + if (Token::Match(tok, "?|&&|%oror%") && tok->astOperand1() && tok->astOperand2()) { + T* condTok = tok->astOperand1(); + T* childTok = tok->astOperand2(); + bool checkThen, checkElse; + std::tie(checkThen, checkElse) = evalCond(condTok); + if (!checkThen && !checkElse) { + if (!traverseUnknown && analyzer->stopOnCondition(condTok) && stopUpdates()) { + return Progress::Continue; + } + checkThen = true; + checkElse = true; + } + if (childTok->str() == ":") { + if (checkThen && traverseRecursive(childTok->astOperand1(), f, traverseUnknown) == Progress::Break) + return Break(); + if (checkElse && traverseRecursive(childTok->astOperand2(), f, traverseUnknown) == Progress::Break) + return Break(); + } else { + if (!checkThen && tok->str() == "&&") + return Progress::Continue; + if (!checkElse && tok->str() == "||") + return Progress::Continue; + if (traverseRecursive(childTok, f, traverseUnknown) == Progress::Break) + return Break(); + } + } + return Progress::Continue; + } + + Progress update(Token* tok) { + Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Forward); + actions |= action; + if (!action.isNone() && !analyzeOnly) + analyzer->update(tok, action, Analyzer::Direction::Forward); + if (action.isInconclusive() && !analyzer->lowerToInconclusive()) + return Break(Analyzer::Terminate::Inconclusive); + if (action.isInvalid()) + return Break(Analyzer::Terminate::Modified); + if (action.isWrite() && !action.isRead()) + // Analysis of this write will continue separately + return Break(Analyzer::Terminate::Modified); + return Progress::Continue; + } + + Progress updateTok(Token* tok, Token** out = nullptr) { + auto f = [this](Token* tok2) { + return update(tok2); + }; + return traverseTok(tok, f, false, out); + } + + Progress updateRecursive(Token* tok) { + auto f = [this](Token* tok2) { + return update(tok2); + }; + return traverseRecursive(tok, f, false); + } + + Analyzer::Action analyzeRecursive(const Token* start) { + Analyzer::Action result = Analyzer::Action::None; + auto f = [&](const Token* tok) { + result = analyzer->analyze(tok, Analyzer::Direction::Forward); + if (result.isModified() || result.isInconclusive()) + return Break(); + return Progress::Continue; + }; + traverseRecursive(start, f, true); + return result; + } + + Analyzer::Action analyzeRange(const Token* start, const Token* end) const { + Analyzer::Action result = Analyzer::Action::None; + for (const Token* tok = start; tok && tok != end; tok = tok->next()) { + Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Forward); + if (action.isModified() || action.isInconclusive()) + return action; + result |= action; + } + return result; + } + + ForwardTraversal fork(bool analyze = false) const { + ForwardTraversal ft = *this; + if (analyze) { + ft.analyzeOnly = true; + ft.analyzeTerminate = true; + } + ft.actions = Analyzer::Action::None; + return ft; + } + + std::vector tryForkScope(Token* endBlock, bool isModified = false) const { + if (analyzer->updateScope(endBlock, isModified)) { + ForwardTraversal ft = fork(); + return {std::move(ft)}; + } + return std::vector {}; + } + + std::vector tryForkUpdateScope(Token* endBlock, bool isModified = false) const { + std::vector result = tryForkScope(endBlock, isModified); + for (ForwardTraversal& ft : result) + ft.updateScope(endBlock); + return result; + } + + static bool hasGoto(const Token* endBlock) { + return Token::findsimplematch(endBlock->link(), "goto", endBlock); + } + + static bool hasJump(const Token* endBlock) { + return Token::findmatch(endBlock->link(), "goto|break", endBlock); + } + + bool hasInnerReturnScope(const Token* start, const Token* end) const { + for (const Token* tok=start; tok != end; tok = tok->previous()) { + if (Token::simpleMatch(tok, "}")) { + const Token* ftok = nullptr; + const bool r = isReturnScope(tok, settings.library, &ftok); + if (r) + return true; + } + } + return false; + } + + bool isEscapeScope(const Token* endBlock, bool& unknown) const { + const Token* ftok = nullptr; + const bool r = isReturnScope(endBlock, settings.library, &ftok); + if (!r && ftok) + unknown = true; + return r; + } + + enum class Status { + None, + Inconclusive, + }; + + Analyzer::Action analyzeScope(const Token* endBlock) const { + return analyzeRange(endBlock->link(), endBlock); + } + + Analyzer::Action checkScope(Token* endBlock) const { + Analyzer::Action a = analyzeScope(endBlock); + tryForkUpdateScope(endBlock, a.isModified()); + return a; + } + + Analyzer::Action checkScope(const Token* endBlock) const { + Analyzer::Action a = analyzeScope(endBlock); + return a; + } + + bool checkBranch(Branch& branch) const { + Analyzer::Action a = analyzeScope(branch.endBlock); + branch.action = a; + std::vector ft1 = tryForkUpdateScope(branch.endBlock, a.isModified()); + const bool bail = hasGoto(branch.endBlock); + if (!a.isModified() && !bail) { + if (ft1.empty()) { + // Traverse into the branch to see if there is a conditional escape + if (!branch.escape && hasInnerReturnScope(branch.endBlock->previous(), branch.endBlock->link())) { + ForwardTraversal ft2 = fork(true); + ft2.updateScope(branch.endBlock); + if (ft2.terminate == Analyzer::Terminate::Escape) { + branch.escape = true; + branch.escapeUnknown = false; + } + } + } else { + if (ft1.front().terminate == Analyzer::Terminate::Escape) { + branch.escape = true; + branch.escapeUnknown = false; + } + } + } + return bail; + } + + bool reentersLoop(Token* endBlock, const Token* condTok, const Token* stepTok) const { + if (!condTok) + return true; + if (Token::simpleMatch(condTok, ":")) + return true; + bool stepChangesCond = false; + if (stepTok) { + std::pair exprToks = stepTok->findExpressionStartEndTokens(); + if (exprToks.first != nullptr && exprToks.second != nullptr) + stepChangesCond |= + findExpressionChanged(condTok, exprToks.first, exprToks.second->next(), &settings) != nullptr; + } + const bool bodyChangesCond = findExpressionChanged(condTok, endBlock->link(), endBlock, &settings); + // Check for mutation in the condition + const bool condChanged = + nullptr != findAstNode(condTok, [&](const Token* tok) { + return isVariableChanged(tok, 0, &settings); + }); + const bool changed = stepChangesCond || bodyChangesCond || condChanged; + if (!changed) + return true; + ForwardTraversal ft = fork(true); + ft.updateScope(endBlock); + return ft.isConditionTrue(condTok) && bodyChangesCond; + } + + Progress updateInnerLoop(Token* endBlock, Token* stepTok, Token* condTok) { + loopEnds.push_back(endBlock); + OnExit oe{[&] { + loopEnds.pop_back(); + }}; + if (endBlock && updateScope(endBlock) == Progress::Break) + return Break(); + if (stepTok && updateRecursive(stepTok) == Progress::Break) + return Break(); + if (condTok && !Token::simpleMatch(condTok, ":") && updateRecursive(condTok) == Progress::Break) + return Break(); + return Progress::Continue; + } + + Progress updateLoop(const Token* endToken, + Token* endBlock, + Token* condTok, + Token* initTok = nullptr, + Token* stepTok = nullptr, + bool exit = false) { + if (initTok && updateRecursive(initTok) == Progress::Break) + return Break(); + const bool isDoWhile = precedes(endBlock, condTok); + bool checkThen = true; + bool checkElse = false; + if (condTok && !Token::simpleMatch(condTok, ":")) + std::tie(checkThen, checkElse) = evalCond(condTok, isDoWhile ? endBlock->previous() : nullptr); + // exiting a do while(false) + if (checkElse && exit) { + if (hasJump(endBlock)) { + if (!analyzer->lowerToPossible()) + return Break(Analyzer::Terminate::Bail); + if (analyzer->isConditional() && stopUpdates()) + return Break(Analyzer::Terminate::Conditional); + } + return Progress::Continue; + } + Analyzer::Action bodyAnalysis = analyzeScope(endBlock); + Analyzer::Action allAnalysis = bodyAnalysis; + Analyzer::Action condAnalysis; + if (condTok) { + condAnalysis = analyzeRecursive(condTok); + allAnalysis |= condAnalysis; + } + if (stepTok) + allAnalysis |= analyzeRecursive(stepTok); + actions |= allAnalysis; + // do while(false) is not really a loop + if (checkElse && isDoWhile && + (condTok->hasKnownIntValue() || + (!bodyAnalysis.isModified() && !condAnalysis.isModified() && condAnalysis.isRead()))) { + if (updateScope(endBlock) == Progress::Break) + return Break(); + return updateRecursive(condTok); + } + if (allAnalysis.isInconclusive()) { + if (!analyzer->lowerToInconclusive()) + return Break(Analyzer::Terminate::Bail); + } else if (allAnalysis.isModified() || (exit && allAnalysis.isIdempotent())) { + if (!analyzer->lowerToPossible()) + return Break(Analyzer::Terminate::Bail); + } + + if (condTok && !Token::simpleMatch(condTok, ":")) { + if (!isDoWhile || (!bodyAnalysis.isModified() && !bodyAnalysis.isIdempotent())) + if (updateRecursive(condTok) == Progress::Break) + return Break(); + } + if (!checkThen && !checkElse && !isDoWhile && analyzer->stopOnCondition(condTok) && stopUpdates()) + return Break(Analyzer::Terminate::Conditional); + // condition is false, we don't enter the loop + if (checkElse) + return Progress::Continue; + if (checkThen || isDoWhile) { + // Since we are re-entering the loop then assume the condition is true to update the state + if (exit) + analyzer->assume(condTok, true, Analyzer::Assume::Quiet | Analyzer::Assume::Absolute); + if (updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break) + return Break(); + // If loop re-enters then it could be modified again + if (allAnalysis.isModified() && reentersLoop(endBlock, condTok, stepTok)) + return Break(Analyzer::Terminate::Bail); + if (allAnalysis.isIncremental()) + return Break(Analyzer::Terminate::Bail); + } else if (allAnalysis.isModified()) { + std::vector ftv = tryForkScope(endBlock, allAnalysis.isModified()); + bool forkContinue = true; + for (ForwardTraversal& ft : ftv) { + if (condTok) + ft.analyzer->assume(condTok, false, Analyzer::Assume::Quiet); + if (ft.updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break) + forkContinue = false; + } + + if (allAnalysis.isModified() || !forkContinue) { + // TODO: Don't bail on missing condition + if (!condTok) + return Break(Analyzer::Terminate::Bail); + if (analyzer->isConditional() && stopUpdates()) + return Break(Analyzer::Terminate::Conditional); + analyzer->assume(condTok, false); + } + if (forkContinue) { + for (ForwardTraversal& ft : ftv) { + if (!ft.actions.isIncremental()) + ft.updateRange(endBlock, endToken); + } + } + if (allAnalysis.isIncremental()) + return Break(Analyzer::Terminate::Bail); + } else { + if (updateInnerLoop(endBlock, stepTok, condTok) == Progress::Break) + return Progress::Break; + if (allAnalysis.isIncremental()) + return Break(Analyzer::Terminate::Bail); + } + return Progress::Continue; + } + + Progress updateLoopExit(const Token* endToken, + Token* endBlock, + Token* condTok, + Token* initTok = nullptr, + Token* stepTok = nullptr) { + return updateLoop(endToken, endBlock, condTok, initTok, stepTok, true); + } + + Progress updateScope(Token* endBlock, int depth = 20) + { + if (!endBlock) + return Break(); + assert(endBlock->link()); + Token* ctx = endBlock->link()->previous(); + if (Token::simpleMatch(ctx, ")")) + ctx = ctx->link()->previous(); + if (ctx) + analyzer->updateState(ctx); + return updateRange(endBlock->link(), endBlock, depth); + } + + Progress updateRange(Token* start, const Token* end, int depth = 20) { + if (depth < 0) + return Break(Analyzer::Terminate::Bail); + std::size_t i = 0; + for (Token* tok = start; precedes(tok, end); tok = tok->next()) { + Token* next = nullptr; + if (tok->index() <= i) + throw InternalError(tok, "Cyclic forward analysis."); + i = tok->index(); + + if (tok->link()) { + // Skip casts.. + if (tok->str() == "(" && !tok->astOperand2() && tok->isCast()) { + tok = tok->link(); + continue; + } + // Skip template arguments.. + if (tok->str() == "<") { + tok = tok->link(); + continue; + } + } + + // Evaluate RHS of assignment before LHS + if (Token* assignTok = assignExpr(tok)) { + if (updateRecursive(assignTok) == Progress::Break) + return Break(); + tok = nextAfterAstRightmostLeaf(assignTok); + if (!tok) + return Break(); + } else if (Token::simpleMatch(tok, ") {") && Token::Match(tok->link()->previous(), "for|while (") && + !Token::simpleMatch(tok->link()->astOperand2(), ":")) { + // In the middle of a loop structure so bail + return Break(Analyzer::Terminate::Bail); + } else if (tok->str() == ";" && tok->astParent()) { + Token* top = tok->astTop(); + if (top && Token::Match(top->previous(), "for|while (") && Token::simpleMatch(top->link(), ") {")) { + Token* endCond = top->link(); + Token* endBlock = endCond->linkAt(1); + Token* condTok = getCondTok(top); + Token* stepTok = getStepTok(top); + // The semicolon should belong to the initTok otherwise something went wrong, so just bail + if (tok->astOperand2() != condTok && !Token::simpleMatch(tok->astOperand2(), ";")) + return Break(Analyzer::Terminate::Bail); + if (updateLoop(end, endBlock, condTok, nullptr, stepTok) == Progress::Break) + return Break(); + } + } else if (tok->str() == "break") { + const Token *scopeEndToken = findNextTokenFromBreak(tok); + if (!scopeEndToken) + return Break(); + tok = skipTo(tok, scopeEndToken, end); + if (!precedes(tok, end)) + return Break(Analyzer::Terminate::Escape); + if (!analyzer->lowerToPossible()) + return Break(Analyzer::Terminate::Bail); + // TODO: Don't break, instead move to the outer scope + if (!tok) + return Break(); + } else if (!tok->variable() && (Token::Match(tok, "%name% :") || tok->str() == "case")) { + if (!analyzer->lowerToPossible()) + return Break(Analyzer::Terminate::Bail); + } else if (tok->link() && tok->str() == "}") { + const Scope* scope = tok->scope(); + if (!scope) + return Break(); + if (Token::Match(tok->link()->previous(), ")|else {")) { + const Token* tok2 = tok->link()->previous(); + const bool inElse = Token::simpleMatch(tok2, "else {"); + const bool inLoop = inElse ? false : Token::Match(tok2->link()->previous(), "while|for ("); + Token* condTok = getCondTokFromEnd(tok); + if (!condTok) + return Break(); + if (!condTok->hasKnownIntValue() || inLoop) { + if (!analyzer->lowerToPossible()) + return Break(Analyzer::Terminate::Bail); + } else if (condTok->values().front().intvalue == inElse) { + return Break(); + } + // Handle loop + if (inLoop) { + Token* stepTok = getStepTokFromEnd(tok); + bool checkThen, checkElse; + std::tie(checkThen, checkElse) = evalCond(condTok); + if (stepTok && !checkElse) { + if (updateRecursive(stepTok) == Progress::Break) + return Break(); + if (updateRecursive(condTok) == Progress::Break) + return Break(); + // Reevaluate condition + std::tie(checkThen, checkElse) = evalCond(condTok); + } + if (!checkElse) { + if (updateLoopExit(end, tok, condTok, nullptr, stepTok) == Progress::Break) + return Break(); + } + } + analyzer->assume(condTok, !inElse, Analyzer::Assume::Quiet); + if (Token::simpleMatch(tok, "} else {")) + tok = tok->linkAt(2); + } else if (scope->type == Scope::eTry) { + if (!analyzer->lowerToPossible()) + return Break(Analyzer::Terminate::Bail); + } else if (scope->type == Scope::eLambda) { + return Break(); + } else if (scope->type == Scope::eDo && Token::simpleMatch(tok, "} while (")) { + if (updateLoopExit(end, tok, tok->tokAt(2)->astOperand2()) == Progress::Break) + return Break(); + tok = tok->linkAt(2); + } else if (Token::simpleMatch(tok->next(), "else {")) { + tok = tok->linkAt(2); + } + } else if (tok->isControlFlowKeyword() && Token::Match(tok, "if|while|for (") && + Token::simpleMatch(tok->next()->link(), ") {")) { + if (settings.checkLevel == Settings::CheckLevel::normal && ++branchCount > 4) { + // TODO: should be logged on function-level instead of file-level + reportError(Severity::information, "normalCheckLevelMaxBranches", "Limiting analysis of branches. Use --check-level=exhaustive to analyze all branches."); + return Break(Analyzer::Terminate::Bail); + } + Token* endCond = tok->next()->link(); + Token* endBlock = endCond->next()->link(); + Token* condTok = getCondTok(tok); + Token* initTok = getInitTok(tok); + if (initTok && updateRecursive(initTok) == Progress::Break) + return Break(); + if (Token::Match(tok, "for|while (")) { + // For-range loop + if (Token::simpleMatch(condTok, ":")) { + Token* conTok = condTok->astOperand2(); + if (conTok && updateRecursive(conTok) == Progress::Break) + return Break(); + bool isEmpty = false; + std::vector result = + analyzer->evaluate(Analyzer::Evaluate::ContainerEmpty, conTok); + if (result.empty()) + analyzer->assume(conTok, false, Analyzer::Assume::ContainerEmpty); + else + isEmpty = result.front() != 0; + if (!isEmpty && updateLoop(end, endBlock, condTok) == Progress::Break) + return Break(); + } else { + Token* stepTok = getStepTok(tok); + // Dont pass initTok since it was already evaluated + if (updateLoop(end, endBlock, condTok, nullptr, stepTok) == Progress::Break) + return Break(); + } + tok = endBlock; + } else { + // Traverse condition + if (updateRecursive(condTok) == Progress::Break) + return Break(); + Branch thenBranch{endBlock}; + Branch elseBranch{endBlock->tokAt(2) ? endBlock->linkAt(2) : nullptr}; + // Check if condition is true or false + std::tie(thenBranch.check, elseBranch.check) = evalCond(condTok); + if (!thenBranch.check && !elseBranch.check && analyzer->stopOnCondition(condTok) && stopUpdates()) + return Break(Analyzer::Terminate::Conditional); + const bool hasElse = Token::simpleMatch(endBlock, "} else {"); + bool bail = false; + + // Traverse then block + thenBranch.escape = isEscapeScope(endBlock, thenBranch.escapeUnknown); + if (thenBranch.check) { + thenBranch.active = true; + if (updateScope(endBlock, depth - 1) == Progress::Break) + return Break(); + } else if (!elseBranch.check) { + thenBranch.active = true; + if (checkBranch(thenBranch)) + bail = true; + } + // Traverse else block + if (hasElse) { + elseBranch.escape = isEscapeScope(endBlock->linkAt(2), elseBranch.escapeUnknown); + if (elseBranch.check) { + elseBranch.active = true; + const Progress result = updateScope(endBlock->linkAt(2), depth - 1); + if (result == Progress::Break) + return Break(); + } else if (!thenBranch.check) { + elseBranch.active = true; + if (checkBranch(elseBranch)) + bail = true; + } + tok = endBlock->linkAt(2); + } else { + tok = endBlock; + } + if (thenBranch.active) + actions |= thenBranch.action; + if (elseBranch.active) + actions |= elseBranch.action; + if (bail) + return Break(Analyzer::Terminate::Bail); + if (thenBranch.isDead() && elseBranch.isDead()) { + if (thenBranch.isModified() && elseBranch.isModified()) + return Break(Analyzer::Terminate::Modified); + if (thenBranch.isConclusiveEscape() && elseBranch.isConclusiveEscape()) + return Break(Analyzer::Terminate::Escape); + return Break(Analyzer::Terminate::Bail); + } + // Conditional return + if (thenBranch.active && thenBranch.isEscape() && !hasElse) { + if (!thenBranch.isConclusiveEscape()) { + if (!analyzer->lowerToInconclusive()) + return Break(Analyzer::Terminate::Bail); + } else if (thenBranch.check) { + return Break(); + } else { + if (analyzer->isConditional() && stopUpdates()) + return Break(Analyzer::Terminate::Conditional); + analyzer->assume(condTok, false); + } + } + if (thenBranch.isInconclusive() || elseBranch.isInconclusive()) { + if (!analyzer->lowerToInconclusive()) + return Break(Analyzer::Terminate::Bail); + } else if (thenBranch.isModified() || elseBranch.isModified()) { + if (!hasElse && analyzer->isConditional() && stopUpdates()) + return Break(Analyzer::Terminate::Conditional); + if (!analyzer->lowerToPossible()) + return Break(Analyzer::Terminate::Bail); + analyzer->assume(condTok, elseBranch.isModified()); + } + } + } else if (Token::simpleMatch(tok, "try {")) { + Token* endBlock = tok->next()->link(); + ForwardTraversal tryTraversal = fork(); + tryTraversal.updateScope(endBlock, depth - 1); + bool bail = tryTraversal.actions.isModified(); + if (bail) { + actions = tryTraversal.actions; + terminate = tryTraversal.terminate; + return Break(); + } + + while (Token::simpleMatch(endBlock, "} catch (")) { + Token* endCatch = endBlock->linkAt(2); + if (!Token::simpleMatch(endCatch, ") {")) + return Break(); + endBlock = endCatch->linkAt(1); + ForwardTraversal ft = fork(); + ft.updateScope(endBlock, depth - 1); + bail |= ft.terminate != Analyzer::Terminate::None || ft.actions.isModified(); + } + if (bail) + return Break(); + tok = endBlock; + } else if (Token::simpleMatch(tok, "do {")) { + Token* endBlock = tok->next()->link(); + Token* condTok = Token::simpleMatch(endBlock, "} while (") ? endBlock->tokAt(2)->astOperand2() : nullptr; + if (updateLoop(end, endBlock, condTok) == Progress::Break) + return Break(); + if (condTok) + tok = endBlock->linkAt(2)->next(); + else + tok = endBlock; + } else if (Token::Match(tok, "assert|ASSERT (")) { + const Token* condTok = tok->next()->astOperand2(); + bool checkThen, checkElse; + std::tie(checkThen, checkElse) = evalCond(condTok); + if (checkElse) + return Break(); + if (!checkThen) + analyzer->assume(condTok, true, Analyzer::Assume::Quiet | Analyzer::Assume::Absolute); + } else if (Token::simpleMatch(tok, "switch (")) { + if (updateRecursive(tok->next()->astOperand2()) == Progress::Break) + return Break(); + return Break(); + } else if (Token* callTok = callExpr(tok)) { + // TODO: Dont traverse tokens a second time + if (start != callTok && tok != callTok && updateRecursive(callTok->astOperand1()) == Progress::Break) + return Break(); + // Since the call could be an unknown macro, traverse the tokens as a range instead of recursively + if (!Token::simpleMatch(callTok, "( )") && + updateRange(callTok->next(), callTok->link(), depth - 1) == Progress::Break) + return Break(); + if (updateTok(callTok) == Progress::Break) + return Break(); + tok = callTok->link(); + if (!tok) + return Break(); + } else { + if (updateTok(tok, &next) == Progress::Break) + return Break(); + if (next) { + if (precedes(next, end)) + tok = next->previous(); + else + return Progress::Continue; + } + } + // Prevent infinite recursion + if (tok->next() == start) + break; + } + return Progress::Continue; + } + + void reportError(Severity severity, const std::string& id, const std::string& msg) { + ErrorMessage::FileLocation loc(tokenList.getSourceFilePath(), 0, 0); + const ErrorMessage errmsg({std::move(loc)}, tokenList.getSourceFilePath(), severity, msg, id, Certainty::normal); + errorLogger.reportErr(errmsg); + } + + static bool isFunctionCall(const Token* tok) + { + if (!Token::simpleMatch(tok, "(")) + return false; + if (tok->isCast()) + return false; + if (!tok->isBinaryOp()) + return false; + if (Token::simpleMatch(tok->link(), ") {")) + return false; + if (isUnevaluated(tok->previous())) + return false; + return Token::Match(tok->previous(), "%name%|)|]|>"); + } + + static Token* assignExpr(Token* tok) { + while (tok->astParent() && astIsLHS(tok)) { + if (tok->astParent()->isAssignmentOp()) + return tok->astParent(); + tok = tok->astParent(); + } + return nullptr; + } + + static Token* callExpr(Token* tok) + { + while (tok->astParent() && astIsLHS(tok)) { + if (!Token::Match(tok, "%name%|::|<|.")) + break; + if (Token::simpleMatch(tok, "<") && !tok->link()) + break; + tok = tok->astParent(); + } + if (isFunctionCall(tok)) + return tok; + return nullptr; + } + + static Token* skipTo(Token* tok, const Token* dest, const Token* end = nullptr) { + if (end && dest->index() > end->index()) + return nullptr; + const int i = dest->index() - tok->index(); + if (i > 0) + return tok->tokAt(dest->index() - tok->index()); + return nullptr; + } + + static Token* getStepTokFromEnd(Token* tok) { + if (!Token::simpleMatch(tok, "}")) + return nullptr; + Token* end = tok->link()->previous(); + if (!Token::simpleMatch(end, ")")) + return nullptr; + return getStepTok(end->link()); + } + }; +} + +Analyzer::Result valueFlowGenericForward(Token* start, const Token* end, const ValuePtr& a, const TokenList& tokenList, ErrorLogger& errorLogger, const Settings& settings) +{ + if (a->invalid()) + return Analyzer::Result{Analyzer::Action::None, Analyzer::Terminate::Bail}; + ForwardTraversal ft{a, tokenList, errorLogger, settings}; + if (start) + ft.analyzer->updateState(start); + ft.updateRange(start, end); + return Analyzer::Result{ ft.actions, ft.terminate }; +} + +Analyzer::Result valueFlowGenericForward(Token* start, const ValuePtr& a, const TokenList& tokenList, ErrorLogger& errorLogger, const Settings& settings) +{ + if (Settings::terminated()) + throw TerminateException(); + if (a->invalid()) + return Analyzer::Result{Analyzer::Action::None, Analyzer::Terminate::Bail}; + ForwardTraversal ft{a, tokenList, errorLogger, settings}; + (void)ft.updateRecursive(start); + return Analyzer::Result{ ft.actions, ft.terminate }; +} diff --git a/cppcheck-2.14.0/lib/forwardanalyzer.h b/cppcheck-2.14.0/lib/forwardanalyzer.h new file mode 100644 index 00000000..f5c22497 --- /dev/null +++ b/cppcheck-2.14.0/lib/forwardanalyzer.h @@ -0,0 +1,39 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef forwardanalyzerH +#define forwardanalyzerH + +#include "analyzer.h" + +class ErrorLogger; +class Settings; +class Token; +class TokenList; +template class ValuePtr; + +Analyzer::Result valueFlowGenericForward(Token* start, + const Token* end, + const ValuePtr& a, + const TokenList& tokenList, + ErrorLogger& errorLogger, + const Settings& settings); + +Analyzer::Result valueFlowGenericForward(Token* start, const ValuePtr& a, const TokenList& tokenList, ErrorLogger& errorLogger, const Settings& settings); + +#endif diff --git a/cppcheck-2.14.0/lib/fwdanalysis.cpp b/cppcheck-2.14.0/lib/fwdanalysis.cpp new file mode 100644 index 00000000..97337c3a --- /dev/null +++ b/cppcheck-2.14.0/lib/fwdanalysis.cpp @@ -0,0 +1,564 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "fwdanalysis.h" + +#include "astutils.h" +#include "config.h" +#include "library.h" +#include "symboldatabase.h" +#include "token.h" +#include "vfvalue.h" + +#include +#include +#include + +static bool isUnchanged(const Token *startToken, const Token *endToken, const std::set &exprVarIds, bool local) +{ + for (const Token *tok = startToken; tok != endToken; tok = tok->next()) { + if (!local && Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) + // TODO: this is a quick bailout + return false; + if (tok->varId() == 0 || exprVarIds.find(tok->varId()) == exprVarIds.end()) + continue; + const Token *parent = tok; + while (parent->astParent() && !parent->astParent()->isAssignmentOp() && parent->astParent()->tokType() != Token::Type::eIncDecOp) { + if (parent->str() == "," || parent->isUnaryOp("&")) + // TODO: This is a quick bailout + return false; + parent = parent->astParent(); + } + if (parent->astParent()) { + if (parent->astParent()->tokType() == Token::Type::eIncDecOp) + return false; + if (parent->astParent()->isAssignmentOp() && parent == parent->astParent()->astOperand1()) + return false; + } + } + return true; +} + +static bool hasFunctionCall(const Token *tok) +{ + if (!tok) + return false; + if (Token::Match(tok, "%name% (")) + // todo, const/pure function? + return true; + return hasFunctionCall(tok->astOperand1()) || hasFunctionCall(tok->astOperand2()); +} + +static bool hasGccCompoundStatement(const Token *tok) +{ + if (!tok) + return false; + if (tok->str() == "{" && Token::simpleMatch(tok->previous(), "( {")) + return true; + return hasGccCompoundStatement(tok->astOperand1()) || hasGccCompoundStatement(tok->astOperand2()); +} + +static bool nonLocal(const Variable* var, bool deref) +{ + return !var || (!var->isLocal() && !var->isArgument()) || (deref && var->isArgument() && var->isPointer()) || var->isStatic() || var->isReference() || var->isExtern(); +} + +static bool hasVolatileCastOrVar(const Token *expr) +{ + bool ret = false; + visitAstNodes(expr, + [&ret](const Token *tok) { + if (tok->variable() && tok->variable()->isVolatile()) + ret = true; + else if (Token::simpleMatch(tok, "( volatile")) + ret = true; + return ret ? ChildrenToVisit::none : ChildrenToVisit::op1_and_op2; + }); + return ret; +} + +FwdAnalysis::Result FwdAnalysis::checkRecursive(const Token *expr, const Token *startToken, const Token *endToken, const std::set &exprVarIds, bool local, bool inInnerClass, int depth) +{ + // Parse the given tokens + if (++depth > 1000) + return Result(Result::Type::BAILOUT); + + for (const Token* tok = startToken; precedes(tok, endToken); tok = tok->next()) { + if (Token::simpleMatch(tok, "try {")) { + // TODO: handle try + return Result(Result::Type::BAILOUT); + } + + if (Token::simpleMatch(tok, "break ;")) { + return Result(Result::Type::BREAK, tok); + } + + if (Token::simpleMatch(tok, "goto")) + return Result(Result::Type::BAILOUT); + + if (!inInnerClass && tok->str() == "{" && tok->scope()->isClassOrStruct()) { + // skip returns from local class definition + FwdAnalysis::Result result = checkRecursive(expr, tok, tok->link(), exprVarIds, local, true, depth); + if (result.type != Result::Type::NONE) + return result; + tok=tok->link(); + } + + if (tok->str() == "continue") + // TODO + return Result(Result::Type::BAILOUT); + + if (const Token *lambdaEndToken = findLambdaEndToken(tok)) { + tok = lambdaEndToken; + const Result lambdaResult = checkRecursive(expr, lambdaEndToken->link()->next(), lambdaEndToken, exprVarIds, local, inInnerClass, depth); + if (lambdaResult.type == Result::Type::READ || lambdaResult.type == Result::Type::BAILOUT) + return lambdaResult; + } + + if (Token::Match(tok, "return|throw")) { + // TODO: Handle these better + // Is expr variable used in expression? + + const Token* opTok = tok->astOperand1(); + if (!opTok) + opTok = tok->next(); + std::pair startEndTokens = opTok->findExpressionStartEndTokens(); + FwdAnalysis::Result result = + checkRecursive(expr, startEndTokens.first, startEndTokens.second->next(), exprVarIds, local, true, depth); + if (result.type != Result::Type::NONE) + return result; + + // #9167: if the return is inside an inner class, it does not tell us anything + if (!inInnerClass) { + if (!local && mWhat == What::Reassign) + return Result(Result::Type::BAILOUT); + + return Result(Result::Type::RETURN); + } + } + + if (tok->str() == "}") { + // Known value => possible value + if (tok->scope() == expr->scope()) + mValueFlowKnown = false; + + if (tok->scope()->isLoopScope()) { + // check condition + const Token *conditionStart = nullptr; + const Token *conditionEnd = nullptr; + if (Token::simpleMatch(tok->link()->previous(), ") {")) { + conditionEnd = tok->link()->previous(); + conditionStart = conditionEnd->link(); + } else if (Token::simpleMatch(tok->link()->previous(), "do {") && Token::simpleMatch(tok, "} while (")) { + conditionStart = tok->tokAt(2); + conditionEnd = conditionStart->link(); + } + if (conditionStart && conditionEnd) { + bool used = false; + for (const Token *condTok = conditionStart; condTok != conditionEnd; condTok = condTok->next()) { + if (exprVarIds.find(condTok->varId()) != exprVarIds.end()) { + used = true; + break; + } + } + if (used) + return Result(Result::Type::BAILOUT); + } + + // check loop body again.. + const FwdAnalysis::Result &result = checkRecursive(expr, tok->link(), tok, exprVarIds, local, inInnerClass, depth); + if (result.type == Result::Type::BAILOUT || result.type == Result::Type::READ) + return result; + } + } + + if (Token::simpleMatch(tok, "else {")) + tok = tok->linkAt(1); + + if (Token::simpleMatch(tok, "asm (")) + return Result(Result::Type::BAILOUT); + + if (mWhat == What::ValueFlow && (Token::Match(tok, "while|for (") || Token::simpleMatch(tok, "do {"))) { + const Token *bodyStart = nullptr; + const Token *conditionStart = nullptr; + if (Token::simpleMatch(tok, "do {")) { + bodyStart = tok->next(); + if (Token::simpleMatch(bodyStart->link(), "} while (")) + conditionStart = bodyStart->link()->tokAt(2); + } else { + conditionStart = tok->next(); + if (Token::simpleMatch(conditionStart->link(), ") {")) + bodyStart = conditionStart->link()->next(); + } + + if (!bodyStart || !conditionStart) + return Result(Result::Type::BAILOUT); + + // Is expr changed in condition? + if (!isUnchanged(conditionStart, conditionStart->link(), exprVarIds, local)) + return Result(Result::Type::BAILOUT); + + // Is expr changed in loop body? + if (!isUnchanged(bodyStart, bodyStart->link(), exprVarIds, local)) + return Result(Result::Type::BAILOUT); + } + + if (mWhat == What::ValueFlow && Token::simpleMatch(tok, "if (") && Token::simpleMatch(tok->linkAt(1), ") {")) { + const Token *bodyStart = tok->linkAt(1)->next(); + const Token *conditionStart = tok->next(); + const Token *condTok = conditionStart->astOperand2(); + if (condTok->hasKnownIntValue()) { + const bool cond = condTok->values().front().intvalue; + if (cond) { + FwdAnalysis::Result result = checkRecursive(expr, bodyStart, bodyStart->link(), exprVarIds, local, true, depth); + if (result.type != Result::Type::NONE) + return result; + } else if (Token::simpleMatch(bodyStart->link(), "} else {")) { + bodyStart = bodyStart->link()->tokAt(2); + FwdAnalysis::Result result = checkRecursive(expr, bodyStart, bodyStart->link(), exprVarIds, local, true, depth); + if (result.type != Result::Type::NONE) + return result; + } + } + tok = bodyStart->link(); + if (isReturnScope(tok, mLibrary)) + return Result(Result::Type::BAILOUT); + if (Token::simpleMatch(tok, "} else {")) + tok = tok->linkAt(2); + if (!tok) + return Result(Result::Type::BAILOUT); + + // Is expr changed in condition? + if (!isUnchanged(conditionStart, conditionStart->link(), exprVarIds, local)) + return Result(Result::Type::BAILOUT); + + // Is expr changed in condition body? + if (!isUnchanged(bodyStart, bodyStart->link(), exprVarIds, local)) + return Result(Result::Type::BAILOUT); + } + + if (!local && Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) { + // TODO: this is a quick bailout + return Result(Result::Type::BAILOUT); + } + + if (mWhat == What::Reassign && + Token::simpleMatch(tok, ";") && + Token::simpleMatch(tok->astParent(), ";") && + Token::simpleMatch(tok->astParent()->astParent(), "(") && + Token::simpleMatch(tok->astParent()->astParent()->previous(), "for (") && + !isUnchanged(tok, tok->astParent()->astParent()->link(), exprVarIds, local)) + // TODO: This is a quick bailout to avoid FP #9420, there are false negatives (TODO_ASSERT_EQUALS) + return Result(Result::Type::BAILOUT); + + if (expr->isName() && Token::Match(tok, "%name% (") && tok->str().find('<') != std::string::npos && tok->str().find(expr->str()) != std::string::npos) + return Result(Result::Type::BAILOUT); + + if (exprVarIds.find(tok->varId()) != exprVarIds.end()) { + const Token *parent = tok; + bool other = false; + bool same = tok->astParent() && isSameExpression(false, expr, tok, mLibrary, true, false, nullptr); + while (!same && Token::Match(parent->astParent(), "*|.|::|[|(|%cop%")) { + parent = parent->astParent(); + if (parent->str() == "(" && !parent->isCast()) + break; + if (isSameExpression(false, expr, parent, mLibrary, true, false, nullptr)) { + same = true; + if (mWhat == What::ValueFlow) { + KnownAndToken v; + v.known = mValueFlowKnown; + v.token = parent; + mValueFlow.push_back(v); + } + } + if (Token::Match(parent, ". %var%") && parent->next()->varId() && exprVarIds.find(parent->next()->varId()) == exprVarIds.end() && + isSameExpression(false, expr->astOperand1(), parent->astOperand1(), mLibrary, true, false, nullptr)) { + other = true; + break; + } + } + if (mWhat != What::ValueFlow && same && Token::simpleMatch(parent->astParent(), "[") && parent == parent->astParent()->astOperand2()) { + return Result(Result::Type::READ); + } + if (other) + continue; + if (Token::simpleMatch(parent->astParent(), "=") && parent == parent->astParent()->astOperand1()) { + if (!local && hasFunctionCall(parent->astParent()->astOperand2())) { + // TODO: this is a quick bailout + return Result(Result::Type::BAILOUT); + } + if (hasOperand(parent->astParent()->astOperand2(), expr)) { + if (mWhat == What::Reassign) + return Result(Result::Type::READ); + continue; + } + const auto startEnd = parent->astParent()->astOperand2()->findExpressionStartEndTokens(); + for (const Token* tok2 = startEnd.first; tok2 != startEnd.second; tok2 = tok2->next()) { + if (tok2->tokType() == Token::eLambda) + return Result(Result::Type::BAILOUT); + // TODO: analyze usage in lambda + } + // ({ .. }) + if (hasGccCompoundStatement(parent->astParent()->astOperand2())) + return Result(Result::Type::BAILOUT); + // cppcheck-suppress shadowFunction - TODO: fix this + const bool reassign = isSameExpression(false, expr, parent, mLibrary, false, false, nullptr); + if (reassign) + return Result(Result::Type::WRITE, parent->astParent()); + return Result(Result::Type::READ); + } + if (mWhat == What::Reassign && parent->valueType() && parent->valueType()->pointer && Token::Match(parent->astParent(), "%assign%") && parent == parent->astParent()->astOperand1()) + return Result(Result::Type::READ); + + if (Token::Match(parent->astParent(), "%assign%") && !parent->astParent()->astParent() && parent == parent->astParent()->astOperand1()) { + if (mWhat == What::Reassign) + return Result(Result::Type::BAILOUT, parent->astParent()); + if (mWhat == What::UnusedValue && (!parent->valueType() || parent->valueType()->reference != Reference::None)) + return Result(Result::Type::BAILOUT, parent->astParent()); + continue; + } + if (mWhat == What::UnusedValue && parent->isUnaryOp("&") && Token::Match(parent->astParent(), "[,(]")) { + // Pass variable to function the writes it + const Token *ftok = parent->astParent(); + while (Token::simpleMatch(ftok, ",")) + ftok = ftok->astParent(); + if (ftok && Token::Match(ftok->previous(), "%name% (")) { + const std::vector args = getArguments(ftok); + int argnr = 0; + while (argnr < args.size() && args[argnr] != parent) + argnr++; + if (argnr < args.size()) { + const Library::Function* functionInfo = mLibrary.getFunction(ftok->astOperand1()); + if (functionInfo) { + const auto it = functionInfo->argumentChecks.find(argnr + 1); + if (it != functionInfo->argumentChecks.end() && it->second.direction == Library::ArgumentChecks::Direction::DIR_OUT) + continue; + } + } + } + return Result(Result::Type::BAILOUT, parent->astParent()); + } + // TODO: this is a quick bailout + return Result(Result::Type::BAILOUT, parent->astParent()); + } + + if (Token::Match(tok, ")|do {")) { + if (tok->str() == ")" && Token::simpleMatch(tok->link()->previous(), "switch (")) + // TODO: parse switch + return Result(Result::Type::BAILOUT); + const Result &result1 = checkRecursive(expr, tok->tokAt(2), tok->linkAt(1), exprVarIds, local, inInnerClass, depth); + if (result1.type == Result::Type::READ || result1.type == Result::Type::BAILOUT) + return result1; + if (mWhat == What::UnusedValue && result1.type == Result::Type::WRITE && expr->variable() && expr->variable()->isReference()) + return result1; + if (mWhat == What::ValueFlow && result1.type == Result::Type::WRITE) + mValueFlowKnown = false; + if (mWhat == What::Reassign && result1.type == Result::Type::BREAK) { + const Token *scopeEndToken = findNextTokenFromBreak(result1.token); + if (scopeEndToken) { + const Result &result2 = checkRecursive(expr, scopeEndToken->next(), endToken, exprVarIds, local, inInnerClass, depth); + if (result2.type == Result::Type::BAILOUT) + return result2; + } + } + if (Token::simpleMatch(tok->linkAt(1), "} else {")) { + const Token *elseStart = tok->linkAt(1)->tokAt(2); + const Result &result2 = checkRecursive(expr, elseStart, elseStart->link(), exprVarIds, local, inInnerClass, depth); + if (mWhat == What::ValueFlow && result2.type == Result::Type::WRITE) + mValueFlowKnown = false; + if (result2.type == Result::Type::READ || result2.type == Result::Type::BAILOUT) + return result2; + if (result1.type == Result::Type::WRITE && result2.type == Result::Type::WRITE) + return result1; + tok = elseStart->link(); + } else { + tok = tok->linkAt(1); + } + } + } + + return Result(Result::Type::NONE); +} + +std::set FwdAnalysis::getExprVarIds(const Token* expr, bool* localOut, bool* unknownVarIdOut) const +{ + // all variable ids in expr. + std::set exprVarIds; + bool local = true; + bool unknownVarId = false; + visitAstNodes(expr, + [&](const Token *tok) { + if (tok->str() == "[" && mWhat == What::UnusedValue) + return ChildrenToVisit::op1; + if (tok->varId() == 0 && tok->isName() && tok->previous()->str() != ".") { + // unknown variable + unknownVarId = true; + return ChildrenToVisit::none; + } + if (tok->varId() > 0) { + exprVarIds.insert(tok->varId()); + if (!Token::simpleMatch(tok->previous(), ".")) { + const Variable *var = tok->variable(); + if (var && var->isReference() && var->isLocal() && Token::Match(var->nameToken(), "%var% [=(]") && !isGlobalData(var->nameToken()->next()->astOperand2())) + return ChildrenToVisit::none; + const bool deref = tok->astParent() && (tok->astParent()->isUnaryOp("*") || (tok->astParent()->str() == "[" && tok == tok->astParent()->astOperand1())); + local &= !nonLocal(tok->variable(), deref); + } + } + return ChildrenToVisit::op1_and_op2; + }); + if (localOut) + *localOut = local; + if (unknownVarIdOut) + *unknownVarIdOut = unknownVarId; + return exprVarIds; +} + +FwdAnalysis::Result FwdAnalysis::check(const Token* expr, const Token* startToken, const Token* endToken) +{ + // all variable ids in expr. + bool local = true; + bool unknownVarId = false; + std::set exprVarIds = getExprVarIds(expr, &local, &unknownVarId); + + if (unknownVarId) + return Result(FwdAnalysis::Result::Type::BAILOUT); + + if (mWhat == What::Reassign && isGlobalData(expr)) + local = false; + + // In unused values checking we do not want to check assignments to + // global data. + if (mWhat == What::UnusedValue && isGlobalData(expr)) + return Result(FwdAnalysis::Result::Type::BAILOUT); + + Result result = checkRecursive(expr, startToken, endToken, exprVarIds, local, false); + + // Break => continue checking in outer scope + while (mWhat!=What::ValueFlow && result.type == FwdAnalysis::Result::Type::BREAK) { + const Token *scopeEndToken = findNextTokenFromBreak(result.token); + if (!scopeEndToken) + break; + result = checkRecursive(expr, scopeEndToken->next(), endToken, exprVarIds, local, false); + } + + return result; +} + +bool FwdAnalysis::hasOperand(const Token *tok, const Token *lhs) const +{ + if (!tok) + return false; + if (isSameExpression(false, tok, lhs, mLibrary, false, false, nullptr)) + return true; + return hasOperand(tok->astOperand1(), lhs) || hasOperand(tok->astOperand2(), lhs); +} + +const Token *FwdAnalysis::reassign(const Token *expr, const Token *startToken, const Token *endToken) +{ + if (hasVolatileCastOrVar(expr)) + return nullptr; + mWhat = What::Reassign; + Result result = check(expr, startToken, endToken); + return result.type == FwdAnalysis::Result::Type::WRITE ? result.token : nullptr; +} + +bool FwdAnalysis::unusedValue(const Token *expr, const Token *startToken, const Token *endToken) +{ + if (isEscapedAlias(expr)) + return false; + if (hasVolatileCastOrVar(expr)) + return false; + if (Token::simpleMatch(expr, "[") && astIsContainerView(expr->astOperand1())) + return false; + mWhat = What::UnusedValue; + Result result = check(expr, startToken, endToken); + return (result.type == FwdAnalysis::Result::Type::NONE || result.type == FwdAnalysis::Result::Type::RETURN) && !possiblyAliased(expr, startToken); +} + +bool FwdAnalysis::possiblyAliased(const Token *expr, const Token *startToken) const +{ + if (expr->isUnaryOp("*") && !expr->astOperand1()->isUnaryOp("&")) + return true; + if (Token::simpleMatch(expr, ". *")) + return true; + + const bool macro = false; + const bool pure = false; + const bool followVar = false; + for (const Token *tok = startToken; tok; tok = tok->previous()) { + + if (Token::Match(tok, "%name% (") && !Token::Match(tok, "if|while|for")) { + // Is argument passed by reference? + const std::vector args = getArguments(tok); + for (int argnr = 0; argnr < args.size(); ++argnr) { + if (!Token::Match(args[argnr], "%name%|.|::")) + continue; + if (tok->function() && tok->function()->getArgumentVar(argnr) && !tok->function()->getArgumentVar(argnr)->isReference() && !tok->function()->isConst()) + continue; + for (const Token *subexpr = expr; subexpr; subexpr = subexpr->astOperand1()) { + if (isSameExpression(macro, subexpr, args[argnr], mLibrary, pure, followVar)) { + const Scope* scope = expr->scope(); // if there is no other variable, assume no aliasing + if (scope->varlist.size() > 1) + return true; + } + } + } + continue; + } + + const Token *addrOf = nullptr; + if (Token::Match(tok, "& %name% =")) + addrOf = tok->tokAt(2)->astOperand2(); + else if (tok->isUnaryOp("&")) + addrOf = tok->astOperand1(); + else if (Token::simpleMatch(tok, "std :: ref (")) + addrOf = tok->tokAt(3)->astOperand2(); + else if (tok->valueType() && tok->valueType()->pointer && + (Token::Match(tok, "%var% = %var% ;") || Token::Match(tok, "%var% {|( %var% }|)")) && + Token::Match(expr->previous(), "%varid% [", tok->tokAt(2)->varId())) + addrOf = tok->tokAt(2); + else + continue; + + for (const Token *subexpr = expr; subexpr; subexpr = subexpr->astOperand1()) { + if (subexpr != addrOf && isSameExpression(macro, subexpr, addrOf, mLibrary, pure, followVar)) + return true; + } + } + return false; +} + +bool FwdAnalysis::isEscapedAlias(const Token* expr) +{ + for (const Token *subexpr = expr; subexpr; subexpr = subexpr->astOperand1()) { + for (const ValueFlow::Value &val : subexpr->values()) { + if (!val.isLocalLifetimeValue()) + continue; + const Variable* var = val.tokvalue->variable(); + if (!var) + continue; + if (!var->isLocal()) + return true; + if (var->isArgument()) + return true; + + } + } + return false; +} diff --git a/cppcheck-2.14.0/lib/fwdanalysis.h b/cppcheck-2.14.0/lib/fwdanalysis.h new file mode 100644 index 00000000..61dbe1c0 --- /dev/null +++ b/cppcheck-2.14.0/lib/fwdanalysis.h @@ -0,0 +1,91 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef fwdanalysisH +#define fwdanalysisH +//--------------------------------------------------------------------------- + +#include "config.h" + +#include +#include + +class Token; +class Library; + +/** + * Forward data flow analysis for checks + * - unused value + * - redundant assignment + * - valueflow analysis + */ +class FwdAnalysis { +public: + explicit FwdAnalysis(const Library &library) : mLibrary(library) {} + + bool hasOperand(const Token *tok, const Token *lhs) const; + + /** + * Check if "expr" is reassigned. The "expr" can be a tree (x.y[12]). + * @param expr Symbolic expression to perform forward analysis for + * @param startToken First token in forward analysis + * @param endToken Last token in forward analysis + * @return Token where expr is reassigned. If it's not reassigned then nullptr is returned. + */ + const Token *reassign(const Token *expr, const Token *startToken, const Token *endToken); + + /** + * Check if "expr" is used. The "expr" can be a tree (x.y[12]). + * @param expr Symbolic expression to perform forward analysis for + * @param startToken First token in forward analysis + * @param endToken Last token in forward analysis + * @return true if expr is used. + */ + bool unusedValue(const Token *expr, const Token *startToken, const Token *endToken); + + struct KnownAndToken { + bool known{}; + const Token* token{}; + }; + + /** Is there some possible alias for given expression */ + bool possiblyAliased(const Token *expr, const Token *startToken) const; + + std::set getExprVarIds(const Token* expr, bool* localOut = nullptr, bool* unknownVarIdOut = nullptr) const; +private: + static bool isEscapedAlias(const Token* expr); + + /** Result of forward analysis */ + struct Result { + enum class Type { NONE, READ, WRITE, BREAK, RETURN, BAILOUT } type; + explicit Result(Type type) : type(type) {} + Result(Type type, const Token *token) : type(type), token(token) {} + const Token* token{}; + }; + + Result check(const Token *expr, const Token *startToken, const Token *endToken); + Result checkRecursive(const Token *expr, const Token *startToken, const Token *endToken, const std::set &exprVarIds, bool local, bool inInnerClass, int depth=0); + + const Library &mLibrary; + enum class What { Reassign, UnusedValue, ValueFlow } mWhat = What::Reassign; + std::vector mValueFlow; + bool mValueFlowKnown = true; +}; + +#endif // fwdanalysisH diff --git a/cppcheck-2.14.0/lib/importproject.cpp b/cppcheck-2.14.0/lib/importproject.cpp new file mode 100644 index 00000000..47c55e09 --- /dev/null +++ b/cppcheck-2.14.0/lib/importproject.cpp @@ -0,0 +1,1357 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "importproject.h" + +#include "path.h" +#include "settings.h" +#include "standards.h" +#include "suppressions.h" +#include "token.h" +#include "tokenlist.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xml.h" + +#include "json.h" + +// TODO: align the exclusion logic with PathMatch +void ImportProject::ignorePaths(const std::vector &ipaths) +{ + for (std::list::iterator it = fileSettings.begin(); it != fileSettings.end();) { + bool ignore = false; + for (std::string i : ipaths) { + if (it->filename.size() > i.size() && it->filename.compare(0,i.size(),i)==0) { + ignore = true; + break; + } + if (isValidGlobPattern(i) && matchglob(i, it->filename)) { + ignore = true; + break; + } + if (!Path::isAbsolute(i)) { + i = mPath + i; + if (it->filename.size() > i.size() && it->filename.compare(0,i.size(),i)==0) { + ignore = true; + break; + } + } + } + if (ignore) + it = fileSettings.erase(it); + else + ++it; + } +} + +void ImportProject::ignoreOtherConfigs(const std::string &cfg) +{ + for (std::list::iterator it = fileSettings.begin(); it != fileSettings.end();) { + if (it->cfg != cfg) + it = fileSettings.erase(it); + else + ++it; + } +} + +void ImportProject::fsSetDefines(FileSettings& fs, std::string defs) +{ + while (defs.find(";%(") != std::string::npos) { + const std::string::size_type pos1 = defs.find(";%("); + const std::string::size_type pos2 = defs.find(';', pos1+1); + defs.erase(pos1, pos2 == std::string::npos ? pos2 : (pos2-pos1)); + } + while (defs.find(";;") != std::string::npos) + defs.erase(defs.find(";;"),1); + while (!defs.empty() && defs[0] == ';') + defs.erase(0, 1); + while (!defs.empty() && endsWith(defs,';')) + defs.erase(defs.size() - 1U); // TODO: Use std::string::pop_back() as soon as travis supports it + bool eq = false; + for (std::size_t pos = 0; pos < defs.size(); ++pos) { + if (defs[pos] == '(' || defs[pos] == '=') + eq = true; + else if (defs[pos] == ';') { + if (!eq) { + defs.insert(pos,"=1"); + pos += 3; + } + if (pos < defs.size()) + eq = false; + } + } + if (!eq && !defs.empty()) + defs += "=1"; + fs.defines.swap(defs); +} + +static bool simplifyPathWithVariables(std::string &s, std::map &variables) +{ + std::set expanded; + std::string::size_type start = 0; + while ((start = s.find("$(")) != std::string::npos) { + const std::string::size_type end = s.find(')',start); + if (end == std::string::npos) + break; + const std::string var = s.substr(start+2,end-start-2); + if (expanded.find(var) != expanded.end()) + break; + expanded.insert(var); + std::map::const_iterator it1 = variables.find(var); + // variable was not found within defined variables + if (it1 == variables.end()) { + const char *envValue = std::getenv(var.c_str()); + if (!envValue) { + //! \todo generate a debug/info message about undefined variable + break; + } + variables[var] = std::string(envValue); + it1 = variables.find(var); + } + s.replace(start, end - start + 1, it1->second); + } + if (s.find("$(") != std::string::npos) + return false; + s = Path::simplifyPath(Path::fromNativeSeparators(std::move(s))); + return true; +} + +void ImportProject::fsSetIncludePaths(FileSettings& fs, const std::string &basepath, const std::list &in, std::map &variables) +{ + std::set found; + // NOLINTNEXTLINE(performance-unnecessary-copy-initialization) + const std::list copyIn(in); + fs.includePaths.clear(); + for (const std::string &ipath : copyIn) { + if (ipath.empty()) + continue; + if (startsWith(ipath,"%(")) + continue; + std::string s(Path::fromNativeSeparators(ipath)); + if (!found.insert(s).second) + continue; + if (s[0] == '/' || (s.size() > 1U && s.compare(1,2,":/") == 0)) { + if (!endsWith(s,'/')) + s += '/'; + fs.includePaths.push_back(std::move(s)); + continue; + } + + if (endsWith(s,'/')) // this is a temporary hack, simplifyPath can crash if path ends with '/' + s.erase(s.size() - 1U); // TODO: Use std::string::pop_back() as soon as travis supports it + + if (s.find("$(") == std::string::npos) { + s = Path::simplifyPath(basepath + s); + } else { + if (!simplifyPathWithVariables(s, variables)) + continue; + } + if (s.empty()) + continue; + fs.includePaths.push_back(s + '/'); + } +} + +ImportProject::Type ImportProject::import(const std::string &filename, Settings *settings) +{ + std::ifstream fin(filename); + if (!fin.is_open()) + return ImportProject::Type::MISSING; + + mPath = Path::getPathFromFilename(Path::fromNativeSeparators(filename)); + if (!mPath.empty() && !endsWith(mPath,'/')) + mPath += '/'; + + const std::vector fileFilters = + settings ? settings->fileFilters : std::vector(); + + if (endsWith(filename, ".json")) { + if (importCompileCommands(fin)) { + setRelativePaths(filename); + return ImportProject::Type::COMPILE_DB; + } + } else if (endsWith(filename, ".sln")) { + if (importSln(fin, mPath, fileFilters)) { + setRelativePaths(filename); + return ImportProject::Type::VS_SLN; + } + } else if (endsWith(filename, ".vcxproj")) { + std::map variables; + if (importVcxproj(filename, variables, emptyString, fileFilters)) { + setRelativePaths(filename); + return ImportProject::Type::VS_VCXPROJ; + } + } else if (endsWith(filename, ".bpr")) { + if (importBcb6Prj(filename)) { + setRelativePaths(filename); + return ImportProject::Type::BORLAND; + } + } else if (settings && endsWith(filename, ".cppcheck")) { + if (importCppcheckGuiProject(fin, settings)) { + setRelativePaths(filename); + return ImportProject::Type::CPPCHECK_GUI; + } + } else { + return ImportProject::Type::UNKNOWN; + } + return ImportProject::Type::FAILURE; +} + +static std::string readUntil(const std::string &command, std::string::size_type *pos, const char until[]) +{ + std::string ret; + bool escapedString = false; + bool str = false; + bool escape = false; + for (; *pos < command.size() && (str || !std::strchr(until, command[*pos])); (*pos)++) { + if (escape) + escape = false; + else if (command[*pos] == '\\') { + if (str) + escape = true; + else if (command[*pos + 1] == '"') { + if (escapedString) + return ret + "\\\""; + escapedString = true; + ret += "\\\""; + (*pos)++; + continue; + } + } else if (command[*pos] == '\"') + str = !str; + ret += command[*pos]; + } + return ret; +} + +static std::string unescape(const std::string &in) +{ + std::string out; + bool escape = false; + for (const char c: in) { + if (escape) { + escape = false; + if (!std::strchr("\\\"\'",c)) + out += "\\"; + out += c; + } else if (c == '\\') + escape = true; + else + out += c; + } + return out; +} + +void ImportProject::fsParseCommand(FileSettings& fs, const std::string& command) +{ + std::string defs; + + // Parse command.. + std::string::size_type pos = 0; + while (std::string::npos != (pos = command.find(' ',pos))) { + while (pos < command.size() && command[pos] == ' ') + pos++; + if (pos >= command.size()) + break; + if (command[pos] != '/' && command[pos] != '-') + continue; + pos++; + if (pos >= command.size()) + break; + const char F = command[pos++]; + if (std::strchr("DUI", F)) { + while (pos < command.size() && command[pos] == ' ') + ++pos; + } + const std::string fval = readUntil(command, &pos, " ="); + if (F=='D') { + std::string defval = readUntil(command, &pos, " "); + defs += fval; + if (defval.size() >= 3 && startsWith(defval,"=\"") && defval.back()=='\"') + defval = "=" + unescape(defval.substr(2, defval.size() - 3)); + else if (defval.size() >= 5 && startsWith(defval, "=\\\"") && endsWith(defval, "\\\"")) + defval = "=\"" + unescape(defval.substr(3, defval.size() - 5)) + "\""; + if (!defval.empty()) + defs += defval; + defs += ';'; + } else if (F=='U') + fs.undefs.insert(fval); + else if (F=='I') { + std::string i = fval; + if (i.size() > 1 && i[0] == '\"' && i.back() == '\"') + i = unescape(i.substr(1, i.size() - 2)); + if (std::find(fs.includePaths.cbegin(), fs.includePaths.cend(), i) == fs.includePaths.cend()) + fs.includePaths.push_back(std::move(i)); + } else if (F=='s' && startsWith(fval,"td")) { + ++pos; + fs.standard = readUntil(command, &pos, " "); + } else if (F == 'i' && fval == "system") { + ++pos; + std::string isystem = readUntil(command, &pos, " "); + fs.systemIncludePaths.push_back(std::move(isystem)); + } else if (F=='m') { + if (fval == "unicode") { + defs += "UNICODE"; + defs += ";"; + } + } else if (F=='f') { + if (fval == "pic") { + defs += "__pic__"; + defs += ";"; + } else if (fval == "PIC") { + defs += "__PIC__"; + defs += ";"; + } else if (fval == "pie") { + defs += "__pie__"; + defs += ";"; + } else if (fval == "PIE") { + defs += "__PIE__"; + defs += ";"; + } + // TODO: support -fsigned-char and -funsigned-char? + // we can only set it globally but in this context it needs to be treated per file + } + } + fsSetDefines(fs, std::move(defs)); +} + +bool ImportProject::importCompileCommands(std::istream &istr) +{ + picojson::value compileCommands; + istr >> compileCommands; + if (!compileCommands.is()) { + printError("compilation database is not a JSON array"); + return false; + } + + for (const picojson::value &fileInfo : compileCommands.get()) { + picojson::object obj = fileInfo.get(); + std::string dirpath = Path::fromNativeSeparators(obj["directory"].get()); + + /* CMAKE produces the directory without trailing / so add it if not + * there - it is needed by setIncludePaths() */ + if (!endsWith(dirpath, '/')) + dirpath += '/'; + + const std::string directory = std::move(dirpath); + + std::string command; + if (obj.count("arguments")) { + if (obj["arguments"].is()) { + for (const picojson::value& arg : obj["arguments"].get()) { + if (arg.is()) { + std::string str = arg.get(); + if (str.find(' ') != std::string::npos) + str = "\"" + str + "\""; + command += str + " "; + } + } + } else { + printError("'arguments' field in compilation database entry is not a JSON array"); + return false; + } + } else if (obj.count("command")) { + if (obj["command"].is()) { + command = obj["command"].get(); + } else { + printError("'command' field in compilation database entry is not a string"); + return false; + } + } else { + printError("no 'arguments' or 'command' field found in compilation database entry"); + return false; + } + + if (!obj.count("file") || !obj["file"].is()) { + printError("skip compilation database entry because it does not have a proper 'file' field"); + continue; + } + + const std::string file = Path::fromNativeSeparators(obj["file"].get()); + + // Accept file? + if (!Path::acceptFile(file)) + continue; + + FileSettings fs; + if (Path::isAbsolute(file)) + fs.filename = Path::simplifyPath(file); +#ifdef _WIN32 + else if (file[0] == '/' && directory.size() > 2 && std::isalpha(directory[0]) && directory[1] == ':') + // directory: C:\foo\bar + // file: /xy/z.c + // => c:/xy/z.c + fs.filename = Path::simplifyPath(directory.substr(0,2) + file); +#endif + else + fs.filename = Path::simplifyPath(directory + file); + if (!sourceFileExists(fs.filename)) { + printError("'" + fs.filename + "' from compilation database does not exist"); + return false; + } + fsParseCommand(fs, command); // read settings; -D, -I, -U, -std, -m*, -f* + std::map variables; + fsSetIncludePaths(fs, directory, fs.includePaths, variables); + fileSettings.push_back(std::move(fs)); + } + + return true; +} + +bool ImportProject::importSln(std::istream &istr, const std::string &path, const std::vector &fileFilters) +{ + std::string line; + + if (!std::getline(istr,line)) { + printError("Visual Studio solution file is empty"); + return false; + } + + if (!startsWith(line, "Microsoft Visual Studio Solution File")) { + // Skip BOM + if (!std::getline(istr, line) || !startsWith(line, "Microsoft Visual Studio Solution File")) { + printError("Visual Studio solution file header not found"); + return false; + } + } + + std::map variables; + variables["SolutionDir"] = path; + + bool found = false; + + while (std::getline(istr,line)) { + if (!startsWith(line,"Project(")) + continue; + const std::string::size_type pos = line.find(".vcxproj"); + if (pos == std::string::npos) + continue; + const std::string::size_type pos1 = line.rfind('\"',pos); + if (pos1 == std::string::npos) + continue; + std::string vcxproj(line.substr(pos1+1, pos-pos1+7)); + vcxproj = Path::toNativeSeparators(std::move(vcxproj)); + if (!Path::isAbsolute(vcxproj)) + vcxproj = path + vcxproj; + vcxproj = Path::fromNativeSeparators(std::move(vcxproj)); + if (!importVcxproj(vcxproj, variables, emptyString, fileFilters)) { + printError("failed to load '" + vcxproj + "' from Visual Studio solution"); + return false; + } + found = true; + } + + if (!found) { + printError("no projects found in Visual Studio solution file"); + return false; + } + + return true; +} + +namespace { + struct ProjectConfiguration { + explicit ProjectConfiguration(const tinyxml2::XMLElement *cfg) { + const char *a = cfg->Attribute("Include"); + if (a) + name = a; + for (const tinyxml2::XMLElement *e = cfg->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (!e->GetText()) + continue; + if (std::strcmp(e->Name(),"Configuration")==0) + configuration = e->GetText(); + else if (std::strcmp(e->Name(),"Platform")==0) { + platformStr = e->GetText(); + if (platformStr == "Win32") + platform = Win32; + else if (platformStr == "x64") + platform = x64; + else + platform = Unknown; + } + } + } + std::string name; + std::string configuration; + enum { Win32, x64, Unknown } platform = Unknown; + std::string platformStr; + }; + + struct ItemDefinitionGroup { + explicit ItemDefinitionGroup(const tinyxml2::XMLElement *idg, std::string includePaths) : additionalIncludePaths(std::move(includePaths)) { + const char *condAttr = idg->Attribute("Condition"); + if (condAttr) + condition = condAttr; + for (const tinyxml2::XMLElement *e1 = idg->FirstChildElement(); e1; e1 = e1->NextSiblingElement()) { + if (std::strcmp(e1->Name(), "ClCompile") == 0) { + enhancedInstructionSet = "StreamingSIMDExtensions2"; + for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (e->GetText()) { + if (std::strcmp(e->Name(), "PreprocessorDefinitions") == 0) + preprocessorDefinitions = e->GetText(); + else if (std::strcmp(e->Name(), "AdditionalIncludeDirectories") == 0) { + if (!additionalIncludePaths.empty()) + additionalIncludePaths += ';'; + additionalIncludePaths += e->GetText(); + } else if (std::strcmp(e->Name(), "LanguageStandard") == 0) { + if (std::strcmp(e->GetText(), "stdcpp14") == 0) + cppstd = Standards::CPP14; + else if (std::strcmp(e->GetText(), "stdcpp17") == 0) + cppstd = Standards::CPP17; + else if (std::strcmp(e->GetText(), "stdcpp20") == 0) + cppstd = Standards::CPP20; + else if (std::strcmp(e->GetText(), "stdcpplatest") == 0) + cppstd = Standards::CPPLatest; + } else if (std::strcmp(e->Name(), "EnableEnhancedInstructionSet") == 0) { + enhancedInstructionSet = e->GetText(); + } + } + } + } + else if (std::strcmp(e1->Name(), "Link") == 0) { + for (const tinyxml2::XMLElement *e = e1->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (!e->GetText()) + continue; + if (std::strcmp(e->Name(), "EntryPointSymbol") == 0) { + entryPointSymbol = e->GetText(); + } + } + } + } + } + + static void replaceAll(std::string &c, const std::string &from, const std::string &to) { + std::string::size_type pos; + while ((pos = c.find(from)) != std::string::npos) { + c.erase(pos,from.size()); + c.insert(pos,to); + } + } + + // see https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-conditions + // properties are .NET String objects and you can call any of its members on them + bool conditionIsTrue(const ProjectConfiguration &p) const { + if (condition.empty()) + return true; + std::string c = '(' + condition + ");"; + replaceAll(c, "$(Configuration)", p.configuration); + replaceAll(c, "$(Platform)", p.platformStr); + + // TODO: improve evaluation + const Settings s; + TokenList tokenlist(&s); + std::istringstream istr(c); + tokenlist.createTokens(istr, Standards::Language::C); // TODO: check result + for (const Token *tok = tokenlist.front(); tok; tok = tok->next()) { + if (tok->str() == "(" && tok->astOperand1() && tok->astOperand2()) { + // TODO: this is wrong - it is Contains() not Equals() + if (tok->astOperand1()->expressionString() == "Configuration.Contains") + return ('\'' + p.configuration + '\'') == tok->astOperand2()->str(); + } + if (tok->str() == "==" && tok->astOperand1() && tok->astOperand2() && tok->astOperand1()->str() == tok->astOperand2()->str()) + return true; + } + return false; + } + std::string condition; + std::string enhancedInstructionSet; + std::string preprocessorDefinitions; + std::string additionalIncludePaths; + std::string entryPointSymbol; // TODO: use this + Standards::cppstd_t cppstd = Standards::CPPLatest; + }; +} + +static std::list toStringList(const std::string &s) +{ + std::list ret; + std::string::size_type pos1 = 0; + std::string::size_type pos2; + while ((pos2 = s.find(';',pos1)) != std::string::npos) { + ret.push_back(s.substr(pos1, pos2-pos1)); + pos1 = pos2 + 1; + if (pos1 >= s.size()) + break; + } + if (pos1 < s.size()) + ret.push_back(s.substr(pos1)); + return ret; +} + +static void importPropertyGroup(const tinyxml2::XMLElement *node, std::map &variables, std::string &includePath, bool *useOfMfc) +{ + if (useOfMfc) { + for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (std::strcmp(e->Name(), "UseOfMfc") == 0) { + *useOfMfc = true; + break; + } + } + } + + const char* labelAttribute = node->Attribute("Label"); + if (labelAttribute && std::strcmp(labelAttribute, "UserMacros") == 0) { + for (const tinyxml2::XMLElement *propertyGroup = node->FirstChildElement(); propertyGroup; propertyGroup = propertyGroup->NextSiblingElement()) { + const std::string name(propertyGroup->Name()); + const char *text = propertyGroup->GetText(); + variables[name] = std::string(text ? text : ""); + } + + } else if (!labelAttribute) { + for (const tinyxml2::XMLElement *propertyGroup = node->FirstChildElement(); propertyGroup; propertyGroup = propertyGroup->NextSiblingElement()) { + if (std::strcmp(propertyGroup->Name(), "IncludePath") != 0) + continue; + const char *text = propertyGroup->GetText(); + if (!text) + continue; + std::string path(text); + const std::string::size_type pos = path.find("$(IncludePath)"); + if (pos != std::string::npos) + path.replace(pos, 14U, includePath); + includePath = std::move(path); + } + } +} + +static void loadVisualStudioProperties(const std::string &props, std::map &variables, std::string &includePath, const std::string &additionalIncludeDirectories, std::list &itemDefinitionGroupList) +{ + std::string filename(props); + // variables can't be resolved + if (!simplifyPathWithVariables(filename, variables)) + return; + + // prepend project dir (if it exists) to transform relative paths into absolute ones + if (!Path::isAbsolute(filename) && variables.count("ProjectDir") > 0) + filename = Path::getAbsoluteFilePath(variables.at("ProjectDir") + filename); + + tinyxml2::XMLDocument doc; + if (doc.LoadFile(filename.c_str()) != tinyxml2::XML_SUCCESS) + return; + const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); + if (rootnode == nullptr) + return; + for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { + if (std::strcmp(node->Name(), "ImportGroup") == 0) { + const char *labelAttribute = node->Attribute("Label"); + if (labelAttribute == nullptr || std::strcmp(labelAttribute, "PropertySheets") != 0) + continue; + for (const tinyxml2::XMLElement *importGroup = node->FirstChildElement(); importGroup; importGroup = importGroup->NextSiblingElement()) { + if (std::strcmp(importGroup->Name(), "Import") == 0) { + const char *projectAttribute = importGroup->Attribute("Project"); + if (projectAttribute == nullptr) + continue; + std::string loadprj(projectAttribute); + if (loadprj.find('$') == std::string::npos) { + loadprj = Path::getPathFromFilename(filename) + loadprj; + } + loadVisualStudioProperties(loadprj, variables, includePath, additionalIncludeDirectories, itemDefinitionGroupList); + } + } + } else if (std::strcmp(node->Name(),"PropertyGroup")==0) { + importPropertyGroup(node, variables, includePath, nullptr); + } else if (std::strcmp(node->Name(),"ItemDefinitionGroup")==0) { + itemDefinitionGroupList.emplace_back(node, additionalIncludeDirectories); + } + } +} + +bool ImportProject::importVcxproj(const std::string &filename, std::map &variables, const std::string &additionalIncludeDirectories, const std::vector &fileFilters) +{ + variables["ProjectDir"] = Path::simplifyPath(Path::getPathFromFilename(filename)); + + std::list projectConfigurationList; + std::list compileList; + std::list itemDefinitionGroupList; + std::string includePath; + + bool useOfMfc = false; + + tinyxml2::XMLDocument doc; + const tinyxml2::XMLError error = doc.LoadFile(filename.c_str()); + if (error != tinyxml2::XML_SUCCESS) { + printError(std::string("Visual Studio project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error)); + return false; + } + const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); + if (rootnode == nullptr) { + printError("Visual Studio project file has no XML root node"); + return false; + } + for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { + if (std::strcmp(node->Name(), "ItemGroup") == 0) { + const char *labelAttribute = node->Attribute("Label"); + if (labelAttribute && std::strcmp(labelAttribute, "ProjectConfigurations") == 0) { + for (const tinyxml2::XMLElement *cfg = node->FirstChildElement(); cfg; cfg = cfg->NextSiblingElement()) { + if (std::strcmp(cfg->Name(), "ProjectConfiguration") == 0) { + const ProjectConfiguration p(cfg); + if (p.platform != ProjectConfiguration::Unknown) { + projectConfigurationList.emplace_back(cfg); + mAllVSConfigs.insert(p.configuration); + } + } + } + } else { + for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (std::strcmp(e->Name(), "ClCompile") == 0) { + const char *include = e->Attribute("Include"); + if (include && Path::acceptFile(include)) + compileList.emplace_back(include); + } + } + } + } else if (std::strcmp(node->Name(), "ItemDefinitionGroup") == 0) { + itemDefinitionGroupList.emplace_back(node, additionalIncludeDirectories); + } else if (std::strcmp(node->Name(), "PropertyGroup") == 0) { + importPropertyGroup(node, variables, includePath, &useOfMfc); + } else if (std::strcmp(node->Name(), "ImportGroup") == 0) { + const char *labelAttribute = node->Attribute("Label"); + if (labelAttribute && std::strcmp(labelAttribute, "PropertySheets") == 0) { + for (const tinyxml2::XMLElement *e = node->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (std::strcmp(e->Name(), "Import") == 0) { + const char *projectAttribute = e->Attribute("Project"); + if (projectAttribute) + loadVisualStudioProperties(projectAttribute, variables, includePath, additionalIncludeDirectories, itemDefinitionGroupList); + } + } + } + } + } + // # TODO: support signedness of char via /J (and potential XML option for it)? + // we can only set it globally but in this context it needs to be treated per file + + for (const std::string &c : compileList) { + const std::string cfilename = Path::simplifyPath(Path::isAbsolute(c) ? c : Path::getPathFromFilename(filename) + c); + if (!fileFilters.empty() && !matchglobs(fileFilters, cfilename)) + continue; + + for (const ProjectConfiguration &p : projectConfigurationList) { + + if (!guiProject.checkVsConfigs.empty()) { + const bool doChecking = std::any_of(guiProject.checkVsConfigs.cbegin(), guiProject.checkVsConfigs.cend(), [&](const std::string& c) { + return c == p.configuration; + }); + if (!doChecking) + continue; + } + + FileSettings fs; + fs.filename = cfilename; + fs.cfg = p.name; + // TODO: detect actual MSC version + fs.msc = true; + fs.useMfc = useOfMfc; + fs.defines = "_WIN32=1"; + if (p.platform == ProjectConfiguration::Win32) + fs.platformType = Platform::Type::Win32W; + else if (p.platform == ProjectConfiguration::x64) { + fs.platformType = Platform::Type::Win64; + fs.defines += ";_WIN64=1"; + } + std::string additionalIncludePaths; + for (const ItemDefinitionGroup &i : itemDefinitionGroupList) { + if (!i.conditionIsTrue(p)) + continue; + fs.standard = Standards::getCPP(i.cppstd); + fs.defines += ';' + i.preprocessorDefinitions; + if (i.enhancedInstructionSet == "StreamingSIMDExtensions") + fs.defines += ";__SSE__"; + else if (i.enhancedInstructionSet == "StreamingSIMDExtensions2") + fs.defines += ";__SSE2__"; + else if (i.enhancedInstructionSet == "AdvancedVectorExtensions") + fs.defines += ";__AVX__"; + else if (i.enhancedInstructionSet == "AdvancedVectorExtensions2") + fs.defines += ";__AVX2__"; + else if (i.enhancedInstructionSet == "AdvancedVectorExtensions512") + fs.defines += ";__AVX512__"; + additionalIncludePaths += ';' + i.additionalIncludePaths; + } + fsSetDefines(fs, fs.defines); + fsSetIncludePaths(fs, Path::getPathFromFilename(filename), toStringList(includePath + ';' + additionalIncludePaths), variables); + fileSettings.push_back(std::move(fs)); + } + } + + return true; +} + +bool ImportProject::importBcb6Prj(const std::string &projectFilename) +{ + tinyxml2::XMLDocument doc; + const tinyxml2::XMLError error = doc.LoadFile(projectFilename.c_str()); + if (error != tinyxml2::XML_SUCCESS) { + printError(std::string("Borland project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error)); + return false; + } + const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); + if (rootnode == nullptr) { + printError("Borland project file has no XML root node"); + return false; + } + + const std::string& projectDir = Path::simplifyPath(Path::getPathFromFilename(projectFilename)); + + std::list compileList; + std::string includePath; + std::string userdefines; + std::string sysdefines; + std::string cflag1; + + for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { + if (std::strcmp(node->Name(), "FILELIST") == 0) { + for (const tinyxml2::XMLElement *f = node->FirstChildElement(); f; f = f->NextSiblingElement()) { + if (std::strcmp(f->Name(), "FILE") == 0) { + const char *filename = f->Attribute("FILENAME"); + if (filename && Path::acceptFile(filename)) + compileList.emplace_back(filename); + } + } + } else if (std::strcmp(node->Name(), "MACROS") == 0) { + for (const tinyxml2::XMLElement *m = node->FirstChildElement(); m; m = m->NextSiblingElement()) { + if (std::strcmp(m->Name(), "INCLUDEPATH") == 0) { + const char *v = m->Attribute("value"); + if (v) + includePath = v; + } else if (std::strcmp(m->Name(), "USERDEFINES") == 0) { + const char *v = m->Attribute("value"); + if (v) + userdefines = v; + } else if (std::strcmp(m->Name(), "SYSDEFINES") == 0) { + const char *v = m->Attribute("value"); + if (v) + sysdefines = v; + } + } + } else if (std::strcmp(node->Name(), "OPTIONS") == 0) { + for (const tinyxml2::XMLElement *m = node->FirstChildElement(); m; m = m->NextSiblingElement()) { + if (std::strcmp(m->Name(), "CFLAG1") == 0) { + const char *v = m->Attribute("value"); + if (v) + cflag1 = v; + } + } + } + } + + std::set cflags; + + // parse cflag1 and fill the cflags set + { + std::string arg; + + for (const char i : cflag1) { + if (i == ' ' && !arg.empty()) { + cflags.insert(arg); + arg.clear(); + continue; + } + arg += i; + } + + if (!arg.empty()) { + cflags.insert(arg); + } + + // cleanup: -t is "An alternate name for the -Wxxx switches; there is no difference" + // -> Remove every known -txxx argument and replace it with its -Wxxx counterpart. + // This way, we know what we have to check for later on. + static const std::map synonyms = { + { "-tC","-WC" }, + { "-tCDR","-WCDR" }, + { "-tCDV","-WCDV" }, + { "-tW","-W" }, + { "-tWC","-WC" }, + { "-tWCDR","-WCDR" }, + { "-tWCDV","-WCDV" }, + { "-tWD","-WD" }, + { "-tWDR","-WDR" }, + { "-tWDV","-WDV" }, + { "-tWM","-WM" }, + { "-tWP","-WP" }, + { "-tWR","-WR" }, + { "-tWU","-WU" }, + { "-tWV","-WV" } + }; + + for (std::map::const_iterator i = synonyms.cbegin(); i != synonyms.cend(); ++i) { + if (cflags.erase(i->first) > 0) { + cflags.insert(i->second); + } + } + } + + std::string predefines; + std::string cppPredefines; + + // Collecting predefines. See BCB6 help topic "Predefined macros" + { + cppPredefines += + // Defined if you've selected C++ compilation; will increase in later releases. + // value 0x0560 (but 0x0564 for our BCB6 SP4) + // @see http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Predefined_Macros#C.2B.2B_Compiler_Versions_in_Predefined_Macros + ";__BCPLUSPLUS__=0x0560" + + // Defined if in C++ mode; otherwise, undefined. + ";__cplusplus=1" + + // Defined as 1 for C++ files(meaning that templates are supported); otherwise, it is undefined. + ";__TEMPLATES__=1" + + // Defined only for C++ programs to indicate that wchar_t is an intrinsically defined data type. + ";_WCHAR_T" + + // Defined only for C++ programs to indicate that wchar_t is an intrinsically defined data type. + ";_WCHAR_T_DEFINED" + + // Defined in any compiler that has an optimizer. + ";__BCOPT__=1" + + // Version number. + // BCB6 is 0x056X (SP4 is 0x0564) + // @see http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Predefined_Macros#C.2B.2B_Compiler_Versions_in_Predefined_Macros + ";__BORLANDC__=0x0560" + ";__TCPLUSPLUS__=0x0560" + ";__TURBOC__=0x0560"; + + // Defined if Calling Convention is set to cdecl; otherwise undefined. + const bool useCdecl = (cflags.find("-p") == cflags.end() + && cflags.find("-pm") == cflags.end() + && cflags.find("-pr") == cflags.end() + && cflags.find("-ps") == cflags.end()); + if (useCdecl) + predefines += ";__CDECL=1"; + + // Defined by default indicating that the default char is unsigned char. Use the -K compiler option to undefine this macro. + const bool treatCharAsUnsignedChar = (cflags.find("-K") != cflags.end()); + if (treatCharAsUnsignedChar) + predefines += ";_CHAR_UNSIGNED=1"; + + // Defined whenever one of the CodeGuard compiler options is used; otherwise it is undefined. + const bool codeguardUsed = (cflags.find("-vGd") != cflags.end() + || cflags.find("-vGt") != cflags.end() + || cflags.find("-vGc") != cflags.end()); + if (codeguardUsed) + predefines += ";__CODEGUARD__"; + + // When defined, the macro indicates that the program is a console application. + const bool isConsoleApp = (cflags.find("-WC") != cflags.end()); + if (isConsoleApp) + predefines += ";__CONSOLE__=1"; + + // Enable stack unwinding. This is true by default; use -xd- to disable. + const bool enableStackUnwinding = (cflags.find("-xd-") == cflags.end()); + if (enableStackUnwinding) + predefines += ";_CPPUNWIND=1"; + + // Defined whenever the -WD compiler option is used; otherwise it is undefined. + const bool isDLL = (cflags.find("-WD") != cflags.end()); + if (isDLL) + predefines += ";__DLL__=1"; + + // Defined when compiling in 32-bit flat memory model. + // TODO: not sure how to switch to another memory model or how to read configuration from project file + predefines += ";__FLAT__=1"; + + // Always defined. The default value is 300. You can change the value to 400 or 500 by using the /4 or /5 compiler options. + if (cflags.find("-6") != cflags.end()) + predefines += ";_M_IX86=600"; + else if (cflags.find("-5") != cflags.end()) + predefines += ";_M_IX86=500"; + else if (cflags.find("-4") != cflags.end()) + predefines += ";_M_IX86=400"; + else + predefines += ";_M_IX86=300"; + + // Defined only if the -WM option is used. It specifies that the multithread library is to be linked. + const bool linkMtLib = (cflags.find("-WM") != cflags.end()); + if (linkMtLib) + predefines += ";__MT__=1"; + + // Defined if Calling Convention is set to Pascal; otherwise undefined. + const bool usePascalCallingConvention = (cflags.find("-p") != cflags.end()); + if (usePascalCallingConvention) + predefines += ";__PASCAL__=1"; + + // Defined if you compile with the -A compiler option; otherwise, it is undefined. + const bool useAnsiKeywordExtensions = (cflags.find("-A") != cflags.end()); + if (useAnsiKeywordExtensions) + predefines += ";__STDC__=1"; + + // Thread Local Storage. Always true in C++Builder. + predefines += ";__TLC__=1"; + + // Defined for Windows-only code. + const bool isWindowsTarget = (cflags.find("-WC") != cflags.end() + || cflags.find("-WCDR") != cflags.end() + || cflags.find("-WCDV") != cflags.end() + || cflags.find("-WD") != cflags.end() + || cflags.find("-WDR") != cflags.end() + || cflags.find("-WDV") != cflags.end() + || cflags.find("-WM") != cflags.end() + || cflags.find("-WP") != cflags.end() + || cflags.find("-WR") != cflags.end() + || cflags.find("-WU") != cflags.end() + || cflags.find("-WV") != cflags.end()); + if (isWindowsTarget) + predefines += ";_Windows"; + + // Defined for console and GUI applications. + // TODO: I'm not sure about the difference to define "_Windows". + // From description, I would assume __WIN32__ is only defined for + // executables, while _Windows would also be defined for DLLs, etc. + // However, in a newly created DLL project, both __WIN32__ and + // _Windows are defined. -> treating them the same for now. + // Also boost uses __WIN32__ for OS identification. + const bool isConsoleOrGuiApp = isWindowsTarget; + if (isConsoleOrGuiApp) + predefines += ";__WIN32__=1"; + } + + // Include paths may contain variables like "$(BCB)\include" or "$(BCB)\include\vcl". + // Those get resolved by ImportProject::FileSettings::setIncludePaths by + // 1. checking the provided variables map ("BCB" => "C:\\Program Files (x86)\\Borland\\CBuilder6") + // 2. checking env variables as a fallback + // Setting env is always possible. Configuring the variables via cli might be an addition. + // Reading the BCB6 install location from registry in windows environments would also be possible, + // but I didn't see any such functionality around the source. Not in favor of adding it only + // for the BCB6 project loading. + std::map variables; + const std::string defines = predefines + ";" + sysdefines + ";" + userdefines; + const std::string cppDefines = cppPredefines + ";" + defines; + const bool forceCppMode = (cflags.find("-P") != cflags.end()); + + for (const std::string &c : compileList) { + // C++ compilation is selected by file extension by default, so these + // defines have to be configured on a per-file base. + // + // > Files with the .CPP extension compile as C++ files. Files with a .C + // > extension, with no extension, or with extensions other than .CPP, + // > .OBJ, .LIB, or .ASM compile as C files. + // (http://docwiki.embarcadero.com/RADStudio/Tokyo/en/BCC32.EXE,_the_C%2B%2B_32-bit_Command-Line_Compiler) + // + // We can also force C++ compilation for all files using the -P command line switch. + const bool cppMode = forceCppMode || Path::getFilenameExtensionInLowerCase(c) == ".cpp"; + FileSettings fs; + fsSetIncludePaths(fs, projectDir, toStringList(includePath), variables); + fsSetDefines(fs, cppMode ? cppDefines : defines); + fs.filename = Path::simplifyPath(Path::isAbsolute(c) ? c : projectDir + c); + fileSettings.push_back(std::move(fs)); + } + + return true; +} + +static std::string joinRelativePath(const std::string &path1, const std::string &path2) +{ + if (!path1.empty() && !Path::isAbsolute(path2)) + return path1 + path2; + return path2; +} + +static std::list readXmlStringList(const tinyxml2::XMLElement *node, const std::string &path, const char name[], const char attribute[]) +{ + std::list ret; + for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) { + if (strcmp(child->Name(), name) != 0) + continue; + const char *attr = attribute ? child->Attribute(attribute) : child->GetText(); + if (attr) + ret.push_back(joinRelativePath(path, attr)); + } + return ret; +} + +static std::string join(const std::list &strlist, const char *sep) +{ + std::string ret; + for (const std::string &s : strlist) { + ret += (ret.empty() ? "" : sep) + s; + } + return ret; +} + +static std::string istream_to_string(std::istream &istr) +{ + std::istreambuf_iterator eos; + return std::string(std::istreambuf_iterator(istr), eos); +} + +static const char * readSafe(const char *s, const char *def) { + return s ? s : def; +} + +bool ImportProject::importCppcheckGuiProject(std::istream &istr, Settings *settings) +{ + tinyxml2::XMLDocument doc; + const std::string xmldata = istream_to_string(istr); + const tinyxml2::XMLError error = doc.Parse(xmldata.data(), xmldata.size()); + if (error != tinyxml2::XML_SUCCESS) { + printError(std::string("Cppcheck GUI project file is not a valid XML - ") + tinyxml2::XMLDocument::ErrorIDToName(error)); + return false; + } + const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); + if (rootnode == nullptr || strcmp(rootnode->Name(), CppcheckXml::ProjectElementName) != 0) { + printError("Cppcheck GUI project file has no XML root node"); + return false; + } + + const std::string &path = mPath; + + std::list paths; + std::list suppressions; + Settings temp; + + // default to --check-level=normal for import for now + temp.setCheckLevel(Settings::CheckLevel::normal); + + guiProject.analyzeAllVsConfigs.clear(); + + bool checkLevelExhaustive = false; + + // TODO: this should support all available command-line options + for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { + if (strcmp(node->Name(), CppcheckXml::RootPathName) == 0) { + if (node->Attribute(CppcheckXml::RootPathNameAttrib)) { + temp.basePaths.push_back(joinRelativePath(path, node->Attribute(CppcheckXml::RootPathNameAttrib))); + temp.relativePaths = true; + } + } else if (strcmp(node->Name(), CppcheckXml::BuildDirElementName) == 0) + temp.buildDir = joinRelativePath(path, readSafe(node->GetText(), "")); + else if (strcmp(node->Name(), CppcheckXml::IncludeDirElementName) == 0) + temp.includePaths = readXmlStringList(node, path, CppcheckXml::DirElementName, CppcheckXml::DirNameAttrib); + else if (strcmp(node->Name(), CppcheckXml::DefinesElementName) == 0) + temp.userDefines = join(readXmlStringList(node, "", CppcheckXml::DefineName, CppcheckXml::DefineNameAttrib), ";"); + else if (strcmp(node->Name(), CppcheckXml::UndefinesElementName) == 0) { + for (const std::string &u : readXmlStringList(node, "", CppcheckXml::UndefineName, nullptr)) + temp.userUndefs.insert(u); + } else if (strcmp(node->Name(), CppcheckXml::ImportProjectElementName) == 0) { + const std::string t_str = readSafe(node->GetText(), ""); + if (!t_str.empty()) + guiProject.projectFile = path + t_str; + } + else if (strcmp(node->Name(), CppcheckXml::PathsElementName) == 0) + paths = readXmlStringList(node, path, CppcheckXml::PathName, CppcheckXml::PathNameAttrib); + else if (strcmp(node->Name(), CppcheckXml::ExcludeElementName) == 0) + guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::ExcludePathName, CppcheckXml::ExcludePathNameAttrib); + else if (strcmp(node->Name(), CppcheckXml::FunctionContracts) == 0) + ; + else if (strcmp(node->Name(), CppcheckXml::VariableContractsElementName) == 0) + ; + else if (strcmp(node->Name(), CppcheckXml::IgnoreElementName) == 0) + guiProject.excludedPaths = readXmlStringList(node, "", CppcheckXml::IgnorePathName, CppcheckXml::IgnorePathNameAttrib); + else if (strcmp(node->Name(), CppcheckXml::LibrariesElementName) == 0) + guiProject.libraries = readXmlStringList(node, "", CppcheckXml::LibraryElementName, nullptr); + else if (strcmp(node->Name(), CppcheckXml::SuppressionsElementName) == 0) { + for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) { + if (strcmp(child->Name(), CppcheckXml::SuppressionElementName) != 0) + continue; + SuppressionList::Suppression s; + s.errorId = readSafe(child->GetText(), ""); + s.fileName = readSafe(child->Attribute("fileName"), ""); + if (!s.fileName.empty()) + s.fileName = joinRelativePath(path, s.fileName); + s.lineNumber = child->IntAttribute("lineNumber", SuppressionList::Suppression::NO_LINE); + s.symbolName = readSafe(child->Attribute("symbolName"), ""); + s.hash = strToInt(readSafe(child->Attribute("hash"), "0")); + suppressions.push_back(std::move(s)); + } + } else if (strcmp(node->Name(), CppcheckXml::VSConfigurationElementName) == 0) + guiProject.checkVsConfigs = readXmlStringList(node, emptyString, CppcheckXml::VSConfigurationName, nullptr); + else if (strcmp(node->Name(), CppcheckXml::PlatformElementName) == 0) + guiProject.platform = readSafe(node->GetText(), ""); + else if (strcmp(node->Name(), CppcheckXml::AnalyzeAllVsConfigsElementName) == 0) + guiProject.analyzeAllVsConfigs = readSafe(node->GetText(), ""); + else if (strcmp(node->Name(), CppcheckXml::Parser) == 0) + temp.clang = true; + else if (strcmp(node->Name(), CppcheckXml::AddonsElementName) == 0) { + const auto& addons = readXmlStringList(node, emptyString, CppcheckXml::AddonElementName, nullptr); + temp.addons.insert(addons.cbegin(), addons.cend()); + } + else if (strcmp(node->Name(), CppcheckXml::TagsElementName) == 0) + node->Attribute(CppcheckXml::TagElementName); // FIXME: Write some warning + else if (strcmp(node->Name(), CppcheckXml::ToolsElementName) == 0) { + const std::list toolList = readXmlStringList(node, emptyString, CppcheckXml::ToolElementName, nullptr); + for (const std::string &toolName : toolList) { + if (toolName == CppcheckXml::ClangTidy) + temp.clangTidy = true; + } + } else if (strcmp(node->Name(), CppcheckXml::CheckHeadersElementName) == 0) + temp.checkHeaders = (strcmp(readSafe(node->GetText(), ""), "true") == 0); + else if (strcmp(node->Name(), CppcheckXml::CheckLevelExhaustiveElementName) == 0) + checkLevelExhaustive = true; + else if (strcmp(node->Name(), CppcheckXml::CheckUnusedTemplatesElementName) == 0) + temp.checkUnusedTemplates = (strcmp(readSafe(node->GetText(), ""), "true") == 0); + else if (strcmp(node->Name(), CppcheckXml::MaxCtuDepthElementName) == 0) + temp.maxCtuDepth = strToInt(readSafe(node->GetText(), "2")); // TODO: bail out when missing? + else if (strcmp(node->Name(), CppcheckXml::MaxTemplateRecursionElementName) == 0) + temp.maxTemplateRecursion = strToInt(readSafe(node->GetText(), "100")); // TODO: bail out when missing? + else if (strcmp(node->Name(), CppcheckXml::CheckUnknownFunctionReturn) == 0) + ; // TODO + else if (strcmp(node->Name(), Settings::SafeChecks::XmlRootName) == 0) { + for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) { + if (strcmp(child->Name(), Settings::SafeChecks::XmlClasses) == 0) + temp.safeChecks.classes = true; + else if (strcmp(child->Name(), Settings::SafeChecks::XmlExternalFunctions) == 0) + temp.safeChecks.externalFunctions = true; + else if (strcmp(child->Name(), Settings::SafeChecks::XmlInternalFunctions) == 0) + temp.safeChecks.internalFunctions = true; + else if (strcmp(child->Name(), Settings::SafeChecks::XmlExternalVariables) == 0) + temp.safeChecks.externalVariables = true; + else { + printError("Unknown '" + std::string(Settings::SafeChecks::XmlRootName) + "' element '" + std::string(child->Name()) + "' in Cppcheck project file"); + return false; + } + } + } else if (strcmp(node->Name(), CppcheckXml::TagWarningsElementName) == 0) + ; // TODO + // Cppcheck Premium features + else if (strcmp(node->Name(), CppcheckXml::BughuntingElementName) == 0) + temp.premiumArgs += " --bughunting"; + else if (strcmp(node->Name(), CppcheckXml::CertIntPrecisionElementName) == 0) + temp.premiumArgs += std::string(" --cert-c-int-precision=") + readSafe(node->GetText(), "0"); + else if (strcmp(node->Name(), CppcheckXml::CodingStandardsElementName) == 0) { + for (const tinyxml2::XMLElement *child = node->FirstChildElement(); child; child = child->NextSiblingElement()) { + if (strcmp(child->Name(), CppcheckXml::CodingStandardElementName) == 0 && child->GetText()) + temp.premiumArgs += std::string(" --") + child->GetText(); + } + } + else if (strcmp(node->Name(), CppcheckXml::ProjectNameElementName) == 0) + ; // no-op + else { + printError("Unknown element '" + std::string(node->Name()) + "' in Cppcheck project file"); + return false; + } + } + settings->basePaths = temp.basePaths; + settings->relativePaths |= temp.relativePaths; + settings->buildDir = temp.buildDir; + settings->includePaths = temp.includePaths; + settings->userDefines = temp.userDefines; + settings->userUndefs = temp.userUndefs; + settings->addons = temp.addons; + settings->clang = temp.clang; + settings->clangTidy = temp.clangTidy; + + if (!settings->premiumArgs.empty()) + settings->premiumArgs += temp.premiumArgs; + else if (!temp.premiumArgs.empty()) + settings->premiumArgs = temp.premiumArgs.substr(1); + + for (const std::string &p : paths) + guiProject.pathNames.push_back(p); + settings->supprs.nomsg.addSuppressions(std::move(suppressions)); + settings->checkHeaders = temp.checkHeaders; + settings->checkUnusedTemplates = temp.checkUnusedTemplates; + settings->maxCtuDepth = temp.maxCtuDepth; + settings->maxTemplateRecursion = temp.maxTemplateRecursion; + settings->safeChecks = temp.safeChecks; + + if (checkLevelExhaustive) + settings->setCheckLevel(Settings::CheckLevel::exhaustive); + else + settings->setCheckLevel(Settings::CheckLevel::normal); + + return true; +} + +void ImportProject::selectOneVsConfig(Platform::Type platform) +{ + std::set filenames; + for (std::list::iterator it = fileSettings.begin(); it != fileSettings.end();) { + if (it->cfg.empty()) { + ++it; + continue; + } + const FileSettings &fs = *it; + bool remove = false; + if (!startsWith(fs.cfg,"Debug")) + remove = true; + if (platform == Platform::Type::Win64 && fs.platformType != platform) + remove = true; + else if ((platform == Platform::Type::Win32A || platform == Platform::Type::Win32W) && fs.platformType == Platform::Type::Win64) + remove = true; + else if (filenames.find(fs.filename) != filenames.end()) + remove = true; + if (remove) { + it = fileSettings.erase(it); + } else { + filenames.insert(fs.filename); + ++it; + } + } +} + +void ImportProject::selectVsConfigurations(Platform::Type platform, const std::vector &configurations) +{ + for (std::list::iterator it = fileSettings.begin(); it != fileSettings.end();) { + if (it->cfg.empty()) { + ++it; + continue; + } + const FileSettings &fs = *it; + const auto config = fs.cfg.substr(0, fs.cfg.find('|')); + bool remove = false; + if (std::find(configurations.begin(), configurations.end(), config) == configurations.end()) + remove = true; + if (platform == Platform::Type::Win64 && fs.platformType != platform) + remove = true; + else if ((platform == Platform::Type::Win32A || platform == Platform::Type::Win32W) && fs.platformType == Platform::Type::Win64) + remove = true; + if (remove) { + it = fileSettings.erase(it); + } else { + ++it; + } + } +} + +std::list ImportProject::getVSConfigs() +{ + return std::list(mAllVSConfigs.cbegin(), mAllVSConfigs.cend()); +} + +void ImportProject::setRelativePaths(const std::string &filename) +{ + if (Path::isAbsolute(filename)) + return; + const std::vector basePaths{Path::fromNativeSeparators(Path::getCurrentPath())}; + for (auto &fs: fileSettings) { + fs.filename = Path::getRelativePath(fs.filename, basePaths); + for (auto &includePath: fs.includePaths) + includePath = Path::getRelativePath(includePath, basePaths); + } +} + +void ImportProject::printError(const std::string &message) +{ + std::cout << "cppcheck: error: " << message << std::endl; +} + +bool ImportProject::sourceFileExists(const std::string &file) +{ + return Path::isFile(file); +} diff --git a/cppcheck-2.14.0/lib/importproject.h b/cppcheck-2.14.0/lib/importproject.h new file mode 100644 index 00000000..54a8f6f0 --- /dev/null +++ b/cppcheck-2.14.0/lib/importproject.h @@ -0,0 +1,180 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef importprojectH +#define importprojectH +//--------------------------------------------------------------------------- + +#include "config.h" +#include "filesettings.h" +#include "platform.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include + +/// @addtogroup Core +/// @{ + +namespace cppcheck { + struct stricmp { + bool operator()(const std::string &lhs, const std::string &rhs) const { + return caseInsensitiveStringCompare(lhs,rhs) < 0; + } + }; +} + +class Settings; + +/** + * @brief Importing project settings. + */ +class CPPCHECKLIB WARN_UNUSED ImportProject { +public: + enum class Type { + NONE, + UNKNOWN, + MISSING, + FAILURE, + COMPILE_DB, + VS_SLN, + VS_VCXPROJ, + BORLAND, + CPPCHECK_GUI + }; + + static void fsParseCommand(FileSettings& fs, const std::string& command); + static void fsSetDefines(FileSettings& fs, std::string defs); + static void fsSetIncludePaths(FileSettings& fs, const std::string &basepath, const std::list &in, std::map &variables); + + std::list fileSettings; + Type projectType{Type::NONE}; + + ImportProject() = default; + virtual ~ImportProject() = default; + ImportProject(const ImportProject&) = default; + ImportProject& operator=(const ImportProject&) = default; + + void selectOneVsConfig(Platform::Type platform); + void selectVsConfigurations(Platform::Type platform, const std::vector &configurations); + + std::list getVSConfigs(); + + // Cppcheck GUI output + struct { + std::string analyzeAllVsConfigs; + std::vector pathNames; + std::list libraries; + std::list excludedPaths; + std::list checkVsConfigs; + std::string projectFile; + std::string platform; + } guiProject; + + void ignorePaths(const std::vector &ipaths); + void ignoreOtherConfigs(const std::string &cfg); + + Type import(const std::string &filename, Settings *settings=nullptr); +protected: + bool importCompileCommands(std::istream &istr); + bool importCppcheckGuiProject(std::istream &istr, Settings *settings); + virtual bool sourceFileExists(const std::string &file); +private: + bool importSln(std::istream &istr, const std::string &path, const std::vector &fileFilters); + bool importVcxproj(const std::string &filename, std::map &variables, const std::string &additionalIncludeDirectories, const std::vector &fileFilters); + bool importBcb6Prj(const std::string &projectFilename); + + static void printError(const std::string &message); + + void setRelativePaths(const std::string &filename); + + std::string mPath; + std::set mAllVSConfigs; +}; + + +namespace CppcheckXml { + static constexpr char ProjectElementName[] = "project"; + static constexpr char ProjectVersionAttrib[] = "version"; + static constexpr char ProjectFileVersion[] = "1"; + static constexpr char BuildDirElementName[] = "builddir"; + static constexpr char ImportProjectElementName[] = "importproject"; + static constexpr char AnalyzeAllVsConfigsElementName[] = "analyze-all-vs-configs"; + static constexpr char Parser[] = "parser"; + static constexpr char IncludeDirElementName[] = "includedir"; + static constexpr char DirElementName[] = "dir"; + static constexpr char DirNameAttrib[] = "name"; + static constexpr char DefinesElementName[] = "defines"; + static constexpr char DefineName[] = "define"; + static constexpr char DefineNameAttrib[] = "name"; + static constexpr char UndefinesElementName[] = "undefines"; + static constexpr char UndefineName[] = "undefine"; + static constexpr char PathsElementName[] = "paths"; + static constexpr char PathName[] = "dir"; + static constexpr char PathNameAttrib[] = "name"; + static constexpr char RootPathName[] = "root"; + static constexpr char RootPathNameAttrib[] = "name"; + static constexpr char IgnoreElementName[] = "ignore"; + static constexpr char IgnorePathName[] = "path"; + static constexpr char IgnorePathNameAttrib[] = "name"; + static constexpr char ExcludeElementName[] = "exclude"; + static constexpr char ExcludePathName[] = "path"; + static constexpr char ExcludePathNameAttrib[] = "name"; + static constexpr char FunctionContracts[] = "function-contracts"; + static constexpr char VariableContractsElementName[] = "variable-contracts"; + static constexpr char LibrariesElementName[] = "libraries"; + static constexpr char LibraryElementName[] = "library"; + static constexpr char PlatformElementName[] = "platform"; + static constexpr char SuppressionsElementName[] = "suppressions"; + static constexpr char SuppressionElementName[] = "suppression"; + static constexpr char AddonElementName[] = "addon"; + static constexpr char AddonsElementName[] = "addons"; + static constexpr char ToolElementName[] = "tool"; + static constexpr char ToolsElementName[] = "tools"; + static constexpr char TagsElementName[] = "tags"; + static constexpr char TagElementName[] = "tag"; + static constexpr char TagWarningsElementName[] = "tag-warnings"; + static constexpr char TagAttributeName[] = "tag"; + static constexpr char WarningElementName[] = "warning"; + static constexpr char HashAttributeName[] = "hash"; + static constexpr char CheckLevelExhaustiveElementName[] = "check-level-exhaustive"; + static constexpr char CheckHeadersElementName[] = "check-headers"; + static constexpr char CheckUnusedTemplatesElementName[] = "check-unused-templates"; + static constexpr char MaxCtuDepthElementName[] = "max-ctu-depth"; + static constexpr char MaxTemplateRecursionElementName[] = "max-template-recursion"; + static constexpr char CheckUnknownFunctionReturn[] = "check-unknown-function-return-values"; + static constexpr char ClangTidy[] = "clang-tidy"; + static constexpr char Name[] = "name"; + static constexpr char VSConfigurationElementName[] = "vs-configurations"; + static constexpr char VSConfigurationName[] = "config"; + // Cppcheck Premium + static constexpr char BughuntingElementName[] = "bug-hunting"; + static constexpr char CodingStandardsElementName[] = "coding-standards"; + static constexpr char CodingStandardElementName[] = "coding-standard"; + static constexpr char CertIntPrecisionElementName[] = "cert-c-int-precision"; + static constexpr char ProjectNameElementName[] = "project-name"; +} + +/// @} +//--------------------------------------------------------------------------- +#endif // importprojectH diff --git a/cppcheck-2.14.0/lib/infer.cpp b/cppcheck-2.14.0/lib/infer.cpp new file mode 100644 index 00000000..0b24a9b4 --- /dev/null +++ b/cppcheck-2.14.0/lib/infer.cpp @@ -0,0 +1,388 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "infer.h" + +#include "calculate.h" +#include "errortypes.h" +#include "valueptr.h" + +#include +#include +#include +#include +#include +#include + +class Token; + +template +static const ValueFlow::Value* getCompareValue(const std::list& values, Predicate pred, Compare compare) +{ + const ValueFlow::Value* result = nullptr; + for (const ValueFlow::Value& value : values) { + if (!pred(value)) + continue; + if (result) + result = &std::min(value, *result, [compare](const ValueFlow::Value& x, const ValueFlow::Value& y) { + return compare(x.intvalue, y.intvalue); + }); + else + result = &value; + } + return result; +} + +namespace { + struct Interval { + std::vector minvalue, maxvalue; + std::vector minRef, maxRef; + + void setMinValue(MathLib::bigint x, const ValueFlow::Value* ref = nullptr) + { + minvalue = {x}; + if (ref) + minRef = {ref}; + } + + void setMaxValue(MathLib::bigint x, const ValueFlow::Value* ref = nullptr) + { + maxvalue = {x}; + if (ref) + maxRef = {ref}; + } + + bool isLessThan(MathLib::bigint x, std::vector* ref = nullptr) const + { + if (!this->maxvalue.empty() && this->maxvalue.front() < x) { + if (ref) + *ref = maxRef; + return true; + } + return false; + } + + bool isGreaterThan(MathLib::bigint x, std::vector* ref = nullptr) const + { + if (!this->minvalue.empty() && this->minvalue.front() > x) { + if (ref) + *ref = minRef; + return true; + } + return false; + } + + bool isScalar() const { + return minvalue.size() == 1 && minvalue == maxvalue; + } + + bool empty() const { + return minvalue.empty() && maxvalue.empty(); + } + + bool isScalarOrEmpty() const { + return empty() || isScalar(); + } + + MathLib::bigint getScalar() const + { + assert(isScalar()); + return minvalue.front(); + } + + std::vector getScalarRef() const + { + assert(isScalar()); + if (minRef != maxRef) + return merge(minRef, maxRef); + return minRef; + } + + static Interval fromInt(MathLib::bigint x, const ValueFlow::Value* ref = nullptr) + { + Interval result; + result.setMinValue(x, ref); + result.setMaxValue(x, ref); + return result; + } + + template + static Interval fromValues(const std::list& values, Predicate predicate) + { + Interval result; + const ValueFlow::Value* minValue = getCompareValue(values, predicate, std::less{}); + if (minValue) { + if (minValue->isImpossible() && minValue->bound == ValueFlow::Value::Bound::Upper) + result.setMinValue(minValue->intvalue + 1, minValue); + if (minValue->isPossible() && minValue->bound == ValueFlow::Value::Bound::Lower) + result.setMinValue(minValue->intvalue, minValue); + if (!minValue->isImpossible() && (minValue->bound == ValueFlow::Value::Bound::Point || minValue->isKnown()) && + std::count_if(values.begin(), values.end(), predicate) == 1) + return Interval::fromInt(minValue->intvalue, minValue); + } + const ValueFlow::Value* maxValue = getCompareValue(values, predicate, std::greater{}); + if (maxValue) { + if (maxValue->isImpossible() && maxValue->bound == ValueFlow::Value::Bound::Lower) + result.setMaxValue(maxValue->intvalue - 1, maxValue); + if (maxValue->isPossible() && maxValue->bound == ValueFlow::Value::Bound::Upper) + result.setMaxValue(maxValue->intvalue, maxValue); + assert(!maxValue->isKnown()); + } + return result; + } + + static Interval fromValues(const std::list& values) + { + return Interval::fromValues(values, [](const ValueFlow::Value&) { + return true; + }); + } + + template + static std::vector apply(const std::vector& x, + const std::vector& y, + F f) + { + if (x.empty()) + return {}; + if (y.empty()) + return {}; + return {f(x.front(), y.front())}; + } + + static std::vector merge(std::vector x, + const std::vector& y) + { + x.insert(x.end(), y.cbegin(), y.cend()); + return x; + } + + friend Interval operator-(const Interval& lhs, const Interval& rhs) + { + Interval result; + result.minvalue = Interval::apply(lhs.minvalue, rhs.maxvalue, std::minus{}); + result.maxvalue = Interval::apply(lhs.maxvalue, rhs.minvalue, std::minus{}); + if (!result.minvalue.empty()) + result.minRef = merge(lhs.minRef, rhs.maxRef); + if (!result.maxvalue.empty()) + result.maxRef = merge(lhs.maxRef, rhs.minRef); + return result; + } + + static std::vector equal(const Interval& lhs, + const Interval& rhs, + std::vector* ref = nullptr) + { + if (!lhs.isScalar()) + return {}; + if (!rhs.isScalar()) + return {}; + if (ref) + *ref = merge(lhs.getScalarRef(), rhs.getScalarRef()); + return {lhs.minvalue == rhs.minvalue}; + } + + static std::vector compare(const Interval& lhs, + const Interval& rhs, + std::vector* ref = nullptr) + { + Interval diff = lhs - rhs; + if (diff.isGreaterThan(0, ref)) + return {1}; + if (diff.isLessThan(0, ref)) + return {-1}; + std::vector eq = Interval::equal(lhs, rhs, ref); + if (!eq.empty()) { + if (eq.front() == 0) + return {1, -1}; + return {0}; + } + if (diff.isGreaterThan(-1, ref)) + return {0, 1}; + if (diff.isLessThan(1, ref)) + return {0, -1}; + return {}; + } + + static std::vector compare(const std::string& op, + const Interval& lhs, + const Interval& rhs, + std::vector* ref = nullptr) + { + std::vector r = compare(lhs, rhs, ref); + if (r.empty()) + return {}; + bool b = calculate(op, r.front(), 0); + if (std::all_of(r.cbegin() + 1, r.cend(), [&](int i) { + return b == calculate(op, i, 0); + })) + return {b}; + return {}; + } + }; +} + +static void addToErrorPath(ValueFlow::Value& value, const std::vector& refs) +{ + std::unordered_set locations; + for (const ValueFlow::Value* ref : refs) { + if (ref->condition && !value.condition) + value.condition = ref->condition; + std::copy_if(ref->errorPath.cbegin(), + ref->errorPath.cend(), + std::back_inserter(value.errorPath), + [&](const ErrorPathItem& e) { + return locations.insert(e.first).second; + }); + std::copy_if(ref->debugPath.cbegin(), + ref->debugPath.cend(), + std::back_inserter(value.debugPath), + [&](const ErrorPathItem& e) { + return locations.insert(e.first).second; + }); + } +} + +static void setValueKind(ValueFlow::Value& value, const std::vector& refs) +{ + bool isPossible = false; + bool isInconclusive = false; + for (const ValueFlow::Value* ref : refs) { + if (ref->isPossible()) + isPossible = true; + if (ref->isInconclusive()) + isInconclusive = true; + } + if (isInconclusive) + value.setInconclusive(); + else if (isPossible) + value.setPossible(); + else + value.setKnown(); +} + +static bool inferNotEqual(const std::list& values, MathLib::bigint x) +{ + return std::any_of(values.cbegin(), values.cend(), [&](const ValueFlow::Value& value) { + return value.isImpossible() && value.intvalue == x; + }); +} + +std::vector infer(const ValuePtr& model, + const std::string& op, + std::list lhsValues, + std::list rhsValues) +{ + std::vector result; + auto notMatch = [&](const ValueFlow::Value& value) { + return !model->match(value); + }; + lhsValues.remove_if(notMatch); + rhsValues.remove_if(notMatch); + if (lhsValues.empty() || rhsValues.empty()) + return result; + + Interval lhs = Interval::fromValues(lhsValues); + Interval rhs = Interval::fromValues(rhsValues); + + if (op == "-") { + Interval diff = lhs - rhs; + if (diff.isScalar()) { + std::vector refs = diff.getScalarRef(); + ValueFlow::Value value(diff.getScalar()); + addToErrorPath(value, refs); + setValueKind(value, refs); + result.push_back(std::move(value)); + } else { + if (!diff.minvalue.empty()) { + ValueFlow::Value value(diff.minvalue.front() - 1); + value.setImpossible(); + value.bound = ValueFlow::Value::Bound::Upper; + addToErrorPath(value, diff.minRef); + result.push_back(std::move(value)); + } + if (!diff.maxvalue.empty()) { + ValueFlow::Value value(diff.maxvalue.front() + 1); + value.setImpossible(); + value.bound = ValueFlow::Value::Bound::Lower; + addToErrorPath(value, diff.maxRef); + result.push_back(std::move(value)); + } + } + } else if ((op == "!=" || op == "==") && lhs.isScalarOrEmpty() && rhs.isScalarOrEmpty()) { + if (lhs.isScalar() && rhs.isScalar()) { + std::vector refs = Interval::merge(lhs.getScalarRef(), rhs.getScalarRef()); + ValueFlow::Value value(calculate(op, lhs.getScalar(), rhs.getScalar())); + addToErrorPath(value, refs); + setValueKind(value, refs); + result.push_back(std::move(value)); + } else { + std::vector refs; + if (lhs.isScalar() && inferNotEqual(rhsValues, lhs.getScalar())) + refs = lhs.getScalarRef(); + else if (rhs.isScalar() && inferNotEqual(lhsValues, rhs.getScalar())) + refs = rhs.getScalarRef(); + if (!refs.empty()) { + ValueFlow::Value value(op == "!="); + addToErrorPath(value, refs); + setValueKind(value, refs); + result.push_back(std::move(value)); + } + } + } else { + std::vector refs; + std::vector r = Interval::compare(op, lhs, rhs, &refs); + if (!r.empty()) { + ValueFlow::Value value(r.front()); + addToErrorPath(value, refs); + setValueKind(value, refs); + result.push_back(std::move(value)); + } + } + + return result; +} + +std::vector infer(const ValuePtr& model, + const std::string& op, + MathLib::bigint lhs, + std::list rhsValues) +{ + return infer(model, op, {model->yield(lhs)}, std::move(rhsValues)); +} + +std::vector infer(const ValuePtr& model, + const std::string& op, + std::list lhsValues, + MathLib::bigint rhs) +{ + return infer(model, op, std::move(lhsValues), {model->yield(rhs)}); +} + +std::vector getMinValue(const ValuePtr& model, const std::list& values) +{ + return Interval::fromValues(values, [&](const ValueFlow::Value& v) { + return model->match(v); + }).minvalue; +} +std::vector getMaxValue(const ValuePtr& model, const std::list& values) +{ + return Interval::fromValues(values, [&](const ValueFlow::Value& v) { + return model->match(v); + }).maxvalue; +} diff --git a/cppcheck-2.14.0/lib/infer.h b/cppcheck-2.14.0/lib/infer.h new file mode 100644 index 00000000..ed477380 --- /dev/null +++ b/cppcheck-2.14.0/lib/infer.h @@ -0,0 +1,59 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef inferH +#define inferH + +#include "config.h" +#include "mathlib.h" +#include "vfvalue.h" + +#include +#include +#include + +template class ValuePtr; + +struct InferModel { + virtual bool match(const ValueFlow::Value& value) const = 0; + virtual ValueFlow::Value yield(MathLib::bigint value) const = 0; + virtual ~InferModel() = default; + InferModel(const InferModel&) = default; +protected: + InferModel() = default; +}; + +std::vector infer(const ValuePtr& model, + const std::string& op, + std::list lhsValues, + std::list rhsValues); + +std::vector infer(const ValuePtr& model, + const std::string& op, + MathLib::bigint lhs, + std::list rhsValues); + +std::vector infer(const ValuePtr& model, + const std::string& op, + std::list lhsValues, + MathLib::bigint rhs); + +CPPCHECKLIB std::vector getMinValue(const ValuePtr& model, const std::list& values); +std::vector getMaxValue(const ValuePtr& model, const std::list& values); + +#endif diff --git a/cppcheck-2.14.0/lib/json.h b/cppcheck-2.14.0/lib/json.h new file mode 100644 index 00000000..f4177b5c --- /dev/null +++ b/cppcheck-2.14.0/lib/json.h @@ -0,0 +1,37 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef jsonH +#define jsonH + +#include "config.h" + +SUPPRESS_WARNING_PUSH("-Wfloat-equal") +SUPPRESS_WARNING_CLANG_PUSH("-Wtautological-type-limit-compare") +SUPPRESS_WARNING_CLANG_PUSH("-Wextra-semi-stmt") +SUPPRESS_WARNING_CLANG_PUSH("-Wzero-as-null-pointer-constant") + +#define PICOJSON_USE_INT64 +#include + +SUPPRESS_WARNING_CLANG_POP +SUPPRESS_WARNING_CLANG_POP +SUPPRESS_WARNING_CLANG_POP +SUPPRESS_WARNING_POP + +#endif // jsonH diff --git a/cppcheck-2.14.0/lib/keywords.cpp b/cppcheck-2.14.0/lib/keywords.cpp new file mode 100644 index 00000000..ab4ab923 --- /dev/null +++ b/cppcheck-2.14.0/lib/keywords.cpp @@ -0,0 +1,227 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "keywords.h" + +#include "utils.h" + +// see https://en.cppreference.com/w/c/keyword + +#define C90_KEYWORDS \ + "auto", "break", "case", "char", "const", "continue", "default", \ + "do", "double", "else", "enum", "extern", "float", "for", "goto", "if", "int", "long", \ + "register", "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef", \ + "union", "unsigned", "void", "volatile", "while" + +#define C99_KEYWORDS \ + "inline", "restrict", "_Bool", "_Complex", "_Imaginary" + +#define C11_KEYWORDS \ + "_Alignas", "_Alignof", "_Atomic", "_Generic", "_Noreturn", "_Static_assert", "_Thread_local" + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-macros" +#endif + +#define C23_KEYWORDS \ + "alignas", "alignof", "bool", "constexpr", "false", "nullptr", "static_assert", "thread_local", "true", "typeof", "typeof_unqual", \ + "_BitInt", "_Decimal128", "_Decimal32", "_Decimal64" + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +static const std::unordered_set c89_keywords_all = { + C90_KEYWORDS +}; + +static const std::unordered_set c89_keywords = c89_keywords_all; + +static const std::unordered_set c99_keywords_all = { + C90_KEYWORDS, C99_KEYWORDS +}; + +static const std::unordered_set c99_keywords = { + C99_KEYWORDS +}; + +static const std::unordered_set c11_keywords_all = { + C90_KEYWORDS, C99_KEYWORDS, C11_KEYWORDS +}; + +static const std::unordered_set c11_keywords = { + C11_KEYWORDS +}; + +/* + static const std::unordered_set c23_keywords_all = { + C90_KEYWORDS, C99_KEYWORDS, C11_KEYWORDS, C23_KEYWORDS + }; + + static const std::unordered_set c23_keywords = { + C23_KEYWORDS + }; + */ + +// see https://en.cppreference.com/w/cpp/keyword + +#define CPP03_KEYWORDS \ + "and", "and_eq", "asm", "auto", "bitand", "bitor", "bool", "break", "case", "catch", "char", \ + "class", "compl", "const", "const_cast", "continue", "default", \ + "delete", "do", "double", "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", \ + "float", "for", "friend", "goto", "if", "inline", "int", "long", \ + "mutable", "namespace", "new", "not", "not_eq", "operator", \ + "or", "or_eq", "private", "protected", "public", "register", "reinterpret_cast", \ + "return", "short", "signed", "sizeof", "static", \ + "static_cast", "struct", "switch", "template", "this", "throw", \ + "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using", \ + "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq" + +#define CPP11_KEYWORDS \ + "alignas", "alignof", "char16_t", "char32_t", "constexpr", "decltype", \ + "noexcept", "nullptr", "static_assert", "thread_local" + +#define CPP20_KEYWORDS \ + "char8_t", "concept", "consteval", "constinit", "co_await", \ + "co_return", "co_yield", "requires" + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-macros" +#endif + +#define CPP_TMTS_KEYWORDS \ + "atomic_cancel", "atomic_commit", "atomic_noexcept", "synchronized" + +#define CPP_REFL_TS_KEYWORDS \ + "reflexpr" + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +static const std::unordered_set cpp03_keywords_all = { + CPP03_KEYWORDS +}; + +static const std::unordered_set cpp03_keywords = cpp03_keywords_all; + +static const std::unordered_set cpp11_keywords_all = { + CPP03_KEYWORDS, CPP11_KEYWORDS +}; + +static const std::unordered_set cpp11_keywords = { + CPP11_KEYWORDS +}; + +static const std::unordered_set cpp14_keywords_all = cpp11_keywords_all; + +static const std::unordered_set cpp14_keywords; + +static const std::unordered_set cpp17_keywords_all = cpp11_keywords_all; + +static const std::unordered_set cpp17_keywords; + +static const std::unordered_set cpp20_keywords_all = { + CPP03_KEYWORDS, CPP11_KEYWORDS, CPP20_KEYWORDS +}; + +static const std::unordered_set cpp20_keywords = { + CPP20_KEYWORDS +}; + +static const std::unordered_set cpp23_keywords; + +static const std::unordered_set cpp23_keywords_all = cpp20_keywords_all; + +// cppcheck-suppress unusedFunction +const std::unordered_set& Keywords::getAll(Standards::cstd_t cStd) +{ + // cppcheck-suppress missingReturn + switch (cStd) { + case Standards::cstd_t::C89: + return c89_keywords_all; + case Standards::cstd_t::C99: + return c99_keywords_all; + case Standards::cstd_t::C11: + return c11_keywords_all; + /*case Standards::cstd_t::C23: + return c23_keywords_all;*/ + } + cppcheck::unreachable(); +} + +// cppcheck-suppress unusedFunction +const std::unordered_set& Keywords::getAll(Standards::cppstd_t cppStd) { + // cppcheck-suppress missingReturn + switch (cppStd) { + case Standards::cppstd_t::CPP03: + return cpp03_keywords_all; + case Standards::cppstd_t::CPP11: + return cpp11_keywords_all; + case Standards::cppstd_t::CPP14: + return cpp14_keywords_all; + case Standards::cppstd_t::CPP17: + return cpp17_keywords_all; + case Standards::cppstd_t::CPP20: + return cpp20_keywords_all; + case Standards::cppstd_t::CPP23: + return cpp23_keywords_all; + } + cppcheck::unreachable(); +} + +// cppcheck-suppress unusedFunction +const std::unordered_set& Keywords::getOnly(Standards::cstd_t cStd) +{ + // cppcheck-suppress missingReturn + switch (cStd) { + case Standards::cstd_t::C89: + return c89_keywords; + case Standards::cstd_t::C99: + return c99_keywords; + case Standards::cstd_t::C11: + return c11_keywords; + /*case Standards::cstd_t::C23: + return c23_keywords_all;*/ + } + cppcheck::unreachable(); +} + +// cppcheck-suppress unusedFunction +const std::unordered_set& Keywords::getOnly(Standards::cppstd_t cppStd) +{ + // cppcheck-suppress missingReturn + switch (cppStd) { + case Standards::cppstd_t::CPP03: + return cpp03_keywords; + case Standards::cppstd_t::CPP11: + return cpp11_keywords; + case Standards::cppstd_t::CPP14: + return cpp14_keywords; + case Standards::cppstd_t::CPP17: + return cpp17_keywords; + case Standards::cppstd_t::CPP20: + return cpp20_keywords; + case Standards::cppstd_t::CPP23: + return cpp23_keywords; + } + cppcheck::unreachable(); +} + diff --git a/cppcheck-2.14.0/lib/keywords.h b/cppcheck-2.14.0/lib/keywords.h new file mode 100644 index 00000000..4b03c18a --- /dev/null +++ b/cppcheck-2.14.0/lib/keywords.h @@ -0,0 +1,37 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef keywordsH +#define keywordsH + +#include "standards.h" + +#include +#include + +class Keywords +{ +public: + static const std::unordered_set& getAll(Standards::cstd_t cStd); + static const std::unordered_set& getAll(Standards::cppstd_t cppStd); + + static const std::unordered_set& getOnly(Standards::cstd_t cStd); + static const std::unordered_set& getOnly(Standards::cppstd_t cppStd); +}; + +#endif diff --git a/cppcheck-2.14.0/lib/lib.pri b/cppcheck-2.14.0/lib/lib.pri new file mode 100644 index 00000000..5125fb68 --- /dev/null +++ b/cppcheck-2.14.0/lib/lib.pri @@ -0,0 +1,145 @@ +# no manual edits - this file is autogenerated by dmake + +include($$PWD/pcrerules.pri) +include($$PWD/../externals/externals.pri) +INCLUDEPATH += $$PWD +HEADERS += $${PWD}/addoninfo.h \ + $${PWD}/analyzer.h \ + $${PWD}/analyzerinfo.h \ + $${PWD}/astutils.h \ + $${PWD}/calculate.h \ + $${PWD}/check.h \ + $${PWD}/check64bit.h \ + $${PWD}/checkassert.h \ + $${PWD}/checkautovariables.h \ + $${PWD}/checkbool.h \ + $${PWD}/checkboost.h \ + $${PWD}/checkbufferoverrun.h \ + $${PWD}/checkclass.h \ + $${PWD}/checkcondition.h \ + $${PWD}/checkers.h \ + $${PWD}/checkersreport.h \ + $${PWD}/checkexceptionsafety.h \ + $${PWD}/checkfunctions.h \ + $${PWD}/checkinternal.h \ + $${PWD}/checkio.h \ + $${PWD}/checkleakautovar.h \ + $${PWD}/checkmemoryleak.h \ + $${PWD}/checknullpointer.h \ + $${PWD}/checkother.h \ + $${PWD}/checkpostfixoperator.h \ + $${PWD}/checksizeof.h \ + $${PWD}/checkstl.h \ + $${PWD}/checkstring.h \ + $${PWD}/checktype.h \ + $${PWD}/checkuninitvar.h \ + $${PWD}/checkunusedfunctions.h \ + $${PWD}/checkunusedvar.h \ + $${PWD}/checkvaarg.h \ + $${PWD}/clangimport.h \ + $${PWD}/color.h \ + $${PWD}/config.h \ + $${PWD}/cppcheck.h \ + $${PWD}/ctu.h \ + $${PWD}/errorlogger.h \ + $${PWD}/errortypes.h \ + $${PWD}/filesettings.h \ + $${PWD}/findtoken.h \ + $${PWD}/forwardanalyzer.h \ + $${PWD}/fwdanalysis.h \ + $${PWD}/importproject.h \ + $${PWD}/infer.h \ + $${PWD}/json.h \ + $${PWD}/keywords.h \ + $${PWD}/library.h \ + $${PWD}/mathlib.h \ + $${PWD}/path.h \ + $${PWD}/pathanalysis.h \ + $${PWD}/pathmatch.h \ + $${PWD}/platform.h \ + $${PWD}/precompiled.h \ + $${PWD}/preprocessor.h \ + $${PWD}/programmemory.h \ + $${PWD}/reverseanalyzer.h \ + $${PWD}/settings.h \ + $${PWD}/smallvector.h \ + $${PWD}/standards.h \ + $${PWD}/summaries.h \ + $${PWD}/suppressions.h \ + $${PWD}/symboldatabase.h \ + $${PWD}/templatesimplifier.h \ + $${PWD}/timer.h \ + $${PWD}/token.h \ + $${PWD}/tokenize.h \ + $${PWD}/tokenlist.h \ + $${PWD}/tokenrange.h \ + $${PWD}/utils.h \ + $${PWD}/valueflow.h \ + $${PWD}/valueptr.h \ + $${PWD}/version.h \ + $${PWD}/vfvalue.h \ + $${PWD}/xml.h + +SOURCES += $${PWD}/valueflow.cpp \ + $${PWD}/tokenize.cpp \ + $${PWD}/symboldatabase.cpp \ + $${PWD}/addoninfo.cpp \ + $${PWD}/analyzerinfo.cpp \ + $${PWD}/astutils.cpp \ + $${PWD}/check.cpp \ + $${PWD}/check64bit.cpp \ + $${PWD}/checkassert.cpp \ + $${PWD}/checkautovariables.cpp \ + $${PWD}/checkbool.cpp \ + $${PWD}/checkboost.cpp \ + $${PWD}/checkbufferoverrun.cpp \ + $${PWD}/checkclass.cpp \ + $${PWD}/checkcondition.cpp \ + $${PWD}/checkers.cpp \ + $${PWD}/checkersreport.cpp \ + $${PWD}/checkexceptionsafety.cpp \ + $${PWD}/checkfunctions.cpp \ + $${PWD}/checkinternal.cpp \ + $${PWD}/checkio.cpp \ + $${PWD}/checkleakautovar.cpp \ + $${PWD}/checkmemoryleak.cpp \ + $${PWD}/checknullpointer.cpp \ + $${PWD}/checkother.cpp \ + $${PWD}/checkpostfixoperator.cpp \ + $${PWD}/checksizeof.cpp \ + $${PWD}/checkstl.cpp \ + $${PWD}/checkstring.cpp \ + $${PWD}/checktype.cpp \ + $${PWD}/checkuninitvar.cpp \ + $${PWD}/checkunusedfunctions.cpp \ + $${PWD}/checkunusedvar.cpp \ + $${PWD}/checkvaarg.cpp \ + $${PWD}/clangimport.cpp \ + $${PWD}/color.cpp \ + $${PWD}/cppcheck.cpp \ + $${PWD}/ctu.cpp \ + $${PWD}/errorlogger.cpp \ + $${PWD}/errortypes.cpp \ + $${PWD}/forwardanalyzer.cpp \ + $${PWD}/fwdanalysis.cpp \ + $${PWD}/importproject.cpp \ + $${PWD}/infer.cpp \ + $${PWD}/keywords.cpp \ + $${PWD}/library.cpp \ + $${PWD}/mathlib.cpp \ + $${PWD}/path.cpp \ + $${PWD}/pathanalysis.cpp \ + $${PWD}/pathmatch.cpp \ + $${PWD}/platform.cpp \ + $${PWD}/preprocessor.cpp \ + $${PWD}/programmemory.cpp \ + $${PWD}/reverseanalyzer.cpp \ + $${PWD}/settings.cpp \ + $${PWD}/summaries.cpp \ + $${PWD}/suppressions.cpp \ + $${PWD}/templatesimplifier.cpp \ + $${PWD}/timer.cpp \ + $${PWD}/token.cpp \ + $${PWD}/tokenlist.cpp \ + $${PWD}/utils.cpp \ + $${PWD}/vfvalue.cpp diff --git a/cppcheck-2.14.0/lib/library.cpp b/cppcheck-2.14.0/lib/library.cpp new file mode 100644 index 00000000..e140f02e --- /dev/null +++ b/cppcheck-2.14.0/lib/library.cpp @@ -0,0 +1,1793 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "library.h" + +#include "astutils.h" +#include "errortypes.h" +#include "mathlib.h" +#include "path.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenlist.h" +#include "utils.h" +#include "valueflow.h" +#include "vfvalue.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xml.h" + +static std::vector getnames(const char *names) +{ + std::vector ret; + while (const char *p = std::strchr(names,',')) { + ret.emplace_back(names, p-names); + names = p + 1; + } + ret.emplace_back(names); + return ret; +} + +static void gettokenlistfromvalid(const std::string& valid, bool cpp, TokenList& tokenList) +{ + std::istringstream istr(valid + ','); + tokenList.createTokens(istr, cpp ? Standards::Language::CPP : Standards::Language::C); // TODO: check result? + for (Token *tok = tokenList.front(); tok; tok = tok->next()) { + if (Token::Match(tok,"- %num%")) { + tok->str("-" + tok->strAt(1)); + tok->deleteNext(); + } + } +} + +Library::Error Library::load(const char exename[], const char path[]) +{ + if (std::strchr(path,',') != nullptr) { + std::string p(path); + for (;;) { + const std::string::size_type pos = p.find(','); + if (pos == std::string::npos) + break; + const Error &e = load(exename, p.substr(0,pos).c_str()); + if (e.errorcode != ErrorCode::OK) + return e; + p = p.substr(pos+1); + } + if (!p.empty()) + return load(exename, p.c_str()); + return Error(); + } + + std::string absolute_path; + // open file.. + tinyxml2::XMLDocument doc; + tinyxml2::XMLError error = doc.LoadFile(path); + if (error == tinyxml2::XML_ERROR_FILE_READ_ERROR && Path::getFilenameExtension(path).empty()) + // Reading file failed, try again... + error = tinyxml2::XML_ERROR_FILE_NOT_FOUND; + if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND) { + // failed to open file.. is there no extension? + std::string fullfilename(path); + if (Path::getFilenameExtension(fullfilename).empty()) { + fullfilename += ".cfg"; + error = doc.LoadFile(fullfilename.c_str()); + if (error != tinyxml2::XML_ERROR_FILE_NOT_FOUND) + absolute_path = Path::getAbsoluteFilePath(fullfilename); + } + + std::list cfgfolders; +#ifdef FILESDIR + cfgfolders.emplace_back(FILESDIR "/cfg"); +#endif + if (exename) { + const std::string exepath(Path::fromNativeSeparators(Path::getPathFromFilename(Path::getCurrentExecutablePath(exename)))); + cfgfolders.push_back(exepath + "cfg"); + cfgfolders.push_back(exepath + "../cfg"); + cfgfolders.push_back(exepath); + } + + while (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND && !cfgfolders.empty()) { + const std::string cfgfolder(cfgfolders.back()); + cfgfolders.pop_back(); + const char *sep = (!cfgfolder.empty() && endsWith(cfgfolder,'/') ? "" : "/"); + const std::string filename(cfgfolder + sep + fullfilename); + error = doc.LoadFile(filename.c_str()); + if (error != tinyxml2::XML_ERROR_FILE_NOT_FOUND) + absolute_path = Path::getAbsoluteFilePath(filename); + } + } else + absolute_path = Path::getAbsoluteFilePath(path); + + if (error == tinyxml2::XML_SUCCESS) { + if (mFiles.find(absolute_path) == mFiles.end()) { + Error err = load(doc); + if (err.errorcode == ErrorCode::OK) + mFiles.insert(absolute_path); + return err; + } + + return Error(ErrorCode::OK); // ignore duplicates + } + + if (error == tinyxml2::XML_ERROR_FILE_NOT_FOUND) + return Error(ErrorCode::FILE_NOT_FOUND); + + doc.PrintError(); + return Error(ErrorCode::BAD_XML); +} + +Library::Container::Yield Library::Container::yieldFrom(const std::string& yieldName) +{ + if (yieldName == "at_index") + return Container::Yield::AT_INDEX; + if (yieldName == "item") + return Container::Yield::ITEM; + if (yieldName == "buffer") + return Container::Yield::BUFFER; + if (yieldName == "buffer-nt") + return Container::Yield::BUFFER_NT; + if (yieldName == "start-iterator") + return Container::Yield::START_ITERATOR; + if (yieldName == "end-iterator") + return Container::Yield::END_ITERATOR; + if (yieldName == "iterator") + return Container::Yield::ITERATOR; + if (yieldName == "size") + return Container::Yield::SIZE; + if (yieldName == "empty") + return Container::Yield::EMPTY; + return Container::Yield::NO_YIELD; +} +Library::Container::Action Library::Container::actionFrom(const std::string& actionName) +{ + if (actionName == "resize") + return Container::Action::RESIZE; + if (actionName == "clear") + return Container::Action::CLEAR; + if (actionName == "push") + return Container::Action::PUSH; + if (actionName == "pop") + return Container::Action::POP; + if (actionName == "find") + return Container::Action::FIND; + if (actionName == "find-const") + return Container::Action::FIND_CONST; + if (actionName == "insert") + return Container::Action::INSERT; + if (actionName == "erase") + return Container::Action::ERASE; + if (actionName == "change-content") + return Container::Action::CHANGE_CONTENT; + if (actionName == "change-internal") + return Container::Action::CHANGE_INTERNAL; + if (actionName == "change") + return Container::Action::CHANGE; + return Container::Action::NO_ACTION; +} + +Library::Error Library::load(const tinyxml2::XMLDocument &doc) +{ + const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); + + if (rootnode == nullptr) { + doc.PrintError(); + return Error(ErrorCode::BAD_XML); + } + + if (strcmp(rootnode->Name(),"def") != 0) + return Error(ErrorCode::UNSUPPORTED_FORMAT, rootnode->Name()); + + const int format = rootnode->IntAttribute("format", 1); // Assume format version 1 if nothing else is specified (very old .cfg files had no 'format' attribute) + + if (format > 2 || format <= 0) + return Error(ErrorCode::UNSUPPORTED_FORMAT); + + std::set unknown_elements; + + for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { + const std::string nodename = node->Name(); + if (nodename == "memory" || nodename == "resource") { + // get allocationId to use.. + int allocationId = 0; + for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) { + if (strcmp(memorynode->Name(),"dealloc")==0) { + const auto names = getnames(memorynode->GetText()); + for (const auto& n : names) { + const std::map::const_iterator it = mDealloc.find(n); + if (it != mDealloc.end()) { + allocationId = it->second.groupId; + break; + } + } + if (allocationId != 0) + break; + } + } + if (allocationId == 0) { + if (nodename == "memory") + while (!ismemory(++mAllocId)); + else + while (!isresource(++mAllocId)); + allocationId = mAllocId; + } + + // add alloc/dealloc/use functions.. + for (const tinyxml2::XMLElement *memorynode = node->FirstChildElement(); memorynode; memorynode = memorynode->NextSiblingElement()) { + const std::string memorynodename = memorynode->Name(); + const auto names = getnames(memorynode->GetText()); + if (memorynodename == "alloc" || memorynodename == "realloc") { + AllocFunc temp = {0}; + temp.groupId = allocationId; + + temp.initData = memorynode->BoolAttribute("init", true); + temp.arg = memorynode->IntAttribute("arg", -1); + + const char *bufferSize = memorynode->Attribute("buffer-size"); + if (!bufferSize) + temp.bufferSize = AllocFunc::BufferSize::none; + else { + if (std::strncmp(bufferSize, "malloc", 6) == 0) + temp.bufferSize = AllocFunc::BufferSize::malloc; + else if (std::strncmp(bufferSize, "calloc", 6) == 0) + temp.bufferSize = AllocFunc::BufferSize::calloc; + else if (std::strncmp(bufferSize, "strdup", 6) == 0) + temp.bufferSize = AllocFunc::BufferSize::strdup; + else + return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, bufferSize); + temp.bufferSizeArg1 = 1; + temp.bufferSizeArg2 = 2; + if (bufferSize[6] == 0) { + // use default values + } else if (bufferSize[6] == ':' && bufferSize[7] >= '1' && bufferSize[7] <= '5') { + temp.bufferSizeArg1 = bufferSize[7] - '0'; + if (bufferSize[8] == ',' && bufferSize[9] >= '1' && bufferSize[9] <= '5') + temp.bufferSizeArg2 = bufferSize[9] - '0'; + } else + return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, bufferSize); + } + + if (memorynodename == "realloc") + temp.reallocArg = memorynode->IntAttribute("realloc-arg", 1); + + auto& map = (memorynodename == "realloc") ? mRealloc : mAlloc; + for (const auto& n : names) + map[n] = temp; + } else if (memorynodename == "dealloc") { + AllocFunc temp = {0}; + temp.groupId = allocationId; + temp.arg = memorynode->IntAttribute("arg", 1); + for (const auto& n : names) + mDealloc[n] = temp; + } else if (memorynodename == "use") + for (const auto& n : names) + functions[n].use = true; + else + unknown_elements.insert(memorynodename); + } + } + + else if (nodename == "define") { + const char *name = node->Attribute("name"); + if (name == nullptr) + return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); + const char *value = node->Attribute("value"); + if (value == nullptr) + return Error(ErrorCode::MISSING_ATTRIBUTE, "value"); + auto result = defines.insert(std::string(name) + " " + value); + if (!result.second) + return Error(ErrorCode::DUPLICATE_DEFINE, name); + } + + else if (nodename == "function") { + const char *name = node->Attribute("name"); + if (name == nullptr) + return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); + for (const std::string &s : getnames(name)) { + const Error &err = loadFunction(node, s, unknown_elements); + if (err.errorcode != ErrorCode::OK) + return err; + } + } + + else if (nodename == "reflection") { + for (const tinyxml2::XMLElement *reflectionnode = node->FirstChildElement(); reflectionnode; reflectionnode = reflectionnode->NextSiblingElement()) { + if (strcmp(reflectionnode->Name(), "call") != 0) { + unknown_elements.insert(reflectionnode->Name()); + continue; + } + + const char * const argString = reflectionnode->Attribute("arg"); + if (!argString) + return Error(ErrorCode::MISSING_ATTRIBUTE, "arg"); + + mReflection[reflectionnode->GetText()] = strToInt(argString); + } + } + + else if (nodename == "markup") { + const char * const extension = node->Attribute("ext"); + if (!extension) + return Error(ErrorCode::MISSING_ATTRIBUTE, "ext"); + mMarkupExtensions.insert(extension); + + mReportErrors[extension] = (node->Attribute("reporterrors", "true") != nullptr); + mProcessAfterCode[extension] = (node->Attribute("aftercode", "true") != nullptr); + + for (const tinyxml2::XMLElement *markupnode = node->FirstChildElement(); markupnode; markupnode = markupnode->NextSiblingElement()) { + const std::string markupnodename = markupnode->Name(); + if (markupnodename == "keywords") { + for (const tinyxml2::XMLElement *librarynode = markupnode->FirstChildElement(); librarynode; librarynode = librarynode->NextSiblingElement()) { + if (strcmp(librarynode->Name(), "keyword") == 0) { + const char* nodeName = librarynode->Attribute("name"); + if (nodeName == nullptr) + return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); + mKeywords[extension].insert(nodeName); + } else + unknown_elements.insert(librarynode->Name()); + } + } + + else if (markupnodename == "exported") { + for (const tinyxml2::XMLElement *exporter = markupnode->FirstChildElement(); exporter; exporter = exporter->NextSiblingElement()) { + if (strcmp(exporter->Name(), "exporter") != 0) { + unknown_elements.insert(exporter->Name()); + continue; + } + + const char * const prefix = exporter->Attribute("prefix"); + if (!prefix) + return Error(ErrorCode::MISSING_ATTRIBUTE, "prefix"); + + for (const tinyxml2::XMLElement *e = exporter->FirstChildElement(); e; e = e->NextSiblingElement()) { + const std::string ename = e->Name(); + if (ename == "prefix") + mExporters[prefix].addPrefix(e->GetText()); + else if (ename == "suffix") + mExporters[prefix].addSuffix(e->GetText()); + else + unknown_elements.insert(ename); + } + } + } + + else if (markupnodename == "imported") { + for (const tinyxml2::XMLElement *librarynode = markupnode->FirstChildElement(); librarynode; librarynode = librarynode->NextSiblingElement()) { + if (strcmp(librarynode->Name(), "importer") == 0) + mImporters[extension].insert(librarynode->GetText()); + else + unknown_elements.insert(librarynode->Name()); + } + } + + else if (markupnodename == "codeblocks") { + for (const tinyxml2::XMLElement *blocknode = markupnode->FirstChildElement(); blocknode; blocknode = blocknode->NextSiblingElement()) { + const std::string blocknodename = blocknode->Name(); + if (blocknodename == "block") { + const char * blockName = blocknode->Attribute("name"); + if (blockName) + mExecutableBlocks[extension].addBlock(blockName); + } else if (blocknodename == "structure") { + const char * start = blocknode->Attribute("start"); + if (start) + mExecutableBlocks[extension].setStart(start); + const char * end = blocknode->Attribute("end"); + if (end) + mExecutableBlocks[extension].setEnd(end); + const char * offset = blocknode->Attribute("offset"); + if (offset) { + // cppcheck-suppress templateInstantiation - TODO: fix this - see #11631 + mExecutableBlocks[extension].setOffset(strToInt(offset)); + } + } + + else + unknown_elements.insert(blocknodename); + } + } + + else + unknown_elements.insert(markupnodename); + } + } + + else if (nodename == "container") { + const char* const id = node->Attribute("id"); + if (!id) + return Error(ErrorCode::MISSING_ATTRIBUTE, "id"); + + Container& container = containers[id]; + + const char* const inherits = node->Attribute("inherits"); + if (inherits) { + const std::unordered_map::const_iterator i = containers.find(inherits); + if (i != containers.end()) + container = i->second; // Take values from parent and overwrite them if necessary + else + return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, inherits); + } + + const char* const startPattern = node->Attribute("startPattern"); + if (startPattern) { + container.startPattern = startPattern; + container.startPattern2 = startPattern; + if (!endsWith(container.startPattern, '<')) + container.startPattern2 += " !!::"; + } + const char* const endPattern = node->Attribute("endPattern"); + if (endPattern) + container.endPattern = endPattern; + const char* const itEndPattern = node->Attribute("itEndPattern"); + if (itEndPattern) + container.itEndPattern = itEndPattern; + const char* const opLessAllowed = node->Attribute("opLessAllowed"); + if (opLessAllowed) + container.opLessAllowed = strcmp(opLessAllowed, "true") == 0; + const char* const hasInitializerListConstructor = node->Attribute("hasInitializerListConstructor"); + if (hasInitializerListConstructor) + container.hasInitializerListConstructor = strcmp(hasInitializerListConstructor, "true") == 0; + const char* const view = node->Attribute("view"); + if (view) + container.view = strcmp(view, "true") == 0; + + for (const tinyxml2::XMLElement *containerNode = node->FirstChildElement(); containerNode; containerNode = containerNode->NextSiblingElement()) { + const std::string containerNodeName = containerNode->Name(); + if (containerNodeName == "size" || containerNodeName == "access" || containerNodeName == "other") { + for (const tinyxml2::XMLElement *functionNode = containerNode->FirstChildElement(); functionNode; functionNode = functionNode->NextSiblingElement()) { + if (strcmp(functionNode->Name(), "function") != 0) { + unknown_elements.insert(functionNode->Name()); + continue; + } + + const char* const functionName = functionNode->Attribute("name"); + if (!functionName) + return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); + + const char* const action_ptr = functionNode->Attribute("action"); + Container::Action action = Container::Action::NO_ACTION; + if (action_ptr) { + std::string actionName = action_ptr; + action = Container::actionFrom(actionName); + if (action == Container::Action::NO_ACTION) + return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, actionName); + } + + const char* const yield_ptr = functionNode->Attribute("yields"); + Container::Yield yield = Container::Yield::NO_YIELD; + if (yield_ptr) { + std::string yieldName = yield_ptr; + yield = Container::yieldFrom(yieldName); + if (yield == Container::Yield::NO_YIELD) + return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, yieldName); + } + + const char* const returnType = functionNode->Attribute("returnType"); + if (returnType) + container.functions[functionName].returnType = returnType; + + container.functions[functionName].action = action; + container.functions[functionName].yield = yield; + } + + if (containerNodeName == "size") { + const char* const templateArg = containerNode->Attribute("templateParameter"); + if (templateArg) + container.size_templateArgNo = strToInt(templateArg); + } else if (containerNodeName == "access") { + const char* const indexArg = containerNode->Attribute("indexOperator"); + if (indexArg) + container.arrayLike_indexOp = strcmp(indexArg, "array-like") == 0; + } + } else if (containerNodeName == "type") { + const char* const templateArg = containerNode->Attribute("templateParameter"); + if (templateArg) + container.type_templateArgNo = strToInt(templateArg); + + const char* const string = containerNode->Attribute("string"); + if (string) + container.stdStringLike = strcmp(string, "std-like") == 0; + const char* const associative = containerNode->Attribute("associative"); + if (associative) + container.stdAssociativeLike = strcmp(associative, "std-like") == 0; + const char* const unstable = containerNode->Attribute("unstable"); + if (unstable) { + std::string unstableType = unstable; + if (unstableType.find("erase") != std::string::npos) + container.unstableErase = true; + if (unstableType.find("insert") != std::string::npos) + container.unstableInsert = true; + } + } else if (containerNodeName == "rangeItemRecordType") { + for (const tinyxml2::XMLElement* memberNode = node->FirstChildElement(); memberNode; memberNode = memberNode->NextSiblingElement()) { + const char *memberName = memberNode->Attribute("name"); + const char *memberTemplateParameter = memberNode->Attribute("templateParameter"); + Container::RangeItemRecordTypeItem member; + member.name = memberName ? memberName : ""; + member.templateParameter = memberTemplateParameter ? strToInt(memberTemplateParameter) : -1; + container.rangeItemRecordType.emplace_back(std::move(member)); + } + } else + unknown_elements.insert(containerNodeName); + } + } + + else if (nodename == "smart-pointer") { + const char *className = node->Attribute("class-name"); + if (!className) + return Error(ErrorCode::MISSING_ATTRIBUTE, "class-name"); + SmartPointer& smartPointer = smartPointers[className]; + smartPointer.name = className; + for (const tinyxml2::XMLElement* smartPointerNode = node->FirstChildElement(); smartPointerNode; + smartPointerNode = smartPointerNode->NextSiblingElement()) { + const std::string smartPointerNodeName = smartPointerNode->Name(); + if (smartPointerNodeName == "unique") + smartPointer.unique = true; + } + } + + else if (nodename == "type-checks") { + for (const tinyxml2::XMLElement *checkNode = node->FirstChildElement(); checkNode; checkNode = checkNode->NextSiblingElement()) { + const std::string &checkName = checkNode->Name(); + for (const tinyxml2::XMLElement *checkTypeNode = checkNode->FirstChildElement(); checkTypeNode; checkTypeNode = checkTypeNode->NextSiblingElement()) { + const std::string checkTypeName = checkTypeNode->Name(); + const char *typeName = checkTypeNode->GetText(); + if (!typeName) + continue; + if (checkTypeName == "check") + mTypeChecks[std::pair(checkName, typeName)] = TypeCheck::check; + else if (checkTypeName == "suppress") + mTypeChecks[std::pair(checkName, typeName)] = TypeCheck::suppress; + else if (checkTypeName == "checkFiniteLifetime") + mTypeChecks[std::pair(checkName, typeName)] = TypeCheck::checkFiniteLifetime; + } + } + } + + else if (nodename == "podtype") { + const char * const name = node->Attribute("name"); + if (!name) + return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); + PodType podType = {0}; + podType.stdtype = PodType::Type::NO; + const char * const stdtype = node->Attribute("stdtype"); + if (stdtype) { + if (std::strcmp(stdtype, "bool") == 0) + podType.stdtype = PodType::Type::BOOL; + else if (std::strcmp(stdtype, "char") == 0) + podType.stdtype = PodType::Type::CHAR; + else if (std::strcmp(stdtype, "short") == 0) + podType.stdtype = PodType::Type::SHORT; + else if (std::strcmp(stdtype, "int") == 0) + podType.stdtype = PodType::Type::INT; + else if (std::strcmp(stdtype, "long") == 0) + podType.stdtype = PodType::Type::LONG; + else if (std::strcmp(stdtype, "long long") == 0) + podType.stdtype = PodType::Type::LONGLONG; + } + const char * const size = node->Attribute("size"); + if (size) + podType.size = strToInt(size); + const char * const sign = node->Attribute("sign"); + if (sign) + podType.sign = *sign; + for (const std::string &s : getnames(name)) + mPodTypes[s] = podType; + } + + else if (nodename == "platformtype") { + const char * const type_name = node->Attribute("name"); + if (type_name == nullptr) + return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); + const char *value = node->Attribute("value"); + if (value == nullptr) + return Error(ErrorCode::MISSING_ATTRIBUTE, "value"); + PlatformType type; + type.mType = value; + std::set platform; + for (const tinyxml2::XMLElement *typenode = node->FirstChildElement(); typenode; typenode = typenode->NextSiblingElement()) { + const std::string typenodename = typenode->Name(); + if (typenodename == "platform") { + const char * const type_attribute = typenode->Attribute("type"); + if (type_attribute == nullptr) + return Error(ErrorCode::MISSING_ATTRIBUTE, "type"); + platform.insert(type_attribute); + } else if (typenodename == "signed") + type.mSigned = true; + else if (typenodename == "unsigned") + type.mUnsigned = true; + else if (typenodename == "long") + type.mLong = true; + else if (typenodename == "pointer") + type.mPointer= true; + else if (typenodename == "ptr_ptr") + type.mPtrPtr = true; + else if (typenodename == "const_ptr") + type.mConstPtr = true; + else + unknown_elements.insert(typenodename); + } + if (platform.empty()) { + const PlatformType * const type_ptr = platform_type(type_name, emptyString); + if (type_ptr) { + if (*type_ptr == type) + return Error(ErrorCode::DUPLICATE_PLATFORM_TYPE, type_name); + return Error(ErrorCode::PLATFORM_TYPE_REDEFINED, type_name); + } + mPlatformTypes[type_name] = std::move(type); + } else { + for (const std::string &p : platform) { + const PlatformType * const type_ptr = platform_type(type_name, p); + if (type_ptr) { + if (*type_ptr == type) + return Error(ErrorCode::DUPLICATE_PLATFORM_TYPE, type_name); + return Error(ErrorCode::PLATFORM_TYPE_REDEFINED, type_name); + } + mPlatforms[p].mPlatformTypes[type_name] = type; + } + } + } + + else if (nodename == "entrypoint") { + const char * const type_name = node->Attribute("name"); + if (type_name == nullptr) + return Error(ErrorCode::MISSING_ATTRIBUTE, "name"); + mEntrypoints.emplace(type_name); + } + + else + unknown_elements.insert(nodename); + } + if (!unknown_elements.empty()) { + std::string str; + for (std::set::const_iterator i = unknown_elements.cbegin(); i != unknown_elements.cend();) { + str += *i; + if (++i != unknown_elements.end()) + str += ", "; + } + return Error(ErrorCode::UNKNOWN_ELEMENT, str); + } + return Error(ErrorCode::OK); +} + +Library::Error Library::loadFunction(const tinyxml2::XMLElement * const node, const std::string &name, std::set &unknown_elements) +{ + if (name.empty()) + return Error(ErrorCode::OK); + + // TODO: write debug warning if we modify an existing entry + Function& func = functions[name]; + + for (const tinyxml2::XMLElement *functionnode = node->FirstChildElement(); functionnode; functionnode = functionnode->NextSiblingElement()) { + const std::string functionnodename = functionnode->Name(); + if (functionnodename == "noreturn") { + const char * const text = functionnode->GetText(); + if (strcmp(text, "false") == 0) + mNoReturn[name] = FalseTrueMaybe::False; + else if (strcmp(text, "maybe") == 0) + mNoReturn[name] = FalseTrueMaybe::Maybe; + else + mNoReturn[name] = FalseTrueMaybe::True; // Safe + } else if (functionnodename == "pure") + func.ispure = true; + else if (functionnodename == "const") { + func.ispure = true; + func.isconst = true; // a constant function is pure + } else if (functionnodename == "leak-ignore") + func.leakignore = true; + else if (functionnodename == "not-overlapping-data") { + NonOverlappingData nonOverlappingData; + nonOverlappingData.ptr1Arg = functionnode->IntAttribute("ptr1-arg", -1); + nonOverlappingData.ptr2Arg = functionnode->IntAttribute("ptr2-arg", -1); + nonOverlappingData.sizeArg = functionnode->IntAttribute("size-arg", -1); + nonOverlappingData.strlenArg = functionnode->IntAttribute("strlen-arg", -1); + mNonOverlappingData[name] = nonOverlappingData; + } else if (functionnodename == "use-retval") { + func.useretval = Library::UseRetValType::DEFAULT; + if (const char *type = functionnode->Attribute("type")) + if (std::strcmp(type, "error-code") == 0) + func.useretval = Library::UseRetValType::ERROR_CODE; + } else if (functionnodename == "returnValue") { + if (const char *expr = functionnode->GetText()) + mReturnValue[name] = expr; + if (const char *type = functionnode->Attribute("type")) + mReturnValueType[name] = type; + if (const char *container = functionnode->Attribute("container")) + mReturnValueContainer[name] = strToInt(container); + // cppcheck-suppress shadowFunction - TODO: fix this + if (const char *unknownReturnValues = functionnode->Attribute("unknownValues")) { + if (std::strcmp(unknownReturnValues, "all") == 0) { + std::vector values{LLONG_MIN, LLONG_MAX}; + mUnknownReturnValues[name] = std::move(values); + } + } + } else if (functionnodename == "arg") { + const char* argNrString = functionnode->Attribute("nr"); + if (!argNrString) + return Error(ErrorCode::MISSING_ATTRIBUTE, "nr"); + const bool bAnyArg = strcmp(argNrString, "any") == 0; + const bool bVariadicArg = strcmp(argNrString, "variadic") == 0; + const int nr = (bAnyArg || bVariadicArg) ? -1 : strToInt(argNrString); + ArgumentChecks &ac = func.argumentChecks[nr]; + ac.optional = functionnode->Attribute("default") != nullptr; + ac.variadic = bVariadicArg; + const char * const argDirection = functionnode->Attribute("direction"); + if (argDirection) { + const size_t argDirLen = strlen(argDirection); + if (!strncmp(argDirection, "in", argDirLen)) { + ac.direction = ArgumentChecks::Direction::DIR_IN; + } else if (!strncmp(argDirection, "out", argDirLen)) { + ac.direction = ArgumentChecks::Direction::DIR_OUT; + } else if (!strncmp(argDirection, "inout", argDirLen)) { + ac.direction = ArgumentChecks::Direction::DIR_INOUT; + } + } + for (const tinyxml2::XMLElement *argnode = functionnode->FirstChildElement(); argnode; argnode = argnode->NextSiblingElement()) { + const std::string argnodename = argnode->Name(); + int indirect = 0; + const char * const indirectStr = argnode->Attribute("indirect"); + if (indirectStr) + indirect = strToInt(indirectStr); + if (argnodename == "not-bool") + ac.notbool = true; + else if (argnodename == "not-null") + ac.notnull = true; + else if (argnodename == "not-uninit") + ac.notuninit = indirect; + else if (argnodename == "formatstr") + ac.formatstr = true; + else if (argnodename == "strz") + ac.strz = true; + else if (argnodename == "valid") { + // Validate the validation expression + const char *p = argnode->GetText(); + if (!isCompliantValidationExpression(p)) + return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, (!p ? "\"\"" : p)); + // Set validation expression + ac.valid = p; + } + else if (argnodename == "minsize") { + const char *typeattr = argnode->Attribute("type"); + if (!typeattr) + return Error(ErrorCode::MISSING_ATTRIBUTE, "type"); + + ArgumentChecks::MinSize::Type type; + if (strcmp(typeattr,"strlen")==0) + type = ArgumentChecks::MinSize::Type::STRLEN; + else if (strcmp(typeattr,"argvalue")==0) + type = ArgumentChecks::MinSize::Type::ARGVALUE; + else if (strcmp(typeattr,"sizeof")==0) + type = ArgumentChecks::MinSize::Type::SIZEOF; + else if (strcmp(typeattr,"mul")==0) + type = ArgumentChecks::MinSize::Type::MUL; + else if (strcmp(typeattr,"value")==0) + type = ArgumentChecks::MinSize::Type::VALUE; + else + return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, typeattr); + + if (type == ArgumentChecks::MinSize::Type::VALUE) { + const char *valueattr = argnode->Attribute("value"); + if (!valueattr) + return Error(ErrorCode::MISSING_ATTRIBUTE, "value"); + long long minsizevalue = 0; + try { + minsizevalue = strToInt(valueattr); + } catch (const std::runtime_error&) { + return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, valueattr); + } + if (minsizevalue <= 0) + return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, valueattr); + ac.minsizes.emplace_back(type, 0); + ac.minsizes.back().value = minsizevalue; + } else { + const char *argattr = argnode->Attribute("arg"); + if (!argattr) + return Error(ErrorCode::MISSING_ATTRIBUTE, "arg"); + if (strlen(argattr) != 1 || argattr[0]<'0' || argattr[0]>'9') + return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, argattr); + + ac.minsizes.reserve(type == ArgumentChecks::MinSize::Type::MUL ? 2 : 1); + ac.minsizes.emplace_back(type, argattr[0] - '0'); + if (type == ArgumentChecks::MinSize::Type::MUL) { + const char *arg2attr = argnode->Attribute("arg2"); + if (!arg2attr) + return Error(ErrorCode::MISSING_ATTRIBUTE, "arg2"); + if (strlen(arg2attr) != 1 || arg2attr[0]<'0' || arg2attr[0]>'9') + return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, arg2attr); + ac.minsizes.back().arg2 = arg2attr[0] - '0'; + } + } + const char* baseTypeAttr = argnode->Attribute("baseType"); // used by VALUE, ARGVALUE + if (baseTypeAttr) + ac.minsizes.back().baseType = baseTypeAttr; + } + + else if (argnodename == "iterator") { + ac.iteratorInfo.it = true; + const char* str = argnode->Attribute("type"); + ac.iteratorInfo.first = (str && std::strcmp(str, "first") == 0); + ac.iteratorInfo.last = (str && std::strcmp(str, "last") == 0); + ac.iteratorInfo.container = argnode->IntAttribute("container", 0); + } + + else + unknown_elements.insert(argnodename); + } + if (ac.notuninit == 0) + ac.notuninit = ac.notnull ? 1 : 0; + } else if (functionnodename == "ignorefunction") { + func.ignore = true; + } else if (functionnodename == "formatstr") { + func.formatstr = true; + const tinyxml2::XMLAttribute* scan = functionnode->FindAttribute("scan"); + const tinyxml2::XMLAttribute* secure = functionnode->FindAttribute("secure"); + func.formatstr_scan = scan && scan->BoolValue(); + func.formatstr_secure = secure && secure->BoolValue(); + } else if (functionnodename == "warn") { + WarnInfo wi; + const char* const severity = functionnode->Attribute("severity"); + if (severity == nullptr) + return Error(ErrorCode::MISSING_ATTRIBUTE, "severity"); + wi.severity = severityFromString(severity); + + const char* const cstd = functionnode->Attribute("cstd"); + if (cstd) { + if (!wi.standards.setC(cstd)) + return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, cstd); + } else + wi.standards.c = Standards::C89; + + const char* const cppstd = functionnode->Attribute("cppstd"); + if (cppstd) { + if (!wi.standards.setCPP(cppstd)) + return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, cppstd); + } else + wi.standards.cpp = Standards::CPP03; + + const char* const reason = functionnode->Attribute("reason"); + const char* const alternatives = functionnode->Attribute("alternatives"); + if (reason && alternatives) { + // Construct message + wi.message = std::string(reason) + " function '" + name + "' called. It is recommended to use "; + std::vector alt = getnames(alternatives); + for (std::size_t i = 0; i < alt.size(); ++i) { + wi.message += "'" + alt[i] + "'"; + if (i == alt.size() - 1) + wi.message += " instead."; + else if (i == alt.size() - 2) + wi.message += " or "; + else + wi.message += ", "; + } + } else { + const char * const message = functionnode->GetText(); + if (!message) + return Error(ErrorCode::MISSING_ATTRIBUTE, "\"reason\" and \"alternatives\" or some text."); + + wi.message = message; + } + + functionwarn[name] = std::move(wi); + } else if (functionnodename == "container") { + const char* const action_ptr = functionnode->Attribute("action"); + Container::Action action = Container::Action::NO_ACTION; + if (action_ptr) { + std::string actionName = action_ptr; + action = Container::actionFrom(actionName); + if (action == Container::Action::NO_ACTION) + return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, actionName); + } + func.containerAction = action; + + const char* const yield_ptr = functionnode->Attribute("yields"); + Container::Yield yield = Container::Yield::NO_YIELD; + if (yield_ptr) { + std::string yieldName = yield_ptr; + yield = Container::yieldFrom(yieldName); + if (yield == Container::Yield::NO_YIELD) + return Error(ErrorCode::BAD_ATTRIBUTE_VALUE, yieldName); + } + func.containerYield = yield; + + const char* const returnType = functionnode->Attribute("returnType"); + if (returnType) + func.returnType = returnType; + } else + unknown_elements.insert(functionnodename); + } + return Error(ErrorCode::OK); +} + +bool Library::isIntArgValid(const Token *ftok, int argnr, const MathLib::bigint argvalue) const +{ + const ArgumentChecks *ac = getarg(ftok, argnr); + if (!ac || ac->valid.empty()) + return true; + if (ac->valid.find('.') != std::string::npos) + return isFloatArgValid(ftok, argnr, argvalue); + TokenList tokenList(nullptr); + gettokenlistfromvalid(ac->valid, ftok->isCpp(), tokenList); + for (const Token *tok = tokenList.front(); tok; tok = tok->next()) { + if (tok->isNumber() && argvalue == MathLib::toBigNumber(tok->str())) + return true; + if (Token::Match(tok, "%num% : %num%") && argvalue >= MathLib::toBigNumber(tok->str()) && argvalue <= MathLib::toBigNumber(tok->strAt(2))) + return true; + if (Token::Match(tok, "%num% : ,") && argvalue >= MathLib::toBigNumber(tok->str())) + return true; + if ((!tok->previous() || tok->previous()->str() == ",") && Token::Match(tok,": %num%") && argvalue <= MathLib::toBigNumber(tok->strAt(1))) + return true; + } + return false; +} + +bool Library::isFloatArgValid(const Token *ftok, int argnr, double argvalue) const +{ + const ArgumentChecks *ac = getarg(ftok, argnr); + if (!ac || ac->valid.empty()) + return true; + TokenList tokenList(nullptr); + gettokenlistfromvalid(ac->valid, ftok->isCpp(), tokenList); + for (const Token *tok = tokenList.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "%num% : %num%") && argvalue >= MathLib::toDoubleNumber(tok->str()) && argvalue <= MathLib::toDoubleNumber(tok->strAt(2))) + return true; + if (Token::Match(tok, "%num% : ,") && argvalue >= MathLib::toDoubleNumber(tok->str())) + return true; + if ((!tok->previous() || tok->previous()->str() == ",") && Token::Match(tok,": %num%") && argvalue <= MathLib::toDoubleNumber(tok->strAt(1))) + return true; + if (Token::Match(tok, "%num%") && MathLib::isFloat(tok->str()) && MathLib::isEqual(tok->str(), MathLib::toString(argvalue))) + return true; + if (Token::Match(tok, "! %num%") && MathLib::isFloat(tok->next()->str())) + return MathLib::isNotEqual(tok->next()->str(), MathLib::toString(argvalue)); + } + return false; +} + +std::string Library::getFunctionName(const Token *ftok, bool &error) const +{ + if (!ftok) { + error = true; + return ""; + } + if (ftok->isName()) { + if (Token::simpleMatch(ftok->astParent(), "::")) + return ftok->str(); + for (const Scope *scope = ftok->scope(); scope; scope = scope->nestedIn) { + if (!scope->isClassOrStruct()) + continue; + const std::vector &derivedFrom = scope->definedType->derivedFrom; + for (const Type::BaseInfo & baseInfo : derivedFrom) { + std::string name; + const Token* tok = baseInfo.nameTok; // baseInfo.name still contains template parameters, but is missing namespaces + if (tok->str() == "::") + tok = tok->next(); + while (Token::Match(tok, "%name%|::")) { + name += tok->str(); + tok = tok->next(); + } + name += "::" + ftok->str(); + if (functions.find(name) != functions.end() && matchArguments(ftok, name)) + return name; + } + } + return ftok->str(); + } + if (ftok->str() == "::") { + if (!ftok->astOperand2()) + return getFunctionName(ftok->astOperand1(), error); + return getFunctionName(ftok->astOperand1(),error) + "::" + getFunctionName(ftok->astOperand2(),error); + } + if (ftok->str() == "." && ftok->astOperand1()) { + const std::string type = astCanonicalType(ftok->astOperand1(), ftok->originalName() == "->"); + if (type.empty()) { + error = true; + return ""; + } + + return type + "::" + getFunctionName(ftok->astOperand2(),error); + } + error = true; + return ""; +} + +std::string Library::getFunctionName(const Token *ftok) const +{ + if (!Token::Match(ftok, "%name% )| (") && (ftok->strAt(-1) != "&" || ftok->previous()->astOperand2())) + return ""; + + // Lookup function name using AST.. + if (ftok->astParent()) { + bool error = false; + const Token * tok = ftok->astParent()->isUnaryOp("&") ? ftok->astParent()->astOperand1() : ftok->next()->astOperand1(); + std::string ret = getFunctionName(tok, error); + if (error) + return {}; + if (startsWith(ret, "::")) + ret.erase(0, 2); + return ret; + } + + // Lookup function name without using AST.. + if (Token::simpleMatch(ftok->previous(), ".")) + return ""; + if (!Token::Match(ftok->tokAt(-2), "%name% ::")) + return ftok->str(); + std::string ret(ftok->str()); + ftok = ftok->tokAt(-2); + while (Token::Match(ftok, "%name% ::")) { + ret = ftok->str() + "::" + ret; + ftok = ftok->tokAt(-2); + } + return ret; +} + +bool Library::isnullargbad(const Token *ftok, int argnr) const +{ + const ArgumentChecks *arg = getarg(ftok, argnr); + if (!arg) { + // scan format string argument should not be null + const std::string funcname = getFunctionName(ftok); + const std::unordered_map::const_iterator it = functions.find(funcname); + if (it != functions.cend() && it->second.formatstr && it->second.formatstr_scan) + return true; + } + return arg && arg->notnull; +} + +bool Library::isuninitargbad(const Token *ftok, int argnr, int indirect, bool *hasIndirect) const +{ + const ArgumentChecks *arg = getarg(ftok, argnr); + if (!arg) { + // non-scan format string argument should not be uninitialized + const std::string funcname = getFunctionName(ftok); + const std::unordered_map::const_iterator it = functions.find(funcname); + if (it != functions.cend() && it->second.formatstr && !it->second.formatstr_scan) + return true; + } + if (hasIndirect && arg && arg->notuninit >= 1) + *hasIndirect = true; + return arg && arg->notuninit >= indirect; +} + + +/** get allocation info for function */ +const Library::AllocFunc* Library::getAllocFuncInfo(const Token *tok) const +{ + while (Token::simpleMatch(tok, "::")) + tok = tok->astOperand2() ? tok->astOperand2() : tok->astOperand1(); + const std::string funcname = getFunctionName(tok); + return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mAlloc, funcname); +} + +/** get deallocation info for function */ +const Library::AllocFunc* Library::getDeallocFuncInfo(const Token *tok) const +{ + while (Token::simpleMatch(tok, "::")) + tok = tok->astOperand2() ? tok->astOperand2() : tok->astOperand1(); + const std::string funcname = getFunctionName(tok); + return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mDealloc, funcname); +} + +/** get reallocation info for function */ +const Library::AllocFunc* Library::getReallocFuncInfo(const Token *tok) const +{ + while (Token::simpleMatch(tok, "::")) + tok = tok->astOperand2() ? tok->astOperand2() : tok->astOperand1(); + const std::string funcname = getFunctionName(tok); + return isNotLibraryFunction(tok) && functions.find(funcname) != functions.end() ? nullptr : getAllocDealloc(mRealloc, funcname); +} + +/** get allocation id for function */ +int Library::getAllocId(const Token *tok, int arg) const +{ + const Library::AllocFunc* af = getAllocFuncInfo(tok); + return (af && af->arg == arg) ? af->groupId : 0; +} + +/** get deallocation id for function */ +int Library::getDeallocId(const Token *tok, int arg) const +{ + const Library::AllocFunc* af = getDeallocFuncInfo(tok); + return (af && af->arg == arg) ? af->groupId : 0; +} + +/** get reallocation id for function */ +int Library::getReallocId(const Token *tok, int arg) const +{ + const Library::AllocFunc* af = getReallocFuncInfo(tok); + return (af && af->arg == arg) ? af->groupId : 0; +} + + +const Library::ArgumentChecks * Library::getarg(const Token *ftok, int argnr) const +{ + if (isNotLibraryFunction(ftok)) + return nullptr; + const std::unordered_map::const_iterator it1 = functions.find(getFunctionName(ftok)); + if (it1 == functions.cend()) + return nullptr; + const std::map::const_iterator it2 = it1->second.argumentChecks.find(argnr); + if (it2 != it1->second.argumentChecks.cend()) + return &it2->second; + const std::map::const_iterator it3 = it1->second.argumentChecks.find(-1); + if (it3 != it1->second.argumentChecks.cend()) + return &it3->second; + return nullptr; +} + +bool Library::isScopeNoReturn(const Token *end, std::string *unknownFunc) const +{ + if (unknownFunc) + unknownFunc->clear(); + + if (Token::Match(end->tokAt(-2), "!!{ ; }")) { + const Token *lastTop = end->tokAt(-2)->astTop(); + if (Token::simpleMatch(lastTop, "<<") && + Token::simpleMatch(lastTop->astOperand1(), "(") && + Token::Match(lastTop->astOperand1()->previous(), "%name% (")) + return isnoreturn(lastTop->astOperand1()->previous()); + } + + if (!Token::simpleMatch(end->tokAt(-2), ") ; }")) + return false; + + const Token *funcname = end->linkAt(-2)->previous(); + const Token *start = funcname; + if (Token::Match(funcname->tokAt(-3),"( * %name% )")) { + funcname = funcname->previous(); + start = funcname->tokAt(-3); + } else if (funcname->isName()) { + while (Token::Match(start, "%name%|.|::")) + start = start->previous(); + } else { + return false; + } + if (Token::Match(start,"[;{}]") && Token::Match(funcname, "%name% )| (")) { + if (funcname->isKeyword()) + return false; + if (funcname->str() == "exit") + return true; + if (!isnotnoreturn(funcname)) { + if (unknownFunc && !isnoreturn(funcname)) + *unknownFunc = funcname->str(); + return true; + } + } + return false; +} + +const Library::Container* Library::detectContainerInternal(const Token* const typeStart, DetectContainer detect, bool* isIterator, bool withoutStd) const +{ + const Token* firstLinkedTok = nullptr; + for (const Token* tok = typeStart; tok && !tok->varId(); tok = tok->next()) { + if (!tok->link()) + continue; + + firstLinkedTok = tok; + break; + } + + for (const std::pair & c : containers) { + const Container& container = c.second; + if (container.startPattern.empty()) + continue; + + const int offset = (withoutStd && startsWith(container.startPattern2, "std :: ")) ? 7 : 0; + + // If endPattern is undefined, it will always match, but itEndPattern has to be defined. + if (detect != IteratorOnly && container.endPattern.empty()) { + if (!Token::Match(typeStart, container.startPattern2.c_str() + offset)) + continue; + + if (isIterator) + *isIterator = false; + return &container; + } + + if (!firstLinkedTok) + continue; + + const bool matchedStartPattern = Token::Match(typeStart, container.startPattern2.c_str() + offset); + if (!matchedStartPattern) + continue; + + if (detect != ContainerOnly && Token::Match(firstLinkedTok->link(), container.itEndPattern.c_str())) { + if (isIterator) + *isIterator = true; + return &container; + } + if (detect != IteratorOnly && Token::Match(firstLinkedTok->link(), container.endPattern.c_str())) { + if (isIterator) + *isIterator = false; + return &container; + } + } + return nullptr; +} + +const Library::Container* Library::detectContainer(const Token* typeStart) const +{ + return detectContainerInternal(typeStart, ContainerOnly); +} + +const Library::Container* Library::detectIterator(const Token* typeStart) const +{ + return detectContainerInternal(typeStart, IteratorOnly); +} + +const Library::Container* Library::detectContainerOrIterator(const Token* typeStart, bool* isIterator, bool withoutStd) const +{ + bool res; + const Library::Container* c = detectContainerInternal(typeStart, Both, &res, withoutStd); + if (c && isIterator) + *isIterator = res; + return c; +} + +bool Library::isContainerYield(const Token * const cond, Library::Container::Yield y, const std::string& fallback) +{ + if (!cond) + return false; + if (cond->str() == "(") { + const Token* tok = cond->astOperand1(); + if (tok && tok->str() == ".") { + if (tok->astOperand1() && tok->astOperand1()->valueType()) { + if (const Library::Container *container = tok->astOperand1()->valueType()->container) { + return tok->astOperand2() && y == container->getYield(tok->astOperand2()->str()); + } + } else if (!fallback.empty()) { + return Token::simpleMatch(cond, "( )") && cond->previous()->str() == fallback; + } + } + } + return false; +} + +// returns true if ftok is not a library function +bool Library::isNotLibraryFunction(const Token *ftok) const +{ + if (ftok->isKeyword() || ftok->isStandardType()) + return true; + + if (ftok->function() && ftok->function()->nestedIn && ftok->function()->nestedIn->type != Scope::eGlobal) + return true; + + // variables are not library functions. + if (ftok->varId()) + return true; + + return !matchArguments(ftok, getFunctionName(ftok)); +} + +bool Library::matchArguments(const Token *ftok, const std::string &functionName) const +{ + if (functionName.empty()) + return false; + const int callargs = numberOfArgumentsWithoutAst(ftok); + const std::unordered_map::const_iterator it = functions.find(functionName); + if (it == functions.cend()) + return false; + int args = 0; + int firstOptionalArg = -1; + for (const std::pair & argCheck : it->second.argumentChecks) { + if (argCheck.first > args) + args = argCheck.first; + if (argCheck.second.optional && (firstOptionalArg == -1 || firstOptionalArg > argCheck.first)) + firstOptionalArg = argCheck.first; + + if (argCheck.second.formatstr || argCheck.second.variadic) + return args <= callargs; + } + return (firstOptionalArg < 0) ? args == callargs : (callargs >= firstOptionalArg-1 && callargs <= args); +} + +const Library::WarnInfo* Library::getWarnInfo(const Token* ftok) const +{ + if (isNotLibraryFunction(ftok)) + return nullptr; + const std::map::const_iterator i = functionwarn.find(getFunctionName(ftok)); + if (i == functionwarn.cend()) + return nullptr; + return &i->second; +} + +bool Library::isCompliantValidationExpression(const char* p) +{ + if (!p || !*p) + return false; + + bool error = false; + bool range = false; + bool has_dot = false; + bool has_E = false; + + error = *p == '.'; + for (; *p; p++) { + if (std::isdigit(*p)) { + error |= (*(p + 1) == '-'); + } + else if (*p == ':') { + // cppcheck-suppress bitwiseOnBoolean - TODO: fix this + error |= range | (*(p + 1) == '.'); + range = true; + has_dot = false; + has_E = false; + } + else if ((*p == '-') || (*p == '+')) { + error |= (!std::isdigit(*(p + 1))); + } + else if (*p == ',') { + range = false; + error |= *(p + 1) == '.'; + has_dot = false; + has_E = false; + } else if (*p == '.') { + // cppcheck-suppress bitwiseOnBoolean - TODO: fix this + error |= has_dot | (!std::isdigit(*(p + 1))); + has_dot = true; + } else if (*p == 'E' || *p == 'e') { + error |= has_E; + has_E = true; + } else if (*p == '!') { + error |= !((*(p+1) == '-') || (*(p+1) == '+') || (std::isdigit(*(p + 1)))); + } else + return false; + } + return !error; +} + +bool Library::formatstr_function(const Token* ftok) const +{ + if (isNotLibraryFunction(ftok)) + return false; + + const std::unordered_map::const_iterator it = functions.find(getFunctionName(ftok)); + if (it != functions.cend()) + return it->second.formatstr; + return false; +} + +int Library::formatstr_argno(const Token* ftok) const +{ + const std::map& argumentChecksFunc = functions.at(getFunctionName(ftok)).argumentChecks; + auto it = std::find_if(argumentChecksFunc.cbegin(), argumentChecksFunc.cend(), [](const std::pair& a) { + return a.second.formatstr; + }); + return it == argumentChecksFunc.cend() ? -1 : it->first - 1; +} + +bool Library::formatstr_scan(const Token* ftok) const +{ + return functions.at(getFunctionName(ftok)).formatstr_scan; +} + +bool Library::formatstr_secure(const Token* ftok) const +{ + return functions.at(getFunctionName(ftok)).formatstr_secure; +} + +const Library::NonOverlappingData* Library::getNonOverlappingData(const Token *ftok) const +{ + if (isNotLibraryFunction(ftok)) + return nullptr; + const std::unordered_map::const_iterator it = mNonOverlappingData.find(getFunctionName(ftok)); + return (it != mNonOverlappingData.cend()) ? &it->second : nullptr; +} + +Library::UseRetValType Library::getUseRetValType(const Token *ftok) const +{ + if (isNotLibraryFunction(ftok)) { + if (Token::simpleMatch(ftok->astParent(), ".")) { + const Token* contTok = ftok->astParent()->astOperand1(); + using Yield = Library::Container::Yield; + const Yield yield = astContainerYield(contTok); + if (yield == Yield::START_ITERATOR || yield == Yield::END_ITERATOR || yield == Yield::AT_INDEX || + yield == Yield::SIZE || yield == Yield::EMPTY || yield == Yield::BUFFER || yield == Yield::BUFFER_NT || + ((yield == Yield::ITEM || yield == Yield::ITERATOR) && astContainerAction(contTok) == Library::Container::Action::NO_ACTION)) + return Library::UseRetValType::DEFAULT; + } + return Library::UseRetValType::NONE; + } + const std::unordered_map::const_iterator it = functions.find(getFunctionName(ftok)); + if (it != functions.cend()) + return it->second.useretval; + return Library::UseRetValType::NONE; +} + +const std::string& Library::returnValue(const Token *ftok) const +{ + if (isNotLibraryFunction(ftok)) + return emptyString; + const std::map::const_iterator it = mReturnValue.find(getFunctionName(ftok)); + return it != mReturnValue.cend() ? it->second : emptyString; +} + +const std::string& Library::returnValueType(const Token *ftok) const +{ + if (isNotLibraryFunction(ftok)) { + if (Token::simpleMatch(ftok->astParent(), ".") && ftok->astParent()->astOperand1()) { + const Token* contTok = ftok->astParent()->astOperand1(); + if (contTok->valueType() && contTok->valueType()->container) + return contTok->valueType()->container->getReturnType(ftok->str()); + } + return emptyString; + } + const std::map::const_iterator it = mReturnValueType.find(getFunctionName(ftok)); + return it != mReturnValueType.cend() ? it->second : emptyString; +} + +int Library::returnValueContainer(const Token *ftok) const +{ + if (isNotLibraryFunction(ftok)) + return -1; + const std::map::const_iterator it = mReturnValueContainer.find(getFunctionName(ftok)); + return it != mReturnValueContainer.cend() ? it->second : -1; +} + +std::vector Library::unknownReturnValues(const Token *ftok) const +{ + if (isNotLibraryFunction(ftok)) + return std::vector(); + const std::map>::const_iterator it = mUnknownReturnValues.find(getFunctionName(ftok)); + return (it == mUnknownReturnValues.cend()) ? std::vector() : it->second; +} + +const Library::Function *Library::getFunction(const Token *ftok) const +{ + if (isNotLibraryFunction(ftok)) + return nullptr; + const std::unordered_map::const_iterator it1 = functions.find(getFunctionName(ftok)); + if (it1 == functions.cend()) + return nullptr; + return &it1->second; +} + + +bool Library::hasminsize(const Token *ftok) const +{ + if (isNotLibraryFunction(ftok)) + return false; + const std::unordered_map::const_iterator it = functions.find(getFunctionName(ftok)); + if (it == functions.cend()) + return false; + return std::any_of(it->second.argumentChecks.cbegin(), it->second.argumentChecks.cend(), [](const std::pair& a) { + return !a.second.minsizes.empty(); + }); +} + +Library::ArgumentChecks::Direction Library::getArgDirection(const Token* ftok, int argnr) const +{ + const ArgumentChecks* arg = getarg(ftok, argnr); + if (arg) + return arg->direction; + if (formatstr_function(ftok)) { + const int fs_argno = formatstr_argno(ftok); + if (fs_argno >= 0 && argnr >= fs_argno) { + if (formatstr_scan(ftok)) + return ArgumentChecks::Direction::DIR_OUT; + return ArgumentChecks::Direction::DIR_IN; + } + } + return ArgumentChecks::Direction::DIR_UNKNOWN; +} + +bool Library::ignorefunction(const std::string& functionName) const +{ + const std::unordered_map::const_iterator it = functions.find(functionName); + if (it != functions.cend()) + return it->second.ignore; + return false; +} +bool Library::isUse(const std::string& functionName) const +{ + const std::unordered_map::const_iterator it = functions.find(functionName); + if (it != functions.cend()) + return it->second.use; + return false; +} +bool Library::isLeakIgnore(const std::string& functionName) const +{ + const std::unordered_map::const_iterator it = functions.find(functionName); + if (it != functions.cend()) + return it->second.leakignore; + return false; +} +bool Library::isFunctionConst(const std::string& functionName, bool pure) const +{ + const std::unordered_map::const_iterator it = functions.find(functionName); + if (it != functions.cend()) + return pure ? it->second.ispure : it->second.isconst; + return false; +} +bool Library::isFunctionConst(const Token *ftok) const +{ + if (ftok->function() && ftok->function()->isConst()) + return true; + if (isNotLibraryFunction(ftok)) { + if (Token::simpleMatch(ftok->astParent(), ".")) { + using Yield = Library::Container::Yield; + const Yield yield = astContainerYield(ftok->astParent()->astOperand1()); + if (yield == Yield::EMPTY || yield == Yield::SIZE || yield == Yield::BUFFER_NT) + return true; + } + return false; + } + const std::unordered_map::const_iterator it = functions.find(getFunctionName(ftok)); + return (it != functions.cend() && it->second.isconst); +} + +bool Library::isnoreturn(const Token *ftok) const +{ + if (ftok->function() && ftok->function()->isAttributeNoreturn()) + return true; + if (isNotLibraryFunction(ftok)) { + if (Token::simpleMatch(ftok->astParent(), ".")) { + const Token* contTok = ftok->astParent()->astOperand1(); + if (astContainerAction(contTok) != Library::Container::Action::NO_ACTION || + astContainerYield(contTok) != Library::Container::Yield::NO_YIELD) + return false; + } + return false; + } + const std::unordered_map::const_iterator it = mNoReturn.find(getFunctionName(ftok)); + if (it == mNoReturn.end()) + return false; + if (it->second == FalseTrueMaybe::Maybe) + return true; + return it->second == FalseTrueMaybe::True; +} + +bool Library::isnotnoreturn(const Token *ftok) const +{ + if (ftok->function() && ftok->function()->isAttributeNoreturn()) + return false; + if (isNotLibraryFunction(ftok)) + return false; + const std::unordered_map::const_iterator it = mNoReturn.find(getFunctionName(ftok)); + if (it == mNoReturn.end()) + return false; + if (it->second == FalseTrueMaybe::Maybe) + return false; + return it->second == FalseTrueMaybe::False; +} + +bool Library::markupFile(const std::string &path) const +{ + return mMarkupExtensions.find(Path::getFilenameExtensionInLowerCase(path)) != mMarkupExtensions.end(); +} + +bool Library::processMarkupAfterCode(const std::string &path) const +{ + const std::map::const_iterator it = mProcessAfterCode.find(Path::getFilenameExtensionInLowerCase(path)); + return (it == mProcessAfterCode.cend() || it->second); +} + +bool Library::reportErrors(const std::string &path) const +{ + const std::map::const_iterator it = mReportErrors.find(Path::getFilenameExtensionInLowerCase(path)); + return (it == mReportErrors.cend() || it->second); +} + +bool Library::isexecutableblock(const std::string &file, const std::string &token) const +{ + const std::unordered_map::const_iterator it = mExecutableBlocks.find(Path::getFilenameExtensionInLowerCase(file)); + return (it != mExecutableBlocks.cend() && it->second.isBlock(token)); +} + +int Library::blockstartoffset(const std::string &file) const +{ + int offset = -1; + const std::unordered_map::const_iterator map_it + = mExecutableBlocks.find(Path::getFilenameExtensionInLowerCase(file)); + + if (map_it != mExecutableBlocks.end()) { + offset = map_it->second.offset(); + } + return offset; +} + +const std::string& Library::blockstart(const std::string &file) const +{ + const std::unordered_map::const_iterator map_it + = mExecutableBlocks.find(Path::getFilenameExtensionInLowerCase(file)); + + if (map_it != mExecutableBlocks.end()) { + return map_it->second.start(); + } + return emptyString; +} + +const std::string& Library::blockend(const std::string &file) const +{ + const std::unordered_map::const_iterator map_it + = mExecutableBlocks.find(Path::getFilenameExtensionInLowerCase(file)); + + if (map_it != mExecutableBlocks.end()) { + return map_it->second.end(); + } + return emptyString; +} + +bool Library::iskeyword(const std::string &file, const std::string &keyword) const +{ + const std::map>::const_iterator it = + mKeywords.find(Path::getFilenameExtensionInLowerCase(file)); + return (it != mKeywords.end() && it->second.count(keyword)); +} + +bool Library::isimporter(const std::string& file, const std::string &importer) const +{ + const std::map>::const_iterator it = + mImporters.find(Path::getFilenameExtensionInLowerCase(file)); + return (it != mImporters.end() && it->second.count(importer) > 0); +} + +const Token* Library::getContainerFromYield(const Token* tok, Library::Container::Yield yield) const +{ + if (!tok) + return nullptr; + if (Token::Match(tok->tokAt(-2), ". %name% (")) { + const Token* containerTok = tok->tokAt(-2)->astOperand1(); + if (!astIsContainer(containerTok)) + return nullptr; + if (containerTok->valueType()->container && + containerTok->valueType()->container->getYield(tok->strAt(-1)) == yield) + return containerTok; + if (yield == Library::Container::Yield::EMPTY && Token::simpleMatch(tok->tokAt(-1), "empty ( )")) + return containerTok; + if (yield == Library::Container::Yield::SIZE && Token::Match(tok->tokAt(-1), "size|length ( )")) + return containerTok; + } else if (Token::Match(tok->previous(), "%name% (")) { + if (const Library::Function* f = this->getFunction(tok->previous())) { + if (f->containerYield == yield) { + return tok->astOperand2(); + } + } + } + return nullptr; +} + +// cppcheck-suppress unusedFunction +const Token* Library::getContainerFromAction(const Token* tok, Library::Container::Action action) const +{ + if (!tok) + return nullptr; + if (Token::Match(tok->tokAt(-2), ". %name% (")) { + const Token* containerTok = tok->tokAt(-2)->astOperand1(); + if (!astIsContainer(containerTok)) + return nullptr; + if (containerTok->valueType()->container && + containerTok->valueType()->container->getAction(tok->strAt(-1)) == action) + return containerTok; + if (Token::simpleMatch(tok->tokAt(-1), "empty ( )")) + return containerTok; + } else if (Token::Match(tok->previous(), "%name% (")) { + if (const Library::Function* f = this->getFunction(tok->previous())) { + if (f->containerAction == action) { + return tok->astOperand2(); + } + } + } + return nullptr; +} + +bool Library::isSmartPointer(const Token* tok) const +{ + return detectSmartPointer(tok); +} + +const Library::SmartPointer* Library::detectSmartPointer(const Token* tok, bool withoutStd) const +{ + std::string typestr = withoutStd ? "std::" : ""; + while (Token::Match(tok, "%name%|::")) { + typestr += tok->str(); + tok = tok->next(); + } + auto it = smartPointers.find(typestr); + if (it == smartPointers.end()) + return nullptr; + return &it->second; +} + +const Library::Container * getLibraryContainer(const Token * tok) +{ + if (!tok) + return nullptr; + // TODO: Support dereferencing iterators + // TODO: Support dereferencing with -> + if (tok->isUnaryOp("*") && astIsPointer(tok->astOperand1())) { + for (const ValueFlow::Value& v:tok->astOperand1()->values()) { + if (!v.isLocalLifetimeValue()) + continue; + if (v.lifetimeKind != ValueFlow::Value::LifetimeKind::Address) + continue; + return getLibraryContainer(v.tokvalue); + } + } + if (!tok->valueType()) + return nullptr; + return tok->valueType()->container; +} + +Library::TypeCheck Library::getTypeCheck(std::string check, std::string typeName) const +{ + auto it = mTypeChecks.find(std::pair(std::move(check), std::move(typeName))); + return it == mTypeChecks.end() ? TypeCheck::def : it->second; +} + +bool Library::hasAnyTypeCheck(const std::string& typeName) const +{ + return std::any_of(mTypeChecks.begin(), mTypeChecks.end(), [&](const std::pair, Library::TypeCheck>& tc) { + return tc.first.second == typeName; + }); +} + +std::shared_ptr createTokenFromExpression(const std::string& returnValue, + const Settings* settings, + bool cpp, + std::unordered_map* lookupVarId) +{ + std::shared_ptr tokenList = std::make_shared(settings); + { + const std::string code = "return " + returnValue + ";"; + std::istringstream istr(code); + if (!tokenList->createTokens(istr, cpp ? Standards::Language::CPP : Standards::Language::C)) + return nullptr; + } + + // combine operators, set links, etc.. + std::stack lpar; + for (Token* tok2 = tokenList->front(); tok2; tok2 = tok2->next()) { + if (Token::Match(tok2, "[!<>=] =")) { + tok2->str(tok2->str() + "="); + tok2->deleteNext(); + } else if (tok2->str() == "(") + lpar.push(tok2); + else if (tok2->str() == ")") { + if (lpar.empty()) + return nullptr; + Token::createMutualLinks(lpar.top(), tok2); + lpar.pop(); + } + } + if (!lpar.empty()) + return nullptr; + + // set varids + for (Token* tok2 = tokenList->front(); tok2; tok2 = tok2->next()) { + if (!startsWith(tok2->str(), "arg")) + continue; + nonneg int const id = strToInt(tok2->str().c_str() + 3); + tok2->varId(id); + if (lookupVarId) + (*lookupVarId)[id] = tok2; + } + + // Evaluate expression + tokenList->createAst(); + Token* expr = tokenList->front()->astOperand1(); + ValueFlow::valueFlowConstantFoldAST(expr, *settings); + return {tokenList, expr}; +} diff --git a/cppcheck-2.14.0/lib/library.h b/cppcheck-2.14.0/lib/library.h new file mode 100644 index 00000000..6f6c4d24 --- /dev/null +++ b/cppcheck-2.14.0/lib/library.h @@ -0,0 +1,615 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "mathlib.h" +#include "standards.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +class Token; +class Settings; +enum class Severity; + +namespace tinyxml2 { + class XMLDocument; + class XMLElement; +} + +/// @addtogroup Core +/// @{ + +/** + * @brief Library definitions handling + */ +class CPPCHECKLIB Library { + // TODO: get rid of this + friend class TestSymbolDatabase; // For testing only + friend class TestSingleExecutorBase; // For testing only + friend class TestThreadExecutorBase; // For testing only + friend class TestProcessExecutorBase; // For testing only + +public: + Library() = default; + + enum class ErrorCode { + OK, + FILE_NOT_FOUND, BAD_XML, UNKNOWN_ELEMENT, MISSING_ATTRIBUTE, BAD_ATTRIBUTE_VALUE, + UNSUPPORTED_FORMAT, DUPLICATE_PLATFORM_TYPE, PLATFORM_TYPE_REDEFINED, DUPLICATE_DEFINE + }; + + class Error { + public: + Error() : errorcode(ErrorCode::OK) {} + explicit Error(ErrorCode e) : errorcode(e) {} + template + Error(ErrorCode e, T&& r) : errorcode(e), reason(r) {} + ErrorCode errorcode; + std::string reason; + }; + + Error load(const char exename[], const char path[]); + Error load(const tinyxml2::XMLDocument &doc); + + struct AllocFunc { + int groupId; + int arg; + enum class BufferSize {none,malloc,calloc,strdup}; + BufferSize bufferSize; + int bufferSizeArg1; + int bufferSizeArg2; + int reallocArg; + bool initData; + }; + + /** get allocation info for function */ + const AllocFunc* getAllocFuncInfo(const Token *tok) const; + + /** get deallocation info for function */ + const AllocFunc* getDeallocFuncInfo(const Token *tok) const; + + /** get reallocation info for function */ + const AllocFunc* getReallocFuncInfo(const Token *tok) const; + + /** get allocation id for function */ + int getAllocId(const Token *tok, int arg) const; + + /** get deallocation id for function */ + int getDeallocId(const Token *tok, int arg) const; + + /** get reallocation id for function */ + int getReallocId(const Token *tok, int arg) const; + + // TODO: get rid of this + /** get allocation info for function by name (deprecated, use other alloc) */ + const AllocFunc* getAllocFuncInfo(const char name[]) const { + return getAllocDealloc(mAlloc, name); + } + + // TODO: get rid of this + /** get deallocation info for function by name (deprecated, use other alloc) */ + const AllocFunc* getDeallocFuncInfo(const char name[]) const { + return getAllocDealloc(mDealloc, name); + } + + // TODO: get rid of this + /** get allocation id for function by name (deprecated, use other alloc) */ + // cppcheck-suppress unusedFunction + int allocId(const char name[]) const { + const AllocFunc* af = getAllocDealloc(mAlloc, name); + return af ? af->groupId : 0; + } + + // TODO: get rid of this + /** get deallocation id for function by name (deprecated, use other alloc) */ + int deallocId(const char name[]) const { + const AllocFunc* af = getAllocDealloc(mDealloc, name); + return af ? af->groupId : 0; + } + + static bool isCompliantValidationExpression(const char* p); + + /** is allocation type memory? */ + static bool ismemory(const int id) { + return ((id > 0) && ((id & 1) == 0)); + } + static bool ismemory(const AllocFunc* const func) { + return func && (func->groupId > 0) && ((func->groupId & 1) == 0); + } + + /** is allocation type resource? */ + static bool isresource(const int id) { + return ((id > 0) && ((id & 1) == 1)); + } + static bool isresource(const AllocFunc* const func) { + return func && (func->groupId > 0) && ((func->groupId & 1) == 1); + } + + bool formatstr_function(const Token* ftok) const; + int formatstr_argno(const Token* ftok) const; + bool formatstr_scan(const Token* ftok) const; + bool formatstr_secure(const Token* ftok) const; + + struct NonOverlappingData { + int ptr1Arg; + int ptr2Arg; + int sizeArg; + int strlenArg; + }; + const NonOverlappingData* getNonOverlappingData(const Token *ftok) const; + + struct WarnInfo { + std::string message; + Standards standards; + Severity severity; + }; + std::map functionwarn; + + const WarnInfo* getWarnInfo(const Token* ftok) const; + + // returns true if ftok is not a library function + bool isNotLibraryFunction(const Token *ftok) const; + bool matchArguments(const Token *ftok, const std::string &functionName) const; + + enum class UseRetValType { NONE, DEFAULT, ERROR_CODE }; + UseRetValType getUseRetValType(const Token* ftok) const; + + const std::string& returnValue(const Token *ftok) const; + const std::string& returnValueType(const Token *ftok) const; + int returnValueContainer(const Token *ftok) const; + std::vector unknownReturnValues(const Token *ftok) const; + + bool isnoreturn(const Token *ftok) const; + bool isnotnoreturn(const Token *ftok) const; + + bool isScopeNoReturn(const Token *end, std::string *unknownFunc) const; + + class Container { + public: + Container() = default; + + enum class Action { + RESIZE, + CLEAR, + PUSH, + POP, + FIND, + FIND_CONST, + INSERT, + ERASE, + CHANGE_CONTENT, + CHANGE, + CHANGE_INTERNAL, + NO_ACTION + }; + enum class Yield { + AT_INDEX, + ITEM, + BUFFER, + BUFFER_NT, + START_ITERATOR, + END_ITERATOR, + ITERATOR, + SIZE, + EMPTY, + NO_YIELD + }; + struct Function { + Action action; + Yield yield; + std::string returnType; + }; + struct RangeItemRecordTypeItem { + std::string name; + int templateParameter; // TODO: use this + }; + std::string startPattern, startPattern2, endPattern, itEndPattern; + std::map functions; + int type_templateArgNo = -1; + std::vector rangeItemRecordType; + int size_templateArgNo = -1; + bool arrayLike_indexOp{}; + bool stdStringLike{}; + bool stdAssociativeLike{}; + bool opLessAllowed = true; + bool hasInitializerListConstructor{}; + bool unstableErase{}; + bool unstableInsert{}; + bool view{}; + + Action getAction(const std::string& function) const { + const std::map::const_iterator i = functions.find(function); + if (i != functions.end()) + return i->second.action; + return Action::NO_ACTION; + } + + Yield getYield(const std::string& function) const { + const std::map::const_iterator i = functions.find(function); + if (i != functions.end()) + return i->second.yield; + return Yield::NO_YIELD; + } + + const std::string& getReturnType(const std::string& function) const { + auto i = functions.find(function); + return (i != functions.end()) ? i->second.returnType : emptyString; + } + + static Yield yieldFrom(const std::string& yieldName); + static Action actionFrom(const std::string& actionName); + }; + std::unordered_map containers; + const Container* detectContainer(const Token* typeStart) const; + const Container* detectIterator(const Token* typeStart) const; + const Container* detectContainerOrIterator(const Token* typeStart, bool* isIterator = nullptr, bool withoutStd = false) const; + + struct ArgumentChecks { + bool notbool{}; + bool notnull{}; + int notuninit = -1; + bool formatstr{}; + bool strz{}; + bool optional{}; + bool variadic{}; + std::string valid; + + struct IteratorInfo { + int container{}; + bool it{}; + bool first{}; + bool last{}; + }; + IteratorInfo iteratorInfo; + + struct MinSize { + enum class Type { NONE, STRLEN, ARGVALUE, SIZEOF, MUL, VALUE }; + MinSize(Type t, int a) : type(t), arg(a) {} + Type type; + int arg; + int arg2 = 0; + long long value = 0; + std::string baseType; + }; + std::vector minsizes; + + enum class Direction { + DIR_IN, ///< Input to called function. Data is treated as read-only. + DIR_OUT, ///< Output to caller. Data is passed by reference or address and is potentially written. + DIR_INOUT, ///< Input to called function, and output to caller. Data is passed by reference or address and is potentially modified. + DIR_UNKNOWN ///< direction not known / specified + }; + Direction direction = Direction::DIR_UNKNOWN; + }; + + struct Function { + std::map argumentChecks; // argument nr => argument data + bool use{}; + bool leakignore{}; + bool isconst{}; + bool ispure{}; + UseRetValType useretval = UseRetValType::NONE; + bool ignore{}; // ignore functions/macros from a library (gtk, qt etc) + bool formatstr{}; + bool formatstr_scan{}; + bool formatstr_secure{}; + Container::Action containerAction = Container::Action::NO_ACTION; + Container::Yield containerYield = Container::Yield::NO_YIELD; + std::string returnType; + }; + + const Function *getFunction(const Token *ftok) const; + std::unordered_map functions; + bool isUse(const std::string& functionName) const; + bool isLeakIgnore(const std::string& functionName) const; + bool isFunctionConst(const std::string& functionName, bool pure) const; + bool isFunctionConst(const Token *ftok) const; + + bool isboolargbad(const Token *ftok, int argnr) const { + const ArgumentChecks *arg = getarg(ftok, argnr); + return arg && arg->notbool; + } + + bool isnullargbad(const Token *ftok, int argnr) const; + bool isuninitargbad(const Token *ftok, int argnr, int indirect = 0, bool *hasIndirect=nullptr) const; + + bool isargformatstr(const Token *ftok, int argnr) const { + const ArgumentChecks *arg = getarg(ftok, argnr); + return arg && arg->formatstr; + } + + bool isargstrz(const Token *ftok, int argnr) const { + const ArgumentChecks *arg = getarg(ftok, argnr); + return arg && arg->strz; + } + + bool isIntArgValid(const Token *ftok, int argnr, const MathLib::bigint argvalue) const; + bool isFloatArgValid(const Token *ftok, int argnr, double argvalue) const; + + const std::string& validarg(const Token *ftok, int argnr) const { + const ArgumentChecks *arg = getarg(ftok, argnr); + return arg ? arg->valid : emptyString; + } + + const ArgumentChecks::IteratorInfo *getArgIteratorInfo(const Token *ftok, int argnr) const { + const ArgumentChecks *arg = getarg(ftok, argnr); + return arg && arg->iteratorInfo.it ? &arg->iteratorInfo : nullptr; + } + + bool hasminsize(const Token *ftok) const; + + const std::vector *argminsizes(const Token *ftok, int argnr) const { + const ArgumentChecks *arg = getarg(ftok, argnr); + return arg ? &arg->minsizes : nullptr; + } + + ArgumentChecks::Direction getArgDirection(const Token* ftok, int argnr) const; + + bool markupFile(const std::string &path) const; + + bool processMarkupAfterCode(const std::string &path) const; + + const std::set &markupExtensions() const { + return mMarkupExtensions; + } + + bool reportErrors(const std::string &path) const; + + bool ignorefunction(const std::string &functionName) const; + + bool isexecutableblock(const std::string &file, const std::string &token) const; + + int blockstartoffset(const std::string &file) const; + + const std::string& blockstart(const std::string &file) const; + const std::string& blockend(const std::string &file) const; + + bool iskeyword(const std::string &file, const std::string &keyword) const; + + bool isexporter(const std::string &prefix) const { + return mExporters.find(prefix) != mExporters.end(); + } + + bool isexportedprefix(const std::string &prefix, const std::string &token) const { + const std::map::const_iterator it = mExporters.find(prefix); + return (it != mExporters.end() && it->second.isPrefix(token)); + } + + bool isexportedsuffix(const std::string &prefix, const std::string &token) const { + const std::map::const_iterator it = mExporters.find(prefix); + return (it != mExporters.end() && it->second.isSuffix(token)); + } + + bool isimporter(const std::string& file, const std::string &importer) const; + + const Token* getContainerFromYield(const Token* tok, Container::Yield yield) const; + const Token* getContainerFromAction(const Token* tok, Container::Action action) const; + + bool isreflection(const std::string &token) const { + return mReflection.find(token) != mReflection.end(); + } + + int reflectionArgument(const std::string &token) const { + const std::map::const_iterator it = mReflection.find(token); + if (it != mReflection.end()) + return it->second; + return -1; + } + + bool isentrypoint(const std::string &func) const { + return func == "main" || mEntrypoints.find(func) != mEntrypoints.end(); + } + + std::set defines; // to provide some library defines + + struct SmartPointer { + std::string name; + bool unique = false; + }; + + std::unordered_map smartPointers; + bool isSmartPointer(const Token *tok) const; + const SmartPointer* detectSmartPointer(const Token* tok, bool withoutStd = false) const; + + struct PodType { + unsigned int size; + char sign; + enum class Type { NO, BOOL, CHAR, SHORT, INT, LONG, LONGLONG } stdtype; + }; + const PodType *podtype(const std::string &name) const { + const std::unordered_map::const_iterator it = mPodTypes.find(name); + return (it != mPodTypes.end()) ? &(it->second) : nullptr; + } + + struct PlatformType { + bool operator == (const PlatformType & type) const { + return (mSigned == type.mSigned && + mUnsigned == type.mUnsigned && + mLong == type.mLong && + mPointer == type.mPointer && + mPtrPtr == type.mPtrPtr && + mConstPtr == type.mConstPtr && + mType == type.mType); + } + bool operator != (const PlatformType & type) const { + return !(*this == type); + } + std::string mType; + bool mSigned{}; + bool mUnsigned{}; + bool mLong{}; + bool mPointer{}; + bool mPtrPtr{}; + bool mConstPtr{}; + }; + + struct Platform { + const PlatformType *platform_type(const std::string &name) const { + const std::map::const_iterator it = mPlatformTypes.find(name); + return (it != mPlatformTypes.end()) ? &(it->second) : nullptr; + } + std::map mPlatformTypes; + }; + + const PlatformType *platform_type(const std::string &name, const std::string & platform) const { + const std::map::const_iterator it = mPlatforms.find(platform); + if (it != mPlatforms.end()) { + const PlatformType * const type = it->second.platform_type(name); + if (type) + return type; + } + + const std::map::const_iterator it2 = mPlatformTypes.find(name); + return (it2 != mPlatformTypes.end()) ? &(it2->second) : nullptr; + } + + /** + * Get function name for function call + */ + std::string getFunctionName(const Token *ftok) const; + + static bool isContainerYield(const Token * const cond, Library::Container::Yield y, const std::string& fallback=emptyString); + + /** Suppress/check a type */ + enum class TypeCheck { def, + check, + suppress, + checkFiniteLifetime, // (unusedvar) object has side effects, but immediate destruction is wrong + }; + TypeCheck getTypeCheck(std::string check, std::string typeName) const; + bool hasAnyTypeCheck(const std::string& typeName) const; + +private: + // load a xml node + Error loadFunction(const tinyxml2::XMLElement * const node, const std::string &name, std::set &unknown_elements); + + class ExportedFunctions { + public: + void addPrefix(std::string prefix) { + mPrefixes.insert(std::move(prefix)); + } + void addSuffix(std::string suffix) { + mSuffixes.insert(std::move(suffix)); + } + bool isPrefix(const std::string& prefix) const { + return (mPrefixes.find(prefix) != mPrefixes.end()); + } + bool isSuffix(const std::string& suffix) const { + return (mSuffixes.find(suffix) != mSuffixes.end()); + } + + private: + std::set mPrefixes; + std::set mSuffixes; + }; + class CodeBlock { + public: + CodeBlock() = default; + + void setStart(const char* s) { + mStart = s; + } + void setEnd(const char* e) { + mEnd = e; + } + void setOffset(const int o) { + mOffset = o; + } + void addBlock(const char* blockName) { + mBlocks.insert(blockName); + } + const std::string& start() const { + return mStart; + } + const std::string& end() const { + return mEnd; + } + int offset() const { + return mOffset; + } + bool isBlock(const std::string& blockName) const { + return mBlocks.find(blockName) != mBlocks.end(); + } + + private: + std::string mStart; + std::string mEnd; + int mOffset{}; + std::set mBlocks; + }; + enum class FalseTrueMaybe { False, True, Maybe }; + int mAllocId{}; + std::set mFiles; + std::map mAlloc; // allocation functions + std::map mDealloc; // deallocation functions + std::map mRealloc; // reallocation functions + std::unordered_map mNoReturn; // is function noreturn? + std::map mReturnValue; + std::map mReturnValueType; + std::map mReturnValueContainer; + std::map> mUnknownReturnValues; + std::map mReportErrors; + std::map mProcessAfterCode; + std::set mMarkupExtensions; // file extensions of markup files + std::map> mKeywords; // keywords for code in the library + std::unordered_map mExecutableBlocks; // keywords for blocks of executable code + std::map mExporters; // keywords that export variables/functions to libraries (meta-code/macros) + std::map> mImporters; // keywords that import variables/functions + std::map mReflection; // invocation of reflection + std::unordered_map mPodTypes; // pod types + std::map mPlatformTypes; // platform independent typedefs + std::map mPlatforms; // platform dependent typedefs + std::map, TypeCheck> mTypeChecks; + std::unordered_map mNonOverlappingData; + std::unordered_set mEntrypoints; + + const ArgumentChecks * getarg(const Token *ftok, int argnr) const; + + std::string getFunctionName(const Token *ftok, bool &error) const; + + static const AllocFunc* getAllocDealloc(const std::map &data, const std::string &name) { + const std::map::const_iterator it = data.find(name); + return (it == data.end()) ? nullptr : &it->second; + } + + enum DetectContainer { ContainerOnly, IteratorOnly, Both }; + const Library::Container* detectContainerInternal(const Token* typeStart, DetectContainer detect, bool* isIterator = nullptr, bool withoutStd = false) const; +}; + +CPPCHECKLIB const Library::Container * getLibraryContainer(const Token * tok); + +std::shared_ptr createTokenFromExpression(const std::string& returnValue, + const Settings* settings, + bool cpp, + std::unordered_map* lookupVarId = nullptr); + +/// @} +//--------------------------------------------------------------------------- +#endif // libraryH diff --git a/cppcheck-2.14.0/lib/matchcompiler.h b/cppcheck-2.14.0/lib/matchcompiler.h new file mode 100644 index 00000000..323f4550 --- /dev/null +++ b/cppcheck-2.14.0/lib/matchcompiler.h @@ -0,0 +1,74 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef matchcompilerH +#define matchcompilerH + +#include + +namespace MatchCompiler { + + template + class ConstString { + public: + using StringRef = const char (&)[n]; + explicit ConstString(StringRef s) + : _s(s) {} + + operator StringRef() const { + return _s; + } + + private: + StringRef _s; + }; + + template + inline bool equalN(const char s1[], const char s2[]) + { + return (*s1 == *s2) && equalN(s1+1, s2+1); + } + + template<> + inline bool equalN<0>(const char /*s1*/[], const char /*s2*/[]) + { + return true; + } + + template + inline bool operator==(const std::string & s1, ConstString const & s2) + { + return equalN(s1.c_str(), s2); + } + + template + inline bool operator!=(const std::string & s1, ConstString const & s2) + { + return !operator==(s1,s2); + } + + template + inline ConstString makeConstString(const char (&s)[n]) + { + return ConstString(s); + } +} + +#endif // matchcompilerH + diff --git a/cppcheck-2.14.0/lib/mathlib.cpp b/cppcheck-2.14.0/lib/mathlib.cpp new file mode 100644 index 00000000..b56fdc07 --- /dev/null +++ b/cppcheck-2.14.0/lib/mathlib.cpp @@ -0,0 +1,1363 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "errortypes.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +const int MathLib::bigint_bits = 64; + +MathLib::value::value(const std::string &s) +{ + if (MathLib::isFloat(s)) { + mType = MathLib::value::Type::FLOAT; + mDoubleValue = MathLib::toDoubleNumber(s); + return; + } + + if (!MathLib::isInt(s)) + throw InternalError(nullptr, "Invalid value: " + s); + + mType = MathLib::value::Type::INT; + mIntValue = MathLib::toBigNumber(s); + + if (isIntHex(s) && mIntValue < 0) + mIsUnsigned = true; + + // read suffix + if (s.size() >= 2U) { + for (std::size_t i = s.size() - 1U; i > 0U; --i) { + const char c = s[i]; + if (c == 'u' || c == 'U') + mIsUnsigned = true; + else if (c == 'l' || c == 'L') { + if (mType == MathLib::value::Type::INT) + mType = MathLib::value::Type::LONG; + else if (mType == MathLib::value::Type::LONG) + mType = MathLib::value::Type::LONGLONG; + } else if (i > 2U && c == '4' && s[i-1] == '6' && s[i-2] == 'i') + mType = MathLib::value::Type::LONGLONG; + } + } +} + +std::string MathLib::value::str() const +{ + std::ostringstream ostr; + if (mType == MathLib::value::Type::FLOAT) { + if (std::isnan(mDoubleValue)) + return "nan.0"; + if (std::isinf(mDoubleValue)) + return (mDoubleValue > 0) ? "inf.0" : "-inf.0"; + + ostr.precision(9); + ostr << std::fixed << mDoubleValue; + + // remove trailing zeros + std::string ret(ostr.str()); + std::string::size_type pos = ret.size() - 1U; + while (ret[pos] == '0') + pos--; + if (ret[pos] == '.') + ++pos; + + return ret.substr(0, pos+1); + } + + if (mIsUnsigned) + ostr << static_cast(mIntValue) << "U"; + else + ostr << mIntValue; + if (mType == MathLib::value::Type::LONG) + ostr << "L"; + else if (mType == MathLib::value::Type::LONGLONG) + ostr << "LL"; + return ostr.str(); +} + +void MathLib::value::promote(const MathLib::value &v) +{ + if (isInt() && v.isInt()) { + if (mType < v.mType) { + mType = v.mType; + mIsUnsigned = v.mIsUnsigned; + } else if (mType == v.mType) { + mIsUnsigned |= v.mIsUnsigned; + } + } else if (!isFloat()) { + mIsUnsigned = false; + mDoubleValue = mIntValue; + mType = MathLib::value::Type::FLOAT; + } +} + + +MathLib::value MathLib::value::calc(char op, const MathLib::value &v1, const MathLib::value &v2) +{ + value temp(v1); + temp.promote(v2); + if (temp.isFloat()) { + switch (op) { + case '+': + temp.mDoubleValue += v2.getDoubleValue(); + break; + case '-': + temp.mDoubleValue -= v2.getDoubleValue(); + break; + case '*': + temp.mDoubleValue *= v2.getDoubleValue(); + break; + case '/': + temp.mDoubleValue /= v2.getDoubleValue(); + break; + case '%': + case '&': + case '|': + case '^': + throw InternalError(nullptr, "Invalid calculation"); + default: + throw InternalError(nullptr, "Unhandled calculation"); + } + } else if (temp.mIsUnsigned) { + switch (op) { + case '+': + temp.mIntValue += (unsigned long long)v2.mIntValue; + break; + case '-': + temp.mIntValue -= (unsigned long long)v2.mIntValue; + break; + case '*': + temp.mIntValue *= (unsigned long long)v2.mIntValue; + break; + case '/': + if (v2.mIntValue == 0) + throw InternalError(nullptr, "Internal Error: Division by zero"); + if (v1.mIntValue == std::numeric_limits::min() && std::abs(v2.mIntValue)<=1) + throw InternalError(nullptr, "Internal Error: Division overflow"); + temp.mIntValue /= (unsigned long long)v2.mIntValue; + break; + case '%': + if (v2.mIntValue == 0) + throw InternalError(nullptr, "Internal Error: Division by zero"); + temp.mIntValue %= (unsigned long long)v2.mIntValue; + break; + case '&': + temp.mIntValue &= (unsigned long long)v2.mIntValue; + break; + case '|': + temp.mIntValue |= (unsigned long long)v2.mIntValue; + break; + case '^': + temp.mIntValue ^= (unsigned long long)v2.mIntValue; + break; + default: + throw InternalError(nullptr, "Unhandled calculation"); + } + } else { + switch (op) { + case '+': + temp.mIntValue += v2.mIntValue; + break; + case '-': + temp.mIntValue -= v2.mIntValue; + break; + case '*': + temp.mIntValue *= v2.mIntValue; + break; + case '/': + if (v2.mIntValue == 0) + throw InternalError(nullptr, "Internal Error: Division by zero"); + if (v1.mIntValue == std::numeric_limits::min() && std::abs(v2.mIntValue)<=1) + throw InternalError(nullptr, "Internal Error: Division overflow"); + temp.mIntValue /= v2.mIntValue; + break; + case '%': + if (v2.mIntValue == 0) + throw InternalError(nullptr, "Internal Error: Division by zero"); + temp.mIntValue %= v2.mIntValue; + break; + case '&': + temp.mIntValue &= v2.mIntValue; + break; + case '|': + temp.mIntValue |= v2.mIntValue; + break; + case '^': + temp.mIntValue ^= v2.mIntValue; + break; + default: + throw InternalError(nullptr, "Unhandled calculation"); + } + } + return temp; +} + + +int MathLib::value::compare(const MathLib::value &v) const +{ + value temp(*this); + temp.promote(v); + + if (temp.isFloat()) { + if (temp.mDoubleValue < v.getDoubleValue()) + return -1; + if (temp.mDoubleValue > v.getDoubleValue()) + return 1; + return 0; + } + + if (temp.mIsUnsigned) { + if ((unsigned long long)mIntValue < (unsigned long long)v.mIntValue) + return -1; + if ((unsigned long long)mIntValue > (unsigned long long)v.mIntValue) + return 1; + return 0; + } + + if (mIntValue < v.mIntValue) + return -1; + if (mIntValue > v.mIntValue) + return 1; + return 0; +} + +MathLib::value MathLib::value::add(int v) const +{ + MathLib::value temp(*this); + if (temp.isInt()) + temp.mIntValue += v; + else + temp.mDoubleValue += v; + return temp; +} + +MathLib::value MathLib::value::shiftLeft(const MathLib::value &v) const +{ + if (!isInt() || !v.isInt()) + throw InternalError(nullptr, "Shift operand is not integer"); + MathLib::value ret(*this); + if (v.mIntValue >= MathLib::bigint_bits) { + return ret; + } + ret.mIntValue <<= v.mIntValue; + return ret; +} + +MathLib::value MathLib::value::shiftRight(const MathLib::value &v) const +{ + if (!isInt() || !v.isInt()) + throw InternalError(nullptr, "Shift operand is not integer"); + MathLib::value ret(*this); + if (v.mIntValue >= MathLib::bigint_bits) { + return ret; + } + ret.mIntValue >>= v.mIntValue; + return ret; +} + +// TODO: remove handling of non-literal stuff +MathLib::biguint MathLib::toBigUNumber(const std::string & str) +{ + // hexadecimal numbers: + if (isIntHex(str)) { + try { + const biguint ret = std::stoull(str, nullptr, 16); + return ret; + } catch (const std::out_of_range& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toBigUNumber: out_of_range: " + str); + } catch (const std::invalid_argument& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toBigUNumber: invalid_argument: " + str); + } + } + + // octal numbers: + if (isOct(str)) { + try { + const biguint ret = std::stoull(str, nullptr, 8); + return ret; + } catch (const std::out_of_range& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toBigUNumber: out_of_range: " + str); + } catch (const std::invalid_argument& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toBigUNumber: invalid_argument: " + str); + } + } + + // binary numbers: + if (isBin(str)) { + biguint ret = 0; + for (std::string::size_type i = str[0] == '0'?2:3; i < str.length(); i++) { + if (str[i] != '1' && str[i] != '0') + break; + 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 biguint type. + // Use min/max values as an approximation. See #5843 + // TODO: bail out when we are out of range? + const double doubleval = toDoubleNumber(str); + if (doubleval > (double)std::numeric_limits::max()) + return std::numeric_limits::max(); + // cast to bigint to avoid UBSAN warning about negative double being out-of-range + return static_cast(static_cast(doubleval)); + } + + if (isCharLiteral(str)) + return simplecpp::characterLiteralToLL(str); + + try { + std::size_t idx = 0; + const biguint ret = std::stoull(str, &idx, 10); + if (idx != str.size()) { + const std::string s = str.substr(idx); + if (!isValidIntegerSuffix(s, true)) + throw InternalError(nullptr, "Internal Error. MathLib::toBigUNumber: input was not completely consumed: " + str); + } + return ret; + } catch (const std::out_of_range& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toBigUNumber: out_of_range: " + str); + } catch (const std::invalid_argument& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toBigUNumber: invalid_argument: " + str); + } +} + +unsigned int MathLib::encodeMultiChar(const std::string& str) +{ + return std::accumulate(str.cbegin(), str.cend(), uint32_t(), [](uint32_t v, char c) { + return (v << 8) | c; + }); +} + +// TODO: remove handling of non-literal stuff +MathLib::bigint MathLib::toBigNumber(const std::string & str) +{ + // hexadecimal numbers: + if (isIntHex(str)) { + try { + const biguint ret = std::stoull(str, nullptr, 16); + return (bigint)ret; + } catch (const std::out_of_range& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toBigNumber: out_of_range: " + str); + } catch (const std::invalid_argument& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toBigNumber: invalid_argument: " + str); + } + } + + // octal numbers: + if (isOct(str)) { + try { + const biguint ret = std::stoull(str, nullptr, 8); + return ret; + } catch (const std::out_of_range& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toBigNumber: out_of_range: " + str); + } catch (const std::invalid_argument& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toBigNumber: invalid_argument: " + str); + } + } + + // binary numbers: + if (isBin(str)) { + bigint ret = 0; + for (std::string::size_type i = str[0] == '0'?2:3; i < str.length(); i++) { + if (str[i] != '1' && str[i] != '0') + break; + 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 + // TODO: bail out when we are out of range? + const double doubleval = toDoubleNumber(str); + if (doubleval > (double)std::numeric_limits::max()) + return std::numeric_limits::max(); + if (doubleval < (double)std::numeric_limits::min()) + return std::numeric_limits::min(); + return static_cast(doubleval); + } + + if (isCharLiteral(str)) + return simplecpp::characterLiteralToLL(str); + + try { + std::size_t idx = 0; + const biguint ret = std::stoull(str, &idx, 10); + if (idx != str.size()) { + const std::string s = str.substr(idx); + if (!isValidIntegerSuffix(s, true)) + throw InternalError(nullptr, "Internal Error. MathLib::toBigNumber: input was not completely consumed: " + str); + } + return ret; + } catch (const std::out_of_range& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toBigNumber: out_of_range: " + str); + } catch (const std::invalid_argument& /*e*/) { + throw InternalError(nullptr, "Internal Error. MathLib::toBigNumber: invalid_argument: " + str); + } +} + +// in-place conversion of (sub)string to double. Requires no heap. +static double myStod(const std::string& str, std::string::const_iterator from, std::string::const_iterator to, int base) +{ + double result = 0.; + bool positivesign = true; + std::string::const_iterator it; + if ('+' == *from) { + it = from + 1; + } else if ('-' == *from) { + it = from + 1; + positivesign = false; + } else + it = from; + const std::size_t decimalsep = str.find('.', it-str.begin()); + int distance; + if (std::string::npos == decimalsep) { + distance = to - it; + } else if (decimalsep > (to - str.begin())) + return 0.; // error handling?? + else + distance = int(decimalsep)-(from - str.begin()); + auto digitval = [&](char c) { + if ((10 < base) && (c > '9')) + return 10 + std::tolower(c) - 'a'; + return c - '0'; + }; + for (; it!=to; ++it) { + if ('.' == *it) + continue; + --distance; + result += digitval(*it)* std::pow(base, distance); + } + return positivesign ? result : -result; +} + +// Assuming a limited support of built-in hexadecimal floats (see C99, C++17) that is a fall-back implementation. +// Performance has been optimized WRT to heap activity, however the calculation part is not optimized. +static double floatHexToDoubleNumber(const std::string& str) +{ + const std::size_t p = str.find_first_of("pP",3); + const double factor1 = myStod(str, str.cbegin() + 2, str.cbegin()+p, 16); + const bool suffix = (str.back() == 'f') || (str.back() == 'F') || (str.back() == 'l') || (str.back() == 'L'); + const double exponent = myStod(str, str.cbegin() + p + 1, suffix ? str.cend()-1:str.cend(), 10); + const double factor2 = std::pow(2, exponent); + return factor1 * factor2; +} + +double MathLib::toDoubleNumber(const std::string &str) +{ + if (isCharLiteral(str)) { + try { + return simplecpp::characterLiteralToLL(str); + } catch (const std::exception& e) { + throw InternalError(nullptr, "Internal Error. MathLib::toDoubleNumber: characterLiteralToLL(" + str + ") => " + e.what()); + } + } + if (isIntHex(str)) + return static_cast(toBigNumber(str)); +#ifdef _LIBCPP_VERSION + if (isFloat(str)) // Workaround libc++ bug at https://github.com/llvm/llvm-project/issues/18156 + // TODO: handle locale + // TODO: make sure all characters are being consumed + return std::strtod(str.c_str(), nullptr); +#endif + if (isFloatHex(str)) + return floatHexToDoubleNumber(str); + // otherwise, convert to double + std::istringstream istr(str); + istr.imbue(std::locale::classic()); + double ret; + if (!(istr >> ret)) + throw InternalError(nullptr, "Internal Error. MathLib::toDoubleNumber: conversion failed: " + str); + std::string s; + if (istr >> s) { + if (isDecimalFloat(str)) + return ret; + if (!isValidIntegerSuffix(s, true)) + throw InternalError(nullptr, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: " + str); + } + return ret; +} + +template<> std::string MathLib::toString(double value) +{ + std::ostringstream result; + result.precision(12); + result << value; + std::string s = result.str(); + if (s == "-0") + return "0.0"; + if (s.find_first_of(".e") == std::string::npos) + return s + ".0"; + return s; +} + +bool MathLib::isFloat(const std::string &str) +{ + return isDecimalFloat(str) || isFloatHex(str); +} + +bool MathLib::isDecimalFloat(const std::string &str) +{ + if (str.empty()) + return false; + enum class State { + START, BASE_DIGITS1, LEADING_DECIMAL, TRAILING_DECIMAL, BASE_DIGITS2, E, MANTISSA_PLUSMINUS, MANTISSA_DIGITS, SUFFIX_F, SUFFIX_L, SUFFIX_LITERAL_LEADER, SUFFIX_LITERAL + } state = State::START; + std::string::const_iterator it = str.cbegin(); + if ('+' == *it || '-' == *it) + ++it; + for (; it != str.cend(); ++it) { + switch (state) { + case State::START: + if (*it=='.') + state = State::LEADING_DECIMAL; + else if (std::isdigit(static_cast(*it))) + state = State::BASE_DIGITS1; + else + return false; + break; + case State::LEADING_DECIMAL: + if (std::isdigit(static_cast(*it))) + state = State::BASE_DIGITS2; + else + return false; + break; + case State::BASE_DIGITS1: + if (*it=='e' || *it=='E') + state = State::E; + else if (*it=='.') + state = State::TRAILING_DECIMAL; + else if (!std::isdigit(static_cast(*it))) + return false; + break; + case State::TRAILING_DECIMAL: + if (*it=='e' || *it=='E') + state = State::E; + else if (*it=='f' || *it=='F') + state = State::SUFFIX_F; + else if (*it=='l' || *it=='L') + state = State::SUFFIX_L; + else if (*it == '_') + state = State::SUFFIX_LITERAL_LEADER; + else if (std::isdigit(static_cast(*it))) + state = State::BASE_DIGITS2; + else + return false; + break; + case State::BASE_DIGITS2: + if (*it=='e' || *it=='E') + state = State::E; + else if (*it=='f' || *it=='F') + state = State::SUFFIX_F; + else if (*it=='l' || *it=='L') + state = State::SUFFIX_L; + else if (*it == '_') + state = State::SUFFIX_LITERAL_LEADER; + else if (!std::isdigit(static_cast(*it))) + return false; + break; + case State::E: + if (*it=='+' || *it=='-') + state = State::MANTISSA_PLUSMINUS; + else if (std::isdigit(static_cast(*it))) + state = State::MANTISSA_DIGITS; + else + return false; + break; + case State::MANTISSA_PLUSMINUS: + if (!std::isdigit(static_cast(*it))) + return false; + else + state = State::MANTISSA_DIGITS; + break; + case State::MANTISSA_DIGITS: + if (*it=='f' || *it=='F') + state = State::SUFFIX_F; + else if (*it=='l' || *it=='L') + state = State::SUFFIX_L; + else if (!std::isdigit(static_cast(*it))) + return false; + break; + // Ensure at least one post _ char for user defined literals + case State::SUFFIX_LITERAL: + case State::SUFFIX_LITERAL_LEADER: + state = State::SUFFIX_LITERAL; + break; + case State::SUFFIX_F: + return false; + case State::SUFFIX_L: + return false; + } + } + return (state==State::BASE_DIGITS2 || state==State::MANTISSA_DIGITS || state==State::TRAILING_DECIMAL || state==State::SUFFIX_F || state==State::SUFFIX_L || (state==State::SUFFIX_LITERAL)); +} + +bool MathLib::isNegative(const std::string &str) +{ + if (str.empty()) + return false; + return (str[0] == '-'); +} + +bool MathLib::isPositive(const std::string &str) +{ + if (str.empty()) + return false; + return !MathLib::isNegative(str); +} + +static bool isValidIntegerSuffixIt(std::string::const_iterator it, std::string::const_iterator end, bool supportMicrosoftExtensions=true) +{ + enum class Status { START, SUFFIX_U, SUFFIX_UL, SUFFIX_ULL, SUFFIX_UZ, SUFFIX_L, SUFFIX_LU, SUFFIX_LL, SUFFIX_LLU, SUFFIX_I, SUFFIX_I6, SUFFIX_I64, SUFFIX_UI, SUFFIX_UI6, SUFFIX_UI64, SUFFIX_Z, SUFFIX_LITERAL_LEADER, SUFFIX_LITERAL } state = Status::START; + for (; it != end; ++it) { + switch (state) { + case Status::START: + if (*it == 'u' || *it == 'U') + state = Status::SUFFIX_U; + else if (*it == 'l' || *it == 'L') + state = Status::SUFFIX_L; + else if (*it == 'z' || *it == 'Z') + state = Status::SUFFIX_Z; + else if (supportMicrosoftExtensions && (*it == 'i' || *it == 'I')) + state = Status::SUFFIX_I; + else if (*it == '_') + state = Status::SUFFIX_LITERAL_LEADER; + else + return false; + break; + case Status::SUFFIX_U: + if (*it == 'l' || *it == 'L') + state = Status::SUFFIX_UL; // UL + else if (*it == 'z' || *it == 'Z') + state = Status::SUFFIX_UZ; // UZ + else if (supportMicrosoftExtensions && (*it == 'i' || *it == 'I')) + state = Status::SUFFIX_UI; + else + return false; + break; + case Status::SUFFIX_UL: + if (*it == 'l' || *it == 'L') + state = Status::SUFFIX_ULL; // ULL + else + return false; + break; + case Status::SUFFIX_L: + if (*it == 'u' || *it == 'U') + state = Status::SUFFIX_LU; // LU + else if (*it == 'l' || *it == 'L') + state = Status::SUFFIX_LL; // LL + else + return false; + break; + case Status::SUFFIX_LU: + return false; + case Status::SUFFIX_LL: + if (*it == 'u' || *it == 'U') + state = Status::SUFFIX_LLU; // LLU + else + return false; + break; + case Status::SUFFIX_I: + if (*it == '6') + state = Status::SUFFIX_I6; + else + return false; + break; + case Status::SUFFIX_I6: + if (*it == '4') + state = Status::SUFFIX_I64; + else + return false; + break; + case Status::SUFFIX_UI: + if (*it == '6') + state = Status::SUFFIX_UI6; + else + return false; + break; + case Status::SUFFIX_UI6: + if (*it == '4') + state = Status::SUFFIX_UI64; + else + return false; + break; + case Status::SUFFIX_Z: + if (*it == 'u' || *it == 'U') + state = Status::SUFFIX_UZ; + else + return false; + break; + // Ensure at least one post _ char for user defined literals + case Status::SUFFIX_LITERAL: + case Status::SUFFIX_LITERAL_LEADER: + state = Status::SUFFIX_LITERAL; + break; + default: + return false; + } + } + return ((state == Status::SUFFIX_U) || + (state == Status::SUFFIX_L) || + (state == Status::SUFFIX_Z) || + (state == Status::SUFFIX_UL) || + (state == Status::SUFFIX_UZ) || + (state == Status::SUFFIX_LU) || + (state == Status::SUFFIX_LL) || + (state == Status::SUFFIX_ULL) || + (state == Status::SUFFIX_LLU) || + (state == Status::SUFFIX_I64) || + (state == Status::SUFFIX_UI64) || + (state == Status::SUFFIX_LITERAL)); +} + +// cppcheck-suppress unusedFunction +bool MathLib::isValidIntegerSuffix(const std::string& str, bool supportMicrosoftExtensions) +{ + return isValidIntegerSuffixIt(str.cbegin(), str.cend(), supportMicrosoftExtensions); +} + + + +/*! \brief Does the string represent an octal number? + * In case leading or trailing white space is provided, the function + * returns false. + * Additional information can be found here: + * http://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html + * + * \param str The string to check. In case the string is empty, the function returns false. + * \return Return true in case a octal number is provided and false otherwise. + **/ +bool MathLib::isOct(const std::string& str) +{ + enum class Status { + START, OCTAL_PREFIX, DIGITS + } state = Status::START; + if (str.empty()) + return false; + std::string::const_iterator it = str.cbegin(); + if ('+' == *it || '-' == *it) + ++it; + for (; it != str.cend(); ++it) { + switch (state) { + case Status::START: + if (*it == '0') + state = Status::OCTAL_PREFIX; + else + return false; + break; + case Status::OCTAL_PREFIX: + if (isOctalDigit(static_cast(*it))) + state = Status::DIGITS; + else + return false; + break; + case Status::DIGITS: + if (isOctalDigit(static_cast(*it))) + state = Status::DIGITS; + else + return isValidIntegerSuffixIt(it,str.end()); + break; + } + } + return state == Status::DIGITS; +} + +bool MathLib::isIntHex(const std::string& str) +{ + enum class Status { + START, HEX_0, HEX_X, DIGIT + } state = Status::START; + if (str.empty()) + return false; + std::string::const_iterator it = str.cbegin(); + if ('+' == *it || '-' == *it) + ++it; + for (; it != str.cend(); ++it) { + switch (state) { + case Status::START: + if (*it == '0') + state = Status::HEX_0; + else + return false; + break; + case Status::HEX_0: + if (*it == 'x' || *it == 'X') + state = Status::HEX_X; + else + return false; + break; + case Status::HEX_X: + if (isxdigit(static_cast(*it))) + state = Status::DIGIT; + else + return false; + break; + case Status::DIGIT: + if (isxdigit(static_cast(*it))) + ; // state = Status::DIGIT; + else + return isValidIntegerSuffixIt(it,str.end()); + break; + } + } + return Status::DIGIT == state; +} + +bool MathLib::isFloatHex(const std::string& str) +{ + enum class Status { + START, HEX_0, HEX_X, WHOLE_NUMBER_DIGIT, POINT, FRACTION, EXPONENT_P, EXPONENT_SIGN, EXPONENT_DIGITS, EXPONENT_SUFFIX + } state = Status::START; + if (str.empty()) + return false; + std::string::const_iterator it = str.cbegin(); + if ('+' == *it || '-' == *it) + ++it; + for (; it != str.cend(); ++it) { + switch (state) { + case Status::START: + if (*it == '0') + state = Status::HEX_0; + else + return false; + break; + case Status::HEX_0: + if (*it == 'x' || *it == 'X') + state = Status::HEX_X; + else + return false; + break; + case Status::HEX_X: + if (isxdigit(static_cast(*it))) + state = Status::WHOLE_NUMBER_DIGIT; + else if (*it == '.') + state = Status::POINT; + else + return false; + break; + case Status::WHOLE_NUMBER_DIGIT: + if (isxdigit(static_cast(*it))) + ; // state = Status::WHOLE_NUMBER_DIGITS; + else if (*it=='.') + state = Status::FRACTION; + else if (*it=='p' || *it=='P') + state = Status::EXPONENT_P; + else + return false; + break; + case Status::POINT: + case Status::FRACTION: + if (isxdigit(static_cast(*it))) + state = Status::FRACTION; + else if (*it == 'p' || *it == 'P') + state = Status::EXPONENT_P; + else + return false; + break; + case Status::EXPONENT_P: + if (isdigit(static_cast(*it))) + state = Status::EXPONENT_DIGITS; + else if (*it == '+' || *it == '-') + state = Status::EXPONENT_SIGN; + else + return false; + break; + case Status::EXPONENT_SIGN: + if (isdigit(static_cast(*it))) + state = Status::EXPONENT_DIGITS; + else + return false; + break; + case Status::EXPONENT_DIGITS: + if (isdigit(static_cast(*it))) + ; // state = Status::EXPONENT_DIGITS; + else if (*it == 'f' || *it == 'F' || *it == 'l' || *it == 'L') + state = Status::EXPONENT_SUFFIX; + else + return false; + break; + case Status::EXPONENT_SUFFIX: + return false; + } + } + return (Status::EXPONENT_DIGITS == state) || (Status::EXPONENT_SUFFIX == state); +} + + +/*! \brief Does the string represent a binary number? + * In case leading or trailing white space is provided, the function + * returns false. + * Additional information can be found here: + * http://gcc.gnu.org/onlinedocs/gcc/Binary-constants.html + * + * \param str The string to check. In case the string is empty, the function returns false. + * \return Return true in case a binary number is provided and false otherwise. + **/ +bool MathLib::isBin(const std::string& str) +{ + enum class Status { + START, GNU_BIN_PREFIX_0, GNU_BIN_PREFIX_B, DIGIT + } state = Status::START; + if (str.empty()) + return false; + std::string::const_iterator it = str.cbegin(); + if ('+' == *it || '-' == *it) + ++it; + for (; it != str.cend(); ++it) { + switch (state) { + case Status::START: + if (*it == '0') + state = Status::GNU_BIN_PREFIX_0; + else + return false; + break; + case Status::GNU_BIN_PREFIX_0: + if (*it == 'b' || *it == 'B') + state = Status::GNU_BIN_PREFIX_B; + else + return false; + break; + case Status::GNU_BIN_PREFIX_B: + if (*it == '0' || *it == '1') + state = Status::DIGIT; + else + return false; + break; + case Status::DIGIT: + if (*it == '0' || *it == '1') + ; // state = Status::DIGIT; + else + return isValidIntegerSuffixIt(it,str.end()); + break; + } + } + return state == Status::DIGIT; +} + +bool MathLib::isDec(const std::string & str) +{ + enum class Status { + START, DIGIT + } state = Status::START; + if (str.empty()) + return false; + std::string::const_iterator it = str.cbegin(); + if ('+' == *it || '-' == *it) + ++it; + for (; it != str.cend(); ++it) { + switch (state) { + case Status::START: + if (isdigit(static_cast(*it))) + state = Status::DIGIT; + else + return false; + break; + case Status::DIGIT: + if (isdigit(static_cast(*it))) + state = Status::DIGIT; + else + return isValidIntegerSuffixIt(it,str.end()); + break; + } + } + return state == Status::DIGIT; +} + +bool MathLib::isInt(const std::string & str) +{ + return isDec(str) || isIntHex(str) || isOct(str) || isBin(str); +} + +std::string MathLib::getSuffix(const std::string& value) +{ + if (value.size() > 3 && value[value.size() - 3] == 'i' && value[value.size() - 2] == '6' && value[value.size() - 1] == '4') { + if (value[value.size() - 4] == 'u') + return "ULL"; + return "LL"; + } + bool isUnsigned = false; + unsigned int longState = 0; + for (std::size_t i = 1U; i < value.size(); ++i) { + const char c = value[value.size() - i]; + if (c == 'u' || c == 'U') + isUnsigned = true; + else if (c == 'L' || c == 'l') + longState++; + else break; + } + if (longState == 0) + return isUnsigned ? "U" : ""; + if (longState == 1) + return isUnsigned ? "UL" : "L"; + if (longState == 2) + return isUnsigned ? "ULL" : "LL"; + return ""; +} + +static std::string intsuffix(const std::string & first, const std::string & second) +{ + const std::string suffix1 = MathLib::getSuffix(first); + const std::string suffix2 = MathLib::getSuffix(second); + if (suffix1 == "ULL" || suffix2 == "ULL") + return "ULL"; + if (suffix1 == "LL" || suffix2 == "LL") + return "LL"; + if (suffix1 == "UL" || suffix2 == "UL") + return "UL"; + if (suffix1 == "L" || suffix2 == "L") + return "L"; + if (suffix1 == "U" || suffix2 == "U") + return "U"; + + return suffix1.empty() ? suffix2 : suffix1; +} + +std::string MathLib::add(const std::string & first, const std::string & second) +{ +#ifdef TEST_MATHLIB_VALUE + return (value(first) + value(second)).str(); +#else + if (MathLib::isInt(first) && MathLib::isInt(second)) { + return std::to_string(toBigNumber(first) + toBigNumber(second)) + intsuffix(first, second); + } + + double d1 = toDoubleNumber(first); + double d2 = toDoubleNumber(second); + + int count = 0; + while (d1 > 100000.0 * d2 && toString(d1+d2)==first && ++count<5) + d2 *= 10.0; + while (d2 > 100000.0 * d1 && toString(d1+d2)==second && ++count<5) + d1 *= 10.0; + + return toString(d1 + d2); +#endif +} + +std::string MathLib::subtract(const std::string &first, const std::string &second) +{ +#ifdef TEST_MATHLIB_VALUE + return (value(first) - value(second)).str(); +#else + if (MathLib::isInt(first) && MathLib::isInt(second)) { + return std::to_string(toBigNumber(first) - toBigNumber(second)) + intsuffix(first, second); + } + + if (first == second) + return "0.0"; + + double d1 = toDoubleNumber(first); + double d2 = toDoubleNumber(second); + + int count = 0; + while (d1 > 100000.0 * d2 && toString(d1-d2)==first && ++count<5) + d2 *= 10.0; + while (d2 > 100000.0 * d1 && toString(d1-d2)==second && ++count<5) + d1 *= 10.0; + + return toString(d1 - d2); +#endif +} + +std::string MathLib::divide(const std::string &first, const std::string &second) +{ +#ifdef TEST_MATHLIB_VALUE + return (value(first) / value(second)).str(); +#else + if (MathLib::isInt(first) && MathLib::isInt(second)) { + const bigint a = toBigNumber(first); + const bigint b = toBigNumber(second); + if (b == 0) + throw InternalError(nullptr, "Internal Error: Division by zero"); + if (a == std::numeric_limits::min() && std::abs(b)<=1) + throw InternalError(nullptr, "Internal Error: Division overflow"); + return std::to_string(toBigNumber(first) / b) + intsuffix(first, second); + } + if (isNullValue(second)) { + if (isNullValue(first)) + return "nan.0"; + return isPositive(first) == isPositive(second) ? "inf.0" : "-inf.0"; + } + return toString(toDoubleNumber(first) / toDoubleNumber(second)); +#endif +} + +std::string MathLib::multiply(const std::string &first, const std::string &second) +{ +#ifdef TEST_MATHLIB_VALUE + return (value(first) * value(second)).str(); +#else + if (MathLib::isInt(first) && MathLib::isInt(second)) { + return std::to_string(toBigNumber(first) * toBigNumber(second)) + intsuffix(first, second); + } + return toString(toDoubleNumber(first) * toDoubleNumber(second)); +#endif +} + +std::string MathLib::mod(const std::string &first, const std::string &second) +{ +#ifdef TEST_MATHLIB_VALUE + return (value(first) % value(second)).str(); +#else + if (MathLib::isInt(first) && MathLib::isInt(second)) { + const bigint b = toBigNumber(second); + if (b == 0) + throw InternalError(nullptr, "Internal Error: Division by zero"); + return std::to_string(toBigNumber(first) % b) + intsuffix(first, second); + } + return toString(std::fmod(toDoubleNumber(first),toDoubleNumber(second))); +#endif +} + +std::string MathLib::calculate(const std::string &first, const std::string &second, char action) +{ + switch (action) { + case '+': + return MathLib::add(first, second); + + case '-': + return MathLib::subtract(first, second); + + case '*': + return MathLib::multiply(first, second); + + case '/': + return MathLib::divide(first, second); + + case '%': + return MathLib::mod(first, second); + + case '&': + return std::to_string(MathLib::toBigNumber(first) & MathLib::toBigNumber(second)) + intsuffix(first, second); + + case '|': + return std::to_string(MathLib::toBigNumber(first) | MathLib::toBigNumber(second)) + intsuffix(first, second); + + case '^': + return std::to_string(MathLib::toBigNumber(first) ^ MathLib::toBigNumber(second)) + intsuffix(first, second); + + default: + throw InternalError(nullptr, std::string("Unexpected action '") + action + "' in MathLib::calculate(). Please report this to Cppcheck developers."); + } +} + +std::string MathLib::sin(const std::string &tok) +{ + return toString(std::sin(toDoubleNumber(tok))); +} + + +std::string MathLib::cos(const std::string &tok) +{ + return toString(std::cos(toDoubleNumber(tok))); +} + +std::string MathLib::tan(const std::string &tok) +{ + return toString(std::tan(toDoubleNumber(tok))); +} + + +std::string MathLib::abs(const std::string &tok) +{ + if (isNegative(tok)) + return tok.substr(1, tok.length() - 1); + return 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); +} + +// cppcheck-suppress unusedFunction +bool MathLib::isGreater(const std::string &first, const std::string &second) +{ + return toDoubleNumber(first) > toDoubleNumber(second); +} + +// cppcheck-suppress unusedFunction +bool MathLib::isGreaterEqual(const std::string &first, const std::string &second) +{ + return toDoubleNumber(first) >= toDoubleNumber(second); +} + +// cppcheck-suppress unusedFunction +bool MathLib::isLess(const std::string &first, const std::string &second) +{ + return toDoubleNumber(first) < toDoubleNumber(second); +} + +bool MathLib::isLessEqual(const std::string &first, const std::string &second) +{ + return toDoubleNumber(first) <= toDoubleNumber(second); +} + +/*! \brief Does the string represent the numerical value of 0? + * In case leading or trailing white space is provided, the function + * returns false. + * Requirement for this function: + * - This code is allowed to be slow because of simplicity of the code. + * + * \param[in] str The string to check. In case the string is empty, the function returns false. + * \return Return true in case the string represents a numerical null value. + **/ +bool MathLib::isNullValue(const std::string &str) +{ + if (str.empty() || (!std::isdigit(static_cast(str[0])) && (str[0] != '.' && str[0] != '-' && str[0] != '+'))) + return false; // Has to be a number + + if (!isInt(str) && !isFloat(str)) + return false; + const bool isHex = isIntHex(str) || isFloatHex(str); + for (const char i : str) { + if (std::isdigit(static_cast(i)) && i != '0') // May not contain digits other than 0 + return false; + if (i == 'p' || i == 'P' || (!isHex && (i == 'E' || i == 'e'))) + return true; + if (isHex && isxdigit(i) && i != '0') + return false; + } + return true; +} + +bool MathLib::isOctalDigit(char c) +{ + return (c >= '0' && c <= '7'); +} + +bool MathLib::isDigitSeparator(const std::string& iCode, std::string::size_type iPos) +{ + if (iPos == 0 || iPos >= iCode.size() || iCode[iPos] != '\'') + return false; + std::string::size_type i = iPos - 1; + while (std::isxdigit(iCode[i])) { + if (i == 0) + return true; // Only xdigits before ' + --i; + } + if (i == iPos - 1) // No xdigit before ' + return false; + + switch (iCode[i]) { + case ' ': + case '.': + case ',': + case 'x': + case '(': + case '{': + case '+': + case '-': + case '*': + case '%': + case '/': + case '&': + case '|': + case '^': + case '~': + case '=': + return true; + case '\'': + return isDigitSeparator(iCode, i); + default: + return false; + } +} + +MathLib::value operator+(const MathLib::value &v1, const MathLib::value &v2) +{ + return MathLib::value::calc('+',v1,v2); +} + +MathLib::value operator-(const MathLib::value &v1, const MathLib::value &v2) +{ + return MathLib::value::calc('-',v1,v2); +} + +MathLib::value operator*(const MathLib::value &v1, const MathLib::value &v2) +{ + return MathLib::value::calc('*',v1,v2); +} + +MathLib::value operator/(const MathLib::value &v1, const MathLib::value &v2) +{ + return MathLib::value::calc('/',v1,v2); +} + +MathLib::value operator%(const MathLib::value &v1, const MathLib::value &v2) +{ + return MathLib::value::calc('%',v1,v2); +} + +MathLib::value operator&(const MathLib::value &v1, const MathLib::value &v2) +{ + return MathLib::value::calc('&',v1,v2); +} + +MathLib::value operator|(const MathLib::value &v1, const MathLib::value &v2) +{ + return MathLib::value::calc('|',v1,v2); +} + +MathLib::value operator^(const MathLib::value &v1, const MathLib::value &v2) +{ + return MathLib::value::calc('^',v1,v2); +} + +MathLib::value operator<<(const MathLib::value &v1, const MathLib::value &v2) +{ + return v1.shiftLeft(v2); +} + +MathLib::value operator>>(const MathLib::value &v1, const MathLib::value &v2) +{ + return v1.shiftRight(v2); +} diff --git a/cppcheck-2.14.0/lib/mathlib.h b/cppcheck-2.14.0/lib/mathlib.h new file mode 100644 index 00000000..795250fe --- /dev/null +++ b/cppcheck-2.14.0/lib/mathlib.h @@ -0,0 +1,152 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef mathlibH +#define mathlibH +//--------------------------------------------------------------------------- + +#include "config.h" + +#include + +/// @addtogroup Core +/// @{ + +/** @brief simple math functions that uses operands stored in std::string. useful when performing math on tokens. */ + +class CPPCHECKLIB MathLib { + friend class TestMathLib; + +public: + /** @brief value class */ + class value { + private: + long long mIntValue{}; + double mDoubleValue{}; + enum class Type { INT, LONG, LONGLONG, FLOAT } mType; + bool mIsUnsigned{}; + + void promote(const value &v); + + public: + explicit value(const std::string &s); + std::string str() const; + bool isInt() const { + return mType != Type::FLOAT; + } + bool isFloat() const { + return mType == Type::FLOAT; + } + + double getDoubleValue() const { + return isFloat() ? mDoubleValue : (double)mIntValue; + } + + static value calc(char op, const value &v1, const value &v2); + int compare(const value &v) const; + value add(int v) const; + value shiftLeft(const value &v) const; + value shiftRight(const value &v) const; + }; + + using bigint = long long; + using biguint = unsigned long long; + static const int bigint_bits; + + /** @brief for conversion of numeric literals - for atoi-like conversions please use strToInt() */ + static bigint toBigNumber(const std::string & str); + /** @brief for conversion of numeric literals - for atoi-like conversions please use strToInt() */ + static biguint toBigUNumber(const std::string & str); + + template static std::string toString(T value) = delete; + /** @brief for conversion of numeric literals */ + static double toDoubleNumber(const std::string & str); + + static bool isInt(const std::string & str); + static bool isFloat(const std::string &str); + static bool isDecimalFloat(const std::string &str); + static bool isNegative(const std::string &str); + static bool isPositive(const std::string &str); + static bool isDec(const std::string & str); + static bool isFloatHex(const std::string& str); + static bool isIntHex(const std::string& str); + static bool isOct(const std::string& str); + static bool isBin(const std::string& str); + + static std::string getSuffix(const std::string& value); + /** + * Only used in unit tests + * + * \param[in] str string + * \param[in] supportMicrosoftExtensions support Microsoft extension: i64 + * \return true if str is a non-empty valid integer suffix + */ + static bool isValidIntegerSuffix(const std::string& str, bool supportMicrosoftExtensions=true); + + static std::string add(const std::string & first, const std::string & second); + static std::string subtract(const std::string & first, const std::string & second); + static std::string multiply(const std::string & first, const std::string & second); + static std::string divide(const std::string & first, const std::string & second); + static std::string mod(const std::string & first, const std::string & second); + static std::string calculate(const std::string & first, const std::string & second, char action); + + static std::string sin(const std::string & tok); + static std::string cos(const std::string & tok); + static std::string tan(const std::string & tok); + static std::string abs(const std::string & tok); + static bool isEqual(const std::string & first, const std::string & second); + static bool isNotEqual(const std::string & first, const std::string & second); + static bool isGreater(const std::string & first, const std::string & second); + static bool isGreaterEqual(const std::string & first, const std::string & second); + static bool isLess(const std::string & first, const std::string & second); + static bool isLessEqual(const std::string & first, const std::string & second); + static bool isNullValue(const std::string & str); + /** + * Return true if given character is 0,1,2,3,4,5,6 or 7. + * @param[in] c The character to check + * @return true if given character is octal digit. + */ + static bool isOctalDigit(char c); + + static unsigned int encodeMultiChar(const std::string& str); + + /** + * \param[in] iCode Code being considered + * \param[in] iPos A posision within iCode + * \return Whether iCode[iPos] is a C++14 digit separator + */ + static bool isDigitSeparator(const std::string& iCode, std::string::size_type iPos); +}; + +MathLib::value operator+(const MathLib::value &v1, const MathLib::value &v2); +MathLib::value operator-(const MathLib::value &v1, const MathLib::value &v2); +MathLib::value operator*(const MathLib::value &v1, const MathLib::value &v2); +MathLib::value operator/(const MathLib::value &v1, const MathLib::value &v2); +MathLib::value operator%(const MathLib::value &v1, const MathLib::value &v2); +MathLib::value operator&(const MathLib::value &v1, const MathLib::value &v2); +MathLib::value operator|(const MathLib::value &v1, const MathLib::value &v2); +MathLib::value operator^(const MathLib::value &v1, const MathLib::value &v2); +MathLib::value operator<<(const MathLib::value &v1, const MathLib::value &v2); +MathLib::value operator>>(const MathLib::value &v1, const MathLib::value &v2); + +template<> CPPCHECKLIB std::string MathLib::toString(double value); + +/// @} +//--------------------------------------------------------------------------- +#endif // mathlibH diff --git a/cppcheck-2.14.0/lib/path.cpp b/cppcheck-2.14.0/lib/path.cpp new file mode 100644 index 00000000..f7d69803 --- /dev/null +++ b/cppcheck-2.14.0/lib/path.cpp @@ -0,0 +1,345 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#if defined(__GNUC__) && (defined(_WIN32) || defined(__CYGWIN__)) +#undef __STRICT_ANSI__ +#endif + +#include "path.h" +#include "utils.h" + +#include +#include +#include +#include +#include + +#include + +#ifndef _WIN32 +#include +#include +#else +#include +#include +#endif +#if defined(__CYGWIN__) +#include +#endif +#if defined(__APPLE__) +#include +#endif + + +/** Is the filesystem case insensitive? */ +static constexpr bool caseInsensitiveFilesystem() +{ +#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) + // Windows is case insensitive + // MacOS is case insensitive by default (also supports case sensitivity) + return true; +#else + // TODO: Non-windows filesystems might be case insensitive + return false; +#endif +} + +std::string Path::toNativeSeparators(std::string path) +{ +#if defined(_WIN32) + constexpr char separ = '/'; + constexpr char native = '\\'; +#else + constexpr char separ = '\\'; + constexpr char native = '/'; +#endif + std::replace(path.begin(), path.end(), separ, native); + return path; +} + +std::string Path::fromNativeSeparators(std::string path) +{ + constexpr char nonnative = '\\'; + constexpr char newsepar = '/'; + std::replace(path.begin(), path.end(), nonnative, newsepar); + return path; +} + +std::string Path::simplifyPath(std::string originalPath) +{ + return simplecpp::simplifyPath(std::move(originalPath)); +} + +std::string Path::getPathFromFilename(const std::string &filename) +{ + const std::size_t pos = filename.find_last_of("\\/"); + + if (pos != std::string::npos) + return filename.substr(0, 1 + pos); + + return ""; +} + +bool Path::sameFileName(const std::string &fname1, const std::string &fname2) +{ + return caseInsensitiveFilesystem() ? (caseInsensitiveStringCompare(fname1, fname2) == 0) : (fname1 == fname2); +} + +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, bool lowercase) +{ + const std::string::size_type dotLocation = path.find_last_of('.'); + if (dotLocation == std::string::npos) + return ""; + + std::string extension = path.substr(dotLocation); + if (lowercase || caseInsensitiveFilesystem()) { + // on a case insensitive filesystem the case doesn't matter so + // let's return the extension in lowercase + strTolower(extension); + } + return extension; +} + +std::string Path::getFilenameExtensionInLowerCase(const std::string &path) +{ + return getFilenameExtension(path, true); +} + +std::string Path::getCurrentPath() +{ + char currentPath[4096]; + +#ifndef _WIN32 + if (getcwd(currentPath, 4096) != nullptr) +#else + if (_getcwd(currentPath, 4096) != nullptr) +#endif + return std::string(currentPath); + + return ""; +} + +std::string Path::getCurrentExecutablePath(const char* fallback) +{ + char buf[4096] = {}; + bool success{}; +#ifdef _WIN32 + success = (GetModuleFileNameA(nullptr, buf, sizeof(buf)) < sizeof(buf)); +#elif defined(__APPLE__) + uint32_t size = sizeof(buf); + success = (_NSGetExecutablePath(buf, &size) == 0); +#else + const char* procPath = +#ifdef __SVR4 // Solaris + "/proc/self/path/a.out"; +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) + "/proc/curproc/file"; +#else // Linux + "/proc/self/exe"; +#endif + success = (readlink(procPath, buf, sizeof(buf)) != -1); +#endif + return success ? std::string(buf) : std::string(fallback); +} + +bool Path::isAbsolute(const std::string& path) +{ + const std::string& nativePath = toNativeSeparators(path); + +#ifdef _WIN32 + if (path.length() < 2) + return false; + + // On Windows, 'C:\foo\bar' is an absolute path, while 'C:foo\bar' is not + return startsWith(nativePath, "\\\\") || (std::isalpha(nativePath[0]) != 0 && nativePath.compare(1, 2, ":\\") == 0); +#else + return !nativePath.empty() && nativePath[0] == '/'; +#endif +} + +std::string Path::getRelativePath(const std::string& absolutePath, const std::vector& basePaths) +{ + for (const std::string &bp : basePaths) { + if (absolutePath == bp || bp.empty()) // Seems to be a file, or path is empty + continue; + + if (absolutePath.compare(0, bp.length(), bp) != 0) + continue; + + if (endsWith(bp,'/')) + return absolutePath.substr(bp.length()); + if (absolutePath.size() > bp.size() && absolutePath[bp.length()] == '/') + return absolutePath.substr(bp.length() + 1); + } + return absolutePath; +} + +static const std::unordered_set cpp_src_exts = { + ".cpp", ".cxx", ".cc", ".c++", ".tpp", ".txx", ".ipp", ".ixx" +}; + +static const std::unordered_set c_src_exts = { + ".c", ".cl" +}; + +static const std::unordered_set header_exts = { + ".h", ".hpp", ".h++", ".hxx", ".hh" +}; + +bool Path::isC(const std::string &path) +{ + // In unix, ".C" is considered C++ file + const std::string extension = getFilenameExtension(path); + return extension == ".c" || + extension == ".cl"; +} + +bool Path::isCPP(const std::string &path) +{ + const std::string extension = getFilenameExtensionInLowerCase(path); + return extension == ".cpp" || + extension == ".cxx" || + extension == ".cc" || + extension == ".c++" || + extension == ".hpp" || + extension == ".hxx" || + extension == ".hh" || + extension == ".tpp" || + extension == ".txx" || + extension == ".ipp" || + extension == ".ixx" || + getFilenameExtension(path) == ".C"; // In unix, ".C" is considered C++ file +} + +bool Path::acceptFile(const std::string &path, const std::set &extra) +{ + bool header = false; + return (identify(path, &header) != Standards::Language::None && !header) || extra.find(getFilenameExtension(path)) != extra.end(); +} + +// cppcheck-suppress unusedFunction +bool Path::isHeader(const std::string &path) +{ + const std::string extension = getFilenameExtensionInLowerCase(path); + return startsWith(extension, ".h"); +} + +Standards::Language Path::identify(const std::string &path, bool *header) +{ + // cppcheck-suppress uninitvar - TODO: FP + if (header) + *header = false; + + std::string ext = getFilenameExtension(path); + if (ext == ".C") + return Standards::Language::CPP; + if (c_src_exts.find(ext) != c_src_exts.end()) + return Standards::Language::C; + // cppcheck-suppress knownConditionTrueFalse - TODO: FP + if (!caseInsensitiveFilesystem()) + strTolower(ext); + if (ext == ".h") { + if (header) + *header = true; + return Standards::Language::C; // treat as C for now + } + if (cpp_src_exts.find(ext) != cpp_src_exts.end()) + return Standards::Language::CPP; + if (header_exts.find(ext) != header_exts.end()) { + if (header) + *header = true; + return Standards::Language::CPP; + } + return Standards::Language::None; +} + +bool Path::isHeader2(const std::string &path) +{ + bool header; + (void)Path::identify(path, &header); + return header; +} + +std::string Path::getAbsoluteFilePath(const std::string& filePath) +{ + std::string absolute_path; +#ifdef _WIN32 + char absolute[_MAX_PATH]; + if (_fullpath(absolute, filePath.c_str(), _MAX_PATH)) + absolute_path = absolute; +#elif defined(__linux__) || defined(__sun) || defined(__hpux) || defined(__GNUC__) || defined(__CPPCHECK__) + char * absolute = realpath(filePath.c_str(), nullptr); + if (absolute) + absolute_path = absolute; + free(absolute); +#else +#error Platform absolute path function needed +#endif + return absolute_path; +} + +std::string Path::stripDirectoryPart(const std::string &file) +{ +#if defined(_WIN32) && !defined(__MINGW32__) + constexpr char native = '\\'; +#else + constexpr char native = '/'; +#endif + + const std::string::size_type p = file.rfind(native); + if (p != std::string::npos) { + return file.substr(p + 1); + } + return file; +} + +#ifdef _WIN32 +using mode_t = unsigned short; +#endif + +static mode_t file_type(const std::string &path) +{ + struct stat file_stat; + if (stat(path.c_str(), &file_stat) == -1) + return 0; + return file_stat.st_mode & S_IFMT; +} + +bool Path::isFile(const std::string &path) +{ + return file_type(path) == S_IFREG; +} + +bool Path::isDirectory(const std::string &path) +{ + return file_type(path) == S_IFDIR; +} + +std::string Path::join(const std::string& path1, const std::string& path2) { + if (path1.empty() || path2.empty()) + return path1 + path2; + if (path2.front() == '/') + return path2; + return ((path1.back() == '/') ? path1 : (path1 + "/")) + path2; +} diff --git a/cppcheck-2.14.0/lib/path.h b/cppcheck-2.14.0/lib/path.h new file mode 100644 index 00000000..9e0e7330 --- /dev/null +++ b/cppcheck-2.14.0/lib/path.h @@ -0,0 +1,224 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "standards.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. + * @param lowercase Return the extension in lower case + * @return Filename extension (containing the dot, e.g. ".h" or ".CPP"). + */ + static std::string getFilenameExtension(const std::string &path, bool lowercase = false); + + /** + * @brief Get an extension of the filename in lower case. + * @param path Path containing filename. + * @return Filename extension (containing the dot, e.g. ".h"). + */ + static std::string getFilenameExtensionInLowerCase(const std::string &path); + + /** + * @brief Returns the absolute path of current working directory + * @return absolute path of current working directory + */ + static std::string getCurrentPath(); + + /** + * @brief Returns the absolute path to the current executable + * @return absolute path to the current executable + */ + static std::string getCurrentExecutablePath(const char* fallback); + + /** + * @brief Check if given path is absolute + * @param path Path to check + * @return true if given path is absolute + */ + static bool isAbsolute(const std::string& path); + + /** + * @brief Create a relative path from an absolute one, if absolute path is inside the basePaths. + * @param absolutePath Path to be made relative. + * @param basePaths Paths to which it may be made relative. + * @return relative path, if possible. Otherwise absolutePath is returned unchanged + */ + static std::string getRelativePath(const std::string& absolutePath, const std::vector& basePaths); + + /** + * @brief Get an absolute file path from a relative one. + * @param filePath File path to be made absolute. + * @return absolute path, if possible. Otherwise an empty path is returned + */ + static std::string getAbsoluteFilePath(const std::string& filePath); + + /** + * @brief Check if the file extension indicates that it's a C/C++ source file. + * Check if the file has source file extension: *.c;*.cpp;*.cxx;*.c++;*.cc;*.txx + * @param filename filename to check. path info is optional + * @return true if the file extension indicates it should be checked + */ + static bool acceptFile(const std::string &filename) { + const std::set extra; + return acceptFile(filename, extra); + } + + /** + * @brief Check if the file extension indicates that it's a C/C++ source file. + * Check if the file has source file extension: *.c;*.cpp;*.cxx;*.c++;*.cc;*.txx + * @param path filename to check. path info is optional + * @param extra extra file extensions + * @return true if the file extension indicates it should be checked + */ + static bool acceptFile(const std::string &path, const std::set &extra); + + /** + * @brief Identify language based on file extension. + * @param path filename to check. path info is optional + * @return true if extension is meant for C files + * @deprecated does not account for headers - use @identify() instead + */ + static DEPRECATED bool isC(const std::string &path); + + /** + * @brief Identify language based on file extension. + * @param path filename to check. path info is optional + * @return true if extension is meant for C++ files + * @deprecated returns true for some header extensions - use @identify() instead + */ + static DEPRECATED bool isCPP(const std::string &path); + + /** + * @brief Is filename a header based on file extension + * @param path filename to check. path info is optional + * @return true if filename extension is meant for headers + * @deprecated returns only heuristic result - use @identify() or @isHeader2() instead + */ + static DEPRECATED bool isHeader(const std::string &path); + + /** + * @brief Is filename a header based on file extension + * @param path filename to check. path info is optional + * @return true if filename extension is meant for headers + */ + static bool isHeader2(const std::string &path); + + /** + * @brief Identify the language based on the file extension + * @param path filename to check. path info is optional + * @param header if provided indicates if the file is a header + * @return the language type + */ + static Standards::Language identify(const std::string &path, bool *header = nullptr); + + /** + * @brief Get filename without a directory path part. + * @param file filename to be stripped. path info is optional + * @return filename without directory path part. + */ + static std::string stripDirectoryPart(const std::string &file); + + /** + * @brief Checks if given path is a file + * @param path Path to be checked + * @return true if given path is a file + */ + static bool isFile(const std::string &path); + + /** + * @brief Checks if a given path is a directory + * @param path Path to be checked + * @return true if given path is a directory + */ + static bool isDirectory(const std::string &path); + + /** + * join 2 paths with '/' separators + */ + static std::string join(const std::string& path1, const std::string& path2); +}; + +/// @} +//--------------------------------------------------------------------------- +#endif // pathH diff --git a/cppcheck-2.14.0/lib/pathanalysis.cpp b/cppcheck-2.14.0/lib/pathanalysis.cpp new file mode 100644 index 00000000..1ad6a941 --- /dev/null +++ b/cppcheck-2.14.0/lib/pathanalysis.cpp @@ -0,0 +1,195 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "pathanalysis.h" + +#include "astutils.h" +#include "symboldatabase.h" +#include "token.h" +#include "vfvalue.h" + +#include +#include +#include + +const Scope* PathAnalysis::findOuterScope(const Scope * scope) +{ + if (!scope) + return nullptr; + if (scope->isLocal() && scope->type != Scope::eSwitch) + return findOuterScope(scope->nestedIn); + return scope; +} + +static const Token* assignExpr(const Token* tok) +{ + while (tok->astParent() && astIsLHS(tok)) { + if (Token::Match(tok->astParent(), "%assign%")) + return tok->astParent(); + tok = tok->astParent(); + } + return nullptr; +} + +std::pair PathAnalysis::checkCond(const Token * tok, bool& known) +{ + if (tok->hasKnownIntValue()) { + known = true; + return std::make_pair(tok->values().front().intvalue, !tok->values().front().intvalue); + } + auto it = std::find_if(tok->values().cbegin(), tok->values().cend(), [](const ValueFlow::Value& v) { + return v.isIntValue(); + }); + // If all possible values are the same, then assume all paths have the same value + if (it != tok->values().cend() && std::all_of(it, tok->values().cend(), [&](const ValueFlow::Value& v) { + if (v.isIntValue()) + return v.intvalue == it->intvalue; + return true; + })) { + known = false; + return std::make_pair(it->intvalue, !it->intvalue); + } + return std::make_pair(true, true); +} + +PathAnalysis::Progress PathAnalysis::forwardRecursive(const Token* tok, Info info, const std::function& f) +{ + if (!tok) + return Progress::Continue; + if (tok->astOperand1() && forwardRecursive(tok->astOperand1(), info, f) == Progress::Break) + return Progress::Break; + info.tok = tok; + if (f(info) == Progress::Break) + return Progress::Break; + if (tok->astOperand2() && forwardRecursive(tok->astOperand2(), std::move(info), f) == Progress::Break) + return Progress::Break; + return Progress::Continue; +} + +PathAnalysis::Progress PathAnalysis::forwardRange(const Token* startToken, const Token* endToken, Info info, const std::function& f) const +{ + for (const Token *tok = startToken; precedes(tok, endToken); tok = tok->next()) { + if (Token::Match(tok, "asm|goto|break|continue")) + return Progress::Break; + if (Token::Match(tok, "return|throw")) { + forwardRecursive(tok, std::move(info), f); + return Progress::Break; + // Evaluate RHS of assignment before LHS + } + if (const Token* assignTok = assignExpr(tok)) { + if (forwardRecursive(assignTok->astOperand2(), info, f) == Progress::Break) + return Progress::Break; + if (forwardRecursive(assignTok->astOperand1(), info, f) == Progress::Break) + return Progress::Break; + tok = nextAfterAstRightmostLeaf(assignTok); + if (!tok) + return Progress::Break; + } else if (Token::simpleMatch(tok, "}") && Token::simpleMatch(tok->link()->previous(), ") {") && Token::Match(tok->link()->linkAt(-1)->previous(), "if|while|for (")) { + const Token * blockStart = tok->link()->linkAt(-1)->previous(); + const Token * condTok = getCondTok(blockStart); + if (!condTok) + continue; + info.errorPath.emplace_back(condTok, "Assuming condition is true."); + // Traverse a loop a second time + if (Token::Match(blockStart, "for|while (")) { + const Token* endCond = blockStart->linkAt(1); + bool traverseLoop = true; + // Only traverse simple for loops + if (Token::simpleMatch(blockStart, "for") && !Token::Match(endCond->tokAt(-3), "; ++|--|%var% %var%|++|-- ) {")) + traverseLoop = false; + // Traverse loop a second time + if (traverseLoop) { + // Traverse condition + if (forwardRecursive(condTok, info, f) == Progress::Break) + return Progress::Break; + // TODO: Should we traverse the body: forwardRange(tok->link(), tok, info, f)? + } + } + if (Token::simpleMatch(tok, "} else {")) { + tok = tok->linkAt(2); + } + } else if (Token::Match(tok, "if|while|for (") && Token::simpleMatch(tok->next()->link(), ") {")) { + const Token * endCond = tok->next()->link(); + const Token * endBlock = endCond->next()->link(); + const Token * condTok = getCondTok(tok); + if (!condTok) + continue; + // Traverse condition + if (forwardRange(tok->next(), tok->next()->link(), info, f) == Progress::Break) + return Progress::Break; + Info i = info; + i.known = false; + i.errorPath.emplace_back(condTok, "Assuming condition is true."); + + // Check if condition is true or false + bool checkThen = false; + bool checkElse = false; + std::tie(checkThen, checkElse) = checkCond(condTok, i.known); + + // Traverse then block + if (checkThen) { + if (forwardRange(endCond->next(), endBlock, i, f) == Progress::Break) + return Progress::Break; + } + // Traverse else block + if (Token::simpleMatch(endBlock, "} else {")) { + if (checkElse) { + i.errorPath.back().second = "Assuming condition is false."; + const Progress result = forwardRange(endCond->next(), endBlock, std::move(i), f); + if (result == Progress::Break) + return Progress::Break; + } + tok = endBlock->linkAt(2); + } else { + tok = endBlock; + } + } else if (Token::simpleMatch(tok, "} else {")) { + tok = tok->linkAt(2); + } else { + info.tok = tok; + if (f(info) == Progress::Break) + return Progress::Break; + } + // Prevent infinite recursion + if (tok->next() == start) + break; + } + return Progress::Continue; +} + +void PathAnalysis::forward(const std::function& f) const +{ + const Scope * endScope = findOuterScope(start->scope()); + if (!endScope) + return; + const Token * endToken = endScope->bodyEnd; + Info info{start, ErrorPath{}, true}; + forwardRange(start, endToken, std::move(info), f); +} + +bool reaches(const Token * start, const Token * dest, const Library& library, ErrorPath* errorPath) +{ + PathAnalysis::Info info = PathAnalysis{start, library}.forwardFind([&](const PathAnalysis::Info& i) { + return (i.tok == dest); + }); + if (!info.tok) + return false; + if (errorPath) + errorPath->insert(errorPath->end(), info.errorPath.cbegin(), info.errorPath.cend()); + return true; +} diff --git a/cppcheck-2.14.0/lib/pathanalysis.h b/cppcheck-2.14.0/lib/pathanalysis.h new file mode 100644 index 00000000..94c3318a --- /dev/null +++ b/cppcheck-2.14.0/lib/pathanalysis.h @@ -0,0 +1,82 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GUARD_PATHANALYSIS_H +#define GUARD_PATHANALYSIS_H + +#include "errortypes.h" + +#include +#include +#include + +class Library; +class Scope; +class Token; + +struct PathAnalysis { + enum class Progress { + Continue, + Break + }; + PathAnalysis(const Token* start, const Library& library) + : start(start), library(&library) + {} + const Token * start; + const Library * library; + + struct Info { + const Token* tok; + ErrorPath errorPath; + bool known; + }; + + void forward(const std::function& f) const; + + Info forwardFind(std::function pred) const { + Info result{}; + forward([&](const Info& info) { + if (pred(info)) { + result = info; + return Progress::Break; + } + return Progress::Continue; + }); + return result; + } +private: + + static Progress forwardRecursive(const Token* tok, Info info, const std::function& f); + Progress forwardRange(const Token* startToken, const Token* endToken, Info info, const std::function& f) const; + + static const Scope* findOuterScope(const Scope * scope); + + static std::pair checkCond(const Token * tok, bool& known); +}; + +/** + * @brief Returns true if there is a path between the two tokens + * + * @param start Starting point of the path + * @param dest The path destination + * @param errorPath Adds the path traversal to the errorPath + */ +bool reaches(const Token * start, const Token * dest, const Library& library, ErrorPath* errorPath); + +#endif + diff --git a/cppcheck-2.14.0/lib/pathmatch.cpp b/cppcheck-2.14.0/lib/pathmatch.cpp new file mode 100644 index 00000000..a72a57fe --- /dev/null +++ b/cppcheck-2.14.0/lib/pathmatch.cpp @@ -0,0 +1,85 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "pathmatch.h" + +#include "path.h" +#include "utils.h" + +#include +#include + +PathMatch::PathMatch(std::vector excludedPaths, bool caseSensitive) + : mExcludedPaths(std::move(excludedPaths)), mCaseSensitive(caseSensitive) +{ + if (!mCaseSensitive) + for (std::string& excludedPath : mExcludedPaths) + strTolower(excludedPath); + mWorkingDirectory.push_back(Path::getCurrentPath()); +} + +bool PathMatch::match(const std::string &path) const +{ + if (path.empty()) + return false; + + // TODO: align the exclusion logic with ImportProject::ignorePaths() + for (std::vector::const_iterator i = mExcludedPaths.cbegin(); i != mExcludedPaths.cend(); ++i) { + const std::string excludedPath((!Path::isAbsolute(path) && Path::isAbsolute(*i)) ? Path::getRelativePath(*i, mWorkingDirectory) : *i); + + std::string findpath = Path::fromNativeSeparators(path); + if (!mCaseSensitive) + strTolower(findpath); + + // Filtering directory name + if (endsWith(excludedPath,'/')) { + if (!endsWith(findpath,'/')) + findpath = removeFilename(findpath); + + if (excludedPath.length() > findpath.length()) + continue; + // Match relative paths starting with mask + // -isrc matches src/foo.cpp + if (findpath.compare(0, excludedPath.size(), excludedPath) == 0) + return true; + // Match only full directory name in middle or end of the path + // -isrc matches myproject/src/ but does not match + // myproject/srcfiles/ or myproject/mysrc/ + if (findpath.find("/" + excludedPath) != std::string::npos) + return true; + } + // Filtering filename + else { + if (excludedPath.length() > findpath.length()) + continue; + // Check if path ends with mask + // -ifoo.cpp matches (./)foo.c, src/foo.cpp and proj/src/foo.cpp + // -isrc/file.cpp matches src/foo.cpp and proj/src/foo.cpp + if (findpath.compare(findpath.size() - excludedPath.size(), findpath.size(), excludedPath) == 0) + return true; + + } + } + return false; +} + +std::string PathMatch::removeFilename(const std::string &path) +{ + const std::size_t ind = path.find_last_of('/'); + return path.substr(0, ind + 1); +} diff --git a/cppcheck-2.14.0/lib/pathmatch.h b/cppcheck-2.14.0/lib/pathmatch.h new file mode 100644 index 00000000..7c8df1ed --- /dev/null +++ b/cppcheck-2.14.0/lib/pathmatch.h @@ -0,0 +1,68 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef PATHMATCH_H +#define PATHMATCH_H + +#include "config.h" + +#include +#include + +/// @addtogroup CLI +/// @{ + +/** + * @brief Simple path matching for ignoring paths in CLI. + */ +class CPPCHECKLIB PathMatch { +public: + + /** + * The constructor. + * @param excludedPaths List of masks. + * @param caseSensitive Match the case of the characters when + * matching paths? + */ + explicit PathMatch(std::vector excludedPaths, bool caseSensitive = true); + + /** + * @brief Match path against list of masks. + * @param path Path to match. + * @return true if any of the masks match the path, false otherwise. + */ + bool match(const std::string &path) const; + +protected: + + /** + * @brief Remove filename part from the path. + * @param path Path to edit. + * @return path without filename part. + */ + static std::string removeFilename(const std::string &path); + +private: + std::vector mExcludedPaths; + bool mCaseSensitive; + std::vector mWorkingDirectory; +}; + +/// @} + +#endif // PATHMATCH_H diff --git a/cppcheck-2.14.0/lib/pcrerules.pri b/cppcheck-2.14.0/lib/pcrerules.pri new file mode 100644 index 00000000..267b54ab --- /dev/null +++ b/cppcheck-2.14.0/lib/pcrerules.pri @@ -0,0 +1,13 @@ +# 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.") +} diff --git a/cppcheck-2.14.0/lib/platform.cpp b/cppcheck-2.14.0/lib/platform.cpp new file mode 100644 index 00000000..f54b9110 --- /dev/null +++ b/cppcheck-2.14.0/lib/platform.cpp @@ -0,0 +1,446 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "platform.h" + +#include "path.h" + +#include +#include +#include +#include + +#include "xml.h" + +Platform::Platform() +{ + set(Type::Native); +} + + +bool Platform::set(Type t) +{ + switch (t) { + case Type::Unspecified: // unknown type sizes (sizes etc are set but are not known) + case Type::Native: // same as system this code was compile on + type = t; + sizeof_bool = sizeof(bool); + sizeof_short = sizeof(short); + sizeof_int = sizeof(int); + sizeof_long = sizeof(long); + sizeof_long_long = sizeof(long long); + sizeof_float = sizeof(float); + sizeof_double = sizeof(double); + sizeof_long_double = sizeof(long double); + sizeof_wchar_t = sizeof(wchar_t); + sizeof_size_t = sizeof(std::size_t); + sizeof_pointer = sizeof(void *); + if (type == Type::Unspecified) { + defaultSign = '\0'; + } else { + defaultSign = std::numeric_limits::is_signed ? 's' : 'u'; + } + char_bit = 8; + short_bit = char_bit * sizeof_short; + int_bit = char_bit * sizeof_int; + long_bit = char_bit * sizeof_long; + long_long_bit = char_bit * sizeof_long_long; + return true; + case Type::Win32W: + case Type::Win32A: + type = t; + sizeof_bool = 1; // 4 in Visual C++ 4.2 + sizeof_short = 2; + sizeof_int = 4; + sizeof_long = 4; + sizeof_long_long = 8; + sizeof_float = 4; + sizeof_double = 8; + sizeof_long_double = 8; + sizeof_wchar_t = 2; + sizeof_size_t = 4; + sizeof_pointer = 4; + defaultSign = '\0'; + char_bit = 8; + short_bit = char_bit * sizeof_short; + int_bit = char_bit * sizeof_int; + long_bit = char_bit * sizeof_long; + long_long_bit = char_bit * sizeof_long_long; + return true; + case Type::Win64: + type = t; + sizeof_bool = 1; + sizeof_short = 2; + sizeof_int = 4; + sizeof_long = 4; + sizeof_long_long = 8; + sizeof_float = 4; + sizeof_double = 8; + sizeof_long_double = 8; + sizeof_wchar_t = 2; + sizeof_size_t = 8; + sizeof_pointer = 8; + defaultSign = '\0'; + char_bit = 8; + short_bit = char_bit * sizeof_short; + int_bit = char_bit * sizeof_int; + long_bit = char_bit * sizeof_long; + long_long_bit = char_bit * sizeof_long_long; + return true; + case Type::Unix32: + type = t; + sizeof_bool = 1; + sizeof_short = 2; + sizeof_int = 4; + sizeof_long = 4; + sizeof_long_long = 8; + sizeof_float = 4; + sizeof_double = 8; + sizeof_long_double = 12; + sizeof_wchar_t = 4; + sizeof_size_t = 4; + sizeof_pointer = 4; + defaultSign = '\0'; + char_bit = 8; + short_bit = char_bit * sizeof_short; + int_bit = char_bit * sizeof_int; + long_bit = char_bit * sizeof_long; + long_long_bit = char_bit * sizeof_long_long; + return true; + case Type::Unix64: + type = t; + sizeof_bool = 1; + sizeof_short = 2; + sizeof_int = 4; + sizeof_long = 8; + sizeof_long_long = 8; + sizeof_float = 4; + sizeof_double = 8; + sizeof_long_double = 16; + sizeof_wchar_t = 4; + sizeof_size_t = 8; + sizeof_pointer = 8; + defaultSign = '\0'; + char_bit = 8; + short_bit = char_bit * sizeof_short; + int_bit = char_bit * sizeof_int; + long_bit = char_bit * sizeof_long; + long_long_bit = char_bit * sizeof_long_long; + return true; + case Type::File: + // sizes are not set. + return false; + } + // unsupported platform + return false; +} + +bool Platform::set(const std::string& platformstr, std::string& errstr, const std::vector& paths, bool verbose) +{ + if (platformstr == "win32A") + set(Type::Win32A); + else if (platformstr == "win32W") + set(Type::Win32W); + else if (platformstr == "win64") + set(Type::Win64); + else if (platformstr == "unix32") + set(Type::Unix32); + else if (platformstr == "unix64") + set(Type::Unix64); + else if (platformstr == "native") + set(Type::Native); + else if (platformstr == "unspecified") + set(Type::Unspecified); + else if (paths.empty()) { + errstr = "unrecognized platform: '" + platformstr + "' (no lookup)."; + return false; + } + else { + bool found = false; + for (const std::string& path : paths) { + if (verbose) + std::cout << "looking for platform '" + platformstr + "' in '" + path + "'" << std::endl; + if (loadFromFile(path.c_str(), platformstr, verbose)) { + found = true; + break; + } + } + if (!found) { + errstr = "unrecognized platform: '" + platformstr + "'."; + return false; + } + } + + return true; +} + +bool Platform::loadFromFile(const char exename[], const std::string &filename, bool verbose) +{ + // TODO: only append .xml if missing + // TODO: use native separators + std::vector filenames{ + filename, + filename + ".xml", + "platforms/" + filename, + "platforms/" + filename + ".xml" + }; + if (exename && (std::string::npos != Path::fromNativeSeparators(exename).find('/'))) { + filenames.push_back(Path::getPathFromFilename(Path::fromNativeSeparators(exename)) + filename); + filenames.push_back(Path::getPathFromFilename(Path::fromNativeSeparators(exename)) + "platforms/" + filename); + filenames.push_back(Path::getPathFromFilename(Path::fromNativeSeparators(exename)) + "platforms/" + filename + ".xml"); + } +#ifdef FILESDIR + std::string filesdir = FILESDIR; + if (!filesdir.empty() && filesdir[filesdir.size()-1] != '/') + filesdir += '/'; + filenames.push_back(filesdir + ("platforms/" + filename)); + filenames.push_back(filesdir + ("platforms/" + filename + ".xml")); +#endif + + // open file.. + tinyxml2::XMLDocument doc; + bool success = false; + for (const std::string & f : filenames) { + if (verbose) + std::cout << "try to load platform file '" << f << "' ... "; + if (doc.LoadFile(f.c_str()) == tinyxml2::XML_SUCCESS) { + if (verbose) + std::cout << "Success" << std::endl; + success = true; + break; + } + if (verbose) + std::cout << doc.ErrorStr() << std::endl; + } + if (!success) + return false; + + return loadFromXmlDocument(&doc); +} + +static unsigned int xmlTextAsUInt(const tinyxml2::XMLElement* node, bool& error) +{ + unsigned int retval = 0; + if (node->QueryUnsignedText(&retval) != tinyxml2::XML_SUCCESS) + error = true; + return retval; +} + +bool Platform::loadFromXmlDocument(const tinyxml2::XMLDocument *doc) +{ + const tinyxml2::XMLElement * const rootnode = doc->FirstChildElement(); + + if (!rootnode || std::strcmp(rootnode->Name(), "platform") != 0) + return false; + + bool error = false; + for (const tinyxml2::XMLElement *node = rootnode->FirstChildElement(); node; node = node->NextSiblingElement()) { + if (std::strcmp(node->Name(), "default-sign") == 0) { + const char* str = node->GetText(); + if (str) + defaultSign = *str; + else + error = true; + } else if (std::strcmp(node->Name(), "char_bit") == 0) + char_bit = xmlTextAsUInt(node, error); + else if (std::strcmp(node->Name(), "sizeof") == 0) { + for (const tinyxml2::XMLElement *sz = node->FirstChildElement(); sz; sz = sz->NextSiblingElement()) { + if (std::strcmp(sz->Name(), "short") == 0) + sizeof_short = xmlTextAsUInt(sz, error); + else if (std::strcmp(sz->Name(), "bool") == 0) + sizeof_bool = xmlTextAsUInt(sz, error); + else if (std::strcmp(sz->Name(), "int") == 0) + sizeof_int = xmlTextAsUInt(sz, error); + else if (std::strcmp(sz->Name(), "long") == 0) + sizeof_long = xmlTextAsUInt(sz, error); + else if (std::strcmp(sz->Name(), "long-long") == 0) + sizeof_long_long = xmlTextAsUInt(sz, error); + else if (std::strcmp(sz->Name(), "float") == 0) + sizeof_float = xmlTextAsUInt(sz, error); + else if (std::strcmp(sz->Name(), "double") == 0) + sizeof_double = xmlTextAsUInt(sz, error); + else if (std::strcmp(sz->Name(), "long-double") == 0) + sizeof_long_double = xmlTextAsUInt(sz, error); + else if (std::strcmp(sz->Name(), "pointer") == 0) + sizeof_pointer = xmlTextAsUInt(sz, error); + else if (std::strcmp(sz->Name(), "size_t") == 0) + sizeof_size_t = xmlTextAsUInt(sz, error); + else if (std::strcmp(sz->Name(), "wchar_t") == 0) + sizeof_wchar_t = xmlTextAsUInt(sz, error); + } + } + } + + short_bit = char_bit * sizeof_short; + int_bit = char_bit * sizeof_int; + long_bit = char_bit * sizeof_long; + long_long_bit = char_bit * sizeof_long_long; + + type = Type::File; + return !error; +} + +std::string Platform::getLimitsDefines(bool c99) const +{ + std::string s; + + // climits / limits.h + s += "CHAR_BIT="; + s += std::to_string(char_bit); + s += ";SCHAR_MIN="; + s += std::to_string(min_value(char_bit)); + s += ";SCHAR_MAX="; + s += std::to_string(max_value(char_bit)); + s += ";UCHAR_MAX="; + s += std::to_string(max_value(char_bit+1)); + s += ";CHAR_MIN="; + if (defaultSign == 'u') + s += std::to_string(min_value(char_bit)); + else + s += std::to_string(0); + s += ";CHAR_MAX="; + if (defaultSign == 'u') + s += std::to_string(max_value(char_bit+1)); + else + s += std::to_string(max_value(char_bit)); + // TODO + //s += ";MB_LEN_MAX="; + s += ";SHRT_MIN="; + s += std::to_string(min_value(short_bit)); + s += ";SHRT_MAX="; + s += std::to_string(max_value(short_bit)); + s += ";USHRT_MAX="; + s += std::to_string(max_value(short_bit+1)); + s += ";INT_MIN="; + s += std::to_string(min_value(int_bit)); + s += ";INT_MAX="; + s += std::to_string(max_value(int_bit)); + s += ";UINT_MAX="; + s += std::to_string(max_value(int_bit+1)); + s += ";LONG_MIN="; + s += std::to_string(min_value(long_bit)); + s += ";LONG_MAX="; + s += std::to_string(max_value(long_bit)); + s += ";ULONG_MAX="; + s += std::to_string(max_value(long_bit+1)); + if (c99) { + s += ";LLONG_MIN="; + s += std::to_string(min_value(long_long_bit)); + s += ";LLONG_MAX="; + s += std::to_string(max_value(long_long_bit)); + s += ";ULLONG_MAX="; + s += std::to_string(max_value(long_long_bit + 1)); + } + + // cstdint / stdint.h + // FIXME: these are currently hard-coded in std.cfg + /* + INTMAX_MIN + INTMAX_MAX + UINTMAX_MAX + INTN_MIN + INTN_MAX + UINTN_MAX + INT_LEASTN_MIN + INT_LEASTN_MAX + UINT_LEASTN_MAX + INT_FASTN_MIN + INT_FASTN_MAX + UINT_FASTN_MAX + INTPTR_MIN + INTPTR_MAX + UINTPTR_MAX + SIZE_MAX + PTRDIFF_MIN + PTRDIFF_MAX + SIG_ATOMIC_MIN + SIG_ATOMIC_MAX + WCHAR_MIN + WCHAR_MAX + WINT_MIN + WINT_MAX + + // function-like macros + // implemented in std.cfg + INTMAX_C + UINTMAX_C + INTN_C + UINTN_C + */ + + // cfloat / float.h + /* + // TODO: implement + FLT_RADIX + + FLT_MANT_DIG + DBL_MANT_DIG + LDBL_MANT_DIG + + FLT_DIG + DBL_DIG + LDBL_DIG + + FLT_MIN_EXP + DBL_MIN_EXP + LDBL_MIN_EXP + + FLT_MIN_10_EXP + DBL_MIN_10_EXP + LDBL_MIN_10_EXP + + FLT_MAX_EXP + DBL_MAX_EXP + LDBL_MAX_EXP + + FLT_MAX_10_EXP + DBL_MAX_10_EXP + LDBL_MAX_10_EXP + + FLT_MAX + DBL_MAX + LDBL_MAX + + FLT_EPSILON + DBL_EPSILON + LDBL_EPSILON + + FLT_MIN + DBL_MIN + LDBL_MIN + + FLT_ROUNDS + + // C99 / C++11 only + FLT_EVAL_METHOD + + DECIMAL_DIG + */ + + return s; +} + +std::string Platform::getLimitsDefines(Standards::cstd_t cstd) const +{ + return getLimitsDefines(cstd >= Standards::cstd_t::C99); +} + +std::string Platform::getLimitsDefines(Standards::cppstd_t cppstd) const +{ + return getLimitsDefines(cppstd >= Standards::cppstd_t::CPP11); +} diff --git a/cppcheck-2.14.0/lib/platform.h b/cppcheck-2.14.0/lib/platform.h new file mode 100644 index 00000000..e822d0bb --- /dev/null +++ b/cppcheck-2.14.0/lib/platform.h @@ -0,0 +1,195 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef platformH +#define platformH +//--------------------------------------------------------------------------- + +#include "config.h" +#include "standards.h" + +#include +#include +#include +#include +#include + +/// @addtogroup Core +/// @{ + +namespace tinyxml2 { + class XMLDocument; +} + +/** + * @brief Platform settings + */ +class CPPCHECKLIB Platform { +private: + static long long min_value(int bit) { + if (bit >= 64) + return LLONG_MIN; + return -(1LL << (bit-1)); + } + + static long long max_value(int bit) { + if (bit >= 64) + return (~0ULL) >> 1; + return (1LL << (bit-1)) - 1LL; + } + + /** provides list of defines specified by the limit.h/climits includes */ + std::string getLimitsDefines(bool c99) const; +public: + Platform(); + + bool isIntValue(long long value) const { + return value >= min_value(int_bit) && value <= max_value(int_bit); + } + + bool isIntValue(unsigned long long value) const { + const unsigned long long intMax = max_value(int_bit); + return value <= intMax; + } + + bool isLongValue(long long value) const { + return value >= min_value(long_bit) && value <= max_value(long_bit); + } + + bool isLongValue(unsigned long long value) const { + const unsigned long long longMax = max_value(long_bit); + return value <= longMax; + } + + bool isLongLongValue(unsigned long long value) const { + const unsigned long long longLongMax = max_value(long_long_bit); + return value <= longLongMax; + } + + nonneg int char_bit; /// bits in char + nonneg int short_bit; /// bits in short + nonneg int int_bit; /// bits in int + nonneg int long_bit; /// bits in long + nonneg int long_long_bit; /// bits in long long + + /** size of standard types */ + std::size_t sizeof_bool; + std::size_t sizeof_short; + std::size_t sizeof_int; + std::size_t sizeof_long; + std::size_t sizeof_long_long; + std::size_t sizeof_float; + std::size_t sizeof_double; + std::size_t sizeof_long_double; + std::size_t sizeof_wchar_t; + std::size_t sizeof_size_t; + std::size_t sizeof_pointer; + + char defaultSign; // unsigned:'u', signed:'s', unknown:'\0' + + enum Type { + Unspecified, // No platform specified + Native, // whatever system this code was compiled on + Win32A, + Win32W, + Win64, + Unix32, + Unix64, + File + }; + + /** platform type */ + Type type; + + /** set the platform type for predefined platforms - deprecated use set(const std::string&, std::string&) instead */ + bool set(Type t); + + /** set the platform type */ + bool set(const std::string& platformstr, std::string& errstr, const std::vector& paths = {}, bool verbose = false); + + /** + * load platform file + * @param exename application path + * @param filename platform filename + * @param verbose log verbose information about the lookup + * @return returns true if file was loaded successfully + */ + bool loadFromFile(const char exename[], const std::string &filename, bool verbose = false); + + /** load platform from xml document, primarily for testing */ + bool loadFromXmlDocument(const tinyxml2::XMLDocument *doc); + + /** + * @brief Returns true if platform type is Windows + * @return true if Windows platform type. + */ + bool isWindows() const { + return type == Type::Win32A || + type == Type::Win32W || + type == Type::Win64; + } + + const char *toString() const { + return toString(type); + } + + static const char *toString(Type pt) { + switch (pt) { + case Type::Unspecified: + return "unspecified"; + case Type::Native: + return "native"; + case Type::Win32A: + return "win32A"; + case Type::Win32W: + return "win32W"; + case Type::Win64: + return "win64"; + case Type::Unix32: + return "unix32"; + case Type::Unix64: + return "unix64"; + case Type::File: + return "platformFile"; + default: + throw std::runtime_error("unknown platform"); + } + } + + long long unsignedCharMax() const { + return max_value(char_bit + 1); + } + + long long signedCharMax() const { + return max_value(char_bit); + } + + long long signedCharMin() const { + return min_value(char_bit); + } + + /** provides list of defines specified by the limit.h/climits includes */ + std::string getLimitsDefines(Standards::cstd_t cstd) const; + /** provides list of defines specified by the limit.h/climits includes */ + std::string getLimitsDefines(Standards::cppstd_t cppstd) const; +}; + +/// @} +//--------------------------------------------------------------------------- +#endif // platformH diff --git a/cppcheck-2.14.0/lib/precompiled.h b/cppcheck-2.14.0/lib/precompiled.h new file mode 100644 index 00000000..872dd00e --- /dev/null +++ b/cppcheck-2.14.0/lib/precompiled.h @@ -0,0 +1,32 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +// IWYU pragma: begin_keep +#include "astutils.h" +#include "errorlogger.h" +#include "library.h" +//#include "matchcompiler.h" +#include "mathlib.h" +#include "token.h" +#include "settings.h" +#include "suppressions.h" +#include "utils.h" +#include "valueflow.h" +// IWYU pragma: end_keep diff --git a/cppcheck-2.14.0/lib/preprocessor.cpp b/cppcheck-2.14.0/lib/preprocessor.cpp new file mode 100644 index 00000000..c6c51d1a --- /dev/null +++ b/cppcheck-2.14.0/lib/preprocessor.cpp @@ -0,0 +1,1014 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "preprocessor.h" + +#include "errorlogger.h" +#include "errortypes.h" +#include "library.h" +#include "path.h" +#include "platform.h" +#include "settings.h" +#include "standards.h" +#include "suppressions.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include + +#include + +static bool sameline(const simplecpp::Token *tok1, const simplecpp::Token *tok2) +{ + return tok1 && tok2 && tok1->location.sameline(tok2->location); +} + +/** + * Remove heading and trailing whitespaces from the input parameter. + * If string is all spaces/tabs, return empty string. + * @param s The string to trim. + */ +static std::string trim(const std::string& s) +{ + const std::string::size_type beg = s.find_first_not_of(" \t"); + if (beg == std::string::npos) + return ""; + const std::string::size_type end = s.find_last_not_of(" \t"); + return s.substr(beg, end - beg + 1); +} + +Directive::Directive(std::string _file, const int _linenr, const std::string &_str) : + file(std::move(_file)), + linenr(_linenr), + str(trim(_str)) +{} + +char Preprocessor::macroChar = char(1); + +Preprocessor::Preprocessor(const Settings& settings, ErrorLogger &errorLogger) : mSettings(settings), mErrorLogger(errorLogger) +{} + +Preprocessor::~Preprocessor() +{ + for (const std::pair& tokenList : mTokenLists) + delete tokenList.second; +} + +namespace { + struct BadInlineSuppression { + BadInlineSuppression(std::string file, const int line, std::string msg) : file(std::move(file)), line(line), errmsg(std::move(msg)) {} + std::string file; + int line; + std::string errmsg; + }; +} + +static bool parseInlineSuppressionCommentToken(const simplecpp::Token *tok, std::list &inlineSuppressions, std::list &bad) +{ + const std::string cppchecksuppress("cppcheck-suppress"); + + const std::string &comment = tok->str(); + if (comment.size() < cppchecksuppress.size()) + return false; + const std::string::size_type pos1 = comment.find_first_not_of("/* \t"); + if (pos1 == std::string::npos) + return false; + if (pos1 + cppchecksuppress.size() >= comment.size()) + return false; + if (comment.substr(pos1, cppchecksuppress.size()) != cppchecksuppress) + return false; + + // check if it has a prefix + const std::string::size_type posEndComment = comment.find_first_of(" [", pos1+cppchecksuppress.size()); + + // skip spaces after "cppcheck-suppress" and its possible prefix + const std::string::size_type pos2 = comment.find_first_not_of(' ', posEndComment); + if (pos2 == std::string::npos) + return false; + + SuppressionList::Type errorType = SuppressionList::Type::unique; + + // determine prefix if specified + if (posEndComment >= (pos1 + cppchecksuppress.size() + 1)) { + if (comment.at(pos1 + cppchecksuppress.size()) != '-') + return false; + + const unsigned int argumentLength = + posEndComment - (pos1 + cppchecksuppress.size() + 1); + + const std::string suppressTypeString = + comment.substr(pos1 + cppchecksuppress.size() + 1, argumentLength); + + if ("file" == suppressTypeString) + errorType = SuppressionList::Type::file; + else if ("begin" == suppressTypeString) + errorType = SuppressionList::Type::blockBegin; + else if ("end" == suppressTypeString) + errorType = SuppressionList::Type::blockEnd; + else if ("macro" == suppressTypeString) + errorType = SuppressionList::Type::macro; + else + return false; + } + + if (comment[pos2] == '[') { + // multi suppress format + std::string errmsg; + std::vector suppressions = SuppressionList::parseMultiSuppressComment(comment, &errmsg); + + for (SuppressionList::Suppression &s : suppressions) { + s.type = errorType; + s.lineNumber = tok->location.line; + } + + if (!errmsg.empty()) + bad.emplace_back(tok->location.file(), tok->location.line, std::move(errmsg)); + + std::copy_if(suppressions.cbegin(), suppressions.cend(), std::back_inserter(inlineSuppressions), [](const SuppressionList::Suppression& s) { + return !s.errorId.empty(); + }); + } else { + //single suppress format + std::string errmsg; + SuppressionList::Suppression s; + if (!s.parseComment(comment, &errmsg)) + return false; + + s.type = errorType; + s.lineNumber = tok->location.line; + + if (!s.errorId.empty()) + inlineSuppressions.push_back(std::move(s)); + + if (!errmsg.empty()) + bad.emplace_back(tok->location.file(), tok->location.line, std::move(errmsg)); + } + + return true; +} + +static void addInlineSuppressions(const simplecpp::TokenList &tokens, const Settings &settings, SuppressionList &suppressions, std::list &bad) +{ + std::list inlineSuppressionsBlockBegin; + + bool onlyComments = true; + + for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) { + if (!tok->comment) { + onlyComments = false; + continue; + } + + std::list inlineSuppressions; + if (!parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad)) + continue; + + if (!sameline(tok->previous, tok)) { + // find code after comment.. + if (tok->next) { + tok = tok->next; + + while (tok->comment) { + parseInlineSuppressionCommentToken(tok, inlineSuppressions, bad); + if (tok->next) { + tok = tok->next; + } else { + break; + } + } + } + } + + if (inlineSuppressions.empty()) + continue; + + // It should never happen + if (!tok) + continue; + + // Relative filename + std::string relativeFilename(tok->location.file()); + if (settings.relativePaths) { + for (const std::string & basePath : settings.basePaths) { + const std::string bp = basePath + "/"; + if (relativeFilename.compare(0,bp.size(),bp)==0) { + relativeFilename = relativeFilename.substr(bp.size()); + } + } + } + relativeFilename = Path::simplifyPath(relativeFilename); + + // Macro name + std::string macroName; + if (tok->str() == "#" && tok->next && tok->next->str() == "define") { + const simplecpp::Token *macroNameTok = tok->next->next; + if (sameline(tok, macroNameTok) && macroNameTok->name) { + macroName = macroNameTok->str(); + } + } + + // Add the suppressions. + for (SuppressionList::Suppression &suppr : inlineSuppressions) { + suppr.fileName = relativeFilename; + + if (SuppressionList::Type::blockBegin == suppr.type) + { + inlineSuppressionsBlockBegin.push_back(std::move(suppr)); + } else if (SuppressionList::Type::blockEnd == suppr.type) { + bool throwError = true; + + if (!inlineSuppressionsBlockBegin.empty()) { + const SuppressionList::Suppression lastBeginSuppression = inlineSuppressionsBlockBegin.back(); + + auto supprBegin = inlineSuppressionsBlockBegin.begin(); + while (supprBegin != inlineSuppressionsBlockBegin.end()) + { + if (lastBeginSuppression.lineNumber != supprBegin->lineNumber) { + ++supprBegin; + continue; + } + + if (suppr.symbolName == supprBegin->symbolName && suppr.lineNumber > supprBegin->lineNumber) { + suppr.lineBegin = supprBegin->lineNumber; + suppr.lineEnd = suppr.lineNumber; + suppr.lineNumber = supprBegin->lineNumber; + suppr.type = SuppressionList::Type::block; + inlineSuppressionsBlockBegin.erase(supprBegin); + suppressions.addSuppression(std::move(suppr)); + throwError = false; + break; + } + ++supprBegin; + } + } + + if (throwError) { + // NOLINTNEXTLINE(bugprone-use-after-move) - moved only when thrownError is false + bad.emplace_back(suppr.fileName, suppr.lineNumber, "Suppress End: No matching begin"); + } + } else if (SuppressionList::Type::unique == suppr.type || suppr.type == SuppressionList::Type::macro) { + // special handling when suppressing { warnings for backwards compatibility + const bool thisAndNextLine = tok->previous && + tok->previous->previous && + tok->next && + !sameline(tok->previous->previous, tok->previous) && + tok->location.line + 1 == tok->next->location.line && + tok->location.fileIndex == tok->next->location.fileIndex && + tok->previous->str() == "{"; + + suppr.thisAndNextLine = thisAndNextLine; + suppr.lineNumber = tok->location.line; + suppr.macroName = macroName; + suppressions.addSuppression(std::move(suppr)); + } else if (SuppressionList::Type::file == suppr.type) { + if (onlyComments) + suppressions.addSuppression(std::move(suppr)); + else + bad.emplace_back(suppr.fileName, suppr.lineNumber, "File suppression should be at the top of the file"); + } + } + } + + for (const SuppressionList::Suppression & suppr: inlineSuppressionsBlockBegin) + // cppcheck-suppress useStlAlgorithm + bad.emplace_back(suppr.fileName, suppr.lineNumber, "Suppress Begin: No matching end"); +} + +void Preprocessor::inlineSuppressions(const simplecpp::TokenList &tokens, SuppressionList &suppressions) +{ + if (!mSettings.inlineSuppressions) + return; + std::list err; + ::addInlineSuppressions(tokens, mSettings, suppressions, err); + for (std::map::const_iterator it = mTokenLists.cbegin(); it != mTokenLists.cend(); ++it) { + if (it->second) + ::addInlineSuppressions(*it->second, mSettings, suppressions, err); + } + for (const BadInlineSuppression &bad : err) { + error(bad.file, bad.line, bad.errmsg); + } +} + +std::list Preprocessor::createDirectives(const simplecpp::TokenList &tokens) const +{ + // directive list.. + std::list directives; + + std::vector list; + list.reserve(1U + mTokenLists.size()); + list.push_back(&tokens); + for (std::map::const_iterator it = mTokenLists.cbegin(); it != mTokenLists.cend(); ++it) { + list.push_back(it->second); + } + + for (const simplecpp::TokenList *tokenList : list) { + for (const simplecpp::Token *tok = tokenList->cfront(); tok; tok = tok->next) { + if ((tok->op != '#') || (tok->previous && tok->previous->location.line == tok->location.line)) + continue; + if (tok->next && tok->next->str() == "endfile") + continue; + Directive directive(tok->location.file(), tok->location.line, emptyString); + for (const simplecpp::Token *tok2 = tok; tok2 && tok2->location.line == directive.linenr; tok2 = tok2->next) { + if (tok2->comment) + continue; + if (!directive.str.empty() && (tok2->location.col > tok2->previous->location.col + tok2->previous->str().size())) + directive.str += ' '; + if (directive.str == "#" && tok2->str() == "file") + directive.str += "include"; + else + directive.str += tok2->str(); + } + directives.push_back(std::move(directive)); + } + } + + return directives; +} + +static std::string readcondition(const simplecpp::Token *iftok, const std::set &defined, const std::set &undefined) +{ + const simplecpp::Token *cond = iftok->next; + if (!sameline(iftok,cond)) + return ""; + + const simplecpp::Token *next1 = cond->next; + const simplecpp::Token *next2 = next1 ? next1->next : nullptr; + const simplecpp::Token *next3 = next2 ? next2->next : nullptr; + + unsigned int len = 1; + if (sameline(iftok,next1)) + len = 2; + if (sameline(iftok,next2)) + len = 3; + if (sameline(iftok,next3)) + len = 4; + + if (len == 1 && cond->str() == "0") + return "0"; + + if (len == 1 && cond->name) { + if (defined.find(cond->str()) == defined.end()) + return cond->str(); + } + + if (len == 2 && cond->op == '!' && next1->name) { + if (defined.find(next1->str()) == defined.end()) + return next1->str() + "=0"; + } + + if (len == 3 && cond->op == '(' && next1->name && next2->op == ')') { + if (defined.find(next1->str()) == defined.end() && undefined.find(next1->str()) == undefined.end()) + return next1->str(); + } + + if (len == 3 && cond->name && next1->str() == "==" && next2->number) { + if (defined.find(cond->str()) == defined.end()) + return cond->str() + '=' + cond->next->next->str(); + } + + std::set configset; + for (; sameline(iftok,cond); cond = cond->next) { + if (cond->op == '!') { + if (!sameline(iftok,cond->next) || !cond->next->name) + break; + if (cond->next->str() == "defined") + continue; + configset.insert(cond->next->str() + "=0"); + continue; + } + if (cond->str() != "defined") + continue; + const simplecpp::Token *dtok = cond->next; + if (!dtok) + break; + if (dtok->op == '(') + dtok = dtok->next; + if (sameline(iftok,dtok) && dtok->name && defined.find(dtok->str()) == defined.end() && undefined.find(dtok->str()) == undefined.end()) + configset.insert(dtok->str()); + } + std::string cfgStr; + for (const std::string &s : configset) { + if (!cfgStr.empty()) + cfgStr += ';'; + cfgStr += s; + } + return cfgStr; +} + +static bool hasDefine(const std::string &userDefines, const std::string &cfg) +{ + if (cfg.empty()) { + return false; + } + + std::string::size_type pos = 0; + while (pos < userDefines.size()) { + pos = userDefines.find(cfg, pos); + if (pos == std::string::npos) + break; + const std::string::size_type pos2 = pos + cfg.size(); + if ((pos == 0 || userDefines[pos-1U] == ';') && (pos2 == userDefines.size() || userDefines[pos2] == '=')) + return true; + pos = pos2; + } + return false; +} + +static std::string cfg(const std::vector &configs, const std::string &userDefines) +{ + std::set configs2(configs.cbegin(), configs.cend()); + std::string ret; + for (const std::string &c : configs2) { + if (c.empty()) + continue; + if (c == "0") + return ""; + if (hasDefine(userDefines, c)) + continue; + if (!ret.empty()) + ret += ';'; + ret += c; + } + return ret; +} + +static bool isUndefined(const std::string &cfg, const std::set &undefined) +{ + for (std::string::size_type pos1 = 0U; pos1 < cfg.size();) { + const std::string::size_type pos2 = cfg.find(';',pos1); + const std::string def = (pos2 == std::string::npos) ? cfg.substr(pos1) : cfg.substr(pos1, pos2 - pos1); + + const std::string::size_type eq = def.find('='); + if (eq == std::string::npos && undefined.find(def) != undefined.end()) + return true; + if (eq != std::string::npos && undefined.find(def.substr(0,eq)) != undefined.end() && def.substr(eq) != "=0") + return true; + + pos1 = (pos2 == std::string::npos) ? pos2 : pos2 + 1U; + } + return false; +} + +static bool getConfigsElseIsFalse(const std::vector &configs_if, const std::string &userDefines) +{ + return std::any_of(configs_if.cbegin(), configs_if.cend(), + [=](const std::string &cfg) { + return hasDefine(userDefines, cfg); + }); +} + +static const simplecpp::Token *gotoEndIf(const simplecpp::Token *cmdtok) +{ + int level = 0; + while (nullptr != (cmdtok = cmdtok->next)) { + if (cmdtok->op == '#' && !sameline(cmdtok->previous,cmdtok) && sameline(cmdtok, cmdtok->next)) { + if (startsWith(cmdtok->next->str(),"if")) + ++level; + else if (cmdtok->next->str() == "endif") { + --level; + if (level < 0) + return cmdtok; + } + } + } + return nullptr; +} + +static void getConfigs(const simplecpp::TokenList &tokens, std::set &defined, const std::string &userDefines, const std::set &undefined, std::set &ret) +{ + std::vector configs_if; + std::vector configs_ifndef; + std::string elseError; + + for (const simplecpp::Token *tok = tokens.cfront(); tok; tok = tok->next) { + if (tok->op != '#' || sameline(tok->previous, tok)) + continue; + const simplecpp::Token *cmdtok = tok->next; + if (!sameline(tok, cmdtok)) + continue; + if (cmdtok->str() == "ifdef" || cmdtok->str() == "ifndef" || cmdtok->str() == "if") { + std::string config; + if (cmdtok->str() == "ifdef" || cmdtok->str() == "ifndef") { + const simplecpp::Token *expr1 = cmdtok->next; + if (sameline(tok,expr1) && expr1->name && !sameline(tok,expr1->next)) + config = expr1->str(); + if (defined.find(config) != defined.end()) + config.clear(); + } else if (cmdtok->str() == "if") { + config = readcondition(cmdtok, defined, undefined); + } + + // skip undefined configurations.. + if (isUndefined(config, undefined)) + config.clear(); + + bool ifndef = false; + if (cmdtok->str() == "ifndef") + ifndef = true; + else { + const std::array match{"if", "!", "defined", "(", config, ")"}; + int i = 0; + ifndef = true; + for (const simplecpp::Token *t = cmdtok; i < match.size(); t = t->next) { + if (!t || t->str() != match[i++]) { + ifndef = false; + break; + } + } + } + + // include guard.. + if (ifndef && tok->location.fileIndex > 0) { + bool includeGuard = true; + for (const simplecpp::Token *t = tok->previous; t; t = t->previous) { + if (t->location.fileIndex == tok->location.fileIndex) { + includeGuard = false; + break; + } + } + if (includeGuard) { + configs_if.emplace_back(/*std::string()*/); + configs_ifndef.emplace_back(/*std::string()*/); + continue; + } + } + + configs_if.push_back((cmdtok->str() == "ifndef") ? std::string() : config); + configs_ifndef.push_back((cmdtok->str() == "ifndef") ? std::move(config) : std::string()); + ret.insert(cfg(configs_if,userDefines)); + } else if (cmdtok->str() == "elif" || cmdtok->str() == "else") { + if (getConfigsElseIsFalse(configs_if,userDefines)) { + tok = gotoEndIf(tok); + if (!tok) + break; + tok = tok->previous; + continue; + } + if (cmdtok->str() == "else" && + cmdtok->next && + !sameline(cmdtok,cmdtok->next) && + sameline(cmdtok->next, cmdtok->next->next) && + cmdtok->next->op == '#' && + cmdtok->next->next->str() == "error") { + const std::string &ifcfg = cfg(configs_if, userDefines); + if (!ifcfg.empty()) { + if (!elseError.empty()) + elseError += ';'; + elseError += ifcfg; + } + } + if (!configs_if.empty()) + configs_if.pop_back(); + if (cmdtok->str() == "elif") { + std::string config = readcondition(cmdtok, defined, undefined); + if (isUndefined(config,undefined)) + config.clear(); + configs_if.push_back(std::move(config)); + ret.insert(cfg(configs_if, userDefines)); + } else if (!configs_ifndef.empty()) { + configs_if.push_back(configs_ifndef.back()); + ret.insert(cfg(configs_if, userDefines)); + } + } else if (cmdtok->str() == "endif" && !sameline(tok, cmdtok->next)) { + if (!configs_if.empty()) + configs_if.pop_back(); + if (!configs_ifndef.empty()) + configs_ifndef.pop_back(); + } else if (cmdtok->str() == "error") { + if (!configs_ifndef.empty() && !configs_ifndef.back().empty()) { + if (configs_ifndef.size() == 1U) + ret.erase(emptyString); + std::vector configs(configs_if); + configs.push_back(configs_ifndef.back()); + ret.erase(cfg(configs, userDefines)); + std::set temp; + temp.swap(ret); + for (const std::string &c: temp) { + if (c.find(configs_ifndef.back()) != std::string::npos) + ret.insert(c); + else if (c.empty()) + ret.insert(""); + else + ret.insert(c + ";" + configs_ifndef.back()); + } + if (!elseError.empty()) + elseError += ';'; + elseError += cfg(configs_ifndef, userDefines); + } + if (!configs_if.empty() && !configs_if.back().empty()) { + const std::string &last = configs_if.back(); + if (last.size() > 2U && last.compare(last.size()-2U,2,"=0") == 0) { + std::vector configs(configs_if); + ret.erase(cfg(configs, userDefines)); + configs[configs.size() - 1U] = last.substr(0,last.size()-2U); + if (configs.size() == 1U) + ret.erase(""); + if (!elseError.empty()) + elseError += ';'; + elseError += cfg(configs, userDefines); + } + } + } else if (cmdtok->str() == "define" && sameline(tok, cmdtok->next) && cmdtok->next->name) { + defined.insert(cmdtok->next->str()); + } + } + if (!elseError.empty()) + ret.insert(std::move(elseError)); +} + + +std::set Preprocessor::getConfigs(const simplecpp::TokenList &tokens) const +{ + std::set ret = { "" }; + if (!tokens.cfront()) + return ret; + + std::set defined = { "__cplusplus" }; + + ::getConfigs(tokens, defined, mSettings.userDefines, mSettings.userUndefs, ret); + + for (std::map::const_iterator it = mTokenLists.cbegin(); it != mTokenLists.cend(); ++it) { + if (!mSettings.configurationExcluded(it->first)) + ::getConfigs(*(it->second), defined, mSettings.userDefines, mSettings.userUndefs, ret); + } + + return ret; +} + +static void splitcfg(const std::string &cfg, std::list &defines, const std::string &defaultValue) +{ + for (std::string::size_type defineStartPos = 0U; defineStartPos < cfg.size();) { + const std::string::size_type defineEndPos = cfg.find(';', defineStartPos); + std::string def = (defineEndPos == std::string::npos) ? cfg.substr(defineStartPos) : cfg.substr(defineStartPos, defineEndPos - defineStartPos); + if (!defaultValue.empty() && def.find('=') == std::string::npos) + def += '=' + defaultValue; + defines.push_back(std::move(def)); + if (defineEndPos == std::string::npos) + break; + defineStartPos = defineEndPos + 1U; + } +} + +static simplecpp::DUI createDUI(const Settings &mSettings, const std::string &cfg, const std::string &filename) +{ + // TODO: make it possible to specify platform-dependent sizes + simplecpp::DUI dui; + + splitcfg(mSettings.userDefines, dui.defines, "1"); + if (!cfg.empty()) + splitcfg(cfg, dui.defines, emptyString); + + for (const std::string &def : mSettings.library.defines) { + const std::string::size_type pos = def.find_first_of(" ("); + if (pos == std::string::npos) { + dui.defines.push_back(def); + continue; + } + std::string s = def; + if (s[pos] == ' ') { + s[pos] = '='; + } else { + s[s.find(')')+1] = '='; + } + dui.defines.push_back(std::move(s)); + } + + dui.undefined = mSettings.userUndefs; // -U + dui.includePaths = mSettings.includePaths; // -I + dui.includes = mSettings.userIncludes; // --include + // TODO: use mSettings.standards.stdValue instead + // TODO: error out on unknown language? + const Standards::Language lang = Path::identify(filename); + if (lang == Standards::Language::CPP) { + dui.std = mSettings.standards.getCPP(); + splitcfg(mSettings.platform.getLimitsDefines(Standards::getCPP(dui.std)), dui.defines, ""); + } + else if (lang == Standards::Language::C) { + dui.std = mSettings.standards.getC(); + splitcfg(mSettings.platform.getLimitsDefines(Standards::getC(dui.std)), dui.defines, ""); + } + dui.clearIncludeCache = mSettings.clearIncludeCache; + return dui; +} + +bool Preprocessor::hasErrors(const simplecpp::Output &output) +{ + switch (output.type) { + case simplecpp::Output::ERROR: + case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: + case simplecpp::Output::SYNTAX_ERROR: + case simplecpp::Output::UNHANDLED_CHAR_ERROR: + case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND: + case simplecpp::Output::FILE_NOT_FOUND: + return true; + case simplecpp::Output::WARNING: + case simplecpp::Output::MISSING_HEADER: + case simplecpp::Output::PORTABILITY_BACKSLASH: + break; + } + return false; +} + +bool Preprocessor::hasErrors(const simplecpp::OutputList &outputList) +{ + const auto it = std::find_if(outputList.cbegin(), outputList.cend(), [](const simplecpp::Output &output) { + return hasErrors(output); + }); + return it != outputList.cend(); +} + +void Preprocessor::handleErrors(const simplecpp::OutputList& outputList, bool throwError) +{ + const bool showerror = (!mSettings.userDefines.empty() && !mSettings.force); + reportOutput(outputList, showerror); + if (throwError) { + const auto it = std::find_if(outputList.cbegin(), outputList.cend(), [](const simplecpp::Output &output){ + return hasErrors(output); + }); + if (it != outputList.cend()) { + throw *it; + } + } +} + +bool Preprocessor::loadFiles(const simplecpp::TokenList &rawtokens, std::vector &files) +{ + const simplecpp::DUI dui = createDUI(mSettings, emptyString, files[0]); + + simplecpp::OutputList outputList; + mTokenLists = simplecpp::load(rawtokens, files, dui, &outputList); + handleErrors(outputList, false); + return !hasErrors(outputList); +} + +void Preprocessor::removeComments() +{ + for (std::pair& tokenList : mTokenLists) { + if (tokenList.second) + tokenList.second->removeComments(); + } +} + +void Preprocessor::setPlatformInfo(simplecpp::TokenList *tokens) const +{ + tokens->sizeOfType["bool"] = mSettings.platform.sizeof_bool; + tokens->sizeOfType["short"] = mSettings.platform.sizeof_short; + tokens->sizeOfType["int"] = mSettings.platform.sizeof_int; + tokens->sizeOfType["long"] = mSettings.platform.sizeof_long; + tokens->sizeOfType["long long"] = mSettings.platform.sizeof_long_long; + tokens->sizeOfType["float"] = mSettings.platform.sizeof_float; + tokens->sizeOfType["double"] = mSettings.platform.sizeof_double; + tokens->sizeOfType["long double"] = mSettings.platform.sizeof_long_double; + tokens->sizeOfType["bool *"] = mSettings.platform.sizeof_pointer; + tokens->sizeOfType["short *"] = mSettings.platform.sizeof_pointer; + tokens->sizeOfType["int *"] = mSettings.platform.sizeof_pointer; + tokens->sizeOfType["long *"] = mSettings.platform.sizeof_pointer; + tokens->sizeOfType["long long *"] = mSettings.platform.sizeof_pointer; + tokens->sizeOfType["float *"] = mSettings.platform.sizeof_pointer; + tokens->sizeOfType["double *"] = mSettings.platform.sizeof_pointer; + tokens->sizeOfType["long double *"] = mSettings.platform.sizeof_pointer; +} + +simplecpp::TokenList Preprocessor::preprocess(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector &files, bool throwError) +{ + const simplecpp::DUI dui = createDUI(mSettings, cfg, files[0]); + + simplecpp::OutputList outputList; + std::list macroUsage; + std::list ifCond; + simplecpp::TokenList tokens2(files); + simplecpp::preprocess(tokens2, tokens1, files, mTokenLists, dui, &outputList, ¯oUsage, &ifCond); + mMacroUsage = std::move(macroUsage); + mIfCond = std::move(ifCond); + + handleErrors(outputList, throwError); + + tokens2.removeComments(); + + return tokens2; +} + +std::string Preprocessor::getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector &files, const bool writeLocations) +{ + simplecpp::TokenList tokens2 = preprocess(tokens1, cfg, files, false); + unsigned int prevfile = 0; + unsigned int line = 1; + std::ostringstream ret; + for (const simplecpp::Token *tok = tokens2.cfront(); tok; tok = tok->next) { + if (writeLocations && tok->location.fileIndex != prevfile) { + ret << "\n#line " << tok->location.line << " \"" << tok->location.file() << "\"\n"; + prevfile = tok->location.fileIndex; + line = tok->location.line; + } + + if (tok->previous && line >= tok->location.line) // #7912 + ret << ' '; + while (tok->location.line > line) { + ret << '\n'; + line++; + } + if (!tok->macro.empty()) + ret << Preprocessor::macroChar; + ret << tok->str(); + } + + return ret.str(); +} + +void Preprocessor::reportOutput(const simplecpp::OutputList &outputList, bool showerror) +{ + for (const simplecpp::Output &out : outputList) { + switch (out.type) { + case simplecpp::Output::ERROR: + if (!startsWith(out.msg,"#error") || showerror) + error(out.location.file(), out.location.line, out.msg); + break; + case simplecpp::Output::WARNING: + case simplecpp::Output::PORTABILITY_BACKSLASH: + break; + case simplecpp::Output::MISSING_HEADER: { + const std::string::size_type pos1 = out.msg.find_first_of("<\""); + const std::string::size_type pos2 = out.msg.find_first_of(">\"", pos1 + 1U); + if (pos1 < pos2 && pos2 != std::string::npos) + missingInclude(out.location.file(), out.location.line, out.msg.substr(pos1+1, pos2-pos1-1), out.msg[pos1] == '\"' ? UserHeader : SystemHeader); + } + break; + case simplecpp::Output::INCLUDE_NESTED_TOO_DEEPLY: + case simplecpp::Output::SYNTAX_ERROR: + case simplecpp::Output::UNHANDLED_CHAR_ERROR: + error(out.location.file(), out.location.line, out.msg); + break; + case simplecpp::Output::EXPLICIT_INCLUDE_NOT_FOUND: + case simplecpp::Output::FILE_NOT_FOUND: + error(emptyString, 0, out.msg); + break; + } + } +} + +void Preprocessor::error(const std::string &filename, unsigned int linenr, const std::string &msg) +{ + std::list locationList; + if (!filename.empty()) { + std::string file = Path::fromNativeSeparators(filename); + if (mSettings.relativePaths) + file = Path::getRelativePath(file, mSettings.basePaths); + + locationList.emplace_back(file, linenr, 0); + } + mErrorLogger.reportErr(ErrorMessage(std::move(locationList), + mFile0, + Severity::error, + msg, + "preprocessorErrorDirective", + Certainty::normal)); +} + +// Report that include is missing +void Preprocessor::missingInclude(const std::string &filename, unsigned int linenr, const std::string &header, HeaderTypes headerType) +{ + if (!mSettings.checks.isEnabled(Checks::missingInclude)) + return; + + std::list locationList; + if (!filename.empty()) { + locationList.emplace_back(filename, linenr, 0); + } + ErrorMessage errmsg(std::move(locationList), mFile0, Severity::information, + (headerType==SystemHeader) ? + "Include file: <" + header + "> not found. Please note: Cppcheck does not need standard library headers to get proper results." : + "Include file: \"" + header + "\" not found.", + (headerType==SystemHeader) ? "missingIncludeSystem" : "missingInclude", + Certainty::normal); + mErrorLogger.reportErr(errmsg); +} + +void Preprocessor::getErrorMessages(ErrorLogger &errorLogger, const Settings &settings) +{ + Preprocessor preprocessor(settings, errorLogger); + preprocessor.missingInclude(emptyString, 1, emptyString, UserHeader); + preprocessor.missingInclude(emptyString, 1, emptyString, SystemHeader); + preprocessor.error(emptyString, 1, "#error message"); // #error .. +} + +void Preprocessor::dump(std::ostream &out) const +{ + // Create a xml dump. + + if (!mMacroUsage.empty()) { + out << " " << std::endl; + for (const simplecpp::MacroUsage ¯oUsage: mMacroUsage) { + out << " " << std::endl; + } + out << " " << std::endl; + } + + if (!mIfCond.empty()) { + out << " " << std::endl; + for (const simplecpp::IfCond &ifCond: mIfCond) { + out << " " << std::endl; + } + out << " " << std::endl; + } +} + +std::size_t Preprocessor::calculateHash(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const +{ + std::string hashData = toolinfo; + for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) { + if (!tok->comment) + hashData += tok->str(); + } + for (std::map::const_iterator it = mTokenLists.cbegin(); it != mTokenLists.cend(); ++it) { + for (const simplecpp::Token *tok = it->second->cfront(); tok; tok = tok->next) { + if (!tok->comment) + hashData += tok->str(); + } + } + return (std::hash{})(hashData); +} + +void Preprocessor::simplifyPragmaAsm(simplecpp::TokenList *tokenList) const +{ + Preprocessor::simplifyPragmaAsmPrivate(tokenList); + for (const std::pair& list : mTokenLists) { + Preprocessor::simplifyPragmaAsmPrivate(list.second); + } +} + +void Preprocessor::simplifyPragmaAsmPrivate(simplecpp::TokenList *tokenList) +{ + // assembler code.. + for (simplecpp::Token *tok = tokenList->front(); tok; tok = tok->next) { + if (tok->op != '#') + continue; + if (sameline(tok, tok->previousSkipComments())) + continue; + + const simplecpp::Token * const tok2 = tok->nextSkipComments(); + if (!tok2 || !sameline(tok, tok2) || tok2->str() != "pragma") + continue; + + const simplecpp::Token * const tok3 = tok2->nextSkipComments(); + if (!tok3 || !sameline(tok, tok3) || tok3->str() != "asm") + continue; + + const simplecpp::Token *endasm = tok3; + while ((endasm = endasm->next) != nullptr) { + if (endasm->op != '#' || sameline(endasm,endasm->previousSkipComments())) + continue; + const simplecpp::Token * const endasm2 = endasm->nextSkipComments(); + if (!endasm2 || !sameline(endasm, endasm2) || endasm2->str() != "pragma") + continue; + const simplecpp::Token * const endasm3 = endasm2->nextSkipComments(); + if (!endasm3 || !sameline(endasm2, endasm3) || endasm3->str() != "endasm") + continue; + while (sameline(endasm,endasm3)) + endasm = endasm->next; + break; + } + + const simplecpp::Token * const tok4 = tok3->next; + tok->setstr("asm"); + const_cast(tok2)->setstr("("); + const_cast(tok3)->setstr(")"); + const_cast(tok4)->setstr(";"); + while (tok4->next != endasm) + tokenList->deleteToken(tok4->next); + } +} diff --git a/cppcheck-2.14.0/lib/preprocessor.h b/cppcheck-2.14.0/lib/preprocessor.h new file mode 100644 index 00000000..08c856f1 --- /dev/null +++ b/cppcheck-2.14.0/lib/preprocessor.h @@ -0,0 +1,158 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef preprocessorH +#define preprocessorH +//--------------------------------------------------------------------------- + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +class ErrorLogger; +class Settings; +class SuppressionList; + +/** + * @brief A preprocessor directive + * Each preprocessor directive (\#include, \#define, \#undef, \#if, \#ifdef, \#else, \#endif) + * will be recorded as an instance of this class. + * + * file and linenr denote the location where where the directive is defined. + * + */ + +struct CPPCHECKLIB Directive { + /** name of (possibly included) file where directive is defined */ + std::string file; + + /** line number in (possibly included) file where directive is defined */ + unsigned int linenr; + + /** the actual directive text */ + std::string str; + + /** record a directive (possibly filtering src) */ + Directive(std::string _file, const int _linenr, const std::string &_str); +}; + +/// @addtogroup Core +/// @{ + +/** + * @brief The cppcheck preprocessor. + * The preprocessor has special functionality for extracting the various ifdef + * configurations that exist in a source file. + */ +class CPPCHECKLIB WARN_UNUSED Preprocessor { + // TODO: get rid of this + friend class PreprocessorHelper; + friend class TestPreprocessor; + friend class TestUnusedVar; + +public: + + /** + * Include file types. + */ + enum HeaderTypes { + UserHeader = 1, + SystemHeader + }; + + /** character that is inserted in expanded macros */ + static char macroChar; + + explicit Preprocessor(const Settings& settings, ErrorLogger &errorLogger); + virtual ~Preprocessor(); + + void inlineSuppressions(const simplecpp::TokenList &tokens, SuppressionList &suppressions); + + std::list createDirectives(const simplecpp::TokenList &tokens) const; + + std::set getConfigs(const simplecpp::TokenList &tokens) const; + + void handleErrors(const simplecpp::OutputList &outputList, bool throwError); + + bool loadFiles(const simplecpp::TokenList &rawtokens, std::vector &files); + + void removeComments(); + + void setPlatformInfo(simplecpp::TokenList *tokens) const; + + simplecpp::TokenList preprocess(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector &files, bool throwError = false); + + std::string getcode(const simplecpp::TokenList &tokens1, const std::string &cfg, std::vector &files, const bool writeLocations); + + /** + * Calculate HASH. Using toolinfo, tokens1, filedata. + * + * @param tokens1 Sourcefile tokens + * @param toolinfo Arbitrary extra toolinfo + * @return HASH + */ + std::size_t calculateHash(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const; + + void simplifyPragmaAsm(simplecpp::TokenList *tokenList) const; + + static void getErrorMessages(ErrorLogger &errorLogger, const Settings &settings); + + /** + * dump all directives present in source file + */ + void dump(std::ostream &out) const; + + void reportOutput(const simplecpp::OutputList &outputList, bool showerror); + + static bool hasErrors(const simplecpp::Output &output); + +private: + static void simplifyPragmaAsmPrivate(simplecpp::TokenList *tokenList); + + 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); + + static bool hasErrors(const simplecpp::OutputList &outputList); + + const Settings& mSettings; + ErrorLogger &mErrorLogger; + + /** list of all directives met while preprocessing file */ + + std::map mTokenLists; + + /** filename for cpp/c file - useful when reporting errors */ + std::string mFile0; + + /** simplecpp tracking info */ + std::list mMacroUsage; + std::list mIfCond; +}; + +/// @} +//--------------------------------------------------------------------------- +#endif // preprocessorH diff --git a/cppcheck-2.14.0/lib/programmemory.cpp b/cppcheck-2.14.0/lib/programmemory.cpp new file mode 100644 index 00000000..61e63b24 --- /dev/null +++ b/cppcheck-2.14.0/lib/programmemory.cpp @@ -0,0 +1,1798 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "programmemory.h" + +#include "astutils.h" +#include "calculate.h" +#include "infer.h" +#include "library.h" +#include "mathlib.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "utils.h" +#include "valueflow.h" +#include "valueptr.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ExprIdToken::ExprIdToken(const Token* tok) : tok(tok), exprid(tok ? tok->exprId() : 0) {} + +nonneg int ExprIdToken::getExpressionId() const { + return tok ? tok->exprId() : exprid; +} + +std::size_t ExprIdToken::Hash::operator()(ExprIdToken etok) const +{ + return std::hash()(etok.getExpressionId()); +} + +void ProgramMemory::setValue(const Token* expr, const ValueFlow::Value& value) { + mValues[expr] = value; + ValueFlow::Value subvalue = value; + const Token* subexpr = solveExprValue( + expr, + [&](const Token* tok) -> std::vector { + if (tok->hasKnownIntValue()) + return {tok->values().front().intvalue}; + MathLib::bigint result = 0; + if (getIntValue(tok->exprId(), result)) + return {result}; + return {}; + }, + subvalue); + if (subexpr) + mValues[subexpr] = std::move(subvalue); +} +const ValueFlow::Value* ProgramMemory::getValue(nonneg int exprid, bool impossible) const +{ + const ProgramMemory::Map::const_iterator it = mValues.find(exprid); + const bool found = it != mValues.cend() && (impossible || !it->second.isImpossible()); + if (found) + return &it->second; + return nullptr; +} + +// cppcheck-suppress unusedFunction +bool ProgramMemory::getIntValue(nonneg int exprid, MathLib::bigint& result) const +{ + const ValueFlow::Value* value = getValue(exprid); + if (value && value->isIntValue()) { + result = value->intvalue; + return true; + } + return false; +} + +void ProgramMemory::setIntValue(const Token* expr, MathLib::bigint value, bool impossible) +{ + ValueFlow::Value v(value); + if (impossible) + v.setImpossible(); + setValue(expr, v); +} + +bool ProgramMemory::getTokValue(nonneg int exprid, const Token*& result) const +{ + const ValueFlow::Value* value = getValue(exprid); + if (value && value->isTokValue()) { + result = value->tokvalue; + return true; + } + return false; +} + +// cppcheck-suppress unusedFunction +bool ProgramMemory::getContainerSizeValue(nonneg int exprid, MathLib::bigint& result) const +{ + const ValueFlow::Value* value = getValue(exprid); + if (value && value->isContainerSizeValue()) { + result = value->intvalue; + return true; + } + return false; +} +bool ProgramMemory::getContainerEmptyValue(nonneg int exprid, MathLib::bigint& result) const +{ + const ValueFlow::Value* value = getValue(exprid, true); + if (value && value->isContainerSizeValue()) { + if (value->isImpossible() && value->intvalue == 0) { + result = false; + return true; + } + if (!value->isImpossible()) { + result = (value->intvalue == 0); + return true; + } + } + return false; +} + +void ProgramMemory::setContainerSizeValue(const Token* expr, MathLib::bigint value, bool isEqual) +{ + ValueFlow::Value v(value); + v.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + if (!isEqual) + v.valueKind = ValueFlow::Value::ValueKind::Impossible; + setValue(expr, v); +} + +void ProgramMemory::setUnknown(const Token* expr) { + mValues[expr].valueType = ValueFlow::Value::ValueType::UNINIT; +} + +bool ProgramMemory::hasValue(nonneg int exprid) +{ + return mValues.find(exprid) != mValues.end(); +} + +const ValueFlow::Value& ProgramMemory::at(nonneg int exprid) const { + return mValues.at(exprid); +} +ValueFlow::Value& ProgramMemory::at(nonneg int exprid) { + return mValues.at(exprid); +} + +void ProgramMemory::erase_if(const std::function& pred) +{ + for (auto it = mValues.begin(); it != mValues.end();) { + if (pred(it->first)) + it = mValues.erase(it); + else + ++it; + } +} + +void ProgramMemory::swap(ProgramMemory &pm) +{ + mValues.swap(pm.mValues); +} + +void ProgramMemory::clear() +{ + mValues.clear(); +} + +bool ProgramMemory::empty() const +{ + return mValues.empty(); +} + +void ProgramMemory::replace(ProgramMemory pm) +{ + for (auto&& p : pm.mValues) { + mValues[p.first] = std::move(p.second); + } +} + +void ProgramMemory::insert(const ProgramMemory &pm) +{ + for (auto&& p : pm) + mValues.insert(p); +} + +static ValueFlow::Value execute(const Token* expr, ProgramMemory& pm, const Settings* settings); + +static bool evaluateCondition(MathLib::bigint r, const Token* condition, ProgramMemory& pm, const Settings* settings) +{ + if (!condition) + return false; + MathLib::bigint result = 0; + bool error = false; + execute(condition, pm, &result, &error, settings); + return !error && result == r; +} + +bool conditionIsFalse(const Token* condition, ProgramMemory pm, const Settings* settings) +{ + return evaluateCondition(0, condition, pm, settings); +} + +bool conditionIsTrue(const Token* condition, ProgramMemory pm, const Settings* settings) +{ + return evaluateCondition(1, condition, pm, settings); +} + +static bool frontIs(const std::vector& v, bool i) +{ + if (v.empty()) + return false; + if (v.front()) + return i; + return !i; +} + +static bool isTrue(const ValueFlow::Value& v) +{ + if (v.isUninitValue()) + return false; + if (v.isImpossible()) + return v.intvalue == 0; + return v.intvalue != 0; +} + +static bool isFalse(const ValueFlow::Value& v) +{ + if (v.isUninitValue()) + return false; + if (v.isImpossible()) + return false; + return v.intvalue == 0; +} + +static bool isTrueOrFalse(const ValueFlow::Value& v, bool b) +{ + if (b) + return isTrue(v); + return isFalse(v); +} + +// If the scope is a non-range for loop +static bool isBasicForLoop(const Token* tok) +{ + if (!tok) + return false; + if (Token::simpleMatch(tok, "}")) + return isBasicForLoop(tok->link()); + if (!Token::simpleMatch(tok->previous(), ") {")) + return false; + const Token* start = tok->linkAt(-1); + if (!start) + return false; + if (!Token::simpleMatch(start->previous(), "for (")) + return false; + if (!Token::simpleMatch(start->astOperand2(), ";")) + return false; + return true; +} + +static void programMemoryParseCondition(ProgramMemory& pm, const Token* tok, const Token* endTok, const Settings* settings, bool then) +{ + auto eval = [&](const Token* t) -> std::vector { + if (!t) + return std::vector{}; + if (t->hasKnownIntValue()) + return {t->values().front().intvalue}; + MathLib::bigint result = 0; + bool error = false; + execute(t, pm, &result, &error, settings); + if (!error) + return {result}; + return std::vector{}; + }; + if (Token::Match(tok, "==|>=|<=|<|>|!=")) { + ValueFlow::Value truevalue; + ValueFlow::Value falsevalue; + const Token* vartok = parseCompareInt(tok, truevalue, falsevalue, eval); + if (!vartok) + return; + if (vartok->exprId() == 0) + return; + if (!truevalue.isIntValue()) + return; + if (endTok && findExpressionChanged(vartok, tok->next(), endTok, settings)) + return; + const bool impossible = (tok->str() == "==" && !then) || (tok->str() == "!=" && then); + const ValueFlow::Value& v = then ? truevalue : falsevalue; + pm.setValue(vartok, impossible ? asImpossible(v) : v); + const Token* containerTok = settings->library.getContainerFromYield(vartok, Library::Container::Yield::SIZE); + if (containerTok) + pm.setContainerSizeValue(containerTok, v.intvalue, !impossible); + } else if (Token::simpleMatch(tok, "!")) { + programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, !then); + } else if (then && Token::simpleMatch(tok, "&&")) { + programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then); + programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then); + } else if (!then && Token::simpleMatch(tok, "||")) { + programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then); + programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then); + } else if (Token::Match(tok, "&&|%oror%")) { + std::vector lhs = eval(tok->astOperand1()); + std::vector rhs = eval(tok->astOperand2()); + if (lhs.empty() || rhs.empty()) { + if (frontIs(lhs, !then)) + programMemoryParseCondition(pm, tok->astOperand2(), endTok, settings, then); + else if (frontIs(rhs, !then)) + programMemoryParseCondition(pm, tok->astOperand1(), endTok, settings, then); + else + pm.setIntValue(tok, 0, then); + } + } else if (tok && tok->exprId() > 0) { + if (endTok && findExpressionChanged(tok, tok->next(), endTok, settings)) + return; + pm.setIntValue(tok, 0, then); + assert(settings); + const Token* containerTok = settings->library.getContainerFromYield(tok, Library::Container::Yield::EMPTY); + if (containerTok) + pm.setContainerSizeValue(containerTok, 0, then); + } +} + +static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Scope* scope, const Token* endTok, const Settings* settings) +{ + if (!scope) + return; + if (!scope->isLocal()) + return; + assert(scope != scope->nestedIn); + fillProgramMemoryFromConditions(pm, scope->nestedIn, endTok, settings); + if (scope->type == Scope::eIf || scope->type == Scope::eWhile || scope->type == Scope::eElse || scope->type == Scope::eFor) { + const Token* condTok = getCondTokFromEnd(scope->bodyEnd); + if (!condTok) + return; + MathLib::bigint result = 0; + bool error = false; + execute(condTok, pm, &result, &error, settings); + if (error) + programMemoryParseCondition(pm, condTok, endTok, settings, scope->type != Scope::eElse); + } +} + +static void fillProgramMemoryFromConditions(ProgramMemory& pm, const Token* tok, const Settings* settings) +{ + fillProgramMemoryFromConditions(pm, tok->scope(), tok, settings); +} + +static void fillProgramMemoryFromAssignments(ProgramMemory& pm, const Token* tok, const Settings* settings, const ProgramMemory& state, const ProgramMemory::Map& vars) +{ + int indentlevel = 0; + for (const Token *tok2 = tok; tok2; tok2 = tok2->previous()) { + if ((Token::simpleMatch(tok2, "=") || Token::Match(tok2->previous(), "%var% (|{")) && tok2->astOperand1() && + tok2->astOperand2()) { + bool setvar = false; + const Token* vartok = tok2->astOperand1(); + for (const auto& p:vars) { + if (p.first != vartok->exprId()) + continue; + if (vartok == tok) + continue; + pm.setValue(vartok, p.second); + setvar = true; + } + if (!setvar) { + if (!pm.hasValue(vartok->exprId())) { + const Token* valuetok = tok2->astOperand2(); + pm.setValue(vartok, execute(valuetok, pm, settings)); + } + } + } else if (tok2->exprId() > 0 && Token::Match(tok2, ".|(|[|*|%var%") && !pm.hasValue(tok2->exprId()) && + isVariableChanged(tok2, 0, settings)) { + pm.setUnknown(tok2); + } + + if (tok2->str() == "{") { + if (indentlevel <= 0) { + const Token* cond = getCondTokFromEnd(tok2->link()); + // Keep progressing with anonymous/do scopes and always true branches + if (!Token::Match(tok2->previous(), "do|; {") && !conditionIsTrue(cond, state, settings) && + (cond || !isBasicForLoop(tok2))) + break; + } else + --indentlevel; + if (Token::simpleMatch(tok2->previous(), "else {")) + tok2 = tok2->linkAt(-2)->previous(); + } + if (tok2->str() == "}" && !Token::Match(tok2->link()->previous(), "%var% {")) { + const Token *cond = getCondTokFromEnd(tok2); + const bool inElse = Token::simpleMatch(tok2->link()->previous(), "else {"); + if (cond) { + if (conditionIsFalse(cond, state, settings)) { + if (inElse) { + ++indentlevel; + continue; + } + } else if (conditionIsTrue(cond, state, settings)) { + if (inElse) + tok2 = tok2->link()->tokAt(-2); + ++indentlevel; + continue; + } + } + break; + } + } +} + +static void removeModifiedVars(ProgramMemory& pm, const Token* tok, const Token* origin) +{ + pm.erase_if([&](const ExprIdToken& e) { + return isVariableChanged(origin, tok, e.getExpressionId(), false, nullptr); + }); +} + +static ProgramMemory getInitialProgramState(const Token* tok, + const Token* origin, + const Settings& settings, + const ProgramMemory::Map& vars = ProgramMemory::Map {}) +{ + ProgramMemory pm; + if (origin) { + fillProgramMemoryFromConditions(pm, origin, nullptr); + const ProgramMemory state = pm; + fillProgramMemoryFromAssignments(pm, tok, &settings, state, vars); + removeModifiedVars(pm, tok, origin); + } + return pm; +} + +ProgramMemoryState::ProgramMemoryState(const Settings* s) : settings(s) {} + +void ProgramMemoryState::insert(const ProgramMemory &pm, const Token* origin) +{ + if (origin) + for (auto&& p : pm) + origins.insert(std::make_pair(p.first.getExpressionId(), origin)); + state.insert(pm); +} + +void ProgramMemoryState::replace(ProgramMemory pm, const Token* origin) +{ + if (origin) + for (const auto& p : pm) + origins[p.first.getExpressionId()] = origin; + state.replace(std::move(pm)); +} + +static void addVars(ProgramMemory& pm, const ProgramMemory::Map& vars) +{ + for (const auto& p:vars) { + const ValueFlow::Value &value = p.second; + pm.setValue(p.first.tok, value); + } +} + +void ProgramMemoryState::addState(const Token* tok, const ProgramMemory::Map& vars) +{ + ProgramMemory pm = state; + addVars(pm, vars); + fillProgramMemoryFromConditions(pm, tok, settings); + ProgramMemory local = pm; + fillProgramMemoryFromAssignments(pm, tok, settings, local, vars); + addVars(pm, vars); + replace(std::move(pm), tok); +} + +void ProgramMemoryState::assume(const Token* tok, bool b, bool isEmpty) +{ + ProgramMemory pm = state; + if (isEmpty) + pm.setContainerSizeValue(tok, 0, b); + else + programMemoryParseCondition(pm, tok, nullptr, settings, b); + const Token* origin = tok; + const Token* top = tok->astTop(); + if (top && Token::Match(top->previous(), "for|while|if (") && !Token::simpleMatch(tok->astParent(), "?")) { + origin = top->link()->next(); + if (!b && origin->link()) { + origin = origin->link(); + } + } + replace(std::move(pm), origin); +} + +void ProgramMemoryState::removeModifiedVars(const Token* tok) +{ + const ProgramMemory& pm = state; + auto eval = [&](const Token* cond) -> std::vector { + if (conditionIsTrue(cond, pm, settings)) + return {1}; + if (conditionIsFalse(cond, pm, settings)) + return {0}; + return {}; + }; + state.erase_if([&](const ExprIdToken& e) { + const Token* start = origins[e.getExpressionId()]; + const Token* expr = e.tok; + if (!expr || findExpressionChangedSkipDeadCode(expr, start, tok, settings, eval)) { + origins.erase(e.getExpressionId()); + return true; + } + return false; + }); +} + +ProgramMemory ProgramMemoryState::get(const Token* tok, const Token* ctx, const ProgramMemory::Map& vars) const +{ + ProgramMemoryState local = *this; + if (ctx) + local.addState(ctx, vars); + const Token* start = previousBeforeAstLeftmostLeaf(tok); + if (!start) + start = tok; + + if (!ctx || precedes(start, ctx)) { + local.removeModifiedVars(start); + local.addState(start, vars); + } else { + local.removeModifiedVars(ctx); + } + return local.state; +} + +ProgramMemory getProgramMemory(const Token* tok, const Token* expr, const ValueFlow::Value& value, const Settings& settings) +{ + ProgramMemory programMemory; + programMemory.replace(getInitialProgramState(tok, value.tokvalue, settings)); + programMemory.replace(getInitialProgramState(tok, value.condition, settings)); + fillProgramMemoryFromConditions(programMemory, tok, &settings); + programMemory.setValue(expr, value); + const ProgramMemory state = programMemory; + fillProgramMemoryFromAssignments(programMemory, tok, &settings, state, {{expr, value}}); + return programMemory; +} + +static bool isNumericValue(const ValueFlow::Value& value) { + return value.isIntValue() || value.isFloatValue(); +} + +static double asFloat(const ValueFlow::Value& value) +{ + return value.isFloatValue() ? value.floatValue : value.intvalue; +} + +static std::string removeAssign(const std::string& assign) { + return std::string{assign.cbegin(), assign.cend() - 1}; +} + +namespace { + struct assign { + template + void operator()(T& x, const U& y) const + { + x = y; + } + }; +} + +static bool isIntegralValue(const ValueFlow::Value& value) +{ + return value.isIntValue() || value.isIteratorValue() || value.isSymbolicValue(); +} + +static ValueFlow::Value evaluate(const std::string& op, const ValueFlow::Value& lhs, const ValueFlow::Value& rhs) +{ + ValueFlow::Value result; + combineValueProperties(lhs, rhs, result); + if (lhs.isImpossible() && rhs.isImpossible()) + return ValueFlow::Value::unknown(); + if (lhs.isImpossible() || rhs.isImpossible()) { + // noninvertible + if (contains({"%", "/", "&", "|"}, op)) + return ValueFlow::Value::unknown(); + result.setImpossible(); + } + if (isNumericValue(lhs) && isNumericValue(rhs)) { + if (lhs.isFloatValue() || rhs.isFloatValue()) { + result.valueType = ValueFlow::Value::ValueType::FLOAT; + bool error = false; + result.floatValue = calculate(op, asFloat(lhs), asFloat(rhs), &error); + if (error) + return ValueFlow::Value::unknown(); + return result; + } + } + // Must be integral types + if (!isIntegralValue(lhs) && !isIntegralValue(rhs)) + return ValueFlow::Value::unknown(); + // If not the same type then one must be int + if (lhs.valueType != rhs.valueType && !lhs.isIntValue() && !rhs.isIntValue()) + return ValueFlow::Value::unknown(); + const bool compareOp = contains({"==", "!=", "<", ">", ">=", "<="}, op); + // Comparison must be the same type + if (compareOp && lhs.valueType != rhs.valueType) + return ValueFlow::Value::unknown(); + // Only add, subtract, and compare for non-integers + if (!compareOp && !contains({"+", "-"}, op) && !lhs.isIntValue() && !rhs.isIntValue()) + return ValueFlow::Value::unknown(); + // Both can't be iterators for non-compare + if (!compareOp && lhs.isIteratorValue() && rhs.isIteratorValue()) + return ValueFlow::Value::unknown(); + // Symbolic values must be in the same ring + if (lhs.isSymbolicValue() && rhs.isSymbolicValue() && lhs.tokvalue != rhs.tokvalue) + return ValueFlow::Value::unknown(); + if (!lhs.isIntValue() && !compareOp) { + result.valueType = lhs.valueType; + result.tokvalue = lhs.tokvalue; + } else if (!rhs.isIntValue() && !compareOp) { + result.valueType = rhs.valueType; + result.tokvalue = rhs.tokvalue; + } else { + result.valueType = ValueFlow::Value::ValueType::INT; + } + bool error = false; + result.intvalue = calculate(op, lhs.intvalue, rhs.intvalue, &error); + if (error) + return ValueFlow::Value::unknown(); + if (result.isImpossible() && op == "!=") { + if (isTrue(result)) { + result.intvalue = 1; + } else if (isFalse(result)) { + result.intvalue = 0; + } else { + return ValueFlow::Value::unknown(); + } + result.setPossible(); + result.bound = ValueFlow::Value::Bound::Point; + } + return result; +} + +using BuiltinLibraryFunction = std::function&)>; +static std::unordered_map createBuiltinLibraryFunctions() +{ + std::unordered_map functions; + functions["strlen"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!(v.isTokValue() && v.tokvalue->tokType() == Token::eString)) + return ValueFlow::Value::unknown(); + v.valueType = ValueFlow::Value::ValueType::INT; + v.intvalue = Token::getStrLength(v.tokvalue); + v.tokvalue = nullptr; + return v; + }; + functions["strcmp"] = [](const std::vector& args) { + if (args.size() != 2) + return ValueFlow::Value::unknown(); + const ValueFlow::Value& lhs = args[0]; + if (!(lhs.isTokValue() && lhs.tokvalue->tokType() == Token::eString)) + return ValueFlow::Value::unknown(); + const ValueFlow::Value& rhs = args[1]; + if (!(rhs.isTokValue() && rhs.tokvalue->tokType() == Token::eString)) + return ValueFlow::Value::unknown(); + ValueFlow::Value v(getStringLiteral(lhs.tokvalue->str()).compare(getStringLiteral(rhs.tokvalue->str()))); + ValueFlow::combineValueProperties(lhs, rhs, v); + return v; + }; + functions["strncmp"] = [](const std::vector& args) { + if (args.size() != 3) + return ValueFlow::Value::unknown(); + const ValueFlow::Value& lhs = args[0]; + if (!(lhs.isTokValue() && lhs.tokvalue->tokType() == Token::eString)) + return ValueFlow::Value::unknown(); + const ValueFlow::Value& rhs = args[1]; + if (!(rhs.isTokValue() && rhs.tokvalue->tokType() == Token::eString)) + return ValueFlow::Value::unknown(); + const ValueFlow::Value& len = args[2]; + if (!len.isIntValue()) + return ValueFlow::Value::unknown(); + ValueFlow::Value v(getStringLiteral(lhs.tokvalue->str()) + .compare(0, len.intvalue, getStringLiteral(rhs.tokvalue->str()), 0, len.intvalue)); + ValueFlow::combineValueProperties(lhs, rhs, v); + return v; + }; + functions["sin"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::sin(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["lgamma"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::lgamma(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["cos"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::cos(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["tan"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::tan(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["asin"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::asin(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["acos"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::acos(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["atan"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::atan(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["atan2"] = [](const std::vector& args) { + if (args.size() != 2 || !std::all_of(args.cbegin(), args.cend(), [](const ValueFlow::Value& v) { + return v.isFloatValue() || v.isIntValue(); + })) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + ValueFlow::Value v; + combineValueProperties(args[0], args[1], v); + v.floatValue = std::atan2(value, args[1].isFloatValue() ? args[1].floatValue : args[1].intvalue); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["remainder"] = [](const std::vector& args) { + if (args.size() != 2 || !std::all_of(args.cbegin(), args.cend(), [](const ValueFlow::Value& v) { + return v.isFloatValue() || v.isIntValue(); + })) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + ValueFlow::Value v; + combineValueProperties(args[0], args[1], v); + v.floatValue = std::remainder(value, args[1].isFloatValue() ? args[1].floatValue : args[1].intvalue); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["nextafter"] = [](const std::vector& args) { + if (args.size() != 2 || !std::all_of(args.cbegin(), args.cend(), [](const ValueFlow::Value& v) { + return v.isFloatValue() || v.isIntValue(); + })) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + ValueFlow::Value v; + combineValueProperties(args[0], args[1], v); + v.floatValue = std::nextafter(value, args[1].isFloatValue() ? args[1].floatValue : args[1].intvalue); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["nexttoward"] = [](const std::vector& args) { + if (args.size() != 2 || !std::all_of(args.cbegin(), args.cend(), [](const ValueFlow::Value& v) { + return v.isFloatValue() || v.isIntValue(); + })) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + ValueFlow::Value v; + combineValueProperties(args[0], args[1], v); + v.floatValue = std::nexttoward(value, args[1].isFloatValue() ? args[1].floatValue : args[1].intvalue); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["hypot"] = [](const std::vector& args) { + if (args.size() != 2 || !std::all_of(args.cbegin(), args.cend(), [](const ValueFlow::Value& v) { + return v.isFloatValue() || v.isIntValue(); + })) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + ValueFlow::Value v; + combineValueProperties(args[0], args[1], v); + v.floatValue = std::hypot(value, args[1].isFloatValue() ? args[1].floatValue : args[1].intvalue); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["fdim"] = [](const std::vector& args) { + if (args.size() != 2 || !std::all_of(args.cbegin(), args.cend(), [](const ValueFlow::Value& v) { + return v.isFloatValue() || v.isIntValue(); + })) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + ValueFlow::Value v; + combineValueProperties(args[0], args[1], v); + v.floatValue = std::fdim(value, args[1].isFloatValue() ? args[1].floatValue : args[1].intvalue); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["fmax"] = [](const std::vector& args) { + if (args.size() != 2 || !std::all_of(args.cbegin(), args.cend(), [](const ValueFlow::Value& v) { + return v.isFloatValue() || v.isIntValue(); + })) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + ValueFlow::Value v; + combineValueProperties(args[0], args[1], v); + v.floatValue = std::fmax(value, args[1].isFloatValue() ? args[1].floatValue : args[1].intvalue); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["fmin"] = [](const std::vector& args) { + if (args.size() != 2 || !std::all_of(args.cbegin(), args.cend(), [](const ValueFlow::Value& v) { + return v.isFloatValue() || v.isIntValue(); + })) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + ValueFlow::Value v; + combineValueProperties(args[0], args[1], v); + v.floatValue = std::fmin(value, args[1].isFloatValue() ? args[1].floatValue : args[1].intvalue); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["fmod"] = [](const std::vector& args) { + if (args.size() != 2 || !std::all_of(args.cbegin(), args.cend(), [](const ValueFlow::Value& v) { + return v.isFloatValue() || v.isIntValue(); + })) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + ValueFlow::Value v; + combineValueProperties(args[0], args[1], v); + v.floatValue = std::fmod(value, args[1].isFloatValue() ? args[1].floatValue : args[1].intvalue); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["pow"] = [](const std::vector& args) { + if (args.size() != 2 || !std::all_of(args.cbegin(), args.cend(), [](const ValueFlow::Value& v) { + return v.isFloatValue() || v.isIntValue(); + })) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + ValueFlow::Value v; + combineValueProperties(args[0], args[1], v); + v.floatValue = std::pow(value, args[1].isFloatValue() ? args[1].floatValue : args[1].intvalue); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["scalbln"] = [](const std::vector& args) { + if (args.size() != 2 || !std::all_of(args.cbegin(), args.cend(), [](const ValueFlow::Value& v) { + return v.isFloatValue() || v.isIntValue(); + })) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + ValueFlow::Value v; + combineValueProperties(args[0], args[1], v); + v.floatValue = std::scalbln(value, args[1].isFloatValue() ? args[1].floatValue : args[1].intvalue); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["ldexp"] = [](const std::vector& args) { + if (args.size() != 2 || !std::all_of(args.cbegin(), args.cend(), [](const ValueFlow::Value& v) { + return v.isFloatValue() || v.isIntValue(); + })) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + ValueFlow::Value v; + combineValueProperties(args[0], args[1], v); + v.floatValue = std::ldexp(value, args[1].isFloatValue() ? args[1].floatValue : args[1].intvalue); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["ilogb"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.intvalue = std::ilogb(value); + v.valueType = ValueFlow::Value::ValueType::INT; + return v; + }; + functions["erf"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::erf(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["erfc"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::erfc(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["floor"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::floor(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["sqrt"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::sqrt(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["cbrt"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::cbrt(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["ceil"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::ceil(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["exp"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::exp(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["exp2"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::exp2(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["expm1"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::expm1(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["fabs"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::fabs(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["log"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::log(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["log10"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::log10(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["log1p"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::log1p(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["log2"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::log2(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["logb"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::logb(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["nearbyint"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::nearbyint(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["sinh"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::sinh(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["cosh"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::cosh(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["tanh"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::tanh(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["asinh"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::asinh(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["acosh"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::acosh(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["atanh"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::atanh(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["round"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::round(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["tgamma"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::tgamma(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + functions["trunc"] = [](const std::vector& args) { + if (args.size() != 1) + return ValueFlow::Value::unknown(); + ValueFlow::Value v = args[0]; + if (!v.isFloatValue() && !v.isIntValue()) + return ValueFlow::Value::unknown(); + const double value = args[0].isFloatValue() ? args[0].floatValue : args[0].intvalue; + v.floatValue = std::trunc(value); + v.valueType = ValueFlow::Value::ValueType::FLOAT; + return v; + }; + return functions; +} + +static BuiltinLibraryFunction getBuiltinLibraryFunction(const std::string& name) +{ + static const std::unordered_map functions = createBuiltinLibraryFunctions(); + auto it = functions.find(name); + if (it == functions.end()) + return nullptr; + return it->second; +} +static bool TokenExprIdCompare(const Token* tok1, const Token* tok2) { + return tok1->exprId() < tok2->exprId(); +} +static bool TokenExprIdEqual(const Token* tok1, const Token* tok2) { + return tok1->exprId() == tok2->exprId(); +} + +static std::vector setDifference(const std::vector& v1, const std::vector& v2) +{ + std::vector result; + std::set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(result), &TokenExprIdCompare); + return result; +} + +static bool evalSameCondition(const ProgramMemory& state, + const Token* storedValue, + const Token* cond, + const Settings* settings) +{ + assert(!conditionIsTrue(cond, state, settings)); + ProgramMemory pm = state; + programMemoryParseCondition(pm, storedValue, nullptr, settings, true); + if (pm == state) + return false; + return conditionIsTrue(cond, std::move(pm), settings); +} + +static void pruneConditions(std::vector& conds, + bool b, + const std::unordered_map& state) +{ + conds.erase(std::remove_if(conds.begin(), + conds.end(), + [&](const Token* cond) { + if (cond->exprId() == 0) + return false; + auto it = state.find(cond->exprId()); + if (it == state.end()) + return false; + const ValueFlow::Value& v = it->second; + return isTrueOrFalse(v, !b); + }), + conds.end()); +} + +namespace { + struct Executor { + ProgramMemory* pm = nullptr; + const Settings* settings = nullptr; + int fdepth = 4; + int depth = 10; + + Executor(ProgramMemory* pm, const Settings* settings) : pm(pm), settings(settings) {} + + static ValueFlow::Value unknown() { + return ValueFlow::Value::unknown(); + } + + std::unordered_map executeAll(const std::vector& toks, + const bool* b = nullptr) const + { + std::unordered_map result; + auto state = *this; + for (const Token* tok : toks) { + ValueFlow::Value r = state.execute(tok); + if (r.isUninitValue()) + continue; + const bool brk = b && isTrueOrFalse(r, *b); + result.emplace(tok->exprId(), std::move(r)); + // Short-circuit evaluation + if (brk) + break; + } + return result; + } + + static std::vector flattenConditions(const Token* tok) + { + return astFlatten(tok, tok->str().c_str()); + } + static bool sortConditions(std::vector& conditions) + { + if (std::any_of(conditions.begin(), conditions.end(), [](const Token* child) { + return Token::Match(child, "&&|%oror%"); + })) + return false; + std::sort(conditions.begin(), conditions.end(), &TokenExprIdCompare); + conditions.erase(std::unique(conditions.begin(), conditions.end(), &TokenExprIdCompare), conditions.end()); + return !conditions.empty() && conditions.front()->exprId() != 0; + } + + ValueFlow::Value executeMultiCondition(bool b, const Token* expr) + { + if (pm->hasValue(expr->exprId())) { + const ValueFlow::Value& v = pm->at(expr->exprId()); + if (v.isIntValue()) + return v; + } + + // Evaluate recursively if there are no exprids + if ((expr->astOperand1() && expr->astOperand1()->exprId() == 0) || + (expr->astOperand2() && expr->astOperand2()->exprId() == 0)) { + ValueFlow::Value lhs = execute(expr->astOperand1()); + if (isTrueOrFalse(lhs, b)) + return lhs; + ValueFlow::Value rhs = execute(expr->astOperand2()); + if (isTrueOrFalse(rhs, b)) + return rhs; + if (isTrueOrFalse(lhs, !b) && isTrueOrFalse(rhs, !b)) + return lhs; + return unknown(); + } + + nonneg int n = astCount(expr, expr->str().c_str()); + if (n > 50) + return unknown(); + std::vector conditions1 = flattenConditions(expr); + if (conditions1.empty()) + return unknown(); + std::unordered_map condValues = executeAll(conditions1, &b); + bool allNegated = true; + ValueFlow::Value negatedValue = unknown(); + for (const auto& p : condValues) { + const ValueFlow::Value& v = p.second; + if (isTrueOrFalse(v, b)) + return v; + allNegated &= isTrueOrFalse(v, !b); + if (allNegated && negatedValue.isUninitValue()) + negatedValue = v; + } + if (condValues.size() == conditions1.size() && allNegated) + return negatedValue; + if (n > 4) + return unknown(); + if (!sortConditions(conditions1)) + return unknown(); + + for (const auto& p : *pm) { + const Token* tok = p.first.tok; + if (!tok) + continue; + const ValueFlow::Value& value = p.second; + + if (tok->str() == expr->str() && !astHasExpr(tok, expr->exprId())) { + // TODO: Handle when it is greater + if (n != astCount(tok, expr->str().c_str())) + continue; + std::vector conditions2 = flattenConditions(tok); + if (!sortConditions(conditions2)) + return unknown(); + if (conditions1.size() == conditions2.size() && + std::equal(conditions1.begin(), conditions1.end(), conditions2.begin(), &TokenExprIdEqual)) + return value; + std::vector diffConditions1 = setDifference(conditions1, conditions2); + std::vector diffConditions2 = setDifference(conditions2, conditions1); + pruneConditions(diffConditions1, !b, condValues); + pruneConditions(diffConditions2, !b, executeAll(diffConditions2)); + if (diffConditions1.size() != diffConditions2.size()) + continue; + if (diffConditions1.size() == conditions1.size()) + continue; + for (const Token* cond1 : diffConditions1) { + auto it = std::find_if(diffConditions2.begin(), diffConditions2.end(), [&](const Token* cond2) { + return evalSameCondition(*pm, cond2, cond1, settings); + }); + if (it == diffConditions2.end()) + break; + diffConditions2.erase(it); + } + if (diffConditions2.empty()) + return value; + } + } + return unknown(); + } + + ValueFlow::Value executeImpl(const Token* expr) + { + const ValueFlow::Value* value = nullptr; + if (!expr) + return unknown(); + if (expr->hasKnownIntValue() && !expr->isAssignmentOp() && expr->str() != ",") + return expr->values().front(); + if ((value = expr->getKnownValue(ValueFlow::Value::ValueType::FLOAT)) || + (value = expr->getKnownValue(ValueFlow::Value::ValueType::TOK)) || + (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_START)) || + (value = expr->getKnownValue(ValueFlow::Value::ValueType::ITERATOR_END)) || + (value = expr->getKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE))) { + return *value; + } + if (expr->isNumber()) { + if (MathLib::isFloat(expr->str())) + return unknown(); + MathLib::bigint i = MathLib::toBigNumber(expr->str()); + if (i < 0 && astIsUnsigned(expr)) + return unknown(); + return ValueFlow::Value{i}; + } + if (expr->isBoolean()) + return ValueFlow::Value{expr->str() == "true"}; + if (Token::Match(expr->tokAt(-2), ". %name% (") && astIsContainer(expr->tokAt(-2)->astOperand1())) { + const Token* containerTok = expr->tokAt(-2)->astOperand1(); + const Library::Container::Yield yield = containerTok->valueType()->container->getYield(expr->strAt(-1)); + if (yield == Library::Container::Yield::SIZE) { + ValueFlow::Value v = execute(containerTok); + if (!v.isContainerSizeValue()) + return unknown(); + v.valueType = ValueFlow::Value::ValueType::INT; + return v; + } + if (yield == Library::Container::Yield::EMPTY) { + ValueFlow::Value v = execute(containerTok); + if (!v.isContainerSizeValue()) + return unknown(); + if (v.isImpossible() && v.intvalue == 0) + return ValueFlow::Value{0}; + if (!v.isImpossible()) + return ValueFlow::Value{v.intvalue == 0}; + } + } else if (expr->isAssignmentOp() && expr->astOperand1() && expr->astOperand2() && + expr->astOperand1()->exprId() > 0) { + ValueFlow::Value rhs = execute(expr->astOperand2()); + if (rhs.isUninitValue()) + return unknown(); + if (expr->str() != "=") { + if (!pm->hasValue(expr->astOperand1()->exprId())) + return unknown(); + ValueFlow::Value& lhs = pm->at(expr->astOperand1()->exprId()); + rhs = evaluate(removeAssign(expr->str()), lhs, rhs); + if (lhs.isIntValue()) + ValueFlow::Value::visitValue(rhs, std::bind(assign{}, std::ref(lhs.intvalue), std::placeholders::_1)); + else if (lhs.isFloatValue()) + ValueFlow::Value::visitValue(rhs, + std::bind(assign{}, std::ref(lhs.floatValue), std::placeholders::_1)); + else + return unknown(); + return lhs; + } + pm->setValue(expr->astOperand1(), rhs); + return rhs; + } else if (expr->str() == "&&" && expr->astOperand1() && expr->astOperand2()) { + return executeMultiCondition(false, expr); + } else if (expr->str() == "||" && expr->astOperand1() && expr->astOperand2()) { + return executeMultiCondition(true, expr); + } else if (expr->str() == "," && expr->astOperand1() && expr->astOperand2()) { + execute(expr->astOperand1()); + return execute(expr->astOperand2()); + } else if (expr->tokType() == Token::eIncDecOp && expr->astOperand1() && expr->astOperand1()->exprId() != 0) { + if (!pm->hasValue(expr->astOperand1()->exprId())) + return ValueFlow::Value::unknown(); + ValueFlow::Value& lhs = pm->at(expr->astOperand1()->exprId()); + if (!lhs.isIntValue()) + return unknown(); + // overflow + if (!lhs.isImpossible() && lhs.intvalue == 0 && expr->str() == "--" && astIsUnsigned(expr->astOperand1())) + return unknown(); + + if (expr->str() == "++") + lhs.intvalue++; + else + lhs.intvalue--; + return lhs; + } else if (expr->str() == "[" && expr->astOperand1() && expr->astOperand2()) { + const Token* tokvalue = nullptr; + if (!pm->getTokValue(expr->astOperand1()->exprId(), tokvalue)) { + auto tokvalue_it = std::find_if(expr->astOperand1()->values().cbegin(), + expr->astOperand1()->values().cend(), + std::mem_fn(&ValueFlow::Value::isTokValue)); + if (tokvalue_it == expr->astOperand1()->values().cend() || !tokvalue_it->isKnown()) { + return unknown(); + } + tokvalue = tokvalue_it->tokvalue; + } + if (!tokvalue || !tokvalue->isLiteral()) { + return unknown(); + } + const std::string strValue = tokvalue->strValue(); + ValueFlow::Value rhs = execute(expr->astOperand2()); + if (!rhs.isIntValue()) + return unknown(); + const MathLib::bigint index = rhs.intvalue; + if (index >= 0 && index < strValue.size()) + return ValueFlow::Value{strValue[index]}; + if (index == strValue.size()) + return ValueFlow::Value{}; + } else if (Token::Match(expr, "%cop%") && expr->astOperand1() && expr->astOperand2()) { + ValueFlow::Value lhs = execute(expr->astOperand1()); + ValueFlow::Value rhs = execute(expr->astOperand2()); + ValueFlow::Value r = unknown(); + if (!lhs.isUninitValue() && !rhs.isUninitValue()) + r = evaluate(expr->str(), lhs, rhs); + if (expr->isComparisonOp() && (r.isUninitValue() || r.isImpossible())) { + if (rhs.isIntValue()) { + std::vector result = + infer(ValueFlow::makeIntegralInferModel(), expr->str(), expr->astOperand1()->values(), {std::move(rhs)}); + if (!result.empty() && result.front().isKnown()) + return result.front(); + } + if (lhs.isIntValue()) { + std::vector result = + infer(ValueFlow::makeIntegralInferModel(), expr->str(), {std::move(lhs)}, expr->astOperand2()->values()); + if (!result.empty() && result.front().isKnown()) + return result.front(); + } + return unknown(); + } + return r; + } + // Unary ops + else if (Token::Match(expr, "!|+|-") && expr->astOperand1() && !expr->astOperand2()) { + ValueFlow::Value lhs = execute(expr->astOperand1()); + if (!lhs.isIntValue()) + return unknown(); + if (expr->str() == "!") { + if (isTrue(lhs)) { + lhs.intvalue = 0; + } else if (isFalse(lhs)) { + lhs.intvalue = 1; + } else { + return unknown(); + } + lhs.setPossible(); + lhs.bound = ValueFlow::Value::Bound::Point; + } + if (expr->str() == "-") + lhs.intvalue = -lhs.intvalue; + return lhs; + } else if (expr->str() == "?" && expr->astOperand1() && expr->astOperand2()) { + ValueFlow::Value cond = execute(expr->astOperand1()); + if (!cond.isIntValue()) + return unknown(); + const Token* child = expr->astOperand2(); + if (isFalse(cond)) + return execute(child->astOperand2()); + if (isTrue(cond)) + return execute(child->astOperand1()); + + return unknown(); + } else if (expr->str() == "(" && expr->isCast()) { + if (Token::simpleMatch(expr->previous(), ">") && expr->previous()->link()) + return execute(expr->astOperand2()); + return execute(expr->astOperand1()); + } + if (expr->exprId() > 0 && pm->hasValue(expr->exprId())) { + ValueFlow::Value result = pm->at(expr->exprId()); + if (result.isImpossible() && result.isIntValue() && result.intvalue == 0 && isUsedAsBool(expr)) { + result.intvalue = !result.intvalue; + result.setKnown(); + } + return result; + } + + if (Token::Match(expr->previous(), ">|%name% {|(")) { + const Token* ftok = expr->previous(); + const Function* f = ftok->function(); + ValueFlow::Value result = unknown(); + if (settings && expr->str() == "(") { + std::vector tokArgs = getArguments(expr); + std::vector args(tokArgs.size()); + std::transform( + tokArgs.cbegin(), tokArgs.cend(), args.begin(), [&](const Token* tok) { + return execute(tok); + }); + if (f) { + if (fdepth >= 0 && !f->isImplicitlyVirtual()) { + ProgramMemory functionState; + for (std::size_t i = 0; i < args.size(); ++i) { + const Variable* const arg = f->getArgumentVar(i); + if (!arg) + return unknown(); + functionState.setValue(arg->nameToken(), args[i]); + } + Executor ex = *this; + ex.pm = &functionState; + ex.fdepth--; + auto r = ex.execute(f->functionScope); + if (!r.empty()) + result = r.front(); + // TODO: Track values changed by reference + } + } else { + BuiltinLibraryFunction lf = getBuiltinLibraryFunction(ftok->str()); + if (lf) + return lf(args); + const std::string& returnValue = settings->library.returnValue(ftok); + if (!returnValue.empty()) { + std::unordered_map arg_map; + int argn = 0; + for (const ValueFlow::Value& v : args) { + if (!v.isUninitValue()) + arg_map[argn] = v; + argn++; + } + return evaluateLibraryFunction(arg_map, returnValue, settings, ftok->isCpp()); + } + } + } + // Check if function modifies argument + visitAstNodes(expr->astOperand2(), [&](const Token* child) { + if (child->exprId() > 0 && pm->hasValue(child->exprId())) { + ValueFlow::Value& v = pm->at(child->exprId()); + if (v.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE) { + if (ValueFlow::isContainerSizeChanged(child, v.indirect, *settings)) + v = unknown(); + } else if (v.valueType != ValueFlow::Value::ValueType::UNINIT) { + if (isVariableChanged(child, v.indirect, settings)) + v = unknown(); + } + } + return ChildrenToVisit::op1_and_op2; + }); + return result; + } + + return unknown(); + } + static const ValueFlow::Value* getImpossibleValue(const Token* tok) + { + if (!tok) + return nullptr; + std::vector values; + for (const ValueFlow::Value& v : tok->values()) { + if (!v.isImpossible()) + continue; + if (v.isContainerSizeValue() || v.isIntValue()) { + values.push_back(std::addressof(v)); + } + } + auto it = + std::max_element(values.begin(), values.end(), [](const ValueFlow::Value* x, const ValueFlow::Value* y) { + return x->intvalue < y->intvalue; + }); + if (it == values.end()) + return nullptr; + return *it; + } + + static bool updateValue(ValueFlow::Value& v, ValueFlow::Value x) + { + const bool returnValue = !x.isUninitValue() && !x.isImpossible(); + if (v.isUninitValue() || returnValue) + v = std::move(x); + return returnValue; + } + + ValueFlow::Value execute(const Token* expr) + { + depth--; + OnExit onExit{[&] { + depth++; + }}; + if (depth < 0) + return unknown(); + ValueFlow::Value v = unknown(); + if (updateValue(v, executeImpl(expr))) + return v; + if (!expr) + return v; + if (expr->exprId() > 0 && pm->hasValue(expr->exprId())) { + if (updateValue(v, pm->at(expr->exprId()))) + return v; + } + // Find symbolic values + for (const ValueFlow::Value& value : expr->values()) { + if (!value.isSymbolicValue()) + continue; + if (!value.isKnown()) + continue; + if (value.tokvalue->exprId() > 0 && !pm->hasValue(value.tokvalue->exprId())) + continue; + ValueFlow::Value v2 = pm->at(value.tokvalue->exprId()); + if (!v2.isIntValue() && value.intvalue != 0) + continue; + v2.intvalue += value.intvalue; + return v2; + } + if (v.isImpossible() && v.isIntValue()) + return v; + if (const ValueFlow::Value* value = getImpossibleValue(expr)) + return *value; + return v; + } + + std::vector execute(const Scope* scope) + { + if (!scope) + return {unknown()}; + if (!scope->bodyStart) + return {unknown()}; + for (const Token* tok = scope->bodyStart->next(); precedes(tok, scope->bodyEnd); tok = tok->next()) { + const Token* top = tok->astTop(); + if (!top) + return {unknown()}; + + if (Token::simpleMatch(top, "return") && top->astOperand1()) + return {execute(top->astOperand1())}; + + if (Token::Match(top, "%op%")) { + if (execute(top).isUninitValue()) + return {unknown()}; + const Token* next = nextAfterAstRightmostLeaf(top); + if (!next) + return {unknown()}; + tok = next; + } else if (Token::simpleMatch(top->previous(), "if (")) { + const Token* condTok = top->astOperand2(); + ValueFlow::Value v = execute(condTok); + if (!v.isIntValue()) + return {unknown()}; + const Token* thenStart = top->link()->next(); + const Token* next = thenStart->link(); + const Token* elseStart = nullptr; + if (Token::simpleMatch(thenStart->link(), "} else {")) { + elseStart = thenStart->link()->tokAt(2); + next = elseStart->link(); + } + std::vector result; + if (isTrue(v)) { + result = execute(thenStart->scope()); + } else if (isFalse(v)) { + if (elseStart) + result = execute(elseStart->scope()); + } else { + return {unknown()}; + } + if (!result.empty()) + return result; + tok = next; + } else { + return {unknown()}; + } + } + return {}; + } + }; +} // namespace + +static ValueFlow::Value execute(const Token* expr, ProgramMemory& pm, const Settings* settings) +{ + Executor ex{&pm, settings}; + return ex.execute(expr); +} + +std::vector execute(const Scope* scope, ProgramMemory& pm, const Settings& settings) +{ + Executor ex{&pm, &settings}; + return ex.execute(scope); +} + +ValueFlow::Value evaluateLibraryFunction(const std::unordered_map& args, + const std::string& returnValue, + const Settings* settings, + bool cpp) +{ + thread_local static std::unordered_map& arg)>> + functions = {}; + if (functions.count(returnValue) == 0) { + + std::unordered_map lookupVarId; + std::shared_ptr expr = createTokenFromExpression(returnValue, settings, cpp, &lookupVarId); + + functions[returnValue] = + [lookupVarId, expr, settings](const std::unordered_map& xargs) { + if (!expr) + return ValueFlow::Value::unknown(); + ProgramMemory pm{}; + for (const auto& p : xargs) { + auto it = lookupVarId.find(p.first); + if (it != lookupVarId.end()) + pm.setValue(it->second, p.second); + } + return execute(expr.get(), pm, settings); + }; + } + return functions.at(returnValue)(args); +} + +void execute(const Token* expr, + ProgramMemory& programMemory, + MathLib::bigint* result, + bool* error, + const Settings* settings) +{ + ValueFlow::Value v = execute(expr, programMemory, settings); + if (!v.isIntValue() || v.isImpossible()) { + if (error) + *error = true; + } else if (result) + *result = v.intvalue; +} diff --git a/cppcheck-2.14.0/lib/programmemory.h b/cppcheck-2.14.0/lib/programmemory.h new file mode 100644 index 00000000..c1c956d1 --- /dev/null +++ b/cppcheck-2.14.0/lib/programmemory.h @@ -0,0 +1,218 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef GUARD_PROGRAMMEMORY_H +#define GUARD_PROGRAMMEMORY_H + +#include "config.h" +#include "mathlib.h" +#include "vfvalue.h" // needed for alias + +#include +#include +#include +#include +#include +#include +#include + +class Scope; +class Token; +class Settings; + +// Class used to handle heterogeneous lookup in unordered_map(since we can't use C++20 yet) +struct ExprIdToken { + const Token* tok = nullptr; + nonneg int exprid = 0; + + ExprIdToken() = default; + // cppcheck-suppress noExplicitConstructor + // NOLINTNEXTLINE(google-explicit-constructor) + ExprIdToken(const Token* tok); + // TODO: Make this constructor only available from ProgramMemory + // cppcheck-suppress noExplicitConstructor + // NOLINTNEXTLINE(google-explicit-constructor) + ExprIdToken(nonneg int exprid) : exprid(exprid) {} + + nonneg int getExpressionId() const; + + bool operator==(const ExprIdToken& rhs) const { + return getExpressionId() == rhs.getExpressionId(); + } + + bool operator<(const ExprIdToken& rhs) const { + return getExpressionId() < rhs.getExpressionId(); + } + + template + friend bool operator!=(const T& lhs, const U& rhs) + { + return !(lhs == rhs); + } + + template + friend bool operator<=(const T& lhs, const U& rhs) + { + return !(lhs > rhs); + } + + template + friend bool operator>(const T& lhs, const U& rhs) + { + return rhs < lhs; + } + + template + friend bool operator>=(const T& lhs, const U& rhs) + { + return !(lhs < rhs); + } + + const Token& operator*() const NOEXCEPT { + return *tok; + } + + const Token* operator->() const NOEXCEPT { + return tok; + } + + struct Hash { + std::size_t operator()(ExprIdToken etok) const; + }; +}; + +struct ProgramMemory { + using Map = std::unordered_map; + + ProgramMemory() = default; + + explicit ProgramMemory(Map values) : mValues(std::move(values)) {} + + void setValue(const Token* expr, const ValueFlow::Value& value); + const ValueFlow::Value* getValue(nonneg int exprid, bool impossible = false) const; + + bool getIntValue(nonneg int exprid, MathLib::bigint& result) const; + void setIntValue(const Token* expr, MathLib::bigint value, bool impossible = false); + + bool getContainerSizeValue(nonneg int exprid, MathLib::bigint& result) const; + bool getContainerEmptyValue(nonneg int exprid, MathLib::bigint& result) const; + void setContainerSizeValue(const Token* expr, MathLib::bigint value, bool isEqual = true); + + void setUnknown(const Token* expr); + + bool getTokValue(nonneg int exprid, const Token*& result) const; + bool hasValue(nonneg int exprid); + + const ValueFlow::Value& at(nonneg int exprid) const; + ValueFlow::Value& at(nonneg int exprid); + + void erase_if(const std::function& pred); + + void swap(ProgramMemory &pm); + + void clear(); + + bool empty() const; + + void replace(ProgramMemory pm); + + void insert(const ProgramMemory &pm); + + Map::iterator begin() { + return mValues.begin(); + } + + Map::iterator end() { + return mValues.end(); + } + + Map::const_iterator begin() const { + return mValues.begin(); + } + + Map::const_iterator end() const { + return mValues.end(); + } + + friend bool operator==(const ProgramMemory& x, const ProgramMemory& y) { + return x.mValues == y.mValues; + } + + friend bool operator!=(const ProgramMemory& x, const ProgramMemory& y) { + return x.mValues != y.mValues; + } + +private: + Map mValues; +}; + +struct ProgramMemoryState { + ProgramMemory state; + std::map origins; + const Settings* settings; + + explicit ProgramMemoryState(const Settings* s); + + void insert(const ProgramMemory &pm, const Token* origin = nullptr); + void replace(ProgramMemory pm, const Token* origin = nullptr); + + void addState(const Token* tok, const ProgramMemory::Map& vars); + + void assume(const Token* tok, bool b, bool isEmpty = false); + + void removeModifiedVars(const Token* tok); + + ProgramMemory get(const Token* tok, const Token* ctx, const ProgramMemory::Map& vars) const; +}; + +std::vector execute(const Scope* scope, ProgramMemory& pm, const Settings& settings); + +void execute(const Token* expr, + ProgramMemory& programMemory, + MathLib::bigint* result, + bool* error, + const Settings* settings); + +/** + * Is condition always false when variable has given value? + * \param condition top ast token in condition + * \param pm program memory + */ +bool conditionIsFalse(const Token* condition, ProgramMemory pm, const Settings* settings); + +/** + * Is condition always true when variable has given value? + * \param condition top ast token in condition + * \param pm program memory + */ +bool conditionIsTrue(const Token* condition, ProgramMemory pm, const Settings* settings); + +/** + * Get program memory by looking backwards from given token. + */ +ProgramMemory getProgramMemory(const Token* tok, const Token* expr, const ValueFlow::Value& value, const Settings& settings); + +ValueFlow::Value evaluateLibraryFunction(const std::unordered_map& args, + const std::string& returnValue, + const Settings* settings, + bool cpp); + +#endif + + + diff --git a/cppcheck-2.14.0/lib/reverseanalyzer.cpp b/cppcheck-2.14.0/lib/reverseanalyzer.cpp new file mode 100644 index 00000000..637b4748 --- /dev/null +++ b/cppcheck-2.14.0/lib/reverseanalyzer.cpp @@ -0,0 +1,404 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "reverseanalyzer.h" + +#include "analyzer.h" +#include "astutils.h" +#include "errortypes.h" +#include "forwardanalyzer.h" +#include "mathlib.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "valueptr.h" + +#include +#include +#include +#include +#include +#include + +namespace { + struct ReverseTraversal { + ReverseTraversal(const ValuePtr& analyzer, const TokenList& tokenlist, ErrorLogger& errorLogger, const Settings& settings) + : analyzer(analyzer), tokenlist(tokenlist), errorLogger(errorLogger), settings(settings) + {} + ValuePtr analyzer; + const TokenList& tokenlist; + ErrorLogger& errorLogger; + const Settings& settings; + + std::pair evalCond(const Token* tok) const { + std::vector result = analyzer->evaluate(tok); + // TODO: We should convert to bool + const bool checkThen = std::any_of(result.cbegin(), result.cend(), [](int x) { + return x == 1; + }); + const bool checkElse = std::any_of(result.cbegin(), result.cend(), [](int x) { + return x == 0; + }); + return std::make_pair(checkThen, checkElse); + } + + bool update(Token* tok) { + Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Reverse); + if (action.isInconclusive() && !analyzer->lowerToInconclusive()) + return false; + if (action.isInvalid()) + return false; + if (!action.isNone()) + analyzer->update(tok, action, Analyzer::Direction::Reverse); + return true; + } + + static Token* getParentFunction(Token* tok) + { + if (!tok) + return nullptr; + if (!tok->astParent()) + return nullptr; + int argn = -1; + if (Token* ftok = getTokenArgumentFunction(tok, argn)) { + while (!Token::Match(ftok, "(|{")) { + if (!ftok) + return nullptr; + if (ftok->index() >= tok->index()) + return nullptr; + if (!ftok->link() || ftok->str() == ")") + ftok = ftok->next(); + else + ftok = ftok->link()->next(); + } + if (ftok == tok) + return nullptr; + return ftok; + } + return nullptr; + } + + static Token* getTopFunction(Token* tok) + { + if (!tok) + return nullptr; + if (!tok->astParent()) + return tok; + Token* parent = tok; + Token* top = tok; + while ((parent = getParentFunction(parent))) + top = parent; + return top; + } + + bool updateRecursive(Token* start) { + bool continueB = true; + visitAstNodes(start, [&](Token* tok) { + const Token* parent = tok->astParent(); + while (Token::simpleMatch(parent, ":")) + parent = parent->astParent(); + if (isUnevaluated(tok) || isDeadCode(tok, parent)) + return ChildrenToVisit::none; + continueB &= update(tok); + if (continueB) + return ChildrenToVisit::op1_and_op2; + return ChildrenToVisit::done; + }); + return continueB; + } + + Analyzer::Action analyzeRecursive(const Token* start) const { + Analyzer::Action result = Analyzer::Action::None; + visitAstNodes(start, [&](const Token* tok) { + result |= analyzer->analyze(tok, Analyzer::Direction::Reverse); + if (result.isModified()) + return ChildrenToVisit::done; + return ChildrenToVisit::op1_and_op2; + }); + return result; + } + + Analyzer::Action analyzeRange(const Token* start, const Token* end) const { + Analyzer::Action result = Analyzer::Action::None; + for (const Token* tok = start; tok && tok != end; tok = tok->next()) { + Analyzer::Action action = analyzer->analyze(tok, Analyzer::Direction::Reverse); + if (action.isModified()) + return action; + result |= action; + } + return result; + } + + Token* isDeadCode(Token* tok, const Token* end = nullptr) const { + int opSide = 0; + for (; tok && tok->astParent(); tok = tok->astParent()) { + if (tok == end) + break; + Token* parent = tok->astParent(); + if (Token::simpleMatch(parent, ":")) { + if (astIsLHS(tok)) + opSide = 1; + else if (astIsRHS(tok)) + opSide = 2; + else + opSide = 0; + } + if (tok != parent->astOperand2()) + continue; + if (Token::simpleMatch(parent, ":")) + parent = parent->astParent(); + if (!Token::Match(parent, "%oror%|&&|?")) + continue; + const Token* condTok = parent->astOperand1(); + if (!condTok) + continue; + bool checkThen, checkElse; + std::tie(checkThen, checkElse) = evalCond(condTok); + + if (parent->str() == "?") { + if (checkElse && opSide == 1) + return parent; + if (checkThen && opSide == 2) + return parent; + } + if (!checkThen && parent->str() == "&&") + return parent; + if (!checkElse && parent->str() == "||") + return parent; + } + return nullptr; + } + + void traverse(Token* start, const Token* end = nullptr) { + if (start == end) + return; + std::size_t i = start->index(); + for (Token* tok = start->previous(); succeeds(tok, end); tok = tok->previous()) { + if (tok->index() >= i) + throw InternalError(tok, "Cyclic reverse analysis."); + i = tok->index(); + if (tok == start || (tok->str() == "{" && (tok->scope()->type == Scope::ScopeType::eFunction || + tok->scope()->type == Scope::ScopeType::eLambda))) { + const Function* f = tok->scope()->function; + if (f && f->isConstructor()) { + if (const Token* initList = f->constructorMemberInitialization()) + traverse(tok->previous(), tok->tokAt(initList->index() - tok->index())); + } + break; + } + if (Token::Match(tok, "return|break|continue")) + break; + if (Token::Match(tok, "%name% :")) + break; + if (Token::simpleMatch(tok, ":")) + continue; + // Evaluate LHS of assignment before RHS + if (Token* assignTok = assignExpr(tok)) { + // If assignTok has broken ast then stop + if (!assignTok->astOperand1() || !assignTok->astOperand2()) + break; + Token* assignTop = assignTok; + bool continueB = true; + while (assignTop->isAssignmentOp()) { + if (!Token::Match(assignTop->astOperand1(), "%assign%")) { + continueB &= updateRecursive(assignTop->astOperand1()); + } + if (!assignTop->astParent()) + break; + assignTop = assignTop->astParent(); + } + // Is assignment in dead code + if (Token* parent = isDeadCode(assignTok)) { + tok = parent; + continue; + } + // Simple assign + if (assignTok->str() == "=" && (assignTok->astParent() == assignTop || assignTok == assignTop)) { + Analyzer::Action rhsAction = + analyzer->analyze(assignTok->astOperand2(), Analyzer::Direction::Reverse); + Analyzer::Action lhsAction = + analyzer->analyze(assignTok->astOperand1(), Analyzer::Direction::Reverse); + // Assignment from + if (rhsAction.isRead() && !lhsAction.isInvalid() && assignTok->astOperand1()->exprId() > 0) { + const std::string info = "Assignment from '" + assignTok->expressionString() + "'"; + ValuePtr a = analyzer->reanalyze(assignTok->astOperand1(), info); + if (a) { + valueFlowGenericForward(nextAfterAstRightmostLeaf(assignTok->astOperand2()), + assignTok->astOperand2()->scope()->bodyEnd, + a, + tokenlist, + errorLogger, + settings); + } + // Assignment to + } else if (lhsAction.matches() && !assignTok->astOperand2()->hasKnownIntValue() && + assignTok->astOperand2()->exprId() > 0 && + isConstExpression(assignTok->astOperand2(), settings.library)) { + const std::string info = "Assignment to '" + assignTok->expressionString() + "'"; + ValuePtr a = analyzer->reanalyze(assignTok->astOperand2(), info); + if (a) { + valueFlowGenericForward(nextAfterAstRightmostLeaf(assignTok->astOperand2()), + assignTok->astOperand2()->scope()->bodyEnd, + a, + tokenlist, + errorLogger, + settings); + valueFlowGenericReverse(assignTok->astOperand1()->previous(), end, a, tokenlist, errorLogger, settings); + } + } + } + if (!continueB) + break; + if (!updateRecursive(assignTop->astOperand2())) + break; + tok = previousBeforeAstLeftmostLeaf(assignTop)->next(); + continue; + } + if (tok->str() == ")" && !isUnevaluated(tok)) { + if (Token* top = getTopFunction(tok->link())) { + if (!updateRecursive(top)) + break; + Token* next = previousBeforeAstLeftmostLeaf(top); + if (next && precedes(next, tok)) + tok = next->next(); + } + continue; + } + if (tok->str() == "}") { + Token* condTok = getCondTokFromEnd(tok); + if (!condTok) + break; + Analyzer::Action condAction = analyzeRecursive(condTok); + const bool inLoop = condTok->astTop() && Token::Match(condTok->astTop()->previous(), "for|while ("); + // Evaluate condition of for and while loops first + if (inLoop) { + if (Token::findmatch(tok->link(), "goto|break", tok)) + break; + if (condAction.isModified()) + break; + valueFlowGenericForward(condTok, analyzer, tokenlist, errorLogger, settings); + } + Token* thenEnd; + const bool hasElse = Token::simpleMatch(tok->link()->tokAt(-2), "} else {"); + if (hasElse) { + thenEnd = tok->link()->tokAt(-2); + } else { + thenEnd = tok; + } + + Analyzer::Action thenAction = analyzeRange(thenEnd->link(), thenEnd); + Analyzer::Action elseAction = Analyzer::Action::None; + if (hasElse) { + elseAction = analyzeRange(tok->link(), tok); + } + if (thenAction.isModified() && inLoop) + break; + if (thenAction.isModified() && !elseAction.isModified()) + analyzer->assume(condTok, hasElse); + else if (elseAction.isModified() && !thenAction.isModified()) + analyzer->assume(condTok, !hasElse); + // Bail if one of the branches are read to avoid FPs due to over constraints + else if (thenAction.isIdempotent() || elseAction.isIdempotent() || thenAction.isRead() || + elseAction.isRead()) + break; + if (thenAction.isInvalid() || elseAction.isInvalid()) + break; + + if (!thenAction.isModified() && !elseAction.isModified()) + valueFlowGenericForward(condTok, analyzer, tokenlist, errorLogger, settings); + else if (condAction.isRead()) + break; + // If the condition modifies the variable then bail + if (condAction.isModified()) + break; + tok = condTok->astTop()->previous(); + continue; + } + if (tok->str() == "{") { + if (tok->previous() && + (Token::simpleMatch(tok->previous(), "do") || + (tok->strAt(-1) == ")" && Token::Match(tok->linkAt(-1)->previous(), "for|while (")))) { + Analyzer::Action action = analyzeRange(tok, tok->link()); + if (action.isModified()) + break; + } + Token* condTok = getCondTokFromEnd(tok->link()); + if (condTok) { + Analyzer::Result r = valueFlowGenericForward(condTok, analyzer, tokenlist, errorLogger, settings); + if (r.action.isModified()) + break; + } + if (Token::simpleMatch(tok->tokAt(-2), "} else {")) + tok = tok->linkAt(-2); + if (Token::simpleMatch(tok->previous(), ") {")) + tok = tok->previous()->link(); + continue; + } + if (Token* next = isUnevaluated(tok)) { + tok = next; + continue; + } + if (Token* parent = isDeadCode(tok)) { + tok = parent; + continue; + } + if (tok->str() == "case") { + const Scope* scope = tok->scope(); + while (scope && scope->type != Scope::eSwitch) + scope = scope->nestedIn; + if (!scope || scope->type != Scope::eSwitch) + break; + tok = tok->tokAt(scope->bodyStart->index() - tok->index() - 1); + continue; + } + if (!update(tok)) + break; + } + } + + static Token* assignExpr(Token* tok) { + if (Token::Match(tok, ")|}")) + tok = tok->link(); + while (tok->astParent() && (astIsRHS(tok) || !tok->astParent()->isBinaryOp())) { + if (tok->astParent()->isAssignmentOp()) + return tok->astParent(); + tok = tok->astParent(); + } + return nullptr; + } + + static Token* isUnevaluated(Token* tok) { + if (Token::Match(tok, ")|>") && tok->link()) { + Token* start = tok->link(); + if (::isUnevaluated(start->previous())) + return start->previous(); + if (Token::simpleMatch(start, "<")) + return start; + } + return nullptr; + } + }; +} + +void valueFlowGenericReverse(Token* start, const Token* end, const ValuePtr& a, const TokenList& tokenlist, ErrorLogger& errorLogger, const Settings& settings) +{ + if (a->invalid()) + return; + ReverseTraversal rt{a, tokenlist, errorLogger, settings}; + rt.traverse(start, end); +} diff --git a/cppcheck-2.14.0/lib/reverseanalyzer.h b/cppcheck-2.14.0/lib/reverseanalyzer.h new file mode 100644 index 00000000..3f0285e5 --- /dev/null +++ b/cppcheck-2.14.0/lib/reverseanalyzer.h @@ -0,0 +1,32 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef reverseanalyzerH +#define reverseanalyzerH + +struct Analyzer; +class ErrorLogger; +class Settings; +class Token; +class TokenList; +template +class ValuePtr; + +void valueFlowGenericReverse(Token* start, const Token* end, const ValuePtr& a, const TokenList& tokenlist, ErrorLogger& errorLogger, const Settings& settings); + +#endif diff --git a/cppcheck-2.14.0/lib/settings.cpp b/cppcheck-2.14.0/lib/settings.cpp new file mode 100644 index 00000000..e1780aa0 --- /dev/null +++ b/cppcheck-2.14.0/lib/settings.cpp @@ -0,0 +1,624 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" +#include "settings.h" +#include "path.h" +#include "summaries.h" +#include "vfvalue.h" + +#include +#include +#include +#include + +#include "json.h" + +#ifndef _WIN32 +#include // for getpid() +#else +#include // for getpid() +#endif + + +std::atomic Settings::mTerminated; + +const char Settings::SafeChecks::XmlRootName[] = "safe-checks"; +const char Settings::SafeChecks::XmlClasses[] = "class-public"; +const char Settings::SafeChecks::XmlExternalFunctions[] = "external-functions"; +const char Settings::SafeChecks::XmlInternalFunctions[] = "internal-functions"; +const char Settings::SafeChecks::XmlExternalVariables[] = "external-variables"; + +static int getPid() +{ +#ifndef _WIN32 + return getpid(); +#else + return _getpid(); +#endif +} + +Settings::Settings() +{ + severity.setEnabled(Severity::error, true); + certainty.setEnabled(Certainty::normal, true); + setCheckLevel(Settings::CheckLevel::exhaustive); + executor = defaultExecutor(); + pid = getPid(); +} + +std::string Settings::loadCppcheckCfg(Settings& settings, Suppressions& suppressions) +{ + // TODO: this always needs to be run *after* the Settings has been filled + static const std::string cfgFilename = "cppcheck.cfg"; + std::string fileName; +#ifdef FILESDIR + if (Path::isFile(Path::join(FILESDIR, cfgFilename))) + fileName = Path::join(FILESDIR, cfgFilename); +#endif + // cppcheck-suppress knownConditionTrueFalse + if (fileName.empty()) { + // TODO: make sure that exename is set + fileName = Path::getPathFromFilename(settings.exename) + cfgFilename; + if (!Path::isFile(fileName)) + return ""; + } + + std::ifstream fin(fileName); + if (!fin.is_open()) + return "could not open file"; + picojson::value json; + fin >> json; + { + const std::string& lastErr = picojson::get_last_error(); + if (!lastErr.empty()) + return "not a valid JSON - " + lastErr; + } + const picojson::object& obj = json.get(); + { + const picojson::object::const_iterator it = obj.find("productName"); + if (it != obj.cend()) { + const auto& v = it->second; + if (!v.is()) + return "'productName' is not a string"; + settings.cppcheckCfgProductName = v.get(); + } + } + { + const picojson::object::const_iterator it = obj.find("about"); + if (it != obj.cend()) { + const auto& v = it->second; + if (!v.is()) + return "'about' is not a string"; + settings.cppcheckCfgAbout = v.get(); + } + } + { + const picojson::object::const_iterator it = obj.find("addons"); + if (it != obj.cend()) { + const auto& entry = it->second; + if (!entry.is()) + return "'addons' is not an array"; + for (const picojson::value &v : entry.get()) + { + if (!v.is()) + return "'addons' array entry is not a string"; + const std::string &s = v.get(); + if (!Path::isAbsolute(s)) + settings.addons.emplace(Path::join(Path::getPathFromFilename(fileName), s)); + else + settings.addons.emplace(s); + } + } + } + { + const picojson::object::const_iterator it = obj.find("suppressions"); + if (it != obj.cend()) { + const auto& entry = it->second; + if (!entry.is()) + return "'suppressions' is not an array"; + for (const picojson::value &v : entry.get()) + { + if (!v.is()) + return "'suppressions' array entry is not a string"; + const std::string &s = v.get(); + const std::string err = suppressions.nomsg.addSuppressionLine(s); + if (!err.empty()) + return "could not parse suppression '" + s + "' - " + err; + } + } + } + { + const picojson::object::const_iterator it = obj.find("safety"); + if (it != obj.cend()) { + const auto& v = it->second; + if (!v.is()) + return "'safety' is not a bool"; + settings.safety = settings.safety || v.get(); + } + } + + return ""; +} + +std::pair Settings::getNameAndVersion(const std::string& productName) { + if (productName.empty()) + return {}; + const std::string::size_type pos1 = productName.rfind(' '); + if (pos1 == std::string::npos) + return {}; + if (pos1 + 2 >= productName.length()) + return {}; + for (auto pos2 = pos1 + 1; pos2 < productName.length(); ++pos2) { + const char c = productName[pos2]; + const char prev = productName[pos2-1]; + if (std::isdigit(c)) + continue; + if (c == '.' && std::isdigit(prev)) + continue; + if (c == 's' && pos2 + 1 == productName.length() && std::isdigit(prev)) + continue; + return {}; + } + return {productName.substr(0, pos1), productName.substr(pos1+1)}; +} + +std::string Settings::parseEnabled(const std::string &str, std::tuple, SimpleEnableGroup> &groups) +{ + // 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("--enable parameter is empty"); + std::string errmsg(parseEnabled(str.substr(prevPos, pos - prevPos), groups)); + if (!errmsg.empty()) + return errmsg; + ++pos; + prevPos = pos; + } + if (prevPos >= str.length()) + return std::string("--enable parameter is empty"); + return parseEnabled(str.substr(prevPos), groups); + } + + auto& severity = std::get<0>(groups); + auto& checks = std::get<1>(groups); + + if (str == "all") { + // "error" is always enabled and cannot be controlled - so exclude it from "all" + SimpleEnableGroup newSeverity; + newSeverity.fill(); + newSeverity.disable(Severity::error); + severity.enable(newSeverity); + checks.enable(Checks::missingInclude); + checks.enable(Checks::unusedFunction); + } else if (str == "warning") { + severity.enable(Severity::warning); + } else if (str == "style") { + severity.enable(Severity::style); + } else if (str == "performance") { + severity.enable(Severity::performance); + } else if (str == "portability") { + severity.enable(Severity::portability); + } else if (str == "information") { + severity.enable(Severity::information); + } else if (str == "unusedFunction") { + checks.enable(Checks::unusedFunction); + } else if (str == "missingInclude") { + checks.enable(Checks::missingInclude); + } +#ifdef CHECK_INTERNAL + else if (str == "internal") { + checks.enable(Checks::internalCheck); + } +#endif + else { + // the actual option is prepending in the applyEnabled() call + if (str.empty()) + return " parameter is empty"; + return " parameter with the unknown name '" + str + "'"; + } + + return ""; +} + +std::string Settings::addEnabled(const std::string &str) +{ + return applyEnabled(str, true); +} + +std::string Settings::removeEnabled(const std::string &str) +{ + return applyEnabled(str, false); +} + +std::string Settings::applyEnabled(const std::string &str, bool enable) +{ + std::tuple, SimpleEnableGroup> groups; + std::string errmsg = parseEnabled(str, groups); + if (!errmsg.empty()) + return (enable ? "--enable" : "--disable") + errmsg; + + const auto s = std::get<0>(groups); + const auto c = std::get<1>(groups); + if (enable) { + severity.enable(s); + checks.enable(c); + } + else { + severity.disable(s); + checks.disable(c); + } + // FIXME: hack to make sure "error" is always enabled + severity.enable(Severity::error); + return errmsg; +} + +bool Settings::isEnabled(const ValueFlow::Value *value, bool inconclusiveCheck) const +{ + if (!severity.isEnabled(Severity::warning) && (value->condition || value->defaultArg)) + return false; + if (!certainty.isEnabled(Certainty::inconclusive) && (inconclusiveCheck || value->isInconclusive())) + return false; + return true; +} + +void Settings::loadSummaries() +{ + Summaries::loadReturn(buildDir, summaryReturn); +} + +void Settings::setCheckLevel(CheckLevel level) +{ + if (level == CheckLevel::normal) { + // Checking should finish in reasonable time. + checkLevel = level; + performanceValueFlowMaxSubFunctionArgs = 8; + performanceValueFlowMaxIfCount = 100; + } + else if (level == CheckLevel::exhaustive) { + // Checking can take a little while. ~ 10 times slower than normal analysis is OK. + checkLevel = CheckLevel::exhaustive; + performanceValueFlowMaxIfCount = -1; + performanceValueFlowMaxSubFunctionArgs = 256; + } +} + +// TODO: auto generate these tables + +static const std::set autosarCheckers{ + "accessMoved", + "argumentSize", + "arrayIndexOutOfBounds", + "arrayIndexOutOfBoundsCond", + "arrayIndexThenCheck", + "bufferAccessOutOfBounds", + "comparePointers", + "constParameter", + "cstyleCast", + "ctuOneDefinitionViolation", + "doubleFree", + "duplInheritedMember", + "duplicateBreak", + "funcArgNamesDifferent", + "functionConst", + "functionStatic", + "invalidContainer", + "memleak", + "mismatchAllocDealloc", + "missingReturn", + "negativeIndex", + "noExplicitConstructor", + "nullPointer", + "nullPointerArithmetic", + "nullPointerArithmeticRedundantCheck", + "nullPointerDefaultArg", + "nullPointerRedundantCheck", + "objectIndex", + "overlappingWriteFunction", + "overlappingWriteUnion", + "pointerOutOfBounds", + "pointerOutOfBoundsCond", + "preprocessorErrorDirective", + "redundantAssignment", + "redundantInitialization", + "returnDanglingLifetime", + "shiftTooManyBits", + "sizeofSideEffects", + "throwInDestructor", + "throwInNoexceptFunction", + "uninitData", + "uninitMember", + "unreachableCode", + "unreadVariable", + "unsignedLessThanZero", + "unusedFunction", + "unusedStructMember", + "unusedValue", + "unusedVariable", + "useInitializerList", + "variableScope", + "virtualCallInConstructor", + "zerodiv", + "zerodivcond" +}; + +static const std::set certCCheckers{ + "IOWithoutPositioning", + "autoVariables", + "autovarInvalidDeallocation", + "bitwiseOnBoolean", + "comparePointers", + "danglingLifetime", + "deallocret", + "deallocuse", + "doubleFree", + "floatConversionOverflow", + "invalidFunctionArg", + "invalidLengthModifierError", + "invalidLifetime", + "invalidScanfFormatWidth", + "invalidscanf", + "leakReturnValNotUsed", + "leakUnsafeArgAlloc", + "memleak", + "memleakOnRealloc", + "mismatchAllocDealloc", + "missingReturn", + "nullPointer", + "nullPointerArithmetic", + "nullPointerArithmeticRedundantCheck", + "nullPointerDefaultArg", + "nullPointerRedundantCheck", + "preprocessorErrorDirective", + "resourceLeak", + "sizeofCalculation", + "stringLiteralWrite", + "uninitStructMember", + "uninitdata", + "uninitvar", + "unknownEvaluationOrder", + "useClosedFile", + "wrongPrintfScanfArgNum", + "wrongPrintfScanfParameterPositionError" +}; + +static const std::set certCppCheckers{ + "IOWithoutPositioning", + "accessMoved", + "comparePointers", + "containerOutOfBounds", + "ctuOneDefinitionViolation", + "deallocMismatch", + "deallocThrow", + "deallocuse", + "doubleFree", + "eraseDereference", + "exceptThrowInDestructor", + "initializerList", + "invalidContainer", + "lifetime", + "memleak", + "missingReturn", + "nullPointer", + "operatorEqToSelf", + "sizeofCalculation", + "uninitvar", + "virtualCallInConstructor", + "virtualDestructor" +}; + +static const std::set misrac2012Checkers{ + "alwaysFalse", + "alwaysTrue", + "argumentSize", + "autovarInvalidDeallocation", + "bufferAccessOutOfBounds", + "comparePointers", + "compareValueOutOfTypeRangeError", + "constPointer", + "danglingLifetime", + "duplicateBreak", + "error", + "funcArgNamesDifferent", + "incompatibleFileOpen", + "invalidFunctionArg", + "knownConditionTrueFalse", + "leakNoVarFunctionCall", + "leakReturnValNotUsed", + "memleak", + "memleakOnRealloc", + "missingReturn", + "overlappingWriteFunction", + "overlappingWriteUnion", + "pointerOutOfBounds", + "preprocessorErrorDirective", + "redundantAssignInSwitch", + "redundantAssignment", + "redundantCondition", + "resourceLeak", + "shadowVariable", + "sizeofCalculation", + "syntaxError", + "uninitvar", + "unknownEvaluationOrder", + "unreadVariable", + "unusedLabel", + "unusedVariable", + "useClosedFile", + "writeReadOnlyFile" +}; + +static const std::set misrac2023Checkers{ + "alwaysFalse", + "alwaysTrue", + "argumentSize", + "autovarInvalidDeallocation", + "bufferAccessOutOfBounds", + "comparePointers", + "compareValueOutOfTypeRangeError", + "constPointer", + "danglingLifetime", + "duplicateBreak", + "error", + "funcArgNamesDifferent", + "incompatibleFileOpen", + "invalidFunctionArg", + "knownConditionTrueFalse", + "leakNoVarFunctionCall", + "leakReturnValNotUsed", + "memleak", + "memleakOnRealloc", + "missingReturn", + "overlappingWriteFunction", + "overlappingWriteUnion", + "pointerOutOfBounds", + "preprocessorErrorDirective", + "redundantAssignInSwitch", + "redundantAssignment", + "redundantCondition", + "resourceLeak", + "shadowVariable", + "sizeofCalculation", + "syntaxError", + "uninitvar", + "unknownEvaluationOrder", + "unreadVariable", + "unusedLabel", + "unusedVariable", + "useClosedFile", + "writeReadOnlyFile" +}; + +static const std::set misracpp2008Checkers{ + "autoVariables", + "comparePointers", + "constParameter", + "constVariable", + "cstyleCast", + "ctuOneDefinitionViolation", + "danglingLifetime", + "duplInheritedMember", + "duplicateBreak", + "exceptThrowInDestructor", + "funcArgNamesDifferent", + "functionConst", + "functionStatic", + "missingReturn", + "noExplicit", + "overlappingWriteFunction", + "overlappingWriteUnion", + "pointerOutOfBounds", + "preprocessorErrorDirective", + "redundantAssignment", + "redundantInitialization", + "returnReference", + "returnTempReference", + "shadowVariable", + "shiftTooManyBits", + "sizeofSideEffects", + "throwInDestructor", + "uninitDerivedMemberVar", + "uninitDerivedMemberVarPrivate", + "uninitMemberVar", + "uninitMemberVarPrivate", + "uninitStructMember", + "uninitdata", + "uninitvar", + "unknownEvaluationOrder", + "unreachableCode", + "unreadVariable", + "unsignedLessThanZero", + "unusedFunction", + "unusedStructMember", + "unusedVariable", + "varScope", + "variableScope", + "virtualCallInConstructor" +}; + +bool Settings::isPremiumEnabled(const char id[]) const +{ + if (premiumArgs.find("autosar") != std::string::npos && autosarCheckers.count(id)) + return true; + if (premiumArgs.find("cert-c-") != std::string::npos && certCCheckers.count(id)) + return true; + if (premiumArgs.find("cert-c++") != std::string::npos && certCppCheckers.count(id)) + return true; + if (premiumArgs.find("misra-c-") != std::string::npos && (misrac2012Checkers.count(id) || misrac2023Checkers.count(id))) + return true; + if (premiumArgs.find("misra-c++") != std::string::npos && misracpp2008Checkers.count(id)) + return true; + return false; +} + +void Settings::setMisraRuleTexts(const ExecuteCmdFn& executeCommand) +{ + if (premiumArgs.find("--misra-c-20") != std::string::npos) { + const auto it = std::find_if(addonInfos.cbegin(), addonInfos.cend(), [](const AddonInfo& a) { + return a.name == "premiumaddon.json"; + }); + if (it != addonInfos.cend()) { + std::string arg; + if (premiumArgs.find("--misra-c-2023") != std::string::npos) + arg = "--misra-c-2023-rule-texts"; + else + arg = "--misra-c-2012-rule-texts"; + std::string output; + executeCommand(it->executable, {std::move(arg)}, "2>&1", output); + setMisraRuleTexts(output); + } + } +} + +void Settings::setMisraRuleTexts(const std::string& data) +{ + mMisraRuleTexts.clear(); + std::istringstream istr(data); + std::string line; + while (std::getline(istr, line)) { + std::string::size_type pos = line.find(' '); + if (pos == std::string::npos) + continue; + std::string id = line.substr(0, pos); + std::string text = line.substr(pos + 1); + if (id.empty() || text.empty()) + continue; + mMisraRuleTexts[id] = std::move(text); + } +} + +std::string Settings::getMisraRuleText(const std::string& id, const std::string& text) const { + if (id.compare(0, 9, "misra-c20") != 0) + return text; + const auto it = mMisraRuleTexts.find(id.substr(id.rfind('-') + 1)); + return it != mMisraRuleTexts.end() ? it->second : text; +} + +Settings::ExecutorType Settings::defaultExecutor() +{ + static constexpr ExecutorType defaultExecutor = +#if defined(HAS_THREADING_MODEL_FORK) + ExecutorType::Process; +#elif defined(HAS_THREADING_MODEL_THREAD) + ExecutorType::Thread; +#endif + return defaultExecutor; +} diff --git a/cppcheck-2.14.0/lib/settings.h b/cppcheck-2.14.0/lib/settings.h new file mode 100644 index 00000000..6cbceeef --- /dev/null +++ b/cppcheck-2.14.0/lib/settings.h @@ -0,0 +1,484 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "addoninfo.h" +#include "config.h" +#include "errortypes.h" +#include "library.h" +#include "platform.h" +#include "standards.h" +#include "suppressions.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum class SHOWTIME_MODES; +namespace ValueFlow { + class Value; +} + +/// @addtogroup Core +/// @{ + +template +class SimpleEnableGroup { + uint32_t mFlags = 0; +public: + uint32_t intValue() const { + return mFlags; + } + void clear() { + mFlags = 0; + } + void fill() { + mFlags = 0xFFFFFFFF; + } + bool isEnabled(T flag) const { + return (mFlags & (1U << (uint32_t)flag)) != 0; + } + void enable(T flag) { + mFlags |= (1U << (uint32_t)flag); + } + void enable(SimpleEnableGroup group) { + mFlags |= group.intValue(); + } + void disable(T flag) { + mFlags &= ~(1U << (uint32_t)flag); + } + void disable(SimpleEnableGroup group) { + mFlags &= ~(group.intValue()); + } + void setEnabled(T flag, bool enabled) { + if (enabled) + enable(flag); + else + disable(flag); + } +}; + + +/** + * @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 WARN_UNUSED Settings { +private: + + /** @brief terminate checking */ + static std::atomic mTerminated; + +public: + Settings(); + + static std::string loadCppcheckCfg(Settings& settings, Suppressions& suppressions); + + static std::pair getNameAndVersion(const std::string& productName); + + /** @brief addons, either filename of python/json file or json data */ + std::unordered_set addons; + + /** @brief the loaded addons infos */ + std::vector addonInfos; + + /** @brief Path to the python interpreter to be used to run addons. */ + std::string addonPython; + + /** @brief Paths used as base for conversion to relative paths. */ + std::vector basePaths; + + /** @brief --cppcheck-build-dir. Always uses / as path separator. No trailing path separator. */ + std::string buildDir; + + /** @brief check all configurations (false if -D or --max-configs is used */ + bool checkAllConfigurations = true; + + /** Is the 'configuration checking' wanted? */ + bool checkConfiguration{}; + + /** + * Check code in the headers, this is on by default but can + * be turned off to save CPU */ + bool checkHeaders = true; + + /** Check for incomplete info in library files? */ + bool checkLibrary{}; + + /** @brief The maximum time in seconds for the checks of a single file */ + int checksMaxTime{}; + + /** @brief --checkers-report= : Generate report of executed checkers */ + std::string checkersReportFilename; + + /** @brief check unknown function return values */ + std::set checkUnknownFunctionReturn; + + /** Check unused/uninstantiated templates */ + bool checkUnusedTemplates = true; + + /** Use Clang */ + bool clang{}; + + /** Custom Clang executable */ + std::string clangExecutable = "clang"; + + /** Use clang-tidy */ + bool clangTidy{}; + + /** Internal: Clear the simplecpp non-existing include cache */ + bool clearIncludeCache{}; + + /** @brief include paths excluded from checking the configuration */ + std::set configExcludePaths; + + /** cppcheck.cfg: Custom product name */ + std::string cppcheckCfgProductName; + + /** cppcheck.cfg: About text */ + std::string cppcheckCfgAbout; + + /** @brief Are we running from DACA script? */ + bool daca{}; + + /** @brief Is --debug-normal given? */ + bool debugnormal{}; + + /** @brief Is --debug-simplified given? */ + bool debugSimplified{}; + + /** @brief Is --debug-template given? */ + bool debugtemplate{}; + + /** @brief Is --debug-warnings given? */ + bool debugwarnings{}; + + /** @brief Is --dump given? */ + bool dump{}; + std::string dumpFile; + + /** @brief Name of the language that is enforced. Empty per default. */ + Standards::Language enforcedLang{}; + +#if defined(USE_WINDOWS_SEH) || defined(USE_UNIX_SIGNAL_HANDLING) + /** @brief Is --exception-handling given */ + bool exceptionHandling{}; +#endif + + enum class ExecutorType + { +#ifdef HAS_THREADING_MODEL_THREAD + Thread, +#endif +#ifdef HAS_THREADING_MODEL_FORK + Process +#endif + }; + + ExecutorType executor; + + // argv[0] + std::string exename; + + /** @brief If errors are found, this value is returned from main(). + Default value is 0. */ + int exitCode{}; + + /** @brief List of --file-filter for analyzing special files */ + std::vector fileFilters; + + /** @brief Force checking the files with "too many" configurations (--force). */ + bool force{}; + + /** @brief List of include paths, e.g. "my/includes/" which should be used + for finding include files inside source files. (-I) */ + std::list includePaths; + + /** @brief Is --inline-suppr given? */ + bool inlineSuppressions{}; + + /** @brief How many processes/threads should do checking at the same + time. Default is 1. (-j N) */ + unsigned int jobs = 1; + + /** @brief --library= */ + std::list libraries; + + /** Library */ + Library library; + + /** @brief Load average value */ + int loadAverage{}; + + /** @brief Maximum number of configurations to check before bailing. + Default is 12. (--max-configs=N) */ + int maxConfigs = 12; + + /** @brief --max-ctu-depth */ + int maxCtuDepth = 2; + + /** @brief max template recursion */ + int maxTemplateRecursion = 100; + + /** @brief write results (--output-file=<file>) */ + std::string outputFile; + + Platform platform; + + /** @brief Experimental: --performance-valueflow-max-time=T */ + int performanceValueFlowMaxTime = -1; + + /** @brief --performance-valueflow-max-if-count=C */ + int performanceValueFlowMaxIfCount = -1; + + /** @brief max number of sets of arguments to pass to subfuncions in valueflow */ + int performanceValueFlowMaxSubFunctionArgs = 256; + + /** @brief pid of cppcheck. Intention is that this is set in the main process. */ + int pid; + + /** @brief plist output (--plist-output=<dir>) */ + std::string plistOutput; + + /** @brief Extra arguments for Cppcheck Premium addon */ + std::string premiumArgs; + + /** Is checker id enabled by premiumArgs */ + bool isPremiumEnabled(const char id[]) const; + + /** @brief Using -E for debugging purposes */ + bool preprocessOnly{}; + + /** @brief Is --quiet given? */ + bool quiet{}; + + /** @brief Use relative paths in output. */ + bool relativePaths{}; + + /** @brief --report-progress */ + int reportProgress{-1}; + +#ifdef HAVE_RULES + /** Rule */ + struct CPPCHECKLIB Rule { + std::string tokenlist = "normal"; // use normal tokenlist + std::string pattern; + std::string id = "rule"; // default id + std::string summary; + Severity severity = Severity::style; // default severity + }; + + /** + * @brief Extra rules + */ + std::list rules; +#endif + + /** + * @brief Safety certified behavior + * Show checkers report when Cppcheck finishes + * Make cppcheck checking more strict about critical errors + * - returns nonzero if there is critical errors + * - a critical error id is not suppressed (by mistake?) with glob pattern + */ + bool safety = false; + + /** Do not only check how interface is used. Also check that interface is safe. */ + struct CPPCHECKLIB SafeChecks { + + static const char XmlRootName[]; + static const char XmlClasses[]; + static const char XmlExternalFunctions[]; + static const char XmlInternalFunctions[]; + static const char XmlExternalVariables[]; + + void clear() { + classes = externalFunctions = internalFunctions = externalVariables = false; + } + + /** + * Public interface of classes + * - public function parameters can have any value + * - public functions can be called in any order + * - public variables can have any value + */ + bool classes{}; + + /** + * External functions + * - external functions can be called in any order + * - function parameters can have any values + */ + bool externalFunctions{}; + + /** + * Experimental: assume that internal functions can be used in any way + * This is only available in the GUI. + */ + bool internalFunctions{}; + + /** + * Global variables that can be modified outside the TU. + * - Such variable can have "any" value + */ + bool externalVariables{}; + }; + + SafeChecks safeChecks; + + SimpleEnableGroup severity; + SimpleEnableGroup certainty; + SimpleEnableGroup checks; + + /** @brief show timing information (--showtime=file|summary|top5) */ + SHOWTIME_MODES showtime{}; + + /** Struct contains standards settings */ + Standards standards; + + /** @brief suppressions */ + Suppressions supprs; + + /** @brief The output format in which the errors are printed in text mode, + e.g. "{severity} {file}:{line} {message} {id}" */ + std::string templateFormat; + + /** @brief The output format in which the error locations are printed in + * text mode, e.g. "{file}:{line} {info}" */ + std::string templateLocation; + + /** @brief The maximum time in seconds for the template instantiation */ + std::size_t templateMaxTime{}; + + /** @brief The maximum time in seconds for the typedef simplification */ + std::size_t typedefMaxTime{}; + + /** @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 the maximum iterations of valueflow (--valueflow-max-iterations=T) */ + std::size_t valueFlowMaxIterations = 4; + + /** @brief Is --verbose given? */ + bool verbose{}; + + /** @brief write XML results (--xml) */ + bool xml{}; + + /** @brief XML version (--xml-version=..) */ + int xml_version = 2; + + /** + * @brief return true if a included file is to be excluded in Preprocessor::getConfigs + * @return true for the file to be excluded. + */ + bool configurationExcluded(const std::string &file) const { + return std::any_of(configExcludePaths.begin(), configExcludePaths.end(), [&file](const std::string& path) { + return file.length() >= path.length() && file.compare(0, path.length(), path) == 0; + }); + } + + /** + * @brief Enable extra checks by id. See isEnabled() + * @param str single id or list of id values to be enabled + * or empty string to enable all. e.g. "style,possibleError" + * @return error message. empty upon success + */ + std::string addEnabled(const std::string &str); + + /** + * @brief Disable extra checks by id + * @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 removeEnabled(const std::string &str); + + /** + * @brief Returns true if given value can be shown + * @return true if the value can be shown + */ + bool isEnabled(const ValueFlow::Value *value, bool inconclusiveCheck=false) const; + + /** Is library specified? */ + bool hasLib(const std::string &lib) const { + return std::find(libraries.cbegin(), libraries.cend(), lib) != libraries.cend(); + } + + /** @brief Request termination of checking */ + static void terminate(bool t = true) { + Settings::mTerminated = t; + } + + /** @brief termination requested? */ + static bool terminated() { + return Settings::mTerminated; + } + + std::set summaryReturn; + + void loadSummaries(); + + bool useSingleJob() const { + return jobs == 1; + } + + enum class CheckLevel { + normal, + exhaustive + }; + CheckLevel checkLevel = CheckLevel::exhaustive; + + void setCheckLevel(CheckLevel level); + + using ExecuteCmdFn = std::function,std::string,std::string&)>; + void setMisraRuleTexts(const ExecuteCmdFn& executeCommand); + void setMisraRuleTexts(const std::string& data); + std::string getMisraRuleText(const std::string& id, const std::string& text) const; + + static ExecutorType defaultExecutor(); + +private: + static std::string parseEnabled(const std::string &str, std::tuple, SimpleEnableGroup> &groups); + std::string applyEnabled(const std::string &str, bool enable); + std::map mMisraRuleTexts; +}; + +/// @} +//--------------------------------------------------------------------------- +#endif // settingsH diff --git a/cppcheck-2.14.0/lib/smallvector.h b/cppcheck-2.14.0/lib/smallvector.h new file mode 100644 index 00000000..b09a016d --- /dev/null +++ b/cppcheck-2.14.0/lib/smallvector.h @@ -0,0 +1,71 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef smallvectorH +#define smallvectorH + +#include + +static constexpr std::size_t DefaultSmallVectorSize = 3; + +#ifdef HAVE_BOOST +#include + +template +using SmallVector = boost::container::small_vector; +#else +#include +#include + +template +struct TaggedAllocator : std::allocator +{ + template + // cppcheck-suppress noExplicitConstructor + // NOLINTNEXTLINE(google-explicit-constructor) + TaggedAllocator(Ts&&... ts) + : std::allocator(std::forward(ts)...) + {} + + template + // cppcheck-suppress noExplicitConstructor + // NOLINTNEXTLINE(google-explicit-constructor) + TaggedAllocator(const TaggedAllocator /*unused*/) {} + + template + struct rebind + { + using other = TaggedAllocator; + }; +}; + +template +class SmallVector : public std::vector> +{ +public: + template + // NOLINTNEXTLINE(google-explicit-constructor) + SmallVector(Ts&&... ts) + : std::vector>(std::forward(ts)...) + { + this->reserve(N); + } +}; +#endif + +#endif diff --git a/cppcheck-2.14.0/lib/sourcelocation.h b/cppcheck-2.14.0/lib/sourcelocation.h new file mode 100644 index 00000000..759f30d1 --- /dev/null +++ b/cppcheck-2.14.0/lib/sourcelocation.h @@ -0,0 +1,98 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef sourcelocationH +#define sourcelocationH + +#include "config.h" + +#ifdef __CPPCHECK__ +#define CPPCHECK_HAS_SOURCE_LOCATION 0 +#define CPPCHECK_HAS_SOURCE_LOCATION_TS 0 +#define CPPCHECK_HAS_SOURCE_LOCATION_INTRINSICS 0 +#else + +#if __has_include() && __cplusplus >= 202003L +#define CPPCHECK_HAS_SOURCE_LOCATION 1 +#else +#define CPPCHECK_HAS_SOURCE_LOCATION 0 +#endif +#if __has_include() && __cplusplus >= 201402L +#define CPPCHECK_HAS_SOURCE_LOCATION_TS 1 +#else +#define CPPCHECK_HAS_SOURCE_LOCATION_TS 0 +#endif + +#if __has_builtin(__builtin_FILE) +#define CPPCHECK_HAS_SOURCE_LOCATION_INTRINSICS 1 +#if !__has_builtin(__builtin_COLUMN) +#define __builtin_COLUMN() 0 +#endif +#else +#define CPPCHECK_HAS_SOURCE_LOCATION_INTRINSICS 0 +#endif + +#endif + +#if CPPCHECK_HAS_SOURCE_LOCATION +#include +using SourceLocation = std::source_location; +#elif CPPCHECK_HAS_SOURCE_LOCATION_TS +#include +using SourceLocation = std::experimental::source_location; +#else +#include +struct SourceLocation { +#if CPPCHECK_HAS_SOURCE_LOCATION_INTRINSICS + static SourceLocation current(std::uint_least32_t line = __builtin_LINE(), + std::uint_least32_t column = __builtin_COLUMN(), + const char* file_name = __builtin_FILE(), + const char* function_name = __builtin_FUNCTION()) + { + SourceLocation result{}; + result.m_line = line; + result.m_column = column; + result.m_file_name = file_name; + result.m_function_name = function_name; + return result; + } +#else + static SourceLocation current() { + return SourceLocation(); + } +#endif + std::uint_least32_t m_line = 0; + std::uint_least32_t m_column = 0; + const char* m_file_name = ""; + const char* m_function_name = ""; + std::uint_least32_t line() const { + return m_line; + } + std::uint_least32_t column() const { + return m_column; + } + const char* file_name() const { + return m_file_name; + } + const char* function_name() const { + return m_function_name; + } +}; +#endif + +#endif diff --git a/cppcheck-2.14.0/lib/standards.h b/cppcheck-2.14.0/lib/standards.h new file mode 100644 index 00000000..281a20b9 --- /dev/null +++ b/cppcheck-2.14.0/lib/standards.h @@ -0,0 +1,138 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef standardsH +#define standardsH +//--------------------------------------------------------------------------- + +#include "utils.h" + +#include + +/// @addtogroup Core +/// @{ + + +/** + * @brief This is just a container for standards settings. + * This struct contains all possible standards that cppcheck recognize. + */ +struct Standards { + enum Language { None, C, CPP }; + + /** C code standard */ + enum cstd_t { C89, C99, C11, CLatest = C11 } c = CLatest; + + /** C++ code standard */ + enum cppstd_t { CPP03, CPP11, CPP14, CPP17, CPP20, CPP23, CPPLatest = CPP23 } cpp = CPPLatest; + + /** --std value given on command line */ + std::string stdValue; + + bool setC(const std::string& str) { + stdValue = str; + if (str == "c89" || str == "C89") { + c = C89; + return true; + } + if (str == "c99" || str == "C99") { + c = C99; + return true; + } + if (str == "c11" || str == "C11") { + c = C11; + return true; + } + return false; + } + std::string getC() const { + switch (c) { + case C89: + return "c89"; + case C99: + return "c99"; + case C11: + return "c11"; + } + return ""; + } + static cstd_t getC(const std::string &std) { + if (std == "c89") { + return Standards::C89; + } + if (std == "c99") { + return Standards::C99; + } + if (std == "c11") { + return Standards::C11; + } + return Standards::CLatest; + } + bool setCPP(std::string str) { + stdValue = str; + strTolower(str); + cpp = getCPP(str); + return !stdValue.empty() && str == getCPP(); + } + std::string getCPP() const { + return getCPP(cpp); + } + static std::string getCPP(cppstd_t std) { + switch (std) { + case CPP03: + return "c++03"; + case CPP11: + return "c++11"; + case CPP14: + return "c++14"; + case CPP17: + return "c++17"; + case CPP20: + return "c++20"; + case CPP23: + return "c++23"; + } + return ""; + } + static cppstd_t getCPP(const std::string &std) { + if (std == "c++03") { + return Standards::CPP03; + } + if (std == "c++11") { + return Standards::CPP11; + } + if (std == "c++14") { + return Standards::CPP14; + } + if (std == "c++17") { + return Standards::CPP17; + } + if (std == "c++20") { + return Standards::CPP20; + } + if (std == "c++23") { + return Standards::CPP23; + } + return Standards::CPPLatest; + } +}; + +/// @} +//--------------------------------------------------------------------------- +#endif // standardsH diff --git a/cppcheck-2.14.0/lib/summaries.cpp b/cppcheck-2.14.0/lib/summaries.cpp new file mode 100644 index 00000000..a893b13a --- /dev/null +++ b/cppcheck-2.14.0/lib/summaries.cpp @@ -0,0 +1,196 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "summaries.h" + +#include "analyzerinfo.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "tokenlist.h" + +#include +#include +#include +#include +#include +#include + + + +std::string Summaries::create(const Tokenizer &tokenizer, const std::string &cfg) +{ + const SymbolDatabase *symbolDatabase = tokenizer.getSymbolDatabase(); + const Settings &settings = tokenizer.getSettings(); + + std::ostringstream ostr; + for (const Scope *scope : symbolDatabase->functionScopes) { + const Function *f = scope->function; + if (!f) + continue; + + // Summarize function + std::set noreturn; + std::set globalVars; + std::set calledFunctions; + for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (tok->variable() && tok->variable()->isGlobal()) + globalVars.insert(tok->variable()->name()); + if (Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) { + calledFunctions.insert(tok->str()); + if (Token::simpleMatch(tok->linkAt(1), ") ; }")) + noreturn.insert(tok->str()); + } + } + + // Write summary for function + auto join = [](const std::set &data) -> std::string { + std::string ret; + const char *sep = ""; + for (const std::string &d: data) + { + ret += sep + d; + sep = ","; + } + return ret; + }; + + ostr << f->name(); + if (!globalVars.empty()) + ostr << " global:[" << join(globalVars) << "]"; + if (!calledFunctions.empty()) + ostr << " call:[" << join(calledFunctions) << "]"; + if (!noreturn.empty()) + ostr << " noreturn:[" << join(noreturn) << "]"; + ostr << std::endl; + } + + if (!settings.buildDir.empty()) { + std::string filename = AnalyzerInformation::getAnalyzerInfoFile(settings.buildDir, tokenizer.list.getSourceFilePath(), cfg); + const std::string::size_type pos = filename.rfind(".a"); + if (pos != std::string::npos) { + filename[pos+1] = 's'; + std::ofstream fout(filename); + fout << ostr.str(); + } + } + + return ostr.str(); +} + + + + +static std::vector getSummaryFiles(const std::string &filename) +{ + std::vector ret; + std::ifstream fin(filename); + if (!fin.is_open()) + return ret; + std::string line; + while (std::getline(fin, line)) { + const std::string::size_type dotA = line.find(".a"); + const std::string::size_type colon = line.find(':'); + if (colon > line.size() || dotA > colon) + continue; + std::string f = line.substr(0,colon); + f[dotA + 1] = 's'; + ret.push_back(std::move(f)); + } + return ret; +} + +static std::vector getSummaryData(const std::string &line, const std::string &data) +{ + std::vector ret; + const std::string::size_type start = line.find(" " + data + ":["); + if (start == std::string::npos) + return ret; + const std::string::size_type end = line.find(']', start); + if (end >= line.size()) + return ret; + + std::string::size_type pos1 = start + 3 + data.size(); + while (pos1 < end) { + const std::string::size_type pos2 = line.find_first_of(",]",pos1); + ret.push_back(line.substr(pos1, pos2-pos1-1)); + pos1 = pos2 + 1; + } + + return ret; +} + +static void removeFunctionCalls(const std::string& calledFunction, + std::map> &functionCalledBy, + std::map> &functionCalls, + std::vector &add) +{ + std::vector calledBy = functionCalledBy[calledFunction]; + functionCalledBy.erase(calledFunction); + for (const std::string &c: calledBy) { + std::vector &calls = functionCalls[c]; + calls.erase(std::remove(calls.begin(), calls.end(), calledFunction), calls.end()); + if (calls.empty()) { + add.push_back(calledFunction); + removeFunctionCalls(c, functionCalledBy, functionCalls, add); + } + } +} + +void Summaries::loadReturn(const std::string &buildDir, std::set &summaryReturn) +{ + if (buildDir.empty()) + return; + + std::vector return1; + std::map> functionCalls; + std::map> functionCalledBy; + + // extract "functionNoreturn" and "functionCalledBy" from summaries + std::vector summaryFiles = getSummaryFiles(buildDir + "/files.txt"); + for (const std::string &filename: summaryFiles) { + std::ifstream fin(buildDir + '/' + filename); + if (!fin.is_open()) + continue; + std::string line; + while (std::getline(fin, line)) { + // Get function name + constexpr std::string::size_type pos1 = 0; + const std::string::size_type pos2 = line.find(' ', pos1); + const std::string functionName = (pos2 == std::string::npos) ? line : line.substr(0, pos2); + std::vector call = getSummaryData(line, "call"); + functionCalls[functionName] = call; + if (call.empty()) + return1.push_back(functionName); + else { + for (const std::string &c: call) { + functionCalledBy[c].push_back(functionName); + } + } + } + } + summaryReturn.insert(return1.cbegin(), return1.cend()); + + // recursively set "summaryNoreturn" + for (const std::string &f: return1) { + std::vector return2; + removeFunctionCalls(f, functionCalledBy, functionCalls, return2); + summaryReturn.insert(return2.cbegin(), return2.cend()); + } +} diff --git a/cppcheck-2.14.0/lib/summaries.h b/cppcheck-2.14.0/lib/summaries.h new file mode 100644 index 00000000..b15cc5e9 --- /dev/null +++ b/cppcheck-2.14.0/lib/summaries.h @@ -0,0 +1,38 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef summariesH +#define summariesH +//--------------------------------------------------------------------------- + +#include "config.h" + +#include +#include + +class Tokenizer; + +namespace Summaries { + CPPCHECKLIB std::string create(const Tokenizer &tokenizer, const std::string &cfg); + CPPCHECKLIB void loadReturn(const std::string &buildDir, std::set &summaryReturn); +} + +//--------------------------------------------------------------------------- +#endif +//--------------------------------------------------------------------------- diff --git a/cppcheck-2.14.0/lib/suppressions.cpp b/cppcheck-2.14.0/lib/suppressions.cpp new file mode 100644 index 00000000..5986367b --- /dev/null +++ b/cppcheck-2.14.0/lib/suppressions.cpp @@ -0,0 +1,579 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "suppressions.h" + +#include "errorlogger.h" +#include "errortypes.h" +#include "path.h" +#include "utils.h" +#include "token.h" +#include "tokenize.h" +#include "tokenlist.h" + +#include +#include // std::isdigit, std::isalnum, etc +#include +#include // std::bind, std::placeholders +#include +#include + +#include "xml.h" + +static const char ID_UNUSEDFUNCTION[] = "unusedFunction"; +static const char ID_CHECKERSREPORT[] = "checkersReport"; + +SuppressionList::ErrorMessage SuppressionList::ErrorMessage::fromErrorMessage(const ::ErrorMessage &msg, const std::set ¯oNames) +{ + SuppressionList::ErrorMessage ret; + ret.hash = msg.hash; + ret.errorId = msg.id; + if (!msg.callStack.empty()) { + ret.setFileName(msg.callStack.back().getfile(false)); + ret.lineNumber = msg.callStack.back().line; + } else { + ret.lineNumber = SuppressionList::Suppression::NO_LINE; + } + ret.certainty = msg.certainty; + ret.symbolNames = msg.symbolNames(); + ret.macroNames = macroNames; + return ret; +} + +static bool isAcceptedErrorIdChar(char c) +{ + switch (c) { + case '_': + case '-': + case '.': + case '*': + return true; + default: + return c > 0 && std::isalnum(c); + } +} + +std::string SuppressionList::parseFile(std::istream &istr) +{ + // Change '\r' to '\n' in the istr + std::string filedata; + std::string line; + while (std::getline(istr, line)) + filedata += line + "\n"; + std::replace(filedata.begin(), filedata.end(), '\r', '\n'); + + // Parse filedata.. + std::istringstream istr2(filedata); + while (std::getline(istr2, line)) { + // Skip empty lines + if (line.empty()) + continue; + + // Skip comments + if (line.length() > 1 && line[0] == '#') + continue; + if (line.length() >= 2 && line[0] == '/' && line[1] == '/') + continue; + + const std::string errmsg(addSuppressionLine(line)); + if (!errmsg.empty()) + return errmsg; + } + + return ""; +} + + +std::string SuppressionList::parseXmlFile(const char *filename) +{ + tinyxml2::XMLDocument doc; + const tinyxml2::XMLError error = doc.LoadFile(filename); + if (error != tinyxml2::XML_SUCCESS) + return std::string("failed to load suppressions XML '") + filename + "' (" + tinyxml2::XMLDocument::ErrorIDToName(error) + ")."; + + const tinyxml2::XMLElement * const rootnode = doc.FirstChildElement(); + if (!rootnode) + return std::string("failed to load suppressions XML '") + filename + "' (no root node found)."; + // TODO: check for proper root node 'suppressions' + for (const tinyxml2::XMLElement * e = rootnode->FirstChildElement(); e; e = e->NextSiblingElement()) { + if (std::strcmp(e->Name(), "suppress") != 0) + return std::string("invalid suppression xml file '") + filename + "', expected 'suppress' element but got a '" + e->Name() + "'."; + + Suppression s; + for (const tinyxml2::XMLElement * e2 = e->FirstChildElement(); e2; e2 = e2->NextSiblingElement()) { + const char *text = e2->GetText() ? e2->GetText() : ""; + if (std::strcmp(e2->Name(), "id") == 0) + s.errorId = text; + else if (std::strcmp(e2->Name(), "fileName") == 0) + s.fileName = text; + else if (std::strcmp(e2->Name(), "lineNumber") == 0) + s.lineNumber = strToInt(text); + else if (std::strcmp(e2->Name(), "symbolName") == 0) + s.symbolName = text; + else if (*text && std::strcmp(e2->Name(), "hash") == 0) + s.hash = strToInt(text); + else + return std::string("unknown element '") + e2->Name() + "' in suppressions XML '" + filename + "', expected id/fileName/lineNumber/symbolName/hash."; + } + + const std::string err = addSuppression(std::move(s)); + if (!err.empty()) + return err; + } + + return ""; +} + +std::vector SuppressionList::parseMultiSuppressComment(const std::string &comment, std::string *errorMessage) +{ + std::vector suppressions; + + // If this function is called we assume that comment starts with "cppcheck-suppress[". + const std::string::size_type start_position = comment.find('['); + const std::string::size_type end_position = comment.find(']', start_position); + if (end_position == std::string::npos) { + if (errorMessage && errorMessage->empty()) + *errorMessage = "Bad multi suppression '" + comment + "'. legal format is cppcheck-suppress[errorId, errorId symbolName=arr, ...]"; + return suppressions; + } + + // parse all suppressions + for (std::string::size_type pos = start_position; pos < end_position;) { + const std::string::size_type pos1 = pos + 1; + pos = comment.find(',', pos1); + const std::string::size_type pos2 = (pos < end_position) ? pos : end_position; + if (pos1 == pos2) + continue; + + Suppression s; + std::istringstream iss(comment.substr(pos1, pos2-pos1)); + + iss >> s.errorId; + if (!iss) { + if (errorMessage && errorMessage->empty()) + *errorMessage = "Bad multi suppression '" + comment + "'. legal format is cppcheck-suppress[errorId, errorId symbolName=arr, ...]"; + suppressions.clear(); + return suppressions; + } + + const std::string symbolNameString = "symbolName="; + + while (iss) { + std::string word; + iss >> word; + if (!iss) + break; + if (word.find_first_not_of("+-*/%#;") == std::string::npos) + break; + if (startsWith(word, symbolNameString)) { + s.symbolName = word.substr(symbolNameString.size()); + } else { + if (errorMessage && errorMessage->empty()) + *errorMessage = "Bad multi suppression '" + comment + "'. legal format is cppcheck-suppress[errorId, errorId symbolName=arr, ...]"; + suppressions.clear(); + return suppressions; + } + } + + suppressions.push_back(std::move(s)); + } + + return suppressions; +} + +std::string SuppressionList::addSuppressionLine(const std::string &line) +{ + std::istringstream lineStream; + SuppressionList::Suppression suppression; + + // Strip any end of line comments + std::string::size_type endpos = std::min(line.find('#'), line.find("//")); + if (endpos != std::string::npos) { + while (endpos > 0 && std::isspace(line[endpos-1])) { + endpos--; + } + lineStream.str(line.substr(0, endpos)); + } else { + lineStream.str(line); + } + + if (std::getline(lineStream, suppression.errorId, ':')) { + if (std::getline(lineStream, suppression.fileName)) { + // If there is not a dot after the last colon in "file" then + // the colon is a separator and the contents after the colon + // is a line number.. + + // Get position of last colon + const std::string::size_type pos = suppression.fileName.rfind(':'); + + // if a colon is found and there is no dot after it.. + if (pos != std::string::npos && + suppression.fileName.find('.', pos) == std::string::npos) { + // Try to parse out the line number + try { + std::istringstream istr1(suppression.fileName.substr(pos+1)); + istr1 >> suppression.lineNumber; + } catch (...) { + suppression.lineNumber = SuppressionList::Suppression::NO_LINE; + } + + if (suppression.lineNumber != SuppressionList::Suppression::NO_LINE) { + suppression.fileName.erase(pos); + } + } + } + } + + suppression.fileName = Path::simplifyPath(suppression.fileName); + + return addSuppression(std::move(suppression)); +} + +std::string SuppressionList::addSuppression(SuppressionList::Suppression suppression) +{ + // Check if suppression is already in list + auto foundSuppression = std::find_if(mSuppressions.begin(), mSuppressions.end(), + std::bind(&Suppression::isSameParameters, &suppression, std::placeholders::_1)); + if (foundSuppression != mSuppressions.end()) { + // Update matched state of existing global suppression + if (!suppression.isLocal() && suppression.matched) + foundSuppression->matched = suppression.matched; + return ""; + } + + // Check that errorId is valid.. + if (suppression.errorId.empty() && suppression.hash == 0) + return "Failed to add suppression. No id."; + + for (std::string::size_type pos = 0; pos < suppression.errorId.length(); ++pos) { + if (!isAcceptedErrorIdChar(suppression.errorId[pos])) { + return "Failed to add suppression. Invalid id \"" + suppression.errorId + "\""; + } + if (pos == 0 && std::isdigit(suppression.errorId[pos])) { + return "Failed to add suppression. Invalid id \"" + suppression.errorId + "\""; + } + } + + if (!isValidGlobPattern(suppression.errorId)) + return "Failed to add suppression. Invalid glob pattern '" + suppression.errorId + "'."; + if (!isValidGlobPattern(suppression.fileName)) + return "Failed to add suppression. Invalid glob pattern '" + suppression.fileName + "'."; + + mSuppressions.push_back(std::move(suppression)); + + return ""; +} + +std::string SuppressionList::addSuppressions(std::list suppressions) +{ + for (auto &newSuppression : suppressions) { + auto errmsg = addSuppression(std::move(newSuppression)); + if (!errmsg.empty()) + return errmsg; + } + return ""; +} + +void SuppressionList::ErrorMessage::setFileName(std::string s) +{ + mFileName = Path::simplifyPath(std::move(s)); +} + +bool SuppressionList::Suppression::parseComment(std::string comment, std::string *errorMessage) +{ + if (comment.size() < 2) + return false; + + if (comment.find(';') != std::string::npos) + comment.erase(comment.find(';')); + + if (comment.find("//", 2) != std::string::npos) + comment.erase(comment.find("//",2)); + + if (comment.compare(comment.size() - 2, 2, "*/") == 0) + comment.erase(comment.size() - 2, 2); + + const std::set cppchecksuppress{ + "cppcheck-suppress", + "cppcheck-suppress-begin", + "cppcheck-suppress-end", + "cppcheck-suppress-file", + "cppcheck-suppress-macro" + }; + + std::istringstream iss(comment.substr(2)); + std::string word; + iss >> word; + if (!cppchecksuppress.count(word)) + return false; + + iss >> errorId; + if (!iss) + return false; + + const std::string symbolNameString = "symbolName="; + + while (iss) { + iss >> word; + if (!iss) + break; + if (word.find_first_not_of("+-*/%#;") == std::string::npos) + break; + if (startsWith(word, symbolNameString)) + symbolName = word.substr(symbolNameString.size()); + else if (errorMessage && errorMessage->empty()) + *errorMessage = "Bad suppression attribute '" + word + "'. You can write comments in the comment after a ; or //. Valid suppression attributes; symbolName=sym"; + } + return true; +} + +bool SuppressionList::Suppression::isSuppressed(const SuppressionList::ErrorMessage &errmsg) const +{ + if (hash > 0 && hash != errmsg.hash) + return false; + if (!errorId.empty() && !matchglob(errorId, errmsg.errorId)) + return false; + if (type == SuppressionList::Type::macro) { + if (errmsg.macroNames.count(macroName) == 0) + return false; + } else { + if (!fileName.empty() && !matchglob(fileName, errmsg.getFileName())) + return false; + if ((SuppressionList::Type::unique == type) && (lineNumber != NO_LINE) && (lineNumber != errmsg.lineNumber)) { + if (!thisAndNextLine || lineNumber + 1 != errmsg.lineNumber) + return false; + } + if ((SuppressionList::Type::block == type) && ((errmsg.lineNumber < lineBegin) || (errmsg.lineNumber > lineEnd))) + return false; + } + if (!symbolName.empty()) { + for (std::string::size_type pos = 0; pos < errmsg.symbolNames.size();) { + const std::string::size_type pos2 = errmsg.symbolNames.find('\n',pos); + std::string symname; + if (pos2 == std::string::npos) { + symname = errmsg.symbolNames.substr(pos); + pos = pos2; + } else { + symname = errmsg.symbolNames.substr(pos,pos2-pos); + pos = pos2+1; + } + if (matchglob(symbolName, symname)) + return true; + } + return false; + } + return true; +} + +bool SuppressionList::Suppression::isMatch(const SuppressionList::ErrorMessage &errmsg) +{ + if (!isSuppressed(errmsg)) + return false; + matched = true; + checked = true; + return true; +} + +std::string SuppressionList::Suppression::getText() const +{ + std::string ret; + if (!errorId.empty()) + ret = errorId; + if (!fileName.empty()) + ret += " fileName=" + fileName; + if (lineNumber != NO_LINE) + ret += " lineNumber=" + std::to_string(lineNumber); + if (!symbolName.empty()) + ret += " symbolName=" + symbolName; + if (hash > 0) + ret += " hash=" + std::to_string(hash); + if (startsWith(ret," ")) + return ret.substr(1); + return ret; +} + +bool SuppressionList::isSuppressed(const SuppressionList::ErrorMessage &errmsg, bool global) +{ + const bool unmatchedSuppression(errmsg.errorId == "unmatchedSuppression"); + bool returnValue = false; + for (Suppression &s : mSuppressions) { + if (!global && !s.isLocal()) + continue; + if (unmatchedSuppression && s.errorId != errmsg.errorId) + continue; + if (s.isMatch(errmsg)) + returnValue = true; + } + return returnValue; +} + +bool SuppressionList::isSuppressedExplicitly(const SuppressionList::ErrorMessage &errmsg, bool global) +{ + for (Suppression &s : mSuppressions) { + if (!global && !s.isLocal()) + continue; + if (s.errorId != errmsg.errorId) // Error id must match exactly + continue; + if (s.isMatch(errmsg)) + return true; + } + return false; +} + +bool SuppressionList::isSuppressed(const ::ErrorMessage &errmsg, const std::set& macroNames) +{ + if (mSuppressions.empty()) + return false; + return isSuppressed(SuppressionList::ErrorMessage::fromErrorMessage(errmsg, macroNames)); +} + +void SuppressionList::dump(std::ostream & out) const +{ + out << " " << std::endl; + for (const Suppression &suppression : mSuppressions) { + out << " 0) + out << " hash=\"" << suppression.hash << '\"'; + if (suppression.lineBegin != Suppression::NO_LINE) + out << " lineBegin=\"" << suppression.lineBegin << '"'; + if (suppression.lineEnd != Suppression::NO_LINE) + out << " lineEnd=\"" << suppression.lineEnd << '"'; + if (suppression.type == SuppressionList::Type::file) + out << " type=\"file\""; + else if (suppression.type == SuppressionList::Type::block) + out << " type=\"block\""; + else if (suppression.type == SuppressionList::Type::blockBegin) + out << " type=\"blockBegin\""; + else if (suppression.type == SuppressionList::Type::blockEnd) + out << " type=\"blockEnd\""; + else if (suppression.type == SuppressionList::Type::macro) + out << " type=\"macro\""; + out << " />" << std::endl; + } + out << " " << std::endl; +} + +std::list SuppressionList::getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const +{ + std::string tmpFile = Path::simplifyPath(file); + std::list result; + for (const Suppression &s : mSuppressions) { + if (s.matched || ((s.lineNumber != Suppression::NO_LINE) && !s.checked)) + continue; + if (s.type == SuppressionList::Type::macro) + continue; + if (s.hash > 0) + continue; + if (s.errorId == ID_CHECKERSREPORT) + continue; + if (!unusedFunctionChecking && s.errorId == ID_UNUSEDFUNCTION) + continue; + if (tmpFile.empty() || !s.isLocal() || s.fileName != tmpFile) + continue; + result.push_back(s); + } + return result; +} + +std::list SuppressionList::getUnmatchedGlobalSuppressions(const bool unusedFunctionChecking) const +{ + std::list result; + for (const Suppression &s : mSuppressions) { + if (s.matched || ((s.lineNumber != Suppression::NO_LINE) && !s.checked)) + continue; + if (s.hash > 0) + continue; + if (!unusedFunctionChecking && s.errorId == ID_UNUSEDFUNCTION) + continue; + if (s.errorId == ID_CHECKERSREPORT) + continue; + if (s.isLocal()) + continue; + result.push_back(s); + } + return result; +} + +const std::list &SuppressionList::getSuppressions() const +{ + return mSuppressions; +} + +void SuppressionList::markUnmatchedInlineSuppressionsAsChecked(const Tokenizer &tokenizer) { + int currLineNr = -1; + int currFileIdx = -1; + for (const Token *tok = tokenizer.tokens(); tok; tok = tok->next()) { + if (currFileIdx != tok->fileIndex() || currLineNr != tok->linenr()) { + currLineNr = tok->linenr(); + currFileIdx = tok->fileIndex(); + for (auto &suppression : mSuppressions) { + if (suppression.type == SuppressionList::Type::unique) { + if (!suppression.checked && (suppression.lineNumber == currLineNr) && (suppression.fileName == tokenizer.list.file(tok))) { + suppression.checked = true; + } + } else if (suppression.type == SuppressionList::Type::block) { + if ((!suppression.checked && (suppression.lineBegin <= currLineNr) && (suppression.lineEnd >= currLineNr) && (suppression.fileName == tokenizer.list.file(tok)))) { + suppression.checked = true; + } + } else if (!suppression.checked && suppression.fileName == tokenizer.list.file(tok)) { + suppression.checked = true; + } + } + } + } +} + +bool SuppressionList::reportUnmatchedSuppressions(const std::list &unmatched, ErrorLogger &errorLogger) +{ + bool err = false; + // Report unmatched suppressions + for (const SuppressionList::Suppression &s : unmatched) { + // don't report "unmatchedSuppression" as unmatched + if (s.errorId == "unmatchedSuppression") + continue; + + // check if this unmatched suppression is suppressed + bool suppressed = false; + for (const SuppressionList::Suppression &s2 : unmatched) { + if (s2.errorId == "unmatchedSuppression") { + if ((s2.fileName.empty() || s2.fileName == "*" || s2.fileName == s.fileName) && + (s2.lineNumber == SuppressionList::Suppression::NO_LINE || s2.lineNumber == s.lineNumber)) { + suppressed = true; + break; + } + } + } + + if (suppressed) + continue; + + std::list<::ErrorMessage::FileLocation> callStack; + if (!s.fileName.empty()) + callStack.emplace_back(s.fileName, s.lineNumber, 0); + errorLogger.reportErr(::ErrorMessage(std::move(callStack), emptyString, Severity::information, "Unmatched suppression: " + s.errorId, "unmatchedSuppression", Certainty::normal)); + err = true; + } + return err; +} diff --git a/cppcheck-2.14.0/lib/suppressions.h b/cppcheck-2.14.0/lib/suppressions.h new file mode 100644 index 00000000..98a582a9 --- /dev/null +++ b/cppcheck-2.14.0/lib/suppressions.h @@ -0,0 +1,271 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +//--------------------------------------------------------------------------- +#ifndef suppressionsH +#define suppressionsH +//--------------------------------------------------------------------------- + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +/// @addtogroup Core +/// @{ + +class Tokenizer; +class ErrorMessage; +class ErrorLogger; +enum class Certainty; + +/** @brief class for handling suppressions */ +class CPPCHECKLIB SuppressionList { +public: + + enum class Type { + unique, file, block, blockBegin, blockEnd, macro + }; + + struct CPPCHECKLIB ErrorMessage { + std::size_t hash; + std::string errorId; + void setFileName(std::string s); + const std::string &getFileName() const { + return mFileName; + } + int lineNumber; + Certainty certainty; + std::string symbolNames; + std::set macroNames; + + static SuppressionList::ErrorMessage fromErrorMessage(const ::ErrorMessage &msg, const std::set ¯oNames); + private: + std::string mFileName; + }; + + struct CPPCHECKLIB Suppression { + Suppression() = default; + Suppression(std::string id, std::string file, int line=NO_LINE) : errorId(std::move(id)), fileName(std::move(file)), lineNumber(line) {} + + bool operator<(const Suppression &other) const { + if (errorId != other.errorId) + return errorId < other.errorId; + if (lineNumber < other.lineNumber) + return true; + if (fileName != other.fileName) + return fileName < other.fileName; + if (symbolName != other.symbolName) + return symbolName < other.symbolName; + if (macroName != other.macroName) + return macroName < other.macroName; + if (hash != other.hash) + return hash < other.hash; + if (thisAndNextLine != other.thisAndNextLine) + return thisAndNextLine; + return false; + } + + bool operator==(const Suppression &other) const { + if (errorId != other.errorId) + return false; + if (lineNumber < other.lineNumber) + return false; + if (fileName != other.fileName) + return false; + if (symbolName != other.symbolName) + return false; + if (macroName != other.macroName) + return false; + if (hash != other.hash) + return false; + if (type != other.type) + return false; + if (lineBegin != other.lineBegin) + return false; + if (lineEnd != other.lineEnd) + return false; + return true; + } + + /** + * Parse inline suppression in comment + * @param comment the full comment text + * @param errorMessage output parameter for error message (wrong suppression attribute) + * @return true if it is a inline comment. + */ + bool parseComment(std::string comment, std::string *errorMessage); + + bool isSuppressed(const ErrorMessage &errmsg) const; + + bool isMatch(const ErrorMessage &errmsg); + + std::string getText() const; + + bool isLocal() const { + return !fileName.empty() && fileName.find_first_of("?*") == std::string::npos; + } + + bool isSameParameters(const Suppression &other) const { + return errorId == other.errorId && + fileName == other.fileName && + lineNumber == other.lineNumber && + symbolName == other.symbolName && + hash == other.hash && + thisAndNextLine == other.thisAndNextLine; + } + + std::string errorId; + std::string fileName; + int lineNumber = NO_LINE; + int lineBegin = NO_LINE; + int lineEnd = NO_LINE; + Type type = Type::unique; + std::string symbolName; + std::string macroName; + std::size_t hash{}; + bool thisAndNextLine{}; // Special case for backwards compatibility: { // cppcheck-suppress something + bool matched{}; + bool checked{}; // for inline suppressions, checked or not + + enum { NO_LINE = -1 }; + }; + + /** + * @brief Don't show errors listed in the file. + * @param istr Open file stream where errors can be read. + * @return error message. empty upon success + */ + std::string parseFile(std::istream &istr); + + /** + * @brief Don't show errors listed in the file. + * @param filename file name + * @return error message. empty upon success + */ + std::string parseXmlFile(const char *filename); + + /** + * Parse multi inline suppression in comment + * @param comment the full comment text + * @param errorMessage output parameter for error message (wrong suppression attribute) + * @return empty vector if something wrong. + */ + static std::vector parseMultiSuppressComment(const std::string &comment, std::string *errorMessage); + + /** + * @brief Don't show the given error. + * @param line Description of error to suppress (in id:file:line format). + * @return error message. empty upon success + */ + std::string addSuppressionLine(const std::string &line); + + /** + * @brief Don't show this error. File and/or line are optional. In which case + * the errorId alone is used for filtering. + * @param suppression suppression details + * @return error message. empty upon success + */ + std::string addSuppression(Suppression suppression); + + /** + * @brief Combine list of suppressions into the current suppressions. + * @param suppressions list of suppression details + * @return error message. empty upon success + */ + std::string addSuppressions(std::list suppressions); + + /** + * @brief Returns true if this message should not be shown to the user. + * @param errmsg error message + * @param global use global suppressions + * @return true if this error is suppressed. + */ + bool isSuppressed(const ErrorMessage &errmsg, bool global = true); + + /** + * @brief Returns true if this message is "explicitly" suppressed. The suppression "id" must match textually exactly. + * @param errmsg error message + * @param global use global suppressions + * @return true if this error is explicitly suppressed. + */ + bool isSuppressedExplicitly(const ErrorMessage &errmsg, bool global = true); + + /** + * @brief Returns true if this message should not be shown to the user. + * @param errmsg error message + * @return true if this error is suppressed. + */ + bool isSuppressed(const ::ErrorMessage &errmsg, const std::set& macroNames); + + /** + * @brief Create an xml dump of suppressions + * @param out stream to write XML to + */ + void dump(std::ostream &out) const; + + /** + * @brief Returns list of unmatched local (per-file) suppressions. + * @return list of unmatched suppressions + */ + std::list getUnmatchedLocalSuppressions(const std::string &file, const bool unusedFunctionChecking) const; + + /** + * @brief Returns list of unmatched global (glob pattern) suppressions. + * @return list of unmatched suppressions + */ + std::list getUnmatchedGlobalSuppressions(const bool unusedFunctionChecking) const; + + /** + * @brief Returns list of all suppressions. + * @return list of suppressions + */ + const std::list &getSuppressions() const; + + /** + * @brief Marks Inline Suppressions as checked if source line is in the token stream + */ + void markUnmatchedInlineSuppressionsAsChecked(const Tokenizer &tokenizer); + + /** + * Report unmatched suppressions + * @param unmatched list of unmatched suppressions (from Settings::Suppressions::getUnmatched(Local|Global)Suppressions) + * @return true is returned if errors are reported + */ + static bool reportUnmatchedSuppressions(const std::list &unmatched, ErrorLogger &errorLogger); + +private: + /** @brief List of error which the user doesn't want to see. */ + std::list mSuppressions; +}; + +struct Suppressions +{ + /** @brief suppress message (--suppressions) */ + SuppressionList nomsg; + /** @brief suppress exitcode */ + SuppressionList nofail; +}; + +/// @} +//--------------------------------------------------------------------------- +#endif // suppressionsH diff --git a/cppcheck-2.14.0/lib/symboldatabase.cpp b/cppcheck-2.14.0/lib/symboldatabase.cpp new file mode 100644 index 00000000..c7208220 --- /dev/null +++ b/cppcheck-2.14.0/lib/symboldatabase.cpp @@ -0,0 +1,8226 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#include "symboldatabase.h" + +#include "astutils.h" +#include "errorlogger.h" +#include "errortypes.h" +#include "keywords.h" +#include "library.h" +#include "mathlib.h" +#include "path.h" +#include "platform.h" +#include "settings.h" +#include "standards.h" +#include "templatesimplifier.h" +#include "token.h" +#include "tokenize.h" +#include "tokenlist.h" +#include "utils.h" +#include "valueflow.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//--------------------------------------------------------------------------- + +SymbolDatabase::SymbolDatabase(Tokenizer& tokenizer, const Settings& settings, ErrorLogger& errorLogger) + : mTokenizer(tokenizer), mSettings(settings), mErrorLogger(errorLogger) +{ + if (!mTokenizer.tokens()) + return; + + if (mSettings.platform.defaultSign == 's' || mSettings.platform.defaultSign == 'S') + mDefaultSignedness = ValueType::SIGNED; + else if (mSettings.platform.defaultSign == 'u' || mSettings.platform.defaultSign == 'U') + mDefaultSignedness = ValueType::UNSIGNED; + else + mDefaultSignedness = ValueType::UNKNOWN_SIGN; + + createSymbolDatabaseFindAllScopes(); + createSymbolDatabaseClassInfo(); + createSymbolDatabaseVariableInfo(); + createSymbolDatabaseCopyAndMoveConstructors(); + createSymbolDatabaseFunctionScopes(); + createSymbolDatabaseClassAndStructScopes(); + createSymbolDatabaseFunctionReturnTypes(); + createSymbolDatabaseNeedInitialization(); + createSymbolDatabaseVariableSymbolTable(); + createSymbolDatabaseSetScopePointers(); + createSymbolDatabaseSetVariablePointers(); + setValueTypeInTokenList(false); + createSymbolDatabaseSetTypePointers(); + createSymbolDatabaseSetFunctionPointers(true); + createSymbolDatabaseSetSmartPointerType(); + setValueTypeInTokenList(false); + createSymbolDatabaseEnums(); + createSymbolDatabaseEscapeFunctions(); + createSymbolDatabaseIncompleteVars(); + createSymbolDatabaseExprIds(); + debugSymbolDatabase(); +} + +static const Token* skipScopeIdentifiers(const Token* tok) +{ + if (Token::Match(tok, ":: %name%")) + tok = tok->next(); + while (Token::Match(tok, "%name% ::") || + (Token::Match(tok, "%name% <") && Token::Match(tok->linkAt(1), ">|>> ::"))) { + if (tok->strAt(1) == "::") + tok = tok->tokAt(2); + else + tok = tok->linkAt(1)->tokAt(2); + } + + return tok; +} + +static bool isExecutableScope(const Token* tok) +{ + if (!Token::simpleMatch(tok, "{")) + return false; + const Token * tok2 = tok->link()->previous(); + if (Token::simpleMatch(tok2, "; }")) + return true; + if (tok2 == tok) + return false; + if (Token::simpleMatch(tok2, "} }")) { // inner scope + const Token* startTok = tok2->link(); + if (Token::Match(startTok->previous(), "do|try|else {")) + return true; + if (Token::Match(startTok->previous(), ")|] {")) + return !findLambdaStartToken(tok2); + return isExecutableScope(startTok); + } + return false; +} + +static bool isEnumDefinition(const Token* tok) +{ + if (!Token::Match(tok, "enum class| %name% {|:")) + return false; + while (!Token::Match(tok, "[{:]")) + tok = tok->next(); + if (tok->str() == "{") + return true; + tok = tok->next(); // skip ':' + while (Token::Match(tok, "%name%|::")) + tok = tok->next(); + return Token::simpleMatch(tok, "{"); +} + +void SymbolDatabase::createSymbolDatabaseFindAllScopes() +{ + // create global scope + scopeList.emplace_back(this, nullptr, nullptr); + + // pointer to current scope + Scope *scope = &scopeList.back(); + + // Store the ending of init lists + std::stack> endInitList; + auto inInitList = [&] { + if (endInitList.empty()) + return false; + return endInitList.top().second == scope; + }; + + auto addLambda = [this, &scope](const Token* tok, const Token* lambdaEndToken) -> const Token* { + const Token* lambdaStartToken = lambdaEndToken->link(); + const Token* argStart = lambdaStartToken->astParent(); + const Token* funcStart = Token::simpleMatch(argStart, "[") ? argStart : argStart->astParent(); + const Function* function = addGlobalFunction(scope, tok, argStart, funcStart); + if (!function) + mTokenizer.syntaxError(tok); + return lambdaStartToken; + }; + + // Store current access in each scope (depends on evaluation progress) + std::map access; + + // find all scopes + for (const Token *tok = mTokenizer.tokens(); tok; tok = tok ? tok->next() : nullptr) { + // #5593 suggested to add here: + mErrorLogger.reportProgress(mTokenizer.list.getSourceFilePath(), + "SymbolDatabase", + tok->progressValue()); + // Locate next class + if ((tok->isCpp() && tok->isKeyword() && + ((Token::Match(tok, "class|struct|union|namespace ::| %name% final| {|:|::|<") && + !Token::Match(tok->previous(), "new|friend|const|enum|typedef|mutable|volatile|using|)|(|<")) || + isEnumDefinition(tok))) + || (tok->isC() && tok->isKeyword() && Token::Match(tok, "struct|union|enum %name% {"))) { + const Token *tok2 = tok->tokAt(2); + + if (tok->strAt(1) == "::") + tok2 = tok2->next(); + else if (tok->isCpp() && tok->strAt(1) == "class") + tok2 = tok2->next(); + + while (Token::Match(tok2, ":: %name%")) + tok2 = tok2->tokAt(2); + while (Token::Match(tok2, "%name% :: %name%")) + tok2 = tok2->tokAt(2); + + // skip over template args + while (tok2 && tok2->str() == "<" && tok2->link()) { + tok2 = tok2->link()->next(); + while (Token::Match(tok2, ":: %name%")) + tok2 = tok2->tokAt(2); + } + + // skip over final + if (tok2 && tok2->isCpp() && Token::simpleMatch(tok2, "final")) + tok2 = tok2->next(); + + // make sure we have valid code + if (!Token::Match(tok2, "{|:")) { + // check for qualified variable + if (tok2 && tok2->next()) { + if (tok2->next()->str() == ";") + tok = tok2->next(); + else if (Token::simpleMatch(tok2->next(), "= {") && + Token::simpleMatch(tok2->linkAt(2), "} ;")) + tok = tok2->linkAt(2)->next(); + else if (Token::Match(tok2->next(), "(|{") && + tok2->next()->link()->strAt(1) == ";") + tok = tok2->next()->link()->next(); + // skip variable declaration + else if (Token::Match(tok2, "*|&|>")) + continue; + else if (Token::Match(tok2, "%name% (") && Tokenizer::isFunctionHead(tok2->next(), "{;")) + continue; + else if (Token::Match(tok2, "%name% [|=")) + continue; + // skip template + else if (Token::simpleMatch(tok2, ";") && + Token::Match(tok->previous(), "template|> class|struct")) { + tok = tok2; + continue; + } + // forward declaration + else if (Token::simpleMatch(tok2, ";") && + Token::Match(tok, "class|struct|union")) { + // TODO: see if it can be used + tok = tok2; + continue; + } + // skip constructor + else if (Token::simpleMatch(tok2, "(") && + Token::simpleMatch(tok2->link(), ") ;")) { + tok = tok2->link()->next(); + continue; + } else + throw InternalError(tok2, "SymbolDatabase bailout; unhandled code", InternalError::SYNTAX); + continue; + } + break; // bail + } + + const Token * name = tok->next(); + + if (name->str() == "class" && name->strAt(-1) == "enum") + name = name->next(); + + Scope *new_scope = findScope(name, scope); + + if (new_scope) { + // only create base list for classes and structures + if (new_scope->isClassOrStruct()) { + // goto initial '{' + if (!new_scope->definedType) + mTokenizer.syntaxError(nullptr); // #6808 + tok2 = new_scope->definedType->initBaseInfo(tok, tok2); + // make sure we have valid code + if (!tok2) { + break; + } + } + + // definition may be different than declaration + if (tok->isCpp() && tok->str() == "class") { + access[new_scope] = AccessControl::Private; + new_scope->type = Scope::eClass; + } else if (tok->str() == "struct") { + access[new_scope] = AccessControl::Public; + new_scope->type = Scope::eStruct; + } + + new_scope->classDef = tok; + new_scope->setBodyStartEnd(tok2); + // make sure we have valid code + if (!new_scope->bodyEnd) { + mTokenizer.syntaxError(tok); + } + scope = new_scope; + tok = tok2; + } else { + scopeList.emplace_back(this, tok, scope); + new_scope = &scopeList.back(); + + if (tok->str() == "class") + access[new_scope] = AccessControl::Private; + else if (tok->str() == "struct" || tok->str() == "union") + access[new_scope] = AccessControl::Public; + + // fill typeList... + if (new_scope->isClassOrStructOrUnion() || new_scope->type == Scope::eEnum) { + Type* new_type = findType(name, scope); + if (!new_type) { + typeList.emplace_back(new_scope->classDef, new_scope, scope); + new_type = &typeList.back(); + scope->definedTypesMap[new_type->name()] = new_type; + } else + new_type->classScope = new_scope; + new_scope->definedType = new_type; + } + + // only create base list for classes and structures + if (new_scope->isClassOrStruct()) { + // goto initial '{' + tok2 = new_scope->definedType->initBaseInfo(tok, tok2); + + // make sure we have valid code + if (!tok2) { + mTokenizer.syntaxError(tok); + } + } else if (new_scope->type == Scope::eEnum) { + if (tok2->str() == ":") { + tok2 = tok2->tokAt(2); + while (Token::Match(tok2, "%name%|::")) + tok2 = tok2->next(); + } + } + + new_scope->setBodyStartEnd(tok2); + + // make sure we have valid code + if (!new_scope->bodyEnd) { + mTokenizer.syntaxError(tok); + } + + if (new_scope->type == Scope::eEnum) { + tok2 = new_scope->addEnum(tok); + scope->nestedList.push_back(new_scope); + + if (!tok2) + mTokenizer.syntaxError(tok); + } else { + // make the new scope the current scope + scope->nestedList.push_back(new_scope); + scope = new_scope; + } + + tok = tok2; + } + } + + // Namespace and unknown macro (#3854) + else if (tok->isCpp() && tok->isKeyword() && + Token::Match(tok, "namespace %name% %type% (") && + tok->tokAt(2)->isUpperCaseName() && + Token::simpleMatch(tok->linkAt(3), ") {")) { + scopeList.emplace_back(this, tok, scope); + + Scope *new_scope = &scopeList.back(); + access[new_scope] = AccessControl::Public; + + const Token *tok2 = tok->linkAt(3)->next(); + + new_scope->setBodyStartEnd(tok2); + + // make sure we have valid code + if (!new_scope->bodyEnd) { + scopeList.pop_back(); + break; + } + + // make the new scope the current scope + scope->nestedList.push_back(new_scope); + scope = &scopeList.back(); + + tok = tok2; + } + + // forward declaration + else if (tok->isKeyword() && Token::Match(tok, "class|struct|union %name% ;") && + tok->strAt(-1) != "friend") { + if (!findType(tok->next(), scope)) { + // fill typeList.. + typeList.emplace_back(tok, nullptr, scope); + Type* new_type = &typeList.back(); + scope->definedTypesMap[new_type->name()] = new_type; + } + tok = tok->tokAt(2); + } + + // using namespace + else if (tok->isCpp() && tok->isKeyword() && Token::Match(tok, "using namespace ::| %type% ;|::")) { + Scope::UsingInfo using_info; + + using_info.start = tok; // save location + using_info.scope = findNamespace(tok->tokAt(2), scope); + + scope->usingList.push_back(using_info); + + // check for global namespace + if (tok->strAt(2) == "::") + tok = tok->tokAt(4); + else + tok = tok->tokAt(3); + + // skip over qualification + while (Token::Match(tok, "%type% ::")) + tok = tok->tokAt(2); + } + + // using type alias + else if (tok->isCpp() && tok->isKeyword() && Token::Match(tok, "using %name% =") && !tok->tokAt(2)->isSimplifiedTypedef()) { + if (tok->strAt(-1) != ">" && !findType(tok->next(), scope)) { + // fill typeList.. + typeList.emplace_back(tok, nullptr, scope); + Type* new_type = &typeList.back(); + scope->definedTypesMap[new_type->name()] = new_type; + } + + tok = tok->tokAt(3); + + while (tok && tok->str() != ";") { + if (Token::simpleMatch(tok, "decltype (")) + tok = tok->linkAt(1); + else + tok = tok->next(); + } + } + + // unnamed struct and union + else if (tok->isKeyword() && Token::Match(tok, "struct|union {") && + Token::Match(tok->next()->link(), "} *|&| %name% ;|[|=")) { + scopeList.emplace_back(this, tok, scope); + + Scope *new_scope = &scopeList.back(); + access[new_scope] = AccessControl::Public; + + const Token* varNameTok = tok->next()->link()->next(); + if (varNameTok->str() == "*") { + varNameTok = varNameTok->next(); + } else if (varNameTok->str() == "&") { + varNameTok = varNameTok->next(); + } + + typeList.emplace_back(tok, new_scope, scope); + { + Type* new_type = &typeList.back(); + new_scope->definedType = new_type; + scope->definedTypesMap[new_type->name()] = new_type; + } + + scope->addVariable(varNameTok, tok, tok, access[scope], new_scope->definedType, scope, &mSettings); + + const Token *tok2 = tok->next(); + + new_scope->setBodyStartEnd(tok2); + + // make sure we have valid code + if (!new_scope->bodyEnd) { + scopeList.pop_back(); + break; + } + + // make the new scope the current scope + scope->nestedList.push_back(new_scope); + scope = new_scope; + + tok = tok2; + } + + // anonymous struct, union and namespace + else if (tok->isKeyword() && ((Token::Match(tok, "struct|union {") && + Token::simpleMatch(tok->next()->link(), "} ;")) || + Token::simpleMatch(tok, "namespace {"))) { + scopeList.emplace_back(this, tok, scope); + + Scope *new_scope = &scopeList.back(); + access[new_scope] = AccessControl::Public; + + const Token *tok2 = tok->next(); + + new_scope->setBodyStartEnd(tok2); + + typeList.emplace_back(tok, new_scope, scope); + { + Type* new_type = &typeList.back(); + new_scope->definedType = new_type; + scope->definedTypesMap[new_type->name()] = new_type; + } + + // make sure we have valid code + if (!new_scope->bodyEnd) { + scopeList.pop_back(); + break; + } + + // make the new scope the current scope + scope->nestedList.push_back(new_scope); + scope = new_scope; + + tok = tok2; + } + + // forward declared enum + else if (tok->isKeyword() && (Token::Match(tok, "enum class| %name% ;") || Token::Match(tok, "enum class| %name% : %name% ;"))) { + typeList.emplace_back(tok, nullptr, scope); + Type* new_type = &typeList.back(); + scope->definedTypesMap[new_type->name()] = new_type; + tok = tok->tokAt(2); + } + + // check for end of scope + else if (tok == scope->bodyEnd) { + do { + access.erase(scope); + scope = const_cast(scope->nestedIn); + } while (scope->type != Scope::eGlobal && succeeds(tok, scope->bodyEnd)); + continue; + } + // check for end of init list + else if (inInitList() && tok == endInitList.top().first) { + endInitList.pop(); + continue; + } + + // check if in class or structure or union + else if (scope->isClassOrStructOrUnion()) { + const Token *funcStart = nullptr; + const Token *argStart = nullptr; + const Token *declEnd = nullptr; + + // What section are we in.. + if (tok->str() == "private:") + access[scope] = AccessControl::Private; + else if (tok->str() == "protected:") + access[scope] = AccessControl::Protected; + else if (tok->str() == "public:" || tok->str() == "__published:") + access[scope] = AccessControl::Public; + else if (Token::Match(tok, "public|protected|private %name% :")) { + if (tok->str() == "private") + access[scope] = AccessControl::Private; + else if (tok->str() == "protected") + access[scope] = AccessControl::Protected; + else + access[scope] = AccessControl::Public; + + tok = tok->tokAt(2); + } + + // class function? + else if (isFunction(tok, scope, funcStart, argStart, declEnd)) { + if (tok->previous()->str() != "::" || tok->strAt(-2) == scope->className) { + Function function(tok, scope, funcStart, argStart); + + // save the access type + function.access = access[scope]; + + const Token *end = function.argDef->link(); + + // count the number of constructors + if (function.isConstructor()) + scope->numConstructors++; + + // assume implementation is inline (definition and implementation same) + function.token = function.tokenDef; + function.arg = function.argDef; + + // out of line function + if (const Token *endTok = Tokenizer::isFunctionHead(end, ";")) { + tok = endTok; + scope->addFunction(std::move(function)); + } + + // inline function + else { + // find start of function '{' + bool foundInitList = false; + while (end && end->str() != "{" && end->str() != ";") { + if (end->link() && Token::Match(end, "(|<")) { + end = end->link(); + } else if (foundInitList && + Token::Match(end, "%name%|> {") && + Token::Match(end->linkAt(1), "} ,|{")) { + end = end->linkAt(1); + } else { + if (end->str() == ":") + foundInitList = true; + end = end->next(); + } + } + + if (!end || end->str() == ";") + continue; + + scope->addFunction(function); + + Function* funcptr = &scope->functionList.back(); + const Token *tok2 = funcStart; + + addNewFunction(scope, tok2); + if (scope) { + scope->functionOf = function.nestedIn; + scope->function = funcptr; + scope->function->functionScope = scope; + } + + tok = tok2; + } + } + + // nested class or friend function? + else { + /** @todo check entire qualification for match */ + const Scope * const nested = scope->findInNestedListRecursive(tok->strAt(-2)); + + if (nested) + addClassFunction(scope, tok, argStart); + else { + /** @todo handle friend functions */ + } + } + } + + // friend class declaration? + else if (tok->isCpp() && tok->isKeyword() && Token::Match(tok, "friend class|struct| ::| %any% ;|::")) { + Type::FriendInfo friendInfo; + + // save the name start + friendInfo.nameStart = tok->strAt(1) == "class" ? tok->tokAt(2) : tok->next(); + friendInfo.nameEnd = friendInfo.nameStart; + + // skip leading "::" + if (friendInfo.nameEnd->str() == "::") + friendInfo.nameEnd = friendInfo.nameEnd->next(); + + // skip qualification "name ::" + while (friendInfo.nameEnd && friendInfo.nameEnd->strAt(1) == "::") + friendInfo.nameEnd = friendInfo.nameEnd->tokAt(2); + + // fill this in after parsing is complete + friendInfo.type = nullptr; + + if (!scope->definedType) + mTokenizer.syntaxError(tok); + + scope->definedType->friendList.push_back(friendInfo); + } + } else if (scope->type == Scope::eNamespace || scope->type == Scope::eGlobal) { + const Token *funcStart = nullptr; + const Token *argStart = nullptr; + const Token *declEnd = nullptr; + + // function? + if (isFunction(tok, scope, funcStart, argStart, declEnd)) { + // has body? + if (declEnd && declEnd->str() == "{") { + tok = funcStart; + + // class function + if (tok->previous() && tok->previous()->str() == "::") + addClassFunction(scope, tok, argStart); + + // class destructor + else if (tok->previous() && + tok->previous()->str() == "~" && + tok->strAt(-2) == "::") + addClassFunction(scope, tok, argStart); + + // regular function + else { + const Function* const function = addGlobalFunction(scope, tok, argStart, funcStart); + + if (!function) + mTokenizer.syntaxError(tok); + } + + // syntax error? + if (!scope) + mTokenizer.syntaxError(tok); + } + // function prototype? + else if (declEnd && declEnd->str() == ";") { + if (tok->astParent() && tok->astParent()->str() == "::" && + Token::Match(declEnd->previous(), "default|delete")) { + addClassFunction(scope, tok, argStart); + continue; + } + + bool newFunc = true; // Is this function already in the database? + auto range = scope->functionMap.equal_range(tok->str()); + for (std::multimap::const_iterator it = range.first; it != range.second; ++it) { + if (it->second->argsMatch(scope, it->second->argDef, argStart, emptyString, 0)) { + newFunc = false; + break; + } + } + + // save function prototype in database + if (newFunc) { + addGlobalFunctionDecl(scope, tok, argStart, funcStart); + } + + tok = declEnd; + continue; + } + } else if (const Token *lambdaEndToken = findLambdaEndToken(tok)) { + tok = addLambda(tok, lambdaEndToken); + } + } else if (scope->isExecutable()) { + if (tok->isKeyword() && Token::Match(tok, "else|try|do {")) { + const Token* tok1 = tok->next(); + if (tok->str() == "else") + scopeList.emplace_back(this, tok, scope, Scope::eElse, tok1); + else if (tok->str() == "do") + scopeList.emplace_back(this, tok, scope, Scope::eDo, tok1); + else //if (tok->str() == "try") + scopeList.emplace_back(this, tok, scope, Scope::eTry, tok1); + + tok = tok1; + scope->nestedList.push_back(&scopeList.back()); + scope = &scopeList.back(); + } else if (tok->isKeyword() && Token::Match(tok, "if|for|while|catch|switch (") && Token::simpleMatch(tok->next()->link(), ") {")) { + const Token *scopeStartTok = tok->next()->link()->next(); + if (tok->str() == "if") + scopeList.emplace_back(this, tok, scope, Scope::eIf, scopeStartTok); + else if (tok->str() == "for") { + scopeList.emplace_back(this, tok, scope, Scope::eFor, scopeStartTok); + } else if (tok->str() == "while") + scopeList.emplace_back(this, tok, scope, Scope::eWhile, scopeStartTok); + else if (tok->str() == "catch") { + scopeList.emplace_back(this, tok, scope, Scope::eCatch, scopeStartTok); + } else // if (tok->str() == "switch") + scopeList.emplace_back(this, tok, scope, Scope::eSwitch, scopeStartTok); + + scope->nestedList.push_back(&scopeList.back()); + scope = &scopeList.back(); + if (scope->type == Scope::eFor) + scope->checkVariable(tok->tokAt(2), AccessControl::Local, mSettings); // check for variable declaration and add it to new scope if found + else if (scope->type == Scope::eCatch) + scope->checkVariable(tok->tokAt(2), AccessControl::Throw, mSettings); // check for variable declaration and add it to new scope if found + tok = scopeStartTok; + } else if (Token::Match(tok, "%var% {")) { + endInitList.emplace(tok->next()->link(), scope); + tok = tok->next(); + } else if (const Token *lambdaEndToken = findLambdaEndToken(tok)) { + tok = addLambda(tok, lambdaEndToken); + } else if (tok->str() == "{") { + if (inInitList()) { + endInitList.emplace(tok->link(), scope); + } else if (isExecutableScope(tok)) { + scopeList.emplace_back(this, tok, scope, Scope::eUnconditional, tok); + scope->nestedList.push_back(&scopeList.back()); + scope = &scopeList.back(); + } else if (scope->isExecutable()) { + endInitList.emplace(tok->link(), scope); + } else { + tok = tok->link(); + } + } else if (Token::Match(tok, "extern %type%")) { + const Token * ftok = tok->next(); + while (Token::Match(ftok, "%name%|*|&")) + ftok = ftok->next(); + if (!ftok || ftok->str() != "(") + continue; + ftok = ftok->previous(); + if (Token::simpleMatch(ftok->linkAt(1), ") ;")) { + const Token *funcStart = nullptr; + const Token *argStart = nullptr; + const Token *declEnd = nullptr; + if (isFunction(ftok, scope, funcStart, argStart, declEnd)) { + if (declEnd && declEnd->str() == ";") { + bool newFunc = true; // Is this function already in the database? + auto range = scope->functionMap.equal_range(ftok->str()); + for (std::multimap::const_iterator it = range.first; it != range.second; ++it) { + if (it->second->argsMatch(scope, it->second->argDef, argStart, emptyString, 0)) { + newFunc = false; + break; + } + } + // save function prototype in database + if (newFunc) { + Function function(ftok, scope, funcStart, argStart); + if (function.isExtern()) { + scope->addFunction(std::move(function)); + tok = declEnd; + } + } + } + } + } + } + // syntax error? + if (!scope) + mTokenizer.syntaxError(tok); + // End of scope or list should be handled above + if (tok->str() == "}") + mTokenizer.syntaxError(tok); + } + } +} + +void SymbolDatabase::createSymbolDatabaseClassInfo() +{ + if (mTokenizer.isC()) + return; + + // fill in using info + for (Scope& scope : scopeList) { + for (Scope::UsingInfo& usingInfo : scope.usingList) { + // only find if not already found + if (usingInfo.scope == nullptr) { + // check scope for match + const Scope * const found = findScope(usingInfo.start->tokAt(2), &scope); + if (found) { + // set found scope + usingInfo.scope = found; + break; + } + } + } + } + + // fill in base class info + for (Type& type : typeList) { + // finish filling in base class info + for (Type::BaseInfo & i : type.derivedFrom) { + const Type* found = findType(i.nameTok, type.enclosingScope, /*lookOutside*/ true); + if (found && found->findDependency(&type)) { + // circular dependency + //mTokenizer.syntaxError(nullptr); + } else { + i.type = found; + } + } + } + + // fill in friend info + for (Type & type : typeList) { + for (Type::FriendInfo &friendInfo : type.friendList) { + friendInfo.type = findType(friendInfo.nameStart, type.enclosingScope); + } + } +} + + +void SymbolDatabase::createSymbolDatabaseVariableInfo() +{ + // fill in variable info + for (Scope& scope : scopeList) { + // find variables + scope.getVariableList(mSettings); + } + + // fill in function arguments + for (Scope& scope : scopeList) { + std::list::iterator func; + + for (func = scope.functionList.begin(); func != scope.functionList.end(); ++func) { + // add arguments + func->addArguments(this, &scope); + } + } +} + +void SymbolDatabase::createSymbolDatabaseCopyAndMoveConstructors() +{ + // fill in class and struct copy/move constructors + for (Scope& scope : scopeList) { + if (!scope.isClassOrStruct()) + continue; + + std::list::iterator func; + for (func = scope.functionList.begin(); func != scope.functionList.end(); ++func) { + if (!func->isConstructor() || func->minArgCount() != 1) + continue; + + const Variable* firstArg = func->getArgumentVar(0); + if (firstArg->type() == scope.definedType) { + if (firstArg->isRValueReference()) + func->type = Function::eMoveConstructor; + else if (firstArg->isReference() && !firstArg->isPointer()) + func->type = Function::eCopyConstructor; + } + + if (func->type == Function::eCopyConstructor || + func->type == Function::eMoveConstructor) + scope.numCopyOrMoveConstructors++; + } + } +} + +void SymbolDatabase::createSymbolDatabaseFunctionScopes() +{ + // fill in function scopes + for (const Scope & scope : scopeList) { + if (scope.type == Scope::eFunction) + functionScopes.push_back(&scope); + } +} + +void SymbolDatabase::createSymbolDatabaseClassAndStructScopes() +{ + // fill in class and struct scopes + for (const Scope& scope : scopeList) { + if (scope.isClassOrStruct()) + classAndStructScopes.push_back(&scope); + } +} + +void SymbolDatabase::createSymbolDatabaseFunctionReturnTypes() +{ + // fill in function return types + for (Scope& scope : scopeList) { + std::list::iterator func; + + for (func = scope.functionList.begin(); func != scope.functionList.end(); ++func) { + // add return types + if (func->retDef) { + const Token *type = func->retDef; + while (Token::Match(type, "static|const|struct|union|enum")) + type = type->next(); + if (type) { + func->retType = findVariableTypeInBase(&scope, type); + if (!func->retType) + func->retType = findTypeInNested(type, func->nestedIn); + } + } + } + } +} + +void SymbolDatabase::createSymbolDatabaseNeedInitialization() +{ + if (mTokenizer.isC()) { + // For C code it is easy, as there are no constructors and no default values + for (const Scope& scope : scopeList) { + if (scope.definedType) + scope.definedType->needInitialization = Type::NeedInitialization::True; + } + } else { + // For C++, it is more difficult: Determine if user defined type needs initialization... + unsigned int unknowns = 0; // stop checking when there are no unknowns + unsigned int retry = 0; // bail if we don't resolve all the variable types for some reason + + do { + unknowns = 0; + + for (Scope& scope : scopeList) { + if (!scope.isClassOrStructOrUnion()) + continue; + if (scope.classDef && Token::simpleMatch(scope.classDef->previous(), ">")) // skip uninstantiated template + continue; + + if (!scope.definedType) { + mBlankTypes.emplace_back(); + scope.definedType = &mBlankTypes.back(); + } + + if (scope.isClassOrStruct() && scope.definedType->needInitialization == Type::NeedInitialization::Unknown) { + // check for default constructor + bool hasDefaultConstructor = false; + + for (const Function& func : scope.functionList) { + if (func.type == Function::eConstructor) { + // check for no arguments: func ( ) + if (func.argCount() == 0) { + hasDefaultConstructor = true; + break; + } + + /** check for arguments with default values */ + if (func.argCount() == func.initializedArgCount()) { + hasDefaultConstructor = true; + break; + } + } + } + + // User defined types with user defined default constructor doesn't need initialization. + // We assume the default constructor initializes everything. + // Another check will figure out if the constructor actually initializes everything. + if (hasDefaultConstructor) + scope.definedType->needInitialization = Type::NeedInitialization::False; + + // check each member variable to see if it needs initialization + else { + bool needInitialization = false; + bool unknown = false; + + for (const Variable& var: scope.varlist) { + if (var.isStatic()) + continue; + if (var.isClass() && !var.isReference()) { + if (var.type()) { + // does this type need initialization? + if (var.type()->needInitialization == Type::NeedInitialization::True && !var.hasDefault() && !var.isStatic()) + needInitialization = true; + else if (var.type()->needInitialization == Type::NeedInitialization::Unknown) { + if (!(var.valueType() && var.valueType()->type == ValueType::CONTAINER)) + unknown = true; + } + } + } else if (!var.hasDefault()) { + needInitialization = true; + break; + } + } + + if (needInitialization) + scope.definedType->needInitialization = Type::NeedInitialization::True; + else if (!unknown) + scope.definedType->needInitialization = Type::NeedInitialization::False; + else { + if (scope.definedType->needInitialization == Type::NeedInitialization::Unknown) + unknowns++; + } + } + } else if (scope.type == Scope::eUnion && scope.definedType->needInitialization == Type::NeedInitialization::Unknown) + scope.definedType->needInitialization = Type::NeedInitialization::True; + } + + retry++; + } while (unknowns && retry < 100); + + // this shouldn't happen so output a debug warning + if (retry == 100 && mSettings.debugwarnings) { + for (const Scope& scope : scopeList) { + if (scope.isClassOrStruct() && scope.definedType->needInitialization == Type::NeedInitialization::Unknown) + debugMessage(scope.classDef, "debug", "SymbolDatabase couldn't resolve all user defined types."); + } + } + } +} + +void SymbolDatabase::createSymbolDatabaseVariableSymbolTable() +{ + // create variable symbol table + mVariableList.resize(mTokenizer.varIdCount() + 1); + std::fill_n(mVariableList.begin(), mVariableList.size(), nullptr); + + // check all scopes for variables + for (Scope& scope : scopeList) { + // add all variables + for (Variable& var: scope.varlist) { + const unsigned int varId = var.declarationId(); + if (varId) + mVariableList[varId] = &var; + // fix up variables without type + if (!var.type() && !var.typeStartToken()->isStandardType()) { + const Type *type = findType(var.typeStartToken(), &scope); + if (type) + var.type(type); + } + } + + // add all function parameters + for (Function& func : scope.functionList) { + for (Variable& arg: func.argumentList) { + // check for named parameters + if (arg.nameToken() && arg.declarationId()) { + const unsigned int declarationId = arg.declarationId(); + mVariableList[declarationId] = &arg; + // fix up parameters without type + if (!arg.type() && !arg.typeStartToken()->isStandardType()) { + const Type *type = findTypeInNested(arg.typeStartToken(), &scope); + if (type) + arg.type(type); + } + } + } + } + } + + // fill in missing variables if possible + for (const Scope *func: functionScopes) { + for (const Token *tok = func->bodyStart->next(); tok && tok != func->bodyEnd; tok = tok->next()) { + // check for member variable + if (!Token::Match(tok, "%var% .|[")) + continue; + const Token* tokDot = tok->next(); + while (Token::simpleMatch(tokDot, "[")) + tokDot = tokDot->link()->next(); + if (!Token::Match(tokDot, ". %var%")) + continue; + const Token *member = tokDot->next(); + if (mVariableList[member->varId()] == nullptr) { + const Variable *var1 = mVariableList[tok->varId()]; + if (var1 && var1->typeScope()) { + const Variable* memberVar = var1->typeScope()->getVariable(member->str()); + if (memberVar) { + // add this variable to the look up table + mVariableList[member->varId()] = memberVar; + } + } + } + } + } +} + +void SymbolDatabase::createSymbolDatabaseSetScopePointers() +{ + auto setScopePointers = [](const Scope &scope, const Token *bodyStart, const Token *bodyEnd) { + assert(bodyStart); + assert(bodyEnd); + + const_cast(bodyEnd)->scope(&scope); + + for (auto* tok = const_cast(bodyStart); tok != bodyEnd; tok = tok->next()) { + if (bodyStart != bodyEnd && tok->str() == "{") { + bool isEndOfScope = false; + for (Scope* innerScope: scope.nestedList) { + const auto &list = innerScope->bodyStartList; + if (std::find(list.cbegin(), list.cend(), tok) != list.cend()) { // Is begin of inner scope + tok = tok->link(); + if (tok->next() == bodyEnd || !tok->next()) { + isEndOfScope = true; + break; + } + tok = tok->next(); + break; + } + } + if (isEndOfScope) + break; + } + tok->scope(&scope); + } + }; + + // Set scope pointers + for (const Scope& scope: scopeList) { + if (scope.type == Scope::eGlobal) + setScopePointers(scope, mTokenizer.list.front(), mTokenizer.list.back()); + else { + for (const Token *bodyStart: scope.bodyStartList) + setScopePointers(scope, bodyStart, bodyStart->link()); + } + } +} + +void SymbolDatabase::createSymbolDatabaseSetFunctionPointers(bool firstPass) +{ + if (firstPass) { + // Set function definition and declaration pointers + for (const Scope& scope: scopeList) { + for (const Function& func: scope.functionList) { + if (func.tokenDef) + const_cast(func.tokenDef)->function(&func); + + if (func.token) + const_cast(func.token)->function(&func); + } + } + } + + // Set function call pointers + const Token* inTemplateArg = nullptr; + for (Token* tok = mTokenizer.list.front(); tok != mTokenizer.list.back(); tok = tok->next()) { + if (inTemplateArg == nullptr && tok->link() && tok->str() == "<") + inTemplateArg = tok->link(); + if (inTemplateArg == tok) + inTemplateArg = nullptr; + if (tok->isName() && !tok->function() && tok->varId() == 0 && ((tok->astParent() && tok->astParent()->isComparisonOp()) || Token::Match(tok, "%name% [{(,)>;]")) && !isReservedName(tok)) { + if (tok->next()->str() == ">" && !tok->next()->link()) + continue; + + const Function *function = findFunction(tok); + if (!function || (inTemplateArg && function->isConstructor())) + continue; + + tok->function(function); + + if (tok->next()->str() != "(") + const_cast(function)->functionPointerUsage = tok; + } + } + + // Set C++ 11 delegate constructor function call pointers + for (const Scope& scope: scopeList) { + for (const Function& func: scope.functionList) { + // look for initializer list + if (func.isConstructor() && func.functionScope && func.functionScope->functionOf && func.arg) { + const Token * tok = func.arg->link()->next(); + if (tok->str() == "noexcept") { + const Token * closingParenTok = tok->linkAt(1); + if (!closingParenTok || !closingParenTok->next()) { + continue; + } + tok = closingParenTok->next(); + } + if (tok->str() != ":") { + continue; + } + tok = tok->next(); + while (tok && tok != func.functionScope->bodyStart) { + if (Token::Match(tok, "%name% {|(")) { + if (tok->str() == func.tokenDef->str()) { + const Function *function = func.functionScope->functionOf->findFunction(tok); + if (function) + const_cast(tok)->function(function); + break; + } + tok = tok->linkAt(1); + } + tok = tok->next(); + } + } + } + } +} + +void SymbolDatabase::createSymbolDatabaseSetTypePointers() +{ + std::unordered_set typenames; + for (const Type &t : typeList) { + typenames.insert(t.name()); + } + + // Set type pointers + for (Token* tok = mTokenizer.list.front(); tok != mTokenizer.list.back(); tok = tok->next()) { + if (!tok->isName() || tok->varId() || tok->function() || tok->type() || tok->enumerator()) + continue; + + if (typenames.find(tok->str()) == typenames.end()) + continue; + + const Type *type = findVariableType(tok->scope(), tok); + if (type) + tok->type(type); + } +} + +void SymbolDatabase::createSymbolDatabaseSetSmartPointerType() +{ + for (Scope &scope: scopeList) { + for (Variable &var: scope.varlist) { + if (var.valueType() && var.valueType()->smartPointerTypeToken && !var.valueType()->smartPointerType) { + ValueType vt(*var.valueType()); + vt.smartPointerType = vt.smartPointerTypeToken->type(); + var.setValueType(vt); + } + } + } +} + +void SymbolDatabase::fixVarId(VarIdMap & varIds, const Token * vartok, Token * membertok, const Variable * membervar) +{ + VarIdMap::iterator varId = varIds.find(vartok->varId()); + if (varId == varIds.end()) { + MemberIdMap memberId; + if (membertok->varId() == 0) { + memberId[membervar->nameToken()->varId()] = mTokenizer.newVarId(); + mVariableList.push_back(membervar); + } else + mVariableList[membertok->varId()] = membervar; + varIds.insert(std::make_pair(vartok->varId(), memberId)); + varId = varIds.find(vartok->varId()); + } + MemberIdMap::iterator memberId = varId->second.find(membervar->nameToken()->varId()); + if (memberId == varId->second.end()) { + if (membertok->varId() == 0) { + varId->second.insert(std::make_pair(membervar->nameToken()->varId(), mTokenizer.newVarId())); + mVariableList.push_back(membervar); + memberId = varId->second.find(membervar->nameToken()->varId()); + } else + mVariableList[membertok->varId()] = membervar; + } + if (membertok->varId() == 0) + membertok->varId(memberId->second); +} + +static bool isContainerYieldElement(Library::Container::Yield yield); + +void SymbolDatabase::createSymbolDatabaseSetVariablePointers() +{ + VarIdMap varIds; + + auto setMemberVar = [&](const Variable* membervar, Token* membertok, const Token* vartok) -> void { + if (membervar) { + membertok->variable(membervar); + if (vartok && (membertok->varId() == 0 || mVariableList[membertok->varId()] == nullptr)) + fixVarId(varIds, vartok, membertok, membervar); + } + }; + + // Set variable pointers + for (Token* tok = mTokenizer.list.front(); tok != mTokenizer.list.back(); tok = tok->next()) { + if (!tok->isName() || tok->isKeyword() || tok->isStandardType()) + continue; + if (tok->varId()) + tok->variable(getVariableFromVarId(tok->varId())); + + // Set Token::variable pointer for array member variable + // Since it doesn't point at a fixed location it doesn't have varid + const bool isVar = tok->variable() && (tok->variable()->typeScope() || tok->variable()->isSmartPointer() || + (tok->valueType() && (tok->valueType()->type == ValueType::CONTAINER || tok->valueType()->type == ValueType::ITERATOR))); + const bool isArrayAccess = isVar && Token::simpleMatch(tok->astParent(), "["); + const bool isDirectAccess = isVar && !isArrayAccess && Token::simpleMatch(tok->astParent(), "."); + const bool isDerefAccess = isVar && !isDirectAccess && Token::simpleMatch(tok->astParent(), "*") && Token::simpleMatch(tok->astParent()->astParent(), "."); + if (isVar && (isArrayAccess || isDirectAccess || isDerefAccess)) { + Token* membertok{}; + if (isArrayAccess) { + membertok = tok->astParent(); + while (Token::simpleMatch(membertok, "[")) + membertok = membertok->astParent(); + if (membertok) + membertok = membertok->astOperand2(); + } + else if (isDirectAccess) { + membertok = tok->astParent()->astOperand2(); + if (membertok == tok) { + Token* gptok = tok->astParent()->astParent(); + if (Token::simpleMatch(gptok, ".")) // chained access + membertok = gptok->astOperand2(); + else if (Token::simpleMatch(gptok, "[") && Token::simpleMatch(gptok->astParent(), ".")) + membertok = gptok->astParent()->astOperand2(); + } + } + else { // isDerefAccess + membertok = tok->astParent(); + while (Token::simpleMatch(membertok, "*")) + membertok = membertok->astParent(); + if (membertok) + membertok = membertok->astOperand2(); + } + + if (membertok && membertok != tok) { + const Variable *var = tok->variable(); + if (var->typeScope()) { + const Variable *membervar = var->typeScope()->getVariable(membertok->str()); + setMemberVar(membervar, membertok, tok); + } else if (const ::Type *type = var->smartPointerType()) { + const Scope *classScope = type->classScope; + const Variable *membervar = classScope ? classScope->getVariable(membertok->str()) : nullptr; + setMemberVar(membervar, membertok, tok); + } else if (tok->valueType() && tok->valueType()->type == ValueType::CONTAINER) { + if (const Token* ctt = tok->valueType()->containerTypeToken) { + while (ctt && ctt->isKeyword()) + ctt = ctt->next(); + const Type* ct = findTypeInNested(ctt, tok->scope()); + if (ct && ct->classScope && ct->classScope->definedType) { + const Variable *membervar = ct->classScope->getVariable(membertok->str()); + setMemberVar(membervar, membertok, tok); + } + } + } else if (const Type* iterType = var->iteratorType()) { + if (iterType->classScope && iterType->classScope->definedType) { + const Variable *membervar = iterType->classScope->getVariable(membertok->str()); + setMemberVar(membervar, membertok, tok); + } + } + } + } + + // check for function returning record type + // func(...).var + // func(...)[...].var + else if (tok->function() && tok->next()->str() == "(" && + (Token::Match(tok->next()->link(), ") . %name% !!(") || + (Token::Match(tok->next()->link(), ") [") && Token::Match(tok->next()->link()->next()->link(), "] . %name% !!(")))) { + const Type *type = tok->function()->retType; + Token* membertok; + if (tok->next()->link()->next()->str() == ".") + membertok = tok->next()->link()->next()->next(); + else + membertok = tok->next()->link()->next()->link()->next()->next(); + if (type) { + const Variable *membervar = membertok->variable(); + if (!membervar) { + if (type->classScope) { + membervar = type->classScope->getVariable(membertok->str()); + setMemberVar(membervar, membertok, tok->function()->retDef); + } + } + } else if (mSettings.library.detectSmartPointer(tok->function()->retDef)) { + if (const Token* templateArg = Token::findsimplematch(tok->function()->retDef, "<")) { + if (const Type* spType = findTypeInNested(templateArg->next(), tok->scope())) { + if (spType->classScope) { + const Variable* membervar = spType->classScope->getVariable(membertok->str()); + setMemberVar(membervar, membertok, tok->function()->retDef); + } + } + } + } + } + else if (Token::simpleMatch(tok->astParent(), ".") && tok->next()->str() == "(" && + astIsContainer(tok->astParent()->astOperand1()) && Token::Match(tok->next()->link(), ") . %name% !!(")) { + const ValueType* vt = tok->astParent()->astOperand1()->valueType(); + const Library::Container* cont = vt->container; + auto it = cont->functions.find(tok->str()); + if (it != cont->functions.end() && isContainerYieldElement(it->second.yield) && vt->containerTypeToken) { + Token* memberTok = tok->next()->link()->tokAt(2); + const Scope* scope = vt->containerTypeToken->scope(); + const Type* contType{}; + const std::string& typeStr = vt->containerTypeToken->str(); // TODO: handle complex type expressions + while (scope && !contType) { + contType = scope->findType(typeStr); // find the type stored in the container + scope = scope->nestedIn; + } + if (contType && contType->classScope) { + const Variable* membervar = contType->classScope->getVariable(memberTok->str()); + setMemberVar(membervar, memberTok, vt->containerTypeToken); + } + } + } + } +} + +void SymbolDatabase::createSymbolDatabaseEnums() +{ + // fill in enumerators in enum + for (const Scope &scope : scopeList) { + if (scope.type != Scope::eEnum) + continue; + + // add enumerators to enumerator tokens + for (const Enumerator & i : scope.enumeratorList) + const_cast(i.name)->enumerator(&i); + } + + std::set tokensThatAreNotEnumeratorValues; + + for (const Scope &scope : scopeList) { + if (scope.type != Scope::eEnum) + continue; + + for (const Enumerator & enumerator : scope.enumeratorList) { + // look for initialization tokens that can be converted to enumerators and convert them + if (enumerator.start) { + if (!enumerator.end) + mTokenizer.syntaxError(enumerator.start); + for (const Token * tok3 = enumerator.start; tok3 && tok3 != enumerator.end->next(); tok3 = tok3->next()) { + if (tok3->tokType() == Token::eName) { + const Enumerator * e = findEnumerator(tok3, tokensThatAreNotEnumeratorValues); + if (e) + const_cast(tok3)->enumerator(e); + } + } + } + } + } + + // find enumerators + for (Token* tok = mTokenizer.list.front(); tok != mTokenizer.list.back(); tok = tok->next()) { + const bool isVariable = (tok->tokType() == Token::eVariable && !tok->variable()); + if (tok->tokType() != Token::eName && !isVariable) + continue; + const Enumerator * enumerator = findEnumerator(tok, tokensThatAreNotEnumeratorValues); + if (enumerator) { + if (isVariable) + tok->varId(0); + tok->enumerator(enumerator); + } + } +} + +void SymbolDatabase::createSymbolDatabaseIncompleteVars() +{ + for (Token* tok = mTokenizer.list.front(); tok != mTokenizer.list.back(); tok = tok->next()) { + const Scope * scope = tok->scope(); + if (!scope) + continue; + if (!scope->isExecutable()) + continue; + if (tok->varId() != 0) + continue; + if (tok->isCast() && !isCPPCast(tok) && tok->link() && tok->str() == "(") { + tok = tok->link(); + continue; + } + if (tok->isCpp() && (Token::Match(tok, "catch|typeid (") || + Token::Match(tok, "static_cast|dynamic_cast|const_cast|reinterpret_cast"))) { + tok = tok->linkAt(1); + continue; + } + if (tok->str() == "NULL") + continue; + if (tok->isKeyword() || !tok->isNameOnly()) + continue; + if (tok->type()) + continue; + if (Token::Match(tok->next(), "::|.|(|{|:|%var%")) + continue; + if (Token::Match(tok->next(), "&|&&|* *| *| )|,|%var%|const")) + continue; + // Very likely a typelist + if (Token::Match(tok->tokAt(-2), "%type% ,") || Token::Match(tok->next(), ", %type%")) + continue; + // Inside template brackets + if (Token::simpleMatch(tok->next(), "<") && tok->linkAt(1)) { + tok = tok->linkAt(1); + continue; + } + // Skip goto labels + if (Token::simpleMatch(tok->previous(), "goto")) + continue; + std::string fstr = tok->str(); + const Token* ftok = tok->previous(); + while (Token::simpleMatch(ftok, "::")) { + if (!Token::Match(ftok->previous(), "%name%")) + break; + fstr.insert(0, ftok->previous()->str() + "::"); + ftok = ftok->tokAt(-2); + } + if (mSettings.library.functions.find(fstr) != mSettings.library.functions.end()) + continue; + if (tok->isCpp()) { + const Token* parent = tok->astParent(); + while (Token::Match(parent, "::|[|{")) + parent = parent->astParent(); + if (Token::simpleMatch(parent, "new")) + continue; + // trailing return type + if (Token::simpleMatch(ftok, ".") && ftok->originalName() == "->" && Token::Match(ftok->tokAt(-1), "[])]")) + continue; + } + tok->isIncompleteVar(true); + } +} + +void SymbolDatabase::createSymbolDatabaseEscapeFunctions() +{ + for (const Scope& scope : scopeList) { + if (scope.type != Scope::eFunction) + continue; + Function * function = scope.function; + if (!function) + continue; + if (Token::findsimplematch(scope.bodyStart, "return", scope.bodyEnd)) + continue; + function->isEscapeFunction(isReturnScope(scope.bodyEnd, mSettings.library, nullptr, true)); + } +} + +static bool isExpression(const Token* tok) +{ + if (!tok) + return false; + if (Token::simpleMatch(tok, "{") && tok->scope() && tok->scope()->bodyStart != tok && + (tok->astOperand1() || tok->astOperand2())) + return true; + if (!Token::Match(tok, "(|.|[|::|?|:|++|--|%cop%|%assign%")) + return false; + if (Token::Match(tok, "*|&|&&")) { + const Token* vartok = findAstNode(tok, [&](const Token* tok2) { + const Variable* var = tok2->variable(); + if (!var) + return false; + return var->nameToken() == tok2; + }); + if (vartok) + return false; + } + return true; +} + +static std::string getIncompleteNameID(const Token* tok) +{ + std::string result = tok->str() + "@"; + while (Token::Match(tok->astParent(), ".|::")) + tok = tok->astParent(); + return result + tok->expressionString(); +} + +namespace { + struct ExprIdKey { + std::string parentOp; + nonneg int operand1; + nonneg int operand2; + bool operator<(const ExprIdKey& k) const { + return std::tie(parentOp, operand1, operand2) < std::tie(k.parentOp, k.operand1, k.operand2); + } + }; + using ExprIdMap = std::map; + void setParentExprId(Token* tok, ExprIdMap& exprIdMap, nonneg int &id) { + if (!tok->astParent() || tok->astParent()->isControlFlowKeyword()) + return; + const Token* op1 = tok->astParent()->astOperand1(); + if (op1 && op1->exprId() == 0) + return; + const Token* op2 = tok->astParent()->astOperand2(); + if (op2 && op2->exprId() == 0 && + !((tok->astParent()->astParent() && tok->astParent()->isAssignmentOp() && tok->astParent()->astParent()->isAssignmentOp()) || + isLambdaCaptureList(op2) || + (op2->str() == "(" && isLambdaCaptureList(op2->astOperand1())) || + Token::simpleMatch(op2, "{ }"))) + return; + + if (tok->astParent()->isExpandedMacro() || Token::Match(tok->astParent(), "++|--")) { + tok->astParent()->exprId(id); + ++id; + setParentExprId(tok->astParent(), exprIdMap, id); + return; + } + + ExprIdKey key; + key.parentOp = tok->astParent()->str(); + key.operand1 = op1 ? op1->exprId() : 0; + key.operand2 = op2 ? op2->exprId() : 0; + + if (tok->astParent()->isCast() && tok->astParent()->str() == "(") { + const Token* typeStartToken; + const Token* typeEndToken; + if (tok->astParent()->astOperand2()) { + typeStartToken = tok->astParent()->astOperand1(); + typeEndToken = tok; + } else { + typeStartToken = tok->astParent()->next(); + typeEndToken = tok->astParent()->link(); + } + std::string type; + for (const Token* t = typeStartToken; t != typeEndToken; t = t->next()) { + type += " " + t->str(); + } + key.parentOp += type; + } + + for (const auto& ref: followAllReferences(op1)) { + if (ref.token->exprId() != 0) { // cppcheck-suppress useStlAlgorithm + key.operand1 = ref.token->exprId(); + break; + } + } + for (const auto& ref: followAllReferences(op2)) { + if (ref.token->exprId() != 0) { // cppcheck-suppress useStlAlgorithm + key.operand2 = ref.token->exprId(); + break; + } + } + + if (key.operand1 > key.operand2 && key.operand2 && + Token::Match(tok->astParent(), "%or%|%oror%|+|*|&|&&|^|==|!=")) { + // In C++ the order of operands of + might matter + if (!tok->isCpp() || + key.parentOp != "+" || + !tok->astParent()->valueType() || + tok->astParent()->valueType()->isIntegral() || + tok->astParent()->valueType()->isFloat() || + tok->astParent()->valueType()->pointer > 0) + std::swap(key.operand1, key.operand2); + } + + const auto it = exprIdMap.find(key); + if (it == exprIdMap.end()) { + exprIdMap[key] = id; + tok->astParent()->exprId(id); + ++id; + } else { + tok->astParent()->exprId(it->second); + } + setParentExprId(tok->astParent(), exprIdMap, id); + } +} + +void SymbolDatabase::createSymbolDatabaseExprIds() +{ + // Find highest varId + nonneg int maximumVarId = 0; + for (const Token* tok = mTokenizer.list.front(); tok; tok = tok->next()) { + if (tok->varId() > maximumVarId) + maximumVarId = tok->varId(); + } + nonneg int id = maximumVarId + 1; + // Find incomplete vars that are used in constant context + std::unordered_map unknownConstantIds; + const Token* inConstExpr = nullptr; + for (const Token* tok = mTokenizer.list.front(); tok != mTokenizer.list.back(); tok = tok->next()) { + if (Token::Match(tok, "decltype|sizeof|typeof (") && tok->next()->link()) { + tok = tok->next()->link()->previous(); + } else if (tok == inConstExpr) { + inConstExpr = nullptr; + } else if (inConstExpr) { + if (!tok->isIncompleteVar()) + continue; + if (!isExpression(tok->astParent())) + continue; + const std::string& name = getIncompleteNameID(tok); + if (unknownConstantIds.count(name) > 0) + continue; + unknownConstantIds[name] = id++; + } else if (tok->link() && tok->str() == "<") { + inConstExpr = tok->link(); + } else if (Token::Match(tok, "%var% [") && tok->variable() && tok->variable()->nameToken() == tok) { + inConstExpr = tok->next()->link(); + } + } + + auto exprScopes = functionScopes; // functions + global lambdas + std::copy_if(scopeList.front().nestedList.begin(), scopeList.front().nestedList.end(), std::back_inserter(exprScopes), [](const Scope* scope) { + return scope && scope->type == Scope::eLambda; + }); + + for (const Scope * scope : exprScopes) { + std::unordered_map> exprs; + + std::unordered_map unknownIds; + // Assign IDs to incomplete vars which are part of an expression + // Such variables should be assumed global + for (auto* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { + if (!tok->isIncompleteVar()) + continue; + if (!isExpression(tok->astParent())) + continue; + const std::string& name = getIncompleteNameID(tok); + nonneg int sid = 0; + if (unknownConstantIds.count(name) > 0) { + sid = unknownConstantIds.at(name); + tok->isIncompleteConstant(true); + } else if (unknownIds.count(name) == 0) { + sid = id++; + unknownIds[name] = sid; + } else { + sid = unknownIds.at(name); + } + assert(sid > 0); + tok->exprId(sid); + } + + // Assign IDs + ExprIdMap exprIdMap; + std::map> baseIds; + for (auto* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { + if (tok->varId() > 0) { + tok->exprId(tok->varId()); + if (tok->astParent() && tok->astParent()->exprId() == 0) + setParentExprId(tok, exprIdMap, id); + } else if (tok->astParent() && !tok->astOperand1() && !tok->astOperand2()) { + if (tok->tokType() == Token::Type::eBracket) + continue; + if (tok->astParent()->str() == "=") + continue; + if (tok->isControlFlowKeyword()) + continue; + + if (Token::Match(tok, "%name% <") && tok->next()->link()) { + tok->exprId(id); + ++id; + } else { + const auto it = baseIds.find(tok->str()); + if (it != baseIds.end() && compareTokenFlags(tok, it->second.second, /*macro*/ true)) { + tok->exprId(it->second.first); + } else { + baseIds[tok->str()] = { id, tok }; + tok->exprId(id); + ++id; + } + } + + setParentExprId(tok, exprIdMap, id); + } + } + for (Token* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { + if (tok->varId() == 0 && tok->exprId() > 0 && tok->astParent() && !tok->astOperand1() && !tok->astOperand2()) { + if (tok->isNumber() || tok->isKeyword() || Token::Match(tok->astParent(), ".|::") || + (Token::simpleMatch(tok->astParent(), "(") && precedes(tok, tok->astParent()))) + tok->exprId(0); + } + } + } + + // Mark expressions that are unique + std::vector> uniqueExprId(id); + for (Token* tok = mTokenizer.list.front(); tok; tok = tok->next()) { + const auto id2 = tok->exprId(); + if (id2 == 0 || id2 <= maximumVarId) + continue; + uniqueExprId[id2].first = tok; + uniqueExprId[id2].second++; + } + for (const auto& p : uniqueExprId) { + if (!p.first || p.second != 1) + continue; + if (p.first->variable()) { + const Variable* var = p.first->variable(); + if (var->nameToken() != p.first) + continue; + } + p.first->setUniqueExprId(); + } +} + +void SymbolDatabase::setArrayDimensionsUsingValueFlow() +{ + // set all unknown array dimensions + for (const Variable *var : mVariableList) { + // check each array variable + if (!var || !var->isArray()) + continue; + // check each array dimension + for (const Dimension &const_dimension : var->dimensions()) { + auto &dimension = const_cast(const_dimension); + if (dimension.num != 0 || !dimension.tok) + continue; + + if (Token::Match(dimension.tok->previous(), "[<,]")) { + if (dimension.known) + continue; + if (!Token::Match(dimension.tok->previous(), "[<,]")) + continue; + + // In template arguments, there might not be AST + // Determine size by using the "raw tokens" + TokenList tokenList(&mSettings); + tokenList.addtoken(";", 0, 0, 0, false); + bool fail = false; + for (const Token *tok = dimension.tok; tok && !Token::Match(tok, "[,>]"); tok = tok->next()) { + if (!tok->isName()) + tokenList.addtoken(tok->str(), 0, 0, 0, false); + + else if (tok->hasKnownIntValue()) + tokenList.addtoken(std::to_string(tok->getKnownIntValue()), 0, 0, 0, false); + + else { + fail = true; + break; + } + } + + if (fail) + continue; + + tokenList.addtoken(";", 0, 0, 0, false); + + for (Token *tok = tokenList.front(); tok;) { + if (TemplateSimplifier::simplifyNumericCalculations(tok, false)) + tok = tokenList.front(); + else + tok = tok->next(); + } + + if (Token::Match(tokenList.front(), "; %num% ;")) { + dimension.known = true; + dimension.num = MathLib::toBigNumber(tokenList.front()->next()->str()); + } + + continue; + } + + // Normal array [..dimension..] + dimension.known = false; + + // check for a single token dimension + if (dimension.tok->hasKnownIntValue()) { + dimension.known = true; + dimension.num = dimension.tok->getKnownIntValue(); + continue; + } + + if (dimension.tok->valueType() && dimension.tok->valueType()->pointer == 0) { + int bits = 0; + switch (dimension.tok->valueType()->type) { + case ValueType::Type::CHAR: + bits = mSettings.platform.char_bit; + break; + case ValueType::Type::SHORT: + bits = mSettings.platform.short_bit; + break; + case ValueType::Type::INT: + bits = mSettings.platform.int_bit; + break; + case ValueType::Type::LONG: + bits = mSettings.platform.long_bit; + break; + case ValueType::Type::LONGLONG: + bits = mSettings.platform.long_long_bit; + break; + default: + break; + } + + if (bits > 0 && bits <= 62) { + if (dimension.tok->valueType()->sign == ValueType::Sign::UNSIGNED) + dimension.num = 1LL << bits; + else + dimension.num = 1LL << (bits - 1); + } + } + } + } +} + +SymbolDatabase::~SymbolDatabase() +{ + // Clear scope, type, function and variable pointers + for (Token* tok = mTokenizer.list.front(); tok; tok = tok->next()) { + tok->scope(nullptr); + tok->type(nullptr); + tok->function(nullptr); + tok->variable(nullptr); + tok->enumerator(nullptr); + tok->setValueType(nullptr); + } +} + +bool SymbolDatabase::isFunction(const Token *tok, const Scope* outerScope, const Token *&funcStart, const Token *&argStart, const Token*& declEnd) const +{ + if (tok->varId()) + return false; + + // function returning function pointer? '... ( ... %name% ( ... ))( ... ) {' + // function returning reference to array '... ( & %name% ( ... ))[ ... ] {' + // TODO: Activate this again + if ((false) && tok->str() == "(" && tok->strAt(1) != "*" && // NOLINT(readability-simplify-boolean-expr) + (tok->link()->previous()->str() == ")" || Token::simpleMatch(tok->link()->tokAt(-2), ") const"))) { + const Token* tok2 = tok->link()->next(); + if (tok2 && tok2->str() == "(" && Token::Match(tok2->link()->next(), "{|;|const|=")) { + const Token* argStartTok; + if (tok->link()->previous()->str() == "const") + argStartTok = tok->link()->linkAt(-2); + else + argStartTok = tok->link()->linkAt(-1); + funcStart = argStartTok->previous(); + argStart = argStartTok; + declEnd = Token::findmatch(tok2->link()->next(), "{|;"); + return true; + } + if (tok2 && tok2->str() == "[") { + while (tok2 && tok2->str() == "[") + tok2 = tok2->link()->next(); + if (Token::Match(tok2, "{|;|const|=")) { + const Token* argStartTok; + if (tok->link()->previous()->str() == "const") + argStartTok = tok->link()->linkAt(-2); + else + argStartTok = tok->link()->linkAt(-1); + funcStart = argStartTok->previous(); + argStart = argStartTok; + declEnd = Token::findmatch(tok2, "{|;"); + return true; + } + } + } + + else if (!tok->isName() || !tok->next() || !tok->next()->link()) + return false; + + // regular function? + else if (Token::Match(tok, "%name% (") && !isReservedName(tok) && tok->previous() && + (Token::Match(tok->previous(), "%name%|>|&|&&|*|::|~") || // Either a return type or scope qualifier in front of tok + outerScope->isClassOrStructOrUnion())) { // or a ctor/dtor + const Token* tok1 = tok->previous(); + const Token* tok2 = tok->next()->link()->next(); + + if (!Tokenizer::isFunctionHead(tok->next(), ";:{")) + return false; + + // skip over destructor "~" + if (tok1->str() == "~") + tok1 = tok1->previous(); + + // skip over qualification + while (Token::simpleMatch(tok1, "::")) { + tok1 = tok1->previous(); + if (tok1 && tok1->isName()) + tok1 = tok1->previous(); + else if (tok1 && tok1->str() == ">" && tok1->link() && Token::Match(tok1->link()->previous(), "%name%")) + tok1 = tok1->link()->tokAt(-2); + } + + // skip over const, noexcept, throw, override, final and volatile specifiers + while (Token::Match(tok2, "const|noexcept|throw|override|final|volatile|&|&&")) { + tok2 = tok2->next(); + if (tok2 && tok2->str() == "(") + tok2 = tok2->link()->next(); + } + + // skip over trailing return type + bool hasTrailingRet = false; + if (tok2 && tok2->str() == ".") { + hasTrailingRet = true; + for (tok2 = tok2->next(); tok2; tok2 = tok2->next()) { + if (Token::Match(tok2, ";|{|=|override|final")) + break; + if (tok2->link() && Token::Match(tok2, "<|[|(")) + tok2 = tok2->link(); + } + } + + // done if constructor or destructor + if (!Token::Match(tok1, "{|}|;|public:|protected:|private:") && tok1) { + // skip over pointers and references + while (Token::Match(tok1, "%type%|*|&|&&") && !endsWith(tok1->str(), ':') && (!isReservedName(tok1) || tok1->str() == "const")) + tok1 = tok1->previous(); + + // skip over decltype + if (Token::simpleMatch(tok1, ")") && tok1->link() && + Token::simpleMatch(tok1->link()->previous(), "decltype (")) + tok1 = tok1->link()->tokAt(-2); + + // skip over template + if (tok1 && tok1->str() == ">") { + if (tok1->link()) + tok1 = tok1->link()->previous(); + else + return false; + } + + // function can't have number or variable as return type + if (tok1 && (tok1->isNumber() || tok1->varId())) + return false; + + // skip over return type + if (tok1 && tok1->isName()) { + if (tok1->str() == "return") + return false; + if (tok1->str() != "friend") + tok1 = tok1->previous(); + } + + // skip over qualification + while (Token::simpleMatch(tok1, "::")) { + tok1 = tok1->previous(); + if (tok1 && tok1->isName()) + tok1 = tok1->previous(); + else if (tok1 && tok1->str() == ">" && tok1->link() && Token::Match(tok1->link()->previous(), "%name%")) + tok1 = tok1->link()->tokAt(-2); + else if (Token::simpleMatch(tok1, ")") && tok1->link() && + Token::simpleMatch(tok1->link()->previous(), "decltype (")) + tok1 = tok1->link()->tokAt(-2); + } + + // skip over modifiers and other stuff + while (Token::Match(tok1, "const|static|extern|template|virtual|struct|class|enum|%name%")) { + // friend type func(); is not a function + if (tok1->isCpp() && tok1->str() == "friend" && tok2->str() == ";") + return false; + tok1 = tok1->previous(); + } + + // should be at a sequence point if this is a function + if (!Token::Match(tok1, ">|{|}|;|public:|protected:|private:") && tok1) + return false; + } + + if (tok2 && + (Token::Match(tok2, ";|{|=") || + (tok2->isUpperCaseName() && Token::Match(tok2, "%name% ;|{")) || + (tok2->isUpperCaseName() && Token::Match(tok2, "%name% (") && tok2->next()->link()->strAt(1) == "{") || + Token::Match(tok2, ": ::| %name% (|::|<|{") || + Token::Match(tok2, "&|&&| ;|{") || + Token::Match(tok2, "= delete|default ;") || + (hasTrailingRet && Token::Match(tok2, "final|override")))) { + funcStart = tok; + argStart = tok->next(); + declEnd = Token::findmatch(tok2, "{|;"); + return true; + } + } + + // UNKNOWN_MACRO(a,b) { ... } + else if (outerScope->type == Scope::eGlobal && + Token::Match(tok, "%name% (") && + tok->isUpperCaseName() && + Token::simpleMatch(tok->linkAt(1), ") {") && + (!tok->previous() || Token::Match(tok->previous(), "[;{}]"))) { + funcStart = tok; + argStart = tok->next(); + declEnd = tok->linkAt(1)->next(); + return true; + } + + // template constructor? + else if (Token::Match(tok, "%name% <") && Token::simpleMatch(tok->next()->link(), "> (")) { + const Token* tok2 = tok->next()->link()->next()->link(); + if (Token::Match(tok2, ") const| ;|{|=") || + Token::Match(tok2, ") : ::| %name% (|::|<|{") || + Token::Match(tok2, ") const| noexcept {|;|(")) { + funcStart = tok; + argStart = tok2->link(); + declEnd = Token::findmatch(tok2->next(), "{|;"); + return true; + } + } + + // regular C function with missing return or invalid C++ ? + else if (Token::Match(tok, "%name% (") && !isReservedName(tok) && + Token::simpleMatch(tok->linkAt(1), ") {") && + (!tok->previous() || Token::Match(tok->previous(), ";|}"))) { + if (tok->isC()) { + returnImplicitIntError(tok); + funcStart = tok; + argStart = tok->next(); + declEnd = tok->linkAt(1)->next(); + return true; + } + mTokenizer.syntaxError(tok); + } + + return false; +} + +void SymbolDatabase::validateExecutableScopes() const +{ + const std::size_t functions = functionScopes.size(); + for (std::size_t i = 0; i < functions; ++i) { + const Scope* const scope = functionScopes[i]; + const Function* const function = scope->function; + if (scope->isExecutable() && !function) { + const std::list callstack(1, scope->classDef); + const std::string msg = std::string("Executable scope '") + scope->classDef->str() + "' with unknown function."; + const ErrorMessage errmsg(callstack, &mTokenizer.list, Severity::debug, + "symbolDatabaseWarning", + msg, + Certainty::normal); + mErrorLogger.reportErr(errmsg); + } + } +} + +namespace { + const Function* getFunctionForArgumentvariable(const Variable * const var) + { + if (const Scope* scope = var->nameToken()->scope()) { + auto it = std::find_if(scope->functionList.begin(), scope->functionList.end(), [&](const Function& function) { + for (std::size_t arg = 0; arg < function.argCount(); ++arg) { + if (var == function.getArgumentVar(arg)) + return true; + } + return false; + }); + if (it != scope->functionList.end()) + return &*it; + } + return nullptr; + } +} + +void SymbolDatabase::validateVariables() const +{ + for (std::vector::const_iterator iter = mVariableList.cbegin(); iter!=mVariableList.cend(); ++iter) { + const Variable * const var = *iter; + if (var) { + if (!var->scope()) { + const Function* function = getFunctionForArgumentvariable(var); + if (!var->isArgument() || (!function || function->hasBody())) { // variables which only appear in a function declaration do not have a scope + throw InternalError(var->nameToken(), "Analysis failed (variable without scope). If the code is valid then please report this failure.", InternalError::INTERNAL); + } + } + } + } +} + +void SymbolDatabase::validate() const +{ + if (mSettings.debugwarnings) { + validateExecutableScopes(); + } + validateVariables(); +} + +void SymbolDatabase::clangSetVariables(const std::vector &variableList) +{ + mVariableList = variableList; +} + +void SymbolDatabase::debugSymbolDatabase() const +{ + if (!mSettings.debugnormal && !mSettings.debugwarnings) + return; + for (const Token* tok = mTokenizer.list.front(); tok != mTokenizer.list.back(); tok = tok->next()) { + if (tok->astParent() && tok->astParent()->getTokenDebug() == tok->getTokenDebug()) + continue; + if (tok->getTokenDebug() == TokenDebug::ValueType) { + + std::string msg = "Value type is "; + ErrorPath errorPath; + if (tok->valueType()) { + msg += tok->valueType()->str(); + errorPath.insert(errorPath.end(), tok->valueType()->debugPath.cbegin(), tok->valueType()->debugPath.cend()); + + } else { + msg += "missing"; + } + errorPath.emplace_back(tok, ""); + mErrorLogger.reportErr( + {errorPath, &mTokenizer.list, Severity::debug, "valueType", msg, CWE{0}, Certainty::normal}); + } + } +} + +Variable::Variable(const Token *name_, const std::string &clangType, const Token *typeStart, + const Token *typeEnd, nonneg int index_, AccessControl access_, + const Type *type_, const Scope *scope_) + : mNameToken(name_), + mTypeStartToken(typeStart), + mTypeEndToken(typeEnd), + mIndex(index_), + mAccess(access_), + mFlags(0), + mType(type_), + mScope(scope_) +{ + if (!mTypeStartToken && mTypeEndToken) { + mTypeStartToken = mTypeEndToken; + while (Token::Match(mTypeStartToken->previous(), "%type%|*|&")) + mTypeStartToken = mTypeStartToken->previous(); + } + + while (Token::Match(mTypeStartToken, "const|struct|static")) { + if (mTypeStartToken->str() == "static") + setFlag(fIsStatic, true); + mTypeStartToken = mTypeStartToken->next(); + } + + if (Token::simpleMatch(mTypeEndToken, "&")) + setFlag(fIsReference, true); + else if (Token::simpleMatch(mTypeEndToken, "&&")) { + setFlag(fIsReference, true); + setFlag(fIsRValueRef, true); + } + + std::string::size_type pos = clangType.find('['); + if (pos != std::string::npos) { + setFlag(fIsArray, true); + do { + const std::string::size_type pos1 = pos+1; + pos = clangType.find(']', pos1); + Dimension dim; + dim.tok = nullptr; + dim.known = pos > pos1; + if (pos > pos1) + dim.num = MathLib::toBigNumber(clangType.substr(pos1, pos - pos1)); + else + dim.num = 0; + mDimensions.push_back(dim); + ++pos; + } while (pos < clangType.size() && clangType[pos] == '['); + } + + // Is there initialization in variable declaration + const Token *initTok = mNameToken ? mNameToken->next() : nullptr; + while (initTok && initTok->str() == "[") + initTok = initTok->link()->next(); + if (Token::Match(initTok, "=|{") || (initTok && initTok->isSplittedVarDeclEq())) + setFlag(fIsInit, true); +} + +Variable::Variable(const Variable &var, const Scope *scope) +{ + *this = var; + mScope = scope; +} + +Variable::Variable(const Variable &var) +{ + *this = var; +} + +Variable::~Variable() +{ + delete mValueType; +} + +Variable& Variable::operator=(const Variable &var) +{ + if (this == &var) + return *this; + + ValueType* vt = nullptr; + if (var.mValueType) + vt = new ValueType(*var.mValueType); + + mNameToken = var.mNameToken; + mTypeStartToken = var.mTypeStartToken; + mTypeEndToken = var.mTypeEndToken; + mIndex = var.mIndex; + mAccess = var.mAccess; + mFlags = var.mFlags; + mType = var.mType; + mScope = var.mScope; + mDimensions = var.mDimensions; + delete mValueType; + mValueType = vt; + + return *this; +} + +bool Variable::isMember() const { + return mScope && mScope->isClassOrStructOrUnion(); +} + +bool Variable::isPointerArray() const +{ + return isArray() && nameToken() && nameToken()->previous() && (nameToken()->previous()->str() == "*"); +} + +bool Variable::isUnsigned() const +{ + return mValueType ? (mValueType->sign == ValueType::Sign::UNSIGNED) : mTypeStartToken->isUnsigned(); +} + +const Token * Variable::declEndToken() const +{ + Token const * declEnd = typeStartToken(); + while (declEnd && !Token::Match(declEnd, "[;,)={]")) { + if (declEnd->link() && Token::Match(declEnd,"(|[|<")) + declEnd = declEnd->link(); + declEnd = declEnd->next(); + } + return declEnd; +} + +void Variable::evaluate(const Settings* settings) +{ + // Is there initialization in variable declaration + const Token *initTok = mNameToken ? mNameToken->next() : nullptr; + while (Token::Match(initTok, "[")) { + initTok = initTok->link()->next(); + if (Token::simpleMatch(initTok, ")")) + initTok = initTok->next(); + } + if (Token::Match(initTok, "[={(]") || (initTok && initTok->isSplittedVarDeclEq())) + setFlag(fIsInit, true); + + if (!settings) + return; + + const Library & lib = settings->library; + + // TODO: ValueType::parseDecl() is also performing a container lookup + bool isContainer = false; + if (mNameToken) + setFlag(fIsArray, arrayDimensions(*settings, isContainer)); + + if (mTypeStartToken) + setValueType(ValueType::parseDecl(mTypeStartToken,*settings)); + + const Token* tok = mTypeStartToken; + while (tok && tok->previous() && tok->previous()->isName()) + tok = tok->previous(); + const Token* end = mTypeEndToken; + if (end) + end = end->next(); + while (tok != end) { + if (tok->str() == "static") + setFlag(fIsStatic, true); + else if (tok->str() == "extern") + setFlag(fIsExtern, true); + else if (tok->str() == "volatile" || Token::simpleMatch(tok, "std :: atomic <")) + setFlag(fIsVolatile, true); + else if (tok->str() == "mutable") + setFlag(fIsMutable, true); + else if (tok->str() == "const") + setFlag(fIsConst, true); + else if (tok->str() == "constexpr") { + setFlag(fIsConst, true); + setFlag(fIsStatic, true); + } else if (tok->str() == "*") { + setFlag(fIsPointer, !isArray() || (isContainer && !Token::Match(tok->next(), "%name% [")) || Token::Match(tok, "* const| %name% )")); + setFlag(fIsConst, false); // Points to const, isn't necessarily const itself + } else if (tok->str() == "&") { + if (isReference()) + setFlag(fIsRValueRef, true); + setFlag(fIsReference, true); + } else if (tok->str() == "&&") { // Before simplification, && isn't split up + setFlag(fIsRValueRef, true); + setFlag(fIsReference, true); // Set also fIsReference + } + + if (tok->isAttributeMaybeUnused()) { + setFlag(fIsMaybeUnused, true); + } + + if (tok->str() == "<" && tok->link()) + tok = tok->link(); + else + tok = tok->next(); + } + + while (Token::Match(mTypeStartToken, "static|const|constexpr|volatile %any%")) + mTypeStartToken = mTypeStartToken->next(); + while (mTypeEndToken && mTypeEndToken->previous() && Token::Match(mTypeEndToken, "const|volatile")) + mTypeEndToken = mTypeEndToken->previous(); + + if (mTypeStartToken) { + std::string strtype = mTypeStartToken->str(); + for (const Token *typeToken = mTypeStartToken; Token::Match(typeToken, "%type% :: %type%"); typeToken = typeToken->tokAt(2)) + strtype += "::" + typeToken->strAt(2); + setFlag(fIsClass, !lib.podtype(strtype) && !mTypeStartToken->isStandardType() && !isEnumType() && !isPointer() && strtype != "..."); + setFlag(fIsStlType, Token::simpleMatch(mTypeStartToken, "std ::")); + setFlag(fIsStlString, ::isStlStringType(mTypeStartToken)); + setFlag(fIsSmartPointer, mTypeStartToken->isCpp() && lib.isSmartPointer(mTypeStartToken)); + } + if (mAccess == AccessControl::Argument) { + tok = mNameToken; + if (!tok) { + // Argument without name + tok = mTypeEndToken; + // back up to start of array dimensions + while (tok && tok->str() == "]") + tok = tok->link()->previous(); + // add array dimensions if present + if (tok && tok->next()->str() == "[") + setFlag(fIsArray, arrayDimensions(*settings, isContainer)); + } + if (!tok) + return; + tok = tok->next(); + while (tok->str() == "[") + tok = tok->link(); + setFlag(fHasDefault, tok->str() == "="); + } + // check for C++11 member initialization + if (mScope && mScope->isClassOrStruct()) { + // type var = x or + // type var = {x} + // type var = x; gets simplified to: type var ; var = x ; + Token const * declEnd = declEndToken(); + if ((Token::Match(declEnd, "; %name% =") && declEnd->strAt(1) == mNameToken->str()) || + Token::Match(declEnd, "=|{")) + setFlag(fHasDefault, true); + } + + if (mTypeStartToken) { + if (Token::Match(mTypeStartToken, "float|double")) + setFlag(fIsFloatType, true); + } +} + +void Variable::setValueType(const ValueType &valueType) +{ + if (valueType.type == ValueType::Type::UNKNOWN_TYPE) { + const Token *declType = Token::findsimplematch(mTypeStartToken, "decltype (", mTypeEndToken); + if (declType && !declType->next()->valueType()) + return; + } + ValueType* vt = new ValueType(valueType); + delete mValueType; + mValueType = vt; + if ((mValueType->pointer > 0) && (!isArray() || Token::Match(mNameToken->previous(), "( * %name% )"))) + setFlag(fIsPointer, true); + setFlag(fIsConst, mValueType->constness & (1U << mValueType->pointer)); + setFlag(fIsVolatile, mValueType->volatileness & (1U << mValueType->pointer)); + if (mValueType->smartPointerType) + setFlag(fIsSmartPointer, true); +} + +const Type* Variable::smartPointerType() const +{ + if (!isSmartPointer()) + return nullptr; + + if (mValueType->smartPointerType) + return mValueType->smartPointerType; + + // TODO: Cache result, handle more complex type expression + const Token* typeTok = typeStartToken(); + while (Token::Match(typeTok, "%name%|::")) + typeTok = typeTok->next(); + if (Token::Match(typeTok, "< %name% >")) { + // cppcheck-suppress shadowFunction - TODO: fix this + const Scope* scope = typeTok->scope(); + const Type* ptrType{}; + while (scope && !ptrType) { + ptrType = scope->findType(typeTok->next()->str()); + scope = scope->nestedIn; + } + return ptrType; + } + return nullptr; +} + +const Type* Variable::iteratorType() const +{ + if (!mValueType || mValueType->type != ValueType::ITERATOR) + return nullptr; + + if (mValueType->containerTypeToken) + return mValueType->containerTypeToken->type(); + + return nullptr; +} + +bool Variable::isStlStringViewType() const +{ + return getFlag(fIsStlType) && valueType() && valueType()->container && valueType()->container->stdStringLike && valueType()->container->view; +} + +std::string Variable::getTypeName() const +{ + std::string ret; + // TODO: For known types, generate the full type name + for (const Token *typeTok = mTypeStartToken; Token::Match(typeTok, "%name%|::") && typeTok->varId() == 0; typeTok = typeTok->next()) { + ret += typeTok->str(); + if (Token::simpleMatch(typeTok->next(), "<") && typeTok->next()->link()) // skip template arguments + typeTok = typeTok->next()->link(); + } + return ret; +} + +static bool isOperator(const Token *tokenDef) +{ + if (!tokenDef) + return false; + if (tokenDef->isOperatorKeyword()) + return true; + const std::string &name = tokenDef->str(); + return name.size() > 8 && startsWith(name,"operator") && std::strchr("+-*/%&|~^<>!=[(", name[8]); +} + +Function::Function(const Token *tok, + const Scope *scope, + const Token *tokDef, + const Token *tokArgDef) + : tokenDef(tokDef), + argDef(tokArgDef), + nestedIn(scope) +{ + // operator function + if (::isOperator(tokenDef)) { + isOperator(true); + + // 'operator =' is special + if (tokenDef->str() == "operator=") + type = Function::eOperatorEqual; + } + + else if (tokenDef->str() == "[") { + type = Function::eLambda; + } + + // class constructor/destructor + else if (scope->isClassOrStructOrUnion() && + ((tokenDef->str() == scope->className) || + (tokenDef->str().substr(0, scope->className.size()) == scope->className && + tokenDef->str().size() > scope->className.size() + 1 && + tokenDef->str()[scope->className.size() + 1] == '<'))) { + // destructor + if (tokenDef->previous()->str() == "~") + type = Function::eDestructor; + // constructor of any kind + else + type = Function::eConstructor; + + isExplicit(tokenDef->strAt(-1) == "explicit" || tokenDef->strAt(-2) == "explicit"); + } + + const Token *tok1 = setFlags(tok, scope); + + // find the return type + if (!isConstructor() && !isDestructor()) { + // @todo auto type deduction should be checked + // @todo attributes and exception specification can also precede trailing return type + if (Token::Match(argDef->link()->next(), "const|volatile| &|&&| .")) { // Trailing return type + hasTrailingReturnType(true); + if (argDef->link()->strAt(1) == ".") + retDef = argDef->link()->tokAt(2); + else if (argDef->link()->strAt(2) == ".") + retDef = argDef->link()->tokAt(3); + else if (argDef->link()->strAt(3) == ".") + retDef = argDef->link()->tokAt(4); + } else if (!isLambda()) { + if (tok1->str() == ">") + tok1 = tok1->next(); + while (Token::Match(tok1, "extern|virtual|static|friend|struct|union|enum")) + tok1 = tok1->next(); + retDef = tok1; + } + } + + const Token *end = argDef->link(); + + // parse function attributes.. + tok = end->next(); + while (tok) { + if (tok->str() == "const") + isConst(true); + else if (tok->str() == "&") + hasLvalRefQualifier(true); + else if (tok->str() == "&&") + hasRvalRefQualifier(true); + else if (tok->str() == "override") + setFlag(fHasOverrideSpecifier, true); + else if (tok->str() == "final") + setFlag(fHasFinalSpecifier, true); + else if (tok->str() == "volatile") + isVolatile(true); + else if (tok->str() == "noexcept") { + isNoExcept(!Token::simpleMatch(tok->next(), "( false )")); + if (tok->next()->str() == "(") + tok = tok->linkAt(1); + } else if (Token::simpleMatch(tok, "throw (")) { + isThrow(true); + if (tok->strAt(2) != ")") + throwArg = tok->next(); + tok = tok->linkAt(1); + } else if (Token::Match(tok, "= 0|default|delete ;")) { + const std::string& modifier = tok->strAt(1); + isPure(modifier == "0"); + isDefault(modifier == "default"); + isDelete(modifier == "delete"); + } else if (tok->str() == ".") { // trailing return type + // skip over return type + while (tok && !Token::Match(tok->next(), ";|{|override|final")) + tok = tok->next(); + } else + break; + if (tok) + tok = tok->next(); + } + + if (Tokenizer::isFunctionHead(end, ":{")) { + // assume implementation is inline (definition and implementation same) + token = tokenDef; + arg = argDef; + isInline(true); + hasBody(true); + } +} + +Function::Function(const Token *tokenDef, const std::string &clangType) + : tokenDef(tokenDef) +{ + // operator function + if (::isOperator(tokenDef)) { + isOperator(true); + + // 'operator =' is special + if (tokenDef->str() == "operator=") + type = Function::eOperatorEqual; + } + + setFlags(tokenDef, tokenDef->scope()); + + if (endsWith(clangType, " const")) + isConst(true); +} + +const Token *Function::setFlags(const Token *tok1, const Scope *scope) +{ + if (tok1->isInline()) + isInlineKeyword(true); + + // look for end of previous statement + while (tok1->previous() && !Token::Match(tok1->previous(), ";|}|{|public:|protected:|private:")) { + tok1 = tok1->previous(); + + if (tok1->isInline()) + isInlineKeyword(true); + + // extern function + if (tok1->isExternC() || tok1->str() == "extern") { + isExtern(true); + } + + // virtual function + else if (tok1->str() == "virtual") { + hasVirtualSpecifier(true); + } + + // static function + else if (tok1->str() == "static") { + isStatic(true); + if (scope->type == Scope::eNamespace || scope->type == Scope::eGlobal) + isStaticLocal(true); + } + + // friend function + else if (tok1->str() == "friend") { + isFriend(true); + } + + // constexpr function + else if (tok1->str() == "constexpr") { + isConstexpr(true); + } + + // decltype + else if (tok1->str() == ")" && Token::simpleMatch(tok1->link()->previous(), "decltype (")) { + tok1 = tok1->link()->previous(); + } + + else if (tok1->link() && tok1->str() == ">") { + // Function template + if (Token::simpleMatch(tok1->link()->previous(), "template <")) { + templateDef = tok1->link()->previous(); + break; + } + tok1 = tok1->link(); + } + } + return tok1; +} + +std::string Function::fullName() const +{ + std::string ret = name(); + for (const Scope *s = nestedIn; s; s = s->nestedIn) { + if (!s->className.empty()) + ret = s->className + "::" + ret; + } + ret += "("; + for (const Variable &a : argumentList) + ret += (a.index() == 0 ? "" : ",") + a.name(); + return ret + ")"; +} + +static std::string qualifiedName(const Scope *scope) +{ + std::string name = scope->className; + while (scope->nestedIn) { + if (!scope->nestedIn->className.empty()) + name = (scope->nestedIn->className + " :: ") + name; + scope = scope->nestedIn; + } + return name; +} + +static bool usingNamespace(const Scope *scope, const Token *first, const Token *second, int &offset) +{ + // check if qualifications match first before checking if using is needed + const Token *tok1 = first; + const Token *tok2 = second; + bool match = false; + while (Token::Match(tok1, "%type% :: %type%") && Token::Match(tok2, "%type% :: %type%")) { + if (tok1->str() == tok2->str()) { + tok1 = tok1->tokAt(2); + tok2 = tok2->tokAt(2); + match = true; + } else { + match = false; + break; + } + } + + if (match) + return false; + + offset = 0; + std::string name = first->str(); + + while (Token::Match(first, "%type% :: %type%")) { + if (offset) + name += (" :: " + first->str()); + offset += 2; + first = first->tokAt(2); + if (first->str() == second->str()) { + break; + } + } + + if (offset) { + while (scope) { + for (const auto & info : scope->usingList) { + if (info.scope) { + if (name == qualifiedName(info.scope)) + return true; + } + // no scope so get name from using + else { + const Token *start = info.start->tokAt(2); + std::string nsName; + while (start && start->str() != ";") { + if (!nsName.empty()) + nsName += " "; + nsName += start->str(); + start = start->next(); + } + if (nsName == name) + return true; + } + } + scope = scope->nestedIn; + } + } + + return false; +} + +static bool typesMatch( + const Scope *first_scope, + const Token *first_token, + const Scope *second_scope, + const Token *second_token, + const Token *&new_first, + const Token *&new_second) +{ + // get first type + const Type* first_type = first_scope->check->findType(first_token, first_scope, /*lookOutside*/ true); + if (first_type) { + // get second type + const Type* second_type = second_scope->check->findType(second_token, second_scope, /*lookOutside*/ true); + // check if types match + if (first_type == second_type) { + const Token* tok1 = first_token; + while (tok1 && tok1->str() != first_type->name()) + tok1 = tok1->next(); + const Token *tok2 = second_token; + while (tok2 && tok2->str() != second_type->name()) + tok2 = tok2->next(); + // update parser token positions + if (tok1 && tok2) { + new_first = tok1->previous(); + new_second = tok2->previous(); + return true; + } + } + } + return false; +} + +bool Function::argsMatch(const Scope *scope, const Token *first, const Token *second, const std::string &path, nonneg int path_length) const +{ + if (!first->isCpp()) // C does not support overloads + return true; + + int arg_path_length = path_length; + int offset = 0; + int openParen = 0; + + // check for () == (void) and (void) == () + if ((Token::simpleMatch(first, "( )") && Token::simpleMatch(second, "( void )")) || + (Token::simpleMatch(first, "( void )") && Token::simpleMatch(second, "( )"))) + return true; + + auto skipTopLevelConst = [](const Token* start) -> const Token* { + const Token* tok = start->next(); + if (Token::simpleMatch(tok, "const")) { + tok = tok->next(); + while (Token::Match(tok, "%name%|%type%|::")) + tok = tok->next(); + if (Token::Match(tok, ",|)|=")) + return start->next(); + } + return start; + }; + + while (first->str() == second->str() && + first->isLong() == second->isLong() && + first->isUnsigned() == second->isUnsigned()) { + if (first->str() == "(") + openParen++; + + // at end of argument list + else if (first->str() == ")") { + if (openParen == 1) + return true; + --openParen; + } + + // skip optional type information + if (Token::Match(first->next(), "struct|enum|union|class")) + first = first->next(); + if (Token::Match(second->next(), "struct|enum|union|class")) + second = second->next(); + + // skip const on type passed by value + const Token* const oldSecond = second; + first = skipTopLevelConst(first); + second = skipTopLevelConst(second); + + // skip default value assignment + if (oldSecond == second && first->next()->str() == "=") { + first = first->nextArgument(); + if (first) + first = first->tokAt(-2); + if (second->next()->str() == "=") { + second = second->nextArgument(); + if (second) + second = second->tokAt(-2); + if (!first || !second) { // End of argument list (first or second) + return !first && !second; + } + } else if (!first) { // End of argument list (first) + return !second->nextArgument(); // End of argument list (second) + } + } else if (oldSecond == second && second->next()->str() == "=") { + second = second->nextArgument(); + if (second) + second = second->tokAt(-2); + if (!second) { // End of argument list (second) + return !first->nextArgument(); + } + } + + // definition missing variable name + else if ((first->next()->str() == "," && second->next()->str() != ",") || + (Token::Match(first, "!!( )") && second->next()->str() != ")")) { + second = second->next(); + // skip default value assignment + if (second->next()->str() == "=") { + do { + second = second->next(); + } while (!Token::Match(second->next(), ",|)")); + } + } else if (first->next()->str() == "[" && second->next()->str() != "[") + second = second->next(); + + // function missing variable name + else if ((second->next()->str() == "," && first->next()->str() != ",") || + (Token::Match(second, "!!( )") && first->next()->str() != ")")) { + first = first->next(); + // skip default value assignment + if (first->next()->str() == "=") { + do { + first = first->next(); + } while (!Token::Match(first->next(), ",|)")); + } + } else if (second->next()->str() == "[" && first->next()->str() != "[") + first = first->next(); + + // unnamed parameters + else if (Token::Match(first, "(|, %type% ,|)") && Token::Match(second, "(|, %type% ,|)")) { + if (first->next()->expressionString() != second->next()->expressionString()) + break; + first = first->next(); + second = second->next(); + continue; + } + + // argument list has different number of arguments + else if (openParen == 1 && second->str() == ")" && first->str() != ")") + break; + + // check for type * x == type x[] + else if (Token::Match(first->next(), "* %name%| ,|)|=") && + Token::Match(second->next(), "%name%| [ ] ,|)")) { + do { + first = first->next(); + } while (!Token::Match(first->next(), ",|)")); + do { + second = second->next(); + } while (!Token::Match(second->next(), ",|)")); + } + + // const after * + else if (first->next()->str() == "*" && second->next()->str() == "*" && + ((first->strAt(2) != "const" && second->strAt(2) == "const") || + (first->strAt(2) == "const" && second->strAt(2) != "const"))) { + if (first->strAt(2) != "const") { + if (Token::Match(first->tokAt(2), "%name%| ,|)") && Token::Match(second->tokAt(3), "%name%| ,|)")) { + first = first->tokAt(Token::Match(first->tokAt(2), "%name%") ? 2 : 1); + second = second->tokAt(Token::Match(second->tokAt(3), "%name%") ? 3 : 2); + } else { + first = first->next(); + second = second->tokAt(2); + } + } else { + if (Token::Match(second->tokAt(2), "%name%| ,|)") && Token::Match(first->tokAt(3), "%name%| ,|)")) { + first = first->tokAt(Token::Match(first->tokAt(3), "%name%") ? 3 : 2); + second = second->tokAt(Token::Match(second->tokAt(2), "%name%") ? 2 : 1); + } else { + first = first->tokAt(2); + second = second->next(); + } + } + } + + // variable names are different + else if ((Token::Match(first->next(), "%name% ,|)|=|[") && + Token::Match(second->next(), "%name% ,|)|[")) && + (first->next()->str() != second->next()->str())) { + // skip variable names + first = first->next(); + second = second->next(); + + // skip default value assignment + if (first->next()->str() == "=") { + do { + first = first->next(); + } while (!Token::Match(first->next(), ",|)")); + } + } + + // using namespace + else if (usingNamespace(scope, first->next(), second->next(), offset)) + first = first->tokAt(offset); + + // same type with different qualification + else if (typesMatch(scope, first->next(), nestedIn, second->next(), first, second)) + ; + + // variable with class path + else if (arg_path_length && Token::Match(first->next(), "%name%") && first->strAt(1) != "const") { + std::string param = path; + + if (Token::simpleMatch(second->next(), param.c_str(), param.size())) { + // check for redundant qualification before skipping it + if (!Token::simpleMatch(first->next(), param.c_str(), param.size())) { + second = second->tokAt(arg_path_length); + arg_path_length = 0; + } + } + + // nested or base class variable + else if (arg_path_length <= 2 && Token::Match(first->next(), "%name%") && + (Token::Match(second->next(), "%name% :: %name%") || + (Token::Match(second->next(), "%name% <") && + Token::Match(second->linkAt(1), "> :: %name%"))) && + ((second->next()->str() == scope->className) || + (scope->nestedIn && second->next()->str() == scope->nestedIn->className) || + (scope->definedType && scope->definedType->isDerivedFrom(second->next()->str()))) && + (first->next()->str() == second->strAt(3))) { + if (Token::Match(second->next(), "%name% <")) + second = second->linkAt(1)->next(); + else + second = second->tokAt(2); + } + + // remove class name + else if (arg_path_length > 2 && first->strAt(1) != second->strAt(1)) { + std::string short_path = path; + unsigned int short_path_length = arg_path_length; + + // remove last " :: " + short_path.resize(short_path.size() - 4); + short_path_length--; + + // remove last name + std::string::size_type lastSpace = short_path.find_last_of(' '); + if (lastSpace != std::string::npos) { + short_path.resize(lastSpace+1); + short_path_length--; + if (short_path[short_path.size() - 1] == '>') { + short_path.resize(short_path.size() - 3); + while (short_path[short_path.size() - 1] == '<') { + lastSpace = short_path.find_last_of(' '); + short_path.resize(lastSpace+1); + short_path_length--; + } + } + } + + param = std::move(short_path); + if (Token::simpleMatch(second->next(), param.c_str(), param.size())) { + second = second->tokAt(int(short_path_length)); + arg_path_length = 0; + } + } + } + + first = first->next(); + second = second->next(); + + // reset path length + if (first->str() == "," || second->str() == ",") + arg_path_length = path_length; + } + + return false; +} + +static bool isUnknownType(const Token* start, const Token* end) +{ + while (Token::Match(start, "const|volatile")) + start = start->next(); + start = skipScopeIdentifiers(start); + if (start->tokAt(1) == end && !start->type() && !start->isStandardType()) + return true; + // TODO: Try to deduce the type of the expression + if (Token::Match(start, "decltype|typeof")) + return true; + return false; +} + +static const Token* getEnableIfReturnType(const Token* start) +{ + if (!start) + return nullptr; + for (const Token* tok = start->next(); precedes(tok, start->link()); tok = tok->next()) { + if (tok->link() && Token::Match(tok, "(|[|{|<")) { + tok = tok->link(); + continue; + } + if (Token::simpleMatch(tok, ",")) + return tok->next(); + } + return nullptr; +} + +template +static bool checkReturns(const Function* function, bool unknown, bool emptyEnableIf, Predicate pred) +{ + if (!function) + return false; + if (function->type != Function::eFunction && function->type != Function::eOperatorEqual) + return false; + const Token* defStart = function->retDef; + if (!defStart) + return unknown; + const Token* defEnd = function->returnDefEnd(); + if (!defEnd) + return unknown; + if (defEnd == defStart) + return unknown; + if (pred(defStart, defEnd)) + return true; + if (Token::Match(defEnd->tokAt(-1), "*|&|&&")) + return false; + // void STDCALL foo() + while (defEnd->previous() != defStart && Token::Match(defEnd->tokAt(-2), "%name%|> %name%") && + !Token::Match(defEnd->tokAt(-2), "const|volatile")) + defEnd = defEnd->previous(); + // enable_if + const Token* enableIfEnd = nullptr; + if (Token::simpleMatch(defEnd->previous(), ">")) + enableIfEnd = defEnd->previous(); + else if (Token::simpleMatch(defEnd->tokAt(-3), "> :: type")) + enableIfEnd = defEnd->tokAt(-3); + if (enableIfEnd && enableIfEnd->link() && + Token::Match(enableIfEnd->link()->previous(), "enable_if|enable_if_t|EnableIf")) { + if (const Token* start = getEnableIfReturnType(enableIfEnd->link())) { + defStart = start; + defEnd = enableIfEnd; + } else { + return emptyEnableIf; + } + } + assert(defEnd != defStart); + if (pred(defStart, defEnd)) + return true; + if (isUnknownType(defStart, defEnd)) + return unknown; + return false; +} + +bool Function::returnsConst(const Function* function, bool unknown) +{ + return checkReturns(function, unknown, false, [](const Token* defStart, const Token* defEnd) { + return Token::findsimplematch(defStart, "const", defEnd); + }); +} + +bool Function::returnsReference(const Function* function, bool unknown, bool includeRValueRef) +{ + return checkReturns(function, unknown, false, [includeRValueRef](const Token* /*defStart*/, const Token* defEnd) { + return includeRValueRef ? Token::Match(defEnd->previous(), "&|&&") : Token::simpleMatch(defEnd->previous(), "&"); + }); +} + +bool Function::returnsPointer(const Function* function, bool unknown) +{ + return checkReturns(function, unknown, false, [](const Token* /*defStart*/, const Token* defEnd) { + return Token::simpleMatch(defEnd->previous(), "*"); + }); +} + +bool Function::returnsStandardType(const Function* function, bool unknown) +{ + return checkReturns(function, unknown, true, [](const Token* /*defStart*/, const Token* defEnd) { + return defEnd->previous() && defEnd->previous()->isStandardType(); + }); +} + +bool Function::returnsVoid(const Function* function, bool unknown) +{ + return checkReturns(function, unknown, true, [](const Token* /*defStart*/, const Token* defEnd) { + return Token::simpleMatch(defEnd->previous(), "void"); + }); +} + +std::vector Function::findReturns(const Function* f) +{ + std::vector result; + if (!f) + return result; + const Scope* scope = f->functionScope; + if (!scope) + return result; + if (!scope->bodyStart) + return result; + for (const Token* tok = scope->bodyStart->next(); tok && tok != scope->bodyEnd; tok = tok->next()) { + if (tok->str() == "{" && tok->scope() && + (tok->scope()->type == Scope::eLambda || tok->scope()->type == Scope::eClass)) { + tok = tok->link(); + continue; + } + if (Token::simpleMatch(tok->astParent(), "return")) { + result.push_back(tok); + } + // Skip lambda functions since the scope may not be set correctly + const Token* lambdaEndToken = findLambdaEndToken(tok); + if (lambdaEndToken) { + tok = lambdaEndToken; + } + } + return result; +} + +const Token * Function::constructorMemberInitialization() const +{ + if (!isConstructor() || !arg) + return nullptr; + if (Token::simpleMatch(arg->link(), ") :")) + return arg->link()->next(); + if (Token::simpleMatch(arg->link(), ") noexcept (") && arg->link()->linkAt(2)->strAt(1) == ":") + return arg->link()->linkAt(2)->next(); + return nullptr; +} + +bool Function::isSafe(const Settings &settings) const +{ + if (settings.safeChecks.externalFunctions) { + if (nestedIn->type == Scope::ScopeType::eNamespace && token->fileIndex() != 0) + return true; + if (nestedIn->type == Scope::ScopeType::eGlobal && (token->fileIndex() != 0 || !isStatic())) + return true; + } + + if (settings.safeChecks.internalFunctions) { + if (nestedIn->type == Scope::ScopeType::eNamespace && token->fileIndex() == 0) + return true; + if (nestedIn->type == Scope::ScopeType::eGlobal && (token->fileIndex() == 0 || isStatic())) + return true; + } + + if (settings.safeChecks.classes && access == AccessControl::Public && (nestedIn->type == Scope::ScopeType::eClass || nestedIn->type == Scope::ScopeType::eStruct)) + return true; + + return false; +} + +Function* SymbolDatabase::addGlobalFunction(Scope*& scope, const Token*& tok, const Token *argStart, const Token* funcStart) +{ + Function* function = nullptr; + // Lambda functions are always unique + if (tok->str() != "[") { + auto range = scope->functionMap.equal_range(tok->str()); + for (std::multimap::const_iterator it = range.first; it != range.second; ++it) { + const Function *f = it->second; + if (f->hasBody()) + continue; + if (f->argsMatch(scope, f->argDef, argStart, emptyString, 0)) { + function = const_cast(it->second); + break; + } + } + } + + if (!function) + function = addGlobalFunctionDecl(scope, tok, argStart, funcStart); + + function->arg = argStart; + function->token = funcStart; + function->hasBody(true); + + addNewFunction(scope, tok); + + if (scope) { + scope->function = function; + function->functionScope = scope; + return function; + } + return nullptr; +} + +Function* SymbolDatabase::addGlobalFunctionDecl(Scope*& scope, const Token *tok, const Token *argStart, const Token* funcStart) +{ + Function function(tok, scope, funcStart, argStart); + scope->addFunction(std::move(function)); + return &scope->functionList.back(); +} + +void SymbolDatabase::addClassFunction(Scope *&scope, const Token *&tok, const Token *argStart) +{ + const bool destructor(tok->previous()->str() == "~"); + const bool has_const(argStart->link()->strAt(1) == "const"); + const bool lval(argStart->link()->strAt(has_const ? 2 : 1) == "&"); + const bool rval(argStart->link()->strAt(has_const ? 2 : 1) == "&&"); + int count = 0; + std::string path; + unsigned int path_length = 0; + const Token *tok1 = tok; + + if (destructor) + tok1 = tok1->previous(); + + // back up to head of path + while (tok1 && tok1->previous() && tok1->previous()->str() == "::" && tok1->tokAt(-2) && + ((tok1->tokAt(-2)->isName() && !tok1->tokAt(-2)->isStandardType()) || + (tok1->strAt(-2) == ">" && tok1->linkAt(-2) && Token::Match(tok1->linkAt(-2)->previous(), "%name%")))) { + count++; + const Token * tok2 = tok1->tokAt(-2); + if (tok2->str() == ">") + tok2 = tok2->link()->previous(); + + if (tok2) { + do { + path = tok1->previous()->str() + " " + path; + tok1 = tok1->previous(); + path_length++; + } while (tok1 != tok2); + } else + return; // syntax error ? + } + + // syntax error? + if (!tok1) + return; + + // add global namespace if present + if (tok1->strAt(-1) == "::") { + path_length++; + path.insert(0, ":: "); + } + + // search for match + for (std::list::iterator 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::vector::const_iterator it2; + for (it2 = scope1->usingList.cbegin(); it2 != scope1->usingList.cend(); ++it2) { + if (it2->scope) { + Function * func = findFunctionInScope(tok1, it2->scope, path, path_length); + if (func) { + if (!func->hasBody()) { + const Token *closeParen = tok->next()->link(); + if (closeParen) { + const Token *eq = Tokenizer::isFunctionHead(closeParen, ";"); + if (eq && Token::simpleMatch(eq->tokAt(-2), "= default ;")) { + func->isDefault(true); + return; + } + } + func->hasBody(true); + func->token = tok; + func->arg = argStart; + addNewFunction(scope, tok); + if (scope) { + scope->functionOf = func->nestedIn; + scope->function = func; + scope->function->functionScope = scope; + } + return; + } + } + } + } + } + + const bool isAnonymousNamespace = (scope1->type == Scope::eNamespace && scope1->className.empty()); + if ((scope1->className == tok1->str() && (scope1->type != Scope::eFunction)) || isAnonymousNamespace) { + // do the scopes match (same scope) or do their names match (multiple namespaces) + if ((scope == scope1->nestedIn) || (scope && + scope->className == scope1->nestedIn->className && + !scope->className.empty() && + scope->type == scope1->nestedIn->type)) { + + // nested scopes => check that they match + { + const Scope *s1 = scope; + const Scope *s2 = scope1->nestedIn; + while (s1 && s2) { + if (s1->className != s2->className) + break; + s1 = s1->nestedIn; + s2 = s2->nestedIn; + } + // Not matching scopes + if (s1 || s2) + continue; + } + + Scope *scope2 = scope1; + + while (scope2 && count > 1) { + count--; + if (tok1->strAt(1) == "<") + tok1 = tok1->linkAt(1)->tokAt(2); + else + tok1 = tok1->tokAt(2); + scope2 = scope2->findRecordInNestedList(tok1->str()); + } + if (scope2 && isAnonymousNamespace) + scope2 = scope2->findRecordInNestedList(tok1->str()); + + if (count == 1 && scope2) { + match = true; + scope1 = scope2; + } + } + } + + if (match) { + auto range = scope1->functionMap.equal_range(tok->str()); + for (std::multimap::const_iterator it = range.first; it != range.second; ++it) { + auto * func = const_cast(it->second); + if (!func->hasBody()) { + if (func->argsMatch(scope1, func->argDef, tok->next(), path, path_length)) { + const Token *closeParen = tok->next()->link(); + if (closeParen) { + const Token *eq = Tokenizer::isFunctionHead(closeParen, ";"); + if (eq && Token::simpleMatch(eq->tokAt(-2), "= default ;")) { + func->isDefault(true); + return; + } + if (func->type == Function::eDestructor && destructor) { + func->hasBody(true); + } else if (func->type != Function::eDestructor && !destructor) { + // normal function? + const bool hasConstKeyword = closeParen->next()->str() == "const"; + if ((func->isConst() == hasConstKeyword) && + (func->hasLvalRefQualifier() == lval) && + (func->hasRvalRefQualifier() == rval)) { + func->hasBody(true); + } + } + } + + if (func->hasBody()) { + func->token = tok; + func->arg = argStart; + addNewFunction(scope, tok); + if (scope) { + scope->functionOf = scope1; + scope->function = func; + scope->function->functionScope = scope; + } + return; + } + } + } + } + } + } + + // class function of unknown class + addNewFunction(scope, tok); +} + +void SymbolDatabase::addNewFunction(Scope *&scope, const Token *&tok) +{ + const Token *tok1 = tok; + scopeList.emplace_back(this, tok1, scope); + Scope *newScope = &scopeList.back(); + + // find start of function '{' + bool foundInitList = false; + while (tok1 && tok1->str() != "{" && tok1->str() != ";") { + if (tok1->link() && Token::Match(tok1, "(|[|<")) { + tok1 = tok1->link(); + } else if (foundInitList && Token::Match(tok1, "%name%|> {") && Token::Match(tok1->linkAt(1), "} ,|{")) { + tok1 = tok1->linkAt(1); + } else { + if (tok1->str() == ":") + foundInitList = true; + tok1 = tok1->next(); + } + } + + if (tok1 && tok1->str() == "{") { + newScope->setBodyStartEnd(tok1); + + // syntax error? + if (!newScope->bodyEnd) { + mTokenizer.unmatchedToken(tok1); + } else { + scope->nestedList.push_back(newScope); + scope = newScope; + } + } else if (tok1 && Token::Match(tok1->tokAt(-2), "= default|delete ;")) { + scopeList.pop_back(); + } else { + throw InternalError(tok, "Analysis failed (function not recognized). If the code is valid then please report this failure."); + } + tok = tok1; +} + +bool Type::isClassType() const +{ + return classScope && classScope->type == Scope::ScopeType::eClass; +} + +bool Type::isEnumType() const +{ + //We explicitly check for "enum" because a forward declared enum doesn't get its own scope + return (classDef && classDef->str() == "enum") || + (classScope && classScope->type == Scope::ScopeType::eEnum); +} + +bool Type::isStructType() const +{ + return classScope && classScope->type == Scope::ScopeType::eStruct; +} + +bool Type::isUnionType() const +{ + return classScope && classScope->type == Scope::ScopeType::eUnion; +} + +const Token *Type::initBaseInfo(const Token *tok, const Token *tok1) +{ + // goto initial '{' + const Token *tok2 = tok1; + while (tok2 && tok2->str() != "{") { + // skip unsupported templates + if (tok2->str() == "<") + tok2 = tok2->link(); + + // check for base classes + else if (Token::Match(tok2, ":|,")) { + tok2 = tok2->next(); + + // check for invalid code + if (!tok2 || !tok2->next()) + return nullptr; + + Type::BaseInfo base; + + if (tok2->str() == "virtual") { + base.isVirtual = true; + tok2 = tok2->next(); + } + + if (tok2->str() == "public") { + base.access = AccessControl::Public; + tok2 = tok2->next(); + } else if (tok2->str() == "protected") { + base.access = AccessControl::Protected; + tok2 = tok2->next(); + } else if (tok2->str() == "private") { + base.access = AccessControl::Private; + tok2 = tok2->next(); + } else { + if (tok->str() == "class") + base.access = AccessControl::Private; + else if (tok->str() == "struct") + base.access = AccessControl::Public; + } + if (!tok2) + return nullptr; + if (tok2->str() == "virtual") { + base.isVirtual = true; + tok2 = tok2->next(); + } + if (!tok2) + return nullptr; + + base.nameTok = tok2; + // handle global namespace + if (tok2->str() == "::") { + tok2 = tok2->next(); + } + + // handle derived base classes + while (Token::Match(tok2, "%name% ::")) { + tok2 = tok2->tokAt(2); + } + if (!tok2) + return nullptr; + + base.name = tok2->str(); + + tok2 = tok2->next(); + // add unhandled templates + if (tok2 && tok2->link() && tok2->str() == "<") { + for (const Token* const end = tok2->link()->next(); tok2 != end; tok2 = tok2->next()) { + base.name += tok2->str(); + } + } + + const Type * baseType = classScope->check->findType(base.nameTok, enclosingScope); + if (baseType && !baseType->findDependency(this)) + base.type = baseType; + + // save pattern for base class name + derivedFrom.push_back(std::move(base)); + } else + tok2 = tok2->next(); + } + + return tok2; +} + +std::string Type::name() const +{ + const Token* start = classDef->next(); + if (classScope && classScope->enumClass && isEnumType()) + start = start->tokAt(1); + else if (start->str() == "class") + start = start->tokAt(1); + else if (!start->isName()) + return emptyString; + const Token* next = start; + while (Token::Match(next, "::|<|>|(|)|[|]|*|&|&&|%name%")) { + if (Token::Match(next, "<|(|[") && next->link()) + next = next->link(); + next = next->next(); + } + std::string result; + for (const Token* tok = start; tok != next; tok = tok->next()) { + if (!result.empty()) + result += ' '; + result += tok->str(); + } + return result; +} + +void SymbolDatabase::debugMessage(const Token *tok, const std::string &type, const std::string &msg) const +{ + if (tok && mSettings.debugwarnings) { + const std::list locationList(1, tok); + const ErrorMessage errmsg(locationList, &mTokenizer.list, + Severity::debug, + type, + msg, + Certainty::normal); + mErrorLogger.reportErr(errmsg); + } +} + +void SymbolDatabase::returnImplicitIntError(const Token *tok) const +{ + if (tok && mSettings.severity.isEnabled(Severity::portability) && (tok->isC() && mSettings.standards.c != Standards::C89)) { + const std::list locationList(1, tok); + const ErrorMessage errmsg(locationList, &mTokenizer.list, + Severity::portability, + "returnImplicitInt", + "Omitted return type of function '" + tok->str() + "' defaults to int, this is not supported by ISO C99 and later standards.", + Certainty::normal); + mErrorLogger.reportErr(errmsg); + } +} + +const Function* Type::getFunction(const std::string& funcName) const +{ + if (classScope) { + const std::multimap::const_iterator it = classScope->functionMap.find(funcName); + + if (it != classScope->functionMap.end()) + return it->second; + } + + for (const Type::BaseInfo & i : derivedFrom) { + if (i.type) { + const Function* const func = i.type->getFunction(funcName); + if (func) + return func; + } + } + return nullptr; +} + +bool Type::hasCircularDependencies(std::set* ancestors) const +{ + std::set knownAncestors; + if (!ancestors) { + ancestors=&knownAncestors; + } + for (std::vector::const_iterator parent=derivedFrom.cbegin(); parent!=derivedFrom.cend(); ++parent) { + if (!parent->type) + continue; + if (this==parent->type) + return true; + if (ancestors->find(*parent)!=ancestors->end()) + return true; + + ancestors->insert(*parent); + if (parent->type->hasCircularDependencies(ancestors)) + return true; + } + return false; +} + +bool Type::findDependency(const Type* ancestor) const +{ + return this == ancestor || std::any_of(derivedFrom.cbegin(), derivedFrom.cend(), [&](const BaseInfo& d) { + return d.type && (d.type == this || d.type->findDependency(ancestor)); + }); +} + +bool Type::isDerivedFrom(const std::string & ancestor) const +{ + for (std::vector::const_iterator parent=derivedFrom.cbegin(); parent!=derivedFrom.cend(); ++parent) { + if (parent->name == ancestor) + return true; + if (parent->type && parent->type->isDerivedFrom(ancestor)) + return true; + } + return false; +} + +bool Variable::arrayDimensions(const Settings& settings, bool& isContainer) +{ + isContainer = false; + const Library::Container* container = (mTypeStartToken && mTypeStartToken->isCpp()) ? settings.library.detectContainer(mTypeStartToken) : nullptr; + if (container && container->arrayLike_indexOp && container->size_templateArgNo > 0) { + const Token* tok = Token::findsimplematch(mTypeStartToken, "<"); + if (tok) { + isContainer = true; + Dimension dimension_; + tok = tok->next(); + for (int i = 0; i < container->size_templateArgNo && tok; i++) { + tok = tok->nextTemplateArgument(); + } + if (Token::Match(tok, "%num% [,>]")) { + dimension_.tok = tok; + dimension_.known = true; + dimension_.num = MathLib::toBigNumber(tok->str()); + } else if (tok) { + dimension_.tok = tok; + dimension_.known = false; + } + mDimensions.push_back(dimension_); + return true; + } + } + + const Token *dim = mNameToken; + if (!dim) { + // Argument without name + dim = mTypeEndToken; + // back up to start of array dimensions + while (dim && dim->str() == "]") + dim = dim->link()->previous(); + } + if (dim) + dim = dim->next(); + if (dim && dim->str() == ")") + dim = dim->next(); + + bool arr = false; + while (dim && dim->next() && dim->str() == "[") { + Dimension dimension_; + dimension_.known = false; + // check for empty array dimension [] + if (dim->next()->str() != "]") { + dimension_.tok = dim->astOperand2(); + ValueFlow::valueFlowConstantFoldAST(const_cast(dimension_.tok), settings); + if (dimension_.tok && dimension_.tok->hasKnownIntValue()) { + dimension_.num = dimension_.tok->getKnownIntValue(); + dimension_.known = true; + } + } + mDimensions.push_back(dimension_); + dim = dim->link()->next(); + arr = true; + } + return arr; +} + +static std::string scopeTypeToString(Scope::ScopeType type) +{ + switch (type) { + case Scope::ScopeType::eGlobal: + return "Global"; + case Scope::ScopeType::eClass: + return "Class"; + case Scope::ScopeType::eStruct: + return "Struct"; + case Scope::ScopeType::eUnion: + return "Union"; + case Scope::ScopeType::eNamespace: + return "Namespace"; + case Scope::ScopeType::eFunction: + return "Function"; + case Scope::ScopeType::eIf: + return "If"; + case Scope::ScopeType::eElse: + return "Else"; + case Scope::ScopeType::eFor: + return "For"; + case Scope::ScopeType::eWhile: + return "While"; + case Scope::ScopeType::eDo: + return "Do"; + case Scope::ScopeType::eSwitch: + return "Switch"; + case Scope::ScopeType::eTry: + return "Try"; + case Scope::ScopeType::eCatch: + return "Catch"; + case Scope::ScopeType::eUnconditional: + return "Unconditional"; + case Scope::ScopeType::eLambda: + return "Lambda"; + case Scope::ScopeType::eEnum: + return "Enum"; + } + return "Unknown"; +} + +static std::ostream & operator << (std::ostream & s, Scope::ScopeType type) +{ + s << scopeTypeToString(type); + return s; +} + +static std::string accessControlToString(AccessControl access) +{ + switch (access) { + case AccessControl::Public: + return "Public"; + case AccessControl::Protected: + return "Protected"; + case AccessControl::Private: + return "Private"; + case AccessControl::Global: + return "Global"; + case AccessControl::Namespace: + return "Namespace"; + case AccessControl::Argument: + return "Argument"; + case AccessControl::Local: + return "Local"; + case AccessControl::Throw: + return "Throw"; + } + return "Unknown"; +} + +static const char* functionTypeToString(Function::Type type) +{ + switch (type) { + case Function::eConstructor: + return "Constructor"; + case Function::eCopyConstructor: + return "CopyConstructor"; + case Function::eMoveConstructor: + return "MoveConstructor"; + case Function::eOperatorEqual: + return "OperatorEqual"; + case Function::eDestructor: + return "Destructor"; + case Function::eFunction: + return "Function"; + case Function::eLambda: + return "Lambda"; + default: + return "Unknown"; + } +} + +static std::string tokenToString(const Token* tok, const Tokenizer& tokenizer) +{ + std::ostringstream oss; + if (tok) { + oss << tok->str() << " "; + oss << tokenizer.list.fileLine(tok) << " "; + } + oss << tok; + return oss.str(); +} + +static std::string scopeToString(const Scope* scope, const Tokenizer& tokenizer) +{ + std::ostringstream oss; + if (scope) { + oss << scope->type << " "; + if (!scope->className.empty()) + oss << scope->className << " "; + if (scope->classDef) + oss << tokenizer.list.fileLine(scope->classDef) << " "; + } + oss << scope; + return oss.str(); +} + +static std::string tokenType(const Token * tok) +{ + std::ostringstream oss; + if (tok) { + if (tok->isUnsigned()) + oss << "unsigned "; + else if (tok->isSigned()) + oss << "signed "; + if (tok->isComplex()) + oss << "_Complex "; + if (tok->isLong()) + oss << "long "; + oss << tok->str(); + } + return oss.str(); +} + +void SymbolDatabase::printVariable(const Variable *var, const char *indent) const +{ + std::cout << indent << "mNameToken: " << tokenToString(var->nameToken(), mTokenizer) << std::endl; + if (var->nameToken()) { + std::cout << indent << " declarationId: " << var->declarationId() << std::endl; + } + std::cout << indent << "mTypeStartToken: " << tokenToString(var->typeStartToken(), mTokenizer) << std::endl; + std::cout << indent << "mTypeEndToken: " << tokenToString(var->typeEndToken(), mTokenizer) << std::endl; + + if (var->typeStartToken()) { + const Token * autoTok = nullptr; + std::cout << indent << " "; + for (const Token * tok = var->typeStartToken(); tok != var->typeEndToken()->next(); tok = tok->next()) { + std::cout << " " << tokenType(tok); + if (tok->str() == "auto") + autoTok = tok; + } + std::cout << std::endl; + if (autoTok) { + const ValueType * valueType = autoTok->valueType(); + std::cout << indent << " auto valueType: " << valueType << std::endl; + if (var->typeStartToken()->valueType()) { + std::cout << indent << " " << valueType->str() << std::endl; + } + } + } else if (var->valueType()) { + std::cout << indent << " " << var->valueType()->str() << std::endl; + } + std::cout << indent << "mIndex: " << var->index() << std::endl; + std::cout << indent << "mAccess: " << accessControlToString(var->accessControl()) << std::endl; + std::cout << indent << "mFlags: " << std::endl; + std::cout << indent << " isMutable: " << var->isMutable() << std::endl; + std::cout << indent << " isStatic: " << var->isStatic() << std::endl; + std::cout << indent << " isExtern: " << var->isExtern() << std::endl; + std::cout << indent << " isLocal: " << var->isLocal() << std::endl; + std::cout << indent << " isConst: " << var->isConst() << std::endl; + std::cout << indent << " isClass: " << var->isClass() << std::endl; + std::cout << indent << " isArray: " << var->isArray() << std::endl; + std::cout << indent << " isPointer: " << var->isPointer() << std::endl; + std::cout << indent << " isReference: " << var->isReference() << std::endl; + std::cout << indent << " isRValueRef: " << var->isRValueReference() << std::endl; + std::cout << indent << " hasDefault: " << var->hasDefault() << std::endl; + std::cout << indent << " isStlType: " << var->isStlType() << std::endl; + std::cout << indent << "mType: "; + if (var->type()) { + std::cout << var->type()->type() << " " << var->type()->name(); + std::cout << " " << mTokenizer.list.fileLine(var->type()->classDef); + std::cout << " " << var->type() << std::endl; + } else + std::cout << "none" << std::endl; + + if (var->nameToken()) { + const ValueType * valueType = var->nameToken()->valueType(); + std::cout << indent << "valueType: " << valueType << std::endl; + if (valueType) { + std::cout << indent << " " << valueType->str() << std::endl; + } + } + + std::cout << indent << "mScope: " << scopeToString(var->scope(), mTokenizer) << std::endl; + + std::cout << indent << "mDimensions:"; + for (std::size_t i = 0; i < var->dimensions().size(); i++) { + std::cout << " " << var->dimension(i); + if (!var->dimensions()[i].known) + std::cout << "?"; + } + std::cout << std::endl; +} + +void SymbolDatabase::printOut(const char *title) const +{ + std::cout << std::setiosflags(std::ios::boolalpha); + if (title) + std::cout << "\n### " << title << " ###\n"; + + for (std::list::const_iterator scope = scopeList.cbegin(); scope != scopeList.cend(); ++scope) { + std::cout << "Scope: " << &*scope << " " << scope->type << std::endl; + std::cout << " className: " << scope->className << std::endl; + std::cout << " classDef: " << tokenToString(scope->classDef, mTokenizer) << std::endl; + std::cout << " bodyStart: " << tokenToString(scope->bodyStart, mTokenizer) << std::endl; + std::cout << " bodyEnd: " << tokenToString(scope->bodyEnd, mTokenizer) << std::endl; + + // find the function body if not implemented inline + for (auto func = scope->functionList.cbegin(); func != scope->functionList.cend(); ++func) { + std::cout << " Function: " << &*func << std::endl; + std::cout << " name: " << tokenToString(func->tokenDef, mTokenizer) << std::endl; + std::cout << " type: " << functionTypeToString(func->type) << std::endl; + std::cout << " access: " << accessControlToString(func->access) << std::endl; + std::cout << " hasBody: " << func->hasBody() << std::endl; + std::cout << " isInline: " << func->isInline() << std::endl; + std::cout << " isConst: " << func->isConst() << std::endl; + std::cout << " hasVirtualSpecifier: " << func->hasVirtualSpecifier() << std::endl; + std::cout << " isPure: " << func->isPure() << std::endl; + std::cout << " isStatic: " << func->isStatic() << std::endl; + std::cout << " isStaticLocal: " << func->isStaticLocal() << std::endl; + std::cout << " isExtern: " << func->isExtern() << std::endl; + std::cout << " isFriend: " << func->isFriend() << std::endl; + std::cout << " isExplicit: " << func->isExplicit() << std::endl; + std::cout << " isDefault: " << func->isDefault() << std::endl; + std::cout << " isDelete: " << func->isDelete() << std::endl; + std::cout << " hasOverrideSpecifier: " << func->hasOverrideSpecifier() << std::endl; + std::cout << " hasFinalSpecifier: " << func->hasFinalSpecifier() << std::endl; + std::cout << " isNoExcept: " << func->isNoExcept() << std::endl; + std::cout << " isThrow: " << func->isThrow() << std::endl; + std::cout << " isOperator: " << func->isOperator() << std::endl; + std::cout << " hasLvalRefQual: " << func->hasLvalRefQualifier() << std::endl; + std::cout << " hasRvalRefQual: " << func->hasRvalRefQualifier() << std::endl; + std::cout << " isVariadic: " << func->isVariadic() << std::endl; + std::cout << " isVolatile: " << func->isVolatile() << std::endl; + std::cout << " hasTrailingReturnType: " << func->hasTrailingReturnType() << std::endl; + std::cout << " attributes:"; + if (func->isAttributeConst()) + std::cout << " const "; + if (func->isAttributePure()) + std::cout << " pure "; + if (func->isAttributeNoreturn()) + std::cout << " noreturn "; + if (func->isAttributeNothrow()) + std::cout << " nothrow "; + if (func->isAttributeConstructor()) + std::cout << " constructor "; + if (func->isAttributeDestructor()) + std::cout << " destructor "; + if (func->isAttributeNodiscard()) + std::cout << " nodiscard "; + std::cout << std::endl; + std::cout << " noexceptArg: " << (func->noexceptArg ? func->noexceptArg->str() : "none") << std::endl; + std::cout << " throwArg: " << (func->throwArg ? func->throwArg->str() : "none") << std::endl; + std::cout << " tokenDef: " << tokenToString(func->tokenDef, mTokenizer) << std::endl; + std::cout << " argDef: " << tokenToString(func->argDef, mTokenizer) << std::endl; + if (!func->isConstructor() && !func->isDestructor()) + std::cout << " retDef: " << tokenToString(func->retDef, mTokenizer) << std::endl; + if (func->retDef) { + std::cout << " "; + for (const Token * tok = func->retDef; tok && tok != func->tokenDef && !Token::Match(tok, "{|;|override|final"); tok = tok->next()) + std::cout << " " << tokenType(tok); + std::cout << std::endl; + } + std::cout << " retType: " << func->retType << std::endl; + + if (const ValueType* valueType = func->tokenDef->next()->valueType()) { + std::cout << " valueType: " << valueType << std::endl; + std::cout << " " << valueType->str() << std::endl; + } + + if (func->hasBody()) { + std::cout << " token: " << tokenToString(func->token, mTokenizer) << std::endl; + std::cout << " arg: " << tokenToString(func->arg, mTokenizer) << std::endl; + } + std::cout << " nestedIn: " << scopeToString(func->nestedIn, mTokenizer) << std::endl; + std::cout << " functionScope: " << scopeToString(func->functionScope, mTokenizer) << std::endl; + + for (auto var = func->argumentList.cbegin(); var != func->argumentList.cend(); ++var) { + std::cout << " Variable: " << &*var << std::endl; + printVariable(&*var, " "); + } + } + + for (auto var = scope->varlist.cbegin(); var != scope->varlist.cend(); ++var) { + std::cout << " Variable: " << &*var << std::endl; + printVariable(&*var, " "); + } + + if (scope->type == Scope::eEnum) { + std::cout << " enumType: "; + if (scope->enumType) { + std::cout << scope->enumType->stringify(false, true, false); + } else + std::cout << "int"; + std::cout << std::endl; + std::cout << " enumClass: " << scope->enumClass << std::endl; + for (const Enumerator &enumerator : scope->enumeratorList) { + std::cout << " Enumerator: " << enumerator.name->str() << " = "; + if (enumerator.value_known) + std::cout << enumerator.value; + + if (enumerator.start) { + const Token * tok = enumerator.start; + std::cout << (enumerator.value_known ? " " : "") << "[" << tok->str(); + while (tok && tok != enumerator.end) { + if (tok->next()) + std::cout << " " << tok->next()->str(); + tok = tok->next(); + } + + std::cout << "]"; + } + + std::cout << std::endl; + } + } + + std::cout << " nestedIn: " << scope->nestedIn; + if (scope->nestedIn) { + std::cout << " " << scope->nestedIn->type << " " + << scope->nestedIn->className; + } + std::cout << std::endl; + + std::cout << " definedType: " << scope->definedType << std::endl; + + std::cout << " nestedList[" << scope->nestedList.size() << "] = ("; + + std::size_t count = scope->nestedList.size(); + for (std::vector::const_iterator nsi = scope->nestedList.cbegin(); nsi != scope->nestedList.cend(); ++nsi) { + std::cout << " " << (*nsi) << " " << (*nsi)->type << " " << (*nsi)->className; + if (count-- > 1) + std::cout << ","; + } + + std::cout << " )" << std::endl; + + for (auto use = scope->usingList.cbegin(); use != scope->usingList.cend(); ++use) { + std::cout << " using: " << use->scope << " " << use->start->strAt(2); + const Token *tok1 = use->start->tokAt(3); + while (tok1 && tok1->str() == "::") { + std::cout << "::" << tok1->strAt(1); + tok1 = tok1->tokAt(2); + } + std::cout << " " << mTokenizer.list.fileLine(use->start) << std::endl; + } + + std::cout << " functionOf: " << scopeToString(scope->functionOf, mTokenizer) << std::endl; + + std::cout << " function: " << scope->function; + if (scope->function) + std::cout << " " << scope->function->name(); + std::cout << std::endl; + } + + for (std::list::const_iterator type = typeList.cbegin(); type != typeList.cend(); ++type) { + std::cout << "Type: " << &(*type) << std::endl; + std::cout << " name: " << type->name() << std::endl; + std::cout << " classDef: " << tokenToString(type->classDef, mTokenizer) << std::endl; + std::cout << " classScope: " << type->classScope << std::endl; + std::cout << " enclosingScope: " << type->enclosingScope; + if (type->enclosingScope) { + std::cout << " " << type->enclosingScope->type << " " + << type->enclosingScope->className; + } + std::cout << std::endl; + std::cout << " needInitialization: " << (type->needInitialization == Type::NeedInitialization::Unknown ? "Unknown" : + type->needInitialization == Type::NeedInitialization::True ? "True" : + type->needInitialization == Type::NeedInitialization::False ? "False" : + "Invalid") << std::endl; + + std::cout << " derivedFrom[" << type->derivedFrom.size() << "] = ("; + std::size_t count = type->derivedFrom.size(); + for (const Type::BaseInfo & i : type->derivedFrom) { + if (i.isVirtual) + std::cout << "Virtual "; + + std::cout << (i.access == AccessControl::Public ? " Public" : + i.access == AccessControl::Protected ? " Protected" : + i.access == AccessControl::Private ? " Private" : + " Unknown"); + + if (i.type) + std::cout << " " << i.type; + else + std::cout << " Unknown"; + + std::cout << " " << i.name; + if (count-- > 1) + std::cout << ","; + } + + std::cout << " )" << std::endl; + + std::cout << " friendList[" << type->friendList.size() << "] = ("; + for (size_t i = 0; i < type->friendList.size(); i++) { + if (type->friendList[i].type) + std::cout << type->friendList[i].type; + else + std::cout << " Unknown"; + + std::cout << ' '; + if (type->friendList[i].nameEnd) + std::cout << type->friendList[i].nameEnd->str(); + if (i+1 < type->friendList.size()) + std::cout << ','; + } + + std::cout << " )" << std::endl; + } + + for (std::size_t i = 1; i < mVariableList.size(); i++) { + std::cout << "mVariableList[" << i << "]: " << mVariableList[i]; + if (mVariableList[i]) { + std::cout << " " << mVariableList[i]->name() << " " + << mTokenizer.list.fileLine(mVariableList[i]->nameToken()); + } + std::cout << std::endl; + } + std::cout << std::resetiosflags(std::ios::boolalpha); +} + +void SymbolDatabase::printXml(std::ostream &out) const +{ + std::string outs; + + std::set variables; + + // Scopes.. + outs += " \n"; + for (std::list::const_iterator scope = scopeList.cbegin(); scope != scopeList.cend(); ++scope) { + outs += " type); + outs += "\""; + if (!scope->className.empty()) { + outs += " className=\""; + outs += ErrorLogger::toxml(scope->className); + outs += "\""; + } + if (scope->bodyStart) { + outs += " bodyStart=\""; + outs += id_string(scope->bodyStart); + outs += '\"'; + } + if (scope->bodyEnd) { + outs += " bodyEnd=\""; + outs += id_string(scope->bodyEnd); + outs += '\"'; + } + if (scope->nestedIn) { + outs += " nestedIn=\""; + outs += id_string(scope->nestedIn); + outs += "\""; + } + if (scope->function) { + outs += " function=\""; + outs += id_string(scope->function); + outs += "\""; + } + if (scope->definedType) { + outs += " definedType=\""; + outs += id_string(scope->definedType); + outs += "\""; + } + if (scope->functionList.empty() && scope->varlist.empty()) + outs += "/>\n"; + else { + outs += ">\n"; + if (!scope->functionList.empty()) { + outs += " \n"; + for (std::list::const_iterator function = scope->functionList.cbegin(); function != scope->functionList.cend(); ++function) { + outs += " token); + outs += "\" tokenDef=\""; + outs += id_string(function->tokenDef); + outs += "\" name=\""; + outs += ErrorLogger::toxml(function->name()); + outs += '\"'; + outs += " type=\""; + outs += functionTypeToString(function->type); + outs += '\"'; + if (function->nestedIn->definedType) { + if (function->hasVirtualSpecifier()) + outs += " hasVirtualSpecifier=\"true\""; + else if (function->isImplicitlyVirtual()) + outs += " isImplicitlyVirtual=\"true\""; + } + if (function->access == AccessControl::Public || function->access == AccessControl::Protected || function->access == AccessControl::Private) { + outs += " access=\""; + outs += accessControlToString(function->access); + outs +="\""; + } + if (function->isOperator()) + outs += " isOperator=\"true\""; + if (function->isExplicit()) + outs += " isExplicit=\"true\""; + if (function->hasOverrideSpecifier()) + outs += " hasOverrideSpecifier=\"true\""; + if (function->hasFinalSpecifier()) + outs += " hasFinalSpecifier=\"true\""; + if (function->isInlineKeyword()) + outs += " isInlineKeyword=\"true\""; + if (function->isStatic()) + outs += " isStatic=\"true\""; + if (function->isAttributeNoreturn()) + outs += " isAttributeNoreturn=\"true\""; + if (const Function* overriddenFunction = function->getOverriddenFunction()) { + outs += " overriddenFunction=\""; + outs += id_string(overriddenFunction); + outs += "\""; + } + if (function->argCount() == 0U) + outs += "/>\n"; + else { + outs += ">\n"; + for (unsigned int argnr = 0; argnr < function->argCount(); ++argnr) { + const Variable *arg = function->getArgumentVar(argnr); + outs += " \n"; + variables.insert(arg); + } + outs += " \n"; + } + } + outs += " \n"; + } + if (!scope->varlist.empty()) { + outs += " \n"; + for (std::list::const_iterator var = scope->varlist.cbegin(); var != scope->varlist.cend(); ++var) { + outs += " \n"; + } + outs += " \n"; + } + outs += " \n"; + } + } + outs += " \n"; + + if (!typeList.empty()) { + outs += " \n"; + for (const Type& type:typeList) { + outs += " nameToken()); + outs += '\"'; + outs += " typeStartToken=\""; + outs += id_string(var->typeStartToken()); + outs += '\"'; + outs += " typeEndToken=\""; + outs += id_string(var->typeEndToken()); + outs += '\"'; + outs += " access=\""; + outs += accessControlToString(var->mAccess); + outs += '\"'; + outs += " scope=\""; + outs += id_string(var->scope()); + outs += '\"'; + if (var->valueType()) { + outs += " constness=\""; + outs += std::to_string(var->valueType()->constness); + outs += '\"'; + + outs += " volatileness=\""; + outs += std::to_string(var->valueType()->volatileness); + outs += '\"'; + } + outs += " isArray=\""; + outs += bool_to_string(var->isArray()); + outs += '\"'; + outs += " isClass=\""; + outs += bool_to_string(var->isClass()); + outs += '\"'; + outs += " isConst=\""; + outs += bool_to_string(var->isConst()); + outs += '\"'; + outs += " isExtern=\""; + outs += bool_to_string(var->isExtern()); + outs += '\"'; + outs += " isPointer=\""; + outs += bool_to_string(var->isPointer()); + outs += '\"'; + outs += " isReference=\""; + outs += bool_to_string(var->isReference()); + outs += '\"'; + outs += " isStatic=\""; + outs += bool_to_string(var->isStatic()); + outs += '\"'; + outs += " isVolatile=\""; + outs += bool_to_string(var->isVolatile()); + outs += '\"'; + outs += "/>\n"; + } + outs += " \n"; + + out << outs; +} + +//--------------------------------------------------------------------------- + +static const Type* findVariableTypeIncludingUsedNamespaces(const SymbolDatabase* symbolDatabase, const Scope* scope, const Token* typeTok) +{ + const Type* argType = symbolDatabase->findVariableType(scope, typeTok); + if (argType) + return argType; + + // look for variable type in any using namespace in this scope or above + while (scope) { + for (const Scope::UsingInfo &ui : scope->usingList) { + if (ui.scope) { + argType = symbolDatabase->findVariableType(ui.scope, typeTok); + if (argType) + return argType; + } + } + scope = scope->nestedIn; + } + return nullptr; +} + +//--------------------------------------------------------------------------- + +void Function::addArguments(const SymbolDatabase *symbolDatabase, const Scope *scope) +{ + // check for non-empty argument list "( ... )" + const Token * start = arg ? arg : argDef; + if (!Token::simpleMatch(start, "(")) + return; + if (!(start && start->link() != start->next() && !Token::simpleMatch(start, "( void )"))) + return; + + unsigned int count = 0; + + for (const Token* tok = start->next(); tok; tok = tok->next()) { + if (Token::Match(tok, ",|)")) + return; // Syntax error + + const Token* startTok = tok; + const Token* endTok = nullptr; + const Token* nameTok = nullptr; + + do { + if (Token::simpleMatch(tok, "decltype (")) { + tok = tok->linkAt(1)->next(); + continue; + } + if (tok != startTok && !nameTok && Token::Match(tok, "( & %var% ) [")) { + nameTok = tok->tokAt(2); + endTok = nameTok->previous(); + tok = tok->link(); + } else if (tok != startTok && !nameTok && Token::Match(tok, "( * %var% ) (") && Token::Match(tok->link()->linkAt(1), ") [,)]")) { + nameTok = tok->tokAt(2); + endTok = nameTok->previous(); + tok = tok->link()->linkAt(1); + } else if (tok != startTok && !nameTok && Token::Match(tok, "( * %var% ) [")) { + nameTok = tok->tokAt(2); + endTok = nameTok->previous(); + tok = tok->link(); + } else if (tok->varId() != 0) { + nameTok = tok; + endTok = tok->previous(); + } else if (tok->str() == "[") { + // skip array dimension(s) + tok = tok->link(); + while (tok->next()->str() == "[") + tok = tok->next()->link(); + } else if (tok->str() == "<") { + tok = tok->link(); + if (!tok) // something is wrong so just bail out + return; + } + + tok = tok->next(); + + if (!tok) // something is wrong so just bail + return; + } while (tok->str() != "," && tok->str() != ")" && tok->str() != "="); + + const Token *typeTok = startTok; + // skip over stuff to get to type + while (Token::Match(typeTok, "const|volatile|enum|struct|::")) + typeTok = typeTok->next(); + if (Token::Match(typeTok, ",|)")) { // #8333 + symbolDatabase->mTokenizer.syntaxError(typeTok); + } + if (Token::Match(typeTok, "%type% <") && Token::Match(typeTok->linkAt(1), "> :: %type%")) + typeTok = typeTok->linkAt(1)->tokAt(2); + // skip over qualification + while (Token::Match(typeTok, "%type% ::")) { + typeTok = typeTok->tokAt(2); + if (Token::Match(typeTok, "%type% <") && Token::Match(typeTok->linkAt(1), "> :: %type%")) + typeTok = typeTok->linkAt(1)->tokAt(2); + } + + // check for argument with no name or missing varid + if (!endTok) { + if (tok->previous()->isName() && !Token::Match(tok->tokAt(-1), "const|volatile")) { + if (tok->previous() != typeTok) { + nameTok = tok->previous(); + endTok = nameTok->previous(); + + if (hasBody()) + symbolDatabase->debugMessage(nameTok, "varid0", "Function::addArguments found argument \'" + nameTok->str() + "\' with varid 0."); + } else + endTok = typeTok; + } else + endTok = tok->previous(); + } + + const ::Type *argType = nullptr; + if (!typeTok->isStandardType()) { + argType = findVariableTypeIncludingUsedNamespaces(symbolDatabase, scope, typeTok); + + // save type + const_cast(typeTok)->type(argType); + } + + // skip default values + if (tok->str() == "=") { + do { + if (tok->link() && Token::Match(tok, "[{[(<]")) + tok = tok->link(); + tok = tok->next(); + } while (tok->str() != "," && tok->str() != ")"); + } + + // skip over stuff before type + while (Token::Match(startTok, "enum|struct|const|volatile")) + startTok = startTok->next(); + + if (startTok == nameTok) + break; + + argumentList.emplace_back(nameTok, startTok, endTok, count++, AccessControl::Argument, argType, functionScope, &symbolDatabase->mSettings); + + if (tok->str() == ")") { + // check for a variadic function or a variadic template function + if (Token::simpleMatch(endTok, "...")) + isVariadic(true); + + break; + } + } + + // count default arguments + for (const Token* tok = argDef->next(); tok && tok != argDef->link(); tok = tok->next()) { + if (tok->str() == "=") { + initArgCount++; + if (tok->strAt(1) == "[") { + const Token* lambdaStart = tok->next(); + if (type == eLambda) + tok = findLambdaEndTokenWithoutAST(lambdaStart); + else { + tok = findLambdaEndToken(lambdaStart); + if (!tok) + tok = findLambdaEndTokenWithoutAST(lambdaStart); + } + if (!tok) + throw InternalError(lambdaStart, "Analysis failed (lambda not recognized). If the code is valid then please report this failure.", InternalError::INTERNAL); + } + } + } +} + +bool Function::isImplicitlyVirtual(bool defaultVal) const +{ + if (hasVirtualSpecifier()) //If it has the virtual specifier it's definitely virtual + return true; + if (hasOverrideSpecifier()) //If it has the override specifier then it's either virtual or not going to compile + return true; + bool foundAllBaseClasses = true; + if (getOverriddenFunction(&foundAllBaseClasses)) //If it overrides a base class's method then it's virtual + return true; + if (foundAllBaseClasses) //If we've seen all the base classes and none of the above were true then it must not be virtual + return false; + return defaultVal; //If we can't see all the bases classes then we can't say conclusively +} + +std::vector Function::getOverloadedFunctions() const +{ + std::vector result; + const Scope* scope = nestedIn; + + while (scope) { + const bool isMemberFunction = scope->isClassOrStruct() && !isStatic(); + for (std::multimap::const_iterator it = scope->functionMap.find(tokenDef->str()); + it != scope->functionMap.end() && it->first == tokenDef->str(); + ++it) { + const Function* func = it->second; + if (isMemberFunction && isMemberFunction == func->isStatic()) + continue; + result.push_back(func); + } + if (isMemberFunction) + break; + scope = scope->nestedIn; + } + + return result; +} + +const Function *Function::getOverriddenFunction(bool *foundAllBaseClasses) const +{ + if (foundAllBaseClasses) + *foundAllBaseClasses = true; + if (!nestedIn->isClassOrStruct()) + return nullptr; + return getOverriddenFunctionRecursive(nestedIn->definedType, foundAllBaseClasses); +} + +// prevent recursion if base is the same except for different template parameters +static bool isDerivedFromItself(const std::string& thisName, const std::string& baseName) +{ + if (thisName.back() != '>') + return false; + const auto pos = thisName.find('<'); + if (pos == std::string::npos) + return false; + return thisName.compare(0, pos + 1, baseName, 0, pos + 1) == 0; +} + +const Function * Function::getOverriddenFunctionRecursive(const ::Type* baseType, bool *foundAllBaseClasses) const +{ + // check each base class + for (const ::Type::BaseInfo & i : baseType->derivedFrom) { + const ::Type* derivedFromType = i.type; + // check if base class exists in database + if (!derivedFromType || !derivedFromType->classScope) { + if (foundAllBaseClasses) + *foundAllBaseClasses = false; + continue; + } + + const Scope *parent = derivedFromType->classScope; + + // check if function defined in base class + auto range = parent->functionMap.equal_range(tokenDef->str()); + for (std::multimap::const_iterator it = range.first; it != range.second; ++it) { + const Function * func = it->second; + if (func->isImplicitlyVirtual()) { // Base is virtual and of same name + const Token *temp1 = func->tokenDef->previous(); + const Token *temp2 = tokenDef->previous(); + bool match = true; + + // check for matching return parameters + while (!Token::Match(temp1, "virtual|public:|private:|protected:|{|}|;")) { + if (temp1->str() != temp2->str() && + !(temp1->type() && temp2->type() && temp2->type()->isDerivedFrom(temp1->type()->name()))) { + match = false; + break; + } + + temp1 = temp1->previous(); + temp2 = temp2->previous(); + } + + // check for matching function parameters + match = match && argsMatch(baseType->classScope, func->argDef, argDef, emptyString, 0); + + // check for matching cv-ref qualifiers + match = match + && isConst() == func->isConst() + && isVolatile() == func->isVolatile() + && hasRvalRefQualifier() == func->hasRvalRefQualifier() + && hasLvalRefQualifier() == func->hasLvalRefQualifier(); + + // it's a match + if (match) { + return func; + } + } + } + + if (isDestructor()) { + auto it = std::find_if(parent->functionList.begin(), parent->functionList.end(), [](const Function& f) { + return f.isDestructor() && f.isImplicitlyVirtual(); + }); + if (it != parent->functionList.end()) + return &*it; + } + + if (!derivedFromType->derivedFrom.empty() && !derivedFromType->hasCircularDependencies() && !isDerivedFromItself(baseType->classScope->className, i.name)) { + // avoid endless recursion, see #5289 Crash: Stack overflow in isImplicitlyVirtual_rec when checking SVN and + // #5590 with a loop within the class hierarchy. + const Function *func = getOverriddenFunctionRecursive(derivedFromType, foundAllBaseClasses); + if (func) { + return func; + } + } + } + return nullptr; +} + +const Variable* Function::getArgumentVar(nonneg int num) const +{ + if (num < argumentList.size()) { + auto it = argumentList.begin(); + std::advance(it, num); + return &*it; + } + return nullptr; +} + + +//--------------------------------------------------------------------------- + +Scope::Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_, ScopeType type_, const Token *start_) : + check(check_), + classDef(classDef_), + nestedIn(nestedIn_), + type(type_) +{ + setBodyStartEnd(start_); +} + +Scope::Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_) : + check(check_), + classDef(classDef_), + nestedIn(nestedIn_) +{ + const Token *nameTok = classDef; + if (!classDef) { + type = Scope::eGlobal; + } else if (classDef->str() == "class" && classDef->isCpp()) { + type = Scope::eClass; + nameTok = nameTok->next(); + } else if (classDef->str() == "struct") { + type = Scope::eStruct; + nameTok = nameTok->next(); + } else if (classDef->str() == "union") { + type = Scope::eUnion; + nameTok = nameTok->next(); + } else if (classDef->str() == "namespace") { + type = Scope::eNamespace; + nameTok = nameTok->next(); + } else if (classDef->str() == "enum") { + type = Scope::eEnum; + nameTok = nameTok->next(); + if (nameTok->str() == "class") { + enumClass = true; + nameTok = nameTok->next(); + } + } else if (classDef->str() == "[") { + type = Scope::eLambda; + } else { + type = Scope::eFunction; + } + // skip over qualification if present + nameTok = skipScopeIdentifiers(nameTok); + if (nameTok && ((type == Scope::eEnum && Token::Match(nameTok, ":|{")) || nameTok->str() != "{")) // anonymous and unnamed structs/unions don't have a name + className = nameTok->str(); +} + +AccessControl Scope::defaultAccess() const +{ + switch (type) { + case eGlobal: + return AccessControl::Global; + case eClass: + return AccessControl::Private; + case eStruct: + return AccessControl::Public; + case eUnion: + return AccessControl::Public; + case eNamespace: + return AccessControl::Namespace; + default: + return AccessControl::Local; + } +} + +void Scope::addVariable(const Token *token_, const Token *start_, const Token *end_, + AccessControl access_, const Type *type_, const Scope *scope_, const Settings* settings) +{ + // keep possible size_t -> int truncation outside emplace_back() to have a single line + // C4267 VC++ warning instead of several dozens lines + const int varIndex = varlist.size(); + varlist.emplace_back(token_, start_, end_, varIndex, access_, type_, scope_, settings); +} + +// Get variable list.. +void Scope::getVariableList(const Settings& settings) +{ + if (!bodyStartList.empty()) { + for (const Token *bs: bodyStartList) + getVariableList(settings, bs->next(), bs->link()); + } + + // global scope + else if (type == Scope::eGlobal) + getVariableList(settings, check->mTokenizer.tokens(), nullptr); + + // forward declaration + else + return; +} + +void Scope::getVariableList(const Settings& settings, const Token* start, const Token* end) +{ + // Variable declared in condition: if (auto x = bar()) + if (Token::Match(classDef, "if|while ( %type%") && Token::simpleMatch(classDef->next()->astOperand2(), "=")) { + checkVariable(classDef->tokAt(2), defaultAccess(), settings); + } + + AccessControl varaccess = defaultAccess(); + for (const Token *tok = start; tok && tok != end; tok = tok->next()) { + // syntax error? + if (tok->next() == nullptr) + break; + + // Is it a function? + if (tok->str() == "{") { + tok = tok->link(); + continue; + } + + // Is it a nested class or structure? + if (tok->isKeyword() && 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; + } + break; + } + if (tok->isKeyword() && Token::Match(tok, "struct|union {")) { + if (Token::Match(tok->next()->link(), "} %name% ;|[")) { + tok = tok->next()->link()->tokAt(2); + continue; + } + if (Token::simpleMatch(tok->next()->link(), "} ;")) { + tok = tok->next(); + continue; + } + } + + // Borland C++: Skip all variables in the __published section. + // These are automatically initialized. + else if (tok->str() == "__published:") { + for (; tok; tok = tok->next()) { + if (tok->str() == "{") + tok = tok->link(); + if (Token::Match(tok->next(), "private:|protected:|public:")) + break; + } + if (tok) + continue; + break; + } + + // "private:" "public:" "protected:" etc + else if (tok->str() == "public:") { + varaccess = AccessControl::Public; + continue; + } else if (tok->str() == "protected:") { + varaccess = AccessControl::Protected; + continue; + } else if (tok->str() == "private:") { + varaccess = AccessControl::Private; + continue; + } + + // Is it a forward declaration? + else if (tok->isKeyword() && Token::Match(tok, "class|struct|union %name% ;")) { + tok = tok->tokAt(2); + continue; + } + + // Borland C++: Ignore properties.. + else if (tok->str() == "__property") + continue; + + // skip return, goto and delete + else if (tok->isKeyword() && Token::Match(tok, "return|delete|goto")) { + while (tok->next() && + tok->next()->str() != ";" && + tok->next()->str() != "}" /* ticket #4994 */) { + tok = tok->next(); + } + continue; + } + + // skip case/default + if (tok->isKeyword() && Token::Match(tok, "case|default")) { + while (tok->next() && !Token::Match(tok->next(), "[:;{}]")) + tok = tok->next(); + continue; + } + + // Search for start of statement.. + if (tok->previous() && !Token::Match(tok->previous(), ";|{|}|public:|protected:|private:")) + continue; + if (tok->str() == ";") + continue; + + tok = checkVariable(tok, varaccess, settings); + + if (!tok) + break; + } +} + +const Token *Scope::checkVariable(const Token *tok, AccessControl varaccess, const Settings& settings) +{ + // Is it a throw..? + if (tok->isKeyword() && Token::Match(tok, "throw %any% (") && + Token::simpleMatch(tok->linkAt(2), ") ;")) { + return tok->linkAt(2); + } + + if (tok->isKeyword() && Token::Match(tok, "throw %any% :: %any% (") && + Token::simpleMatch(tok->linkAt(4), ") ;")) { + return tok->linkAt(4); + } + + // friend? + if (tok->isKeyword() && Token::Match(tok, "friend %type%") && tok->next()->varId() == 0) { + const Token *next = Token::findmatch(tok->tokAt(2), ";|{"); + if (next && next->str() == "{") + next = next->link(); + return next; + } + + // skip const|volatile|static|mutable|extern + while (tok && tok->isKeyword() && Token::Match(tok, "const|constexpr|volatile|static|mutable|extern")) { + tok = tok->next(); + } + + // the start of the type tokens does not include the above modifiers + const Token *typestart = tok; + + // C++17 structured bindings + if (tok && tok->isCpp() && (settings.standards.cpp >= Standards::CPP17) && Token::Match(tok, "auto &|&&| [")) { + const Token *typeend = Token::findsimplematch(typestart, "[")->previous(); + for (tok = typeend->tokAt(2); Token::Match(tok, "%name%|,"); tok = tok->next()) { + if (tok->varId()) + addVariable(tok, typestart, typeend, varaccess, nullptr, this, &settings); + } + return typeend->linkAt(1); + } + + while (tok && tok->isKeyword() && Token::Match(tok, "class|struct|union|enum")) { + tok = tok->next(); + } + + // This is the start of a statement + const Token *vartok = nullptr; + const Token *typetok = nullptr; + + if (tok && isVariableDeclaration(tok, vartok, typetok)) { + // If the vartok was set in the if-blocks above, create a entry for this variable.. + tok = vartok->next(); + while (Token::Match(tok, "[|{")) + tok = tok->link()->next(); + + if (vartok->varId() == 0) { + if (!vartok->isBoolean()) + check->debugMessage(vartok, "varid0", "Scope::checkVariable found variable \'" + vartok->str() + "\' with varid 0."); + return tok; + } + + const Type *vType = nullptr; + + if (typetok) { + vType = findVariableTypeIncludingUsedNamespaces(check, this, typetok); + + const_cast(typetok)->type(vType); + } + + // skip "enum" or "struct" + if (Token::Match(typestart, "enum|struct")) + typestart = typestart->next(); + + addVariable(vartok, typestart, vartok->previous(), varaccess, vType, this, &settings); + } + + return tok; +} + +const Variable *Scope::getVariable(const std::string &varname) const +{ + auto it = std::find_if(varlist.begin(), varlist.end(), [&varname](const Variable& var) { + return var.name() == varname; + }); + if (it != varlist.end()) + return &*it; + + if (definedType) { + for (const Type::BaseInfo& baseInfo: definedType->derivedFrom) { + if (baseInfo.type && baseInfo.type->classScope) { + if (const Variable* var = baseInfo.type->classScope->getVariable(varname)) + return var; + } + } + } + return nullptr; +} + +static const Token* skipPointers(const Token* tok) +{ + while (Token::Match(tok, "*|&|&&") || (Token::Match(tok, "( [*&]") && Token::Match(tok->link()->next(), "(|["))) { + tok = tok->next(); + if (tok && tok->strAt(-1) == "(" && Token::Match(tok, "%type% ::")) + tok = tok->tokAt(2); + } + + if (Token::simpleMatch(tok, "( *") && Token::simpleMatch(tok->link()->previous(), "] ) ;")) { + const Token *tok2 = skipPointers(tok->next()); + if (Token::Match(tok2, "%name% [") && Token::simpleMatch(tok2->linkAt(1), "] ) ;")) + return tok2; + } + + return tok; +} + +static const Token* skipPointersAndQualifiers(const Token* tok) +{ + tok = skipPointers(tok); + while (Token::Match(tok, "const|static|volatile")) { + tok = tok->next(); + tok = skipPointers(tok); + } + + return tok; +} + +bool Scope::isVariableDeclaration(const Token* const tok, const Token*& vartok, const Token*& typetok) const +{ + if (!tok) + return false; + + const bool isCPP = tok->isCpp(); + + if (isCPP && Token::Match(tok, "throw|new")) + return false; + + const bool isCPP11 = isCPP && check->mSettings.standards.cpp >= Standards::CPP11; + + if (isCPP11 && tok->str() == "using") + return false; + + const Token* localTypeTok = skipScopeIdentifiers(tok); + const Token* localVarTok = nullptr; + + while (Token::simpleMatch(localTypeTok, "alignas (") && Token::Match(localTypeTok->linkAt(1), ") %name%")) + localTypeTok = localTypeTok->linkAt(1)->next(); + + if (Token::Match(localTypeTok, "%type% <")) { + if (Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast <")) + return false; + + const Token* closeTok = localTypeTok->next()->link(); + if (closeTok) { + localVarTok = skipPointers(closeTok->next()); + + if (Token::Match(localVarTok, ":: %type% %name% [;=({]")) { + if (localVarTok->strAt(3) != "(" || + Token::Match(localVarTok->linkAt(3), "[)}] ;")) { + localTypeTok = localVarTok->next(); + localVarTok = localVarTok->tokAt(2); + } + } + } + } else if (Token::Match(localTypeTok, "%type%")) { + + if (isCPP11 && Token::simpleMatch(localTypeTok, "decltype (") && Token::Match(localTypeTok->linkAt(1), ") %name%|*|&|&&")) + localVarTok = skipPointersAndQualifiers(localTypeTok->linkAt(1)->next()); + else { + localVarTok = skipPointersAndQualifiers(localTypeTok->next()); + if (isCPP11 && Token::simpleMatch(localVarTok, "decltype (") && Token::Match(localVarTok->linkAt(1), ") %name%|*|&|&&")) + localVarTok = skipPointersAndQualifiers(localVarTok->linkAt(1)->next()); + } + } + + if (!localVarTok) + return false; + + while (Token::Match(localVarTok, "const|*|&")) + localVarTok = localVarTok->next(); + + if (Token::Match(localVarTok, "%name% ;|=") || (localVarTok && localVarTok->varId() && localVarTok->strAt(1) == ":")) { + vartok = localVarTok; + typetok = localTypeTok; + } else if (Token::Match(localVarTok, "%name% )|[") && localVarTok->str() != "operator") { + vartok = localVarTok; + typetok = localTypeTok; + } else if (localVarTok && localVarTok->varId() && Token::Match(localVarTok, "%name% (|{") && + Token::Match(localVarTok->next()->link(), ")|} ;")) { + vartok = localVarTok; + typetok = localTypeTok; + } else if (type == eCatch && + Token::Match(localVarTok, "%name% )")) { + vartok = localVarTok; + typetok = localTypeTok; + } + + return nullptr != vartok; +} + +const Token * Scope::addEnum(const Token * tok) +{ + const Token * tok2 = tok->next(); + + // skip over class if present + if (tok2->isCpp() && tok2->str() == "class") + tok2 = tok2->next(); + + // skip over name + tok2 = tok2->next(); + + // save type if present + if (tok2->str() == ":") { + tok2 = tok2->next(); + + enumType = tok2; + while (Token::Match(tok2, "%name%|::")) + tok2 = tok2->next(); + } + + // add enumerators + if (tok2->str() == "{") { + const Token * end = tok2->link(); + tok2 = tok2->next(); + + while (Token::Match(tok2, "%name% =|,|}") || + (Token::Match(tok2, "%name% (") && Token::Match(tok2->linkAt(1), ") ,|}"))) { + Enumerator enumerator(this); + + // save enumerator name + enumerator.name = tok2; + + // skip over name + tok2 = tok2->next(); + + if (tok2->str() == "=") { + // skip over "=" + tok2 = tok2->next(); + + if (tok2->str() == "}") + return nullptr; + + enumerator.start = tok2; + + while (!Token::Match(tok2, ",|}")) { + if (tok2->link()) + tok2 = tok2->link(); + enumerator.end = tok2; + tok2 = tok2->next(); + } + } else if (tok2->str() == "(") { + // skip over unknown macro + tok2 = tok2->link()->next(); + } + + if (tok2->str() == ",") { + enumeratorList.push_back(enumerator); + tok2 = tok2->next(); + } else if (tok2->str() == "}") { + enumeratorList.push_back(enumerator); + break; + } + } + + if (tok2 == end) { + tok2 = tok2->next(); + + if (tok2 && tok2->str() != ";" && (tok2->isCpp() || tok2->str() != ")")) + tok2 = nullptr; + } else + tok2 = nullptr; + } else + tok2 = nullptr; + + return tok2; +} + +static const Scope* findEnumScopeInBase(const Scope* scope, const std::string& tokStr) +{ + if (scope->definedType) { + const std::vector& derivedFrom = scope->definedType->derivedFrom; + for (const Type::BaseInfo& i : derivedFrom) { + const Type *derivedFromType = i.type; + if (derivedFromType && derivedFromType->classScope) { + if (const Scope* enumScope = derivedFromType->classScope->findRecordInNestedList(tokStr)) + return enumScope; + } + } + } + return nullptr; +} + +const Enumerator * SymbolDatabase::findEnumerator(const Token * tok, std::set& tokensThatAreNotEnumeratorValues) const +{ + if (tok->isKeyword()) + return nullptr; + + const std::string& tokStr = tok->str(); + const Scope* scope = tok->scope(); + + // check for qualified name + if (tok->strAt(-1) == "::") { + // find first scope + const Token *tok1 = tok; + while (Token::Match(tok1->tokAt(-2), "%name% ::")) + tok1 = tok1->tokAt(-2); + + if (tok1->strAt(-1) == "::") + scope = &scopeList.front(); + else { + const Scope* temp = nullptr; + if (scope) + temp = scope->findRecordInNestedList(tok1->str()); + // find first scope + while (scope && scope->nestedIn) { + if (!temp) + temp = scope->nestedIn->findRecordInNestedList(tok1->str()); + if (!temp && scope->functionOf) { + temp = scope->functionOf->findRecordInNestedList(tok1->str()); + const Scope* nested = scope->functionOf->nestedIn; + while (!temp && nested) { + temp = nested->findRecordInNestedList(tok1->str()); + nested = nested->nestedIn; + } + } + if (!temp) + temp = findEnumScopeInBase(scope, tok1->str()); + if (temp) { + scope = temp; + break; + } + scope = scope->nestedIn; + } + } + + if (scope) { + tok1 = tok1->tokAt(2); + while (scope && Token::Match(tok1, "%name% ::")) { + scope = scope->findRecordInNestedList(tok1->str()); + tok1 = tok1->tokAt(2); + } + + if (scope) { + const Enumerator * enumerator = scope->findEnumerator(tokStr); + + if (enumerator) // enum class + return enumerator; + // enum + for (std::vector::const_iterator it = scope->nestedList.cbegin(), end = scope->nestedList.cend(); it != end; ++it) { + enumerator = (*it)->findEnumerator(tokStr); + + if (enumerator && !(enumerator->scope && enumerator->scope->enumClass)) + return enumerator; + } + } + } + } else { // unqualified name + + if (tokensThatAreNotEnumeratorValues.find(tokStr) != tokensThatAreNotEnumeratorValues.end()) + return nullptr; + + if (tok->scope()->type == Scope::eGlobal) { + const Token* astTop = tok->astTop(); + if (Token::simpleMatch(astTop, ":") && Token::simpleMatch(astTop->astOperand1(), "(")) { // ctor init list + const Token* ctor = astTop->astOperand1()->previous(); + if (ctor && ctor->function() && ctor->function()->nestedIn) + scope = ctor->function()->nestedIn; + } + } + const Enumerator * enumerator = scope->findEnumerator(tokStr); + + if (enumerator && !(enumerator->scope && enumerator->scope->enumClass)) + return enumerator; + + if (Token::simpleMatch(tok->astParent(), ".")) { + const Token* varTok = tok->astParent()->astOperand1(); + if (varTok && varTok->variable() && varTok->variable()->type() && varTok->variable()->type()->classScope) + scope = varTok->variable()->type()->classScope; + } + else if (Token::simpleMatch(tok->astParent(), "[")) { + const Token* varTok = tok->astParent()->previous(); + if (varTok && varTok->variable() && varTok->variable()->scope() && Token::simpleMatch(tok->astParent()->astOperand1(), "::")) + scope = varTok->variable()->scope(); + } + + for (std::vector::const_iterator s = scope->nestedList.cbegin(); s != scope->nestedList.cend(); ++s) { + enumerator = (*s)->findEnumerator(tokStr); + + if (enumerator && !(enumerator->scope && enumerator->scope->enumClass)) + return enumerator; + } + + if (scope->definedType) { + const std::vector & derivedFrom = scope->definedType->derivedFrom; + for (const Type::BaseInfo & i : derivedFrom) { + const Type *derivedFromType = i.type; + if (derivedFromType && derivedFromType->classScope) { + enumerator = derivedFromType->classScope->findEnumerator(tokStr); + + if (enumerator && !(enumerator->scope && enumerator->scope->enumClass)) + return enumerator; + } + } + } + + while (scope->nestedIn) { + if (scope->type == Scope::eFunction && scope->functionOf) + scope = scope->functionOf; + else + scope = scope->nestedIn; + + enumerator = scope->findEnumerator(tokStr); + + if (enumerator && !(enumerator->scope && enumerator->scope->enumClass)) + return enumerator; + + for (std::vector::const_iterator s = scope->nestedList.cbegin(); s != scope->nestedList.cend(); ++s) { + enumerator = (*s)->findEnumerator(tokStr); + + if (enumerator && !(enumerator->scope && enumerator->scope->enumClass)) + return enumerator; + } + } + } + + tokensThatAreNotEnumeratorValues.insert(tokStr); + + return nullptr; +} + +//--------------------------------------------------------------------------- + +const Type* SymbolDatabase::findVariableTypeInBase(const Scope* scope, const Token* typeTok) +{ + if (scope && scope->definedType && !scope->definedType->derivedFrom.empty()) { + const std::vector &derivedFrom = scope->definedType->derivedFrom; + for (const Type::BaseInfo & i : derivedFrom) { + const Type *base = i.type; + if (base && base->classScope) { + if (base->classScope == scope) + return nullptr; + const Type * type = base->classScope->findType(typeTok->str()); + if (type) + return type; + type = findVariableTypeInBase(base->classScope, typeTok); + if (type) + return type; + } + } + } + + return nullptr; +} + +//--------------------------------------------------------------------------- + +const Type* SymbolDatabase::findVariableType(const Scope *start, const Token *typeTok) const +{ + const Scope *scope = start; + + // check if type does not have a namespace + if (typeTok->strAt(-1) != "::" && typeTok->strAt(1) != "::") { + // check if type same as scope + if (start->isClassOrStruct() && typeTok->str() == start->className) + return start->definedType; + + while (scope) { + // look for type in this scope + const Type * type = scope->findType(typeTok->str()); + + if (type) + return type; + + // look for type in base classes if possible + if (scope->isClassOrStruct()) { + type = findVariableTypeInBase(scope, typeTok); + + if (type) + return type; + } + + // check if in member function class to see if it's present in class + if (scope->type == Scope::eFunction && scope->functionOf) { + const Scope *scope1 = scope->functionOf; + + type = scope1->findType(typeTok->str()); + + if (type) + return type; + + type = findVariableTypeInBase(scope1, typeTok); + + if (type) + return type; + } + + scope = scope->nestedIn; + } + } + + // check for a qualified name and use it when given + else if (typeTok->strAt(-1) == "::") { + // check if type is not part of qualification + if (typeTok->strAt(1) == "::") + return nullptr; + + // find start of qualified function name + const Token *tok1 = typeTok; + + while ((Token::Match(tok1->tokAt(-2), "%type% ::") && !tok1->tokAt(-2)->isKeyword()) || + (Token::simpleMatch(tok1->tokAt(-2), "> ::") && tok1->linkAt(-2) && Token::Match(tok1->linkAt(-2)->tokAt(-1), "%type%"))) { + if (tok1->strAt(-1) == "::") + tok1 = tok1->tokAt(-2); + else + tok1 = tok1->linkAt(-2)->tokAt(-1); + } + + // check for global scope + if (tok1->strAt(-1) == "::") { + scope = &scopeList.front(); + + scope = scope->findRecordInNestedList(tok1->str()); + } + + // find start of qualification + else { + while (scope) { + if (scope->className == tok1->str()) + break; + + const Scope *scope1 = scope->findRecordInNestedList(tok1->str()); + + if (scope1) { + scope = scope1; + break; + } + if (scope->type == Scope::eFunction && scope->functionOf) + scope = scope->functionOf; + else + scope = scope->nestedIn; + } + } + + if (scope) { + // follow qualification + while (scope && (Token::Match(tok1, "%type% ::") || + (Token::Match(tok1, "%type% <") && Token::simpleMatch(tok1->linkAt(1), "> ::")))) { + if (tok1->strAt(1) == "::") + tok1 = tok1->tokAt(2); + else + tok1 = tok1->linkAt(1)->tokAt(2); + const Scope * temp = scope->findRecordInNestedList(tok1->str()); + if (!temp) { + // look in base classes + const Type * type = findVariableTypeInBase(scope, tok1); + + if (type) + return type; + } + scope = temp; + } + + if (scope && scope->definedType) + return scope->definedType; + } + } + + return nullptr; +} + +static bool hasEmptyCaptureList(const Token* tok) { + if (!Token::simpleMatch(tok, "{")) + return false; + const Token* listTok = tok->astParent(); + if (Token::simpleMatch(listTok, "(")) + listTok = listTok->astParent(); + return Token::simpleMatch(listTok, "[ ]"); +} + +bool Scope::hasInlineOrLambdaFunction() const +{ + return std::any_of(nestedList.begin(), nestedList.end(), [&](const Scope* s) { + // Inline function + if (s->type == Scope::eUnconditional && Token::simpleMatch(s->bodyStart->previous(), ") {")) + return true; + // Lambda function + if (s->type == Scope::eLambda && !hasEmptyCaptureList(s->bodyStart)) + return true; + if (s->hasInlineOrLambdaFunction()) + return true; + return false; + }); +} + +void Scope::findFunctionInBase(const std::string & name, nonneg int args, std::vector & matches) const +{ + if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) { + const std::vector &derivedFrom = definedType->derivedFrom; + for (const Type::BaseInfo & i : derivedFrom) { + const Type *base = i.type; + if (base && base->classScope) { + if (base->classScope == this) // Ticket #5120, #5125: Recursive class; tok should have been found already + continue; + + auto range = base->classScope->functionMap.equal_range(name); + for (std::multimap::const_iterator it = range.first; it != range.second; ++it) { + const Function *func = it->second; + if ((func->isVariadic() && args >= (func->argCount() - 1)) || + (args == func->argCount() || (args < func->argCount() && args >= func->minArgCount()))) { + matches.push_back(func); + } + } + + base->classScope->findFunctionInBase(name, args, matches); + } + } + } +} + +const Scope *Scope::findRecordInBase(const std::string & name) const +{ + if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) { + const std::vector &derivedFrom = definedType->derivedFrom; + for (const Type::BaseInfo & i : derivedFrom) { + const Type *base = i.type; + if (base && base->classScope) { + if (base->classScope == this) // Recursive class; tok should have been found already + continue; + + if (base->name() == name) { + return base->classScope; + } + + const ::Type * t = base->classScope->findType(name); + if (t) + return t->classScope; + } + } + } + + return nullptr; +} + +std::vector Scope::findAssociatedScopes() const +{ + std::vector result = {this}; + if (isClassOrStruct() && definedType && !definedType->derivedFrom.empty()) { + const std::vector& derivedFrom = definedType->derivedFrom; + for (const Type::BaseInfo& i : derivedFrom) { + const Type* base = i.type; + if (base && base->classScope) { + if (contains(result, base->classScope)) + continue; + std::vector baseScopes = base->classScope->findAssociatedScopes(); + result.insert(result.end(), baseScopes.cbegin(), baseScopes.cend()); + } + } + } + return result; +} + +//--------------------------------------------------------------------------- + +static void checkVariableCallMatch(const Variable* callarg, const Variable* funcarg, size_t& same, size_t& fallback1, size_t& fallback2) +{ + if (callarg) { + const ValueType::MatchResult res = ValueType::matchParameter(callarg->valueType(), callarg, funcarg); + if (res == ValueType::MatchResult::SAME) { + same++; + return; + } + if (res == ValueType::MatchResult::FALLBACK1) { + fallback1++; + return; + } + if (res == ValueType::MatchResult::FALLBACK2) { + fallback2++; + return; + } + if (res == ValueType::MatchResult::NOMATCH) + return; + + const bool ptrequals = callarg->isArrayOrPointer() == funcarg->isArrayOrPointer(); + const bool constEquals = !callarg->isArrayOrPointer() || ((callarg->typeStartToken()->strAt(-1) == "const") == (funcarg->typeStartToken()->strAt(-1) == "const")); + if (ptrequals && constEquals && + callarg->typeStartToken()->str() == funcarg->typeStartToken()->str() && + callarg->typeStartToken()->isUnsigned() == funcarg->typeStartToken()->isUnsigned() && + callarg->typeStartToken()->isLong() == funcarg->typeStartToken()->isLong()) { + same++; + } else if (callarg->isArrayOrPointer()) { + if (ptrequals && constEquals && funcarg->typeStartToken()->str() == "void") + fallback1++; + else if (constEquals && funcarg->isStlStringType() && Token::Match(callarg->typeStartToken(), "char|wchar_t")) + fallback2++; + } else if (ptrequals) { + const bool takesInt = Token::Match(funcarg->typeStartToken(), "char|short|int|long"); + const bool takesFloat = Token::Match(funcarg->typeStartToken(), "float|double"); + const bool passesInt = Token::Match(callarg->typeStartToken(), "char|short|int|long"); + const bool passesFloat = Token::Match(callarg->typeStartToken(), "float|double"); + if ((takesInt && passesInt) || (takesFloat && passesFloat)) + fallback1++; + else if ((takesInt && passesFloat) || (takesFloat && passesInt)) + fallback2++; + } + } +} + +static std::string getTypeString(const Token *typeToken) +{ + if (!typeToken) + return ""; + while (Token::Match(typeToken, "%name%|*|&|::")) { + if (typeToken->str() == "::") { + std::string ret; + while (Token::Match(typeToken, ":: %name%")) { + ret += "::" + typeToken->strAt(1); + typeToken = typeToken->tokAt(2); + if (typeToken->str() == "<") { + for (const Token *tok = typeToken; tok != typeToken->link(); tok = tok->next()) + ret += tok->str(); + ret += ">"; + typeToken = typeToken->link()->next(); + } + } + return ret; + } + if (Token::Match(typeToken, "%name% const| %var%|*|&")) { + return typeToken->str(); + } + typeToken = typeToken->next(); + } + return ""; +} + +static bool hasMatchingConstructor(const Scope* classScope, const ValueType* argType) { + if (!classScope || !argType) + return false; + return std::any_of(classScope->functionList.cbegin(), + classScope->functionList.cend(), + [&](const Function& f) { + if (!f.isConstructor() || f.argCount() != 1 || !f.getArgumentVar(0)) + return false; + const ValueType* vt = f.getArgumentVar(0)->valueType(); + return vt && + vt->type == argType->type && + (argType->sign == ValueType::Sign::UNKNOWN_SIGN || vt->sign == argType->sign) && + vt->pointer == argType->pointer && + (vt->constness & 1) >= (argType->constness & 1) && + (vt->volatileness & 1) >= (argType->volatileness & 1); + }); +} + +const Function* Scope::findFunction(const Token *tok, bool requireConst) const +{ + const bool isCall = Token::Match(tok->next(), "(|{"); + + const std::vector arguments = getArguments(tok); + + std::vector matches; + + // find all the possible functions that could match + const std::size_t args = arguments.size(); + + auto addMatchingFunctions = [&](const Scope *scope) { + auto range = scope->functionMap.equal_range(tok->str()); + for (std::multimap::const_iterator it = range.first; it != range.second; ++it) { + const Function *func = it->second; + if (!isCall || args == func->argCount() || + (func->isVariadic() && args >= (func->minArgCount() - 1)) || + (args < func->argCount() && args >= func->minArgCount())) { + matches.push_back(func); + } + } + }; + + addMatchingFunctions(this); + + // check in anonymous namespaces + for (const Scope *nestedScope : nestedList) { + if (nestedScope->type == eNamespace && nestedScope->className.empty()) + addMatchingFunctions(nestedScope); + } + + const std::size_t numberOfMatchesNonBase = matches.size(); + + // check in base classes + findFunctionInBase(tok->str(), args, matches); + + // Non-call => Do not match parameters + if (!isCall) { + return matches.empty() ? nullptr : matches[0]; + } + + std::vector fallback1Func, fallback2Func; + + // check each function against the arguments in the function call for a match + for (std::size_t i = 0; i < matches.size();) { + if (i > 0 && i == numberOfMatchesNonBase && fallback1Func.empty() && !fallback2Func.empty()) + break; + + bool constFallback = false; + const Function * func = matches[i]; + size_t same = 0; + + if (requireConst && !func->isConst()) { + i++; + continue; + } + + if (!requireConst || !func->isConst()) { + // get the function this call is in + const Scope * scope = tok->scope(); + + // check if this function is a member function + if (scope && scope->functionOf && scope->functionOf->isClassOrStruct() && scope->function && + func->nestedIn == scope->functionOf) { + // check if isConst mismatches + if (scope->function->isConst() != func->isConst()) { + if (scope->function->isConst()) { + ++i; + continue; + } + constFallback = true; + } + } + } + + size_t fallback1 = 0; + size_t fallback2 = 0; + bool erased = false; + for (std::size_t j = 0; j < args; ++j) { + + // don't check variadic arguments + if (func->isVariadic() && j > (func->argCount() - 1)) { + break; + } + const Variable *funcarg = func->getArgumentVar(j); + + if (!arguments[j]->valueType()) { + const Token *vartok = arguments[j]; + int pointer = 0; + while (vartok && (vartok->isUnaryOp("&") || vartok->isUnaryOp("*"))) { + pointer += vartok->isUnaryOp("&") ? 1 : -1; + vartok = vartok->astOperand1(); + } + if (vartok && vartok->variable()) { + const Token *callArgTypeToken = vartok->variable()->typeStartToken(); + const Token *funcArgTypeToken = funcarg->typeStartToken(); + + auto parseDecl = [](const Token *typeToken) -> ValueType { + ValueType ret; + while (Token::Match(typeToken->previous(), "%name%")) + typeToken = typeToken->previous(); + while (Token::Match(typeToken, "%name%|*|&|::|<")) + { + if (typeToken->str() == "const") + ret.constness |= (1 << ret.pointer); + else if (typeToken->str() == "volatile") + ret.volatileness |= (1 << ret.pointer); + else if (typeToken->str() == "*") + ret.pointer++; + else if (typeToken->str() == "<") { + if (!typeToken->link()) + break; + typeToken = typeToken->link(); + } + typeToken = typeToken->next(); + } + return ret; + }; + + const std::string type1 = getTypeString(callArgTypeToken); + const std::string type2 = getTypeString(funcArgTypeToken); + if (!type1.empty() && type1 == type2) { + ValueType callArgType = parseDecl(callArgTypeToken); + callArgType.pointer += pointer; + ValueType funcArgType = parseDecl(funcArgTypeToken); + + callArgType.sign = funcArgType.sign = ValueType::Sign::SIGNED; + callArgType.type = funcArgType.type = ValueType::Type::INT; + + const ValueType::MatchResult res = ValueType::matchParameter(&callArgType, &funcArgType); + if (res == ValueType::MatchResult::SAME) + ++same; + else if (res == ValueType::MatchResult::FALLBACK1) + ++fallback1; + else if (res == ValueType::MatchResult::FALLBACK2) + ++fallback2; + continue; + } + } + } + + // check for a match with a variable + if (Token::Match(arguments[j], "%var% ,|)")) { + const Variable * callarg = arguments[j]->variable(); + checkVariableCallMatch(callarg, funcarg, same, fallback1, fallback2); + } + + else if (funcarg->isStlStringType() && arguments[j]->valueType() && arguments[j]->valueType()->pointer == 1 && arguments[j]->valueType()->type == ValueType::Type::CHAR) + fallback2++; + + // check for a match with nullptr + else if (funcarg->isPointer() && Token::Match(arguments[j], "nullptr|NULL ,|)")) + same++; + + else if (funcarg->isPointer() && MathLib::isNullValue(arguments[j]->str())) + fallback1++; + + else if (!funcarg->isPointer() && funcarg->type() && hasMatchingConstructor(funcarg->type()->classScope, arguments[j]->valueType())) + fallback2++; + + // Try to evaluate the apparently more complex expression + else if (arguments[j]->isCpp()) { + const Token *vartok = arguments[j]; + if (vartok->str() == ".") { + const Token* rml = nextAfterAstRightmostLeaf(vartok); + if (rml) + vartok = rml->previous(); + } + while (vartok->isUnaryOp("&") || vartok->isUnaryOp("*")) + vartok = vartok->astOperand1(); + const Variable* var = vartok->variable(); + // smart pointer deref? + bool unknownDeref = false; + if (var && vartok->astParent() && vartok->astParent()->str() == "*") { + if (var->isSmartPointer() && var->valueType() && var->valueType()->smartPointerTypeToken) + var = var->valueType()->smartPointerTypeToken->variable(); + else + unknownDeref = true; + } + const Token* valuetok = arguments[j]; + if (valuetok->str() == "::") { + const Token* rml = nextAfterAstRightmostLeaf(valuetok); + if (rml) + valuetok = rml->previous(); + } + if (vartok->isEnumerator()) + valuetok = vartok; + const ValueType::MatchResult res = ValueType::matchParameter(valuetok->valueType(), var, funcarg); + if (res == ValueType::MatchResult::SAME) + ++same; + else if (res == ValueType::MatchResult::FALLBACK1) + ++fallback1; + else if (res == ValueType::MatchResult::FALLBACK2) + ++fallback2; + else if (res == ValueType::MatchResult::NOMATCH) { + if (unknownDeref) + continue; + // can't match so remove this function from possible matches + matches.erase(matches.begin() + i); + erased = true; + break; + } + } + + else + // C code: if number of arguments match then do not match types + fallback1++; + } + + const size_t hasToBe = func->isVariadic() ? (func->argCount() - 1) : args; + + // check if all arguments matched + if (same == hasToBe) { + if (constFallback || (!requireConst && func->isConst())) + fallback1Func.emplace_back(func); + else + return func; + } + + else { + if (same + fallback1 == hasToBe) + fallback1Func.emplace_back(func); + else if (same + fallback2 + fallback1 == hasToBe) + fallback2Func.emplace_back(func); + } + + if (!erased) + ++i; + } + + // Fallback cases + for (const auto& fb : { fallback1Func, fallback2Func }) { + if (fb.size() == 1) + return fb.front(); + if (fb.size() == 2) { + if (fb[0]->isConst() && !fb[1]->isConst()) + return fb[1]; + if (fb[1]->isConst() && !fb[0]->isConst()) + return fb[0]; + } + } + + // remove pure virtual function if there is an overrider + auto itPure = std::find_if(matches.begin(), matches.end(), [](const Function* m) { + return m->isPure(); + }); + if (itPure != matches.end() && std::any_of(matches.begin(), matches.end(), [&](const Function* m) { + return m->isImplicitlyVirtual() && m != *itPure; + })) + matches.erase(itPure); + + // Only one candidate left + if (matches.size() == 1) + return matches[0]; + + // Prioritize matches in derived scopes + for (const auto& fb : { fallback1Func, fallback2Func }) { + const Function* ret = nullptr; + for (int i = 0; i < fb.size(); ++i) { + if (std::find(matches.cbegin(), matches.cend(), fb[i]) == matches.cend()) + continue; + if (this == fb[i]->nestedIn) { + if (!ret) + ret = fb[i]; + else { + ret = nullptr; + break; + } + } + } + if (ret) + return ret; + } + + return nullptr; +} + +//--------------------------------------------------------------------------- + +const Function* SymbolDatabase::findFunction(const Token* const tok) const +{ + // find the scope this function is in + const Scope *currScope = tok->scope(); + while (currScope && currScope->isExecutable()) { + if (const Function* f = currScope->findFunction(tok)) { + return f; + } + if (currScope->functionOf) + currScope = currScope->functionOf; + else + currScope = currScope->nestedIn; + } + + // check for a qualified name and use it when given + if (tok->strAt(-1) == "::") { + // find start of qualified function name + const Token *tok1 = tok; + + while (Token::Match(tok1->tokAt(-2), ">|%type% ::")) { + if (tok1->strAt(-2) == ">") { + if (tok1->linkAt(-2)) + tok1 = tok1->linkAt(-2)->tokAt(-1); + else + break; + } else + tok1 = tok1->tokAt(-2); + } + + // check for global scope + if (tok1->strAt(-1) == "::") { + currScope = &scopeList.front(); + + if (const Function* f = currScope->findFunction(tok)) + return f; + + currScope = currScope->findRecordInNestedList(tok1->str()); + } + + // find start of qualification + else { + while (currScope) { + if (currScope->className == tok1->str()) + break; + + const Scope *scope = currScope->findRecordInNestedList(tok1->str()); + + if (scope) { + currScope = scope; + break; + } + currScope = currScope->nestedIn; + } + } + + if (currScope) { + while (currScope && tok1 && !(Token::Match(tok1, "%type% :: %name% [(),>]") || + (Token::Match(tok1, "%type% <") && Token::Match(tok1->linkAt(1), "> :: %name% (")))) { + if (tok1->strAt(1) == "::") + tok1 = tok1->tokAt(2); + else if (tok1->strAt(1) == "<") + tok1 = tok1->linkAt(1)->tokAt(2); + else + tok1 = nullptr; + + if (tok1) { + const Function* func = currScope->findFunction(tok1); + if (func) + return func; + + currScope = currScope->findRecordInNestedList(tok1->str()); + } + } + + if (tok1) + tok1 = tok1->tokAt(2); + + if (currScope && tok1) { + const Function* func = currScope->findFunction(tok1); + if (func) + return func; + } + } + } + + // check for member function + else if (Token::Match(tok->tokAt(-2), "!!this .")) { + const Token* tok1 = tok->previous()->astOperand1(); + if (tok1 && tok1->valueType() && tok1->valueType()->typeScope) + return tok1->valueType()->typeScope->findFunction(tok, tok1->valueType()->constness == 1); + if (tok1 && Token::Match(tok1->previous(), "%name% (") && tok1->previous()->function() && + tok1->previous()->function()->retDef) { + ValueType vt = ValueType::parseDecl(tok1->previous()->function()->retDef, mSettings); + if (vt.typeScope) + return vt.typeScope->findFunction(tok, vt.constness == 1); + } else if (Token::Match(tok1, "%var% .")) { + const Variable *var = getVariableFromVarId(tok1->varId()); + if (var && var->typeScope()) + return var->typeScope()->findFunction(tok, var->valueType()->constness == 1); + if (var && var->smartPointerType() && var->smartPointerType()->classScope && tok1->next()->originalName() == "->") + return var->smartPointerType()->classScope->findFunction(tok, var->valueType()->constness == 1); + if (var && var->iteratorType() && var->iteratorType()->classScope && tok1->next()->originalName() == "->") + return var->iteratorType()->classScope->findFunction(tok, var->valueType()->constness == 1); + } else if (Token::simpleMatch(tok->previous()->astOperand1(), "(")) { + const Token *castTok = tok->previous()->astOperand1(); + if (castTok->isCast()) { + ValueType vt = ValueType::parseDecl(castTok->next(),mSettings); + if (vt.typeScope) + return vt.typeScope->findFunction(tok, vt.constness == 1); + } + } + } + + // check in enclosing scopes + else { + while (currScope) { + const Function *func = currScope->findFunction(tok); + if (func) + return func; + currScope = currScope->nestedIn; + } + // check using namespace + currScope = tok->scope(); + while (currScope) { + for (const auto& ul : currScope->usingList) { + if (ul.scope) { + const Function* func = ul.scope->findFunction(tok); + if (func) + return func; + } + } + currScope = currScope->nestedIn; + } + } + // Check for constructor + if (Token::Match(tok, "%name% (|{")) { + ValueType vt = ValueType::parseDecl(tok, mSettings); + if (vt.typeScope) + return vt.typeScope->findFunction(tok, false); + } + return nullptr; +} + +//--------------------------------------------------------------------------- + +const Scope *SymbolDatabase::findScopeByName(const std::string& name) const +{ + auto it = std::find_if(scopeList.cbegin(), scopeList.cend(), [&](const Scope& s) { + return s.className == name; + }); + return it == scopeList.end() ? nullptr : &*it; +} + +//--------------------------------------------------------------------------- + +template ), REQUIRES("T must be a Type class", std::is_convertible )> +S* findRecordInNestedListImpl(S& thisScope, const std::string & name, bool isC) +{ + for (S* scope: thisScope.nestedList) { + if (scope->className == name && scope->type != Scope::eFunction) + return scope; + if (isC) { + S* nestedScope = scope->findRecordInNestedList(name, isC); + if (nestedScope) + return nestedScope; + } + } + + T * nested_type = thisScope.findType(name); + + if (nested_type) { + if (nested_type->isTypeAlias()) { + if (nested_type->typeStart == nested_type->typeEnd) + return thisScope.findRecordInNestedList(nested_type->typeStart->str()); // TODO: pass isC? + } else + return const_cast(nested_type->classScope); + } + + return nullptr; +} + +const Scope* Scope::findRecordInNestedList(const std::string & name, bool isC) const +{ + return findRecordInNestedListImpl(*this, name, isC); +} + +Scope* Scope::findRecordInNestedList(const std::string & name, bool isC) +{ + return findRecordInNestedListImpl(*this, name, isC); +} + +//--------------------------------------------------------------------------- + +template ), REQUIRES("T must be a Type class", std::is_convertible )> +T* findTypeImpl(S& thisScope, const std::string & name) +{ + auto it = thisScope.definedTypesMap.find(name); + + // Type was found + if (thisScope.definedTypesMap.end() != it) + return it->second; + + // is type defined in anonymous namespace.. + it = thisScope.definedTypesMap.find(emptyString); + if (it != thisScope.definedTypesMap.end()) { + for (S *scope : thisScope.nestedList) { + if (scope->className.empty() && (scope->type == thisScope.eNamespace || scope->isClassOrStructOrUnion())) { + T *t = scope->findType(name); + if (t) + return t; + } + } + } + + // Type was not found + return nullptr; +} + +const Type* Scope::findType(const std::string& name) const +{ + return findTypeImpl(*this, name); +} + +Type* Scope::findType(const std::string& name) +{ + return findTypeImpl(*this, name); +} + +//--------------------------------------------------------------------------- + +Scope *Scope::findInNestedListRecursive(const std::string & name) +{ + auto it = std::find_if(nestedList.cbegin(), nestedList.cend(), [&](const Scope* s) { + return s->className == name; + }); + if (it != nestedList.end()) + return *it; + + for (Scope* scope: nestedList) { + Scope *child = scope->findInNestedListRecursive(name); + if (child) + return child; + } + return nullptr; +} + +//--------------------------------------------------------------------------- + +const Function *Scope::getDestructor() const +{ + auto it = std::find_if(functionList.cbegin(), functionList.cend(), [](const Function& f) { + return f.type == Function::eDestructor; + }); + return it == functionList.end() ? nullptr : &*it; +} + +//--------------------------------------------------------------------------- + +const Scope *SymbolDatabase::findScope(const Token *tok, const Scope *startScope) const +{ + const Scope *scope = nullptr; + // absolute path + if (tok->str() == "::") { + tok = tok->next(); + scope = &scopeList.front(); + } + // relative path + else if (tok->isName()) { + scope = startScope; + } + + while (scope && tok && tok->isName()) { + if (tok->strAt(1) == "::") { + scope = scope->findRecordInNestedList(tok->str()); + tok = tok->tokAt(2); + } else if (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::")) { + scope = scope->findRecordInNestedList(tok->str()); + tok = tok->linkAt(1)->tokAt(2); + } else + return scope->findRecordInNestedList(tok->str()); + } + + // not a valid path + return nullptr; +} + +//--------------------------------------------------------------------------- + +const Type* SymbolDatabase::findType(const Token *startTok, const Scope *startScope, bool lookOutside) const +{ + // skip over struct or union + if (Token::Match(startTok, "struct|union")) + startTok = startTok->next(); + + // type same as scope + if (startTok->str() == startScope->className && startScope->isClassOrStruct() && startTok->strAt(1) != "::") + return startScope->definedType; + + if (startTok->isC()) { + const Scope* scope = startScope; + while (scope) { + if (startTok->str() == scope->className && scope->isClassOrStruct()) + return scope->definedType; + const Scope* typeScope = scope->findRecordInNestedList(startTok->str(), /*isC*/ true); + if (typeScope) { + if (startTok->str() == typeScope->className && typeScope->isClassOrStruct()) { + if (const Type* type = typeScope->definedType) + return type; + } + } + scope = scope->nestedIn; + } + return nullptr; + } + + const Scope* start_scope = startScope; + + // absolute path - directly start in global scope + if (startTok->str() == "::") { + startTok = startTok->next(); + start_scope = &scopeList.front(); + } + + const Token* tok = startTok; + const Scope* scope = start_scope; + + while (scope && tok && tok->isName()) { + if (tok->strAt(1) == "::" || (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::"))) { + scope = scope->findRecordInNestedList(tok->str()); + if (scope) { + if (tok->strAt(1) == "::") + tok = tok->tokAt(2); + else + tok = tok->linkAt(1)->tokAt(2); + } else { + start_scope = start_scope->nestedIn; + if (!start_scope) + break; + scope = start_scope; + tok = startTok; + } + } else { + const Scope* scope1{}; + const Type* type = scope->findType(tok->str()); + if (type) + return type; + if (lookOutside && (scope1 = scope->findRecordInBase(tok->str()))) { + type = scope1->definedType; + if (type) + return type; + } else if (lookOutside && scope->type == Scope::ScopeType::eNamespace) { + scope = scope->nestedIn; + continue; + } else + break; + } + } + + // check using namespaces + while (startScope) { + for (std::vector::const_iterator it = startScope->usingList.cbegin(); + it != startScope->usingList.cend(); ++it) { + tok = startTok; + scope = it->scope; + start_scope = startScope; + + while (scope && tok && tok->isName()) { + if (tok->strAt(1) == "::" || (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::"))) { + scope = scope->findRecordInNestedList(tok->str()); + if (scope) { + if (tok->strAt(1) == "::") + tok = tok->tokAt(2); + else + tok = tok->linkAt(1)->tokAt(2); + } else { + start_scope = start_scope->nestedIn; + if (!start_scope) + break; + scope = start_scope; + tok = startTok; + } + } else { + const Type * type = scope->findType(tok->str()); + if (type) + return type; + if (const Scope *scope1 = scope->findRecordInBase(tok->str())) { + type = scope1->definedType; + if (type) + return type; + } else + break; + } + } + } + startScope = startScope->nestedIn; + } + + // not a valid path + return nullptr; +} + +//--------------------------------------------------------------------------- + +const Type* SymbolDatabase::findTypeInNested(const Token *startTok, const Scope *startScope) const +{ + // skip over struct or union + if (Token::Match(startTok, "struct|union|enum")) + startTok = startTok->next(); + + // type same as scope + if (startScope->isClassOrStruct() && startTok->str() == startScope->className && !Token::simpleMatch(startTok->next(), "::")) + return startScope->definedType; + + bool hasPath = false; + + // absolute path - directly start in global scope + if (startTok->str() == "::") { + hasPath = true; + startTok = startTok->next(); + startScope = &scopeList.front(); + } + + const Token* tok = startTok; + const Scope* scope = startScope; + + while (scope && tok && tok->isName()) { + if (tok->strAt(1) == "::" || (tok->strAt(1) == "<" && Token::simpleMatch(tok->linkAt(1), "> ::"))) { + hasPath = true; + scope = scope->findRecordInNestedList(tok->str()); + if (scope) { + if (tok->strAt(1) == "::") + tok = tok->tokAt(2); + else + tok = tok->linkAt(1)->tokAt(2); + } else { + startScope = startScope->nestedIn; + if (!startScope) + break; + scope = startScope; + tok = startTok; + } + } else { + const Type * type = scope->findType(tok->str()); + if (hasPath || type) + return type; + + scope = scope->nestedIn; + if (!scope) + break; + } + } + + // not a valid path + return nullptr; +} + +//--------------------------------------------------------------------------- + +const Scope * SymbolDatabase::findNamespace(const Token * tok, const Scope * scope) const +{ + const Scope * s = findScope(tok, scope); + + if (s) + return s; + if (scope->nestedIn) + return findNamespace(tok, scope->nestedIn); + + return nullptr; +} + +//--------------------------------------------------------------------------- + +Function * SymbolDatabase::findFunctionInScope(const Token *func, const Scope *ns, const std::string & path, nonneg int path_length) +{ + const Function * function = nullptr; + const bool destructor = func->strAt(-1) == "~"; + + auto range = ns->functionMap.equal_range(func->str()); + for (std::multimap::const_iterator it = range.first; it != range.second; ++it) { + if (it->second->argsMatch(ns, it->second->argDef, func->next(), path, path_length) && + it->second->isDestructor() == destructor) { + function = it->second; + break; + } + } + + if (!function) { + const Scope * scope = ns->findRecordInNestedList(func->str()); + if (scope && Token::Match(func->tokAt(1), "::|<")) { + if (func->strAt(1) == "::") + func = func->tokAt(2); + else if (func->linkAt(1)) + func = func->linkAt(1)->tokAt(2); + else + return nullptr; + if (func->str() == "~") + func = func->next(); + function = findFunctionInScope(func, scope, path, path_length); + } + } + + return const_cast(function); +} + +//--------------------------------------------------------------------------- + +bool SymbolDatabase::isReservedName(const Token* tok) +{ + const std::string& iName = tok->str(); + if (tok->isCpp()) { + static const auto& cpp_keywords = Keywords::getAll(Standards::cppstd_t::CPPLatest); + return cpp_keywords.find(iName) != cpp_keywords.cend(); + } + static const auto& c_keywords = Keywords::getAll(Standards::cstd_t::CLatest); + return c_keywords.find(iName) != c_keywords.cend(); +} + +nonneg int SymbolDatabase::sizeOfType(const Token *type) const +{ + int size = mTokenizer.sizeOfType(type); + + if (size == 0 && type->type() && type->type()->isEnumType() && type->type()->classScope) { + size = mSettings.platform.sizeof_int; + const Token * enum_type = type->type()->classScope->enumType; + if (enum_type) + size = mTokenizer.sizeOfType(enum_type); + } + + return size; +} + +static const Token* parsedecl(const Token* type, + ValueType* const valuetype, + ValueType::Sign defaultSignedness, + const Settings& settings, + SourceLocation loc = SourceLocation::current()); + +void SymbolDatabase::setValueType(Token* tok, const Variable& var, const SourceLocation &loc) +{ + ValueType valuetype; + if (mSettings.debugnormal || mSettings.debugwarnings) + valuetype.setDebugPath(tok, loc); + if (var.nameToken()) + valuetype.bits = var.nameToken()->bits(); + + valuetype.pointer = var.dimensions().size(); + // HACK: don't set pointer for plain std::array + if (var.valueType() && var.valueType()->container && Token::simpleMatch(var.typeStartToken(), "std :: array") && !Token::simpleMatch(var.nameToken()->next(), "[")) + valuetype.pointer = 0; + + valuetype.typeScope = var.typeScope(); + if (var.valueType()) { + valuetype.container = var.valueType()->container; + valuetype.containerTypeToken = var.valueType()->containerTypeToken; + } + valuetype.smartPointerType = var.smartPointerType(); + if (parsedecl(var.typeStartToken(), &valuetype, mDefaultSignedness, mSettings)) { + if (tok->str() == "." && tok->astOperand1()) { + const ValueType * const vt = tok->astOperand1()->valueType(); + if (vt && (vt->constness & 1) != 0) + valuetype.constness |= 1; + if (vt && (vt->volatileness & 1) != 0) + valuetype.volatileness |= 1; + } + setValueType(tok, valuetype); + } +} + +static ValueType::Type getEnumType(const Scope* scope, const Platform& platform); + +void SymbolDatabase::setValueType(Token* tok, const Enumerator& enumerator, const SourceLocation &loc) +{ + ValueType valuetype; + if (mSettings.debugnormal || mSettings.debugwarnings) + valuetype.setDebugPath(tok, loc); + valuetype.typeScope = enumerator.scope; + const Token * type = enumerator.scope->enumType; + if (type) { + valuetype.type = ValueType::typeFromString(type->str(), type->isLong()); + if (valuetype.type == ValueType::Type::UNKNOWN_TYPE && type->isStandardType()) + valuetype.fromLibraryType(type->str(), mSettings); + + if (valuetype.isIntegral()) { + if (type->isSigned()) + valuetype.sign = ValueType::Sign::SIGNED; + else if (type->isUnsigned()) + valuetype.sign = ValueType::Sign::UNSIGNED; + else if (valuetype.type == ValueType::Type::CHAR) + valuetype.sign = mDefaultSignedness; + else + valuetype.sign = ValueType::Sign::SIGNED; + } + + setValueType(tok, valuetype); + } else { + valuetype.sign = ValueType::SIGNED; + valuetype.type = getEnumType(enumerator.scope, mSettings.platform); + setValueType(tok, valuetype); + } +} + +static void setAutoTokenProperties(Token * const autoTok) +{ + const ValueType *valuetype = autoTok->valueType(); + if (valuetype->isIntegral() || valuetype->isFloat()) + autoTok->isStandardType(true); +} + +static bool isContainerYieldElement(Library::Container::Yield yield) +{ + return yield == Library::Container::Yield::ITEM || yield == Library::Container::Yield::AT_INDEX || + yield == Library::Container::Yield::BUFFER || yield == Library::Container::Yield::BUFFER_NT; +} + +static bool isContainerYieldPointer(Library::Container::Yield yield) +{ + return yield == Library::Container::Yield::BUFFER || yield == Library::Container::Yield::BUFFER_NT; +} + +void SymbolDatabase::setValueType(Token* tok, const ValueType& valuetype, const SourceLocation &loc) +{ + auto* valuetypePtr = new ValueType(valuetype); + if (mSettings.debugnormal || mSettings.debugwarnings) + valuetypePtr->setDebugPath(tok, loc); + tok->setValueType(valuetypePtr); + Token *parent = tok->astParent(); + if (!parent || parent->valueType()) + return; + if (!parent->astOperand1()) + return; + + const ValueType *vt1 = parent->astOperand1()->valueType(); + const ValueType *vt2 = parent->astOperand2() ? parent->astOperand2()->valueType() : nullptr; + + if (vt1 && Token::Match(parent, "<<|>>")) { + if (!parent->isCpp() || (vt2 && vt2->isIntegral())) { + if (vt1->type < ValueType::Type::BOOL || vt1->type >= ValueType::Type::INT) { + ValueType vt(*vt1); + vt.reference = Reference::None; + setValueType(parent, vt); + } else { + ValueType vt(*vt1); + vt.type = ValueType::Type::INT; // Integer promotion + vt.sign = ValueType::Sign::SIGNED; + vt.reference = Reference::None; + setValueType(parent, vt); + } + + } + return; + } + + if (vt1 && vt1->container && vt1->containerTypeToken && Token::Match(parent, ". %name% (") && + isContainerYieldElement(vt1->container->getYield(parent->next()->str()))) { + ValueType item; + if (parsedecl(vt1->containerTypeToken, &item, mDefaultSignedness, mSettings)) { + if (item.constness == 0) + item.constness = vt1->constness; + if (item.volatileness == 0) + item.volatileness = vt1->volatileness; + if (isContainerYieldPointer(vt1->container->getYield(parent->next()->str()))) + item.pointer += 1; + else + item.reference = Reference::LValue; + setValueType(parent->tokAt(2), item); + } + } + + if (vt1 && vt1->smartPointerType && Token::Match(parent, ". %name% (") && parent->originalName() == "->" && !parent->next()->function()) { + const Scope *scope = vt1->smartPointerType->classScope; + const Function *f = scope ? scope->findFunction(parent->next(), false) : nullptr; + if (f) + parent->next()->function(f); + } + + if (parent->isAssignmentOp()) { + if (vt1) { + auto vt = *vt1; + vt.reference = Reference::None; + setValueType(parent, vt); + } else if (parent->isCpp() && ((Token::Match(parent->tokAt(-3), "%var% ; %var% =") && parent->strAt(-3) == parent->strAt(-1)) || + Token::Match(parent->tokAt(-1), "%var% ="))) { + Token *var1Tok = parent->strAt(-2) == ";" ? parent->tokAt(-3) : parent->tokAt(-1); + Token *autoTok = nullptr; + if (Token::simpleMatch(var1Tok->tokAt(-1), "auto")) + autoTok = var1Tok->previous(); + else if (Token::Match(var1Tok->tokAt(-2), "auto *|&|&&")) + autoTok = var1Tok->tokAt(-2); + else if (Token::simpleMatch(var1Tok->tokAt(-3), "auto * const")) + autoTok = var1Tok->tokAt(-3); + if (autoTok) { + ValueType vt(*vt2); + if (vt.constness & (1 << vt.pointer)) + vt.constness &= ~(1 << vt.pointer); + if (vt.volatileness & (1 << vt.pointer)) + vt.volatileness &= ~(1 << vt.pointer); + if (autoTok->strAt(1) == "*" && vt.pointer) + vt.pointer--; + if (Token::Match(autoTok->tokAt(-1), "const|constexpr")) + vt.constness |= (1 << vt.pointer); + if (Token::simpleMatch(autoTok->tokAt(-1), "volatile")) + vt.volatileness |= (1 << vt.pointer); + setValueType(autoTok, vt); + setAutoTokenProperties(autoTok); + if (vt2->pointer > vt.pointer) + vt.pointer++; + setValueType(var1Tok, vt); + if (var1Tok != parent->previous()) + setValueType(parent->previous(), vt); + auto *var = const_cast(parent->previous()->variable()); + if (var) { + ValueType vt2_(*vt2); + if (vt2_.pointer == 0 && autoTok->strAt(1) == "*") + vt2_.pointer = 1; + if ((vt.constness & (1 << vt2->pointer)) != 0) + vt2_.constness |= (1 << vt2->pointer); + if ((vt.volatileness & (1 << vt2->pointer)) != 0) + vt2_.volatileness |= (1 << vt2->pointer); + if (!Token::Match(autoTok->tokAt(1), "*|&")) { + vt2_.constness = vt.constness; + vt2_.volatileness = vt.volatileness; + } + if (Token::simpleMatch(autoTok->tokAt(1), "* const")) + vt2_.constness |= (1 << vt2->pointer); + if (Token::simpleMatch(autoTok->tokAt(1), "* volatile")) + vt2_.volatileness |= (1 << vt2->pointer); + var->setValueType(vt2_); + if (vt2->typeScope && vt2->typeScope->definedType) { + var->type(vt2->typeScope->definedType); + if (autoTok->valueType()->pointer == 0) + autoTok->type(vt2->typeScope->definedType); + } + } + } + } + return; + } + + if (parent->str() == "[" && (!parent->isCpp() || parent->astOperand1() == tok) && valuetype.pointer > 0U && !Token::Match(parent->previous(), "[{,]")) { + const Token *op1 = parent->astOperand1(); + while (op1 && op1->str() == "[") + op1 = op1->astOperand1(); + + ValueType vt(valuetype); + // the "[" is a dereference unless this is a variable declaration + if (!(op1 && op1->variable() && op1->variable()->nameToken() == op1)) + vt.pointer -= 1U; + setValueType(parent, vt); + return; + } + if (Token::Match(parent->previous(), "%name% (") && parent->astOperand1() == tok && valuetype.pointer > 0U) { + ValueType vt(valuetype); + vt.pointer -= 1U; + setValueType(parent, vt); + return; + } + // std::move + if (vt2 && parent->str() == "(" && Token::simpleMatch(parent->tokAt(-3), "std :: move (")) { + ValueType vt = valuetype; + vt.reference = Reference::RValue; + setValueType(parent, vt); + return; + } + if (parent->str() == "*" && !parent->astOperand2() && valuetype.pointer > 0U) { + ValueType vt(valuetype); + vt.pointer -= 1U; + setValueType(parent, vt); + return; + } + // Dereference iterator + if (parent->str() == "*" && !parent->astOperand2() && valuetype.type == ValueType::Type::ITERATOR && + valuetype.containerTypeToken) { + ValueType vt; + if (parsedecl(valuetype.containerTypeToken, &vt, mDefaultSignedness, mSettings)) { + if (vt.constness == 0) + vt.constness = valuetype.constness; + if (vt.volatileness == 0) + vt.volatileness = valuetype.volatileness; + vt.reference = Reference::LValue; + setValueType(parent, vt); + return; + } + } + // Dereference smart pointer + if (parent->str() == "*" && !parent->astOperand2() && valuetype.type == ValueType::Type::SMART_POINTER && + valuetype.smartPointerTypeToken) { + ValueType vt; + if (parsedecl(valuetype.smartPointerTypeToken, &vt, mDefaultSignedness, mSettings)) { + if (vt.constness == 0) + vt.constness = valuetype.constness; + if (vt.volatileness == 0) + vt.volatileness = valuetype.volatileness; + setValueType(parent, vt); + return; + } + } + if (parent->str() == "*" && Token::simpleMatch(parent->astOperand2(), "[") && valuetype.pointer > 0U) { + const Token *op1 = parent->astOperand2()->astOperand1(); + while (op1 && op1->str() == "[") + op1 = op1->astOperand1(); + const ValueType& vt(valuetype); + if (op1 && op1->variable() && op1->variable()->nameToken() == op1) { + setValueType(parent, vt); + return; + } + } + if (parent->str() == "&" && !parent->astOperand2()) { + ValueType vt(valuetype); + vt.reference = Reference::None; //Given int& x; the type of &x is int* not int&* + bool isArrayToPointerDecay = false; + for (const Token* child = parent->astOperand1(); child;) { + if (Token::Match(child, ".|::")) + child = child->astOperand2(); + else { + isArrayToPointerDecay = child->variable() && child->variable()->isArray(); + break; + } + } + if (!isArrayToPointerDecay) + vt.pointer += 1U; + setValueType(parent, vt); + return; + } + + if ((parent->str() == "." || parent->str() == "::") && + parent->astOperand2() && parent->astOperand2()->isName()) { + const Variable* var = parent->astOperand2()->variable(); + if (!var && valuetype.typeScope && vt1) { + const std::string &name = parent->astOperand2()->str(); + const Scope *typeScope = vt1->typeScope; + if (!typeScope) + return; + auto it = std::find_if(typeScope->varlist.begin(), typeScope->varlist.end(), [&name](const Variable& v) { + return v.nameToken()->str() == name; + }); + if (it != typeScope->varlist.end()) + var = &*it; + } + if (var) { + setValueType(parent, *var); + return; + } + if (const Enumerator* enu = parent->astOperand2()->enumerator()) + setValueType(parent, *enu); + return; + } + + // range for loop, auto + if (vt2 && + parent->str() == ":" && + Token::Match(parent->astParent(), "( const| auto *|&|&&| %var% :") && // TODO: east-const, multiple const, ref to ptr + !parent->previous()->valueType() && + Token::simpleMatch(parent->astParent()->astOperand1(), "for")) { + const bool isconst = Token::simpleMatch(parent->astParent()->next(), "const"); + const bool isvolatile = Token::simpleMatch(parent->astParent()->next(), "volatile"); + Token * const autoToken = parent->astParent()->tokAt(isconst ? 2 : 1); + if (vt2->pointer) { + ValueType autovt(*vt2); + autovt.pointer--; + autovt.constness = 0; + autovt.volatileness = 0; + setValueType(autoToken, autovt); + setAutoTokenProperties(autoToken); + ValueType varvt(*vt2); + varvt.pointer--; + if (Token::simpleMatch(autoToken->next(), "&")) + varvt.reference = Reference::LValue; + if (isconst) { + if (varvt.pointer && varvt.reference != Reference::None) + varvt.constness |= (1 << varvt.pointer); + else + varvt.constness |= 1; + } + if (isvolatile) { + if (varvt.pointer && varvt.reference != Reference::None) + varvt.volatileness |= (1 << varvt.pointer); + else + varvt.volatileness |= 1; + } + setValueType(parent->previous(), varvt); + auto *var = const_cast(parent->previous()->variable()); + if (var) { + var->setValueType(varvt); + if (vt2->typeScope && vt2->typeScope->definedType) { + var->type(vt2->typeScope->definedType); + autoToken->type(vt2->typeScope->definedType); + } + } + } else if (vt2->container) { + // TODO: Determine exact type of RHS + const Token *typeStart = parent->astOperand2(); + while (typeStart) { + if (typeStart->variable()) + typeStart = typeStart->variable()->typeStartToken(); + else if (typeStart->str() == "(" && typeStart->previous() && typeStart->previous()->function()) + typeStart = typeStart->previous()->function()->retDef; + else + break; + } + + // Try to determine type of "auto" token. + // TODO: Get type better + bool setType = false; + ValueType autovt; + const Type *templateArgType = nullptr; // container element type / smart pointer type + if (!vt2->container->rangeItemRecordType.empty()) { + setType = true; + autovt.type = ValueType::Type::RECORD; + } else if (vt2->containerTypeToken) { + if (mSettings.library.isSmartPointer(vt2->containerTypeToken)) { + const Token *smartPointerTypeTok = vt2->containerTypeToken; + while (Token::Match(smartPointerTypeTok, "%name%|::")) + smartPointerTypeTok = smartPointerTypeTok->next(); + if (Token::simpleMatch(smartPointerTypeTok, "<")) { + if ((templateArgType = findTypeInNested(smartPointerTypeTok->next(), tok->scope()))) { + setType = true; + autovt.smartPointerType = templateArgType; + autovt.type = ValueType::Type::NONSTD; + } + } + } else if (parsedecl(vt2->containerTypeToken, &autovt, mDefaultSignedness, mSettings)) { + setType = true; + templateArgType = vt2->containerTypeToken->type(); + if (Token::simpleMatch(autoToken->next(), "&")) + autovt.reference = Reference::LValue; + else if (Token::simpleMatch(autoToken->next(), "&&")) + autovt.reference = Reference::RValue; + if (autoToken->previous()->str() == "const") { + if (autovt.pointer && autovt.reference != Reference::None) + autovt.constness |= 2; + else + autovt.constness |= 1; + } + if (autoToken->previous()->str() == "volatile") { + if (autovt.pointer && autovt.reference != Reference::None) + autovt.volatileness |= 2; + else + autovt.volatileness |= 1; + } + } + } + + if (setType) { + // Type of "auto" has been determined.. set type information for "auto" and variable tokens + setValueType(autoToken, autovt); + setAutoTokenProperties(autoToken); + ValueType varvt(autovt); + if (autoToken->strAt(1) == "*" && autovt.pointer) + autovt.pointer--; + if (isconst) + varvt.constness |= (1 << autovt.pointer); + if (isvolatile) + varvt.volatileness |= (1 << autovt.pointer); + setValueType(parent->previous(), varvt); + auto * var = const_cast(parent->previous()->variable()); + if (var) { + var->setValueType(varvt); + if (templateArgType && templateArgType->classScope && templateArgType->classScope->definedType) { + autoToken->type(templateArgType->classScope->definedType); + var->type(templateArgType->classScope->definedType); + } + } + } + } + } + + if (vt1 && vt1->containerTypeToken && parent->str() == "[") { + ValueType vtParent; + if (parsedecl(vt1->containerTypeToken, &vtParent, mDefaultSignedness, mSettings)) { + setValueType(parent, vtParent); + return; + } + } + + if (parent->isCpp() && vt2 && Token::simpleMatch(parent->previous(), "decltype (")) { + setValueType(parent, *vt2); + return; + } + + // c++17 auto type deduction of braced init list + if (parent->isCpp() && mSettings.standards.cpp >= Standards::CPP17 && vt2 && Token::Match(parent->tokAt(-2), "auto %var% {")) { + Token *autoTok = parent->tokAt(-2); + setValueType(autoTok, *vt2); + setAutoTokenProperties(autoTok); + if (parent->previous()->variable()) + const_cast(parent->previous()->variable())->setValueType(*vt2); + else + debugMessage(parent->previous(), "debug", "Missing variable class for variable with varid"); + return; + } + + if (!vt1) + return; + if (parent->astOperand2() && !vt2) + return; + + const bool ternary = parent->str() == ":" && parent->astParent() && parent->astParent()->str() == "?"; + if (ternary) { + if (vt2 && vt1->pointer == vt2->pointer && vt1->type == vt2->type && vt1->sign == vt2->sign) + setValueType(parent, *vt2); + parent = parent->astParent(); + } + + if (ternary || parent->isArithmeticalOp() || parent->tokType() == Token::eIncDecOp) { + + // CONTAINER + x => CONTAINER + if (parent->str() == "+" && vt1->type == ValueType::Type::CONTAINER && vt2 && vt2->isIntegral()) { + setValueType(parent, *vt1); + return; + } + // x + CONTAINER => CONTAINER + if (parent->str() == "+" && vt1->isIntegral() && vt2 && vt2->type == ValueType::Type::CONTAINER) { + setValueType(parent, *vt2); + return; + } + + if (parent->isArithmeticalOp()) { + if (vt1->pointer != 0U && vt2 && vt2->pointer == 0U) { + setValueType(parent, *vt1); + return; + } + + if (vt1->pointer == 0U && vt2 && vt2->pointer != 0U) { + setValueType(parent, *vt2); + return; + } + } else if (ternary) { + if (vt1->pointer != 0U && vt2 && vt2->pointer == 0U) { + if (vt2->isPrimitive()) + setValueType(parent, *vt1); + else + setValueType(parent, *vt2); + return; + } + + if (vt1->pointer == 0U && vt2 && vt2->pointer != 0U) { + if (vt1->isPrimitive()) + setValueType(parent, *vt2); + else + setValueType(parent, *vt1); + return; + } + + if (vt1->isTypeEqual(vt2)) { + setValueType(parent, *vt1); + return; + } + } + + if (vt1->pointer != 0U) { + if (ternary || parent->tokType() == Token::eIncDecOp) // result is pointer + setValueType(parent, *vt1); + else // result is pointer diff + setValueType(parent, ValueType(ValueType::Sign::SIGNED, ValueType::Type::INT, 0U, 0U, "ptrdiff_t")); + return; + } + + if (vt1->type == ValueType::Type::LONGDOUBLE || (vt2 && vt2->type == ValueType::Type::LONGDOUBLE)) { + setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::LONGDOUBLE, 0U)); + return; + } + if (vt1->type == ValueType::Type::DOUBLE || (vt2 && vt2->type == ValueType::Type::DOUBLE)) { + setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::DOUBLE, 0U)); + return; + } + if (vt1->type == ValueType::Type::FLOAT || (vt2 && vt2->type == ValueType::Type::FLOAT)) { + setValueType(parent, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::FLOAT, 0U)); + return; + } + + // iterator +/- integral = iterator + if (vt1->type == ValueType::Type::ITERATOR && vt2 && vt2->isIntegral() && + (parent->str() == "+" || parent->str() == "-")) { + setValueType(parent, *vt1); + return; + } + + if (parent->str() == "+" && vt1->type == ValueType::Type::CONTAINER && vt2 && vt2->type == ValueType::Type::CONTAINER && vt1->container == vt2->container) { + setValueType(parent, *vt1); + return; + } + } + + if (vt1->isIntegral() && vt1->pointer == 0U && + (!vt2 || (vt2->isIntegral() && vt2->pointer == 0U)) && + (ternary || parent->isArithmeticalOp() || parent->tokType() == Token::eBitOp || parent->tokType() == Token::eIncDecOp || parent->isAssignmentOp())) { + + ValueType vt; + if (!vt2 || vt1->type > vt2->type) { + vt.type = vt1->type; + vt.sign = vt1->sign; + vt.originalTypeName = vt1->originalTypeName; + } else if (vt1->type == vt2->type) { + vt.type = vt1->type; + if (vt1->sign == ValueType::Sign::UNSIGNED || vt2->sign == ValueType::Sign::UNSIGNED) + vt.sign = ValueType::Sign::UNSIGNED; + else if (vt1->sign == ValueType::Sign::UNKNOWN_SIGN || vt2->sign == ValueType::Sign::UNKNOWN_SIGN) + vt.sign = ValueType::Sign::UNKNOWN_SIGN; + else + vt.sign = ValueType::Sign::SIGNED; + vt.originalTypeName = (vt1->originalTypeName.empty() ? vt2 : vt1)->originalTypeName; + } else { + vt.type = vt2->type; + vt.sign = vt2->sign; + vt.originalTypeName = vt2->originalTypeName; + } + if (vt.type < ValueType::Type::INT && !(ternary && vt.type==ValueType::Type::BOOL)) { + vt.type = ValueType::Type::INT; + vt.sign = ValueType::Sign::SIGNED; + vt.originalTypeName.clear(); + } + + setValueType(parent, vt); + return; + } +} + +static ValueType::Type getEnumType(const Scope* scope, const Platform& platform) // TODO: also determine sign? +{ + ValueType::Type type = ValueType::Type::INT; + for (const Token* tok = scope->bodyStart; tok && tok != scope->bodyEnd; tok = tok->next()) { + if (!tok->isAssignmentOp()) + continue; + const Token* vTok = tok->astOperand2(); + if (!vTok->hasKnownIntValue()) { + if (!vTok->isLiteral()) + continue; + if (const ValueType* vt = vTok->valueType()) { + if ((vt->type > type && (vt->type == ValueType::Type::LONG || vt->type == ValueType::Type::LONGLONG))) + type = vt->type; + } + continue; + } + const MathLib::bigint value = vTok->getKnownIntValue(); + if (!platform.isIntValue(value)) { + type = ValueType::Type::LONG; + if (!platform.isLongValue(value)) + type = ValueType::Type::LONGLONG; + } + } + return type; +} + +static const Token* parsedecl(const Token* type, + ValueType* const valuetype, + ValueType::Sign defaultSignedness, + const Settings& settings, + SourceLocation loc) +{ + if (settings.debugnormal || settings.debugwarnings) + valuetype->setDebugPath(type, loc); + const Token * const previousType = type; + const unsigned int pointer0 = valuetype->pointer; + while (Token::Match(type->previous(), "%name%") && !endsWith(type->previous()->str(), ':')) + type = type->previous(); + valuetype->sign = ValueType::Sign::UNKNOWN_SIGN; + if (!valuetype->typeScope && !valuetype->smartPointerType) + valuetype->type = ValueType::Type::UNKNOWN_TYPE; + else if (valuetype->smartPointerType) + valuetype->type = ValueType::Type::SMART_POINTER; + else if (valuetype->typeScope->type == Scope::eEnum) { + const Token * enum_type = valuetype->typeScope->enumType; + if (enum_type) { + if (enum_type->isSigned()) + valuetype->sign = ValueType::Sign::SIGNED; + else if (enum_type->isUnsigned()) + valuetype->sign = ValueType::Sign::UNSIGNED; + else + valuetype->sign = defaultSignedness; + const ValueType::Type t = ValueType::typeFromString(enum_type->str(), enum_type->isLong()); + if (t != ValueType::Type::UNKNOWN_TYPE) + valuetype->type = t; + else if (enum_type->isStandardType()) + valuetype->fromLibraryType(enum_type->str(), settings); + } else + valuetype->type = getEnumType(valuetype->typeScope, settings.platform); + } else + valuetype->type = ValueType::Type::RECORD; + bool par = false; + while (Token::Match(type, "%name%|*|&|&&|::|(") && !Token::Match(type, "typename|template") && type->varId() == 0 && + !type->variable() && !type->function()) { + bool isIterator = false; + if (type->str() == "(") { + if (Token::Match(type->link(), ") const| {")) + break; + if (par) + break; + par = true; + } + if (Token::simpleMatch(type, "decltype (") && type->next()->valueType()) { + const ValueType *vt2 = type->next()->valueType(); + if (valuetype->sign == ValueType::Sign::UNKNOWN_SIGN) + valuetype->sign = vt2->sign; + if (valuetype->type == ValueType::Type::UNKNOWN_TYPE) + valuetype->type = vt2->type; + valuetype->constness += vt2->constness; + valuetype->pointer += vt2->pointer; + valuetype->reference = vt2->reference; + type = type->linkAt(1)->next(); + continue; + } + if (type->isSigned()) + valuetype->sign = ValueType::Sign::SIGNED; + else if (type->isUnsigned()) + valuetype->sign = ValueType::Sign::UNSIGNED; + if (valuetype->type == ValueType::Type::UNKNOWN_TYPE && + type->type() && type->type()->isTypeAlias() && type->type()->typeStart && + type->type()->typeStart->str() != type->str() && type->type()->typeStart != previousType) + parsedecl(type->type()->typeStart, valuetype, defaultSignedness, settings); + else if (Token::Match(type, "const|constexpr")) + valuetype->constness |= (1 << (valuetype->pointer - pointer0)); + else if (Token::simpleMatch(type, "volatile")) + valuetype->volatileness |= (1 << (valuetype->pointer - pointer0)); + else if (settings.clang && type->str().size() > 2 && type->str().find("::") < type->str().find('<')) { + TokenList typeTokens(&settings); + std::string::size_type pos1 = 0; + do { + const std::string::size_type pos2 = type->str().find("::", pos1); + if (pos2 == std::string::npos) { + typeTokens.addtoken(type->str().substr(pos1), 0, 0, 0, false); + break; + } + typeTokens.addtoken(type->str().substr(pos1, pos2 - pos1), 0, 0, 0, false); + typeTokens.addtoken("::", 0, 0, 0, false); + pos1 = pos2 + 2; + } while (pos1 < type->str().size()); + const Library::Container* container = + settings.library.detectContainerOrIterator(typeTokens.front(), &isIterator); + if (container) { + if (isIterator) + valuetype->type = ValueType::Type::ITERATOR; + else + valuetype->type = ValueType::Type::CONTAINER; + valuetype->container = container; + } else { + const Scope *scope = type->scope(); + valuetype->typeScope = scope->check->findScope(typeTokens.front(), scope); + if (valuetype->typeScope) + valuetype->type = (scope->type == Scope::ScopeType::eClass) ? ValueType::Type::RECORD : ValueType::Type::NONSTD; + } + } else if (const Library::Container* container = (type->isCpp() ? settings.library.detectContainerOrIterator(type, &isIterator) : nullptr)) { + if (isIterator) + valuetype->type = ValueType::Type::ITERATOR; + else + valuetype->type = ValueType::Type::CONTAINER; + valuetype->container = container; + while (Token::Match(type, "%type%|::|<") && type->str() != "const") { + if (type->str() == "<" && type->link()) { + if (container->type_templateArgNo >= 0) { + const Token *templateType = type->next(); + for (int j = 0; templateType && j < container->type_templateArgNo; j++) + templateType = templateType->nextTemplateArgument(); + valuetype->containerTypeToken = templateType; + } + type = type->link(); + } + type = type->next(); + } + if (type && type->str() == "(" && type->previous()->function()) + // we are past the end of the type + type = type->previous(); + continue; + } else if (const Library::SmartPointer* smartPointer = (type->isCpp() ? settings.library.detectSmartPointer(type) : nullptr)) { + const Token* argTok = Token::findsimplematch(type, "<"); + if (!argTok) + break; + valuetype->smartPointer = smartPointer; + valuetype->smartPointerTypeToken = argTok->next(); + valuetype->smartPointerType = argTok->next()->type(); + valuetype->type = ValueType::Type::SMART_POINTER; + type = argTok->link(); + if (type) + type = type->next(); + continue; + } else if (Token::Match(type, "%name% :: %name%")) { + std::string typestr; + const Token *end = type; + while (Token::Match(end, "%name% :: %name%")) { + typestr += end->str() + "::"; + end = end->tokAt(2); + } + typestr += end->str(); + if (valuetype->fromLibraryType(typestr, settings)) + type = end; + } else if (ValueType::Type::UNKNOWN_TYPE != ValueType::typeFromString(type->str(), type->isLong())) { + const ValueType::Type t0 = valuetype->type; + valuetype->type = ValueType::typeFromString(type->str(), type->isLong()); + if (t0 == ValueType::Type::LONG) { + if (valuetype->type == ValueType::Type::LONG) + valuetype->type = ValueType::Type::LONGLONG; + else if (valuetype->type == ValueType::Type::DOUBLE) + valuetype->type = ValueType::Type::LONGDOUBLE; + } + } else if (type->str() == "auto") { + const ValueType *vt = type->valueType(); + if (!vt) + return nullptr; + valuetype->type = vt->type; + valuetype->pointer = vt->pointer; + valuetype->reference = vt->reference; + if (vt->sign != ValueType::Sign::UNKNOWN_SIGN) + valuetype->sign = vt->sign; + valuetype->constness = vt->constness; + valuetype->volatileness = vt->volatileness; + valuetype->originalTypeName = vt->originalTypeName; + const bool hasConst = Token::simpleMatch(type->previous(), "const"); + const bool hasVolatile = Token::simpleMatch(type->previous(), "volatile"); + while (Token::Match(type, "%name%|*|&|&&|::") && !type->variable()) { + if (type->str() == "*") { + valuetype->pointer = 1; + if (hasConst) + valuetype->constness = 1; + if (hasVolatile) + valuetype->volatileness = 1; + } else if (type->str() == "&") { + valuetype->reference = Reference::LValue; + } else if (type->str() == "&&") { + valuetype->reference = Reference::RValue; + } + if (type->str() == "const") + valuetype->constness |= (1 << valuetype->pointer); + if (type->str() == "volatile") + valuetype->volatileness |= (1 << valuetype->pointer); + type = type->next(); + } + break; + } else if (!valuetype->typeScope && (type->str() == "struct" || type->str() == "enum")) + valuetype->type = type->str() == "struct" ? ValueType::Type::RECORD : ValueType::Type::NONSTD; + else if (!valuetype->typeScope && type->type() && type->type()->classScope) { + if (type->type()->classScope->type == Scope::ScopeType::eEnum) { + valuetype->sign = ValueType::Sign::SIGNED; + valuetype->type = getEnumType(type->type()->classScope, settings.platform); + } else { + valuetype->type = ValueType::Type::RECORD; + } + valuetype->typeScope = type->type()->classScope; + } else if (type->isName() && valuetype->sign != ValueType::Sign::UNKNOWN_SIGN && valuetype->pointer == 0U) + return nullptr; + else if (type->str() == "*") + valuetype->pointer++; + else if (type->str() == "&") + valuetype->reference = Reference::LValue; + else if (type->str() == "&&") + valuetype->reference = Reference::RValue; + else if (type->isStandardType()) + valuetype->fromLibraryType(type->str(), settings); + else if (Token::Match(type->previous(), "!!:: %name% !!::")) + valuetype->fromLibraryType(type->str(), settings); + if (!type->originalName().empty()) + valuetype->originalTypeName = type->originalName(); + type = type->next(); + if (type && type->link() && type->str() == "<") + type = type->link()->next(); + } + + // Set signedness for integral types.. + if (valuetype->isIntegral() && valuetype->sign == ValueType::Sign::UNKNOWN_SIGN) { + if (valuetype->type == ValueType::Type::CHAR) + valuetype->sign = defaultSignedness; + else if (valuetype->type >= ValueType::Type::SHORT) + valuetype->sign = ValueType::Sign::SIGNED; + } + + return (type && (valuetype->type != ValueType::Type::UNKNOWN_TYPE || valuetype->pointer > 0 || valuetype->reference != Reference::None)) ? type : nullptr; +} + +static const Scope *getClassScope(const Token *tok) +{ + return tok && tok->valueType() && tok->valueType()->typeScope && tok->valueType()->typeScope->isClassOrStruct() ? + tok->valueType()->typeScope : + nullptr; +} + +static const Function *getOperatorFunction(const Token * const tok) +{ + const std::string functionName("operator" + tok->str()); + std::multimap::const_iterator it; + + const Scope *classScope = getClassScope(tok->astOperand1()); + if (classScope) { + it = classScope->functionMap.find(functionName); + if (it != classScope->functionMap.end()) + return it->second; + } + + classScope = getClassScope(tok->astOperand2()); + if (classScope) { + it = classScope->functionMap.find(functionName); + if (it != classScope->functionMap.end()) + return it->second; + } + + return nullptr; +} + +static const Function* getFunction(const Token* tok) { + if (!tok) + return nullptr; + if (tok->function() && tok->function()->retDef) + return tok->function(); + if (const Variable* lvar = tok->variable()) { // lambda + const Function* lambda{}; + if (Token::Match(lvar->nameToken()->next(), "; %varid% = [", lvar->declarationId())) + lambda = lvar->nameToken()->tokAt(4)->function(); + else if (Token::simpleMatch(lvar->nameToken()->next(), "{ [")) + lambda = lvar->nameToken()->tokAt(2)->function(); + if (lambda && lambda->retDef) + return lambda; + } + return nullptr; +} + +void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *tokens) +{ + if (!tokens) + tokens = mTokenizer.list.front(); + + for (Token *tok = tokens; tok; tok = tok->next()) + tok->setValueType(nullptr); + + for (Token *tok = tokens; tok; tok = tok->next()) { + if (tok->isNumber()) { + if (MathLib::isFloat(tok->str())) { + ValueType::Type type = ValueType::Type::DOUBLE; + const char suffix = tok->str()[tok->str().size() - 1]; + if (suffix == 'f' || suffix == 'F') + type = ValueType::Type::FLOAT; + else if (suffix == 'L' || suffix == 'l') + type = ValueType::Type::LONGDOUBLE; + setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, type, 0U)); + } else if (MathLib::isInt(tok->str())) { + const std::string tokStr = MathLib::abs(tok->str()); + const bool unsignedSuffix = (tokStr.find_last_of("uU") != std::string::npos); + ValueType::Sign sign = unsignedSuffix ? ValueType::Sign::UNSIGNED : ValueType::Sign::SIGNED; + ValueType::Type type = ValueType::Type::INT; + const MathLib::biguint value = MathLib::toBigUNumber(tokStr); + for (std::size_t pos = tokStr.size() - 1U; pos > 0U; --pos) { + const char suffix = tokStr[pos]; + if (suffix == 'u' || suffix == 'U') + sign = ValueType::Sign::UNSIGNED; + else if (suffix == 'l' || suffix == 'L') + type = (type == ValueType::Type::INT) ? ValueType::Type::LONG : ValueType::Type::LONGLONG; + else if (pos > 2U && suffix == '4' && tokStr[pos - 1] == '6' && tokStr[pos - 2] == 'i') { + type = ValueType::Type::LONGLONG; + pos -= 2; + } else break; + } + if (mSettings.platform.type != Platform::Type::Unspecified) { + if (type <= ValueType::Type::INT && mSettings.platform.isIntValue(unsignedSuffix ? (value >> 1) : value)) + type = ValueType::Type::INT; + else if (type <= ValueType::Type::INT && !MathLib::isDec(tokStr) && mSettings.platform.isIntValue(value >> 2)) { + type = ValueType::Type::INT; + sign = ValueType::Sign::UNSIGNED; + } else if (type <= ValueType::Type::LONG && mSettings.platform.isLongValue(unsignedSuffix ? (value >> 1) : value)) + type = ValueType::Type::LONG; + else if (type <= ValueType::Type::LONG && !MathLib::isDec(tokStr) && mSettings.platform.isLongValue(value >> 2)) { + type = ValueType::Type::LONG; + sign = ValueType::Sign::UNSIGNED; + } else if (mSettings.platform.isLongLongValue(unsignedSuffix ? (value >> 1) : value)) + type = ValueType::Type::LONGLONG; + else { + type = ValueType::Type::LONGLONG; + sign = ValueType::Sign::UNSIGNED; + } + } + + setValueType(tok, ValueType(sign, type, 0U)); + } + } else if (tok->isComparisonOp() || tok->tokType() == Token::eLogicalOp) { + if (tok->isCpp() && tok->isComparisonOp() && (getClassScope(tok->astOperand1()) || getClassScope(tok->astOperand2()))) { + const Function *function = getOperatorFunction(tok); + if (function) { + ValueType vt; + parsedecl(function->retDef, &vt, mDefaultSignedness, mSettings); + setValueType(tok, vt); + continue; + } + } + setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0U)); + } else if (tok->isBoolean()) { + setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0U)); + } else if (tok->tokType() == Token::eChar || tok->tokType() == Token::eString) { + nonneg int const pointer = tok->tokType() == Token::eChar ? 0U : 1U; + nonneg int const constness = tok->tokType() == Token::eChar ? 0U : 1U; + nonneg int const volatileness = 0U; + ValueType valuetype(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::CHAR, pointer, constness, volatileness); + + if (tok->isCpp() && mSettings.standards.cpp >= Standards::CPP20 && tok->isUtf8()) { + valuetype.originalTypeName = "char8_t"; + valuetype.fromLibraryType(valuetype.originalTypeName, mSettings); + } else if (tok->isUtf16()) { + valuetype.originalTypeName = "char16_t"; + valuetype.fromLibraryType(valuetype.originalTypeName, mSettings); + } else if (tok->isUtf32()) { + valuetype.originalTypeName = "char32_t"; + valuetype.fromLibraryType(valuetype.originalTypeName, mSettings); + } else if (tok->isLong()) { + valuetype.originalTypeName = "wchar_t"; + valuetype.type = ValueType::Type::WCHAR_T; + } else if ((tok->tokType() == Token::eChar) && ((!tok->isCpp() && tok->isCChar()) || (tok->isCMultiChar()))) { + valuetype.type = ValueType::Type::INT; + valuetype.sign = ValueType::Sign::SIGNED; + } + setValueType(tok, valuetype); + } else if (tok->link() && Token::Match(tok, "(|{")) { + const Token* start = tok->astOperand1() ? tok->astOperand1()->findExpressionStartEndTokens().first : nullptr; + // cast + if (tok->isCast() && !tok->astOperand2() && Token::Match(tok, "( %name%")) { + ValueType valuetype; + if (Token::simpleMatch(parsedecl(tok->next(), &valuetype, mDefaultSignedness, mSettings), ")")) + setValueType(tok, valuetype); + } + + // C++ cast + else if (tok->astOperand2() && Token::Match(tok->astOperand1(), "static_cast|const_cast|dynamic_cast|reinterpret_cast < %name%") && tok->astOperand1()->linkAt(1)) { + ValueType valuetype; + if (Token::simpleMatch(parsedecl(tok->astOperand1()->tokAt(2), &valuetype, mDefaultSignedness, mSettings), ">")) + setValueType(tok, valuetype); + } + + // Construct smart pointer + else if (start && start->isCpp() && mSettings.library.isSmartPointer(start)) { + ValueType valuetype; + if (parsedecl(start, &valuetype, mDefaultSignedness, mSettings)) { + setValueType(tok, valuetype); + setValueType(tok->astOperand1(), valuetype); + } + + } + + // function or lambda + else if (const Function* f = getFunction(tok->previous())) { + ValueType valuetype; + if (parsedecl(f->retDef, &valuetype, mDefaultSignedness, mSettings)) + setValueType(tok, valuetype); + } + + else if (Token::simpleMatch(tok->previous(), "sizeof (")) { + ValueType valuetype(ValueType::Sign::UNSIGNED, ValueType::Type::LONG, 0U); + if (mSettings.platform.type == Platform::Type::Win64) + valuetype.type = ValueType::Type::LONGLONG; + + valuetype.originalTypeName = "size_t"; + setValueType(tok, valuetype); + + if (Token::Match(tok, "( %type% %type%| *| *| )")) { + ValueType vt; + if (parsedecl(tok->next(), &vt, mDefaultSignedness, mSettings)) { + setValueType(tok->next(), vt); + } + } + } + + // function style cast + else if (tok->previous() && tok->previous()->isStandardType()) { + ValueType valuetype; + if (tok->astOperand1() && valuetype.fromLibraryType(tok->astOperand1()->expressionString(), mSettings)) { + setValueType(tok, valuetype); + continue; + } + + valuetype.type = ValueType::typeFromString(tok->previous()->str(), tok->previous()->isLong()); + if (tok->previous()->isUnsigned()) + valuetype.sign = ValueType::Sign::UNSIGNED; + else if (tok->previous()->isSigned()) + valuetype.sign = ValueType::Sign::SIGNED; + else if (valuetype.isIntegral() && valuetype.type != ValueType::UNKNOWN_INT) + valuetype.sign = mDefaultSignedness; + setValueType(tok, valuetype); + } + + // constructor call + else if (tok->previous() && tok->previous()->function() && tok->previous()->function()->isConstructor()) { + ValueType valuetype; + valuetype.type = ValueType::RECORD; + valuetype.typeScope = tok->previous()->function()->token->scope(); + setValueType(tok, valuetype); + } + + else if (Token::simpleMatch(tok->previous(), "= {") && tok->tokAt(-2) && tok->tokAt(-2)->valueType()) { + ValueType vt = *tok->tokAt(-2)->valueType(); + setValueType(tok, vt); + } + + // library type/function + else if (tok->previous()) { + // Aggregate constructor + if (Token::Match(tok->previous(), "%name%")) { + ValueType valuetype; + if (parsedecl(tok->previous(), &valuetype, mDefaultSignedness, mSettings)) { + if (valuetype.typeScope) { + setValueType(tok, valuetype); + continue; + } + } + } + if (tok->isCpp() && tok->astParent() && Token::Match(tok->astOperand1(), "%name%|::")) { + const Token *typeStartToken = tok->astOperand1(); + while (typeStartToken && typeStartToken->str() == "::") + typeStartToken = typeStartToken->astOperand1(); + if (mSettings.library.detectContainerOrIterator(typeStartToken) || + mSettings.library.detectSmartPointer(typeStartToken)) { + ValueType vt; + if (parsedecl(typeStartToken, &vt, mDefaultSignedness, mSettings)) { + setValueType(tok, vt); + continue; + } + } + + const std::string e = tok->astOperand1()->expressionString(); + + if ((e == "std::make_shared" || e == "std::make_unique") && Token::Match(tok->astOperand1(), ":: %name% < %name%")) { + ValueType vt; + parsedecl(tok->astOperand1()->tokAt(3), &vt, mDefaultSignedness, mSettings); + if (vt.typeScope) { + vt.smartPointerType = vt.typeScope->definedType; + vt.typeScope = nullptr; + } + if (e == "std::make_shared" && mSettings.library.smartPointers.count("std::shared_ptr") > 0) + vt.smartPointer = &mSettings.library.smartPointers.at("std::shared_ptr"); + if (e == "std::make_unique" && mSettings.library.smartPointers.count("std::unique_ptr") > 0) + vt.smartPointer = &mSettings.library.smartPointers.at("std::unique_ptr"); + vt.type = ValueType::Type::SMART_POINTER; + vt.smartPointerTypeToken = tok->astOperand1()->tokAt(3); + setValueType(tok, vt); + continue; + } + + ValueType podtype; + if (podtype.fromLibraryType(e, mSettings)) { + setValueType(tok, podtype); + continue; + } + } + + const std::string& typestr(mSettings.library.returnValueType(tok->previous())); + if (!typestr.empty()) { + ValueType valuetype; + TokenList tokenList(&mSettings); + std::istringstream istr(typestr+";"); + tokenList.createTokens(istr, tok->isCpp() ? Standards::Language::CPP : Standards::Language::C); // TODO: check result? + tokenList.simplifyStdType(); + if (parsedecl(tokenList.front(), &valuetype, mDefaultSignedness, mSettings)) { + valuetype.originalTypeName = typestr; + setValueType(tok, valuetype); + } + } + + //Is iterator fetching function invoked on container? + const bool isReturnIter = typestr == "iterator"; + if (typestr.empty() || isReturnIter) { + if (Token::simpleMatch(tok->astOperand1(), ".") && + tok->astOperand1()->astOperand1() && + tok->astOperand1()->astOperand2() && + tok->astOperand1()->astOperand1()->valueType() && + tok->astOperand1()->astOperand1()->valueType()->container) { + const Library::Container *cont = tok->astOperand1()->astOperand1()->valueType()->container; + const auto it = cont->functions.find(tok->astOperand1()->astOperand2()->str()); + if (it != cont->functions.end()) { + if (it->second.yield == Library::Container::Yield::START_ITERATOR || + it->second.yield == Library::Container::Yield::END_ITERATOR || + it->second.yield == Library::Container::Yield::ITERATOR) { + ValueType vt; + vt.type = ValueType::Type::ITERATOR; + vt.container = cont; + vt.containerTypeToken = + tok->astOperand1()->astOperand1()->valueType()->containerTypeToken; + setValueType(tok, vt); + continue; + } + } + //Is iterator fetching function called? + } else if (Token::simpleMatch(tok->astOperand1(), "::") && + tok->astOperand2() && + tok->astOperand2()->isVariable()) { + const auto* const paramVariable = tok->astOperand2()->variable(); + if (!paramVariable || + !paramVariable->valueType() || + !paramVariable->valueType()->container) { + continue; + } + + const auto yield = astFunctionYield(tok->previous(), mSettings); + if (yield == Library::Container::Yield::START_ITERATOR || + yield == Library::Container::Yield::END_ITERATOR || + yield == Library::Container::Yield::ITERATOR) { + ValueType vt; + vt.type = ValueType::Type::ITERATOR; + vt.container = paramVariable->valueType()->container; + vt.containerTypeToken = paramVariable->valueType()->containerTypeToken; + setValueType(tok, vt); + } + } + if (isReturnIter) { + const std::vector args = getArguments(tok); + if (!args.empty()) { + const Library::ArgumentChecks::IteratorInfo* info = mSettings.library.getArgIteratorInfo(tok->previous(), 1); + if (info && info->it) { + const Token* contTok = args[0]; + if (Token::simpleMatch(args[0]->astOperand1(), ".") && args[0]->astOperand1()->astOperand1()) // .begin() + contTok = args[0]->astOperand1()->astOperand1(); + else if (Token::simpleMatch(args[0], "(") && args[0]->astOperand2()) // std::begin() + contTok = args[0]->astOperand2(); + while (Token::simpleMatch(contTok, "[")) // move to container token + contTok = contTok->astOperand1(); + if (Token::simpleMatch(contTok, ".")) + contTok = contTok->astOperand2(); + if (contTok && contTok->variable() && contTok->variable()->valueType() && contTok->variable()->valueType()->container) { + ValueType vt; + vt.type = ValueType::Type::ITERATOR; + vt.container = contTok->variable()->valueType()->container; + vt.containerTypeToken = contTok->variable()->valueType()->containerTypeToken; + setValueType(tok, vt); + } else if (Token::simpleMatch(contTok, "(") && contTok->astOperand1() && contTok->astOperand1()->function()) { + const Function* func = contTok->astOperand1()->function(); + if (const ValueType* funcVt = func->tokenDef->next()->valueType()) { + ValueType vt; + vt.type = ValueType::Type::ITERATOR; + vt.container = funcVt->container; + vt.containerTypeToken = funcVt->containerTypeToken; + setValueType(tok, vt); + } + } + } + } + } + continue; + } + TokenList tokenList(&mSettings); + std::istringstream istr(typestr+";"); + if (tokenList.createTokens(istr, tok->isCpp() ? Standards::Language::CPP : Standards::Language::C)) { + ValueType vt; + tokenList.simplifyPlatformTypes(); + tokenList.simplifyStdType(); + if (parsedecl(tokenList.front(), &vt, mDefaultSignedness, mSettings)) { + vt.originalTypeName = typestr; + setValueType(tok, vt); + } + } + } + } else if (tok->str() == "return") { + const Scope *functionScope = tok->scope(); + while (functionScope && functionScope->isExecutable() && functionScope->type != Scope::eLambda && functionScope->type != Scope::eFunction) + functionScope = functionScope->nestedIn; + if (functionScope && functionScope->type == Scope::eFunction && functionScope->function && + functionScope->function->retDef) { + ValueType vt = ValueType::parseDecl(functionScope->function->retDef, mSettings); + setValueType(tok, vt); + if (Token::simpleMatch(tok, "return {")) + setValueType(tok->next(), vt); + } + } else if (tok->variable()) { + setValueType(tok, *tok->variable()); + if (!tok->variable()->valueType() && tok->valueType()) + const_cast(tok->variable())->setValueType(*tok->valueType()); + } else if (tok->enumerator()) { + setValueType(tok, *tok->enumerator()); + } else if (tok->isKeyword() && tok->str() == "new") { + const Token *typeTok = tok->next(); + if (Token::Match(typeTok, "( std| ::| nothrow )")) + typeTok = typeTok->link()->next(); + bool isIterator = false; + if (const Library::Container* c = mSettings.library.detectContainerOrIterator(typeTok, &isIterator)) { + ValueType vt; + vt.pointer = 1; + vt.container = c; + vt.type = isIterator ? ValueType::Type::ITERATOR : ValueType::Type::CONTAINER; + setValueType(tok, vt); + continue; + } + std::string typestr; + while (Token::Match(typeTok, "%name% :: %name%")) { + typestr += typeTok->str() + "::"; + typeTok = typeTok->tokAt(2); + } + if (!Token::Match(typeTok, "%type% ;|[|(")) + continue; + typestr += typeTok->str(); + ValueType vt; + vt.pointer = 1; + if (typeTok->type() && typeTok->type()->classScope) { + vt.type = ValueType::Type::RECORD; + vt.typeScope = typeTok->type()->classScope; + } else { + vt.type = ValueType::typeFromString(typestr, typeTok->isLong()); + if (vt.type == ValueType::Type::UNKNOWN_TYPE) + vt.fromLibraryType(typestr, mSettings); + if (vt.type == ValueType::Type::UNKNOWN_TYPE) + continue; + if (typeTok->isUnsigned()) + vt.sign = ValueType::Sign::UNSIGNED; + else if (typeTok->isSigned()) + vt.sign = ValueType::Sign::SIGNED; + if (vt.sign == ValueType::Sign::UNKNOWN_SIGN && vt.isIntegral()) + vt.sign = (vt.type == ValueType::Type::CHAR) ? mDefaultSignedness : ValueType::Sign::SIGNED; + } + setValueType(tok, vt); + if (Token::simpleMatch(tok->astOperand1(), "(")) { + vt.pointer--; + setValueType(tok->astOperand1(), vt); + } + } else if (tok->isKeyword() && tok->str() == "return" && tok->scope()) { + const Scope* fscope = tok->scope(); + while (fscope && !fscope->function) + fscope = fscope->nestedIn; + if (fscope && fscope->function && fscope->function->retDef) { + ValueType vt; + parsedecl(fscope->function->retDef, &vt, mDefaultSignedness, mSettings); + setValueType(tok, vt); + } + } else if (tok->isKeyword() && tok->str() == "this" && tok->scope()->isExecutable()) { + const Scope* fscope = tok->scope(); + while (fscope && !fscope->function) + fscope = fscope->nestedIn; + const Scope* defScope = fscope && fscope->function->tokenDef ? fscope->function->tokenDef->scope() : nullptr; + if (defScope && defScope->isClassOrStruct()) { + ValueType vt(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::RECORD, 1); + vt.typeScope = defScope; + if (fscope->function->isConst()) + vt.constness = 1; + if (fscope->function->isVolatile()) + vt.volatileness = 1; + setValueType(tok, vt); + } + } + } + + if (reportDebugWarnings && mSettings.debugwarnings) { + for (Token *tok = tokens; tok; tok = tok->next()) { + if (tok->str() == "auto" && !tok->valueType()) { + if (Token::Match(tok->next(), "%name% ; %name% = [") && isLambdaCaptureList(tok->tokAt(5))) + continue; + if (Token::Match(tok->next(), "%name% {|= [") && isLambdaCaptureList(tok->tokAt(3))) + continue; + debugMessage(tok, "autoNoType", "auto token with no type."); + } + } + } + + // Update functions with new type information. + createSymbolDatabaseSetFunctionPointers(false); + + // Update auto variables with new type information. + createSymbolDatabaseSetVariablePointers(); +} + +ValueType ValueType::parseDecl(const Token *type, const Settings &settings) +{ + ValueType vt; + parsedecl(type, &vt, settings.platform.defaultSign == 'u' ? Sign::UNSIGNED : Sign::SIGNED, settings); + return vt; +} + +ValueType::Type ValueType::typeFromString(const std::string &typestr, bool longType) +{ + if (typestr == "void") + return ValueType::Type::VOID; + if (typestr == "bool" || typestr == "_Bool") + return ValueType::Type::BOOL; + if (typestr== "char") + return ValueType::Type::CHAR; + if (typestr == "short") + return ValueType::Type::SHORT; + if (typestr == "wchar_t") + return ValueType::Type::WCHAR_T; + if (typestr == "int") + return ValueType::Type::INT; + if (typestr == "long") + return longType ? ValueType::Type::LONGLONG : ValueType::Type::LONG; + if (typestr == "float") + return ValueType::Type::FLOAT; + if (typestr == "double") + return longType ? ValueType::Type::LONGDOUBLE : ValueType::Type::DOUBLE; + return ValueType::Type::UNKNOWN_TYPE; +} + +bool ValueType::fromLibraryType(const std::string &typestr, const Settings &settings) +{ + const Library::PodType* podtype = settings.library.podtype(typestr); + if (podtype && (podtype->sign == 's' || podtype->sign == 'u')) { + if (podtype->size == 1) + type = ValueType::Type::CHAR; + else if (podtype->size == settings.platform.sizeof_int) + type = ValueType::Type::INT; + else if (podtype->size == settings.platform.sizeof_short) + type = ValueType::Type::SHORT; + else if (podtype->size == settings.platform.sizeof_long) + type = ValueType::Type::LONG; + else if (podtype->size == settings.platform.sizeof_long_long) + type = ValueType::Type::LONGLONG; + else if (podtype->stdtype == Library::PodType::Type::BOOL) + type = ValueType::Type::BOOL; + else if (podtype->stdtype == Library::PodType::Type::CHAR) + type = ValueType::Type::CHAR; + else if (podtype->stdtype == Library::PodType::Type::SHORT) + type = ValueType::Type::SHORT; + else if (podtype->stdtype == Library::PodType::Type::INT) + type = ValueType::Type::INT; + else if (podtype->stdtype == Library::PodType::Type::LONG) + type = ValueType::Type::LONG; + else if (podtype->stdtype == Library::PodType::Type::LONGLONG) + type = ValueType::Type::LONGLONG; + else + type = ValueType::Type::UNKNOWN_INT; + sign = (podtype->sign == 'u') ? ValueType::UNSIGNED : ValueType::SIGNED; + return true; + } + if (podtype && podtype->stdtype == Library::PodType::Type::NO) { + type = ValueType::Type::POD; + sign = ValueType::UNKNOWN_SIGN; + return true; + } + + const Library::PlatformType *platformType = settings.library.platform_type(typestr, settings.platform.toString()); + if (platformType) { + if (platformType->mType == "char") + type = ValueType::Type::CHAR; + else if (platformType->mType == "short") + type = ValueType::Type::SHORT; + else if (platformType->mType == "wchar_t") + type = ValueType::Type::WCHAR_T; + else if (platformType->mType == "int") + type = platformType->mLong ? ValueType::Type::LONG : ValueType::Type::INT; + else if (platformType->mType == "long") + type = platformType->mLong ? ValueType::Type::LONGLONG : ValueType::Type::LONG; + if (platformType->mSigned) + sign = ValueType::SIGNED; + else if (platformType->mUnsigned) + sign = ValueType::UNSIGNED; + if (platformType->mPointer) + pointer = 1; + if (platformType->mPtrPtr) + pointer = 2; + if (platformType->mConstPtr) + constness = 1; + return true; + } + if (!podtype && (typestr == "size_t" || typestr == "std::size_t")) { + originalTypeName = "size_t"; + sign = ValueType::UNSIGNED; + if (settings.platform.sizeof_size_t == settings.platform.sizeof_long) + type = ValueType::Type::LONG; + else if (settings.platform.sizeof_size_t == settings.platform.sizeof_long_long) + type = ValueType::Type::LONGLONG; + else if (settings.platform.sizeof_size_t == settings.platform.sizeof_int) + type = ValueType::Type::INT; + else + type = ValueType::Type::UNKNOWN_INT; + return true; + } + + return false; +} + +std::string ValueType::dump() const +{ + std::string ret; + switch (type) { + case UNKNOWN_TYPE: + return ""; + case NONSTD: + ret += "valueType-type=\"nonstd\""; + break; + case POD: + ret += "valueType-type=\"pod\""; + break; + case RECORD: + ret += "valueType-type=\"record\""; + break; + case SMART_POINTER: + ret += "valueType-type=\"smart-pointer\""; + break; + case CONTAINER: { + ret += "valueType-type=\"container\""; + ret += " valueType-containerId=\""; + ret += id_string(container); + ret += "\""; + break; + } + case ITERATOR: + ret += "valueType-type=\"iterator\""; + break; + case VOID: + ret += "valueType-type=\"void\""; + break; + case BOOL: + ret += "valueType-type=\"bool\""; + break; + case CHAR: + ret += "valueType-type=\"char\""; + break; + case SHORT: + ret += "valueType-type=\"short\""; + break; + case WCHAR_T: + ret += "valueType-type=\"wchar_t\""; + break; + case INT: + ret += "valueType-type=\"int\""; + break; + case LONG: + ret += "valueType-type=\"long\""; + break; + case LONGLONG: + ret += "valueType-type=\"long long\""; + break; + case UNKNOWN_INT: + ret += "valueType-type=\"unknown int\""; + break; + case FLOAT: + ret += "valueType-type=\"float\""; + break; + case DOUBLE: + ret += "valueType-type=\"double\""; + break; + case LONGDOUBLE: + ret += "valueType-type=\"long double\""; + break; + } + + switch (sign) { + case Sign::UNKNOWN_SIGN: + break; + case Sign::SIGNED: + ret += " valueType-sign=\"signed\""; + break; + case Sign::UNSIGNED: + ret += " valueType-sign=\"unsigned\""; + break; + } + + if (bits > 0) { + ret += " valueType-bits=\""; + ret += std::to_string(bits); + ret += '\"'; + } + + if (pointer > 0) { + ret += " valueType-pointer=\""; + ret += std::to_string(pointer); + ret += '\"'; + } + + if (constness > 0) { + ret += " valueType-constness=\""; + ret += std::to_string(constness); + ret += '\"'; + } + + if (volatileness > 0) { + ret += " valueType-volatileness=\""; + ret += std::to_string(volatileness); + ret += '\"'; + } + + if (reference == Reference::None) + ret += " valueType-reference=\"None\""; + else if (reference == Reference::LValue) + ret += " valueType-reference=\"LValue\""; + else if (reference == Reference::RValue) + ret += " valueType-reference=\"RValue\""; + + if (typeScope) { + ret += " valueType-typeScope=\""; + ret += id_string(typeScope); + ret += '\"'; + } + + if (!originalTypeName.empty()) { + ret += " valueType-originalTypeName=\""; + ret += ErrorLogger::toxml(originalTypeName); + ret += '\"'; + } + + return ret; +} + +bool ValueType::isConst(nonneg int indirect) const +{ + if (indirect > pointer) + return false; + return constness & (1 << (pointer - indirect)); +} + +bool ValueType::isVolatile(nonneg int indirect) const +{ + if (indirect > pointer) + return false; + return volatileness & (1 << (pointer - indirect)); +} +MathLib::bigint ValueType::typeSize(const Platform &platform, bool p) const +{ + if (p && pointer) + return platform.sizeof_pointer; + + if (typeScope && typeScope->definedType && typeScope->definedType->sizeOf) + return typeScope->definedType->sizeOf; + + switch (type) { + case ValueType::Type::BOOL: + return platform.sizeof_bool; + case ValueType::Type::CHAR: + return 1; + case ValueType::Type::SHORT: + return platform.sizeof_short; + case ValueType::Type::WCHAR_T: + return platform.sizeof_wchar_t; + case ValueType::Type::INT: + return platform.sizeof_int; + case ValueType::Type::LONG: + return platform.sizeof_long; + case ValueType::Type::LONGLONG: + return platform.sizeof_long_long; + case ValueType::Type::FLOAT: + return platform.sizeof_float; + case ValueType::Type::DOUBLE: + return platform.sizeof_double; + case ValueType::Type::LONGDOUBLE: + return platform.sizeof_long_double; + default: + break; + } + + // Unknown invalid size + return 0; +} + +bool ValueType::isTypeEqual(const ValueType* that) const +{ + if (!that) + return false; + auto tie = [](const ValueType* vt) { + return std::tie(vt->type, vt->container, vt->pointer, vt->typeScope, vt->smartPointer); + }; + return tie(this) == tie(that); +} + +std::string ValueType::str() const +{ + std::string ret; + if (constness & 1) + ret = " const"; + if (volatileness & 1) + ret = " volatile"; + if (type == VOID) + ret += " void"; + else if (isIntegral()) { + if (sign == SIGNED) + ret += " signed"; + else if (sign == UNSIGNED) + ret += " unsigned"; + if (type == BOOL) + ret += " bool"; + else if (type == CHAR) + ret += " char"; + else if (type == SHORT) + ret += " short"; + else if (type == WCHAR_T) + ret += " wchar_t"; + else if (type == INT) + ret += " int"; + else if (type == LONG) + ret += " long"; + else if (type == LONGLONG) + ret += " long long"; + else if (type == UNKNOWN_INT) + ret += " unknown_int"; + } else if (type == FLOAT) + ret += " float"; + else if (type == DOUBLE) + ret += " double"; + else if (type == LONGDOUBLE) + ret += " long double"; + else if ((type == ValueType::Type::NONSTD || type == ValueType::Type::RECORD) && typeScope) { + std::string className(typeScope->className); + const Scope *scope = typeScope->definedType ? typeScope->definedType->enclosingScope : typeScope->nestedIn; + while (scope && scope->type != Scope::eGlobal) { + if (scope->type == Scope::eClass || scope->type == Scope::eStruct || scope->type == Scope::eNamespace) + className = scope->className + "::" + className; + scope = (scope->definedType && scope->definedType->enclosingScope) ? scope->definedType->enclosingScope : scope->nestedIn; + } + ret += ' ' + className; + } else if (type == ValueType::Type::CONTAINER && container) { + ret += " container(" + container->startPattern + ')'; + } else if (type == ValueType::Type::ITERATOR && container) { + ret += " iterator(" + container->startPattern + ')'; + } else if (type == ValueType::Type::SMART_POINTER && smartPointer) { + ret += " smart-pointer(" + smartPointer->name + ")"; + } + for (unsigned int p = 0; p < pointer; p++) { + ret += " *"; + if (constness & (2 << p)) + ret += " const"; + if (volatileness & (2 << p)) + ret += " volatile"; + } + if (reference == Reference::LValue) + ret += " &"; + else if (reference == Reference::RValue) + ret += " &&"; + if (ret.empty()) + return ret; + return ret.substr(1); +} + +void ValueType::setDebugPath(const Token* tok, SourceLocation ctx, const SourceLocation &local) +{ + std::string file = ctx.file_name(); + if (file.empty()) + return; + std::string s = Path::stripDirectoryPart(file) + ":" + std::to_string(ctx.line()) + ": " + ctx.function_name() + + " => " + local.function_name(); + debugPath.emplace_back(tok, std::move(s)); +} + +ValueType::MatchResult ValueType::matchParameter(const ValueType *call, const ValueType *func) +{ + if (!call || !func) + return ValueType::MatchResult::UNKNOWN; + if (call->pointer != func->pointer) { + if (call->pointer > 1 && func->pointer == 1 && func->type == ValueType::Type::VOID) + return ValueType::MatchResult::FALLBACK1; + if (call->pointer == 1 && func->pointer == 0 && func->isIntegral() && func->sign != ValueType::Sign::SIGNED) + return ValueType::MatchResult::FALLBACK1; + if (call->pointer == 1 && call->type == ValueType::Type::CHAR && func->pointer == 0 && func->container && func->container->stdStringLike) + return ValueType::MatchResult::FALLBACK2; + return ValueType::MatchResult::NOMATCH; // TODO + } + if (call->pointer > 0) { + if ((call->constness | func->constness) != func->constness) + return ValueType::MatchResult::NOMATCH; + if ((call->volatileness | func->volatileness) != func->volatileness) + return ValueType::MatchResult::NOMATCH; + if (call->constness == 0 && func->constness != 0 && func->reference != Reference::None) + return ValueType::MatchResult::NOMATCH; + if (call->volatileness == 0 && func->volatileness != 0 && func->reference != Reference::None) + return ValueType::MatchResult::NOMATCH; + } + if (call->type != func->type || (call->isEnum() && !func->isEnum())) { + if (call->type == ValueType::Type::VOID || func->type == ValueType::Type::VOID) + return ValueType::MatchResult::FALLBACK1; + if (call->pointer > 0) + return func->type == ValueType::UNKNOWN_TYPE ? ValueType::MatchResult::UNKNOWN : ValueType::MatchResult::NOMATCH; + if (call->isIntegral() && func->isIntegral()) + return call->type < func->type ? + ValueType::MatchResult::FALLBACK1 : + ValueType::MatchResult::FALLBACK2; + if (call->isFloat() && func->isFloat()) + return ValueType::MatchResult::FALLBACK1; + if (call->isIntegral() && func->isFloat()) + return ValueType::MatchResult::FALLBACK2; + if (call->isFloat() && func->isIntegral()) + return ValueType::MatchResult::FALLBACK2; + return ValueType::MatchResult::UNKNOWN; // TODO + } + + if (call->typeScope != nullptr || func->typeScope != nullptr) { + if (call->typeScope != func->typeScope && + !(call->typeScope && func->typeScope && call->typeScope->definedType && call->typeScope->definedType->isDerivedFrom(func->typeScope->className))) + return ValueType::MatchResult::NOMATCH; + } + + if (call->container != nullptr || func->container != nullptr) { + if (call->container != func->container) + return ValueType::MatchResult::NOMATCH; + } + + if (func->typeScope != nullptr && func->container != nullptr) { + if (func->type < ValueType::Type::VOID || func->type == ValueType::Type::UNKNOWN_INT) + return ValueType::MatchResult::UNKNOWN; + } + + if (call->isIntegral() && func->isIntegral() && call->sign != ValueType::Sign::UNKNOWN_SIGN && func->sign != ValueType::Sign::UNKNOWN_SIGN && call->sign != func->sign) + return ValueType::MatchResult::FALLBACK1; + + if (func->reference != Reference::None && (func->constness > call->constness || func->volatileness > call->volatileness)) + return ValueType::MatchResult::FALLBACK1; + + return ValueType::MatchResult::SAME; +} + +ValueType::MatchResult ValueType::matchParameter(const ValueType *call, const Variable *callVar, const Variable *funcVar) +{ + ValueType vt; + const ValueType* pvt = funcVar->valueType(); + if (pvt && funcVar->isArray() && !(funcVar->isStlType() && Token::simpleMatch(funcVar->typeStartToken(), "std :: array"))) { // std::array doesn't decay to a pointer + vt = *pvt; + if (vt.pointer == 0) // don't bump array of pointers + ++vt.pointer; + pvt = &vt; + } + const ValueType::MatchResult res = ValueType::matchParameter(call, pvt); + if (callVar && ((res == ValueType::MatchResult::SAME && call->container) || res == ValueType::MatchResult::UNKNOWN)) { + const std::string type1 = getTypeString(callVar->typeStartToken()); + const std::string type2 = getTypeString(funcVar->typeStartToken()); + const bool templateVar = + funcVar->scope() && funcVar->scope()->function && funcVar->scope()->function->templateDef; + if (type1 == type2) + return ValueType::MatchResult::SAME; + if (!templateVar && type1.find("auto") == std::string::npos && type2.find("auto") == std::string::npos) + return ValueType::MatchResult::NOMATCH; + } + return res; +} diff --git a/cppcheck-2.14.0/lib/symboldatabase.h b/cppcheck-2.14.0/lib/symboldatabase.h new file mode 100644 index 00000000..3d96fa4e --- /dev/null +++ b/cppcheck-2.14.0/lib/symboldatabase.h @@ -0,0 +1,1483 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef symboldatabaseH +#define symboldatabaseH +//--------------------------------------------------------------------------- + +#include "config.h" +#include "errortypes.h" +#include "library.h" +#include "mathlib.h" +#include "sourcelocation.h" +#include "token.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class Platform; +class ErrorLogger; +class Function; +class Scope; +class Settings; +class SymbolDatabase; +class Tokenizer; +class ValueType; + +/** + * @brief Access control enumerations. + */ +enum class AccessControl { Public, Protected, Private, Global, Namespace, Argument, Local, Throw }; + +/** + * @brief Array dimension information. + */ +struct Dimension { + const Token* tok{}; ///< size token + MathLib::bigint num{}; ///< (assumed) dimension length when size is a number, 0 if not known + bool known = true; ///< Known size +}; + +/** @brief Information about a class type. */ +class CPPCHECKLIB Type { +public: + const Token* classDef; ///< Points to "class" token + const Scope* classScope; + const Scope* enclosingScope; + enum class NeedInitialization { + Unknown, True, False + } needInitialization = NeedInitialization::Unknown; + + struct BaseInfo { + 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 { + const Token* nameStart{}; + const Token* nameEnd{}; + const Type* type{}; + }; + + std::vector derivedFrom; + std::vector friendList; + + const Token* typeStart{}; + const Token* typeEnd{}; + MathLib::bigint sizeOf{}; + + explicit Type(const Token* classDef_ = nullptr, const Scope* classScope_ = nullptr, const Scope* enclosingScope_ = nullptr) : + classDef(classDef_), + classScope(classScope_), + enclosingScope(enclosingScope_) { + if (classDef_ && classDef_->str() == "enum") + needInitialization = NeedInitialization::True; + else if (classDef_ && classDef_->str() == "using") { + typeStart = classDef->tokAt(3); + typeEnd = typeStart; + while (typeEnd->next() && typeEnd->next()->str() != ";") { + if (Token::simpleMatch(typeEnd, "decltype (")) + typeEnd = typeEnd->linkAt(1); + else + typeEnd = typeEnd->next(); + } + } + } + + std::string name() const; + + const std::string& type() const { + return classDef ? classDef->str() : emptyString; + } + + bool isClassType() const; + bool isEnumType() const; + bool isStructType() const; + bool isUnionType() const; + + bool isTypeAlias() const { + return classDef && classDef->str() == "using"; + } + + const Token *initBaseInfo(const Token *tok, const Token *tok1); + + const Function* getFunction(const std::string& funcName) const; + + /** + * Check for circulare dependencies, i.e. loops within the class hierarchy + * @param ancestors list of ancestors. For internal usage only, clients should not supply this argument. + * @return true if there is a circular dependency + */ + bool hasCircularDependencies(std::set* ancestors = nullptr) const; + + /** + * Check for dependency + * @param ancestor potential ancestor + * @return true if there is a dependency + */ + bool findDependency(const Type* ancestor) const; + + bool isDerivedFrom(const std::string & ancestor) const; +}; + +struct CPPCHECKLIB Enumerator { + explicit Enumerator(const Scope * scope_) : scope(scope_) {} + const Scope * scope; + const Token* name{}; + MathLib::bigint value{}; + const Token* start{}; + const Token* end{}; + bool value_known{}; +}; + +/** @brief Information about a member variable. */ +class CPPCHECKLIB Variable { + /** @brief flags mask used to access specific bit. */ + enum { + fIsMutable = (1 << 0), /** @brief mutable variable */ + fIsStatic = (1 << 1), /** @brief static variable */ + fIsConst = (1 << 2), /** @brief const variable */ + fIsExtern = (1 << 3), /** @brief extern variable */ + fIsClass = (1 << 4), /** @brief user defined type */ + fIsArray = (1 << 5), /** @brief array variable */ + fIsPointer = (1 << 6), /** @brief pointer variable */ + fIsReference = (1 << 7), /** @brief reference variable */ + fIsRValueRef = (1 << 8), /** @brief rvalue reference variable */ + fHasDefault = (1 << 9), /** @brief function argument with default value */ + fIsStlType = (1 << 10), /** @brief STL type ('std::') */ + fIsStlString = (1 << 11), /** @brief std::string|wstring|basic_string<T>|u16string|u32string */ + fIsFloatType = (1 << 12), /** @brief Floating point type */ + fIsVolatile = (1 << 13), /** @brief volatile */ + fIsSmartPointer = (1 << 14),/** @brief std::shared_ptr|unique_ptr */ + fIsMaybeUnused = (1 << 15), /** @brief marked [[maybe_unused]] */ + fIsInit = (1 << 16), /** @brief Is variable initialized in declaration */ + }; + + /** + * Get specified flag state. + * @param flag_ flag to get state of + * @return true if flag set or false in flag not set + */ + bool getFlag(unsigned int flag_) const { + return ((mFlags & flag_) != 0); + } + + /** + * Set specified flag state. + * @param flag_ flag to set state + * @param state_ new state of flag + */ + void setFlag(unsigned int flag_, bool state_) { + mFlags = state_ ? mFlags | flag_ : mFlags & ~flag_; + } + + /** + * @brief parse and save array dimension information + * @param settings Platform settings and library + * @param isContainer Is the array container-like? + * @return true if array, false if not + */ + bool arrayDimensions(const Settings& settings, bool& isContainer); + +public: + Variable(const Token *name_, const Token *start_, const Token *end_, + nonneg int index_, AccessControl access_, const Type *type_, + const Scope *scope_, const Settings* settings) + : mNameToken(name_), + mTypeStartToken(start_), + mTypeEndToken(end_), + mIndex(index_), + mAccess(access_), + mFlags(0), + mType(type_), + mScope(scope_) { + evaluate(settings); + } + + Variable(const Token *name_, const std::string &clangType, const Token *typeStart, + const Token *typeEnd, nonneg int index_, AccessControl access_, + const Type *type_, const Scope *scope_); + + Variable(const Variable &var, const Scope *scope); + + Variable(const Variable &var); + + ~Variable(); + + Variable &operator=(const Variable &var); + + /** + * Get name token. + * @return name token + */ + const Token *nameToken() const { + return mNameToken; + } + + /** + * Get type start token. + * The type start token doesn't account 'static' and 'const' qualifiers + * E.g.: + * static const int * const p = ...; + * type start token ^ + * @return type start token + */ + const Token *typeStartToken() const { + return mTypeStartToken; + } + + /** + * Get type end token. + * The type end token doesn't account the forward 'const' qualifier + * E.g.: + * static const int * const p = ...; + * type end token ^ + * @return type end token + */ + const Token *typeEndToken() const { + return mTypeEndToken; + } + + /** + * Get end token of variable declaration + * E.g. + * int i[2][3] = ... + * end token ^ + * @return variable declaration end token + */ + const Token *declEndToken() const; + + /** + * Get name string. + * @return name string + */ + const std::string &name() const { + // name may not exist for function arguments + if (mNameToken) + return mNameToken->str(); + + return emptyString; + } + + /** + * Get declaration ID (varId used for variable in its declaration). + * @return declaration ID + */ + nonneg int declarationId() const { + // name may not exist for function arguments + if (mNameToken) + return mNameToken->varId(); + + return 0; + } + + /** + * Get index of variable in declared order. + * @return variable index + */ + nonneg int index() const { + return mIndex; + } + + /** + * Is variable public. + * @return true if public, false if not + */ + bool isPublic() const { + return mAccess == AccessControl::Public; + } + + /** + * Is variable protected. + * @return true if protected, false if not + */ + bool isProtected() const { + return mAccess == AccessControl::Protected; + } + + /** + * Is variable private. + * @return true if private, false if not + */ + bool isPrivate() const { + return mAccess == AccessControl::Private; + } + + /** + * Is variable global. + * @return true if global, false if not + */ + bool isGlobal() const { + return mAccess == AccessControl::Global; + } + + /** + * Is variable in a namespace. + * @return true if in a namespace, false if not + */ + // cppcheck-suppress unusedFunction + bool isNamespace() const { + return mAccess == AccessControl::Namespace; + } + + /** + * Is variable a function argument. + * @return true if a function argument, false if not + */ + bool isArgument() const { + return mAccess == AccessControl::Argument; + } + + /** + * Is variable local. + * @return true if local, false if not + */ + bool isLocal() const { + return (mAccess == AccessControl::Local) && !isExtern(); + } + + /** + * Is variable a member of a user-defined type. + * @return true if member, false if not or unknown + */ + bool isMember() const; + + /** + * Is variable mutable. + * @return true if mutable, false if not + */ + bool isMutable() const { + return getFlag(fIsMutable); + } + + /** + * Is variable volatile. + * @return true if volatile, false if not + */ + bool isVolatile() const { + return getFlag(fIsVolatile); + } + + /** + * Is variable static. + * @return true if static, false if not + */ + bool isStatic() const { + return getFlag(fIsStatic); + } + + /** + * Is variable extern. + * @return true if extern, false if not + */ + bool isExtern() const { + return getFlag(fIsExtern); + } + + /** + * Is variable const. + * @return true if const, false if not + */ + bool isConst() const { + return getFlag(fIsConst); + } + + /** + * Is variable a throw type. + * @return true if throw type, false if not + */ + bool isThrow() const { + return mAccess == AccessControl::Throw; + } + + /** + * Is variable a user defined (or unknown) type. + * @return true if user defined type, false if not + */ + bool isClass() const { + return getFlag(fIsClass); + } + + /** + * Is variable an array. + * @return true if array, false if not + */ + bool isArray() const { + return getFlag(fIsArray) && !getFlag(fIsPointer); + } + + /** + * Is pointer variable. + * @return true if pointer, false otherwise + */ + bool isPointer() const { + return getFlag(fIsPointer); + } + + /** + * Is variable a pointer to an array + * @return true if pointer to array, false otherwise + */ + bool isPointerToArray() const { + return isPointer() && getFlag(fIsArray); + } + + /** + * Is variable an array of pointers + * @return true if array or pointers, false otherwise + */ + bool isPointerArray() const; + + /** + * Is array or pointer variable. + * @return true if pointer or array, false otherwise + */ + bool isArrayOrPointer() const { + return getFlag(fIsArray) || getFlag(fIsPointer); + } + + /** + * Is reference variable. + * @return true if reference, false otherwise + */ + bool isReference() const { + return getFlag(fIsReference); + } + + /** + * Is reference variable. + * @return true if reference, false otherwise + */ + bool isRValueReference() const { + return getFlag(fIsRValueRef); + } + + /** + * Is variable unsigned. + * @return true only if variable _is_ unsigned. if the sign is unknown, false is returned. + */ + bool isUnsigned() const; + + /** + * Does variable have a default value. + * @return true if has a default falue, false if not + */ + bool hasDefault() const { + return getFlag(fHasDefault); + } + + /** + * Is variable initialized in its declaration + * @return true if variable declaration contains initialization + */ + bool isInit() const { + return getFlag(fIsInit); + } + + /** + * Get Type pointer of known type. + * @return pointer to type if known, NULL if not known + */ + const Type *type() const { + return mType; + } + + /** + * Get Scope pointer of known type. + * @return pointer to type scope if known, NULL if not known + */ + const Scope *typeScope() const { + return mType ? mType->classScope : nullptr; + } + + /** + * Get Scope pointer of enclosing scope. + * @return pointer to enclosing scope + */ + const Scope *scope() const { + return mScope; + } + + /** + * Get array dimensions. + * @return array dimensions vector + */ + const std::vector &dimensions() const { + return mDimensions; + } + + /** + * Get array dimension length. + * @return length of dimension + */ + MathLib::bigint dimension(nonneg int index_) const { + return mDimensions.at(index_).num; + } + + /** + * Get array dimension known. + * @return length of dimension known + */ + bool dimensionKnown(nonneg int index_) const { + return mDimensions.at(index_).known; + } + + void setDimensions(const std::vector &dimensions_) { + mDimensions = dimensions_; + } + + /** + * Checks if the variable is an STL type ('std::') + * E.g.: + * std::string s; + * ... + * sVar->isStlType() == true + * @return true if it is an stl type and its type matches any of the types in 'stlTypes' + */ + bool isStlType() const { + return getFlag(fIsStlType); + } + + /** + * Checks if the variable is an STL type ('std::') + * E.g.: + * std::string s; + * ... + * sVar->isStlType() == true + * @return true if it is an stl type and its type matches any of the types in 'stlTypes' + */ + bool isStlStringType() const { + return getFlag(fIsStlString); + } + + bool isStlStringViewType() const; + + bool isSmartPointer() const { + return getFlag(fIsSmartPointer); + } + + const Type* smartPointerType() const; + const Type* iteratorType() const; + + /** + * Checks if the variable is of any of the STL types passed as arguments ('std::') + * E.g.: + * std::string s; + * ... + * const char *str[] = {"string", "wstring"}; + * sVar->isStlType(str) == true + * @param stlType stl type + * @return true if it is an stl type and its type matches any of the types in 'stlTypes' + */ + bool isStlType(const std::string& stlType) const { + return isStlType() && stlType==mTypeStartToken->strAt(2); + } + + /** + * Checks if the variable is of any of the STL types passed as arguments ('std::') + * E.g.: + * std::string s; + * ... + * const std::set str = make_container< std::set >() << "string" << "wstring"; + * sVar->isStlType(str) == true + * @param stlTypes set of stl types + * @return true if it is an stl type and its type matches any of the types in 'stlTypes' + */ + bool isStlType(const std::set& stlTypes) const { + return isStlType() && stlTypes.find(mTypeStartToken->strAt(2))!=stlTypes.end(); + } + + /** + * Determine whether it's a floating number type + * @return true if the type is known and it's a floating type (float, double and long double) or a pointer/array to it + */ + bool isFloatingType() const { + return getFlag(fIsFloatType); + } + + /** + * Determine whether it's an enumeration type + * @return true if the type is known and it's an enumeration type + */ + bool isEnumType() const { + return type() && type()->isEnumType(); + } + + bool isMaybeUnused() const { + return getFlag(fIsMaybeUnused); + } + + const ValueType *valueType() const { + return mValueType; + } + + void setValueType(const ValueType &valueType); + + AccessControl accessControl() const { + return mAccess; + } + + std::string getTypeName() const; + +private: + // only symbol database can change the type + friend class SymbolDatabase; + + /** + * Set Type pointer to known type. + * @param t type + */ + void type(const Type * t) { + mType = t; + } + + /** @brief variable name token */ + const Token *mNameToken; + + /** @brief variable type start token */ + const Token *mTypeStartToken; + + /** @brief variable type end token */ + const Token *mTypeEndToken; + + /** @brief order declared */ + nonneg int mIndex; + + /** @brief what section is this variable declared in? */ + AccessControl mAccess; // public/protected/private + + /** @brief flags */ + unsigned int mFlags; + + /** @brief pointer to user defined type info (for known types) */ + const Type *mType; + + /** @brief pointer to scope this variable is in */ + const Scope *mScope; + + const ValueType* mValueType{}; + + /** @brief array dimensions */ + std::vector mDimensions; + + /** @brief fill in information, depending on Tokens given at instantiation */ + void evaluate(const Settings* settings); +}; + +class CPPCHECKLIB Function { + // only symbol database can change this + friend class SymbolDatabase; + + /** @brief flags mask used to access specific bit. */ + enum { + fHasBody = (1 << 0), ///< @brief has implementation + fIsInline = (1 << 1), ///< @brief implementation in class definition + fIsConst = (1 << 2), ///< @brief is const + fHasVirtualSpecifier = (1 << 3), ///< @brief does declaration contain 'virtual' specifier + fIsPure = (1 << 4), ///< @brief is pure virtual + fIsStatic = (1 << 5), ///< @brief is static + fIsStaticLocal = (1 << 6), ///< @brief is static local + fIsExtern = (1 << 7), ///< @brief is extern + fIsFriend = (1 << 8), ///< @brief is friend + fIsExplicit = (1 << 9), ///< @brief is explicit + fIsDefault = (1 << 10), ///< @brief is default + fIsDelete = (1 << 11), ///< @brief is delete + fHasOverrideSpecifier = (1 << 12), ///< @brief does declaration contain 'override' specifier? + fHasFinalSpecifier = (1 << 13), ///< @brief does declaration contain 'final' specifier? + fIsNoExcept = (1 << 14), ///< @brief is noexcept + fIsThrow = (1 << 15), ///< @brief is throw + fIsOperator = (1 << 16), ///< @brief is operator + fHasLvalRefQual = (1 << 17), ///< @brief has & lvalue ref-qualifier + fHasRvalRefQual = (1 << 18), ///< @brief has && rvalue ref-qualifier + fIsVariadic = (1 << 19), ///< @brief is variadic + fIsVolatile = (1 << 20), ///< @brief is volatile + fHasTrailingReturnType = (1 << 21), ///< @brief has trailing return type + fIsEscapeFunction = (1 << 22), ///< @brief Function throws or exits + fIsInlineKeyword = (1 << 23), ///< @brief Function has "inline" keyword + fIsConstexpr = (1 << 24), ///< @brief is constexpr + }; + + /** + * Get specified flag state. + * @param flag flag to get state of + * @return true if flag set or false in flag not set + */ + bool getFlag(unsigned int flag) const { + return ((mFlags & flag) != 0); + } + + /** + * Set specified flag state. + * @param flag flag to set state + * @param state new state of flag + */ + void setFlag(unsigned int flag, bool state) { + mFlags = state ? mFlags | flag : mFlags & ~flag; + } + +public: + enum Type { eConstructor, eCopyConstructor, eMoveConstructor, eOperatorEqual, eDestructor, eFunction, eLambda }; + + Function(const Token *tok, const Scope *scope, const Token *tokDef, const Token *tokArgDef); + Function(const Token *tokenDef, const std::string &clangType); + + const std::string &name() const { + return tokenDef->str(); + } + + std::string fullName() const; + + nonneg int argCount() const { + return argumentList.size(); + } + nonneg int minArgCount() const { + return argumentList.size() - initArgCount; + } + const Variable* getArgumentVar(nonneg int num) const; + nonneg int initializedArgCount() const { + return initArgCount; + } + void addArguments(const SymbolDatabase *symbolDatabase, const Scope *scope); + + /** @brief check if this function is virtual in the base classes */ + bool isImplicitlyVirtual(bool defaultVal = false) const; + + std::vector getOverloadedFunctions() const; + + /** @brief get function in base class that is overridden */ + const Function *getOverriddenFunction(bool *foundAllBaseClasses = nullptr) const; + + bool isLambda() const { + return type==eLambda; + } + + bool isConstructor() const { + return type==eConstructor || + type==eCopyConstructor || + type==eMoveConstructor; + } + + bool isDestructor() const { + return type==eDestructor; + } + bool isAttributeConstructor() const { + return tokenDef->isAttributeConstructor(); + } + bool isAttributeDestructor() const { + return tokenDef->isAttributeDestructor(); + } + bool isAttributePure() const { + return tokenDef->isAttributePure(); + } + bool isAttributeConst() const { + return tokenDef->isAttributeConst(); + } + bool isAttributeNoreturn() const { + return tokenDef->isAttributeNoreturn(); + } + bool isAttributeNothrow() const { + return tokenDef->isAttributeNothrow(); + } + bool isAttributeNodiscard() const { + return tokenDef->isAttributeNodiscard(); + } + + bool hasBody() const { + return getFlag(fHasBody); + } + bool isInline() const { + return getFlag(fIsInline); + } + bool isConst() const { + return getFlag(fIsConst); + } + bool hasVirtualSpecifier() const { + return getFlag(fHasVirtualSpecifier); + } + bool isPure() const { + return getFlag(fIsPure); + } + bool isStatic() const { + return getFlag(fIsStatic); + } + bool isStaticLocal() const { + return getFlag(fIsStaticLocal); + } + bool isExtern() const { + return getFlag(fIsExtern); + } + bool isFriend() const { + return getFlag(fIsFriend); + } + bool isExplicit() const { + return getFlag(fIsExplicit); + } + bool isDefault() const { + return getFlag(fIsDefault); + } + bool isDelete() const { + return getFlag(fIsDelete); + } + bool isNoExcept() const { + return getFlag(fIsNoExcept); + } + bool isThrow() const { + return getFlag(fIsThrow); + } + bool hasOverrideSpecifier() const { + return getFlag(fHasOverrideSpecifier); + } + bool hasFinalSpecifier() const { + return getFlag(fHasFinalSpecifier); + } + bool isOperator() const { + return getFlag(fIsOperator); + } + bool hasLvalRefQualifier() const { + return getFlag(fHasLvalRefQual); + } + bool hasRvalRefQualifier() const { + return getFlag(fHasRvalRefQual); + } + bool isVariadic() const { + return getFlag(fIsVariadic); + } + bool isVolatile() const { + return getFlag(fIsVolatile); + } + bool hasTrailingReturnType() const { + return getFlag(fHasTrailingReturnType); + } + void hasBody(bool state) { + setFlag(fHasBody, state); + } + bool isInlineKeyword() const { + return getFlag(fIsInlineKeyword); + } + + bool isEscapeFunction() const { + return getFlag(fIsEscapeFunction); + } + void isEscapeFunction(bool state) { + setFlag(fIsEscapeFunction, state); + } + + bool isConstexpr() const { + return getFlag(fIsConstexpr); + } + void isConstexpr(bool state) { + setFlag(fIsConstexpr, state); + } + bool isSafe(const Settings &settings) const; + + const Token* tokenDef{}; ///< function name token in class definition + const Token* argDef{}; ///< function argument start '(' in class definition + const Token* token{}; ///< function name token in implementation + const Token* arg{}; ///< function argument start '(' + const Token* retDef{}; ///< function return type token + const ::Type* retType{}; ///< function return type + const Scope* functionScope{}; ///< scope of function body + const Scope* nestedIn{}; ///< Scope the function is declared in + std::list argumentList; ///< argument list, must remain list due to clangimport usage! + nonneg int initArgCount{}; ///< number of args with default values + Type type = eFunction; ///< constructor, destructor, ... + const Token* noexceptArg{}; ///< noexcept token + const Token* throwArg{}; ///< throw token + const Token* templateDef{}; ///< points to 'template <' before function + const Token* functionPointerUsage{}; ///< function pointer usage + AccessControl access{}; ///< public/protected/private + + bool argsMatch(const Scope *scope, const Token *first, const Token *second, const std::string &path, nonneg int path_length) const; + + static bool returnsConst(const Function* function, bool unknown = false); + + static bool returnsPointer(const Function* function, bool unknown = false); + static bool returnsReference(const Function* function, bool unknown = false, bool includeRValueRef = false); + static bool returnsStandardType(const Function* function, bool unknown = false); + + static bool returnsVoid(const Function* function, bool unknown = false); + + static std::vector findReturns(const Function* f); + + const Token* returnDefEnd() const { + if (this->hasTrailingReturnType()) + return Token::findmatch(retDef, "{|;"); + return tokenDef; + } + + /** + * @return token to ":" if the function is a constructor + * and it contains member initialization otherwise a nullptr is returned + */ + const Token * constructorMemberInitialization() const; + +private: + /** Recursively determine if this function overrides a virtual function in a base class */ + const Function * getOverriddenFunctionRecursive(const ::Type* baseType, bool *foundAllBaseClasses) const; + + unsigned int mFlags{}; + + void isInline(bool state) { + setFlag(fIsInline, state); + } + void isConst(bool state) { + setFlag(fIsConst, state); + } + void hasVirtualSpecifier(bool state) { + setFlag(fHasVirtualSpecifier, state); + } + void isPure(bool state) { + setFlag(fIsPure, state); + } + void isStatic(bool state) { + setFlag(fIsStatic, state); + } + void isStaticLocal(bool state) { + setFlag(fIsStaticLocal, state); + } + void isExtern(bool state) { + setFlag(fIsExtern, state); + } + void isFriend(bool state) { + setFlag(fIsFriend, state); + } + void isExplicit(bool state) { + setFlag(fIsExplicit, state); + } + void isDefault(bool state) { + setFlag(fIsDefault, state); + } + void isDelete(bool state) { + setFlag(fIsDelete, state); + } + void isNoExcept(bool state) { + setFlag(fIsNoExcept, state); + } + void isThrow(bool state) { + setFlag(fIsThrow, state); + } + void isOperator(bool state) { + setFlag(fIsOperator, state); + } + void hasLvalRefQualifier(bool state) { + setFlag(fHasLvalRefQual, state); + } + void hasRvalRefQualifier(bool state) { + setFlag(fHasRvalRefQual, state); + } + void isVariadic(bool state) { + setFlag(fIsVariadic, state); + } + void isVolatile(bool state) { + setFlag(fIsVolatile, state); + } + void hasTrailingReturnType(bool state) { + setFlag(fHasTrailingReturnType, state); + } + void isInlineKeyword(bool state) { + setFlag(fIsInlineKeyword, state); + } + const Token *setFlags(const Token *tok1, const Scope *scope); +}; + +class CPPCHECKLIB Scope { + // let tests access private function for testing + friend class TestSymbolDatabase; + +public: + struct UsingInfo { + const Token *start; + const Scope *scope; + }; + + enum ScopeType { eGlobal, eClass, eStruct, eUnion, eNamespace, eFunction, eIf, eElse, eFor, eWhile, eDo, eSwitch, eUnconditional, eTry, eCatch, eLambda, eEnum }; + + Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_); + Scope(const SymbolDatabase *check_, const Token *classDef_, const Scope *nestedIn_, ScopeType type_, const Token *start_); + + const SymbolDatabase* check{}; + std::string className; + const Token* classDef{}; ///< class/struct/union/namespace token + const Token* bodyStart{}; ///< '{' token + const Token* bodyEnd{}; ///< '}' token + std::list functionList; + std::multimap functionMap; + std::list varlist; + const Scope* nestedIn{}; + std::vector nestedList; + nonneg int numConstructors{}; + nonneg int numCopyOrMoveConstructors{}; + std::vector usingList; + ScopeType type{}; + Type* definedType{}; + std::map definedTypesMap; + std::vector bodyStartList; + + // function specific fields + const Scope* functionOf{}; ///< scope this function belongs to + Function* function{}; ///< function info for this function + + // enum specific fields + const Token* enumType{}; + bool enumClass{}; + + std::vector enumeratorList; + + void setBodyStartEnd(const Token *start) { + bodyStart = start; + bodyEnd = start ? start->link() : nullptr; + if (start) + bodyStartList.push_back(start); + } + + bool isAnonymous() const { + // TODO: Check if class/struct is anonymous + return className.size() > 9 && startsWith(className,"Anonymous") && std::isdigit(className[9]); + } + + const Enumerator * findEnumerator(const std::string & name) const { + auto it = std::find_if(enumeratorList.cbegin(), enumeratorList.cend(), [&](const Enumerator& i) { + return i.name->str() == name; + }); + return it == enumeratorList.end() ? nullptr : &*it; + } + + bool isNestedIn(const Scope * outer) const { + if (!outer) + return false; + if (outer == this) + return true; + const Scope * parent = nestedIn; + while (outer != parent && parent) + parent = parent->nestedIn; + return parent && parent == outer; + } + + static Function* nestedInFunction(const Scope* scope) { + while (scope) { + if (scope->type == Scope::eFunction) + break; + scope = scope->nestedIn; + } + if (!scope) + return nullptr; + return scope->function; + } + + bool isClassOrStruct() const { + return (type == eClass || type == eStruct); + } + + bool isClassOrStructOrUnion() const { + return (type == eClass || type == eStruct || type == eUnion); + } + + bool isExecutable() const { + return type != eClass && type != eStruct && type != eUnion && type != eGlobal && type != eNamespace && type != eEnum; + } + + bool isLoopScope() const { + return type == Scope::ScopeType::eFor || type == Scope::ScopeType::eWhile || type == Scope::ScopeType::eDo; + } + + bool isLocal() const { + return (type == eIf || type == eElse || + type == eFor || type == eWhile || type == eDo || + type == eSwitch || type == eUnconditional || + type == eTry || type == eCatch); + } + + // Is there lambda/inline function(s) in this scope? + bool hasInlineOrLambdaFunction() const; + + /** + * @brief find a function + * @param tok token of function call + * @param requireConst if const refers to a const variable only const methods should be matched + * @return pointer to function if found or NULL if not found + */ + const Function *findFunction(const Token *tok, bool requireConst=false) const; + + const Scope *findRecordInNestedList(const std::string & name, bool isC = false) const; + Scope *findRecordInNestedList(const std::string & name, bool isC = false); + + const Type* findType(const std::string& name) const; + Type* findType(const std::string& name); + + /** + * @brief find if name is in nested list + * @param name name of nested scope + */ + Scope *findInNestedListRecursive(const std::string & name); + + void addVariable(const Token *token_, const Token *start_, + const Token *end_, AccessControl access_, const Type *type_, + const Scope *scope_, const Settings* settings); + + /** @brief initialize varlist */ + void getVariableList(const Settings& settings); + + const Function *getDestructor() const; + + void addFunction(Function func) { + functionList.push_back(std::move(func)); + + const Function * back = &functionList.back(); + + functionMap.insert(make_pair(back->tokenDef->str(), back)); + } + + AccessControl defaultAccess() const; + + /** + * @brief check if statement is variable declaration and add it if it is + * @param tok pointer to start of statement + * @param varaccess access control of statement + * @param settings Settings + * @return pointer to last token + */ + const Token *checkVariable(const Token *tok, AccessControl varaccess, const Settings& settings); + + /** + * @brief get variable from name + * @param varname name of variable + * @return pointer to variable + */ + const Variable *getVariable(const std::string &varname) const; + + const Token * addEnum(const Token * tok); + + const Scope *findRecordInBase(const std::string &name) const; + + std::vector findAssociatedScopes() 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* const tok, const Token*& vartok, const Token*& typetok) const; + + void findFunctionInBase(const std::string & name, nonneg int args, std::vector & matches) const; + + /** @brief initialize varlist */ + void getVariableList(const Settings& settings, const Token *start, const Token *end); +}; + +enum class Reference { + None, + LValue, + RValue +}; + +/** Value type */ +class CPPCHECKLIB ValueType { +public: + enum Sign { UNKNOWN_SIGN, SIGNED, UNSIGNED } sign = UNKNOWN_SIGN; + enum Type { + UNKNOWN_TYPE, + POD, + NONSTD, + RECORD, + SMART_POINTER, + CONTAINER, + ITERATOR, + VOID, + BOOL, + CHAR, + SHORT, + WCHAR_T, + INT, + LONG, + LONGLONG, + UNKNOWN_INT, + FLOAT, + DOUBLE, + LONGDOUBLE + } type = UNKNOWN_TYPE; + nonneg int bits{}; ///< bitfield bitcount + nonneg int pointer{}; ///< 0=>not pointer, 1=>*, 2=>**, 3=>***, etc + nonneg int constness{}; ///< bit 0=data, bit 1=*, bit 2=** + nonneg int volatileness{}; ///< bit 0=data, bit 1=*, bit 2=** + Reference reference = Reference::None; ///< Is the outermost indirection of this type a reference or rvalue + ///< reference or not? pointer=2, Reference=LValue would be a T**& + const Scope* typeScope{}; ///< if the type definition is seen this point out the type scope + const ::Type* smartPointerType{}; ///< Smart pointer type + const Token* smartPointerTypeToken{}; ///< Smart pointer type token + const Library::SmartPointer* smartPointer{}; ///< Smart pointer + const Library::Container* container{}; ///< If the type is a container defined in a cfg file, this is the used + ///< container + const Token* containerTypeToken{}; ///< The container type token. the template argument token that defines the + ///< container element type. + std::string originalTypeName; ///< original type name as written in the source code. eg. this might be "uint8_t" + ///< when type is CHAR. + ErrorPath debugPath; ///< debug path to the type + + ValueType() = default; + ValueType(Sign s, Type t, nonneg int p) + : sign(s), + type(t), + pointer(p) + {} + ValueType(Sign s, Type t, nonneg int p, nonneg int c) + : sign(s), + type(t), + pointer(p), + constness(c) + {} + ValueType(Sign s, Type t, nonneg int p, nonneg int c, nonneg int v) + : sign(s), + type(t), + pointer(p), + constness(c), + volatileness(v) + {} + ValueType(Sign s, Type t, nonneg int p, nonneg int c, std::string otn) + : sign(s), + type(t), + pointer(p), + constness(c), + originalTypeName(std::move(otn)) + {} + + static ValueType parseDecl(const Token *type, const Settings &settings); + + static Type typeFromString(const std::string &typestr, bool longType); + + enum class MatchResult { UNKNOWN, SAME, FALLBACK1, FALLBACK2, NOMATCH }; + static MatchResult matchParameter(const ValueType *call, const ValueType *func); + static MatchResult matchParameter(const ValueType *call, const Variable *callVar, const Variable *funcVar); + + bool isPrimitive() const { + return (type >= ValueType::Type::BOOL); + } + + bool isIntegral() const { + return (type >= ValueType::Type::BOOL && type <= ValueType::Type::UNKNOWN_INT); + } + + bool isFloat() const { + return (type >= ValueType::Type::FLOAT && type <= ValueType::Type::LONGDOUBLE); + } + + bool fromLibraryType(const std::string &typestr, const Settings &settings); + + bool isEnum() const { + return typeScope && typeScope->type == Scope::eEnum; + } + + bool isConst(nonneg int indirect = 0) const; + + bool isVolatile(nonneg int indirect = 0) const; + + MathLib::bigint typeSize(const Platform &platform, bool p=false) const; + + /// Check if type is the same ignoring const and references + bool isTypeEqual(const ValueType* that) const; + + std::string str() const; + std::string dump() const; + + void setDebugPath(const Token* tok, SourceLocation ctx, const SourceLocation &local = SourceLocation::current()); +}; + + +class CPPCHECKLIB SymbolDatabase { + friend class TestSymbolDatabase; +public: + SymbolDatabase(Tokenizer& tokenizer, const Settings& settings, ErrorLogger& errorLogger); + ~SymbolDatabase(); + + /** @brief Information about all namespaces/classes/structures */ + std::list scopeList; + + /** @brief Fast access to function scopes */ + std::vector functionScopes; + + /** @brief Fast access to class and struct scopes */ + std::vector classAndStructScopes; + + /** @brief Fast access to types */ + std::list typeList; + + /** + * @brief find a variable type if it's a user defined type + * @param start scope to start looking in + * @param typeTok token containing variable type + * @return pointer to type if found or NULL if not found + */ + const Type* findVariableType(const Scope* start, const Token* typeTok) const; + + /** + * @brief find a function + * @param tok token of function call + * @return pointer to function if found or NULL if not found + */ + const Function* findFunction(const Token* tok) const; + + /** For unit testing only */ + const Scope* findScopeByName(const std::string& name) const; + + const Type* findType(const Token* startTok, const Scope* startScope, bool lookOutside = false) const; + Type* findType(const Token* startTok, Scope* startScope, bool lookOutside = false) + { + return const_cast(this->findType(startTok, const_cast(startScope), lookOutside)); + } + + const Scope *findScope(const Token *tok, const Scope *startScope) const; + Scope *findScope(const Token *tok, Scope *startScope) { + return const_cast(this->findScope(tok, const_cast(startScope))); + } + + // cppcheck-suppress unusedFunction + bool isVarId(nonneg int varid) const { + return varid < mVariableList.size(); + } + + const Variable *getVariableFromVarId(nonneg int varId) const { + return mVariableList.at(varId); + } + + const std::vector & variableList() const { + return mVariableList; + } + + /** + * @brief output a debug message + */ + void debugMessage(const Token *tok, const std::string &type, const std::string &msg) const; + + void returnImplicitIntError(const Token *tok) const; + + void printOut(const char * title = nullptr) const; + void printVariable(const Variable *var, const char *indent) const; + void printXml(std::ostream &out) const; + + /* + * @brief Do a sanity check + */ + void validate() const; + + /** Set valuetype in provided tokenlist */ + void setValueTypeInTokenList(bool reportDebugWarnings, Token *tokens=nullptr); + + /** + * Calculates sizeof value for given type. + * @param type Token which will contain e.g. "int", "*", or string. + * @return sizeof for given type, or 0 if it can't be calculated. + */ + nonneg int sizeOfType(const Token *type) const; + + /** Set array dimensions when valueflow analysis is completed */ + void setArrayDimensionsUsingValueFlow(); // cppcheck-suppress functionConst // has side effects + + void clangSetVariables(const std::vector &variableList); + void createSymbolDatabaseExprIds(); + +private: + friend class Scope; + friend class Function; + + // Create symboldatabase... + void createSymbolDatabaseFindAllScopes(); + void createSymbolDatabaseClassInfo(); + void createSymbolDatabaseVariableInfo(); + void createSymbolDatabaseCopyAndMoveConstructors(); + void createSymbolDatabaseFunctionScopes(); + void createSymbolDatabaseClassAndStructScopes(); + void createSymbolDatabaseFunctionReturnTypes(); + void createSymbolDatabaseNeedInitialization(); + void createSymbolDatabaseVariableSymbolTable(); + void createSymbolDatabaseSetScopePointers(); + void createSymbolDatabaseSetFunctionPointers(bool firstPass); // cppcheck-suppress functionConst // has side effects + void createSymbolDatabaseSetVariablePointers(); + // cppcheck-suppress functionConst + void createSymbolDatabaseSetTypePointers(); + void createSymbolDatabaseSetSmartPointerType(); + void createSymbolDatabaseEnums(); // cppcheck-suppress functionConst // has side effects + void createSymbolDatabaseEscapeFunctions(); // cppcheck-suppress functionConst // has side effects + // cppcheck-suppress functionConst + void createSymbolDatabaseIncompleteVars(); + + void debugSymbolDatabase() const; + + void addClassFunction(Scope *&scope, const Token *&tok, const Token *argStart); + static Function *addGlobalFunctionDecl(Scope*& scope, const Token* tok, const Token *argStart, const Token* funcStart); + Function *addGlobalFunction(Scope*& scope, const Token*& tok, const Token *argStart, const Token* funcStart); + void addNewFunction(Scope *&scope, const Token *&tok); + bool isFunction(const Token *tok, const Scope* outerScope, const Token *&funcStart, const Token *&argStart, const Token*& declEnd) const; + const Type *findTypeInNested(const Token *startTok, const Scope *startScope) const; + const Scope *findNamespace(const Token * tok, const Scope * scope) const; + static Function *findFunctionInScope(const Token *func, const Scope *ns, const std::string & path, nonneg int path_length); + static const Type *findVariableTypeInBase(const Scope *scope, const Token *typeTok); + + using MemberIdMap = std::map; + using VarIdMap = std::map; + + void fixVarId(VarIdMap & varIds, const Token * vartok, Token * membertok, const Variable * membervar); + + /** Whether the token is a keyword as defined in http://en.cppreference.com/w/c/keyword and http://en.cppreference.com/w/cpp/keyword*/ + static bool isReservedName(const Token* tok); + + const Enumerator * findEnumerator(const Token * tok, std::set& tokensThatAreNotEnumeratorValues) const; + + void setValueType(Token* tok, const ValueType& valuetype, const SourceLocation &loc = SourceLocation::current()); + void setValueType(Token* tok, const Variable& var, const SourceLocation &loc = SourceLocation::current()); + void setValueType(Token* tok, const Enumerator& enumerator, const SourceLocation &loc = SourceLocation::current()); + + void validateExecutableScopes() const; + /** + * @brief Check variable list, e.g. variables w/o scope + */ + void validateVariables() const; + + Tokenizer& mTokenizer; + const Settings &mSettings; + ErrorLogger &mErrorLogger; + + /** variable symbol table */ + std::vector mVariableList; + + /** list for missing types */ + std::list mBlankTypes; + + ValueType::Sign mDefaultSignedness; +}; + + +//--------------------------------------------------------------------------- +#endif // symboldatabaseH diff --git a/cppcheck-2.14.0/lib/templatesimplifier.cpp b/cppcheck-2.14.0/lib/templatesimplifier.cpp new file mode 100644 index 00000000..c2785187 --- /dev/null +++ b/cppcheck-2.14.0/lib/templatesimplifier.cpp @@ -0,0 +1,4033 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "templatesimplifier.h" + +#include "errorlogger.h" +#include "errortypes.h" +#include "mathlib.h" +#include "settings.h" +#include "standards.h" +#include "token.h" +#include "tokenize.h" +#include "tokenlist.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static Token *skipRequires(Token *tok) +{ + if (!Token::simpleMatch(tok, "requires")) + return tok; + + while (Token::Match(tok, "%oror%|&&|requires %name%|(")) { + Token *after = tok->next(); + if (after->str() == "(") { + tok = after->link()->next(); + continue; + } + if (Token::simpleMatch(after, "requires (") && Token::simpleMatch(after->linkAt(1), ") {")) { + tok = after->linkAt(1)->linkAt(1)->next(); + continue; + } + while (Token::Match(after, "%name% :: %name%")) + after = after->tokAt(2); + if (Token::Match(after, "%name% <")) { + after = after->next()->findClosingBracket(); + tok = after ? after->next() : nullptr; + } else + break; + } + return tok; +} + +namespace { + class FindToken { + public: + explicit FindToken(const Token *token) : mToken(token) {} + bool operator()(const TemplateSimplifier::TokenAndName &tokenAndName) const { + return tokenAndName.token() == mToken; + } + private: + const Token * const mToken; + }; + + class FindName { + public: + explicit FindName(std::string name) : mName(std::move(name)) {} + bool operator()(const TemplateSimplifier::TokenAndName &tokenAndName) const { + return tokenAndName.name() == mName; + } + private: + const std::string mName; + }; + + class FindFullName { + public: + explicit FindFullName(std::string fullName) : mFullName(std::move(fullName)) {} + bool operator()(const TemplateSimplifier::TokenAndName &tokenAndName) const { + return tokenAndName.fullName() == mFullName; + } + private: + const std::string mFullName; + }; +} + +TemplateSimplifier::TokenAndName::TokenAndName(Token *token, std::string scope) : + mToken(token), mScope(std::move(scope)), mName(mToken ? mToken->str() : ""), + mFullName(mScope.empty() ? mName : (mScope + " :: " + mName)), + mNameToken(nullptr), mParamEnd(nullptr), mFlags(0) +{ + if (mToken) { + if (mToken->strAt(1) == "<") { + const Token *end = mToken->next()->findClosingBracket(); + if (end && end->strAt(1) == "(") { + isFunction(true); + } + } + mToken->templateSimplifierPointer(this); + } +} + +TemplateSimplifier::TokenAndName::TokenAndName(Token *token, std::string scope, const Token *nameToken, const Token *paramEnd) : + mToken(token), mScope(std::move(scope)), mName(nameToken->str()), + mFullName(mScope.empty() ? mName : (mScope + " :: " + mName)), + mNameToken(nameToken), mParamEnd(paramEnd), mFlags(0) +{ + // only set flags for declaration + if (mToken && mNameToken && mParamEnd) { + isSpecialization(Token::simpleMatch(mToken, "template < >")); + + if (!isSpecialization()) { + if (Token::simpleMatch(mToken->next()->findClosingBracket(), "> template <")) { + const Token * temp = mNameToken->tokAt(-2); + while (Token::Match(temp, ">|%name% ::")) { + if (temp->str() == ">") + temp = temp->findOpeningBracket()->previous(); + else + temp = temp->tokAt(-2); + } + isPartialSpecialization(temp->strAt(1) == "<"); + } else + isPartialSpecialization(mNameToken->strAt(1) == "<"); + } + + isAlias(mParamEnd->strAt(1) == "using"); + + if (isAlias() && isPartialSpecialization()) { + throw InternalError(mToken, "partial specialization of alias templates is not permitted", InternalError::SYNTAX); + } + if (isAlias() && isSpecialization()) { + throw InternalError(mToken, "explicit specialization of alias templates is not permitted", InternalError::SYNTAX); + } + + isFriend(mParamEnd->strAt(1) == "friend"); + const Token *next = mParamEnd->next(); + if (isFriend()) + next = next->next(); + + isClass(Token::Match(next, "class|struct|union %name% <|{|:|;|::")); + if (mToken->strAt(1) == "<" && !isSpecialization()) { + const Token *end = mToken->next()->findClosingBracket(); + isVariadic(end && Token::findmatch(mToken->tokAt(2), "%name% ...", end)); + } + const Token *tok1 = mNameToken->next(); + if (tok1->str() == "<") { + const Token *closing = tok1->findClosingBracket(); + if (closing) + tok1 = closing->next(); + else + throw InternalError(mToken, "unsupported syntax", InternalError::SYNTAX); + } + isFunction(tok1->str() == "("); + isVariable(!isClass() && !isAlias() && !isFriend() && Token::Match(tok1, "=|;")); + if (!isFriend()) { + if (isVariable()) + isForwardDeclaration(tok1->str() == ";"); + else if (!isAlias()) { + if (isFunction()) + tok1 = tok1->link()->next(); + while (tok1 && !Token::Match(tok1, ";|{")) { + if (tok1->str() == "<") + tok1 = tok1->findClosingBracket(); + else if (Token::Match(tok1, "(|[") && tok1->link()) + tok1 = tok1->link(); + if (tok1) + tok1 = tok1->next(); + } + if (tok1) + isForwardDeclaration(tok1->str() == ";"); + } + } + // check for member class or function and adjust scope + if ((isFunction() || isClass()) && + (mNameToken->strAt(-1) == "::" || Token::simpleMatch(mNameToken->tokAt(-2), ":: ~"))) { + const Token * start = mNameToken; + if (start->strAt(-1) == "~") + start = start->previous(); + const Token *end = start; + + while (start && (Token::Match(start->tokAt(-2), "%name% ::") || + (Token::simpleMatch(start->tokAt(-2), "> ::") && + start->tokAt(-2)->findOpeningBracket() && + Token::Match(start->tokAt(-2)->findOpeningBracket()->previous(), "%name% <")))) { + if (start->strAt(-2) == ">") + start = start->tokAt(-2)->findOpeningBracket()->previous(); + else + start = start->tokAt(-2); + } + + if (start && start != end) { + if (!mScope.empty()) + mScope += " ::"; + while (start && start->next() != end) { + if (start->str() == "<") + start = start->findClosingBracket(); + else { + if (!mScope.empty()) + mScope += " "; + mScope += start->str(); + } + start = start->next(); + } + if (start) + mFullName = mScope.empty() ? mName : (mScope + " :: " + mName); + } + } + } + + // make sure at most only one family flag is set + assert(isClass() ? !(isFunction() || isVariable()) : true); + assert(isFunction() ? !(isClass() || isVariable()) : true); + assert(isVariable() ? !(isClass() || isFunction()) : true); + + if (mToken) + mToken->templateSimplifierPointer(this); +} + +TemplateSimplifier::TokenAndName::TokenAndName(const TokenAndName& other) : + mToken(other.mToken), mScope(other.mScope), mName(other.mName), mFullName(other.mFullName), + mNameToken(other.mNameToken), mParamEnd(other.mParamEnd), mFlags(other.mFlags) +{ + if (mToken) + mToken->templateSimplifierPointer(this); +} + +TemplateSimplifier::TokenAndName::~TokenAndName() +{ + if (mToken && mToken->templateSimplifierPointers()) + mToken->templateSimplifierPointers()->erase(this); +} + +std::string TemplateSimplifier::TokenAndName::dump(const std::vector& fileNames) const { + std::string ret = " fileIndex())) + "\" line=\"" + std::to_string(mToken->linenr()) + "\">\n"; + for (const Token* tok = mToken; tok && !Token::Match(tok, "[;{}]"); tok = tok->next()) + ret += " str()) + "\"/>\n"; + return ret + " \n"; +} + +const Token * TemplateSimplifier::TokenAndName::aliasStartToken() const +{ + if (mParamEnd) + return mParamEnd->tokAt(4); + return nullptr; +} + +const Token * TemplateSimplifier::TokenAndName::aliasEndToken() const +{ + if (aliasStartToken()) + return Token::findsimplematch(aliasStartToken(), ";"); + return nullptr; +} + +bool TemplateSimplifier::TokenAndName::isAliasToken(const Token *tok) const +{ + const Token *end = aliasEndToken(); + + for (const Token *tok1 = aliasStartToken(); tok1 != end; tok1 = tok1->next()) { + if (tok1 == tok) + return true; + } + return false; +} + +TemplateSimplifier::TemplateSimplifier(Tokenizer &tokenizer) + : mTokenizer(tokenizer), mTokenList(mTokenizer.list), mSettings(mTokenizer.getSettings()), + mErrorLogger(mTokenizer.mErrorLogger) +{} + +void TemplateSimplifier::checkComplicatedSyntaxErrorsInTemplates() +{ + // check for more complicated syntax errors when using templates.. + for (const Token *tok = mTokenList.front(); tok; tok = tok->next()) { + // skip executing scopes (ticket #3183).. + if (Token::simpleMatch(tok, "( {")) { + tok = tok->link(); + if (!tok) + syntaxError(nullptr); + } + // skip executing scopes.. + const Token *start = Tokenizer::startOfExecutableScope(tok); + if (start) { + tok = start->link(); + } + + // skip executing scopes (ticket #1985).. + else if (Token::simpleMatch(tok, "try {")) { + tok = tok->next()->link(); + while (Token::simpleMatch(tok, "} catch (")) { + tok = tok->linkAt(2); + if (Token::simpleMatch(tok, ") {")) + tok = tok->next()->link(); + } + } + + if (!tok) + syntaxError(nullptr); + // not start of statement? + if (tok->previous() && !Token::Match(tok, "[;{}]")) + continue; + + // skip starting tokens.. ;;; typedef typename foo::bar::.. + while (Token::Match(tok, ";|{")) + tok = tok->next(); + while (Token::Match(tok, "typedef|typename")) + tok = tok->next(); + while (Token::Match(tok, "%type% ::")) + tok = tok->tokAt(2); + if (!tok) + break; + + // template variable or type.. + if (Token::Match(tok, "%type% <") && !Token::simpleMatch(tok, "template")) { + // these are used types.. + std::set usedtypes; + + // parse this statement and see if the '<' and '>' are matching + unsigned int level = 0; + for (const Token *tok2 = tok; tok2 && !Token::simpleMatch(tok2, ";"); tok2 = tok2->next()) { + if (Token::simpleMatch(tok2, "{") && + (!Token::Match(tok2->previous(), ">|%type%") || Token::simpleMatch(tok2->link(), "} ;"))) + break; + if (tok2->str() == "(") + tok2 = tok2->link(); + else if (tok2->str() == "<") { + bool inclevel = false; + if (Token::simpleMatch(tok2->previous(), "operator <")) + ; + else if (level == 0 && Token::Match(tok2->previous(), "%type%")) { + // @todo add better expression detection + if (!(Token::Match(tok2->next(), "*| %type%|%num% ;") || + Token::Match(tok2->next(), "*| %type% . %type% ;"))) { + inclevel = true; + } + } else if (tok2->next() && tok2->next()->isStandardType() && !Token::Match(tok2->tokAt(2), "(|{")) + inclevel = true; + else if (Token::simpleMatch(tok2, "< typename")) + inclevel = true; + else if (Token::Match(tok2->tokAt(-2), "<|, %type% <") && usedtypes.find(tok2->previous()->str()) != usedtypes.end()) + inclevel = true; + else if (Token::Match(tok2, "< %type%") && usedtypes.find(tok2->next()->str()) != usedtypes.end()) + inclevel = true; + else if (Token::Match(tok2, "< %type%")) { + // is the next token a type and not a variable/constant? + // assume it's a type if there comes another "<" + const Token *tok3 = tok2->next(); + while (Token::Match(tok3, "%type% ::")) + tok3 = tok3->tokAt(2); + if (Token::Match(tok3, "%type% <")) + inclevel = true; + } else if (tok2->strAt(-1) == ">") + syntaxError(tok); + + if (inclevel) { + ++level; + if (Token::Match(tok2->tokAt(-2), "<|, %type% <")) + usedtypes.insert(tok2->previous()->str()); + } + } else if (tok2->str() == ">") { + if (level > 0) + --level; + } else if (tok2->str() == ">>") { + if (level > 0) + --level; + if (level > 0) + --level; + } + } + if (level > 0) + syntaxError(tok); + } + } +} + +unsigned int TemplateSimplifier::templateParameters(const Token *tok) +{ + unsigned int numberOfParameters = 1; + + if (!tok) + return 0; + if (tok->str() != "<") + return 0; + if (Token::Match(tok->previous(), "%var% <")) + return 0; + tok = tok->next(); + if (!tok || tok->str() == ">") + return 0; + + unsigned int level = 0; + + while (tok) { + // skip template template + if (level == 0 && Token::simpleMatch(tok, "template <")) { + const Token *closing = tok->next()->findClosingBracket(); + if (closing) { + if (closing->str() == ">>") + return numberOfParameters; + tok = closing->next(); + if (!tok) + syntaxError(tok); + if (Token::Match(tok, ">|>>|>>=")) + return numberOfParameters; + if (tok->str() == ",") { + ++numberOfParameters; + tok = tok->next(); + continue; + } + } else + return 0; + } + + // skip const/volatile + if (Token::Match(tok, "const|volatile")) + tok = tok->next(); + + // skip struct/union + if (Token::Match(tok, "struct|union")) + tok = tok->next(); + + // Skip '&' + if (Token::Match(tok, "& ::| %name%")) + tok = tok->next(); + + // Skip variadic types (Ticket #5774, #6059, #6172) + if (Token::simpleMatch(tok, "...")) { + if ((tok->previous()->isName() && !Token::Match(tok->tokAt(-2), "<|,|::")) || + (!tok->previous()->isName() && !Token::Match(tok->previous(), ">|&|&&|*"))) + return 0; // syntax error + tok = tok->next(); + if (!tok) + return 0; + if (tok->str() == ">") { + if (level == 0) + return numberOfParameters; + --level; + } else if (tok->str() == ">>" || tok->str() == ">>=") { + if (level == 1) + return numberOfParameters; + level -= 2; + } else if (tok->str() == ",") { + if (level == 0) + ++numberOfParameters; + tok = tok->next(); + continue; + } + } + + // Skip '=', '?', ':' + if (Token::Match(tok, "=|?|:")) + tok = tok->next(); + if (!tok) + return 0; + + // Skip links + if (Token::Match(tok, "(|{")) { + tok = tok->link(); + if (tok) + tok = tok->next(); + if (!tok) + return 0; + if (tok->str() == ">" && level == 0) + return numberOfParameters; + if ((tok->str() == ">>" || tok->str() == ">>=") && level == 1) + return numberOfParameters; + if (tok->str() == ",") { + if (level == 0) + ++numberOfParameters; + tok = tok->next(); + } + continue; + } + + // skip std:: + if (tok->str() == "::") + tok = tok->next(); + while (Token::Match(tok, "%name% ::")) { + tok = tok->tokAt(2); + if (tok && tok->str() == "*") // Ticket #5759: Class member pointer as a template argument; skip '*' + tok = tok->next(); + } + if (!tok) + return 0; + + // num/type .. + if (!tok->isNumber() && tok->tokType() != Token::eChar && tok->tokType() != Token::eString && !tok->isName() && !tok->isOp()) + return 0; + tok = tok->next(); + if (!tok) + return 0; + + // * / const + while (Token::Match(tok, "*|&|&&|const")) + tok = tok->next(); + + if (!tok) + return 0; + + // Function pointer or prototype.. + while (Token::Match(tok, "(|[")) { + if (!tok->link()) + syntaxError(tok); + + tok = tok->link()->next(); + while (Token::Match(tok, "const|volatile")) // Ticket #5786: Skip function cv-qualifiers + tok = tok->next(); + } + if (!tok) + return 0; + + // inner template + if (tok->str() == "<" && tok->previous()->isName()) { + ++level; + tok = tok->next(); + } + + if (!tok) + return 0; + + // ,/> + while (Token::Match(tok, ">|>>|>>=")) { + if (level == 0) + return tok->str() == ">" && !Token::Match(tok->next(), "%num%") ? numberOfParameters : 0; + --level; + if (tok->str() == ">>" || tok->str() == ">>=") { + if (level == 0) + return !Token::Match(tok->next(), "%num%") ? numberOfParameters : 0; + --level; + } + tok = tok->next(); + + if (Token::Match(tok, "(|[")) + tok = tok->link()->next(); + + if (!tok) + return 0; + } + + if (tok->str() != ",") + continue; + if (level == 0) + ++numberOfParameters; + tok = tok->next(); + } + return 0; +} + +template )> +static T *findTemplateDeclarationEndImpl(T *tok) +{ + if (Token::simpleMatch(tok, "template <")) { + tok = tok->next()->findClosingBracket(); + if (tok) + tok = tok->next(); + } + + if (!tok) + return nullptr; + + T * tok2 = tok; + bool in_init = false; + while (tok2 && !Token::Match(tok2, ";|{")) { + if (tok2->str() == "<") + tok2 = tok2->findClosingBracket(); + else if (Token::Match(tok2, "(|[") && tok2->link()) + tok2 = tok2->link(); + else if (tok2->str() == ":") + in_init = true; + else if (in_init && Token::Match(tok2, "%name% (|{")) { + tok2 = tok2->linkAt(1); + if (tok2->strAt(1) == "{") + in_init = false; + } + if (tok2) + tok2 = tok2->next(); + } + if (tok2 && tok2->str() == "{") { + tok = tok2->link(); + if (tok && tok->strAt(1) == ";") + tok = tok->next(); + } else if (tok2 && tok2->str() == ";") + tok = tok2; + else + tok = nullptr; + + return tok; +} + +Token *TemplateSimplifier::findTemplateDeclarationEnd(Token *tok) +{ + return findTemplateDeclarationEndImpl(tok); +} + +const Token *TemplateSimplifier::findTemplateDeclarationEnd(const Token *tok) +{ + return findTemplateDeclarationEndImpl(tok); +} + +void TemplateSimplifier::eraseTokens(Token *begin, const Token *end) +{ + if (!begin || begin == end) + return; + + while (begin->next() && begin->next() != end) { + begin->deleteNext(); + } +} + +void TemplateSimplifier::deleteToken(Token *tok) +{ + if (tok->next()) + tok->next()->deletePrevious(); + else + tok->deleteThis(); +} + +static void invalidateForwardDecls(const Token* beg, const Token* end, std::map* forwardDecls) { + if (!forwardDecls) + return; + for (auto& fwd : *forwardDecls) { + for (const Token* tok = beg; tok != end; tok = tok->next()) + if (fwd.second == tok) { + fwd.second = nullptr; + break; + } + } +} + +bool TemplateSimplifier::removeTemplate(Token *tok, std::map* forwardDecls) +{ + if (!Token::simpleMatch(tok, "template <")) + return false; + + Token *end = findTemplateDeclarationEnd(tok); + if (end && end->next()) { + invalidateForwardDecls(tok, end->next(), forwardDecls); + eraseTokens(tok, end->next()); + deleteToken(tok); + return true; + } + + return false; +} + +bool TemplateSimplifier::getTemplateDeclarations() +{ + bool codeWithTemplates = false; + for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { + if (!Token::simpleMatch(tok, "template <")) + continue; + // ignore template template parameter + if (tok->strAt(-1) == "<" || tok->strAt(-1) == ",") + continue; + // ignore nested template + if (tok->strAt(-1) == ">") + continue; + // skip to last nested template parameter + const Token *tok1 = tok; + while (tok1 && tok1->next()) { + const Token *closing = tok1->next()->findClosingBracket(); + if (!Token::simpleMatch(closing, "> template <")) + break; + tok1 = closing->next(); + } + if (!Token::Match(tok, "%any% %any%")) + syntaxError(tok); + if (tok->strAt(2)=="typename" && + !Token::Match(tok->tokAt(3), "%name%|...|,|=|>")) + syntaxError(tok->next()); + codeWithTemplates = true; + const Token * const parmEnd = tok1->next()->findClosingBracket(); + for (const Token *tok2 = parmEnd; tok2; tok2 = tok2->next()) { + if (tok2->str() == "(" && tok2->link()) + tok2 = tok2->link(); + else if (tok2->str() == ")") + break; + // skip decltype(...) + else if (Token::simpleMatch(tok2, "decltype (")) + tok2 = tok2->linkAt(1); + else if (Token::Match(tok2, "{|=|;")) { + const int namepos = getTemplateNamePosition(parmEnd); + if (namepos > 0) { + TokenAndName decl(tok, tok->scopeInfo()->name, parmEnd->tokAt(namepos), parmEnd); + if (decl.isForwardDeclaration()) { + // Declaration => add to mTemplateForwardDeclarations + mTemplateForwardDeclarations.emplace_back(std::move(decl)); + } else { + // Implementation => add to mTemplateDeclarations + mTemplateDeclarations.emplace_back(std::move(decl)); + } + Token *end = findTemplateDeclarationEnd(tok); + if (end) + tok = end; + break; + } + } + } + } + return codeWithTemplates; +} + +void TemplateSimplifier::addInstantiation(Token *token, const std::string &scope) +{ + simplifyTemplateArgs(token->tokAt(2), token->next()->findClosingBracket()); + + TokenAndName instantiation(token, scope); + + // check if instantiation already exists before adding it + const std::list::const_iterator it = std::find(mTemplateInstantiations.cbegin(), + mTemplateInstantiations.cend(), + instantiation); + + if (it == mTemplateInstantiations.cend()) + mTemplateInstantiations.emplace_back(std::move(instantiation)); +} + +static const Token* getFunctionToken(const Token* nameToken) +{ + if (Token::Match(nameToken, "%name% (")) + return nameToken->next(); + + if (Token::Match(nameToken, "%name% <")) { + const Token* end = nameToken->next()->findClosingBracket(); + if (Token::simpleMatch(end, "> (")) + return end->next(); + } + + return nullptr; +} + +static void getFunctionArguments(const Token* nameToken, std::vector& args) +{ + const Token* functionToken = getFunctionToken(nameToken); + if (!functionToken) + return; + + const Token* argToken = functionToken->next(); + + if (argToken->str() == ")") + return; + + args.push_back(argToken); + + while ((argToken = argToken->nextArgumentBeforeCreateLinks2())) + args.push_back(argToken); +} + +static bool isConstMethod(const Token* nameToken) +{ + const Token* functionToken = getFunctionToken(nameToken); + if (!functionToken) + return false; + const Token* endToken = functionToken->link(); + return Token::simpleMatch(endToken, ") const"); +} + +static bool areAllParamsTypes(const std::vector ¶ms) +{ + if (params.empty()) + return false; + + return std::all_of(params.cbegin(), params.cend(), [](const Token* param) { + return Token::Match(param->previous(), "typename|class %name% ,|>"); + }); +} + +void TemplateSimplifier::getTemplateInstantiations() +{ + std::multimap functionNameMap; + + for (const auto & decl : mTemplateDeclarations) { + if (decl.isFunction()) + functionNameMap.insert(std::make_pair(decl.name(), &decl)); + } + + for (const auto & decl : mTemplateForwardDeclarations) { + if (decl.isFunction()) + functionNameMap.insert(std::make_pair(decl.name(), &decl)); + } + + const Token *skip = nullptr; + + for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { + + // template definition.. skip it + if (Token::simpleMatch(tok, "template <")) { + tok = tok->next()->findClosingBracket(); + if (!tok) + break; + + const bool isUsing = tok->strAt(1) == "using"; + if (isUsing && Token::Match(tok->tokAt(2), "%name% <")) { + // Can't have specialized type alias so ignore it + Token *tok2 = Token::findsimplematch(tok->tokAt(3), ";"); + if (tok2) + tok = tok2; + } else if (tok->strAt(-1) == "<") { + // Don't ignore user specialization but don't consider it an instantiation. + // Instantiations in return type, function parameters, and executable code + // are not ignored. + const unsigned int pos = getTemplateNamePosition(tok); + if (pos > 0) + skip = tok->tokAt(pos); + } else { + // #7914 + // Ignore template instantiations within template definitions: they will only be + // handled if the definition is actually instantiated + + Token * tok2 = findTemplateDeclarationEnd(tok->next()); + if (tok2) + tok = tok2; + } + } else if (Token::Match(tok, "template using %name% <")) { + // Can't have specialized type alias so ignore it + Token *tok2 = Token::findsimplematch(tok->tokAt(3), ";"); + if (tok2) + tok = tok2; + } else if (Token::Match(tok, "using %name% <")) { + // Can't have specialized type alias so ignore it + Token *tok2 = Token::findsimplematch(tok->tokAt(2), ";"); + if (tok2) + tok = tok2; + } else if (Token::Match(tok->previous(), "(|{|}|;|=|>|<<|:|.|*|&|return|<|,|!|[ %name% ::|<|(") || + Token::Match(tok->previous(), "%type% %name% ::|<") || + Token::Match(tok->tokAt(-2), "[,:] private|protected|public %name% ::|<")) { + std::string scopeName = tok->scopeInfo()->name; + std::string qualification; + Token * qualificationTok = tok; + while (Token::Match(tok, "%name% :: %name%")) { + qualification += (qualification.empty() ? "" : " :: ") + tok->str(); + tok = tok->tokAt(2); + } + + // skip specialization + if (tok == skip) { + skip = nullptr; + continue; + } + + // look for function instantiation with type deduction + if (tok->strAt(1) == "(") { + std::vector instantiationArgs; + getFunctionArguments(tok, instantiationArgs); + + std::string fullName; + if (!qualification.empty()) + fullName = qualification + " :: " + tok->str(); + else if (!scopeName.empty()) + fullName = scopeName + " :: " + tok->str(); + else + fullName = tok->str(); + + // get all declarations with this name + auto range = functionNameMap.equal_range(tok->str()); + for (auto pos = range.first; pos != range.second; ++pos) { + // look for declaration with same qualification or constructor with same qualification + if (pos->second->fullName() == fullName || + (pos->second->scope() == fullName && tok->str() == pos->second->name())) { + std::vector templateParams; + getTemplateParametersInDeclaration(pos->second->token()->tokAt(2), templateParams); + + // todo: handle more than one template parameter + if (templateParams.size() != 1 || !areAllParamsTypes(templateParams)) + continue; + + std::vector declarationParams; + getFunctionArguments(pos->second->nameToken(), declarationParams); + + // function argument counts must match + if (instantiationArgs.empty() || instantiationArgs.size() != declarationParams.size()) + continue; + + size_t match = 0; + size_t argMatch = 0; + for (size_t i = 0; i < declarationParams.size(); ++i) { + // fixme: only type deduction from literals is supported + const bool isArgLiteral = Token::Match(instantiationArgs[i], "%num%|%str%|%char%|%bool% ,|)"); + if (isArgLiteral && Token::Match(declarationParams[i], "const| %type% &| %name%| ,|)")) { + match++; + + // check if parameter types match + if (templateParams[0]->str() == declarationParams[i]->str()) + argMatch = i; + else { + // todo: check if non-template args match for function overloads + } + } + } + + if (match == declarationParams.size()) { + const Token *arg = instantiationArgs[argMatch]; + tok->insertToken(">"); + switch (arg->tokType()) { + case Token::eBoolean: + tok->insertToken("bool"); + break; + case Token::eChar: + if (arg->isLong()) + tok->insertToken("wchar_t"); + else + tok->insertToken("char"); + break; + case Token::eString: + tok->insertToken("*"); + if (arg->isLong()) + tok->insertToken("wchar_t"); + else + tok->insertToken("char"); + tok->insertToken("const"); + break; + case Token::eNumber: { + MathLib::value num(arg->str()); + if (num.isFloat()) { + // MathLib::getSuffix doesn't work for floating point numbers + const char suffix = arg->str().back(); + if (suffix == 'f' || suffix == 'F') + tok->insertToken("float"); + else if (suffix == 'l' || suffix == 'L') { + tok->insertToken("double"); + tok->next()->isLong(true); + } else + tok->insertToken("double"); + } else if (num.isInt()) { + std::string suffix = MathLib::getSuffix(tok->strAt(3)); + if (suffix.find("LL") != std::string::npos) { + tok->insertToken("long"); + tok->next()->isLong(true); + } else if (suffix.find('L') != std::string::npos) + tok->insertToken("long"); + else + tok->insertToken("int"); + if (suffix.find('U') != std::string::npos) + tok->next()->isUnsigned(true); + } + break; + } + default: + break; + } + tok->insertToken("<"); + break; + } + } + } + } + + if (!Token::Match(tok, "%name% <") || + Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast")) + continue; + + if (tok == skip) { + skip = nullptr; + continue; + } + + // Add inner template instantiations first => go to the ">" + // and then parse backwards, adding all seen instantiations + Token *tok2 = tok->next()->findClosingBracket(); + + // parse backwards and add template instantiations + // TODO + for (; tok2 && tok2 != tok; tok2 = tok2->previous()) { + if (Token::Match(tok2, ",|< %name% <") && + (tok2->strAt(3) == ">" || templateParameters(tok2->tokAt(2)))) { + addInstantiation(tok2->next(), tok->scopeInfo()->name); + } else if (Token::Match(tok2->next(), "class|struct")) + tok2->deleteNext(); + } + + // Add outer template.. + if (templateParameters(tok->next()) || tok->strAt(2) == ">") { + while (true) { + const std::string fullName = scopeName + (scopeName.empty()?"":" :: ") + + qualification + (qualification.empty()?"":" :: ") + tok->str(); + const std::list::const_iterator it = std::find_if(mTemplateDeclarations.cbegin(), mTemplateDeclarations.cend(), FindFullName(fullName)); + if (it != mTemplateDeclarations.end()) { + // full name matches + addInstantiation(tok, it->scope()); + break; + } + // full name doesn't match so try with using namespaces if available + bool found = false; + for (const auto & nameSpace : tok->scopeInfo()->usingNamespaces) { + std::string fullNameSpace = scopeName + (scopeName.empty()?"":" :: ") + + nameSpace + (qualification.empty()?"":" :: ") + qualification; + std::string newFullName = fullNameSpace + " :: " + tok->str(); + const std::list::const_iterator it1 = std::find_if(mTemplateDeclarations.cbegin(), mTemplateDeclarations.cend(), FindFullName(std::move(newFullName))); + if (it1 != mTemplateDeclarations.end()) { + // insert using namespace into token stream + std::string::size_type offset = 0; + std::string::size_type pos = 0; + while ((pos = nameSpace.find(' ', offset)) != std::string::npos) { + qualificationTok->insertTokenBefore(nameSpace.substr(offset, pos - offset)); + offset = pos + 1; + } + qualificationTok->insertTokenBefore(nameSpace.substr(offset)); + qualificationTok->insertTokenBefore("::"); + addInstantiation(tok, it1->scope()); + found = true; + break; + } + } + if (found) + break; + + if (scopeName.empty()) { + if (!qualification.empty()) + addInstantiation(tok, qualification); + else + addInstantiation(tok, tok->scopeInfo()->name); + break; + } + const std::string::size_type pos = scopeName.rfind(" :: "); + scopeName = (pos == std::string::npos) ? std::string() : scopeName.substr(0,pos); + } + } + } + } +} + + +void TemplateSimplifier::useDefaultArgumentValues() +{ + for (TokenAndName &declaration : mTemplateDeclarations) + useDefaultArgumentValues(declaration); + + for (TokenAndName &declaration : mTemplateForwardDeclarations) + useDefaultArgumentValues(declaration); +} + +void TemplateSimplifier::useDefaultArgumentValues(TokenAndName &declaration) +{ + // Ticket #5762: Skip specialization tokens + if (declaration.isSpecialization() || declaration.isAlias() || declaration.isFriend()) + return; + + // template parameters with default value has syntax such as: + // x = y + // this list will contain all the '=' tokens for such arguments + struct Default { + Token *eq; + Token *end; + }; + std::list eq; + // and this set the position of parameters with a default value + std::set defaultedArgPos; + + // parameter number. 1,2,3,.. + std::size_t templatepar = 1; + + // parameter depth + std::size_t templateParmDepth = 0; + + // map type parameter name to index + std::map typeParameterNames; + + // Scan template declaration.. + for (Token *tok = declaration.token()->next(); tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "template <")) { + Token* end = tok->next()->findClosingBracket(); + if (end) + tok = end; + continue; + } + + if (tok->link() && Token::Match(tok, "{|(|[")) { // Ticket #6835 + tok = tok->link(); + continue; + } + + if (tok->str() == "<" && + (tok->strAt(1) == ">" || (tok->previous()->isName() && + typeParameterNames.find(tok->strAt(-1)) == typeParameterNames.end()))) + ++templateParmDepth; + + // end of template parameters? + if (tok->str() == ">") { + if (templateParmDepth<2) { + if (!eq.empty()) + eq.back().end = tok; + break; + } + --templateParmDepth; + } + + // map type parameter name to index + if (Token::Match(tok, "typename|class|%type% %name% ,|>")) + typeParameterNames[tok->strAt(1)] = templatepar - 1; + + // next template parameter + if (tok->str() == "," && (1 == templateParmDepth)) { // Ticket #5823: Properly count parameters + if (!eq.empty()) + eq.back().end = tok; + ++templatepar; + } + + // default parameter value? + else if (Token::Match(tok, "= !!>")) { + if (defaultedArgPos.insert(templatepar).second) { + eq.emplace_back(Default{tok, nullptr}); + } else { + // Ticket #5605: Syntax error (two equal signs for the same parameter), bail out + eq.clear(); + break; + } + } + } + if (eq.empty()) + return; + + // iterate through all template instantiations + for (const TokenAndName &instantiation : mTemplateInstantiations) { + if (declaration.fullName() != instantiation.fullName()) + continue; + + // instantiation arguments.. + std::vector> instantiationArgs; + std::size_t index = 0; + const Token *end = instantiation.token()->next()->findClosingBracket(); + if (!end) + continue; + if (end != instantiation.token()->tokAt(2)) + instantiationArgs.resize(1); + for (const Token *tok1 = instantiation.token()->tokAt(2); tok1 && tok1 != end; tok1 = tok1->next()) { + if (tok1->link() && Token::Match(tok1, "{|(|[")) { + const Token *endLink = tok1->link(); + do { + instantiationArgs[index].push_back(tok1); + tok1 = tok1->next(); + } while (tok1 && tok1 != endLink); + instantiationArgs[index].push_back(tok1); + } else if (tok1->str() == "<" && + (tok1->strAt(1) == ">" || (tok1->previous()->isName() && + typeParameterNames.find(tok1->strAt(-1)) == typeParameterNames.end()))) { + const Token *endLink = tok1->findClosingBracket(); + do { + instantiationArgs[index].push_back(tok1); + tok1 = tok1->next(); + } while (tok1 && tok1 != endLink); + instantiationArgs[index].push_back(tok1); + } else if (tok1->str() == ",") { + ++index; + instantiationArgs.resize(index + 1); + } else + instantiationArgs[index].push_back(tok1); + } + + // count the parameters.. + Token *tok = instantiation.token()->next(); + unsigned int usedpar = templateParameters(tok); + Token *instantiationEnd = tok->findClosingBracket(); + tok = instantiationEnd; + + if (tok && tok->str() == ">") { + tok = tok->previous(); + std::list::const_iterator it = eq.cbegin(); + for (std::size_t i = (templatepar - eq.size()); it != eq.cend() && i < usedpar; ++i) + ++it; + int count = 0; + while (it != eq.cend()) { + // check for end + if (!it->end) { + if (mSettings.debugwarnings && mSettings.severity.isEnabled(Severity::debug)) { + const std::list locationList(1, it->eq); + const ErrorMessage errmsg(locationList, &mTokenizer.list, + Severity::debug, + "noparamend", + "TemplateSimplifier couldn't find end of template parameter.", + Certainty::normal); + mErrorLogger.reportErr(errmsg); + } + break; + } + + if ((usedpar + count) && usedpar <= (instantiationArgs.size() + count)) { + tok->insertToken(","); + tok = tok->next(); + } + std::stack links; + for (const Token* from = it->eq->next(); from && from != it->end; from = from->next()) { + auto entry = typeParameterNames.find(from->str()); + if (entry != typeParameterNames.end() && entry->second < instantiationArgs.size()) { + for (const Token *tok1 : instantiationArgs[entry->second]) { + tok->insertToken(tok1->str(), tok1->originalName()); + tok = tok->next(); + + if (Token::Match(tok, "(|[|{")) + links.push(tok); + else if (!links.empty() && Token::Match(tok, ")|]|}")) { + Token::createMutualLinks(links.top(), tok); + links.pop(); + } + } + } else { + tok->insertToken(from->str(), from->originalName()); + tok = tok->next(); + + if (Token::Match(tok, "(|[|{")) + links.push(tok); + else if (!links.empty() && Token::Match(tok, ")|]|}")) { + Token::createMutualLinks(links.top(), tok); + links.pop(); + } + } + } + ++it; + count++; + usedpar++; + } + } + + simplifyTemplateArgs(instantiation.token()->next(), instantiationEnd); + } + + for (const auto & entry : eq) { + Token *const eqtok = entry.eq; + Token *tok2; + int indentlevel = 0; + for (tok2 = eqtok->next(); tok2; tok2 = tok2->next()) { + if (Token::Match(tok2, ";|)|}|]")) { // bail out #6607 + tok2 = nullptr; + break; + } + if (Token::Match(tok2, "(|{|[")) + tok2 = tok2->link(); + else if (Token::Match(tok2, "%type% <") && (tok2->strAt(2) == ">" || templateParameters(tok2->next()))) { + const std::list::iterator ti = std::find_if(mTemplateInstantiations.begin(), + mTemplateInstantiations.end(), + FindToken(tok2)); + if (ti != mTemplateInstantiations.end()) + mTemplateInstantiations.erase(ti); + ++indentlevel; + } else if (indentlevel > 0 && tok2->str() == ">") + --indentlevel; + else if (indentlevel == 0 && Token::Match(tok2, ",|>")) + break; + if (indentlevel < 0) + break; + } + // something went wrong, don't call eraseTokens() + // with a nullptr "end" parameter (=all remaining tokens). + if (!tok2) + continue; + + // don't strip args from uninstantiated templates + const std::list::iterator ti2 = std::find_if(mTemplateInstantiations.begin(), + mTemplateInstantiations.end(), + FindName(declaration.name())); + + if (ti2 == mTemplateInstantiations.end()) + continue; + + eraseTokens(eqtok, tok2); + eqtok->deleteThis(); + + // update parameter end pointer + declaration.paramEnd(declaration.token()->next()->findClosingBracket()); + } +} + +void TemplateSimplifier::simplifyTemplateAliases() +{ + for (std::list::iterator it1 = mTemplateDeclarations.begin(); it1 != mTemplateDeclarations.end();) { + const TokenAndName &aliasDeclaration = *it1; + + if (!aliasDeclaration.isAlias()) { + ++it1; + continue; + } + + // alias parameters.. + std::vector aliasParameters; + getTemplateParametersInDeclaration(aliasDeclaration.token()->tokAt(2), aliasParameters); + std::map aliasParameterNames; + for (unsigned int argnr = 0; argnr < aliasParameters.size(); ++argnr) + aliasParameterNames[aliasParameters[argnr]->str()] = argnr; + + // Look for alias usages.. + bool found = false; + for (std::list::iterator it2 = mTemplateInstantiations.begin(); it2 != mTemplateInstantiations.end();) { + const TokenAndName &aliasUsage = *it2; + if (!aliasUsage.token() || aliasUsage.fullName() != aliasDeclaration.fullName()) { + ++it2; + continue; + } + + // don't recurse + if (aliasDeclaration.isAliasToken(aliasUsage.token())) { + ++it2; + continue; + } + + std::vector> args; + Token *tok2 = aliasUsage.token()->tokAt(2); + while (tok2) { + Token * const start = tok2; + while (tok2 && !Token::Match(tok2, "[,>;{}]")) { + if (tok2->link() && Token::Match(tok2, "(|[")) + tok2 = tok2->link(); + else if (tok2->str() == "<") { + tok2 = tok2->findClosingBracket(); + if (!tok2) + break; + } + tok2 = tok2->next(); + } + + args.emplace_back(start, tok2); + if (tok2 && tok2->str() == ",") { + tok2 = tok2->next(); + } else { + break; + } + } + if (!tok2 || tok2->str() != ">" || + (!aliasDeclaration.isVariadic() && (args.size() != aliasParameters.size())) || + (aliasDeclaration.isVariadic() && (args.size() < aliasParameters.size()))) { + ++it2; + continue; + } + + mChanged = true; + + // copy template-id from declaration to after instantiation + Token * dst = aliasUsage.token()->next()->findClosingBracket(); + const Token* end = TokenList::copyTokens(dst, aliasDeclaration.aliasStartToken(), aliasDeclaration.aliasEndToken()->previous(), false)->next(); + + // replace parameters + for (Token *tok1 = dst->next(); tok1 != end; tok1 = tok1->next()) { + if (!tok1->isName()) + continue; + if (aliasParameterNames.find(tok1->str()) != aliasParameterNames.end()) { + const unsigned int argnr = aliasParameterNames[tok1->str()]; + const Token * const fromStart = args[argnr].first; + const Token * const fromEnd = args[argnr].second->previous(); + Token *temp = TokenList::copyTokens(tok1, fromStart, fromEnd, true); + const bool tempOK(temp && temp != tok1->next()); + tok1->deleteThis(); + if (tempOK) + tok1 = temp; // skip over inserted parameters + } else if (tok1->str() == "typename") + tok1->deleteThis(); + } + + // add new instantiations + for (Token *tok1 = dst->next(); tok1 != end; tok1 = tok1->next()) { + if (!tok1->isName()) + continue; + if (aliasParameterNames.find(tok2->str()) == aliasParameterNames.end()) { + // Create template instance.. + if (Token::Match(tok1, "%name% <")) { + const std::list::const_iterator it = std::find_if(mTemplateInstantiations.cbegin(), + mTemplateInstantiations.cend(), + FindToken(tok1)); + if (it != mTemplateInstantiations.cend()) + addInstantiation(tok2, it->scope()); + } + } + } + + // erase the instantiation tokens + eraseTokens(aliasUsage.token()->previous(), dst->next()); + found = true; + + // erase this instantiation + it2 = mTemplateInstantiations.erase(it2); + } + + if (found) { + auto *end = const_cast(aliasDeclaration.aliasEndToken()); + + // remove declaration tokens + if (aliasDeclaration.token()->previous()) + eraseTokens(aliasDeclaration.token()->previous(), end->next() ? end->next() : end); + else { + eraseTokens(mTokenList.front(), end->next() ? end->next() : end); + deleteToken(mTokenList.front()); + } + + // remove declaration + it1 = mTemplateDeclarations.erase(it1); + } else + ++it1; + } +} + +bool TemplateSimplifier::instantiateMatch(const Token *instance, const std::size_t numberOfArguments, bool variadic, const char patternAfter[]) +{ + assert(instance->strAt(1) == "<"); + + auto n = templateParameters(instance->next()); + if (variadic ? (n + 1 < numberOfArguments) : (numberOfArguments != n)) + return false; + + if (patternAfter) { + const Token *tok = instance->next()->findClosingBracket(); + if (!tok || !Token::Match(tok->next(), patternAfter)) + return false; + } + + // nothing mismatching was found.. + return true; +} + +// Utility function for TemplateSimplifier::getTemplateNamePosition, that works on template functions +bool TemplateSimplifier::getTemplateNamePositionTemplateFunction(const Token *tok, int &namepos) +{ + namepos = 1; + while (tok && tok->next()) { + if (Token::Match(tok->next(), ";|{")) + return false; + // skip decltype(...) + if (Token::simpleMatch(tok->next(), "decltype (")) { + const Token * end = tok->linkAt(2)->previous(); + while (tok->next() && tok != end) { + tok = tok->next(); + namepos++; + } + } else if (Token::Match(tok->next(), "%type% <")) { + const Token *closing = tok->tokAt(2)->findClosingBracket(); + if (closing) { + if (closing->strAt(1) == "(" && Tokenizer::isFunctionHead(closing->next(), ";|{|:")) + return true; + while (tok->next() && tok->next() != closing) { + tok = tok->next(); + namepos++; + } + } + } else if (Token::Match(tok->next(), "%type% (") && Tokenizer::isFunctionHead(tok->tokAt(2), ";|{|:")) { + return true; + } + tok = tok->next(); + namepos++; + } + return false; +} + +bool TemplateSimplifier::getTemplateNamePositionTemplateVariable(const Token *tok, int &namepos) +{ + namepos = 1; + while (tok && tok->next()) { + if (Token::Match(tok->next(), ";|{|(|using")) + return false; + // skip decltype(...) + if (Token::simpleMatch(tok->next(), "decltype (")) { + const Token * end = tok->linkAt(2); + while (tok->next() && tok != end) { + tok = tok->next(); + namepos++; + } + } else if (Token::Match(tok->next(), "%type% <")) { + const Token *closing = tok->tokAt(2)->findClosingBracket(); + if (closing) { + if (Token::Match(closing->next(), "=|;")) + return true; + while (tok->next() && tok->next() != closing) { + tok = tok->next(); + namepos++; + } + } + } else if (Token::Match(tok->next(), "%type% =|;")) { + return true; + } + tok = tok->next(); + namepos++; + } + return false; +} + +bool TemplateSimplifier::getTemplateNamePositionTemplateClass(const Token *tok, int &namepos) +{ + if (Token::Match(tok, "> friend| class|struct|union %type% :|<|;|{|::")) { + namepos = tok->strAt(1) == "friend" ? 3 : 2; + tok = tok->tokAt(namepos); + while (Token::Match(tok, "%type% :: %type%") || + (Token::Match(tok, "%type% <") && Token::Match(tok->next()->findClosingBracket(), "> :: %type%"))) { + if (tok->strAt(1) == "::") { + tok = tok->tokAt(2); + namepos += 2; + } else { + const Token *end = tok->next()->findClosingBracket(); + if (!end || !end->tokAt(2)) { + // syntax error + namepos = -1; + return true; + } + end = end->tokAt(2); + do { + tok = tok->next(); + namepos += 1; + } while (tok && tok != end); + } + } + return true; + } + return false; +} + +int TemplateSimplifier::getTemplateNamePosition(const Token *tok) +{ + if (!tok || tok->str() != ">") + syntaxError(tok); + + auto it = mTemplateNamePos.find(tok); + if (!mSettings.debugtemplate && it != mTemplateNamePos.end()) { + return it->second; + } + // get the position of the template name + int namepos = 0; + if (getTemplateNamePositionTemplateClass(tok, namepos)) + ; + else if (Token::Match(tok, "> using %name% =")) { + // types may not be defined in alias template declarations + if (!Token::Match(tok->tokAt(4), "class|struct|union|enum %name%| {")) + namepos = 2; + } else if (getTemplateNamePositionTemplateVariable(tok, namepos)) + ; + else if (!getTemplateNamePositionTemplateFunction(tok, namepos)) + namepos = -1; // Name not found + mTemplateNamePos[tok] = namepos; + return namepos; +} + +void TemplateSimplifier::addNamespace(const TokenAndName &templateDeclaration, const Token *tok) +{ + // find start of qualification + const Token * tokStart = tok; + int offset = 0; + while (Token::Match(tokStart->tokAt(-2), "%name% ::")) { + tokStart = tokStart->tokAt(-2); + offset -= 2; + } + // decide if namespace needs to be inserted in or appended to token list + const bool insert = tokStart != tok; + + std::string::size_type start = 0; + std::string::size_type end = 0; + bool inTemplate = false; + int level = 0; + while ((end = templateDeclaration.scope().find(' ', start)) != std::string::npos) { + std::string token = templateDeclaration.scope().substr(start, end - start); + // done if scopes overlap + if (token == tokStart->str() && tok->strAt(-1) != "::") + break; + if (token == "<") { + inTemplate = true; + ++level; + } + if (inTemplate) { + if (insert) + mTokenList.back()->tokAt(offset)->str(mTokenList.back()->strAt(offset) + token); + else + mTokenList.back()->str(mTokenList.back()->str() + token); + if (token == ">") { + --level; + if (level == 0) + inTemplate = false; + } + } else { + if (insert) + mTokenList.back()->tokAt(offset)->insertToken(token, emptyString); + else + mTokenList.addtoken(token, tok->linenr(), tok->column(), tok->fileIndex()); + } + start = end + 1; + } + // don't add if it already exists + std::string token = templateDeclaration.scope().substr(start, end - start); + if (token != tokStart->str() || tok->strAt(-1) != "::") { + if (insert) { + if (!inTemplate) + mTokenList.back()->tokAt(offset)->insertToken(templateDeclaration.scope().substr(start), emptyString); + else + mTokenList.back()->tokAt(offset)->str(mTokenList.back()->strAt(offset) + templateDeclaration.scope().substr(start)); + mTokenList.back()->tokAt(offset)->insertToken("::", emptyString); + } else { + if (!inTemplate) + mTokenList.addtoken(templateDeclaration.scope().substr(start), tok->linenr(), tok->column(), tok->fileIndex()); + else + mTokenList.back()->str(mTokenList.back()->str() + templateDeclaration.scope().substr(start)); + mTokenList.addtoken("::", tok->linenr(), tok->column(), tok->fileIndex()); + } + } +} + +bool TemplateSimplifier::alreadyHasNamespace(const TokenAndName &templateDeclaration, const Token *tok) +{ + const std::string& scope = templateDeclaration.scope(); + + // get the length in tokens of the namespace + std::string::size_type pos = 0; + int offset = -2; + + while ((pos = scope.find("::", pos)) != std::string::npos) { + offset -= 2; + pos += 2; + } + + return Token::simpleMatch(tok->tokAt(offset), scope.c_str(), scope.size()); +} + +struct newInstantiation { + newInstantiation(Token* t, std::string s) : token(t), scope(std::move(s)) {} + Token* token; + std::string scope; +}; + +void TemplateSimplifier::expandTemplate( + const TokenAndName &templateDeclaration, + const TokenAndName &templateInstantiation, + const std::vector &typeParametersInDeclaration, + const std::string &newName, + bool copy) +{ + bool inTemplateDefinition = false; + const Token *startOfTemplateDeclaration = nullptr; + const Token *endOfTemplateDefinition = nullptr; + const Token * const templateDeclarationNameToken = templateDeclaration.nameToken(); + const Token * const templateDeclarationToken = templateDeclaration.paramEnd(); + const bool isClass = templateDeclaration.isClass(); + const bool isFunction = templateDeclaration.isFunction(); + const bool isSpecialization = templateDeclaration.isSpecialization(); + const bool isVariable = templateDeclaration.isVariable(); + + std::vector newInstantiations; + + // add forward declarations + if (copy && isClass) { + templateDeclaration.token()->insertTokenBefore(templateDeclarationToken->strAt(1)); + templateDeclaration.token()->insertTokenBefore(newName); + templateDeclaration.token()->insertTokenBefore(";"); + } else if ((isFunction && (copy || isSpecialization)) || + (isVariable && !isSpecialization) || + (isClass && isSpecialization && mTemplateSpecializationMap.find(templateDeclaration.token()) != mTemplateSpecializationMap.end())) { + Token * dst = templateDeclaration.token(); + Token * dstStart = dst->previous(); + bool isStatic = false; + std::string scope; + const Token * start; + const Token * end; + auto it = mTemplateForwardDeclarationsMap.find(dst); + if (!isSpecialization && it != mTemplateForwardDeclarationsMap.end()) { + dst = it->second; + dstStart = dst->previous(); + const Token * temp1 = dst->tokAt(1)->findClosingBracket(); + const Token * temp2 = temp1->tokAt(getTemplateNamePosition(temp1)); + start = temp1->next(); + end = temp2->linkAt(1)->next(); + } else { + if (it != mTemplateForwardDeclarationsMap.end()) { + const std::list::const_iterator it1 = std::find_if(mTemplateForwardDeclarations.cbegin(), + mTemplateForwardDeclarations.cend(), + FindToken(it->second)); + if (it1 != mTemplateForwardDeclarations.cend()) + mMemberFunctionsToDelete.push_back(*it1); + } + + auto it2 = mTemplateSpecializationMap.find(dst); + if (it2 != mTemplateSpecializationMap.end()) { + dst = it2->second; + dstStart = dst->previous(); + isStatic = dst->next()->findClosingBracket()->strAt(1) == "static"; + const Token * temp = templateDeclarationNameToken; + while (Token::Match(temp->tokAt(-2), "%name% ::")) { + scope.insert(0, temp->strAt(-2) + " :: "); + temp = temp->tokAt(-2); + } + } + start = templateDeclarationToken->next(); + end = templateDeclarationNameToken->next(); + if (end->str() == "<") + end = end->findClosingBracket()->next(); + if (end->str() == "(") + end = end->link()->next(); + else if (isVariable && end->str() == "=") { + const Token *temp = end->next(); + while (temp && temp->str() != ";") { + if (temp->link() && Token::Match(temp, "{|[|(")) + temp = temp->link(); + temp = temp->next(); + } + end = temp; + } + } + unsigned int typeindentlevel = 0; + while (end && !(typeindentlevel == 0 && Token::Match(end, ";|{|:"))) { + if (Token::Match(end, "<|(|{")) + ++typeindentlevel; + else if (Token::Match(end, ">|)|}")) + --typeindentlevel; + end = end->next(); + } + + if (isStatic) { + dst->insertTokenBefore("static"); + if (start) { + dst->previous()->linenr(start->linenr()); + dst->previous()->column(start->column()); + } + } + + std::map links; + bool inAssignment = false; + while (start && start != end) { + if (isVariable && start->str() == "=") + inAssignment = true; + unsigned int itype = 0; + while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != start->str()) + ++itype; + + if (itype < typeParametersInDeclaration.size() && itype < mTypesUsedInTemplateInstantiation.size() && + (!isVariable || !Token::Match(typeParametersInDeclaration[itype]->previous(), "<|, %type% >|,"))) { + typeindentlevel = 0; + std::stack brackets1; // holds "(" and "{" tokens + bool pointerType = false; + Token * const dst1 = dst->previous(); + const bool isVariadicTemplateArg = templateDeclaration.isVariadic() && itype + 1 == typeParametersInDeclaration.size(); + if (isVariadicTemplateArg && Token::Match(start, "%name% ... %name%")) + start = start->tokAt(2); + const std::string endStr(isVariadicTemplateArg ? ">" : ",>"); + for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype].token(); + typetok && (typeindentlevel > 0 || endStr.find(typetok->str()[0]) == std::string::npos); + typetok = typetok->next()) { + if (typeindentlevel == 0 && typetok->str() == "*") + pointerType = true; + if (Token::simpleMatch(typetok, "...")) + continue; + if (Token::Match(typetok, "%name% <") && (typetok->strAt(2) == ">" || templateParameters(typetok->next()))) + ++typeindentlevel; + else if (typeindentlevel > 0 && typetok->str() == ">") + --typeindentlevel; + else if (typetok->str() == "(") + ++typeindentlevel; + else if (typetok->str() == ")") + --typeindentlevel; + dst->insertToken(typetok->str(), typetok->originalName(), typetok->getMacroName(), true); + dst->previous()->linenr(start->linenr()); + dst->previous()->column(start->column()); + Token *previous = dst->previous(); + previous->isTemplateArg(true); + previous->isSigned(typetok->isSigned()); + previous->isUnsigned(typetok->isUnsigned()); + previous->isLong(typetok->isLong()); + if (Token::Match(previous, "{|(|[")) { + brackets1.push(previous); + } else if (previous->str() == "}") { + assert(brackets1.empty() == false); + assert(brackets1.top()->str() == "{"); + Token::createMutualLinks(brackets1.top(), previous); + brackets1.pop(); + } else if (previous->str() == ")") { + assert(brackets1.empty() == false); + assert(brackets1.top()->str() == "("); + Token::createMutualLinks(brackets1.top(), previous); + brackets1.pop(); + } else if (previous->str() == "]") { + assert(brackets1.empty() == false); + assert(brackets1.top()->str() == "["); + Token::createMutualLinks(brackets1.top(), previous); + brackets1.pop(); + } + } + if (pointerType && Token::simpleMatch(dst1, "const")) { + dst->insertToken("const", dst1->originalName(), dst1->getMacroName(), true); + dst->previous()->linenr(start->linenr()); + dst->previous()->column(start->column()); + dst1->deleteThis(); + } + } else { + if (isSpecialization && !copy && !scope.empty() && Token::Match(start, (scope + templateDeclarationNameToken->str()).c_str())) { + // skip scope + while (start->strAt(1) != templateDeclarationNameToken->str()) + start = start->next(); + } else if (start->str() == templateDeclarationNameToken->str() && + !(templateDeclaration.isFunction() && templateDeclaration.scope().empty() && + (start->strAt(-1) == "." || Token::simpleMatch(start->tokAt(-2), ". template")))) { + if (start->strAt(1) != "<" || Token::Match(start, newName.c_str()) || !inAssignment) { + dst->insertTokenBefore(newName); + dst->previous()->linenr(start->linenr()); + dst->previous()->column(start->column()); + if (start->strAt(1) == "<") + start = start->next()->findClosingBracket(); + } else { + dst->insertTokenBefore(start->str()); + dst->previous()->linenr(start->linenr()); + dst->previous()->column(start->column()); + newInstantiations.emplace_back(dst->previous(), templateDeclaration.scope()); + } + } else { + // check if type is a template + if (start->strAt(1) == "<") { + // get the instantiated name + const Token * closing = start->next()->findClosingBracket(); + if (closing) { + std::string name; + const Token * type = start; + while (type && type != closing->next()) { + if (!name.empty()) + name += " "; + name += type->str(); + type = type->next(); + } + // check if type is instantiated + if (std::any_of(mTemplateInstantiations.cbegin(), mTemplateInstantiations.cend(), [&](const TokenAndName& inst) { + return Token::simpleMatch(inst.token(), name.c_str(), name.size()); + })) { + // use the instantiated name + dst->insertTokenBefore(name); + dst->previous()->linenr(start->linenr()); + dst->previous()->column(start->column()); + start = closing; + } + } + // just copy the token if it wasn't instantiated + if (start != closing) { + dst->insertToken(start->str(), start->originalName(), start->getMacroName(), true); + dst->previous()->linenr(start->linenr()); + dst->previous()->column(start->column()); + dst->previous()->isSigned(start->isSigned()); + dst->previous()->isUnsigned(start->isUnsigned()); + dst->previous()->isLong(start->isLong()); + } + } else { + dst->insertToken(start->str(), start->originalName(), start->getMacroName(), true); + dst->previous()->linenr(start->linenr()); + dst->previous()->column(start->column()); + dst->previous()->isSigned(start->isSigned()); + dst->previous()->isUnsigned(start->isUnsigned()); + dst->previous()->isLong(start->isLong()); + } + } + + if (!start) + continue; + + if (start->link()) { + if (Token::Match(start, "[|{|(")) { + links[start->link()] = dst->previous(); + } else if (Token::Match(start, "]|}|)")) { + std::map::iterator link = links.find(start); + // make sure link is valid + if (link != links.end()) { + Token::createMutualLinks(link->second, dst->previous()); + links.erase(start); + } + } + } + } + + start = start->next(); + } + dst->insertTokenBefore(";"); + dst->previous()->linenr(dst->tokAt(-2)->linenr()); + dst->previous()->column(dst->tokAt(-2)->column() + 1); + + if (isVariable || isFunction) + simplifyTemplateArgs(dstStart, dst); + } + + if (copy && (isClass || isFunction)) { + // check if this is an explicit instantiation + Token * start = templateInstantiation.token(); + while (start && !Token::Match(start->previous(), "}|;|extern")) + start = start->previous(); + if (Token::Match(start, "template !!<")) { + if (start->strAt(-1) == "extern") + start = start->previous(); + mExplicitInstantiationsToDelete.emplace_back(start, ""); + } + } + + for (Token *tok3 = mTokenList.front(); tok3; tok3 = tok3 ? tok3->next() : nullptr) { + if (inTemplateDefinition) { + if (!endOfTemplateDefinition) { + if (isVariable) { + Token *temp = tok3->findClosingBracket(); + if (temp) { + while (temp && temp->str() != ";") { + if (temp->link() && Token::Match(temp, "{|[|(")) + temp = temp->link(); + temp = temp->next(); + } + endOfTemplateDefinition = temp; + } + } else if (tok3->str() == "{") + endOfTemplateDefinition = tok3->link(); + } + if (tok3 == endOfTemplateDefinition) { + inTemplateDefinition = false; + startOfTemplateDeclaration = nullptr; + } + } + + if (tok3->str()=="template") { + if (tok3->next() && tok3->next()->str()=="<") { + std::vector localTypeParametersInDeclaration; + getTemplateParametersInDeclaration(tok3->tokAt(2), localTypeParametersInDeclaration); + inTemplateDefinition = localTypeParametersInDeclaration.size() == typeParametersInDeclaration.size(); // Partial specialization + } else { + inTemplateDefinition = false; // Only template instantiation + } + startOfTemplateDeclaration = tok3; + } + if (Token::Match(tok3, "(|[")) + tok3 = tok3->link(); + + // Start of template.. + if (tok3 == templateDeclarationToken) { + tok3 = tok3->next(); + if (tok3->str() == "static") + tok3 = tok3->next(); + } + + // member function implemented outside class definition + else if (inTemplateDefinition && + Token::Match(tok3, "%name% <") && + templateInstantiation.name() == tok3->str() && + instantiateMatch(tok3, typeParametersInDeclaration.size(), templateDeclaration.isVariadic(), ":: ~| %name% (")) { + // there must be template.. + bool istemplate = false; + Token * tok5 = nullptr; // start of function return type + for (Token *prev = tok3; prev && !Token::Match(prev, "[;{}]"); prev = prev->previous()) { + if (prev->str() == "template") { + istemplate = true; + tok5 = prev; + break; + } + } + if (!istemplate) + continue; + + const Token *tok4 = tok3->next()->findClosingBracket(); + while (tok4 && tok4->str() != "(") + tok4 = tok4->next(); + if (!Tokenizer::isFunctionHead(tok4, ":{")) + continue; + // find function return type start + tok5 = tok5->next()->findClosingBracket(); + if (tok5) + tok5 = tok5->next(); + // copy return type + std::stack brackets2; // holds "(" and "{" tokens + while (tok5 && tok5 != tok3) { + // replace name if found + if (Token::Match(tok5, "%name% <") && tok5->str() == templateInstantiation.name()) { + if (copy) { + if (!templateDeclaration.scope().empty() && tok5->strAt(-1) != "::") + addNamespace(templateDeclaration, tok5); + mTokenList.addtoken(newName, tok5->linenr(), tok5->column(), tok5->fileIndex()); + tok5 = tok5->next()->findClosingBracket(); + } else { + tok5->str(newName); + eraseTokens(tok5, tok5->next()->findClosingBracket()->next()); + } + } else if (copy) { + bool added = false; + if (tok5->isName() && !Token::Match(tok5, "class|typename|struct") && !tok5->isStandardType()) { + // search for this token in the type vector + unsigned int itype = 0; + while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != tok5->str()) + ++itype; + + // replace type with given type.. + if (itype < typeParametersInDeclaration.size() && itype < mTypesUsedInTemplateInstantiation.size()) { + std::stack brackets1; // holds "(" and "{" tokens + for (const Token *typetok = mTypesUsedInTemplateInstantiation[itype].token(); + typetok && !Token::Match(typetok, ",|>"); + typetok = typetok->next()) { + if (!Token::simpleMatch(typetok, "...")) { + mTokenList.addtoken(typetok, tok5); + Token *back = mTokenList.back(); + if (Token::Match(back, "{|(|[")) { + brackets1.push(back); + } else if (back->str() == "}") { + assert(brackets1.empty() == false); + assert(brackets1.top()->str() == "{"); + Token::createMutualLinks(brackets1.top(), back); + brackets1.pop(); + } else if (back->str() == ")") { + assert(brackets1.empty() == false); + assert(brackets1.top()->str() == "("); + Token::createMutualLinks(brackets1.top(), back); + brackets1.pop(); + } else if (back->str() == "]") { + assert(brackets1.empty() == false); + assert(brackets1.top()->str() == "["); + Token::createMutualLinks(brackets1.top(), back); + brackets1.pop(); + } + back->isTemplateArg(true); + back->isUnsigned(typetok->isUnsigned()); + back->isSigned(typetok->isSigned()); + back->isLong(typetok->isLong()); + added = true; + break; + } + } + } + } + if (!added) { + mTokenList.addtoken(tok5); + Token *back = mTokenList.back(); + if (Token::Match(back, "{|(|[")) { + brackets2.push(back); + } else if (back->str() == "}") { + assert(brackets2.empty() == false); + assert(brackets2.top()->str() == "{"); + Token::createMutualLinks(brackets2.top(), back); + brackets2.pop(); + } else if (back->str() == ")") { + assert(brackets2.empty() == false); + assert(brackets2.top()->str() == "("); + Token::createMutualLinks(brackets2.top(), back); + brackets2.pop(); + } else if (back->str() == "]") { + assert(brackets2.empty() == false); + assert(brackets2.top()->str() == "["); + Token::createMutualLinks(brackets2.top(), back); + brackets2.pop(); + } + } + } + + tok5 = tok5->next(); + } + if (copy) { + if (!templateDeclaration.scope().empty() && tok3->strAt(-1) != "::") + addNamespace(templateDeclaration, tok3); + mTokenList.addtoken(newName, tok3->linenr(), tok3->column(), tok3->fileIndex()); + } + + while (tok3 && tok3->str() != "::") + tok3 = tok3->next(); + + const std::list::const_iterator it = std::find_if(mTemplateDeclarations.cbegin(), + mTemplateDeclarations.cend(), + FindToken(startOfTemplateDeclaration)); + if (it != mTemplateDeclarations.cend()) + mMemberFunctionsToDelete.push_back(*it); + } + + // not part of template.. go on to next token + else + continue; + + std::stack brackets; // holds "(", "[" and "{" tokens + + // FIXME use full name matching somehow + const std::string lastName = (templateInstantiation.name().find(' ') != std::string::npos) ? templateInstantiation.name().substr(templateInstantiation.name().rfind(' ')+1) : templateInstantiation.name(); + + std::stack templates; + for (; tok3; tok3 = tok3->next()) { + if (tok3->isName() && !Token::Match(tok3, "class|typename|struct") && !tok3->isStandardType()) { + // search for this token in the type vector + unsigned int itype = 0; + while (itype < typeParametersInDeclaration.size() && typeParametersInDeclaration[itype]->str() != tok3->str()) + ++itype; + + // replace type with given type.. + if (itype < typeParametersInDeclaration.size() && itype < mTypesUsedInTemplateInstantiation.size()) { + unsigned int typeindentlevel = 0; + std::stack brackets1; // holds "(" and "{" tokens + Token * const beforeTypeToken = mTokenList.back(); + bool pointerType = false; + const bool isVariadicTemplateArg = templateDeclaration.isVariadic() && itype + 1 == typeParametersInDeclaration.size(); + if (isVariadicTemplateArg && mTypesUsedInTemplateInstantiation.size() > 1 && !Token::simpleMatch(tok3->next(), "...")) + continue; + if (isVariadicTemplateArg && Token::Match(tok3, "%name% ... %name%")) + tok3 = tok3->tokAt(2); + const std::string endStr(isVariadicTemplateArg ? ">" : ",>"); + for (Token *typetok = mTypesUsedInTemplateInstantiation[itype].token(); + typetok && (typeindentlevel > 0 || endStr.find(typetok->str()[0]) == std::string::npos); + typetok = typetok->next()) { + if (typeindentlevel == 0 && typetok->str() == "*") + pointerType = true; + if (Token::simpleMatch(typetok, "...")) + continue; + if (Token::Match(typetok, "%name% <") && + (typetok->strAt(2) == ">" || templateParameters(typetok->next()))) { + brackets1.push(typetok->next()); + ++typeindentlevel; + } else if (typeindentlevel > 0 && typetok->str() == ">" && brackets1.top()->str() == "<") { + --typeindentlevel; + brackets1.pop(); + } else if (Token::Match(typetok, "const_cast|dynamic_cast|reinterpret_cast|static_cast <")) { + brackets1.push(typetok->next()); + ++typeindentlevel; + } else if (typetok->str() == "(") + ++typeindentlevel; + else if (typetok->str() == ")") + --typeindentlevel; + Token *back; + if (copy) { + mTokenList.addtoken(typetok, tok3); + back = mTokenList.back(); + } else + back = typetok; + if (Token::Match(back, "{|(|[")) + brackets1.push(back); + else if (back->str() == "}") { + assert(brackets1.empty() == false); + assert(brackets1.top()->str() == "{"); + if (copy) + Token::createMutualLinks(brackets1.top(), back); + brackets1.pop(); + } else if (back->str() == ")") { + assert(brackets1.empty() == false); + assert(brackets1.top()->str() == "("); + if (copy) + Token::createMutualLinks(brackets1.top(), back); + brackets1.pop(); + } else if (back->str() == "]") { + assert(brackets1.empty() == false); + assert(brackets1.top()->str() == "["); + if (copy) + Token::createMutualLinks(brackets1.top(), back); + brackets1.pop(); + } + if (copy) + back->isTemplateArg(true); + } + if (pointerType && Token::simpleMatch(beforeTypeToken, "const")) { + mTokenList.addtoken(beforeTypeToken); + beforeTypeToken->deleteThis(); + } + continue; + } + } + + // replace name.. + if (tok3->str() == lastName) { + if (Token::simpleMatch(tok3->next(), "<")) { + Token *closingBracket = tok3->next()->findClosingBracket(); + if (closingBracket) { + // replace multi token name with single token name + if (tok3 == templateDeclarationNameToken || + Token::Match(tok3, newName.c_str())) { + if (copy) { + mTokenList.addtoken(newName, tok3); + tok3 = closingBracket; + } else { + tok3->str(newName); + eraseTokens(tok3, closingBracket->next()); + } + continue; + } + if (!templateDeclaration.scope().empty() && + !alreadyHasNamespace(templateDeclaration, tok3) && + !Token::Match(closingBracket->next(), "(|::")) { + if (copy) + addNamespace(templateDeclaration, tok3); + } + } + } else { + // don't modify friend + if (Token::Match(tok3->tokAt(-3), "> friend class|struct|union")) { + if (copy) + mTokenList.addtoken(tok3); + } else if (copy) { + // add namespace if necessary + if (!templateDeclaration.scope().empty() && + (isClass ? tok3->strAt(1) != "(" : true)) { + addNamespace(templateDeclaration, tok3); + } + mTokenList.addtoken(newName, tok3); + } else if (!Token::Match(tok3->next(), "[:{=;[]),]")) + tok3->str(newName); + continue; + } + } + + // copy + if (copy) + mTokenList.addtoken(tok3); + + // look for template definitions + if (Token::simpleMatch(tok3, "template <")) { + Token * tok2 = findTemplateDeclarationEnd(tok3); + if (tok2) + templates.push(tok2); + } else if (!templates.empty() && templates.top() == tok3) + templates.pop(); + + if (Token::Match(tok3, "%type% <") && + !Token::Match(tok3, "template|static_cast|const_cast|reinterpret_cast|dynamic_cast") && + Token::Match(tok3->next()->findClosingBracket(), ">|>>")) { + const Token *closingBracket = tok3->next()->findClosingBracket(); + if (Token::simpleMatch(closingBracket->next(), "&")) { + int num = 0; + const Token *par = tok3->next(); + while (num < typeParametersInDeclaration.size() && par != closingBracket) { + const std::string pattern("[<,] " + typeParametersInDeclaration[num]->str() + " [,>]"); + if (!Token::Match(par, pattern.c_str())) + break; + ++num; + par = par->tokAt(2); + } + if (num < typeParametersInDeclaration.size() || par != closingBracket) + continue; + } + + // don't add instantiations in template definitions + if (!templates.empty()) + continue; + + std::string scope; + const Token *prev = tok3; + for (; Token::Match(prev->tokAt(-2), "%name% ::"); prev = prev->tokAt(-2)) { + if (scope.empty()) + scope = prev->strAt(-2); + else + scope = prev->strAt(-2) + " :: " + scope; + } + + // check for global scope + if (prev->strAt(-1) != "::") { + // adjust for current scope + std::string token_scope = tok3->scopeInfo()->name; + const std::string::size_type end = token_scope.find_last_of(" :: "); + if (end != std::string::npos) { + token_scope.resize(end); + if (scope.empty()) + scope = std::move(token_scope); + else + scope = token_scope + " :: " + scope; + } + } + + if (copy) + newInstantiations.emplace_back(mTokenList.back(), std::move(scope)); + else if (!inTemplateDefinition) + newInstantiations.emplace_back(tok3, std::move(scope)); + } + + // link() newly tokens manually + else if (copy) { + if (tok3->str() == "{") { + brackets.push(mTokenList.back()); + } else if (tok3->str() == "(") { + brackets.push(mTokenList.back()); + } else if (tok3->str() == "[") { + brackets.push(mTokenList.back()); + } else if (tok3->str() == "}") { + assert(brackets.empty() == false); + assert(brackets.top()->str() == "{"); + Token::createMutualLinks(brackets.top(), mTokenList.back()); + brackets.pop(); + if (brackets.empty() && !Token::Match(tok3, "} >|,|{")) { + inTemplateDefinition = false; + if (isClass && tok3->strAt(1) == ";") { + const Token* tokSemicolon = tok3->next(); + mTokenList.addtoken(tokSemicolon, tokSemicolon->linenr(), tokSemicolon->column(), tokSemicolon->fileIndex()); + } + break; + } + } else if (tok3->str() == ")") { + assert(brackets.empty() == false); + assert(brackets.top()->str() == "("); + Token::createMutualLinks(brackets.top(), mTokenList.back()); + brackets.pop(); + } else if (tok3->str() == "]") { + assert(brackets.empty() == false); + assert(brackets.top()->str() == "["); + Token::createMutualLinks(brackets.top(), mTokenList.back()); + brackets.pop(); + } + } + } + + assert(brackets.empty()); + } + + // add new instantiations + for (const auto & inst : newInstantiations) { + if (!inst.token) + continue; + simplifyTemplateArgs(inst.token->tokAt(2), inst.token->next()->findClosingBracket(), &newInstantiations); + // only add recursive instantiation if its arguments are a constant expression + if (templateDeclaration.name() != inst.token->str() || + (inst.token->tokAt(2)->isNumber() || inst.token->tokAt(2)->isStandardType())) + mTemplateInstantiations.emplace_back(inst.token, inst.scope); + } +} + +static bool isLowerThanLogicalAnd(const Token *lower) +{ + return lower->isAssignmentOp() || Token::Match(lower, "}|;|(|[|]|)|,|?|:|%oror%|return|throw|case"); +} +static bool isLowerThanOr(const Token* lower) +{ + return isLowerThanLogicalAnd(lower) || lower->str() == "&&"; +} +static bool isLowerThanXor(const Token* lower) +{ + return isLowerThanOr(lower) || lower->str() == "|"; +} +static bool isLowerThanAnd(const Token* lower) +{ + return isLowerThanXor(lower) || lower->str() == "^"; +} +static bool isLowerThanShift(const Token* lower) +{ + return isLowerThanAnd(lower) || lower->str() == "&"; +} +static bool isLowerThanPlusMinus(const Token* lower) +{ + return isLowerThanShift(lower) || Token::Match(lower, "%comp%|<<|>>"); +} +static bool isLowerThanMulDiv(const Token* lower) +{ + return isLowerThanPlusMinus(lower) || Token::Match(lower, "+|-"); +} +static bool isLowerEqualThanMulDiv(const Token* lower) +{ + return isLowerThanMulDiv(lower) || Token::Match(lower, "[*/%]"); +} + + +bool TemplateSimplifier::simplifyNumericCalculations(Token *tok, bool isTemplate) +{ + bool ret = false; + // (1-2) + while (tok->tokAt(3) && tok->isNumber() && tok->tokAt(2)->isNumber()) { // %any% %num% %any% %num% %any% + const Token *before = tok->previous(); + if (!before) + break; + const Token* op = tok->next(); + const Token* after = tok->tokAt(3); + const std::string &num1 = op->previous()->str(); + const std::string &num2 = op->next()->str(); + if (Token::Match(before, "* %num% /") && (num2 != "0") && num1 == MathLib::multiply(num2, MathLib::divide(num1, num2))) { + // Division where result is a whole number + } else if (!((op->str() == "*" && (isLowerThanMulDiv(before) || before->str() == "*") && isLowerEqualThanMulDiv(after)) || // associative + (Token::Match(op, "[/%]") && isLowerThanMulDiv(before) && isLowerEqualThanMulDiv(after)) || // NOT associative + (Token::Match(op, "[+-]") && isLowerThanMulDiv(before) && isLowerThanMulDiv(after)) || // Only partially (+) associative, but handled later + (Token::Match(op, ">>|<<") && isLowerThanShift(before) && isLowerThanPlusMinus(after)) || // NOT associative + (op->str() == "&" && isLowerThanShift(before) && isLowerThanShift(after)) || // associative + (op->str() == "^" && isLowerThanAnd(before) && isLowerThanAnd(after)) || // associative + (op->str() == "|" && isLowerThanXor(before) && isLowerThanXor(after)) || // associative + (op->str() == "&&" && isLowerThanOr(before) && isLowerThanOr(after)) || + (op->str() == "||" && isLowerThanLogicalAnd(before) && isLowerThanLogicalAnd(after)))) + break; + + // Don't simplify "%num% / 0" + if (Token::Match(op, "[/%] 0")) { + if (isTemplate) + throw InternalError(op, "Instantiation error: Divide by zero in template instantiation.", InternalError::INSTANTIATION); + return ret; + } + + // Integer operations + if (Token::Match(op, ">>|<<|&|^|%or%")) { + // Don't simplify if operand is negative, shifting with negative + // operand is UB. Bitmasking with negative operand is implementation + // defined behaviour. + if (MathLib::isNegative(num1) || MathLib::isNegative(num2)) + break; + + const MathLib::value v1(num1); + const MathLib::value v2(num2); + + if (!v1.isInt() || !v2.isInt()) + break; + + switch (op->str()[0]) { + case '<': + tok->str((v1 << v2).str()); + break; + case '>': + tok->str((v1 >> v2).str()); + break; + case '&': + tok->str((v1 & v2).str()); + break; + case '|': + tok->str((v1 | v2).str()); + break; + case '^': + tok->str((v1 ^ v2).str()); + break; + } + } + + // Logical operations + else if (Token::Match(op, "%oror%|&&")) { + const bool op1 = !MathLib::isNullValue(num1); + const bool op2 = !MathLib::isNullValue(num2); + const bool result = (op->str() == "||") ? (op1 || op2) : (op1 && op2); + tok->str(result ? "1" : "0"); + } + + else if (Token::Match(tok->previous(), "- %num% - %num%")) + tok->str(MathLib::add(num1, num2)); + else if (Token::Match(tok->previous(), "- %num% + %num%")) + tok->str(MathLib::subtract(num1, num2)); + else { + try { + tok->str(MathLib::calculate(num1, num2, op->str()[0])); + } catch (InternalError &e) { + e.token = tok; + throw; + } + } + + tok->deleteNext(2); + + ret = true; + } + + return ret; +} + +static Token *skipTernaryOp(Token *tok, const Token *backToken) +{ + unsigned int colonLevel = 1; + while (nullptr != (tok = tok->next())) { + if (tok->str() == "?") { + ++colonLevel; + } else if (tok->str() == ":") { + --colonLevel; + if (colonLevel == 0) { + tok = tok->next(); + break; + } + } + if (tok->link() && tok->str() == "(") + tok = tok->link(); + else if (Token::Match(tok->next(), "[{};)]") || tok->next() == backToken) + break; + } + if (colonLevel > 0) // Ticket #5214: Make sure the ':' matches the proper '?' + return nullptr; + return tok; +} + +static void invalidateInst(const Token* beg, const Token* end, std::vector* newInst) { + if (!newInst) + return; + for (auto& inst : *newInst) { + for (const Token* tok = beg; tok != end; tok = tok->next()) + if (inst.token == tok) { + inst.token = nullptr; + break; + } + } +} + +void TemplateSimplifier::simplifyTemplateArgs(Token *start, const Token *end, std::vector* newInst) +{ + // start could be erased so use the token before start if available + Token * first = (start && start->previous()) ? start->previous() : mTokenList.front(); + bool again = true; + + while (again) { + again = false; + + for (Token *tok = first->next(); tok && tok != end; tok = tok->next()) { + if (tok->str() == "sizeof") { + // sizeof('x') + if (Token::Match(tok->next(), "( %char% )")) { + tok->deleteNext(); + tok->deleteThis(); + tok->deleteNext(); + tok->str(std::to_string(1)); + again = true; + } + + // sizeof ("text") + else if (Token::Match(tok->next(), "( %str% )")) { + tok->deleteNext(); + tok->deleteThis(); + tok->deleteNext(); + tok->str(std::to_string(Token::getStrLength(tok) + 1)); + again = true; + } + + else if (Token::Match(tok->next(), "( %type% * )")) { + tok->str(std::to_string(mTokenizer.sizeOfType(tok->tokAt(3)))); + tok->deleteNext(4); + again = true; + } else if (Token::simpleMatch(tok->next(), "( * )")) { + tok->str(std::to_string(mTokenizer.sizeOfType(tok->tokAt(2)))); + tok->deleteNext(3); + again = true; + } else if (Token::Match(tok->next(), "( %type% )")) { + const unsigned int size = mTokenizer.sizeOfType(tok->tokAt(2)); + if (size > 0) { + tok->str(std::to_string(size)); + tok->deleteNext(3); + again = true; + } + } else if (tok->strAt(1) == "(") { + tok = tok->linkAt(1); + } + } else if (Token::Match(tok, "%num% %comp% %num%") && + MathLib::isInt(tok->str()) && + MathLib::isInt(tok->strAt(2))) { + if ((Token::Match(tok->previous(), "(|&&|%oror%|,") || tok == start) && + (Token::Match(tok->tokAt(3), ")|&&|%oror%|?") || tok->tokAt(3) == end)) { + const MathLib::bigint op1(MathLib::toBigNumber(tok->str())); + const std::string &cmp(tok->next()->str()); + const MathLib::bigint op2(MathLib::toBigNumber(tok->strAt(2))); + + std::string result; + + if (cmp == "==") + result = bool_to_string(op1 == op2); + else if (cmp == "!=") + result = bool_to_string(op1 != op2); + else if (cmp == "<=") + result = bool_to_string(op1 <= op2); + else if (cmp == ">=") + result = bool_to_string(op1 >= op2); + else if (cmp == "<") + result = bool_to_string(op1 < op2); + else + result = bool_to_string(op1 > op2); + + tok->str(result); + tok->deleteNext(2); + again = true; + tok = tok->previous(); + } + } + } + + if (simplifyCalculations(first->next(), end)) + again = true; + + for (Token *tok = first->next(); tok && tok != end; tok = tok->next()) { + if (tok->str() == "?" && + ((tok->previous()->isNumber() || tok->previous()->isBoolean()) || + Token::Match(tok->tokAt(-3), "( %bool%|%num% )"))) { + const int offset = (tok->previous()->str() == ")") ? 2 : 1; + + // Find the token ":" then go to the next token + Token *colon = skipTernaryOp(tok, end); + if (!colon || colon->previous()->str() != ":" || !colon->next()) + continue; + + //handle the GNU extension: "x ? : y" <-> "x ? x : y" + if (colon->previous() == tok->next()) + tok->insertToken(tok->strAt(-offset)); + + // go back before the condition, if possible + tok = tok->tokAt(-2); + if (offset == 2) { + // go further back before the "(" + tok = tok->tokAt(-2); + //simplify the parentheses + tok->deleteNext(); + tok->next()->deleteNext(); + } + + if (Token::Match(tok->next(), "false|0")) { + invalidateInst(tok->next(), colon, newInst); + // Use code after colon, remove code before it. + Token::eraseTokens(tok, colon); + + tok = tok->next(); + again = true; + } + + // The condition is true. Delete the operator after the ":".. + else { + // delete the condition token and the "?" + tok->deleteNext(2); + + unsigned int ternaryOplevel = 0; + for (const Token *endTok = colon; endTok; endTok = endTok->next()) { + if (Token::Match(endTok, "(|[|{")) + endTok = endTok->link(); + else if (endTok->str() == "<" && (endTok->strAt(1) == ">" || templateParameters(endTok))) + endTok = endTok->findClosingBracket(); + else if (endTok->str() == "?") + ++ternaryOplevel; + else if (Token::Match(endTok, ")|}|]|;|,|:|>")) { + if (endTok->str() == ":" && ternaryOplevel) + --ternaryOplevel; + else if (endTok->str() == ">" && !end) + ; + else { + invalidateInst(colon->tokAt(-1), endTok, newInst); + Token::eraseTokens(colon->tokAt(-2), endTok); + again = true; + break; + } + } + } + } + } + } + + for (Token *tok = first->next(); tok && tok != end; tok = tok->next()) { + if (Token::Match(tok, "( %num%|%bool% )") && + (tok->previous() && !tok->previous()->isName())) { + tok->deleteThis(); + tok->deleteNext(); + again = true; + } + } + } +} + +static bool validTokenStart(bool bounded, const Token *tok, const Token *frontToken, int offset) +{ + if (!bounded) + return true; + + if (frontToken) + frontToken = frontToken->previous(); + + while (tok && offset <= 0) { + if (tok == frontToken) + return false; + ++offset; + tok = tok->previous(); + } + + return tok && offset > 0; +} + +static bool validTokenEnd(bool bounded, const Token *tok, const Token *backToken, int offset) +{ + if (!bounded) + return true; + + while (tok && offset >= 0) { + if (tok == backToken) + return false; + --offset; + tok = tok->next(); + } + + return tok && offset < 0; +} + +// TODO: This is not the correct class for simplifyCalculations(), so it +// should be moved away. +bool TemplateSimplifier::simplifyCalculations(Token* frontToken, const Token *backToken, bool isTemplate) +{ + bool ret = false; + const bool bounded = frontToken || backToken; + if (!frontToken) { + frontToken = mTokenList.front(); + } + for (Token *tok = frontToken; tok && tok != backToken; tok = tok->next()) { + // Remove parentheses around variable.. + // keep parentheses here: dynamic_cast(p); + // keep parentheses here: A operator * (int); + // keep parentheses here: int ( * ( * f ) ( ... ) ) (int) ; + // keep parentheses here: int ( * * ( * compilerHookVector ) (void) ) ( ) ; + // keep parentheses here: operator new [] (size_t); + // keep parentheses here: Functor()(a ... ) + // keep parentheses here: ) ( var ) ; + if (validTokenEnd(bounded, tok, backToken, 4) && + (Token::Match(tok->next(), "( %name% ) ;|)|,|]") || + (Token::Match(tok->next(), "( %name% ) %cop%") && + (tok->tokAt(2)->varId()>0 || + !Token::Match(tok->tokAt(4), "[*&+-~]")))) && + !tok->isName() && + tok->str() != ">" && + tok->str() != ")" && + tok->str() != "]") { + tok->deleteNext(); + tok = tok->next(); + tok->deleteNext(); + ret = true; + } + + if (validTokenEnd(bounded, tok, backToken, 3) && + Token::Match(tok->previous(), "(|&&|%oror% %char% %comp% %num% &&|%oror%|)")) { + tok->str(std::to_string(MathLib::toBigNumber(tok->str()))); + } + + if (validTokenEnd(bounded, tok, backToken, 5) && + Token::Match(tok, "decltype ( %type% { } )")) { + tok->deleteThis(); + tok->deleteThis(); + tok->deleteNext(); + tok->deleteNext(); + tok->deleteNext(); + ret = true; + } + + if (validTokenEnd(bounded, tok, backToken, 3) && + Token::Match(tok, "decltype ( %bool%|%num% )")) { + tok->deleteThis(); + tok->deleteThis(); + if (tok->isBoolean()) + tok->str("bool"); + else if (MathLib::isFloat(tok->str())) { + // MathLib::getSuffix doesn't work for floating point numbers + const char suffix = tok->str().back(); + if (suffix == 'f' || suffix == 'F') + tok->str("float"); + else if (suffix == 'l' || suffix == 'L') { + tok->str("double"); + tok->isLong(true); + } else + tok->str("double"); + } else if (MathLib::isInt(tok->str())) { + std::string suffix = MathLib::getSuffix(tok->str()); + if (suffix.find("LL") != std::string::npos) { + tok->str("long"); + tok->isLong(true); + } else if (suffix.find('L') != std::string::npos) + tok->str("long"); + else + tok->str("int"); + tok->isUnsigned(suffix.find('U') != std::string::npos); + } + tok->deleteNext(); + ret = true; + } + + if (validTokenEnd(bounded, tok, backToken, 2) && + (Token::Match(tok, "char|short|int|long { }") || + Token::Match(tok, "char|short|int|long ( )"))) { + tok->str("0"); // FIXME add type suffix + tok->isSigned(false); + tok->isUnsigned(false); + tok->isLong(false); + tok->deleteNext(); + tok->deleteNext(); + ret = true; + } + + if (tok && tok->isNumber()) { + if (validTokenEnd(bounded, tok, backToken, 2) && + simplifyNumericCalculations(tok, isTemplate)) { + ret = true; + Token *prev = tok->tokAt(-2); + while (validTokenStart(bounded, tok, frontToken, -2) && + prev && simplifyNumericCalculations(prev, isTemplate)) { + tok = prev; + prev = prev->tokAt(-2); + } + } + + // Remove redundant conditions (0&&x) (1||x) + if (validTokenStart(bounded, tok, frontToken, -1) && + validTokenEnd(bounded, tok, backToken, 1) && + (Token::Match(tok->previous(), "[(=,] 0 &&") || + Token::Match(tok->previous(), "[(=,] 1 %oror%"))) { + unsigned int par = 0; + const Token *tok2 = tok; + const bool andAnd = (tok->next()->str() == "&&"); + for (; tok2; tok2 = tok2->next()) { + if (tok2->str() == "(" || tok2->str() == "[") + ++par; + else if (tok2->str() == ")" || tok2->str() == "]") { + if (par == 0) + break; + --par; + } else if (par == 0 && isLowerThanLogicalAnd(tok2) && (andAnd || tok2->str() != "||")) + break; + } + if (tok2) { + eraseTokens(tok, tok2); + ret = true; + } + continue; + } + + if (tok->str() == "0" && validTokenStart(bounded, tok, frontToken, -1)) { + if (validTokenEnd(bounded, tok, backToken, 1) && + ((Token::Match(tok->previous(), "[+-] 0 %cop%|;") && isLowerThanMulDiv(tok->next())) || + (Token::Match(tok->previous(), "%or% 0 %cop%|;") && isLowerThanXor(tok->next())))) { + tok = tok->previous(); + if (Token::Match(tok->tokAt(-4), "[;{}] %name% = %name% [+-|] 0 ;") && + tok->strAt(-3) == tok->previous()->str()) { + tok = tok->tokAt(-4); + tok->deleteNext(5); + } else { + tok = tok->previous(); + tok->deleteNext(2); + } + ret = true; + } else if (validTokenEnd(bounded, tok, backToken, 1) && + (Token::Match(tok->previous(), "[=([,] 0 [+|]") || + Token::Match(tok->previous(), "return|case 0 [+|]"))) { + tok = tok->previous(); + tok->deleteNext(2); + ret = true; + } else if ((((Token::Match(tok->previous(), "[=[(,] 0 * %name%|%num% ,|]|)|;|=|%cop%") || + Token::Match(tok->previous(), "return|case 0 *|&& %name%|%num% ,|:|;|=|%cop%")) && + validTokenEnd(bounded, tok, backToken, 3)) || + (((Token::Match(tok->previous(), "[=[(,] 0 * (") || + Token::Match(tok->previous(), "return|case 0 *|&& (")) && + validTokenEnd(bounded, tok, backToken, 2))))) { + tok->deleteNext(); + if (tok->next()->str() == "(") + eraseTokens(tok, tok->next()->link()); + tok->deleteNext(); + ret = true; + } else if (validTokenEnd(bounded, tok, backToken, 4) && + (Token::Match(tok->previous(), "[=[(,] 0 && *|& %any% ,|]|)|;|=|%cop%") || + Token::Match(tok->previous(), "return|case 0 && *|& %any% ,|:|;|=|%cop%"))) { + tok->deleteNext(); + tok->deleteNext(); + if (tok->next()->str() == "(") + eraseTokens(tok, tok->next()->link()); + tok->deleteNext(); + ret = true; + } + } + + if (tok->str() == "1" && validTokenStart(bounded, tok, frontToken, -1)) { + if (validTokenEnd(bounded, tok, backToken, 3) && + (Token::Match(tok->previous(), "[=[(,] 1 %oror% %any% ,|]|)|;|=|%cop%") || + Token::Match(tok->previous(), "return|case 1 %oror% %any% ,|:|;|=|%cop%"))) { + tok->deleteNext(); + if (tok->next()->str() == "(") + eraseTokens(tok, tok->next()->link()); + tok->deleteNext(); + ret = true; + } else if (validTokenEnd(bounded, tok, backToken, 4) && + (Token::Match(tok->previous(), "[=[(,] 1 %oror% *|& %any% ,|]|)|;|=|%cop%") || + Token::Match(tok->previous(), "return|case 1 %oror% *|& %any% ,|:|;|=|%cop%"))) { + tok->deleteNext(); + tok->deleteNext(); + if (tok->next()->str() == "(") + eraseTokens(tok, tok->next()->link()); + tok->deleteNext(); + ret = true; + } + } + + if ((Token::Match(tok->tokAt(-2), "%any% * 1") && + validTokenStart(bounded, tok, frontToken, -2)) || + (Token::Match(tok->previous(), "%any% 1 *") && + validTokenStart(bounded, tok, frontToken, -1))) { + tok = tok->previous(); + if (tok->str() == "*") + tok = tok->previous(); + tok->deleteNext(2); + ret = true; + } + + // Remove parentheses around number.. + if (validTokenStart(bounded, tok, frontToken, -2) && + Token::Match(tok->tokAt(-2), "%op%|< ( %num% )") && + tok->strAt(-2) != ">") { + tok = tok->previous(); + tok->deleteThis(); + tok->deleteNext(); + ret = true; + } + + if (validTokenStart(bounded, tok, frontToken, -1) && + validTokenEnd(bounded, tok, backToken, 1) && + (Token::Match(tok->previous(), "( 0 [|+]") || + Token::Match(tok->previous(), "[|+-] 0 )"))) { + tok = tok->previous(); + if (Token::Match(tok, "[|+-]")) + tok = tok->previous(); + tok->deleteNext(2); + ret = true; + } + + if (validTokenEnd(bounded, tok, backToken, 2) && + Token::Match(tok, "%num% %comp% %num%") && + MathLib::isInt(tok->str()) && + MathLib::isInt(tok->strAt(2))) { + if (validTokenStart(bounded, tok, frontToken, -1) && + Token::Match(tok->previous(), "(|&&|%oror%") && + Token::Match(tok->tokAt(3), ")|&&|%oror%|?")) { + const MathLib::bigint op1(MathLib::toBigNumber(tok->str())); + const std::string &cmp(tok->next()->str()); + const MathLib::bigint op2(MathLib::toBigNumber(tok->strAt(2))); + + std::string result; + + if (cmp == "==") + result = (op1 == op2) ? "1" : "0"; + else if (cmp == "!=") + result = (op1 != op2) ? "1" : "0"; + else if (cmp == "<=") + result = (op1 <= op2) ? "1" : "0"; + else if (cmp == ">=") + result = (op1 >= op2) ? "1" : "0"; + else if (cmp == "<") + result = (op1 < op2) ? "1" : "0"; + else + result = (op1 > op2) ? "1" : "0"; + + tok->str(result); + tok->deleteNext(2); + ret = true; + tok = tok->previous(); + } + } + } + } + return ret; +} + +void TemplateSimplifier::getTemplateParametersInDeclaration( + const Token * tok, + std::vector & typeParametersInDeclaration) +{ + assert(tok->strAt(-1) == "<"); + + typeParametersInDeclaration.clear(); + const Token *end = tok->previous()->findClosingBracket(); + bool inDefaultValue = false; + for (; tok && tok!= end; tok = tok->next()) { + if (Token::simpleMatch(tok, "template <")) { + const Token *closing = tok->next()->findClosingBracket(); + if (closing) + tok = closing->next(); + } else if (tok->link() && Token::Match(tok, "{|(|[")) + tok = tok->link(); + else if (Token::Match(tok, "%name% ,|>|=")) { + if (!inDefaultValue) { + typeParametersInDeclaration.push_back(tok); + if (tok->strAt(1) == "=") + inDefaultValue = true; + } + } else if (inDefaultValue) { + if (tok->str() == ",") + inDefaultValue = false; + else if (tok->str() == "<") { + const Token *closing = tok->findClosingBracket(); + if (closing) + tok = closing; + } + } + } +} + +bool TemplateSimplifier::matchSpecialization( + const Token *templateDeclarationNameToken, + const Token *templateInstantiationNameToken, + const std::list & specializations) +{ + // Is there a matching specialization? + for (std::list::const_iterator it = specializations.cbegin(); it != specializations.cend(); ++it) { + if (!Token::Match(*it, "%name% <")) + continue; + const Token *startToken = (*it); + while (startToken->previous() && !Token::Match(startToken->previous(), "[;{}]")) + startToken = startToken->previous(); + if (!Token::simpleMatch(startToken, "template <")) + continue; + // cppcheck-suppress shadowFunction - TODO: fix this + std::vector templateParameters; + getTemplateParametersInDeclaration(startToken->tokAt(2), templateParameters); + + const Token *instToken = templateInstantiationNameToken->tokAt(2); + const Token *declToken = (*it)->tokAt(2); + const Token * const endToken = (*it)->next()->findClosingBracket(); + if (!endToken) + continue; + while (declToken != endToken) { + if (declToken->str() != instToken->str() || + declToken->isSigned() != instToken->isSigned() || + declToken->isUnsigned() != instToken->isUnsigned() || + declToken->isLong() != instToken->isLong()) { + int nr = 0; + while (nr < templateParameters.size() && templateParameters[nr]->str() != declToken->str()) + ++nr; + + if (nr == templateParameters.size()) + break; + } + declToken = declToken->next(); + instToken = instToken->next(); + } + + if (declToken && instToken && declToken == endToken && instToken->str() == ">") { + // specialization matches. + return templateDeclarationNameToken == *it; + } + } + + // No specialization matches. Return true if the declaration is not a specialization. + return Token::Match(templateDeclarationNameToken, "%name% !!<") && + (templateDeclarationNameToken->str().find('<') == std::string::npos); +} + +std::string TemplateSimplifier::getNewName( + Token *tok2, + std::list &typeStringsUsedInTemplateInstantiation) +{ + std::string typeForNewName; + unsigned int indentlevel = 0; + const Token * endToken = tok2->next()->findClosingBracket(); + for (Token *tok3 = tok2->tokAt(2); tok3 != endToken && (indentlevel > 0 || tok3->str() != ">"); tok3 = tok3->next()) { + // #2721 - unhandled [ => bail out + if (tok3->str() == "[" && !Token::Match(tok3->next(), "%num%| ]")) { + typeForNewName.clear(); + break; + } + if (!tok3->next()) { + typeForNewName.clear(); + break; + } + if (Token::Match(tok3->tokAt(-2), "<|,|:: %name% <") && (tok3->strAt(1) == ">" || templateParameters(tok3))) + ++indentlevel; + else if (indentlevel > 0 && Token::Match(tok3, "> ,|>|::")) + --indentlevel; + else if (indentlevel == 0 && Token::Match(tok3->previous(), "[<,]")) { + mTypesUsedInTemplateInstantiation.emplace_back(tok3, ""); + } + if (Token::Match(tok3, "(|[")) + ++indentlevel; + else if (Token::Match(tok3, ")|]")) + --indentlevel; + const bool constconst = tok3->str() == "const" && tok3->strAt(1) == "const"; + if (!constconst) { + if (tok3->isUnsigned()) + typeStringsUsedInTemplateInstantiation.emplace_back("unsigned"); + else if (tok3->isSigned()) + typeStringsUsedInTemplateInstantiation.emplace_back("signed"); + if (tok3->isLong()) + typeStringsUsedInTemplateInstantiation.emplace_back("long"); + typeStringsUsedInTemplateInstantiation.push_back(tok3->str()); + } + // add additional type information + if (!constconst && !Token::Match(tok3, "class|struct|enum")) { + if (!typeForNewName.empty()) + typeForNewName += ' '; + if (tok3->isUnsigned()) + typeForNewName += "unsigned "; + else if (tok3->isSigned()) + typeForNewName += "signed "; + if (tok3->isLong()) { + typeForNewName += "long "; + } + typeForNewName += tok3->str(); + } + } + + return typeForNewName; +} + +bool TemplateSimplifier::simplifyTemplateInstantiations( + const TokenAndName &templateDeclaration, + const std::list &specializations, + const std::time_t maxtime, + std::set &expandedtemplates) +{ + // this variable is not used at the moment. The intention was to + // allow continuous instantiations until all templates has been expanded + //bool done = false; + + // Contains tokens such as "T" + std::vector typeParametersInDeclaration; + getTemplateParametersInDeclaration(templateDeclaration.token()->tokAt(2), typeParametersInDeclaration); + const bool printDebug = mSettings.debugwarnings; + const bool specialized = templateDeclaration.isSpecialization(); + const bool isfunc = templateDeclaration.isFunction(); + const bool isVar = templateDeclaration.isVariable(); + + // locate template usage.. + std::string::size_type numberOfTemplateInstantiations = mTemplateInstantiations.size(); + unsigned int recursiveCount = 0; + + bool instantiated = false; + + for (const TokenAndName &instantiation : mTemplateInstantiations) { + // skip deleted instantiations + if (!instantiation.token()) + continue; + if (numberOfTemplateInstantiations != mTemplateInstantiations.size()) { + numberOfTemplateInstantiations = mTemplateInstantiations.size(); + ++recursiveCount; + if (recursiveCount > mSettings.maxTemplateRecursion) { + if (mSettings.severity.isEnabled(Severity::information)) { + std::list typeStringsUsedInTemplateInstantiation; + const std::string typeForNewName = templateDeclaration.name() + "<" + getNewName(instantiation.token(), typeStringsUsedInTemplateInstantiation) + ">"; + + const std::list callstack(1, instantiation.token()); + const ErrorMessage errmsg(callstack, + &mTokenizer.list, + Severity::information, + "templateRecursion", + "TemplateSimplifier: max template recursion (" + + std::to_string(mSettings.maxTemplateRecursion) + + ") reached for template '"+typeForNewName+"'. You might want to limit Cppcheck recursion.", + Certainty::normal); + mErrorLogger.reportErr(errmsg); + } + + // bail out.. + break; + } + } + + // already simplified + if (!Token::Match(instantiation.token(), "%name% <")) + continue; + + if (!((instantiation.fullName() == templateDeclaration.fullName()) || + (instantiation.name() == templateDeclaration.name() && + instantiation.fullName() == templateDeclaration.scope()))) { + // FIXME: fallback to not matching scopes until type deduction works + + // names must match + if (instantiation.name() != templateDeclaration.name()) + continue; + + // scopes must match when present + if (!instantiation.scope().empty() && !templateDeclaration.scope().empty()) + continue; + } + + // make sure constructors and destructors don't match each other + if (templateDeclaration.nameToken()->strAt(-1) == "~" && instantiation.token()->strAt(-1) != "~") + continue; + + // template families should match + if (!instantiation.isFunction() && templateDeclaration.isFunction()) { + // there are exceptions + if (!Token::simpleMatch(instantiation.token()->tokAt(-2), "decltype (")) + continue; + } + + if (templateDeclaration.isFunction() && instantiation.isFunction()) { + std::vector declFuncArgs; + getFunctionArguments(templateDeclaration.nameToken(), declFuncArgs); + std::vector instFuncParams; + getFunctionArguments(instantiation.token(), instFuncParams); + + if (declFuncArgs.size() != instFuncParams.size()) { + // check for default arguments + const Token* tok = templateDeclaration.nameToken()->tokAt(2); + const Token* end = templateDeclaration.nameToken()->linkAt(1); + size_t count = 0; + for (; tok != end; tok = tok->next()) { + if (tok->str() == "=") + count++; + } + + if (instFuncParams.size() < (declFuncArgs.size() - count) || instFuncParams.size() > declFuncArgs.size()) + continue; + } + } + + // A global function can't be called through a pointer. + if (templateDeclaration.isFunction() && templateDeclaration.scope().empty() && + (instantiation.token()->strAt(-1) == "." || + Token::simpleMatch(instantiation.token()->tokAt(-2), ". template"))) + continue; + + if (!matchSpecialization(templateDeclaration.nameToken(), instantiation.token(), specializations)) + continue; + + Token * const tok2 = instantiation.token(); + if (!mTokenList.getFiles().empty()) + mErrorLogger.reportProgress(mTokenList.getFiles()[0], "TemplateSimplifier::simplifyTemplateInstantiations()", tok2->progressValue()); + + if (maxtime > 0 && std::time(nullptr) > maxtime) { + if (mSettings.debugwarnings) { + ErrorMessage::FileLocation loc(mTokenList.getFiles()[0], 0, 0); + ErrorMessage errmsg({std::move(loc)}, + emptyString, + Severity::debug, + "Template instantiation maximum time exceeded", + "templateMaxTime", + Certainty::normal); + mErrorLogger.reportErr(errmsg); + } + return false; + } + + assert(mTokenList.validateToken(tok2)); // that assertion fails on examples from #6021 + + const Token *startToken = tok2; + while (Token::Match(startToken->tokAt(-2), ">|%name% :: %name%")) { + if (startToken->strAt(-2) == ">") { + const Token * tok3 = startToken->tokAt(-2)->findOpeningBracket(); + if (tok3) + startToken = tok3->previous(); + else + break; + } else + startToken = startToken->tokAt(-2); + } + + if (Token::Match(startToken->previous(), ";|{|}|=|const") && + (!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), templateDeclaration.isVariadic(), isfunc ? "(" : isVar ? ";|%op%|(" : "*|&|::| %name%"))) + continue; + + // New type.. + mTypesUsedInTemplateInstantiation.clear(); + std::list typeStringsUsedInTemplateInstantiation; + std::string typeForNewName = getNewName(tok2, typeStringsUsedInTemplateInstantiation); + + if ((typeForNewName.empty() && !templateDeclaration.isVariadic()) || + (!typeParametersInDeclaration.empty() && !instantiateMatch(tok2, typeParametersInDeclaration.size(), templateDeclaration.isVariadic(), nullptr))) { + if (printDebug) { + std::list callstack(1, tok2); + mErrorLogger.reportErr(ErrorMessage(callstack, &mTokenList, Severity::debug, "templateInstantiation", + "Failed to instantiate template \"" + instantiation.name() + "\". The checking continues anyway.", Certainty::normal)); + } + if (typeForNewName.empty()) + continue; + break; + } + + // New classname/funcname.. + const std::string newName(templateDeclaration.name() + " < " + typeForNewName + " >"); + const std::string newFullName(templateDeclaration.scope() + (templateDeclaration.scope().empty() ? "" : " :: ") + newName); + + if (expandedtemplates.insert(newFullName).second) { + expandTemplate(templateDeclaration, instantiation, typeParametersInDeclaration, newName, !specialized && !isVar); + instantiated = true; + mChanged = true; + } + + // Replace all these template usages.. + replaceTemplateUsage(instantiation, typeStringsUsedInTemplateInstantiation, newName); + } + + // process uninstantiated templates + // TODO: remove the specialized check and handle all uninstantiated templates someday. + if (!instantiated && specialized) { + auto * tok2 = const_cast(templateDeclaration.nameToken()); + if (!mTokenList.getFiles().empty()) + mErrorLogger.reportProgress(mTokenList.getFiles()[0], "TemplateSimplifier::simplifyTemplateInstantiations()", tok2->progressValue()); + + if (maxtime > 0 && std::time(nullptr) > maxtime) { + if (mSettings.debugwarnings) { + ErrorMessage::FileLocation loc(mTokenList.getFiles()[0], 0, 0); + ErrorMessage errmsg({std::move(loc)}, + emptyString, + Severity::debug, + "Template instantiation maximum time exceeded", + "templateMaxTime", + Certainty::normal); + mErrorLogger.reportErr(errmsg); + } + return false; + } + + assert(mTokenList.validateToken(tok2)); // that assertion fails on examples from #6021 + + Token *startToken = tok2; + while (Token::Match(startToken->tokAt(-2), ">|%name% :: %name%")) { + if (startToken->strAt(-2) == ">") { + Token * tok3 = startToken->tokAt(-2)->findOpeningBracket(); + if (tok3) + startToken = tok3->previous(); + else + break; + } else + startToken = startToken->tokAt(-2); + } + + // TODO: re-enable when specialized check is removed + // if (Token::Match(startToken->previous(), ";|{|}|=|const") && + // (!specialized && !instantiateMatch(tok2, typeParametersInDeclaration.size(), isfunc ? "(" : isVar ? ";|%op%|(" : "*|&|::| %name%"))) + // return false; + + // already simplified + if (!Token::Match(tok2, "%name% <")) + return false; + + if (!matchSpecialization(templateDeclaration.nameToken(), tok2, specializations)) + return false; + + // New type.. + mTypesUsedInTemplateInstantiation.clear(); + std::list typeStringsUsedInTemplateInstantiation; + std::string typeForNewName = getNewName(tok2, typeStringsUsedInTemplateInstantiation); + + if (typeForNewName.empty()) { + if (printDebug) { + std::list callstack(1, tok2); + mErrorLogger.reportErr(ErrorMessage(callstack, &mTokenList, Severity::debug, "templateInstantiation", + "Failed to instantiate template \"" + templateDeclaration.name() + "\". The checking continues anyway.", Certainty::normal)); + } + return false; + } + + // New classname/funcname.. + const std::string newName(templateDeclaration.name() + " < " + typeForNewName + " >"); + const std::string newFullName(templateDeclaration.scope() + (templateDeclaration.scope().empty() ? "" : " :: ") + newName); + + if (expandedtemplates.insert(newFullName).second) { + expandTemplate(templateDeclaration, templateDeclaration, typeParametersInDeclaration, newName, !specialized && !isVar); + instantiated = true; + mChanged = true; + } + + // Replace all these template usages.. + replaceTemplateUsage(templateDeclaration, typeStringsUsedInTemplateInstantiation, newName); + } + + // Template has been instantiated .. then remove the template declaration + return instantiated; +} + +static bool matchTemplateParameters(const Token *nameTok, const std::list &strings) +{ + std::list::const_iterator it = strings.cbegin(); + const Token *tok = nameTok->tokAt(2); + const Token *end = nameTok->next()->findClosingBracket(); + if (!end) + return false; + while (tok && tok != end && it != strings.cend()) { + if (tok->isUnsigned()) { + if (*it != "unsigned") + return false; + + ++it; + if (it == strings.cend()) + return false; + } else if (tok->isSigned()) { + if (*it != "signed") + return false; + + ++it; + if (it == strings.cend()) + return false; + } + if (tok->isLong()) { + if (*it != "long") + return false; + + ++it; + if (it == strings.cend()) + return false; + } + if (*it != tok->str()) + return false; + tok = tok->next(); + ++it; + } + return it == strings.cend() && tok && tok->str() == ">"; +} + +void TemplateSimplifier::replaceTemplateUsage( + const TokenAndName &instantiation, + const std::list &typeStringsUsedInTemplateInstantiation, + const std::string &newName) +{ + std::list> removeTokens; + for (Token *nameTok = mTokenList.front(); nameTok; nameTok = nameTok->next()) { + if (!Token::Match(nameTok, "%name% <") || + Token::Match(nameTok, "template|const_cast|dynamic_cast|reinterpret_cast|static_cast")) + continue; + + std::set* pointers = nameTok->templateSimplifierPointers(); + + // check if instantiation matches token instantiation from pointer + if (pointers && !pointers->empty()) { + // check full name + if (instantiation.fullName() != (*pointers->begin())->fullName()) { + // FIXME: fallback to just matching name + if (nameTok->str() != instantiation.name()) + continue; + } + } + // no pointer available look at tokens directly + else { + // FIXME: fallback to just matching name + if (nameTok->str() != instantiation.name()) + continue; + } + + if (!matchTemplateParameters(nameTok, typeStringsUsedInTemplateInstantiation)) + continue; + + Token *tok2 = nameTok->next()->findClosingBracket(); + + if (!tok2) + break; + + const Token * const nameTok1 = nameTok; + nameTok->str(newName); + + // matching template usage => replace tokens.. + // Foo < int > => Foo + for (const Token *tok = nameTok1->next(); tok != tok2; tok = tok->next()) { + if (tok->isName() && tok->templateSimplifierPointers() && !tok->templateSimplifierPointers()->empty()) { + std::list::iterator ti; + for (ti = mTemplateInstantiations.begin(); ti != mTemplateInstantiations.end();) { + if (ti->token() == tok) { + ti = mTemplateInstantiations.erase(ti); + break; + } + ++ti; + } + } + } + // Fix crash in #9007 + if (Token::simpleMatch(nameTok->previous(), ">")) + mTemplateNamePos.erase(nameTok->previous()); + removeTokens.emplace_back(nameTok, tok2->next()); + + nameTok = tok2; + } + while (!removeTokens.empty()) { + eraseTokens(removeTokens.back().first, removeTokens.back().second); + removeTokens.pop_back(); + } +} + +static bool specMatch( + const TemplateSimplifier::TokenAndName &spec, + const TemplateSimplifier::TokenAndName &decl) +{ + // make sure decl is really a declaration + if (decl.isPartialSpecialization() || decl.isSpecialization() || decl.isAlias() || decl.isFriend()) + return false; + + if (!spec.isSameFamily(decl)) + return false; + + // make sure the scopes and names match + if (spec.fullName() == decl.fullName()) { + if (spec.isFunction()) { + std::vector specArgs; + std::vector declArgs; + getFunctionArguments(spec.nameToken(), specArgs); + getFunctionArguments(decl.nameToken(), declArgs); + + if (specArgs.size() == declArgs.size()) { + // @todo make sure function parameters also match + return true; + } + } else + return true; + } + + return false; +} + +void TemplateSimplifier::getSpecializations() +{ + // try to locate a matching declaration for each user defined specialization + for (const auto& spec : mTemplateDeclarations) { + if (spec.isSpecialization()) { + auto it = std::find_if(mTemplateDeclarations.cbegin(), mTemplateDeclarations.cend(), [&](const TokenAndName& decl) { + return specMatch(spec, decl); + }); + if (it != mTemplateDeclarations.cend()) + mTemplateSpecializationMap[spec.token()] = it->token(); + else { + it = std::find_if(mTemplateForwardDeclarations.cbegin(), mTemplateForwardDeclarations.cend(), [&](const TokenAndName& decl) { + return specMatch(spec, decl); + }); + if (it != mTemplateForwardDeclarations.cend()) + mTemplateSpecializationMap[spec.token()] = it->token(); + } + } + } +} + +void TemplateSimplifier::getPartialSpecializations() +{ + // try to locate a matching declaration for each user defined partial specialization + for (const auto& spec : mTemplateDeclarations) { + if (spec.isPartialSpecialization()) { + auto it = std::find_if(mTemplateDeclarations.cbegin(), mTemplateDeclarations.cend(), [&](const TokenAndName& decl) { + return specMatch(spec, decl); + }); + if (it != mTemplateDeclarations.cend()) + mTemplatePartialSpecializationMap[spec.token()] = it->token(); + else { + it = std::find_if(mTemplateForwardDeclarations.cbegin(), mTemplateForwardDeclarations.cend(), [&](const TokenAndName& decl) { + return specMatch(spec, decl); + }); + if (it != mTemplateForwardDeclarations.cend()) + mTemplatePartialSpecializationMap[spec.token()] = it->token(); + } + } + } +} + +void TemplateSimplifier::fixForwardDeclaredDefaultArgumentValues() +{ + // try to locate a matching declaration for each forward declaration + for (const auto & forwardDecl : mTemplateForwardDeclarations) { + std::vector params1; + + getTemplateParametersInDeclaration(forwardDecl.token()->tokAt(2), params1); + + for (auto & decl : mTemplateDeclarations) { + // skip partializations, type aliases and friends + if (decl.isPartialSpecialization() || decl.isAlias() || decl.isFriend()) + continue; + + std::vector params2; + + getTemplateParametersInDeclaration(decl.token()->tokAt(2), params2); + + // make sure the number of arguments match + if (params1.size() == params2.size()) { + // make sure the scopes and names match + if (forwardDecl.fullName() == decl.fullName()) { + // save forward declaration for lookup later + if ((decl.nameToken()->strAt(1) == "(" && forwardDecl.nameToken()->strAt(1) == "(") || + (decl.nameToken()->strAt(1) == "{" && forwardDecl.nameToken()->strAt(1) == ";")) { + mTemplateForwardDeclarationsMap[decl.token()] = forwardDecl.token(); + } + + for (size_t k = 0; k < params1.size(); k++) { + // copy default value to declaration if not present + if (params1[k]->strAt(1) == "=" && params2[k]->strAt(1) != "=") { + int level = 0; + const Token *end = params1[k]->next(); + while (end && !(level == 0 && Token::Match(end, ",|>"))) { + if (Token::Match(end, "{|(|<")) + level++; + else if (Token::Match(end, "}|)|>")) + level--; + end = end->next(); + } + if (end) + TokenList::copyTokens(const_cast(params2[k]), params1[k]->next(), end->previous()); + } + } + + // update parameter end pointer + decl.paramEnd(decl.token()->next()->findClosingBracket()); + } + } + } + } +} + +void TemplateSimplifier::printOut(const TokenAndName &tokenAndName, const std::string &indent) const +{ + std::cout << indent << "token: "; + if (tokenAndName.token()) + std::cout << "\"" << tokenAndName.token()->str() << "\" " << mTokenList.fileLine(tokenAndName.token()); + else + std::cout << "nullptr"; + std::cout << std::endl; + std::cout << indent << "scope: \"" << tokenAndName.scope() << "\"" << std::endl; + std::cout << indent << "name: \"" << tokenAndName.name() << "\"" << std::endl; + std::cout << indent << "fullName: \"" << tokenAndName.fullName() << "\"" << std::endl; + std::cout << indent << "nameToken: "; + if (tokenAndName.nameToken()) + std::cout << "\"" << tokenAndName.nameToken()->str() << "\" " << mTokenList.fileLine(tokenAndName.nameToken()); + else + std::cout << "nullptr"; + std::cout << std::endl; + std::cout << indent << "paramEnd: "; + if (tokenAndName.paramEnd()) + std::cout << "\"" << tokenAndName.paramEnd()->str() << "\" " << mTokenList.fileLine(tokenAndName.paramEnd()); + else + std::cout << "nullptr"; + std::cout << std::endl; + std::cout << indent << "flags: "; + if (tokenAndName.isClass()) + std::cout << " isClass"; + if (tokenAndName.isFunction()) + std::cout << " isFunction"; + if (tokenAndName.isVariable()) + std::cout << " isVariable"; + if (tokenAndName.isAlias()) + std::cout << " isAlias"; + if (tokenAndName.isSpecialization()) + std::cout << " isSpecialization"; + if (tokenAndName.isPartialSpecialization()) + std::cout << " isPartialSpecialization"; + if (tokenAndName.isForwardDeclaration()) + std::cout << " isForwardDeclaration"; + if (tokenAndName.isVariadic()) + std::cout << " isVariadic"; + if (tokenAndName.isFriend()) + std::cout << " isFriend"; + std::cout << std::endl; + if (tokenAndName.token() && !tokenAndName.paramEnd() && tokenAndName.token()->strAt(1) == "<") { + const Token *end = tokenAndName.token()->next()->findClosingBracket(); + if (end) { + const Token *start = tokenAndName.token()->next(); + std::cout << indent << "type: "; + while (start && start != end) { + if (start->isUnsigned()) + std::cout << "unsigned"; + else if (start->isSigned()) + std::cout << "signed"; + if (start->isLong()) + std::cout << "long"; + std::cout << start->str(); + start = start->next(); + } + std::cout << end->str() << std::endl; + } + } else if (tokenAndName.isAlias() && tokenAndName.paramEnd()) { + if (tokenAndName.aliasStartToken()) { + std::cout << indent << "aliasStartToken: \"" << tokenAndName.aliasStartToken()->str() << "\" " + << mTokenList.fileLine(tokenAndName.aliasStartToken()) << std::endl; + } + if (tokenAndName.aliasEndToken()) { + std::cout << indent << "aliasEndToken: \"" << tokenAndName.aliasEndToken()->str() << "\" " + << mTokenList.fileLine(tokenAndName.aliasEndToken()) << std::endl; + } + } +} + +void TemplateSimplifier::printOut(const std::string & text) const +{ + std::cout << std::endl; + std::cout << text << std::endl; + std::cout << std::endl; + std::cout << "mTemplateDeclarations: " << mTemplateDeclarations.size() << std::endl; + int count = 0; + for (const auto & decl : mTemplateDeclarations) { + std::cout << "mTemplateDeclarations[" << count++ << "]:" << std::endl; + printOut(decl); + } + std::cout << "mTemplateForwardDeclarations: " << mTemplateForwardDeclarations.size() << std::endl; + count = 0; + for (const auto & decl : mTemplateForwardDeclarations) { + std::cout << "mTemplateForwardDeclarations[" << count++ << "]:" << std::endl; + printOut(decl); + } + std::cout << "mTemplateForwardDeclarationsMap: " << mTemplateForwardDeclarationsMap.size() << std::endl; + unsigned int mapIndex = 0; + for (const auto & mapItem : mTemplateForwardDeclarationsMap) { + unsigned int declIndex = 0; + for (const auto & decl : mTemplateDeclarations) { + if (mapItem.first == decl.token()) { + unsigned int forwardIndex = 0; + for (const auto & forwardDecl : mTemplateForwardDeclarations) { + if (mapItem.second == forwardDecl.token()) { + std::cout << "mTemplateForwardDeclarationsMap[" << mapIndex << "]:" << std::endl; + std::cout << " mTemplateDeclarations[" << declIndex + << "] => mTemplateForwardDeclarations[" << forwardIndex << "]" << std::endl; + break; + } + forwardIndex++; + } + break; + } + declIndex++; + } + mapIndex++; + } + std::cout << "mTemplateSpecializationMap: " << mTemplateSpecializationMap.size() << std::endl; + for (const auto & mapItem : mTemplateSpecializationMap) { + unsigned int decl1Index = 0; + for (const auto & decl1 : mTemplateDeclarations) { + if (decl1.isSpecialization() && mapItem.first == decl1.token()) { + bool found = false; + unsigned int decl2Index = 0; + for (const auto & decl2 : mTemplateDeclarations) { + if (mapItem.second == decl2.token()) { + std::cout << "mTemplateSpecializationMap[" << mapIndex << "]:" << std::endl; + std::cout << " mTemplateDeclarations[" << decl1Index + << "] => mTemplateDeclarations[" << decl2Index << "]" << std::endl; + found = true; + break; + } + decl2Index++; + } + if (!found) { + decl2Index = 0; + for (const auto & decl2 : mTemplateForwardDeclarations) { + if (mapItem.second == decl2.token()) { + std::cout << "mTemplateSpecializationMap[" << mapIndex << "]:" << std::endl; + std::cout << " mTemplateDeclarations[" << decl1Index + << "] => mTemplateForwardDeclarations[" << decl2Index << "]" << std::endl; + break; + } + decl2Index++; + } + } + break; + } + decl1Index++; + } + mapIndex++; + } + std::cout << "mTemplatePartialSpecializationMap: " << mTemplatePartialSpecializationMap.size() << std::endl; + for (const auto & mapItem : mTemplatePartialSpecializationMap) { + unsigned int decl1Index = 0; + for (const auto & decl1 : mTemplateDeclarations) { + if (mapItem.first == decl1.token()) { + bool found = false; + unsigned int decl2Index = 0; + for (const auto & decl2 : mTemplateDeclarations) { + if (mapItem.second == decl2.token()) { + std::cout << "mTemplatePartialSpecializationMap[" << mapIndex << "]:" << std::endl; + std::cout << " mTemplateDeclarations[" << decl1Index + << "] => mTemplateDeclarations[" << decl2Index << "]" << std::endl; + found = true; + break; + } + decl2Index++; + } + if (!found) { + decl2Index = 0; + for (const auto & decl2 : mTemplateForwardDeclarations) { + if (mapItem.second == decl2.token()) { + std::cout << "mTemplatePartialSpecializationMap[" << mapIndex << "]:" << std::endl; + std::cout << " mTemplateDeclarations[" << decl1Index + << "] => mTemplateForwardDeclarations[" << decl2Index << "]" << std::endl; + break; + } + decl2Index++; + } + } + break; + } + decl1Index++; + } + mapIndex++; + } + std::cout << "mTemplateInstantiations: " << mTemplateInstantiations.size() << std::endl; + count = 0; + for (const auto & decl : mTemplateInstantiations) { + std::cout << "mTemplateInstantiations[" << count++ << "]:" << std::endl; + printOut(decl); + } +} + +void TemplateSimplifier::simplifyTemplates(const std::time_t maxtime) +{ + // convert "sizeof ..." to "sizeof..." + for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "sizeof ...")) { + tok->str("sizeof..."); + tok->deleteNext(); + } + } + + // Remove "typename" unless used in template arguments or using type alias.. + for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "typename %name%") && !Token::Match(tok->tokAt(-3), "using %name% =")) + tok->deleteThis(); + + if (Token::simpleMatch(tok, "template <")) { + tok = tok->next()->findClosingBracket(); + if (!tok) + break; + } + } + + if (mSettings.standards.cpp >= Standards::CPP20) { + // Remove concepts/requires + // TODO concepts are not removed yet + for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { + if (!Token::Match(tok, ")|>|>> requires %name%|(")) + continue; + const Token* end = skipRequires(tok->next()); + if (end) + Token::eraseTokens(tok, end); + } + + // explicit(bool) + for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "explicit (")) { + const bool isFalse = Token::simpleMatch(tok->tokAt(2), "false )"); + Token::eraseTokens(tok, tok->linkAt(1)->next()); + if (isFalse) + tok->deleteThis(); + } + } + } + + mTokenizer.calculateScopes(); + + unsigned int passCount = 0; + constexpr unsigned int passCountMax = 10; + for (; passCount < passCountMax; ++passCount) { + if (passCount) { + // it may take more than one pass to simplify type aliases + bool usingChanged = false; + while (mTokenizer.simplifyUsing()) + usingChanged = true; + + if (!usingChanged && !mChanged) + break; + + mChanged = usingChanged; + mTemplateDeclarations.clear(); + mTemplateForwardDeclarations.clear(); + mTemplateForwardDeclarationsMap.clear(); + mTemplateSpecializationMap.clear(); + mTemplatePartialSpecializationMap.clear(); + mTemplateInstantiations.clear(); + mInstantiatedTemplates.clear(); + mExplicitInstantiationsToDelete.clear(); + mTemplateNamePos.clear(); + } + + getTemplateDeclarations(); + + if (passCount == 0) { + mDump.clear(); + for (const TokenAndName& t: mTemplateDeclarations) + mDump += t.dump(mTokenizer.list.getFiles()); + for (const TokenAndName& t: mTemplateForwardDeclarations) + mDump += t.dump(mTokenizer.list.getFiles()); + if (!mDump.empty()) + mDump = " \n" + mDump + " \n"; + } + + // Make sure there is something to simplify. + if (mTemplateDeclarations.empty() && mTemplateForwardDeclarations.empty()) + return; + + if (mSettings.debugtemplate && mSettings.debugnormal) { + std::string title("Template Simplifier pass " + std::to_string(passCount + 1)); + mTokenList.front()->printOut(title.c_str(), mTokenList.getFiles()); + } + + // Copy default argument values from forward declaration to declaration + fixForwardDeclaredDefaultArgumentValues(); + + // Locate user defined specializations. + getSpecializations(); + + // Locate user defined partial specializations. + getPartialSpecializations(); + + // Locate possible instantiations of templates.. + getTemplateInstantiations(); + + // Template arguments with default values + useDefaultArgumentValues(); + + simplifyTemplateAliases(); + + if (mSettings.debugtemplate) + printOut("### Template Simplifier pass " + std::to_string(passCount + 1) + " ###"); + + // Keep track of the order the names appear so sort can preserve that order + std::unordered_map nameOrdinal; + int ordinal = 0; + for (const auto& decl : mTemplateDeclarations) { + nameOrdinal.emplace(decl.fullName(), ordinal++); + } + + auto score = [&](const Token* arg) { + int i = 0; + for (const Token* tok = arg; tok; tok = tok->next()) { + if (tok->str() == ",") + return i; + if (tok->link() && Token::Match(tok, "(|{|[")) + tok = tok->link(); + else if (tok->str() == "<") { + const Token* temp = tok->findClosingBracket(); + if (temp) + tok = temp; + } else if (Token::Match(tok, ")|;")) + return i; + else if (Token::simpleMatch(tok, "const")) + i--; + } + return 0; + }; + // Sort so const parameters come first in the list + mTemplateDeclarations.sort([&](const TokenAndName& x, const TokenAndName& y) { + if (x.fullName() != y.fullName()) + return nameOrdinal.at(x.fullName()) < nameOrdinal.at(y.fullName()); + if (x.isFunction() && y.isFunction()) { + std::vector xargs; + getFunctionArguments(x.nameToken(), xargs); + std::vector yargs; + getFunctionArguments(y.nameToken(), yargs); + if (xargs.size() != yargs.size()) + return xargs.size() < yargs.size(); + if (isConstMethod(x.nameToken()) != isConstMethod(y.nameToken())) + return isConstMethod(x.nameToken()); + return std::lexicographical_compare(xargs.begin(), + xargs.end(), + yargs.begin(), + yargs.end(), + [&](const Token* xarg, const Token* yarg) { + if (xarg != yarg) + return score(xarg) < score(yarg); + return false; + }); + } + return false; + }); + + std::set expandedtemplates; + + for (std::list::const_reverse_iterator iter1 = mTemplateDeclarations.crbegin(); iter1 != mTemplateDeclarations.crend(); ++iter1) { + if (iter1->isAlias() || iter1->isFriend()) + continue; + + // get specializations.. + std::list specializations; + for (std::list::const_iterator iter2 = mTemplateDeclarations.cbegin(); iter2 != mTemplateDeclarations.cend(); ++iter2) { + if (iter2->isAlias() || iter2->isFriend()) + continue; + + if (iter1->fullName() == iter2->fullName()) + specializations.push_back(iter2->nameToken()); + } + + const bool instantiated = simplifyTemplateInstantiations( + *iter1, + specializations, + maxtime, + expandedtemplates); + if (instantiated) { + mInstantiatedTemplates.push_back(*iter1); + mTemplateNamePos.clear(); // positions might be invalid after instantiations + } + } + + for (std::list::const_iterator it = mInstantiatedTemplates.cbegin(); it != mInstantiatedTemplates.cend(); ++it) { + auto decl = std::find_if(mTemplateDeclarations.begin(), mTemplateDeclarations.end(), [&it](const TokenAndName& decl) { + return decl.token() == it->token(); + }); + if (decl != mTemplateDeclarations.end()) { + if (it->isSpecialization()) { + // delete the "template < >" + Token * tok = it->token(); + tok->deleteNext(2); + tok->deleteThis(); + } else { + // remove forward declaration if found + auto it1 = mTemplateForwardDeclarationsMap.find(it->token()); + if (it1 != mTemplateForwardDeclarationsMap.end()) + removeTemplate(it1->second, &mTemplateForwardDeclarationsMap); + removeTemplate(it->token(), &mTemplateForwardDeclarationsMap); + } + mTemplateDeclarations.erase(decl); + } + } + + // remove out of line member functions + while (!mMemberFunctionsToDelete.empty()) { + const std::list::iterator it = std::find_if(mTemplateDeclarations.begin(), + mTemplateDeclarations.end(), + FindToken(mMemberFunctionsToDelete.cbegin()->token())); + // multiple functions can share the same declaration so make sure it hasn't already been deleted + if (it != mTemplateDeclarations.end()) { + removeTemplate(it->token()); + mTemplateDeclarations.erase(it); + } else { + const std::list::iterator it1 = std::find_if(mTemplateForwardDeclarations.begin(), + mTemplateForwardDeclarations.end(), + FindToken(mMemberFunctionsToDelete.cbegin()->token())); + // multiple functions can share the same declaration so make sure it hasn't already been deleted + if (it1 != mTemplateForwardDeclarations.end()) { + removeTemplate(it1->token()); + mTemplateForwardDeclarations.erase(it1); + } + } + mMemberFunctionsToDelete.erase(mMemberFunctionsToDelete.begin()); + } + + // remove explicit instantiations + for (const TokenAndName& j : mExplicitInstantiationsToDelete) { + Token * start = j.token(); + if (start) { + Token * end = start->next(); + while (end && end->str() != ";") + end = end->next(); + if (start->previous()) + start = start->previous(); + if (end && end->next()) + end = end->next(); + eraseTokens(start, end); + } + } + } + + if (passCount == passCountMax) { + if (mSettings.debugwarnings) { + const std::list locationList(1, mTokenList.front()); + const ErrorMessage errmsg(locationList, &mTokenizer.list, + Severity::debug, + "debug", + "TemplateSimplifier: pass count limit hit before simplifications were finished.", + Certainty::normal); + mErrorLogger.reportErr(errmsg); + } + } + + // Tweak uninstantiated C++17 fold expressions (... && args) + if (mSettings.standards.cpp >= Standards::CPP17) { + bool simplify = false; + for (Token *tok = mTokenList.front(); tok; tok = tok->next()) { + if (tok->str() == "template") + simplify = false; + if (tok->str() == "{") + simplify = true; + if (!simplify || tok->str() != "(") + continue; + const Token *op = nullptr; + const Token *args = nullptr; + if (Token::Match(tok, "( ... %op%")) { + op = tok->tokAt(2); + args = tok->link()->previous(); + } else if (Token::Match(tok, "( %name% %op% ...")) { + op = tok->tokAt(2); + args = tok->link()->previous()->isName() ? nullptr : tok->next(); + } else if (Token::Match(tok->link()->tokAt(-3), "%op% ... )")) { + op = tok->link()->tokAt(-2); + args = tok->next(); + } else if (Token::Match(tok->link()->tokAt(-3), "... %op% %name% )")) { + op = tok->link()->tokAt(-2); + args = tok->next()->isName() ? nullptr : tok->link()->previous(); + } else { + continue; + } + + const std::string strop = op->str(); + const std::string strargs = (args && args->isName()) ? args->str() : ""; + + Token::eraseTokens(tok, tok->link()); + tok->insertToken(")"); + if (!strargs.empty()) { + tok->insertToken("..."); + tok->insertToken(strargs); + } + tok->insertToken("("); + Token::createMutualLinks(tok->next(), tok->link()->previous()); + tok->insertToken("__cppcheck_fold_" + strop + "__"); + } + } +} + +void TemplateSimplifier::syntaxError(const Token *tok) +{ + throw InternalError(tok, "syntax error", InternalError::SYNTAX); +} diff --git a/cppcheck-2.14.0/lib/templatesimplifier.h b/cppcheck-2.14.0/lib/templatesimplifier.h new file mode 100644 index 00000000..46749a2a --- /dev/null +++ b/cppcheck-2.14.0/lib/templatesimplifier.h @@ -0,0 +1,516 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +//--------------------------------------------------------------------------- +#ifndef templatesimplifierH +#define templatesimplifierH +//--------------------------------------------------------------------------- + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +class ErrorLogger; +class Settings; +class Token; +class Tokenizer; +class TokenList; +struct newInstantiation; + +/// @addtogroup Core +/// @{ + +/** @brief Simplify templates from the preprocessed and partially simplified code. */ +class CPPCHECKLIB TemplateSimplifier { + friend class TestSimplifyTemplate; + +public: + explicit TemplateSimplifier(Tokenizer &tokenizer); + + const std::string& dump() const { + return mDump; + } + + /** + */ + void checkComplicatedSyntaxErrorsInTemplates(); + + /** + * is the token pointing at a template parameters block + * < int , 3 > => yes + * \param tok start token that must point at "<" + * \return number of parameters (invalid parameters => 0) + */ + static unsigned int templateParameters(const Token *tok); + + /** + * Token and its full scopename + */ + class TokenAndName { + Token *mToken; + std::string mScope; + std::string mName; + std::string mFullName; + const Token *mNameToken; + const Token *mParamEnd; + unsigned int mFlags; + + enum { + fIsClass = (1 << 0), // class template + fIsFunction = (1 << 1), // function template + fIsVariable = (1 << 2), // variable template + fIsAlias = (1 << 3), // alias template + fIsSpecialization = (1 << 4), // user specialized template + fIsPartialSpecialization = (1 << 5), // user partial specialized template + fIsForwardDeclaration = (1 << 6), // forward declaration + fIsVariadic = (1 << 7), // variadic template + fIsFriend = (1 << 8), // friend template + fFamilyMask = (fIsClass | fIsFunction | fIsVariable) + }; + + void isClass(bool state) { + setFlag(fIsClass, state); + } + void isFunction(bool state) { + setFlag(fIsFunction, state); + } + void isVariable(bool state) { + setFlag(fIsVariable, state); + } + void isAlias(bool state) { + setFlag(fIsAlias, state); + } + void isSpecialization(bool state) { + setFlag(fIsSpecialization, state); + } + void isPartialSpecialization(bool state) { + setFlag(fIsPartialSpecialization, state); + } + void isForwardDeclaration(bool state) { + setFlag(fIsForwardDeclaration, state); + } + void isVariadic(bool state) { + setFlag(fIsVariadic, state); + } + void isFriend(bool state) { + setFlag(fIsFriend, state); + } + + /** + * Get specified flag state. + * @param flag flag to get state of + * @return true if flag set or false in flag not set + */ + bool getFlag(unsigned int flag) const { + return ((mFlags & flag) != 0); + } + + /** + * Set specified flag state. + * @param flag flag to set state + * @param state new state of flag + */ + void setFlag(unsigned int flag, bool state) { + mFlags = state ? mFlags | flag : mFlags & ~flag; + } + + public: + /** + * Constructor used for instantiations. + * \param token template instantiation name token "name<...>" + * \param scope full qualification of template(scope) + */ + TokenAndName(Token *token, std::string scope); + /** + * Constructor used for declarations. + * \param token template declaration token "template < ... >" + * \param scope full qualification of template(scope) + * \param nameToken template name token "template < ... > class name" + * \param paramEnd template parameter end token ">" + */ + TokenAndName(Token *token, std::string scope, const Token *nameToken, const Token *paramEnd); + TokenAndName(const TokenAndName& other); + ~TokenAndName(); + + bool operator == (const TokenAndName & rhs) const { + return mToken == rhs.mToken && mScope == rhs.mScope && mName == rhs.mName && mFullName == rhs.mFullName && + mNameToken == rhs.mNameToken && mParamEnd == rhs.mParamEnd && mFlags == rhs.mFlags; + } + + std::string dump(const std::vector& fileNames) const; + + // TODO: do not return non-const pointer from const object + Token * token() const { + return mToken; + } + void token(Token * token) { + mToken = token; + } + const std::string & scope() const { + return mScope; + } + const std::string & name() const { + return mName; + } + const std::string & fullName() const { + return mFullName; + } + const Token * nameToken() const { + return mNameToken; + } + const Token * paramEnd() const { + return mParamEnd; + } + void paramEnd(const Token *end) { + mParamEnd = end; + } + + bool isClass() const { + return getFlag(fIsClass); + } + bool isFunction() const { + return getFlag(fIsFunction); + } + bool isVariable() const { + return getFlag(fIsVariable); + } + bool isAlias() const { + return getFlag(fIsAlias); + } + bool isSpecialization() const { + return getFlag(fIsSpecialization); + } + bool isPartialSpecialization() const { + return getFlag(fIsPartialSpecialization); + } + bool isForwardDeclaration() const { + return getFlag(fIsForwardDeclaration); + } + bool isVariadic() const { + return getFlag(fIsVariadic); + } + bool isFriend() const { + return getFlag(fIsFriend); + } + + /** + * Get alias start token. + * template < ... > using X = foo < ... >; + * ^ + * @return alias start token + */ + const Token * aliasStartToken() const; + + /** + * Get alias end token. + * template < ... > using X = foo < ... >; + * ^ + * @return alias end token + */ + const Token * aliasEndToken() const; + + /** + * Is token an alias token? + * template < ... > using X = foo < ... >; + * ^ + * @param tok token to check + * @return true if alias token, false if not + */ + bool isAliasToken(const Token *tok) const; + + /** + * Is declaration the same family (class, function or variable). + * + * @param decl declaration to compare to + * @return true if same family, false if different family + */ + bool isSameFamily(const TemplateSimplifier::TokenAndName &decl) const { + // Make sure a family flag is set and matches. + // This works because at most only one flag will be set. + return ((mFlags & fFamilyMask) && (decl.mFlags & fFamilyMask)); + } + }; + + /** + * Find last token of a template declaration. + * @param tok start token of declaration "template" or token after "template < ... >" + * @return last token of declaration or nullptr if syntax error + */ + static Token *findTemplateDeclarationEnd(Token *tok); + static const Token *findTemplateDeclarationEnd(const Token *tok); + + /** + * Match template declaration/instantiation + * @param instance template instantiation + * @param numberOfArguments number of template arguments + * @param variadic last template argument is variadic + * @param patternAfter pattern that must match the tokens after the ">" + * @return match => true + */ + static bool instantiateMatch(const Token *instance, const std::size_t numberOfArguments, bool variadic, const char patternAfter[]); + + /** + * Match template declaration/instantiation + * @param tok The ">" token e.g. before "class" + * @return -1 to bail out or positive integer to identity the position + * of the template name. + */ + int getTemplateNamePosition(const Token *tok); + + /** + * Get class template name position + * @param tok The ">" token e.g. before "class" + * @param namepos return offset to name + * @return true if name found, false if not + * */ + static bool getTemplateNamePositionTemplateClass(const Token *tok, int &namepos); + + /** + * Get function template name position + * @param tok The ">" token + * @param namepos return offset to name + * @return true if name found, false if not + * */ + static bool getTemplateNamePositionTemplateFunction(const Token *tok, int &namepos); + + /** + * Get variable template name position + * @param tok The ">" token + * @param namepos return offset to name + * @return true if name found, false if not + * */ + static bool getTemplateNamePositionTemplateVariable(const Token *tok, int &namepos); + + /** + * Simplify templates + * @param maxtime time when the simplification should be stopped + */ + void simplifyTemplates(const std::time_t maxtime); + + /** + * Simplify constant calculations such as "1+2" => "3" + * @param tok start token + * @return true if modifications to token-list are done. + * false if no modifications are done. + */ + static bool simplifyNumericCalculations(Token *tok, bool isTemplate = true); + + /** + * Simplify constant calculations such as "1+2" => "3". + * This also performs simple cleanup of parentheses etc. + * @return true if modifications to token-list are done. + * false if no modifications are done. + */ + bool simplifyCalculations(Token* frontToken = nullptr, const Token *backToken = nullptr, bool isTemplate = true); + + /** Simplify template instantiation arguments. + * @param start first token of arguments + * @param end token following last argument token + */ + void simplifyTemplateArgs(Token *start, const Token *end, std::vector* newInst = nullptr); + +private: + /** + * Get template declarations + * @return true if code has templates. + */ + bool getTemplateDeclarations(); + + /** Add template instantiation. + * @param token first token of instantiation + * @param scope scope of instantiation + */ + void addInstantiation(Token *token, const std::string &scope); + + /** + * Get template instantiations + */ + void getTemplateInstantiations(); + + /** + * Fix forward declared default argument values by copying them + * when they are not present in the declaration. + */ + void fixForwardDeclaredDefaultArgumentValues(); + + /** + * simplify template instantiations (use default argument values) + */ + void useDefaultArgumentValues(); + + /** + * simplify template instantiations (use default argument values) + * @param declaration template declaration or forward declaration + */ + void useDefaultArgumentValues(TokenAndName &declaration); + + /** + * Try to locate a matching declaration for each user defined + * specialization. + */ + void getSpecializations(); + + /** + * Try to locate a matching declaration for each user defined + * partial specialization. + */ + void getPartialSpecializations(); + + /** + * simplify template aliases + */ + void simplifyTemplateAliases(); + + /** + * Simplify templates : expand all instantiations for a template + * @todo It seems that inner templates should be instantiated recursively + * @param templateDeclaration template declaration + * @param specializations template specializations (list each template name token) + * @param maxtime time when the simplification will stop + * @param expandedtemplates all templates that has been expanded so far. The full names are stored. + * @return true if the template was instantiated + */ + bool simplifyTemplateInstantiations( + const TokenAndName &templateDeclaration, + const std::list &specializations, + const std::time_t maxtime, + std::set &expandedtemplates); + + /** + * Simplify templates : add namespace to template name + * @param templateDeclaration template declaration + * @param tok place to insert namespace + */ + void addNamespace(const TokenAndName &templateDeclaration, const Token *tok); + + /** + * Simplify templates : check if namespace already present + * @param templateDeclaration template declaration + * @param tok place to start looking for namespace + * @return true if namespace already present + */ + static bool alreadyHasNamespace(const TokenAndName &templateDeclaration, const Token *tok); + + /** + * Expand a template. Create "expanded" class/function at end of tokenlist. + * @param templateDeclaration Template declaration information + * @param templateInstantiation Full name of template + * @param typeParametersInDeclaration The type parameters of the template + * @param newName New name of class/function. + * @param copy copy or expand in place + */ + void expandTemplate( + const TokenAndName &templateDeclaration, + const TokenAndName &templateInstantiation, + const std::vector &typeParametersInDeclaration, + const std::string &newName, + bool copy); + + /** + * Replace all matching template usages 'Foo < int >' => 'Foo' + * @param instantiation Template instantiation information. + * @param typeStringsUsedInTemplateInstantiation template parameters. list of token strings. + * @param newName The new type name + */ + void replaceTemplateUsage(const TokenAndName &instantiation, + const std::list &typeStringsUsedInTemplateInstantiation, + const std::string &newName); + + /** + * @brief TemplateParametersInDeclaration + * @param tok template < typename T, typename S > + * ^ tok + * @param typeParametersInDeclaration template < typename T, typename S > + * ^ [0] ^ [1] + */ + static void getTemplateParametersInDeclaration( + const Token * tok, + std::vector & typeParametersInDeclaration); + + /** + * Remove a specific "template < ..." template class/function + */ + static bool removeTemplate(Token *tok, std::map* forwardDecls = nullptr); + + /** Syntax error */ + NORETURN static void syntaxError(const Token *tok); + + static bool matchSpecialization( + const Token *templateDeclarationNameToken, + const Token *templateInstantiationNameToken, + const std::list & specializations); + + /* + * Same as Token::eraseTokens() but tries to fix up lists with pointers to the deleted tokens. + * @param begin Tokens after this will be erased. + * @param end Tokens before this will be erased. + */ + static void eraseTokens(Token *begin, const Token *end); + + /** + * Delete specified token without invalidating pointer to following token. + * tok will be invalidated. + * @param tok token to delete + */ + static void deleteToken(Token *tok); + + /** + * Get the new token name. + * @param tok2 name token + * @param typeStringsUsedInTemplateInstantiation type strings use in template instantiation + * @return new token name + */ + std::string getNewName( + Token *tok2, + std::list &typeStringsUsedInTemplateInstantiation); + + void printOut( + const TokenAndName &tokenAndName, + const std::string &indent = " ") const; + void printOut(const std::string &text = emptyString) const; + + Tokenizer &mTokenizer; + TokenList &mTokenList; + const Settings &mSettings; + ErrorLogger &mErrorLogger; + bool mChanged{}; + + std::list mTemplateDeclarations; + std::list mTemplateForwardDeclarations; + std::map mTemplateForwardDeclarationsMap; + std::map mTemplateSpecializationMap; + std::map mTemplatePartialSpecializationMap; + std::list mTemplateInstantiations; + std::list mInstantiatedTemplates; + std::list mMemberFunctionsToDelete; + std::vector mExplicitInstantiationsToDelete; + std::vector mTypesUsedInTemplateInstantiation; + std::unordered_map mTemplateNamePos; + std::string mDump; +}; + +/// @} +//--------------------------------------------------------------------------- +#endif // templatesimplifierH diff --git a/cppcheck-2.14.0/lib/timer.cpp b/cppcheck-2.14.0/lib/timer.cpp new file mode 100644 index 00000000..78a176e9 --- /dev/null +++ b/cppcheck-2.14.0/lib/timer.cpp @@ -0,0 +1,144 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "utils.h" + +#include +#include +#include +#include + +namespace { + using dataElementType = std::pair; + bool more_second_sec(const dataElementType& lhs, const dataElementType& rhs) + { + return lhs.second.seconds() > rhs.second.seconds(); + } + + // TODO: remove and print through (synchronized) ErrorLogger instead + std::mutex stdCoutLock; +} + +// TODO: this does not include any file context when SHOWTIME_FILE thus rendering it useless - should we include the logging with the progress logging? +// that could also get rid of the broader locking +void TimerResults::showResults(SHOWTIME_MODES mode) const +{ + if (mode == SHOWTIME_MODES::SHOWTIME_NONE || mode == SHOWTIME_MODES::SHOWTIME_FILE_TOTAL) + return; + + TimerResultsData overallData; + std::vector data; + + { + std::lock_guard l(mResultsSync); + + data.reserve(mResults.size()); + data.insert(data.begin(), mResults.cbegin(), mResults.cend()); + } + std::sort(data.begin(), data.end(), more_second_sec); + + // lock the whole logging operation to avoid multiple threads printing their results at the same time + std::lock_guard l(stdCoutLock); + + std::cout << std::endl; + + size_t ordinal = 1; // maybe it would be nice to have an ordinal in output later! + for (std::vector::const_iterator iter=data.cbegin(); iter!=data.cend(); ++iter) { + const double sec = iter->second.seconds(); + const double secAverage = sec / (double)(iter->second.mNumberOfResults); + bool hasParent = false; + { + // Do not use valueFlow.. in "Overall time" because those are included in Tokenizer already + if (startsWith(iter->first,"valueFlow")) + hasParent = true; + + // Do not use inner timers in "Overall time" + const std::string::size_type pos = iter->first.rfind("::"); + if (pos != std::string::npos) + hasParent = std::any_of(data.cbegin(), data.cend(), [iter,pos](const dataElementType& d) { + return d.first.size() == pos && iter->first.compare(0, d.first.size(), d.first) == 0; + }); + } + if (!hasParent) + overallData.mClocks += iter->second.mClocks; + if ((mode != SHOWTIME_MODES::SHOWTIME_TOP5_FILE && mode != SHOWTIME_MODES::SHOWTIME_TOP5_SUMMARY) || (ordinal<=5)) { + std::cout << iter->first << ": " << sec << "s (avg. " << secAverage << "s - " << iter->second.mNumberOfResults << " result(s))" << std::endl; + } + ++ordinal; + } + + const double secOverall = overallData.seconds(); + std::cout << "Overall time: " << secOverall << "s" << std::endl; +} + +void TimerResults::addResults(const std::string& str, std::clock_t clocks) +{ + std::lock_guard l(mResultsSync); + + mResults[str].mClocks += clocks; + mResults[str].mNumberOfResults++; +} + +void TimerResults::reset() +{ + std::lock_guard l(mResultsSync); + mResults.clear(); +} + +Timer::Timer(std::string str, SHOWTIME_MODES showtimeMode, TimerResultsIntf* timerResults) + : mStr(std::move(str)) + , mTimerResults(timerResults) + , mStart(std::clock()) + , mShowTimeMode(showtimeMode) + , mStopped(showtimeMode == SHOWTIME_MODES::SHOWTIME_NONE || showtimeMode == SHOWTIME_MODES::SHOWTIME_FILE_TOTAL) +{} + +Timer::Timer(bool fileTotal, std::string filename) + : mStr(std::move(filename)) + , mStopped(!fileTotal) +{} + +Timer::~Timer() +{ + stop(); +} + +void Timer::stop() +{ + if ((mShowTimeMode != SHOWTIME_MODES::SHOWTIME_NONE) && !mStopped) { + const std::clock_t end = std::clock(); + const std::clock_t diff = end - mStart; + + if (mShowTimeMode == SHOWTIME_MODES::SHOWTIME_FILE) { + const double sec = (double)diff / CLOCKS_PER_SEC; + std::lock_guard l(stdCoutLock); + std::cout << mStr << ": " << sec << "s" << std::endl; + } else if (mShowTimeMode == SHOWTIME_MODES::SHOWTIME_FILE_TOTAL) { + const double sec = (double)diff / CLOCKS_PER_SEC; + std::lock_guard l(stdCoutLock); + std::cout << "Check time: " << mStr << ": " << sec << "s" << std::endl; + } else { + if (mTimerResults) + mTimerResults->addResults(mStr, diff); + } + } + + mStopped = true; +} diff --git a/cppcheck-2.14.0/lib/timer.h b/cppcheck-2.14.0/lib/timer.h new file mode 100644 index 00000000..a42f72b5 --- /dev/null +++ b/cppcheck-2.14.0/lib/timer.h @@ -0,0 +1,89 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +//--------------------------------------------------------------------------- +#ifndef timerH +#define timerH +//--------------------------------------------------------------------------- + +#include "config.h" + +#include +#include +#include +#include + +enum class SHOWTIME_MODES { + SHOWTIME_NONE, + SHOWTIME_FILE, + SHOWTIME_FILE_TOTAL, + SHOWTIME_SUMMARY, + SHOWTIME_TOP5_SUMMARY, + SHOWTIME_TOP5_FILE +}; + +class CPPCHECKLIB TimerResultsIntf { +public: + virtual ~TimerResultsIntf() = default; + + virtual void addResults(const std::string& str, std::clock_t clocks) = 0; +}; + +struct TimerResultsData { + std::clock_t mClocks{}; + long mNumberOfResults{}; + + double seconds() const { + const double ret = (double)((unsigned long)mClocks) / (double)CLOCKS_PER_SEC; + return ret; + } +}; + +class CPPCHECKLIB TimerResults : public TimerResultsIntf { +public: + TimerResults() = default; + + void showResults(SHOWTIME_MODES mode) const; + void addResults(const std::string& str, std::clock_t clocks) override; + + void reset(); + +private: + std::map mResults; + mutable std::mutex mResultsSync; +}; + +class CPPCHECKLIB Timer { +public: + Timer(std::string str, SHOWTIME_MODES showtimeMode, TimerResultsIntf* timerResults = nullptr); + Timer(bool fileTotal, std::string filename); + ~Timer(); + + Timer(const Timer&) = delete; + Timer& operator=(const Timer&) = delete; + + void stop(); + +private: + const std::string mStr; + TimerResultsIntf* mTimerResults{}; + std::clock_t mStart = std::clock(); + const SHOWTIME_MODES mShowTimeMode = SHOWTIME_MODES::SHOWTIME_FILE_TOTAL; + bool mStopped{}; +}; +//--------------------------------------------------------------------------- +#endif // timerH diff --git a/cppcheck-2.14.0/lib/token.cpp b/cppcheck-2.14.0/lib/token.cpp new file mode 100644 index 00000000..17e08e26 --- /dev/null +++ b/cppcheck-2.14.0/lib/token.cpp @@ -0,0 +1,2760 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "token.h" + +#include "astutils.h" +#include "errortypes.h" +#include "library.h" +#include "settings.h" +#include "simplecpp.h" +#include "symboldatabase.h" +#include "tokenlist.h" +#include "utils.h" +#include "tokenrange.h" +#include "valueflow.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + struct less { + template + bool operator()(const T &x, const U &y) const { + return x < y; + } + }; +} + +const std::list TokenImpl::mEmptyValueList; + +Token::Token(TokensFrontBack &tokensFrontBack) : + mTokensFrontBack(tokensFrontBack) +{ + mImpl = new TokenImpl(); +} + +Token::Token(const Token* tok) + : Token(const_cast(tok)->mTokensFrontBack) +{ + fileIndex(tok->fileIndex()); + linenr(tok->linenr()); +} + +Token::~Token() +{ + delete mImpl; +} + +/* + * Get a TokenRange which starts at this token and contains every token following it in order up to but not including 't' + * e.g. for the sequence of tokens A B C D E, C.until(E) would yield the Range C D + * note t can be nullptr to iterate all the way to the end. + */ +// cppcheck-suppress unusedFunction // only used in testtokenrange.cpp +ConstTokenRange Token::until(const Token* t) const +{ + return ConstTokenRange(this, t); +} + +static const std::unordered_set controlFlowKeywords = { + "goto", + "do", + "if", + "else", + "for", + "while", + "switch", + "case", + "break", + "continue", + "return" +}; + +// TODO: replace with Keywords::getX()? +// Another list of keywords +static const std::unordered_set baseKeywords = { + "asm", + "auto", + "break", + "case", + "const", + "continue", + "default", + "do", + "else", + "enum", + "extern", + "for", + "goto", + "if", + "inline", + "register", + "restrict", + "return", + "sizeof", + "static", + "struct", + "switch", + "typedef", + "union", + "volatile", + "while", + "void" +}; + +void Token::update_property_info() +{ + setFlag(fIsControlFlowKeyword, controlFlowKeywords.find(mStr) != controlFlowKeywords.end()); + + if (!mStr.empty()) { + if (mStr == "true" || mStr == "false") + tokType(eBoolean); + else if (isStringLiteral(mStr)) + tokType(eString); + else if (isCharLiteral(mStr)) + tokType(eChar); + else if (std::isalpha((unsigned char)mStr[0]) || mStr[0] == '_' || mStr[0] == '$') { // Name + if (mImpl->mVarId) + tokType(eVariable); + else if (mTokensFrontBack.list.isKeyword(mStr)) + tokType(eKeyword); + else if (baseKeywords.count(mStr) > 0) + tokType(eKeyword); + else if (mTokType != eVariable && mTokType != eFunction && mTokType != eType && mTokType != eKeyword) + tokType(eName); + } else if (simplecpp::Token::isNumberLike(mStr)) { + if (MathLib::isInt(mStr) || MathLib::isFloat(mStr)) + tokType(eNumber); + else + tokType(eName); // assume it is a user defined literal + } else if (mStr == "=" || mStr == "<<=" || mStr == ">>=" || + (mStr.size() == 2U && mStr[1] == '=' && std::strchr("+-*/%&^|", mStr[0]))) + tokType(eAssignmentOp); + else if (mStr.size() == 1 && mStr.find_first_of(",[]()?:") != std::string::npos) + tokType(eExtendedOp); + else if (mStr=="<<" || mStr==">>" || (mStr.size()==1 && mStr.find_first_of("+-*/%") != std::string::npos)) + tokType(eArithmeticalOp); + else if (mStr.size() == 1 && mStr.find_first_of("&|^~") != std::string::npos) + tokType(eBitOp); + else if (mStr.size() <= 2 && + (mStr == "&&" || + mStr == "||" || + mStr == "!")) + tokType(eLogicalOp); + else if (mStr.size() <= 2 && !mLink && + (mStr == "==" || + mStr == "!=" || + mStr == "<" || + mStr == "<=" || + mStr == ">" || + mStr == ">=")) + tokType(eComparisonOp); + else if (mStr == "<=>") + tokType(eComparisonOp); + else if (mStr.size() == 2 && + (mStr == "++" || + mStr == "--")) + tokType(eIncDecOp); + else if (mStr.size() == 1 && (mStr.find_first_of("{}") != std::string::npos || (mLink && mStr.find_first_of("<>") != std::string::npos))) + tokType(eBracket); + else if (mStr == "...") + tokType(eEllipsis); + else + tokType(eOther); + } else { + tokType(eNone); + } + + update_property_char_string_literal(); + update_property_isStandardType(); +} + +static const std::unordered_set stdTypes = { "bool" + , "_Bool" + , "char" + , "double" + , "float" + , "int" + , "long" + , "short" + , "size_t" + , "void" + , "wchar_t" +}; + +void Token::update_property_isStandardType() +{ + isStandardType(false); + + if (mStr.size() < 3) + return; + + if (stdTypes.find(mStr)!=stdTypes.end()) { + isStandardType(true); + tokType(eType); + } +} + +void Token::update_property_char_string_literal() +{ + if (mTokType != Token::eString && mTokType != Token::eChar) + return; + + isLong(((mTokType == Token::eString) && isPrefixStringCharLiteral(mStr, '"', "L")) || + ((mTokType == Token::eChar) && isPrefixStringCharLiteral(mStr, '\'', "L"))); +} + +bool Token::isUpperCaseName() const +{ + if (!isName()) + return false; + return std::none_of(mStr.begin(), mStr.end(), [](char c) { + return std::islower(c); + }); +} + +void Token::concatStr(std::string const& b) +{ + mStr.pop_back(); + mStr.append(getStringLiteral(b) + "\""); + + if (isCChar() && isStringLiteral(b) && b[0] != '"') { + mStr.insert(0, b.substr(0, b.find('"'))); + } + update_property_info(); +} + +std::string Token::strValue() const +{ + assert(mTokType == eString); + std::string ret(getStringLiteral(mStr)); + std::string::size_type pos = 0U; + while ((pos = ret.find('\\', pos)) != std::string::npos) { + ret.erase(pos,1U); + if (ret[pos] >= 'a') { + if (ret[pos] == 'n') + ret[pos] = '\n'; + else if (ret[pos] == 'r') + ret[pos] = '\r'; + else if (ret[pos] == 't') + ret[pos] = '\t'; + } + if (ret[pos] == '0') + return ret.substr(0,pos); + pos++; + } + return ret; +} + +void Token::deleteNext(nonneg int count) +{ + while (mNext && count > 0) { + Token *n = mNext; + + // #8154 we are about to be unknown -> destroy the link to us + if (n->mLink && n->mLink->mLink == n) + n->mLink->link(nullptr); + + mNext = n->next(); + delete n; + --count; + } + + if (mNext) + mNext->previous(this); + else + mTokensFrontBack.back = this; +} + +void Token::deletePrevious(nonneg int count) +{ + while (mPrevious && count > 0) { + Token *p = mPrevious; + + // #8154 we are about to be unknown -> destroy the link to us + if (p->mLink && p->mLink->mLink == p) + p->mLink->link(nullptr); + + mPrevious = p->previous(); + delete p; + --count; + } + + if (mPrevious) + mPrevious->next(this); + else + mTokensFrontBack.front = this; +} + +void Token::swapWithNext() +{ + if (mNext) { + std::swap(mStr, mNext->mStr); + std::swap(mTokType, mNext->mTokType); + std::swap(mFlags, mNext->mFlags); + std::swap(mImpl, mNext->mImpl); + if (mImpl->mTemplateSimplifierPointers) + // cppcheck-suppress shadowFunction - TODO: fix this + for (auto *templateSimplifierPointer : *mImpl->mTemplateSimplifierPointers) { + templateSimplifierPointer->token(this); + } + + if (mNext->mImpl->mTemplateSimplifierPointers) + // cppcheck-suppress shadowFunction - TODO: fix this + for (auto *templateSimplifierPointer : *mNext->mImpl->mTemplateSimplifierPointers) { + templateSimplifierPointer->token(mNext); + } + if (mNext->mLink) + mNext->mLink->mLink = this; + if (this->mLink) + this->mLink->mLink = mNext; + std::swap(mLink, mNext->mLink); + } +} + +void Token::takeData(Token *fromToken) +{ + mStr = fromToken->mStr; + tokType(fromToken->mTokType); + mFlags = fromToken->mFlags; + delete mImpl; + mImpl = fromToken->mImpl; + fromToken->mImpl = nullptr; + if (mImpl->mTemplateSimplifierPointers) + // cppcheck-suppress shadowFunction - TODO: fix this + for (auto *templateSimplifierPointer : *mImpl->mTemplateSimplifierPointers) { + templateSimplifierPointer->token(this); + } + mLink = fromToken->mLink; + if (mLink) + mLink->link(this); +} + +void Token::deleteThis() +{ + if (mNext) { // Copy next to this and delete next + takeData(mNext); + mNext->link(nullptr); // mark as unlinked + deleteNext(); + } else if (mPrevious) { // Copy previous to this and delete previous + takeData(mPrevious); + mPrevious->link(nullptr); + deletePrevious(); + } else { + // We are the last token in the list, we can't delete + // ourselves, so just make us empty + str(";"); + } +} + +void Token::replace(Token *replaceThis, Token *start, Token *end) +{ + // Fix the whole in the old location of start and end + if (start->previous()) + start->previous()->next(end->next()); + + if (end->next()) + end->next()->previous(start->previous()); + + // Move start and end to their new location + if (replaceThis->previous()) + replaceThis->previous()->next(start); + + if (replaceThis->next()) + replaceThis->next()->previous(end); + + start->previous(replaceThis->previous()); + end->next(replaceThis->next()); + + if (end->mTokensFrontBack.back == end) { + while (end->next()) + end = end->next(); + end->mTokensFrontBack.back = end; + } + + // Update mProgressValue, fileIndex and linenr + for (Token *tok = start; tok != end->next(); tok = tok->next()) + tok->mImpl->mProgressValue = replaceThis->mImpl->mProgressValue; + + // Delete old token, which is replaced + delete replaceThis; +} + +template )> +static T *tokAtImpl(T *tok, int index) +{ + while (index > 0 && tok) { + tok = tok->next(); + --index; + } + while (index < 0 && tok) { + tok = tok->previous(); + ++index; + } + return tok; +} + +const Token *Token::tokAt(int index) const +{ + return tokAtImpl(this, index); +} + +Token *Token::tokAt(int index) +{ + return tokAtImpl(this, index); +} + +template )> +static T *linkAtImpl(T *thisTok, int index) +{ + T *tok = thisTok->tokAt(index); + if (!tok) { + throw InternalError(thisTok, "Internal error. Token::linkAt called with index outside the tokens range."); + } + return tok->link(); +} + +const Token *Token::linkAt(int index) const +{ + return linkAtImpl(this, index); +} + +Token *Token::linkAt(int index) +{ + return linkAtImpl(this, index); +} + +const std::string &Token::strAt(int index) const +{ + const Token *tok = this->tokAt(index); + return tok ? tok->mStr : emptyString; +} + +static +#if defined(__GNUC__) +// GCC does not inline this by itself +// need to use the old syntax since the C++11 [[xxx:always_inline]] cannot be used here +inline __attribute__((always_inline)) +#endif +int multiComparePercent(const Token *tok, const char*& haystack, nonneg int varid) +{ + ++haystack; + // Compare only the first character of the string for optimization reasons + switch (haystack[0]) { + case 'v': + if (haystack[3] == '%') { // %var% + haystack += 4; + if (tok->varId() != 0) + return 1; + } else { // %varid% + if (varid == 0) { + throw InternalError(tok, "Internal error. Token::Match called with varid 0. Please report this to Cppcheck developers"); + } + + haystack += 6; + + if (tok->varId() == varid) + return 1; + } + break; + case 't': + // Type (%type%) + { + haystack += 5; + if (tok->isName() && tok->varId() == 0) + return 1; + } + break; + case 'a': + // Accept any token (%any%) or assign (%assign%) + { + if (haystack[3] == '%') { // %any% + haystack += 4; + return 1; + } + // %assign% + haystack += 7; + if (tok->isAssignmentOp()) + return 1; + } + break; + case 'n': + // Number (%num%) or name (%name%) + { + if (haystack[4] == '%') { // %name% + haystack += 5; + if (tok->isName()) + return 1; + } else { + haystack += 4; + if (tok->isNumber()) + return 1; + } + } + break; + case 'c': { + haystack += 1; + // Character (%char%) + if (haystack[0] == 'h') { + haystack += 4; + if (tok->tokType() == Token::eChar) + return 1; + } + // Const operator (%cop%) + else if (haystack[1] == 'p') { + haystack += 3; + if (tok->isConstOp()) + return 1; + } + // Comparison operator (%comp%) + else { + haystack += 4; + if (tok->isComparisonOp()) + return 1; + } + } + break; + case 's': + // String (%str%) + { + haystack += 4; + if (tok->tokType() == Token::eString) + return 1; + } + break; + case 'b': + // Bool (%bool%) + { + haystack += 5; + if (tok->isBoolean()) + return 1; + } + break; + case 'o': { + ++haystack; + if (haystack[1] == '%') { + // Op (%op%) + if (haystack[0] == 'p') { + haystack += 2; + if (tok->isOp()) + return 1; + } + // Or (%or%) + else { + haystack += 2; + if (tok->tokType() == Token::eBitOp && tok->str() == "|") + return 1; + } + } + + // Oror (%oror%) + else { + haystack += 4; + if (tok->tokType() == Token::eLogicalOp && tok->str() == "||") + return 1; + } + } + break; + default: + //unknown %cmd%, abort + throw InternalError(tok, "Unexpected command"); + } + + if (*haystack == '|') + haystack += 1; + else + return -1; + + return 0xFFFF; +} + +static +#if defined(__GNUC__) +// need to use the old syntax since the C++11 [[xxx:always_inline]] cannot be used here +inline __attribute__((always_inline)) +#endif +int multiCompareImpl(const Token *tok, const char *haystack, nonneg int varid) +{ + const char *needle = tok->str().c_str(); + const char *needlePointer = needle; + for (;;) { + if (needlePointer == needle && haystack[0] == '%' && haystack[1] != '|' && haystack[1] != '\0' && haystack[1] != ' ') { + const int ret = multiComparePercent(tok, haystack, varid); + if (ret < 2) + return ret; + } else if (*haystack == '|') { + if (*needlePointer == 0) { + // If needle is at the end, we have a match. + return 1; + } + + needlePointer = needle; + ++haystack; + } else if (*needlePointer == *haystack) { + if (*needlePointer == '\0') + return 1; + ++needlePointer; + ++haystack; + } else if (*haystack == ' ' || *haystack == '\0') { + if (needlePointer == needle) + return 0; + break; + } + // If haystack and needle don't share the same character, + // find next '|' character. + else { + needlePointer = needle; + + do { + ++haystack; + + if (*haystack == ' ' || *haystack == '\0') { + return -1; + } + if (*haystack == '|') { + break; + } + } while (true); + + ++haystack; + } + } + + if (*needlePointer == '\0') + return 1; + + return -1; +} + +// cppcheck-suppress unusedFunction - used in tests only +int Token::multiCompare(const Token *tok, const char *haystack, nonneg int varid) +{ + return multiCompareImpl(tok, haystack, varid); +} + +bool Token::simpleMatch(const Token *tok, const char pattern[], size_t pattern_len) +{ + if (!tok) + return false; // shortcut + const char *current = pattern; + const char *end = pattern + pattern_len; + // cppcheck-suppress shadowFunction - TODO: fix this + const char *next = static_cast(std::memchr(pattern, ' ', pattern_len)); + if (!next) + next = end; + + while (*current) { + const std::size_t length = next - current; + + if (!tok || length != tok->mStr.length() || std::strncmp(current, tok->mStr.c_str(), length) != 0) + return false; + + current = next; + if (*next) { + next = std::strchr(++current, ' '); + if (!next) + next = end; + } + tok = tok->next(); + } + + return true; +} + +bool Token::firstWordEquals(const char *str, const char *word) +{ + for (;;) { + if (*str != *word) + return (*str == ' ' && *word == 0); + if (*str == 0) + break; + + ++str; + ++word; + } + + return true; +} + +const char *Token::chrInFirstWord(const char *str, char c) +{ + for (;;) { + if (*str == ' ' || *str == 0) + return nullptr; + + if (*str == c) + return str; + + ++str; + } +} + +bool Token::Match(const Token *tok, const char pattern[], nonneg int varid) +{ + if (!(*pattern)) + return true; + + const char *p = pattern; + while (true) { + // 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; + } + + return false; + } + + // [.. => search for a one-character token.. + if (p[0] == '[' && chrInFirstWord(p, ']')) { + if (tok->str().length() != 1) + return false; + + const char *temp = p+1; + bool chrFound = false; + int count = 0; + while (*temp && *temp != ' ') { + if (*temp == ']') { + ++count; + } + + else if (*temp == tok->str()[0]) { + chrFound = true; + break; + } + + ++temp; + } + + if (count > 1 && tok->str()[0] == ']') + chrFound = true; + + if (!chrFound) + return false; + + p = temp; + } + + // 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; + } + + // Parse multi options, such as void|int|char (accept token which is one of these 3) + else { + const int res = multiCompareImpl(tok, p, varid); + if (res == 0) { + // Empty alternative matches, use the same token on next round + while (*p && *p != ' ') + ++p; + continue; + } + if (res == -1) { + // No match + return false; + } + } + + // using strchr() for the other instances leads to a performance decrease + if (!(p = strchr(p, ' '))) + break; + + tok = tok->next(); + } + + // The end of the pattern has been reached and nothing wrong has been found + return true; +} + +nonneg int Token::getStrLength(const Token *tok) +{ + assert(tok != nullptr); + assert(tok->mTokType == eString); + + int len = 0; + // cppcheck-suppress shadowFunction - TODO: fix this + const std::string str(getStringLiteral(tok->str())); + std::string::const_iterator it = str.cbegin(); + const std::string::const_iterator end = str.cend(); + + while (it != end) { + if (*it == '\\') { + ++it; + + // string ends at '\0' + if (*it == '0') + return len; + } + + if (*it == '\0') + return len; + + ++it; + ++len; + } + + return len; +} + +nonneg int Token::getStrArraySize(const Token *tok) +{ + assert(tok != nullptr); + assert(tok->tokType() == eString); + // cppcheck-suppress shadowFunction - TODO: fix this + const std::string str(getStringLiteral(tok->str())); + int sizeofstring = 1; + for (int i = 0; i < (int)str.size(); i++) { + if (str[i] == '\\') + ++i; + ++sizeofstring; + } + return sizeofstring; +} + +nonneg int Token::getStrSize(const Token *tok, const Settings &settings) +{ + assert(tok != nullptr && tok->tokType() == eString); + nonneg int sizeofType = 1; + if (tok->valueType()) { + ValueType vt(*tok->valueType()); + vt.pointer = 0; + sizeofType = ValueFlow::getSizeOf(vt, settings); + } + return getStrArraySize(tok) * sizeofType; +} + +void Token::move(Token *srcStart, Token *srcEnd, Token *newLocation) +{ + /**[newLocation] -> b -> c -> [srcStart] -> [srcEnd] -> f */ + + // Fix the gap, which tokens to be moved will leave + srcStart->previous()->next(srcEnd->next()); + srcEnd->next()->previous(srcStart->previous()); + + // Fix the tokens to be moved + srcEnd->next(newLocation->next()); + srcStart->previous(newLocation); + + // Fix the tokens at newLocation + newLocation->next()->previous(srcEnd); + newLocation->next(srcStart); + + // Update _progressValue + for (Token *tok = srcStart; tok != srcEnd->next(); tok = tok->next()) + tok->mImpl->mProgressValue = newLocation->mImpl->mProgressValue; +} + +template )> +static T* nextArgumentImpl(T *thisTok) +{ + for (T* tok = thisTok; tok; tok = tok->next()) { + if (tok->str() == ",") + return tok->next(); + if (tok->link() && Token::Match(tok, "(|{|[|<")) + tok = tok->link(); + else if (Token::Match(tok, ")|;")) + return nullptr; + } + return nullptr; +} + +const Token* Token::nextArgument() const +{ + return nextArgumentImpl(this); +} + +Token *Token::nextArgument() +{ + return nextArgumentImpl(this); +} + +const Token* Token::nextArgumentBeforeCreateLinks2() const +{ + for (const Token* tok = this; tok; tok = tok->next()) { + if (tok->str() == ",") + return tok->next(); + if (tok->link() && Token::Match(tok, "(|{|[")) + tok = tok->link(); + else if (tok->str() == "<") { + const Token* temp = tok->findClosingBracket(); + if (temp) + tok = temp; + } else if (Token::Match(tok, ")|;")) + return nullptr; + } + return nullptr; +} + +const Token* Token::nextTemplateArgument() const +{ + for (const Token* tok = this; tok; tok = tok->next()) { + if (tok->str() == ",") + return tok->next(); + if (tok->link() && Token::Match(tok, "(|{|[|<")) + tok = tok->link(); + else if (Token::Match(tok, ">|;")) + return nullptr; + } + return nullptr; +} + +static bool isOperator(const Token *tok) +{ + if (tok->link()) + tok = tok->link(); + // TODO handle multi token operators + return tok->strAt(-1) == "operator"; +} + +const Token * Token::findClosingBracket() const +{ + if (mStr != "<") + return nullptr; + + if (!mPrevious) + return nullptr; + + if (!(mPrevious->isName() || Token::simpleMatch(mPrevious, "]") || + Token::Match(mPrevious->previous(), "operator %op% <") || + Token::Match(mPrevious->tokAt(-2), "operator [([] [)]] <"))) + return nullptr; + + const Token *closing = nullptr; + const bool templateParameter(strAt(-1) == "template"); + std::set templateParameters; + + bool isDecl = true; + for (const Token *prev = previous(); prev; prev = prev->previous()) { + if (prev->str() == "=") + isDecl = false; + if (Token::simpleMatch(prev, "template <")) + isDecl = true; + if (Token::Match(prev, "[;{}]")) + break; + } + + unsigned int depth = 0; + for (closing = this; closing != nullptr; closing = closing->next()) { + if (Token::Match(closing, "{|[|(")) { + closing = closing->link(); + if (!closing) + return nullptr; // #6803 + } else if (Token::Match(closing, "}|]|)|;")) + return nullptr; + // we can make some guesses for template parameters + else if (closing->str() == "<" && closing->previous() && + (closing->previous()->isName() || Token::simpleMatch(closing->previous(), "]") || isOperator(closing->previous())) && + (templateParameter ? templateParameters.find(closing->strAt(-1)) == templateParameters.end() : true)) + ++depth; + else if (closing->str() == ">") { + if (--depth == 0) + return closing; + } else if (closing->str() == ">>" || closing->str() == ">>=") { + if (!isDecl && depth == 1) + continue; + if (depth <= 2) + return closing; + depth -= 2; + } + // save named template parameter + else if (templateParameter && depth == 1 && closing->str() == "," && + closing->previous()->isName() && !Match(closing->previous(), "class|typename|.")) + templateParameters.insert(closing->strAt(-1)); + } + + return closing; +} + +Token * Token::findClosingBracket() +{ + // return value of const function + return const_cast(const_cast(this)->findClosingBracket()); +} + +const Token * Token::findOpeningBracket() const +{ + if (mStr != ">") + return nullptr; + + const Token *opening = nullptr; + + unsigned int depth = 0; + for (opening = this; opening != nullptr; opening = opening->previous()) { + if (Token::Match(opening, "}|]|)")) { + opening = opening->link(); + if (!opening) + return nullptr; + } else if (Token::Match(opening, "{|{|(|;")) + return nullptr; + else if (opening->str() == ">") + ++depth; + else if (opening->str() == "<") { + if (--depth == 0) + return opening; + } + } + + return opening; +} + +Token * Token::findOpeningBracket() +{ + // return value of const function + return const_cast(const_cast(this)->findOpeningBracket()); +} + +//--------------------------------------------------------------------------- + +template )> +static T *findsimplematchImpl(T * const startTok, const char pattern[], size_t pattern_len) +{ + for (T* tok = startTok; tok; tok = tok->next()) { + if (Token::simpleMatch(tok, pattern, pattern_len)) + return tok; + } + return nullptr; +} + +const Token *Token::findsimplematch(const Token * const startTok, const char pattern[], size_t pattern_len) +{ + return findsimplematchImpl(startTok, pattern, pattern_len); +} + +Token *Token::findsimplematch(Token * const startTok, const char pattern[], size_t pattern_len) +{ + return findsimplematchImpl(startTok, pattern, pattern_len); +} + +template )> +static T *findsimplematchImpl(T * const startTok, const char pattern[], size_t pattern_len, const Token * const end) +{ + for (T* tok = startTok; tok && tok != end; tok = tok->next()) { + if (Token::simpleMatch(tok, pattern, pattern_len)) + return tok; + } + return nullptr; +} + +const Token *Token::findsimplematch(const Token * const startTok, const char pattern[], size_t pattern_len, const Token * const end) +{ + return findsimplematchImpl(startTok, pattern, pattern_len, end); +} + +Token *Token::findsimplematch(Token * const startTok, const char pattern[], size_t pattern_len, const Token * const end) { + return findsimplematchImpl(startTok, pattern, pattern_len, end); +} + +template )> +static T *findmatchImpl(T * const startTok, const char pattern[], const nonneg int varId) +{ + for (T* tok = startTok; tok; tok = tok->next()) { + if (Token::Match(tok, pattern, varId)) + return tok; + } + return nullptr; +} + +const Token *Token::findmatch(const Token * const startTok, const char pattern[], const nonneg int varId) +{ + return findmatchImpl(startTok, pattern, varId); +} + +Token *Token::findmatch(Token * const startTok, const char pattern[], const nonneg int varId) { + return findmatchImpl(startTok, pattern, varId); +} + +template )> +static T *findmatchImpl(T * const startTok, const char pattern[], const Token * const end, const nonneg int varId) +{ + for (T* tok = startTok; tok && tok != end; tok = tok->next()) { + if (Token::Match(tok, pattern, varId)) + return tok; + } + return nullptr; +} + +const Token *Token::findmatch(const Token * const startTok, const char pattern[], const Token * const end, const nonneg int varId) +{ + return findmatchImpl(startTok, pattern, end, varId); +} + +Token *Token::findmatch(Token * const startTok, const char pattern[], const Token * const end, const nonneg int varId) { + return findmatchImpl(startTok, pattern, end, varId); +} + +void Token::function(const Function *f) +{ + mImpl->mFunction = f; + if (f) { + if (f->isLambda()) + tokType(eLambda); + else + tokType(eFunction); + } else if (mTokType == eFunction) + tokType(eName); +} + +Token* Token::insertToken(const std::string& tokenStr, const std::string& originalNameStr, const std::string& macroNameStr, bool prepend) +{ + Token *newToken; + if (mStr.empty()) + newToken = this; + else + newToken = new Token(mTokensFrontBack); + newToken->str(tokenStr); + newToken->originalName(originalNameStr); + newToken->setMacroName(macroNameStr); + + if (newToken != this) { + newToken->mImpl->mLineNumber = mImpl->mLineNumber; + newToken->mImpl->mFileIndex = mImpl->mFileIndex; + newToken->mImpl->mProgressValue = mImpl->mProgressValue; + + if (prepend) { + if (this->previous()) { + newToken->previous(this->previous()); + newToken->previous()->next(newToken); + } else { + mTokensFrontBack.front = newToken; + } + this->previous(newToken); + newToken->next(this); + } else { + if (this->next()) { + newToken->next(this->next()); + newToken->next()->previous(newToken); + } else { + mTokensFrontBack.back = newToken; + } + this->next(newToken); + newToken->previous(this); + } + + if (mImpl->mScopeInfo) { + // If the brace is immediately closed there is no point opening a new scope for it + if (newToken->str() == "{") { + std::string nextScopeNameAddition; + // This might be the opening of a member function + Token *tok1 = newToken; + while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) + tok1 = tok1->previous(); + if (tok1->previous() && tok1->strAt(-1) == ")") { + tok1 = tok1->linkAt(-1); + if (Token::Match(tok1->previous(), "throw|noexcept")) { + tok1 = tok1->previous(); + while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) + tok1 = tok1->previous(); + if (tok1->strAt(-1) != ")") + return newToken; + } else if (Token::Match(newToken->tokAt(-2), ":|, %name%")) { + tok1 = tok1->tokAt(-2); + if (tok1->strAt(-1) != ")") + return newToken; + } + if (tok1->strAt(-1) == ">") + tok1 = tok1->previous()->findOpeningBracket(); + if (tok1 && Token::Match(tok1->tokAt(-3), "%name% :: %name%")) { + tok1 = tok1->tokAt(-2); + // cppcheck-suppress shadowFunction - TODO: fix this + std::string scope = tok1->strAt(-1); + while (Token::Match(tok1->tokAt(-2), ":: %name%")) { + scope = tok1->strAt(-3) + " :: " + scope; + tok1 = tok1->tokAt(-2); + } + nextScopeNameAddition += scope; + } + } + + // Or it might be a namespace/class/struct + if (Token::Match(newToken->previous(), "%name%|>")) { + Token* nameTok = newToken->previous(); + while (nameTok && !Token::Match(nameTok, "namespace|class|struct|union %name% {|::|:|<")) { + nameTok = nameTok->previous(); + } + if (nameTok) { + for (nameTok = nameTok->next(); nameTok && !Token::Match(nameTok, "{|:|<"); nameTok = nameTok->next()) { + nextScopeNameAddition.append(nameTok->str()); + nextScopeNameAddition.append(" "); + } + if (!nextScopeNameAddition.empty()) + nextScopeNameAddition.pop_back(); + } + } + + // New scope is opening, record it here + std::shared_ptr newScopeInfo = std::make_shared(mImpl->mScopeInfo->name, nullptr, mImpl->mScopeInfo->usingNamespaces); + + if (!newScopeInfo->name.empty() && !nextScopeNameAddition.empty()) newScopeInfo->name.append(" :: "); + newScopeInfo->name.append(nextScopeNameAddition); + nextScopeNameAddition = ""; + + newToken->scopeInfo(std::move(newScopeInfo)); + } else if (newToken->str() == "}") { + Token* matchingTok = newToken->previous(); + int depth = 0; + while (matchingTok && (depth != 0 || !Token::simpleMatch(matchingTok, "{"))) { + if (Token::simpleMatch(matchingTok, "}")) depth++; + if (Token::simpleMatch(matchingTok, "{")) depth--; + matchingTok = matchingTok->previous(); + } + if (matchingTok && matchingTok->previous()) { + newToken->mImpl->mScopeInfo = matchingTok->previous()->scopeInfo(); + } + } else { + if (prepend && newToken->previous()) { + newToken->mImpl->mScopeInfo = newToken->previous()->scopeInfo(); + } else { + newToken->mImpl->mScopeInfo = mImpl->mScopeInfo; + } + if (newToken->str() == ";") { + const Token* statementStart; + for (statementStart = newToken; statementStart->previous() && !Token::Match(statementStart->previous(), ";|{"); statementStart = statementStart->previous()); + if (Token::Match(statementStart, "using namespace %name% ::|;")) { + const Token * tok1 = statementStart->tokAt(2); + std::string nameSpace; + while (tok1 && tok1->str() != ";") { + if (!nameSpace.empty()) + nameSpace += " "; + nameSpace += tok1->str(); + tok1 = tok1->next(); + } + mImpl->mScopeInfo->usingNamespaces.insert(nameSpace); + } + } + } + } + } + return newToken; +} + +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(stringifyOptions::forPrintOut(), nullptr, nullptr) << std::endl; +} + +void Token::printOut(const char *title, const std::vector &fileNames) const +{ + if (title && title[0]) + std::cout << "\n### " << title << " ###\n"; + std::cout << stringifyList(stringifyOptions::forPrintOut(), &fileNames, nullptr) << std::endl; +} + +// cppcheck-suppress unusedFunction - used for debugging +void Token::printLines(int lines) const +{ + const Token *end = this; + while (end && end->linenr() < lines + linenr()) + end = end->next(); + std::cout << stringifyList(stringifyOptions::forDebugExprId(), nullptr, end) << std::endl; +} + +std::string Token::stringify(const stringifyOptions& options) const +{ + std::string ret; + if (options.attributes) { + if (isUnsigned()) + ret += "unsigned "; + else if (isSigned()) + ret += "signed "; + if (isComplex()) + ret += "_Complex "; + if (isLong()) { + if (!(mTokType == eString || mTokType == eChar)) + ret += "long "; + } + } + if (options.macro && isExpandedMacro()) + ret += '$'; + if (isName() && mStr.find(' ') != std::string::npos) { + for (const char i : mStr) { + if (i != ' ') + ret += i; + } + } else if (mStr[0] != '\"' || mStr.find('\0') == std::string::npos) + ret += mStr; + else { + for (const char i : mStr) { + if (i == '\0') + ret += "\\0"; + else + ret += i; + } + } + if (options.varid && mImpl->mVarId != 0) { + ret += '@'; + ret += (options.idtype ? "var" : ""); + ret += std::to_string(mImpl->mVarId); + } else if (options.exprid && mImpl->mExprId != 0) { + ret += '@'; + ret += (options.idtype ? "expr" : ""); + if ((mImpl->mExprId & (1U << efIsUnique)) != 0) + ret += "UNIQUE"; + else + ret += std::to_string(mImpl->mExprId); + } + + return ret; +} + +std::string Token::stringify(bool varid, bool attributes, bool macro) const +{ + stringifyOptions options; + options.varid = varid; + options.attributes = attributes; + options.macro = macro; + return stringify(options); +} + +std::string Token::stringifyList(const stringifyOptions& options, const std::vector* fileNames, const Token* end) const +{ + if (this == end) + return ""; + + std::string ret; + + unsigned int lineNumber = mImpl->mLineNumber - (options.linenumbers ? 1U : 0U); + // cppcheck-suppress shadowFunction - TODO: fix this + unsigned int fileIndex = options.files ? ~0U : mImpl->mFileIndex; + std::map lineNumbers; + for (const Token *tok = this; tok != end; tok = tok->next()) { + assert(tok && "end precedes token"); + if (!tok) + return ret; + bool fileChange = false; + if (tok->mImpl->mFileIndex != fileIndex) { + if (fileIndex != ~0U) { + lineNumbers[fileIndex] = tok->mImpl->mFileIndex; + } + + fileIndex = tok->mImpl->mFileIndex; + if (options.files) { + ret += "\n\n##file "; + if (fileNames && fileNames->size() > tok->mImpl->mFileIndex) + ret += fileNames->at(tok->mImpl->mFileIndex); + else + ret += std::to_string(fileIndex); + ret += '\n'; + } + + lineNumber = lineNumbers[fileIndex]; + fileChange = true; + } + + if (options.linebreaks && (lineNumber != tok->linenr() || fileChange)) { + if (lineNumber+4 < tok->linenr() && fileIndex == tok->mImpl->mFileIndex) { + ret += '\n'; + ret += std::to_string(lineNumber+1); + ret += ":\n|\n"; + ret += std::to_string(tok->linenr()-1); + ret += ":\n"; + ret += std::to_string(tok->linenr()); + ret += ": "; + } else if (this == tok && options.linenumbers) { + ret += std::to_string(tok->linenr()); + ret += ": "; + } else if (lineNumber > tok->linenr()) { + lineNumber = tok->linenr(); + ret += '\n'; + if (options.linenumbers) { + ret += std::to_string(lineNumber); + ret += ':'; + ret += ' '; + } + } else { + while (lineNumber < tok->linenr()) { + ++lineNumber; + ret += '\n'; + if (options.linenumbers) { + ret += std::to_string(lineNumber); + ret += ':'; + if (lineNumber == tok->linenr()) + ret += ' '; + } + } + } + lineNumber = tok->linenr(); + } + + ret += tok->stringify(options); // print token + if (tok->next() != end && (!options.linebreaks || (tok->next()->linenr() == tok->linenr() && tok->next()->fileIndex() == tok->fileIndex()))) + ret += ' '; + } + if (options.linebreaks && (options.files || options.linenumbers)) + ret += '\n'; + return ret; +} +std::string Token::stringifyList(bool varid, bool attributes, bool linenumbers, bool linebreaks, bool files, const std::vector* fileNames, const Token* end) const +{ + stringifyOptions options; + options.varid = varid; + options.attributes = attributes; + options.macro = attributes; + options.linenumbers = linenumbers; + options.linebreaks = linebreaks; + options.files = files; + return stringifyList(options, fileNames, end); +} + +std::string Token::stringifyList(const Token* end, bool attributes) const +{ + return stringifyList(false, attributes, false, false, false, nullptr, end); +} + +std::string Token::stringifyList(bool varid) const +{ + return stringifyList(varid, false, true, true, true, nullptr, nullptr); +} + +void Token::astParent(Token* tok) +{ + const Token* tok2 = tok; + while (tok2) { + if (this == tok2) + throw InternalError(this, "Internal error. AST cyclic dependency."); + tok2 = tok2->astParent(); + } + // Clear children to avoid nodes referenced twice + if (this->astParent()) { + Token* parent = this->astParent(); + if (parent->astOperand1() == this) + parent->mImpl->mAstOperand1 = nullptr; + if (parent->astOperand2() == this) + parent->mImpl->mAstOperand2 = nullptr; + } + mImpl->mAstParent = tok; +} + +void Token::astOperand1(Token *tok) +{ + if (mImpl->mAstOperand1) + mImpl->mAstOperand1->astParent(nullptr); + // goto parent operator + if (tok) { + tok = tok->astTop(); + tok->astParent(this); + } + mImpl->mAstOperand1 = tok; +} + +void Token::astOperand2(Token *tok) +{ + if (mImpl->mAstOperand2) + mImpl->mAstOperand2->astParent(nullptr); + // goto parent operator + if (tok) { + tok = tok->astTop(); + tok->astParent(this); + } + mImpl->mAstOperand2 = tok; +} + +static const Token* goToLeftParenthesis(const Token* start, const Token* end) +{ + // move start to lpar in such expression: '(*it).x' + int par = 0; + for (const Token *tok = start; tok && tok != end; tok = tok->next()) { + if (tok->str() == "(") + ++par; + else if (tok->str() == ")") { + if (par == 0) + start = tok->link(); + else + --par; + } + } + return start; +} + +static const Token* goToRightParenthesis(const Token* start, const Token* end) +{ + // move end to rpar in such expression: '2>(x+1)' + int par = 0; + for (const Token *tok = end; tok && tok != start; tok = tok->previous()) { + if (tok->str() == ")") + ++par; + else if (tok->str() == "(") { + if (par == 0) + end = tok->link(); + else + --par; + } + } + return end; +} + +std::pair Token::findExpressionStartEndTokens() const +{ + const Token * const top = this; + + // find start node in AST tree + const Token *start = top; + while (start->astOperand1() && precedes(start->astOperand1(), start)) + start = start->astOperand1(); + + // find end node in AST tree + const Token *end = top; + while (end->astOperand1() && (end->astOperand2() || end->isUnaryPreOp())) { + // lambda.. + if (end->str() == "[") { + const Token *lambdaEnd = findLambdaEndToken(end); + if (lambdaEnd) { + end = lambdaEnd; + break; + } + } + if (Token::Match(end,"(|[|{") && + !(Token::Match(end, "( ::| %type%") && !end->astOperand2())) { + end = end->link(); + break; + } + end = end->astOperand2() ? end->astOperand2() : end->astOperand1(); + } + + // skip parentheses + start = goToLeftParenthesis(start, end); + end = goToRightParenthesis(start, end); + if (Token::simpleMatch(end, "{")) + end = end->link(); + + if (precedes(top, start)) + throw InternalError(start, "Cannot find start of expression"); + if (succeeds(top, end)) + throw InternalError(end, "Cannot find end of expression"); + + return std::pair(start,end); +} + +bool Token::isCalculation() const +{ + if (!Token::Match(this, "%cop%|++|--")) + return false; + + if (Token::Match(this, "*|&")) { + // dereference or address-of? + if (!this->astOperand2()) + return false; + + if (this->astOperand2()->str() == "[") + return false; + + // type specification? + std::stack operands; + operands.push(this); + while (!operands.empty()) { + const Token *op = operands.top(); + operands.pop(); + if (op->isNumber() || op->varId() > 0) + return true; + if (op->astOperand1()) + operands.push(op->astOperand1()); + if (op->astOperand2()) + operands.push(op->astOperand2()); + else if (Token::Match(op, "*|&")) + return false; + } + + // type specification => return false + return false; + } + + return true; +} + +bool Token::isUnaryPreOp() const +{ + if (!astOperand1() || astOperand2()) + return false; + if (this->tokType() != Token::eIncDecOp) + return true; + const Token *tokbefore = mPrevious; + const Token *tokafter = mNext; + for (int distance = 1; distance < 10 && tokbefore; distance++) { + if (tokbefore == mImpl->mAstOperand1) + return false; + if (tokafter == mImpl->mAstOperand1) + return true; + tokbefore = tokbefore->mPrevious; + tokafter = tokafter->mPrevious; + } + return false; // <- guess +} + +static std::string stringFromTokenRange(const Token* start, const Token* end) +{ + std::string ret; + if (end) + end = end->next(); + for (const Token *tok = start; tok && tok != end; tok = tok->next()) { + if (tok->isUnsigned()) + ret += "unsigned "; + if (tok->isLong() && !tok->isLiteral()) + ret += "long "; + if (tok->tokType() == Token::eString) { + for (const unsigned char c: tok->str()) { + if (c == '\n') + ret += "\\n"; + else if (c == '\r') + ret += "\\r"; + else if (c == '\t') + ret += "\\t"; + else if (c >= ' ' && c <= 126) + ret += c; + else { + char str[10]; + sprintf(str, "\\x%02x", c); + ret += str; + } + } + } else if (tok->originalName().empty() || tok->isUnsigned() || tok->isLong()) { + ret += tok->str(); + } else + ret += tok->originalName(); + if (Token::Match(tok, "%name%|%num% %name%|%num%")) + ret += ' '; + } + return ret; +} + +std::string Token::expressionString() const +{ + const auto tokens = findExpressionStartEndTokens(); + return stringFromTokenRange(tokens.first, tokens.second); +} + +static void astStringXml(const Token *tok, nonneg int indent, std::ostream &out) +{ + const std::string strindent(indent, ' '); + + out << strindent << "str() << '\"'; + if (tok->varId()) + out << " varId=\"" << 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, const std::vector &fileNames, std::ostream &out) const +{ + if (!xml) + out << "\n\n##AST" << std::endl; + + std::set printed; + for (const Token *tok = this; tok; tok = tok->next()) { + if (!tok->mImpl->mAstParent && tok->mImpl->mAstOperand1) { + if (printed.find(tok) != printed.end()) + continue; + printed.insert(tok); + + if (xml) { + out << "scope() << "\" fileIndex=\"" << tok->fileIndex() << "\" linenr=\"" << tok->linenr() + << "\" column=\"" << tok->column() << "\">" << std::endl; + astStringXml(tok, 2U, out); + out << "" << std::endl; + } else if (verbose) + out << "[" << fileNames[tok->fileIndex()] << ":" << tok->linenr() << "]" << std::endl << tok->astStringVerbose() << std::endl; + else + out << tok->astString(" ") << std::endl; + if (tok->str() == "(") + tok = tok->link(); + } + } +} + +static void indent(std::string &str, const nonneg int indent1, const nonneg int indent2) +{ + for (int i = 0; i < indent1; ++i) + str += ' '; + for (int i = indent1; i < indent2; i += 2) + str += "| "; +} + +void Token::astStringVerboseRecursive(std::string& ret, const nonneg int indent1, const nonneg int indent2) const +{ + if (isExpandedMacro()) + ret += '$'; + ret += mStr; + if (mImpl->mValueType) + ret += " \'" + mImpl->mValueType->str() + '\''; + if (function()) { + std::ostringstream ostr; + ostr << std::hex << function(); + ret += " f:" + ostr.str(); + } + ret += '\n'; + + if (mImpl->mAstOperand1) { + int i1 = indent1, i2 = indent2 + 2; + if (indent1 == indent2 && !mImpl->mAstOperand2) + i1 += 2; + indent(ret, indent1, indent2); + ret += mImpl->mAstOperand2 ? "|-" : "`-"; + mImpl->mAstOperand1->astStringVerboseRecursive(ret, i1, i2); + } + if (mImpl->mAstOperand2) { + int i1 = indent1, i2 = indent2 + 2; + if (indent1 == indent2) + i1 += 2; + indent(ret, indent1, indent2); + ret += "`-"; + mImpl->mAstOperand2->astStringVerboseRecursive(ret, i1, i2); + } +} + +std::string Token::astStringVerbose() const +{ + std::string ret; + astStringVerboseRecursive(ret); + return ret; +} + +std::string Token::astStringZ3() const +{ + if (!astOperand1()) + return str(); + if (!astOperand2()) + return "(" + str() + " " + astOperand1()->astStringZ3() + ")"; + return "(" + str() + " " + astOperand1()->astStringZ3() + " " + astOperand2()->astStringZ3() + ")"; +} + +void Token::printValueFlow(bool xml, std::ostream &out) const +{ + std::string outs; + + // cppcheck-suppress shadowFunction + int fileIndex = -1; + int line = 0; + if (xml) + outs += " \n"; + else + outs += "\n\n##Value flow\n"; + for (const Token *tok = this; tok; tok = tok->next()) { + // cppcheck-suppress shadowFunction - TODO: fix this + const auto* const values = tok->mImpl->mValues; + if (!values) + continue; + if (values->empty()) // Values might be removed by removeContradictions + continue; + if (xml) { + outs += " "; + outs += '\n'; + } + else { + if (fileIndex != tok->fileIndex()) { + outs += "File "; + outs += tok->mTokensFrontBack.list.getFiles()[tok->fileIndex()]; + outs += '\n'; + line = 0; + } + if (line != tok->linenr()) { + outs += "Line "; + outs += std::to_string(tok->linenr()); + outs += '\n'; + } + } + fileIndex = tok->fileIndex(); + line = tok->linenr(); + if (!xml) { + ValueFlow::Value::ValueKind valueKind = values->front().valueKind; + const bool same = std::all_of(values->begin(), values->end(), [&](const ValueFlow::Value& value) { + return value.valueKind == valueKind; + }); + outs += " "; + outs += tok->str(); + outs += " "; + if (same) { + switch (valueKind) { + case ValueFlow::Value::ValueKind::Impossible: + case ValueFlow::Value::ValueKind::Known: + outs += "always "; + break; + case ValueFlow::Value::ValueKind::Inconclusive: + outs += "inconclusive "; + break; + case ValueFlow::Value::ValueKind::Possible: + outs += "possible "; + break; + } + } + if (values->size() > 1U) + outs += '{'; + } + for (const ValueFlow::Value& value : *values) { + if (xml) { + outs += " valueType() && tok->valueType()->sign == ValueType::UNSIGNED) { + outs += "intvalue=\""; + outs += std::to_string(static_cast(value.intvalue)); + outs += '\"'; + } + else { + outs += "intvalue=\""; + outs += std::to_string(value.intvalue); + outs += '\"'; + } + break; + case ValueFlow::Value::ValueType::TOK: + outs += "tokvalue=\""; + outs += id_string(value.tokvalue); + outs += '\"'; + break; + case ValueFlow::Value::ValueType::FLOAT: + outs += "floatvalue=\""; + outs += std::to_string(value.floatValue); // TODO: should this be MathLib::toString()? + outs += '\"'; + break; + case ValueFlow::Value::ValueType::MOVED: + outs += "movedvalue=\""; + outs += ValueFlow::Value::toString(value.moveKind); + outs += '\"'; + break; + case ValueFlow::Value::ValueType::UNINIT: + outs += "uninit=\"1\""; + break; + case ValueFlow::Value::ValueType::BUFFER_SIZE: + outs += "buffer-size=\""; + outs += std::to_string(value.intvalue); + outs += "\""; + break; + case ValueFlow::Value::ValueType::CONTAINER_SIZE: + outs += "container-size=\""; + outs += std::to_string(value.intvalue); + outs += '\"'; + break; + case ValueFlow::Value::ValueType::ITERATOR_START: + outs += "iterator-start=\""; + outs += std::to_string(value.intvalue); + outs += '\"'; + break; + case ValueFlow::Value::ValueType::ITERATOR_END: + outs += "iterator-end=\""; + outs += std::to_string(value.intvalue); + outs += '\"'; + break; + case ValueFlow::Value::ValueType::LIFETIME: + outs += "lifetime=\""; + outs += id_string(value.tokvalue); + outs += '\"'; + outs += " lifetime-scope=\""; + outs += ValueFlow::Value::toString(value.lifetimeScope); + outs += "\""; + outs += " lifetime-kind=\""; + outs += ValueFlow::Value::toString(value.lifetimeKind); + outs += "\""; + break; + case ValueFlow::Value::ValueType::SYMBOLIC: + outs += "symbolic=\""; + outs += id_string(value.tokvalue); + outs += '\"'; + outs += " symbolic-delta=\""; + outs += std::to_string(value.intvalue); + outs += '\"'; + break; + } + outs += " bound=\""; + outs += ValueFlow::Value::toString(value.bound); + outs += "\""; + if (value.condition) { + outs += " condition-line=\""; + outs += std::to_string(value.condition->linenr()); + outs += '\"'; + } + if (value.isKnown()) + outs += " known=\"true\""; + else if (value.isPossible()) + outs += " possible=\"true\""; + else if (value.isImpossible()) + outs += " impossible=\"true\""; + else if (value.isInconclusive()) + outs += " inconclusive=\"true\""; + + outs += " path=\""; + outs += std::to_string(value.path); + outs += "\""; + + outs += "/>\n"; + } + + else { + if (&value != &values->front()) + outs += ","; + outs += value.toString(); + } + } + if (xml) + outs += " \n"; + else if (values->size() > 1U) + outs += "}\n"; + else + outs += '\n'; + } + if (xml) + outs += " \n"; + + out << outs; +} + +const ValueFlow::Value * Token::getValueLE(const MathLib::bigint val, const Settings *settings) const +{ + if (!mImpl->mValues) + return nullptr; + return ValueFlow::findValue(*mImpl->mValues, settings, [&](const ValueFlow::Value& v) { + return !v.isImpossible() && v.isIntValue() && v.intvalue <= val; + }); +} + +const ValueFlow::Value * Token::getValueGE(const MathLib::bigint val, const Settings *settings) const +{ + if (!mImpl->mValues) + return nullptr; + return ValueFlow::findValue(*mImpl->mValues, settings, [&](const ValueFlow::Value& v) { + return !v.isImpossible() && v.isIntValue() && v.intvalue >= val; + }); +} + +const ValueFlow::Value * Token::getInvalidValue(const Token *ftok, nonneg int argnr, const Settings *settings) const +{ + if (!mImpl->mValues || !settings) + return nullptr; + const ValueFlow::Value *ret = nullptr; + for (std::list::const_iterator it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) { + if (it->isImpossible()) + continue; + if ((it->isIntValue() && !settings->library.isIntArgValid(ftok, argnr, it->intvalue)) || + (it->isFloatValue() && !settings->library.isFloatArgValid(ftok, argnr, it->floatValue))) { + if (!ret || ret->isInconclusive() || (ret->condition && !it->isInconclusive())) + ret = &(*it); + if (!ret->isInconclusive() && !ret->condition) + break; + } + } + if (ret) { + if (ret->isInconclusive() && !settings->certainty.isEnabled(Certainty::inconclusive)) + return nullptr; + if (ret->condition && !settings->severity.isEnabled(Severity::warning)) + return nullptr; + } + return ret; +} + +const Token *Token::getValueTokenMinStrSize(const Settings &settings, MathLib::bigint* path) const +{ + if (!mImpl->mValues) + return nullptr; + const Token *ret = nullptr; + int minsize = INT_MAX; + for (std::list::const_iterator it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) { + if (it->isTokValue() && it->tokvalue && it->tokvalue->tokType() == Token::eString) { + const int size = getStrSize(it->tokvalue, settings); + if (!ret || size < minsize) { + minsize = size; + ret = it->tokvalue; + if (path) + *path = it->path; + } + } + } + return ret; +} + +const Token *Token::getValueTokenMaxStrLength() const +{ + if (!mImpl->mValues) + return nullptr; + const Token *ret = nullptr; + int maxlength = 0; + for (std::list::const_iterator it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) { + if (it->isTokValue() && it->tokvalue && it->tokvalue->tokType() == Token::eString) { + const int length = getStrLength(it->tokvalue); + if (!ret || length > maxlength) { + maxlength = length; + ret = it->tokvalue; + } + } + } + return ret; +} + +static bool isAdjacent(const ValueFlow::Value& x, const ValueFlow::Value& y) +{ + if (x.bound != ValueFlow::Value::Bound::Point && x.bound == y.bound) + return true; + if (x.valueType == ValueFlow::Value::ValueType::FLOAT) + return false; + return std::abs(x.intvalue - y.intvalue) == 1; +} + +static bool removePointValue(std::list& values, std::list::iterator& x) +{ + const bool isPoint = x->bound == ValueFlow::Value::Bound::Point; + if (!isPoint) + x->decreaseRange(); + else + x = values.erase(x); + return isPoint; +} + +static bool removeContradiction(std::list& values) +{ + bool result = false; + for (auto itx = values.begin(); itx != values.end(); ++itx) { + if (itx->isNonValue()) + continue; + + auto ity = itx; + ++ity; + for (; ity != values.end(); ++ity) { + if (ity->isNonValue()) + continue; + if (*itx == *ity) + continue; + if (itx->valueType != ity->valueType) + continue; + if (itx->isImpossible() == ity->isImpossible()) + continue; + if (itx->isSymbolicValue() && !ValueFlow::Value::sameToken(itx->tokvalue, ity->tokvalue)) + continue; + if (!itx->equalValue(*ity)) { + auto compare = [](const std::list::const_iterator& x, const std::list::const_iterator& y) { + return x->compareValue(*y, less{}); + }; + auto itMax = std::max(itx, ity, compare); + auto itMin = std::min(itx, ity, compare); + // TODO: Adjust non-points instead of removing them + if (itMax->isImpossible() && itMax->bound == ValueFlow::Value::Bound::Upper) { + values.erase(itMin); + return true; + } + if (itMin->isImpossible() && itMin->bound == ValueFlow::Value::Bound::Lower) { + values.erase(itMax); + return true; + } + continue; + } + const bool removex = !itx->isImpossible() || ity->isKnown(); + const bool removey = !ity->isImpossible() || itx->isKnown(); + if (itx->bound == ity->bound) { + if (removex) + values.erase(itx); + if (removey) + values.erase(ity); + // itx and ity are invalidated + return true; + } + result = removex || removey; + bool bail = false; + if (removex && removePointValue(values, itx)) + bail = true; + if (removey && removePointValue(values, ity)) + bail = true; + if (bail) + return true; + } + } + return result; +} + +using ValueIterator = std::list::iterator; + +template +static ValueIterator removeAdjacentValues(std::list& values, ValueIterator x, Iterator start, Iterator last) +{ + if (!isAdjacent(*x, **start)) + return std::next(x); + auto it = std::adjacent_find(start, last, [](ValueIterator x, ValueIterator y) { + return !isAdjacent(*x, *y); + }); + if (it == last) + it--; + (*it)->bound = x->bound; + std::for_each(std::move(start), std::move(it), [&](ValueIterator y) { + values.erase(y); + }); + return values.erase(x); +} + +static void mergeAdjacent(std::list& values) +{ + for (auto x = values.begin(); x != values.end();) { + if (x->isNonValue()) { + x++; + continue; + } + if (x->bound == ValueFlow::Value::Bound::Point) { + x++; + continue; + } + std::vector adjValues; + for (auto y = values.begin(); y != values.end(); y++) { + if (x == y) + continue; + if (y->isNonValue()) + continue; + if (x->valueType != y->valueType) + continue; + if (x->valueKind != y->valueKind) + continue; + if (x->isSymbolicValue() && !ValueFlow::Value::sameToken(x->tokvalue, y->tokvalue)) + continue; + if (x->bound != y->bound) { + if (y->bound != ValueFlow::Value::Bound::Point && isAdjacent(*x, *y)) { + adjValues.clear(); + break; + } + // No adjacent points for floating points + if (x->valueType == ValueFlow::Value::ValueType::FLOAT) + continue; + if (y->bound != ValueFlow::Value::Bound::Point) + continue; + } + if (x->bound == ValueFlow::Value::Bound::Lower && !y->compareValue(*x, less{})) + continue; + if (x->bound == ValueFlow::Value::Bound::Upper && !x->compareValue(*y, less{})) + continue; + adjValues.push_back(y); + } + if (adjValues.empty()) { + x++; + continue; + } + std::sort(adjValues.begin(), adjValues.end(), [&values](ValueIterator xx, ValueIterator yy) { + (void)values; + assert(xx != values.end() && yy != values.end()); + return xx->compareValue(*yy, less{}); + }); + if (x->bound == ValueFlow::Value::Bound::Lower) + x = removeAdjacentValues(values, x, adjValues.rbegin(), adjValues.rend()); + else if (x->bound == ValueFlow::Value::Bound::Upper) + x = removeAdjacentValues(values, x, adjValues.begin(), adjValues.end()); + } +} + +static void removeOverlaps(std::list& values) +{ + for (const ValueFlow::Value& x : values) { + if (x.isNonValue()) + continue; + values.remove_if([&](const ValueFlow::Value& y) { + if (y.isNonValue()) + return false; + if (&x == &y) + return false; + if (x.valueType != y.valueType) + return false; + if (x.valueKind != y.valueKind) + return false; + // TODO: Remove points covered in a lower or upper bound + // TODO: Remove lower or upper bound already covered by a lower and upper bound + if (!x.equalValue(y)) + return false; + if (x.bound != y.bound) + return false; + return true; + }); + } + mergeAdjacent(values); +} + +// Removing contradictions is an NP-hard problem. Instead we run multiple +// passes to try to catch most contradictions +static void removeContradictions(std::list& values) +{ + removeOverlaps(values); + for (int i = 0; i < 4; i++) { + if (!removeContradiction(values)) + return; + removeOverlaps(values); + } +} + +static bool sameValueType(const ValueFlow::Value& x, const ValueFlow::Value& y) +{ + if (x.valueType != y.valueType) + return false; + // Symbolic are the same type if they share the same tokvalue + if (x.isSymbolicValue()) + return x.tokvalue->exprId() == 0 || x.tokvalue->exprId() == y.tokvalue->exprId(); + return true; +} + +bool Token::addValue(const ValueFlow::Value &value) +{ + if (value.isKnown() && mImpl->mValues) { + // Clear all other values of the same type since value is known + mImpl->mValues->remove_if([&](const ValueFlow::Value& x) { + return sameValueType(x, value); + }); + } + + // Don't add a value if its already known + if (!value.isKnown() && mImpl->mValues && + std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), [&](const ValueFlow::Value& x) { + return x.isKnown() && sameValueType(x, value) && !x.equalValue(value); + })) + return false; + + // assert(value.isKnown() || !mImpl->mValues || std::none_of(mImpl->mValues->begin(), mImpl->mValues->end(), + // [&](const ValueFlow::Value& x) { + // return x.isKnown() && sameValueType(x, value); + // })); + + if (mImpl->mValues) { + // Don't handle more than 10 values for performance reasons + // TODO: add setting? + if (mImpl->mValues->size() >= 10U) + return false; + + // if value already exists, don't add it again + std::list::iterator it; + for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) { + // different types => continue + if (it->valueType != value.valueType) + continue; + + if (it->isImpossible() != value.isImpossible()) + continue; + + // different value => continue + if (!it->equalValue(value)) + continue; + + if ((value.isTokValue() || value.isLifetimeValue()) && (it->tokvalue != value.tokvalue) && (it->tokvalue->str() != value.tokvalue->str())) + continue; + + // same value, but old value is inconclusive so replace it + if (it->isInconclusive() && !value.isInconclusive() && !value.isImpossible()) { + *it = value; + if (it->varId == 0) + it->varId = mImpl->mVarId; + break; + } + + // Same value already exists, don't add new value + return false; + } + + // Add value + if (it == mImpl->mValues->end()) { + ValueFlow::Value v(value); + if (v.varId == 0) + v.varId = mImpl->mVarId; + if (v.isKnown() && v.isIntValue()) + mImpl->mValues->push_front(std::move(v)); + else + mImpl->mValues->push_back(std::move(v)); + } + } else { + ValueFlow::Value v(value); + if (v.varId == 0) + v.varId = mImpl->mVarId; + mImpl->mValues = new std::list; + mImpl->mValues->push_back(std::move(v)); + } + + removeContradictions(*mImpl->mValues); + + return true; +} + +void Token::assignProgressValues(Token *tok) +{ + int total_count = 0; + for (Token *tok2 = tok; tok2; tok2 = tok2->next()) + ++total_count; + int count = 0; + for (Token *tok2 = tok; tok2; tok2 = tok2->next()) + tok2->mImpl->mProgressValue = count++ *100 / total_count; +} + +void Token::assignIndexes() +{ + // cppcheck-suppress shadowFunction - TODO: fix this + int index = (mPrevious ? mPrevious->mImpl->mIndex : 0) + 1; + for (Token *tok = this; tok; tok = tok->next()) + tok->mImpl->mIndex = index++; +} + +void Token::setValueType(ValueType *vt) +{ + if (vt != mImpl->mValueType) { + delete mImpl->mValueType; + mImpl->mValueType = vt; + } +} + +void Token::type(const ::Type *t) +{ + mImpl->mType = t; + if (t) { + tokType(eType); + isEnumType(mImpl->mType->isEnumType()); + } else if (mTokType == eType) + tokType(eName); +} + +const ::Type* Token::typeOf(const Token* tok, const Token** typeTok) +{ + if (!tok) + return nullptr; + if (typeTok != nullptr) + *typeTok = tok; + const Token* lhsVarTok{}; + if (tok->type()) + return tok->type(); + if (tok->variable()) + return tok->variable()->type(); + if (tok->function()) + return tok->function()->retType; + if (Token::simpleMatch(tok, "return")) { + // cppcheck-suppress shadowFunction - TODO: fix this + const Scope *scope = tok->scope(); + if (!scope) + return nullptr; + // cppcheck-suppress shadowFunction - TODO: fix this + const Function *function = scope->function; + if (!function) + return nullptr; + return function->retType; + } + if (Token::Match(tok->previous(), "%type%|= (|{")) + return typeOf(tok->previous(), typeTok); + if (Token::simpleMatch(tok, "=") && (lhsVarTok = getLHSVariableToken(tok)) != tok->next()) + return Token::typeOf(lhsVarTok, typeTok); + if (Token::simpleMatch(tok, ".")) + return Token::typeOf(tok->astOperand2(), typeTok); + if (Token::simpleMatch(tok, "[")) + return Token::typeOf(tok->astOperand1(), typeTok); + if (Token::simpleMatch(tok, "{")) { + int argnr; + const Token* ftok = getTokenArgumentFunction(tok, argnr); + if (argnr < 0) + return nullptr; + if (!ftok) + return nullptr; + if (ftok == tok) + return nullptr; + std::vector vars = getArgumentVars(ftok, argnr); + if (vars.empty()) + return nullptr; + if (std::all_of( + vars.cbegin(), vars.cend(), [&](const Variable* var) { + return var->type() == vars.front()->type(); + })) + return vars.front()->type(); + } + + return nullptr; +} + +std::pair Token::typeDecl(const Token* tok, bool pointedToType) +{ + if (!tok) + return {}; + if (tok->type()) + return {tok, tok->next()}; + if (tok->variable()) { + const Variable *var = tok->variable(); + if (!var->typeStartToken() || !var->typeEndToken()) + return {}; + if (pointedToType && astIsSmartPointer(var->nameToken())) { + const ValueType* vt = var->valueType(); + if (vt && vt->smartPointerTypeToken) + return { vt->smartPointerTypeToken, vt->smartPointerTypeToken->linkAt(-1) }; + } + if (pointedToType && astIsIterator(var->nameToken())) { + const ValueType* vt = var->valueType(); + if (vt && vt->containerTypeToken) + return { vt->containerTypeToken, vt->containerTypeToken->linkAt(-1) }; + } + std::pair result; + if (Token::simpleMatch(var->typeStartToken(), "auto")) { + const Token * tok2 = var->declEndToken(); + if (Token::Match(tok2, "; %varid% =", var->declarationId())) + tok2 = tok2->tokAt(2); + if (Token::simpleMatch(tok2, "=") && Token::Match(tok2->astOperand2(), "!!=") && tok != tok2->astOperand2()) { + tok2 = tok2->astOperand2(); + + if (Token::simpleMatch(tok2, "[") && tok2->astOperand1()) { + const ValueType* vt = tok2->astOperand1()->valueType(); + if (vt && vt->containerTypeToken) + return { vt->containerTypeToken, vt->containerTypeToken->linkAt(-1) }; + } + + const Token* varTok = tok2; // try to find a variable + if (Token::Match(varTok, ":: %name%")) + varTok = varTok->next(); + while (Token::Match(varTok, "%name% ::")) + varTok = varTok->tokAt(2); + std::pair r = typeDecl(varTok); + if (r.first) + return r; + + if (pointedToType && tok2->astOperand1() && Token::simpleMatch(tok2, "new")) { + if (Token::simpleMatch(tok2->astOperand1(), "(")) + return { tok2->next(), tok2->astOperand1() }; + const Token* declEnd = nextAfterAstRightmostLeaf(tok2->astOperand1()); + if (Token::simpleMatch(declEnd, "<") && declEnd->link()) + declEnd = declEnd->link()->next(); + return { tok2->next(), declEnd }; + } + const Token *typeBeg{}, *typeEnd{}; + if (tok2->str() == "::" && Token::simpleMatch(tok2->astOperand2(), "{")) { // empty initlist + typeBeg = previousBeforeAstLeftmostLeaf(tok2); + typeEnd = tok2->astOperand2(); + } + else if (tok2->str() == "{") { + typeBeg = previousBeforeAstLeftmostLeaf(tok2); + typeEnd = tok2; + } + if (typeBeg) + result = { typeBeg->next(), typeEnd }; // handle smart pointers/iterators first + } + if (astIsRangeBasedForDecl(var->nameToken()) && astIsContainer(var->nameToken()->astParent()->astOperand2())) { // range-based for + const ValueType* vt = var->nameToken()->astParent()->astOperand2()->valueType(); + if (vt && vt->containerTypeToken) + return { vt->containerTypeToken, vt->containerTypeToken->linkAt(-1) }; + } + } + if (result.first) + return result; + return {var->typeStartToken(), var->typeEndToken()->next()}; + } + if (Token::simpleMatch(tok, "return")) { + // cppcheck-suppress shadowFunction - TODO: fix this + const Scope* scope = tok->scope(); + if (!scope) + return {}; + // cppcheck-suppress shadowFunction - TODO: fix this + const Function* function = scope->function; + if (!function) + return {}; + return { function->retDef, function->returnDefEnd() }; + } + if (tok->previous() && tok->previous()->function()) { + // cppcheck-suppress shadowFunction - TODO: fix this + const Function *function = tok->previous()->function(); + return {function->retDef, function->returnDefEnd()}; + } + if (Token::simpleMatch(tok, "=")) + return Token::typeDecl(tok->astOperand1()); + if (Token::simpleMatch(tok, ".")) + return Token::typeDecl(tok->astOperand2()); + + const ::Type * t = typeOf(tok); + if (!t || !t->classDef) + return {}; + return {t->classDef->next(), t->classDef->tokAt(2)}; +} +std::string Token::typeStr(const Token* tok) +{ + if (tok->valueType()) { + const ValueType * vt = tok->valueType(); + std::string ret = vt->str(); + if (!ret.empty()) + return ret; + } + std::pair r = Token::typeDecl(tok); + if (!r.first || !r.second) + return ""; + return r.first->stringifyList(r.second, false); +} + +void Token::scopeInfo(std::shared_ptr newScopeInfo) +{ + mImpl->mScopeInfo = std::move(newScopeInfo); +} +std::shared_ptr Token::scopeInfo() const +{ + return mImpl->mScopeInfo; +} + +bool Token::hasKnownIntValue() const +{ + if (!mImpl->mValues) + return false; + return std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), [](const ValueFlow::Value& value) { + return value.isKnown() && value.isIntValue(); + }); +} + +bool Token::hasKnownValue() const +{ + return mImpl->mValues && std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), std::mem_fn(&ValueFlow::Value::isKnown)); +} + +bool Token::hasKnownValue(ValueFlow::Value::ValueType t) const +{ + return mImpl->mValues && + std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), [&](const ValueFlow::Value& value) { + return value.isKnown() && value.valueType == t; + }); +} + +bool Token::hasKnownSymbolicValue(const Token* tok) const +{ + if (tok->exprId() == 0) + return false; + return mImpl->mValues && + std::any_of(mImpl->mValues->begin(), mImpl->mValues->end(), [&](const ValueFlow::Value& value) { + return value.isKnown() && value.isSymbolicValue() && value.tokvalue && + value.tokvalue->exprId() == tok->exprId(); + }); +} + +const ValueFlow::Value* Token::getKnownValue(ValueFlow::Value::ValueType t) const +{ + if (!mImpl->mValues) + return nullptr; + auto it = std::find_if(mImpl->mValues->begin(), mImpl->mValues->end(), [&](const ValueFlow::Value& value) { + return value.isKnown() && value.valueType == t; + }); + return it == mImpl->mValues->end() ? nullptr : &*it; +} + +const ValueFlow::Value* Token::getValue(const MathLib::bigint val) const +{ + if (!mImpl->mValues) + return nullptr; + const auto it = std::find_if(mImpl->mValues->begin(), mImpl->mValues->end(), [=](const ValueFlow::Value& value) { + return value.isIntValue() && !value.isImpossible() && value.intvalue == val; + }); + return it == mImpl->mValues->end() ? nullptr : &*it; +} + +template +static const ValueFlow::Value* getCompareValue(const std::list& values, + bool condition, + MathLib::bigint path, + Compare compare) +{ + const ValueFlow::Value* ret = nullptr; + for (const ValueFlow::Value& value : values) { + if (!value.isIntValue()) + continue; + if (value.isImpossible()) + continue; + if (path > -0 && value.path != 0 && value.path != path) + continue; + if ((!ret || compare(value.intvalue, ret->intvalue)) && ((value.condition != nullptr) == condition)) + ret = &value; + } + return ret; +} + +const ValueFlow::Value* Token::getMaxValue(bool condition, MathLib::bigint path) const +{ + if (!mImpl->mValues) + return nullptr; + return getCompareValue(*mImpl->mValues, condition, path, std::greater{}); +} + +const ValueFlow::Value* Token::getMinValue(bool condition, MathLib::bigint path) const +{ + if (!mImpl->mValues) + return nullptr; + return getCompareValue(*mImpl->mValues, condition, path, std::less{}); +} + +const ValueFlow::Value* Token::getMovedValue() const +{ + if (!mImpl->mValues) + return nullptr; + const auto it = std::find_if(mImpl->mValues->begin(), mImpl->mValues->end(), [](const ValueFlow::Value& value) { + return value.isMovedValue() && !value.isImpossible() && + value.moveKind != ValueFlow::Value::MoveKind::NonMovedVariable; + }); + return it == mImpl->mValues->end() ? nullptr : &*it; +} + +// cppcheck-suppress unusedFunction +const ValueFlow::Value* Token::getContainerSizeValue(const MathLib::bigint val) const +{ + if (!mImpl->mValues) + return nullptr; + const auto it = std::find_if(mImpl->mValues->begin(), mImpl->mValues->end(), [=](const ValueFlow::Value& value) { + return value.isContainerSizeValue() && !value.isImpossible() && value.intvalue == val; + }); + return it == mImpl->mValues->end() ? nullptr : &*it; +} + +TokenImpl::~TokenImpl() +{ + delete mOriginalName; + delete mValueType; + delete mValues; + + if (mTemplateSimplifierPointers) { + for (auto *templateSimplifierPointer : *mTemplateSimplifierPointers) { + templateSimplifierPointer->token(nullptr); + } + } + delete mTemplateSimplifierPointers; + + while (mCppcheckAttributes) { + CppcheckAttributes *c = mCppcheckAttributes; + mCppcheckAttributes = mCppcheckAttributes->next; + delete c; + } +} + +void TokenImpl::setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint value) +{ + CppcheckAttributes *attr = mCppcheckAttributes; + while (attr && attr->type != type) + attr = attr->next; + if (attr) + attr->value = value; + else { + attr = new CppcheckAttributes; + attr->type = type; + attr->value = value; + attr->next = mCppcheckAttributes; + mCppcheckAttributes = attr; + } +} + +bool TokenImpl::getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint &value) const +{ + CppcheckAttributes *attr = mCppcheckAttributes; + while (attr && attr->type != type) + attr = attr->next; + if (attr) + value = attr->value; + return attr != nullptr; +} + +Token* findTypeEnd(Token* tok) +{ + while (Token::Match(tok, "%name%|.|::|*|&|&&|<|(|template|decltype|sizeof")) { + if (Token::Match(tok, "(|<")) + tok = tok->link(); + if (!tok) + return nullptr; + tok = tok->next(); + } + return tok; +} + +Token* findLambdaEndScope(Token* tok) +{ + if (!Token::simpleMatch(tok, "[")) + return nullptr; + tok = tok->link(); + if (!Token::Match(tok, "] (|{")) + return nullptr; + tok = tok->linkAt(1); + if (Token::simpleMatch(tok, "}")) + return tok; + if (Token::simpleMatch(tok, ") {")) + return tok->linkAt(1); + if (!Token::simpleMatch(tok, ")")) + return nullptr; + tok = tok->next(); + while (Token::Match(tok, "mutable|constexpr|consteval|noexcept|.")) { + if (Token::simpleMatch(tok, "noexcept (")) + tok = tok->linkAt(1); + if (Token::simpleMatch(tok, ".")) { + tok = findTypeEnd(tok); + break; + } + tok = tok->next(); + } + if (Token::simpleMatch(tok, "{")) + return tok->link(); + return nullptr; +} +const Token* findLambdaEndScope(const Token* tok) { + return findLambdaEndScope(const_cast(tok)); +} + +bool Token::isCpp() const +{ + return mTokensFrontBack.list.isCPP(); +} + +bool Token::isC() const +{ + return mTokensFrontBack.list.isC(); +} diff --git a/cppcheck-2.14.0/lib/token.h b/cppcheck-2.14.0/lib/token.h new file mode 100644 index 00000000..11e815aa --- /dev/null +++ b/cppcheck-2.14.0/lib/token.h @@ -0,0 +1,1493 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef tokenH +#define tokenH +//--------------------------------------------------------------------------- + +#include "config.h" +#include "mathlib.h" +#include "templatesimplifier.h" +#include "utils.h" +#include "vfvalue.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct Enumerator; +class Function; +class Scope; +class Settings; +class Type; +class ValueType; +class Variable; +class ConstTokenRange; +class Token; +struct TokensFrontBack; + +struct ScopeInfo2 { + ScopeInfo2(std::string name_, const Token *bodyEnd_, std::set usingNamespaces_ = std::set()) : name(std::move(name_)), bodyEnd(bodyEnd_), usingNamespaces(std::move(usingNamespaces_)) {} + std::string name; + const Token* const bodyEnd{}; + std::set usingNamespaces; +}; + +enum class TokenDebug { None, ValueFlow, ValueType }; + +struct TokenImpl { + nonneg int mVarId{}; + nonneg int mFileIndex{}; + nonneg int mLineNumber{}; + nonneg int mColumn{}; + nonneg int mExprId{}; + + /** + * A value from 0-100 that provides a rough idea about where in the token + * list this token is located. + */ + nonneg int mProgressValue{}; + + /** + * Token index. Position in token list + */ + nonneg int mIndex{}; + + /** Bitfield bit count. */ + unsigned char mBits{}; + + // AST.. + Token* mAstOperand1{}; + Token* mAstOperand2{}; + Token* mAstParent{}; + + // symbol database information + const Scope* mScope{}; + union { + const Function *mFunction; + const Variable *mVariable; + const ::Type* mType; + const Enumerator *mEnumerator; + }; + + // original name like size_t + std::string* mOriginalName{}; + + // If this token came from a macro replacement list, this is the name of that macro + std::string mMacroName; + + // ValueType + ValueType* mValueType{}; + + // ValueFlow + std::list* mValues{}; + static const std::list mEmptyValueList; + + // Pointer to a template in the template simplifier + std::set* mTemplateSimplifierPointers{}; + + // Pointer to the object representing this token's scope + std::shared_ptr mScopeInfo; + + // __cppcheck_in_range__ + struct CppcheckAttributes { + enum Type { LOW, HIGH } type = LOW; + MathLib::bigint value{}; + CppcheckAttributes* next{}; + }; + CppcheckAttributes* mCppcheckAttributes{}; + + // For memoization, to speed up parsing of huge arrays #8897 + enum class Cpp11init { UNKNOWN, CPP11INIT, NOINIT } mCpp11init = Cpp11init::UNKNOWN; + + TokenDebug mDebug{}; + + void setCppcheckAttribute(CppcheckAttributes::Type type, MathLib::bigint value); + bool getCppcheckAttribute(CppcheckAttributes::Type type, MathLib::bigint &value) const; + + TokenImpl() : mFunction(nullptr) {} + + ~TokenImpl(); +}; + +/// @addtogroup Core +/// @{ + +/** + * @brief The token list that the TokenList generates is a linked-list of this class. + * + * Tokens are stored as strings. The "if", "while", etc are stored in plain text. + * The reason the Token class is needed (instead of using the string class) is that some extra functionality is also needed for tokens: + * - location of the token is stored (fileIndex, linenr, column) + * - functions for classifying the token (isName, isNumber, isBoolean, isStandardType) + * + * The Token class also has other functions for management of token list, matching tokens, etc. + */ +class CPPCHECKLIB Token { + friend class TestToken; + +private: + TokensFrontBack& mTokensFrontBack; + +public: + Token(const Token &) = delete; + Token& operator=(const Token &) = delete; + + enum Type { + eVariable, eType, eFunction, eKeyword, eName, // Names: Variable (varId), Type (typeId, later), Function (FuncId, later), Language keyword, Name (unknown identifier) + eNumber, eString, eChar, eBoolean, eLiteral, eEnumerator, // Literals: Number, String, Character, Boolean, User defined literal (C++11), Enumerator + eArithmeticalOp, eComparisonOp, eAssignmentOp, eLogicalOp, eBitOp, eIncDecOp, eExtendedOp, // Operators: Arithmetical, Comparison, Assignment, Logical, Bitwise, ++/--, Extended + eBracket, // {, }, <, >: < and > only if link() is set. Otherwise they are comparison operators. + eLambda, // A function without a name + eEllipsis, // "..." + eOther, + eNone + }; + + explicit Token(TokensFrontBack &tokensFrontBack); + // for usage in CheckIO::ArgumentInfo only + explicit Token(const Token *tok); + ~Token(); + + ConstTokenRange until(const Token * t) const; + + template + void str(T&& s) { + mStr = s; + mImpl->mVarId = 0; + + update_property_info(); + } + + /** + * Concatenate two (quoted) strings. Automatically cuts of the last/first character. + * Example: "hello ""world" -> "hello world". Used by the token simplifier. + */ + void concatStr(std::string const& b); + + const std::string &str() const { + return mStr; + } + + /** + * Unlink and delete the next 'count' tokens. + */ + void deleteNext(nonneg int count = 1); + + /** + * Unlink and delete the previous 'count' tokens. + */ + void deletePrevious(nonneg int count = 1); + + /** + * Swap the contents of this token with the next token. + */ + void swapWithNext(); + + /** + * @return token in given index, related to this token. + * For example index 1 would return next token, and 2 + * would return next from that one. + */ + const Token *tokAt(int index) const; + Token *tokAt(int index); + + /** + * @return 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 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 + */ + template + static bool simpleMatch(const Token *tok, const char (&pattern)[count]) { + return simpleMatch(tok, pattern, count-1); + } + + static bool simpleMatch(const Token *tok, const char pattern[], size_t pattern_len); + + /** + * Match given token (or list of tokens) to a pattern list. + * + * Possible patterns + * - "%any%" any token + * - "%assign%" a assignment operand + * - "%bool%" true or false + * - "%char%" Any token enclosed in '-character. + * - "%comp%" Any token such that isComparisonOp() returns true. + * - "%cop%" Any token such that isConstOp() returns true. + * - "%name%" any token which is a name, variable or type e.g. "hello" or "int". Also matches keywords. + * - "%num%" Any numeric token, e.g. "23" + * - "%op%" Any token such that isOp() returns true. + * - "%or%" A bitwise-or operator '|' + * - "%oror%" A logical-or operator '||' + * - "%type%" Anything that can be a variable type, e.g. "int". Also matches keywords. + * - "%str%" Any token starting with "-character (C-string). + * - "%var%" Match with token with varId > 0 + * - "%varid%" Match with parameter varid + * - "[abc]" Any of the characters 'a' or 'b' or 'c' + * - "int|void|char" Any of the strings, int, void or char + * - "int|void|char|" Any of the strings, int, void or char or no token + * - "!!else" No tokens or any token that is not "else". + * - "someRandomText" If token contains "someRandomText". + * + * multi-compare patterns such as "int|void|char" can contain %%or%, %%oror% and %%op% + * it is recommended to put such an %%cmd% as the first pattern. + * For example: "%var%|%num%|)" means yes to a variable, a number or ')'. + * + * The patterns can be also combined to compare to multiple tokens at once + * by separating tokens with a space, e.g. + * ") const|void {" will return true if first token is ')' next token is either + * "const" or "void" and token after that is '{'. If even one of the tokens does not + * match its pattern, false is returned. + * + * @param tok List of tokens to be compared to the pattern + * @param pattern The pattern against which the tokens are compared, + * e.g. "const" or ") const|volatile| {". + * @param varid if %%varid% is given in the pattern the Token::varId + * will be matched against this argument + * @return true if given token matches with given pattern + * false if given token does not match with given pattern + */ + static bool Match(const Token *tok, const char pattern[], nonneg int varid = 0); + + /** + * @return length of C-string. + * + * Should be called for %%str%% tokens only. + * + * @param tok token with C-string + **/ + static nonneg int getStrLength(const Token *tok); + + /** + * @return array length of C-string. + * + * Should be called for %%str%% tokens only. + * + * @param tok token with C-string + **/ + static nonneg int getStrArraySize(const Token *tok); + + /** + * @return sizeof of C-string. + * + * Should be called for %%str%% tokens only. + * + * @param tok token with C-string + * @param settings Settings + **/ + static nonneg int getStrSize(const Token *tok, const Settings & settings); + + const ValueType *valueType() const { + return mImpl->mValueType; + } + void setValueType(ValueType *vt); + + const ValueType *argumentType() const { + const Token *top = this; + while (top && !Token::Match(top->astParent(), ",|(")) + top = top->astParent(); + return top ? top->mImpl->mValueType : nullptr; + } + + Token::Type tokType() const { + return mTokType; + } + void tokType(Token::Type t) { + mTokType = t; + + const bool memoizedIsName = (mTokType == eName || mTokType == eType || mTokType == eVariable || + mTokType == eFunction || mTokType == eKeyword || mTokType == eBoolean || + mTokType == eEnumerator); // TODO: "true"/"false" aren't really a name... + setFlag(fIsName, memoizedIsName); + + const bool memoizedIsLiteral = (mTokType == eNumber || mTokType == eString || mTokType == eChar || + mTokType == eBoolean || mTokType == eLiteral || mTokType == eEnumerator); + setFlag(fIsLiteral, memoizedIsLiteral); + } + bool isKeyword() const { + return mTokType == eKeyword; + } + bool isName() const { + return getFlag(fIsName); + } + bool isNameOnly() const { + return mFlags == fIsName && mTokType == eName; + } + bool isUpperCaseName() const; + bool isLiteral() const { + return getFlag(fIsLiteral); + } + bool isNumber() const { + return mTokType == eNumber; + } + bool isEnumerator() const { + return mTokType == eEnumerator; + } + bool isVariable() const { + return mTokType == eVariable; + } + bool isOp() const { + return (isConstOp() || + isAssignmentOp() || + mTokType == eIncDecOp); + } + bool isConstOp() const { + return (isArithmeticalOp() || + mTokType == eLogicalOp || + mTokType == eComparisonOp || + mTokType == eBitOp); + } + bool isExtendedOp() const { + return isConstOp() || + mTokType == eExtendedOp; + } + bool isArithmeticalOp() const { + return mTokType == eArithmeticalOp; + } + bool isComparisonOp() const { + return mTokType == eComparisonOp; + } + bool isAssignmentOp() const { + return mTokType == eAssignmentOp; + } + bool isBoolean() const { + return mTokType == eBoolean; + } + bool isIncDecOp() const { + return mTokType == eIncDecOp; + } + bool isBinaryOp() const { + return astOperand1() != nullptr && astOperand2() != nullptr; + } + bool isUnaryOp(const std::string &s) const { + return s == mStr && astOperand1() != nullptr && astOperand2() == nullptr; + } + bool isUnaryPreOp() const; + + uint64_t flags() const { + return mFlags; + } + void flags(const uint64_t flags_) { + mFlags = flags_; + } + bool isUnsigned() const { + return getFlag(fIsUnsigned); + } + void isUnsigned(const bool sign) { + setFlag(fIsUnsigned, sign); + } + bool isSigned() const { + return getFlag(fIsSigned); + } + void isSigned(const bool sign) { + setFlag(fIsSigned, sign); + } + // cppcheck-suppress unusedFunction + bool isPointerCompare() const { + return getFlag(fIsPointerCompare); + } + void isPointerCompare(const bool b) { + setFlag(fIsPointerCompare, b); + } + bool isLong() const { + return getFlag(fIsLong); + } + void isLong(bool size) { + setFlag(fIsLong, size); + } + bool isStandardType() const { + return getFlag(fIsStandardType); + } + void isStandardType(const bool b) { + setFlag(fIsStandardType, b); + } + bool isExpandedMacro() const { + return !mImpl->mMacroName.empty(); + } + bool isCast() const { + return getFlag(fIsCast); + } + void isCast(bool c) { + setFlag(fIsCast, c); + } + bool isAttributeConstructor() const { + return getFlag(fIsAttributeConstructor); + } + void isAttributeConstructor(const bool ac) { + setFlag(fIsAttributeConstructor, ac); + } + bool isAttributeDestructor() const { + return getFlag(fIsAttributeDestructor); + } + void isAttributeDestructor(const bool value) { + setFlag(fIsAttributeDestructor, value); + } + bool isAttributeUnused() const { + return getFlag(fIsAttributeUnused); + } + void isAttributeUnused(bool unused) { + setFlag(fIsAttributeUnused, unused); + } + bool isAttributeUsed() const { + return getFlag(fIsAttributeUsed); + } + void isAttributeUsed(const bool unused) { + setFlag(fIsAttributeUsed, unused); + } + bool isAttributePure() const { + return getFlag(fIsAttributePure); + } + void isAttributePure(const bool value) { + setFlag(fIsAttributePure, value); + } + bool isAttributeConst() const { + return getFlag(fIsAttributeConst); + } + void isAttributeConst(bool value) { + setFlag(fIsAttributeConst, value); + } + bool isAttributeNoreturn() const { + return getFlag(fIsAttributeNoreturn); + } + void isAttributeNoreturn(const bool value) { + setFlag(fIsAttributeNoreturn, value); + } + bool isAttributeNothrow() const { + return getFlag(fIsAttributeNothrow); + } + void isAttributeNothrow(const bool value) { + setFlag(fIsAttributeNothrow, value); + } + bool isAttributeExport() const { + return getFlag(fIsAttributeExport); + } + void isAttributeExport(const bool value) { + setFlag(fIsAttributeExport, value); + } + bool isAttributePacked() const { + return getFlag(fIsAttributePacked); + } + void isAttributePacked(const bool value) { + setFlag(fIsAttributePacked, value); + } + bool isAttributeNodiscard() const { + return getFlag(fIsAttributeNodiscard); + } + void isAttributeNodiscard(const bool value) { + setFlag(fIsAttributeNodiscard, value); + } + bool isAttributeMaybeUnused() const { + return getFlag(fIsAttributeMaybeUnused); + } + void isAttributeMaybeUnused(const bool value) { + setFlag(fIsAttributeMaybeUnused, value); + } + void setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint value) { + mImpl->setCppcheckAttribute(type, value); + } + bool getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type type, MathLib::bigint &value) const { + return mImpl->getCppcheckAttribute(type, value); + } + // cppcheck-suppress unusedFunction + bool hasCppcheckAttributes() const { + return nullptr != mImpl->mCppcheckAttributes; + } + bool isControlFlowKeyword() const { + return getFlag(fIsControlFlowKeyword); + } + bool isOperatorKeyword() const { + return getFlag(fIsOperatorKeyword); + } + void isOperatorKeyword(const bool value) { + setFlag(fIsOperatorKeyword, value); + } + bool isComplex() const { + return getFlag(fIsComplex); + } + void isComplex(const bool value) { + setFlag(fIsComplex, value); + } + bool isEnumType() const { + return getFlag(fIsEnumType); + } + void isEnumType(const bool value) { + setFlag(fIsEnumType, value); + } + bool isAtAddress() const { + return getFlag(fAtAddress); + } + void isAtAddress(bool b) { + setFlag(fAtAddress, b); + } + bool isIncompleteVar() const { + return getFlag(fIncompleteVar); + } + void isIncompleteVar(bool b) { + setFlag(fIncompleteVar, b); + } + + bool isSimplifiedTypedef() const { + return getFlag(fIsSimplifiedTypedef); + } + void isSimplifiedTypedef(bool b) { + setFlag(fIsSimplifiedTypedef, b); + } + + bool isIncompleteConstant() const { + return getFlag(fIsIncompleteConstant); + } + void isIncompleteConstant(bool b) { + setFlag(fIsIncompleteConstant, b); + } + + bool isConstexpr() const { + return getFlag(fConstexpr); + } + void isConstexpr(bool b) { + setFlag(fConstexpr, b); + } + + bool isExternC() const { + return getFlag(fExternC); + } + void isExternC(bool b) { + setFlag(fExternC, b); + } + + bool isSplittedVarDeclComma() const { + return getFlag(fIsSplitVarDeclComma); + } + void isSplittedVarDeclComma(bool b) { + setFlag(fIsSplitVarDeclComma, b); + } + + bool isSplittedVarDeclEq() const { + return getFlag(fIsSplitVarDeclEq); + } + void isSplittedVarDeclEq(bool b) { + setFlag(fIsSplitVarDeclEq, b); + } + + bool isImplicitInt() const { + return getFlag(fIsImplicitInt); + } + void isImplicitInt(bool b) { + setFlag(fIsImplicitInt, b); + } + + bool isInline() const { + return getFlag(fIsInline); + } + void isInline(bool b) { + setFlag(fIsInline, b); + } + + bool isAtomic() const { + return getFlag(fIsAtomic); + } + void isAtomic(bool b) { + setFlag(fIsAtomic, b); + } + + bool isRestrict() const { + return getFlag(fIsRestrict); + } + void isRestrict(bool b) { + setFlag(fIsRestrict, b); + } + + bool isRemovedVoidParameter() const { + return getFlag(fIsRemovedVoidParameter); + } + void setRemovedVoidParameter(bool b) { + setFlag(fIsRemovedVoidParameter, b); + } + + bool isTemplate() const { + return getFlag(fIsTemplate); + } + void isTemplate(bool b) { + setFlag(fIsTemplate, b); + } + + bool isSimplifiedScope() const { + return getFlag(fIsSimplifedScope); + } + void isSimplifiedScope(bool b) { + setFlag(fIsSimplifedScope, b); + } + + bool isFinalType() const { + return getFlag(fIsFinalType); + } + void isFinalType(bool b) { + setFlag(fIsFinalType, b); + } + + bool isInitComma() const { + return getFlag(fIsInitComma); + } + void isInitComma(bool b) { + setFlag(fIsInitComma, b); + } + + // cppcheck-suppress unusedFunction + bool isBitfield() const { + return mImpl->mBits > 0; + } + unsigned char bits() const { + return mImpl->mBits; + } + const std::set* templateSimplifierPointers() const { + return mImpl->mTemplateSimplifierPointers; + } + std::set* templateSimplifierPointers() { + return mImpl->mTemplateSimplifierPointers; + } + void templateSimplifierPointer(TemplateSimplifier::TokenAndName* tokenAndName) { + if (!mImpl->mTemplateSimplifierPointers) + mImpl->mTemplateSimplifierPointers = new std::set; + mImpl->mTemplateSimplifierPointers->insert(tokenAndName); + } + void setBits(const unsigned char b) { + mImpl->mBits = b; + } + + bool isUtf8() const { + return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', "u8")) || + ((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "u8"))); + } + + bool isUtf16() const { + return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', "u")) || + ((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "u"))); + } + + bool isUtf32() const { + return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', "U")) || + ((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "U"))); + } + + bool isCChar() const { + return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', emptyString)) || + ((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', emptyString) && mStr.length() == 3)); + } + + bool isCMultiChar() const { + return (((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', emptyString)) && + (mStr.length() > 3)); + } + /** + * @brief Is current token a template argument? + * + * Original code: + * + * template struct S { + * C x; + * }; + * S s; + * + * Resulting code: + * + * struct S { + * int x ; // <- "int" is a template argument + * } + * S s; + */ + bool isTemplateArg() const { + return getFlag(fIsTemplateArg); + } + void isTemplateArg(const bool value) { + setFlag(fIsTemplateArg, value); + } + + std::string getMacroName() const { + return mImpl->mMacroName; + } + void setMacroName(std::string name) { + mImpl->mMacroName = std::move(name); + } + + template + static const Token *findsimplematch(const Token * const startTok, const char (&pattern)[count]) { + return findsimplematch(startTok, pattern, count-1); + } + static const Token *findsimplematch(const Token * const startTok, const char pattern[], size_t pattern_len); + + template + static const Token *findsimplematch(const Token * const startTok, const char (&pattern)[count], const Token * const end) { + return findsimplematch(startTok, pattern, count-1, end); + } + static const Token *findsimplematch(const Token * const startTok, const char pattern[], size_t pattern_len, const Token * const end); + + static const Token *findmatch(const Token * const startTok, const char pattern[], const nonneg int varId = 0); + static const Token *findmatch(const Token * const startTok, const char pattern[], const Token * const end, const nonneg int varId = 0); + + template + static Token *findsimplematch(Token * const startTok, const char (&pattern)[count]) { + return findsimplematch(startTok, pattern, count-1); + } + static Token *findsimplematch(Token * const startTok, const char pattern[], size_t pattern_len); + template + static Token *findsimplematch(Token * const startTok, const char (&pattern)[count], const Token * const end) { + return findsimplematch(startTok, pattern, count-1, end); + } + static Token *findsimplematch(Token * const startTok, const char pattern[], size_t pattern_len, const Token * const end); + + static Token *findmatch(Token * const startTok, const char pattern[], const nonneg int varId = 0); + static Token *findmatch(Token * const startTok, const char pattern[], const Token * const end, const nonneg int varId = 0); + +private: + /** + * Needle is build from multiple alternatives. If one of + * them is equal to haystack, return value is 1. If there + * are no matches, but one alternative to needle is empty + * string, return value is 0. If needle was not found, return + * value is -1. + * + * @param tok Current token (needle) + * @param haystack e.g. "one|two" or "|one|two" + * @param varid optional varid of token + * @return 1 if needle is found from the haystack + * 0 if needle was empty string + * -1 if needle was not found + */ + static int multiCompare(const Token *tok, const char *haystack, nonneg int varid); + +public: + nonneg int fileIndex() const { + return mImpl->mFileIndex; + } + void fileIndex(nonneg int indexOfFile) { + mImpl->mFileIndex = indexOfFile; + } + + nonneg int linenr() const { + return mImpl->mLineNumber; + } + void linenr(nonneg int lineNumber) { + mImpl->mLineNumber = lineNumber; + } + + nonneg int column() const { + return mImpl->mColumn; + } + void column(nonneg int c) { + mImpl->mColumn = c; + } + + Token* next() { + return mNext; + } + + const Token* next() const { + return mNext; + } + + /** + * Delete tokens between begin and end. E.g. if begin = 1 + * and end = 5, tokens 2,3 and 4 would be erased. + * + * @param begin Tokens after this will be erased. + * @param end Tokens before this will be erased. + */ + static void eraseTokens(Token *begin, const Token *end); + + /** + * Insert new token after this token. This function will handle + * relations between next and previous token also. + * @param tokenStr String for the new token. + * @param originalNameStr String used for Token::originalName(). + * @param prepend Insert the new token before this token when it's not + * the first one on the tokens list. + */ + Token* insertToken(const std::string& tokenStr, const std::string& originalNameStr = emptyString, const std::string& macroNameStr = emptyString, bool prepend = false); + + Token* insertTokenBefore(const std::string& tokenStr, const std::string& originalNameStr = emptyString, const std::string& macroNameStr = emptyString) + { + return insertToken(tokenStr, originalNameStr, macroNameStr, true); + } + + Token* previous() { + return mPrevious; + } + + const Token* previous() const { + return mPrevious; + } + + nonneg int varId() const { + return mImpl->mVarId; + } + void varId(nonneg int id) { + mImpl->mVarId = id; + if (id != 0) { + tokType(eVariable); + isStandardType(false); + } else { + update_property_info(); + } + } + + nonneg int exprId() const { + if (mImpl->mExprId) + return mImpl->mExprId; + return mImpl->mVarId; + } + void exprId(nonneg int id) { + mImpl->mExprId = id; + } + + void setUniqueExprId() + { + assert(mImpl->mExprId > 0); + mImpl->mExprId |= 1 << efIsUnique; + } + + bool isUniqueExprId() const + { + return (mImpl->mExprId & (1 << efIsUnique)) != 0; + } + + /** + * 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; + + /** + * print out tokens - used for debugging + */ + void printLines(int lines=5) 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); + + struct stringifyOptions { + bool varid = false; + bool exprid = false; + bool idtype = false; // distinguish varid / exprid + bool attributes = false; + bool macro = false; + bool linenumbers = false; + bool linebreaks = false; + bool files = false; + static stringifyOptions forDebug() { + stringifyOptions options; + options.attributes = true; + options.macro = true; + options.linenumbers = true; + options.linebreaks = true; + options.files = true; + return options; + } + // cppcheck-suppress unusedFunction + static stringifyOptions forDebugVarId() { + stringifyOptions options = forDebug(); + options.varid = true; + return options; + } + static stringifyOptions forDebugExprId() { + stringifyOptions options = forDebug(); + options.exprid = true; + return options; + } + static stringifyOptions forPrintOut() { + stringifyOptions options = forDebug(); + options.exprid = true; + options.varid = true; + options.idtype = true; + return options; + } + }; + + std::string stringify(const stringifyOptions& options) const; + + /** + * Stringify a token + * @param varid Print varids. (Style: "varname\@id") + * @param attributes Print attributes of tokens like "unsigned" in front of it. + * @param macro Prints $ in front of the token if it was expanded from a macro. + */ + std::string stringify(bool varid, bool attributes, bool macro) const; + + std::string stringifyList(const stringifyOptions& options, const std::vector* fileNames = nullptr, const Token* end = nullptr) const; + std::string stringifyList(const Token* end, bool attributes = true) const; + std::string stringifyList(bool varid = false) const; + + /** + * Stringify a list of token, from current instance on. + * @param varid Print varids. (Style: "varname\@id") + * @param attributes Print attributes of tokens like "unsigned" in front of it. + * @param linenumbers Print line number in front of each line + * @param linebreaks Insert "\\n" into string when line number changes + * @param files print Files as numbers or as names (if fileNames is given) + * @param fileNames Vector of filenames. Used (if given) to print filenames as strings instead of numbers. + * @param end Stringification ends before this token is reached. 0 to stringify until end of list. + * @return Stringified token list as a string + */ + std::string stringifyList(bool varid, bool attributes, bool linenumbers, bool linebreaks, bool files, const std::vector* fileNames = nullptr, const Token* end = nullptr) const; + + /** + * Remove the contents for this token from the token list. + * + * The contents are replaced with the contents of the next token and + * the next token is unlinked and deleted from the token list. + * + * So this token will still be valid after the 'deleteThis()'. + */ + void deleteThis(); + + /** + * Create link to given token + * @param linkToToken The token where this token should link + * to. + */ + void link(Token *linkToToken) { + mLink = linkToToken; + if (mStr == "<" || mStr == ">") + update_property_info(); + } + + /** + * Return token where this token links to. + * Supported links are: + * "{" <-> "}" + * "(" <-> ")" + * "[" <-> "]" + * + * @return The token where this token links to. + */ + const Token* link() const { + return mLink; + } + + Token* link() { + return mLink; + } + + /** + * Associate this token with given scope + * @param s Scope to be associated + */ + void scope(const Scope *s) { + mImpl->mScope = s; + } + + /** + * @return a pointer to the scope containing this token. + */ + const Scope *scope() const { + return mImpl->mScope; + } + + /** + * Associate this token with given function + * @param f Function to be associated + */ + void function(const Function *f); + + /** + * @return a pointer to the Function associated with this token. + */ + const Function *function() const { + return mTokType == eFunction || mTokType == eLambda ? mImpl->mFunction : nullptr; + } + + /** + * Associate this token with given variable + * @param v Variable to be associated + */ + void variable(const Variable *v) { + mImpl->mVariable = v; + if (v || mImpl->mVarId) + tokType(eVariable); + else if (mTokType == eVariable) + tokType(eName); + } + + /** + * @return a pointer to the variable associated with this token. + */ + const Variable *variable() const { + return mTokType == eVariable ? mImpl->mVariable : nullptr; + } + + /** + * Associate this token with given type + * @param t Type to be associated + */ + void type(const ::Type *t); + + /** + * @return a pointer to the type associated with this token. + */ + const ::Type *type() const { + return mTokType == eType ? mImpl->mType : nullptr; + } + + static const ::Type* typeOf(const Token* tok, const Token** typeTok = nullptr); + + /** + * @return the declaration associated with this token. + * @param pointedToType return the pointed-to type? + */ + static std::pair typeDecl(const Token* tok, bool pointedToType = false); + + static std::string typeStr(const Token* tok); + + /** + * @return a pointer to the Enumerator associated with this token. + */ + const Enumerator *enumerator() const { + return mTokType == eEnumerator ? mImpl->mEnumerator : nullptr; + } + + /** + * Associate this token with given enumerator + * @param e Enumerator to be associated + */ + void enumerator(const Enumerator *e) { + mImpl->mEnumerator = e; + if (e) + tokType(eEnumerator); + else if (mTokType == eEnumerator) + tokType(eName); + } + + /** + * Links two elements against each other. + **/ + static void createMutualLinks(Token *begin, Token *end); + + /** + * This can be called only for tokens that are strings, else + * the assert() is called. If Token is e.g. '"hello"', this will return + * 'hello' (removing the double quotes). + * @return String value + */ + std::string strValue() const; + + /** + * Move srcStart and srcEnd tokens and all tokens between them + * into new a location. Only links between tokens are changed. + * @param srcStart This is the first token to be moved + * @param srcEnd The last token to be moved + * @param newLocation srcStart will be placed after this token. + */ + static void move(Token *srcStart, Token *srcEnd, Token *newLocation); + + /** Get progressValue (0 - 100) */ + nonneg int progressValue() const { + return mImpl->mProgressValue; + } + + /** Calculate progress values for all tokens */ + static void assignProgressValues(Token *tok); + + /** + * @return the first token of the next argument. Does only work on argument + * lists. Requires that Tokenizer::createLinks2() has been called before. + * Returns 0, if there is no next argument. + */ + const Token* nextArgument() const; + Token *nextArgument(); + + /** + * @return the first token of the next argument. Does only work on argument + * lists. Should be used only before Tokenizer::createLinks2() was called. + * Returns 0, if there is no next argument. + */ + const Token* nextArgumentBeforeCreateLinks2() const; + + /** + * @return the first token of the next template argument. Does only work on template argument + * lists. Requires that Tokenizer::createLinks2() has been called before. + * Returns 0, if there is no next argument. + */ + const Token* nextTemplateArgument() const; + + /** + * Returns the closing bracket of opening '<'. Should only be used if link() + * is unavailable. + * @return closing '>', ')', ']' or '}'. if no closing bracket is found, NULL is returned + */ + const Token* findClosingBracket() const; + Token* findClosingBracket(); + + const Token* findOpeningBracket() const; + Token* findOpeningBracket(); + + /** + * @return the original name. + */ + const std::string & originalName() const { + return mImpl->mOriginalName ? *mImpl->mOriginalName : emptyString; + } + + const std::list& values() const { + return mImpl->mValues ? *mImpl->mValues : TokenImpl::mEmptyValueList; + } + + /** + * Sets the original name. + */ + template + void originalName(T&& name) { + if (!mImpl->mOriginalName) + mImpl->mOriginalName = new std::string(name); + else + *mImpl->mOriginalName = name; + } + + bool hasKnownIntValue() const; + bool hasKnownValue() const; + bool hasKnownValue(ValueFlow::Value::ValueType t) const; + bool hasKnownSymbolicValue(const Token* tok) const; + + const ValueFlow::Value* getKnownValue(ValueFlow::Value::ValueType t) const; + MathLib::bigint getKnownIntValue() const { + return mImpl->mValues->front().intvalue; + } + + const ValueFlow::Value* getValue(const MathLib::bigint val) const; + + const ValueFlow::Value* getMaxValue(bool condition, MathLib::bigint path = 0) const; + const ValueFlow::Value* getMinValue(bool condition, MathLib::bigint path = 0) const; + + const ValueFlow::Value* getMovedValue() const; + + const ValueFlow::Value * getValueLE(const MathLib::bigint val, const Settings *settings) const; + const ValueFlow::Value * getValueGE(const MathLib::bigint val, const Settings *settings) const; + + const ValueFlow::Value * getInvalidValue(const Token *ftok, nonneg int argnr, const Settings *settings) const; + + const ValueFlow::Value* getContainerSizeValue(const MathLib::bigint val) const; + + const Token *getValueTokenMaxStrLength() const; + const Token *getValueTokenMinStrSize(const Settings &settings, MathLib::bigint* path = nullptr) const; + + /** Add token value. Return true if value is added. */ + bool addValue(const ValueFlow::Value &value); + + void removeValues(std::function pred) { + if (mImpl->mValues) + mImpl->mValues->remove_if(std::move(pred)); + } + + nonneg int index() const { + return mImpl->mIndex; + } + + void assignIndexes(); + +private: + + void next(Token *nextToken) { + mNext = nextToken; + } + void previous(Token *previousToken) { + mPrevious = previousToken; + } + + /** used by deleteThis() to take data from token to delete */ + void takeData(Token *fromToken); + + /** + * Works almost like strcmp() except returns only true or false and + * if str has empty space ' ' character, that character is handled + * as if it were '\\0' + */ + static bool firstWordEquals(const char *str, const char *word); + + /** + * Works almost like strchr() except + * if str has empty space ' ' character, that character is handled + * as if it were '\\0' + */ + static const char *chrInFirstWord(const char *str, char c); + + std::string mStr; + + Token* mNext{}; + Token* mPrevious{}; + Token* mLink{}; + + enum : uint64_t { + fIsUnsigned = (1ULL << 0), + fIsSigned = (1ULL << 1), + fIsPointerCompare = (1ULL << 2), + fIsLong = (1ULL << 3), + fIsStandardType = (1ULL << 4), + //fIsExpandedMacro = (1ULL << 5), + fIsCast = (1ULL << 6), + fIsAttributeConstructor = (1ULL << 7), // __attribute__((constructor)) __attribute__((constructor(priority))) + fIsAttributeDestructor = (1ULL << 8), // __attribute__((destructor)) __attribute__((destructor(priority))) + fIsAttributeUnused = (1ULL << 9), // __attribute__((unused)) + fIsAttributePure = (1ULL << 10), // __attribute__((pure)) + fIsAttributeConst = (1ULL << 11), // __attribute__((const)) + fIsAttributeNoreturn = (1ULL << 12), // __attribute__((noreturn)), __declspec(noreturn) + fIsAttributeNothrow = (1ULL << 13), // __attribute__((nothrow)), __declspec(nothrow) + fIsAttributeUsed = (1ULL << 14), // __attribute__((used)) + fIsAttributePacked = (1ULL << 15), // __attribute__((packed)) + fIsAttributeExport = (1ULL << 16), // __attribute__((__visibility__("default"))), __declspec(dllexport) + fIsAttributeMaybeUnused = (1ULL << 17), // [[maybe_unused]] + fIsAttributeNodiscard = (1ULL << 18), // __attribute__ ((warn_unused_result)), [[nodiscard]] + fIsControlFlowKeyword = (1ULL << 19), // if/switch/while/... + fIsOperatorKeyword = (1ULL << 20), // operator=, etc + fIsComplex = (1ULL << 21), // complex/_Complex type + fIsEnumType = (1ULL << 22), // enumeration type + fIsName = (1ULL << 23), + fIsLiteral = (1ULL << 24), + fIsTemplateArg = (1ULL << 25), + fAtAddress = (1ULL << 26), // @ 0x4000 + fIncompleteVar = (1ULL << 27), + fConstexpr = (1ULL << 28), + fExternC = (1ULL << 29), + fIsSplitVarDeclComma = (1ULL << 30), // set to true when variable declarations are split up ('int a,b;' => 'int a; int b;') + fIsSplitVarDeclEq = (1ULL << 31), // set to true when variable declaration with initialization is split up ('int a=5;' => 'int a; a=5;') + fIsImplicitInt = (1ULL << 32), // Is "int" token implicitly added? + fIsInline = (1ULL << 33), // Is this a inline type + fIsTemplate = (1ULL << 34), + fIsSimplifedScope = (1ULL << 35), // scope added when simplifying e.g. if (int i = ...; ...) + fIsRemovedVoidParameter = (1ULL << 36), // A void function parameter has been removed + fIsIncompleteConstant = (1ULL << 37), + fIsRestrict = (1ULL << 38), // Is this a restrict pointer type + fIsAtomic = (1ULL << 39), // Is this a _Atomic declaration + fIsSimplifiedTypedef = (1ULL << 40), + fIsFinalType = (1ULL << 41), // Is this a type with final specifier + fIsInitComma = (1ULL << 42), // Is this comma located inside some {..}. i.e: {1,2,3,4} + }; + + enum : uint64_t { + efMaxSize = sizeof(nonneg int) * 8, + efIsUnique = efMaxSize - 2, + }; + + Token::Type mTokType = eNone; + + uint64_t mFlags{}; + + TokenImpl* mImpl{}; + + /** + * Get specified flag state. + * @param flag_ flag to get state of + * @return true if flag set or false in flag not set + */ + bool getFlag(uint64_t flag_) const { + return ((mFlags & flag_) != 0); + } + + /** + * Set specified flag state. + * @param flag_ flag to set state + * @param state_ new state of flag + */ + void setFlag(uint64_t flag_, bool state_) { + mFlags = state_ ? mFlags | flag_ : mFlags & ~flag_; + } + + /** Updates internal property cache like _isName or _isBoolean. + Called after any mStr() modification. */ + void update_property_info(); + + /** Update internal property cache about isStandardType() */ + void update_property_isStandardType(); + + /** Update internal property cache about string and char literals */ + void update_property_char_string_literal(); + + /** Internal helper function to avoid excessive string allocations */ + void astStringVerboseRecursive(std::string& ret, const nonneg int indent1 = 0, const nonneg int indent2 = 0) const; + +public: + void astOperand1(Token *tok); + void astOperand2(Token *tok); + void astParent(Token* tok); + + Token * astOperand1() { + return mImpl->mAstOperand1; + } + const Token * astOperand1() const { + return mImpl->mAstOperand1; + } + Token * astOperand2() { + return mImpl->mAstOperand2; + } + const Token * astOperand2() const { + return mImpl->mAstOperand2; + } + Token * astParent() { + return mImpl->mAstParent; + } + const Token * astParent() const { + return mImpl->mAstParent; + } + Token * astSibling() { + if (!astParent()) + return nullptr; + if (this == astParent()->astOperand1()) + return astParent()->astOperand2(); + if (this == astParent()->astOperand2()) + return astParent()->astOperand1(); + return nullptr; + + } + const Token * astSibling() const { + if (!astParent()) + return nullptr; + if (this == astParent()->astOperand1()) + return astParent()->astOperand2(); + if (this == astParent()->astOperand2()) + return astParent()->astOperand1(); + return nullptr; + + } + Token *astTop() { + Token *ret = this; + while (ret->mImpl->mAstParent) + ret = ret->mImpl->mAstParent; + return ret; + } + + const Token *astTop() const { + const Token *ret = this; + while (ret->mImpl->mAstParent) + ret = ret->mImpl->mAstParent; + return ret; + } + + std::pair findExpressionStartEndTokens() const; + + /** + * Is current token a calculation? Only true for operands. + * For '*' and '&' tokens it is looked up if this is a + * dereference or address-of. A dereference or address-of is not + * counted as a calculation. + * @return returns true if current token is a calculation + */ + bool isCalculation() const; + + void clearValueFlow() { + delete mImpl->mValues; + mImpl->mValues = nullptr; + } + + std::string astString(const char *sep = "") const { + std::string ret; + if (mImpl->mAstOperand1) + ret = mImpl->mAstOperand1->astString(sep); + if (mImpl->mAstOperand2) + ret += mImpl->mAstOperand2->astString(sep); + return ret + sep + mStr; + } + + std::string astStringVerbose() const; + + std::string astStringZ3() const; + + std::string expressionString() const; + + void printAst(bool verbose, bool xml, const std::vector &fileNames, std::ostream &out) const; + + void printValueFlow(bool xml, std::ostream &out) const; + + void scopeInfo(std::shared_ptr newScopeInfo); + std::shared_ptr scopeInfo() const; + + void setCpp11init(bool cpp11init) const { + mImpl->mCpp11init=cpp11init ? TokenImpl::Cpp11init::CPP11INIT : TokenImpl::Cpp11init::NOINIT; + } + TokenImpl::Cpp11init isCpp11init() const { + return mImpl->mCpp11init; + } + + TokenDebug getTokenDebug() const { + return mImpl->mDebug; + } + void setTokenDebug(TokenDebug td) { + mImpl->mDebug = td; + } + + bool isCpp() const; + + bool isC() const; +}; + +Token* findTypeEnd(Token* tok); +Token* findLambdaEndScope(Token* tok); +const Token* findLambdaEndScope(const Token* tok); + +/// @} +//--------------------------------------------------------------------------- +#endif // tokenH diff --git a/cppcheck-2.14.0/lib/tokenize.cpp b/cppcheck-2.14.0/lib/tokenize.cpp new file mode 100644 index 00000000..5c811e2d --- /dev/null +++ b/cppcheck-2.14.0/lib/tokenize.cpp @@ -0,0 +1,10708 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "errorlogger.h" +#include "errortypes.h" +#include "library.h" +#include "mathlib.h" +#include "path.h" +#include "platform.h" +#include "preprocessor.h" +#include "settings.h" +#include "standards.h" +#include "summaries.h" +#include "symboldatabase.h" +#include "templatesimplifier.h" +#include "timer.h" +#include "token.h" +#include "utils.h" +#include "valueflow.h" +#include "vfvalue.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//--------------------------------------------------------------------------- + +namespace { + // local struct used in setVarId + // in order to store information about the scope + struct VarIdScopeInfo { + VarIdScopeInfo() = default; + VarIdScopeInfo(bool isExecutable, bool isStructInit, bool isEnum, nonneg int startVarid) + : isExecutable(isExecutable), isStructInit(isStructInit), isEnum(isEnum), startVarid(startVarid) {} + + const bool isExecutable{}; + const bool isStructInit{}; + const bool isEnum{}; + const nonneg int startVarid{}; + }; +} + +/** Return whether tok is the "{" that starts an enumerator list */ +static bool isEnumStart(const Token* tok) +{ + if (!tok || tok->str() != "{") + return false; + return (tok->strAt(-1) == "enum") || (tok->strAt(-2) == "enum") || Token::Match(tok->tokAt(-3), "enum class %name%"); +} + +template +static void skipEnumBody(T *&tok) +{ + T *defStart = tok; + while (Token::Match(defStart, "%name%|::|:")) + defStart = defStart->next(); + if (defStart && defStart->str() == "{") + tok = defStart->link()->next(); +} + +const Token * Tokenizer::isFunctionHead(const Token *tok, const std::string &endsWith) +{ + if (!tok) + return nullptr; + if (tok->str() == "(") + tok = tok->link(); + if (Token::Match(tok, ") ;|{|[")) { + tok = tok->next(); + while (tok && tok->str() == "[" && tok->link()) { + if (endsWith.find(tok->str()) != std::string::npos) + return tok; + tok = tok->link()->next(); + } + return (tok && endsWith.find(tok->str()) != std::string::npos) ? tok : nullptr; + } + if (tok->isCpp() && tok->str() == ")") { + tok = tok->next(); + while (Token::Match(tok, "const|noexcept|override|final|volatile|mutable|&|&& !!(") || + (Token::Match(tok, "%name% !!(") && tok->isUpperCaseName())) + tok = tok->next(); + if (tok && tok->str() == ")") + tok = tok->next(); + while (tok && tok->str() == "[") + tok = tok->link()->next(); + if (Token::Match(tok, "throw|noexcept (")) + tok = tok->linkAt(1)->next(); + if (Token::Match(tok, "%name% (") && tok->isUpperCaseName()) + tok = tok->linkAt(1)->next(); + if (tok && tok->originalName() == "->") { // trailing return type + for (tok = tok->next(); tok && !Token::Match(tok, ";|{|override|final"); tok = tok->next()) + if (tok->link() && Token::Match(tok, "<|[|(")) + tok = tok->link(); + } + while (Token::Match(tok, "override|final !!(") || + (Token::Match(tok, "%name% !!(") && tok->isUpperCaseName())) + tok = tok->next(); + if (Token::Match(tok, "= 0|default|delete ;")) + tok = tok->tokAt(2); + + return (tok && endsWith.find(tok->str()) != std::string::npos) ? tok : nullptr; + } + return nullptr; +} + +/** + * is tok the start brace { of a class, struct, union, or enum + */ +static bool isClassStructUnionEnumStart(const Token * tok) +{ + if (!Token::Match(tok->previous(), "class|struct|union|enum|%name%|>|>> {")) + return false; + const Token * tok2 = tok->previous(); + while (tok2 && !Token::Match(tok2, "class|struct|union|enum|{|}|;")) + tok2 = tok2->previous(); + return Token::Match(tok2, "class|struct|union|enum"); +} + +//--------------------------------------------------------------------------- + +Tokenizer::Tokenizer(const Settings &settings, ErrorLogger &errorLogger) : + list(&settings), + mSettings(settings), + mErrorLogger(errorLogger), + mTemplateSimplifier(new TemplateSimplifier(*this)) +{} + +Tokenizer::~Tokenizer() +{ + delete mSymbolDatabase; + delete mTemplateSimplifier; +} + + +//--------------------------------------------------------------------------- +// SizeOfType - gives the size of a type +//--------------------------------------------------------------------------- + +nonneg int Tokenizer::sizeOfType(const std::string& type) const +{ + const std::map::const_iterator it = mTypeSize.find(type); + if (it == mTypeSize.end()) { + const Library::PodType* podtype = mSettings.library.podtype(type); + if (!podtype) + return 0; + + return podtype->size; + } + return it->second; +} + +nonneg int Tokenizer::sizeOfType(const Token *type) const +{ + if (!type || type->str().empty()) + return 0; + + if (type->tokType() == Token::eString) + return Token::getStrLength(type) + 1U; + + const std::map::const_iterator it = mTypeSize.find(type->str()); + if (it == mTypeSize.end()) { + const Library::PodType* podtype = mSettings.library.podtype(type->str()); + if (!podtype) + return 0; + + return podtype->size; + } + if (type->isLong()) { + if (type->str() == "double") + return mSettings.platform.sizeof_long_double; + if (type->str() == "long") + return mSettings.platform.sizeof_long_long; + } + + return it->second; +} +//--------------------------------------------------------------------------- + +// check if this statement is a duplicate definition +bool Tokenizer::duplicateTypedef(Token *&tokPtr, const Token *name, const Token *typeDef) const +{ + // check for an end of definition + Token * tok = tokPtr; + if (tok && Token::Match(tok->next(), ";|,|[|=|)|>|(|{")) { + Token * end = tok->next(); + + if (end->str() == "[") { + if (!end->link()) + syntaxError(end); // invalid code + end = end->link()->next(); + } else if (end->str() == ",") { + // check for derived class + if (Token::Match(tok->previous(), "public|private|protected")) + return false; + + // find end of definition + while (end && end->next() && !Token::Match(end->next(), ";|)|>")) { + if (end->next()->str() == "(") + end = end->linkAt(1); + + end = (end)?end->next():nullptr; + } + if (end) + end = end->next(); + } else if (end->str() == "(") { + if (startsWith(tok->previous()->str(), "operator")) + // conversion operator + return false; + if (tok->previous()->str() == "typedef") + // typedef of function returning this type + return false; + if (Token::Match(tok->previous(), "public:|private:|protected:")) + return false; + if (tok->previous()->str() == ">") { + if (!Token::Match(tok->tokAt(-2), "%type%")) + return false; + + if (!Token::Match(tok->tokAt(-3), ",|<")) + return false; + + tokPtr = end->link(); + return true; + } + } + + if (end) { + if (Token::simpleMatch(end, ") {")) { // function parameter ? + // look backwards + if (Token::Match(tok->previous(), "%type%") && + !Token::Match(tok->previous(), "return|new|const|struct")) { + // duplicate definition so skip entire function + tokPtr = end->next()->link(); + return true; + } + } else if (end->str() == ">") { // template parameter ? + // look backwards + if (Token::Match(tok->previous(), "%type%") && + !Token::Match(tok->previous(), "return|new|const|volatile")) { + // duplicate definition so skip entire template + while (end && end->str() != "{") + end = end->next(); + if (end) { + tokPtr = end->link(); + return true; + } + } + } else { + // look backwards + if (Token::Match(tok->previous(), "typedef|}|>") || + (end->str() == ";" && tok->previous()->str() == ",") || + (tok->previous()->str() == "*" && tok->next()->str() != "(") || + (Token::Match(tok->previous(), "%type%") && + (!Token::Match(tok->previous(), "return|new|const|friend|public|private|protected|throw|extern") && + !Token::simpleMatch(tok->tokAt(-2), "friend class")))) { + // scan backwards for the end of the previous statement + while (tok && tok->previous() && !Token::Match(tok->previous(), ";|{")) { + if (tok->previous()->str() == "}") { + tok = tok->previous()->link(); + } else if (tok->previous()->str() == "typedef") { + return true; + } else if (tok->previous()->str() == "enum") { + return true; + } else if (tok->previous()->str() == "struct") { + if (tok->strAt(-2) == "typedef" && + tok->next()->str() == "{" && + typeDef->strAt(3) != "{") { + // declaration after forward declaration + return true; + } + if (tok->next()->str() == "{") + return true; + if (Token::Match(tok->next(), ")|*")) + return true; + if (tok->next()->str() == name->str()) + return true; + if (tok->next()->str() != ";") + return true; + return false; + } else if (tok->previous()->str() == "union") { + return tok->next()->str() != ";"; + } else if (tok->isCpp() && tok->previous()->str() == "class") { + return tok->next()->str() != ";"; + } + if (tok) + tok = tok->previous(); + } + + if (tokPtr->strAt(1) != "(" || !Token::Match(tokPtr->linkAt(1), ") .|(|[")) + return true; + } + } + } + } + + return false; +} + +void Tokenizer::unsupportedTypedef(const Token *tok) const +{ + if (!mSettings.debugwarnings) + return; + + std::ostringstream str; + const Token *tok1 = tok; + int level = 0; + while (tok) { + if (level == 0 && tok->str() == ";") + break; + if (tok->str() == "{") + ++level; + else if (tok->str() == "}") { + if (level == 0) + break; + --level; + } + + if (tok != tok1) + str << " "; + str << tok->str(); + tok = tok->next(); + } + if (tok) + str << " ;"; + + reportError(tok1, Severity::debug, "simplifyTypedef", + "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; + } + if (typeDef->next()->str() == "{") + Token::eraseTokens(typeDef, typeDef->linkAt(1)); + else if (typeDef->next()->str() == "}") + break; + typeDef->deleteNext(); + } + + if (typeDef != list.front()) { + tok = typeDef->previous(); + tok->deleteNext(); + } else { + list.front()->deleteThis(); + tok = list.front(); + } + + return tok; +} + +namespace { + struct Space { + std::string className; + const Token* bodyEnd{}; // for body contains typedef define + const Token* bodyEnd2{}; // for body contains typedef using + bool isNamespace{}; + std::set recordTypes; + }; +} + +static Token *splitDefinitionFromTypedef(Token *tok, nonneg int *unnamedCount) +{ + std::string name; + std::set qualifiers; + + while (Token::Match(tok->next(), "const|volatile")) { + qualifiers.insert(tok->next()->str()); + tok->deleteNext(); + } + + // skip "class|struct|union|enum" + Token *tok1 = tok->tokAt(2); + + const bool hasName = Token::Match(tok1, "%name%"); + + // skip name + if (hasName) { + name = tok1->str(); + tok1 = tok1->next(); + } + + // skip base classes if present + if (tok1->str() == ":") { + tok1 = tok1->next(); + while (tok1 && tok1->str() != "{") + tok1 = tok1->next(); + if (!tok1) + return nullptr; + } + + // skip to end + tok1 = tok1->link(); + + if (!hasName) { // unnamed + if (tok1->next()) { + // use typedef name if available + if (Token::Match(tok1->next(), "%type%")) + name = tok1->next()->str(); + else // create a unique name + name = "Unnamed" + std::to_string((*unnamedCount)++); + tok->next()->insertToken(name); + } else + return nullptr; + } + + tok1->insertToken(";"); + tok1 = tok1->next(); + + if (tok1->next() && tok1->next()->str() == ";" && tok1->previous()->str() == "}") { + tok->deleteThis(); + tok1->deleteThis(); + return nullptr; + } + tok1->insertToken("typedef"); + tok1 = tok1->next(); + Token * tok3 = tok1; + for (const std::string &qualifier : qualifiers) { + tok1->insertToken(qualifier); + 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 */ +const Token *Tokenizer::processFunc(const Token *tok2, bool inOperator) const +{ + if (tok2->next() && tok2->next()->str() != ")" && + tok2->next()->str() != ",") { + // skip over tokens for some types of canonicalization + if (Token::Match(tok2->next(), "( * %type% ) (")) + tok2 = tok2->linkAt(5); + else if (Token::Match(tok2->next(), "* ( * %type% ) (")) + tok2 = tok2->linkAt(6); + else if (Token::Match(tok2->next(), "* ( * %type% ) ;")) + tok2 = tok2->tokAt(5); + else if (Token::Match(tok2->next(), "* ( %type% [") && + Token::Match(tok2->linkAt(4), "] ) ;|=")) + tok2 = tok2->linkAt(4)->next(); + else if (Token::Match(tok2->next(), "* ( * %type% (")) + tok2 = tok2->linkAt(5)->next(); + else if (Token::simpleMatch(tok2->next(), "* [") && + Token::simpleMatch(tok2->linkAt(2), "] ;")) + tok2 = tok2->next(); + else { + if (tok2->next()->str() == "(") + tok2 = tok2->next()->link(); + else if (!inOperator && !Token::Match(tok2->next(), "[|>|;")) { + tok2 = tok2->next(); + + while (Token::Match(tok2, "*|&") && + !Token::Match(tok2->next(), ")|>")) + tok2 = tok2->next(); + + // skip over namespace + while (Token::Match(tok2, "%name% ::")) + tok2 = tok2->tokAt(2); + + if (!tok2) + return nullptr; + + if (tok2->str() == "(" && + tok2->link()->next() && + tok2->link()->next()->str() == "(") { + tok2 = tok2->link(); + + if (tok2->next()->str() == "(") + tok2 = tok2->next()->link(); + } + + // skip over typedef parameter + if (tok2->next() && tok2->next()->str() == "(") { + tok2 = tok2->next()->link(); + if (!tok2->next()) + syntaxError(tok2); + + if (tok2->next()->str() == "(") + tok2 = tok2->next()->link(); + } + } + } + } + return tok2; +} + +Token *Tokenizer::processFunc(Token *tok2, bool inOperator) +{ + return const_cast(processFunc(const_cast(tok2), inOperator)); +} + +void Tokenizer::simplifyUsingToTypedef() +{ + if (!isCPP() || mSettings.standards.cpp < Standards::CPP11) + return; + + for (Token *tok = list.front(); tok; tok = tok->next()) { + // using a::b; => typedef a::b b; + if ((Token::Match(tok, "[;{}] using %name% :: %name% ::|;") && !tok->tokAt(2)->isKeyword()) || + (Token::Match(tok, "[;{}] using :: %name% :: %name% ::|;") && !tok->tokAt(3)->isKeyword())) { + Token *endtok = tok->tokAt(5); + if (Token::Match(endtok, "%name%")) + endtok = endtok->next(); + while (Token::Match(endtok, ":: %name%")) + endtok = endtok->tokAt(2); + if (endtok && endtok->str() == ";") { + tok->next()->str("typedef"); + endtok = endtok->previous(); + endtok->insertToken(endtok->str()); + } + } + } +} + +void Tokenizer::simplifyTypedefLHS() +{ + if (!list.front()) + return; + + for (Token* tok = list.front()->next(); tok; tok = tok->next()) { + if (tok->str() == "typedef") { + bool doSimplify = !Token::Match(tok->previous(), ";|{|}|:|public:|private:|protected:"); + if (doSimplify && Token::simpleMatch(tok->previous(), ")") && Token::Match(tok->linkAt(-1)->previous(), "if|for|while")) + doSimplify = false; + bool haveStart = false; + Token* start{}; + if (!doSimplify && Token::simpleMatch(tok->previous(), "}")) { + start = tok->linkAt(-1)->previous(); + while (Token::Match(start, "%name%")) { + if (Token::Match(start, "class|struct|union|enum")) { + start = start->previous(); + doSimplify = true; + haveStart = true; + break; + } + start = start->previous(); + } + } + if (doSimplify) { + if (!haveStart) { + start = tok; + while (start && !Token::Match(start, "[;{}]")) + start = start->previous(); + } + if (start) + start = start->next(); + else + start = list.front(); + start->insertTokenBefore(tok->str()); + tok->deleteThis(); + } + } + } +} + +namespace { + class TypedefSimplifier { + private: + Token* mTypedefToken; // The "typedef" token + Token* mEndToken{nullptr}; // Semicolon + std::pair mRangeType; + std::pair mRangeTypeQualifiers; + std::pair mRangeAfterVar; + Token* mNameToken{nullptr}; + bool mFail = false; + bool mReplaceFailed = false; + bool mUsed = false; + + public: + explicit TypedefSimplifier(Token* typedefToken) : mTypedefToken(typedefToken) { + Token* start = typedefToken->next(); + if (Token::simpleMatch(start, "typename")) + start = start->next(); + + // TODO handle unnamed structs etc + if (Token::Match(start, "const| enum|struct|union|class %name%| {")) { + const std::pair rangeBefore(start, Token::findsimplematch(start, "{")); + + // find typedef name token + Token* nameToken = rangeBefore.second->link()->next(); + while (Token::Match(nameToken, "%name%|* %name%|*")) + nameToken = nameToken->next(); + const std::pair rangeQualifiers(rangeBefore.second->link()->next(), nameToken); + + if (Token::Match(nameToken, "%name% ;")) { + if (Token::Match(rangeBefore.second->previous(), "enum|struct|union|class {")) + rangeBefore.second->previous()->insertToken(nameToken->str()); + mRangeType = rangeBefore; + mRangeTypeQualifiers = rangeQualifiers; + Token* typeName = rangeBefore.second->previous(); + if (typeName->isKeyword()) { + // TODO typeName->insertToken("T:" + std::to_string(num++)); + typeName->insertToken(nameToken->str()); + } + mNameToken = nameToken; + mEndToken = nameToken->next(); + return; + } + } + + for (Token* type = start; Token::Match(type, "%name%|*|&|&&"); type = type->next()) { + if (type != start && Token::Match(type, "%name% ;") && !type->isStandardType()) { + mRangeType.first = start; + mRangeType.second = type; + mNameToken = type; + mEndToken = mNameToken->next(); + return; + } + if (type != start && Token::Match(type, "%name% [")) { + Token* end = type->linkAt(1); + while (Token::simpleMatch(end, "] [")) + end = end->linkAt(1); + if (!Token::simpleMatch(end, "] ;")) + break; + mRangeType.first = start; + mRangeType.second = type; + mNameToken = type; + mEndToken = end->next(); + mRangeAfterVar.first = mNameToken->next(); + mRangeAfterVar.second = mEndToken; + return; + } + if (Token::Match(type->next(), "( * const| %name% ) (") && Token::simpleMatch(type->linkAt(1)->linkAt(1), ") ;")) { + mNameToken = type->linkAt(1)->previous(); + mEndToken = type->linkAt(1)->linkAt(1)->next(); + mRangeType.first = start; + mRangeType.second = mNameToken; + mRangeAfterVar.first = mNameToken->next(); + mRangeAfterVar.second = mEndToken; + return; + } + if (Token::Match(type, "%name% ( !!(") && Token::simpleMatch(type->linkAt(1), ") ;") && !type->isStandardType()) { + mNameToken = type; + mEndToken = type->linkAt(1)->next(); + mRangeType.first = start; + mRangeType.second = type; + mRangeAfterVar.first = mNameToken->next(); + mRangeAfterVar.second = mEndToken; + return; + } + } + // TODO: handle all typedefs + if ((false)) + printTypedef(typedefToken); + mFail = true; + } + + const Token* getTypedefToken() const { + return mTypedefToken; + } + + bool isUsed() const { + return mUsed; + } + + bool isInvalidConstFunctionType(const std::map& m) const { + if (!Token::Match(mTypedefToken, "typedef const %name% %name% ;")) + return false; + const auto it = m.find(mTypedefToken->strAt(2)); + if (it == m.end()) + return false; + return Token::Match(it->second.mNameToken, "%name% ("); + } + + bool fail() const { + return mFail; + } + + bool replaceFailed() const { + return mReplaceFailed; + } + + bool isStructEtc() const { + return mRangeType.second && mRangeType.second->str() == "{"; + } + + std::string name() const { + return mNameToken ? mNameToken->str() : ""; + } + + void replace(Token* tok) { + if (tok == mNameToken) + return; + + mUsed = true; + + // Special handling for T() when T is a pointer + if (Token::Match(tok, "%name% ( )")) { + bool pointerType = false; + for (const Token* type = mRangeType.first; type != mRangeType.second; type = type->next()) { + if (type->str() == "*" || type->str() == "&") { + pointerType = true; + break; + } + } + for (const Token* type = mRangeTypeQualifiers.first; type != mRangeTypeQualifiers.second; type = type->next()) { + if (type->str() == "*" || type->str() == "&") { + pointerType = true; + break; + } + } + if (pointerType) { + tok->deleteThis(); + tok->next()->insertToken("0"); + Token* tok2 = insertTokens(tok, mRangeType); + insertTokens(tok2, mRangeTypeQualifiers); + return; + } + } + + // Special handling of function pointer cast + const bool isFunctionPointer = Token::Match(mNameToken, "%name% )"); + if (isFunctionPointer && isCast(tok->previous())) { + tok->insertToken("*"); + insertTokens(tok, std::pair(mRangeType.first, mNameToken->linkAt(1))); + tok->deleteThis(); + return; + } + + // Inherited type => skip "struct" / "class" + if (Token::Match(mRangeType.first, "const| struct|class %name% {") && Token::Match(tok->previous(), "public|protected|private|<")) { + tok->originalName(tok->str()); + tok->str(mRangeType.second->previous()->str()); + return; + } + + if (Token::Match(tok, "%name% ::")) { + if (Token::Match(mRangeType.first, "const| struct|class %name% %name% ;")) { + tok->originalName(tok->str()); + tok->str(mRangeType.second->previous()->str()); + } else { + mReplaceFailed = true; + } + return; + } + + // pointer => move "const" + if (Token::simpleMatch(tok->previous(), "const")) { + bool pointerType = false; + for (const Token* type = mRangeType.first; type != mRangeType.second; type = type->next()) { + if (type->str() == "*") { + pointerType = true; + break; + } + } + if (pointerType) { + tok->insertToken("const"); + tok->next()->column(tok->column()); + tok->next()->setMacroName(tok->previous()->getMacroName()); + tok->deletePrevious(); + } + } + + // Do not duplicate class/struct/enum/union + if (Token::Match(tok->previous(), "enum|union|struct|class")) { + bool found = false; + const std::string &kw = tok->previous()->str(); + for (const Token* type = mRangeType.first; type != mRangeType.second; type = type->next()) { + if (type->str() == kw) { + found = true; + break; + } + } + if (found) + tok->deletePrevious(); + else { + mReplaceFailed = true; + return; + } + } + + // don't add class|struct|union in inheritance list + auto rangeType = mRangeType; + if (Token::Match(tok->previous(), "public|private|protected")) { + while (Token::Match(rangeType.first, "const|class|struct|union")) + rangeType.first = rangeType.first->next(); + } + + Token* const tok2 = insertTokens(tok, rangeType); + Token* const tok3 = insertTokens(tok2, mRangeTypeQualifiers); + + tok2->originalName(tok->str()); + tok3->originalName(tok->str()); + Token *after = tok3; + while (Token::Match(after, "%name%|*|&|&&|::")) + after = after->next(); + if (Token::Match(mNameToken, "%name% (") && Token::simpleMatch(tok3->next(), "*")) { + while (Token::Match(after, "(|[")) + after = after->link()->next(); + if (after) { + tok3->insertToken("("); + after->previous()->insertToken(")"); + Token::createMutualLinks(tok3->next(), after->previous()); + } + } + + bool useAfterVarRange = true; + if (Token::simpleMatch(mRangeAfterVar.first, "[")) { + if (Token::Match(after->previous(), "%name% ( !!*")) { + useAfterVarRange = false; + // Function return type => replace array with "*" + for (const Token* a = mRangeAfterVar.first; Token::simpleMatch(a, "["); a = a->link()->next()) + tok3->insertToken("*"); + } else if (Token::Match(after->previous(), "%name% ( * %name% ) [")) { + after = after->linkAt(4)->next(); + } else { + Token* prev = after->previous(); + if (prev->isName() && prev != tok3) + prev = prev->previous(); + if (Token::Match(prev, "*|&|&&") && prev != tok3) { + while (Token::Match(prev, "*|&|&&") && prev != tok3) + prev = prev->previous(); + prev->insertToken("("); + after->previous()->insertToken(")"); + } + } + } + + if (isFunctionPointer) { + if (Token::Match(after, "( * %name% ) (")) + after = after->link()->linkAt(1)->next(); + else if (after->str() == "(") { + useAfterVarRange = false; + if (Token::simpleMatch(tok3->previous(), "( *")) + tok3->deletePrevious(); + } + else if (after->str() == "[") { + while (after && after->str() == "[") + after = after->link()->next(); + } + } + else { + while (Token::simpleMatch(after, "[")) + after = after->link()->next(); + } + + if (!after) + throw InternalError(tok, "Failed to simplify typedef. Is the code valid?"); + + Token* const tok4 = useAfterVarRange ? insertTokens(after->previous(), mRangeAfterVar)->next() : tok3->next(); + + tok->deleteThis(); + + // Unsplit variable declarations + if (tok4 && tok4->isSplittedVarDeclEq() && + ((tok4->isCpp() && Token::Match(tok4->tokAt(-2), "&|&& %name% ;")) || Token::Match(tok4->previous(), "] ; %name% = {"))) { + tok4->deleteNext(); + tok4->deleteThis(); + } + + // Set links + std::stack brackets; + for (; tok != tok4; tok = tok->next()) { + if (Token::Match(tok, "[{([]")) + brackets.push(tok); + else if (Token::Match(tok, "[})]]")) { + Token::createMutualLinks(brackets.top(), tok); + brackets.pop(); + } + } + } + + void removeDeclaration() { + if (Token::simpleMatch(mRangeType.second, "{")) { + while (Token::Match(mTypedefToken, "typedef|const")) + mTypedefToken->deleteThis(); + Token::eraseTokens(mRangeType.second->link(), mEndToken); + } else { + Token::eraseTokens(mTypedefToken, mEndToken); + mTypedefToken->deleteThis(); + } + } + + static int canReplaceStatic(const Token* tok) { + if (!Token::Match(tok, "%name% %name%|*|&|&&|;|(|)|,|::")) { + if (Token::Match(tok->previous(), "( %name% =") && Token::Match(tok->linkAt(-1), ") %name%|{") && !tok->tokAt(-2)->isKeyword()) + return true; + if (Token::Match(tok->previous(), ", %name% =")) + return true; + if (Token::Match(tok->previous(), "new %name% [")) + return true; + if (Token::Match(tok->previous(), "< %name%") && tok->previous()->findClosingBracket()) + return true; + if (Token::Match(tok->previous(), ", %name% >|>>")) { + for (const Token* prev = tok->previous(); prev; prev = prev->previous()) { + if (Token::Match(prev, "[;{}(]")) + break; + if (prev->str() == "<" && prev->findClosingBracket() == tok->next()) + return true; + if (prev->str() == ")") + prev = prev->link(); + } + return true; + } + if (Token::Match(tok->previous(), "public|protected|private")) + return true; + if (Token::Match(tok->previous(), ", %name% :")) { + bool isGeneric = false; + for (; tok; tok = tok->previous()) { + if (Token::Match(tok, ")|]")) + tok = tok->link(); + else if (Token::Match(tok, "[;{}(]")) { + isGeneric = Token::simpleMatch(tok->previous(), "_Generic ("); + break; + } + } + return isGeneric; + } + return false; + } + return -1; + } + + bool canReplace(const Token* tok) { + if (mNameToken == tok) + return false; + if (!Token::Match(tok->previous(), "%name%|;|{|}|(|,|<") && !Token::Match(tok->previous(), "!!. %name% (")) + return false; + { + const int res = canReplaceStatic(tok); + if (res == 0 || res == 1) + return res != 0; + } + if (Token::Match(tok->previous(), "%name%") && !tok->previous()->isKeyword()) + return false; + if (Token::simpleMatch(tok->next(), "(") && Token::Match(tok->linkAt(1), ") %name%|{")) + return false; + if (Token::Match(tok->previous(), "struct|union|class|enum %name% %name%") && + Token::simpleMatch(mRangeType.second, "{") && + tok->str() != mRangeType.second->previous()->str()) + return true; + if (Token::Match(tok->previous(), "; %name% ;")) + return false; + if (Token::Match(tok->previous(), "<|, %name% * ,|>")) + return true; + for (const Token* after = tok->next(); after; after = after->next()) { + if (Token::Match(after, "%name%|::|&|*|&&")) + continue; + if (after->str() == "<" && after->link()) + break; + if (after->isNumber()) + return false; + if (after->isComparisonOp() || after->isArithmeticalOp()) + return false; + break; + } + for (const Token* before = tok->previous(); before; before = before->previous()) { + if (Token::Match(before, "[+-*/&|~!]")) + return false; + if (Token::Match(before, "struct|union|class|enum") || before->isStandardType()) + return false; + if (before->str() == "::") + return false; + if (before->isName()) + continue; + break; + } + return true; + } + + Token* endToken() const { + return mEndToken; + } + + private: + static bool isCast(const Token* tok) { + if (Token::Match(tok, "( %name% ) (|%name%")) + return !tok->tokAt(2)->isKeyword(); + if (Token::Match(tok, "< %name% > (") && tok->previous() && endsWith(tok->previous()->str(), "_cast", 5)) + return true; + return false; + } + + static Token* insertTokens(Token* to, std::pair range) { + for (const Token* from = range.first; from != range.second; from = from->next()) { + to->insertToken(from->str()); + to->next()->column(to->column()); + to = to->next(); + to->isSimplifiedTypedef(true); + to->isExternC(from->isExternC()); + } + return to; + } + + static void printTypedef(const Token *tok) { + int indent = 0; + while (tok && (indent > 0 || tok->str() != ";")) { + if (tok->str() == "{") + ++indent; + else if (tok->str() == "}") + --indent; + std::cout << " " << tok->str(); + tok = tok->next(); + } + std::cout << "\n"; + } + }; +} + +void Tokenizer::simplifyTypedef() +{ + // Simplify global typedefs that are not redefined with the fast 1-pass simplification. + // Then use the slower old typedef simplification. + std::map numberOfTypedefs; + for (Token* tok = list.front(); tok; tok = tok->next()) { + if (tok->str() == "typedef") { + TypedefSimplifier ts(tok); + if (!ts.fail()) + numberOfTypedefs[ts.name()]++; + continue; + } + } + + int indentlevel = 0; + std::map typedefs; + for (Token* tok = list.front(); tok; tok = tok->next()) { + if (!tok->isName()) { + if (tok->str()[0] == '{') + ++indentlevel; + else if (tok->str()[0] == '}') + --indentlevel; + continue; + } + + if (indentlevel == 0 && tok->str() == "typedef") { + TypedefSimplifier ts(tok); + if (!ts.fail() && numberOfTypedefs[ts.name()] == 1) { + if (mSettings.severity.isEnabled(Severity::portability) && ts.isInvalidConstFunctionType(typedefs)) + reportError(tok->next(), Severity::portability, "invalidConstFunctionType", + "It is unspecified behavior to const qualify a function type."); + typedefs.emplace(ts.name(), ts); + if (!ts.isStructEtc()) + tok = ts.endToken(); + } + continue; + } + + auto it = typedefs.find(tok->str()); + if (it != typedefs.end() && it->second.canReplace(tok)) { + std::set r; + while (it != typedefs.end() && r.insert(tok->str()).second) { + it->second.replace(tok); + it = typedefs.find(tok->str()); + } + } else if (tok->str() == "enum") { + while (Token::Match(tok, "%name%|:|::")) + tok = tok->next(); + if (!tok) + break; + if (tok->str() == "{") + tok = tok->link(); + } + } + + if (!typedefs.empty()) + { + // remove typedefs + for (auto &t: typedefs) { + if (!t.second.replaceFailed()) { + const Token* const typedefToken = t.second.getTypedefToken(); + TypedefInfo typedefInfo; + typedefInfo.name = t.second.name(); + typedefInfo.filename = list.file(typedefToken); + typedefInfo.lineNumber = typedefToken->linenr(); + typedefInfo.column = typedefToken->column(); + typedefInfo.used = t.second.isUsed(); + mTypedefInfo.push_back(std::move(typedefInfo)); + + t.second.removeDeclaration(); + } + } + + while (Token::Match(list.front(), "; %any%")) + list.front()->deleteThis(); + } + + simplifyTypedefCpp(); +} + +static bool isEnumScope(const Token* tok) +{ + if (!Token::simpleMatch(tok, "{")) + return false; + tok = tok->previous(); + while (tok && !tok->isKeyword() && Token::Match(tok, "%name%|::|:")) + tok = tok->previous(); + if (Token::simpleMatch(tok, "class")) + tok = tok->previous(); + return Token::simpleMatch(tok, "enum"); +} + +void Tokenizer::simplifyTypedefCpp() +{ + bool isNamespace = false; + std::string className, fullClassName; + bool hasClass = false; + bool goback = false; + + // add global namespace + std::vector spaceInfo(1); + + // Convert "using a::b;" to corresponding typedef statements + simplifyUsingToTypedef(); + + const std::time_t maxTime = mSettings.typedefMaxTime > 0 ? std::time(nullptr) + mSettings.typedefMaxTime: 0; + + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!list.getFiles().empty()) + mErrorLogger.reportProgress(list.getFiles()[0], "Tokenize (typedef)", tok->progressValue()); + + if (Settings::terminated()) + return; + + if (maxTime > 0 && std::time(nullptr) > maxTime) { + if (mSettings.debugwarnings) { + ErrorMessage::FileLocation loc(list.getFiles()[0], 0, 0); + ErrorMessage errmsg({std::move(loc)}, + emptyString, + Severity::debug, + "Typedef simplification instantiation maximum time exceeded", + "typedefMaxTime", + Certainty::normal); + mErrorLogger.reportErr(errmsg); + } + return; + } + + if (goback) { + //jump back once, see the comment at the end of the function + goback = false; + tok = tok->previous(); + } + + if (tok->str() != "typedef") { + if (Token::simpleMatch(tok, "( typedef")) { + // Skip typedefs inside parentheses (#2453 and #4002) + tok = tok->next(); + } else if (Token::Match(tok, "class|struct|namespace %any%") && + (!tok->previous() || tok->previous()->str() != "enum")) { + isNamespace = (tok->str() == "namespace"); + hasClass = true; + className = tok->next()->str(); + const Token *tok1 = tok->next(); + fullClassName = className; + while (Token::Match(tok1, "%name% :: %name%")) { + tok1 = tok1->tokAt(2); + fullClassName += " :: " + tok1->str(); + } + } else if (hasClass && tok->str() == ";") { + hasClass = false; + } else if (hasClass && tok->str() == "{") { + if (!isNamespace) + spaceInfo.back().recordTypes.insert(fullClassName); + + Space info; + info.isNamespace = isNamespace; + info.className = className; + info.bodyEnd = tok->link(); + info.bodyEnd2 = tok->link(); + spaceInfo.push_back(std::move(info)); + + hasClass = false; + } else if (spaceInfo.size() > 1 && tok->str() == "}" && spaceInfo.back().bodyEnd == tok) { + spaceInfo.pop_back(); + } + continue; + } + + // pull struct, union, enum or class definition out of typedef + // use typedef name for unnamed struct, union, enum or class + const Token* tokClass = tok->next(); + while (Token::Match(tokClass, "const|volatile")) + tokClass = tokClass->next(); + if (Token::Match(tokClass, "struct|enum|union|class %type%| {|:")) { + Token *tok1 = splitDefinitionFromTypedef(tok, &mUnnamedCount); + if (!tok1) + continue; + tok = tok1; + } + + /** @todo add support for union */ + if (Token::Match(tok->next(), "enum %type% %type% ;") && tok->strAt(2) == tok->strAt(3)) { + tok->deleteNext(3); + tok->deleteThis(); + if (tok->next()) + tok->deleteThis(); + //now the next token to process is 'tok', not 'tok->next()'; + goback = true; + continue; + } + + Token *typeName; + Token *typeStart = nullptr; + Token *typeEnd = nullptr; + Token *argStart = nullptr; + Token *argEnd = nullptr; + Token *arrayStart = nullptr; + Token *arrayEnd = nullptr; + Token *specStart = nullptr; + Token *specEnd = nullptr; + Token *typeDef = tok; + Token *argFuncRetStart = nullptr; + Token *argFuncRetEnd = nullptr; + Token *funcStart = nullptr; + Token *funcEnd = nullptr; + Token *tokOffset = tok->next(); + bool function = false; + bool functionPtr = false; + bool functionRetFuncPtr = false; + bool functionPtrRetFuncPtr = false; + bool ptrToArray = false; + bool refToArray = false; + bool ptrMember = false; + bool typeOf = false; + Token *namespaceStart = nullptr; + Token *namespaceEnd = nullptr; + + // check for invalid input + if (!tokOffset || tokOffset->isControlFlowKeyword()) + syntaxError(tok); + + if (tokOffset->str() == "::") { + typeStart = tokOffset; + tokOffset = tokOffset->next(); + + while (Token::Match(tokOffset, "%type% ::")) + tokOffset = tokOffset->tokAt(2); + + typeEnd = tokOffset; + + if (Token::Match(tokOffset, "%type%")) + tokOffset = tokOffset->next(); + } else if (Token::Match(tokOffset, "%type% ::")) { + typeStart = tokOffset; + + do { + tokOffset = tokOffset->tokAt(2); + } while (Token::Match(tokOffset, "%type% ::")); + + typeEnd = tokOffset; + + if (Token::Match(tokOffset, "%type%")) + tokOffset = tokOffset->next(); + } else if (Token::Match(tokOffset, "%type%")) { + typeStart = tokOffset; + + while (Token::Match(tokOffset, "const|struct|enum %type%") || + (tokOffset->next() && tokOffset->next()->isStandardType() && !Token::Match(tokOffset->next(), "%name% ;"))) + tokOffset = tokOffset->next(); + + typeEnd = tokOffset; + if (!Token::Match(tokOffset->next(), "%name% ;")) + tokOffset = tokOffset->next(); + + while (Token::Match(tokOffset, "%type%") && + (tokOffset->isStandardType() || Token::Match(tokOffset, "unsigned|signed")) && + !Token::Match(tokOffset->next(), "%name% ;")) { + typeEnd = tokOffset; + tokOffset = tokOffset->next(); + } + + bool atEnd = false; + while (!atEnd) { + if (tokOffset && tokOffset->str() == "::") { + typeEnd = tokOffset; + tokOffset = tokOffset->next(); + } + + if (Token::Match(tokOffset, "%type%") && + tokOffset->next() && !Token::Match(tokOffset->next(), "[|;|,|(")) { + typeEnd = tokOffset; + tokOffset = tokOffset->next(); + } else if (Token::simpleMatch(tokOffset, "const (")) { + typeEnd = tokOffset; + tokOffset = tokOffset->next(); + atEnd = true; + } else + atEnd = true; + } + } else + continue; // invalid input + + // check for invalid input + if (!tokOffset) + syntaxError(tok); + + // check for template + if (!isC() && tokOffset->str() == "<") { + typeEnd = tokOffset->findClosingBracket(); + + while (typeEnd && Token::Match(typeEnd->next(), ":: %type%")) + typeEnd = typeEnd->tokAt(2); + + if (!typeEnd) { + // internal error + return; + } + + while (Token::Match(typeEnd->next(), "const|volatile")) + typeEnd = typeEnd->next(); + + tok = typeEnd; + tokOffset = tok->next(); + } + + std::list pointers; + // check for pointers and references + while (Token::Match(tokOffset, "*|&|&&|const")) { + pointers.push_back(tokOffset->str()); + tokOffset = tokOffset->next(); + } + + // check for invalid input + if (!tokOffset) + syntaxError(tok); + + if (tokOffset->isName() && !tokOffset->isKeyword()) { + // found the type name + typeName = tokOffset; + tokOffset = tokOffset->next(); + + // check for array + while (tokOffset && tokOffset->str() == "[") { + if (!arrayStart) + arrayStart = tokOffset; + arrayEnd = tokOffset->link(); + tokOffset = arrayEnd->next(); + } + + // check for end or another + if (Token::Match(tokOffset, ";|,")) + tok = tokOffset; + + // or a function typedef + else if (tokOffset && tokOffset->str() == "(") { + Token *tokOffset2 = nullptr; + if (Token::Match(tokOffset, "( *|%name%")) { + tokOffset2 = tokOffset->next(); + if (tokOffset2->str() == "typename") + tokOffset2 = tokOffset2->next(); + while (Token::Match(tokOffset2, "%type% ::")) + tokOffset2 = tokOffset2->tokAt(2); + } + + // unhandled typedef, skip it and continue + if (typeName->str() == "void") { + unsupportedTypedef(typeDef); + tok = deleteInvalidTypedef(typeDef); + if (tok == list.front()) + //now the next token to process is 'tok', not 'tok->next()'; + goback = true; + continue; + } + + // function pointer + if (Token::Match(tokOffset2, "* %name% ) (")) { + // name token wasn't a name, it was part of the type + typeEnd = typeEnd->next(); + functionPtr = true; + funcStart = funcEnd = tokOffset2; // * + tokOffset = tokOffset2->tokAt(3); // ( + typeName = tokOffset->tokAt(-2); + argStart = tokOffset; + argEnd = tokOffset->link(); + tok = argEnd->next(); + } + + // function + else if (isFunctionHead(tokOffset->link(), ";,")) { + function = true; + if (tokOffset->link()->next()->str() == "const") { + specStart = tokOffset->link()->next(); + specEnd = specStart; + } + argStart = tokOffset; + argEnd = tokOffset->link(); + tok = argEnd->next(); + if (specStart) + tok = tok->next(); + } + + // syntax error + else + syntaxError(tok); + } + + // unhandled typedef, skip it and continue + else { + unsupportedTypedef(typeDef); + tok = deleteInvalidTypedef(typeDef); + if (tok == list.front()) + //now the next token to process is 'tok', not 'tok->next()'; + goback = true; + continue; + } + } + + // typeof: typedef typeof ( ... ) type; + else if (Token::simpleMatch(tokOffset->previous(), "typeof (") && + Token::Match(tokOffset->link(), ") %type% ;")) { + argStart = tokOffset; + argEnd = tokOffset->link(); + typeName = tokOffset->link()->next(); + tok = typeName->next(); + typeOf = true; + } + + // function: typedef ... ( ... type )( ... ); + // typedef ... (( ... type )( ... )); + // typedef ... ( * ( ... type )( ... )); + else if (tokOffset->str() == "(" && ( + (tokOffset->link() && Token::Match(tokOffset->link()->previous(), "%type% ) (") && + Token::Match(tokOffset->link()->next()->link(), ") const|volatile|;")) || + (Token::simpleMatch(tokOffset, "( (") && + tokOffset->next() && Token::Match(tokOffset->next()->link()->previous(), "%type% ) (") && + Token::Match(tokOffset->next()->link()->next()->link(), ") const|volatile| ) ;|,")) || + (Token::simpleMatch(tokOffset, "( * (") && + tokOffset->linkAt(2) && Token::Match(tokOffset->linkAt(2)->previous(), "%type% ) (") && + Token::Match(tokOffset->linkAt(2)->next()->link(), ") const|volatile| ) ;|,")))) { + if (tokOffset->next()->str() == "(") + tokOffset = tokOffset->next(); + else if (Token::simpleMatch(tokOffset, "( * (")) { + pointers.emplace_back("*"); + tokOffset = tokOffset->tokAt(2); + } + + if (tokOffset->link()->strAt(-2) == "*") + functionPtr = true; + else + function = true; + funcStart = tokOffset->next(); + tokOffset = tokOffset->link(); + funcEnd = tokOffset->tokAt(-2); + typeName = tokOffset->previous(); + argStart = tokOffset->next(); + argEnd = tokOffset->next()->link(); + if (!argEnd) + syntaxError(argStart); + + tok = argEnd->next(); + Token *spec = tok; + if (Token::Match(spec, "const|volatile")) { + specStart = spec; + specEnd = spec; + while (Token::Match(spec->next(), "const|volatile")) { + specEnd = spec->next(); + spec = specEnd; + } + tok = specEnd->next(); + } + if (!tok) + syntaxError(specEnd); + + if (tok->str() == ")") + tok = tok->next(); + } + + else if (Token::Match(tokOffset, "( %type% (")) { + function = true; + if (tokOffset->link()->next()) { + tok = tokOffset->link()->next(); + tokOffset = tokOffset->tokAt(2); + typeName = tokOffset->previous(); + argStart = tokOffset; + argEnd = tokOffset->link(); + } else { + // internal error + continue; + } + } + + // pointer to function returning pointer to function + else if (Token::Match(tokOffset, "( * ( * %type% ) (") && + Token::simpleMatch(tokOffset->linkAt(6), ") ) (") && + Token::Match(tokOffset->linkAt(6)->linkAt(2), ") ;|,")) { + functionPtrRetFuncPtr = true; + + tokOffset = tokOffset->tokAt(6); + typeName = tokOffset->tokAt(-2); + argStart = tokOffset; + argEnd = tokOffset->link(); + if (!argEnd) + syntaxError(arrayStart); + + argFuncRetStart = argEnd->tokAt(2); + argFuncRetEnd = argFuncRetStart->link(); + if (!argFuncRetEnd) + syntaxError(argFuncRetStart); + + tok = argFuncRetEnd->next(); + } + + // function returning pointer to function + else if (Token::Match(tokOffset, "( * %type% (") && + Token::simpleMatch(tokOffset->linkAt(3), ") ) (") && + Token::Match(tokOffset->linkAt(3)->linkAt(2), ") ;|,")) { + functionRetFuncPtr = true; + + tokOffset = tokOffset->tokAt(3); + typeName = tokOffset->previous(); + argStart = tokOffset; + argEnd = tokOffset->link(); + + argFuncRetStart = argEnd->tokAt(2); + if (!argFuncRetStart) + syntaxError(tokOffset); + + argFuncRetEnd = argFuncRetStart->link(); + if (!argFuncRetEnd) + syntaxError(tokOffset); + + tok = argFuncRetEnd->next(); + } else if (Token::Match(tokOffset, "( * ( %type% ) (")) { + functionRetFuncPtr = true; + + tokOffset = tokOffset->tokAt(5); + typeName = tokOffset->tokAt(-2); + argStart = tokOffset; + argEnd = tokOffset->link(); + if (!argEnd) + syntaxError(arrayStart); + + argFuncRetStart = argEnd->tokAt(2); + if (!argFuncRetStart) + syntaxError(tokOffset); + + argFuncRetEnd = argFuncRetStart->link(); + if (!argFuncRetEnd) + syntaxError(tokOffset); + + tok = argFuncRetEnd->next(); + } + + // pointer/reference to array + else if (Token::Match(tokOffset, "( *|& %type% ) [")) { + ptrToArray = (tokOffset->next()->str() == "*"); + refToArray = !ptrToArray; + tokOffset = tokOffset->tokAt(2); + typeName = tokOffset; + arrayStart = tokOffset->tokAt(2); + arrayEnd = arrayStart->link(); + if (!arrayEnd) + syntaxError(arrayStart); + + tok = arrayEnd->next(); + } + + // pointer to class member + else if (Token::Match(tokOffset, "( %type% :: * %type% ) ;")) { + tokOffset = tokOffset->tokAt(2); + namespaceStart = tokOffset->previous(); + namespaceEnd = tokOffset; + ptrMember = true; + tokOffset = tokOffset->tokAt(2); + typeName = tokOffset; + tok = tokOffset->tokAt(2); + } + + // unhandled typedef, skip it and continue + else { + unsupportedTypedef(typeDef); + tok = deleteInvalidTypedef(typeDef); + if (tok == list.front()) + //now the next token to process is 'tok', not 'tok->next()'; + goback = true; + continue; + } + + bool done = false; + bool ok = true; + + TypedefInfo typedefInfo; + typedefInfo.name = typeName->str(); + typedefInfo.filename = list.file(typeName); + typedefInfo.lineNumber = typeName->linenr(); + typedefInfo.column = typeName->column(); + typedefInfo.used = false; + mTypedefInfo.push_back(std::move(typedefInfo)); + + while (!done) { + std::string pattern = typeName->str(); + int scope = 0; + bool simplifyType = false; + bool inMemberFunc = false; + int memberScope = 0; + bool globalScope = false; + int classLevel = spaceInfo.size(); + bool inTypeDef = false; + bool inEnum = false; + std::string removed; + std::string classPath; + for (size_t i = 1; i < spaceInfo.size(); ++i) { + if (!classPath.empty()) + classPath += " :: "; + classPath += spaceInfo[i].className; + } + + for (Token *tok2 = tok; tok2; tok2 = tok2->next()) { + if (Settings::terminated()) + return; + + removed.clear(); + + if (Token::simpleMatch(tok2, "typedef")) + inTypeDef = true; + + if (inTypeDef && Token::simpleMatch(tok2, ";")) + inTypeDef = false; + + // Check for variable declared with the same name + if (!inTypeDef && spaceInfo.size() == 1 && Token::Match(tok2->previous(), "%name%") && + !tok2->previous()->isKeyword()) { + Token* varDecl = tok2; + while (Token::Match(varDecl, "*|&|&&|const")) + varDecl = varDecl->next(); + if (Token::Match(varDecl, "%name% ;|,|)|=") && varDecl->str() == typeName->str()) { + // Skip to the next closing brace + if (Token::Match(varDecl, "%name% ) {")) { // is argument variable + tok2 = varDecl->linkAt(2)->next(); + } else { + tok2 = varDecl; + while (tok2 && !Token::simpleMatch(tok2, "}")) { + if (Token::Match(tok2, "(|{|[")) + tok2 = tok2->link(); + tok2 = tok2->next(); + } + } + if (!tok2) + break; + continue; + } + } + + if (tok2->link()) { // Pre-check for performance + // check for end of scope + if (tok2->str() == "}") { + // check for end of member function + if (inMemberFunc) { + --memberScope; + if (memberScope == 0) + inMemberFunc = false; + } + inEnum = false; + + if (classLevel > 1 && tok2 == spaceInfo[classLevel - 1].bodyEnd2) { + --classLevel; + pattern.clear(); + + for (int i = classLevel; i < spaceInfo.size(); ++i) + pattern += (spaceInfo[i].className + " :: "); + + pattern += typeName->str(); + } else { + if (scope == 0 && !(classLevel > 1 && tok2 == spaceInfo[classLevel - 1].bodyEnd)) + break; + scope = std::max(scope - 1, 0); + } + } + + // check for member functions + else if (tok2->isCpp() && tok2->str() == "(" && isFunctionHead(tok2, "{:")) { + const Token *func = tok2->previous(); + + /** @todo add support for multi-token operators */ + if (func->previous()->str() == "operator") + func = func->previous(); + + if (!func->previous()) + syntaxError(func); + + // check for qualifier + if (Token::Match(func->tokAt(-2), "%name% ::")) { + int offset = -2; + while (Token::Match(func->tokAt(offset - 2), "%name% ::")) + offset -= 2; + // check for available and matching class name + if (spaceInfo.size() > 1 && classLevel < spaceInfo.size() && + func->strAt(offset) == spaceInfo[classLevel].className) { + memberScope = 0; + inMemberFunc = true; + } + } + } + + // check for entering a new scope + else if (tok2->str() == "{") { + // check for entering a new namespace + if (tok2->isCpp()) { + if (tok2->strAt(-2) == "namespace") { + if (classLevel < spaceInfo.size() && + spaceInfo[classLevel].isNamespace && + spaceInfo[classLevel].className == tok2->previous()->str()) { + spaceInfo[classLevel].bodyEnd2 = tok2->link(); + ++classLevel; + pattern.clear(); + for (int i = classLevel; i < spaceInfo.size(); ++i) + pattern += spaceInfo[i].className + " :: "; + + pattern += typeName->str(); + } + ++scope; + } + if (isEnumScope(tok2)) + inEnum = true; + } + + // keep track of scopes within member function + if (inMemberFunc) + ++memberScope; + + ++scope; + } + } + + // check for operator typedef + /** @todo add support for multi-token operators */ + else if (tok2->isCpp() && + tok2->str() == "operator" && + tok2->next() && + tok2->next()->str() == typeName->str() && + tok2->linkAt(2) && + tok2->strAt(2) == "(" && + Token::Match(tok2->linkAt(2), ") const| {")) { + // check for qualifier + if (tok2->previous()->str() == "::") { + // check for available and matching class name + if (spaceInfo.size() > 1 && classLevel < spaceInfo.size() && + tok2->strAt(-2) == spaceInfo[classLevel].className) { + tok2 = tok2->next(); + simplifyType = true; + } + } + } + + else if (Token::Match(tok2->previous(), "class|struct %name% [:{]")) { + // don't replace names in struct/class definition + } + + // check for typedef that can be substituted + else if ((tok2->isNameOnly() || (tok2->isName() && (tok2->isExpandedMacro() || tok2->isInline()))) && + (Token::simpleMatch(tok2, pattern.c_str(), pattern.size()) || + (inMemberFunc && tok2->str() == typeName->str()))) { + // member function class variables don't need qualification + if (!(inMemberFunc && tok2->str() == typeName->str()) && pattern.find("::") != std::string::npos) { // has a "something ::" + Token *start = tok2; + int count = 0; + int back = classLevel - 1; + bool good = true; + // check for extra qualification + while (back >= 1) { + Token *qualificationTok = start->tokAt(-2); + if (!Token::Match(qualificationTok, "%type% ::")) + break; + if (qualificationTok->str() == spaceInfo[back].className) { + start = qualificationTok; + back--; + count++; + } else { + good = false; + break; + } + } + // check global namespace + if (good && back == 1 && start->strAt(-1) == "::") + good = false; + + if (good) { + // remove any extra qualification if present + while (count) { + if (!removed.empty()) + removed.insert(0, " "); + removed.insert(0, tok2->strAt(-2) + " " + tok2->strAt(-1)); + tok2->tokAt(-3)->deleteNext(2); + --count; + } + + // remove global namespace if present + if (tok2->strAt(-1) == "::") { + removed.insert(0, ":: "); + tok2->tokAt(-2)->deleteNext(); + globalScope = true; + } + + // remove qualification if present + for (int i = classLevel; i < spaceInfo.size(); ++i) { + if (!removed.empty()) + removed += " "; + removed += (tok2->str() + " " + tok2->strAt(1)); + tok2->deleteThis(); + tok2->deleteThis(); + } + simplifyType = true; + } + } else { + if (tok2->strAt(-1) == "::") { + int relativeSpaceInfoSize = spaceInfo.size(); + Token * tokBeforeType = tok2->previous(); + while (relativeSpaceInfoSize > 1 && + tokBeforeType && tokBeforeType->str() == "::" && + tokBeforeType->strAt(-1) == spaceInfo[relativeSpaceInfoSize-1].className) { + tokBeforeType = tokBeforeType->tokAt(-2); + --relativeSpaceInfoSize; + } + if (tokBeforeType && tokBeforeType->str() != "::") { + Token::eraseTokens(tokBeforeType, tok2); + simplifyType = true; + } + } else if (Token::Match(tok2->previous(), "case|;|{|} %type% :")) { + tok2 = tok2->next(); + } else if (duplicateTypedef(tok2, typeName, typeDef)) { + // skip to end of scope if not already there + if (tok2->str() != "}") { + while (tok2->next()) { + if (tok2->next()->str() == "{") + tok2 = tok2->linkAt(1)->previous(); + else if (tok2->next()->str() == "}") + break; + + tok2 = tok2->next(); + } + } + } else if (Token::Match(tok2->tokAt(-2), "%type% *|&")) { + // Ticket #5868: Don't substitute variable names + } else if (tok2->previous()->str() != ".") { + simplifyType = (TypedefSimplifier::canReplaceStatic(tok2) != 0); + } + } + } + + simplifyType = simplifyType && (!inEnum || !Token::simpleMatch(tok2->next(), "=")); + simplifyType = simplifyType && !(Token::simpleMatch(tok2->next(), "<") && Token::simpleMatch(typeEnd, ">")); + + if (simplifyType) { + mTypedefInfo.back().used = true; + + // can't simplify 'operator functionPtr ()' and 'functionPtr operator ... ()' + if (functionPtr && (tok2->previous()->str() == "operator" || + (tok2->next() && tok2->next()->str() == "operator"))) { + simplifyType = false; + tok2 = tok2->next(); + continue; + } + + // There are 2 categories of typedef substitutions: + // 1. variable declarations that preserve the variable name like + // global, local, and function parameters + // 2. not variable declarations that have no name like derived + // classes, casts, operators, and template parameters + + // try to determine which category this substitution is + bool inCast = false; + bool inTemplate = false; + bool inOperator = false; + bool inSizeof = false; + + const bool sameStartEnd = (typeStart == typeEnd); + + // check for derived class: class A : some_typedef { + const bool isDerived = Token::Match(tok2->previous(), "public|protected|private|: %type% {|,"); + + // check for cast: (some_typedef) A or static_cast(A) + // todo: check for more complicated casts like: (const some_typedef *)A + if ((tok2->previous()->str() == "(" && tok2->next()->str() == ")" && tok2->strAt(-2) != "sizeof") || + (tok2->previous()->str() == "<" && Token::simpleMatch(tok2->next(), "> (")) || + Token::Match(tok2->tokAt(-2), "( const %name% )")) + inCast = true; + + // check for template parameters: t t1 + else if (Token::Match(tok2->previous(), "<|,") && + Token::Match(tok2->next(), "&|*| &|*| >|,")) + inTemplate = true; + + else if (Token::Match(tok2->tokAt(-2), "sizeof ( %type% )")) + inSizeof = true; + + // check for operator + if (tok2->strAt(-1) == "operator" || + Token::simpleMatch(tok2->tokAt(-2), "operator const")) + inOperator = true; + + if (typeStart->str() == "typename" && tok2->strAt(-1)=="typename") { + // Remove one typename if it is already contained in the goal + typeStart = typeStart->next(); + } + + // skip over class or struct in derived class declaration + bool structRemoved = false; + if ((isDerived || inTemplate) && Token::Match(typeStart, "class|struct")) { + if (typeStart->str() == "struct") + structRemoved = true; + typeStart = typeStart->next(); + } + if (Token::Match(typeStart, "struct|class|union") && Token::Match(tok2, "%name% ::")) + typeStart = typeStart->next(); + + if (sameStartEnd) + typeEnd = typeStart; + + // Is this a "T()" expression where T is a pointer type? + const bool isPointerTypeCall = !inOperator && Token::Match(tok2, "%name% ( )") && !pointers.empty(); + + // start substituting at the typedef name by replacing it with the type + Token* replStart = tok2; // track first replaced token + for (Token* tok3 = typeStart; tok3 && (tok3->str() != ";"); tok3 = tok3->next()) + tok3->isSimplifiedTypedef(true); + if (isPointerTypeCall) { + tok2->deleteThis(); + tok2->insertToken("0"); + tok2 = tok2->next(); + tok2->next()->insertToken("0"); + } + if (Token::Match(tok2->tokAt(-1), "class|struct|union") && tok2->strAt(-1) == typeStart->str()) + tok2->deletePrevious(); + tok2->str(typeStart->str()); + + // restore qualification if it was removed + if (Token::Match(typeStart, "class|struct|union") || structRemoved) { + if (structRemoved) + tok2 = tok2->previous(); + + if (globalScope) { + replStart = tok2->insertToken("::"); + tok2 = tok2->next(); + } + + for (int i = classLevel; i < spaceInfo.size(); ++i) { + tok2->insertToken(spaceInfo[i].className); + tok2 = tok2->next(); + tok2->insertToken("::"); + tok2 = tok2->next(); + } + } + + // add some qualification back if needed + Token *start = tok2; + std::string removed1 = removed; + std::string::size_type idx = removed1.rfind(" ::"); + + if (idx != std::string::npos) + removed1.resize(idx); + if (removed1 == classPath && !removed1.empty()) { + for (std::vector::const_reverse_iterator it = spaceInfo.crbegin(); it != spaceInfo.crend(); ++it) { + if (it->recordTypes.find(start->str()) != it->recordTypes.end()) { + std::string::size_type spaceIdx = 0; + std::string::size_type startIdx = 0; + while ((spaceIdx = removed1.find(' ', startIdx)) != std::string::npos) { + tok2->previous()->insertToken(removed1.substr(startIdx, spaceIdx - startIdx)); + startIdx = spaceIdx + 1; + } + tok2->previous()->insertToken(removed1.substr(startIdx)); + replStart = tok2->previous()->insertToken("::"); + break; + } + idx = removed1.rfind(" ::"); + if (idx == std::string::npos) + break; + + removed1.resize(idx); + } + } + replStart->isSimplifiedTypedef(true); + Token* constTok = Token::simpleMatch(tok2->previous(), "const") ? tok2->previous() : nullptr; + // add remainder of type + tok2 = TokenList::copyTokens(tok2, typeStart->next(), typeEnd); + + if (!pointers.empty()) { + for (const std::string &p : pointers) { + tok2->insertToken(p); + tok2->isSimplifiedTypedef(true); + tok2 = tok2->next(); + } + if (constTok) { + constTok->deleteThis(); + tok2->insertToken("const"); + tok2->isSimplifiedTypedef(true); + tok2 = tok2->next(); + } + } + + if (funcStart && funcEnd) { + tok2->insertToken("("); + tok2 = tok2->next(); + Token *paren = tok2; + tok2 = TokenList::copyTokens(tok2, funcStart, funcEnd); + + if (!inCast) + tok2 = processFunc(tok2, inOperator); + + if (!tok2) + break; + + while (Token::Match(tok2, "%name%|] [")) + tok2 = tok2->linkAt(1); + + tok2->insertToken(")"); + tok2 = tok2->next(); + Token::createMutualLinks(tok2, paren); + + tok2 = TokenList::copyTokens(tok2, argStart, argEnd); + + if (specStart) { + Token *spec = specStart; + tok2->insertToken(spec->str()); + tok2 = tok2->next(); + while (spec != specEnd) { + spec = spec->next(); + tok2->insertToken(spec->str()); + tok2 = tok2->next(); + } + } + } + + else if (functionPtr || function) { + // don't add parentheses around function names because it + // confuses other simplifications + bool needParen = true; + if (!inTemplate && function && tok2->next() && tok2->next()->str() != "*") + needParen = false; + if (needParen) { + tok2->insertToken("("); + tok2 = tok2->next(); + } + Token *tok3 = tok2; + if (namespaceStart) { + const Token *tok4 = namespaceStart; + + while (tok4 != namespaceEnd) { + tok2->insertToken(tok4->str()); + tok2 = tok2->next(); + tok4 = tok4->next(); + } + tok2->insertToken(namespaceEnd->str()); + tok2 = tok2->next(); + } + if (functionPtr) { + tok2->insertToken("*"); + tok2 = tok2->next(); + } + + if (!inCast) + tok2 = processFunc(tok2, inOperator); + + if (needParen) { + if (!tok2) + syntaxError(nullptr); + + tok2->insertToken(")"); + tok2 = tok2->next(); + Token::createMutualLinks(tok2, tok3); + } + if (!tok2) + syntaxError(nullptr); + + tok2 = TokenList::copyTokens(tok2, argStart, argEnd); + if (inTemplate) { + if (!tok2) + syntaxError(nullptr); + + tok2 = tok2->next(); + } + + if (specStart) { + Token *spec = specStart; + tok2->insertToken(spec->str()); + tok2 = tok2->next(); + while (spec != specEnd) { + spec = spec->next(); + tok2->insertToken(spec->str()); + tok2 = tok2->next(); + } + } + } else if (functionRetFuncPtr || functionPtrRetFuncPtr) { + tok2->insertToken("("); + tok2 = tok2->next(); + Token *tok3 = tok2; + tok2->insertToken("*"); + tok2 = tok2->next(); + + Token * tok4 = nullptr; + if (functionPtrRetFuncPtr) { + tok2->insertToken("("); + tok2 = tok2->next(); + tok4 = tok2; + tok2->insertToken("*"); + tok2 = tok2->next(); + } + + // skip over variable name if there + if (!inCast) { + if (!tok2 || !tok2->next()) + syntaxError(nullptr); + + if (tok2->next()->str() != ")") + tok2 = tok2->next(); + } + + if (tok4 && functionPtrRetFuncPtr) { + tok2->insertToken(")"); + tok2 = tok2->next(); + Token::createMutualLinks(tok2, tok4); + } + + tok2 = TokenList::copyTokens(tok2, argStart, argEnd); + + tok2->insertToken(")"); + tok2 = tok2->next(); + Token::createMutualLinks(tok2, tok3); + + tok2 = TokenList::copyTokens(tok2, argFuncRetStart, argFuncRetEnd); + } else if (ptrToArray || refToArray) { + tok2->insertToken("("); + tok2 = tok2->next(); + Token *tok3 = tok2; + + if (ptrToArray) + tok2->insertToken("*"); + else + tok2->insertToken("&"); + tok2 = tok2->next(); + + bool hasName = false; + // skip over name + if (tok2->next() && tok2->next()->str() != ")" && tok2->next()->str() != "," && + tok2->next()->str() != ">") { + hasName = true; + if (tok2->next()->str() != "(") + tok2 = tok2->next(); + + // check for function and skip over args + if (tok2 && tok2->next() && tok2->next()->str() == "(") + tok2 = tok2->next()->link(); + + // check for array + if (tok2 && tok2->next() && tok2->next()->str() == "[") + tok2 = tok2->next()->link(); + } + + tok2->insertToken(")"); + Token::createMutualLinks(tok2->next(), tok3); + + if (!hasName) + tok2 = tok2->next(); + } else if (ptrMember) { + if (Token::simpleMatch(tok2, "* (")) { + tok2->insertToken("*"); + tok2 = tok2->next(); + } else { + // This is the case of casting operator. + // Name is not available, and () should not be + // inserted + const bool castOperator = inOperator && Token::Match(tok2, "%type% ("); + Token *openParenthesis = nullptr; + + if (!castOperator) { + tok2->insertToken("("); + tok2 = tok2->next(); + + openParenthesis = tok2; + } + + const Token *tok4 = namespaceStart; + + while (tok4 != namespaceEnd) { + tok2->insertToken(tok4->str()); + tok2 = tok2->next(); + tok4 = tok4->next(); + } + tok2->insertToken(namespaceEnd->str()); + tok2 = tok2->next(); + + tok2->insertToken("*"); + tok2 = tok2->next(); + + if (openParenthesis) { + // Skip over name, if any + if (Token::Match(tok2->next(), "%name%")) + tok2 = tok2->next(); + + tok2->insertToken(")"); + tok2 = tok2->next(); + + Token::createMutualLinks(tok2, openParenthesis); + } + } + } else if (typeOf) { + tok2 = TokenList::copyTokens(tok2, argStart, argEnd); + } else if (Token::Match(tok2, "%name% [")) { + while (Token::Match(tok2, "%name%|] [")) { + tok2 = tok2->linkAt(1); + } + tok2 = tok2->previous(); + } + + if (arrayStart && arrayEnd) { + do { + if (!tok2->next()) + syntaxError(tok2); // can't recover so quit + + if (!inCast && !inSizeof && !inTemplate) + tok2 = tok2->next(); + + if (tok2->str() == "const") + tok2 = tok2->next(); + + // reference or pointer to array? + if (Token::Match(tok2, "&|*|&&")) { + tok2 = tok2->previous(); + tok2->insertToken("("); + Token *tok3 = tok2->next(); + + // handle missing variable name + if (Token::Match(tok3, "( *|&|&& *|&|&& %name%")) + tok2 = tok3->tokAt(3); + else if (Token::Match(tok2->tokAt(3), "[(),;]")) + tok2 = tok2->tokAt(2); + else if (Token::simpleMatch(tok2->tokAt(3), ">")) + tok2 = tok2->tokAt(2); + else + tok2 = tok2->tokAt(3); + if (!tok2) + syntaxError(nullptr); + + while (tok2->strAt(1) == "::") + tok2 = tok2->tokAt(2); + + // skip over function parameters + if (tok2->str() == "(") + tok2 = tok2->link(); + + if (tok2->strAt(1) == "(") + tok2 = tok2->linkAt(1); + + // skip over const/noexcept + while (Token::Match(tok2->next(), "const|noexcept")) { + tok2 = tok2->next(); + if (Token::Match(tok2->next(), "( true|false )")) + tok2 = tok2->tokAt(3); + } + + tok2->insertToken(")"); + tok2 = tok2->next(); + Token::createMutualLinks(tok2, tok3); + } + + if (!tok2->next()) + syntaxError(tok2); // can't recover so quit + + // skip over array dimensions + while (tok2->next()->str() == "[") + tok2 = tok2->linkAt(1); + + tok2 = TokenList::copyTokens(tok2, arrayStart, arrayEnd); + if (!tok2->next()) + syntaxError(tok2); + + if (tok2->str() == "=") { + if (tok2->next()->str() == "{") + tok2 = tok2->next()->link()->next(); + else if (tok2->next()->str().at(0) == '\"') + tok2 = tok2->tokAt(2); + } + } while (Token::Match(tok2, ", %name% ;|=|,")); + } + + simplifyType = false; + } + if (!tok2) + break; + } + + if (!tok) + syntaxError(nullptr); + + if (tok->str() == ";") + done = true; + else if (tok->str() == ",") { + arrayStart = nullptr; + arrayEnd = nullptr; + tokOffset = tok->next(); + pointers.clear(); + + while (Token::Match(tokOffset, "*|&")) { + pointers.push_back(tokOffset->str()); + tokOffset = tokOffset->next(); + } + + if (Token::Match(tokOffset, "%type%")) { + typeName = tokOffset; + tokOffset = tokOffset->next(); + + if (tokOffset && tokOffset->str() == "[") { + arrayStart = tokOffset; + + for (;;) { + while (tokOffset->next() && !Token::Match(tokOffset->next(), ";|,")) + tokOffset = tokOffset->next(); + + if (!tokOffset->next()) + return; // invalid input + if (tokOffset->next()->str() == ";") + break; + if (tokOffset->str() == "]") + break; + tokOffset = tokOffset->next(); + } + + arrayEnd = tokOffset; + tokOffset = tokOffset->next(); + } + + if (Token::Match(tokOffset, ";|,")) + tok = tokOffset; + else { + // we encountered a typedef we don't support yet so just continue + done = true; + ok = false; + } + } else { + // we encountered a typedef we don't support yet so just continue + done = true; + ok = false; + } + } else { + // something is really wrong (internal error) + done = true; + ok = false; + } + } + + if (ok) { + // remove typedef + Token::eraseTokens(typeDef, tok); + + if (typeDef != list.front()) { + tok = typeDef->previous(); + tok->deleteNext(); + //no need to remove last token in the list + if (tok->tokAt(2)) + tok->deleteNext(); + } else { + list.front()->deleteThis(); + //no need to remove last token in the list + if (list.front()->next()) + list.front()->deleteThis(); + tok = list.front(); + //now the next token to process is 'tok', not 'tok->next()'; + goback = true; + } + } + } +} + +namespace { + struct ScopeInfo3 { + enum Type { Global, Namespace, Record, MemberFunction, Other }; + ScopeInfo3() : parent(nullptr), type(Global), bodyStart(nullptr), bodyEnd(nullptr) {} + ScopeInfo3(ScopeInfo3 *parent_, Type type_, std::string name_, const Token *bodyStart_, const Token *bodyEnd_) + : parent(parent_), type(type_), name(std::move(name_)), bodyStart(bodyStart_), bodyEnd(bodyEnd_) { + if (name.empty()) + return; + fullName = name; + ScopeInfo3 *scope = parent; + while (scope && scope->parent) { + if (scope->name.empty()) + break; + fullName = scope->name + " :: " + fullName; + scope = scope->parent; + } + } + ScopeInfo3 *parent; + std::list children; + Type type; + std::string fullName; + std::string name; + const Token * bodyStart; + const Token * bodyEnd; + std::set usingNamespaces; + std::set recordTypes; + std::set baseTypes; + + ScopeInfo3 *addChild(Type scopeType, const std::string &scopeName, const Token *bodyStartToken, const Token *bodyEndToken) { + children.emplace_back(this, scopeType, scopeName, bodyStartToken, bodyEndToken); + return &children.back(); + } + + bool hasChild(const std::string &childName) const { + return std::any_of(children.cbegin(), children.cend(), [&](const ScopeInfo3& child) { + return child.name == childName; + }); + } + + const ScopeInfo3 * findInChildren(const std::string & scope) const { + for (const auto & child : children) { + if (child.type == Record && (child.name == scope || child.fullName == scope)) + return &child; + + const ScopeInfo3 * temp = child.findInChildren(scope); + if (temp) + return temp; + } + return nullptr; + } + + const ScopeInfo3 * findScope(const std::string & scope) const { + const ScopeInfo3 * tempScope = this; + while (tempScope) { + // check children + auto it = std::find_if(tempScope->children.cbegin(), tempScope->children.cend(), [&](const ScopeInfo3& child) { + return &child != this && child.type == Record && (child.name == scope || child.fullName == scope); + }); + if (it != tempScope->children.end()) + return &*it; + // check siblings for same name + if (tempScope->parent) { + for (const auto &sibling : tempScope->parent->children) { + if (sibling.name == tempScope->name && &sibling != this) { + const ScopeInfo3 * temp = sibling.findInChildren(scope); + if (temp) + return temp; + } + } + } + tempScope = tempScope->parent; + } + return nullptr; + } + + bool findTypeInBase(const std::string &scope) const { + if (scope.empty()) + return false; + // check in base types first + if (baseTypes.find(scope) != baseTypes.end()) + return true; + // check in base types base types + for (const std::string & base : baseTypes) { + const ScopeInfo3 * baseScope = findScope(base); + // bail on uninstantiated recursive template + if (baseScope == this) + return false; + if (baseScope && baseScope->fullName == scope) + return true; + if (baseScope && baseScope->findTypeInBase(scope)) + return true; + } + return false; + } + + ScopeInfo3 * findScope(const ScopeInfo3 * scope) { + if (scope->bodyStart == bodyStart) + return this; + for (auto & child : children) { + ScopeInfo3 * temp = child.findScope(scope); + if (temp) + return temp; + } + return nullptr; + } + }; + + void setScopeInfo(Token *tok, ScopeInfo3 *&scopeInfo, bool debug=false) + { + if (!tok) + return; + if (tok->str() == "{" && scopeInfo->parent && tok == scopeInfo->bodyStart) + return; + if (tok->str() == "}") { + if (scopeInfo->parent && tok == scopeInfo->bodyEnd) + scopeInfo = scopeInfo->parent; + else { + // Try to find parent scope + ScopeInfo3 *parent = scopeInfo->parent; + while (parent && parent->bodyEnd != tok) + parent = parent->parent; + if (parent) { + scopeInfo = parent; + if (debug) + throw std::runtime_error("Internal error: unmatched }"); + } + } + return; + } + if (!Token::Match(tok, "namespace|class|struct|union %name% {|:|::|<")) { + // check for using namespace + if (Token::Match(tok, "using namespace %name% ;|::")) { + const Token * tok1 = tok->tokAt(2); + std::string nameSpace; + while (tok1 && tok1->str() != ";") { + if (!nameSpace.empty()) + nameSpace += " "; + nameSpace += tok1->str(); + tok1 = tok1->next(); + } + scopeInfo->usingNamespaces.insert(std::move(nameSpace)); + } + // check for member function + else if (tok->str() == "{") { + bool added = false; + Token *tok1 = tok; + while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) + tok1 = tok1->previous(); + if (tok1->previous() && (tok1->strAt(-1) == ")" || tok->strAt(-1) == "}")) { + tok1 = tok1->linkAt(-1); + if (Token::Match(tok1->previous(), "throw|noexcept (")) { + tok1 = tok1->previous(); + while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) + tok1 = tok1->previous(); + if (tok1->strAt(-1) != ")") + return; + tok1 = tok1->linkAt(-1); + } else { + while (Token::Match(tok1->tokAt(-2), ":|, %name%")) { + tok1 = tok1->tokAt(-2); + if (tok1->strAt(-1) != ")" && tok1->strAt(-1) != "}") + return; + tok1 = tok1->linkAt(-1); + } + } + if (tok1->strAt(-1) == ">") + tok1 = tok1->previous()->findOpeningBracket(); + if (tok1 && (Token::Match(tok1->tokAt(-3), "%name% :: %name%") || + Token::Match(tok1->tokAt(-4), "%name% :: ~ %name%"))) { + tok1 = tok1->tokAt(-2); + if (tok1->str() == "~") + tok1 = tok1->previous(); + std::string scope = tok1->strAt(-1); + while (Token::Match(tok1->tokAt(-2), ":: %name%")) { + scope = tok1->strAt(-3) + " :: " + scope; + tok1 = tok1->tokAt(-2); + } + scopeInfo = scopeInfo->addChild(ScopeInfo3::MemberFunction, scope, tok, tok->link()); + added = true; + } + } + + if (!added) + scopeInfo = scopeInfo->addChild(ScopeInfo3::Other, emptyString, tok, tok->link()); + } + return; + } + + const bool record = Token::Match(tok, "class|struct|union %name%"); + tok = tok->next(); + std::string classname = tok->str(); + while (Token::Match(tok, "%name% :: %name%")) { + tok = tok->tokAt(2); + classname += " :: " + tok->str(); + } + + // add record type to scope info + if (record) + scopeInfo->recordTypes.insert(classname); + tok = tok->next(); + + // skip template parameters + if (tok && tok->str() == "<") { + tok = tok->findClosingBracket(); + if (tok) + tok = tok->next(); + } + + // get base class types + std::set baseTypes; + if (tok && tok->str() == ":") { + do { + tok = tok->next(); + while (Token::Match(tok, "public|protected|private|virtual")) + tok = tok->next(); + std::string base; + while (tok && !Token::Match(tok, ";|,|{")) { + if (!base.empty()) + base += ' '; + base += tok->str(); + tok = tok->next(); + // add template parameters + if (tok && tok->str() == "<") { + const Token* endTok = tok->findClosingBracket(); + if (endTok) { + endTok = endTok->next(); + while (tok != endTok) { + base += tok->str(); + tok = tok->next(); + } + } + } + } + baseTypes.insert(std::move(base)); + } while (tok && !Token::Match(tok, ";|{")); + } + + if (tok && tok->str() == "{") { + scopeInfo = scopeInfo->addChild(record ? ScopeInfo3::Record : ScopeInfo3::Namespace, classname, tok, tok->link()); + scopeInfo->baseTypes = std::move(baseTypes); + } + } + + Token *findSemicolon(Token *tok) + { + int level = 0; + + for (; tok && (level > 0 || tok->str() != ";"); tok = tok->next()) { + if (tok->str() == "{") + ++level; + else if (level > 0 && tok->str() == "}") + --level; + } + + return tok; + } + + bool usingMatch( + const Token *nameToken, + const std::string &scope, + Token *&tok, + const std::string &scope1, + const ScopeInfo3 *currentScope, + const ScopeInfo3 *memberClassScope) + { + Token *tok1 = tok; + + if (tok1 && tok1->str() != nameToken->str()) + return false; + + // skip this using + if (tok1 == nameToken) { + tok = findSemicolon(tok1); + return false; + } + + // skip other using with this name + if (tok1->strAt(-1) == "using") { + // fixme: this is wrong + // skip to end of scope + if (currentScope->bodyEnd) + tok = const_cast(currentScope->bodyEnd->previous()); + return false; + } + + if (Token::Match(tok1->tokAt(-1), "class|struct|union|enum|namespace")) { + // fixme + return false; + } + + // get qualification + std::string qualification; + const Token* tok2 = tok1; + std::string::size_type index = scope.size(); + std::string::size_type new_index = std::string::npos; + bool match = true; + while (Token::Match(tok2->tokAt(-2), "%name% ::") && !tok2->tokAt(-2)->isKeyword()) { + std::string last; + if (match && !scope1.empty()) { + new_index = scope1.rfind(' ', index - 1); + if (new_index != std::string::npos) + last = scope1.substr(new_index, index - new_index); + else if (!qualification.empty()) + last.clear(); + else + last = scope1; + } else + match = false; + if (match && tok2->strAt(-2) == last) + index = new_index; + else { + if (!qualification.empty()) + qualification = " :: " + qualification; + qualification = tok2->strAt(-2) + qualification; + } + tok2 = tok2->tokAt(-2); + } + + std::string fullScope1 = scope1; + if (!scope1.empty() && !qualification.empty()) + fullScope1 += " :: "; + fullScope1 += qualification; + + if (scope == fullScope1) + return true; + + const ScopeInfo3 *scopeInfo = memberClassScope ? memberClassScope : currentScope; + + // check in base types + if (qualification.empty() && scopeInfo->findTypeInBase(scope)) + return true; + + // check using namespace + const ScopeInfo3 * tempScope = scopeInfo; + while (tempScope) { + //if (!tempScope->parent->usingNamespaces.empty()) { + const std::set& usingNS = tempScope->usingNamespaces; + if (!usingNS.empty()) { + if (qualification.empty()) { + if (usingNS.find(scope) != usingNS.end()) + return true; + } else { + const std::string suffix = " :: " + qualification; + if (std::any_of(usingNS.cbegin(), usingNS.cend(), [&](const std::string& ns) { + return scope == ns + suffix; + })) + return true; + } + } + tempScope = tempScope->parent; + } + + std::string newScope1 = scope1; + + // scopes didn't match so try higher scopes + index = newScope1.size(); + while (!newScope1.empty()) { + const std::string::size_type separator = newScope1.rfind(" :: ", index - 1); + if (separator != std::string::npos) + newScope1.resize(separator); + else + newScope1.clear(); + + std::string newFullScope1 = newScope1; + if (!newScope1.empty() && !qualification.empty()) + newFullScope1 += " :: "; + newFullScope1 += qualification; + + if (scope == newFullScope1) + return true; + } + + return false; + } + + std::string memberFunctionScope(const Token *tok) + { + std::string qualification; + const Token *qualTok = tok->strAt(-2) == "~" ? tok->tokAt(-4) : tok->tokAt(-3); + while (Token::Match(qualTok, "%type% ::")) { + if (!qualification.empty()) + qualification = " :: " + qualification; + qualification = qualTok->str() + qualification; + qualTok = qualTok->tokAt(-2); + } + return qualification; + } + + const Token * memberFunctionEnd(const Token *tok) + { + if (tok->str() != "(") + return nullptr; + const Token *end = tok->link()->next(); + while (end) { + if (end->str() == "{" && !Token::Match(end->tokAt(-2), ":|, %name%")) + return end; + if (end->str() == ";") + break; + end = end->next(); + } + return nullptr; + } +} // namespace + +bool Tokenizer::isMemberFunction(const Token *openParen) +{ + return (Token::Match(openParen->tokAt(-2), ":: %name% (") || + Token::Match(openParen->tokAt(-3), ":: ~ %name% (")) && + isFunctionHead(openParen, "{|:"); +} + +static bool scopesMatch(const std::string &scope1, const std::string &scope2, const ScopeInfo3 *globalScope) +{ + if (scope1.empty() || scope2.empty()) + return false; + + // check if scopes match + if (scope1 == scope2) + return true; + + // check if scopes only differ by global qualification + if (scope1 == (":: " + scope2)) { + std::string::size_type end = scope2.find_first_of(' '); + if (end == std::string::npos) + end = scope2.size(); + if (globalScope->hasChild(scope2.substr(0, end))) + return true; + } else if (scope2 == (":: " + scope1)) { + std::string::size_type end = scope1.find_first_of(' '); + if (end == std::string::npos) + end = scope1.size(); + if (globalScope->hasChild(scope1.substr(0, end))) + return true; + } + + return false; +} + +static unsigned int tokDistance(const Token* tok1, const Token* tok2) { + unsigned int dist = 0; + const Token* tok = tok1; + while (tok != tok2) { + ++dist; + tok = tok->next(); + } + return dist; +} + +bool Tokenizer::simplifyUsing() +{ + if (!isCPP() || mSettings.standards.cpp < Standards::CPP11) + return false; + + // simplify using N::x; to using x = N::x; + for (Token* tok = list.front(); tok; tok = tok->next()) { + if (!Token::Match(tok, "using ::| %name% ::")) + continue; + const Token* ns = tok->tokAt(tok->strAt(1) == "::" ? 2 : 1); + if (ns->isKeyword()) + continue; + Token* end = tok->tokAt(3); + while (end && !Token::Match(end, "[;,]")) { + if (end->str() == "<") // skip template args + end = end->findClosingBracket(); + else + end = end->next(); + } + if (!end) + continue; + if (!end->tokAt(-1)->isNameOnly() || end->tokAt(-2)->isLiteral()) // e.g. operator=, operator""sv + continue; + tok->insertToken(end->strAt(-1))->insertToken("=")->isSimplifiedTypedef(true); + if (end->str() == ",") { // comma-separated list + end->str(";"); + end->insertToken("using"); + } + tok = end; + } + + const unsigned int maxReplacementTokens = 1000; // limit the number of tokens we replace + + bool substitute = false; + ScopeInfo3 scopeInfo; + ScopeInfo3 *currentScope = &scopeInfo; + struct Using { + Using(Token *start, Token *end) : startTok(start), endTok(end) {} + Token *startTok; + Token *endTok; + }; + std::list usingList; + + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!list.getFiles().empty()) + mErrorLogger.reportProgress(list.getFiles()[0], "Tokenize (using)", tok->progressValue()); + + if (Settings::terminated()) + return substitute; + + if (Token::Match(tok, "enum class|struct")) { + Token *bodyStart = tok; + while (Token::Match(bodyStart, "%name%|:|::|<")) { + if (bodyStart->str() == "<") + bodyStart = bodyStart->findClosingBracket(); + bodyStart = bodyStart ? bodyStart->next() : nullptr; + } + if (Token::simpleMatch(bodyStart, "{")) + tok = bodyStart->link(); + continue; + } + + if (Token::Match(tok, "{|}|namespace|class|struct|union") || + Token::Match(tok, "using namespace %name% ;|::")) { + try { + setScopeInfo(tok, currentScope, mSettings.debugwarnings); + } catch (const std::runtime_error &) { + reportError(tok, Severity::debug, "simplifyUsingUnmatchedBodyEnd", + "simplifyUsing: unmatched body end"); + } + continue; + } + + // skip template declarations + if (Token::Match(tok, "template < !!>")) { + // add template record type to scope info + const Token *end = tok->next()->findClosingBracket(); + if (end && Token::Match(end->next(), "class|struct|union %name%")) + currentScope->recordTypes.insert(end->strAt(2)); + + Token *declEndToken = TemplateSimplifier::findTemplateDeclarationEnd(tok); + if (declEndToken) + tok = declEndToken; + continue; + } + + // look for non-template type aliases + if (!(tok->strAt(-1) != ">" && + (Token::Match(tok, "using %name% = ::| %name%") || + (Token::Match(tok, "using %name% [ [") && + Token::Match(tok->linkAt(2), "] ] = ::| %name%"))))) + continue; + + const std::string& name = tok->strAt(1); + const Token *nameToken = tok->next(); + std::string scope = currentScope->fullName; + Token *usingStart = tok; + Token *start; + if (tok->strAt(2) == "=") { + if (currentScope->type == ScopeInfo3::Record && tok->tokAt(2)->isSimplifiedTypedef()) // don't simplify within class definition + continue; + start = tok->tokAt(3); + } + else + start = tok->linkAt(2)->tokAt(3); + Token *usingEnd = findSemicolon(start); + if (!usingEnd) + continue; + + // Move struct defined in using out of using. + // using T = struct t { }; => struct t { }; using T = struct t; + // fixme: this doesn't handle attributes + if (Token::Match(start, "class|struct|union|enum %name%| {|:")) { + Token *structEnd = start->tokAt(1); + const bool hasName = Token::Match(structEnd, "%name%"); + + // skip over name if present + if (hasName) + structEnd = structEnd->next(); + + // skip over base class information + if (structEnd->str() == ":") { + structEnd = structEnd->next(); // skip over ":" + while (structEnd && structEnd->str() != "{") + structEnd = structEnd->next(); + if (!structEnd) + continue; + } + + // use link to go to end + structEnd = structEnd->link(); + + // add ';' after end of struct + structEnd->insertToken(";", emptyString); + + // add name for anonymous struct + if (!hasName) { + std::string newName; + if (structEnd->strAt(2) == ";") + newName = name; + else + newName = "Unnamed" + std::to_string(mUnnamedCount++); + TokenList::copyTokens(structEnd->next(), tok, start); + structEnd->tokAt(5)->insertToken(newName, emptyString); + start->insertToken(newName, emptyString); + } else + TokenList::copyTokens(structEnd->next(), tok, start->next()); + + // add using after end of struct + usingStart = structEnd->tokAt(2); + nameToken = usingStart->next(); + if (usingStart->strAt(2) == "=") + start = usingStart->tokAt(3); + else + start = usingStart->linkAt(2)->tokAt(3); + usingEnd = findSemicolon(start); + + // delete original using before struct + tok->deleteThis(); + tok->deleteThis(); + tok->deleteThis(); + tok = usingStart; + } + + // remove 'typename' and 'template' + else if (start->str() == "typename") { + start->deleteThis(); + Token *temp = start; + while (Token::Match(temp, "%name% ::")) + temp = temp->tokAt(2); + if (Token::Match(temp, "template %name%")) + temp->deleteThis(); + } + + if (usingEnd) + tok = usingEnd; + + // Unfortunately we have to start searching from the beginning + // of the token stream because templates are instantiated at + // the end of the token stream and it may be used before then. + ScopeInfo3 scopeInfo1; + ScopeInfo3 *currentScope1 = &scopeInfo1; + Token *startToken = list.front(); + Token *endToken = nullptr; + bool inMemberFunc = false; + const ScopeInfo3 * memberFuncScope = nullptr; + const Token * memberFuncEnd = nullptr; + + // We can limit the search to the current function when the type alias + // is defined in that function. + if (currentScope->type == ScopeInfo3::Other || + currentScope->type == ScopeInfo3::MemberFunction) { + scopeInfo1 = scopeInfo; + currentScope1 = scopeInfo1.findScope(currentScope); + if (!currentScope1) + return substitute; // something bad happened + startToken = usingEnd->next(); + endToken = const_cast(currentScope->bodyEnd->next()); + if (currentScope->type == ScopeInfo3::MemberFunction) { + const ScopeInfo3 * temp = currentScope->findScope(currentScope->fullName); + if (temp) { + inMemberFunc = true; + memberFuncScope = temp; + memberFuncEnd = endToken; + } + } + } + + std::string scope1 = currentScope1->fullName; + bool skip = false; // don't erase type aliases we can't parse + Token *enumOpenBrace = nullptr; + for (Token* tok1 = startToken; !skip && tok1 && tok1 != endToken; tok1 = tok1->next()) { + // skip enum body + if (tok1 && tok1 == enumOpenBrace) { + tok1 = tok1->link(); + enumOpenBrace = nullptr; + continue; + } + + if ((Token::Match(tok1, "{|}|namespace|class|struct|union") && tok1->strAt(-1) != "using") || + Token::Match(tok1, "using namespace %name% ;|::")) { + try { + setScopeInfo(tok1, currentScope1, mSettings.debugwarnings); + } catch (const std::runtime_error &) { + reportError(tok1, Severity::debug, "simplifyUsingUnmatchedBodyEnd", + "simplifyUsing: unmatched body end"); + } + scope1 = currentScope1->fullName; + if (inMemberFunc && memberFuncEnd && tok1 == memberFuncEnd) { + inMemberFunc = false; + memberFuncScope = nullptr; + memberFuncEnd = nullptr; + } + continue; + } + + // skip template definitions + if (Token::Match(tok1, "template < !!>")) { + Token *declEndToken = TemplateSimplifier::findTemplateDeclarationEnd(tok1); + if (declEndToken) + tok1 = declEndToken; + continue; + } + + // check for enum with body + if (tok1->str() == "enum") { + if (Token::Match(tok1, "enum class|struct")) + tok1 = tok1->next(); + Token *defStart = tok1; + while (Token::Match(defStart, "%name%|::|:")) + defStart = defStart->next(); + if (Token::simpleMatch(defStart, "{")) + enumOpenBrace = defStart; + continue; + } + + // check for member function and adjust scope + if (isMemberFunction(tok1)) { + if (!scope1.empty()) + scope1 += " :: "; + scope1 += memberFunctionScope(tok1); + const ScopeInfo3 * temp = currentScope1->findScope(scope1); + if (temp) { + const Token *end = memberFunctionEnd(tok1); + if (end) { + inMemberFunc = true; + memberFuncScope = temp; + memberFuncEnd = end; + } + } + continue; + } + if (inMemberFunc && memberFuncScope) { + if (!usingMatch(nameToken, scope, tok1, scope1, currentScope1, memberFuncScope)) + continue; + } else if (!usingMatch(nameToken, scope, tok1, scope1, currentScope1, nullptr)) + continue; + + const auto nReplace = tokDistance(start, usingEnd); + if (nReplace > maxReplacementTokens) { + simplifyUsingError(usingStart, usingEnd); + continue; + } + + // remove the qualification + std::string fullScope = scope; + std::string removed; + while (Token::Match(tok1->tokAt(-2), "%name% ::") && !tok1->tokAt(-2)->isKeyword()) { + removed = (tok1->strAt(-2) + " :: ") + removed; + if (fullScope == tok1->strAt(-2)) { + tok1->deletePrevious(); + tok1->deletePrevious(); + break; + } + const std::string::size_type idx = fullScope.rfind("::"); + + if (idx == std::string::npos) + break; + + if (tok1->strAt(-2) == fullScope.substr(idx + 3)) { + tok1->deletePrevious(); + tok1->deletePrevious(); + fullScope.resize(idx - 1); + } else + break; + } + + // remove global namespace if present + if (tok1->strAt(-1) == "::") { + removed.insert(0, ":: "); + tok1->deletePrevious(); + } + + Token * arrayStart = nullptr; + + // parse the type + Token *type = start; + if (type->str() == "::") { + type = type->next(); + while (Token::Match(type, "%type% ::")) + type = type->tokAt(2); + if (Token::Match(type, "%type%")) + type = type->next(); + } else if (Token::Match(type, "%type% ::")) { + do { + type = type->tokAt(2); + } while (Token::Match(type, "%type% ::")); + if (Token::Match(type, "%type%")) + type = type->next(); + } else if (Token::Match(type, "%type%")) { + while (Token::Match(type, "const|class|struct|union|enum %type%") || + (type->next() && type->next()->isStandardType())) + type = type->next(); + + type = type->next(); + + while (Token::Match(type, "%type%") && + (type->isStandardType() || Token::Match(type, "unsigned|signed"))) { + type = type->next(); + } + + bool atEnd = false; + while (!atEnd) { + if (type && type->str() == "::") { + type = type->next(); + } + + if (Token::Match(type, "%type%") && + type->next() && !Token::Match(type->next(), "[|,|(")) { + type = type->next(); + } else if (Token::simpleMatch(type, "const (")) { + type = type->next(); + atEnd = true; + } else + atEnd = true; + } + } else + syntaxError(type); + + // check for invalid input + if (!type) + syntaxError(tok1); + + // check for template + if (type->str() == "<") { + type = type->findClosingBracket(); + + while (type && Token::Match(type->next(), ":: %type%")) + type = type->tokAt(2); + + if (!type) { + syntaxError(tok1); + } + + while (Token::Match(type->next(), "const|volatile")) + type = type->next(); + + type = type->next(); + } + + // check for pointers and references + std::list pointers; + while (Token::Match(type, "*|&|&&|const")) { + pointers.push_back(type->str()); + type = type->next(); + } + + // check for array + if (type && type->str() == "[") { + do { + if (!arrayStart) + arrayStart = type; + + bool atEnd = false; + while (!atEnd) { + while (type->next() && !Token::Match(type->next(), ";|,")) { + type = type->next(); + } + + if (!type->next()) + syntaxError(type); // invalid input + else if (type->next()->str() == ";") + atEnd = true; + else if (type->str() == "]") + atEnd = true; + else + type = type->next(); + } + + type = type->next(); + } while (type && type->str() == "["); + } + + // make sure we are in a good state + if (!tok1 || !tok1->next()) + break; // bail + + Token* after = tok1->next(); + // check if type was parsed + if (type && type == usingEnd) { + // check for array syntax and add type around variable + if (arrayStart) { + if (Token::Match(tok1->next(), "%name%")) { + TokenList::copyTokens(tok1->next(), arrayStart, usingEnd->previous()); + TokenList::copyTokens(tok1, start, arrayStart->previous()); + tok1->deleteThis(); + substitute = true; + } + } else { + // add some qualification back if needed + std::string removed1 = std::move(removed); + std::string::size_type idx = removed1.rfind(" ::"); + if (idx != std::string::npos) + removed1.resize(idx); + if (scopesMatch(removed1, scope, &scopeInfo1)) { + ScopeInfo3 * tempScope = currentScope; + while (tempScope->parent) { + if (tempScope->recordTypes.find(start->str()) != tempScope->recordTypes.end()) { + std::string::size_type spaceIdx = 0; + std::string::size_type startIdx = 0; + while ((spaceIdx = removed1.find(' ', startIdx)) != std::string::npos) { + tok1->previous()->insertToken(removed1.substr(startIdx, spaceIdx - startIdx)); + startIdx = spaceIdx + 1; + } + tok1->previous()->insertToken(removed1.substr(startIdx)); + tok1->previous()->insertToken("::"); + break; + } + idx = removed1.rfind(" ::"); + if (idx == std::string::npos) + break; + + removed1.resize(idx); + tempScope = tempScope->parent; + } + } + + // Is this a "T()" expression where T is a pointer type? + if (Token::Match(tok1, "%name% ( )") && !pointers.empty()) { + Token* tok2 = tok1->linkAt(1); + tok1->deleteThis(); + TokenList::copyTokens(tok1, start, usingEnd->previous()); + tok2->insertToken("0"); + after = tok2->next(); + } + else { // just replace simple type aliases + TokenList::copyTokens(tok1, start, usingEnd->previous()); + tok1->deleteThis(); + } + substitute = true; + } + } else { + skip = true; + simplifyUsingError(usingStart, usingEnd); + } + tok1 = after->previous(); + } + + if (!skip) + usingList.emplace_back(usingStart, usingEnd); + } + + // delete all used type alias definitions + for (std::list::reverse_iterator it = usingList.rbegin(); it != usingList.rend(); ++it) { + Token *usingStart = it->startTok; + Token *usingEnd = it->endTok; + if (usingStart->previous()) { + if (usingEnd->next()) + Token::eraseTokens(usingStart->previous(), usingEnd->next()); + else { + Token::eraseTokens(usingStart->previous(), usingEnd); + usingEnd->deleteThis(); + } + } else { + if (usingEnd->next()) { + Token::eraseTokens(usingStart, usingEnd->next()); + usingStart->deleteThis(); + } else { + // this is the only code being checked so leave ';' + Token::eraseTokens(usingStart, usingEnd); + usingStart->deleteThis(); + } + } + } + + return substitute; +} + +void Tokenizer::simplifyUsingError(const Token* usingStart, const Token* usingEnd) +{ + if (mSettings.debugwarnings) { + std::string str; + for (const Token *tok = usingStart; tok && tok != usingEnd; tok = tok->next()) { + if (!str.empty()) + str += ' '; + str += tok->str(); + } + str += " ;"; + std::list callstack(1, usingStart); + mErrorLogger.reportErr(ErrorMessage(callstack, &list, Severity::debug, "simplifyUsing", + "Failed to parse \'" + str + "\'. The checking continues anyway.", Certainty::normal)); + } +} + +bool Tokenizer::simplifyTokens1(const std::string &configuration) +{ + // Fill the map mTypeSize.. + fillTypeSizes(); + + mConfiguration = configuration; + + if (mTimerResults) { + Timer t("Tokenizer::simplifyTokens1::simplifyTokenList1", mSettings.showtime, mTimerResults); + if (!simplifyTokenList1(list.getFiles().front().c_str())) + return false; + } else { + if (!simplifyTokenList1(list.getFiles().front().c_str())) + return false; + } + + if (mTimerResults) { + Timer t("Tokenizer::simplifyTokens1::createAst", mSettings.showtime, mTimerResults); + list.createAst(); + list.validateAst(mSettings.debugnormal); + } else { + list.createAst(); + list.validateAst(mSettings.debugnormal); + } + + if (mTimerResults) { + Timer t("Tokenizer::simplifyTokens1::createSymbolDatabase", mSettings.showtime, mTimerResults); + createSymbolDatabase(); + } else { + createSymbolDatabase(); + } + + if (mTimerResults) { + Timer t("Tokenizer::simplifyTokens1::setValueType", mSettings.showtime, mTimerResults); + mSymbolDatabase->setValueTypeInTokenList(false); + mSymbolDatabase->setValueTypeInTokenList(true); + } else { + mSymbolDatabase->setValueTypeInTokenList(false); + mSymbolDatabase->setValueTypeInTokenList(true); + } + + if (!mSettings.buildDir.empty()) + Summaries::create(*this, configuration); + + // TODO: do not run valueflow if no checks are being performed at all - e.g. unusedFunctions only + const char* disableValueflowEnv = std::getenv("DISABLE_VALUEFLOW"); + const bool doValueFlow = !disableValueflowEnv || (std::strcmp(disableValueflowEnv, "1") != 0); + + if (doValueFlow) { + if (mTimerResults) { + Timer t("Tokenizer::simplifyTokens1::ValueFlow", mSettings.showtime, mTimerResults); + ValueFlow::setValues(list, *mSymbolDatabase, mErrorLogger, mSettings, mTimerResults); + } else { + ValueFlow::setValues(list, *mSymbolDatabase, mErrorLogger, mSettings, mTimerResults); + } + + arraySizeAfterValueFlow(); + } + + // Warn about unhandled character literals + if (mSettings.severity.isEnabled(Severity::portability)) { + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if (tok->tokType() == Token::eChar && tok->values().empty()) { + try { + simplecpp::characterLiteralToLL(tok->str()); + } catch (const std::exception &e) { + unhandledCharLiteral(tok, e.what()); + } + } + } + } + + if (doValueFlow) { + mSymbolDatabase->setArrayDimensionsUsingValueFlow(); + } + + printDebugOutput(1); + + return true; +} + +//--------------------------------------------------------------------------- + +void Tokenizer::findComplicatedSyntaxErrorsInTemplates() +{ + validate(); + mTemplateSimplifier->checkComplicatedSyntaxErrorsInTemplates(); +} + +void Tokenizer::checkForEnumsWithTypedef() +{ + for (const Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "enum %name% {")) { + tok = tok->tokAt(2); + const Token *tok2 = Token::findsimplematch(tok, "typedef", tok->link()); + if (tok2) + syntaxError(tok2); + tok = tok->link(); + } + } +} + +void Tokenizer::fillTypeSizes() +{ + mTypeSize.clear(); + mTypeSize["char"] = 1; + mTypeSize["_Bool"] = mSettings.platform.sizeof_bool; + mTypeSize["bool"] = mSettings.platform.sizeof_bool; + mTypeSize["short"] = mSettings.platform.sizeof_short; + mTypeSize["int"] = mSettings.platform.sizeof_int; + mTypeSize["long"] = mSettings.platform.sizeof_long; + mTypeSize["long long"] = mSettings.platform.sizeof_long_long; + mTypeSize["float"] = mSettings.platform.sizeof_float; + mTypeSize["double"] = mSettings.platform.sizeof_double; + mTypeSize["long double"] = mSettings.platform.sizeof_long_double; + mTypeSize["wchar_t"] = mSettings.platform.sizeof_wchar_t; + mTypeSize["size_t"] = mSettings.platform.sizeof_size_t; + mTypeSize["*"] = mSettings.platform.sizeof_pointer; +} + +void Tokenizer::combineOperators() +{ + const bool cpp = isCPP(); + + // Combine tokens.. + for (Token *tok = list.front(); tok && tok->next(); tok = tok->next()) { + const char c1 = tok->str()[0]; + + if (tok->str().length() == 1 && tok->next()->str().length() == 1) { + const char c2 = tok->next()->str()[0]; + + // combine +-*/ and = + if (c2 == '=' && (std::strchr("+-*/%|^=!<>", c1)) && !Token::Match(tok->previous(), "%type% *")) { + // skip templates + if (cpp && (tok->str() == ">" || Token::simpleMatch(tok->previous(), "> *"))) { + const Token* opening = + tok->str() == ">" ? tok->findOpeningBracket() : tok->previous()->findOpeningBracket(); + if (opening && Token::Match(opening->previous(), "%name%")) + continue; + } + tok->str(tok->str() + c2); + tok->deleteNext(); + continue; + } + } else if (tok->next()->str() == "=") { + if (tok->str() == ">>") { + tok->str(">>="); + tok->deleteNext(); + } else if (tok->str() == "<<") { + tok->str("<<="); + tok->deleteNext(); + } + } else if (cpp && (c1 == 'p' || c1 == '_') && + Token::Match(tok, "private|protected|public|__published : !!:")) { + bool simplify = false; + int par = 0; + for (const Token *prev = tok->previous(); prev; prev = prev->previous()) { + if (prev->str() == ")") { + ++par; + } else if (prev->str() == "(") { + if (par == 0U) + break; + --par; + } + if (par != 0U || prev->str() == "(") + continue; + if (Token::Match(prev, "[;{}]")) { + simplify = true; + break; + } + if (prev->isName() && prev->isUpperCaseName()) + continue; + if (prev->isName() && endsWith(prev->str(), ':')) + simplify = true; + break; + } + if (simplify) { + tok->str(tok->str() + ":"); + tok->deleteNext(); + } + } else if (tok->str() == "->") { + // If the preceding sequence is "( & %name% )", replace it by "%name%" + Token *t = tok->tokAt(-4); + if (Token::Match(t, "( & %name% )") && !Token::simpleMatch(t->previous(), ">")) { + t->deleteThis(); + t->deleteThis(); + t->deleteNext(); + tok->str("."); + } else { + tok->str("."); + tok->originalName("->"); + } + } + } +} + +void Tokenizer::combineStringAndCharLiterals() +{ + // Combine strings + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!isStringLiteral(tok->str())) + continue; + + tok->str(simplifyString(tok->str())); + + while (Token::Match(tok->next(), "%str%") || Token::Match(tok->next(), "_T|_TEXT|TEXT ( %str% )")) { + if (tok->next()->isName()) { + if (!mSettings.platform.isWindows()) + break; + tok->deleteNext(2); + tok->next()->deleteNext(); + } + // Two strings after each other, combine them + tok->concatStr(simplifyString(tok->next()->str())); + tok->deleteNext(); + } + } +} + +void Tokenizer::concatenateNegativeNumberAndAnyPositive() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!Token::Match(tok, "?|:|,|(|[|{|return|case|sizeof|%op% +|-") || tok->tokType() == Token::eIncDecOp) + continue; + + while (tok->str() != ">" && tok->next() && tok->next()->str() == "+" && (!Token::Match(tok->tokAt(2), "%name% (|;") || Token::Match(tok, "%op%"))) + tok->deleteNext(); + + if (Token::Match(tok->next(), "- %num%")) { + tok->deleteNext(); + tok->next()->str("-" + tok->next()->str()); + } + } +} + +void Tokenizer::simplifyExternC() +{ + if (isC()) + return; + + // Add attributes to all tokens within `extern "C"` inlines and blocks, and remove the `extern "C"` tokens. + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "extern \"C\"|\"C++\"")) { + Token *tok2 = tok->next(); + const bool isExtC = tok->next()->str().size() == 3; + if (tok->strAt(2) == "{") { + tok2 = tok2->next(); // skip { + while ((tok2 = tok2->next()) && tok2 != tok->linkAt(2)) + tok2->isExternC(isExtC); + tok->linkAt(2)->deleteThis(); // } + tok->deleteNext(2); // "C" { + } else { + while ((tok2 = tok2->next()) && !Token::Match(tok2, "[;{]")) + tok2->isExternC(isExtC); + tok->deleteNext(); // "C" + } + tok->deleteThis(); // extern + } + } +} + +void Tokenizer::simplifyRoundCurlyParentheses() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + while (Token::Match(tok, "[;{}:] ( {") && + Token::simpleMatch(tok->linkAt(2), "} ) ;")) { + if (tok->str() == ":" && !Token::Match(tok->tokAt(-2),"[;{}] %type% :")) + break; + Token *end = tok->linkAt(2)->tokAt(-3); + if (Token::Match(end, "[;{}] %num%|%str% ;")) + end->deleteNext(2); + tok->linkAt(2)->previous()->deleteNext(3); + tok->deleteNext(2); + } + if (Token::Match(tok, "( { %bool%|%char%|%num%|%str%|%name% ; } )")) { + tok->deleteNext(); + tok->deleteThis(); + tok->deleteNext(3); + } + } +} + +void Tokenizer::simplifySQL() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!Token::simpleMatch(tok, "__CPPCHECK_EMBEDDED_SQL_EXEC__ SQL")) + continue; + + const Token *end = findSQLBlockEnd(tok); + if (end == nullptr) + syntaxError(nullptr); + + const std::string instruction = tok->stringifyList(end); + // delete all tokens until the embedded SQL block end + Token::eraseTokens(tok, end); + + // insert "asm ( "instruction" ) ;" + tok->str("asm"); + // it can happen that 'end' is NULL when wrong code is inserted + if (!tok->next()) + tok->insertToken(";"); + tok->insertToken(")"); + tok->insertToken("\"" + instruction + "\""); + tok->insertToken("("); + // jump to ';' and continue + tok = tok->tokAt(3); + } +} + +void Tokenizer::simplifyArrayAccessSyntax() +{ + // 0[a] -> a[0] + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->isNumber() && Token::Match(tok, "%num% [ %name% ]")) { + const std::string number(tok->str()); + Token* indexTok = tok->tokAt(2); + tok->str(indexTok->str()); + tok->varId(indexTok->varId()); + indexTok->str(number); + } + } +} + +void Tokenizer::simplifyParameterVoid() +{ + for (Token* tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "%name% ( void )") && !Token::Match(tok, "sizeof|decltype|typeof|return")) { + tok->next()->deleteNext(); + tok->next()->setRemovedVoidParameter(true); + } + } +} + +void Tokenizer::simplifyRedundantConsecutiveBraces() +{ + // Remove redundant consecutive braces, i.e. '.. { { .. } } ..' -> '.. { .. } ..'. + for (Token *tok = list.front(); tok;) { + if (Token::simpleMatch(tok, "= {")) { + tok = tok->linkAt(1); + } else if (Token::simpleMatch(tok, "{ {") && Token::simpleMatch(tok->next()->link(), "} }")) { + //remove internal parentheses + tok->next()->link()->deleteThis(); + tok->deleteNext(); + } else + tok = tok->next(); + } +} + +void Tokenizer::simplifyDoublePlusAndDoubleMinus() +{ + // Convert - - into + and + - into - + for (Token *tok = list.front(); tok; tok = tok->next()) { + while (tok->next()) { + if (tok->str() == "+") { + if (tok->next()->str()[0] == '-') { + tok = tok->next(); + if (tok->str().size() == 1) { + tok = tok->previous(); + tok->str("-"); + tok->deleteNext(); + } else if (tok->isNumber()) { + tok->str(tok->str().substr(1)); + tok = tok->previous(); + tok->str("-"); + } + continue; + } + } else if (tok->str() == "-") { + if (tok->next()->str()[0] == '-') { + tok = tok->next(); + if (tok->str().size() == 1) { + tok = tok->previous(); + tok->str("+"); + tok->deleteNext(); + } else if (tok->isNumber()) { + tok->str(tok->str().substr(1)); + tok = tok->previous(); + tok->str("+"); + } + continue; + } + } + + break; + } + } +} + +/** Specify array size if it hasn't been given */ + +void Tokenizer::arraySize() +{ + auto getStrTok = [](Token* tok, bool addLength, Token*& endStmt) -> Token* { + if (addLength) { + endStmt = tok->tokAt(5); + return tok->tokAt(4); + } + if (Token::Match(tok, "%var% [ ] =")) { + tok = tok->tokAt(4); + int parCount = 0; + while (Token::simpleMatch(tok, "(")) { + ++parCount; + tok = tok->next(); + } + if (Token::Match(tok, "%str%")) { + endStmt = tok->tokAt(parCount + 1); + return tok; + } + } + return nullptr; + }; + + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!tok->isName() || !Token::Match(tok, "%var% [ ] =")) + continue; + bool addlength = false; + if (Token::Match(tok->previous(), "!!* %var% [ ] = { %str% } ;")) { + Token *t = tok->tokAt(3); + t->deleteNext(); + t->next()->deleteNext(); + addlength = true; + } + + Token* endStmt{}; + if (const Token* strTok = getStrTok(tok, addlength, endStmt)) { + const int sz = Token::getStrArraySize(strTok); + tok->next()->insertToken(std::to_string(sz)); + tok = endStmt; + } + + else if (Token::Match(tok, "%var% [ ] = {")) { + MathLib::biguint sz = 1; + tok = tok->next(); + Token *end = tok->linkAt(3); + for (Token *tok2 = tok->tokAt(4); tok2 && tok2 != end; tok2 = tok2->next()) { + if (tok2->link() && Token::Match(tok2, "{|(|[|<")) { + if (tok2->str() == "[" && tok2->link()->strAt(1) == "=") { // designated initializer + if (Token::Match(tok2, "[ %num% ]")) + sz = std::max(sz, MathLib::toBigUNumber(tok2->strAt(1)) + 1U); + else { + sz = 0; + break; + } + } + tok2 = tok2->link(); + } else if (tok2->str() == ",") { + if (!Token::Match(tok2->next(), "[},]")) + ++sz; + else { + tok2 = tok2->previous(); + tok2->deleteNext(); + } + } + } + + if (sz != 0) + tok->insertToken(std::to_string(sz)); + + tok = end->next() ? end->next() : end; + } + } +} + +void Tokenizer::arraySizeAfterValueFlow() +{ + // After ValueFlow, adjust array sizes. + for (const Variable* var: mSymbolDatabase->variableList()) { + if (!var || !var->isArray()) + continue; + if (!Token::Match(var->nameToken(), "%name% [ ] = { [")) + continue; + MathLib::bigint maxIndex = -1; + const Token* const startToken = var->nameToken()->tokAt(4); + const Token* const endToken = startToken->link(); + for (const Token* tok = startToken; tok != endToken; tok = tok->next()) { + if (!Token::Match(tok, "[{,] [") || !Token::simpleMatch(tok->linkAt(1), "] =")) + continue; + const Token* expr = tok->next()->astOperand1(); + if (expr && expr->hasKnownIntValue()) + maxIndex = std::max(maxIndex, expr->getKnownIntValue()); + } + if (maxIndex >= 0) { + // insert array size + Token* tok = const_cast(var->nameToken()->next()); + tok->insertToken(std::to_string(maxIndex + 1)); + // ast + tok->astOperand2(tok->next()); + // Token::scope + tok->next()->scope(tok->scope()); + // Value flow + ValueFlow::Value value(maxIndex + 1); + value.setKnown(); + tok->next()->addValue(value); + // Set array dimensions + Dimension d; + d.num = maxIndex + 1; + std::vector dimensions{d}; + const_cast(var)->setDimensions(dimensions); + } + } +} + +static Token *skipTernaryOp(Token *tok) +{ + int colonLevel = 1; + while (nullptr != (tok = tok->next())) { + if (tok->str() == "?") { + ++colonLevel; + } else if (tok->str() == ":") { + --colonLevel; + if (colonLevel == 0) { + tok = tok->next(); + break; + } + } + if (tok->link() && Token::Match(tok, "[(<]")) + tok = tok->link(); + else if (Token::Match(tok->next(), "[{};)]")) + break; + } + if (colonLevel > 0) // Ticket #5214: Make sure the ':' matches the proper '?' + return nullptr; + return tok; +} + +// Skips until the colon at the end of the case label, the argument must point to the "case" token. +// In case of success returns the colon token. +// In case of failure returns the token that caused the error. +static Token *skipCaseLabel(Token *tok) +{ + assert(tok->str() == "case"); + while (nullptr != (tok = tok->next())) { + if (Token::Match(tok, "(|[")) + tok = tok->link(); + else if (tok->str() == "?") { + Token * tok1 = skipTernaryOp(tok); + if (!tok1) + return tok; + tok = tok1; + } + if (Token::Match(tok, "[:{};]")) + return tok; + } + return nullptr; +} + +const Token * Tokenizer::startOfExecutableScope(const Token * tok) +{ + if (tok->str() != ")") + return nullptr; + + tok = Tokenizer::isFunctionHead(tok, ":{"); + + if (Token::Match(tok, ": %name% [({]")) { + while (Token::Match(tok, "[:,] %name% [({]")) + tok = tok->linkAt(2)->next(); + } + + return (tok && tok->str() == "{") ? tok : nullptr; +} + + +/** simplify labels and case|default in the code: add a ";" if not already in.*/ + +void Tokenizer::simplifyLabelsCaseDefault() +{ + const bool cpp = isCPP(); + bool executablescope = false; + int indentLevel = 0; + for (Token *tok = list.front(); tok; tok = tok->next()) { + // Simplify labels in the executable scope.. + auto *start = const_cast(startOfExecutableScope(tok)); + if (start) { + tok = start; + executablescope = true; + } + + if (!executablescope) + continue; + + if (tok->str() == "{") { + if (tok->previous()->str() == "=") + tok = tok->link(); + else + ++indentLevel; + } else if (tok->str() == "}") { + --indentLevel; + if (indentLevel == 0) { + executablescope = false; + continue; + } + } else if (Token::Match(tok, "(|[")) + tok = tok->link(); + + if (Token::Match(tok, "[;{}:] case")) { + tok = skipCaseLabel(tok->next()); + if (!tok) + break; + if (tok->str() != ":" || tok->strAt(-1) == "case" || !tok->next()) + syntaxError(tok); + if (tok->next()->str() != ";" && tok->next()->str() != "case") + tok->insertToken(";"); + else + tok = tok->previous(); + } else if (Token::Match(tok, "[;{}] %name% : !!;")) { + if (!cpp || !Token::Match(tok->next(), "class|struct|enum")) { + tok = tok->tokAt(2); + tok->insertToken(";"); + } + } + } +} + + +void Tokenizer::simplifyCaseRange() +{ + for (Token* tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "case %num%|%char% ... %num%|%char% :")) { + const MathLib::bigint start = MathLib::toBigNumber(tok->strAt(1)); + MathLib::bigint end = MathLib::toBigNumber(tok->strAt(3)); + end = std::min(start + 50, end); // Simplify it 50 times at maximum + if (start < end) { + tok = tok->tokAt(2); + tok->str(":"); + tok->insertToken("case"); + for (MathLib::bigint i = end-1; i > start; i--) { + tok->insertToken(":"); + tok->insertToken(std::to_string(i)); + tok->insertToken("case"); + } + } + } + } +} + +void Tokenizer::calculateScopes() +{ + for (auto *tok = list.front(); tok; tok = tok->next()) + tok->scopeInfo(nullptr); + + std::string nextScopeNameAddition; + std::shared_ptr primaryScope = std::make_shared("", nullptr); + list.front()->scopeInfo(std::move(primaryScope)); + + for (Token* tok = list.front(); tok; tok = tok->next()) { + if (tok == list.front() || !tok->scopeInfo()) { + if (tok != list.front()) + tok->scopeInfo(tok->previous()->scopeInfo()); + + if (Token::Match(tok, "using namespace %name% ::|<|;")) { + std::string usingNamespaceName; + for (const Token* namespaceNameToken = tok->tokAt(2); + namespaceNameToken && namespaceNameToken->str() != ";"; + namespaceNameToken = namespaceNameToken->next()) { + usingNamespaceName += namespaceNameToken->str(); + usingNamespaceName += " "; + } + if (!usingNamespaceName.empty()) + usingNamespaceName.pop_back(); + tok->scopeInfo()->usingNamespaces.insert(std::move(usingNamespaceName)); + } else if (Token::Match(tok, "namespace|class|struct|union %name% {|::|:|<")) { + for (Token* nameTok = tok->next(); nameTok && !Token::Match(nameTok, "{|:"); nameTok = nameTok->next()) { + if (Token::Match(nameTok, ";|<")) { + nextScopeNameAddition = ""; + break; + } + nextScopeNameAddition.append(nameTok->str()); + nextScopeNameAddition.append(" "); + } + if (!nextScopeNameAddition.empty()) + nextScopeNameAddition.pop_back(); + } + + if (Token::simpleMatch(tok, "{")) { + // This might be the opening of a member function + Token *tok1 = tok; + while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) + tok1 = tok1->previous(); + if (tok1->previous() && tok1->strAt(-1) == ")") { + bool member = true; + tok1 = tok1->linkAt(-1); + if (Token::Match(tok1->previous(), "throw|noexcept")) { + tok1 = tok1->previous(); + while (Token::Match(tok1->previous(), "const|volatile|final|override|&|&&|noexcept")) + tok1 = tok1->previous(); + if (tok1->strAt(-1) != ")") + member = false; + } else if (Token::Match(tok->tokAt(-2), ":|, %name%")) { + tok1 = tok1->tokAt(-2); + if (tok1->strAt(-1) != ")") + member = false; + } + if (member) { + if (tok1->strAt(-1) == ">") + tok1 = tok1->previous()->findOpeningBracket(); + if (tok1 && Token::Match(tok1->tokAt(-3), "%name% :: %name%")) { + tok1 = tok1->tokAt(-2); + std::string scope = tok1->strAt(-1); + while (Token::Match(tok1->tokAt(-2), ":: %name%")) { + scope = tok1->strAt(-3) + " :: " + scope; + tok1 = tok1->tokAt(-2); + } + + if (!nextScopeNameAddition.empty() && !scope.empty()) + nextScopeNameAddition += " :: "; + nextScopeNameAddition += scope; + } + } + } + + // New scope is opening, record it here + std::shared_ptr newScopeInfo = std::make_shared(tok->scopeInfo()->name, tok->link(), tok->scopeInfo()->usingNamespaces); + + if (!newScopeInfo->name.empty() && !nextScopeNameAddition.empty()) + newScopeInfo->name.append(" :: "); + newScopeInfo->name.append(nextScopeNameAddition); + nextScopeNameAddition = ""; + + if (tok->link()) + tok->link()->scopeInfo(tok->scopeInfo()); + tok->scopeInfo(std::move(newScopeInfo)); + } + } + } +} + +void Tokenizer::simplifyTemplates() +{ + if (isC()) + return; + + const std::time_t maxTime = mSettings.templateMaxTime > 0 ? std::time(nullptr) + mSettings.templateMaxTime : 0; + mTemplateSimplifier->simplifyTemplates( + maxTime); +} +//--------------------------------------------------------------------------- + + +namespace { + /** Class used in Tokenizer::setVarIdPass1 */ + class VariableMap { + private: + std::unordered_map mVariableId; + std::unordered_map mVariableId_global; + std::stack>> mScopeInfo; + mutable nonneg int mVarId{}; + public: + VariableMap() = default; + void enterScope(); + bool leaveScope(); + void addVariable(const std::string& varname, bool globalNamespace); + bool hasVariable(const std::string& varname) const { + return mVariableId.find(varname) != mVariableId.end(); + } + + const std::unordered_map& map(bool global) const { + return global ? mVariableId_global : mVariableId; + } + nonneg int& getVarId() { + return mVarId; + } + }; +} + + +void VariableMap::enterScope() +{ + mScopeInfo.emplace(/*std::vector>()*/); +} + +bool VariableMap::leaveScope() +{ + if (mScopeInfo.empty()) + return false; + + for (const std::pair& outerVariable : mScopeInfo.top()) { + if (outerVariable.second != 0) + mVariableId[outerVariable.first] = outerVariable.second; + else + mVariableId.erase(outerVariable.first); + } + mScopeInfo.pop(); + return true; +} + +void VariableMap::addVariable(const std::string& varname, bool globalNamespace) +{ + if (mScopeInfo.empty()) { + mVariableId[varname] = ++mVarId; + if (globalNamespace) + mVariableId_global[varname] = mVariableId[varname]; + return; + } + std::unordered_map::iterator it = mVariableId.find(varname); + if (it == mVariableId.end()) { + mScopeInfo.top().emplace_back(varname, 0); + mVariableId[varname] = ++mVarId; + if (globalNamespace) + mVariableId_global[varname] = mVariableId[varname]; + return; + } + mScopeInfo.top().emplace_back(varname, it->second); + it->second = ++mVarId; +} + +static bool setVarIdParseDeclaration(Token*& tok, const VariableMap& variableMap, bool executableScope) +{ + const Token* const tok1 = tok; + Token* tok2 = tok; + if (!tok2->isName()) + return false; + + nonneg int typeCount = 0; + nonneg int singleNameCount = 0; + bool hasstruct = false; // Is there a "struct" or "class"? + bool bracket = false; + bool ref = false; + while (tok2) { + if (tok2->isName()) { + if (Token::simpleMatch(tok2, "alignas (")) { + tok2 = tok2->linkAt(1)->next(); + continue; + } + if (tok2->isCpp() && Token::Match(tok2, "namespace|public|private|protected")) + return false; + if (tok2->isCpp() && Token::simpleMatch(tok2, "decltype (")) { + typeCount = 1; + tok2 = tok2->linkAt(1)->next(); + continue; + } + if (Token::Match(tok2, "struct|union|enum") || (tok2->isCpp() && Token::Match(tok2, "class|typename"))) { + hasstruct = true; + typeCount = 0; + singleNameCount = 0; + } else if (Token::Match(tok2, "const|extern")) { + // just skip "const", "extern" + } else if (!hasstruct && variableMap.map(false).count(tok2->str()) && tok2->previous()->str() != "::") { + ++typeCount; + tok2 = tok2->next(); + if (!tok2 || tok2->str() != "::") + break; + } else { + if (tok2->str() != "void" || Token::Match(tok2, "void const| *|(")) // just "void" cannot be a variable type + ++typeCount; + ++singleNameCount; + } + } else if (tok2->isCpp() && ((TemplateSimplifier::templateParameters(tok2) > 0) || + Token::simpleMatch(tok2, "< >") /* Ticket #4764 */)) { + const Token *start = tok; + if (Token::Match(start->previous(), "%or%|%oror%|&&|&|^|+|-|*|/")) + return false; + Token* const closingBracket = tok2->findClosingBracket(); + if (closingBracket == nullptr) { /* Ticket #8151 */ + throw tok2; + } + tok2 = closingBracket; + if (tok2->str() != ">") + break; + singleNameCount = 1; + if (Token::Match(tok2, "> %name% %or%|%oror%|&&|&|^|+|-|*|/") && !Token::Match(tok2, "> const [*&]")) + return false; + if (Token::Match(tok2, "> %name% )")) { + if (Token::Match(tok2->linkAt(2)->previous(), "if|for|while (")) + return false; + if (!Token::Match(tok2->linkAt(2)->previous(), "%name%|] (")) + return false; + } + } else if (Token::Match(tok2, "&|&&")) { + ref = !bracket; + } else if (singleNameCount >= 1 && Token::Match(tok2, "( [*&]") && Token::Match(tok2->link(), ") (|[")) { + for (const Token* tok3 = tok2->tokAt(2); Token::Match(tok3, "!!)"); tok3 = tok3->next()) { + if (Token::Match(tok3, "(|[")) + tok3 = tok3->link(); + if (tok3->str() == ",") + return false; + } + bracket = true; // Skip: Seems to be valid pointer to array or function pointer + } else if (singleNameCount >= 1 && Token::Match(tok2, "( * %name% [") && Token::Match(tok2->linkAt(3), "] ) [;,]")) { + bracket = true; + } else if (singleNameCount >= 1 && tok2->previous() && tok2->previous()->isStandardType() && Token::Match(tok2, "( *|&| %name% ) ;")) { + bracket = true; + } else if (tok2->str() == "::") { + singleNameCount = 0; + } else if (tok2->str() != "*" && tok2->str() != "...") { + break; + } + tok2 = tok2->next(); + } + + if (tok2) { + bool isLambdaArg = false; + { + const Token *tok3 = tok->previous(); + if (tok3 && tok3->str() == ",") { + while (tok3 && !Token::Match(tok3,";|(|[|{")) { + if (Token::Match(tok3, ")|]")) + tok3 = tok3->link(); + tok3 = tok3->previous(); + } + + if (tok3 && executableScope && Token::Match(tok3->previous(), "%name% (")) { + const Token *fdecl = tok3->previous(); + int count = 0; + while (Token::Match(fdecl, "%name%|*")) { + fdecl = fdecl->previous(); + count++; + } + if (!Token::Match(fdecl, "[;{}] %name%") || count <= 1) + return false; + } + } + + if (tok3 && tok3->isCpp() && Token::simpleMatch(tok3->previous(), "] (") && + (Token::simpleMatch(tok3->link(), ") {") || Token::Match(tok3->link(), ") . %name%"))) + isLambdaArg = true; + } + + + tok = tok2; + + // In executable scopes, references must be assigned + // Catching by reference is an exception + if (executableScope && ref && !isLambdaArg) { + if (Token::Match(tok2, "(|=|{|:")) + ; // reference is assigned => ok + else if (tok2->str() != ")" || tok2->link()->strAt(-1) != "catch") + return false; // not catching by reference => not declaration + } + } + + // Check if array declaration is valid (#2638) + // invalid declaration: AAA a[4] = 0; + if (typeCount >= 2 && executableScope && Token::Match(tok2, ")| [")) { + const Token *tok3 = tok2->str() == ")" ? tok2->next() : tok2; + while (tok3 && tok3->str() == "[") { + tok3 = tok3->link()->next(); + } + if (Token::Match(tok3, "= %num%")) + return false; + if (bracket && Token::Match(tok1->previous(), "[(,]") && Token::Match(tok3, "[,)]")) + return false; + } + + return (typeCount >= 2 && tok2 && Token::Match(tok2->tokAt(-2), "!!:: %type%")); +} + + +static void setVarIdStructMembers(Token *&tok1, + std::map>& structMembers, + nonneg int &varId) +{ + Token *tok = tok1; + + if (Token::Match(tok, "%name% = { . %name% =|{")) { + const nonneg int struct_varid = tok->varId(); + if (struct_varid == 0) + return; + + std::map& members = structMembers[struct_varid]; + + tok = tok->tokAt(3); + while (tok->str() != "}") { + if (Token::Match(tok, "{|[|(")) + tok = tok->link(); + if (Token::Match(tok->previous(), "[,{] . %name% =|{")) { + tok = tok->next(); + const std::map::iterator it = members.find(tok->str()); + if (it == members.end()) { + members[tok->str()] = ++varId; + tok->varId(varId); + } else { + tok->varId(it->second); + } + } + tok = tok->next(); + } + + return; + } + + while (Token::Match(tok->next(), ")| . %name% !!(")) { + // Don't set varid for trailing return type + if (tok->strAt(1) == ")" && (tok->linkAt(1)->previous()->isName() || tok->linkAt(1)->strAt(-1) == "]") && + Tokenizer::isFunctionHead(tok->linkAt(1), "{|;")) { + tok = tok->tokAt(3); + continue; + } + const nonneg int struct_varid = tok->varId(); + tok = tok->tokAt(2); + if (struct_varid == 0) + continue; + + if (tok->str() == ".") + tok = tok->next(); + + // Don't set varid for template function + if (TemplateSimplifier::templateParameters(tok->next()) > 0) + break; + + std::map& members = structMembers[struct_varid]; + const std::map::iterator it = members.find(tok->str()); + if (it == members.end()) { + members[tok->str()] = ++varId; + tok->varId(varId); + } else { + tok->varId(it->second); + } + } + // tok can't be null + tok1 = tok; +} + +static bool setVarIdClassDeclaration(Token* const startToken, + VariableMap& variableMap, + const nonneg int scopeStartVarId, + std::map>& structMembers) +{ + // end of scope + const Token* const endToken = startToken->link(); + + // determine class name + std::string className; + for (const Token *tok = startToken->previous(); tok; tok = tok->previous()) { + if (!tok->isName() && tok->str() != ":") + break; + if (Token::Match(tok, "class|struct|enum %type% [:{]")) { + className = tok->next()->str(); + break; + } + } + + // replace varids.. + int indentlevel = 0; + bool initList = false; + bool inEnum = false; + const Token *initListArgLastToken = nullptr; + for (Token *tok = startToken->next(); tok != endToken; tok = tok->next()) { + if (!tok) + return false; + if (initList) { + if (tok == initListArgLastToken) + initListArgLastToken = nullptr; + else if (!initListArgLastToken && + Token::Match(tok->previous(), "%name%|>|>> {|(") && + Token::Match(tok->link(), "}|) ,|{")) + initListArgLastToken = tok->link(); + } + if (tok->str() == "{") { + inEnum = isEnumStart(tok); + if (initList && !initListArgLastToken) + initList = false; + ++indentlevel; + } else if (tok->str() == "}") { + --indentlevel; + inEnum = false; + } else if (initList && indentlevel == 0 && Token::Match(tok->previous(), "[,:] %name% [({]")) { + const std::unordered_map::const_iterator it = variableMap.map(false).find(tok->str()); + if (it != variableMap.map(false).end()) { + tok->varId(it->second); + } + } else if (tok->isName() && tok->varId() <= scopeStartVarId) { + if (indentlevel > 0 || initList) { + if (Token::Match(tok->previous(), "::|.") && tok->strAt(-2) != "this" && !Token::simpleMatch(tok->tokAt(-5), "( * this ) .")) + continue; + if (!tok->next()) + return false; + if (tok->next()->str() == "::") { + if (tok->str() == className) + tok = tok->tokAt(2); + else + continue; + } + + if (!inEnum) { + const std::unordered_map::const_iterator it = variableMap.map(false).find(tok->str()); + if (it != variableMap.map(false).end()) { + tok->varId(it->second); + setVarIdStructMembers(tok, structMembers, variableMap.getVarId()); + } + } + } + } else if (indentlevel == 0 && tok->str() == ":" && !initListArgLastToken) + initList = true; + } + return true; +} + + + +// Update the variable ids.. +// Parse each function.. +void Tokenizer::setVarIdClassFunction(const std::string &classname, + Token * const startToken, + const Token * const endToken, + const std::map &varlist, + std::map>& structMembers, + nonneg int &varId_) +{ + const auto pos = classname.rfind(' '); // TODO handle multiple scopes + const std::string lastScope = classname.substr(pos == std::string::npos ? 0 : pos + 1); + for (Token *tok2 = startToken; tok2 && tok2 != endToken; tok2 = tok2->next()) { + if (tok2->varId() != 0 || !tok2->isName()) + continue; + if (Token::Match(tok2->tokAt(-2), ("!!" + lastScope + " ::").c_str())) + continue; + if (Token::Match(tok2->tokAt(-4), "%name% :: %name% ::")) // Currently unsupported + continue; + if (Token::Match(tok2->tokAt(-2), "!!this .") && !Token::simpleMatch(tok2->tokAt(-5), "( * this ) .")) + continue; + if (Token::Match(tok2, "%name% ::")) + continue; + + const std::map::const_iterator it = varlist.find(tok2->str()); + if (it != varlist.end()) { + tok2->varId(it->second); + setVarIdStructMembers(tok2, structMembers, varId_); + } + } +} + + + +void Tokenizer::setVarId() +{ + // Clear all variable ids + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->isName()) + tok->varId(0); + } + + setVarIdPass1(); + + setPodTypes(); + + setVarIdPass2(); +} + + +// Variable declarations can't start with "return" etc. +#define NOTSTART_C "NOT", "case", "default", "goto", "not", "return", "sizeof", "typedef" +static const std::unordered_set notstart_c = { NOTSTART_C }; +static const std::unordered_set notstart_cpp = { NOTSTART_C, + "delete", "friend", "new", "throw", "using", "virtual", "explicit", "const_cast", "dynamic_cast", "reinterpret_cast", "static_cast", "template" +}; + +void Tokenizer::setVarIdPass1() +{ + // Variable declarations can't start with "return" etc. + const std::unordered_set& notstart = (isC()) ? notstart_c : notstart_cpp; + + VariableMap variableMap; + std::map> structMembers; + + std::stack scopeStack; + + scopeStack.emplace(/*VarIdScopeInfo()*/); + std::stack functionDeclEndStack; + const Token *functionDeclEndToken = nullptr; + bool initlist = false; + bool inlineFunction = false; + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->isOp()) + continue; + if (tok->isCpp() && Token::simpleMatch(tok, "template <")) { + Token* closingBracket = tok->next()->findClosingBracket(); + if (closingBracket) + tok = closingBracket; + continue; + } + + if (tok == functionDeclEndToken) { + functionDeclEndStack.pop(); + functionDeclEndToken = functionDeclEndStack.empty() ? nullptr : functionDeclEndStack.top(); + if (tok->str() == ":") + initlist = true; + else if (tok->str() == ";") { + if (!variableMap.leaveScope()) + cppcheckError(tok); + } else if (tok->str() == "{") { + scopeStack.emplace(true, scopeStack.top().isStructInit || tok->strAt(-1) == "=", /*isEnum=*/ false, variableMap.getVarId()); + + // check if this '{' is a start of an "if" body + const Token * ifToken = tok->previous(); + if (ifToken && ifToken->str() == ")") + ifToken = ifToken->link(); + else + ifToken = nullptr; + if (ifToken) + ifToken = ifToken->previous(); + if (ifToken && ifToken->str() == "if") { + // open another scope to differentiate between variables declared in the "if" condition and in the "if" body + variableMap.enterScope(); + } + } + } else if (!initlist && tok->str()=="(") { + const Token * newFunctionDeclEnd = nullptr; + if (!scopeStack.top().isExecutable) + newFunctionDeclEnd = isFunctionHead(tok, "{:;"); + else { + const Token* tokenLinkNext = tok->link()->next(); + if (Token::simpleMatch(tokenLinkNext, ".")) { // skip trailing return type + tokenLinkNext = tokenLinkNext->next(); + while (Token::Match(tokenLinkNext, "%name%|::")) { + tokenLinkNext = tokenLinkNext->next(); + if (Token::simpleMatch(tokenLinkNext, "<") && tokenLinkNext->link()) + tokenLinkNext = tokenLinkNext->link()->next(); + } + } + if (tokenLinkNext && tokenLinkNext->str() == "{") // might be for- or while-loop or if-statement + newFunctionDeclEnd = tokenLinkNext; + } + if (newFunctionDeclEnd && newFunctionDeclEnd != functionDeclEndToken) { + functionDeclEndStack.push(newFunctionDeclEnd); + functionDeclEndToken = newFunctionDeclEnd; + variableMap.enterScope(); + } + } else if (Token::Match(tok, "{|}")) { + inlineFunction = false; + + const Token * const startToken = (tok->str() == "{") ? tok : tok->link(); + + // parse anonymous namespaces as part of the current scope + if (!Token::Match(startToken->previous(), "union|struct|enum|namespace {") && + !(initlist && Token::Match(startToken->previous(), "%name%|>|>>|(") && Token::Match(startToken->link(), "} ,|{|)"))) { + + if (tok->str() == "{") { + bool isExecutable; + const Token *prev = tok->previous(); + while (Token::Match(prev, "%name%|.")) + prev = prev->previous(); + const bool isLambda = prev && prev->str() == ")" && Token::simpleMatch(prev->link()->previous(), "] ("); + if ((!isLambda && (tok->strAt(-1) == ")" || Token::Match(tok->tokAt(-2), ") %type%"))) || + (initlist && tok->strAt(-1) == "}")) { + isExecutable = true; + } else { + isExecutable = ((scopeStack.top().isExecutable || initlist || tok->strAt(-1) == "else") && + !isClassStructUnionEnumStart(tok)); + if (!(scopeStack.top().isStructInit || tok->strAt(-1) == "=")) + variableMap.enterScope(); + } + initlist = false; + scopeStack.emplace(isExecutable, scopeStack.top().isStructInit || tok->strAt(-1) == "=", isEnumStart(tok), variableMap.getVarId()); + } else { /* if (tok->str() == "}") */ + bool isNamespace = false; + for (const Token *tok1 = tok->link()->previous(); tok1 && tok1->isName(); tok1 = tok1->previous()) { + if (tok1->str() == "namespace") { + isNamespace = true; + break; + } + } + // Set variable ids in class declaration.. + if (!initlist && !isC() && !scopeStack.top().isExecutable && tok->link() && !isNamespace) { + if (!setVarIdClassDeclaration(tok->link(), + variableMap, + scopeStack.top().startVarid, + structMembers)) { + syntaxError(nullptr); + } + } + + if (!scopeStack.top().isStructInit) { + variableMap.leaveScope(); + + // check if this '}' is an end of an "else" body or an "if" body without an "else" part + const Token * ifToken = startToken->previous(); + if (ifToken && ifToken->str() == ")") + ifToken = ifToken->link()->previous(); + else + ifToken = nullptr; + if (startToken->strAt(-1) == "else" || (ifToken && ifToken->str() == "if" && tok->strAt(1) != "else")) { + // leave the extra scope used to differentiate between variables declared in the "if" condition and in the "if" body + variableMap.leaveScope(); + } + } + + scopeStack.pop(); + if (scopeStack.empty()) { // should be impossible + scopeStack.emplace(/*VarIdScopeInfo()*/); + } + } + } + } + + if ((!scopeStack.top().isStructInit && + (tok == list.front() || + Token::Match(tok, "[;{}]") || + (tok->str() == "(" && !scopeStack.top().isExecutable && isFunctionHead(tok,";:")) || + (tok->str() == "," && (!scopeStack.top().isExecutable || inlineFunction || !tok->previous()->varId())) || + (tok->isName() && endsWith(tok->str(), ':')))) || + (tok->str() == "(" && isFunctionHead(tok, "{"))) { + + // No variable declarations in sizeof + if (Token::simpleMatch(tok->previous(), "sizeof (")) { + continue; + } + + if (Settings::terminated()) + return; + + // locate the variable name.. + Token* tok2 = (tok->isName()) ? tok : tok->next(); + + // private: protected: public: etc + while (tok2 && endsWith(tok2->str(), ':')) { + tok2 = tok2->next(); + } + if (!tok2) + break; + + // Variable declaration can't start with "return", etc + if (notstart.find(tok2->str()) != notstart.end()) + continue; + + if (!isC() && Token::simpleMatch(tok2, "const new")) + continue; + + bool decl; + if (isCPP() && mSettings.standards.cpp >= Standards::CPP17 && Token::Match(tok, "[(;{}] const| auto &|&&| [")) { + // Structured bindings + tok2 = Token::findsimplematch(tok, "["); + if ((Token::simpleMatch(tok->previous(), "for (") && Token::simpleMatch(tok2->link(), "] :")) || + Token::simpleMatch(tok2->link(), "] =")) { + while (tok2 && tok2->str() != "]") { + if (Token::Match(tok2, "%name% [,]]")) + variableMap.addVariable(tok2->str(), false); + tok2 = tok2->next(); + } + continue; + } + } + + try { /* Ticket #8151 */ + decl = setVarIdParseDeclaration(tok2, variableMap, scopeStack.top().isExecutable); + } catch (const Token * errTok) { + syntaxError(errTok); + } + + if (tok->str() == "(" && isFunctionHead(tok, "{") && scopeStack.top().isExecutable) + inlineFunction = true; + + if (decl) { + if (isCPP()) { + if (Token *declTypeTok = Token::findsimplematch(tok, "decltype (", tok2)) { + for (Token *declTok = declTypeTok->linkAt(1); declTok != declTypeTok; declTok = declTok->previous()) { + if (declTok->isName() && !Token::Match(declTok->previous(), "::|.") && variableMap.hasVariable(declTok->str())) + declTok->varId(variableMap.map(false).find(declTok->str())->second); + } + } + } + + const Token* prev2 = tok2->previous(); + if (Token::Match(prev2, "%type% [;[=,)]") && tok2->previous()->str() != "const") + ; + else if (Token::Match(prev2, "%type% :") && tok->strAt(-1) == "for") + ; + else if (Token::Match(prev2, "%type% ( !!)") && Token::simpleMatch(tok2->link(), ") ;")) { + // In C++ , a variable can't be called operator+ or something like that. + if (prev2->isCpp() && + prev2->isOperatorKeyword()) + continue; + + const Token *tok3 = tok2->next(); + if (!tok3->isStandardType() && tok3->str() != "void" && !Token::Match(tok3, "struct|union|class %type%") && tok3->str() != "." && !Token::Match(tok2->link()->previous(), "[&*]")) { + if (!scopeStack.top().isExecutable) { + // Detecting initializations with () in non-executable scope is hard and often impossible to be done safely. Thus, only treat code as a variable that definitely is one. + decl = false; + bool rhs = false; + for (; tok3; tok3 = tok3->nextArgumentBeforeCreateLinks2()) { + if (tok3->str() == "=") { + rhs = true; + continue; + } + + if (tok3->str() == ",") { + rhs = false; + continue; + } + + if (rhs) + continue; + + if (tok3->isLiteral() || + (tok3->isName() && (variableMap.hasVariable(tok3->str()) || + (tok3->strAt(-1) == "(" && Token::simpleMatch(tok3->next(), "(") && !Token::simpleMatch(tok3->linkAt(1)->next(), "(")))) || + tok3->isOp() || + tok3->str() == "(" || + notstart.find(tok3->str()) != notstart.end()) { + decl = true; + break; + } + } + } + } else + decl = false; + } else if (isCPP() && Token::Match(prev2, "%type% {") && Token::simpleMatch(tok2->link(), "} ;")) { // C++11 initialization style + if (tok2->link() != tok2->next() && // add value-initialized variable T x{}; + (Token::Match(prev2, "do|try|else") || Token::Match(prev2->tokAt(-2), "struct|class|:"))) + continue; + } else + decl = false; + + if (decl) { + if (isC() && Token::Match(prev2->previous(), "&|&&")) + syntaxErrorC(prev2, prev2->strAt(-2) + prev2->strAt(-1) + " " + prev2->str()); + variableMap.addVariable(prev2->str(), scopeStack.size() <= 1); + + if (Token::simpleMatch(tok->previous(), "for (") && Token::Match(prev2, "%name% [=,]")) { + for (const Token *tok3 = prev2->next(); tok3 && tok3->str() != ";"; tok3 = tok3->next()) { + if (Token::Match(tok3, "[([]")) + tok3 = tok3->link(); + if (Token::Match(tok3, ", %name% [,=;]")) + variableMap.addVariable(tok3->next()->str(), false); + } + } + + // set varid for template parameters.. + tok = tok->next(); + while (Token::Match(tok, "%name%|::")) + tok = tok->next(); + if (tok && tok->str() == "<") { + const Token *end = tok->findClosingBracket(); + while (tok != end) { + if (tok->isName() && !(Token::simpleMatch(tok->next(), "<") && + Token::Match(tok->tokAt(-1), ":: %name%"))) { + const std::unordered_map::const_iterator it = variableMap.map(false).find(tok->str()); + if (it != variableMap.map(false).end()) + tok->varId(it->second); + } + tok = tok->next(); + } + } + + tok = tok2->previous(); + } + } + } + + if (tok->isName() && !tok->isKeyword() && !tok->isStandardType()) { + // don't set variable id after a struct|enum|union + if (Token::Match(tok->previous(), "struct|enum|union") || (tok->isCpp() && tok->strAt(-1) == "class")) + continue; + + bool globalNamespace = false; + if (!isC()) { + if (tok->previous() && tok->previous()->str() == "::") { + if (Token::Match(tok->tokAt(-2), ")|]|%name%")) + continue; + globalNamespace = true; + } + if (tok->next() && tok->next()->str() == "::") + continue; + if (Token::simpleMatch(tok->tokAt(-2), ":: template")) + continue; + } + + // function declaration inside executable scope? Function declaration is of form: type name "(" args ")" + if (scopeStack.top().isExecutable && Token::Match(tok, "%name% [,)[]")) { + bool par = false; + const Token* start; + Token* end; + + // search begin of function declaration + for (start = tok; Token::Match(start, "%name%|*|&|,|("); start = start->previous()) { + if (start->str() == "(") { + if (par) + break; + par = true; + } + if (Token::Match(start, "[(,]")) { + if (!Token::Match(start, "[(,] %type% %name%|*|&")) + break; + } + if (start->varId() > 0) + break; + } + + // search end of function declaration + for (end = tok->next(); Token::Match(end, "%name%|*|&|,|[|]|%num%"); end = end->next()) {} + + // there are tokens which can't appear at the begin of a function declaration such as "return" + const bool isNotstartKeyword = start->next() && notstart.find(start->next()->str()) != notstart.end(); + + // now check if it is a function declaration + if (Token::Match(start, "[;{}] %type% %name%|*") && par && Token::simpleMatch(end, ") ;") && !isNotstartKeyword) { + // function declaration => don't set varid + tok = end; + continue; + } + } + + if ((!scopeStack.top().isEnum || !(Token::Match(tok->previous(), "{|,") && Token::Match(tok->next(), ",|=|}"))) && + !Token::simpleMatch(tok->next(), ": ;")) { + const std::unordered_map::const_iterator it = variableMap.map(globalNamespace).find(tok->str()); + if (it != variableMap.map(globalNamespace).end()) { + tok->varId(it->second); + setVarIdStructMembers(tok, structMembers, variableMap.getVarId()); + } + } + } else if (Token::Match(tok, "::|. %name%") && Token::Match(tok->previous(), ")|]|>|%name%")) { + // Don't set varid after a :: or . token + tok = tok->next(); + } else if (tok->str() == ":" && Token::Match(tok->tokAt(-2), "class %type%")) { + do { + tok = tok->next(); + } while (tok && (tok->isName() || tok->str() == ",")); + if (!tok) + break; + tok = tok->previous(); + } + } + + mVarId = variableMap.getVarId(); +} + +namespace { + struct Member { + Member(std::list s, std::list ns, Token *t) : usingnamespaces(std::move(ns)), scope(std::move(s)), tok(t) {} + std::list usingnamespaces; + std::list scope; + Token *tok; + }; +} + +static std::string getScopeName(const std::list &scopeInfo) +{ + std::string ret; + for (const ScopeInfo2 &si : scopeInfo) + ret += (ret.empty() ? "" : " :: ") + (si.name); + return ret; +} + +static Token * matchMemberName(const std::list &scope, const Token *nsToken, Token *memberToken, const std::list &scopeInfo) +{ + std::list::const_iterator scopeIt = scopeInfo.cbegin(); + + // Current scope.. + for (std::list::const_iterator it = scope.cbegin(); it != scope.cend(); ++it) { + if (scopeIt == scopeInfo.cend() || scopeIt->name != *it) + return nullptr; + ++scopeIt; + } + + // using namespace.. + if (nsToken) { + while (Token::Match(nsToken, "%name% ::")) { + if (scopeIt != scopeInfo.end() && nsToken->str() == scopeIt->name) { + nsToken = nsToken->tokAt(2); + ++scopeIt; + } else { + return nullptr; + } + } + if (!Token::Match(nsToken, "%name% ;")) + return nullptr; + if (scopeIt == scopeInfo.end() || nsToken->str() != scopeIt->name) + return nullptr; + ++scopeIt; + } + + // Parse member tokens.. + while (scopeIt != scopeInfo.end()) { + if (!Token::Match(memberToken, "%name% ::|<")) + return nullptr; + if (memberToken->str() != scopeIt->name) + return nullptr; + if (memberToken->next()->str() == "<") { + memberToken = memberToken->next()->findClosingBracket(); + if (!Token::simpleMatch(memberToken, "> ::")) + return nullptr; + } + memberToken = memberToken->tokAt(2); + ++scopeIt; + } + + return Token::Match(memberToken, "~| %name%") ? memberToken : nullptr; +} + +static Token * matchMemberName(const Member &member, const std::list &scopeInfo) +{ + if (scopeInfo.empty()) + return nullptr; + + // Does this member match without "using namespace".. + Token *ret = matchMemberName(member.scope, nullptr, member.tok, scopeInfo); + if (ret) + return ret; + + // Try to match member using the "using namespace ..." namespaces.. + for (const Token *ns : member.usingnamespaces) { + ret = matchMemberName(member.scope, ns, member.tok, scopeInfo); + if (ret) + return ret; + } + + return nullptr; +} + +static Token * matchMemberVarName(const Member &var, const std::list &scopeInfo) +{ + Token *tok = matchMemberName(var, scopeInfo); + if (Token::Match(tok, "%name%")) { + if (!tok->next() || tok->strAt(1) != "(" || (tok->tokAt(2) && tok->tokAt(2)->isLiteral())) + return tok; + } + return nullptr; +} + +static Token * matchMemberFunctionName(const Member &func, const std::list &scopeInfo) +{ + Token *tok = matchMemberName(func, scopeInfo); + return Token::Match(tok, "~| %name% (") ? tok : nullptr; +} + +template +static T* skipInitializerList(T* tok) +{ + T* const start = tok; + while (Token::Match(tok, "[:,] ::| %name%")) { + tok = tok->tokAt(tok->strAt(1) == "::" ? 1 : 2); + while (Token::Match(tok, ":: %name%")) + tok = tok->tokAt(2); + if (!Token::Match(tok, "[({<]") || !tok->link()) + return start; + const bool isTemplate = tok->str() == "<"; + tok = tok->link()->next(); + if (isTemplate && tok && tok->link()) + tok = tok->link()->next(); + } + return tok; +} + +void Tokenizer::setVarIdPass2() +{ + std::map> structMembers; + + // Member functions and variables in this source + std::list allMemberFunctions; + std::list allMemberVars; + if (!isC()) { + std::map endOfScope; + std::list scope; + std::list usingnamespaces; + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!tok->previous() || Token::Match(tok->previous(), "[;{}]")) { + if (Token::Match(tok, "using namespace %name% ::|;")) { + Token *endtok = tok->tokAt(2); + while (Token::Match(endtok, "%name% ::")) + endtok = endtok->tokAt(2); + if (Token::Match(endtok, "%name% ;")) + usingnamespaces.push_back(tok->tokAt(2)); + tok = endtok; + continue; + } + if (Token::Match(tok, "namespace %name% {")) { + scope.push_back(tok->strAt(1)); + endOfScope[tok->linkAt(2)] = tok->strAt(1); + } + } + + if (tok->str() == "}") { + const std::map::iterator it = endOfScope.find(tok); + if (it != endOfScope.end()) + scope.remove(it->second); + } + + Token* const tok1 = tok; + if (Token::Match(tok, "%name% :: ~| %name%")) + tok = tok->next(); + else if (Token::Match(tok, "%name% <") && Token::Match(tok->next()->findClosingBracket(),"> :: ~| %name%")) + tok = tok->next()->findClosingBracket()->next(); + else if (usingnamespaces.empty() || tok->varId() || !tok->isName() || tok->isStandardType() || tok->tokType() == Token::eKeyword || tok->tokType() == Token::eBoolean || + Token::Match(tok->previous(), ".|namespace|class|struct|&|&&|*|> %name%") || Token::Match(tok->previous(), "%type%| %name% ( %type%|)") || Token::Match(tok, "public:|private:|protected:") || + (!tok->next() && Token::Match(tok->previous(), "}|; %name%"))) + continue; + + if (tok->strAt(-1) == "::" && tok->tokAt(-2) && tok->tokAt(-2)->isName()) + continue; + + while (Token::Match(tok, ":: ~| %name%")) { + tok = tok->next(); + if (tok->str() == "~") + tok = tok->next(); + else if (Token::Match(tok, "%name% <") && Token::Match(tok->next()->findClosingBracket(),"> :: ~| %name%")) + tok = tok->next()->findClosingBracket()->next(); + else if (Token::Match(tok, "%name% ::")) + tok = tok->next(); + else + break; + } + if (!tok->next()) + syntaxError(tok); + if (Token::Match(tok, "%name% (") && !(tok->tokAt(2) && tok->tokAt(2)->isLiteral())) + allMemberFunctions.emplace_back(scope, usingnamespaces, tok1); + else + allMemberVars.emplace_back(scope, usingnamespaces, tok1); + } + } + + std::list scopeInfo; + + // class members.. + std::map> varsByClass; + for (Token *tok = list.front(); tok; tok = tok->next()) { + while (tok->str() == "}" && !scopeInfo.empty() && tok == scopeInfo.back().bodyEnd) + scopeInfo.pop_back(); + + if (!Token::Match(tok, "namespace|class|struct %name% {|:|::|<")) + continue; + + const std::string &scopeName(getScopeName(scopeInfo)); + const std::string scopeName2(scopeName.empty() ? std::string() : (scopeName + " :: ")); + + std::list classnameTokens{ tok->next() }; + Token* tokStart = tok->tokAt(2); + while (Token::Match(tokStart, ":: %name%") || tokStart->str() == "<") { + if (tokStart->str() == "<") { + // skip the template part + Token* closeTok = tokStart->findClosingBracket(); + if (!closeTok) + syntaxError(tok); + tokStart = closeTok->next(); + } else { + classnameTokens.push_back(tokStart->next()); + tokStart = tokStart->tokAt(2); + } + } + + std::string classname; + for (const Token *it : classnameTokens) + classname += (classname.empty() ? "" : " :: ") + it->str(); + + std::map &thisClassVars = varsByClass[scopeName2 + classname]; + while (Token::Match(tokStart, ":|::|,|%name%")) { + if (Token::Match(tokStart, "%name% <")) { // TODO: why skip templates? + tokStart = tokStart->next()->findClosingBracket(); + if (tokStart) + tokStart = tokStart->next(); + continue; + } + if (Token::Match(tokStart, "%name% ,|{")) { + std::string baseClassName = tokStart->str(); + const Token* baseStart = tokStart; + while (Token::Match(baseStart->tokAt(-2), "%name% ::")) { // build base class name + baseClassName.insert(0, baseStart->strAt(-2) + " :: "); + baseStart = baseStart->tokAt(-2); + } + std::string scopeName3(scopeName2); + while (!scopeName3.empty()) { + const std::string name = scopeName3 + baseClassName; + if (varsByClass.find(name) != varsByClass.end()) { + baseClassName = name; + break; + } + // Remove last scope name + if (scopeName3.size() <= 8) + break; + scopeName3.erase(scopeName3.size() - 4); + const std::string::size_type pos = scopeName3.rfind(" :: "); + if (pos == std::string::npos) + break; + scopeName3.erase(pos + 4); + } + const std::map& baseClassVars = varsByClass[baseClassName]; + thisClassVars.insert(baseClassVars.cbegin(), baseClassVars.cend()); + } + tokStart = tokStart->next(); + } + if (!Token::simpleMatch(tokStart, "{")) + continue; + + // What member variables are there in this class? + std::transform(classnameTokens.cbegin(), classnameTokens.cend(), std::back_inserter(scopeInfo), [&](const Token* tok) { + return ScopeInfo2(tok->str(), tokStart->link()); + }); + + for (Token *tok2 = tokStart->next(); tok2 && tok2 != tokStart->link(); tok2 = tok2->next()) { + // skip parentheses.. + if (tok2->link()) { + if (tok2->str() == "(") { + Token *funcstart = const_cast(isFunctionHead(tok2, "{")); + if (funcstart) { + setVarIdClassFunction(scopeName2 + classname, funcstart, funcstart->link(), thisClassVars, structMembers, mVarId); + tok2 = funcstart->link(); + continue; + } + } + if (tok2->str() == "{" && !Token::simpleMatch(tok2->previous(), "union")) { + if (tok2->strAt(-1) == ")") + setVarIdClassFunction(scopeName2 + classname, tok2, tok2->link(), thisClassVars, structMembers, mVarId); + tok2 = tok2->link(); + } else if (Token::Match(tok2, "( %name%|)") && !Token::Match(tok2->link(), "(|[")) { + tok2 = tok2->link(); + + // Skip initialization list + if (Token::simpleMatch(tok2, ") :")) + tok2 = skipInitializerList(tok2->next()); + } + } + + // Found a member variable.. + else if (tok2->varId() > 0) + thisClassVars[tok2->str()] = tok2->varId(); + } + + // Are there any member variables in this class? + if (thisClassVars.empty()) + continue; + + // Member variables + for (const Member &var : allMemberVars) { + Token *tok2 = matchMemberVarName(var, scopeInfo); + if (!tok2) + continue; + if (tok2->varId() == 0) + tok2->varId(thisClassVars[tok2->str()]); + } + + if (isC() || tok->str() == "namespace") + continue; + + // Set variable ids in member functions for this class.. + for (const Member &func : allMemberFunctions) { + Token *tok2 = matchMemberFunctionName(func, scopeInfo); + if (!tok2) + continue; + + if (tok2->str() == "~") + tok2 = tok2->linkAt(2); + else + tok2 = tok2->linkAt(1); + + // If this is a function implementation.. add it to funclist + Token * start = const_cast(isFunctionHead(tok2, "{")); + if (start) { + setVarIdClassFunction(classname, start, start->link(), thisClassVars, structMembers, mVarId); + } + + if (Token::Match(tok2, ") %name% (")) + tok2 = tok2->linkAt(2); + + // constructor with initializer list + if (!Token::Match(tok2, ") : ::| %name%")) + continue; + + Token *tok3 = tok2; + while (Token::Match(tok3, "[)}] [,:]")) { + tok3 = tok3->tokAt(2); + if (Token::Match(tok3, ":: %name%")) + tok3 = tok3->next(); + while (Token::Match(tok3, "%name% :: %name%")) + tok3 = tok3->tokAt(2); + if (!Token::Match(tok3, "%name% (|{|<")) + break; + + // set varid + const std::map::const_iterator varpos = thisClassVars.find(tok3->str()); + if (varpos != thisClassVars.end()) + tok3->varId(varpos->second); + + // goto end of var + if (tok3->strAt(1) == "<") { + tok3 = tok3->next()->findClosingBracket(); + if (tok3 && tok3->next() && tok3->next()->link()) + tok3 = tok3->next()->link(); + } else + tok3 = tok3->linkAt(1); + } + if (Token::Match(tok3, ")|} {")) { + setVarIdClassFunction(classname, tok2, tok3->next()->link(), thisClassVars, structMembers, mVarId); + } + } + } +} + +static void linkBrackets(const Tokenizer & tokenizer, std::stack& type, std::stack& links, Token * const token, const char open, const char close) +{ + if (token->str()[0] == open) { + links.push(token); + type.push(token); + } else if (token->str()[0] == close) { + if (links.empty()) { + // Error, { and } don't match. + tokenizer.unmatchedToken(token); + } + if (type.top()->str()[0] != open) { + tokenizer.unmatchedToken(type.top()); + } + type.pop(); + + Token::createMutualLinks(links.top(), token); + links.pop(); + } +} + +void Tokenizer::createLinks() +{ + std::stack type; + std::stack links1; + std::stack links2; + std::stack links3; + for (Token *token = list.front(); token; token = token->next()) { + if (token->link()) { + token->link(nullptr); + } + + linkBrackets(*this, type, links1, token, '{', '}'); + + linkBrackets(*this, type, links2, token, '(', ')'); + + linkBrackets(*this, type, links3, token, '[', ']'); + } + + if (!links1.empty()) { + // Error, { and } don't match. + unmatchedToken(links1.top()); + } + + if (!links2.empty()) { + // Error, ( and ) don't match. + unmatchedToken(links2.top()); + } + + if (!links3.empty()) { + // Error, [ and ] don't match. + unmatchedToken(links3.top()); + } +} + +void Tokenizer::createLinks2() +{ + if (isC()) + return; + + bool isStruct = false; + + std::stack type; + std::stack templateTokens; + for (Token *token = list.front(); token; token = token->next()) { + if (Token::Match(token, "%name%|> %name% [:<]")) + isStruct = true; + else if (Token::Match(token, "[;{}]")) + isStruct = false; + + if (token->link()) { + if (Token::Match(token, "{|[|(")) + type.push(token); + else if (!type.empty() && Token::Match(token, "}|]|)")) { + while (type.top()->str() == "<") { + if (!templateTokens.empty() && templateTokens.top()->next() == type.top()) + templateTokens.pop(); + type.pop(); + } + type.pop(); + } + } else if (templateTokens.empty() && !isStruct && Token::Match(token, "%oror%|&&|;")) { + if (Token::Match(token, "&& [,>]")) + continue; + // If there is some such code: A.. + // Then this is probably a template instantiation if either "B" or "C" has comparisons + if (token->tokType() == Token::eLogicalOp && !type.empty() && type.top()->str() == "<") { + const Token *prev = token->previous(); + bool foundComparison = false; + while (Token::Match(prev, "%name%|%num%|%str%|%cop%|)|]") && prev != type.top()) { + if (prev->str() == ")" || prev->str() == "]") + prev = prev->link(); + else if (prev->tokType() == Token::eLogicalOp) + break; + else if (prev->isComparisonOp()) + foundComparison = true; + prev = prev->previous(); + } + if (prev == type.top() && foundComparison) + continue; + const Token *next = token->next(); + foundComparison = false; + while (Token::Match(next, "%name%|%num%|%str%|%cop%|(|[") && next->str() != ">") { + if (next->str() == "(" || next->str() == "[") + next = next->link(); + else if (next->tokType() == Token::eLogicalOp) + break; + else if (next->isComparisonOp()) + foundComparison = true; + next = next->next(); + } + if (next && next->str() == ">" && foundComparison) + continue; + } + + while (!type.empty() && type.top()->str() == "<") { + const Token* end = type.top()->findClosingBracket(); + if (Token::Match(end, "> %comp%|;|.|=|{|(|::")) + break; + // Variable declaration + if (Token::Match(end, "> %var% ;") && (type.top()->tokAt(-2) == nullptr || Token::Match(type.top()->tokAt(-2), ";|}|{"))) + break; + type.pop(); + } + } else if (token->str() == "<" && + ((token->previous() && (token->previous()->isTemplate() || + (token->previous()->isName() && !token->previous()->varId()) || + (token->strAt(-1) == "]" && (!Token::Match(token->linkAt(-1)->previous(), "%name%|)") || token->linkAt(-1)->previous()->isKeyword())) || + (token->strAt(-1) == ")" && token->linkAt(-1)->strAt(-1) == "operator"))) || + Token::Match(token->next(), ">|>>"))) { + type.push(token); + if (token->previous()->str() == "template") + templateTokens.push(token); + } else if (token->str() == ">" || token->str() == ">>") { + if (type.empty() || type.top()->str() != "<") // < and > don't match. + continue; + Token * const top1 = type.top(); + type.pop(); + Token * const top2 = type.empty() ? nullptr : type.top(); + type.push(top1); + if (!top2 || top2->str() != "<") { + if (token->str() == ">>") + continue; + if (!Token::Match(token->next(), "%name%|%cop%|%assign%|::|,|(|)|{|}|;|[|]|:|.|=|?|...") && + !Token::Match(token->next(), "&& %name% =")) + continue; + } + + if (token->str() == ">>" && top1 && top2) { + type.pop(); + type.pop(); + // Split the angle brackets + token->str(">"); + Token::createMutualLinks(top1, token->insertTokenBefore(">")); + Token::createMutualLinks(top2, token); + if (templateTokens.size() == 2 && (top1 == templateTokens.top() || top2 == templateTokens.top())) { + templateTokens.pop(); + templateTokens.pop(); + } + } else { + type.pop(); + if (Token::Match(token, "> %name%") && !token->next()->isKeyword() && + Token::Match(top1->tokAt(-2), "%op% %name% <") && top1->strAt(-2) != "<" && + (templateTokens.empty() || top1 != templateTokens.top())) + continue; + Token::createMutualLinks(top1, token); + if (!templateTokens.empty() && top1 == templateTokens.top()) + templateTokens.pop(); + } + } + } +} + +void Tokenizer::markCppCasts() +{ + if (isC()) + return; + for (Token* tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast")) { + if (!Token::simpleMatch(tok->next(), "<") || !Token::simpleMatch(tok->linkAt(1), "> (")) + syntaxError(tok); + tok = tok->linkAt(1)->next(); + tok->isCast(true); + } + } + +} + +void Tokenizer::sizeofAddParentheses() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!Token::Match(tok, "sizeof !!(")) + continue; + if (tok->next()->isLiteral() || Token::Match(tok->next(), "%name%|*|~|!|&")) { + Token *endToken = tok->next(); + while (Token::simpleMatch(endToken, "* *")) + endToken = endToken->next(); + while (Token::Match(endToken->next(), "%name%|%num%|%str%|[|(|.|::|++|--|!|~") || (Token::Match(endToken, "%type% * %op%|?|:|const|;|,"))) { + if (Token::Match(endToken->next(), "(|[")) + endToken = endToken->linkAt(1); + else + endToken = endToken->next(); + } + + // Add ( after sizeof and ) behind endToken + tok->insertToken("("); + endToken->insertToken(")"); + Token::createMutualLinks(tok->next(), endToken->next()); + } + } +} + +bool Tokenizer::simplifyTokenList1(const char FileName[]) +{ + if (Settings::terminated()) + return false; + + // if MACRO + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "if|for|while|BOOST_FOREACH %name% (")) { + if (Token::simpleMatch(tok, "for each")) { + // 'for each ( )' -> 'asm ( )' + tok->str("asm"); + tok->deleteNext(); + } else if (tok->strAt(1) == "constexpr") { + tok->deleteNext(); + tok->isConstexpr(true); + } else { + syntaxError(tok); + } + } + } + + // Is there C++ code in C file? + validateC(); + + // Combine strings and character literals, e.g. L"string", L'c', "string1" "string2" + combineStringAndCharLiterals(); + + // replace inline SQL with "asm()" (Oracle PRO*C). Ticket: #1959 + simplifySQL(); + + createLinks(); + + // Simplify debug intrinsics + simplifyDebug(); + + removePragma(); + + // Simplify the C alternative tokens (and, or, etc.) + simplifyCAlternativeTokens(); + + simplifyFunctionTryCatch(); + + simplifyHeadersAndUnusedTemplates(); + + // Remove __asm.. + simplifyAsm(); + + // foo < bar < >> => foo < bar < > > + if (isCPP() || mSettings.daca) + splitTemplateRightAngleBrackets(!isCPP()); + + // Remove extra "template" tokens that are not used by cppcheck + removeExtraTemplateKeywords(); + + simplifySpaceshipOperator(); + + // @.. + simplifyAt(); + + // Bail out if code is garbage + if (mTimerResults) { + Timer t("Tokenizer::simplifyTokens1::simplifyTokenList1::findGarbageCode", mSettings.showtime, mTimerResults); + findGarbageCode(); + } else { + findGarbageCode(); + } + + checkConfiguration(); + + // if (x) MACRO() .. + for (const Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "if (")) { + tok = tok->next()->link(); + if (Token::Match(tok, ") %name% (") && + tok->next()->isUpperCaseName() && + Token::Match(tok->linkAt(2), ") {|else")) { + syntaxError(tok->next()); + } + } + } + + if (Settings::terminated()) + return false; + + // convert C++17 style nested namespaces to old style namespaces + simplifyNestedNamespace(); + + // convert c++20 coroutines + simplifyCoroutines(); + + // simplify namespace aliases + simplifyNamespaceAliases(); + + // Remove [[attribute]] + simplifyCPPAttribute(); + + // remove __attribute__((?)) + simplifyAttribute(); + + // simplify cppcheck attributes __cppcheck_?__(?) + simplifyCppcheckAttribute(); + + // Combine tokens.. + combineOperators(); + + // combine "- %num%" + concatenateNegativeNumberAndAnyPositive(); + + // remove extern "C" and extern "C" {} + if (isCPP()) + simplifyExternC(); + + // simplify weird but legal code: "[;{}] ( { code; } ) ;"->"[;{}] code;" + simplifyRoundCurlyParentheses(); + + // check for simple syntax errors.. + for (const Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "> struct {") && + Token::simpleMatch(tok->linkAt(2), "} ;")) { + syntaxError(tok); + } + } + + if (!simplifyAddBraces()) + return false; + + sizeofAddParentheses(); + + // Simplify: 0[foo] -> *(foo) + for (Token* tok = list.front(); tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "0 [") && tok->linkAt(1)) { + tok->str("*"); + tok->next()->str("("); + tok->linkAt(1)->str(")"); + } + } + + if (Settings::terminated()) + return false; + + // Remove __declspec() + simplifyDeclspec(); + validate(); + + // Remove "inline", "register", and "restrict" + simplifyKeyword(); + + // simplify simple calculations inside <..> + if (isCPP()) { + Token *lt = nullptr; + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "[;{}]")) + lt = nullptr; + else if (Token::Match(tok, "%type% <")) + lt = tok->next(); + else if (lt && Token::Match(tok, ">|>> %name%|::|(")) { + const Token * const end = tok; + for (tok = lt; tok != end; tok = tok->next()) { + if (tok->isNumber()) + TemplateSimplifier::simplifyNumericCalculations(tok); + } + lt = tok->next(); + } + } + } + + // Convert K&R function declarations to modern C + simplifyVarDecl(true); + simplifyFunctionParameters(); + + // simplify case ranges (gcc extension) + simplifyCaseRange(); + + // simplify labels and 'case|default'-like syntaxes + simplifyLabelsCaseDefault(); + + if (!isC() && !mSettings.library.markupFile(FileName)) { + findComplicatedSyntaxErrorsInTemplates(); + } + + if (Settings::terminated()) + return false; + + // remove calling conventions __cdecl, __stdcall.. + simplifyCallingConvention(); + + addSemicolonAfterUnknownMacro(); + + // remove some unhandled macros in global scope + removeMacrosInGlobalScope(); + + // remove undefined macro in class definition: + // class DLLEXPORT Fred { }; + // class Fred FINAL : Base { }; + removeMacroInClassDef(); + + // That call here fixes #7190 + validate(); + + // remove unnecessary member qualification.. + removeUnnecessaryQualification(); + + // convert Microsoft memory functions + simplifyMicrosoftMemoryFunctions(); + + // convert Microsoft string functions + simplifyMicrosoftStringFunctions(); + + if (Settings::terminated()) + return false; + + // remove Borland stuff.. + simplifyBorland(); + + // syntax error: enum with typedef in it + checkForEnumsWithTypedef(); + + // Add parentheses to ternary operator where necessary + prepareTernaryOpForAST(); + + // Change initialisation of variable to assignment + simplifyInitVar(); + + // Split up variable declarations. + simplifyVarDecl(false); + + reportUnknownMacros(); + + simplifyTypedefLHS(); + + // typedef.. + if (mTimerResults) { + Timer t("Tokenizer::simplifyTokens1::simplifyTokenList1::simplifyTypedef", mSettings.showtime, mTimerResults); + simplifyTypedef(); + } else { + simplifyTypedef(); + } + + // using A = B; + while (simplifyUsing()) + ; + + // Add parentheses to ternary operator where necessary + // TODO: this is only necessary if one typedef simplification had a comma and was used within ?: + // If typedef handling is refactored and moved to symboldatabase someday we can remove this + prepareTernaryOpForAST(); + + // class x y { + if (isCPP() && mSettings.severity.isEnabled(Severity::information)) { + for (const Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "class %type% %type% [:{]")) { + unhandled_macro_class_x_y(tok); + } + } + } + + // catch bad typedef canonicalization + // + // to reproduce bad typedef, download upx-ucl from: + // http://packages.debian.org/sid/upx-ucl + // analyse the file src/stub/src/i386-linux.elf.interp-main.c + validate(); + + // The simplify enum have inner loops + if (Settings::terminated()) + return false; + + // Put ^{} statements in asm() + simplifyAsm2(); + + // When the assembly code has been cleaned up, no @ is allowed + for (const Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->str() == "(") { + const Token *tok1 = tok; + tok = tok->link(); + if (!tok) + syntaxError(tok1); + } else if (tok->str() == "@") { + syntaxError(tok); + } + } + + // Order keywords "static" and "const" + simplifyStaticConst(); + + // convert platform dependent types to standard types + // 32 bits: size_t -> unsigned long + // 64 bits: size_t -> unsigned long long + list.simplifyPlatformTypes(); + + // collapse compound standard types into a single token + // unsigned long long int => long (with _isUnsigned=true,_isLong=true) + list.simplifyStdType(); + + if (Settings::terminated()) + return false; + + // simplify bit fields.. + simplifyBitfields(); + + if (Settings::terminated()) + return false; + + // struct simplification "struct S {} s; => struct S { } ; S s ; + simplifyStructDecl(); + + if (Settings::terminated()) + return false; + + // x = ({ 123; }); => { x = 123; } + simplifyAssignmentBlock(); + + if (Settings::terminated()) + return false; + + simplifyVariableMultipleAssign(); + + // Collapse operator name tokens into single token + // operator = => operator= + simplifyOperatorName(); + + // Remove redundant parentheses + simplifyRedundantParentheses(); + + if (isCPP()) { + simplifyTypeIntrinsics(); + + // Handle templates.. + if (mTimerResults) { + Timer t("Tokenizer::simplifyTokens1::simplifyTokenList1::simplifyTemplates", mSettings.showtime, mTimerResults); + simplifyTemplates(); + } else { + simplifyTemplates(); + } + + // The simplifyTemplates have inner loops + if (Settings::terminated()) + return false; + + validate(); // #6847 - invalid code + } + + // 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); + + elseif(); + + validate(); // #6772 "segmentation fault (invalid code) in Tokenizer::setVarId" + + if (mTimerResults) { + Timer t("Tokenizer::simplifyTokens1::simplifyTokenList1::setVarId", mSettings.showtime, mTimerResults); + setVarId(); + } else { + setVarId(); + } + + // Link < with > + createLinks2(); + + // Mark C++ casts + markCppCasts(); + + // specify array size + arraySize(); + + // The simplify enum might have inner loops + if (Settings::terminated()) + return false; + + // Add std:: in front of std classes, when using namespace std; was given + simplifyNamespaceStd(); + + // Change initialisation of variable to assignment + simplifyInitVar(); + + simplifyDoublePlusAndDoubleMinus(); + + simplifyArrayAccessSyntax(); + + Token::assignProgressValues(list.front()); + + removeRedundantSemicolons(); + + simplifyParameterVoid(); + + simplifyRedundantConsecutiveBraces(); + + simplifyEmptyNamespaces(); + + simplifyIfSwitchForInit(); + + simplifyOverloadedOperators(); + + validate(); + + list.front()->assignIndexes(); + + return true; +} +//--------------------------------------------------------------------------- + +void Tokenizer::printDebugOutput(int simplification) const +{ + const bool debug = (simplification != 1U && mSettings.debugSimplified) || + (simplification != 2U && mSettings.debugnormal); + + if (debug && list.front()) { + list.front()->printOut(nullptr, list.getFiles()); + + if (mSettings.xml) + std::cout << "" << std::endl; + + if (mSymbolDatabase) { + if (mSettings.xml) + mSymbolDatabase->printXml(std::cout); + else if (mSettings.verbose) { + mSymbolDatabase->printOut("Symbol database"); + } + } + + if (mSettings.verbose) + list.front()->printAst(mSettings.verbose, mSettings.xml, list.getFiles(), std::cout); + + list.front()->printValueFlow(mSettings.xml, std::cout); + + if (mSettings.xml) + std::cout << "" << std::endl; + } + + if (mSymbolDatabase && simplification == 2U && mSettings.debugwarnings) { + printUnknownTypes(); + + // the typeStartToken() should come before typeEndToken() + for (const Variable *var : mSymbolDatabase->variableList()) { + if (!var) + continue; + + const Token * typetok = var->typeStartToken(); + while (typetok && typetok != var->typeEndToken()) + typetok = typetok->next(); + + if (typetok != var->typeEndToken()) { + reportError(var->typeStartToken(), + Severity::debug, + "debug", + "Variable::typeStartToken() of variable '" + var->name() + "' is not located before Variable::typeEndToken(). The location of the typeStartToken() is '" + var->typeStartToken()->str() + "' at line " + std::to_string(var->typeStartToken()->linenr())); + } + } + } +} + +void Tokenizer::dump(std::ostream &out) const +{ + // Create a xml data dump. + // The idea is not that this will be readable for humans. It's a + // data dump that 3rd party tools could load and get useful info from. + + std::string outs; + + std::set containers; + + outs += " "; + outs += '\n'; + for (const Directive &dir : mDirectives) { + outs += " ' which + // could result in invalid XML, so run it through toxml(). + outs += "str=\""; + outs += ErrorLogger::toxml(dir.str); + outs +="\"/>"; + outs += '\n'; + } + outs += " "; + outs += '\n'; + + // tokens.. + outs += " "; + outs += '\n'; + for (const Token *tok = list.front(); tok; tok = tok->next()) { + outs += " linenr()); + outs += "\" column=\""; + outs += std::to_string(tok->column()); + outs += "\""; + + outs += " str=\""; + outs += ErrorLogger::toxml(tok->str()); + outs += '\"'; + + outs += " scope=\""; + outs += id_string(tok->scope()); + outs += '\"'; + if (tok->isName()) { + outs += " type=\"name\""; + if (tok->isUnsigned()) + outs += " isUnsigned=\"true\""; + else if (tok->isSigned()) + outs += " isSigned=\"true\""; + } else if (tok->isNumber()) { + outs += " type=\"number\""; + if (MathLib::isInt(tok->str())) + outs += " isInt=\"true\""; + if (MathLib::isFloat(tok->str())) + outs += " isFloat=\"true\""; + } else if (tok->tokType() == Token::eString) { + outs += " type=\"string\" strlen=\""; + outs += std::to_string(Token::getStrLength(tok)); + outs += '\"'; + } + else if (tok->tokType() == Token::eChar) + outs += " type=\"char\""; + else if (tok->isBoolean()) + outs += " type=\"boolean\""; + else if (tok->isOp()) { + outs += " type=\"op\""; + if (tok->isArithmeticalOp()) + outs += " isArithmeticalOp=\"true\""; + else if (tok->isAssignmentOp()) + outs += " isAssignmentOp=\"true\""; + else if (tok->isComparisonOp()) + outs += " isComparisonOp=\"true\""; + else if (tok->tokType() == Token::eLogicalOp) + outs += " isLogicalOp=\"true\""; + } + if (tok->isCast()) + outs += " isCast=\"true\""; + if (tok->isExternC()) + outs += " externLang=\"C\""; + if (tok->isExpandedMacro()) + outs += " macroName=\"" + tok->getMacroName() + "\""; + if (tok->isTemplateArg()) + outs += " isTemplateArg=\"true\""; + if (tok->isRemovedVoidParameter()) + outs += " isRemovedVoidParameter=\"true\""; + if (tok->isSplittedVarDeclComma()) + outs += " isSplittedVarDeclComma=\"true\""; + if (tok->isSplittedVarDeclEq()) + outs += " isSplittedVarDeclEq=\"true\""; + if (tok->isImplicitInt()) + outs += " isImplicitInt=\"true\""; + if (tok->isComplex()) + outs += " isComplex=\"true\""; + if (tok->isRestrict()) + outs += " isRestrict=\"true\""; + if (tok->isAtomic()) + outs += " isAtomic=\"true\""; + if (tok->isAttributeExport()) + outs += " isAttributeExport=\"true\""; + if (tok->isAttributeMaybeUnused()) + outs += " isAttributeMaybeUnused=\"true\""; + if (tok->isAttributeUnused()) + outs += " isAttributeUnused=\"true\""; + if (tok->link()) { + outs += " link=\""; + outs += id_string(tok->link()); + outs += '\"'; + } + if (tok->varId() > 0) { + outs += " varId=\""; + outs += std::to_string(tok->varId()); + outs += '\"'; + } + if (tok->exprId() > 0) { + outs += " exprId=\""; + outs += std::to_string(tok->exprId()); + outs += '\"'; + } + if (tok->variable()) { + outs += " variable=\""; + outs += id_string(tok->variable()); + outs += '\"'; + } + if (tok->function()) { + outs += " function=\""; + outs += id_string(tok->function()); + outs += '\"'; + } + if (!tok->values().empty()) { + outs += " values=\""; + outs += id_string(&tok->values()); + outs += '\"'; + } + if (tok->type()) { + outs += " type-scope=\""; + outs += id_string(tok->type()->classScope); + outs += '\"'; + } + if (tok->astParent()) { + outs += " astParent=\""; + outs += id_string(tok->astParent()); + outs += '\"'; + } + if (tok->astOperand1()) { + outs += " astOperand1=\""; + outs += id_string(tok->astOperand1()); + outs += '\"'; + } + if (tok->astOperand2()) { + outs += " astOperand2=\""; + outs += id_string(tok->astOperand2()); + outs += '\"'; + } + if (!tok->originalName().empty()) { + outs += " originalName=\""; + outs += tok->originalName(); + outs += '\"'; + } + if (tok->valueType()) { + const std::string vt = tok->valueType()->dump(); + if (!vt.empty()) { + outs += ' '; + outs += vt; + } + containers.insert(tok->valueType()->container); + } + if (!tok->varId() && tok->scope()->isExecutable() && Token::Match(tok, "%name% (")) { + if (mSettings.library.isnoreturn(tok)) + outs += " noreturn=\"true\""; + } + + outs += "/>"; + outs += '\n'; + } + outs += " "; + outs += '\n'; + + out << outs; + outs.clear(); + + if (mSymbolDatabase) + mSymbolDatabase->printXml(out); + + containers.erase(nullptr); + if (!containers.empty()) { + outs += " "; + outs += '\n'; + for (const Library::Container* c: containers) { + outs += " arrayLike_indexOp); + outs += "\" "; + outs += "std-string-like=\""; + outs += bool_to_string(c->stdStringLike); + outs += "\"/>"; + outs += '\n'; + } + outs += " "; + outs += '\n'; + } + + if (list.front()) + list.front()->printValueFlow(true, out); + + if (!mTypedefInfo.empty()) { + outs += " "; + outs += '\n'; + for (const TypedefInfo &typedefInfo: mTypedefInfo) { + outs += " dump(); + + out << outs; +} + +void Tokenizer::simplifyHeadersAndUnusedTemplates() +{ + if (mSettings.checkHeaders && mSettings.checkUnusedTemplates) + // Full analysis. All information in the headers are kept. + return; + + const bool checkHeaders = mSettings.checkHeaders; + const bool removeUnusedIncludedFunctions = !mSettings.checkHeaders; + const bool removeUnusedIncludedClasses = !mSettings.checkHeaders; + const bool removeUnusedIncludedTemplates = !mSettings.checkUnusedTemplates || !mSettings.checkHeaders; + const bool removeUnusedTemplates = !mSettings.checkUnusedTemplates; + + // checkHeaders: + // + // If it is true then keep all code in the headers. It's possible + // to remove unused types/variables if false positives / false + // negatives can be avoided. + // + // If it is false, then we want to remove selected stuff from the + // headers but not *everything*. The intention here is to not damage + // the analysis of the source file. You should get all warnings in + // the source file. You should not get false positives. + + // functions and types to keep + std::set keep; + for (const Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->isCpp() && Token::simpleMatch(tok, "template <")) { + const Token *closingBracket = tok->next()->findClosingBracket(); + if (Token::Match(closingBracket, "> class|struct %name% {")) + tok = closingBracket->linkAt(3); + } + + if (!tok->isName() || tok->isKeyword()) + continue; + + if (!checkHeaders && tok->fileIndex() != 0) + continue; + + if (Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->linkAt(1), ") {")) { + keep.insert(tok->str()); + continue; + } + + if (Token::Match(tok, "%name% %name%|::|*|&|<")) { + keep.insert(tok->str()); + } + } + + const std::set functionStart{"static", "const", "unsigned", "signed", "void", "bool", "char", "short", "int", "long", "float", "*"}; + + for (Token *tok = list.front(); tok; tok = tok->next()) { + const bool isIncluded = (tok->fileIndex() != 0); + + // Remove executable code + if (isIncluded && !mSettings.checkHeaders && tok->str() == "{") { + // TODO: We probably need to keep the executable code if this function is called from the source file. + const Token *prev = tok->previous(); + while (prev && prev->isName()) + prev = prev->previous(); + if (Token::simpleMatch(prev, ")")) { + // Replace all tokens from { to } with a ";". + Token::eraseTokens(tok,tok->link()->next()); + tok->str(";"); + tok->link(nullptr); + } + } + + if (!tok->previous() || Token::Match(tok->previous(), "[;{}]")) { + // Remove unused function declarations + if (isIncluded && removeUnusedIncludedFunctions) { + while (true) { + Token *start = tok; + while (start && functionStart.find(start->str()) != functionStart.end()) + start = start->next(); + if (Token::Match(start, "%name% (") && Token::Match(start->linkAt(1), ") const| ;") && keep.find(start->str()) == keep.end()) { + Token::eraseTokens(tok, start->linkAt(1)->tokAt(2)); + tok->deleteThis(); + } else + break; + } + } + + if (isIncluded && removeUnusedIncludedClasses) { + if (Token::Match(tok, "class|struct %name% [:{]") && keep.find(tok->strAt(1)) == keep.end()) { + // Remove this class/struct + const Token *endToken = tok->tokAt(2); + if (endToken->str() == ":") { + endToken = endToken->next(); + while (Token::Match(endToken, "%name%|,")) + endToken = endToken->next(); + } + if (endToken && endToken->str() == "{" && Token::simpleMatch(endToken->link(), "} ;")) { + Token::eraseTokens(tok, endToken->link()->next()); + tok->deleteThis(); + } + } + } + + if (removeUnusedTemplates || (isIncluded && removeUnusedIncludedTemplates)) { + if (Token::Match(tok, "template < %name%")) { + const Token *closingBracket = tok->next()->findClosingBracket(); + if (Token::Match(closingBracket, "> class|struct %name% [;:{]") && keep.find(closingBracket->strAt(2)) == keep.end()) { + const Token *endToken = closingBracket->tokAt(3); + if (endToken->str() == ":") { + endToken = endToken->next(); + while (Token::Match(endToken, "%name%|,")) + endToken = endToken->next(); + } + if (endToken && endToken->str() == "{") + endToken = endToken->link()->next(); + if (endToken && endToken->str() == ";") { + Token::eraseTokens(tok, endToken); + tok->deleteThis(); + } + } else if (Token::Match(closingBracket, "> %type% %name% (") && Token::simpleMatch(closingBracket->linkAt(3), ") {") && keep.find(closingBracket->strAt(2)) == keep.end()) { + const Token *endToken = closingBracket->linkAt(3)->linkAt(1)->next(); + Token::eraseTokens(tok, endToken); + tok->deleteThis(); + } + } + } + } + } +} + +void Tokenizer::removeExtraTemplateKeywords() +{ + if (isCPP()) { + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "%name%|>|) .|:: template %name%")) { + tok->next()->deleteNext(); + Token* templateName = tok->tokAt(2); + while (Token::Match(templateName, "%name%|::")) { + templateName->isTemplate(true); + templateName = templateName->next(); + } + if (!templateName) + syntaxError(tok); + if (Token::Match(templateName->previous(), "operator %op%|(")) { + templateName->isTemplate(true); + if (templateName->str() == "(" && templateName->link()) + templateName->link()->isTemplate(true); + } + } + } + } +} + +static std::string getExpression(const Token *tok) +{ + std::string line; + for (const Token *prev = tok->previous(); prev && !Token::Match(prev, "[;{}]"); prev = prev->previous()) + line = prev->str() + " " + line; + line += "!!!" + tok->str() + "!!!"; + for (const Token *next = tok->next(); next && !Token::Match(next, "[;{}]"); next = next->next()) + line += " " + next->str(); + return line; +} + +void Tokenizer::splitTemplateRightAngleBrackets(bool check) +{ + std::vector> vars; + + int scopeLevel = 0; + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->str() == "{") + ++scopeLevel; + else if (tok->str() == "}") { + vars.erase(std::remove_if(vars.begin(), vars.end(), [scopeLevel](const std::pair& v) { + return v.second == scopeLevel; + }), vars.end()); + --scopeLevel; + } + if (Token::Match(tok, "[;{}] %type% %type% [;,=]") && tok->next()->isStandardType()) + vars.emplace_back(tok->strAt(2), scopeLevel); + + // Ticket #6181: normalize C++11 template parameter list closing syntax + if (tok->previous() && tok->str() == "<" && TemplateSimplifier::templateParameters(tok) && std::none_of(vars.begin(), vars.end(), [&](const std::pair& v) { + return v.first == tok->previous()->str(); + })) { + Token *endTok = tok->findClosingBracket(); + if (check) { + if (Token::Match(endTok, ">>|>>=")) + reportError(tok, Severity::debug, "dacaWrongSplitTemplateRightAngleBrackets", "bad closing bracket for !!!str() == ">>") { + endTok->str(">"); + endTok->insertToken(">"); + } else if (endTok && endTok->str() == ">>=") { + endTok->str(">"); + endTok->insertToken("="); + endTok->insertToken(">"); + } + } else if (Token::Match(tok, "class|struct|union|=|:|public|protected|private %name% <") && std::none_of(vars.begin(), vars.end(), [&](const std::pair& v) { + return v.first == tok->next()->str(); + })) { + Token *endTok = tok->tokAt(2)->findClosingBracket(); + if (check) { + if (Token::simpleMatch(endTok, ">>")) + reportError(tok, Severity::debug, "dacaWrongSplitTemplateRightAngleBrackets", "bad closing bracket for !!!> ;|{|%type%")) { + endTok->str(">"); + endTok->insertToken(">"); + } + } + } +} + +void Tokenizer::removeMacrosInGlobalScope() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->str() == "(") { + tok = tok->link(); + if (Token::Match(tok, ") %type% {") && + !tok->next()->isStandardType() && + !tok->next()->isKeyword() && + !Token::Match(tok->next(), "override|final") && + tok->next()->isUpperCaseName()) + tok->deleteNext(); + } + + if (Token::Match(tok, "%type%") && tok->isUpperCaseName() && + (!tok->previous() || Token::Match(tok->previous(), "[;{}]") || (tok->previous()->isName() && endsWith(tok->previous()->str(), ':')))) { + const Token *tok2 = tok->next(); + if (tok2 && tok2->str() == "(") + tok2 = tok2->link()->next(); + + // Several unknown macros... + while (Token::Match(tok2, "%type% (") && tok2->isUpperCaseName()) + tok2 = tok2->linkAt(1)->next(); + + if (Token::Match(tok, "%name% (") && Token::Match(tok2, "%name% *|&|::|<| %name%") && + !Token::Match(tok2, "requires|namespace|class|struct|union|private:|protected:|public:")) + unknownMacroError(tok); + + if (Token::Match(tok, "%type% (") && Token::Match(tok2, "%type% (") && !Token::Match(tok2, "noexcept|throw") && isFunctionHead(tok2->next(), ":;{")) + unknownMacroError(tok); + + // remove unknown macros before namespace|class|struct|union + if (Token::Match(tok2, "namespace|class|struct|union")) { + // is there a "{" for? + const Token *tok3 = tok2; + while (tok3 && !Token::Match(tok3,"[;{}()]")) + tok3 = tok3->next(); + if (tok3 && tok3->str() == "{") { + Token::eraseTokens(tok, tok2); + tok->deleteThis(); + } + continue; + } + + // replace unknown macros before foo( + /* + if (Token::Match(tok2, "%type% (") && isFunctionHead(tok2->next(), "{")) { + std::string typeName; + for (const Token* tok3 = tok; tok3 != tok2; tok3 = tok3->next()) + typeName += tok3->str(); + Token::eraseTokens(tok, tok2); + tok->str(typeName); + } + */ + // remove unknown macros before foo::foo( + if (Token::Match(tok2, "%type% :: %type%")) { + const Token *tok3 = tok2; + while (Token::Match(tok3, "%type% :: %type% ::")) + tok3 = tok3->tokAt(2); + if (Token::Match(tok3, "%type% :: %type% (") && tok3->str() == tok3->strAt(2)) { + Token::eraseTokens(tok, tok2); + tok->deleteThis(); + } + continue; + } + } + + // Skip executable scopes + if (tok->str() == "{") { + const Token *prev = tok->previous(); + while (prev && prev->isName()) + prev = prev->previous(); + if (prev && prev->str() == ")") + tok = tok->link(); + } + } +} + +//--------------------------------------------------------------------------- + +void Tokenizer::removePragma() +{ + if (isC() && mSettings.standards.c == Standards::C89) + return; + if (isCPP() && mSettings.standards.cpp == Standards::CPP03) + return; + for (Token *tok = list.front(); tok; tok = tok->next()) { + while (Token::simpleMatch(tok, "_Pragma (")) { + Token::eraseTokens(tok, tok->linkAt(1)->next()); + tok->deleteThis(); + } + } +} + +//--------------------------------------------------------------------------- + +void Tokenizer::removeMacroInClassDef() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!Token::Match(tok, "class|struct %name% %name% final| {|:")) + continue; + + const bool nextIsUppercase = tok->next()->isUpperCaseName(); + const bool afterNextIsUppercase = tok->tokAt(2)->isUpperCaseName(); + if (nextIsUppercase && !afterNextIsUppercase) + tok->deleteNext(); + else if (!nextIsUppercase && afterNextIsUppercase) + tok->next()->deleteNext(); + } +} + +//--------------------------------------------------------------------------- + +void Tokenizer::addSemicolonAfterUnknownMacro() +{ + if (!isCPP()) + return; + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->str() != ")") + continue; + const Token *macro = tok->link() ? tok->link()->previous() : nullptr; + if (!macro || !macro->isName()) + continue; + if (Token::simpleMatch(tok, ") try") && !Token::Match(macro, "if|for|while")) + tok->insertToken(";"); + else if (Token::simpleMatch(tok, ") using")) + tok->insertToken(";"); + } +} +//--------------------------------------------------------------------------- + +void Tokenizer::simplifyEmptyNamespaces() +{ + if (isC()) + return; + + bool goback = false; + for (Token *tok = list.front(); tok; tok = tok ? tok->next() : nullptr) { + if (goback) { + tok = tok->previous(); + goback = false; + } + if (Token::Match(tok, "(|[|{")) { + tok = tok->link(); + continue; + } + if (!Token::Match(tok, "namespace %name%| {")) + continue; + const bool isAnonymousNS = tok->strAt(1) == "{"; + if (tok->strAt(3 - isAnonymousNS) == "}") { + tok->deleteNext(3 - isAnonymousNS); // remove '%name%| { }' + if (!tok->previous()) { + // remove 'namespace' or replace it with ';' if isolated + tok->deleteThis(); + goback = true; + } else { // '%any% namespace %any%' + tok = tok->previous(); // goto previous token + tok->deleteNext(); // remove next token: 'namespace' + if (tok->str() == "{") { + // Go back in case we were within a namespace that's empty now + tok = tok->tokAt(-2) ? tok->tokAt(-2) : tok->previous(); + goback = true; + } + } + } else { + tok = tok->tokAt(2 - isAnonymousNS); + } + } +} + +void Tokenizer::removeRedundantSemicolons() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->link() && tok->str() == "(") { + tok = tok->link(); + continue; + } + for (;;) { + if (Token::simpleMatch(tok, "; ;")) { + tok->deleteNext(); + } else if (Token::simpleMatch(tok, "; { ; }")) { + tok->deleteNext(3); + } else { + break; + } + } + } +} + + +bool Tokenizer::simplifyAddBraces() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + Token const * tokRet=simplifyAddBracesToCommand(tok); + if (!tokRet) + return false; + } + return true; +} + +Token *Tokenizer::simplifyAddBracesToCommand(Token *tok) +{ + Token * tokEnd=tok; + if (Token::Match(tok,"for|switch|BOOST_FOREACH")) { + tokEnd=simplifyAddBracesPair(tok,true); + } else if (tok->str()=="while") { + Token *tokPossibleDo=tok->previous(); + if (Token::simpleMatch(tok->previous(), "{")) + tokPossibleDo = nullptr; + else if (Token::simpleMatch(tokPossibleDo,"}")) + tokPossibleDo = tokPossibleDo->link(); + if (!tokPossibleDo || tokPossibleDo->strAt(-1) != "do") + tokEnd=simplifyAddBracesPair(tok,true); + } else if (tok->str()=="do") { + tokEnd=simplifyAddBracesPair(tok,false); + if (tokEnd!=tok) { + // walk on to next token, i.e. "while" + // such that simplifyAddBracesPair does not close other braces + // before the "while" + if (tokEnd) { + tokEnd=tokEnd->next(); + if (!tokEnd || tokEnd->str()!="while") // no while + syntaxError(tok); + } + } + } else if (tok->str()=="if" && !Token::simpleMatch(tok->tokAt(-2), "operator \"\"")) { + tokEnd=simplifyAddBracesPair(tok,true); + if (!tokEnd) + return nullptr; + if (tokEnd->strAt(1) == "else") { + Token * tokEndNextNext= tokEnd->tokAt(2); + if (!tokEndNextNext || tokEndNextNext->str() == "}") + syntaxError(tokEndNextNext); + if (tokEndNextNext->str() == "if") + // do not change "else if ..." to "else { if ... }" + tokEnd=simplifyAddBracesToCommand(tokEndNextNext); + else + tokEnd=simplifyAddBracesPair(tokEnd->next(),false); + } + } + + return tokEnd; +} + +Token *Tokenizer::simplifyAddBracesPair(Token *tok, bool commandWithCondition) +{ + Token * tokCondition=tok->next(); + if (!tokCondition) // Missing condition + return tok; + + Token *tokAfterCondition=tokCondition; + if (commandWithCondition) { + if (tokCondition->str()=="(") + tokAfterCondition=tokCondition->link(); + else + syntaxError(tok); // Bad condition + + if (!tokAfterCondition || tokAfterCondition->strAt(1) == "]") + syntaxError(tok); // Bad condition + + tokAfterCondition=tokAfterCondition->next(); + if (!tokAfterCondition || Token::Match(tokAfterCondition, ")|}|,")) { + // No tokens left where to add braces around + return tok; + } + } + // Skip labels + Token * tokStatement = tokAfterCondition; + while (true) { + if (Token::Match(tokStatement, "%name% :")) + tokStatement = tokStatement->tokAt(2); + else if (tokStatement->str() == "case") { + tokStatement = skipCaseLabel(tokStatement); + if (!tokStatement) + return tok; + if (tokStatement->str() != ":") + syntaxError(tokStatement); + tokStatement = tokStatement->next(); + } else + break; + if (!tokStatement) + return tok; + } + Token * tokBracesEnd=nullptr; + if (tokStatement->str() == "{") { + // already surrounded by braces + if (tokStatement != tokAfterCondition) { + // Move the opening brace before labels + Token::move(tokStatement, tokStatement, tokAfterCondition->previous()); + } + tokBracesEnd = tokStatement->link(); + } else if (Token::simpleMatch(tokStatement, "try {") && + Token::simpleMatch(tokStatement->linkAt(1), "} catch (")) { + tokAfterCondition->previous()->insertToken("{"); + Token * tokOpenBrace = tokAfterCondition->previous(); + Token * tokEnd = tokStatement->linkAt(1)->linkAt(2)->linkAt(1); + if (!tokEnd) { + syntaxError(tokStatement); + } + tokEnd->insertToken("}"); + Token * tokCloseBrace = tokEnd->next(); + + Token::createMutualLinks(tokOpenBrace, tokCloseBrace); + tokBracesEnd = tokCloseBrace; + } else { + Token * tokEnd = simplifyAddBracesToCommand(tokStatement); + if (!tokEnd) // Ticket #4887 + return tok; + if (tokEnd->str()!="}") { + // Token does not end with brace + // Look for ; to add own closing brace after it + while (tokEnd && !Token::Match(tokEnd, ";|)|}")) { + if (tokEnd->tokType()==Token::eBracket || tokEnd->str() == "(") { + tokEnd = tokEnd->link(); + if (!tokEnd) { + // Inner bracket does not close + return tok; + } + } + tokEnd=tokEnd->next(); + } + if (!tokEnd || tokEnd->str() != ";") { + // No trailing ; + if (tokStatement->isUpperCaseName()) + unknownMacroError(tokStatement); + else + syntaxError(tokStatement); + } + } + + tokAfterCondition->previous()->insertToken("{"); + Token * tokOpenBrace=tokAfterCondition->previous(); + + tokEnd->insertToken("}"); + Token * tokCloseBrace=tokEnd->next(); + + Token::createMutualLinks(tokOpenBrace,tokCloseBrace); + tokBracesEnd=tokCloseBrace; + } + + return tokBracesEnd; +} + +void Tokenizer::simplifyFunctionParameters() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->link() && Token::Match(tok, "{|[|(")) { + tok = tok->link(); + } + + // Find the function e.g. foo( x ) or foo( x, y ) + else if (Token::Match(tok, "%name% ( %name% [,)]") && + !(tok->strAt(-1) == ":" || tok->strAt(-1) == "," || tok->strAt(-1) == "::")) { + // We have found old style function, now we need to change it + + // First step: Get list of argument names in parentheses + std::map argumentNames; + bool bailOut = false; + Token * tokparam = nullptr; + + //take count of the function name.. + const std::string& funcName(tok->str()); + + //floating token used to check for parameters + Token *tok1 = tok; + + while (nullptr != (tok1 = tok1->tokAt(2))) { + if (!Token::Match(tok1, "%name% [,)]")) { + bailOut = true; + break; + } + + //same parameters: take note of the parameter + if (argumentNames.find(tok1->str()) != argumentNames.end()) + tokparam = tok1; + else if (tok1->str() != funcName) + argumentNames[tok1->str()] = tok1; + else { + if (tok1->next()->str() == ")") { + if (tok1->previous()->str() == ",") { + tok1 = tok1->tokAt(-2); + tok1->deleteNext(2); + } else { + tok1 = tok1->previous(); + tok1->deleteNext(); + bailOut = true; + break; + } + } else { + tok1 = tok1->tokAt(-2); + tok1->next()->deleteNext(2); + } + } + + if (tok1->next()->str() == ")") { + tok1 = tok1->tokAt(2); + //expect at least a type name after round brace.. + if (!tok1 || !tok1->isName()) + bailOut = true; + break; + } + } + + //goto '(' + tok = tok->next(); + + if (bailOut) { + tok = tok->link(); + continue; + } + + tok1 = tok->link()->next(); + + // there should be the sequence '; {' after the round parentheses + for (const Token* tok2 = tok1; tok2; tok2 = tok2->next()) { + if (Token::simpleMatch(tok2, "; {")) + break; + if (tok2->str() == "{") { + bailOut = true; + break; + } + } + + if (bailOut) { + tok = tok->link(); + continue; + } + + // Last step: check out if the declarations between ')' and '{' match the parameters list + std::map argumentNames2; + + while (tok1 && tok1->str() != "{") { + if (Token::Match(tok1, "(|)")) { + bailOut = true; + break; + } + if (tok1->str() == ";") { + if (tokparam) { + syntaxError(tokparam); + } + Token *tok2 = tok1->previous(); + while (tok2->str() == "]") + tok2 = tok2->link()->previous(); + + //it should be a name.. + if (!tok2->isName()) { + bailOut = true; + break; + } + + if (argumentNames2.find(tok2->str()) != argumentNames2.end()) { + //same parameter names... + syntaxError(tok1); + } else + argumentNames2[tok2->str()] = tok2; + + if (argumentNames.find(tok2->str()) == argumentNames.end()) { + //non-matching parameter... bailout + bailOut = true; + break; + } + } + tok1 = tok1->next(); + } + + if (bailOut || !tok1) { + tok = tok->link(); + continue; + } + + //the two containers may not hold the same size... + //in that case, the missing parameters are defined as 'int' + if (argumentNames.size() != argumentNames2.size()) { + //move back 'tok1' to the last ';' + tok1 = tok1->previous(); + for (const std::pair& argumentName : argumentNames) { + if (argumentNames2.find(argumentName.first) == argumentNames2.end()) { + //add the missing parameter argument declaration + tok1->insertToken(";"); + tok1->insertToken(argumentName.first); + //register the change inside argumentNames2 + argumentNames2[argumentName.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(); + + //fix up line number + if (tok->str() == ",") + tok->linenr(tok->previous()->linenr()); + } + //goto forward and continue + tok = tok->next()->link(); + } + } +} + +void Tokenizer::simplifyPointerToStandardType() +{ + if (!isC()) + return; + + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!Token::Match(tok, "& %name% [ 0 ] !![")) + continue; + + if (!Token::Match(tok->previous(), "[,(=]")) + continue; + + // Remove '[ 0 ]' suffix + Token::eraseTokens(tok->next(), tok->tokAt(5)); + // Remove '&' prefix + tok = tok->previous(); + if (!tok) + break; + tok->deleteNext(); + } +} + +void Tokenizer::simplifyFunctionPointers() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + // #2873 - do not simplify function pointer usage here: + // (void)(xy(*p)(0)); + if (Token::simpleMatch(tok, ") (")) { + tok = tok->next()->link(); + continue; + } + + // check for function pointer cast + if (Token::Match(tok, "( %type% %type%| *| *| ( * ) (") || + Token::Match(tok, "static_cast < %type% %type%| *| *| ( * ) (")) { + Token *tok1 = tok; + + if (tok1->isCpp() && tok1->str() == "static_cast") + tok1 = tok1->next(); + + tok1 = tok1->next(); + + if (Token::Match(tok1->next(), "%type%")) + tok1 = tok1->next(); + + while (tok1->next()->str() == "*") + tok1 = tok1->next(); + + // check that the cast ends + if (!Token::Match(tok1->linkAt(4), ") )|>")) + continue; + + // ok simplify this function pointer cast to an ordinary pointer cast + tok1->deleteNext(); + tok1->next()->deleteNext(); + Token::eraseTokens(tok1->next(), tok1->linkAt(2)->next()); + continue; + } + + // check for start of statement + if (tok->previous() && !Token::Match(tok->previous(), "{|}|;|,|(|public:|protected:|private:")) + continue; + + if (Token::Match(tok, "delete|else|return|throw|typedef")) + continue; + + while (Token::Match(tok, "%type%|:: %type%|::")) + tok = tok->next(); + + Token *tok2 = (tok && tok->isName()) ? tok->next() : nullptr; + while (Token::Match(tok2, "*|&")) + tok2 = tok2->next(); + if (!tok2 || tok2->str() != "(") + continue; + while (Token::Match(tok2, "(|:: %type%")) + tok2 = tok2->tokAt(2); + if (!Token::Match(tok2, "(|:: * *| %name%")) + continue; + tok2 = tok2->tokAt(2); + if (tok2->str() == "*") + tok2 = tok2->next(); + while (Token::Match(tok2, "%type%|:: %type%|::")) + tok2 = tok2->next(); + + if (!Token::Match(tok2, "%name% ) (") && + !Token::Match(tok2, "%name% [ ] ) (") && + !(Token::Match(tok2, "%name% (") && Token::simpleMatch(tok2->linkAt(1), ") ) ("))) + continue; + + while (tok && tok->str() != "(") + tok = tok->next(); + + // check that the declaration ends + if (!tok || !tok->link() || !tok->link()->next()) { + syntaxError(nullptr); + } + Token *endTok = tok->link()->next()->link(); + if (Token::simpleMatch(endTok, ") throw (")) + endTok = endTok->linkAt(2); + if (!Token::Match(endTok, ") const|volatile| const|volatile| ;|,|)|=|[|{")) + continue; + + while (Token::Match(endTok->next(), "const|volatile")) + endTok->deleteNext(); + + // ok simplify this function pointer to an ordinary pointer + if (Token::simpleMatch(tok->link()->previous(), ") )")) { + // Function returning function pointer + // void (*dostuff(void))(void) {} + Token::eraseTokens(tok->link(), endTok->next()); + tok->link()->deleteThis(); + tok->deleteThis(); + } else { + Token::eraseTokens(tok->link()->linkAt(1), endTok->next()); + + // remove variable names + int indent = 0; + for (Token* tok3 = tok->link()->tokAt(2); Token::Match(tok3, "%name%|*|&|[|(|)|::|,|<"); tok3 = tok3->next()) { + if (tok3->str() == ")" && --indent < 0) + break; + if (tok3->str() == "<" && tok3->link()) + tok3 = tok3->link(); + else if (Token::Match(tok3, "[")) + tok3 = tok3->link(); + else if (tok3->str() == "(") { + tok3 = tok3->link(); + if (Token::simpleMatch(tok3, ") (")) { + tok3 = tok3->next(); + ++indent; + } else + break; + } + if (Token::Match(tok3, "%type%|*|&|> %name% [,)[]")) + tok3->deleteNext(); + } + + // TODO Keep this info + while (Token::Match(tok, "( %type% ::")) + tok->deleteNext(2); + } + } +} + +void Tokenizer::simplifyVarDecl(const bool only_k_r_fpar) +{ + simplifyVarDecl(list.front(), nullptr, only_k_r_fpar); +} + +void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, const bool only_k_r_fpar) +{ + const bool isCPP11 = isCPP() && (mSettings.standards.cpp >= Standards::CPP11); + + // Split up variable declarations.. + // "int a=4;" => "int a; a=4;" + bool finishedwithkr = true; + bool scopeDecl = false; + for (Token *tok = tokBegin; tok != tokEnd; tok = tok->next()) { + if (Token::Match(tok, "{|;")) + scopeDecl = false; + if (isCPP()) { + if (Token::Match(tok, "class|struct|namespace|union")) + scopeDecl = true; + if (Token::Match(tok, "decltype|noexcept (")) { + tok = tok->next()->link(); + // skip decltype(...){...} + if (tok && Token::simpleMatch(tok->previous(), ") {")) + tok = tok->link(); + } else if (Token::simpleMatch(tok, "= {") || + (!scopeDecl && Token::Match(tok, "%name%|> {") && + !Token::Match(tok, "else|try|do|const|constexpr|override|volatile|noexcept"))) { + if (!tok->next()->link()) + syntaxError(tokBegin); + // Check for lambdas before skipping + if (Token::Match(tok->tokAt(-2), ") . %name%")) { // trailing return type + // TODO: support lambda without parameter clause? + Token* lambdaStart = tok->linkAt(-2)->previous(); + if (Token::simpleMatch(lambdaStart, "]")) + lambdaStart = lambdaStart->link(); + Token* lambdaEnd = findLambdaEndScope(lambdaStart); + if (lambdaEnd) + simplifyVarDecl(lambdaEnd->link()->next(), lambdaEnd, only_k_r_fpar); + } else { + for (Token* tok2 = tok->next(); tok2 != tok->next()->link(); tok2 = tok2->next()) { + Token* lambdaEnd = findLambdaEndScope(tok2); + if (!lambdaEnd) + continue; + simplifyVarDecl(lambdaEnd->link()->next(), lambdaEnd, only_k_r_fpar); + } + } + tok = tok->next()->link(); + } + + } else if (Token::simpleMatch(tok, "= {")) { + tok = tok->next()->link(); + } + if (!tok) { + syntaxError(tokBegin); + } + if (only_k_r_fpar && finishedwithkr) { + if (Token::Match(tok, "(|[|{")) { + tok = tok->link(); + if (tok->next() && Token::Match(tok, ") !!{")) + tok = tok->next(); + else + continue; + } else + continue; + } else if (tok->str() == "(") { + if (isCPP()) { + for (Token * tok2 = tok; tok2 && tok2 != tok->link(); tok2 = tok2->next()) { + if (Token::Match(tok2, "[(,] [")) { + // lambda function at tok2->next() + // find start of lambda body + Token * lambdaBody = tok2; + while (lambdaBody && lambdaBody != tok2->link() && lambdaBody->str() != "{") + lambdaBody = lambdaBody->next(); + if (lambdaBody && lambdaBody != tok2->link() && lambdaBody->link()) + simplifyVarDecl(lambdaBody, lambdaBody->link()->next(), only_k_r_fpar); + } + } + } + tok = tok->link(); + } + + if (!tok) + syntaxError(nullptr); // #7043 invalid code + if (tok->previous() && !Token::Match(tok->previous(), "{|}|;|)|public:|protected:|private:")) + continue; + if (Token::simpleMatch(tok, "template <")) + continue; + + Token *type0 = tok; + if (!Token::Match(type0, "::|extern| %type%")) + continue; + if (Token::Match(type0, "else|return|public:|protected:|private:")) + continue; + if (isCPP11 && type0->str() == "using") + continue; + if (type0->isCpp() && Token::Match(type0, "namespace|delete")) + continue; + + bool isconst = false; + bool isstatic = false; + Token *tok2 = type0; + int typelen = 1; + + if (Token::Match(tok2, "::|extern")) { + tok2 = tok2->next(); + typelen++; + } + + //check if variable is declared 'const' or 'static' or both + while (tok2) { + if (!Token::Match(tok2, "const|static|constexpr") && Token::Match(tok2, "%type% const|static")) { + tok2 = tok2->next(); + ++typelen; + } + + if (Token::Match(tok2, "const|constexpr")) + isconst = true; + + else if (Token::Match(tok2, "static|constexpr")) + isstatic = true; + + else if (Token::Match(tok2, "%type% :: %type%")) { + tok2 = tok2->next(); + ++typelen; + } + + else + break; + + if (tok2->strAt(1) == "*") + break; + + if (Token::Match(tok2->next(), "& %name% ,")) + break; + + tok2 = tok2->next(); + ++typelen; + } + + // strange looking variable declaration => don't split up. + if (Token::Match(tok2, "%type% *|&| %name% , %type% *|&| %name%")) + continue; + + if (Token::Match(tok2, "struct|union|class %type%")) { + tok2 = tok2->next(); + ++typelen; + } + + // check for qualification.. + if (Token::Match(tok2, ":: %type%")) { + ++typelen; + tok2 = tok2->next(); + } + + //skip combinations of templates and namespaces + while (!isC() && (Token::Match(tok2, "%type% <") || Token::Match(tok2, "%type% ::"))) { + if (tok2->next()->str() == "<" && !TemplateSimplifier::templateParameters(tok2->next())) { + tok2 = nullptr; + break; + } + typelen += 2; + tok2 = tok2->tokAt(2); + if (tok2 && tok2->previous()->str() == "::") + continue; + int indentlevel = 0; + int parens = 0; + + for (Token *tok3 = tok2; tok3; tok3 = tok3->next()) { + ++typelen; + + if (!parens && tok3->str() == "<") { + ++indentlevel; + } else if (!parens && tok3->str() == ">") { + if (indentlevel == 0) { + tok2 = tok3->next(); + break; + } + --indentlevel; + } else if (!parens && tok3->str() == ">>") { + if (indentlevel <= 1) { + tok2 = tok3->next(); + break; + } + indentlevel -= 2; + } else if (tok3->str() == "(") { + ++parens; + } else if (tok3->str() == ")") { + if (!parens) { + tok2 = nullptr; + break; + } + --parens; + } else if (tok3->str() == ";") { + break; + } + } + + if (Token::Match(tok2, ":: %type%")) { + ++typelen; + tok2 = tok2->next(); + } + + // east const + if (Token::simpleMatch(tok2, "const")) + isconst = true; + } + + //pattern: "%type% *| ... *| const| %name% ,|=" + if (Token::Match(tok2, "%type%") || + (tok2 && tok2->previous() && tok2->previous()->str() == ">")) { + Token *varName = tok2; + if (!tok2->previous() || tok2->previous()->str() != ">") + varName = varName->next(); + else + --typelen; + if (isCPP() && Token::Match(varName, "public:|private:|protected:|using")) + continue; + //skip all the pointer part + bool isPointerOrRef = false; + while (Token::simpleMatch(varName, "*") || Token::Match(varName, "& %name% ,")) { + isPointerOrRef = true; + varName = varName->next(); + } + + while (Token::Match(varName, "%type% %type%")) { + if (varName->str() != "const" && varName->str() != "volatile") { + ++typelen; + } + varName = varName->next(); + } + // Function pointer + if (Token::simpleMatch(varName, "( *") && + Token::Match(varName->link()->previous(), "%name% ) (") && + Token::simpleMatch(varName->link()->linkAt(1), ") =")) { + Token *endDecl = varName->link()->linkAt(1); + varName = varName->link()->previous(); + endDecl->insertToken(";"); + endDecl = endDecl->next(); + endDecl->next()->isSplittedVarDeclEq(true); + endDecl->insertToken(varName->str()); + endDecl->next()->setMacroName(varName->getMacroName()); + continue; + } + //non-VLA case + if (Token::Match(varName, "%name% ,|=")) { + if (varName->str() != "operator") { + tok2 = varName->next(); // The ',' or '=' token + + if (tok2->str() == "=" && (isstatic || (isconst && !isPointerOrRef))) { + //do not split const non-pointer variables.. + while (tok2 && tok2->str() != "," && tok2->str() != ";") { + if (Token::Match(tok2, "{|(|[")) + tok2 = tok2->link(); + const Token *tok3 = tok2; + if (!isC() && tok2->str() == "<" && TemplateSimplifier::templateParameters(tok2) > 0) { + tok2 = tok2->findClosingBracket(); + } + if (!tok2) + syntaxError(tok3); // #6881 invalid code + tok2 = tok2->next(); + } + if (tok2 && tok2->str() == ";") + tok2 = nullptr; + } + } else + tok2 = nullptr; + } + + //VLA case + else if (Token::Match(varName, "%name% [")) { + tok2 = varName->next(); + + while (Token::Match(tok2->link(), "] ,|=|[")) + tok2 = tok2->link()->next(); + if (!Token::Match(tok2, "=|,")) + tok2 = nullptr; + if (tok2 && tok2->str() == "=") { + while (tok2 && tok2->str() != "," && tok2->str() != ";") { + if (Token::Match(tok2, "{|(|[")) + tok2 = tok2->link(); + tok2 = tok2->next(); + } + if (tok2 && tok2->str() == ";") + tok2 = nullptr; + } + } + + // brace initialization + else if (Token::Match(varName, "%name% {")) { + tok2 = varName->next(); + tok2 = tok2->link(); + if (tok2) + tok2 = tok2->next(); + if (tok2 && tok2->str() != ",") + tok2 = nullptr; + } + + // function declaration + else if (Token::Match(varName, "%name% (")) { + Token* commaTok = varName->linkAt(1)->next(); + while (Token::Match(commaTok, "const|noexcept|override|final")) { + commaTok = commaTok->next(); + if (Token::Match(commaTok, "( true|false )")) + commaTok = commaTok->link()->next(); + } + tok2 = Token::simpleMatch(commaTok, ",") ? commaTok : nullptr; + } + + else + tok2 = nullptr; + } else { + tok2 = nullptr; + } + + if (!tok2) { + if (only_k_r_fpar) + finishedwithkr = false; + continue; + } + + if (tok2->str() == ",") { + tok2->str(";"); + tok2->isSplittedVarDeclComma(true); + //TODO: should we have to add also template '<>' links? + TokenList::insertTokens(tok2, type0, typelen); + } + + else { + Token *eq = tok2; + + while (tok2) { + if (Token::Match(tok2, "{|(|[")) + tok2 = tok2->link(); + + else if (!isC() && tok2->str() == "<" && ((tok2->previous()->isName() && !tok2->previous()->varId()) || tok2->strAt(-1) == "]")) + tok2 = tok2->findClosingBracket(); + + else if (std::strchr(";,", tok2->str()[0])) { + // "type var =" => "type var; var =" + const Token *varTok = type0->tokAt(typelen); + while (Token::Match(varTok, "%name%|*|& %name%|*|&")) + varTok = varTok->next(); + if (!varTok) + syntaxError(tok2); // invalid code + TokenList::insertTokens(eq, varTok, 2); + eq->str(";"); + eq->isSplittedVarDeclEq(true); + + // "= x, " => "= x; type " + if (tok2->str() == ",") { + tok2->str(";"); + tok2->isSplittedVarDeclComma(true); + TokenList::insertTokens(tok2, type0, typelen); + } + break; + } + if (tok2) + tok2 = tok2->next(); + } + } + finishedwithkr = (only_k_r_fpar && tok2 && tok2->strAt(1) == "{"); + } +} + +void Tokenizer::simplifyStaticConst() +{ + // This function will simplify the token list so that the qualifiers "extern", "static" + // and "const" appear in the same order as in the array below. + const std::string qualifiers[] = {"extern", "static", "const"}; + + // Move 'const' before all other qualifiers and types and then + // move 'static' before all other qualifiers and types, ... + for (Token *tok = list.front(); tok; tok = tok->next()) { + bool continue2 = false; + for (int i = 0; i < sizeof(qualifiers)/sizeof(qualifiers[0]); i++) { + + // Keep searching for a qualifier + if (!tok->next() || tok->next()->str() != qualifiers[i]) + continue; + + // Look backwards to find the beginning of the declaration + Token* leftTok = tok; + bool behindOther = false; + for (; leftTok; leftTok = leftTok->previous()) { + for (int j = 0; j <= i; j++) { + if (leftTok->str() == qualifiers[j]) { + behindOther = true; + break; + } + } + if (behindOther) + break; + if (isCPP() && Token::simpleMatch(leftTok, ">")) { + Token* opening = leftTok->findOpeningBracket(); + if (opening) { + leftTok = opening; + continue; + } + } + if (!Token::Match(leftTok, "%type%|struct|::") || + (isCPP() && Token::Match(leftTok, "private:|protected:|public:|operator|template"))) { + break; + } + } + + // The token preceding the declaration should indicate the start of a declaration + if (leftTok == tok) + continue; + + if (leftTok && !behindOther && !Token::Match(leftTok, ";|{|}|(|,|private:|protected:|public:")) { + continue2 = true; + break; + } + + // Move the qualifier to the left-most position in the declaration + tok->deleteNext(); + if (!leftTok) { + list.front()->insertToken(qualifiers[i]); + list.front()->swapWithNext(); + tok = list.front(); + } else if (leftTok->next()) { + leftTok->next()->insertTokenBefore(qualifiers[i]); + tok = leftTok->next(); + } else { + leftTok->insertToken(qualifiers[i]); + tok = leftTok; + } + } + if (continue2) + continue; + } +} + +void Tokenizer::simplifyVariableMultipleAssign() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "%name% = %name% = %num%|%name% ;")) { + // skip intermediate assignments + Token *tok2 = tok->previous(); + while (tok2 && + tok2->str() == "=" && + Token::Match(tok2->previous(), "%name%")) { + tok2 = tok2->tokAt(-2); + } + + if (!tok2 || tok2->str() != ";") { + continue; + } + + Token *stopAt = tok->tokAt(2); + const Token *valueTok = stopAt->tokAt(2); + const std::string& value(valueTok->str()); + tok2 = tok2->next(); + + while (tok2 != stopAt) { + tok2->next()->insertToken(";"); + tok2->next()->insertToken(value); + tok2 = tok2->tokAt(4); + } + } + } +} + +// Binary operators simplification map +static const std::unordered_map cAlternativeTokens = { + std::make_pair("and", "&&") + , std::make_pair("and_eq", "&=") + , std::make_pair("bitand", "&") + , std::make_pair("bitor", "|") + , std::make_pair("not_eq", "!=") + , std::make_pair("or", "||") + , std::make_pair("or_eq", "|=") + , std::make_pair("xor", "^") + , std::make_pair("xor_eq", "^=") +}; + +// Simplify the C alternative tokens: +// and => && +// and_eq => &= +// bitand => & +// bitor => | +// compl => ~ +// not => ! +// not_eq => != +// or => || +// or_eq => |= +// xor => ^ +// xor_eq => ^= +bool Tokenizer::simplifyCAlternativeTokens() +{ + /* executable scope level */ + int executableScopeLevel = 0; + + std::vector alt; + bool replaceAll = false; // replace all or none + + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->str() == ")") { + if (const Token *end = isFunctionHead(tok, "{")) { + ++executableScopeLevel; + tok = const_cast(end); + continue; + } + } + + if (tok->str() == "{") { + if (executableScopeLevel > 0) + ++executableScopeLevel; + continue; + } + + if (tok->str() == "}") { + if (executableScopeLevel > 0) + --executableScopeLevel; + continue; + } + + if (!tok->isName()) + continue; + + const std::unordered_map::const_iterator cOpIt = cAlternativeTokens.find(tok->str()); + if (cOpIt != cAlternativeTokens.end()) { + alt.push_back(tok); + + // Is this a variable declaration.. + if (isC() && Token::Match(tok->previous(), "%type%|* %name% [;,=]")) + return false; + + if (!Token::Match(tok->previous(), "%name%|%num%|%char%|)|]|> %name% %name%|%num%|%char%|%op%|(")) + continue; + if (Token::Match(tok->next(), "%assign%|%or%|%oror%|&&|*|/|%|^") && !Token::Match(tok->previous(), "%num%|%char%|) %name% *")) + continue; + if (executableScopeLevel == 0 && Token::Match(tok, "%name% (")) { + const Token *start = tok; + while (Token::Match(start, "%name%|*")) + start = start->previous(); + if (!start || Token::Match(start, "[;}]")) + continue; + } + replaceAll = true; + } else if (Token::Match(tok, "not|compl")) { + alt.push_back(tok); + + if ((Token::Match(tok->previous(), "%assign%") || Token::Match(tok->next(), "%num%")) && !Token::Match(tok->next(), ".|->")) { + replaceAll = true; + continue; + } + + // Don't simplify 'not p;' (in case 'not' is a type) + if (!Token::Match(tok->next(), "%name%|(") || + Token::Match(tok->previous(), "[;{}]") || + (executableScopeLevel == 0U && tok->strAt(-1) == "(")) + continue; + + replaceAll = true; + } + } + + if (!replaceAll) + return false; + + for (Token *tok: alt) { + const std::unordered_map::const_iterator cOpIt = cAlternativeTokens.find(tok->str()); + if (cOpIt != cAlternativeTokens.end()) + tok->str(cOpIt->second); + else if (tok->str() == "not") + tok->str("!"); + else + tok->str("~"); + } + + return !alt.empty(); +} + +// int i(0); => int i; i = 0; +// int i(0), j; => int i; i = 0; int j; +void Tokenizer::simplifyInitVar() +{ + if (isC()) + return; + + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!tok->isName() || (tok->previous() && !Token::Match(tok->previous(), "[;{}]"))) + continue; + + if (tok->str() == "return") + continue; + + if (Token::Match(tok, "class|struct|union| %type% *| %name% ( &| %any% ) ;")) { + tok = initVar(tok); + } else if (Token::Match(tok, "%type% *| %name% ( %type% (")) { + const Token* tok2 = tok->tokAt(2); + if (!tok2->link()) + tok2 = tok2->next(); + if (!tok2->link() || (tok2->link()->strAt(1) == ";" && !Token::simpleMatch(tok2->linkAt(2), ") ("))) + tok = initVar(tok); + } else if (Token::Match(tok, "class|struct|union| %type% *| %name% ( &| %any% ) ,") && tok->str() != "new") { + Token *tok1 = tok->tokAt(5); + while (tok1->str() != ",") + tok1 = tok1->next(); + tok1->str(";"); + + const int numTokens = (Token::Match(tok, "class|struct|union")) ? 2U : 1U; + TokenList::insertTokens(tok1, tok, numTokens); + tok = initVar(tok); + } + } +} + +Token * Tokenizer::initVar(Token * tok) +{ + // call constructor of class => no simplification + if (Token::Match(tok, "class|struct|union")) { + if (tok->strAt(2) != "*") + return tok; + + tok = tok->next(); + } else if (!tok->isStandardType() && tok->str() != "auto" && 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; + 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; +} + +void Tokenizer::elseif() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->str() != "else") + continue; + + if (!Token::Match(tok->previous(), ";|}")) + syntaxError(tok->previous()); + + if (!Token::Match(tok->next(), "%name%")) + continue; + + if (tok->strAt(1) != "if") + unknownMacroError(tok->next()); + + 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; + } + } + } + } +} + + +void Tokenizer::simplifyIfSwitchForInit() +{ + if (!isCPP() || mSettings.standards.cpp < Standards::CPP17) + return; + + const bool forInit = (mSettings.standards.cpp >= Standards::CPP20); + + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!Token::Match(tok, "if|switch|for (")) + continue; + + Token *semicolon = tok->tokAt(2); + while (!Token::Match(semicolon, "[;)]")) { + if (Token::Match(semicolon, "(|{|[") && semicolon->link()) + semicolon = semicolon->link(); + semicolon = semicolon->next(); + } + if (semicolon->str() != ";") + continue; + + if (tok->str() == "for") { + if (!forInit) + continue; + + // Is it a for range.. + const Token *tok2 = semicolon->next(); + bool rangeFor = false; + while (!Token::Match(tok2, "[;)]")) { + if (tok2->str() == "(") + tok2 = tok2->link(); + else if (!rangeFor && tok2->str() == "?") + break; + else if (tok2->str() == ":") + rangeFor = true; + tok2 = tok2->next(); + } + if (!rangeFor || tok2->str() != ")") + continue; + } + + Token *endpar = tok->linkAt(1); + if (!Token::simpleMatch(endpar, ") {")) + continue; + + Token *endscope = endpar->linkAt(1); + if (Token::simpleMatch(endscope, "} else {")) + endscope = endscope->linkAt(2); + + // Simplify, the initialization expression is broken out.. + semicolon->insertToken(tok->str()); + semicolon->next()->insertToken("("); + Token::createMutualLinks(semicolon->next()->next(), endpar); + tok->deleteNext(); + tok->str("{"); + endscope->insertToken("}"); + Token::createMutualLinks(tok, endscope->next()); + tok->isSimplifiedScope(true); + } +} + + +bool Tokenizer::simplifyRedundantParentheses() +{ + bool ret = false; + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->str() != "(") + continue; + + if (tok->isCpp() && Token::simpleMatch(tok->previous(), "} (")) { + const Token* plp = tok->previous()->link()->previous(); + if (Token::Match(plp, "%name%|>|] {") || (Token::simpleMatch(plp, ")") && Token::simpleMatch(plp->link()->previous(), "]"))) + continue; + } + + if (Token::simpleMatch(tok, "( {")) + continue; + + if (Token::Match(tok->link(), ") %num%")) { + tok = tok->link(); + continue; + } + + // Do not simplify if there is comma inside parentheses.. + if (Token::Match(tok->previous(), "%op% (") || Token::Match(tok->link(), ") %op%")) { + bool innerComma = false; + for (const Token *inner = tok->link()->previous(); inner != tok; inner = inner->previous()) { + if (inner->str() == ")") + inner = inner->link(); + if (inner->str() == ",") { + innerComma = true; + break; + } + } + if (innerComma) + continue; + } + + // !!operator = ( x ) ; + if (tok->strAt(-2) != "operator" && + tok->previous() && tok->previous()->str() == "=" && + tok->next() && tok->next()->str() != "{" && + Token::simpleMatch(tok->link(), ") ;")) { + tok->link()->deleteThis(); + tok->deleteThis(); + continue; + } + + while (Token::simpleMatch(tok, "( (") && + tok->link() && tok->link()->previous() == tok->next()->link()) { + // We have "(( *something* ))", remove the inner + // parentheses + tok->deleteNext(); + tok->link()->tokAt(-2)->deleteNext(); + ret = true; + } + + if (isCPP() && Token::Match(tok->tokAt(-2), "[;{}=(] new (") && Token::Match(tok->link(), ") [;,{}[]")) { + // Remove the parentheses in "new (type)" constructs + tok->link()->deleteThis(); + tok->deleteThis(); + ret = true; + } + + if (Token::Match(tok->previous(), "! ( %name% )")) { + // Remove the parentheses + tok->deleteThis(); + tok->deleteNext(); + ret = true; + } + + if (Token::Match(tok->previous(), "[(,;{}] ( %name% ) .")) { + // Remove the parentheses + tok->deleteThis(); + tok->deleteNext(); + ret = true; + } + + if (Token::Match(tok->previous(), "[(,;{}] ( %name% (") && !tok->next()->isKeyword() && + tok->link()->previous() == tok->linkAt(2)) { + // We have "( func ( *something* ))", remove the outer + // parentheses + tok->link()->deleteThis(); + tok->deleteThis(); + ret = true; + } + + if (Token::Match(tok->previous(), "[,;{}] ( delete [| ]| %name% ) ;")) { + // We have "( delete [| ]| var )", remove the outer + // parentheses + tok->link()->deleteThis(); + tok->deleteThis(); + ret = true; + } + + if (!Token::simpleMatch(tok->tokAt(-2), "operator delete") && + Token::Match(tok->previous(), "delete|; (") && + (tok->previous()->str() != "delete" || tok->next()->varId() > 0) && + Token::Match(tok->link(), ") ;|,")) { + tok->link()->deleteThis(); + tok->deleteThis(); + ret = true; + } + + if (Token::Match(tok->previous(), "[(!*;{}] ( %name% )") && + (tok->next()->varId() != 0 || Token::Match(tok->tokAt(3), "[+-/=]")) && !tok->next()->isStandardType()) { + // We have "( var )", remove the parentheses + tok->deleteThis(); + tok->deleteNext(); + ret = true; + } + + while (Token::Match(tok->previous(), "[;{}[(,!*] ( %name% .")) { + Token *tok2 = tok->tokAt(2); + while (Token::Match(tok2, ". %name%")) { + tok2 = tok2->tokAt(2); + } + if (tok2 != tok->link()) + break; + // We have "( var . var . ... . var )", remove the parentheses + tok = tok->previous(); + tok->deleteNext(); + tok2->deleteThis(); + ret = true; + } + + while (Token::Match(tok->previous(), "[{([,] ( !!{") && + Token::Match(tok->link(), ") [;,])]") && + !Token::simpleMatch(tok->tokAt(-2), "operator ,") && // Ticket #5709 + !Token::findsimplematch(tok, ",", tok->link())) { + // We have "( ... )", remove the parentheses + tok->link()->deleteThis(); + tok->deleteThis(); + ret = true; + } + + if (Token::simpleMatch(tok->previous(), ", (") && + Token::simpleMatch(tok->link(), ") =")) { + tok->link()->deleteThis(); + tok->deleteThis(); + ret = true; + } + + // Simplify "!!operator !!%name%|)|]|>|>> ( %num%|%bool% ) %op%|;|,|)" + if (Token::Match(tok, "( %bool%|%num% ) %cop%|;|,|)") && + tok->strAt(-2) != "operator" && + tok->previous() && + !Token::Match(tok->previous(), "%name%|)|]") && + (!(isCPP() && Token::Match(tok->previous(),">|>>")))) { + tok->link()->deleteThis(); + tok->deleteThis(); + ret = true; + } + + if (Token::Match(tok->previous(), "*|& ( %name% )")) { + // We may have a variable declaration looking like "type_name *(var_name)" + Token *tok2 = tok->tokAt(-2); + while (Token::Match(tok2, "%type%|static|const|extern") && tok2->str() != "operator") { + tok2 = tok2->previous(); + } + if (tok2 && !Token::Match(tok2, "[;,{]")) { + // Not a variable declaration + } else { + tok->deleteThis(); + tok->deleteNext(); + } + } + } + return ret; +} + +void Tokenizer::simplifyTypeIntrinsics() +{ + static const std::unordered_map intrinsics = { + { "__has_nothrow_assign", "has_nothrow_assign" }, + { "__has_nothrow_constructor", "has_nothrow_constructor" }, + { "__has_nothrow_copy", "has_nothrow_copy" }, + { "__has_trivial_assign", "has_trivial_assign" }, + { "__has_trivial_constructor", "has_trivial_constructor" }, + { "__has_trivial_copy", "has_trivial_copy" }, + { "__has_trivial_destructor", "has_trivial_destructor" }, + { "__has_virtual_destructor", "has_virtual_destructor" }, + { "__is_abstract", "is_abstract" }, + { "__is_aggregate", "is_aggregate" }, + { "__is_assignable", "is_assignable" }, + { "__is_base_of", "is_base_of" }, + { "__is_class", "is_class" }, + { "__is_constructible", "is_constructible" }, + { "__is_convertible_to", "is_convertible_to" }, + { "__is_destructible", "is_destructible" }, + { "__is_empty", "is_empty" }, + { "__is_enum", "is_enum" }, + { "__is_final", "is_final" }, + { "__is_nothrow_assignable", "is_nothrow_assignable" }, + { "__is_nothrow_constructible", "is_nothrow_constructible" }, + { "__is_nothrow_destructible", "is_nothrow_destructible" }, + { "__is_pod", "is_pod" }, + { "__is_polymorphic", "is_polymorphic" }, + { "__is_trivially_assignable", "is_trivially_assignable" }, + { "__is_trivially_constructible", "is_trivially_constructible" }, + { "__is_union", "is_union" }, + }; + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!Token::Match(tok, "%name% (")) + continue; + auto p = intrinsics.find(tok->str()); + if (p == intrinsics.end()) + continue; + Token * end = tok->next()->link(); + Token * prev = tok->previous(); + tok->str(p->second); + prev->insertToken("::"); + prev->insertToken("std"); + tok->next()->str("<"); + end->str(">"); + end->insertToken("}"); + end->insertToken("{"); + Token::createMutualLinks(end->tokAt(1), end->tokAt(2)); + } +} + +//--------------------------------------------------------------------------- +// Helper functions for handling the tokens list +//--------------------------------------------------------------------------- + +//--------------------------------------------------------------------------- + +bool Tokenizer::isScopeNoReturn(const Token *endScopeToken, bool *unknown) const +{ + std::string unknownFunc; + const bool ret = mSettings.library.isScopeNoReturn(endScopeToken,&unknownFunc); + if (!unknownFunc.empty() && mSettings.summaryReturn.find(unknownFunc) != mSettings.summaryReturn.end()) { + return false; + } + if (unknown) + *unknown = !unknownFunc.empty(); + if (!unknownFunc.empty() && mSettings.checkLibrary) { + bool warn = true; + if (Token::simpleMatch(endScopeToken->tokAt(-2), ") ; }")) { + const Token * const ftok = endScopeToken->linkAt(-2)->previous(); + if (ftok && (ftok->type() || ftok->function() || ftok->variable())) // constructor call + warn = false; + } + + if (warn) { + reportError(endScopeToken->previous(), + Severity::information, + "checkLibraryNoReturn", + "--check-library: Function " + unknownFunc + "() should have configuration"); + } + } + return ret; +} + +//--------------------------------------------------------------------------- + +void Tokenizer::syntaxError(const Token *tok, const std::string &code) const +{ + printDebugOutput(0); + throw InternalError(tok, code.empty() ? "syntax error" : "syntax error: " + code, InternalError::SYNTAX); +} + +void Tokenizer::unmatchedToken(const Token *tok) const +{ + printDebugOutput(0); + throw InternalError(tok, + "Unmatched '" + tok->str() + "'. Configuration: '" + mConfiguration + "'.", + InternalError::SYNTAX); +} + +void Tokenizer::syntaxErrorC(const Token *tok, const std::string &what) const +{ + printDebugOutput(0); + throw InternalError(tok, "Code '"+what+"' is invalid C code. Use --std or --language to configure the language.", InternalError::SYNTAX); +} + +void Tokenizer::unknownMacroError(const Token *tok1) const +{ + printDebugOutput(0); + throw InternalError(tok1, "There is an unknown macro here somewhere. Configuration is required. If " + tok1->str() + " is a macro then please configure it.", InternalError::UNKNOWN_MACRO); +} + +void Tokenizer::unhandled_macro_class_x_y(const Token *tok) const +{ + reportError(tok, + Severity::information, + "class_X_Y", + "The code '" + + tok->str() + " " + + tok->strAt(1) + " " + + tok->strAt(2) + " " + + tok->strAt(3) + "' is not handled. You can use -I or --include to add handling of this code."); +} + +void Tokenizer::macroWithSemicolonError(const Token *tok, const std::string ¯oName) const +{ + reportError(tok, + Severity::information, + "macroWithSemicolon", + "Ensure that '" + macroName + "' is defined either using -I, --include or -D."); +} + +void Tokenizer::cppcheckError(const Token *tok) const +{ + printDebugOutput(0); + throw InternalError(tok, "Analysis failed. If the code is valid then please report this failure.", InternalError::INTERNAL); +} + +void Tokenizer::unhandledCharLiteral(const Token *tok, const std::string& msg) const +{ + std::string s = tok ? (" " + tok->str()) : ""; + for (int i = 0; i < s.size(); ++i) { + if ((unsigned char)s[i] >= 0x80) + s.clear(); + } + + reportError(tok, + Severity::portability, + "nonStandardCharLiteral", + "Non-standard character literal" + s + ". " + msg); +} + +/** + * Helper function to check whether number is equal to integer constant X + * or floating point pattern X.0 + * @param s the string to check + * @param intConstant the integer constant to check against + * @param floatConstant the string with stringified float constant to check against + * @return true in case s is equal to X or X.0 and false otherwise. + */ +static bool isNumberOneOf(const std::string &s, MathLib::bigint intConstant, const char* floatConstant) +{ + if (MathLib::isInt(s)) { + if (MathLib::toBigNumber(s) == intConstant) + return true; + } else if (MathLib::isFloat(s)) { + if (MathLib::toString(MathLib::toDoubleNumber(s)) == floatConstant) + return true; + } + return false; +} + +// ------------------------------------------------------------------------ +// Helper function to check whether number is one (1 or 0.1E+1 or 1E+0) or not? +// @param s the string to check +// @return true in case s is one and false otherwise. +// ------------------------------------------------------------------------ +bool Tokenizer::isOneNumber(const std::string &s) +{ + if (!MathLib::isPositive(s)) + return false; + return isNumberOneOf(s, 1L, "1.0"); +} +// ------------------------------------------------------------------------ +void Tokenizer::checkConfiguration() const +{ + if (!mSettings.checkConfiguration) + return; + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if (!Token::Match(tok, "%name% (")) + continue; + if (tok->isControlFlowKeyword()) + continue; + for (const Token *tok2 = tok->tokAt(2); tok2 && tok2->str() != ")"; tok2 = tok2->next()) { + if (tok2->str() == ";") { + macroWithSemicolonError(tok, tok->str()); + break; + } + if (Token::Match(tok2, "(|{")) + tok2 = tok2->link(); + } + } +} + +void Tokenizer::validateC() const +{ + if (isCPP()) + return; + for (const Token *tok = tokens(); tok; tok = tok->next()) { + // That might trigger false positives, but it's much faster to have this truncated pattern + if (Token::Match(tok, "const_cast|dynamic_cast|reinterpret_cast|static_cast <")) + syntaxErrorC(tok, "C++ cast <..."); + // Template function.. + if (Token::Match(tok, "%name% < %name% > (")) { + const Token *tok2 = tok->tokAt(5); + while (tok2 && !Token::Match(tok2, "[()]")) + tok2 = tok2->next(); + if (Token::simpleMatch(tok2, ") {")) + syntaxErrorC(tok, tok->str() + '<' + tok->strAt(2) + ">() {}"); + } + if (tok->previous() && !Token::Match(tok->previous(), "[;{}]")) + continue; + if (Token::Match(tok, "using namespace %name% ;")) + syntaxErrorC(tok, "using namespace " + tok->strAt(2)); + if (Token::Match(tok, "template < class|typename %name% [,>]")) + syntaxErrorC(tok, "template<..."); + if (Token::Match(tok, "%name% :: %name%")) + syntaxErrorC(tok, tok->str() + tok->strAt(1) + tok->strAt(2)); + if (Token::Match(tok, "class|namespace %name% [:{]")) + syntaxErrorC(tok, tok->str() + tok->strAt(1) + tok->strAt(2)); + } +} + +void Tokenizer::validate() const +{ + std::stack linkTokens; + const Token *lastTok = nullptr; + for (const Token *tok = tokens(); tok; tok = tok->next()) { + lastTok = tok; + if (Token::Match(tok, "[{([]") || (tok->str() == "<" && tok->link())) { + if (tok->link() == nullptr) + cppcheckError(tok); + + linkTokens.push(tok); + } + + else if (Token::Match(tok, "[})]]") || (Token::Match(tok, ">|>>") && tok->link())) { + if (tok->link() == nullptr) + cppcheckError(tok); + + if (linkTokens.empty()) + cppcheckError(tok); + + if (tok->link() != linkTokens.top()) + cppcheckError(tok); + + if (tok != tok->link()->link()) + cppcheckError(tok); + + linkTokens.pop(); + } + + else if (tok->link() != nullptr) + cppcheckError(tok); + } + + if (!linkTokens.empty()) + cppcheckError(linkTokens.top()); + + // Validate that the Tokenizer::list.back() is updated correctly during simplifications + if (lastTok != list.back()) + cppcheckError(lastTok); +} + +static const Token *findUnmatchedTernaryOp(const Token * const begin, const Token * const end, int depth = 0) +{ + std::stack ternaryOp; + for (const Token *tok = begin; tok != end && tok->str() != ";"; tok = tok->next()) { + if (tok->str() == "?") + ternaryOp.push(tok); + else if (!ternaryOp.empty() && tok->str() == ":") + ternaryOp.pop(); + else if (depth < 100 && Token::Match(tok,"(|[")) { + const Token *inner = findUnmatchedTernaryOp(tok->next(), tok->link(), depth+1); + if (inner) + return inner; + tok = tok->link(); + } + } + return ternaryOp.empty() ? nullptr : ternaryOp.top(); +} + +static bool isCPPAttribute(const Token * tok) +{ + return Token::simpleMatch(tok, "[ [") && tok->link() && tok->link()->previous() == tok->linkAt(1); +} + +static bool isAlignAttribute(const Token * tok) +{ + return Token::simpleMatch(tok, "alignas (") && tok->next()->link(); +} + +template +static T* skipCPPOrAlignAttribute(T * tok) +{ + if (isCPPAttribute(tok)) + return tok->link(); + if (isAlignAttribute(tok)) { + return tok->next()->link(); + } + return tok; +} + +static bool isNonMacro(const Token* tok) +{ + if (tok->isKeyword() || tok->isStandardType()) + return true; + if (cAlternativeTokens.count(tok->str()) > 0) + return true; + if (startsWith(tok->str(), "__")) // attribute/annotation + return true; + if (Token::simpleMatch(tok, "alignas (")) + return true; + return false; +} + +void Tokenizer::reportUnknownMacros() const +{ + // Report unknown macros used in expressions "%name% %num%" + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if (Token::Match(tok, "%name% %num%")) { + // A keyword is not an unknown macro + if (tok->isKeyword()) + continue; + + if (Token::Match(tok->previous(), "%op%|(")) + unknownMacroError(tok); + } + } + + // Report unknown macros before } "{ .. if (x) MACRO }" + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if (Token::Match(tok, ")|; %name% } !!)")) { + if (tok->link() && !Token::simpleMatch(tok->link()->tokAt(-1), "if")) + continue; + const Token* prev = tok->linkAt(2); + while (Token::simpleMatch(prev, "{")) + prev = prev->previous(); + if (Token::Match(prev, ";|)")) + unknownMacroError(tok->next()); + } + } + + // Report unknown macros that contain several statements "MACRO(a;b;c)" + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if (!Token::Match(tok, "%name% (")) + continue; + if (!tok->isUpperCaseName()) + continue; + const Token *endTok = tok->linkAt(1); + for (const Token *inner = tok->tokAt(2); inner != endTok; inner = inner->next()) { + if (Token::Match(inner, "[[({]")) + inner = inner->link(); + else if (inner->str() == ";") + unknownMacroError(tok); + } + } + + // Report unknown macros that contain struct initialization "MACRO(a, .b=3)" + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if (!Token::Match(tok, "%name% (")) + continue; + const Token *endTok = tok->linkAt(1); + for (const Token *inner = tok->tokAt(2); inner != endTok; inner = inner->next()) { + if (Token::Match(inner, "[[({]")) + inner = inner->link(); + else if (Token::Match(inner->previous(), "[,(] . %name% =|{")) + unknownMacroError(tok); + } + } + + // Report unknown macros in non-executable scopes.. + std::set possible; + for (const Token *tok = tokens(); tok; tok = tok->next()) { + // Skip executable scopes.. + if (tok->str() == "{") { + const Token *prev = tok->previous(); + while (prev && prev->isName()) + prev = prev->previous(); + if (prev && prev->str() == ")") + tok = tok->link(); + else + possible.clear(); + } else if (tok->str() == "}") + possible.clear(); + + if (Token::Match(tok, "%name% (") && tok->isUpperCaseName() && Token::simpleMatch(tok->linkAt(1), ") (") && Token::simpleMatch(tok->linkAt(1)->linkAt(1), ") {")) { + // A keyword is not an unknown macro + if (tok->isKeyword()) + continue; + + const Token *bodyStart = tok->linkAt(1)->linkAt(1)->tokAt(2); + const Token *bodyEnd = tok->link(); + for (const Token *tok2 = bodyStart; tok2 && tok2 != bodyEnd; tok2 = tok2->next()) { + if (Token::Match(tok2, "if|switch|for|while|return")) + unknownMacroError(tok); + } + } else if (Token::Match(tok, "%name% (") && tok->isUpperCaseName() && Token::Match(tok->linkAt(1), ") %name% (") && Token::Match(tok->linkAt(1)->linkAt(2), ") [;{]")) { + if (!(tok->linkAt(1)->next() && tok->linkAt(1)->next()->isKeyword())) { // e.g. noexcept(true) + if (possible.count(tok->str()) == 0) + possible.insert(tok->str()); + else + unknownMacroError(tok); + } + } else if (isCPP() && Token::Match(tok, "public|private|protected %name% :")) { + unknownMacroError(tok->next()); + } + } + + // String concatenation with unknown macros + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if ((Token::Match(tok, "%str% %name% (") && Token::Match(tok->linkAt(2), ") %str%")) || + (Token::Match(tok, "%str% %name% %str%") && !(startsWith(tok->strAt(1), "PRI") || startsWith(tok->strAt(1), "SCN")))) { // TODO: implement macros in std.cfg + if (tok->next()->isKeyword()) + continue; + unknownMacroError(tok->next()); + } + if (Token::Match(tok, "[(,] %name% (") && Token::Match(tok->linkAt(2), ") %name% %name%|,|)")) { + if (tok->next()->isKeyword() || tok->linkAt(2)->next()->isKeyword()) + continue; + if (cAlternativeTokens.count(tok->linkAt(2)->next()->str()) > 0) + continue; + if (startsWith(tok->next()->str(), "__")) // attribute/annotation + continue; + unknownMacroError(tok->next()); + } + } + + // Report unknown macros without commas or operators inbetween statements: MACRO1() MACRO2() + for (const Token* tok = tokens(); tok; tok = tok->next()) { + if (!Token::Match(tok, "%name% (")) + continue; + if (isNonMacro(tok) && !tok->isStandardType()) + continue; + + const Token* endTok = tok->linkAt(1); + if (!Token::Match(endTok, ") %name% (|.")) + continue; + + const Token* tok2 = endTok->next(); + if (isNonMacro(tok2)) + continue; + + if (tok2->next()->str() == "(") { + if (Token::Match(tok->previous(), "%name%|::|>")) + continue; + } + + unknownMacroError(tok->isStandardType() ? tok2 : tok); + } +} + +void Tokenizer::findGarbageCode() const +{ + const bool isCPP11 = isCPP() && mSettings.standards.cpp >= Standards::CPP11; + + static const std::unordered_set nonConsecutiveKeywords{ "break", + "continue", + "for", + "goto", + "if", + "return", + "switch", + "throw", + "typedef", + "while" }; + + for (const Token *tok = tokens(); tok; tok = tok->next()) { + // initialization: = { + if (Token::simpleMatch(tok, "= {") && Token::simpleMatch(tok->linkAt(1), "} (")) + syntaxError(tok->linkAt(1)); + + // Inside [] there can't be ; or various keywords + else if (tok->str() == "[") { + for (const Token* inner = tok->next(); inner != tok->link(); inner = inner->next()) { + if (Token::Match(inner, "(|[|{")) + inner = inner->link(); + else if (Token::Match(inner, ";|goto|return|typedef")) + syntaxError(inner); + } + } + + // array assignment + else if (Token::Match(tok, "%assign% [") && Token::simpleMatch(tok->linkAt(1), "] ;")) + syntaxError(tok, tok->str() + "[...];"); + + else if (Token::Match(tok, "[({<] %assign%")) + syntaxError(tok); + + else if (Token::Match(tok, "[`\\@]")) + syntaxError(tok); + + // UNKNOWN_MACRO(return) + if (tok->isKeyword() && Token::Match(tok, "throw|return )") && Token::Match(tok->linkAt(1)->previous(), "%name% (")) + unknownMacroError(tok->linkAt(1)->previous()); + + // UNKNOWN_MACRO(return) + else if (Token::Match(tok, "%name% throw|return") && std::isupper(tok->str()[0])) + unknownMacroError(tok); + + // Assign/increment/decrement literal + else if (Token::Match(tok, "!!) %num%|%str%|%char% %assign%|++|--")) { + if (!isCPP() || mSettings.standards.cpp < Standards::CPP20 || !Token::Match(tok->previous(), "%name% : %num% =")) + syntaxError(tok, tok->next()->str() + " " + tok->strAt(2)); + } + else if (Token::simpleMatch(tok, ") return") && !Token::Match(tok->link()->previous(), "if|while|for (")) { + if (tok->link()->previous() && tok->link()->previous()->isUpperCaseName()) + unknownMacroError(tok->link()->previous()); + else + syntaxError(tok); + } + + if (tok->isControlFlowKeyword() && Token::Match(tok, "if|while|for|switch")) { // if|while|for|switch (EXPR) { ... } + if (tok->previous() && !Token::Match(tok->previous(), "%name%|:|;|{|}|)")) { + if (Token::Match(tok->previous(), "[,(]")) { + const Token *prev = tok->previous(); + while (prev && prev->str() != "(") { + if (prev->str() == ")") + prev = prev->link(); + prev = prev->previous(); + } + if (prev && Token::Match(prev->previous(), "%name% (")) + unknownMacroError(prev->previous()); + } + if (!Token::simpleMatch(tok->tokAt(-2), "operator \"\" if")) + syntaxError(tok); + } + if (!Token::Match(tok->next(), "( !!)")) + syntaxError(tok); + if (tok->str() != "for") { + if (isGarbageExpr(tok->next(), tok->linkAt(1), isCPP() && (mSettings.standards.cpp>=Standards::cppstd_t::CPP17))) + syntaxError(tok); + } + } + + // keyword keyword + if (tok->isKeyword() && nonConsecutiveKeywords.count(tok->str()) != 0) { + if (Token::Match(tok, "%name% %name%") && nonConsecutiveKeywords.count(tok->next()->str()) == 1) + syntaxError(tok); + const Token* prev = tok; + while (prev && prev->isName()) + prev = prev->previous(); + if (Token::Match(prev, "%op%|%num%|%str%|%char%")) { + if (!Token::simpleMatch(tok->tokAt(-2), "operator \"\" if") && + !Token::simpleMatch(tok->tokAt(-2), "extern \"C\"") && + !Token::simpleMatch(prev, "> typedef")) + syntaxError(tok, prev == tok->previous() ? (prev->str() + " " + tok->str()) : (prev->str() + " .. " + tok->str())); + } + } + } + + // invalid struct declaration + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if (Token::Match(tok, "struct|class|enum %name%| {") && (!tok->previous() || Token::Match(tok->previous(), "[;{}]"))) { + const Token *tok2 = tok->linkAt(tok->next()->isName() ? 2 : 1); + if (Token::Match(tok2, "} %op%")) { + tok2 = tok2->next(); + if (!Token::Match(tok2, "*|&|&&")) + syntaxError(tok2, "Unexpected token '" + tok2->str() + "'"); + while (Token::Match(tok2, "*|&|&&")) + tok2 = tok2->next(); + if (!Token::Match(tok2, "%name%")) + syntaxError(tok2, "Unexpected token '" + (tok2 ? tok2->str() : "") + "'"); + } + } + if (Token::Match(tok, "enum : %num%| {")) + syntaxError(tok->tokAt(2), "Unexpected token '" + tok->strAt(2) + "'"); + } + + // Keywords in global scope + static const std::unordered_set nonGlobalKeywords{"break", + "continue", + "for", + "goto", + "if", + "return", + "switch", + "while", + "try", + "catch"}; + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if (tok->str() == "{") + tok = tok->link(); + else if (tok->isKeyword() && nonGlobalKeywords.count(tok->str()) && !Token::Match(tok->tokAt(-2), "operator %str%")) + syntaxError(tok, "keyword '" + tok->str() + "' is not allowed in global scope"); + } + + // case keyword must be inside switch + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "switch (")) { + if (Token::simpleMatch(tok->linkAt(1), ") {")) { + tok = tok->linkAt(1)->linkAt(1); + continue; + } + const Token *switchToken = tok; + tok = tok->linkAt(1); + if (!tok) + syntaxError(switchToken); + // Look for the end of the switch statement, i.e. the first semi-colon or '}' + for (; tok; tok = tok->next()) { + if (tok->str() == "{") { + tok = tok->link(); + } + if (Token::Match(tok, ";|}")) { + // We're at the end of the switch block + if (tok->str() == "}" && tok->strAt(-1) == ":") // Invalid case + syntaxError(switchToken); + break; + } + } + if (!tok) + break; + } else if (tok->str() == "(") { + tok = tok->link(); + } else if (tok->str() == "case") { + syntaxError(tok); + } + } + + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if (!Token::simpleMatch(tok, "for (")) // find for loops + continue; + // count number of semicolons + int semicolons = 0, colons = 0; + const Token* const startTok = tok; + tok = tok->next()->link()->previous(); // find ")" of the for-loop + // walk backwards until we find the beginning (startTok) of the for() again + for (; tok != startTok; tok = tok->previous()) { + if (tok->str() == ";") { // do the counting + semicolons++; + } else if (tok->str() == ":") { + colons++; + } else if (tok->str() == ")") { // skip pairs of ( ) + tok = tok->link(); + } + } + // if we have an invalid number of semicolons inside for( ), assume syntax error + if (semicolons > 2) + syntaxError(tok); + if (semicolons == 1 && !(isCPP() && mSettings.standards.cpp >= Standards::CPP20)) + syntaxError(tok); + if (semicolons == 0 && colons == 0) + syntaxError(tok); + } + + // Operators without operands.. + const Token *templateEndToken = nullptr; + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if (!templateEndToken) { + if (tok->str() == "<" && isCPP()) + templateEndToken = tok->findClosingBracket(); + } else { + if (templateEndToken == tok) + templateEndToken = nullptr; + if (Token::Match(tok, "> %cop%")) + continue; + } + // skip C++ attributes [[...]] + if (isCPP11 && (isCPPAttribute(tok) || isAlignAttribute(tok))) { + tok = skipCPPOrAlignAttribute(tok); + continue; + } + { + bool match1 = Token::Match(tok, "%or%|%oror%|==|!=|+|-|/|!|>=|<=|~|^|++|--|::|sizeof"); + bool match2 = Token::Match(tok->next(), "{|if|else|while|do|for|return|switch|break"); + if (isCPP()) { + match1 = match1 || Token::Match(tok, "throw|decltype|typeof"); + match2 = match2 || Token::Match(tok->next(), "try|catch|namespace"); + } + if (match1 && !tok->isIncDecOp()) { + match2 = match2 || Token::Match(tok->next(), "%assign%"); + } + if (match1 && match2) + syntaxError(tok); + } + if (Token::Match(tok, "%or%|%oror%|~|^|!|%comp%|+|-|/|%")) { + std::string code; + if (Token::Match(tok->next(), ")|]|}")) + code = tok->str() + tok->next()->str(); + if (Token::simpleMatch(tok->next(), "( )")) + code = tok->str() + "()"; + if (!code.empty()) { + if (isC() || (tok->str() != ">" && !Token::simpleMatch(tok->previous(), "operator"))) + syntaxError(tok, code); + } + } + if (Token::Match(tok, "%num%|%bool%|%char%|%str% %num%|%bool%|%char%|%str%") && !Token::Match(tok, "%str% %str%")) + syntaxError(tok); + if (Token::Match(tok, "%assign% typename|class %assign%")) + syntaxError(tok); + if (Token::Match(tok, "%assign% [;)}]") && (!isCPP() || !Token::simpleMatch(tok->previous(), "operator"))) + syntaxError(tok); + if (Token::Match(tok, "%cop%|=|,|[ %or%|%oror%|/|%")) + syntaxError(tok); + if (Token::Match(tok, "[;([{] %comp%|&&|%oror%|%or%|%|/")) + syntaxError(tok); + if (Token::Match(tok, "%cop%|= ]") && !(isCPP() && Token::Match(tok->previous(), "%type%|[|,|%num% &|=|> ]"))) + syntaxError(tok); + if (Token::Match(tok, "[+-] [;,)]}]") && !(isCPP() && Token::Match(tok->previous(), "operator [+-] ;"))) + syntaxError(tok); + if (Token::simpleMatch(tok, ",") && + !Token::Match(tok->tokAt(-2), "[ = , &|%name%")) { + if (Token::Match(tok->previous(), "(|[|{|<|%assign%|%or%|%oror%|==|!=|+|-|/|!|>=|<=|~|^|::|sizeof")) + syntaxError(tok); + if (isCPP() && Token::Match(tok->previous(), "throw|decltype|typeof")) + syntaxError(tok); + if (Token::Match(tok->next(), ")|]|>|%assign%|%or%|%oror%|==|!=|/|>=|<=|&&")) + syntaxError(tok); + } + if (Token::simpleMatch(tok, ".") && + !Token::simpleMatch(tok->previous(), ".") && + !Token::simpleMatch(tok->next(), ".") && + !Token::Match(tok->previous(), "{|, . %name% =|.|[|{") && + !Token::Match(tok->previous(), ", . %name%")) { + if (!Token::Match(tok->previous(), "%name%|)|]|>|}")) + syntaxError(tok, tok->strAt(-1) + " " + tok->str() + " " + tok->strAt(1)); + if (!Token::Match(tok->next(), "%name%|*|~")) + syntaxError(tok, tok->strAt(-1) + " " + tok->str() + " " + tok->strAt(1)); + } + if (Token::Match(tok, "[!|+-/%^~] )|]")) + syntaxError(tok); + if (Token::Match(tok, "==|!=|<=|>= %comp%") && tok->strAt(-1) != "operator") + syntaxError(tok, tok->str() + " " + tok->strAt(1)); + if (Token::simpleMatch(tok, "::") && (!Token::Match(tok->next(), "%name%|*|~") || + (tok->next()->isKeyword() && !Token::Match(tok->next(), "new|delete|operator")))) + syntaxError(tok); + if (Token::Match(tok, "& %comp%|&&|%oror%|&|%or%") && tok->strAt(1) != ">") + syntaxError(tok); + + if (tok->link() && Token::Match(tok, "[([]") && (!tok->tokAt(-1) || !tok->tokAt(-1)->isControlFlowKeyword())) { + const Token* const end = tok->link(); + for (const Token* inner = tok->next(); inner != end; inner = inner->next()) { + if (inner->str() == "{") + inner = inner->link(); + else if (inner->str() == ";") { + if (tok->tokAt(-1) && tok->tokAt(-1)->isUpperCaseName()) + unknownMacroError(tok->tokAt(-1)); + else + syntaxError(inner); + } + } + } + } + + // ternary operator without : + if (const Token *ternaryOp = findUnmatchedTernaryOp(tokens(), nullptr)) + syntaxError(ternaryOp); + + // Code must not start with an arithmetical operand + if (Token::Match(list.front(), "%cop%")) + syntaxError(list.front()); + + // Code must end with } ; ) NAME + if (!Token::Match(list.back(), "%name%|;|}|)")) + syntaxError(list.back()); + if (list.back()->str() == ")" && !Token::Match(list.back()->link()->previous(), "%name%|> (")) + syntaxError(list.back()); + for (const Token *end = list.back(); end && end->isName(); end = end->previous()) { + if (Token::Match(end, "void|char|short|int|long|float|double|const|volatile|static|inline|struct|class|enum|union|template|sizeof|case|break|continue|typedef")) + syntaxError(list.back()); + } + if ((list.back()->str()==")" || list.back()->str()=="}") && list.back()->previous() && list.back()->previous()->isControlFlowKeyword()) + syntaxError(list.back()->previous()); + + // Garbage templates.. + if (isCPP()) { + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if (!Token::simpleMatch(tok, "template <")) + continue; + if (tok->previous() && !Token::Match(tok->previous(), ":|;|{|}|)|>|\"C++\"")) { + if (tok->previous()->isUpperCaseName()) + unknownMacroError(tok->previous()); + else + syntaxError(tok); + } + const Token * const tok1 = tok; + tok = tok->next()->findClosingBracket(); + if (!tok) + syntaxError(tok1); + if (!Token::Match(tok, ">|>> ::|...| %name%") && + !Token::Match(tok, ">|>> [ [ %name%") && + !Token::Match(tok, "> >|*")) + syntaxError(tok->next() ? tok->next() : tok1); + } + } + + // Objective C/C++ + for (const Token *tok = tokens(); tok; tok = tok->next()) { + if (Token::Match(tok, "[;{}] [ %name% %name% ] ;")) + syntaxError(tok->next()); + } +} + + +bool Tokenizer::isGarbageExpr(const Token *start, const Token *end, bool allowSemicolon) +{ + for (const Token *tok = start; tok != end; tok = tok->next()) { + if (tok->isControlFlowKeyword()) + return true; + if (!allowSemicolon && tok->str() == ";") + return true; + if (tok->str() == "{") + tok = tok->link(); + } + return false; +} + +std::string Tokenizer::simplifyString(const std::string &source) +{ + std::string str = source; + + for (std::string::size_type i = 0; i + 1U < str.size(); ++i) { + if (str[i] != '\\') + continue; + + int c = 'a'; // char + int sz = 0; // size of stringdata + if (str[i+1] == 'x') { + sz = 2; + while (sz < 4 && std::isxdigit((unsigned char)str[i+sz])) + sz++; + if (sz > 2) { + std::istringstream istr(str.substr(i+2, sz-2)); + istr >> std::hex >> c; + } + } else if (MathLib::isOctalDigit(str[i+1])) { + sz = 2; + while (sz < 4 && MathLib::isOctalDigit(str[i+sz])) + sz++; + std::istringstream istr(str.substr(i+1, sz-1)); + istr >> std::oct >> c; + str = str.replace(i, sz, std::string(1U, (char)c)); + 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::simplifyFunctionTryCatch() +{ + if (!isCPP()) + return; + + for (Token * tok = list.front(); tok; tok = tok->next()) { + if (!Token::Match(tok, "try {|:")) + continue; + if (!isFunctionHead(tok->previous(), "try")) + continue; + + Token* tryStartToken = skipInitializerList(tok->next()); + + if (!Token::simpleMatch(tryStartToken, "{")) + syntaxError(tryStartToken, "Invalid function-try-catch block code. Did not find '{' for try body."); + + // find the end of the last catch block + Token * const tryEndToken = tryStartToken->link(); + Token * endToken = tryEndToken; + while (Token::simpleMatch(endToken, "} catch (")) { + endToken = endToken->linkAt(2)->next(); + if (!endToken) + break; + if (endToken->str() != "{") { + endToken = nullptr; + break; + } + endToken = endToken->link(); + } + if (!endToken || endToken == tryEndToken) + continue; + + tok->previous()->insertToken("{"); + endToken->insertToken("}"); + Token::createMutualLinks(tok->previous(), endToken->next()); + } +} + +static bool isAnonymousEnum(const Token* tok) +{ + if (!Token::Match(tok, "enum {|:")) + return false; + if (tok->index() > 2 && Token::Match(tok->tokAt(-3), "using %name% =")) + return false; + const Token* end = tok->next(); + if (end->str() == ":") { + end = end->next(); + while (Token::Match(end, "%name%|::")) + end = end->next(); + } + return end && Token::Match(end->link(), "} (| %type%| )| [,;[({=]"); +} + +void Tokenizer::simplifyStructDecl() +{ + const bool cpp = isCPP(); + + // A counter that is used when giving unique names for anonymous structs. + int count = 0; + + // Add names for anonymous structs + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!tok->isName()) + continue; + // check for anonymous struct/union + if (Token::Match(tok, "struct|union {")) { + if (Token::Match(tok->next()->link(), "} const| *|&| const| %type% ,|;|[|(|{|=")) { + tok->insertToken("Anonymous" + std::to_string(count++)); + } + } + // check for derived anonymous class/struct + else if (cpp && Token::Match(tok, "class|struct :")) { + const Token *tok1 = Token::findsimplematch(tok, "{"); + if (tok1 && Token::Match(tok1->link(), "} const| *|&| const| %type% ,|;|[|(|{")) { + tok->insertToken("Anonymous" + std::to_string(count++)); + } + } + // check for anonymous enum + else if (isAnonymousEnum(tok)) { + Token *start = tok->strAt(1) == ":" ? tok->linkAt(3) : tok->linkAt(1); + if (start && Token::Match(start->next(), "( %type% )")) { + start->next()->link()->deleteThis(); + start->next()->deleteThis(); + } + tok->insertToken("Anonymous" + std::to_string(count++)); + } + } + + // "{" token for current scope + std::stack scopeStart; + const Token* functionEnd = nullptr; + + 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() == "{") { + scopeStart.push(tok); + if (!functionEnd && Token::Match(tok->previous(), "const|)")) + functionEnd = tok->link(); + } + + // end of scope + else if (tok->str() == "}") { + if (!scopeStart.empty()) + scopeStart.pop(); + if (tok == functionEnd) + functionEnd = nullptr; + } + + // check for named struct/union + else if (Token::Match(tok, "class|struct|union|enum %type% :|{")) { + Token *start = tok; + while (Token::Match(start->previous(), "%type%")) + start = start->previous(); + const Token * const type = tok->next(); + Token *next = tok->tokAt(2); + + while (next && !Token::Match(next, "[{;]")) + next = next->next(); + if (!next || next->str() == ";") + continue; + Token* after = next->link(); + if (!after) + break; // see #4869 segmentation fault in Tokenizer::simplifyStructDecl (invalid code) + + // check for named type + if (Token::Match(after->next(), "const|static|volatile| *|&| const| (| %type% )| ,|;|[|=|(|{")) { + after->insertToken(";"); + after = after->next(); + while (!Token::Match(start, "struct|class|union|enum")) { + after->insertToken(start->str()); + after = after->next(); + start->deleteThis(); + } + tok = start; + if (!after) + break; // see #4869 segmentation fault in Tokenizer::simplifyStructDecl (invalid code) + after->insertToken(type->str()); + if (start->str() != "class") { + after->insertToken(start->str()); + after = after->next(); + } + + after = after->tokAt(2); + + if (Token::Match(after, "( %type% )")) { + after->link()->deleteThis(); + after->deleteThis(); + } + + // check for initialization + if (Token::Match(after, "%any% (|{")) { + after->insertToken("="); + after = after->next(); + const bool isEnum = start->str() == "enum"; + if (!isEnum && cpp) { + after->insertToken(type->str()); + after = after->next(); + } + + if (isEnum) { + if (Token::Match(after->next(), "{ !!}")) { + after->next()->str("("); + after->linkAt(1)->str(")"); + } + } + } + } + } + + // check for anonymous struct/union + else { + // unnamed anonymous struct/union so possibly remove it + bool done = false; + while (!done && Token::Match(tok, "struct|union {") && Token::simpleMatch(tok->linkAt(1), "} ;")) { + done = true; + + // is this a class/struct/union scope? + bool isClassStructUnionScope = false; + if (!scopeStart.empty()) { + for (const Token* tok2 = scopeStart.top()->previous(); tok2 && !Token::Match(tok2, "[;{}]"); tok2 = tok2->previous()) { + if (Token::Match(tok2, "class|struct|union")) { + isClassStructUnionScope = true; + break; + } + } + } + + // remove unnamed anonymous struct/union + // * not in class/struct/union scopes + if (Token::simpleMatch(tok->linkAt(1), "} ;") && !isClassStructUnionScope && tok->str() != "union") { + tok->linkAt(1)->previous()->deleteNext(2); + tok->deleteNext(); + tok->deleteThis(); + done = false; + } + } + } + } +} + +void Tokenizer::simplifyCallingConvention() +{ + const bool windows = mSettings.platform.isWindows(); + + for (Token *tok = list.front(); tok; tok = tok->next()) { + while (Token::Match(tok, "__cdecl|__stdcall|__fastcall|__thiscall|__clrcall|__syscall|__pascal|__fortran|__far|__near") || (windows && Token::Match(tok, "WINAPI|APIENTRY|CALLBACK"))) { + tok->deleteThis(); + } + } +} + +static bool isAttribute(const Token* tok, bool gcc) { + return gcc ? Token::Match(tok, "__attribute__|__attribute (") : Token::Match(tok, "__declspec|_declspec ("); +} + +static Token* getTokenAfterAttributes(Token* tok, bool gccattr) { + Token* after = tok; + while (isAttribute(after, gccattr)) + after = after->linkAt(1)->next(); + return after; +} + +Token* Tokenizer::getAttributeFuncTok(Token* tok, bool gccattr) const { + if (!Token::Match(tok, "%name% (")) + return nullptr; + Token* const after = getTokenAfterAttributes(tok, gccattr); + if (!after) + syntaxError(tok); + + if (Token::Match(after, "%name%|*|&|(")) { + Token *ftok = after; + while (Token::Match(ftok, "%name%|::|<|*|& !!(")) { + if (ftok->str() == "<") { + ftok = ftok->findClosingBracket(); + if (!ftok) + break; + } + ftok = ftok->next(); + } + if (Token::simpleMatch(ftok, "( *")) + ftok = ftok->tokAt(2); + if (Token::Match(ftok, "%name% (|)")) + return ftok; + } else if (Token::Match(after, "[;{=:]")) { + Token *prev = tok->previous(); + while (Token::Match(prev, "%name%")) + prev = prev->previous(); + if (Token::simpleMatch(prev, ")")) { + if (Token::Match(prev->link()->previous(), "%name% (")) + return prev->link()->previous(); + if (Token::Match(prev->link()->tokAt(-2), "%name% ) (")) + return prev->link()->tokAt(-2); + } + if (Token::simpleMatch(prev, ")") && Token::Match(prev->link()->tokAt(-2), "operator %op% (") && isCPP()) + return prev->link()->tokAt(-2); + if ((!prev || Token::Match(prev, "[;{}*]")) && Token::Match(tok->previous(), "%name%")) + return tok->previous(); + } + return nullptr; +} + +void Tokenizer::simplifyDeclspec() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + while (isAttribute(tok, false)) { + if (Token::Match(tok->tokAt(2), "noreturn|nothrow|dllexport")) { + Token *functok = getAttributeFuncTok(tok, false); + if (functok) { + if (tok->strAt(2) == "noreturn") + functok->isAttributeNoreturn(true); + else if (tok->strAt(2) == "nothrow") + functok->isAttributeNothrow(true); + else + functok->isAttributeExport(true); + } + } else if (tok->strAt(2) == "property") + tok->next()->link()->insertToken("__property"); + + Token::eraseTokens(tok, tok->next()->link()->next()); + tok->deleteThis(); + } + } +} + +void Tokenizer::simplifyAttribute() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!tok->isKeyword() && Token::Match(tok, "%type% (") && !mSettings.library.isNotLibraryFunction(tok)) { + if (mSettings.library.isFunctionConst(tok->str(), true)) + tok->isAttributePure(true); + if (mSettings.library.isFunctionConst(tok->str(), false)) + tok->isAttributeConst(true); + } + while (isAttribute(tok, true)) { + Token *functok = getAttributeFuncTok(tok, true); + + for (Token *attr = tok->tokAt(2); attr->str() != ")"; attr = attr->next()) { + if (Token::Match(attr, "%name% (")) + attr = attr->linkAt(1); + + if (Token::Match(attr, "[(,] constructor|__constructor__ [,()]")) { + if (!functok) + syntaxError(tok); + functok->isAttributeConstructor(true); + } + + else if (Token::Match(attr, "[(,] destructor|__destructor__ [,()]")) { + if (!functok) + syntaxError(tok); + functok->isAttributeDestructor(true); + } + + else if (Token::Match(attr, "[(,] unused|__unused__|used|__used__ [,)]")) { + Token *vartok = nullptr; + Token *after = getTokenAfterAttributes(tok, true); + + // check if after variable name + if (Token::Match(after, ";|=")) { + Token *prev = tok->previous(); + while (Token::simpleMatch(prev, "]")) + prev = prev->link()->previous(); + if (Token::Match(prev, "%type%")) + vartok = prev; + } + + // check if before variable name + else if (Token::Match(after, "%type%")) + vartok = after; + + if (vartok) { + const std::string &attribute(attr->next()->str()); + if (attribute.find("unused") != std::string::npos) + vartok->isAttributeUnused(true); + else + vartok->isAttributeUsed(true); + } + } + + else if (Token::Match(attr, "[(,] pure|__pure__|const|__const__|noreturn|__noreturn__|nothrow|__nothrow__|warn_unused_result [,)]")) { + if (!functok) + syntaxError(tok); + + const std::string &attribute(attr->next()->str()); + if (attribute.find("pure") != std::string::npos) + functok->isAttributePure(true); + else if (attribute.find("const") != std::string::npos) + functok->isAttributeConst(true); + else if (attribute.find("noreturn") != std::string::npos) + functok->isAttributeNoreturn(true); + else if (attribute.find("nothrow") != std::string::npos) + functok->isAttributeNothrow(true); + else if (attribute.find("warn_unused_result") != std::string::npos) + functok->isAttributeNodiscard(true); + } + + else if (Token::Match(attr, "[(,] packed [,)]") && Token::simpleMatch(tok->previous(), "}")) + tok->previous()->isAttributePacked(true); + + else if (functok && Token::simpleMatch(attr, "( __visibility__ ( \"default\" ) )")) + functok->isAttributeExport(true); + } + + Token::eraseTokens(tok, tok->linkAt(1)->next()); + tok->deleteThis(); + } + } +} + +void Tokenizer::simplifyCppcheckAttribute() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->str() != "(") + continue; + if (!tok->previous()) + continue; + const std::string &attr = tok->previous()->str(); + if (!startsWith(attr, "__cppcheck_")) + continue; + if (attr.compare(attr.size()-2, 2, "__") != 0) // TODO: ends_with("__") + continue; + + Token *vartok = tok->link(); + while (Token::Match(vartok->next(), "%name%|*|&|::")) { + vartok = vartok->next(); + if (Token::Match(vartok, "%name% (") && startsWith(vartok->str(),"__cppcheck_")) + vartok = vartok->linkAt(1); + } + + if (vartok->isName()) { + if (Token::Match(tok->previous(), "__cppcheck_low__ ( %num% )")) + vartok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, + MathLib::toBigNumber(tok->next()->str())); + else if (Token::Match(tok->previous(), "__cppcheck_high__ ( %num% )")) + vartok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, + MathLib::toBigNumber(tok->next()->str())); + } + + // Delete cppcheck attribute.. + if (tok->tokAt(-2)) { + tok = tok->tokAt(-2); + Token::eraseTokens(tok, tok->linkAt(2)->next()); + } else { + tok = tok->previous(); + Token::eraseTokens(tok, tok->linkAt(1)->next()); + tok->str(";"); + } + } +} + +void Tokenizer::simplifyCPPAttribute() +{ + if (!isCPP() || mSettings.standards.cpp < Standards::CPP11) + return; + + for (Token *tok = list.front(); tok;) { + if (!isCPPAttribute(tok) && !isAlignAttribute(tok)) { + tok = tok->next(); + continue; + } + if (isCPPAttribute(tok)) { + if (Token::findsimplematch(tok->tokAt(2), "noreturn", tok->link())) { + Token * head = skipCPPOrAlignAttribute(tok)->next(); + while (isCPPAttribute(head) || isAlignAttribute(head)) + head = skipCPPOrAlignAttribute(head)->next(); + while (Token::Match(head, "%name%|::|*|&|<|>|,")) // skip return type + head = head->next(); + if (head && head->str() == "(" && isFunctionHead(head, "{|;")) { + head->previous()->isAttributeNoreturn(true); + } + } else if (Token::findsimplematch(tok->tokAt(2), "nodiscard", tok->link())) { + Token * head = skipCPPOrAlignAttribute(tok)->next(); + while (isCPPAttribute(head) || isAlignAttribute(head)) + head = skipCPPOrAlignAttribute(head)->next(); + while (Token::Match(head, "%name%|::|*|&|<|>|,")) + head = head->next(); + if (head && head->str() == "(" && isFunctionHead(head, "{|;")) { + head->previous()->isAttributeNodiscard(true); + } + } else if (Token::findsimplematch(tok->tokAt(2), "maybe_unused", tok->link())) { + Token* head = skipCPPOrAlignAttribute(tok)->next(); + while (isCPPAttribute(head) || isAlignAttribute(head)) + head = skipCPPOrAlignAttribute(head)->next(); + head->isAttributeMaybeUnused(true); + } else if (Token::Match(tok->previous(), ") [ [ expects|ensures|assert default|audit|axiom| : %name% <|<=|>|>= %num% ] ]")) { + const Token *vartok = tok->tokAt(4); + if (vartok->str() == ":") + vartok = vartok->next(); + Token *argtok = tok->tokAt(-2); + while (argtok && argtok->str() != "(") { + if (argtok->str() == vartok->str()) + break; + if (argtok->str() == ")") + argtok = argtok->link(); + argtok = argtok->previous(); + } + if (argtok && argtok->str() == vartok->str()) { + if (vartok->next()->str() == ">=") + argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, + MathLib::toBigNumber(vartok->strAt(2))); + else if (vartok->next()->str() == ">") + argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, + MathLib::toBigNumber(vartok->strAt(2)) + 1); + else if (vartok->next()->str() == "<=") + argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, + MathLib::toBigNumber(vartok->strAt(2))); + else if (vartok->next()->str() == "<") + argtok->setCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, + MathLib::toBigNumber(vartok->strAt(2)) - 1); + } + } + } else { + if (Token::simpleMatch(tok, "alignas (")) { + // alignment requirements could be checked here + } + } + Token::eraseTokens(tok, skipCPPOrAlignAttribute(tok)->next()); + tok->deleteThis(); + } +} + +void Tokenizer::simplifySpaceshipOperator() +{ + if (isCPP() && mSettings.standards.cpp >= Standards::CPP20) { + for (Token *tok = list.front(); tok && tok->next(); tok = tok->next()) { + if (Token::simpleMatch(tok, "<= >")) { + tok->str("<=>"); + tok->deleteNext(); + } + } + } +} + +static const std::unordered_set keywords = { + "inline" + , "_inline" + , "__inline" + , "__forceinline" + , "register" + , "__restrict" + , "__restrict__" + , "__thread" +}; +// Remove "inline", "register", "restrict", "override", "static" and "constexpr" +// "restrict" keyword +// - New to 1999 ANSI/ISO C standard +// - Not in C++ standard yet +void Tokenizer::simplifyKeyword() +{ + // FIXME: There is a risk that "keywords" are removed by mistake. This + // code should be fixed so it doesn't remove variables etc. Nonstandard + // keywords should be defined with a library instead. For instance the + // linux kernel code at least uses "_inline" as struct member name at some + // places. + + const bool c99 = isC() && mSettings.standards.c >= Standards::C99; + const bool cpp11 = isCPP() && mSettings.standards.cpp >= Standards::CPP11; + const bool cpp20 = isCPP() && mSettings.standards.cpp >= Standards::CPP20; + + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (keywords.find(tok->str()) != keywords.end()) { + // Don't remove struct members + if (!Token::simpleMatch(tok->previous(), ".")) { + const bool isinline = (tok->str().find("inline") != std::string::npos); + const bool isrestrict = (tok->str().find("restrict") != std::string::npos); + if (isinline || isrestrict) { + for (Token *temp = tok->next(); Token::Match(temp, "%name%"); temp = temp->next()) { + if (isinline) + temp->isInline(true); + if (isrestrict) + temp->isRestrict(true); + } + } + tok->deleteThis(); // Simplify.. + } + } + + if (isC() || mSettings.standards.cpp == Standards::CPP03) { + if (tok->str() == "auto") + tok->deleteThis(); + } + + // simplify static keyword: + // void foo( int [ static 5 ] ); ==> void foo( int [ 5 ] ); + if (Token::Match(tok, "[ static %num%")) + tok->deleteNext(); + + if (c99) { + auto getTypeTokens = [tok]() { + std::vector ret; + for (Token *temp = tok; Token::Match(temp, "%name%"); temp = temp->previous()) { + if (!temp->isKeyword()) + ret.emplace_back(temp); + } + for (Token *temp = tok->next(); Token::Match(temp, "%name%"); temp = temp->next()) { + if (!temp->isKeyword()) + ret.emplace_back(temp); + } + return ret; + }; + + if (tok->str() == "restrict") { + for (Token* temp: getTypeTokens()) + temp->isRestrict(true); + tok->deleteThis(); + } + + if (mSettings.standards.c >= Standards::C11) { + while (tok->str() == "_Atomic") { + for (Token* temp: getTypeTokens()) + temp->isAtomic(true); + tok->deleteThis(); + } + } + } + + else if (cpp11) { + if (cpp20 && tok->str() == "consteval") { + tok->originalName(tok->str()); + tok->str("constexpr"); + } else if (cpp20 && tok->str() == "constinit") { + tok->deleteThis(); + } + + // final: + // 1) struct name final { }; <- struct is final + if (Token::Match(tok->previous(), "struct|class|union %type%")) { + Token* finalTok = tok->next(); + if (tok->isUpperCaseName() && Token::Match(finalTok, "%type%") && finalTok->str() != "final") { + tok = finalTok; + finalTok = finalTok->next(); + } + if (Token::simpleMatch(finalTok, "<")) { // specialization + finalTok = finalTok->findClosingBracket(); + if (finalTok) + finalTok = finalTok->next(); + } + if (Token::Match(finalTok, "final [:{]")) { + finalTok->deleteThis(); + tok->previous()->isFinalType(true); + } + } + + // noexcept -> noexcept(true) + // 2) void f() noexcept; -> void f() noexcept(true); + else if (Token::Match(tok, ") const|override|final| noexcept :|{|;|,|const|override|final")) { + // Insertion is done in inverse order + // The brackets are linked together accordingly afterwards + Token* tokNoExcept = tok->next(); + while (tokNoExcept->str() != "noexcept") + tokNoExcept = tokNoExcept->next(); + tokNoExcept->insertToken(")"); + Token * braceEnd = tokNoExcept->next(); + tokNoExcept->insertToken("true"); + tokNoExcept->insertToken("("); + Token * braceStart = tokNoExcept->next(); + tok = tok->tokAt(3); + Token::createMutualLinks(braceStart, braceEnd); + } + + // 3) thread_local -> static + // on single thread thread_local has the effect of static + else if (tok->str() == "thread_local") { + tok->originalName(tok->str()); + tok->str("static"); + } + } + } +} + +static Token* setTokenDebug(Token* start, TokenDebug td) +{ + if (!start->link()) + return nullptr; + Token* end = start->link(); + start->deleteThis(); + for (Token* tok = start; tok != end; tok = tok->next()) { + tok->setTokenDebug(td); + } + end->deleteThis(); + return end; +} + +void Tokenizer::simplifyDebug() +{ + if (!mSettings.debugnormal && !mSettings.debugwarnings) + return; + static const std::unordered_map m = {{"debug_valueflow", TokenDebug::ValueFlow}, + {"debug_valuetype", TokenDebug::ValueType}}; + for (Token* tok = list.front(); tok; tok = tok->next()) { + if (!Token::Match(tok, "%name% (")) + continue; + auto it = m.find(tok->str()); + if (it != m.end()) { + tok->deleteThis(); + tok = setTokenDebug(tok, it->second); + } + } +} + +void Tokenizer::simplifyAssignmentBlock() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "[;{}] %name% = ( {")) { + const std::string &varname = tok->next()->str(); + + // goto the "} )" + int indentlevel = 0; + Token *tok2 = tok; + while (nullptr != (tok2 = tok2->next())) { + if (Token::Match(tok2, "(|{")) + ++indentlevel; + else if (Token::Match(tok2, ")|}")) { + if (indentlevel <= 2) + break; + --indentlevel; + } else if (indentlevel == 2 && tok2->str() == varname && Token::Match(tok2->previous(), "%type%|*")) + // declaring variable in inner scope with same name as lhs variable + break; + } + if (indentlevel == 2 && Token::simpleMatch(tok2, "} )")) { + tok2 = tok2->tokAt(-3); + if (Token::Match(tok2, "[;{}] %num%|%name% ;")) { + tok2->insertToken("="); + tok2->insertToken(tok->next()->str()); + tok2->next()->varId(tok->next()->varId()); + tok->deleteNext(3); + tok2->tokAt(5)->deleteNext(); + } + } + } + } +} + +// Remove __asm.. +void Tokenizer::simplifyAsm() +{ + std::string instruction; + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "__asm|_asm|asm {") && + tok->next()->link()->next()) { + instruction = tok->tokAt(2)->stringifyList(tok->next()->link()); + Token::eraseTokens(tok, tok->next()->link()->next()); + } + + else if (Token::Match(tok, "asm|__asm|__asm__ volatile|__volatile|__volatile__| (")) { + // Goto "(" + Token *partok = tok->next(); + if (partok->str() != "(") + partok = partok->next(); + instruction = partok->next()->stringifyList(partok->link()); + Token::eraseTokens(tok, partok->link()->next()); + } + + else if (Token::Match(tok, "_asm|__asm")) { + Token *endasm = tok->next(); + const Token *firstSemiColon = nullptr; + int comment = 0; + while (Token::Match(endasm, "%num%|%name%|,|:|;") || (endasm && endasm->linenr() == comment)) { + if (Token::Match(endasm, "_asm|__asm|__endasm")) + break; + if (endasm->str() == ";") { + comment = endasm->linenr(); + if (!firstSemiColon) + firstSemiColon = endasm; + } + endasm = endasm->next(); + } + if (Token::simpleMatch(endasm, "__endasm")) { + instruction = tok->next()->stringifyList(endasm); + Token::eraseTokens(tok, endasm->next()); + if (!Token::simpleMatch(tok->next(), ";")) + tok->insertToken(";"); + } else if (firstSemiColon) { + instruction = tok->next()->stringifyList(firstSemiColon); + Token::eraseTokens(tok, firstSemiColon); + } else if (!endasm) { + instruction = tok->next()->stringifyList(endasm); + Token::eraseTokens(tok, endasm); + tok->insertToken(";"); + } else + continue; + } + + else + continue; + + if (Token::Match(tok->previous(), ") %name% %name% (")) { + tok->deleteThis(); + continue; + } + + // insert "asm ( "instruction" )" + tok->str("asm"); + if (tok->strAt(1) != ";" && tok->strAt(1) != "{") + tok->insertToken(";"); + tok->insertToken(")"); + tok->insertToken("\"" + instruction + "\""); + tok->insertToken("("); + + tok = tok->next(); + Token::createMutualLinks(tok, tok->tokAt(2)); + + //move the new tokens in the same line as ";" if available + tok = tok->tokAt(2); + if (tok->next() && tok->next()->str() == ";" && + tok->next()->linenr() != tok->linenr()) { + const int endposition = tok->next()->linenr(); + tok = tok->tokAt(-3); + for (int i = 0; i < 4; ++i) { + tok = tok->next(); + tok->linenr(endposition); + } + } + } +} + +void Tokenizer::simplifyAsm2() +{ + // Block declarations: ^{} + // A C extension used to create lambda like closures. + + // Put ^{} statements in asm() + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->str() != "^") + continue; + + if (Token::simpleMatch(tok, "^ {") || (Token::simpleMatch(tok->linkAt(1), ") {") && tok->strAt(-1) != "operator")) { + Token * start = tok; + while (start && !Token::Match(start, "[,(;{}=]")) { + if (start->link() && Token::Match(start, ")|]|>")) + start = start->link(); + start = start->previous(); + } + + const Token *last = tok->next()->link(); + if (Token::simpleMatch(last, ") {")) + last = last->linkAt(1); + last = last->next(); + while (last && !Token::Match(last, "%cop%|,|;|{|}|)")) { + if (Token::Match(last, "(|[")) + last = last->link(); + last = last->next(); + } + + if (start && last) { + std::string asmcode; + while (start->next() != last) { + asmcode += start->next()->str(); + start->deleteNext(); + } + if (last->str() == "}") + start->insertToken(";"); + start->insertToken(")"); + start->insertToken("\"" + asmcode + "\""); + start->insertToken("("); + start->insertToken("asm"); + start->tokAt(2)->link(start->tokAt(4)); + start->tokAt(4)->link(start->tokAt(2)); + tok = start->tokAt(4); + } + } + } +} + +void Tokenizer::simplifyAt() +{ + std::set var; + + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "%name%|] @ %num%|%name%|%str%|(")) { + const Token *end = tok->tokAt(2); + if (end->isLiteral()) + end = end->next(); + else if (end->str() == "(") { + int par = 0; + while ((end = end->next()) != nullptr) { + if (end->str() == "(") + par++; + else if (end->str() == ")") { + if (--par < 0) + break; + } + } + end = end ? end->next() : nullptr; + } else if (var.find(end->str()) != var.end()) + end = end->next(); + else + continue; + + if (Token::Match(end, ": %num% ;")) + end = end->tokAt(2); + + if (Token::Match(end, "[;=]")) { + if (tok->isName()) + var.insert(tok->str()); + tok->isAtAddress(true); + Token::eraseTokens(tok, end); + } + } + + // keywords in compiler from cosmic software for STM8 + // TODO: Should use platform configuration. + if (Token::Match(tok, "@ builtin|eeprom|far|inline|interrupt|near|noprd|nostack|nosvf|packed|stack|svlreg|tiny|vector")) { + tok->str(tok->next()->str() + "@"); + tok->deleteNext(); + } + } +} + +// Simplify bitfields +void Tokenizer::simplifyBitfields() +{ + bool goback = false; + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (goback) { + goback = false; + tok = tok->previous(); + } + Token *last = nullptr; + + if (Token::simpleMatch(tok, "for (")) + tok = tok->linkAt(1); + + if (!Token::Match(tok, ";|{|}|public:|protected:|private:")) + continue; + + bool isEnum = false; + if (tok->str() == "}") { + const Token *type = tok->link()->previous(); + while (type && type->isName()) { + if (type->str() == "enum") { + isEnum = true; + break; + } + type = type->previous(); + } + } + + if (Token::Match(tok->next(), "const| %type% %name% :") && + !Token::Match(tok->next(), "case|public|protected|private|class|struct") && + !Token::simpleMatch(tok->tokAt(2), "default :")) { + Token *tok1 = (tok->next()->str() == "const") ? tok->tokAt(3) : tok->tokAt(2); + if (Token::Match(tok1, "%name% : %num% [;=]")) + tok1->setBits(MathLib::toBigNumber(tok1->strAt(2))); + if (tok1 && tok1->tokAt(2) && + (Token::Match(tok1->tokAt(2), "%bool%|%num%") || + !Token::Match(tok1->tokAt(2), "public|protected|private| %type% ::|<|,|{|;"))) { + while (tok1->next() && !Token::Match(tok1->next(), "[;,)]{}=]")) { + if (Token::Match(tok1->next(), "[([]")) + Token::eraseTokens(tok1, tok1->next()->link()); + tok1->deleteNext(); + } + + last = tok1->next(); + } + } else if (isEnum && Token::Match(tok, "} %name%| : %num% ;")) { + if (tok->next()->str() == ":") { + tok->deleteNext(2); + tok->insertToken("Anonymous"); + } else { + tok->next()->deleteNext(2); + } + } else if (Token::Match(tok->next(), "const| %type% : %num%|%bool% ;") && + tok->next()->str() != "default") { + const int offset = (tok->next()->str() == "const") ? 1 : 0; + if (!Token::Match(tok->tokAt(3 + offset), "[{};()]")) { + tok->deleteNext(4 + offset); + goback = true; + } + } + + if (last && last->str() == ",") { + Token * tok1 = last; + tok1->str(";"); + + const Token *const tok2 = tok->next(); + tok1->insertToken(tok2->str()); + tok1 = tok1->next(); + tok1->isSigned(tok2->isSigned()); + tok1->isUnsigned(tok2->isUnsigned()); + tok1->isLong(tok2->isLong()); + } + } +} + +static bool isStdContainerOrIterator(const Token* tok, const Settings& settings) +{ + const Library::Container* ctr = settings.library.detectContainerOrIterator(tok, nullptr, /*withoutStd*/ true); + return ctr && startsWith(ctr->startPattern, "std ::"); +} + +static bool isStdSmartPointer(const Token* tok, const Settings& settings) +{ + const Library::SmartPointer* ptr = settings.library.detectSmartPointer(tok, /*withoutStd*/ true); + return ptr && startsWith(ptr->name, "std::"); +} + +// Add std:: in front of std classes, when using namespace std; was given +void Tokenizer::simplifyNamespaceStd() +{ + if (!isCPP()) + return; + + std::set userFunctions; + + for (Token* tok = Token::findsimplematch(list.front(), "using namespace std ;"); tok; tok = tok->next()) { + bool insert = false; + if (Token::Match(tok, "enum class|struct| %name%| :|{")) { // Don't replace within enum definitions + skipEnumBody(tok); + } + if (!tok->isName() || tok->isKeyword() || tok->isStandardType() || tok->varId()) + continue; + if (Token::Match(tok->previous(), ".|::|namespace")) + continue; + if (Token::simpleMatch(tok->next(), "(")) { + if (isFunctionHead(tok->next(), "{")) + userFunctions.insert(tok->str()); + else if (isFunctionHead(tok->next(), ";")) { + const Token *start = tok; + while (Token::Match(start->previous(), "%type%|*|&")) + start = start->previous(); + if (start != tok && start->isName() && !start->isKeyword() && (!start->previous() || Token::Match(start->previous(), "[;{}]"))) + userFunctions.insert(tok->str()); + } + if (userFunctions.find(tok->str()) == userFunctions.end() && mSettings.library.matchArguments(tok, "std::" + tok->str())) + insert = true; + } else if (Token::simpleMatch(tok->next(), "<") && + (isStdContainerOrIterator(tok, mSettings) || isStdSmartPointer(tok, mSettings))) + insert = true; + else if (mSettings.library.hasAnyTypeCheck("std::" + tok->str()) || + mSettings.library.podtype("std::" + tok->str()) || + isStdContainerOrIterator(tok, mSettings)) + 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("::"); + } + } + + for (Token* tok = list.front(); tok; tok = tok->next()) { + if (Token::simpleMatch(tok, "using namespace std ;")) { + Token::eraseTokens(tok, tok->tokAt(4)); + tok->deleteThis(); + } + } +} + + +void Tokenizer::simplifyMicrosoftMemoryFunctions() +{ + // skip if not Windows + if (!mSettings.platform.isWindows()) + return; + + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->strAt(1) != "(") + continue; + + if (Token::Match(tok, "CopyMemory|RtlCopyMemory|RtlCopyBytes")) { + tok->str("memcpy"); + } else if (Token::Match(tok, "MoveMemory|RtlMoveMemory")) { + tok->str("memmove"); + } else if (Token::Match(tok, "FillMemory|RtlFillMemory|RtlFillBytes")) { + // FillMemory(dst, len, val) -> memset(dst, val, len) + tok->str("memset"); + + Token *tok1 = tok->tokAt(2); + if (tok1) + tok1 = tok1->nextArgument(); // Second argument + if (tok1) { + Token *tok2 = tok1->nextArgument(); // Third argument + + if (tok2) + Token::move(tok1->previous(), tok2->tokAt(-2), tok->next()->link()->previous()); // Swap third with second argument + } + } else if (Token::Match(tok, "ZeroMemory|RtlZeroMemory|RtlZeroBytes|RtlSecureZeroMemory")) { + // ZeroMemory(dst, len) -> memset(dst, 0, len) + tok->str("memset"); + + Token *tok1 = tok->tokAt(2); + if (tok1) + tok1 = tok1->nextArgument(); // Second argument + + if (tok1) { + tok1 = tok1->previous(); + tok1->insertToken("0"); + tok1 = tok1->next(); + tok1->insertToken(","); + } + } else if (Token::simpleMatch(tok, "RtlCompareMemory")) { + // RtlCompareMemory(src1, src2, len) -> memcmp(src1, src2, len) + tok->str("memcmp"); + // For the record, when memcmp returns 0, both strings are equal. + // When RtlCompareMemory returns len, both strings are equal. + // It might be needed to improve this replacement by something + // like ((len - memcmp(src1, src2, len)) % (len + 1)) to + // respect execution path (if required) + } + } +} + +namespace { + struct triplet { + triplet(const char* m, const char* u) : mbcs(m), unicode(u) {} + std::string mbcs, unicode; + }; + + const std::map apis = { + std::make_pair("_topen", triplet("open", "_wopen")), + std::make_pair("_tsopen_s", triplet("_sopen_s", "_wsopen_s")), + std::make_pair("_tfopen", triplet("fopen", "_wfopen")), + std::make_pair("_tfopen_s", triplet("fopen_s", "_wfopen_s")), + std::make_pair("_tfreopen", triplet("freopen", "_wfreopen")), + std::make_pair("_tfreopen_s", triplet("freopen_s", "_wfreopen_s")), + std::make_pair("_tcscat", triplet("strcat", "wcscat")), + std::make_pair("_tcschr", triplet("strchr", "wcschr")), + std::make_pair("_tcscmp", triplet("strcmp", "wcscmp")), + std::make_pair("_tcsdup", triplet("strdup", "wcsdup")), + std::make_pair("_tcscpy", triplet("strcpy", "wcscpy")), + std::make_pair("_tcslen", triplet("strlen", "wcslen")), + std::make_pair("_tcsncat", triplet("strncat", "wcsncat")), + std::make_pair("_tcsncpy", triplet("strncpy", "wcsncpy")), + std::make_pair("_tcsnlen", triplet("strnlen", "wcsnlen")), + std::make_pair("_tcsrchr", triplet("strrchr", "wcsrchr")), + std::make_pair("_tcsstr", triplet("strstr", "wcsstr")), + std::make_pair("_tcstok", triplet("strtok", "wcstok")), + std::make_pair("_ftprintf", triplet("fprintf", "fwprintf")), + std::make_pair("_tprintf", triplet("printf", "wprintf")), + std::make_pair("_stprintf", triplet("sprintf", "swprintf")), + std::make_pair("_sntprintf", triplet("_snprintf", "_snwprintf")), + std::make_pair("_ftscanf", triplet("fscanf", "fwscanf")), + std::make_pair("_tscanf", triplet("scanf", "wscanf")), + std::make_pair("_stscanf", triplet("sscanf", "swscanf")), + std::make_pair("_ftprintf_s", triplet("fprintf_s", "fwprintf_s")), + std::make_pair("_tprintf_s", triplet("printf_s", "wprintf_s")), + std::make_pair("_stprintf_s", triplet("sprintf_s", "swprintf_s")), + std::make_pair("_sntprintf_s", triplet("_snprintf_s", "_snwprintf_s")), + std::make_pair("_ftscanf_s", triplet("fscanf_s", "fwscanf_s")), + std::make_pair("_tscanf_s", triplet("scanf_s", "wscanf_s")), + std::make_pair("_stscanf_s", triplet("sscanf_s", "swscanf_s")) + }; +} + +void Tokenizer::simplifyMicrosoftStringFunctions() +{ + // skip if not Windows + if (!mSettings.platform.isWindows()) + return; + + const bool ansi = mSettings.platform.type == Platform::Type::Win32A; + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->strAt(1) != "(") + continue; + + const std::map::const_iterator match = apis.find(tok->str()); + if (match!=apis.end()) { + tok->str(ansi ? match->second.mbcs : match->second.unicode); + tok->originalName(match->first); + } else if (Token::Match(tok, "_T|_TEXT|TEXT ( %char%|%str% )")) { + tok->deleteNext(); + tok->deleteThis(); + tok->deleteNext(); + if (!ansi) { + tok->isLong(true); + if (tok->str()[0] != 'L') + tok->str("L" + tok->str()); + } + while (Token::Match(tok->next(), "_T|_TEXT|TEXT ( %char%|%str% )")) { + tok->next()->deleteNext(); + tok->next()->deleteThis(); + tok->next()->deleteNext(); + tok->concatStr(tok->next()->str()); + tok->deleteNext(); + } + } + } +} + +// Remove Borland code +void Tokenizer::simplifyBorland() +{ + // skip if not Windows + if (!mSettings.platform.isWindows()) + return; + if (isC()) + return; + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "( __closure * %name% )")) { + tok->deleteNext(); + } + } + + // I think that these classes are always declared at the outer scope + // I save some time by ignoring inner classes. + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (tok->str() == "{" && !Token::Match(tok->tokAt(-2), "namespace %type%")) { + tok = tok->link(); + if (!tok) + break; + } else if (Token::Match(tok, "class %name% :|{")) { + while (tok && tok->str() != "{" && tok->str() != ";") + tok = tok->next(); + if (!tok) + break; + if (tok->str() == ";") + continue; + + const Token* end = tok->link()->next(); + for (Token *tok2 = tok->next(); tok2 != end; tok2 = tok2->next()) { + if (tok2->str() == "__property" && + Token::Match(tok2->previous(), ";|{|}|protected:|public:|__published:")) { + while (tok2->next() && !Token::Match(tok2->next(), "{|;")) + tok2->deleteNext(); + tok2->deleteThis(); + if (tok2->str() == "{") { + Token::eraseTokens(tok2, tok2->link()); + tok2->deleteNext(); + tok2->deleteThis(); + + // insert "; __property ;" + tok2->previous()->insertToken(";"); + tok2->previous()->insertToken("__property"); + tok2->previous()->insertToken(";"); + } + } + } + } + } +} + +void Tokenizer::createSymbolDatabase() +{ + if (!mSymbolDatabase) + mSymbolDatabase = new SymbolDatabase(*this, mSettings, mErrorLogger); + mSymbolDatabase->validate(); +} + +bool Tokenizer::operatorEnd(const Token * tok) +{ + if (tok && tok->str() == ")") { + if (isFunctionHead(tok, "{|;|?|:|[")) + return true; + + tok = tok->next(); + while (tok && !Token::Match(tok, "[=;{),]")) { + if (Token::Match(tok, "const|volatile|override")) { + tok = tok->next(); + } else if (tok->str() == "noexcept") { + tok = tok->next(); + if (tok && tok->str() == "(") { + tok = tok->link()->next(); + } + } else if (tok->str() == "throw" && tok->next() && tok->next()->str() == "(") { + tok = tok->next()->link()->next(); + } + // unknown macros ") MACRO {" and ") MACRO(...) {" + else if (tok->isUpperCaseName()) { + tok = tok->next(); + if (tok && tok->str() == "(") { + tok = tok->link()->next(); + } + } else if (Token::Match(tok, "%op% !!(") || + (Token::Match(tok, "%op% (") && !isFunctionHead(tok->next(), "{"))) + break; + else + return false; + } + + return true; + } + + return false; +} + +void Tokenizer::simplifyOperatorName() +{ + if (isC()) + return; + + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "using|:: operator %op%|%name% ;")) { + tok->next()->str("operator" + tok->strAt(2)); + tok->next()->deleteNext(); + continue; + } + + if (tok->str() != "operator") + continue; + // operator op + if (Token::Match(tok, "operator %op% (") && !operatorEnd(tok->linkAt(2))) { + tok->str(tok->str() + tok->next()->str()); + tok->deleteNext(); + continue; + } + std::string op; + Token *par = tok->next(); + bool done = false; + while (!done && par) { + done = true; + if (par->isName()) { + op += par->str(); + par = par->next(); + // merge namespaces eg. 'operator std :: string () const {' + if (Token::Match(par, ":: %name%|%op%|.")) { + op += par->str(); + par = par->next(); + } + done = false; + } else if (Token::Match(par, ".|%op%|,")) { + // check for operator in template + if (par->str() == "," && !op.empty()) + break; + if (!(Token::Match(par, "<|>") && !op.empty())) { + op += par->str() == "." ? par->originalName() : par->str(); + par = par->next(); + done = false; + } + } else if (Token::simpleMatch(par, "[ ]")) { + op += "[]"; + par = par->tokAt(2); + done = false; + } else if (Token::Match(par, "( *| )")) { + // break out and simplify.. + if (operatorEnd(par->next())) + break; + + while (par->str() != ")") { + op += par->str(); + par = par->next(); + } + op += ")"; + par = par->next(); + if (Token::simpleMatch(par, "...")) { + op.clear(); + par = nullptr; + break; + } + done = false; + } else if (Token::Match(par, "\"\" %name% )| (|;|<")) { + op += "\"\""; + op += par->strAt(1); + par = par->tokAt(2); + if (par->str() == ")") { + par->link()->deleteThis(); + par = par->next(); + par->deletePrevious(); + tok = par->tokAt(-3); + } + done = true; + } else if (par->str() == "::") { + op += par->str(); + par = par->next(); + done = false; + } else if (par->str() == ";" || par->str() == ")") { + done = true; + } else if (par->str() != "(") { + syntaxError(par, "operator"); + } + } + + const bool returnsRef = Token::simpleMatch(par, "( & (") && tok->next()->isName(); + if (par && !op.empty()) { + if (returnsRef) { + par->next()->insertToken("operator" + op)->isOperatorKeyword(true); + tok->deleteThis(); + } + else { + tok->str("operator" + op); + Token::eraseTokens(tok, par); + } + } + + if (!op.empty() && !returnsRef) + tok->isOperatorKeyword(true); + } + + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "%op% %str% %name%")) { + const std::string name = tok->strAt(2); + Token * const str = tok->next(); + str->deleteNext(); + tok->insertToken("operator\"\"" + name); + tok = tok->next(); + tok->isOperatorKeyword(true); + tok->insertToken("("); + str->insertToken(")"); + Token::createMutualLinks(tok->next(), str->next()); + str->insertToken(std::to_string(Token::getStrLength(str))); + str->insertToken(","); + } + } + + if (mSettings.debugwarnings) { + const Token *tok = list.front(); + + while ((tok = Token::findsimplematch(tok, "operator")) != nullptr) { + reportError(tok, Severity::debug, "debug", + "simplifyOperatorName: found unsimplified operator name"); + tok = tok->next(); + } + } +} + +void Tokenizer::simplifyOverloadedOperators() +{ + if (isC()) + return; + std::set classNames; + std::set classVars; + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!tok->isName()) + continue; + + if (Token::simpleMatch(tok, "this ) (") && Token::simpleMatch(tok->tokAt(-2), "( *")) { + tok = tok->next(); + tok->insertToken("operator()"); + tok->insertToken("."); + continue; + } + + // Get classes that have operator() member + if (Token::Match(tok, "class|struct %name% [:{]")) { + int indent = 0; + for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { + if (tok2->str() == "}") + break; + if (indent == 0 && tok2->str() == ";") + break; + if (tok2->str() == "{") { + if (indent == 0) + ++indent; + else + tok2 = tok2->link(); + } else if (indent == 1 && Token::simpleMatch(tok2, "operator() (") && isFunctionHead(tok2->next(), ";{")) { + classNames.insert(tok->strAt(1)); + break; + } + } + } + + // Get variables that have operator() member + if (Token::Match(tok, "%type% &| %var%") && classNames.find(tok->str()) != classNames.end()) { + tok = tok->next(); + while (!tok->isName()) + tok = tok->next(); + classVars.insert(tok->varId()); + } + + // Simplify operator() calls + if (Token::Match(tok, "%var% (") && classVars.find(tok->varId()) != classVars.end()) { + // constructor init list.. + if (Token::Match(tok->previous(), "[:,]")) { + const Token *start = tok->previous(); + while (Token::simpleMatch(start, ",")) { + if (Token::simpleMatch(start->previous(), ")")) + start = start->linkAt(-1); + else + break; + if (Token::Match(start->previous(), "%name%")) + start = start->tokAt(-2); + else + break; + } + const Token *after = tok->linkAt(1); + while (Token::Match(after, ")|} , %name% (|{")) + after = after->linkAt(3); + + // Do not simplify initlist + if (Token::simpleMatch(start, ":") && Token::simpleMatch(after, ") {")) + continue; + } + + tok->insertToken("operator()"); + tok->insertToken("."); + } + } +} + +// remove unnecessary member qualification.. +void Tokenizer::removeUnnecessaryQualification() +{ + if (isC()) + return; + + std::vector classInfo; + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "class|struct|namespace %type% :|{") && + (!tok->previous() || tok->previous()->str() != "enum")) { + Space info; + info.isNamespace = tok->str() == "namespace"; + tok = tok->next(); + info.className = tok->str(); + tok = tok->next(); + while (tok && tok->str() != "{") + tok = tok->next(); + if (!tok) + return; + info.bodyEnd = tok->link(); + classInfo.push_back(std::move(info)); + } else if (!classInfo.empty()) { + if (tok == classInfo.back().bodyEnd) + classInfo.pop_back(); + else if (tok->str() == classInfo.back().className && + !classInfo.back().isNamespace && tok->previous()->str() != ":" && + (Token::Match(tok, "%type% :: ~| %type% (") || + Token::Match(tok, "%type% :: operator"))) { + const Token *tok1 = tok->tokAt(3); + if (tok->strAt(2) == "operator") { + // check for operator () + if (tok1->str() == "(") + tok1 = tok1->next(); + + while (tok1 && tok1->str() != "(") { + if (tok1->str() == ";") + break; + tok1 = tok1->next(); + } + if (!tok1 || tok1->str() != "(") + continue; + } else if (tok->strAt(2) == "~") + tok1 = tok1->next(); + + if (!tok1 || !Token::Match(tok1->link(), ") const| {|;|:")) { + continue; + } + + const bool isConstructorOrDestructor = + Token::Match(tok, "%type% :: ~| %type%") && (tok->strAt(2) == tok->str() || (tok->strAt(2) == "~" && tok->strAt(3) == tok->str())); + if (!isConstructorOrDestructor) { + bool isPrependedByType = Token::Match(tok->previous(), "%type%"); + if (!isPrependedByType) { + const Token* tok2 = tok->tokAt(-2); + isPrependedByType = Token::Match(tok2, "%type% *|&"); + } + if (!isPrependedByType) { + const Token* tok3 = tok->tokAt(-3); + isPrependedByType = Token::Match(tok3, "%type% * *|&"); + } + if (!isPrependedByType) { + // It's not a constructor declaration and it's not a function declaration so + // this is a function call which can have all the qualifiers just fine - skip. + continue; + } + } + } + } + } +} + +void Tokenizer::printUnknownTypes() const +{ + if (!mSymbolDatabase) + return; + + std::vector> unknowns; + + for (int i = 1; i <= mVarId; ++i) { + const Variable *var = mSymbolDatabase->getVariableFromVarId(i); + if (!var) + continue; + // is unknown type? + if (var->type() || var->typeStartToken()->isStandardType()) + continue; + + std::string name; + const Token * nameTok; + + // single token type? + if (var->typeStartToken() == var->typeEndToken()) { + nameTok = var->typeStartToken(); + name = nameTok->str(); + } + + // complicated type + else { + const Token *tok = var->typeStartToken(); + int level = 0; + + nameTok = tok; + + while (tok) { + // skip pointer and reference part of type + if (level == 0 && Token::Match(tok, "*|&")) + break; + + name += tok->str(); + + if (Token::Match(tok, "struct|union|enum")) + name += " "; + + // pointers and references are OK in template + else if (tok->str() == "<") + ++level; + else if (tok->str() == ">") + --level; + + if (tok == var->typeEndToken()) + break; + + tok = tok->next(); + } + } + + unknowns.emplace_back(std::move(name), nameTok); + } + + if (!unknowns.empty()) { + std::string last; + int count = 0; + + for (auto it = unknowns.cbegin(); it != unknowns.cend(); ++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::prepareTernaryOpForAST() +{ + // http://en.cppreference.com/w/cpp/language/operator_precedence says about ternary operator: + // "The expression in the middle of the conditional operator (between ? and :) is parsed as if parenthesized: its precedence relative to ?: is ignored." + // The AST parser relies on this function to add such parentheses where necessary. + for (Token* tok = list.front(); tok; tok = tok->next()) { + if (tok->str() == "?") { + bool parenthesesNeeded = false; + int depth = 0; + Token* tok2 = tok->next(); + for (; tok2; tok2 = tok2->next()) { + if (tok2->link() && Token::Match(tok2, "[|(|<")) + tok2 = tok2->link(); + else if (tok2->str() == ":") { + if (depth == 0) + break; + depth--; + } else if (tok2->str() == ";" || (tok2->link() && tok2->str() != "{" && tok2->str() != "}")) + break; + else if (tok2->str() == ",") + parenthesesNeeded = true; + else if (tok2->str() == "<") + parenthesesNeeded = true; + else if (tok2->str() == "?") { + depth++; + parenthesesNeeded = true; + } + } + if (parenthesesNeeded && tok2 && tok2->str() == ":") { + tok->insertToken("("); + tok2->insertTokenBefore(")"); + Token::createMutualLinks(tok->next(), tok2->previous()); + } + } + } +} + +void Tokenizer::reportError(const Token* tok, const Severity 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 severity, const std::string& id, const std::string& msg, bool inconclusive) const +{ + const ErrorMessage errmsg(callstack, &list, severity, id, msg, inconclusive ? Certainty::inconclusive : Certainty::normal); + mErrorLogger.reportErr(errmsg); +} + +void Tokenizer::setPodTypes() +{ + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!tok->isName() || tok->varId()) + continue; + + // pod type + const Library::PodType *podType = mSettings.library.podtype(tok->str()); + if (podType) { + const Token *prev = tok->previous(); + while (prev && prev->isName()) + prev = prev->previous(); + if (prev && !Token::Match(prev, ";|{|}|,|(")) + continue; + tok->isStandardType(true); + } + } +} + +const Token *Tokenizer::findSQLBlockEnd(const Token *tokSQLStart) +{ + const Token *tokLastEnd = nullptr; + for (const Token *tok = tokSQLStart->tokAt(2); tok != nullptr; tok = tok->next()) { + if (tokLastEnd == nullptr && tok->str() == ";") + tokLastEnd = tok; + else if (tok->str() == "__CPPCHECK_EMBEDDED_SQL_EXEC__") { + if (Token::simpleMatch(tok->tokAt(-2), "END - __CPPCHECK_EMBEDDED_SQL_EXEC__ ;")) + return tok->next(); + return tokLastEnd; + } else if (Token::Match(tok, "{|}|==|&&|!|^|<<|>>|++|+=|-=|/=|*=|>>=|<<=|~")) + break; // We are obviously outside the SQL block + } + + return tokLastEnd; +} + +void Tokenizer::simplifyNestedNamespace() +{ + if (!isCPP()) + return; + + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (Token::Match(tok, "namespace %name% ::") && tok->strAt(-1) != "using") { + Token * tok2 = tok->tokAt(2); + + // validate syntax + while (Token::Match(tok2, ":: %name%")) + tok2 = tok2->tokAt(2); + + if (!tok2 || tok2->str() != "{") + return; // syntax error + + std::stack links; + tok2 = tok->tokAt(2); + + while (tok2->str() == "::") { + links.push(tok2); + tok2->str("{"); + tok2->insertToken("namespace"); + tok2 = tok2->tokAt(3); + } + + tok = tok2; + + if (!links.empty() && tok2->str() == "{") { + tok2 = tok2->link(); + while (!links.empty()) { + tok2->insertToken("}"); + tok2 = tok2->next(); + Token::createMutualLinks(links.top(), tok2); + links.pop(); + } + } + } + } +} + +void Tokenizer::simplifyCoroutines() +{ + if (!isCPP() || mSettings.standards.cpp < Standards::CPP20) + return; + for (Token *tok = list.front(); tok; tok = tok->next()) { + if (!tok->isName() || !Token::Match(tok, "co_return|co_yield|co_await")) + continue; + Token *end = tok->next(); + while (end && end->str() != ";") { + if (Token::Match(end, "[({[]")) + end = end->link(); + else if (Token::Match(end, "[)]}]")) + break; + end = end->next(); + } + if (Token::simpleMatch(end, ";")) { + tok->insertToken("("); + end->previous()->insertToken(")"); + Token::createMutualLinks(tok->next(), end->previous()); + } + } +} + +static bool sameTokens(const Token *first, const Token *last, const Token *other) +{ + while (other && first->str() == other->str()) { + if (first == last) + return true; + first = first->next(); + other = other->next(); + } + + return false; +} + +static bool alreadyHasNamespace(const Token *first, const Token *last, const Token *end) +{ + while (end && last->str() == end->str()) { + if (first == last) + return true; + last = last->previous(); + end = end->previous(); + } + + return false; +} + +static Token * deleteAlias(Token * tok) +{ + Token::eraseTokens(tok, Token::findsimplematch(tok, ";")); + + // delete first token + tok->deleteThis(); + + // delete ';' if not last token + tok->deleteThis(); + + return tok; +} + +void Tokenizer::simplifyNamespaceAliases() +{ + if (!isCPP()) + return; + + int scope = 0; + + for (Token *tok = list.front(); tok; tok = tok->next()) { + bool isPrev{}; + if (tok->str() == "{") + scope++; + else if (tok->str() == "}") + scope--; + else if (Token::Match(tok, "namespace %name% =") || (isPrev = Token::Match(tok->previous(), "namespace %name% ="))) { + if (isPrev) + tok = tok->previous(); + const std::string name(tok->next()->str()); + Token * tokNameStart = tok->tokAt(3); + Token * tokNameEnd = tokNameStart; + + while (tokNameEnd && tokNameEnd->next() && tokNameEnd->next()->str() != ";") { + if (tokNameEnd->str() == "(") { + if (tokNameEnd->previous()->isName()) + unknownMacroError(tokNameEnd->previous()); + else + syntaxError(tokNameEnd); + } + tokNameEnd = tokNameEnd->next(); + } + + if (!tokNameEnd) + return; // syntax error + + int endScope = scope; + Token * tokLast = tokNameEnd->next(); + if (!tokLast) + return; + Token * tokNext = tokLast->next(); + Token * tok2 = tokNext; + + while (tok2 && endScope >= scope) { + if (Token::simpleMatch(tok2, "{")) + endScope++; + else if (Token::simpleMatch(tok2, "}")) + endScope--; + else if (tok2->str() == name) { + if (Token::Match(tok2->previous(), "namespace %name% =")) { + // check for possible duplicate aliases + if (sameTokens(tokNameStart, tokNameEnd, tok2->tokAt(2))) { + // delete duplicate + tok2 = deleteAlias(tok2->previous()); + continue; + } + // conflicting declaration (syntax error) + // cppcheck-suppress duplicateBranch - remove when TODO below is addressed + if (endScope == scope) { + // delete conflicting declaration + tok2 = deleteAlias(tok2->previous()); + } + + // new declaration + else { + // TODO: use the new alias in this scope + tok2 = deleteAlias(tok2->previous()); + } + continue; + } + + if (tok2->strAt(1) == "::" && !alreadyHasNamespace(tokNameStart, tokNameEnd, tok2)) { + if (Token::simpleMatch(tok2->tokAt(-1), "::") && tokNameStart->str() == "::") + tok2->deletePrevious(); + tok2->str(tokNameStart->str()); + Token * tok3 = tokNameStart; + while (tok3 != tokNameEnd) { + tok2->insertToken(tok3->next()->str()); + tok2 = tok2->next(); + tok3 = tok3->next(); + } + } + } + tok2 = tok2->next(); + } + + if (tok->previous() && tokNext) { + Token::eraseTokens(tok->previous(), tokNext); + tok = tokNext->previous(); + } else if (tok->previous()) { + Token::eraseTokens(tok->previous(), tokLast); + tok = tokLast; + } else if (tokNext) { + Token::eraseTokens(tok, tokNext); + tok->deleteThis(); + } else { + Token::eraseTokens(tok, tokLast); + tok->deleteThis(); + } + } + } +} + +void Tokenizer::setDirectives(std::list directives) +{ + mDirectives = std::move(directives); +} + +bool Tokenizer::hasIfdef(const Token *start, const Token *end) const +{ + const auto& directives = mDirectives; + return std::any_of(directives.cbegin(), directives.cend(), [&](const Directive& d) { + return startsWith(d.str, "#if") && + d.linenr >= start->linenr() && + d.linenr <= end->linenr() && + start->fileIndex() < list.getFiles().size() && + d.file == list.getFiles()[start->fileIndex()]; + }); +} + +bool Tokenizer::isPacked(const Token * bodyStart) const +{ + const auto& directives = mDirectives; + // TODO: should this return true if the #pragma exists in any line before the start token? + return std::any_of(directives.cbegin(), directives.cend(), [&](const Directive& d) { + return d.linenr < bodyStart->linenr() && d.str == "#pragma pack(1)" && d.file == list.getFiles().front(); + }); +} diff --git a/cppcheck-2.14.0/lib/tokenize.h b/cppcheck-2.14.0/lib/tokenize.h new file mode 100644 index 00000000..23c8650e --- /dev/null +++ b/cppcheck-2.14.0/lib/tokenize.h @@ -0,0 +1,688 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef tokenizeH +#define tokenizeH +//--------------------------------------------------------------------------- + +#include "config.h" +#include "tokenlist.h" + +#include +#include +#include +#include +#include + +class Settings; +class SymbolDatabase; +class TimerResults; +class Token; +class TemplateSimplifier; +class ErrorLogger; +struct Directive; // IWYU pragma: keep +enum class Severity; + +/// @addtogroup Core +/// @{ + +/** @brief The main purpose is to tokenize the source code. It also has functions that simplify the token list */ +class CPPCHECKLIB Tokenizer { + + friend class SymbolDatabase; + friend class TemplateSimplifier; + + friend class TestSimplifyTemplate; + friend class TestSimplifyTypedef; + friend class TestTokenizer; + +public: + explicit Tokenizer(const Settings & settings, ErrorLogger &errorLogger); + ~Tokenizer(); + + void setTimerResults(TimerResults *tr) { + mTimerResults = tr; + } + + /** Is the code C. Used for bailouts */ + bool isC() const { + return list.isC(); + } + + /** Is the code CPP. Used for bailouts */ + bool isCPP() const { + return list.isCPP(); + } + + /** + * Check if inner scope ends with a call to a noreturn function + * \param endScopeToken The '}' token + * \param unknown set to true if it's unknown if the scope is noreturn + * \return true if scope ends with a function call that might be 'noreturn' + */ + bool isScopeNoReturn(const Token *endScopeToken, bool *unknown = nullptr) const; + + bool simplifyTokens1(const std::string &configuration); + +private: + /** Set variable id */ + void setVarId(); + void setVarIdPass1(); + void setVarIdPass2(); + + /** + * Basic simplification of tokenlist + * + * @param FileName The filename to run; used to do + * markup checks. + * + * @return false if there is an error that requires aborting + * the checking of this file. + */ + bool simplifyTokenList1(const char FileName[]); + + /** + * If --check-headers=no has been given; then remove unneeded code in headers. + * - All executable code. + * - Unused types/variables/etc + */ + void simplifyHeadersAndUnusedTemplates(); + + /** + * Remove extra "template" keywords that are not used by Cppcheck + */ + void removeExtraTemplateKeywords(); + + + /** Split up template right angle brackets. + * foo < bar < >> => foo < bar < > > + */ + void splitTemplateRightAngleBrackets(bool check); + +public: + /** + * Calculates sizeof value for given type. + * @param type Token which will contain e.g. "int", "*", or string. + * @return sizeof for given type, or 0 if it can't be calculated. + */ + nonneg int sizeOfType(const Token* type) const; + nonneg int sizeOfType(const std::string& type) const; + +private: + void simplifyDebug(); + + /** Simplify assignment where rhs is a block : "x=({123;});" => "{x=123;}" */ + void simplifyAssignmentBlock(); + + /** Insert array size where it isn't given */ + void arraySize(); + void arraySizeAfterValueFlow(); // cppcheck-suppress functionConst + + /** Simplify labels and 'case|default' syntaxes. + */ + void simplifyLabelsCaseDefault(); + + /** simplify case ranges (gcc extension) + */ + void simplifyCaseRange(); + + /** Remove macros in global scope */ + void removeMacrosInGlobalScope(); + + void addSemicolonAfterUnknownMacro(); + + // Remove C99 and CPP11 _Pragma(str) + void removePragma(); + + /** Remove undefined macro in class definition: + * class DLLEXPORT Fred { }; + * class Fred FINAL : Base { }; + */ + void removeMacroInClassDef(); + + /** Add parentheses for sizeof: sizeof x => sizeof(x) */ + void sizeofAddParentheses(); + + /** + * Simplify variable declarations (split up) + * \param only_k_r_fpar Only simplify K&R function parameters + */ + void simplifyVarDecl(const bool only_k_r_fpar); + void simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, const bool only_k_r_fpar); // cppcheck-suppress functionConst // has side effects + + /** + * Simplify variable initialization + * '; int *p(0);' => '; int *p = 0;' + */ + void simplifyInitVar(); + static Token* initVar(Token* tok); + + /** + * 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 multiple assignments. + * Example: "a = b = c = 0;" => "a = 0; b = 0; c = 0;" + */ + void simplifyVariableMultipleAssign(); + + /** + * Simplify the 'C Alternative Tokens' + * Examples: + * "if(s and t)" => "if(s && t)" + * "while((r bitand s) and not t)" => while((r & s) && !t)" + * "a and_eq b;" => "a &= b;" + */ + bool simplifyCAlternativeTokens(); + + /** 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); + + // Convert "using ...;" to corresponding typedef + void simplifyUsingToTypedef(); + + /** + * typedef A mytype; + * mytype c; + * + * Becomes: + * typedef A mytype; + * A c; + */ + void simplifyTypedef(); + void simplifyTypedefCpp(); + /** + * Move typedef token to the left og the expression + */ + void simplifyTypedefLHS(); + + /** + */ + static bool isMemberFunction(const Token *openParen); + + /** + */ + bool simplifyUsing(); + void simplifyUsingError(const Token* usingStart, const Token* usingEnd); + + /** Simplify useless C++ empty namespaces, like: 'namespace %name% { }'*/ + void simplifyEmptyNamespaces(); + + /** Simplify "if else" */ + void elseif(); + + /** Simplify C++17/C++20 if/switch/for initialization expression */ + void simplifyIfSwitchForInit(); + + /** + * Reduces "; ;" to ";", except in "( ; ; )" + */ + void removeRedundantSemicolons(); + + /** 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(); + + /** + * Simplify functions like "void f(x) int x; {" + * into "void f(int x) {" + */ + void simplifyFunctionParameters(); + + /** Simplify function level try blocks: + * Convert "void f() try {} catch (int) {}" + * to "void f() { try {} catch (int) {} }" + */ + void simplifyFunctionTryCatch(); + + /** + * Simplify templates + */ + void simplifyTemplates(); + + void simplifyDoublePlusAndDoubleMinus(); + + void simplifyRedundantConsecutiveBraces(); + + void simplifyArrayAccessSyntax(); + + void simplifyParameterVoid(); + + void fillTypeSizes(); + + void combineOperators(); + + void combineStringAndCharLiterals(); + + void concatenateNegativeNumberAndAnyPositive(); + + void simplifyExternC(); + + void simplifyRoundCurlyParentheses(); + + void simplifyTypeIntrinsics(); + + void simplifySQL(); + + void checkForEnumsWithTypedef(); + + void findComplicatedSyntaxErrorsInTemplates(); + + /** + * 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); + +public: + /** + * is token pointing at function head? + * @param tok A '(' or ')' token in a possible function head + * @param endsWith string after function head + * @return token matching with endsWith if syntax seems to be a function head else nullptr + */ + static const Token * isFunctionHead(const Token *tok, const std::string &endsWith); + + bool hasIfdef(const Token *start, const Token *end) const; + + bool isPacked(const Token * bodyStart) const; + +private: + + /** Simplify pointer to standard type (C only) */ + void simplifyPointerToStandardType(); + + /** Simplify function pointers */ + void simplifyFunctionPointers(); + + /** + * Send error message to error logger about internal bug. + * @param tok the token that this bug concerns. + */ + NORETURN 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(); + + /** + * Set isCast() for C++ casts + */ + void markCppCasts(); + +public: + + /** Syntax error */ + NORETURN void syntaxError(const Token *tok, const std::string &code = emptyString) const; + + /** Syntax error. Unmatched character. */ + NORETURN void unmatchedToken(const Token *tok) const; + + /** Syntax error. C++ code in C file. */ + NORETURN void syntaxErrorC(const Token *tok, const std::string &what) const; + + /** Warn about unknown macro(s), configuration is recommended */ + NORETURN void unknownMacroError(const Token *tok1) const; + + void unhandledCharLiteral(const Token *tok, const std::string& msg) const; + +private: + + /** Report that there is an unhandled "class x y {" code */ + void unhandled_macro_class_x_y(const Token *tok) const; + + /** Check configuration (unknown macros etc) */ + void checkConfiguration() const; + void macroWithSemicolonError(const Token *tok, const std::string ¯oName) const; + + /** + * Is there C++ code in C file? + */ + void validateC() const; + + /** + * assert that tokens are ok - used during debugging for example + * to catch problems in simplifyTokenList1/2. + */ + void validate() const; + + /** Detect unknown macros and throw unknownMacro */ + void reportUnknownMacros() const; + + /** Detect garbage code and call syntaxError() if found. */ + void findGarbageCode() const; + + /** Detect garbage expression */ + static bool isGarbageExpr(const Token *start, const Token *end, bool allowSemicolon); + + /** + * Remove __declspec() + */ + void simplifyDeclspec(); + + /** + * Remove calling convention + */ + void simplifyCallingConvention(); + + /** + * Remove \__attribute\__ ((?)) + */ + void simplifyAttribute(); + + /** Get function token for a attribute */ + Token* getAttributeFuncTok(Token* tok, bool gccattr) const; + + /** + * Remove \__cppcheck\__ ((?)) + */ + void simplifyCppcheckAttribute(); + + /** Simplify c++20 spaceship operator */ + void simplifySpaceshipOperator(); + + /** + * Remove keywords "volatile", "inline", "register", and "restrict" + */ + void simplifyKeyword(); + + /** + * Remove __asm + */ + void simplifyAsm(); + + /** + * asm heuristics, Put ^{} statements in asm() + */ + void simplifyAsm2(); + + /** + * Simplify \@… (compiler extension) + */ + void simplifyAt(); + + /** + * Simplify bitfields - the field width is removed as we don't use it. + */ + void simplifyBitfields(); + + /** + * Remove unnecessary member qualification + */ + void removeUnnecessaryQualification(); + + /** + * Add std:: in front of std classes, when using namespace std; was given + */ + void simplifyNamespaceStd(); + + /** + * Convert Microsoft memory functions + * CopyMemory(dst, src, len) -> memcpy(dst, src, len) + * FillMemory(dst, len, val) -> memset(dst, val, len) + * MoveMemory(dst, src, len) -> memmove(dst, src, len) + * ZeroMemory(dst, len) -> memset(dst, 0, len) + */ + void simplifyMicrosoftMemoryFunctions(); + + /** + * Convert Microsoft string functions + * _tcscpy -> strcpy + */ + void simplifyMicrosoftStringFunctions(); + + /** + * Remove Borland code + */ + void simplifyBorland(); + + /** + * Collapse operator name tokens into single token + * operator = => operator= + */ + void simplifyOperatorName(); + + /** simplify overloaded operators: 'obj(123)' => 'obj . operator() ( 123 )' */ + void simplifyOverloadedOperators(); + + /** + * Remove [[attribute]] (C++11 and later) from TokenList + */ + void simplifyCPPAttribute(); + + /** + * Convert namespace aliases + */ + void simplifyNamespaceAliases(); + + /** + * Convert C++17 style nested namespace to older style + */ + void simplifyNestedNamespace(); + + /** + * Simplify coroutines - just put parentheses around arguments for + * co_* keywords so they can be handled like function calls in data + * flow. + */ + void simplifyCoroutines(); + + /** + * Prepare ternary operators with parentheses so that the AST can be created + * */ + void prepareTernaryOpForAST(); + + /** + * report error message + */ + void reportError(const Token* tok, const Severity severity, const std::string& id, const std::string& msg, bool inconclusive = false) const; + void reportError(const std::list& callstack, Severity severity, const std::string& id, const std::string& msg, bool inconclusive = false) const; + + bool duplicateTypedef(Token *&tokPtr, const Token *name, const Token *typeDef) const; + + void unsupportedTypedef(const Token *tok) const; + + static void setVarIdClassFunction(const std::string &classname, + Token * const startToken, + const Token * const endToken, + const std::map &varlist, + std::map>& structMembers, + nonneg int &varId_); + + /** + * Output list of unknown types. + */ + void printUnknownTypes() const; + + /** Find end of SQL (or PL/SQL) block */ + static const Token *findSQLBlockEnd(const Token *tokSQLStart); + + static bool operatorEnd(const Token * tok); + +public: + const SymbolDatabase *getSymbolDatabase() const { + return mSymbolDatabase; + } + void createSymbolDatabase(); + + /** print --debug output if debug flags match the simplification: + * 0=unknown/both simplifications + * 1=1st simplifications + * 2=2nd simplifications + */ + void printDebugOutput(int simplification) const; + + void dump(std::ostream &out) const; + + Token *deleteInvalidTypedef(Token *typeDef); + + /** + * Get variable count. + * @return number of variables + */ + nonneg int varIdCount() const { + return mVarId; + } + + /** + * Token list: stores all tokens. + */ + TokenList list; + // Implement tokens() as a wrapper for convenience when using the TokenList + const Token* tokens() const { + return list.front(); + } + + Token* tokens() { + return list.front(); + } + + /** + * Helper function to check whether number is one (1 or 0.1E+1 or 1E+0) or not? + * @param s the string to check + * @return true in case is is one and false otherwise. + */ + static bool isOneNumber(const std::string &s); + + /** + * Helper function to check 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); + + const Settings &getSettings() const { + return mSettings; + } + + void calculateScopes(); + + /** Disable copy constructor */ + Tokenizer(const Tokenizer &) = delete; + + /** Disable assignment operator */ + Tokenizer &operator=(const Tokenizer &) = delete; + + void setDirectives(std::list directives); + +private: + const Token *processFunc(const Token *tok2, bool inOperator) const; + Token *processFunc(Token *tok2, bool inOperator); + + /** + * Get new variable id. + * @return new variable id + */ + nonneg int newVarId() { + return ++mVarId; + } + + /** Set pod types */ + void setPodTypes(); + + /** settings */ + const Settings & mSettings; + + /** errorlogger */ + ErrorLogger& mErrorLogger; + + /** Symbol database that all checks etc can use */ + SymbolDatabase* mSymbolDatabase{}; + + TemplateSimplifier * const mTemplateSimplifier; + + /** E.g. "A" for code where "#ifdef A" is true. This is used to + print additional information in error situations. */ + std::string mConfiguration; + + /** sizeof information for known types */ + std::map mTypeSize; + + struct TypedefInfo { + std::string name; + std::string filename; + int lineNumber; + int column; + bool used; + }; + std::vector mTypedefInfo; + + std::list mDirectives; + + /** variable count */ + nonneg int mVarId{}; + + /** unnamed count "Unnamed0", "Unnamed1", "Unnamed2", ... */ + nonneg int mUnnamedCount{}; + + /** + * TimerResults + */ + TimerResults* mTimerResults{}; +}; + +/// @} + +//--------------------------------------------------------------------------- +#endif // tokenizeH diff --git a/cppcheck-2.14.0/lib/tokenlist.cpp b/cppcheck-2.14.0/lib/tokenlist.cpp new file mode 100644 index 00000000..25d1764f --- /dev/null +++ b/cppcheck-2.14.0/lib/tokenlist.cpp @@ -0,0 +1,2189 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "astutils.h" +#include "errorlogger.h" +#include "errortypes.h" +#include "keywords.h" +#include "library.h" +#include "path.h" +#include "platform.h" +#include "settings.h" +#include "standards.h" +#include "token.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +//#define N_ASSERT_LANG + +#ifndef N_ASSERT_LANG +#include +#define ASSERT_LANG(x) assert(x) +#else +#define ASSERT_LANG(x) +#endif + +// 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 constexpr int AST_MAX_DEPTH = 150; + + +TokenList::TokenList(const Settings* settings) + : mTokensFrontBack(*this) + , mSettings(settings) +{ + if (mSettings && (mSettings->enforcedLang != Standards::Language::None)) { + mLang = mSettings->enforcedLang; + } +} + +TokenList::~TokenList() +{ + deallocateTokens(); +} + +//--------------------------------------------------------------------------- + +const std::string& TokenList::getSourceFilePath() const +{ + if (getFiles().empty()) { + return emptyString; + } + return getFiles()[0]; +} + +//--------------------------------------------------------------------------- + +// Deallocate lists.. +void TokenList::deallocateTokens() +{ + deleteTokens(mTokensFrontBack.front); + mTokensFrontBack.front = nullptr; + mTokensFrontBack.back = nullptr; + mFiles.clear(); +} + +void TokenList::determineCppC() +{ + // only try to determine if it wasn't enforced + if (mLang == Standards::Language::None) { + mLang = Path::identify(getSourceFilePath()); + // TODO: cannot enable assert as this might occur for unknown extensions + //ASSERT_LANG(mLang != Standards::Language::None); + if (mLang == Standards::Language::None) { + // TODO: should default to C instead like we do for headers + // default to C++ + mLang = Standards::Language::CPP; + } + } +} + +int TokenList::appendFileIfNew(std::string fileName) +{ + // Has this file been tokenized already? + for (int i = 0; i < mFiles.size(); ++i) + if (Path::sameFileName(mFiles[i], fileName)) + return i; + + // The "mFiles" vector remembers what files have been tokenized.. + mFiles.push_back(std::move(fileName)); + + // Update mIsC and mIsCpp properties + if (mFiles.size() == 1) { // Update only useful if first file added to _files + determineCppC(); + } + return mFiles.size() - 1; +} + +void TokenList::clangSetOrigFiles() +{ + mOrigFiles = mFiles; +} + +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 nonneg int lineno, const nonneg int column, const nonneg int fileno, bool split) +{ + if (str.empty()) + return; + + // If token contains # characters, split it up + if (split) { + size_t begin = 0; + size_t end = 0; + while ((end = str.find("##", begin)) != std::string::npos) { + addtoken(str.substr(begin, end - begin), lineno, fileno, false); + addtoken("##", lineno, column, fileno, false); + begin = end+2; + } + if (begin != 0) { + addtoken(str.substr(begin), lineno, column, fileno, false); + return; + } + } + + if (mTokensFrontBack.back) { + mTokensFrontBack.back->insertToken(str); + } else { + mTokensFrontBack.front = new Token(mTokensFrontBack); + mTokensFrontBack.back = mTokensFrontBack.front; + mTokensFrontBack.back->str(str); + } + + mTokensFrontBack.back->linenr(lineno); + mTokensFrontBack.back->column(column); + mTokensFrontBack.back->fileIndex(fileno); +} + +void TokenList::addtoken(const std::string& str, const Token *locationTok) +{ + if (str.empty()) + return; + + if (mTokensFrontBack.back) { + mTokensFrontBack.back->insertToken(str); + } else { + mTokensFrontBack.front = new Token(mTokensFrontBack); + mTokensFrontBack.back = mTokensFrontBack.front; + mTokensFrontBack.back->str(str); + } + + mTokensFrontBack.back->linenr(locationTok->linenr()); + mTokensFrontBack.back->column(locationTok->column()); + mTokensFrontBack.back->fileIndex(locationTok->fileIndex()); +} + +void TokenList::addtoken(const Token * tok, const nonneg int lineno, const nonneg int column, const nonneg int fileno) +{ + if (tok == nullptr) + return; + + if (mTokensFrontBack.back) { + mTokensFrontBack.back->insertToken(tok->str(), tok->originalName()); + } else { + mTokensFrontBack.front = new Token(mTokensFrontBack); + mTokensFrontBack.back = mTokensFrontBack.front; + mTokensFrontBack.back->str(tok->str()); + if (!tok->originalName().empty()) + mTokensFrontBack.back->originalName(tok->originalName()); + } + + mTokensFrontBack.back->linenr(lineno); + mTokensFrontBack.back->column(column); + mTokensFrontBack.back->fileIndex(fileno); + mTokensFrontBack.back->flags(tok->flags()); +} + +void TokenList::addtoken(const Token *tok, const Token *locationTok) +{ + if (tok == nullptr || locationTok == nullptr) + return; + + if (mTokensFrontBack.back) { + mTokensFrontBack.back->insertToken(tok->str(), tok->originalName()); + } else { + mTokensFrontBack.front = new Token(mTokensFrontBack); + mTokensFrontBack.back = mTokensFrontBack.front; + mTokensFrontBack.back->str(tok->str()); + if (!tok->originalName().empty()) + mTokensFrontBack.back->originalName(tok->originalName()); + } + + mTokensFrontBack.back->flags(tok->flags()); + mTokensFrontBack.back->linenr(locationTok->linenr()); + mTokensFrontBack.back->column(locationTok->column()); + mTokensFrontBack.back->fileIndex(locationTok->fileIndex()); +} + +void TokenList::addtoken(const Token *tok) +{ + if (tok == nullptr) + return; + + if (mTokensFrontBack.back) { + mTokensFrontBack.back->insertToken(tok->str(), tok->originalName(), tok->getMacroName()); + } else { + mTokensFrontBack.front = new Token(mTokensFrontBack); + mTokensFrontBack.back = mTokensFrontBack.front; + mTokensFrontBack.back->str(tok->str()); + mTokensFrontBack.back->originalName(tok->originalName()); + mTokensFrontBack.back->setMacroName(tok->getMacroName()); + } + + mTokensFrontBack.back->flags(tok->flags()); + mTokensFrontBack.back->linenr(tok->linenr()); + mTokensFrontBack.back->column(tok->column()); + mTokensFrontBack.back->fileIndex(tok->fileIndex()); +} + + +//--------------------------------------------------------------------------- +// copyTokens - Copy and insert tokens +//--------------------------------------------------------------------------- + +Token *TokenList::copyTokens(Token *dest, const Token *first, const Token *last, bool one_line) +{ + std::stack links; + Token *tok2 = dest; + int linenr = dest->linenr(); + const int commonFileIndex = dest->fileIndex(); + for (const Token *tok = first; tok != last->next(); tok = tok->next()) { + tok2->insertToken(tok->str()); + tok2 = tok2->next(); + tok2->fileIndex(commonFileIndex); + tok2->linenr(linenr); + tok2->tokType(tok->tokType()); + tok2->flags(tok->flags()); + tok2->varId(tok->varId()); + tok2->setTokenDebug(tok->getTokenDebug()); + + // Check for links and fix them up + if (Token::Match(tok2, "(|[|{")) + links.push(tok2); + else if (Token::Match(tok2, ")|]|}")) { + if (links.empty()) + return tok2; + + Token * link = links.top(); + + tok2->link(link); + link->link(tok2); + + links.pop(); + } + if (!one_line && tok->next()) + linenr += tok->next()->linenr() - tok->linenr(); + } + return tok2; +} + +//--------------------------------------------------------------------------- +// InsertTokens - Copy and insert tokens +//--------------------------------------------------------------------------- + +void TokenList::insertTokens(Token *dest, const Token *src, nonneg int n) +{ + std::stack link; + + while (n > 0) { + dest->insertToken(src->str(), src->originalName()); + dest = dest->next(); + + // Set links + if (Token::Match(dest, "(|[|{")) + link.push(dest); + else if (!link.empty() && Token::Match(dest, ")|]|}")) { + Token::createMutualLinks(dest, link.top()); + link.pop(); + } + + dest->fileIndex(src->fileIndex()); + dest->linenr(src->linenr()); + dest->column(src->column()); + dest->varId(src->varId()); + dest->tokType(src->tokType()); + dest->flags(src->flags()); + dest->setMacroName(src->getMacroName()); + src = src->next(); + --n; + } +} + +//--------------------------------------------------------------------------- +// Tokenize - tokenizes a given file. +//--------------------------------------------------------------------------- + +bool TokenList::createTokens(std::istream &code, const std::string& file0) +{ + ASSERT_LANG(!file0.empty()); + + appendFileIfNew(file0); + + return createTokensInternal(code, file0); +} + +//--------------------------------------------------------------------------- + +bool TokenList::createTokens(std::istream &code, Standards::Language lang) +{ + ASSERT_LANG(lang != Standards::Language::None); + if (mLang == Standards::Language::None) { + mLang = lang; + } else { + ASSERT_LANG(lang == mLang); + } + + return createTokensInternal(code, ""); +} + +//--------------------------------------------------------------------------- + +bool TokenList::createTokensInternal(std::istream &code, const std::string& file0) +{ + simplecpp::OutputList outputList; + simplecpp::TokenList tokens(code, mFiles, file0, &outputList); + + createTokens(std::move(tokens)); + + return outputList.empty(); +} + +//--------------------------------------------------------------------------- + +// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) +void TokenList::createTokens(simplecpp::TokenList&& tokenList) +{ + if (tokenList.cfront()) { + // this is a copy + // TODO: the same as TokenList.files - move that instead + // TODO: this points to mFiles when called from createTokens(std::istream &, const std::string&) + mOrigFiles = mFiles = tokenList.cfront()->location.files; + } + else + mFiles.clear(); + + determineCppC(); + + for (const simplecpp::Token *tok = tokenList.cfront(); tok;) { + + // TODO: move from TokenList + std::string str = tok->str(); + + // Float literal + if (str.size() > 1 && str[0] == '.' && std::isdigit(str[1])) + str = '0' + str; + + if (mTokensFrontBack.back) { + mTokensFrontBack.back->insertToken(str); + } else { + mTokensFrontBack.front = new Token(mTokensFrontBack); + mTokensFrontBack.back = mTokensFrontBack.front; + mTokensFrontBack.back->str(str); + } + + mTokensFrontBack.back->fileIndex(tok->location.fileIndex); + mTokensFrontBack.back->linenr(tok->location.line); + mTokensFrontBack.back->column(tok->location.col); + mTokensFrontBack.back->setMacroName(tok->macro); + + tok = tok->next; + if (tok) + tokenList.deleteToken(tok->previous); + } + + if (mSettings && mSettings->relativePaths) { + for (std::string & mFile : mFiles) + mFile = Path::getRelativePath(mFile, mSettings->basePaths); + } + + Token::assignProgressValues(mTokensFrontBack.front); +} + +//--------------------------------------------------------------------------- + +std::size_t TokenList::calculateHash() const +{ + std::string hashData; + for (const Token* tok = front(); tok; tok = tok->next()) { + hashData += std::to_string(tok->flags()); + hashData += std::to_string(tok->varId()); + hashData += std::to_string(tok->tokType()); + hashData += tok->str(); + hashData += tok->originalName(); + } + return (std::hash{})(hashData); +} + + +//--------------------------------------------------------------------------- + +namespace { + struct AST_state { + std::stack op; + int depth{}; + int inArrayAssignment{}; + bool cpp; + int assign{}; + bool inCase{}; // true from case to : + bool stopAtColon{}; // help to properly parse ternary operators + const Token* functionCallEndPar{}; + explicit AST_state(bool cpp) : cpp(cpp) {} + }; +} + +static Token* skipDecl(Token* tok, std::vector* inner = nullptr) +{ + auto isDecltypeFuncParam = [](const Token* tok) -> bool { + if (!Token::simpleMatch(tok, ")")) + return false; + tok = tok->next(); + while (Token::Match(tok, "*|&|&&|const")) + tok = tok->next(); + if (Token::simpleMatch(tok, "(")) + tok = tok->link()->next(); + return Token::Match(tok, "%name%| ,|)"); + }; + + if (!Token::Match(tok->previous(), "( %name%")) + return tok; + Token *vartok = tok; + while (Token::Match(vartok, "%name%|*|&|::|<")) { + if (vartok->str() == "<") { + if (vartok->link()) + vartok = vartok->link(); + else + return tok; + } else if (Token::Match(vartok, "%var% [:=(]")) { + return vartok; + } else if (Token::Match(vartok, "decltype|typeof (") && !isDecltypeFuncParam(tok->linkAt(1))) { + if (inner) + inner->push_back(vartok->tokAt(2)); + return vartok->linkAt(1)->next(); + } + vartok = vartok->next(); + } + return tok; +} + +static bool iscast(const Token *tok, bool cpp) +{ + if (!Token::Match(tok, "( ::| %name%")) + return false; + + if (Token::simpleMatch(tok->link(), ") ( )")) + return false; + + if (Token::Match(tok->link(), ") %assign%|,|...")) + return false; + + if (tok->previous() && tok->previous()->isName() && tok->previous()->str() != "return" && + (!cpp || !Token::Match(tok->previous(), "delete|throw"))) + return false; + + if (Token::simpleMatch(tok->previous(), ">") && tok->previous()->link()) + return false; + + if (Token::Match(tok, "( (| typeof (") && Token::Match(tok->link(), ") %num%")) + return true; + + if (Token::Match(tok->link(), ") }|)|]|;")) + return false; + + if (Token::Match(tok->link(), ") %cop%") && !Token::Match(tok->link(), ") [&*+-~!]")) + return false; + + if (Token::Match(tok->previous(), "= ( %name% ) {") && tok->next()->varId() == 0) + return true; + + bool type = false; + for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) { + if (tok2->varId() != 0) + return false; + if (cpp && !type && tok2->str() == "new") + return false; + + while (tok2->link() && Token::Match(tok2, "(|[|<")) + tok2 = tok2->link()->next(); + + if (tok2->str() == ")") { + if (Token::simpleMatch(tok2, ") (") && Token::simpleMatch(tok2->linkAt(1), ") .")) + return true; + if (Token::simpleMatch(tok2, ") {") && !type) { + const Token *tok3 = tok2->linkAt(1); + while (tok3 != tok2 && Token::Match(tok3, "[{}]")) + tok3 = tok3->previous(); + return tok3->str() != ";"; + } + const bool res = type || tok2->strAt(-1) == "*" || Token::simpleMatch(tok2, ") ~") || + (Token::Match(tok2, ") %any%") && + (!tok2->next()->isOp() || Token::Match(tok2->next(), "!|~|++|--")) && + !Token::Match(tok2->next(), "[[]);,?:.]")); + return res; + } + + if (Token::Match(tok2, "&|&& )")) + return true; + + if (!Token::Match(tok2, "%name%|*|::")) + return false; + + if (tok2->isStandardType() && (tok2->next()->str() != "(" || Token::Match(tok2->next(), "( * *| )"))) + type = true; + } + + return false; +} + +// int(1), int*(2), .. +static Token * findCppTypeInitPar(Token *tok) +{ + if (!tok || !Token::Match(tok->previous(), "[,()] %name%")) + return nullptr; + bool istype = false; + while (Token::Match(tok, "%name%|::|<")) { + if (tok->str() == "<") { + tok = tok->link(); + if (!tok) + return nullptr; + } + istype |= tok->isStandardType(); + tok = tok->next(); + } + if (!istype) + return nullptr; + if (!Token::Match(tok, "[*&]")) + return nullptr; + while (Token::Match(tok, "[*&]")) + tok = tok->next(); + return (tok && tok->str() == "(") ? tok : nullptr; +} + +// X{} X{} etc +static bool iscpp11init_impl(const Token * const tok); +static bool iscpp11init(const Token * const tok) +{ + if (tok->isCpp11init() == TokenImpl::Cpp11init::UNKNOWN) + tok->setCpp11init(iscpp11init_impl(tok)); + return tok->isCpp11init() == TokenImpl::Cpp11init::CPP11INIT; +} + +static bool iscpp11init_impl(const Token * const tok) +{ + if (Token::simpleMatch(tok, "{") && Token::simpleMatch(tok->link()->previous(), "; }")) + return false; + const Token *nameToken = tok; + while (nameToken && nameToken->str() == "{") { + if (nameToken->isCpp11init() != TokenImpl::Cpp11init::UNKNOWN) + return nameToken->isCpp11init() == TokenImpl::Cpp11init::CPP11INIT; + nameToken = nameToken->previous(); + if (nameToken && nameToken->str() == "," && Token::simpleMatch(nameToken->previous(), "} ,")) + nameToken = nameToken->linkAt(-1); + } + if (!nameToken) + return false; + if (nameToken->str() == ")" && Token::simpleMatch(nameToken->link()->previous(), "decltype (") && + !Token::simpleMatch(nameToken->link()->tokAt(-2), ".")) + nameToken = nameToken->link()->previous(); + if (Token::simpleMatch(nameToken, ", {")) + return true; + if (nameToken->str() == ">" && nameToken->link()) + nameToken = nameToken->link()->previous(); + if (Token::Match(nameToken, "]|*")) { + const Token* newTok = nameToken->link() ? nameToken->link()->previous() : nameToken->previous(); + while (Token::Match(newTok, "%type%|::|*") && !newTok->isKeyword()) + newTok = newTok->previous(); + if (Token::simpleMatch(newTok, "new")) + return true; + } + + auto isCaseStmt = [](const Token* colonTok) { + if (!Token::Match(colonTok->tokAt(-1), "%name%|%num%|%char%|) :")) + return false; + if (const Token* castTok = colonTok->linkAt(-1)) { + if (Token::simpleMatch(castTok->astParent(), "case")) + return true; + } + const Token* caseTok = colonTok->tokAt(-2); + while (caseTok && Token::Match(caseTok->tokAt(-1), "::|%name%")) + caseTok = caseTok->tokAt(-1); + return Token::simpleMatch(caseTok, "case"); + }; + + const Token *endtok = nullptr; + if (Token::Match(nameToken, "%name%|return|: {") && !isCaseStmt(nameToken) && + (!Token::simpleMatch(nameToken->tokAt(2), "[") || findLambdaEndScope(nameToken->tokAt(2)))) + endtok = nameToken->linkAt(1); + else if (Token::Match(nameToken,"%name% <") && Token::simpleMatch(nameToken->linkAt(1),"> {")) + endtok = nameToken->linkAt(1)->linkAt(1); + else if (Token::Match(nameToken->previous(), "%name%|> ( {")) + endtok = nameToken->linkAt(1); + else if (Token::simpleMatch(nameToken, "decltype") && nameToken->linkAt(1)) + endtok = nameToken->linkAt(1)->linkAt(1); + else + return false; + if (Token::Match(nameToken, "else|try|do|const|constexpr|override|volatile|&|&&")) + return false; + if (Token::simpleMatch(nameToken->previous(), ". void {") && nameToken->previous()->originalName() == "->") + return false; // trailing return type. The only function body that can contain no semicolon is a void function. + if (Token::simpleMatch(nameToken->previous(), "namespace") || Token::simpleMatch(nameToken, "namespace") /*anonymous namespace*/) + return false; + if (precedes(nameToken->next(), endtok) && !Token::Match(nameToken, "return|:")) { + // If there is semicolon between {..} this is not a initlist + for (const Token *tok2 = nameToken->next(); tok2 != endtok; tok2 = tok2->next()) { + if (tok2->str() == ";") + return false; + const Token * lambdaEnd = findLambdaEndScope(tok2); + if (lambdaEnd) + tok2 = lambdaEnd; + } + } + // There is no initialisation for example here: 'class Fred {};' + if (!Token::simpleMatch(endtok, "} ;")) + return true; + const Token *prev = nameToken; + while (Token::Match(prev, "%name%|::|:|<|>|(|)|,|%num%|%cop%|...")) { + if (Token::Match(prev, "class|struct|union|enum")) + return false; + + prev = prev->previous(); + } + return true; +} + +static bool isQualifier(const Token* tok) +{ + while (Token::Match(tok, "&|&&|*")) + tok = tok->next(); + return Token::Match(tok, "{|;"); +} + +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 (state.depth > AST_MAX_DEPTH) + throw InternalError(tok, "maximum AST depth exceeded", InternalError::AST); + if (tok) + f(tok, state); + state.depth--; + } + + if (!state.op.empty() && (!precedes(state.op.top(), unaryop) || unaryop->isIncDecOp() || Token::Match(unaryop, "[({[]"))) { // nullary functions, empty lists/arrays + 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(); + if (Token::Match(binop, "::|. ~")) + tok = tok->next(); + state.depth++; + if (tok && state.depth <= AST_MAX_DEPTH) + f(tok, state); + if (state.depth > AST_MAX_DEPTH) + throw InternalError(tok, "maximum AST depth exceeded", InternalError::AST); + state.depth--; + } + + // TODO: Should we check if op is empty. + // * Is it better to add assertion that it isn't? + // * Write debug warning if it's empty? + if (!state.op.empty()) { + binop->astOperand2(state.op.top()); + state.op.pop(); + } + if (!state.op.empty()) { + binop->astOperand1(state.op.top()); + state.op.pop(); + } + state.op.push(binop); +} + +static void compileExpression(Token *&tok, AST_state& state); + +static void compileTerm(Token *&tok, AST_state& state) +{ + if (!tok) + return; + if (Token::Match(tok, "L %str%|%char%")) + tok = tok->next(); + if (state.inArrayAssignment && Token::Match(tok->previous(), "[{,] . %name%")) { // Jump over . in C style struct initialization + state.op.push(tok); + tok->astOperand1(tok->next()); + tok = tok->tokAt(2); + } + if (state.inArrayAssignment && Token::Match(tok->previous(), "[{,] [ %num%|%name% ]")) { + state.op.push(tok); + tok->astOperand1(tok->next()); + tok = tok->tokAt(3); + } + if (tok->isLiteral()) { + state.op.push(tok); + do { + tok = tok->next(); + } while (Token::Match(tok, "%name%|%str%")); + } else if (tok->isName()) { + if (Token::Match(tok, "return|case") || (state.cpp && (tok->str() == "throw"))) { + if (tok->str() == "case") + state.inCase = true; + const bool tokIsReturn = tok->str() == "return"; + const bool stopAtColon = state.stopAtColon; + state.stopAtColon=true; + compileUnaryOp(tok, state, compileExpression); + state.stopAtColon=stopAtColon; + if (tokIsReturn) + state.op.pop(); + if (state.inCase && Token::simpleMatch(tok, ": ;")) { + state.inCase = false; + tok = tok->next(); + } + } else if (Token::Match(tok, "sizeof !!(")) { + compileUnaryOp(tok, state, compileExpression); + state.op.pop(); + } else if (state.cpp && findCppTypeInitPar(tok)) { // int(0), int*(123), .. + tok = findCppTypeInitPar(tok); + state.op.push(tok); + tok = tok->tokAt(2); + } else if (state.cpp && iscpp11init(tok)) { // X{} X{} etc + state.op.push(tok); + tok = tok->next(); + if (tok->str() == "<") + tok = tok->link()->next(); + + if (Token::Match(tok, "{ . %name% =|{")) { + const Token* end = tok->link(); + const int inArrayAssignment = state.inArrayAssignment; + state.inArrayAssignment = 1; + compileBinOp(tok, state, compileExpression); + state.inArrayAssignment = inArrayAssignment; + if (tok == end) + tok = tok->next(); + else + throw InternalError(tok, "Syntax error. Unexpected tokens in designated initializer.", InternalError::AST); + } + } else if (!state.cpp || !Token::Match(tok, "new|delete %name%|*|&|::|(|[")) { + std::vector inner; + tok = skipDecl(tok, &inner); + for (Token* tok3 : inner) { + AST_state state1(state.cpp); + compileExpression(tok3, state1); + } + bool repeat = true; + while (repeat) { + repeat = false; + if (Token::Match(tok->next(), "%name%")) { + tok = tok->next(); + repeat = true; + } + if (Token::simpleMatch(tok->next(), "<") && Token::Match(tok->linkAt(1), "> %name%")) { + tok = tok->next()->link()->next(); + repeat = true; + } + } + state.op.push(tok); + if (Token::Match(tok, "%name% <") && tok->linkAt(1)) + tok = tok->linkAt(1); + else if (Token::Match(tok, "%name% ...") || (state.op.size() == 1 && state.depth == 0 && Token::Match(tok->tokAt(-3), "!!& ) ( %name% ) ="))) + tok = tok->next(); + tok = tok->next(); + if (Token::Match(tok, "%str%")) { + while (Token::Match(tok, "%name%|%str%")) + tok = tok->next(); + } + if (Token::Match(tok, "%name% %assign%")) + tok = tok->next(); + } + } else if (tok->str() == "{") { + const Token *prev = tok->previous(); + if (Token::simpleMatch(prev, ") {") && iscast(prev->link(), state.cpp)) + prev = prev->link()->previous(); + if (Token::simpleMatch(tok->link(),"} [")) { + tok = tok->next(); + } else if ((state.cpp && iscpp11init(tok)) || Token::simpleMatch(tok->previous(), "] {")) { + Token *const end = tok->link(); + if (state.op.empty() || Token::Match(tok->previous(), "[{,]") || Token::Match(tok->tokAt(-2), "%name% (")) { + if (Token::Match(tok, "{ . %name% =|{")) { + const int inArrayAssignment = state.inArrayAssignment; + state.inArrayAssignment = 1; + compileBinOp(tok, state, compileExpression); + state.inArrayAssignment = inArrayAssignment; + } else if (Token::simpleMatch(tok, "{ }")) { + state.op.push(tok); + tok = tok->next(); + } else { + compileUnaryOp(tok, state, compileExpression); + if (precedes(tok,end)) // typically for something like `MACRO(x, { if (c) { ... } })`, where end is the last curly, and tok is the open curly for the if + tok = end; + } + } else + compileBinOp(tok, state, compileExpression); + if (tok != end) + throw InternalError(tok, "Syntax error. Unexpected tokens in initializer.", InternalError::AST); + if (tok->next()) + tok = tok->next(); + } else if (state.cpp && Token::Match(tok->tokAt(-2), "%name% ( {") && !Token::findsimplematch(tok, ";", tok->link())) { + if (Token::simpleMatch(tok, "{ }")) + tok = tok->tokAt(2); + else { + Token *tok1 = tok; + state.inArrayAssignment++; + compileUnaryOp(tok, state, compileExpression); + state.inArrayAssignment--; + tok = tok1->link()->next(); + } + } else if (!state.inArrayAssignment && !Token::simpleMatch(prev, "=")) { + state.op.push(tok); + tok = tok->link()->next(); + } else { + if (tok->link() != tok->next()) { + state.inArrayAssignment++; + compileUnaryOp(tok, state, compileExpression); + if (Token::Match(tok, "} [,};]") && state.inArrayAssignment > 0) { + tok = tok->next(); + state.inArrayAssignment--; + } + } else { + state.op.push(tok); + tok = tok->tokAt(2); + } + } + } +} + +static void compileScope(Token *&tok, AST_state& state) +{ + compileTerm(tok, state); + while (tok) { + if (tok->str() == "::") { + const Token *lastOp = state.op.empty() ? nullptr : state.op.top(); + if (Token::Match(lastOp, ":: %name%")) + lastOp = lastOp->next(); + if (Token::Match(lastOp, "%name%") && + (lastOp->next() == tok || (Token::Match(lastOp, "%name% <") && lastOp->linkAt(1) && tok == lastOp->linkAt(1)->next()))) + compileBinOp(tok, state, compileTerm); + else + compileUnaryOp(tok, state, compileTerm); + } else break; + } +} + +static bool isPrefixUnary(const Token* tok, bool cpp) +{ + if (cpp && Token::simpleMatch(tok->previous(), "* [") && Token::simpleMatch(tok->link(), "] {")) { + for (const Token* prev = tok->previous(); Token::Match(prev, "%name%|::|*|&|>|>>"); prev = prev->previous()) { + if (Token::Match(prev, ">|>>")) { + if (!prev->link()) + break; + prev = prev->link(); + } + if (prev->str() == "new") + return false; + } + } + if (!tok->previous() + || ((Token::Match(tok->previous(), "(|[|{|%op%|;|?|:|,|.|return|::") || (cpp && tok->strAt(-1) == "throw")) + && (tok->previous()->tokType() != Token::eIncDecOp || tok->tokType() == Token::eIncDecOp))) + return true; + + if (tok->previous()->str() == "}") { + const Token* parent = tok->linkAt(-1)->tokAt(-1); + return !Token::Match(parent, "%type%") || parent->isKeyword(); + } + + if (tok->str() == "*" && tok->previous()->tokType() == Token::eIncDecOp && isPrefixUnary(tok->previous(), cpp)) + return true; + + return tok->strAt(-1) == ")" && iscast(tok->linkAt(-1), cpp); +} + +static void compilePrecedence2(Token *&tok, AST_state& state) +{ + auto doCompileScope = [&](const Token* tok) -> bool { + const bool isStartOfCpp11Init = state.cpp && tok && tok->str() == "{" && iscpp11init(tok); + if (isStartOfCpp11Init || Token::simpleMatch(tok, "(")) { + tok = tok->previous(); + while (Token::simpleMatch(tok, "*")) + tok = tok->previous(); + while (tok && Token::Match(tok->previous(), ":: %type%")) + tok = tok->tokAt(-2); + if (tok && !tok->isKeyword()) + tok = tok->previous(); + return !Token::Match(tok, "new ::| %type%"); + } + return !findLambdaEndTokenWithoutAST(tok); + }; + + bool isNew = true; + if (doCompileScope(tok)) { + compileScope(tok, state); + isNew = false; + } + while (tok) { + if (tok->tokType() == Token::eIncDecOp && !isPrefixUnary(tok, state.cpp)) { + compileUnaryOp(tok, state, compileScope); + } else if (tok->str() == "...") { + if (!Token::simpleMatch(tok->previous(), ")")) + state.op.push(tok); + tok = tok->next(); + break; + } else if (tok->str() == "." && tok->strAt(1) != "*") { + if (tok->strAt(1) == ".") { + state.op.push(tok); + tok = tok->tokAt(3); + break; + } + compileBinOp(tok, state, compileScope); + } else if (tok->str() == "[") { + if (state.cpp && isPrefixUnary(tok, /*cpp*/ true) && Token::Match(tok->link(), "] (|{|<")) { // Lambda + // What we do here: + // - Nest the round bracket under the square bracket. + // - Nest what follows the lambda (if anything) with the lambda opening [ + // - Compile the content of the lambda function as separate tree (this is done later) + // this must be consistent with isLambdaCaptureList + Token* const squareBracket = tok; + // Parse arguments in the capture list + if (tok->strAt(1) != "]") { + Token* tok2 = tok->next(); + AST_state state2(state.cpp); + compileExpression(tok2, state2); + if (!state2.op.empty()) { + squareBracket->astOperand2(state2.op.top()); + } + } + + const bool hasTemplateArg = Token::simpleMatch(squareBracket->link(), "] <") && + Token::simpleMatch(squareBracket->link()->next()->link(), "> ("); + if (Token::simpleMatch(squareBracket->link(), "] (") || hasTemplateArg) { + Token* const roundBracket = hasTemplateArg ? squareBracket->link()->next()->link()->next() : squareBracket->link()->next(); + Token* curlyBracket = roundBracket->link()->next(); + while (Token::Match(curlyBracket, "mutable|const|constexpr|consteval")) + curlyBracket = curlyBracket->next(); + if (Token::simpleMatch(curlyBracket, "noexcept")) { + if (Token::simpleMatch(curlyBracket->next(), "(")) + curlyBracket = curlyBracket->linkAt(1)->next(); + else + curlyBracket = curlyBracket->next(); + } + if (curlyBracket && curlyBracket->originalName() == "->") + curlyBracket = findTypeEnd(curlyBracket->next()); + if (curlyBracket && curlyBracket->str() == "{") { + squareBracket->astOperand1(roundBracket); + roundBracket->astOperand1(curlyBracket); + state.op.push(squareBracket); + tok = curlyBracket->link()->next(); + continue; + } + } else { + Token* const curlyBracket = squareBracket->link()->next(); + squareBracket->astOperand1(curlyBracket); + state.op.push(squareBracket); + tok = curlyBracket->link() ? curlyBracket->link()->next() : nullptr; + continue; + } + } + + Token* const tok2 = tok; + if (tok->strAt(1) != "]") { + compileBinOp(tok, state, compileExpression); + if (Token::Match(tok2->previous(), "%type%|* [") && Token::Match(tok, "] { !!}")) { + tok = tok->next(); + Token* const tok3 = tok; + compileBinOp(tok, state, compileExpression); + if (tok != tok3->link()) + throw InternalError(tok, "Syntax error in {..}", InternalError::AST); + tok = tok->next(); + continue; + } + } + else + compileUnaryOp(tok, state, compileExpression); + tok = tok2->link()->next(); + } else if (Token::simpleMatch(tok, "( {") && Token::simpleMatch(tok->linkAt(1)->previous(), "; } )") && !Token::Match(tok->previous(), "%name% (")) { + state.op.push(tok->next()); + tok = tok->link()->next(); + continue; + } else if (tok->str() == "(" && (!iscast(tok, state.cpp) || Token::Match(tok->previous(), "if|while|for|switch|catch"))) { + Token* tok2 = tok; + tok = tok->next(); + const bool opPrevTopSquare = !state.op.empty() && state.op.top() && state.op.top()->str() == "["; + const std::size_t oldOpSize = state.op.size(); + compileExpression(tok, state); + tok = tok2; + if ((oldOpSize > 0 && (isNew || Token::simpleMatch(tok->previous(), "} ("))) + || (tok->previous() && tok->previous()->isName() && !Token::Match(tok->previous(), "return|case") && (!state.cpp || !Token::Match(tok->previous(), "throw|delete"))) + || (tok->strAt(-1) == "]" && (!state.cpp || !Token::Match(tok->linkAt(-1)->previous(), "new|delete"))) + || (tok->strAt(-1) == ">" && tok->linkAt(-1)) + || (tok->strAt(-1) == ")" && !iscast(tok->linkAt(-1), state.cpp)) // 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(); + if (Token::simpleMatch(tok, "::")) + compileBinOp(tok, state, compileTerm); + } else if (iscast(tok, state.cpp) && Token::simpleMatch(tok->link(), ") {") && Token::simpleMatch(tok->link()->linkAt(1), "} [")) { + Token *cast = tok; + tok = tok->link()->next(); + Token *tok1 = tok; + compileUnaryOp(tok, state, compileExpression); + cast->astOperand1(tok1); + tok = tok1->link()->next(); + } else if (state.cpp && tok->str() == "{" && iscpp11init(tok)) { + Token* end = tok->link(); + if (Token::simpleMatch(tok, "{ }")) + { + compileUnaryOp(tok, state, nullptr); + tok = tok->next(); + } + else + { + compileBinOp(tok, state, compileExpression); + } + if (tok == end) + tok = end->next(); + else + throw InternalError(tok, "Syntax error. Unexpected tokens in initializer.", InternalError::AST); + } else break; + } +} + +static void compilePrecedence3(Token *&tok, AST_state& state) +{ + compilePrecedence2(tok, state); + while (tok) { + if ((Token::Match(tok, "[+-!~*&]") || tok->tokType() == Token::eIncDecOp) && + isPrefixUnary(tok, state.cpp)) { + if (Token::Match(tok, "* [*,)]")) { + Token* tok2 = tok->next(); + while (tok2->next() && tok2->str() == "*") + tok2 = tok2->next(); + if (Token::Match(tok2, "[>),]")) { + tok = tok2; + continue; + } + } + compileUnaryOp(tok, state, compilePrecedence3); + } else if (tok->str() == "(" && iscast(tok, state.cpp)) { + Token* castTok = tok; + castTok->isCast(true); + tok = tok->link()->next(); + const int inArrayAssignment = state.inArrayAssignment; + if (tok && tok->str() == "{") + state.inArrayAssignment = 1; + compilePrecedence3(tok, state); + state.inArrayAssignment = inArrayAssignment; + compileUnaryOp(castTok, state, nullptr); + } else if (state.cpp && Token::Match(tok, "new %name%|::|(")) { + Token* newtok = tok; + tok = tok->next(); + bool innertype = false; + if (tok->str() == "(") { + if (Token::Match(tok, "( &| %name%") && Token::Match(tok->link(), ") ( %type%") && Token::simpleMatch(tok->link()->linkAt(1), ") (")) + tok = tok->link()->next(); + if (Token::Match(tok->link(), ") ::| %type%")) { + if (Token::Match(tok, "( !!)")) { + Token *innerTok = tok->next(); + AST_state innerState(true); + compileExpression(innerTok, innerState); + } + tok = tok->link()->next(); + } else if (Token::Match(tok, "( %type%") && Token::Match(tok->link(), ") [();,[]")) { + tok = tok->next(); + innertype = true; + } else if (Token::Match(tok, "( &| %name%") && Token::simpleMatch(tok->link(), ") (")) { + tok = tok->next(); + innertype = true; + } else { + /* bad code */ + continue; + } + } + + Token* leftToken = tok; + if (Token::simpleMatch(tok, "::")) { + tok->astOperand1(tok->next()); + tok = tok->next(); + } + else { + while (Token::Match(tok->next(), ":: %name%")) { + Token* scopeToken = tok->next(); //The :: + scopeToken->astOperand1(leftToken); + scopeToken->astOperand2(scopeToken->next()); + leftToken = scopeToken; + tok = scopeToken->next(); + } + } + + state.op.push(tok); + while (Token::Match(tok, "%name%|*|&|<|::")) { + if (tok->link()) + tok = tok->link(); + tok = tok->next(); + } + if (Token::Match(tok, "( const| %type% ) (")) { + state.op.push(tok->next()); + tok = tok->link()->next(); + compileBinOp(tok, state, compilePrecedence2); + } else if (Token::Match(tok, "(|{|[")) + compilePrecedence2(tok, state); + else if (innertype && Token::simpleMatch(tok, ") [")) { + tok = tok->next(); + compilePrecedence2(tok, state); + } + compileUnaryOp(newtok, state, nullptr); + if (Token::simpleMatch(newtok->previous(), ":: new")) { + newtok->previous()->astOperand1(newtok); + state.op.pop(); + } + if (innertype && Token::simpleMatch(tok, ") ,")) + tok = tok->next(); + } else if (state.cpp && Token::Match(tok, "delete %name%|*|&|::|(|[")) { + Token* tok2 = tok; + tok = tok->next(); + if (tok && tok->str() == "[") + tok = tok->link()->next(); + compilePrecedence3(tok, state); + compileUnaryOp(tok2, state, nullptr); + if (Token::simpleMatch(tok2->previous(), ":: delete")) { + tok2->previous()->astOperand1(tok2); + state.op.pop(); + } + } + // TODO: Handle sizeof + else break; + } +} + +static void compilePointerToElem(Token *&tok, AST_state& state) +{ + compilePrecedence3(tok, state); + while (tok) { + if (Token::simpleMatch(tok, ". *")) { + compileBinOp(tok, state, compilePrecedence3); + } else break; + } +} + +static void compileMulDiv(Token *&tok, AST_state& state) +{ + compilePointerToElem(tok, state); + while (tok) { + if (Token::Match(tok, "[/%]") || (tok->str() == "*" && !tok->astOperand1() && !isQualifier(tok))) { + if (Token::Match(tok, "* [*,)]")) { + Token* tok2 = tok->next(); + while (tok2->next() && tok2->str() == "*") + tok2 = tok2->next(); + if (Token::Match(tok2, "[>),]")) { + tok = tok2; + break; + } + } + compileBinOp(tok, state, compilePointerToElem); + } else break; + } +} + +static void compileAddSub(Token *&tok, AST_state& state) +{ + compileMulDiv(tok, state); + while (tok) { + if (Token::Match(tok, "+|-") && !tok->astOperand1()) { + compileBinOp(tok, state, compileMulDiv); + } else break; + } +} + +static void compileShift(Token *&tok, AST_state& state) +{ + compileAddSub(tok, state); + while (tok) { + if (Token::Match(tok, "<<|>>")) { + compileBinOp(tok, state, compileAddSub); + } else break; + } +} + +static void compileThreewayComp(Token *&tok, AST_state& state) +{ + compileShift(tok, state); + while (tok) { + if (tok->str() == "<=>") { + compileBinOp(tok, state, compileShift); + } else break; + } +} + +static void compileRelComp(Token *&tok, AST_state& state) +{ + compileThreewayComp(tok, state); + while (tok) { + if (Token::Match(tok, "<|<=|>=|>") && !tok->link()) { + compileBinOp(tok, state, compileThreewayComp); + } else break; + } +} + +static void compileEqComp(Token *&tok, AST_state& state) +{ + compileRelComp(tok, state); + while (tok) { + if (Token::Match(tok, "==|!=")) { + compileBinOp(tok, state, compileRelComp); + } else break; + } +} + +static void compileAnd(Token *&tok, AST_state& state) +{ + compileEqComp(tok, state); + while (tok) { + if (tok->str() == "&" && !tok->astOperand1() && !isQualifier(tok)) { + Token* tok2 = tok->next(); + if (!tok2) + break; + if (tok2->str() == "&") + tok2 = tok2->next(); + if (state.cpp && Token::Match(tok2, ",|)")) { + tok = tok2; + break; // rValue reference + } + compileBinOp(tok, state, compileEqComp); + } else break; + } +} + +static void compileXor(Token *&tok, AST_state& state) +{ + compileAnd(tok, state); + while (tok) { + if (tok->str() == "^") { + compileBinOp(tok, state, compileAnd); + } else break; + } +} + +static void compileOr(Token *&tok, AST_state& state) +{ + compileXor(tok, state); + while (tok) { + if (tok->str() == "|") { + compileBinOp(tok, state, compileXor); + } else break; + } +} + +static void compileLogicAnd(Token *&tok, AST_state& state) +{ + compileOr(tok, state); + while (tok) { + if (tok->str() == "&&" && !isQualifier(tok)) { + if (!tok->astOperand1()) { + Token* tok2 = tok->next(); + if (!tok2) + break; + if (state.cpp && Token::Match(tok2, ",|)")) { + tok = tok2; + break; // rValue reference + } + } + compileBinOp(tok, state, compileOr); + } else break; + } +} + +static void compileLogicOr(Token *&tok, AST_state& state) +{ + compileLogicAnd(tok, state); + while (tok) { + if (tok->str() == "||") { + compileBinOp(tok, state, compileLogicAnd); + } else break; + } +} + +static void compileAssignTernary(Token *&tok, AST_state& state) +{ + compileLogicOr(tok, state); + while (tok) { + if (tok->isAssignmentOp()) { + state.assign++; + const Token *tok1 = tok->next(); + compileBinOp(tok, state, compileAssignTernary); + if (Token::simpleMatch(tok1, "{") && tok == tok1->link() && tok->next()) + tok = tok->next(); + if (state.assign > 0) + state.assign--; + } else if (tok->str() == "?") { + // http://en.cppreference.com/w/cpp/language/operator_precedence says about ternary operator: + // "The expression in the middle of the conditional operator (between ? and :) is parsed as if parenthesized: its precedence relative to ?: is ignored." + // Hence, we rely on Tokenizer::prepareTernaryOpForAST() to add such parentheses where necessary. + const bool stopAtColon = state.stopAtColon; + state.stopAtColon = false; + if (tok->strAt(1) == ":") { + state.op.push(nullptr); + } + const int assign = state.assign; + state.assign = 0; + compileBinOp(tok, state, compileAssignTernary); + state.assign = assign; + state.stopAtColon = stopAtColon; + } else if (tok->str() == ":") { + if (state.depth == 1U && state.inCase) { + state.inCase = false; + tok = tok->next(); + break; + } + if (state.stopAtColon) + break; + if (state.assign > 0) + break; + compileBinOp(tok, state, compileAssignTernary); + } else break; + } +} + +static void compileComma(Token *&tok, AST_state& state) +{ + compileAssignTernary(tok, state); + while (tok) { + if (tok->str() == ",") { + if (Token::simpleMatch(tok, ", }")) + tok = tok->next(); + else + compileBinOp(tok, state, compileAssignTernary); + } else if (tok->str() == ";" && state.functionCallEndPar && tok->index() < state.functionCallEndPar->index()) { + compileBinOp(tok, state, compileAssignTernary); + } else break; + } +} + +static void compileExpression(Token *&tok, AST_state& state) +{ + if (state.depth > AST_MAX_DEPTH) + throw InternalError(tok, "maximum AST depth exceeded", InternalError::AST); // ticket #5592 + if (tok) + compileComma(tok, state); +} + +const Token* isLambdaCaptureList(const Token * tok) +{ + // a lambda expression '[x](y){}' is compiled as: + // [ + // `-( <<-- optional + // `-{ + // see compilePrecedence2 + if (!Token::simpleMatch(tok, "[")) + return nullptr; + if (!Token::Match(tok->link(), "] (|{")) + return nullptr; + if (Token::simpleMatch(tok->astOperand1(), "{") && tok->astOperand1() == tok->link()->next()) + return tok->astOperand1(); + if (!tok->astOperand1() || tok->astOperand1()->str() != "(") + return nullptr; + const Token * params = tok->astOperand1(); + if (!Token::simpleMatch(params->astOperand1(), "{")) + return nullptr; + return params->astOperand1(); +} + +const Token* findLambdaEndTokenWithoutAST(const Token* tok) { + if (!(Token::simpleMatch(tok, "[") && tok->link())) + return nullptr; + tok = tok->link()->next(); + if (Token::simpleMatch(tok, "(") && tok->link()) + tok = tok->link()->next(); + if (Token::simpleMatch(tok, ".")) { // trailing return type + tok = tok->next(); + while (Token::Match(tok, "%type%|%name%|::|&|&&|*|<|(")) { + if (tok->link()) + tok = tok->link()->next(); + else + tok = tok->next(); + } + } + if (!(Token::simpleMatch(tok, "{") && tok->link())) + return nullptr; + return tok->link()->next(); +} + +static Token * createAstAtToken(Token *tok); + +// Compile inner expressions inside inner ({..}) and lambda bodies +static void createAstAtTokenInner(Token * const tok1, const Token *endToken, bool cpp) +{ + for (Token* tok = tok1; precedes(tok, endToken); tok = tok ? tok->next() : nullptr) { + if (tok->str() == "{" && !iscpp11init(tok)) { + const Token * const endToken2 = tok->link(); + bool hasAst = false; + for (const Token *inner = tok->next(); inner != endToken2; inner = inner->next()) { + if (inner->astOperand1()) { + hasAst = true; + break; + } + if (tok->isConstOp()) + break; + if (inner->str() == "{") + inner = inner->link(); + } + if (!hasAst) { + for (; tok && tok != endToken && tok != endToken2; tok = tok ? tok->next() : nullptr) + tok = createAstAtToken(tok); + } + } else if (cpp && tok->str() == "[") { + if (isLambdaCaptureList(tok)) { + tok = tok->astOperand1(); + if (tok->str() == "(") + tok = tok->astOperand1(); + const Token * const endToken2 = tok->link(); + tok = tok->next(); + for (; tok && tok != endToken && tok != endToken2; tok = tok ? tok->next() : nullptr) + tok = createAstAtToken(tok); + } + } + else if (Token::simpleMatch(tok, "( * ) [")) { + bool hasAst = false; + for (const Token* tok2 = tok->linkAt(3); tok2 != tok; tok2 = tok2->previous()) { + if (tok2->astParent() || tok2->astOperand1() || tok2->astOperand2()) { + hasAst = true; + break; + } + } + if (!hasAst) { + Token *const startTok = tok = tok->tokAt(4); + const Token* const endtok = startTok->linkAt(-1); + AST_state state(cpp); + compileExpression(tok, state); + createAstAtTokenInner(startTok, endtok, cpp); + } + } + } +} + +static Token * findAstTop(Token *tok1, const Token *tok2) +{ + for (Token *tok = tok1; tok && (tok != tok2); tok = tok->next()) { + if (tok->astParent() || tok->astOperand1() || tok->astOperand2()) { + while (tok->astParent() && tok->astParent()->index() >= tok1->index() && tok->astParent()->index() <= tok2->index()) + tok = tok->astParent(); + return tok; + } + if (Token::simpleMatch(tok, "( {")) + tok = tok->link(); + } + for (Token *tok = tok1; tok && (tok != tok2); tok = tok->next()) { + if (tok->isName() || tok->isNumber()) + return tok; + if (Token::simpleMatch(tok, "( {")) + tok = tok->link(); + } + return nullptr; +} + +static Token * createAstAtToken(Token *tok) +{ + const bool cpp = tok->isCpp(); + // skip function pointer declaration + if (Token::Match(tok, "%type% %type%") && !Token::Match(tok, "return|throw|new|delete")) { + Token* tok2 = tok->tokAt(2); + // skip type tokens and qualifiers etc + while (Token::Match(tok2, "%type%|*|&")) + tok2 = tok2->next(); + if (Token::Match(tok2, "%var% [;,)]")) + return tok2; + } + if (Token::Match(tok, "%type%") && !Token::Match(tok, "return|throw|if|while|new|delete")) { + bool isStandardTypeOrQualifier = false; + Token* type = tok; + while (Token::Match(type, "%type%|*|&|<")) { + if (type->isName() && (type->isStandardType() || Token::Match(type, "const|mutable|static|volatile"))) + isStandardTypeOrQualifier = true; + if (type->str() == "<") { + if (type->link()) + type = type->link(); + else + break; + } + type = type->next(); + } + if (isStandardTypeOrQualifier && Token::Match(type, "%var% [;,)]")) + return type; + if (Token::Match(type, "( * *| %var%") && + Token::Match(type->link()->previous(), "%var%|] ) (") && + Token::Match(type->link()->linkAt(1), ") [;,)]")) + return type->link()->linkAt(1)->next(); + } + + if (Token::simpleMatch(tok, "for (")) { + if (cpp && Token::Match(tok, "for ( const| auto &|&&| [")) { + Token *decl = Token::findsimplematch(tok, "["); + if (Token::simpleMatch(decl->link(), "] :")) { + AST_state state1(cpp); + while (decl->str() != "]") { + if (Token::Match(decl, "%name% ,|]")) { + state1.op.push(decl); + } else if (decl->str() == ",") { + if (!state1.op.empty()) { + decl->astOperand1(state1.op.top()); + state1.op.pop(); + } + if (!state1.op.empty()) { + state1.op.top()->astOperand2(decl); + state1.op.pop(); + } + state1.op.push(decl); + } + decl = decl->next(); + } + if (state1.op.size() > 1) { + Token *lastName = state1.op.top(); + state1.op.pop(); + state1.op.top()->astOperand2(lastName); + } + decl = decl->next(); + + Token *colon = decl; + compileExpression(decl, state1); + + tok->next()->astOperand1(tok); + tok->next()->astOperand2(colon); + + return decl; + } + } + + std::vector inner; + Token* tok2 = skipDecl(tok->tokAt(2), &inner); + for (Token* tok3 : inner) { + AST_state state1(cpp); + compileExpression(tok3, state1); + } + Token *init1 = nullptr; + Token * const endPar = tok->next()->link(); + if (tok2 == tok->tokAt(2) && Token::Match(tok2, "%op%|(")) { + init1 = tok2; + AST_state state1(cpp); + compileExpression(tok2, state1); + if (Token::Match(init1, "( !!{")) { + for (Token *tok3 = init1; tok3 && tok3 != tok3->link(); tok3 = tok3->next()) { + if (tok3->astParent()) { + while (tok3->astParent()) + tok3 = tok3->astParent(); + init1 = tok3; + break; + } + if (!Token::Match(tok3, "%op%|(|[")) + init1 = tok3; + } + } + } else { + while (tok2 && tok2 != endPar && tok2->str() != ";") { + if (tok2->str() == "<" && tok2->link()) { + tok2 = tok2->link(); + } else if (Token::Match(tok2, "%name% )| %op%|(|[|.|:|::") || Token::Match(tok2->previous(), "[(;{}] %cop%|(")) { + init1 = tok2; + AST_state state1(cpp); + compileExpression(tok2, state1); + if (Token::Match(tok2, ";|)")) + break; + init1 = nullptr; + } + if (!tok2) // #7109 invalid code + return nullptr; + tok2 = tok2->next(); + } + } + if (!tok2 || tok2->str() != ";") { + if (tok2 == endPar && init1) { + createAstAtTokenInner(init1->next(), endPar, cpp); + tok->next()->astOperand2(init1); + tok->next()->astOperand1(tok); + } + return tok2; + } + + Token * const init = init1 ? init1 : tok2; + + Token * const semicolon1 = tok2; + tok2 = tok2->next(); + AST_state state2(cpp); + compileExpression(tok2, state2); + + Token * const semicolon2 = tok2; + if (!semicolon2) + return nullptr; // invalid code #7235 + + if (semicolon2->str() == ";") { + tok2 = tok2->next(); + AST_state state3(cpp); + if (Token::simpleMatch(tok2, "( {")) { + state3.op.push(tok2->next()); + tok2 = tok2->link()->next(); + } + compileExpression(tok2, state3); + + tok2 = findAstTop(semicolon1->next(), semicolon2); + if (tok2) + semicolon2->astOperand1(tok2); + tok2 = findAstTop(semicolon2->next(), endPar); + if (tok2) + semicolon2->astOperand2(tok2); + else if (!state3.op.empty()) + semicolon2->astOperand2(state3.op.top()); + semicolon1->astOperand2(semicolon2); + } else { + if (!cpp || state2.op.empty() || !Token::simpleMatch(state2.op.top(), ":")) + throw InternalError(tok, "syntax error", InternalError::SYNTAX); + + semicolon1->astOperand2(state2.op.top()); + } + + if (init != semicolon1) + semicolon1->astOperand1(init->astTop()); + tok->next()->astOperand1(tok); + tok->next()->astOperand2(semicolon1); + + createAstAtTokenInner(endPar->link(), endPar, cpp); + + return endPar; + } + + if (Token::simpleMatch(tok, "( {")) + return tok; + + if (Token::Match(tok, "%type% <") && tok->linkAt(1) && !Token::Match(tok->linkAt(1), "> [({]")) + return tok->linkAt(1); + + if (cpp && !tok->isKeyword() && Token::Match(tok, "%type% ::|<|%name%")) { + Token *tok2 = tok; + while (true) { + if (Token::Match(tok2, "%name%|> :: %name%")) + tok2 = tok2->tokAt(2); + else if (Token::Match(tok2, "%name% <") && tok2->linkAt(1)) + tok2 = tok2->linkAt(1); + else + break; + } + if (Token::Match(tok2, "%name%|> %name% {") && tok2->next()->varId() && iscpp11init(tok2->tokAt(2))) { + Token *const tok1 = tok = tok2->next(); + AST_state state(cpp); + compileExpression(tok, state); + createAstAtTokenInner(tok1->next(), tok1->linkAt(1), cpp); + return tok; + } + } + + if (Token::Match(tok, "%type% %name%|*|&|::") && !Token::Match(tok, "return|new")) { + int typecount = 0; + Token *typetok = tok; + while (Token::Match(typetok, "%type%|::|*|&")) { + if (typetok->isName() && !Token::simpleMatch(typetok->previous(), "::")) + typecount++; + typetok = typetok->next(); + } + if (Token::Match(typetok, "%var% =") && typetok->varId()) + tok = typetok; + + // Do not create AST for function declaration + if (typetok && + typecount >= 2 && + !Token::Match(tok, "return|throw") && + Token::Match(typetok->previous(), "%name% ( !!*") && + typetok->previous()->varId() == 0 && + !typetok->previous()->isKeyword() && + (Token::Match(typetok->link(), ") const|;|{") || Token::Match(typetok->link(), ") const| = delete ;"))) + return typetok; + } + + if (Token::Match(tok, "return|case") || + (cpp && tok->str() == "throw") || + !tok->previous() || + Token::Match(tok, "%name% %op%|(|[|.|::|<|?|;") || + (cpp && Token::Match(tok, "%name% {") && iscpp11init(tok->next())) || + Token::Match(tok->previous(), "[;{}] %cop%|++|--|( !!{") || + Token::Match(tok->previous(), "[;{}] %num%|%str%|%char%") || + Token::Match(tok->previous(), "[;{}] delete new")) { + if (cpp && (Token::Match(tok->tokAt(-2), "[;{}] new|delete %name%") || Token::Match(tok->tokAt(-3), "[;{}] :: new|delete %name%"))) + tok = tok->previous(); + + Token * const tok1 = tok; + AST_state state(cpp); + if (Token::Match(tok, "%name% (")) + state.functionCallEndPar = tok->linkAt(1); + if (Token::simpleMatch(tok->tokAt(-1), "::") && (!tok->tokAt(-2) || !tok->tokAt(-2)->isName())) + tok = tok->tokAt(-1); + compileExpression(tok, state); + Token * const endToken = tok; + if (endToken == tok1 || !endToken) + return tok1; + + createAstAtTokenInner(tok1->next(), endToken, cpp); + + return endToken->previous(); + } + + if (cpp && tok->str() == "{" && iscpp11init(tok)) { + Token * const tok1 = tok; + AST_state state(cpp); + compileExpression(tok, state); + Token* const endToken = tok; + if (endToken == tok1 || !endToken) + return tok1; + + createAstAtTokenInner(tok1->next(), endToken, cpp); + return endToken->previous(); + } + + return tok; +} + +void TokenList::createAst() const +{ + for (Token *tok = mTokensFrontBack.front; tok; tok = tok ? tok->next() : nullptr) { + Token* const nextTok = createAstAtToken(tok); + if (precedes(nextTok, tok)) + throw InternalError(tok, "Syntax Error: Infinite loop when creating AST.", InternalError::AST); + tok = nextTok; + } +} + +namespace { + struct OnException { + std::function f; + + ~OnException() { +#ifndef _MSC_VER + if (std::uncaught_exception()) + f(); +#endif + } + }; +} + +void TokenList::validateAst(bool print) const +{ + OnException oe{[&] { + if (print) + mTokensFrontBack.front->printOut(); + }}; + // Check for some known issues in AST to avoid crash/hang later on + std::set safeAstTokens; // list of "safe" AST tokens without endless recursion + for (const Token *tok = mTokensFrontBack.front; tok; tok = tok->next()) { + // Syntax error if binary operator only has 1 operand + if ((tok->isAssignmentOp() || tok->isComparisonOp() || Token::Match(tok,"[|^/%]")) && tok->astOperand1() && !tok->astOperand2()) + throw InternalError(tok, "Syntax Error: AST broken, binary operator has only one operand.", InternalError::AST); + + // Syntax error if we encounter "?" with operand2 that is not ":" + if (tok->str() == "?") { + if (!tok->astOperand1() || !tok->astOperand2()) + throw InternalError(tok, "AST broken, ternary operator missing operand(s)", InternalError::AST); + if (tok->astOperand2()->str() != ":") + throw InternalError(tok, "Syntax Error: AST broken, ternary operator lacks ':'.", InternalError::AST); + } + + // Check for endless recursion + const Token* parent = tok->astParent(); + if (parent) { + std::set astTokens; // list of ancestors + astTokens.insert(tok); + do { + if (safeAstTokens.find(parent) != safeAstTokens.end()) + break; + if (astTokens.find(parent) != astTokens.end()) + throw InternalError(tok, "AST broken: endless recursion from '" + tok->str() + "'", InternalError::AST); + astTokens.insert(parent); + } while ((parent = parent->astParent()) != nullptr); + safeAstTokens.insert(astTokens.cbegin(), astTokens.cend()); + } else if (tok->str() == ";") { + safeAstTokens.clear(); + } else { + safeAstTokens.insert(tok); + } + + // Don't check templates + if (tok->str() == "<" && tok->link()) { + tok = tok->link(); + continue; + } + if (tok->isCast()) { + if (!tok->astOperand2() && precedes(tok->astOperand1(), tok)) + throw InternalError(tok, "AST broken: '" + tok->str() + "' has improper operand.", InternalError::AST); + if (tok->astOperand1() && tok->link()) { // skip casts (not part of the AST) + tok = tok->link(); + continue; + } + } + + if (findLambdaEndToken(tok)) { // skip lambda captures + tok = tok->link(); + continue; + } + + // Check binary operators + if (Token::Match(tok, "%or%|%oror%|%assign%|%comp%")) { + // Skip pure virtual functions + if (Token::simpleMatch(tok->previous(), ") = 0")) + continue; + // Skip operator definitions + if (Token::simpleMatch(tok->previous(), "operator")) + continue; + // Skip incomplete code + if (!tok->astOperand1() && !tok->astOperand2() && !tok->astParent()) + continue; + // Skip lambda assignment and/or initializer + if (Token::Match(tok, "= {|^|[")) + continue; + // FIXME: Workaround broken AST assignment in type aliases + if (Token::Match(tok->previous(), "%name% = %name%")) + continue; + if (!tok->astOperand1() || !tok->astOperand2()) + throw InternalError(tok, "Syntax Error: AST broken, binary operator '" + tok->str() + "' doesn't have two operands.", InternalError::AST); + } + + // Check control blocks and asserts + if (Token::Match(tok->previous(), "if|while|for|switch|assert|ASSERT (")) { + if (!tok->astOperand1() || !tok->astOperand2()) + throw InternalError(tok, + "Syntax Error: AST broken, '" + tok->previous()->str() + + "' doesn't have two operands.", + InternalError::AST); + } + + // Check member access + if (Token::Match(tok, "%var% .")) { + if (!tok->astParent()) { + throw InternalError( + tok, "Syntax Error: AST broken, '" + tok->str() + "' doesn't have a parent.", InternalError::AST); + } + if (!tok->next()->astOperand1() || !tok->next()->astOperand2()) { + const std::string& op = + tok->next()->originalName().empty() ? tok->next()->str() : tok->next()->originalName(); + throw InternalError( + tok, "Syntax Error: AST broken, '" + op + "' doesn't have two operands.", InternalError::AST); + } + } + } +} + +std::string TokenList::getOrigFile(const Token *tok) const +{ + return mOrigFiles.at(tok->fileIndex()); +} + +const std::string& TokenList::file(const Token *tok) const +{ + return mFiles.at(tok->fileIndex()); +} + +std::string TokenList::fileLine(const Token *tok) const +{ + return ErrorMessage::FileLocation(tok, this).stringify(); +} + +bool TokenList::validateToken(const Token* tok) const +{ + if (!tok) + return true; + for (const Token *t = mTokensFrontBack.front; t; t = t->next()) { + if (tok==t) + return true; + } + return false; +} + +void TokenList::simplifyPlatformTypes() +{ + if (!mSettings) + return; + + const bool isCPP11 = isCPP() && (mSettings->standards.cpp >= Standards::CPP11); + + enum { isLongLong, isLong, isInt } type; + + /** @todo This assumes a flat address space. Not true for segmented address space (FAR *). */ + + if (mSettings->platform.sizeof_size_t == mSettings->platform.sizeof_long) + type = isLong; + else if (mSettings->platform.sizeof_size_t == mSettings->platform.sizeof_long_long) + type = isLongLong; + else if (mSettings->platform.sizeof_size_t == mSettings->platform.sizeof_int) + type = isInt; + else + return; + + for (Token *tok = front(); tok; tok = tok->next()) { + // pre-check to reduce unneeded match calls + if (!Token::Match(tok, "std| ::| %type%")) + continue; + bool isUnsigned; + if (Token::Match(tok, "std| ::| size_t|uintptr_t|uintmax_t")) { + if (isCPP11 && tok->strAt(-1) == "using" && tok->strAt(1) == "=") + continue; + isUnsigned = true; + } else if (Token::Match(tok, "std| ::| ssize_t|ptrdiff_t|intptr_t|intmax_t")) { + if (isCPP11 && tok->strAt(-1) == "using" && tok->strAt(1) == "=") + continue; + isUnsigned = false; + } else + continue; + + bool inStd = false; + if (tok->str() == "::") { + tok->deleteThis(); + } else if (tok->str() == "std") { + if (tok->next()->str() != "::") + continue; + inStd = true; + tok->deleteNext(); + tok->deleteThis(); + } + + if (inStd) + tok->originalName("std::" + tok->str()); + else + tok->originalName(tok->str()); + if (isUnsigned) + tok->isUnsigned(true); + + switch (type) { + case isLongLong: + tok->isLong(true); + tok->str("long"); + break; + case isLong: + tok->str("long"); + break; + case isInt: + tok->str("int"); + break; + } + } + + const std::string platform_type(mSettings->platform.toString()); + + for (Token *tok = front(); tok; tok = tok->next()) { + if (tok->tokType() != Token::eType && tok->tokType() != Token::eName) + continue; + + const Library::PlatformType * const platformtype = mSettings->library.platform_type(tok->str(), platform_type); + + if (platformtype) { + // check for namespace + if (tok->strAt(-1) == "::") { + const Token * tok1 = tok->tokAt(-2); + // skip when non-global namespace defined + if (tok1 && tok1->tokType() == Token::eName) + continue; + tok = tok->previous(); + tok->deleteThis(); + } + tok->originalName(tok->str()); + Token *typeToken; + if (platformtype->mConstPtr) { + tok->str("const"); + tok->isSimplifiedTypedef(true); + tok->insertToken("*")->isSimplifiedTypedef(true); + tok->insertToken(platformtype->mType)->isSimplifiedTypedef(true); + typeToken = tok; + } else if (platformtype->mPointer) { + tok->str(platformtype->mType); + tok->isSimplifiedTypedef(true); + typeToken = tok; + tok->insertToken("*")->isSimplifiedTypedef(true); + } else if (platformtype->mPtrPtr) { + tok->str(platformtype->mType); + tok->isSimplifiedTypedef(true); + typeToken = tok; + tok->insertToken("*")->isSimplifiedTypedef(true); + tok->insertToken("*")->isSimplifiedTypedef(true); + } else { + tok->str(platformtype->mType); + tok->isSimplifiedTypedef(true); + typeToken = tok; + } + if (platformtype->mSigned) + typeToken->isSigned(true); + if (platformtype->mUnsigned) + typeToken->isUnsigned(true); + if (platformtype->mLong) + typeToken->isLong(true); + } + } +} + +void TokenList::simplifyStdType() +{ + auto isVarDeclC = [](const Token* tok) -> bool { + if (!Token::simpleMatch(tok, "}")) + return false; + tok = tok->link()->previous(); + while (Token::Match(tok, "%name%")) { + if (Token::Match(tok, "struct|union|enum")) + return true; + tok = tok->previous(); + } + return false; + }; + + for (Token *tok = front(); tok; tok = tok->next()) { + + if (isC() && Token::Match(tok, "const|extern *|&|%name%") && (!tok->previous() || Token::Match(tok->previous(), "[;{}]"))) { + if (Token::Match(tok->next(), "%name% !!;")) + continue; + if (isVarDeclC(tok->previous())) + continue; + + tok->insertToken("int"); + tok->next()->isImplicitInt(true); + continue; + } + + if (Token::Match(tok, "char|short|int|long|unsigned|signed|double|float") || (isC() && (!mSettings || (mSettings->standards.c >= Standards::C99)) && Token::Match(tok, "complex|_Complex"))) { + bool isFloat= false; + bool isSigned = false; + bool isUnsigned = false; + bool isComplex = false; + int countLong = 0; + Token* typeSpec = nullptr; + + Token* tok2 = tok; + for (; tok2->next(); tok2 = tok2->next()) { + if (tok2->str() == "long") { + countLong++; + if (!isFloat) + typeSpec = tok2; + } else if (tok2->str() == "short") { + typeSpec = tok2; + } else if (tok2->str() == "unsigned") + isUnsigned = true; + else if (tok2->str() == "signed") + isSigned = true; + else if (Token::Match(tok2, "float|double")) { + isFloat = true; + typeSpec = tok2; + } else if (isC() && (!mSettings || (mSettings->standards.c >= Standards::C99)) && Token::Match(tok2, "complex|_Complex")) + isComplex = !isFloat || tok2->str() == "_Complex" || Token::Match(tok2->next(), "*|&|%name%"); // Ensure that "complex" is not the variables name + else if (Token::Match(tok2, "char|int")) { + if (!typeSpec) + typeSpec = tok2; + } else + break; + } + + if (!typeSpec) { // unsigned i; or similar declaration + if (!isComplex) { // Ensure that "complex" is not the variables name + tok->str("int"); + tok->isSigned(isSigned); + tok->isUnsigned(isUnsigned); + tok->isImplicitInt(true); + } + } else { + typeSpec->isLong(typeSpec->isLong() || (isFloat && countLong == 1) || countLong > 1); + typeSpec->isComplex(typeSpec->isComplex() || (isFloat && isComplex)); + typeSpec->isSigned(typeSpec->isSigned() || isSigned); + typeSpec->isUnsigned(typeSpec->isUnsigned() || isUnsigned); + + // Remove specifiers + const Token* tok3 = tok->previous(); + tok2 = tok2->previous(); + while (tok3 != tok2) { + if (tok2 != typeSpec && + (isComplex || !Token::Match(tok2, "complex|_Complex"))) // Ensure that "complex" is not the variables name + tok2->deleteThis(); + tok2 = tok2->previous(); + } + } + } + } +} + +bool TokenList::isKeyword(const std::string &str) const +{ + if (isCPP()) { + // TODO: integrate into keywords? + // types and literals are not handled as keywords + static const std::unordered_set cpp_types = {"bool", "false", "true"}; + if (cpp_types.find(str) != cpp_types.end()) + return false; + + if (mSettings) { + const auto &cpp_keywords = Keywords::getAll(mSettings->standards.cpp); + return cpp_keywords.find(str) != cpp_keywords.end(); + } + + static const auto& latest_cpp_keywords = Keywords::getAll(Standards::cppstd_t::CPPLatest); + return latest_cpp_keywords.find(str) != latest_cpp_keywords.end(); + } + + // TODO: integrate into Keywords? + // types are not handled as keywords + static const std::unordered_set c_types = {"char", "double", "float", "int", "long", "short"}; + if (c_types.find(str) != c_types.end()) + return false; + + if (mSettings) { + const auto &c_keywords = Keywords::getAll(mSettings->standards.c); + return c_keywords.find(str) != c_keywords.end(); + } + + static const auto& latest_c_keywords = Keywords::getAll(Standards::cstd_t::CLatest); + return latest_c_keywords.find(str) != latest_c_keywords.end(); +} + +bool TokenList::isC() const +{ + ASSERT_LANG(mLang != Standards::Language::None); + + // TODO: remove the fallback + if (mLang == Standards::Language::None) + return false; // treat as C++ by default + + return mLang == Standards::Language::C; +} + +bool TokenList::isCPP() const +{ + ASSERT_LANG(mLang != Standards::Language::None); + + // TODO: remove the fallback + if (mLang == Standards::Language::None) + return true; // treat as C++ by default + + return mLang == Standards::Language::CPP; +} + +void TokenList::setLang(Standards::Language lang) +{ + ASSERT_LANG(lang != Standards::Language::None); + ASSERT_LANG(mLang == Standards::Language::None); + + mLang = lang; +} diff --git a/cppcheck-2.14.0/lib/tokenlist.h b/cppcheck-2.14.0/lib/tokenlist.h new file mode 100644 index 00000000..c03cf3b6 --- /dev/null +++ b/cppcheck-2.14.0/lib/tokenlist.h @@ -0,0 +1,230 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef tokenlistH +#define tokenlistH +//--------------------------------------------------------------------------- + +#include "config.h" +#include "standards.h" + +#include +#include +#include +#include + +class Token; +class TokenList; +class Settings; + +namespace simplecpp { + class TokenList; +} + +/// @addtogroup Core +/// @{ + +/** + * @brief This struct stores pointers to the front and back tokens of the list this token is in. + */ +struct TokensFrontBack { + explicit TokensFrontBack(const TokenList& list) : list(list) {} + Token *front{}; + Token* back{}; + const TokenList& list; +}; + +class CPPCHECKLIB TokenList { +public: + // TODO: pass settings as reference + explicit TokenList(const Settings* settings); + ~TokenList(); + + TokenList(const TokenList &) = delete; + TokenList &operator=(const TokenList &) = delete; + + /** @return the source file path. e.g. "file.cpp" */ + const std::string& getSourceFilePath() const; + + /** @return true if the code is C */ + bool isC() const; + + /** @return true if the code is C++ */ + bool isCPP() const; + + void setLang(Standards::Language lang); + + /** + * 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 nonneg int lineno, const nonneg int column, const nonneg int fileno, bool split = false); + void addtoken(const std::string& str, const Token *locationTok); + + void addtoken(const Token *tok, const nonneg int lineno, const nonneg int column, const nonneg int fileno); + void addtoken(const Token *tok, const Token *locationTok); + void addtoken(const Token *tok); + + static void insertTokens(Token *dest, const Token *src, nonneg int n); + + /** + * Copy tokens. + * @param dest destination token where copied tokens will be inserted after + * @param first first token to copy + * @param last last token to copy + * @param one_line true=>copy all tokens to the same line as dest. false=>copy all tokens to dest while keeping the 'line breaks' + * @return new location of last token copied + */ + static Token *copyTokens(Token *dest, const Token *first, const Token *last, bool one_line = true); + + /** + * Create tokens from code. + * The code must be preprocessed first: + * - multiline strings are not handled. + * - UTF in the code are not handled. + * - comments are not handled. + * @param code input stream for code + * @param file0 source file name + */ + bool createTokens(std::istream &code, const std::string& file0); + bool createTokens(std::istream &code, Standards::Language lang); + + void createTokens(simplecpp::TokenList&& tokenList); + + /** Deallocate list */ + void deallocateTokens(); + + /** append file name if seen the first time; return its index in any case */ + int appendFileIfNew(std::string fileName); + + /** get first token of list */ + const Token *front() const { + return mTokensFrontBack.front; + } + // NOLINTNEXTLINE(readability-make-member-function-const) - do not allow usage of mutable pointer from const object + Token *front() { + return mTokensFrontBack.front; + } + + /** get last token of list */ + const Token *back() const { + return mTokensFrontBack.back; + } + // NOLINTNEXTLINE(readability-make-member-function-const) - do not allow usage of mutable pointer from const object + Token *back() { + return mTokensFrontBack.back; + } + + /** + * Get filenames (the sourcefile + the files it include). + * The first filename is the filename for the sourcefile + * @return vector with filenames + */ + const std::vector& getFiles() const { + return mFiles; + } + + std::string getOrigFile(const Token *tok) const; + + /** + * get filename for given token + * @param tok The given token + * @return filename for the given token + */ + const std::string& file(const Token *tok) const; + + /** + * Get file:line for a given token + * @param tok given token + * @return location for given token + */ + std::string fileLine(const Token *tok) const; + + /** + * Calculates a hash of the token list used to compare multiple + * token lists with each other as quickly as possible. + */ + std::size_t calculateHash() const; + + /** + * Create abstract syntax tree. + */ + void createAst() const; + + /** + * Check abstract syntax tree. + * Throws InternalError on failure + */ + void validateAst(bool print) const; + + /** + * Verify that the given token is an element of the tokenlist. + * That method is implemented for debugging purposes. + * @param[in] tok token to be checked + * \return true if token was found in tokenlist, false else. In case of nullptr true is returned. + */ + bool validateToken(const Token* tok) const; + + /** + * Convert platform dependent types to standard types. + * 32 bits: size_t -> unsigned long + * 64 bits: size_t -> unsigned long long + */ + void simplifyPlatformTypes(); + + /** + * Collapse compound standard types into a single token. + * unsigned long long int => long _isUnsigned=true,_isLong=true + */ + void simplifyStdType(); + + void clangSetOrigFiles(); + + bool isKeyword(const std::string &str) const; + +private: + void determineCppC(); + + bool createTokensInternal(std::istream &code, const std::string& file0); + + /** Token list */ + TokensFrontBack mTokensFrontBack; + + /** filenames for the tokenized source code (source + included) */ + std::vector mFiles; + + /** Original filenames for the tokenized source code (source + included) */ + std::vector mOrigFiles; + + /** settings */ + const Settings* const mSettings{}; + + /** File is known to be C/C++ code */ + Standards::Language mLang{Standards::Language::None}; +}; + +/// @} + +const Token* isLambdaCaptureList(const Token* tok); +const Token* findLambdaEndTokenWithoutAST(const Token* tok); + +//--------------------------------------------------------------------------- +#endif // tokenlistH diff --git a/cppcheck-2.14.0/lib/tokenrange.h b/cppcheck-2.14.0/lib/tokenrange.h new file mode 100644 index 00000000..dda2710e --- /dev/null +++ b/cppcheck-2.14.0/lib/tokenrange.h @@ -0,0 +1,83 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef tokenrangeH +#define tokenrangeH +//--------------------------------------------------------------------------- + +#include "config.h" + +#include +#include +#include + +class Token; + +template )> +class TokenRangeBase { + T* mFront; + T* mBack; + +public: + TokenRangeBase(T* front, T* back) : mFront(front), mBack(back) {} + + struct TokenIterator { + using iterator_category = std::forward_iterator_tag; + using value_type = T*; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = T*; + + T* mt; + TokenIterator() : mt(nullptr) {} + explicit TokenIterator(T* t) : mt(t) {} + TokenIterator& operator++() { + mt = mt->next(); + return *this; + } + bool operator==(const TokenIterator& b) const { + return mt == b.mt; + } + bool operator!=(const TokenIterator& b) const { + return mt != b.mt; + } + T* operator*() const { + return mt; + } + }; + + TokenIterator begin() const { + return TokenIterator(mFront); + } + TokenIterator end() const { + return TokenIterator(mBack); + } +}; + +class TokenRange : public TokenRangeBase { +public: + TokenRange(Token* front, Token* back) : TokenRangeBase(front, back) {} +}; + +class ConstTokenRange : public TokenRangeBase { +public: + ConstTokenRange(const Token* front, const Token* back) : TokenRangeBase(front, back) {} +}; + +#endif // tokenrangeH diff --git a/cppcheck-2.14.0/lib/utils.cpp b/cppcheck-2.14.0/lib/utils.cpp new file mode 100644 index 00000000..cf8c0d0c --- /dev/null +++ b/cppcheck-2.14.0/lib/utils.cpp @@ -0,0 +1,131 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "utils.h" + +#include +#include +#include +#include +#include + + +int caseInsensitiveStringCompare(const std::string &lhs, const std::string &rhs) +{ + if (lhs.size() != rhs.size()) + return (lhs.size() < rhs.size()) ? -1 : 1; + for (unsigned int i = 0; i < lhs.size(); ++i) { + const int c1 = std::toupper(lhs[i]); + const int c2 = std::toupper(rhs[i]); + if (c1 != c2) + return (c1 < c2) ? -1 : 1; + } + return 0; +} + +bool isValidGlobPattern(const std::string& pattern) +{ + for (std::string::const_iterator i = pattern.cbegin(); i != pattern.cend(); ++i) { + if (*i == '*' || *i == '?') { + const std::string::const_iterator j = i + 1; + if (j != pattern.cend() && (*j == '*' || *j == '?')) { + return false; + } + } + } + return true; +} + +bool matchglob(const std::string& pattern, const std::string& name) +{ + const char* p = pattern.c_str(); + const char* n = name.c_str(); + std::stack, std::vector>> 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.emplace(p, n); + } + break; + case '?': + // Any character matches unless we're at the end of the name + if (*n != '\0') { + n++; + } else { + matching = false; + } + break; + default: + // Non-wildcard characters match literally + if (*n == *p) { + n++; + } else if (*n == '\\' && *p == '/') { + n++; + } else if (*n == '/' && *p == '\\') { + n++; + } else { + matching = false; + } + break; + } + p++; + } + + // If we haven't failed matching and we've reached the end of the name, then success + if (matching && *n == '\0') { + return true; + } + + // If there are no other paths to try, then fail + if (backtrack.empty()) { + return false; + } + + // Restore pointers from backtrack stack + p = backtrack.top().first; + n = backtrack.top().second; + backtrack.pop(); + + // Advance name pointer by one because the current position didn't work + n++; + } +} + +bool matchglobs(const std::vector &patterns, const std::string &name) { + return std::any_of(begin(patterns), end(patterns), [&name](const std::string &pattern) { + return matchglob(pattern, name); + }); +} + +void strTolower(std::string& str) +{ + // This wrapper exists because Sun's CC does not allow a static_cast + // from extern "C" int(*)(int) to int(*)(int). + std::transform(str.cbegin(), str.cend(), str.begin(), [](int c) { + return std::tolower(c); + }); +} diff --git a/cppcheck-2.14.0/lib/utils.h b/cppcheck-2.14.0/lib/utils.h new file mode 100644 index 00000000..740acf00 --- /dev/null +++ b/cppcheck-2.14.0/lib/utils.h @@ -0,0 +1,370 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef utilsH +#define utilsH +//--------------------------------------------------------------------------- + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct SelectMapKeys { + template + // NOLINTNEXTLINE(readability-const-return-type) - false positive + typename Pair::first_type operator()(const Pair& p) const { + return p.first; + } +}; + +struct SelectMapValues { + template + typename Pair::second_type operator()(const Pair& p) const { + return p.second; + } +}; + +struct OnExit { + std::function f; + + ~OnExit() { + f(); + } +}; + +template +bool contains(const Range& r, const T& x) +{ + return std::find(r.cbegin(), r.cend(), x) != r.cend(); +} + +template +bool contains(const std::initializer_list& r, const T& x) +{ + return std::find(r.begin(), r.end(), x) != r.end(); +} + +template +bool contains(const std::initializer_list& r, const U& x) +{ + return std::find(r.begin(), r.end(), x) != r.end(); +} + +template +inline std::array makeArray(T x, Ts... xs) +{ + return {std::move(x), std::move(xs)...}; +} + +// Enum hash for C++11. This is not needed in C++14 +struct EnumClassHash { + template + std::size_t operator()(T t) const + { + return static_cast(t); + } +}; + +inline bool startsWith(const std::string& str, const char start[], std::size_t startlen) +{ + return str.compare(0, startlen, start) == 0; +} + +template +bool startsWith(const std::string& str, const char (&start)[N]) +{ + return startsWith(str, start, N - 1); +} + +inline bool startsWith(const std::string& str, const std::string& start) +{ + return startsWith(str, start.c_str(), start.length()); +} + +inline bool endsWith(const std::string &str, char c) +{ + return !str.empty() && str.back() == c; +} + +inline bool endsWith(const std::string &str, const char end[], std::size_t endlen) +{ + return (str.size() >= endlen) && (str.compare(str.size()-endlen, endlen, end)==0); +} + +template +bool endsWith(const std::string& str, const char (&end)[N]) +{ + return endsWith(str, end, N - 1); +} + +inline static bool isPrefixStringCharLiteral(const std::string &str, char q, const std::string& p) +{ + // str must be at least the prefix plus the start and end quote + if (str.length() < p.length() + 2) + return false; + + // check for end quote + if (!endsWith(str, q)) + return false; + + // check for start quote + if (str[p.size()] != q) + return false; + + // check for prefix + if (str.compare(0, p.size(), p) != 0) + return false; + + return true; +} + +inline static bool isStringCharLiteral(const std::string &str, char q) +{ + // early out to avoid the loop + if (!endsWith(str, q)) + return false; + + static const std::array suffixes{"", "u8", "u", "U", "L"}; + return std::any_of(suffixes.cbegin(), suffixes.cend(), [&](const std::string& p) { + return isPrefixStringCharLiteral(str, q, p); + }); +} + +inline static bool isStringLiteral(const std::string &str) +{ + return isStringCharLiteral(str, '"'); +} + +inline static bool isCharLiteral(const std::string &str) +{ + return isStringCharLiteral(str, '\''); +} + +inline static std::string getStringCharLiteral(const std::string &str, char q) +{ + const std::size_t quotePos = str.find(q); + return str.substr(quotePos + 1U, str.size() - quotePos - 2U); +} + +inline static std::string getStringLiteral(const std::string &str) +{ + if (isStringLiteral(str)) + return getStringCharLiteral(str, '"'); + return ""; +} + +inline static std::string getCharLiteral(const std::string &str) +{ + if (isCharLiteral(str)) + return getStringCharLiteral(str, '\''); + return ""; +} + +inline static const char *getOrdinalText(int i) +{ + if (i == 1) + return "st"; + if (i == 2) + return "nd"; + if (i == 3) + return "rd"; + return "th"; +} + +CPPCHECKLIB int caseInsensitiveStringCompare(const std::string& lhs, const std::string& rhs); + +CPPCHECKLIB bool isValidGlobPattern(const std::string& pattern); + +CPPCHECKLIB bool matchglob(const std::string& pattern, const std::string& name); + +CPPCHECKLIB bool matchglobs(const std::vector &patterns, const std::string &name); + +CPPCHECKLIB void strTolower(std::string& str); + +template::value, bool>::type=true> +bool strToInt(const std::string& str, T &num, std::string* err = nullptr) +{ + long long tmp; + try { + std::size_t idx = 0; + tmp = std::stoll(str, &idx); + if (idx != str.size()) { + if (err) + *err = "not an integer"; + return false; + } + } catch (const std::out_of_range&) { + if (err) + *err = "out of range (stoll)"; + return false; + } catch (const std::invalid_argument &) { + if (err) + *err = "not an integer"; + return false; + } + if (str.front() == '-' && std::numeric_limits::min() == 0) { + if (err) + *err = "needs to be positive"; + return false; + } + if (tmp < std::numeric_limits::min() || tmp > std::numeric_limits::max()) { + if (err) + *err = "out of range (limits)"; + return false; + } + num = static_cast(tmp); + return true; +} + +template::value, bool>::type=true> +bool strToInt(const std::string& str, T &num, std::string* err = nullptr) +{ + unsigned long long tmp; + try { + std::size_t idx = 0; + tmp = std::stoull(str, &idx); + if (idx != str.size()) { + if (err) + *err = "not an integer"; + return false; + } + } catch (const std::out_of_range&) { + if (err) + *err = "out of range (stoull)"; + return false; + } catch (const std::invalid_argument &) { + if (err) + *err = "not an integer"; + return false; + } + if (str.front() == '-') { + if (err) + *err = "needs to be positive"; + return false; + } + if (tmp > std::numeric_limits::max()) { + if (err) + *err = "out of range (limits)"; + return false; + } + num = tmp; + return true; +} + +template +T strToInt(const std::string& str) +{ + T tmp = 0; + std::string err; + if (!strToInt(str, tmp, &err)) + throw std::runtime_error("converting '" + str + "' to integer failed - " + err); + return tmp; +} + +/** + * Simple helper function: + * \return size of array + * */ +template +// cppcheck-suppress unusedFunction - only used in conditional code +std::size_t getArrayLength(const T (& /*unused*/)[size]) +{ + return size; +} + +/** + * @brief get id string. i.e. for dump files + * it will be a hexadecimal output. + */ +static inline std::string id_string_i(std::uintptr_t l) +{ + if (!l) + return "0"; + + static constexpr int ptr_size = sizeof(void*); + + // two characters of each byte / contains terminating \0 + static constexpr int buf_size = (ptr_size * 2) + 1; + + char buf[buf_size]; + + // needs to be signed so we don't underflow in padding loop + int idx = buf_size - 1; + buf[idx] = '\0'; + + while (l != 0) + { + char c; + const uintptr_t temp = l % 16; // get the remainder + if (temp < 10) { + // 0-9 + c = '0' + temp; + } + else { + // a-f + c = 'a' + (temp - 10); + } + buf[--idx] = c; // store in reverse order + l = l / 16; + } + + return &buf[idx]; +} + +static inline std::string id_string(const void* p) +{ + return id_string_i(reinterpret_cast(p)); +} + +static inline const char* bool_to_string(bool b) +{ + return b ? "true" : "false"; +} + +namespace cppcheck +{ + NORETURN inline void unreachable() + { +#if defined(__GNUC__) + __builtin_unreachable(); +#elif defined(_MSC_VER) + __assume(false); +#else +#error "no unreachable implementation" +#endif + } +} + +template +static inline T* empty_if_null(T* p) +{ + return p ? p : ""; +} + +#endif diff --git a/cppcheck-2.14.0/lib/valueflow.cpp b/cppcheck-2.14.0/lib/valueflow.cpp new file mode 100644 index 00000000..3fa962b2 --- /dev/null +++ b/cppcheck-2.14.0/lib/valueflow.cpp @@ -0,0 +1,9751 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * @brief This is the ValueFlow component in Cppcheck. + * + * Each @sa Token in the token list has a list of values. These are + * the "possible" values for the Token at runtime. + * + * In the --debug and --debug-normal output you can see the ValueFlow data. For example: + * + * int f() + * { + * int x = 10; + * return 4 * x + 2; + * } + * + * The --debug-normal output says: + * + * ##Value flow + * Line 3 + * 10 always 10 + * Line 4 + * 4 always 4 + * * always 40 + * x always 10 + * + always 42 + * 2 always 2 + * + * All value flow analysis is executed in the ValueFlow::setValues() function. The ValueFlow analysis is executed after + * the tokenizer/ast/symboldatabase/etc.. The ValueFlow analysis is done in a series of valueFlow* function calls, where + * each such function call can only use results from previous function calls. The function calls should be arranged so + * that valueFlow* that do not require previous ValueFlow information should be first. + * + * Type of analysis + * ================ + * + * This is "flow sensitive" value flow analysis. We _usually_ track the value for 1 variable at a time. + * + * How are calculations handled + * ============================ + * + * Here is an example code: + * + * x = 3 + 4; + * + * The valueFlowNumber set the values for the "3" and "4" tokens by calling setTokenValue(). + * The setTokenValue() handle the calculations automatically. When both "3" and "4" have values, the "+" can be + * calculated. setTokenValue() recursively calls itself when parents in calculations can be calculated. + * + * Forward / Reverse flow analysis + * =============================== + * + * In forward value flow analysis we know a value and see what happens when we are stepping the program forward. Like + * normal execution. The valueFlowForward is used in this analysis. + * + * In reverse value flow analysis we know the value of a variable at line X. And try to "execute backwards" to determine + * possible values before line X. The valueFlowReverse is used in this analysis. + * + * + */ + +#include "valueflow.h" + +#include "analyzer.h" +#include "astutils.h" +#include "calculate.h" +#include "checkuninitvar.h" +#include "config.h" +#include "errorlogger.h" +#include "errortypes.h" +#include "findtoken.h" +#include "forwardanalyzer.h" +#include "infer.h" +#include "library.h" +#include "mathlib.h" +#include "path.h" +#include "platform.h" +#include "programmemory.h" +#include "reverseanalyzer.h" +#include "settings.h" +#include "smallvector.h" +#include "sourcelocation.h" +#include "standards.h" +#include "symboldatabase.h" +#include "timer.h" +#include "token.h" +#include "tokenlist.h" +#include "utils.h" +#include "valueptr.h" +#include "vfvalue.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void bailoutInternal(const std::string& type, const TokenList &tokenlist, ErrorLogger &errorLogger, const Token *tok, const std::string &what, const std::string &file, int line, std::string function) +{ + if (function.find("operator") != std::string::npos) + function = "(valueFlow)"; + ErrorMessage::FileLocation loc(tok, &tokenlist); + const std::string location = Path::stripDirectoryPart(file) + ":" + std::to_string(line) + ":"; + ErrorMessage errmsg({std::move(loc)}, tokenlist.getSourceFilePath(), Severity::debug, + (file.empty() ? "" : location) + function + " bailout: " + what, type, Certainty::normal); + errorLogger.reportErr(errmsg); +} + +#define bailout2(type, tokenlist, errorLogger, tok, what) bailoutInternal((type), (tokenlist), (errorLogger), (tok), (what), __FILE__, __LINE__, __func__) + +#define bailout(tokenlist, errorLogger, tok, what) bailout2("valueFlowBailout", (tokenlist), (errorLogger), (tok), (what)) + +#define bailoutIncompleteVar(tokenlist, errorLogger, tok, what) bailoutInternal("valueFlowBailoutIncompleteVar", (tokenlist), (errorLogger), (tok), (what), "", 0, __func__) + +static std::string debugString(const ValueFlow::Value& v) +{ + std::string kind; + switch (v.valueKind) { + + case ValueFlow::Value::ValueKind::Impossible: + case ValueFlow::Value::ValueKind::Known: + kind = "always"; + break; + case ValueFlow::Value::ValueKind::Inconclusive: + kind = "inconclusive"; + break; + case ValueFlow::Value::ValueKind::Possible: + kind = "possible"; + break; + } + return kind + " " + v.toString(); +} + +static void setSourceLocation(ValueFlow::Value& v, + SourceLocation ctx, + const Token* tok, + SourceLocation local = SourceLocation::current()) +{ + std::string file = ctx.file_name(); + if (file.empty()) + return; + std::string s = Path::stripDirectoryPart(file) + ":" + std::to_string(ctx.line()) + ": " + ctx.function_name() + + " => " + local.function_name() + ": " + debugString(v); + v.debugPath.emplace_back(tok, std::move(s)); +} + +static void changeKnownToPossible(std::list &values, int indirect=-1) +{ + for (ValueFlow::Value& v: values) { + if (indirect >= 0 && v.indirect != indirect) + continue; + v.changeKnownToPossible(); + } +} + +static void removeImpossible(std::list& values, int indirect = -1) +{ + values.remove_if([&](const ValueFlow::Value& v) { + if (indirect >= 0 && v.indirect != indirect) + return false; + return v.isImpossible(); + }); +} + +static void lowerToPossible(std::list& values, int indirect = -1) +{ + changeKnownToPossible(values, indirect); + removeImpossible(values, indirect); +} + +static void changePossibleToKnown(std::list& values, int indirect = -1) +{ + for (ValueFlow::Value& v : values) { + if (indirect >= 0 && v.indirect != indirect) + continue; + if (!v.isPossible()) + continue; + if (v.bound != ValueFlow::Value::Bound::Point) + continue; + v.setKnown(); + } +} + +static bool isNonConditionalPossibleIntValue(const ValueFlow::Value& v) +{ + if (v.conditional) + return false; + if (v.condition) + return false; + if (!v.isPossible()) + return false; + if (!v.isIntValue()) + return false; + return true; +} + +static void setValueUpperBound(ValueFlow::Value& value, bool upper) +{ + if (upper) + value.bound = ValueFlow::Value::Bound::Upper; + else + value.bound = ValueFlow::Value::Bound::Lower; +} + +static void setValueBound(ValueFlow::Value& value, const Token* tok, bool invert) +{ + if (Token::Match(tok, "<|<=")) { + setValueUpperBound(value, !invert); + } else if (Token::Match(tok, ">|>=")) { + setValueUpperBound(value, invert); + } +} + +static void setConditionalValue(ValueFlow::Value& value, const Token* tok, MathLib::bigint i) +{ + assert(value.isIntValue()); + value.intvalue = i; + value.assumeCondition(tok); + value.setPossible(); +} + +static void setConditionalValues(const Token* tok, + bool lhs, + MathLib::bigint value, + ValueFlow::Value& true_value, + ValueFlow::Value& false_value) +{ + if (Token::Match(tok, "==|!=|>=|<=")) { + setConditionalValue(true_value, tok, value); + const char* greaterThan = ">="; + const char* lessThan = "<="; + if (lhs) + std::swap(greaterThan, lessThan); + if (Token::simpleMatch(tok, greaterThan, strlen(greaterThan))) { + setConditionalValue(false_value, tok, value - 1); + } else if (Token::simpleMatch(tok, lessThan, strlen(lessThan))) { + setConditionalValue(false_value, tok, value + 1); + } else { + setConditionalValue(false_value, tok, value); + } + } else { + const char* greaterThan = ">"; + const char* lessThan = "<"; + if (lhs) + std::swap(greaterThan, lessThan); + if (Token::simpleMatch(tok, greaterThan, strlen(greaterThan))) { + setConditionalValue(true_value, tok, value + 1); + setConditionalValue(false_value, tok, value); + } else if (Token::simpleMatch(tok, lessThan, strlen(lessThan))) { + setConditionalValue(true_value, tok, value - 1); + setConditionalValue(false_value, tok, value); + } + } + setValueBound(true_value, tok, lhs); + setValueBound(false_value, tok, !lhs); +} + +static bool isSaturated(MathLib::bigint value) +{ + return value == std::numeric_limits::max() || value == std::numeric_limits::min(); +} + +static void parseCompareEachInt( + const Token* tok, + const std::function& each, + const std::function(const Token*)>& evaluate) +{ + if (!tok->astOperand1() || !tok->astOperand2()) + return; + if (tok->isComparisonOp()) { + std::vector value1 = evaluate(tok->astOperand1()); + std::vector value2 = evaluate(tok->astOperand2()); + if (!value1.empty() && !value2.empty()) { + if (tok->astOperand1()->hasKnownIntValue()) + value2.clear(); + if (tok->astOperand2()->hasKnownIntValue()) + value1.clear(); + } + for (const ValueFlow::Value& v1 : value1) { + ValueFlow::Value true_value = v1; + ValueFlow::Value false_value = v1; + if (isSaturated(v1.intvalue) || astIsFloat(tok->astOperand2(), /*unknown*/ false)) + continue; + setConditionalValues(tok, true, v1.intvalue, true_value, false_value); + each(tok->astOperand2(), std::move(true_value), std::move(false_value)); + } + for (const ValueFlow::Value& v2 : value2) { + ValueFlow::Value true_value = v2; + ValueFlow::Value false_value = v2; + if (isSaturated(v2.intvalue) || astIsFloat(tok->astOperand1(), /*unknown*/ false)) + continue; + setConditionalValues(tok, false, v2.intvalue, true_value, false_value); + each(tok->astOperand1(), std::move(true_value), std::move(false_value)); + } + } +} + +static void parseCompareEachInt( + const Token* tok, + const std::function& each) +{ + parseCompareEachInt(tok, each, [](const Token* t) -> std::vector { + if (t->hasKnownIntValue()) + return {t->values().front()}; + std::vector result; + std::copy_if(t->values().cbegin(), t->values().cend(), std::back_inserter(result), [&](const ValueFlow::Value& v) { + if (v.path < 1) + return false; + if (!isNonConditionalPossibleIntValue(v)) + return false; + return true; + }); + return result; + }); +} + +const Token* ValueFlow::parseCompareInt(const Token* tok, + ValueFlow::Value& true_value, + ValueFlow::Value& false_value, + const std::function(const Token*)>& evaluate) +{ + const Token* result = nullptr; + parseCompareEachInt( + tok, + [&](const Token* vartok, ValueFlow::Value true_value2, ValueFlow::Value false_value2) { + if (result) + return; + result = vartok; + true_value = std::move(true_value2); + false_value = std::move(false_value2); + }, + [&](const Token* t) -> std::vector { + std::vector r; + std::vector v = evaluate(t); + + std::transform(v.cbegin(), v.cend(), std::back_inserter(r), [&](MathLib::bigint i) { + return ValueFlow::Value{i}; + }); + return r; + }); + return result; +} + +const Token *ValueFlow::parseCompareInt(const Token *tok, ValueFlow::Value &true_value, ValueFlow::Value &false_value) +{ + return parseCompareInt(tok, true_value, false_value, [](const Token* t) -> std::vector { + if (t->hasKnownIntValue()) + return {t->values().front().intvalue}; + return std::vector{}; + }); +} + +static bool isEscapeScope(const Token* tok, const Settings& settings, bool unknown = false) +{ + if (!Token::simpleMatch(tok, "{")) + return false; + // TODO this search for termTok in all subscopes. It should check the end of the scope. + const Token * termTok = Token::findmatch(tok, "return|continue|break|throw|goto", tok->link()); + if (termTok && termTok->scope() == tok->scope()) + return true; + std::string unknownFunction; + if (settings.library.isScopeNoReturn(tok->link(), &unknownFunction)) + return unknownFunction.empty() || unknown; + return false; +} + +static ValueFlow::Value castValue(ValueFlow::Value value, const ValueType::Sign sign, nonneg int bit) +{ + if (value.isFloatValue()) { + value.valueType = ValueFlow::Value::ValueType::INT; + if (value.floatValue >= std::numeric_limits::min() && value.floatValue <= std::numeric_limits::max()) { + value.intvalue = value.floatValue; + } else { // don't perform UB + value.intvalue = 0; + } + } + if (bit < MathLib::bigint_bits) { + constexpr MathLib::biguint one = 1; + value.intvalue &= (one << bit) - 1; + if (sign == ValueType::Sign::SIGNED && value.intvalue & (one << (bit - 1))) { + value.intvalue |= ~((one << bit) - 1ULL); + } + } + return value; +} + +static bool isNumeric(const ValueFlow::Value& value) { + return value.isIntValue() || value.isFloatValue(); +} + +void ValueFlow::combineValueProperties(const ValueFlow::Value &value1, const ValueFlow::Value &value2, ValueFlow::Value &result) +{ + if (value1.isKnown() && value2.isKnown()) + result.setKnown(); + else if (value1.isImpossible() || value2.isImpossible()) + result.setImpossible(); + else if (value1.isInconclusive() || value2.isInconclusive()) + result.setInconclusive(); + else + result.setPossible(); + if (value1.tokvalue) + result.tokvalue = value1.tokvalue; + else if (value2.tokvalue) + result.tokvalue = value2.tokvalue; + if (value1.isSymbolicValue()) { + result.valueType = value1.valueType; + result.tokvalue = value1.tokvalue; + } + if (value2.isSymbolicValue()) { + result.valueType = value2.valueType; + result.tokvalue = value2.tokvalue; + } + if (value1.isIteratorValue()) + result.valueType = value1.valueType; + if (value2.isIteratorValue()) + result.valueType = value2.valueType; + result.condition = value1.condition ? value1.condition : value2.condition; + result.varId = (value1.varId != 0) ? value1.varId : value2.varId; + result.varvalue = (result.varId == value1.varId) ? value1.varvalue : value2.varvalue; + result.errorPath = (value1.errorPath.empty() ? value2 : value1).errorPath; + result.safe = value1.safe || value2.safe; + if (value1.bound == ValueFlow::Value::Bound::Point || value2.bound == ValueFlow::Value::Bound::Point) { + if (value1.bound == ValueFlow::Value::Bound::Upper || value2.bound == ValueFlow::Value::Bound::Upper) + result.bound = ValueFlow::Value::Bound::Upper; + if (value1.bound == ValueFlow::Value::Bound::Lower || value2.bound == ValueFlow::Value::Bound::Lower) + result.bound = ValueFlow::Value::Bound::Lower; + } + if (value1.path != value2.path) + result.path = -1; + else + result.path = value1.path; +} + +static const Token *getCastTypeStartToken(const Token *parent, const Settings& settings) +{ + // TODO: This might be a generic utility function? + if (!Token::Match(parent, "{|(")) + return nullptr; + // Functional cast + if (parent->isBinaryOp() && Token::Match(parent->astOperand1(), "%type% (|{") && + parent->astOperand1()->tokType() == Token::eType && astIsPrimitive(parent)) + return parent->astOperand1(); + if (parent->str() != "(") + return nullptr; + if (!parent->astOperand2() && Token::Match(parent, "( %name%|::")) { + const Token* ftok = parent->next(); + if (ftok->isStandardType()) + return ftok; + if (Token::simpleMatch(ftok, "::")) + ftok = ftok->next(); + while (Token::Match(ftok, "%name% ::")) + ftok = ftok->tokAt(2); + if (settings.library.isNotLibraryFunction(ftok)) + return parent->next(); + } + if (parent->astOperand2() && Token::Match(parent->astOperand1(), "const_cast|dynamic_cast|reinterpret_cast|static_cast <")) + return parent->astOperand1()->tokAt(2); + return nullptr; +} + +// does the operation cause a loss of information? +static bool isNonInvertibleOperation(const Token* tok) +{ + return !Token::Match(tok, "+|-"); +} + +static bool isComputableValue(const Token* parent, const ValueFlow::Value& value) +{ + const bool noninvertible = isNonInvertibleOperation(parent); + if (noninvertible && value.isImpossible()) + return false; + if (!value.isIntValue() && !value.isFloatValue() && !value.isTokValue() && !value.isIteratorValue()) + return false; + if (value.isIteratorValue() && !Token::Match(parent, "+|-")) + return false; + if (value.isTokValue() && (!parent->isComparisonOp() || !Token::Match(value.tokvalue, "{|%str%"))) + return false; + return true; +} + +static Library::Container::Yield getContainerYield(Token* tok, const Settings& settings, Token** parent = nullptr) +{ + if (Token::Match(tok, ". %name% (") && tok->astParent() == tok->tokAt(2) && tok->astOperand1() && + tok->astOperand1()->valueType()) { + const Library::Container* c = getLibraryContainer(tok->astOperand1()); + if (parent) + *parent = tok->astParent(); + return c ? c->getYield(tok->strAt(1)) : Library::Container::Yield::NO_YIELD; + } + if (Token::Match(tok->previous(), "%name% (")) { + if (parent) + *parent = tok; + if (const Library::Function* f = settings.library.getFunction(tok->previous())) { + return f->containerYield; + } + } + return Library::Container::Yield::NO_YIELD; +} + +/** Set token value for cast */ +static void setTokenValueCast(Token *parent, const ValueType &valueType, const ValueFlow::Value &value, const Settings &settings); + +static bool isCompatibleValueTypes(ValueFlow::Value::ValueType x, ValueFlow::Value::ValueType y) +{ + static const std::unordered_map, + EnumClassHash> + compatibleTypes = { + {ValueFlow::Value::ValueType::INT, + {ValueFlow::Value::ValueType::FLOAT, + ValueFlow::Value::ValueType::SYMBOLIC, + ValueFlow::Value::ValueType::TOK}}, + {ValueFlow::Value::ValueType::FLOAT, {ValueFlow::Value::ValueType::INT}}, + {ValueFlow::Value::ValueType::TOK, {ValueFlow::Value::ValueType::INT}}, + {ValueFlow::Value::ValueType::ITERATOR_START, {ValueFlow::Value::ValueType::INT}}, + {ValueFlow::Value::ValueType::ITERATOR_END, {ValueFlow::Value::ValueType::INT}}, + }; + if (x == y) + return true; + auto it = compatibleTypes.find(x); + if (it == compatibleTypes.end()) + return false; + return it->second.count(y) > 0; +} + +static bool isCompatibleValues(const ValueFlow::Value& value1, const ValueFlow::Value& value2) +{ + if (value1.isSymbolicValue() && value2.isSymbolicValue() && value1.tokvalue->exprId() != value2.tokvalue->exprId()) + return false; + if (!isCompatibleValueTypes(value1.valueType, value2.valueType)) + return false; + if (value1.isKnown() || value2.isKnown()) + return true; + if (value1.isImpossible() || value2.isImpossible()) + return false; + if (value1.varId == 0 || value2.varId == 0) + return true; + if (value1.varId == value2.varId && value1.varvalue == value2.varvalue && value1.isIntValue() && value2.isIntValue()) + return true; + return false; +} + +static ValueFlow::Value truncateImplicitConversion(Token* parent, const ValueFlow::Value& value, const Settings& settings) +{ + if (!value.isIntValue() && !value.isFloatValue()) + return value; + if (!parent) + return value; + if (!parent->isBinaryOp()) + return value; + if (!parent->isConstOp()) + return value; + if (!astIsIntegral(parent->astOperand1(), false)) + return value; + if (!astIsIntegral(parent->astOperand2(), false)) + return value; + const ValueType* vt1 = parent->astOperand1()->valueType(); + const ValueType* vt2 = parent->astOperand2()->valueType(); + // If the sign is the same there is no truncation + if (vt1->sign == vt2->sign) + return value; + const size_t n1 = ValueFlow::getSizeOf(*vt1, settings); + const size_t n2 = ValueFlow::getSizeOf(*vt2, settings); + ValueType::Sign sign = ValueType::Sign::UNSIGNED; + if (n1 < n2) + sign = vt2->sign; + else if (n1 > n2) + sign = vt1->sign; + ValueFlow::Value v = castValue(value, sign, std::max(n1, n2) * 8); + v.wideintvalue = value.intvalue; + return v; +} + +/** set ValueFlow value and perform calculations if possible */ +static void setTokenValue(Token* tok, + ValueFlow::Value value, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) +{ + // Skip setting values that are too big since its ambiguous + if (!value.isImpossible() && value.isIntValue() && value.intvalue < 0 && astIsUnsigned(tok) && + ValueFlow::getSizeOf(*tok->valueType(), settings) >= sizeof(MathLib::bigint)) + return; + + if (!value.isImpossible() && value.isIntValue()) + value = truncateImplicitConversion(tok->astParent(), value, settings); + + if (settings.debugnormal) + setSourceLocation(value, loc, tok); + + if (!tok->addValue(value)) + return; + + if (value.path < 0) + return; + + Token *parent = tok->astParent(); + if (!parent) + return; + + if (Token::simpleMatch(parent, ",") && !parent->isInitComma() && astIsRHS(tok)) { + const Token* callParent = findParent(parent, [](const Token* p) { + return !Token::simpleMatch(p, ","); + }); + // Ensure that the comma isn't a function call + if (!callParent || (!Token::Match(callParent->previous(), "%name%|> (") && !Token::simpleMatch(callParent, "{") && + (!Token::Match(callParent, "( %name%") || settings.library.isNotLibraryFunction(callParent->next())) && + !(callParent->str() == "(" && (Token::simpleMatch(callParent->astOperand1(), "*") || Token::Match(callParent->astOperand1(), "%name%|("))))) { + setTokenValue(parent, std::move(value), settings); + return; + } + } + + if (Token::simpleMatch(parent, "=") && astIsRHS(tok)) { + setTokenValue(parent, value, settings); + if (!value.isUninitValue()) + return; + } + + if (value.isContainerSizeValue() && astIsContainer(tok)) { + // .empty, .size, +"abc", +'a' + if (Token::Match(parent, "+|==|!=") && parent->astOperand1() && parent->astOperand2()) { + for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) { + if (value1.isImpossible()) + continue; + for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) { + if (value2.isImpossible()) + continue; + if (value1.path != value2.path) + continue; + ValueFlow::Value result; + if (Token::Match(parent, "%comp%")) + result.valueType = ValueFlow::Value::ValueType::INT; + else + result.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + + if (value1.isContainerSizeValue() && value2.isContainerSizeValue()) + result.intvalue = calculate(parent->str(), value1.intvalue, value2.intvalue); + else if (value1.isContainerSizeValue() && value2.isTokValue() && value2.tokvalue->tokType() == Token::eString) + result.intvalue = calculate(parent->str(), value1.intvalue, MathLib::bigint(Token::getStrLength(value2.tokvalue))); + else if (value2.isContainerSizeValue() && value1.isTokValue() && value1.tokvalue->tokType() == Token::eString) + result.intvalue = calculate(parent->str(), MathLib::bigint(Token::getStrLength(value1.tokvalue)), value2.intvalue); + else + continue; + + combineValueProperties(value1, value2, result); + + if (Token::simpleMatch(parent, "==") && result.intvalue) + continue; + if (Token::simpleMatch(parent, "!=") && !result.intvalue) + continue; + + setTokenValue(parent, std::move(result), settings); + } + } + } + Token* next = nullptr; + const Library::Container::Yield yields = getContainerYield(parent, settings, &next); + if (yields == Library::Container::Yield::SIZE) { + ValueFlow::Value v(value); + v.valueType = ValueFlow::Value::ValueType::INT; + setTokenValue(next, std::move(v), settings); + } else if (yields == Library::Container::Yield::EMPTY) { + ValueFlow::Value v(value); + v.valueType = ValueFlow::Value::ValueType::INT; + v.bound = ValueFlow::Value::Bound::Point; + if (value.isImpossible()) { + if (value.intvalue == 0) + v.setKnown(); + else if ((value.bound == ValueFlow::Value::Bound::Upper && value.intvalue > 0) || + (value.bound == ValueFlow::Value::Bound::Lower && value.intvalue < 0)) { + v.intvalue = 0; + v.setKnown(); + } else + v.setPossible(); + } else { + v.intvalue = !v.intvalue; + } + setTokenValue(next, std::move(v), settings); + } + return; + } + + if (value.isLifetimeValue()) { + if (!ValueFlow::isLifetimeBorrowed(parent, settings)) + return; + if (value.lifetimeKind == ValueFlow::Value::LifetimeKind::Iterator && astIsIterator(parent)) { + setTokenValue(parent,std::move(value),settings); + } else if (astIsPointer(tok) && astIsPointer(parent) && !parent->isUnaryOp("*") && + (parent->isArithmeticalOp() || parent->isCast())) { + setTokenValue(parent,std::move(value),settings); + } + return; + } + + if (value.isUninitValue()) { + if (Token::Match(tok, ". %var%")) + setTokenValue(tok->next(), value, settings); + if (parent->isCast()) { + setTokenValue(parent, std::move(value), settings); + return; + } + ValueFlow::Value pvalue = value; + if (!value.subexpressions.empty() && Token::Match(parent, ". %var%")) { + if (contains(value.subexpressions, parent->next()->str())) + pvalue.subexpressions.clear(); + else + return; + } + if (parent->isUnaryOp("&")) { + pvalue.indirect++; + setTokenValue(parent, std::move(pvalue), settings); + } else if (Token::Match(parent, ". %var%") && parent->astOperand1() == tok && parent->astOperand2()) { + if (parent->originalName() == "->" && pvalue.indirect > 0) + pvalue.indirect--; + setTokenValue(parent->astOperand2(), std::move(pvalue), settings); + } else if (Token::Match(parent->astParent(), ". %var%") && parent->astParent()->astOperand1() == parent) { + if (parent->astParent()->originalName() == "->" && pvalue.indirect > 0) + pvalue.indirect--; + setTokenValue(parent->astParent()->astOperand2(), std::move(pvalue), settings); + } else if (parent->isUnaryOp("*") && pvalue.indirect > 0) { + pvalue.indirect--; + setTokenValue(parent, std::move(pvalue), settings); + } + return; + } + + // cast.. + if (const Token *castType = getCastTypeStartToken(parent, settings)) { + if (contains({ValueFlow::Value::ValueType::INT, ValueFlow::Value::ValueType::SYMBOLIC}, value.valueType) && + Token::simpleMatch(parent->astOperand1(), "dynamic_cast")) + return; + const ValueType &valueType = ValueType::parseDecl(castType, settings); + if (value.isImpossible() && value.isIntValue() && value.intvalue < 0 && astIsUnsigned(tok) && + valueType.sign == ValueType::SIGNED && tok->valueType() && + ValueFlow::getSizeOf(*tok->valueType(), settings) >= ValueFlow::getSizeOf(valueType, settings)) + return; + setTokenValueCast(parent, valueType, value, settings); + } + + else if (parent->str() == ":") { + setTokenValue(parent,std::move(value),settings); + } + + else if (parent->str() == "?" && tok->str() == ":" && tok == parent->astOperand2() && parent->astOperand1()) { + // is condition always true/false? + if (parent->astOperand1()->hasKnownValue()) { + const ValueFlow::Value &condvalue = parent->astOperand1()->values().front(); + const bool cond(condvalue.isTokValue() || (condvalue.isIntValue() && condvalue.intvalue != 0)); + if (cond && !tok->astOperand1()) { // true condition, no second operator + setTokenValue(parent, condvalue, settings); + } else { + const Token *op = cond ? tok->astOperand1() : tok->astOperand2(); + if (!op) // #7769 segmentation fault at setTokenValue() + return; + const std::list &values = op->values(); + if (std::find(values.cbegin(), values.cend(), value) != values.cend()) + setTokenValue(parent, std::move(value), settings); + } + } else if (!value.isImpossible()) { + // is condition only depending on 1 variable? + // cppcheck-suppress[variableScope] #8541 + nonneg int varId = 0; + bool ret = false; + visitAstNodes(parent->astOperand1(), + [&](const Token *t) { + if (t->varId()) { + if (varId > 0 || value.varId != 0) + ret = true; + varId = t->varId(); + } else if (t->str() == "(" && Token::Match(t->previous(), "%name%")) + ret = true; // function call + return ret ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; + }); + if (ret) + return; + + ValueFlow::Value v(std::move(value)); + v.conditional = true; + v.changeKnownToPossible(); + + setTokenValue(parent, std::move(v), settings); + } + } + + else if (parent->str() == "?" && value.isIntValue() && tok == parent->astOperand1() && value.isKnown() && + parent->astOperand2() && parent->astOperand2()->astOperand1() && parent->astOperand2()->astOperand2()) { + const std::list &values = (value.intvalue == 0 + ? parent->astOperand2()->astOperand2()->values() + : parent->astOperand2()->astOperand1()->values()); + + for (const ValueFlow::Value &v : values) + setTokenValue(parent, v, settings); + } + + // Calculations.. + else if ((parent->isArithmeticalOp() || parent->isComparisonOp() || (parent->tokType() == Token::eBitOp) || (parent->tokType() == Token::eLogicalOp)) && + parent->astOperand1() && + parent->astOperand2()) { + + const bool noninvertible = isNonInvertibleOperation(parent); + + // Skip operators with impossible values that are not invertible + if (noninvertible && value.isImpossible()) + return; + + // known result when a operand is 0. + if (Token::Match(parent, "[&*]") && astIsIntegral(parent, true) && value.isKnown() && value.isIntValue() && + value.intvalue == 0) { + setTokenValue(parent, std::move(value), settings); + return; + } + + // known result when a operand is true. + if (Token::simpleMatch(parent, "&&") && value.isKnown() && value.isIntValue() && value.intvalue==0) { + setTokenValue(parent, std::move(value), settings); + return; + } + + // known result when a operand is false. + if (Token::simpleMatch(parent, "||") && value.isKnown() && value.isIntValue() && value.intvalue!=0) { + setTokenValue(parent, std::move(value), settings); + return; + } + + for (const ValueFlow::Value &value1 : parent->astOperand1()->values()) { + if (!isComputableValue(parent, value1)) + continue; + for (const ValueFlow::Value &value2 : parent->astOperand2()->values()) { + if (value1.path != value2.path) + continue; + if (!isComputableValue(parent, value2)) + continue; + if (value1.isIteratorValue() && value2.isIteratorValue()) + continue; + if (!isCompatibleValues(value1, value2)) + continue; + ValueFlow::Value result(0); + combineValueProperties(value1, value2, result); + if (astIsFloat(parent, false)) { + if (!result.isIntValue() && !result.isFloatValue()) + continue; + result.valueType = ValueFlow::Value::ValueType::FLOAT; + } + const double floatValue1 = value1.isFloatValue() ? value1.floatValue : value1.intvalue; + const double floatValue2 = value2.isFloatValue() ? value2.floatValue : value2.intvalue; + const auto intValue1 = [&]() -> MathLib::bigint { + return value1.isFloatValue() ? static_cast(value1.floatValue) : value1.intvalue; + }; + const auto intValue2 = [&]() -> MathLib::bigint { + return value2.isFloatValue() ? static_cast(value2.floatValue) : value2.intvalue; + }; + if ((value1.isFloatValue() || value2.isFloatValue()) && Token::Match(parent, "&|^|%|<<|>>|==|!=|%or%")) + continue; + if (Token::Match(parent, "==|!=")) { + if ((value1.isIntValue() && value2.isTokValue()) || (value1.isTokValue() && value2.isIntValue())) { + if (parent->str() == "==") + result.intvalue = 0; + else if (parent->str() == "!=") + result.intvalue = 1; + } else if (value1.isIntValue() && value2.isIntValue()) { + bool error = false; + result.intvalue = calculate(parent->str(), intValue1(), intValue2(), &error); + if (error) + continue; + } else if (value1.isTokValue() && value2.isTokValue() && + (astIsContainer(parent->astOperand1()) || astIsContainer(parent->astOperand2()))) { + const Token* tok1 = value1.tokvalue; + const Token* tok2 = value2.tokvalue; + bool equal = false; + if (Token::Match(tok1, "%str%") && Token::Match(tok2, "%str%")) { + equal = tok1->str() == tok2->str(); + } else if (Token::simpleMatch(tok1, "{") && Token::simpleMatch(tok2, "{")) { + std::vector args1 = getArguments(tok1); + std::vector args2 = getArguments(tok2); + if (args1.size() == args2.size()) { + if (!std::all_of(args1.begin(), args1.end(), std::mem_fn(&Token::hasKnownIntValue))) + continue; + if (!std::all_of(args2.begin(), args2.end(), std::mem_fn(&Token::hasKnownIntValue))) + continue; + equal = std::equal(args1.begin(), + args1.end(), + args2.begin(), + [&](const Token* atok, const Token* btok) { + return atok->values().front().intvalue == + btok->values().front().intvalue; + }); + } else { + equal = false; + } + } else { + continue; + } + result.intvalue = parent->str() == "==" ? equal : !equal; + } else { + continue; + } + setTokenValue(parent, std::move(result), settings); + } else if (Token::Match(parent, "%op%")) { + if (Token::Match(parent, "%comp%")) { + if (!result.isFloatValue() && !value1.isIntValue() && !value2.isIntValue()) + continue; + } else { + if (value1.isTokValue() || value2.isTokValue()) + break; + } + bool error = false; + if (result.isFloatValue()) { + result.floatValue = calculate(parent->str(), floatValue1, floatValue2, &error); + } else { + result.intvalue = calculate(parent->str(), intValue1(), intValue2(), &error); + } + if (error) + continue; + // If the bound comes from the second value then invert the bound when subtracting + if (Token::simpleMatch(parent, "-") && value2.bound == result.bound && + value2.bound != ValueFlow::Value::Bound::Point) + result.invertBound(); + setTokenValue(parent, std::move(result), settings); + } + } + } + } + + // ! + else if (parent->str() == "!") { + for (const ValueFlow::Value &val : tok->values()) { + if (!val.isIntValue()) + continue; + if (val.isImpossible() && val.intvalue != 0) + continue; + ValueFlow::Value v(val); + if (val.isImpossible()) + v.setKnown(); + else + v.intvalue = !v.intvalue; + setTokenValue(parent, std::move(v), settings); + } + } + + // ~ + else if (parent->str() == "~") { + for (const ValueFlow::Value &val : tok->values()) { + if (!val.isIntValue()) + continue; + ValueFlow::Value v(val); + v.intvalue = ~v.intvalue; + int bits = 0; + if (tok->valueType() && + tok->valueType()->sign == ValueType::Sign::UNSIGNED && + tok->valueType()->pointer == 0) { + if (tok->valueType()->type == ValueType::Type::INT) + bits = settings.platform.int_bit; + else if (tok->valueType()->type == ValueType::Type::LONG) + bits = settings.platform.long_bit; + } + if (bits > 0 && bits < MathLib::bigint_bits) + v.intvalue &= (((MathLib::biguint)1)<isUnaryOp("-")) { + for (const ValueFlow::Value &val : tok->values()) { + if (!val.isIntValue() && !val.isFloatValue()) + continue; + ValueFlow::Value v(val); + if (v.isIntValue()) { + if (v.intvalue == LLONG_MIN) + // Value can't be inverted + continue; + v.intvalue = -v.intvalue; + } else + v.floatValue = -v.floatValue; + v.invertBound(); + setTokenValue(parent, std::move(v), settings); + } + } + + // increment + else if (parent->str() == "++") { + for (const ValueFlow::Value &val : tok->values()) { + if (!val.isIntValue() && !val.isFloatValue() && !val.isSymbolicValue()) + continue; + ValueFlow::Value v(val); + if (parent == tok->previous()) { + if (v.isIntValue() || v.isSymbolicValue()) + v.intvalue = v.intvalue + 1; + else + v.floatValue = v.floatValue + 1.0; + } + setTokenValue(parent, std::move(v), settings); + } + } + + // decrement + else if (parent->str() == "--") { + for (const ValueFlow::Value &val : tok->values()) { + if (!val.isIntValue() && !val.isFloatValue() && !val.isSymbolicValue()) + continue; + ValueFlow::Value v(val); + if (parent == tok->previous()) { + if (v.isIntValue() || v.isSymbolicValue()) + v.intvalue = v.intvalue - 1; + else + v.floatValue = v.floatValue - 1.0; + } + setTokenValue(parent, std::move(v), settings); + } + } + + // C++ init + else if (parent->str() == "{" && Token::simpleMatch(parent->previous(), "= {") && Token::simpleMatch(parent->link(), "} ;")) { + const Token* lhs = parent->previous()->astOperand1(); + if (lhs && lhs->valueType()) { + if (lhs->valueType()->isIntegral() || lhs->valueType()->isFloat() || (lhs->valueType()->pointer > 0 && value.isIntValue())) { + setTokenValue(parent, std::move(value), settings); + } + } + } + + else if (Token::Match(parent, ":: %name%") && parent->astOperand2() == tok) { + setTokenValue(parent, std::move(value), settings); + } + // Calling std::size or std::empty on an array + else if (value.isTokValue() && Token::simpleMatch(value.tokvalue, "{") && tok->variable() && + tok->variable()->isArray() && Token::Match(parent->previous(), "%name% (") && astIsRHS(tok)) { + std::vector args = getArguments(value.tokvalue); + if (const Library::Function* f = settings.library.getFunction(parent->previous())) { + if (f->containerYield == Library::Container::Yield::SIZE) { + ValueFlow::Value v(std::move(value)); + v.valueType = ValueFlow::Value::ValueType::INT; + v.intvalue = args.size(); + setTokenValue(parent, std::move(v), settings); + } else if (f->containerYield == Library::Container::Yield::EMPTY) { + ValueFlow::Value v(std::move(value)); + v.intvalue = args.empty(); + v.valueType = ValueFlow::Value::ValueType::INT; + setTokenValue(parent, std::move(v), settings); + } + } + } +} + +static void setTokenValueCast(Token *parent, const ValueType &valueType, const ValueFlow::Value &value, const Settings &settings) +{ + if (valueType.pointer || value.isImpossible()) + setTokenValue(parent,value,settings); + else if (valueType.type == ValueType::Type::CHAR) + setTokenValue(parent, castValue(value, valueType.sign, settings.platform.char_bit), settings); + else if (valueType.type == ValueType::Type::SHORT) + setTokenValue(parent, castValue(value, valueType.sign, settings.platform.short_bit), settings); + else if (valueType.type == ValueType::Type::INT) + setTokenValue(parent, castValue(value, valueType.sign, settings.platform.int_bit), settings); + else if (valueType.type == ValueType::Type::LONG) + setTokenValue(parent, castValue(value, valueType.sign, settings.platform.long_bit), settings); + else if (valueType.type == ValueType::Type::LONGLONG) + setTokenValue(parent, castValue(value, valueType.sign, settings.platform.long_long_bit), settings); + else if (valueType.isFloat() && isNumeric(value)) { + ValueFlow::Value floatValue = value; + floatValue.valueType = ValueFlow::Value::ValueType::FLOAT; + if (value.isIntValue()) + floatValue.floatValue = value.intvalue; + setTokenValue(parent, std::move(floatValue), settings); + } else if (value.isIntValue()) { + const long long charMax = settings.platform.signedCharMax(); + const long long charMin = settings.platform.signedCharMin(); + if (charMin <= value.intvalue && value.intvalue <= charMax) { + // unknown type, but value is small so there should be no truncation etc + setTokenValue(parent,value,settings); + } + } +} + +template +static size_t accumulateStructMembers(const Scope* scope, F f) +{ + size_t total = 0; + std::set anonScopes; + for (const Variable& var : scope->varlist) { + if (var.isStatic()) + continue; + if (const ValueType* vt = var.valueType()) { + if (vt->type == ValueType::Type::RECORD && vt->typeScope == scope) + return 0; + const MathLib::bigint dim = std::accumulate(var.dimensions().cbegin(), var.dimensions().cend(), 1LL, [](MathLib::bigint i1, const Dimension& dim) { + return i1 * dim.num; + }); + if (var.nameToken()->scope() != scope && var.nameToken()->scope()->definedType) { // anonymous union + const auto ret = anonScopes.insert(var.nameToken()->scope()); + if (ret.second) + total = f(total, *vt, dim); + } + else + total = f(total, *vt, dim); + } + if (total == 0) + return 0; + } + return total; +} + +static size_t bitCeil(size_t x) +{ + if (x <= 1) + return 1; + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x |= x >> 32; + return x + 1; +} + +static size_t getAlignOf(const ValueType& vt, const Settings& settings, int maxRecursion = 0) +{ + if (maxRecursion == 100) + return 0; + if (vt.pointer || vt.reference != Reference::None || vt.isPrimitive()) { + auto align = ValueFlow::getSizeOf(vt, settings); + return align == 0 ? 0 : bitCeil(align); + } + if (vt.type == ValueType::Type::RECORD && vt.typeScope) { + auto accHelper = [&](size_t max, const ValueType& vt2, size_t /*dim*/) { + size_t a = getAlignOf(vt2, settings, ++maxRecursion); + return std::max(max, a); + }; + size_t total = 0; + if (const Type* dt = vt.typeScope->definedType) { + total = std::accumulate(dt->derivedFrom.begin(), dt->derivedFrom.end(), total, [&](size_t v, const Type::BaseInfo& bi) { + if (bi.type && bi.type->classScope) + v += accumulateStructMembers(bi.type->classScope, accHelper); + return v; + }); + } + return total + accumulateStructMembers(vt.typeScope, accHelper); + } + if (vt.type == ValueType::Type::CONTAINER) + return settings.platform.sizeof_pointer; // Just guess + return 0; +} + +static nonneg int getSizeOfType(const Token *typeTok, const Settings &settings) +{ + const ValueType &valueType = ValueType::parseDecl(typeTok, settings); + + return ValueFlow::getSizeOf(valueType, settings); +} + +size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings &settings, int maxRecursion) +{ + if (maxRecursion == 100) + return 0; + if (vt.pointer || vt.reference != Reference::None) + return settings.platform.sizeof_pointer; + if (vt.type == ValueType::Type::BOOL || vt.type == ValueType::Type::CHAR) + return 1; + if (vt.type == ValueType::Type::SHORT) + return settings.platform.sizeof_short; + if (vt.type == ValueType::Type::WCHAR_T) + return settings.platform.sizeof_wchar_t; + if (vt.type == ValueType::Type::INT) + return settings.platform.sizeof_int; + if (vt.type == ValueType::Type::LONG) + return settings.platform.sizeof_long; + if (vt.type == ValueType::Type::LONGLONG) + return settings.platform.sizeof_long_long; + if (vt.type == ValueType::Type::FLOAT) + return settings.platform.sizeof_float; + if (vt.type == ValueType::Type::DOUBLE) + return settings.platform.sizeof_double; + if (vt.type == ValueType::Type::LONGDOUBLE) + return settings.platform.sizeof_long_double; + if (vt.type == ValueType::Type::CONTAINER) + return 3 * settings.platform.sizeof_pointer; // Just guess + if (vt.type == ValueType::Type::RECORD && vt.typeScope) { + auto accHelper = [&](size_t total, const ValueType& vt2, size_t dim) -> size_t { + size_t n = ValueFlow::getSizeOf(vt2, settings, ++maxRecursion); + size_t a = getAlignOf(vt2, settings); + if (n == 0 || a == 0) + return 0; + n *= dim; + size_t padding = (a - (total % a)) % a; + return vt.typeScope->type == Scope::eUnion ? std::max(total, n) : total + padding + n; + }; + size_t total = accumulateStructMembers(vt.typeScope, accHelper); + if (const Type* dt = vt.typeScope->definedType) { + total = std::accumulate(dt->derivedFrom.begin(), dt->derivedFrom.end(), total, [&](size_t v, const Type::BaseInfo& bi) { + if (bi.type && bi.type->classScope) + v += accumulateStructMembers(bi.type->classScope, accHelper); + return v; + }); + } + if (total == 0) + return 0; + size_t align = getAlignOf(vt, settings); + if (align == 0) + return 0; + total += (align - (total % align)) % align; + return total; + } + return 0; +} + + +static bool getMinMaxValues(const ValueType* vt, const Platform& platform, MathLib::bigint& minValue, MathLib::bigint& maxValue); + +// Handle various constants.. +static Token * valueFlowSetConstantValue(Token *tok, const Settings &settings) +{ + if ((tok->isNumber() && MathLib::isInt(tok->str())) || (tok->tokType() == Token::eChar)) { + try { + MathLib::bigint signedValue = MathLib::toBigNumber(tok->str()); + const ValueType* vt = tok->valueType(); + if (vt && vt->sign == ValueType::UNSIGNED && signedValue < 0 && ValueFlow::getSizeOf(*vt, settings) < sizeof(MathLib::bigint)) { + MathLib::bigint minValue{}, maxValue{}; + if (getMinMaxValues(tok->valueType(), settings.platform, minValue, maxValue)) + signedValue += maxValue + 1; + } + ValueFlow::Value value(signedValue); + if (!tok->isTemplateArg()) + value.setKnown(); + setTokenValue(tok, std::move(value), settings); + } catch (const std::exception & /*e*/) { + // Bad character literal + } + } else if (tok->isNumber() && MathLib::isFloat(tok->str())) { + ValueFlow::Value value; + value.valueType = ValueFlow::Value::ValueType::FLOAT; + value.floatValue = MathLib::toDoubleNumber(tok->str()); + if (!tok->isTemplateArg()) + value.setKnown(); + setTokenValue(tok, std::move(value), settings); + } else if (tok->enumerator() && tok->enumerator()->value_known) { + ValueFlow::Value value(tok->enumerator()->value); + if (!tok->isTemplateArg()) + value.setKnown(); + setTokenValue(tok, std::move(value), settings); + } else if (tok->str() == "NULL" || (tok->isCpp() && tok->str() == "nullptr")) { + ValueFlow::Value value(0); + if (!tok->isTemplateArg()) + value.setKnown(); + setTokenValue(tok, std::move(value), settings); + } else if (Token::simpleMatch(tok, "sizeof (")) { + if (tok->next()->astOperand2() && !tok->next()->astOperand2()->isLiteral() && tok->next()->astOperand2()->valueType() && + (tok->next()->astOperand2()->valueType()->pointer == 0 || // <- TODO this is a bailout, abort when there are array->pointer conversions + (tok->next()->astOperand2()->variable() && !tok->next()->astOperand2()->variable()->isArray())) && + !tok->next()->astOperand2()->valueType()->isEnum()) { // <- TODO this is a bailout, handle enum with non-int types + const size_t sz = ValueFlow::getSizeOf(*tok->next()->astOperand2()->valueType(), settings); + if (sz) { + ValueFlow::Value value(sz); + value.setKnown(); + setTokenValue(tok->next(), std::move(value), settings); + return tok->linkAt(1); + } + } + + const Token *tok2 = tok->tokAt(2); + // skip over tokens to find variable or type + while (tok2 && !tok2->isStandardType() && Token::Match(tok2, "%name% ::|.|[")) { + if (tok2->next()->str() == "[") + tok2 = tok2->linkAt(1)->next(); + else + tok2 = tok2->tokAt(2); + } + if (Token::simpleMatch(tok, "sizeof ( *")) { + const ValueType *vt = tok->tokAt(2)->valueType(); + const size_t sz = vt ? ValueFlow::getSizeOf(*vt, settings) : 0; + if (sz > 0) { + ValueFlow::Value value(sz); + if (!tok2->isTemplateArg() && settings.platform.type != Platform::Type::Unspecified) + value.setKnown(); + setTokenValue(tok->next(), std::move(value), settings); + } + } else if (tok2->enumerator() && tok2->enumerator()->scope) { + long long size = settings.platform.sizeof_int; + const Token * type = tok2->enumerator()->scope->enumType; + if (type) { + size = getSizeOfType(type, settings); + if (size == 0) + tok->linkAt(1); + } + ValueFlow::Value value(size); + if (!tok2->isTemplateArg() && settings.platform.type != Platform::Type::Unspecified) + value.setKnown(); + setTokenValue(tok, value, settings); + setTokenValue(tok->next(), std::move(value), settings); + } else if (tok2->type() && tok2->type()->isEnumType()) { + long long size = settings.platform.sizeof_int; + if (tok2->type()->classScope) { + const Token * type = tok2->type()->classScope->enumType; + if (type) { + size = getSizeOfType(type, settings); + } + } + ValueFlow::Value value(size); + if (!tok2->isTemplateArg() && settings.platform.type != Platform::Type::Unspecified) + value.setKnown(); + setTokenValue(tok, value, settings); + setTokenValue(tok->next(), std::move(value), settings); + } else if (Token::Match(tok, "sizeof ( %var% ) /") && tok->next()->astParent() == tok->tokAt(4) && + tok->tokAt(4)->astOperand2() && Token::simpleMatch(tok->tokAt(4)->astOperand2()->previous(), "sizeof (")) { + // Get number of elements in array + const Token *sz1 = tok->tokAt(2); + const Token *sz2 = tok->tokAt(4)->astOperand2(); // left parenthesis of sizeof on rhs + const nonneg int varid1 = sz1->varId(); + if (varid1 && + sz1->variable() && + sz1->variable()->isArray() && + !sz1->variable()->dimensions().empty() && + sz1->variable()->dimensionKnown(0) && + Token::Match(sz2->astOperand2(), "*|[") && Token::Match(sz2->astOperand2()->astOperand1(), "%varid%", varid1)) { + ValueFlow::Value value(sz1->variable()->dimension(0)); + if (!tok2->isTemplateArg() && settings.platform.type != Platform::Type::Unspecified) + value.setKnown(); + setTokenValue(tok->tokAt(4), std::move(value), settings); + } + } else if (Token::Match(tok2, "%var% )")) { + const Variable *var = tok2->variable(); + // only look for single token types (no pointers or references yet) + if (var && var->typeStartToken() == var->typeEndToken()) { + // find the size of the type + size_t size = 0; + if (var->isEnumType()) { + size = settings.platform.sizeof_int; + if (var->type()->classScope && var->type()->classScope->enumType) + size = getSizeOfType(var->type()->classScope->enumType, settings); + } else if (var->valueType()) { + size = ValueFlow::getSizeOf(*var->valueType(), settings); + } else if (!var->type()) { + size = getSizeOfType(var->typeStartToken(), settings); + } + // find the number of elements + size_t count = 1; + for (size_t i = 0; i < var->dimensions().size(); ++i) { + if (var->dimensionKnown(i)) + count *= var->dimension(i); + else + count = 0; + } + if (size && count > 0) { + ValueFlow::Value value(count * size); + if (settings.platform.type != Platform::Type::Unspecified) + value.setKnown(); + setTokenValue(tok, value, settings); + setTokenValue(tok->next(), std::move(value), settings); + } + } + } else if (tok2->tokType() == Token::eString) { + const size_t sz = Token::getStrSize(tok2, settings); + if (sz > 0) { + ValueFlow::Value value(sz); + value.setKnown(); + setTokenValue(tok->next(), std::move(value), settings); + } + } else if (tok2->tokType() == Token::eChar) { + nonneg int sz = 0; + if (tok2->isCpp() && settings.standards.cpp >= Standards::CPP20 && tok2->isUtf8()) + sz = 1; + else if (tok2->isUtf16()) + sz = 2; + else if (tok2->isUtf32()) + sz = 4; + else if (tok2->isLong()) + sz = settings.platform.sizeof_wchar_t; + else if ((!tok2->isCpp() && tok2->isCChar()) || (tok2->isCMultiChar())) + sz = settings.platform.sizeof_int; + else + sz = 1; + + if (sz > 0) { + ValueFlow::Value value(sz); + value.setKnown(); + setTokenValue(tok->next(), std::move(value), settings); + } + } else if (!tok2->type()) { + const ValueType& vt = ValueType::parseDecl(tok2, settings); + size_t sz = ValueFlow::getSizeOf(vt, settings); + const Token* brac = tok2->astParent(); + while (Token::simpleMatch(brac, "[")) { + const Token* num = brac->astOperand2(); + if (num && ((num->isNumber() && MathLib::isInt(num->str())) || num->tokType() == Token::eChar)) { + try { + const MathLib::biguint dim = MathLib::toBigUNumber(num->str()); + sz *= dim; + brac = brac->astParent(); + continue; + } + catch (const std::exception& /*e*/) { + // Bad integer literal + } + } + sz = 0; + break; + } + if (sz > 0) { + ValueFlow::Value value(sz); + if (!tok2->isTemplateArg() && settings.platform.type != Platform::Type::Unspecified) + value.setKnown(); + setTokenValue(tok->next(), std::move(value), settings); + } + } + // skip over enum + tok = tok->linkAt(1); + } else if (Token::Match(tok, "%name% [{(] [)}]") && (tok->isStandardType() || + (tok->variable() && tok->variable()->nameToken() == tok && + (tok->variable()->isPointer() || (tok->variable()->valueType() && tok->variable()->valueType()->isIntegral()))))) { + ValueFlow::Value value(0); + if (!tok->isTemplateArg()) + value.setKnown(); + setTokenValue(tok->next(), std::move(value), settings); + } else if (Token::simpleMatch(tok, "= { } ;")) { + const Token* lhs = tok->astOperand1(); + if (lhs && lhs->valueType() && (lhs->valueType()->isIntegral() || lhs->valueType()->pointer > 0)) { + ValueFlow::Value value(0); + value.setKnown(); + setTokenValue(tok->next(), std::move(value), settings); + } + } + return tok->next(); +} + +static void valueFlowNumber(TokenList &tokenlist, const Settings& settings) +{ + for (Token *tok = tokenlist.front(); tok;) { + tok = valueFlowSetConstantValue(tok, settings); + } + + if (tokenlist.isCPP()) { + for (Token *tok = tokenlist.front(); tok; tok = tok->next()) { + if (tok->isName() && !tok->varId() && Token::Match(tok, "false|true")) { + ValueFlow::Value value(tok->str() == "true"); + if (!tok->isTemplateArg()) + value.setKnown(); + setTokenValue(tok, std::move(value), settings); + } else if (Token::Match(tok, "[(,] NULL [,)]")) { + // NULL function parameters are not simplified in the + // normal tokenlist + ValueFlow::Value value(0); + if (!tok->isTemplateArg()) + value.setKnown(); + setTokenValue(tok->next(), std::move(value), settings); + } + } + } +} + +static void valueFlowString(TokenList &tokenlist, const Settings& settings) +{ + for (Token *tok = tokenlist.front(); tok; tok = tok->next()) { + if (tok->tokType() == Token::eString) { + ValueFlow::Value strvalue; + strvalue.valueType = ValueFlow::Value::ValueType::TOK; + strvalue.tokvalue = tok; + strvalue.setKnown(); + setTokenValue(tok, std::move(strvalue), settings); + } + } +} + +static void valueFlowArray(TokenList &tokenlist, const Settings &settings) +{ + std::map constantArrays; + + for (Token *tok = tokenlist.front(); tok; tok = tok->next()) { + if (tok->varId() > 0) { + // array + const std::map::const_iterator it = constantArrays.find(tok->varId()); + if (it != constantArrays.end()) { + ValueFlow::Value value; + value.valueType = ValueFlow::Value::ValueType::TOK; + value.tokvalue = it->second; + value.setKnown(); + setTokenValue(tok, std::move(value), settings); + } + + // const array decl + else if (tok->variable() && tok->variable()->isArray() && tok->variable()->isConst() && + tok->variable()->nameToken() == tok && Token::Match(tok, "%var% [ %num%| ] = {")) { + Token* rhstok = tok->next()->link()->tokAt(2); + constantArrays[tok->varId()] = rhstok; + tok = rhstok->link(); + } + + // pointer = array + else if (tok->variable() && tok->variable()->isArray() && Token::simpleMatch(tok->astParent(), "=") && + astIsRHS(tok) && tok->astParent()->astOperand1() && + tok->astParent()->astOperand1()->variable() && + tok->astParent()->astOperand1()->variable()->isPointer()) { + ValueFlow::Value value; + value.valueType = ValueFlow::Value::ValueType::TOK; + value.tokvalue = tok; + value.setKnown(); + setTokenValue(tok, std::move(value), settings); + } + continue; + } + + if (Token::Match(tok, "const %type% %var% [ %num%| ] = {")) { + Token *vartok = tok->tokAt(2); + Token *rhstok = vartok->next()->link()->tokAt(2); + constantArrays[vartok->varId()] = rhstok; + tok = rhstok->link(); + continue; + } + + if (Token::Match(tok, "const char %var% [ %num%| ] = %str% ;")) { + Token *vartok = tok->tokAt(2); + Token *strtok = vartok->next()->link()->tokAt(2); + constantArrays[vartok->varId()] = strtok; + tok = strtok->next(); + continue; + } + } +} + +static bool isNonZero(const Token *tok) +{ + return tok && (!tok->hasKnownIntValue() || tok->values().front().intvalue != 0); +} + +static const Token *getOtherOperand(const Token *tok) +{ + if (!tok) + return nullptr; + if (!tok->astParent()) + return nullptr; + if (tok->astParent()->astOperand1() != tok) + return tok->astParent()->astOperand1(); + if (tok->astParent()->astOperand2() != tok) + return tok->astParent()->astOperand2(); + return nullptr; +} + +static void valueFlowArrayBool(TokenList &tokenlist, const Settings &settings) +{ + for (Token *tok = tokenlist.front(); tok; tok = tok->next()) { + if (tok->hasKnownIntValue()) + continue; + const Variable *var = nullptr; + bool known = false; + const std::list::const_iterator val = + std::find_if(tok->values().cbegin(), tok->values().cend(), std::mem_fn(&ValueFlow::Value::isTokValue)); + if (val == tok->values().end()) { + var = tok->variable(); + known = true; + } else { + var = val->tokvalue->variable(); + known = val->isKnown(); + } + if (!var) + continue; + if (!var->isArray() || var->isArgument() || var->isStlType()) + continue; + if (isNonZero(getOtherOperand(tok)) && Token::Match(tok->astParent(), "%comp%")) + continue; + // TODO: Check for function argument + if ((astIsBool(tok->astParent()) && !Token::Match(tok->astParent(), "(|%name%")) || + (tok->astParent() && Token::Match(tok->astParent()->previous(), "if|while|for ("))) { + ValueFlow::Value value{1}; + if (known) + value.setKnown(); + setTokenValue(tok, std::move(value), settings); + } + } +} + +static void valueFlowArrayElement(TokenList& tokenlist, const Settings& settings) +{ + for (Token* tok = tokenlist.front(); tok; tok = tok->next()) { + if (tok->hasKnownIntValue()) + continue; + const Token* indexTok = nullptr; + const Token* arrayTok = nullptr; + if (Token::simpleMatch(tok, "[") && tok->isBinaryOp()) { + indexTok = tok->astOperand2(); + arrayTok = tok->astOperand1(); + } else if (Token::Match(tok->tokAt(-2), ". %name% (") && astIsContainer(tok->tokAt(-2)->astOperand1())) { + arrayTok = tok->tokAt(-2)->astOperand1(); + const Library::Container* container = getLibraryContainer(arrayTok); + if (!container || container->stdAssociativeLike) + continue; + const Library::Container::Yield yield = container->getYield(tok->strAt(-1)); + if (yield != Library::Container::Yield::AT_INDEX) + continue; + indexTok = tok->astOperand2(); + } + + if (!indexTok || !arrayTok) + continue; + + for (const ValueFlow::Value& arrayValue : arrayTok->values()) { + if (!arrayValue.isTokValue()) + continue; + if (arrayValue.isImpossible()) + continue; + for (const ValueFlow::Value& indexValue : indexTok->values()) { + if (!indexValue.isIntValue()) + continue; + if (indexValue.isImpossible()) + continue; + if (!arrayValue.isKnown() && !indexValue.isKnown() && arrayValue.varId != 0 && indexValue.varId != 0 && + !(arrayValue.varId == indexValue.varId && arrayValue.varvalue == indexValue.varvalue)) + continue; + + ValueFlow::Value result(0); + result.condition = arrayValue.condition ? arrayValue.condition : indexValue.condition; + result.setInconclusive(arrayValue.isInconclusive() || indexValue.isInconclusive()); + result.varId = (arrayValue.varId != 0) ? arrayValue.varId : indexValue.varId; + result.varvalue = (result.varId == arrayValue.varId) ? arrayValue.intvalue : indexValue.intvalue; + if (arrayValue.valueKind == indexValue.valueKind) + result.valueKind = arrayValue.valueKind; + + result.errorPath.insert(result.errorPath.end(), arrayValue.errorPath.cbegin(), arrayValue.errorPath.cend()); + result.errorPath.insert(result.errorPath.end(), indexValue.errorPath.cbegin(), indexValue.errorPath.cend()); + + const MathLib::bigint index = indexValue.intvalue; + + if (arrayValue.tokvalue->tokType() == Token::eString) { + const std::string s = arrayValue.tokvalue->strValue(); + if (index == s.size()) { + result.intvalue = 0; + setTokenValue(tok, std::move(result), settings); + } else if (index >= 0 && index < s.size()) { + result.intvalue = s[index]; + setTokenValue(tok, std::move(result), settings); + } + } else if (Token::simpleMatch(arrayValue.tokvalue, "{")) { + std::vector args = getArguments(arrayValue.tokvalue); + if (index < 0 || index >= args.size()) + continue; + const Token* arg = args[index]; + if (!arg->hasKnownIntValue()) + continue; + const ValueFlow::Value& v = arg->values().front(); + result.intvalue = v.intvalue; + result.errorPath.insert(result.errorPath.end(), v.errorPath.cbegin(), v.errorPath.cend()); + setTokenValue(tok, std::move(result), settings); + } + } + } + } +} + +static void valueFlowPointerAlias(TokenList &tokenlist, const Settings& settings) +{ + for (Token *tok = tokenlist.front(); tok; tok = tok->next()) { + // not address of + if (!tok->isUnaryOp("&")) + continue; + + // parent should be a '=' + if (!Token::simpleMatch(tok->astParent(), "=")) + continue; + + // child should be some buffer or variable + const Token *vartok = tok->astOperand1(); + while (vartok) { + if (vartok->str() == "[") + vartok = vartok->astOperand1(); + else if (vartok->str() == "." || vartok->str() == "::") + vartok = vartok->astOperand2(); + else + break; + } + if (!(vartok && vartok->variable() && !vartok->variable()->isPointer())) + continue; + + ValueFlow::Value value; + value.valueType = ValueFlow::Value::ValueType::TOK; + value.tokvalue = tok; + setTokenValue(tok, std::move(value), settings); + } +} + +static void valueFlowBitAnd(TokenList &tokenlist, const Settings& settings) +{ + for (Token *tok = tokenlist.front(); tok; tok = tok->next()) { + if (tok->str() != "&") + continue; + + if (tok->hasKnownValue()) + continue; + + if (!tok->astOperand1() || !tok->astOperand2()) + continue; + + MathLib::bigint number; + if (MathLib::isInt(tok->astOperand1()->str())) + number = MathLib::toBigNumber(tok->astOperand1()->str()); + else if (MathLib::isInt(tok->astOperand2()->str())) + number = MathLib::toBigNumber(tok->astOperand2()->str()); + else + continue; + + int bit = 0; + while (bit <= (MathLib::bigint_bits - 2) && ((((MathLib::bigint)1) << bit) < number)) + ++bit; + + if ((((MathLib::bigint)1) << bit) == number) { + setTokenValue(tok, ValueFlow::Value(0), settings); + setTokenValue(tok, ValueFlow::Value(number), settings); + } + } +} + +static void valueFlowSameExpressions(TokenList &tokenlist, const Settings& settings) +{ + for (Token *tok = tokenlist.front(); tok; tok = tok->next()) { + if (tok->hasKnownIntValue()) + continue; + + if (!tok->astOperand1() || !tok->astOperand2()) + continue; + + if (tok->astOperand1()->isLiteral() || tok->astOperand2()->isLiteral()) + continue; + + if (!astIsIntegral(tok->astOperand1(), false) && !astIsIntegral(tok->astOperand2(), false)) + continue; + + ValueFlow::Value val; + + if (Token::Match(tok, "==|>=|<=|/")) { + val = ValueFlow::Value(1); + val.setKnown(); + } + + if (Token::Match(tok, "!=|>|<|%|-")) { + val = ValueFlow::Value(0); + val.setKnown(); + } + + if (!val.isKnown()) + continue; + + if (isSameExpression(false, tok->astOperand1(), tok->astOperand2(), settings.library, true, true, &val.errorPath)) { + setTokenValue(tok, std::move(val), settings); + } + } +} + +static bool getExpressionRange(const Token *expr, MathLib::bigint *minvalue, MathLib::bigint *maxvalue) +{ + if (expr->hasKnownIntValue()) { + if (minvalue) + *minvalue = expr->values().front().intvalue; + if (maxvalue) + *maxvalue = expr->values().front().intvalue; + return true; + } + + if (expr->str() == "&" && expr->astOperand1() && expr->astOperand2()) { + MathLib::bigint vals[4]; + const bool lhsHasKnownRange = getExpressionRange(expr->astOperand1(), &vals[0], &vals[1]); + const bool rhsHasKnownRange = getExpressionRange(expr->astOperand2(), &vals[2], &vals[3]); + if (!lhsHasKnownRange && !rhsHasKnownRange) + return false; + if (!lhsHasKnownRange || !rhsHasKnownRange) { + if (minvalue) + *minvalue = lhsHasKnownRange ? vals[0] : vals[2]; + if (maxvalue) + *maxvalue = lhsHasKnownRange ? vals[1] : vals[3]; + } else { + if (minvalue) + *minvalue = vals[0] & vals[2]; + if (maxvalue) + *maxvalue = vals[1] & vals[3]; + } + return true; + } + + if (expr->str() == "%" && expr->astOperand1() && expr->astOperand2()) { + MathLib::bigint vals[4]; + if (!getExpressionRange(expr->astOperand2(), &vals[2], &vals[3])) + return false; + if (vals[2] <= 0) + return false; + const bool lhsHasKnownRange = getExpressionRange(expr->astOperand1(), &vals[0], &vals[1]); + if (lhsHasKnownRange && vals[0] < 0) + return false; + // If lhs has unknown value, it must be unsigned + if (!lhsHasKnownRange && (!expr->astOperand1()->valueType() || expr->astOperand1()->valueType()->sign != ValueType::Sign::UNSIGNED)) + return false; + if (minvalue) + *minvalue = 0; + if (maxvalue) + *maxvalue = vals[3] - 1; + return true; + } + + return false; +} + +static void valueFlowRightShift(TokenList &tokenList, const Settings& settings) +{ + for (Token *tok = tokenList.front(); tok; tok = tok->next()) { + if (tok->str() != ">>") + continue; + + if (tok->hasKnownValue()) + continue; + + if (!tok->astOperand1() || !tok->astOperand2()) + continue; + + if (!tok->astOperand2()->hasKnownValue()) + continue; + + const MathLib::bigint rhsvalue = tok->astOperand2()->values().front().intvalue; + if (rhsvalue < 0) + continue; + + if (!tok->astOperand1()->valueType() || !tok->astOperand1()->valueType()->isIntegral()) + continue; + + if (!tok->astOperand2()->valueType() || !tok->astOperand2()->valueType()->isIntegral()) + continue; + + MathLib::bigint lhsmax=0; + if (!getExpressionRange(tok->astOperand1(), nullptr, &lhsmax)) + continue; + if (lhsmax < 0) + continue; + int lhsbits; + if ((tok->astOperand1()->valueType()->type == ValueType::Type::CHAR) || + (tok->astOperand1()->valueType()->type == ValueType::Type::SHORT) || + (tok->astOperand1()->valueType()->type == ValueType::Type::WCHAR_T) || + (tok->astOperand1()->valueType()->type == ValueType::Type::BOOL) || + (tok->astOperand1()->valueType()->type == ValueType::Type::INT)) + lhsbits = settings.platform.int_bit; + else if (tok->astOperand1()->valueType()->type == ValueType::Type::LONG) + lhsbits = settings.platform.long_bit; + else if (tok->astOperand1()->valueType()->type == ValueType::Type::LONGLONG) + lhsbits = settings.platform.long_long_bit; + else + continue; + if (rhsvalue >= lhsbits || rhsvalue >= MathLib::bigint_bits || (1ULL << rhsvalue) <= lhsmax) + continue; + + ValueFlow::Value val(0); + val.setKnown(); + setTokenValue(tok, std::move(val), settings); + } +} + +static std::vector minUnsignedValue(const Token* tok, int depth = 8) +{ + std::vector result; + if (!tok) + return result; + if (depth < 0) + return result; + if (tok->hasKnownIntValue()) { + result = {tok->values().front().intvalue}; + } else if (!Token::Match(tok, "-|%|&|^") && tok->isConstOp() && tok->astOperand1() && tok->astOperand2()) { + std::vector op1 = minUnsignedValue(tok->astOperand1(), depth - 1); + std::vector op2 = minUnsignedValue(tok->astOperand2(), depth - 1); + if (!op1.empty() && !op2.empty()) { + result = calculate>(tok->str(), op1.front(), op2.front()); + } + } + if (result.empty() && astIsUnsigned(tok)) + result = {0}; + return result; +} + +static bool isConvertedToIntegral(const Token* tok, const Settings& settings) +{ + if (!tok) + return false; + std::vector parentTypes = getParentValueTypes(tok, &settings); + if (parentTypes.empty()) + return false; + const ValueType& vt = parentTypes.front(); + return vt.type != ValueType::UNKNOWN_INT && vt.isIntegral(); +} + +static bool isSameToken(const Token* tok1, const Token* tok2) +{ + if (!tok1 || !tok2) + return false; + if (tok1->exprId() != 0 && tok1->exprId() == tok2->exprId()) + return true; + if (tok1->hasKnownIntValue() && tok2->hasKnownIntValue()) + return tok1->values().front().intvalue == tok2->values().front().intvalue; + return false; +} + +static void valueFlowImpossibleValues(TokenList& tokenList, const Settings& settings) +{ + for (Token* tok = tokenList.front(); tok; tok = tok->next()) { + if (tok->hasKnownIntValue()) + continue; + if (Token::Match(tok, "true|false")) + continue; + if (astIsBool(tok) || Token::Match(tok, "%comp%")) { + ValueFlow::Value lower{-1}; + lower.bound = ValueFlow::Value::Bound::Upper; + lower.setImpossible(); + setTokenValue(tok, std::move(lower), settings); + + ValueFlow::Value upper{2}; + upper.bound = ValueFlow::Value::Bound::Lower; + upper.setImpossible(); + setTokenValue(tok, std::move(upper), settings); + } else if (astIsUnsigned(tok) && !astIsPointer(tok)) { + std::vector minvalue = minUnsignedValue(tok); + if (minvalue.empty()) + continue; + ValueFlow::Value value{std::max(0, minvalue.front()) - 1}; + value.bound = ValueFlow::Value::Bound::Upper; + value.setImpossible(); + setTokenValue(tok, std::move(value), settings); + } + if (Token::simpleMatch(tok, "?") && Token::Match(tok->astOperand1(), "<|<=|>|>=")) { + const Token* condTok = tok->astOperand1(); + const Token* branchesTok = tok->astOperand2(); + + auto tokens = makeArray(condTok->astOperand1(), condTok->astOperand2()); + auto branches = makeArray(branchesTok->astOperand1(), branchesTok->astOperand2()); + bool flipped = false; + if (std::equal(tokens.cbegin(), tokens.cend(), branches.crbegin(), &isSameToken)) + flipped = true; + else if (!std::equal(tokens.cbegin(), tokens.cend(), branches.cbegin(), &isSameToken)) + continue; + const bool isMin = Token::Match(condTok, "<|<=") ^ flipped; + std::vector values; + for (const Token* tok2 : tokens) { + if (tok2->hasKnownIntValue()) { + values.emplace_back(tok2->values().front()); + } else { + ValueFlow::Value symValue{}; + symValue.valueType = ValueFlow::Value::ValueType::SYMBOLIC; + symValue.tokvalue = tok2; + values.push_back(symValue); + std::copy_if(tok2->values().cbegin(), + tok2->values().cend(), + std::back_inserter(values), + [](const ValueFlow::Value& v) { + if (!v.isKnown()) + return false; + return v.isSymbolicValue(); + }); + } + } + for (ValueFlow::Value& value : values) { + value.setImpossible(); + if (isMin) { + value.intvalue++; + value.bound = ValueFlow::Value::Bound::Lower; + } else { + value.intvalue--; + value.bound = ValueFlow::Value::Bound::Upper; + } + setTokenValue(tok, std::move(value), settings); + } + + } else if (Token::simpleMatch(tok, "%") && tok->astOperand2() && tok->astOperand2()->hasKnownIntValue()) { + ValueFlow::Value value{tok->astOperand2()->values().front()}; + value.bound = ValueFlow::Value::Bound::Lower; + value.setImpossible(); + setTokenValue(tok, std::move(value), settings); + } else if (Token::Match(tok, "abs|labs|llabs|fabs|fabsf|fabsl (")) { + ValueFlow::Value value{-1}; + value.bound = ValueFlow::Value::Bound::Upper; + value.setImpossible(); + setTokenValue(tok->next(), std::move(value), settings); + } else if (Token::Match(tok, ". data|c_str (") && astIsContainerOwned(tok->astOperand1())) { + const Library::Container* container = getLibraryContainer(tok->astOperand1()); + if (!container) + continue; + if (!container->stdStringLike) + continue; + if (container->view) + continue; + ValueFlow::Value value{0}; + value.setImpossible(); + setTokenValue(tok->tokAt(2), std::move(value), settings); + } else if (Token::Match(tok, "make_shared|make_unique <") && Token::simpleMatch(tok->linkAt(1), "> (")) { + ValueFlow::Value value{0}; + value.setImpossible(); + setTokenValue(tok->linkAt(1)->next(), std::move(value), settings); + } else if ((tokenList.isCPP() && Token::simpleMatch(tok, "this")) || tok->isUnaryOp("&")) { + ValueFlow::Value value{0}; + value.setImpossible(); + setTokenValue(tok, std::move(value), settings); + } else if (tok->isIncompleteVar() && tok->astParent() && tok->astParent()->isUnaryOp("-") && + isConvertedToIntegral(tok->astParent(), settings)) { + ValueFlow::Value value{0}; + value.setImpossible(); + setTokenValue(tok, std::move(value), settings); + } + } +} + +static void valueFlowEnumValue(SymbolDatabase & symboldatabase, const Settings & settings) +{ + for (Scope & scope : symboldatabase.scopeList) { + if (scope.type != Scope::eEnum) + continue; + MathLib::bigint value = 0; + bool prev_enum_is_known = true; + + for (Enumerator & enumerator : scope.enumeratorList) { + if (enumerator.start) { + Token* rhs = const_cast(enumerator.start->previous()->astOperand2()); + ValueFlow::valueFlowConstantFoldAST(rhs, settings); + if (rhs && rhs->hasKnownIntValue()) { + enumerator.value = rhs->values().front().intvalue; + enumerator.value_known = true; + value = enumerator.value + 1; + prev_enum_is_known = true; + } else + prev_enum_is_known = false; + } else if (prev_enum_is_known) { + enumerator.value = value++; + enumerator.value_known = true; + } + } + } +} + +static void valueFlowGlobalConstVar(TokenList& tokenList, const Settings &settings) +{ + // Get variable values... + std::map vars; + for (const Token* tok = tokenList.front(); tok; tok = tok->next()) { + if (!tok->variable()) + continue; + // Initialization... + if (tok == tok->variable()->nameToken() && + !tok->variable()->isVolatile() && + !tok->variable()->isArgument() && + tok->variable()->isConst() && + tok->valueType() && + tok->valueType()->isIntegral() && + tok->valueType()->pointer == 0 && + tok->valueType()->constness == 1 && + Token::Match(tok, "%name% =") && + tok->next()->astOperand2() && + tok->next()->astOperand2()->hasKnownIntValue()) { + vars[tok->variable()] = tok->next()->astOperand2()->values().front(); + } + } + + // Set values.. + for (Token* tok = tokenList.front(); tok; tok = tok->next()) { + if (!tok->variable()) + continue; + const std::map::const_iterator var = vars.find(tok->variable()); + if (var == vars.end()) + continue; + setTokenValue(tok, var->second, settings); + } +} + +static void valueFlowGlobalStaticVar(TokenList &tokenList, const Settings &settings) +{ + // Get variable values... + std::map vars; + for (const Token *tok = tokenList.front(); tok; tok = tok->next()) { + if (!tok->variable()) + continue; + // Initialization... + if (tok == tok->variable()->nameToken() && + tok->variable()->isStatic() && + !tok->variable()->isConst() && + tok->valueType() && + tok->valueType()->isIntegral() && + tok->valueType()->pointer == 0 && + tok->valueType()->constness == 0 && + Token::Match(tok, "%name% =") && + tok->next()->astOperand2() && + tok->next()->astOperand2()->hasKnownIntValue()) { + vars[tok->variable()] = tok->next()->astOperand2()->values().front(); + } else { + // If variable is written anywhere in TU then remove it from vars + if (!tok->astParent()) + continue; + if (Token::Match(tok->astParent(), "++|--|&") && !tok->astParent()->astOperand2()) + vars.erase(tok->variable()); + else if (tok->astParent()->isAssignmentOp()) { + if (tok == tok->astParent()->astOperand1()) + vars.erase(tok->variable()); + else if (tok->isCpp() && Token::Match(tok->astParent()->tokAt(-2), "& %name% =")) + vars.erase(tok->variable()); + } else if (isLikelyStreamRead(tok->astParent())) { + vars.erase(tok->variable()); + } else if (Token::Match(tok->astParent(), "[(,]")) + vars.erase(tok->variable()); + } + } + + // Set values.. + for (Token *tok = tokenList.front(); tok; tok = tok->next()) { + if (!tok->variable()) + continue; + const std::map::const_iterator var = vars.find(tok->variable()); + if (var == vars.end()) + continue; + setTokenValue(tok, var->second, settings); + } +} + +static ValuePtr makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const TokenList& tokenlist, const Settings& settings); +static ValuePtr makeReverseAnalyzer(const Token* exprTok, ValueFlow::Value value, const TokenList& tokenlist, const Settings& settings); + +static Analyzer::Result valueFlowForward(Token* startToken, + const Token* endToken, + const Token* exprTok, + ValueFlow::Value value, + const TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) +{ + if (settings.debugnormal) + setSourceLocation(value, loc, startToken); + return valueFlowGenericForward(startToken, + endToken, + makeAnalyzer(exprTok, std::move(value), tokenlist, settings), + tokenlist, + errorLogger, + settings); +} + +static Analyzer::Result valueFlowForward(Token* startToken, + const Token* endToken, + const Token* exprTok, + std::list values, + const TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) +{ + Analyzer::Result result{}; + for (ValueFlow::Value& v : values) { + result.update(valueFlowForward(startToken, endToken, exprTok, std::move(v), tokenlist, errorLogger, settings, loc)); + } + return result; +} + +template +static Analyzer::Result valueFlowForward(Token* startToken, + const Token* exprTok, + ValueOrValues v, + TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) +{ + const Token* endToken = nullptr; + const Function* f = Scope::nestedInFunction(startToken->scope()); + if (f && f->functionScope) + endToken = f->functionScope->bodyEnd; + return valueFlowForward(startToken, endToken, exprTok, std::move(v), tokenlist, errorLogger, settings, loc); +} + +static Analyzer::Result valueFlowForwardRecursive(Token* top, + const Token* exprTok, + std::list values, + const TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) +{ + Analyzer::Result result{}; + for (ValueFlow::Value& v : values) { + if (settings.debugnormal) + setSourceLocation(v, loc, top); + result.update( + valueFlowGenericForward(top, makeAnalyzer(exprTok, std::move(v), tokenlist, settings), tokenlist, errorLogger, settings)); + } + return result; +} + +static void valueFlowReverse(Token* tok, + const Token* const endToken, + const Token* const varToken, + std::list values, + const TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) +{ + for (ValueFlow::Value& v : values) { + if (settings.debugnormal) + setSourceLocation(v, loc, tok); + valueFlowGenericReverse(tok, endToken, makeReverseAnalyzer(varToken, std::move(v), tokenlist, settings), tokenlist, errorLogger, settings); + } +} + +// Deprecated +static void valueFlowReverse(const TokenList& tokenlist, + Token* tok, + const Token* const varToken, + ValueFlow::Value val, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) +{ + valueFlowReverse(tok, nullptr, varToken, {std::move(val)}, tokenlist, errorLogger, settings, loc); +} + +static bool isConditionKnown(const Token* tok, bool then) +{ + const char* op = "||"; + if (then) + op = "&&"; + const Token* parent = tok->astParent(); + while (parent && (parent->str() == op || parent->str() == "!" || parent->isCast())) + parent = parent->astParent(); + const Token* top = tok->astTop(); + if (top && Token::Match(top->previous(), "if|while|for (")) + return parent == top || Token::simpleMatch(parent, ";"); + return parent && parent->str() != op; +} + +static const std::string& invertAssign(const std::string& assign) +{ + static std::unordered_map lookup = {{"=", "="}, + {"+=", "-="}, + {"-=", "+="}, + {"*=", "/="}, + {"/=", "*="}, + {"<<=", ">>="}, + {">>=", "<<="}, + {"^=", "^="}}; + auto it = lookup.find(assign); + if (it == lookup.end()) { + return emptyString; + } + return it->second; +} + +static std::string removeAssign(const std::string& assign) { + return std::string{assign.cbegin(), assign.cend() - 1}; +} + +template +static T calculateAssign(const std::string& assign, const T& x, const U& y, bool* error = nullptr) +{ + if (assign.empty() || assign.back() != '=') { + if (error) + *error = true; + return T{}; + } + if (assign == "=") + return y; + return calculate(removeAssign(assign), x, y, error); +} + +template +static void assignValueIfMutable(T& x, const U& y) +{ + x = y; +} + +template +static void assignValueIfMutable(const T& /*unused*/, const U& /*unused*/) +{} + +template )> +static bool evalAssignment(Value& lhsValue, const std::string& assign, const ValueFlow::Value& rhsValue) +{ + bool error = false; + if (lhsValue.isSymbolicValue() && rhsValue.isIntValue()) { + if (assign != "+=" && assign != "-=") + return false; + assignValueIfMutable(lhsValue.intvalue, calculateAssign(assign, lhsValue.intvalue, rhsValue.intvalue, &error)); + } else if (lhsValue.isIntValue() && rhsValue.isIntValue()) { + assignValueIfMutable(lhsValue.intvalue, calculateAssign(assign, lhsValue.intvalue, rhsValue.intvalue, &error)); + } else if (lhsValue.isFloatValue() && rhsValue.isIntValue()) { + assignValueIfMutable(lhsValue.floatValue, + calculateAssign(assign, lhsValue.floatValue, rhsValue.intvalue, &error)); + } else { + return false; + } + return !error; +} + +static ValueFlow::Value::MoveKind isMoveOrForward(const Token* tok) +{ + if (!tok) + return ValueFlow::Value::MoveKind::NonMovedVariable; + const Token* parent = tok->astParent(); + if (!Token::simpleMatch(parent, "(")) + return ValueFlow::Value::MoveKind::NonMovedVariable; + const Token* ftok = parent->astOperand1(); + if (!ftok) + return ValueFlow::Value::MoveKind::NonMovedVariable; + if (Token::simpleMatch(ftok->astOperand1(), "std :: move")) + return ValueFlow::Value::MoveKind::MovedVariable; + if (Token::simpleMatch(ftok->astOperand1(), "std :: forward")) + return ValueFlow::Value::MoveKind::ForwardedVariable; + // TODO: Check for cast + return ValueFlow::Value::MoveKind::NonMovedVariable; +} + +template +struct SingleRange { + T* x; + T* begin() const { + return x; + } + T* end() const { + return x+1; + } +}; + +template +static SingleRange MakeSingleRange(T& x) +{ + return {&x}; +} + +class SelectValueFromVarIdMapRange { + using M = std::unordered_map; + + struct Iterator { + using iterator_category = std::forward_iterator_tag; + using value_type = const ValueFlow::Value; + using pointer = value_type *; + using reference = value_type &; + using difference_type = std::ptrdiff_t; + + explicit Iterator(const M::const_iterator & it) + : mIt(it) {} + + reference operator*() const { + return mIt->second; + } + + pointer operator->() const { + return &mIt->second; + } + + Iterator &operator++() { + // cppcheck-suppress postfixOperator - forward iterator needs to perform post-increment + mIt++; + return *this; + } + + friend bool operator==(const Iterator &a, const Iterator &b) { + return a.mIt == b.mIt; + } + + friend bool operator!=(const Iterator &a, const Iterator &b) { + return a.mIt != b.mIt; + } + + private: + M::const_iterator mIt; + }; + +public: + explicit SelectValueFromVarIdMapRange(const M *m) + : mMap(m) {} + + Iterator begin() const { + return Iterator(mMap->begin()); + } + Iterator end() const { + return Iterator(mMap->end()); + } + +private: + const M *mMap; +}; + +// Check if its an alias of the variable or is being aliased to this variable +template +static bool isAliasOf(const Variable * var, const Token *tok, nonneg int varid, const V& values, bool* inconclusive = nullptr) +{ + if (tok->varId() == varid) + return false; + if (tok->varId() == 0) + return false; + if (isAliasOf(tok, varid, inconclusive)) + return true; + if (var && !var->isPointer()) + return false; + // Search through non value aliases + return std::any_of(values.begin(), values.end(), [&](const ValueFlow::Value& val) { + if (!val.isNonValue()) + return false; + if (val.isInconclusive()) + return false; + if (val.isLifetimeValue() && !val.isLocalLifetimeValue()) + return false; + if (val.isLifetimeValue() && val.lifetimeKind != ValueFlow::Value::LifetimeKind::Address) + return false; + if (!Token::Match(val.tokvalue, ".|&|*|%var%")) + return false; + return astHasVar(val.tokvalue, tok->varId()); + }); +} + +static bool bifurcate(const Token* tok, const std::set& varids, const Settings& settings, int depth = 20); + +static bool bifurcateVariableChanged(const Variable* var, + const std::set& varids, + const Token* start, + const Token* end, + const Settings& settings, + int depth = 20) +{ + bool result = false; + const Token* tok = start; + while ((tok = findVariableChanged( + tok->next(), end, var->isPointer(), var->declarationId(), var->isGlobal(), &settings))) { + if (Token::Match(tok->astParent(), "%assign%")) { + if (!bifurcate(tok->astParent()->astOperand2(), varids, settings, depth - 1)) + return true; + } else { + result = true; + } + } + return result; +} + +static bool bifurcate(const Token* tok, const std::set& varids, const Settings& settings, int depth) +{ + if (depth < 0) + return false; + if (!tok) + return true; + if (tok->hasKnownIntValue()) + return true; + if (tok->isConstOp()) + return bifurcate(tok->astOperand1(), varids, settings, depth) && bifurcate(tok->astOperand2(), varids, settings, depth); + if (tok->varId() != 0) { + if (varids.count(tok->varId()) > 0) + return true; + const Variable* var = tok->variable(); + if (!var) + return false; + const Token* start = var->declEndToken(); + if (!start) + return false; + if (start->strAt(-1) == ")" || start->strAt(-1) == "}") + return false; + if (Token::Match(start, "; %varid% =", var->declarationId())) + start = start->tokAt(2); + if (var->isConst() || !bifurcateVariableChanged(var, varids, start, tok, settings, depth)) + return var->isArgument() || bifurcate(start->astOperand2(), varids, settings, depth - 1); + return false; + } + return false; +} + +struct ValueFlowAnalyzer : Analyzer { + const TokenList& tokenlist; + const Settings& settings; + ProgramMemoryState pms; + + explicit ValueFlowAnalyzer(const TokenList& t, const Settings& s) : tokenlist(t), settings(s), pms(&settings) {} + + virtual const ValueFlow::Value* getValue(const Token* tok) const = 0; + virtual ValueFlow::Value* getValue(const Token* tok) = 0; + + virtual void makeConditional() = 0; + + virtual void addErrorPath(const Token* tok, const std::string& s) = 0; + + virtual bool match(const Token* tok) const = 0; + + virtual bool internalMatch(const Token* /*tok*/) const { + return false; + } + + virtual bool isAlias(const Token* tok, bool& inconclusive) const = 0; + + using ProgramState = ProgramMemory::Map; + + virtual ProgramState getProgramState() const = 0; + + virtual int getIndirect(const Token* tok) const { + const ValueFlow::Value* value = getValue(tok); + if (value) + return value->indirect; + return 0; + } + + virtual bool isGlobal() const { + return false; + } + virtual bool dependsOnThis() const { + return false; + } + virtual bool isVariable() const { + return false; + } + + bool isCPP() const { + return tokenlist.isCPP(); + } + + const Settings& getSettings() const { + return settings; + } + + // Returns Action::Match if its an exact match, return Action::Read if it partially matches the lifetime + Action analyzeLifetime(const Token* tok) const + { + if (!tok) + return Action::None; + if (match(tok)) + return Action::Match; + if (Token::simpleMatch(tok, ".") && analyzeLifetime(tok->astOperand1()) != Action::None) + return Action::Read; + if (astIsRHS(tok) && Token::simpleMatch(tok->astParent(), ".")) + return analyzeLifetime(tok->astParent()); + return Action::None; + } + + struct ConditionState { + bool dependent = true; + bool unknown = true; + + bool isUnknownDependent() const { + return unknown && dependent; + } + }; + + std::unordered_map getSymbols(const Token* tok) const + { + std::unordered_map result; + if (!tok) + return result; + for (const ValueFlow::Value& v : tok->values()) { + if (!v.isSymbolicValue()) + continue; + if (v.isImpossible()) + continue; + if (!v.tokvalue) + continue; + if (v.tokvalue->exprId() == 0) + continue; + if (match(v.tokvalue)) + continue; + result[v.tokvalue->exprId()] = v.tokvalue; + } + return result; + } + + ConditionState analyzeCondition(const Token* tok, int depth = 20) const + { + ConditionState result; + if (!tok) + return result; + if (depth < 0) + return result; + depth--; + if (analyze(tok, Direction::Forward).isRead()) { + result.dependent = true; + result.unknown = false; + return result; + } + if (tok->hasKnownIntValue() || tok->isLiteral()) { + result.dependent = false; + result.unknown = false; + return result; + } + if (Token::Match(tok, "%cop%")) { + if (isLikelyStream(tok->astOperand1())) { + result.dependent = false; + return result; + } + ConditionState lhs = analyzeCondition(tok->astOperand1(), depth - 1); + if (lhs.isUnknownDependent()) + return lhs; + ConditionState rhs = analyzeCondition(tok->astOperand2(), depth - 1); + if (rhs.isUnknownDependent()) + return rhs; + if (Token::Match(tok, "%comp%")) + result.dependent = lhs.dependent && rhs.dependent; + else + result.dependent = lhs.dependent || rhs.dependent; + result.unknown = lhs.unknown || rhs.unknown; + return result; + } + if (Token::Match(tok->previous(), "%name% (")) { + std::vector args = getArguments(tok->previous()); + if (Token::Match(tok->tokAt(-2), ". %name% (")) { + args.push_back(tok->tokAt(-2)->astOperand1()); + } + result.dependent = std::any_of(args.cbegin(), args.cend(), [&](const Token* arg) { + ConditionState cs = analyzeCondition(arg, depth - 1); + return cs.dependent; + }); + if (result.dependent) { + // Check if we can evaluate the function + if (!evaluate(Evaluate::Integral, tok).empty()) + result.unknown = false; + } + return result; + } + + std::unordered_map symbols = getSymbols(tok); + result.dependent = false; + for (auto&& p : symbols) { + const Token* arg = p.second; + ConditionState cs = analyzeCondition(arg, depth - 1); + result.dependent = cs.dependent; + if (result.dependent) + break; + } + if (result.dependent) { + // Check if we can evaluate the token + if (!evaluate(Evaluate::Integral, tok).empty()) + result.unknown = false; + } + return result; + } + + virtual Action isModified(const Token* tok) const { + const Action read = Action::Read; + const ValueFlow::Value* value = getValue(tok); + if (value) { + // Moving a moved value won't change the moved value + if (value->isMovedValue() && isMoveOrForward(tok) != ValueFlow::Value::MoveKind::NonMovedVariable) + return read; + // Inserting elements to container won't change the lifetime + if (astIsContainer(tok) && value->isLifetimeValue() && + contains({Library::Container::Action::PUSH, + Library::Container::Action::INSERT, + Library::Container::Action::CHANGE_INTERNAL}, + astContainerAction(tok))) + return read; + } + bool inconclusive = false; + if (isVariableChangedByFunctionCall(tok, getIndirect(tok), &getSettings(), &inconclusive)) + return read | Action::Invalid; + if (inconclusive) + return read | Action::Inconclusive; + if (isVariableChanged(tok, getIndirect(tok), &getSettings())) { + if (Token::Match(tok->astParent(), "*|[|.|++|--")) + return read | Action::Invalid; + // Check if its assigned to the same value + if (value && !value->isImpossible() && Token::simpleMatch(tok->astParent(), "=") && astIsLHS(tok) && + astIsIntegral(tok->astParent()->astOperand2(), false)) { + std::vector result = evaluateInt(tok->astParent()->astOperand2()); + if (!result.empty() && value->equalTo(result.front())) + return Action::Idempotent; + } + return Action::Invalid; + } + return read; + } + + virtual Action isAliasModified(const Token* tok, int indirect = -1) const { + // Lambda function call + if (Token::Match(tok, "%var% (")) + // TODO: Check if modified in the lambda function + return Action::Invalid; + if (indirect == -1) { + indirect = 0; + if (const ValueType* vt = tok->valueType()) { + indirect = vt->pointer; + if (vt->type == ValueType::ITERATOR) + ++indirect; + } + } + for (int i = 0; i <= indirect; ++i) + if (isVariableChanged(tok, i, &getSettings())) + return Action::Invalid; + return Action::None; + } + + virtual Action isThisModified(const Token* tok) const { + if (isThisChanged(tok, 0, &getSettings())) + return Action::Invalid; + return Action::None; + } + + Action isGlobalModified(const Token* tok) const + { + if (tok->function()) { + if (!tok->function()->isConstexpr() && !isConstFunctionCall(tok, getSettings().library)) + return Action::Invalid; + } else if (getSettings().library.getFunction(tok)) { + // Assume library function doesn't modify user-global variables + return Action::None; + } else if (Token::simpleMatch(tok->astParent(), ".") && astIsContainer(tok->astParent()->astOperand1())) { + // Assume container member function doesn't modify user-global variables + return Action::None; + } else if (tok->tokType() == Token::eType && astIsPrimitive(tok->next())) { + // Function cast does not modify global variables + return Action::None; + } else if (!tok->isKeyword() && Token::Match(tok, "%name% (")) { + return Action::Invalid; + } + return Action::None; + } + + static const std::string& getAssign(const Token* tok, Direction d) + { + if (d == Direction::Forward) + return tok->str(); + return invertAssign(tok->str()); + } + + virtual Action isWritable(const Token* tok, Direction d) const { + const ValueFlow::Value* value = getValue(tok); + if (!value) + return Action::None; + if (!(value->isIntValue() || value->isFloatValue() || value->isSymbolicValue() || value->isLifetimeValue())) + return Action::None; + const Token* parent = tok->astParent(); + // Only if its invertible + if (value->isImpossible() && !Token::Match(parent, "+=|-=|*=|++|--")) + return Action::None; + if (value->isLifetimeValue()) { + if (value->lifetimeKind != ValueFlow::Value::LifetimeKind::Iterator) + return Action::None; + if (!Token::Match(parent, "++|--|+=")) + return Action::None; + return Action::Read | Action::Write; + } + if (parent && parent->isAssignmentOp() && astIsLHS(tok)) { + const Token* rhs = parent->astOperand2(); + std::vector result = evaluateInt(rhs); + if (!result.empty()) { + ValueFlow::Value rhsValue{result.front()}; + Action a; + if (!evalAssignment(*value, getAssign(parent, d), rhsValue)) + a = Action::Invalid; + else + a = Action::Write; + if (parent->str() != "=") { + a |= Action::Read | Action::Incremental; + } else { + if (!value->isImpossible() && value->equalValue(rhsValue)) + a = Action::Idempotent; + if (tok->exprId() != 0 && + findAstNode(rhs, [&](const Token* child) { + return tok->exprId() == child->exprId(); + })) + a |= Action::Incremental; + } + return a; + } + } + + // increment/decrement + if (Token::Match(tok->astParent(), "++|--")) { + return Action::Read | Action::Write | Action::Incremental; + } + return Action::None; + } + + virtual void writeValue(ValueFlow::Value* value, const Token* tok, Direction d) const { + if (!value) + return; + if (!tok->astParent()) + return; + // Lifetime value doesn't change + if (value->isLifetimeValue()) + return; + if (tok->astParent()->isAssignmentOp()) { + const Token* rhs = tok->astParent()->astOperand2(); + std::vector result = evaluateInt(rhs); + assert(!result.empty()); + ValueFlow::Value rhsValue{result.front()}; + if (evalAssignment(*value, getAssign(tok->astParent(), d), rhsValue)) { + std::string info("Compound assignment '" + tok->astParent()->str() + "', assigned value is " + + value->infoString()); + if (tok->astParent()->str() == "=") + value->errorPath.clear(); + value->errorPath.emplace_back(tok, std::move(info)); + } else { + assert(false && "Writable value cannot be evaluated"); + // TODO: Don't set to zero + value->intvalue = 0; + } + } else if (tok->astParent()->tokType() == Token::eIncDecOp) { + bool inc = tok->astParent()->str() == "++"; + const std::string opName(inc ? "incremented" : "decremented"); + if (d == Direction::Reverse) + inc = !inc; + value->intvalue += (inc ? 1 : -1); + value->errorPath.emplace_back(tok, tok->str() + " is " + opName + "', new value is " + value->infoString()); + } + } + + virtual bool useSymbolicValues() const { + return true; + } + + const Token* findMatch(const Token* tok) const + { + return findAstNode(tok, [&](const Token* child) { + return match(child); + }); + } + + bool isSameSymbolicValue(const Token* tok, ValueFlow::Value* value = nullptr) const + { + if (!useSymbolicValues()) + return false; + if (Token::Match(tok, "%assign%")) + return false; + const ValueFlow::Value* currValue = getValue(tok); + if (!currValue) + return false; + // If the same symbolic value is already there then skip + if (currValue->isSymbolicValue() && + std::any_of(tok->values().cbegin(), tok->values().cend(), [&](const ValueFlow::Value& v) { + return v.isSymbolicValue() && currValue->equalValue(v); + })) + return false; + const bool isPoint = currValue->bound == ValueFlow::Value::Bound::Point && currValue->isIntValue(); + const bool exact = !currValue->isIntValue() || currValue->isImpossible(); + for (const ValueFlow::Value& v : tok->values()) { + if (!v.isSymbolicValue()) + continue; + if (currValue->equalValue(v)) + continue; + const bool toImpossible = v.isImpossible() && currValue->isKnown(); + if (!v.isKnown() && !toImpossible) + continue; + if (exact && v.intvalue != 0 && !isPoint) + continue; + std::vector r; + ValueFlow::Value::Bound bound = currValue->bound; + if (match(v.tokvalue)) { + r = {currValue->intvalue}; + } else if (!exact && findMatch(v.tokvalue)) { + r = evaluate(Evaluate::Integral, v.tokvalue, tok); + if (bound == ValueFlow::Value::Bound::Point) + bound = v.bound; + } + if (!r.empty()) { + if (value) { + value->errorPath.insert(value->errorPath.end(), v.errorPath.cbegin(), v.errorPath.cend()); + value->intvalue = r.front() + v.intvalue; + if (toImpossible) + value->setImpossible(); + value->bound = bound; + } + return true; + } + } + return false; + } + + Action analyzeMatch(const Token* tok, Direction d) const { + const Token* parent = tok->astParent(); + if (d == Direction::Reverse && isGlobal() && !dependsOnThis() && Token::Match(parent, ". %name% (")) { + Action a = isGlobalModified(parent->next()); + if (a != Action::None) + return a; + } + if ((astIsPointer(tok) || astIsSmartPointer(tok)) && + (Token::Match(parent, "*|[") || (parent && parent->originalName() == "->")) && getIndirect(tok) <= 0) + return Action::Read; + + Action w = isWritable(tok, d); + if (w != Action::None) + return w; + + // Check for modifications by function calls + return isModified(tok); + } + + Action analyzeToken(const Token* ref, const Token* tok, Direction d, bool inconclusiveRef) const { + if (!ref) + return Action::None; + // If its an inconclusiveRef then ref != tok + assert(!inconclusiveRef || ref != tok); + bool inconclusive = false; + if (match(ref)) { + if (inconclusiveRef) { + Action a = isModified(tok); + if (a.isModified() || a.isInconclusive()) + return Action::Inconclusive; + } else { + return analyzeMatch(tok, d) | Action::Match; + } + } else if (ref->isUnaryOp("*") && !match(ref->astOperand1())) { + const Token* lifeTok = nullptr; + for (const ValueFlow::Value& v:ref->astOperand1()->values()) { + if (!v.isLocalLifetimeValue()) + continue; + if (lifeTok) + return Action::None; + lifeTok = v.tokvalue; + } + if (!lifeTok) + return Action::None; + Action la = analyzeLifetime(lifeTok); + if (la.matches()) { + Action a = Action::Read; + if (isModified(tok).isModified()) + a = Action::Invalid; + if (Token::Match(tok->astParent(), "%assign%") && astIsLHS(tok)) + a |= Action::Invalid; + if (inconclusiveRef && a.isModified()) + return Action::Inconclusive; + return a; + } + if (la.isRead()) { + return isAliasModified(tok); + } + return Action::None; + + } else if (isAlias(ref, inconclusive)) { + inconclusive |= inconclusiveRef; + Action a = isAliasModified(tok); + if (inconclusive && a.isModified()) + return Action::Inconclusive; + return a; + } + if (isSameSymbolicValue(ref)) + return Action::Read | Action::SymbolicMatch; + + return Action::None; + } + + Action analyze(const Token* tok, Direction d) const override { + if (invalid()) + return Action::Invalid; + // Follow references + auto refs = followAllReferences(tok); + const bool inconclusiveRefs = refs.size() != 1; + if (std::none_of(refs.cbegin(), refs.cend(), [&](const ReferenceToken& ref) { + return tok == ref.token; + })) + refs.emplace_back(ReferenceToken{tok, {}}); + for (const ReferenceToken& ref:refs) { + Action a = analyzeToken(ref.token, tok, d, inconclusiveRefs && ref.token != tok); + if (internalMatch(ref.token)) + a |= Action::Internal; + if (a != Action::None) + return a; + } + if (dependsOnThis() && exprDependsOnThis(tok, !isVariable())) + return isThisModified(tok); + + // bailout: global non-const variables + if (isGlobal() && !dependsOnThis() && Token::Match(tok, "%name% (") && + !Token::simpleMatch(tok->linkAt(1), ") {")) { + return isGlobalModified(tok); + } + return Action::None; + } + + template + std::vector evaluateInt(const Token* tok, F getProgramMemory) const + { + if (tok->hasKnownIntValue()) + return {static_cast(tok->values().front().intvalue)}; + std::vector result; + ProgramMemory pm = getProgramMemory(); + if (Token::Match(tok, "&&|%oror%")) { + if (conditionIsTrue(tok, pm, &getSettings())) + result.push_back(1); + if (conditionIsFalse(tok, std::move(pm), &getSettings())) + result.push_back(0); + } else { + MathLib::bigint out = 0; + bool error = false; + execute(tok, pm, &out, &error, &getSettings()); + if (!error) + result.push_back(out); + } + return result; + } + std::vector evaluateInt(const Token* tok) const + { + return evaluateInt(tok, [&] { + return ProgramMemory{getProgramState()}; + }); + } + + std::vector evaluate(Evaluate e, const Token* tok, const Token* ctx = nullptr) const override + { + if (e == Evaluate::Integral) { + return evaluateInt(tok, [&] { + return pms.get(tok, ctx, getProgramState()); + }); + } + if (e == Evaluate::ContainerEmpty) { + const ValueFlow::Value* value = ValueFlow::findValue(tok->values(), nullptr, [](const ValueFlow::Value& v) { + return v.isKnown() && v.isContainerSizeValue(); + }); + if (value) + return {value->intvalue == 0}; + ProgramMemory pm = pms.get(tok, ctx, getProgramState()); + MathLib::bigint out = 0; + if (pm.getContainerEmptyValue(tok->exprId(), out)) + return {static_cast(out)}; + return {}; + } + return {}; + } + + void assume(const Token* tok, bool state, unsigned int flags) override { + // Update program state + pms.removeModifiedVars(tok); + pms.addState(tok, getProgramState()); + pms.assume(tok, state, flags & Assume::ContainerEmpty); + + bool isCondBlock = false; + const Token* parent = tok->astParent(); + if (parent) { + isCondBlock = Token::Match(parent->previous(), "if|while ("); + } + + if (isCondBlock) { + const Token* startBlock = parent->link()->next(); + if (Token::simpleMatch(startBlock, ";") && Token::simpleMatch(parent->tokAt(-2), "} while (")) + startBlock = parent->linkAt(-2); + const Token* endBlock = startBlock->link(); + if (state) { + pms.removeModifiedVars(endBlock); + pms.addState(endBlock->previous(), getProgramState()); + } else { + if (Token::simpleMatch(endBlock, "} else {")) + pms.addState(endBlock->linkAt(2)->previous(), getProgramState()); + } + } + + if (!(flags & Assume::Quiet)) { + if (flags & Assume::ContainerEmpty) { + std::string s = state ? "empty" : "not empty"; + addErrorPath(tok, "Assuming container is " + s); + } else { + std::string s = bool_to_string(state); + addErrorPath(tok, "Assuming condition is " + s); + } + } + if (!(flags & Assume::Absolute)) + makeConditional(); + } + + void updateState(const Token* tok) override + { + // Update program state + pms.removeModifiedVars(tok); + pms.addState(tok, getProgramState()); + } + + virtual void internalUpdate(Token* /*tok*/, const ValueFlow::Value& /*v*/, Direction /*d*/) + { + assert(false && "Internal update unimplemented."); + } + + void update(Token* tok, Action a, Direction d) override { + ValueFlow::Value* value = getValue(tok); + if (!value) + return; + ValueFlow::Value localValue; + if (a.isSymbolicMatch()) { + // Make a copy of the value to modify it + localValue = *value; + value = &localValue; + isSameSymbolicValue(tok, &localValue); + } + if (a.isInternal()) + internalUpdate(tok, *value, d); + // Read first when moving forward + if (d == Direction::Forward && a.isRead()) + setTokenValue(tok, *value, getSettings()); + if (a.isInconclusive()) + (void)lowerToInconclusive(); + if (a.isWrite() && tok->astParent()) { + writeValue(value, tok, d); + } + // Read last when moving in reverse + if (d == Direction::Reverse && a.isRead()) + setTokenValue(tok, *value, getSettings()); + } + + ValuePtr reanalyze(Token* /*tok*/, const std::string& /*msg*/) const override { + return {}; + } +}; + +struct SingleValueFlowAnalyzer : ValueFlowAnalyzer { + std::unordered_map varids; + std::unordered_map aliases; + ValueFlow::Value value; + + SingleValueFlowAnalyzer(ValueFlow::Value v, const TokenList& t, const Settings& s) : ValueFlowAnalyzer(t, s), value(std::move(v)) {} + + const std::unordered_map& getVars() const { + return varids; + } + + const std::unordered_map& getAliasedVars() const { + return aliases; + } + + const ValueFlow::Value* getValue(const Token* /*tok*/) const override { + return &value; + } + ValueFlow::Value* getValue(const Token* /*tok*/) override { + return &value; + } + + void makeConditional() override { + value.conditional = true; + } + + bool useSymbolicValues() const override + { + if (value.isUninitValue()) + return false; + if (value.isLifetimeValue()) + return false; + return true; + } + + void addErrorPath(const Token* tok, const std::string& s) override { + value.errorPath.emplace_back(tok, s); + } + + bool isAlias(const Token* tok, bool& inconclusive) const override { + if (value.isLifetimeValue()) + return false; + for (const auto& m: { + std::ref(getVars()), std::ref(getAliasedVars()) + }) { + for (const auto& p:m.get()) { + nonneg int const varid = p.first; + const Variable* var = p.second; + if (tok->varId() == varid) + return true; + if (isAliasOf(var, tok, varid, MakeSingleRange(value), &inconclusive)) + return true; + } + } + return false; + } + + bool isGlobal() const override { + const auto& vars = getVars(); + return std::any_of(vars.cbegin(), vars.cend(), [] (const std::pair& p) { + const Variable* var = p.second; + return !var->isLocal() && !var->isArgument() && !var->isConst(); + }); + } + + bool lowerToPossible() override { + if (value.isImpossible()) + return false; + value.changeKnownToPossible(); + return true; + } + bool lowerToInconclusive() override { + if (value.isImpossible()) + return false; + value.setInconclusive(); + return true; + } + + bool isConditional() const override { + if (value.conditional) + return true; + if (value.condition) + return !value.isKnown() && !value.isImpossible(); + return false; + } + + bool stopOnCondition(const Token* condTok) const override + { + if (value.isNonValue()) + return false; + if (value.isImpossible()) + return false; + if (isConditional() && !value.isKnown() && !value.isImpossible()) + return true; + if (value.isSymbolicValue()) + return false; + ConditionState cs = analyzeCondition(condTok); + return cs.isUnknownDependent(); + } + + bool updateScope(const Token* endBlock, bool /*modified*/) const override { + const Scope* scope = endBlock->scope(); + if (!scope) + return false; + if (scope->type == Scope::eLambda) + return value.isLifetimeValue(); + if (scope->type == Scope::eIf || scope->type == Scope::eElse || scope->type == Scope::eWhile || + scope->type == Scope::eFor) { + if (value.isKnown() || value.isImpossible()) + return true; + if (value.isLifetimeValue()) + return true; + if (isConditional()) + return false; + const Token* condTok = getCondTokFromEnd(endBlock); + std::set varids2; + std::transform(getVars().cbegin(), getVars().cend(), std::inserter(varids2, varids2.begin()), SelectMapKeys{}); + return bifurcate(condTok, varids2, getSettings()); + } + + return false; + } + + ValuePtr reanalyze(Token* tok, const std::string& msg) const override { + ValueFlow::Value newValue = value; + newValue.errorPath.emplace_back(tok, msg); + return makeAnalyzer(tok, std::move(newValue), tokenlist, settings); + } +}; + +struct ExpressionAnalyzer : SingleValueFlowAnalyzer { + const Token* expr; + bool local = true; + bool unknown{}; + bool dependOnThis{}; + bool uniqueExprId{}; + + ExpressionAnalyzer(const Token* e, ValueFlow::Value val, const TokenList& t, const Settings& s) + : SingleValueFlowAnalyzer(std::move(val), t, s), + expr(e) + { + + assert(e && e->exprId() != 0 && "Not a valid expression"); + dependOnThis = exprDependsOnThis(expr); + setupExprVarIds(expr); + if (value.isSymbolicValue()) { + dependOnThis |= exprDependsOnThis(value.tokvalue); + setupExprVarIds(value.tokvalue); + } + uniqueExprId = + expr->isUniqueExprId() && (Token::Match(expr, "%cop%") || !isVariableChanged(expr, 0, &s)); + } + + static bool nonLocal(const Variable* var, bool deref) { + return !var || (!var->isLocal() && !var->isArgument()) || (deref && var->isArgument() && var->isPointer()) || + var->isStatic() || var->isReference() || var->isExtern(); + } + + void setupExprVarIds(const Token* start, int depth = 0) { + constexpr int maxDepth = 4; + if (depth > maxDepth) + return; + visitAstNodes(start, [&](const Token* tok) { + const bool top = depth == 0 && tok == start; + const bool ispointer = astIsPointer(tok) || astIsSmartPointer(tok) || astIsIterator(tok); + if (!top || !ispointer || value.indirect != 0) { + for (const ValueFlow::Value& v : tok->values()) { + if (!(v.isLocalLifetimeValue() || (ispointer && v.isSymbolicValue() && v.isKnown()))) + continue; + if (!v.tokvalue) + continue; + if (v.tokvalue == tok) + continue; + setupExprVarIds(v.tokvalue, depth + 1); + } + } + if (depth == 0 && tok->isIncompleteVar()) { + // TODO: Treat incomplete var as global, but we need to update + // the alias variables to just expr ids instead of requiring + // Variable + unknown = true; + return ChildrenToVisit::none; + } + if (tok->varId() > 0) { + varids[tok->varId()] = tok->variable(); + if (!Token::simpleMatch(tok->previous(), ".")) { + const Variable* var = tok->variable(); + if (var && var->isReference() && var->isLocal() && Token::Match(var->nameToken(), "%var% [=(]") && + !isGlobalData(var->nameToken()->next()->astOperand2())) + return ChildrenToVisit::none; + const bool deref = tok->astParent() && + (tok->astParent()->isUnaryOp("*") || + (tok->astParent()->str() == "[" && tok == tok->astParent()->astOperand1())); + local &= !nonLocal(tok->variable(), deref); + } + } + return ChildrenToVisit::op1_and_op2; + }); + } + + virtual bool skipUniqueExprIds() const { + return true; + } + + bool invalid() const override { + if (skipUniqueExprIds() && uniqueExprId) + return true; + return unknown; + } + + ProgramState getProgramState() const override { + ProgramState ps; + ps[expr] = value; + return ps; + } + + bool match(const Token* tok) const override { + return tok->exprId() == expr->exprId(); + } + + bool dependsOnThis() const override { + return dependOnThis; + } + + bool isGlobal() const override { + return !local; + } + + bool isVariable() const override { + return expr->varId() > 0; + } + + Action isAliasModified(const Token* tok, int indirect) const override { + if (value.isSymbolicValue() && tok->exprId() == value.tokvalue->exprId()) + indirect = 0; + return SingleValueFlowAnalyzer::isAliasModified(tok, indirect); + } +}; + +struct SameExpressionAnalyzer : ExpressionAnalyzer { + SameExpressionAnalyzer(const Token* e, ValueFlow::Value val, const TokenList& t, const Settings& s) + : ExpressionAnalyzer(e, std::move(val), t, s) + {} + + bool skipUniqueExprIds() const override { + return false; + } + + bool match(const Token* tok) const override + { + return isSameExpression(true, expr, tok, getSettings().library, true, true); + } +}; + +struct OppositeExpressionAnalyzer : ExpressionAnalyzer { + bool isNot{}; + + OppositeExpressionAnalyzer(bool pIsNot, const Token* e, ValueFlow::Value val, const TokenList& t, const Settings& s) + : ExpressionAnalyzer(e, std::move(val), t, s), isNot(pIsNot) + {} + + bool skipUniqueExprIds() const override { + return false; + } + + bool match(const Token* tok) const override { + return isOppositeCond(isNot, expr, tok, getSettings().library, true, true); + } +}; + +struct SubExpressionAnalyzer : ExpressionAnalyzer { + using PartialReadContainer = std::vector>; + // A shared_ptr is used so partial reads can be captured even after forking + std::shared_ptr partialReads; + + SubExpressionAnalyzer(const Token* e, ValueFlow::Value val, const TokenList& t, const Settings& s) + : ExpressionAnalyzer(e, std::move(val), t, s), partialReads(std::make_shared()) + {} + + virtual bool submatch(const Token* tok, bool exact = true) const = 0; + + bool isAlias(const Token* tok, bool& inconclusive) const override + { + if (tok->exprId() == expr->exprId() && tok->astParent() && submatch(tok->astParent(), false)) + return false; + return ExpressionAnalyzer::isAlias(tok, inconclusive); + } + + bool match(const Token* tok) const override + { + return tok->astOperand1() && tok->astOperand1()->exprId() == expr->exprId() && submatch(tok); + } + bool internalMatch(const Token* tok) const override + { + return tok->exprId() == expr->exprId() && !(astIsLHS(tok) && submatch(tok->astParent(), false)); + } + void internalUpdate(Token* tok, const ValueFlow::Value& v, Direction /*d*/) override + { + partialReads->emplace_back(tok, v); + } + + // No reanalysis for subexpression + ValuePtr reanalyze(Token* /*tok*/, const std::string& /*msg*/) const override { + return {}; + } +}; + +struct MemberExpressionAnalyzer : SubExpressionAnalyzer { + std::string varname; + + MemberExpressionAnalyzer(std::string varname, const Token* e, ValueFlow::Value val, const TokenList& t, const Settings& s) + : SubExpressionAnalyzer(e, std::move(val), t, s), varname(std::move(varname)) + {} + + bool submatch(const Token* tok, bool exact) const override + { + if (!Token::Match(tok, ". %var%")) + return false; + if (!exact) + return true; + return tok->next()->str() == varname; + } +}; + +enum class LifetimeCapture { Undefined, ByValue, ByReference }; + +static std::string lifetimeType(const Token *tok, const ValueFlow::Value *val) +{ + std::string result; + if (!val) + return "object"; + switch (val->lifetimeKind) { + case ValueFlow::Value::LifetimeKind::Lambda: + result = "lambda"; + break; + case ValueFlow::Value::LifetimeKind::Iterator: + result = "iterator"; + break; + case ValueFlow::Value::LifetimeKind::Object: + case ValueFlow::Value::LifetimeKind::SubObject: + case ValueFlow::Value::LifetimeKind::Address: + if (astIsPointer(tok)) + result = "pointer"; + else if (Token::simpleMatch(tok, "=") && astIsPointer(tok->astOperand2())) + result = "pointer"; + else + result = "object"; + break; + } + return result; +} + +std::string ValueFlow::lifetimeMessage(const Token *tok, const ValueFlow::Value *val, ErrorPath &errorPath) +{ + const Token *tokvalue = val ? val->tokvalue : nullptr; + const Variable *tokvar = tokvalue ? tokvalue->variable() : nullptr; + const Token *vartok = tokvar ? tokvar->nameToken() : nullptr; + const bool classVar = tokvar ? (!tokvar->isLocal() && !tokvar->isArgument() && !tokvar->isGlobal()) : false; + std::string type = lifetimeType(tok, val); + std::string msg = type; + if (vartok) { + if (!classVar) + errorPath.emplace_back(vartok, "Variable created here."); + const Variable * var = vartok->variable(); + if (var) { + std::string submessage; + switch (val->lifetimeKind) { + case ValueFlow::Value::LifetimeKind::SubObject: + case ValueFlow::Value::LifetimeKind::Object: + case ValueFlow::Value::LifetimeKind::Address: + if (type == "pointer") + submessage = " to local variable"; + else + submessage = " that points to local variable"; + break; + case ValueFlow::Value::LifetimeKind::Lambda: + submessage = " that captures local variable"; + break; + case ValueFlow::Value::LifetimeKind::Iterator: + submessage = " to local container"; + break; + } + if (classVar) + submessage.replace(submessage.find("local"), 5, "member"); + msg += submessage + " '" + var->name() + "'"; + } + } + return msg; +} + +std::vector ValueFlow::getLifetimeObjValues(const Token* tok, bool inconclusive, MathLib::bigint path) +{ + std::vector result; + auto pred = [&](const ValueFlow::Value& v) { + if (!v.isLocalLifetimeValue() && !(path != 0 && v.isSubFunctionLifetimeValue())) + return false; + if (!inconclusive && v.isInconclusive()) + return false; + if (!v.tokvalue) + return false; + if (path >= 0 && v.path != 0 && v.path != path) + return false; + return true; + }; + std::copy_if(tok->values().cbegin(), tok->values().cend(), std::back_inserter(result), pred); + return result; +} + +static bool hasUniqueOwnership(const Token* tok) +{ + if (!tok) + return false; + const Variable* var = tok->variable(); + if (var && var->isArray() && !var->isArgument()) + return true; + if (astIsPointer(tok)) + return false; + if (astIsUniqueSmartPointer(tok)) + return true; + if (astIsContainerOwned(tok)) + return true; + return false; +} + +// Check if dereferencing an object that doesn't have unique ownership +static bool derefShared(const Token* tok) +{ + if (!tok) + return false; + if (!tok->isUnaryOp("*") && tok->str() != "[" && tok->str() != ".") + return false; + if (tok->str() == "." && tok->originalName() != "->") + return false; + const Token* ptrTok = tok->astOperand1(); + return !hasUniqueOwnership(ptrTok); +} + +ValueFlow::Value ValueFlow::getLifetimeObjValue(const Token *tok, bool inconclusive) +{ + std::vector values = ValueFlow::getLifetimeObjValues(tok, inconclusive); + // There should only be one lifetime + if (values.size() != 1) + return ValueFlow::Value{}; + return values.front(); +} + +template +static std::vector getLifetimeTokens(const Token* tok, + bool escape, + ValueFlow::Value::ErrorPath errorPath, + Predicate pred, + int depth = 20) +{ + if (!tok) + return std::vector {}; + if (Token::simpleMatch(tok, "...")) + return std::vector{}; + const Variable *var = tok->variable(); + if (pred(tok)) + return {{tok, std::move(errorPath)}}; + if (depth < 0) + return {{tok, std::move(errorPath)}}; + if (var && var->declarationId() == tok->varId()) { + if (var->isReference() || var->isRValueReference()) { + const Token * const varDeclEndToken = var->declEndToken(); + if (!varDeclEndToken) + return {{tok, true, std::move(errorPath)}}; + if (var->isArgument()) { + errorPath.emplace_back(varDeclEndToken, "Passed to reference."); + return {{tok, true, std::move(errorPath)}}; + } + if (Token::Match(varDeclEndToken, "=|{")) { + errorPath.emplace_back(varDeclEndToken, "Assigned to reference."); + const Token *vartok = varDeclEndToken->astOperand2(); + const bool temporary = isTemporary(vartok, nullptr, true); + const bool nonlocal = var->isStatic() || var->isGlobal(); + if (vartok == tok || (nonlocal && temporary) || + (!escape && (var->isConst() || var->isRValueReference()) && temporary)) + return {{tok, true, std::move(errorPath)}}; + if (vartok) + return getLifetimeTokens(vartok, escape, std::move(errorPath), pred, depth - 1); + } else if (Token::simpleMatch(var->nameToken()->astParent(), ":") && + var->nameToken()->astParent()->astParent() && + Token::simpleMatch(var->nameToken()->astParent()->astParent()->previous(), "for (")) { + errorPath.emplace_back(var->nameToken(), "Assigned to reference."); + const Token* vartok = var->nameToken(); + if (vartok == tok) + return {{tok, true, std::move(errorPath)}}; + const Token* contok = var->nameToken()->astParent()->astOperand2(); + if (astIsContainer(contok)) + return getLifetimeTokens(contok, escape, std::move(errorPath), pred, depth - 1); + return std::vector{}; + } else { + return std::vector {}; + } + } + } else if (Token::Match(tok->previous(), "%name% (")) { + const Function *f = tok->previous()->function(); + if (f) { + if (!Function::returnsReference(f)) + return {{tok, std::move(errorPath)}}; + std::vector result; + std::vector returns = Function::findReturns(f); + for (const Token* returnTok : returns) { + if (returnTok == tok) + continue; + for (ValueFlow::LifetimeToken& lt : getLifetimeTokens(returnTok, escape, errorPath, pred, depth - returns.size())) { + const Token* argvarTok = lt.token; + const Variable* argvar = argvarTok->variable(); + if (!argvar) + continue; + const Token* argTok = nullptr; + if (argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) { + const int n = getArgumentPos(argvar, f); + if (n < 0) + return std::vector {}; + std::vector args = getArguments(tok->previous()); + // TODO: Track lifetimes of default parameters + if (n >= args.size()) + return std::vector {}; + argTok = args[n]; + lt.errorPath.emplace_back(returnTok, "Return reference."); + lt.errorPath.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'."); + } else if (Token::Match(tok->tokAt(-2), ". %name% (") && !derefShared(tok->tokAt(-2)) && + exprDependsOnThis(argvarTok)) { + argTok = tok->tokAt(-2)->astOperand1(); + lt.errorPath.emplace_back(returnTok, "Return reference that depends on 'this'."); + lt.errorPath.emplace_back(tok->previous(), + "Calling member function on '" + argTok->expressionString() + "'."); + } + if (argTok) { + std::vector arglts = ValueFlow::LifetimeToken::setInconclusive( + getLifetimeTokens(argTok, escape, std::move(lt.errorPath), pred, depth - returns.size()), + returns.size() > 1); + result.insert(result.end(), arglts.cbegin(), arglts.cend()); + } + } + } + return result; + } + if (Token::Match(tok->tokAt(-2), ". %name% (") && tok->tokAt(-2)->originalName() != "->" && astIsContainer(tok->tokAt(-2)->astOperand1())) { + const Library::Container* library = getLibraryContainer(tok->tokAt(-2)->astOperand1()); + const Library::Container::Yield y = library->getYield(tok->previous()->str()); + if (y == Library::Container::Yield::AT_INDEX || y == Library::Container::Yield::ITEM) { + errorPath.emplace_back(tok->previous(), "Accessing container."); + return ValueFlow::LifetimeToken::setAddressOf( + getLifetimeTokens(tok->tokAt(-2)->astOperand1(), escape, std::move(errorPath), pred, depth - 1), + false); + } + } + } else if (Token::Match(tok, ".|::|[") || tok->isUnaryOp("*")) { + + const Token *vartok = tok; + while (vartok) { + if (vartok->str() == "[" || vartok->isUnaryOp("*")) + vartok = vartok->astOperand1(); + else if (vartok->str() == ".") { + if (vartok->originalName().empty() || !Token::simpleMatch(vartok->astOperand1(), ".")) + vartok = vartok->astOperand1(); + else + break; + } + else if (vartok->str() == "::") + vartok = vartok->astOperand2(); + else + break; + } + + if (!vartok) + return {{tok, std::move(errorPath)}}; + if (derefShared(vartok->astParent())) { + for (const ValueFlow::Value &v : vartok->values()) { + if (!v.isLocalLifetimeValue()) + continue; + if (v.tokvalue == tok) + continue; + errorPath.insert(errorPath.end(), v.errorPath.cbegin(), v.errorPath.cend()); + return getLifetimeTokens(v.tokvalue, escape, std::move(errorPath), pred, depth - 1); + } + } else { + return ValueFlow::LifetimeToken::setAddressOf(getLifetimeTokens(vartok, escape, std::move(errorPath), pred, depth - 1), + !(astIsContainer(vartok) && Token::simpleMatch(vartok->astParent(), "["))); + } + } else if (Token::simpleMatch(tok, "{") && getArgumentStart(tok) && + !Token::simpleMatch(getArgumentStart(tok), ",") && getArgumentStart(tok)->valueType()) { + const Token* vartok = getArgumentStart(tok); + auto vts = getParentValueTypes(tok); + auto it = std::find_if(vts.cbegin(), vts.cend(), [&](const ValueType& vt) { + return vt.isTypeEqual(vartok->valueType()); + }); + if (it != vts.end()) + return getLifetimeTokens(vartok, escape, std::move(errorPath), pred, depth - 1); + } + return {{tok, std::move(errorPath)}}; +} + +std::vector ValueFlow::getLifetimeTokens(const Token* tok, bool escape, ValueFlow::Value::ErrorPath errorPath) +{ + return getLifetimeTokens(tok, escape, std::move(errorPath), [](const Token*) { + return false; + }); +} + +bool ValueFlow::hasLifetimeToken(const Token* tok, const Token* lifetime) +{ + bool result = false; + getLifetimeTokens(tok, false, ValueFlow::Value::ErrorPath{}, [&](const Token* tok2) { + result = tok2->exprId() == lifetime->exprId(); + return result; + }); + return result; +} + +static const Token* getLifetimeToken(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf = nullptr) +{ + std::vector lts = ValueFlow::getLifetimeTokens(tok); + if (lts.size() != 1) + return nullptr; + if (lts.front().inconclusive) + return nullptr; + if (addressOf) + *addressOf = lts.front().addressOf; + errorPath.insert(errorPath.end(), lts.front().errorPath.cbegin(), lts.front().errorPath.cend()); + return lts.front().token; +} + +const Variable* ValueFlow::getLifetimeVariable(const Token* tok, ValueFlow::Value::ErrorPath& errorPath, bool* addressOf) +{ + const Token* tok2 = getLifetimeToken(tok, errorPath, addressOf); + if (tok2 && tok2->variable()) + return tok2->variable(); + return nullptr; +} + +const Variable* ValueFlow::getLifetimeVariable(const Token* tok) +{ + ValueFlow::Value::ErrorPath errorPath; + return getLifetimeVariable(tok, errorPath, nullptr); +} + +static bool isNotLifetimeValue(const ValueFlow::Value& val) +{ + return !val.isLifetimeValue(); +} + +static bool isLifetimeOwned(const ValueType* vtParent) +{ + if (vtParent->container) + return !vtParent->container->view; + return vtParent->type == ValueType::CONTAINER; +} + +static bool isLifetimeOwned(const ValueType *vt, const ValueType *vtParent) +{ + if (!vtParent) + return false; + if (isLifetimeOwned(vtParent)) + return true; + if (!vt) + return false; + // If converted from iterator to pointer then the iterator is most likely a pointer + if (vtParent->pointer == 1 && vt->pointer == 0 && vt->type == ValueType::ITERATOR) + return false; + if (vt->type != ValueType::UNKNOWN_TYPE && vtParent->type != ValueType::UNKNOWN_TYPE) { + if (vt->pointer != vtParent->pointer) + return true; + if (vt->type != vtParent->type) { + if (vtParent->type == ValueType::RECORD) + return true; + if (isLifetimeOwned(vtParent)) + return true; + } + } + + return false; +} + +static bool isLifetimeBorrowed(const ValueType *vt, const ValueType *vtParent) +{ + if (!vtParent) + return false; + if (!vt) + return false; + if (vt->pointer > 0 && vt->pointer == vtParent->pointer) + return true; + if (vtParent->container && vtParent->container->view) + return true; + if (vt->type != ValueType::UNKNOWN_TYPE && vtParent->type != ValueType::UNKNOWN_TYPE && vtParent->container == vt->container) { + if (vtParent->pointer > vt->pointer) + return true; + if (vtParent->pointer < vt->pointer && vtParent->isIntegral()) + return true; + if (vtParent->str() == vt->str()) + return true; + } + + return false; +} + +static const Token* skipCVRefs(const Token* tok, const Token* endTok) +{ + while (tok != endTok && Token::Match(tok, "const|volatile|auto|&|&&")) + tok = tok->next(); + return tok; +} + +static bool isNotEqual(std::pair x, std::pair y) +{ + const Token* start1 = x.first; + const Token* start2 = y.first; + if (start1 == nullptr || start2 == nullptr) + return false; + while (start1 != x.second && start2 != y.second) { + const Token* tok1 = skipCVRefs(start1, x.second); + if (tok1 != start1) { + start1 = tok1; + continue; + } + const Token* tok2 = skipCVRefs(start2, y.second); + if (tok2 != start2) { + start2 = tok2; + continue; + } + if (start1->str() != start2->str()) + return true; + start1 = start1->next(); + start2 = start2->next(); + } + start1 = skipCVRefs(start1, x.second); + start2 = skipCVRefs(start2, y.second); + return !(start1 == x.second && start2 == y.second); +} +static bool isNotEqual(std::pair x, const std::string& y, bool cpp) +{ + TokenList tokenList(nullptr); + std::istringstream istr(y); + tokenList.createTokens(istr, cpp ? Standards::Language::CPP : Standards::Language::C); // TODO: check result? + return isNotEqual(x, std::make_pair(tokenList.front(), tokenList.back())); +} +static bool isNotEqual(std::pair x, const ValueType* y, bool cpp) +{ + if (y == nullptr) + return false; + if (y->originalTypeName.empty()) + return false; + return isNotEqual(x, y->originalTypeName, cpp); +} + +static bool isDifferentType(const Token* src, const Token* dst) +{ + const Type* t = Token::typeOf(src); + const Type* parentT = Token::typeOf(dst); + if (t && parentT) { + if (t->classDef && parentT->classDef && t->classDef != parentT->classDef) + return true; + } else { + std::pair decl = Token::typeDecl(src); + std::pair parentdecl = Token::typeDecl(dst); + if (isNotEqual(decl, parentdecl)) + return true; + if (isNotEqual(decl, dst->valueType(), dst->isCpp())) + return true; + if (isNotEqual(parentdecl, src->valueType(), src->isCpp())) + return true; + } + return false; +} + +bool ValueFlow::isLifetimeBorrowed(const Token *tok, const Settings &settings) +{ + if (!tok) + return true; + if (tok->str() == ",") + return true; + if (!tok->astParent()) + return true; + const Token* parent = nullptr; + const ValueType* vt = tok->valueType(); + std::vector vtParents = getParentValueTypes(tok, &settings, &parent); + for (const ValueType& vtParent : vtParents) { + if (isLifetimeBorrowed(vt, &vtParent)) + return true; + if (isLifetimeOwned(vt, &vtParent)) + return false; + } + if (parent) { + if (isDifferentType(tok, parent)) + return false; + } + return true; +} + +static void valueFlowLifetimeFunction(Token *tok, TokenList &tokenlist, ErrorLogger &errorLogger, const Settings &settings); + +static void valueFlowLifetimeConstructor(Token *tok, + TokenList &tokenlist, + ErrorLogger &errorLogger, + const Settings &settings); + +static bool isRangeForScope(const Scope* scope) +{ + if (!scope) + return false; + if (scope->type != Scope::eFor) + return false; + if (!scope->bodyStart) + return false; + if (!Token::simpleMatch(scope->bodyStart->previous(), ") {")) + return false; + return Token::simpleMatch(scope->bodyStart->linkAt(-1)->astOperand2(), ":"); +} + +static const Token* getEndOfVarScope(const Variable* var) +{ + if (!var) + return nullptr; + const Scope* innerScope = var->scope(); + const Scope* outerScope = innerScope; + if (var->typeStartToken() && var->typeStartToken()->scope()) + outerScope = var->typeStartToken()->scope(); + if (!innerScope && outerScope) + innerScope = outerScope; + if (!innerScope || !outerScope) + return nullptr; + if (!innerScope->isExecutable()) + return nullptr; + // If the variable is defined in a for/while initializer then we want to + // pick one token after the end so forward analysis can analyze the exit + // conditions + if (innerScope != outerScope && outerScope->isExecutable() && innerScope->isLoopScope() && + !isRangeForScope(innerScope)) + return innerScope->bodyEnd->next(); + return innerScope->bodyEnd; +} + +const Token* ValueFlow::getEndOfExprScope(const Token* tok, const Scope* defaultScope, bool smallest) +{ + const Token* end = nullptr; + bool local = false; + visitAstNodes(tok, [&](const Token* child) { + if (const Variable* var = child->variable()) { + local |= var->isLocal(); + if (var->isLocal() || var->isArgument()) { + const Token* varEnd = getEndOfVarScope(var); + if (!end || (smallest ? precedes(varEnd, end) : succeeds(varEnd, end))) + end = varEnd; + + const Token* top = var->nameToken()->astTop(); + if (top && Token::simpleMatch(top->tokAt(-1), "if (")) { // variable declared in if (...) + const Token* elseTok = top->link()->linkAt(1); + if (Token::simpleMatch(elseTok, "} else {") && tok->scope()->isNestedIn(elseTok->tokAt(2)->scope())) + end = tok->scope()->bodyEnd; + } + } + } + return ChildrenToVisit::op1_and_op2; + }); + if (!end && defaultScope) + end = defaultScope->bodyEnd; + if (!end) { + const Scope* scope = tok->scope(); + if (scope) + end = scope->bodyEnd; + // If there is no local variables then pick the function scope + if (!local) { + while (scope && scope->isLocal()) + scope = scope->nestedIn; + if (scope && scope->isExecutable()) + end = scope->bodyEnd; + } + } + return end; +} + +static void valueFlowForwardLifetime(Token * tok, TokenList &tokenlist, ErrorLogger &errorLogger, const Settings &settings) +{ + // Forward lifetimes to constructed variable + if (Token::Match(tok->previous(), "%var% {|(") && isVariableDecl(tok->previous())) { + std::list values = tok->values(); + values.remove_if(&isNotLifetimeValue); + valueFlowForward(nextAfterAstRightmostLeaf(tok), ValueFlow::getEndOfExprScope(tok), tok->previous(), std::move(values), tokenlist, errorLogger, settings); + return; + } + Token *parent = tok->astParent(); + while (parent && parent->str() == ",") + parent = parent->astParent(); + if (!parent) + return; + // Assignment + if (parent->str() == "=" && (!parent->astParent() || Token::simpleMatch(parent->astParent(), ";"))) { + // Rhs values.. + if (!parent->astOperand2() || parent->astOperand2()->values().empty()) + return; + + if (!ValueFlow::isLifetimeBorrowed(parent->astOperand2(), settings)) + return; + + const Token* expr = getLHSVariableToken(parent); + if (!expr) + return; + + if (expr->exprId() == 0) + return; + + const Token* endOfVarScope = ValueFlow::getEndOfExprScope(expr); + + // Only forward lifetime values + std::list values = parent->astOperand2()->values(); + values.remove_if(&isNotLifetimeValue); + // Dont forward lifetimes that overlap + values.remove_if([&](const ValueFlow::Value& value) { + return findAstNode(value.tokvalue, [&](const Token* child) { + return child->exprId() == expr->exprId(); + }); + }); + + // Skip RHS + Token* nextExpression = nextAfterAstRightmostLeaf(parent); + + if (expr->exprId() > 0) { + valueFlowForward(nextExpression, endOfVarScope->next(), expr, values, tokenlist, errorLogger, settings); + + for (ValueFlow::Value& val : values) { + if (val.lifetimeKind == ValueFlow::Value::LifetimeKind::Address) + val.lifetimeKind = ValueFlow::Value::LifetimeKind::SubObject; + } + // TODO: handle `[` + if (Token::simpleMatch(parent->astOperand1(), ".")) { + const Token* parentLifetime = + getParentLifetime(parent->astOperand1()->astOperand2(), settings.library); + if (parentLifetime && parentLifetime->exprId() > 0) { + valueFlowForward(nextExpression, endOfVarScope, parentLifetime, std::move(values), tokenlist, errorLogger, settings); + } + } + } + // Constructor + } else if (Token::simpleMatch(parent, "{") && !isScopeBracket(parent)) { + valueFlowLifetimeConstructor(parent, tokenlist, errorLogger, settings); + valueFlowForwardLifetime(parent, tokenlist, errorLogger, settings); + // Function call + } else if (Token::Match(parent->previous(), "%name% (")) { + valueFlowLifetimeFunction(parent->previous(), tokenlist, errorLogger, settings); + valueFlowForwardLifetime(parent, tokenlist, errorLogger, settings); + // Variable + } else if (tok->variable() && tok->variable()->scope()) { + const Variable *var = tok->variable(); + const Token *endOfVarScope = var->scope()->bodyEnd; + + std::list values = tok->values(); + Token *nextExpression = nextAfterAstRightmostLeaf(parent); + // Only forward lifetime values + values.remove_if(&isNotLifetimeValue); + valueFlowForward(nextExpression, endOfVarScope, tok, std::move(values), tokenlist, errorLogger, settings); + // Cast + } else if (parent->isCast()) { + std::list values = tok->values(); + // Only forward lifetime values + values.remove_if(&isNotLifetimeValue); + for (ValueFlow::Value& value:values) + setTokenValue(parent, std::move(value), settings); + valueFlowForwardLifetime(parent, tokenlist, errorLogger, settings); + } +} + +struct LifetimeStore { + const Token* argtok{}; + std::string message; + ValueFlow::Value::LifetimeKind type = ValueFlow::Value::LifetimeKind::Object; + ErrorPath errorPath; + bool inconclusive{}; + bool forward = true; + + LifetimeStore() = default; + + LifetimeStore(const Token* argtok, + std::string message, + ValueFlow::Value::LifetimeKind type = ValueFlow::Value::LifetimeKind::Object, + bool inconclusive = false) + : argtok(argtok), + message(std::move(message)), + type(type), + inconclusive(inconclusive) + {} + + template + static void forEach(TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + const std::vector& argtoks, + const std::string& message, + ValueFlow::Value::LifetimeKind type, + F f) { + std::set forwardToks; + for (const Token* arg : argtoks) { + LifetimeStore ls{arg, message, type}; + ls.forward = false; + f(ls); + if (ls.forwardTok) + forwardToks.emplace(ls.forwardTok); + } + for (auto* tok : forwardToks) { + valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); + } + } + + static LifetimeStore fromFunctionArg(const Function * f, const Token *tok, const Variable *var, const TokenList &tokenlist, const Settings& settings, ErrorLogger &errorLogger) { + if (!var) + return LifetimeStore{}; + if (!var->isArgument()) + return LifetimeStore{}; + const int n = getArgumentPos(var, f); + if (n < 0) + return LifetimeStore{}; + std::vector args = getArguments(tok); + if (n >= args.size()) { + if (settings.debugwarnings) + bailout(tokenlist, + errorLogger, + tok, + "Argument mismatch: Function '" + tok->str() + "' returning lifetime from argument index " + + std::to_string(n) + " but only " + std::to_string(args.size()) + + " arguments are available."); + return LifetimeStore{}; + } + const Token *argtok2 = args[n]; + return LifetimeStore{argtok2, "Passed to '" + tok->expressionString() + "'.", ValueFlow::Value::LifetimeKind::Object}; + } + + template + bool byRef(Token* tok, + TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + Predicate pred, + SourceLocation loc = SourceLocation::current()) + { + if (!argtok) + return false; + bool update = false; + for (const ValueFlow::LifetimeToken& lt : ValueFlow::getLifetimeTokens(argtok)) { + if (!settings.certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive) + continue; + ErrorPath er = errorPath; + er.insert(er.end(), lt.errorPath.cbegin(), lt.errorPath.cend()); + if (!lt.token) + return false; + if (!pred(lt.token)) + return false; + er.emplace_back(argtok, message); + + ValueFlow::Value value; + value.valueType = ValueFlow::Value::ValueType::LIFETIME; + value.lifetimeScope = ValueFlow::Value::LifetimeScope::Local; + value.tokvalue = lt.token; + value.errorPath = std::move(er); + value.lifetimeKind = type; + value.setInconclusive(lt.inconclusive || inconclusive); + // Don't add the value a second time + if (std::find(tok->values().cbegin(), tok->values().cend(), value) != tok->values().cend()) + return false; + if (settings.debugnormal) + setSourceLocation(value, loc, tok); + setTokenValue(tok, std::move(value), settings); + update = true; + } + if (update && forward) + forwardLifetime(tok, tokenlist, errorLogger, settings); + return update; + } + + bool byRef(Token* tok, + TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) + { + return byRef( + tok, + tokenlist, + errorLogger, + settings, + [](const Token*) { + return true; + }, + loc); + } + + template + bool byVal(Token* tok, + TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + Predicate pred, + SourceLocation loc = SourceLocation::current()) + { + if (!argtok) + return false; + bool update = false; + if (argtok->values().empty()) { + ErrorPath er; + er.emplace_back(argtok, message); + for (const ValueFlow::LifetimeToken& lt : ValueFlow::getLifetimeTokens(argtok)) { + if (!settings.certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive) + continue; + ValueFlow::Value value; + value.valueType = ValueFlow::Value::ValueType::LIFETIME; + value.tokvalue = lt.token; + value.capturetok = argtok; + value.errorPath = er; + value.lifetimeKind = type; + value.setInconclusive(inconclusive || lt.inconclusive); + const Variable* var = lt.token->variable(); + if (var && var->isArgument()) { + value.lifetimeScope = ValueFlow::Value::LifetimeScope::Argument; + } else { + continue; + } + // Don't add the value a second time + if (std::find(tok->values().cbegin(), tok->values().cend(), value) != tok->values().cend()) + continue; + if (settings.debugnormal) + setSourceLocation(value, loc, tok); + setTokenValue(tok, std::move(value), settings); + update = true; + } + } + for (const ValueFlow::Value &v : argtok->values()) { + if (!v.isLifetimeValue()) + continue; + const Token *tok3 = v.tokvalue; + for (const ValueFlow::LifetimeToken& lt : ValueFlow::getLifetimeTokens(tok3)) { + if (!settings.certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive) + continue; + ErrorPath er = v.errorPath; + er.insert(er.end(), lt.errorPath.cbegin(), lt.errorPath.cend()); + if (!lt.token) + return false; + if (!pred(lt.token)) + return false; + er.emplace_back(argtok, message); + er.insert(er.end(), errorPath.cbegin(), errorPath.cend()); + + ValueFlow::Value value; + value.valueType = ValueFlow::Value::ValueType::LIFETIME; + value.lifetimeScope = v.lifetimeScope; + value.path = v.path; + value.tokvalue = lt.token; + value.capturetok = argtok; + value.errorPath = std::move(er); + value.lifetimeKind = type; + value.setInconclusive(lt.inconclusive || v.isInconclusive() || inconclusive); + // Don't add the value a second time + if (std::find(tok->values().cbegin(), tok->values().cend(), value) != tok->values().cend()) + continue; + if (settings.debugnormal) + setSourceLocation(value, loc, tok); + setTokenValue(tok, std::move(value), settings); + update = true; + } + } + if (update && forward) + forwardLifetime(tok, tokenlist, errorLogger, settings); + return update; + } + + bool byVal(Token* tok, + TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) + { + return byVal( + tok, + tokenlist, + errorLogger, + settings, + [](const Token*) { + return true; + }, + loc); + } + + template + bool byDerefCopy(Token* tok, + TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + Predicate pred, + SourceLocation loc = SourceLocation::current()) const + { + bool update = false; + if (!settings.certainty.isEnabled(Certainty::inconclusive) && inconclusive) + return update; + if (!argtok) + return update; + if (!tok) + return update; + for (const ValueFlow::Value &v : argtok->values()) { + if (!v.isLifetimeValue()) + continue; + const Token *tok2 = v.tokvalue; + ErrorPath er = v.errorPath; + const Variable *var = ValueFlow::getLifetimeVariable(tok2, er); + // TODO: the inserted data is never used + er.insert(er.end(), errorPath.cbegin(), errorPath.cend()); + if (!var) + continue; + const Token * const varDeclEndToken = var->declEndToken(); + for (const Token *tok3 = tok; tok3 && tok3 != varDeclEndToken; tok3 = tok3->previous()) { + if (tok3->varId() == var->declarationId()) { + update |= LifetimeStore{tok3, message, type, inconclusive} + .byVal(tok, tokenlist, errorLogger, settings, pred, loc); + break; + } + } + } + return update; + } + + bool byDerefCopy(Token* tok, + TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) const + { + return byDerefCopy( + tok, + tokenlist, + errorLogger, + settings, + [](const Token*) { + return true; + }, + loc); + } + +private: + // cppcheck-suppress naming-privateMemberVariable + Token* forwardTok{}; + void forwardLifetime(Token* tok, TokenList& tokenlist, ErrorLogger& errorLogger, const Settings& settings) { + forwardTok = tok; + valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); + } +}; + +static bool hasBorrowingVariables(const std::list& vars, const std::vector& args, int depth = 10) +{ + if (depth < 0) + return true; + return std::any_of(vars.cbegin(), vars.cend(), [&](const Variable& var) { + if (const ValueType* vt = var.valueType()) { + if (vt->pointer > 0 && + std::none_of(args.begin(), args.end(), [vt](const Token* arg) { + return arg->valueType() && arg->valueType()->type == vt->type; + })) + return false; + if (vt->pointer > 0) + return true; + if (vt->reference != Reference::None) + return true; + if (vt->isPrimitive()) + return false; + if (vt->isEnum()) + return false; + // TODO: Check container inner type + if (vt->type == ValueType::CONTAINER && vt->container) + return vt->container->view; + if (vt->typeScope) + return hasBorrowingVariables(vt->typeScope->varlist, args, depth - 1); + } + return true; + }); +} + +static void valueFlowLifetimeUserConstructor(Token* tok, + const Function* constructor, + const std::string& name, + const std::vector& args, + TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings) +{ + if (!constructor) + return; + std::unordered_map argToParam; + for (std::size_t i = 0; i < args.size(); i++) + argToParam[args[i]] = constructor->getArgumentVar(i); + if (const Token* initList = constructor->constructorMemberInitialization()) { + std::unordered_map paramCapture; + for (const Token* tok2 : astFlatten(initList->astOperand2(), ",")) { + if (!Token::simpleMatch(tok2, "(")) + continue; + if (!tok2->astOperand1()) + continue; + if (!tok2->astOperand2()) + continue; + const Variable* var = tok2->astOperand1()->variable(); + const Token* expr = tok2->astOperand2(); + if (!var) + continue; + if (!ValueFlow::isLifetimeBorrowed(expr, settings)) + continue; + const Variable* argvar = ValueFlow::getLifetimeVariable(expr); + if (var->isReference() || var->isRValueReference()) { + if (argvar && argvar->isArgument() && (argvar->isReference() || argvar->isRValueReference())) { + paramCapture[argvar] = LifetimeCapture::ByReference; + } + } else { + bool found = false; + for (const ValueFlow::Value& v : expr->values()) { + if (!v.isLifetimeValue()) + continue; + if (v.path > 0) + continue; + if (!v.tokvalue) + continue; + const Variable* lifeVar = v.tokvalue->variable(); + if (!lifeVar) + continue; + LifetimeCapture c = LifetimeCapture::Undefined; + if (!v.isArgumentLifetimeValue() && (lifeVar->isReference() || lifeVar->isRValueReference())) + c = LifetimeCapture::ByReference; + else if (v.isArgumentLifetimeValue()) + c = LifetimeCapture::ByValue; + if (c != LifetimeCapture::Undefined) { + paramCapture[lifeVar] = c; + found = true; + } + } + if (!found && argvar && argvar->isArgument()) + paramCapture[argvar] = LifetimeCapture::ByValue; + } + } + // TODO: Use SubExpressionAnalyzer for members + LifetimeStore::forEach(tokenlist, + errorLogger, + settings, + args, + "Passed to constructor of '" + name + "'.", + ValueFlow::Value::LifetimeKind::SubObject, + [&](LifetimeStore& ls) { + const Variable* paramVar = argToParam.at(ls.argtok); + if (paramCapture.count(paramVar) == 0) + return; + const LifetimeCapture c = paramCapture.at(paramVar); + if (c == LifetimeCapture::ByReference) + ls.byRef(tok, tokenlist, errorLogger, settings); + else + ls.byVal(tok, tokenlist, errorLogger, settings); + }); + } else if (hasBorrowingVariables(constructor->nestedIn->varlist, args)) { + LifetimeStore::forEach(tokenlist, + errorLogger, + settings, + args, + "Passed to constructor of '" + name + "'.", + ValueFlow::Value::LifetimeKind::SubObject, + [&](LifetimeStore& ls) { + ls.inconclusive = true; + const Variable* var = argToParam.at(ls.argtok); + if (var && !var->isConst() && var->isReference()) + ls.byRef(tok, tokenlist, errorLogger, settings); + else + ls.byVal(tok, tokenlist, errorLogger, settings); + }); + } +} + +static void valueFlowLifetimeFunction(Token *tok, TokenList &tokenlist, ErrorLogger &errorLogger, const Settings &settings) +{ + if (!Token::Match(tok, "%name% (")) + return; + Token* memtok = nullptr; + if (Token::Match(tok->astParent(), ". %name% (") && astIsRHS(tok)) + memtok = tok->astParent()->astOperand1(); + const int returnContainer = settings.library.returnValueContainer(tok); + if (returnContainer >= 0) { + std::vector args = getArguments(tok); + for (int argnr = 1; argnr <= args.size(); ++argnr) { + const Library::ArgumentChecks::IteratorInfo *i = settings.library.getArgIteratorInfo(tok, argnr); + if (!i) + continue; + if (i->container != returnContainer) + continue; + const Token * const argTok = args[argnr - 1]; + bool forward = false; + for (ValueFlow::Value val : argTok->values()) { + if (!val.isLifetimeValue()) + continue; + val.errorPath.emplace_back(argTok, "Passed to '" + tok->str() + "'."); + setTokenValue(tok->next(), std::move(val), settings); + forward = true; + } + // Check if lifetime is available to avoid adding the lifetime twice + if (forward) { + valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); + break; + } + } + } else if (Token::Match(tok->tokAt(-2), "std :: ref|cref|tie|front_inserter|back_inserter")) { + for (const Token *argtok : getArguments(tok)) { + LifetimeStore{argtok, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Object}.byRef( + tok->next(), tokenlist, errorLogger, settings); + } + } else if (Token::Match(tok->tokAt(-2), "std :: make_tuple|tuple_cat|make_pair|make_reverse_iterator|next|prev|move|bind")) { + for (const Token *argtok : getArguments(tok)) { + LifetimeStore{argtok, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Object}.byVal( + tok->next(), tokenlist, errorLogger, settings); + } + } else if (memtok && Token::Match(tok->astParent(), ". push_back|push_front|insert|push|assign") && + astIsNonStringContainer(memtok)) { + std::vector args = getArguments(tok); + const std::size_t n = args.size(); + if (n > 1 && Token::typeStr(args[n - 2]) == Token::typeStr(args[n - 1]) && + (((astIsIterator(args[n - 2]) && astIsIterator(args[n - 1])) || + (astIsPointer(args[n - 2]) && astIsPointer(args[n - 1]))))) { + LifetimeStore{ + args.back(), "Added to container '" + memtok->str() + "'.", ValueFlow::Value::LifetimeKind::Object} + .byDerefCopy(memtok, tokenlist, errorLogger, settings); + } else if (!args.empty() && ValueFlow::isLifetimeBorrowed(args.back(), settings)) { + LifetimeStore{ + args.back(), "Added to container '" + memtok->str() + "'.", ValueFlow::Value::LifetimeKind::Object} + .byVal(memtok, tokenlist, errorLogger, settings); + } + } else if (tok->function()) { + const Function *f = tok->function(); + if (f->isConstructor()) { + valueFlowLifetimeUserConstructor(tok->next(), f, tok->str(), getArguments(tok), tokenlist, errorLogger, settings); + return; + } + if (Function::returnsReference(f)) + return; + std::vector returns = Function::findReturns(f); + const bool inconclusive = returns.size() > 1; + bool update = false; + for (const Token* returnTok : returns) { + if (returnTok == tok) + continue; + const Variable *returnVar = ValueFlow::getLifetimeVariable(returnTok); + if (returnVar && returnVar->isArgument() && (returnVar->isConst() || !isVariableChanged(returnVar, &settings))) { + LifetimeStore ls = LifetimeStore::fromFunctionArg(f, tok, returnVar, tokenlist, settings, errorLogger); + ls.inconclusive = inconclusive; + ls.forward = false; + update |= ls.byVal(tok->next(), tokenlist, errorLogger, settings); + } + for (const ValueFlow::Value &v : returnTok->values()) { + if (!v.isLifetimeValue()) + continue; + if (!v.tokvalue) + continue; + if (memtok && + (contains({ValueFlow::Value::LifetimeScope::ThisPointer, ValueFlow::Value::LifetimeScope::ThisValue}, + v.lifetimeScope) || + exprDependsOnThis(v.tokvalue))) { + LifetimeStore ls = LifetimeStore{memtok, + "Passed to member function '" + tok->expressionString() + "'.", + ValueFlow::Value::LifetimeKind::Object}; + ls.inconclusive = inconclusive; + ls.forward = false; + ls.errorPath = v.errorPath; + ls.errorPath.emplace_front(returnTok, "Return " + lifetimeType(returnTok, &v) + "."); + int thisIndirect = v.lifetimeScope == ValueFlow::Value::LifetimeScope::ThisValue ? 0 : 1; + if (derefShared(memtok->astParent())) + thisIndirect--; + if (thisIndirect == -1) + update |= ls.byDerefCopy(tok->next(), tokenlist, errorLogger, settings); + else if (thisIndirect == 0) + update |= ls.byVal(tok->next(), tokenlist, errorLogger, settings); + else if (thisIndirect == 1) + update |= ls.byRef(tok->next(), tokenlist, errorLogger, settings); + continue; + } + const Variable *var = v.tokvalue->variable(); + LifetimeStore ls = LifetimeStore::fromFunctionArg(f, tok, var, tokenlist, settings, errorLogger); + if (!ls.argtok) + continue; + ls.forward = false; + ls.inconclusive = inconclusive; + ls.errorPath = v.errorPath; + ls.errorPath.emplace_front(returnTok, "Return " + lifetimeType(returnTok, &v) + "."); + if (!v.isArgumentLifetimeValue() && (var->isReference() || var->isRValueReference())) { + update |= ls.byRef(tok->next(), tokenlist, errorLogger, settings); + } else if (v.isArgumentLifetimeValue()) { + update |= ls.byVal(tok->next(), tokenlist, errorLogger, settings); + } + } + } + if (update) + valueFlowForwardLifetime(tok->next(), tokenlist, errorLogger, settings); + } else if (tok->valueType()) { + // TODO: Propagate lifetimes with library functions + if (settings.library.getFunction(tok->previous())) + return; + // Assume constructing the valueType + valueFlowLifetimeConstructor(tok->next(), tokenlist, errorLogger, settings); + valueFlowForwardLifetime(tok->next(), tokenlist, errorLogger, settings); + } else { + const std::string& retVal = settings.library.returnValue(tok); + if (startsWith(retVal, "arg")) { + std::size_t iArg{}; + try { + iArg = strToInt(retVal.substr(3)); + } catch (...) { + return; + } + std::vector args = getArguments(tok); + if (iArg > 0 && iArg <= args.size()) { + const Token* varTok = args[iArg - 1]; + if (varTok->variable() && varTok->variable()->isLocal()) + LifetimeStore{ varTok, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Address }.byRef( + tok->next(), tokenlist, errorLogger, settings); + } + } + } +} + +static bool isScope(const Token* tok) +{ + if (!tok) + return false; + if (!Token::simpleMatch(tok, "{")) + return false; + const Scope* scope = tok->scope(); + if (!scope) + return false; + if (!scope->bodyStart) + return false; + return scope->bodyStart == tok; +} + +static const Function* findConstructor(const Scope* scope, const Token* tok, const std::vector& args) +{ + if (!tok) + return nullptr; + const Function* f = tok->function(); + if (!f && tok->astOperand1()) + f = tok->astOperand1()->function(); + // Search for a constructor + if (!f || !f->isConstructor()) { + f = nullptr; + std::vector candidates; + for (const Function& function : scope->functionList) { + if (function.minArgCount() > args.size()) + continue; + if (!function.isConstructor()) + continue; + candidates.push_back(&function); + } + // TODO: Narrow the candidates + if (candidates.size() == 1) + f = candidates.front(); + } + if (!f) + return nullptr; + return f; +} + +static void valueFlowLifetimeClassConstructor(Token* tok, + const Type* t, + TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings) +{ + if (!Token::Match(tok, "(|{")) + return; + if (isScope(tok)) + return; + if (!t) { + if (tok->valueType() && tok->valueType()->type != ValueType::RECORD) + return; + if (tok->str() != "{" && !Token::Match(tok->previous(), "%var% (") && !isVariableDecl(tok->previous())) + return; + // If the type is unknown then assume it captures by value in the + // constructor, but make each lifetime inconclusive + std::vector args = getArguments(tok); + LifetimeStore::forEach(tokenlist, + errorLogger, + settings, + args, + "Passed to initializer list.", + ValueFlow::Value::LifetimeKind::SubObject, + [&](LifetimeStore& ls) { + ls.inconclusive = true; + ls.byVal(tok, tokenlist, errorLogger, settings); + }); + return; + } + const Scope* scope = t->classScope; + if (!scope) + return; + // Aggregate constructor + if (t->derivedFrom.empty() && (t->isClassType() || t->isStructType())) { + std::vector args = getArguments(tok); + if (scope->numConstructors == 0) { + auto it = scope->varlist.cbegin(); + LifetimeStore::forEach( + tokenlist, + errorLogger, + settings, + args, + "Passed to constructor of '" + t->name() + "'.", + ValueFlow::Value::LifetimeKind::SubObject, + [&](LifetimeStore& ls) { + // Skip static variable + it = std::find_if(it, scope->varlist.cend(), [](const Variable& var) { + return !var.isStatic(); + }); + if (it == scope->varlist.cend()) + return; + const Variable& var = *it; + if (var.isReference() || var.isRValueReference()) { + ls.byRef(tok, tokenlist, errorLogger, settings); + } else if (ValueFlow::isLifetimeBorrowed(ls.argtok, settings)) { + ls.byVal(tok, tokenlist, errorLogger, settings); + } + it++; + }); + } else { + const Function* constructor = findConstructor(scope, tok, args); + valueFlowLifetimeUserConstructor(tok, constructor, t->name(), args, tokenlist, errorLogger, settings); + } + } +} + +static void valueFlowLifetimeConstructor(Token* tok, TokenList& tokenlist, ErrorLogger& errorLogger, const Settings& settings) +{ + if (!Token::Match(tok, "(|{")) + return; + if (isScope(tok)) + return; + std::vector vts; + if (tok->valueType()) { + vts = {*tok->valueType()}; + } else if (Token::Match(tok->previous(), "%var% {|(") && isVariableDecl(tok->previous()) && + tok->previous()->valueType()) { + vts = {*tok->previous()->valueType()}; + } else if (Token::simpleMatch(tok, "{") && !Token::Match(tok->previous(), "%name%")) { + vts = getParentValueTypes(tok, &settings); + } + + for (const ValueType& vt : vts) { + if (vt.pointer > 0) { + std::vector args = getArguments(tok); + LifetimeStore::forEach(tokenlist, + errorLogger, + settings, + args, + "Passed to initializer list.", + ValueFlow::Value::LifetimeKind::SubObject, + [&](LifetimeStore& ls) { + ls.byVal(tok, tokenlist, errorLogger, settings); + }); + } else if (vt.container && vt.type == ValueType::CONTAINER) { + std::vector args = getArguments(tok); + if (args.size() == 1 && vt.container->view && astIsContainerOwned(args.front())) { + LifetimeStore{args.front(), "Passed to container view.", ValueFlow::Value::LifetimeKind::SubObject} + .byRef(tok, tokenlist, errorLogger, settings); + } else if (args.size() == 2 && astIsIterator(args[0]) && astIsIterator(args[1])) { + LifetimeStore::forEach( + tokenlist, + errorLogger, + settings, + args, + "Passed to initializer list.", + ValueFlow::Value::LifetimeKind::SubObject, + [&](const LifetimeStore& ls) { + ls.byDerefCopy(tok, tokenlist, errorLogger, settings); + }); + } else if (vt.container->hasInitializerListConstructor) { + LifetimeStore::forEach(tokenlist, + errorLogger, + settings, + args, + "Passed to initializer list.", + ValueFlow::Value::LifetimeKind::SubObject, + [&](LifetimeStore& ls) { + ls.byVal(tok, tokenlist, errorLogger, settings); + }); + } + } else { + const Type* t = nullptr; + if (vt.typeScope && vt.typeScope->definedType) + t = vt.typeScope->definedType; + else + t = Token::typeOf(tok->previous()); + valueFlowLifetimeClassConstructor(tok, t, tokenlist, errorLogger, settings); + } + } +} + +struct Lambda { + explicit Lambda(const Token* tok) + { + if (!Token::simpleMatch(tok, "[") || !tok->link()) + return; + capture = tok; + + if (Token::simpleMatch(capture->link(), "] (")) { + arguments = capture->link()->next(); + } + const Token * afterArguments = arguments ? arguments->link()->next() : capture->link()->next(); + if (afterArguments && afterArguments->originalName() == "->") { + returnTok = afterArguments->next(); + bodyTok = Token::findsimplematch(returnTok, "{"); + } else if (Token::simpleMatch(afterArguments, "{")) { + bodyTok = afterArguments; + } + for (const Token* c:getCaptures()) { + if (Token::Match(c, "this !!.")) { + explicitCaptures[c->variable()] = std::make_pair(c, LifetimeCapture::ByReference); + } else if (Token::simpleMatch(c, "* this")) { + explicitCaptures[c->next()->variable()] = std::make_pair(c->next(), LifetimeCapture::ByValue); + } else if (c->variable()) { + explicitCaptures[c->variable()] = std::make_pair(c, LifetimeCapture::ByValue); + } else if (c->isUnaryOp("&") && Token::Match(c->astOperand1(), "%var%")) { + explicitCaptures[c->astOperand1()->variable()] = + std::make_pair(c->astOperand1(), LifetimeCapture::ByReference); + } else { + const std::string& s = c->expressionString(); + if (s == "=") + implicitCapture = LifetimeCapture::ByValue; + else if (s == "&") + implicitCapture = LifetimeCapture::ByReference; + } + } + } + + const Token* capture{}; + const Token* arguments{}; + const Token* returnTok{}; + const Token* bodyTok{}; + std::unordered_map> explicitCaptures; + LifetimeCapture implicitCapture = LifetimeCapture::Undefined; + + std::vector getCaptures() const { + return getArguments(capture); + } + + bool isLambda() const { + return capture && bodyTok; + } +}; + +static bool isDecayedPointer(const Token *tok) +{ + if (!tok) + return false; + if (!tok->astParent()) + return false; + if (astIsPointer(tok->astParent()) && !Token::simpleMatch(tok->astParent(), "return")) + return true; + if (tok->astParent()->isConstOp()) + return true; + if (!Token::simpleMatch(tok->astParent(), "return")) + return false; + return astIsPointer(tok->astParent()); +} + +static bool isConvertedToView(const Token* tok, const Settings& settings) +{ + std::vector vtParents = getParentValueTypes(tok, &settings); + return std::any_of(vtParents.cbegin(), vtParents.cend(), [&](const ValueType& vt) { + if (!vt.container) + return false; + return vt.container->view; + }); +} + +static bool isContainerOfPointers(const Token* tok, const Settings& settings) +{ + if (!tok) + { + return true; + } + + ValueType vt = ValueType::parseDecl(tok, settings); + return vt.pointer > 0; +} + +static void valueFlowLifetime(TokenList &tokenlist, ErrorLogger &errorLogger, const Settings &settings) +{ + for (Token *tok = tokenlist.front(); tok; tok = tok->next()) { + if (!tok->scope()) + continue; + if (tok->scope()->type == Scope::eGlobal) + continue; + Lambda lam(tok); + // Lambdas + if (lam.isLambda()) { + const Scope * bodyScope = lam.bodyTok->scope(); + + std::set scopes; + // Avoid capturing a variable twice + std::set varids; + bool capturedThis = false; + + auto isImplicitCapturingVariable = [&](const Token *varTok) { + const Variable *var = varTok->variable(); + if (!var) + return false; + if (varids.count(var->declarationId()) > 0) + return false; + if (!var->isLocal() && !var->isArgument()) + return false; + const Scope *scope = var->scope(); + if (!scope) + return false; + if (scopes.count(scope) > 0) + return false; + if (scope->isNestedIn(bodyScope)) + return false; + scopes.insert(scope); + varids.insert(var->declarationId()); + return true; + }; + + bool update = false; + auto captureVariable = [&](const Token* tok2, LifetimeCapture c, const std::function &pred) { + if (varids.count(tok->varId()) > 0) + return; + if (c == LifetimeCapture::ByReference) { + LifetimeStore ls{ + tok2, "Lambda captures variable by reference here.", ValueFlow::Value::LifetimeKind::Lambda}; + ls.forward = false; + update |= ls.byRef(tok, tokenlist, errorLogger, settings, pred); + } else if (c == LifetimeCapture::ByValue) { + LifetimeStore ls{ + tok2, "Lambda captures variable by value here.", ValueFlow::Value::LifetimeKind::Lambda}; + ls.forward = false; + update |= ls.byVal(tok, tokenlist, errorLogger, settings, pred); + pred(tok2); + } + }; + + auto captureThisVariable = [&](const Token* tok2, LifetimeCapture c) { + ValueFlow::Value value; + value.valueType = ValueFlow::Value::ValueType::LIFETIME; + if (c == LifetimeCapture::ByReference) + value.lifetimeScope = ValueFlow::Value::LifetimeScope::ThisPointer; + else if (c == LifetimeCapture::ByValue) + value.lifetimeScope = ValueFlow::Value::LifetimeScope::ThisValue; + value.tokvalue = tok2; + value.errorPath.emplace_back(tok2, "Lambda captures the 'this' variable here."); + value.lifetimeKind = ValueFlow::Value::LifetimeKind::Lambda; + capturedThis = true; + // Don't add the value a second time + if (std::find(tok->values().cbegin(), tok->values().cend(), value) != tok->values().cend()) + return; + setTokenValue(tok, std::move(value), settings); + update |= true; + }; + + // Handle explicit capture + for (const auto& p:lam.explicitCaptures) { + const Variable* var = p.first; + const Token* tok2 = p.second.first; + const LifetimeCapture c = p.second.second; + if (Token::Match(tok2, "this !!.")) { + captureThisVariable(tok2, c); + } else if (var) { + captureVariable(tok2, c, [](const Token*) { + return true; + }); + varids.insert(var->declarationId()); + } + } + + auto isImplicitCapturingThis = [&](const Token* tok2) { + if (capturedThis) + return false; + if (Token::simpleMatch(tok2, "this")) + return true; + if (tok2->variable()) { + if (Token::simpleMatch(tok2->previous(), ".")) + return false; + const Variable* var = tok2->variable(); + if (var->isLocal()) + return false; + if (var->isArgument()) + return false; + return exprDependsOnThis(tok2); + } + if (Token::simpleMatch(tok2, "(")) + return exprDependsOnThis(tok2); + return false; + }; + + for (const Token * tok2 = lam.bodyTok; tok2 != lam.bodyTok->link(); tok2 = tok2->next()) { + if (isImplicitCapturingThis(tok2)) { + captureThisVariable(tok2, LifetimeCapture::ByReference); + } else if (tok2->variable()) { + captureVariable(tok2, lam.implicitCapture, isImplicitCapturingVariable); + } + } + if (update) + valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); + } + // address of + else if (tok->isUnaryOp("&")) { + if (Token::simpleMatch(tok->astParent(), "*")) + continue; + for (const ValueFlow::LifetimeToken& lt : ValueFlow::getLifetimeTokens(tok->astOperand1())) { + if (!settings.certainty.isEnabled(Certainty::inconclusive) && lt.inconclusive) + continue; + ErrorPath errorPath = lt.errorPath; + errorPath.emplace_back(tok, "Address of variable taken here."); + + ValueFlow::Value value; + value.valueType = ValueFlow::Value::ValueType::LIFETIME; + value.lifetimeScope = ValueFlow::Value::LifetimeScope::Local; + value.tokvalue = lt.token; + value.errorPath = std::move(errorPath); + if (lt.addressOf || astIsPointer(lt.token) || !Token::Match(lt.token->astParent(), ".|[")) + value.lifetimeKind = ValueFlow::Value::LifetimeKind::Address; + value.setInconclusive(lt.inconclusive); + setTokenValue(tok, std::move(value), settings); + + valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); + } + } + // Converting to container view + else if (astIsContainerOwned(tok) && isConvertedToView(tok, settings)) { + LifetimeStore ls = + LifetimeStore{tok, "Converted to container view", ValueFlow::Value::LifetimeKind::SubObject}; + ls.byRef(tok, tokenlist, errorLogger, settings); + valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); + } + // container lifetimes + else if (astIsContainer(tok)) { + Token * parent = astParentSkipParens(tok); + if (!parent) + continue; + if (!Token::Match(parent, ". %name% (") && !Token::Match(parent->previous(), "%name% (")) + continue; + + // Skip if its a free function that doesnt yield an iterator to the container + if (Token::Match(parent->previous(), "%name% (") && + !contains({Library::Container::Yield::START_ITERATOR, Library::Container::Yield::END_ITERATOR}, + astFunctionYield(parent->previous(), settings))) + continue; + + ValueFlow::Value master; + master.valueType = ValueFlow::Value::ValueType::LIFETIME; + master.lifetimeScope = ValueFlow::Value::LifetimeScope::Local; + + if (astIsIterator(parent->tokAt(2))) { + master.errorPath.emplace_back(parent->tokAt(2), "Iterator to container is created here."); + master.lifetimeKind = ValueFlow::Value::LifetimeKind::Iterator; + } else if (astIsIterator(parent) && Token::Match(parent->previous(), "%name% (") && + contains({Library::Container::Yield::START_ITERATOR, Library::Container::Yield::END_ITERATOR}, + astFunctionYield(parent->previous(), settings))) { + master.errorPath.emplace_back(parent, "Iterator to container is created here."); + master.lifetimeKind = ValueFlow::Value::LifetimeKind::Iterator; + } else if ((astIsPointer(parent->tokAt(2)) && + !isContainerOfPointers(tok->valueType()->containerTypeToken, settings)) || + Token::Match(parent->next(), "data|c_str")) { + master.errorPath.emplace_back(parent->tokAt(2), "Pointer to container is created here."); + master.lifetimeKind = ValueFlow::Value::LifetimeKind::Object; + } else { + continue; + } + + std::vector toks; + if (tok->isUnaryOp("*") || parent->originalName() == "->") { + for (const ValueFlow::Value& v : tok->values()) { + if (!v.isLocalLifetimeValue()) + continue; + if (v.lifetimeKind != ValueFlow::Value::LifetimeKind::Address) + continue; + if (!v.tokvalue) + continue; + toks.push_back(v.tokvalue); + } + } else if (astIsContainerView(tok)) { + for (const ValueFlow::Value& v : tok->values()) { + if (!v.isLocalLifetimeValue()) + continue; + if (!v.tokvalue) + continue; + if (!astIsContainerOwned(v.tokvalue)) + continue; + toks.push_back(v.tokvalue); + } + } else { + toks = {tok}; + } + + for (const Token* tok2 : toks) { + for (const ReferenceToken& rt : followAllReferences(tok2, false)) { + ValueFlow::Value value = master; + value.tokvalue = rt.token; + value.errorPath.insert(value.errorPath.begin(), rt.errors.cbegin(), rt.errors.cend()); + if (Token::simpleMatch(parent, "(")) + setTokenValue(parent, std::move(value), settings); + else + setTokenValue(parent->tokAt(2), std::move(value), settings); + + if (!rt.token->variable()) { + LifetimeStore ls = LifetimeStore{ + rt.token, master.errorPath.back().second, ValueFlow::Value::LifetimeKind::Object}; + ls.byRef(parent->tokAt(2), tokenlist, errorLogger, settings); + } + } + } + valueFlowForwardLifetime(parent->tokAt(2), tokenlist, errorLogger, settings); + } + // Check constructors + else if (Token::Match(tok, "=|return|%name%|{|,|> {") && !isScope(tok->next())) { + valueFlowLifetimeConstructor(tok->next(), tokenlist, errorLogger, settings); + } + // Check function calls + else if (Token::Match(tok, "%name% (") && !Token::simpleMatch(tok->next()->link(), ") {")) { + valueFlowLifetimeFunction(tok, tokenlist, errorLogger, settings); + } + // Unique pointer lifetimes + else if (astIsUniqueSmartPointer(tok) && astIsLHS(tok) && Token::simpleMatch(tok->astParent(), ". get ( )")) { + Token* ptok = tok->astParent()->tokAt(2); + ErrorPath errorPath = {{ptok, "Raw pointer to smart pointer created here."}}; + ValueFlow::Value value; + value.valueType = ValueFlow::Value::ValueType::LIFETIME; + value.lifetimeScope = ValueFlow::Value::LifetimeScope::Local; + value.lifetimeKind = ValueFlow::Value::LifetimeKind::SubObject; + value.tokvalue = tok; + value.errorPath = std::move(errorPath); + setTokenValue(ptok, std::move(value), settings); + valueFlowForwardLifetime(ptok, tokenlist, errorLogger, settings); + } + // Check variables + else if (tok->variable()) { + ErrorPath errorPath; + const Variable * var = ValueFlow::getLifetimeVariable(tok, errorPath); + if (!var) + continue; + if (var->nameToken() == tok) + continue; + if (var->isArray() && !var->isStlType() && !var->isArgument() && isDecayedPointer(tok)) { + errorPath.emplace_back(tok, "Array decayed to pointer here."); + + ValueFlow::Value value; + value.valueType = ValueFlow::Value::ValueType::LIFETIME; + value.lifetimeScope = ValueFlow::Value::LifetimeScope::Local; + value.tokvalue = var->nameToken(); + value.errorPath = std::move(errorPath); + setTokenValue(tok, std::move(value), settings); + + valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); + } + } + // Forward any lifetimes + else if (std::any_of(tok->values().cbegin(), tok->values().cend(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) { + valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); + } + } +} + +static bool isStdMoveOrStdForwarded(Token * tok, ValueFlow::Value::MoveKind * moveKind, Token ** varTok = nullptr) +{ + if (tok->str() != "std") + return false; + ValueFlow::Value::MoveKind kind = ValueFlow::Value::MoveKind::NonMovedVariable; + Token * variableToken = nullptr; + if (Token::Match(tok, "std :: move ( %var% )")) { + variableToken = tok->tokAt(4); + kind = ValueFlow::Value::MoveKind::MovedVariable; + } else if (Token::simpleMatch(tok, "std :: forward <")) { + Token * const leftAngle = tok->tokAt(3); + Token * rightAngle = leftAngle->link(); + if (Token::Match(rightAngle, "> ( %var% )")) { + variableToken = rightAngle->tokAt(2); + kind = ValueFlow::Value::MoveKind::ForwardedVariable; + } + } + if (!variableToken) + return false; + if (variableToken->strAt(2) == ".") // Only partially moved + return false; + if (variableToken->valueType() && variableToken->valueType()->type >= ValueType::Type::VOID) + return false; + if (moveKind != nullptr) + *moveKind = kind; + if (varTok != nullptr) + *varTok = variableToken; + return true; +} + +static bool isOpenParenthesisMemberFunctionCallOfVarId(const Token * openParenthesisToken, nonneg int varId) +{ + const Token * varTok = openParenthesisToken->tokAt(-3); + return Token::Match(varTok, "%varid% . %name% (", varId) && + varTok->next()->originalName().empty(); +} + +static Token* findOpenParentesisOfMove(Token* moveVarTok) +{ + Token* tok = moveVarTok; + while (tok && tok->str() != "(") + tok = tok->previous(); + return tok; +} + +static Token* findEndOfFunctionCallForParameter(Token* parameterToken) +{ + if (!parameterToken) + return nullptr; + Token* parent = parameterToken->astParent(); + while (parent && !parent->isOp() && !Token::Match(parent, "[({]")) + parent = parent->astParent(); + if (!parent) + return nullptr; + return nextAfterAstRightmostLeaf(parent); +} + +static void valueFlowAfterMove(TokenList& tokenlist, const SymbolDatabase& symboldatabase, ErrorLogger& errorLogger, const Settings& settings) +{ + if (!tokenlist.isCPP() || settings.standards.cpp < Standards::CPP11) + return; + for (const Scope * scope : symboldatabase.functionScopes) { + if (!scope) + continue; + const Token * start = scope->bodyStart; + if (scope->function) { + const Token * memberInitializationTok = scope->function->constructorMemberInitialization(); + if (memberInitializationTok) + start = memberInitializationTok; + } + + for (auto* tok = const_cast(start); tok != scope->bodyEnd; tok = tok->next()) { + Token * varTok; + if (Token::Match(tok, "%var% . reset|clear (") && tok->next()->originalName().empty()) { + varTok = tok; + + const Variable *var = tok->variable(); + if (!var || (!var->isLocal() && !var->isArgument())) + continue; + + ValueFlow::Value value; + value.valueType = ValueFlow::Value::ValueType::MOVED; + value.moveKind = ValueFlow::Value::MoveKind::NonMovedVariable; + value.errorPath.emplace_back(tok, "Calling " + tok->next()->expressionString() + " makes " + tok->str() + " 'non-moved'"); + value.setKnown(); + + setTokenValue(tok, value, settings); + if (var->scope()) { + const Token* const endOfVarScope = var->scope()->bodyEnd; + valueFlowForward(tok->next(), endOfVarScope, tok, std::move(value), tokenlist, errorLogger, settings); + } + continue; + } + ValueFlow::Value::MoveKind moveKind; + if (!isStdMoveOrStdForwarded(tok, &moveKind, &varTok)) + continue; + const nonneg int varId = varTok->varId(); + // x is not MOVED after assignment if code is: x = ... std::move(x) .. ; + const Token *parent = tok->astParent(); + while (parent && parent->str() != "=" && parent->str() != "return" && + !(parent->str() == "(" && isOpenParenthesisMemberFunctionCallOfVarId(parent, varId))) + parent = parent->astParent(); + if (parent && + (parent->str() == "return" || // MOVED in return statement + parent->str() == "(")) // MOVED in self assignment, isOpenParenthesisMemberFunctionCallOfVarId == true + continue; + if (parent && parent->astOperand1() && parent->astOperand1()->varId() == varId) + continue; + const Token* const endOfVarScope = ValueFlow::getEndOfExprScope(varTok); + + Token* openParentesisOfMove = findOpenParentesisOfMove(varTok); + Token* endOfFunctionCall = findEndOfFunctionCallForParameter(openParentesisOfMove); + if (endOfFunctionCall) { + if (endOfFunctionCall->str() == ")") { + Token* ternaryColon = endOfFunctionCall->link()->astParent(); + while (Token::simpleMatch(ternaryColon, "(")) + ternaryColon = ternaryColon->astParent(); + if (Token::simpleMatch(ternaryColon, ":")) { + endOfFunctionCall = ternaryColon->astOperand2(); + if (Token::simpleMatch(endOfFunctionCall, "(")) + endOfFunctionCall = endOfFunctionCall->link(); + } + } + ValueFlow::Value value; + value.valueType = ValueFlow::Value::ValueType::MOVED; + value.moveKind = moveKind; + if (moveKind == ValueFlow::Value::MoveKind::MovedVariable) + value.errorPath.emplace_back(tok, "Calling std::move(" + varTok->str() + ")"); + else // if (moveKind == ValueFlow::Value::ForwardedVariable) + value.errorPath.emplace_back(tok, "Calling std::forward(" + varTok->str() + ")"); + value.setKnown(); + + valueFlowForward(endOfFunctionCall, endOfVarScope, varTok, std::move(value), tokenlist, errorLogger, settings); + } + } + } +} + +static const Token* findIncompleteVar(const Token* start, const Token* end) +{ + for (const Token* tok = start; tok != end; tok = tok->next()) { + if (tok->isIncompleteVar()) + return tok; + } + return nullptr; +} + +static ValueFlow::Value makeConditionValue(long long val, + const Token* condTok, + bool assume, + bool impossible = false, + const Settings* settings = nullptr, + SourceLocation loc = SourceLocation::current()) +{ + ValueFlow::Value v(val); + v.setKnown(); + if (impossible) { + v.intvalue = !v.intvalue; + v.setImpossible(); + } + v.condition = condTok; + if (assume) + v.errorPath.emplace_back(condTok, "Assuming condition '" + condTok->expressionString() + "' is true"); + else + v.errorPath.emplace_back(condTok, "Assuming condition '" + condTok->expressionString() + "' is false"); + if (settings && settings->debugnormal) + setSourceLocation(v, loc, condTok); + return v; +} + +static std::vector getConditions(const Token* tok, const char* op) +{ + std::vector conds = {tok}; + if (tok->str() == op) { + std::vector args = astFlatten(tok, op); + std::copy_if(args.cbegin(), args.cend(), std::back_inserter(conds), [&](const Token* tok2) { + if (tok2->exprId() == 0) + return false; + if (tok2->hasKnownIntValue()) + return false; + if (Token::Match(tok2, "%var%|.") && !astIsBool(tok2)) + return false; + return true; + }); + } + return conds; +} + +static bool isBreakOrContinueScope(const Token* endToken) +{ + if (!Token::simpleMatch(endToken, "}")) + return false; + return Token::Match(endToken->tokAt(-2), "break|continue ;"); +} + +static const Scope* getLoopScope(const Token* tok) +{ + if (!tok) + return nullptr; + const Scope* scope = tok->scope(); + while (scope && scope->type != Scope::eWhile && scope->type != Scope::eFor && scope->type != Scope::eDo) + scope = scope->nestedIn; + return scope; +} + +// +static void valueFlowConditionExpressions(const TokenList &tokenlist, const SymbolDatabase& symboldatabase, ErrorLogger &errorLogger, const Settings &settings) +{ + if (!settings.daca && (settings.checkLevel == Settings::CheckLevel::normal)) + { + if (settings.debugwarnings) { + ErrorMessage::FileLocation loc(tokenlist.getSourceFilePath(), 0, 0); + const ErrorMessage errmsg({std::move(loc)}, tokenlist.getSourceFilePath(), Severity::debug, "Analysis of condition expressions is disabled. Use --check-level=exhaustive to enable it.", "normalCheckLevelConditionExpressions", Certainty::normal); + errorLogger.reportErr(errmsg); + } + return; + } + + for (const Scope * scope : symboldatabase.functionScopes) { + if (const Token* incompleteTok = findIncompleteVar(scope->bodyStart, scope->bodyEnd)) { + if (settings.debugwarnings) + bailoutIncompleteVar(tokenlist, errorLogger, incompleteTok, "Skipping function due to incomplete variable " + incompleteTok->str()); + continue; + } + + if (settings.daca && (settings.checkLevel == Settings::CheckLevel::normal)) + continue; + + for (Token* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::simpleMatch(tok, "if (")) + continue; + Token* parenTok = tok->next(); + if (!Token::simpleMatch(parenTok->link(), ") {")) + continue; + Token * blockTok = parenTok->link()->tokAt(1); + const Token* condTok = parenTok->astOperand2(); + if (condTok->exprId() == 0) + continue; + if (condTok->hasKnownIntValue()) + continue; + if (!isConstExpression(condTok, settings.library)) + continue; + const bool is1 = (condTok->isComparisonOp() || condTok->tokType() == Token::eLogicalOp || astIsBool(condTok)); + + Token* startTok = blockTok; + // Inner condition + { + for (const Token* condTok2 : getConditions(condTok, "&&")) { + if (is1) { + const bool isBool = astIsBool(condTok2) || Token::Match(condTok2, "%comp%|%oror%|&&"); + SameExpressionAnalyzer a1(condTok2, makeConditionValue(1, condTok2, /*assume*/ true, !isBool), tokenlist, settings); // don't set '1' for non-boolean expressions + valueFlowGenericForward(startTok, startTok->link(), a1, tokenlist, errorLogger, settings); + } + + OppositeExpressionAnalyzer a2(true, condTok2, makeConditionValue(0, condTok2, true), tokenlist, settings); + valueFlowGenericForward(startTok, startTok->link(), a2, tokenlist, errorLogger, settings); + } + } + + std::vector conds = getConditions(condTok, "||"); + + // Check else block + if (Token::simpleMatch(startTok->link(), "} else {")) { + startTok = startTok->link()->tokAt(2); + for (const Token* condTok2:conds) { + SameExpressionAnalyzer a1(condTok2, makeConditionValue(0, condTok2, false), tokenlist, settings); + valueFlowGenericForward(startTok, startTok->link(), a1, tokenlist, errorLogger, settings); + + if (is1) { + OppositeExpressionAnalyzer a2(true, condTok2, makeConditionValue(1, condTok2, false), tokenlist, settings); + valueFlowGenericForward(startTok, startTok->link(), a2, tokenlist, errorLogger, settings); + } + } + } + + // Check if the block terminates early + if (isEscapeScope(blockTok, settings)) { + const Scope* scope2 = scope; + // If escaping a loop then only use the loop scope + if (isBreakOrContinueScope(blockTok->link())) { + scope2 = getLoopScope(blockTok->link()); + if (!scope2) + continue; + } + for (const Token* condTok2:conds) { + SameExpressionAnalyzer a1(condTok2, makeConditionValue(0, condTok2, false), tokenlist, settings); + valueFlowGenericForward(startTok->link()->next(), scope2->bodyEnd, a1, tokenlist, errorLogger, settings); + + if (is1) { + OppositeExpressionAnalyzer a2(true, condTok2, makeConditionValue(1, condTok2, false), tokenlist, settings); + valueFlowGenericForward(startTok->link()->next(), scope2->bodyEnd, a2, tokenlist, errorLogger, settings); + } + } + } + } + } +} + +static bool isTruncated(const ValueType* src, const ValueType* dst, const Settings& settings) +{ + if (src->pointer > 0 || dst->pointer > 0) + return src->pointer != dst->pointer; + if (src->smartPointer && dst->smartPointer) + return false; + if ((src->isIntegral() && dst->isIntegral()) || (src->isFloat() && dst->isFloat())) { + const size_t srcSize = ValueFlow::getSizeOf(*src, settings); + const size_t dstSize = ValueFlow::getSizeOf(*dst, settings); + if (srcSize > dstSize) + return true; + if (srcSize == dstSize && src->sign != dst->sign) + return true; + } else if (src->type == dst->type) { + if (src->type == ValueType::Type::RECORD) + return src->typeScope != dst->typeScope; + } else { + return true; + } + return false; +} + +static void setSymbolic(ValueFlow::Value& value, const Token* tok) +{ + assert(tok && tok->exprId() > 0 && "Missing expr id for symbolic value"); + value.valueType = ValueFlow::Value::ValueType::SYMBOLIC; + value.tokvalue = tok; +} + +static ValueFlow::Value makeSymbolic(const Token* tok, MathLib::bigint delta = 0) +{ + ValueFlow::Value value; + value.setKnown(); + setSymbolic(value, tok); + value.intvalue = delta; + return value; +} + +static std::set getVarIds(const Token* tok) +{ + std::set result; + visitAstNodes(tok, [&](const Token* child) { + if (child->varId() > 0) + result.insert(child->varId()); + return ChildrenToVisit::op1_and_op2; + }); + return result; +} + +static void valueFlowSymbolic(const TokenList& tokenlist, const SymbolDatabase& symboldatabase, ErrorLogger& errorLogger, const Settings& settings) +{ + for (const Scope* scope : symboldatabase.functionScopes) { + for (auto* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::simpleMatch(tok, "=")) + continue; + if (tok->astParent()) + continue; + if (!tok->astOperand1()) + continue; + if (!tok->astOperand2()) + continue; + if (tok->astOperand1()->hasKnownIntValue()) + continue; + if (tok->astOperand2()->hasKnownIntValue()) + continue; + if (tok->astOperand1()->exprId() == 0) + continue; + if (tok->astOperand2()->exprId() == 0) + continue; + if (!isConstExpression(tok->astOperand2(), settings.library)) + continue; + if (tok->astOperand1()->valueType() && tok->astOperand2()->valueType()) { + if (isTruncated( + tok->astOperand2()->valueType(), tok->astOperand1()->valueType(), settings)) + continue; + } else if (isDifferentType(tok->astOperand2(), tok->astOperand1())) { + continue; + } + const std::set rhsVarIds = getVarIds(tok->astOperand2()); + const std::vector vars = getLHSVariables(tok); + if (std::any_of(vars.cbegin(), vars.cend(), [&](const Variable* var) { + if (rhsVarIds.count(var->declarationId()) > 0) + return true; + if (var->isLocal()) + return var->isStatic(); + return !var->isArgument(); + })) + continue; + + if (findAstNode(tok, [](const Token* child) { + return child->isIncompleteVar(); + })) + continue; + + Token* start = nextAfterAstRightmostLeaf(tok); + const Token* end = ValueFlow::getEndOfExprScope(tok->astOperand1(), scope); + + ValueFlow::Value rhs = makeSymbolic(tok->astOperand2()); + rhs.errorPath.emplace_back(tok, + tok->astOperand1()->expressionString() + " is assigned '" + + tok->astOperand2()->expressionString() + "' here."); + valueFlowForward(start, end, tok->astOperand1(), std::move(rhs), tokenlist, errorLogger, settings); + + ValueFlow::Value lhs = makeSymbolic(tok->astOperand1()); + lhs.errorPath.emplace_back(tok, + tok->astOperand1()->expressionString() + " is assigned '" + + tok->astOperand2()->expressionString() + "' here."); + valueFlowForward(start, end, tok->astOperand2(), std::move(lhs), tokenlist, errorLogger, settings); + } + } +} + +static const Token* isStrlenOf(const Token* tok, const Token* expr, int depth = 10) +{ + if (depth < 0) + return nullptr; + if (!tok) + return nullptr; + if (!expr) + return nullptr; + if (expr->exprId() == 0) + return nullptr; + if (Token::simpleMatch(tok->previous(), "strlen (")) { + if (tok->astOperand2()->exprId() == expr->exprId()) + return tok; + } else { + for (const ValueFlow::Value& v : tok->values()) { + if (!v.isSymbolicValue()) + continue; + if (!v.isKnown()) + continue; + if (v.intvalue != 0) + continue; + if (const Token* next = isStrlenOf(v.tokvalue, expr, depth - 1)) + return next; + } + } + return nullptr; +} + +static ValueFlow::Value inferCondition(const std::string& op, const Token* varTok, MathLib::bigint val); + +static void valueFlowSymbolicOperators(const SymbolDatabase& symboldatabase, const Settings& settings) +{ + for (const Scope* scope : symboldatabase.functionScopes) { + for (auto* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { + if (tok->hasKnownIntValue()) + continue; + + if (Token::Match(tok, "abs|labs|llabs|fabs|fabsf|fabsl (")) { + const Token* arg = tok->next()->astOperand2(); + if (!arg) + continue; + if (arg->exprId() == 0) + continue; + ValueFlow::Value c = inferCondition(">=", arg, 0); + if (!c.isKnown()) + continue; + + ValueFlow::Value v = makeSymbolic(arg); + v.errorPath = c.errorPath; + v.errorPath.emplace_back(tok, "Passed to " + tok->str()); + if (c.intvalue == 0) + v.setImpossible(); + else + v.setKnown(); + setTokenValue(tok->next(), std::move(v), settings); + } else if (Token::Match(tok, "*|/|<<|>>|^|+|-|%or%")) { + if (!tok->astOperand1()) + continue; + if (!tok->astOperand2()) + continue; + if (!astIsIntegral(tok->astOperand1(), false) && !astIsIntegral(tok->astOperand2(), false)) + continue; + const ValueFlow::Value* constant = nullptr; + const Token* vartok = nullptr; + if (tok->astOperand1()->hasKnownIntValue()) { + constant = &tok->astOperand1()->values().front(); + vartok = tok->astOperand2(); + } + if (tok->astOperand2()->hasKnownIntValue()) { + constant = &tok->astOperand2()->values().front(); + vartok = tok->astOperand1(); + } + if (!constant) + continue; + if (!vartok) + continue; + if (vartok->exprId() == 0) + continue; + if (Token::Match(tok, "<<|>>|/") && !astIsLHS(vartok)) + continue; + if (Token::Match(tok, "<<|>>|^|+|-|%or%") && constant->intvalue != 0) + continue; + if (Token::Match(tok, "*|/") && constant->intvalue != 1) + continue; + std::vector values = {makeSymbolic(vartok)}; + std::unordered_set ids = {vartok->exprId()}; + std::copy_if(vartok->values().cbegin(), + vartok->values().cend(), + std::back_inserter(values), + [&](const ValueFlow::Value& v) { + if (!v.isSymbolicValue()) + return false; + if (!v.tokvalue) + return false; + return ids.insert(v.tokvalue->exprId()).second; + }); + for (ValueFlow::Value& v : values) + setTokenValue(tok, std::move(v), settings); + } else if (Token::simpleMatch(tok, "[")) { + const Token* arrayTok = tok->astOperand1(); + const Token* indexTok = tok->astOperand2(); + if (!arrayTok) + continue; + if (!indexTok) + continue; + for (const ValueFlow::Value& value : indexTok->values()) { + if (!value.isSymbolicValue()) + continue; + if (value.intvalue != 0) + continue; + const Token* strlenTok = isStrlenOf(value.tokvalue, arrayTok); + if (!strlenTok) + continue; + ValueFlow::Value v = value; + v.bound = ValueFlow::Value::Bound::Point; + v.valueType = ValueFlow::Value::ValueType::INT; + v.errorPath.emplace_back(strlenTok, "Return index of first '\\0' character in string"); + setTokenValue(tok, std::move(v), settings); + } + } + } + } +} + +struct SymbolicInferModel : InferModel { + const Token* expr; + explicit SymbolicInferModel(const Token* tok) : expr(tok) { + assert(expr->exprId() != 0); + } + bool match(const ValueFlow::Value& value) const override + { + return value.isSymbolicValue() && value.tokvalue && value.tokvalue->exprId() == expr->exprId(); + } + ValueFlow::Value yield(MathLib::bigint value) const override + { + ValueFlow::Value result(value); + result.valueType = ValueFlow::Value::ValueType::SYMBOLIC; + result.tokvalue = expr; + result.setKnown(); + return result; + } +}; + +static void valueFlowSymbolicInfer(const SymbolDatabase& symboldatabase, const Settings& settings) +{ + for (const Scope* scope : symboldatabase.functionScopes) { + for (auto* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::Match(tok, "-|%comp%")) + continue; + if (tok->hasKnownIntValue()) + continue; + if (!tok->astOperand1()) + continue; + if (!tok->astOperand2()) + continue; + if (tok->astOperand1()->exprId() == 0) + continue; + if (tok->astOperand2()->exprId() == 0) + continue; + if (tok->astOperand1()->hasKnownIntValue()) + continue; + if (tok->astOperand2()->hasKnownIntValue()) + continue; + if (astIsFloat(tok->astOperand1(), false)) + continue; + if (astIsFloat(tok->astOperand2(), false)) + continue; + + SymbolicInferModel leftModel{tok->astOperand1()}; + std::vector values = infer(leftModel, tok->str(), 0, tok->astOperand2()->values()); + if (values.empty()) { + SymbolicInferModel rightModel{tok->astOperand2()}; + values = infer(rightModel, tok->str(), tok->astOperand1()->values(), 0); + } + for (ValueFlow::Value& value : values) { + setTokenValue(tok, std::move(value), settings); + } + } + } +} + +template +static void valueFlowForwardConst(Token* start, + const Token* end, + const Variable* var, + const ContainerOfValue& values, + const Settings& settings, + int /*unused*/ = 0) +{ + if (!precedes(start, end)) + throw InternalError(var->nameToken(), "valueFlowForwardConst: start token does not precede the end token."); + for (Token* tok = start; tok != end; tok = tok->next()) { + if (tok->varId() == var->declarationId()) { + for (const ValueFlow::Value& value : values) + setTokenValue(tok, value, settings); + } else { + [&] { + // Follow references + auto refs = followAllReferences(tok); + auto it = std::find_if(refs.cbegin(), refs.cend(), [&](const ReferenceToken& ref) { + return ref.token->varId() == var->declarationId(); + }); + if (it != refs.end()) { + for (ValueFlow::Value value : values) { + if (refs.size() > 1) + value.setInconclusive(); + value.errorPath.insert(value.errorPath.end(), it->errors.cbegin(), it->errors.cend()); + setTokenValue(tok, std::move(value), settings); + } + return; + } + // Follow symbolic values + for (const ValueFlow::Value& v : tok->values()) { + if (!v.isSymbolicValue()) + continue; + if (!v.tokvalue) + continue; + if (v.tokvalue->varId() != var->declarationId()) + continue; + for (ValueFlow::Value value : values) { + if (!v.isKnown() && value.isImpossible()) + continue; + if (v.intvalue != 0) { + if (!value.isIntValue()) + continue; + value.intvalue += v.intvalue; + } + if (!value.isImpossible()) + value.valueKind = v.valueKind; + value.bound = v.bound; + value.errorPath.insert(value.errorPath.end(), v.errorPath.cbegin(), v.errorPath.cend()); + setTokenValue(tok, std::move(value), settings); + } + } + }(); + } + } +} + +static void valueFlowForwardConst(Token* start, + const Token* end, + const Variable* var, + const std::initializer_list& values, + const Settings& settings) +{ + valueFlowForwardConst(start, end, var, values, settings, 0); +} + +static ValueFlow::Value::Bound findVarBound(const Variable* var, + const Token* start, + const Token* end, + const Settings& settings) +{ + ValueFlow::Value::Bound result = ValueFlow::Value::Bound::Point; + const Token* next = start; + while ((next = findExpressionChangedSkipDeadCode( + var->nameToken(), next->next(), end, &settings, &evaluateKnownValues))) { + ValueFlow::Value::Bound b = ValueFlow::Value::Bound::Point; + if (next->varId() != var->declarationId()) + return ValueFlow::Value::Bound::Point; + if (Token::simpleMatch(next->astParent(), "++")) + b = ValueFlow::Value::Bound::Lower; + else if (Token::simpleMatch(next->astParent(), "--")) + b = ValueFlow::Value::Bound::Upper; + else + return ValueFlow::Value::Bound::Point; + if (result == ValueFlow::Value::Bound::Point) + result = b; + else if (result != b) + return ValueFlow::Value::Bound::Point; + } + return result; +} + +static bool isInitialVarAssign(const Token* tok) +{ + if (!tok) + return false; + if (!tok->variable()) + return false; + if (tok->variable()->nameToken() == tok) + return true; + const Token* prev = tok->tokAt(2); + if (!Token::Match(prev, "%var% ; %var%")) + return false; + return tok->varId() == prev->varId() && tok->variable()->nameToken() == prev; +} + +static void valueFlowForwardAssign(Token* const tok, + const Token* expr, + std::vector vars, + std::list values, + const bool init, + TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings) +{ + if (Token::simpleMatch(tok->astParent(), "return")) + return; + const Token* endOfVarScope = ValueFlow::getEndOfExprScope(expr); + if (std::any_of(values.cbegin(), values.cend(), std::mem_fn(&ValueFlow::Value::isLifetimeValue))) { + valueFlowForwardLifetime(tok, tokenlist, errorLogger, settings); + values.remove_if(std::mem_fn(&ValueFlow::Value::isLifetimeValue)); + } + if (std::all_of( + vars.cbegin(), vars.cend(), [&](const Variable* var) { + return !var->isPointer() && !var->isSmartPointer(); + })) + values.remove_if(std::mem_fn(&ValueFlow::Value::isTokValue)); + if (tok->astParent()) { + for (ValueFlow::Value& value : values) { + std::string valueKind; + if (value.valueKind == ValueFlow::Value::ValueKind::Impossible) { + if (value.bound == ValueFlow::Value::Bound::Point) + valueKind = "never "; + else if (value.bound == ValueFlow::Value::Bound::Lower) + valueKind = "less than "; + else if (value.bound == ValueFlow::Value::Bound::Upper) + valueKind = "greater than "; + } + std::string info = "Assignment '" + tok->astParent()->expressionString() + "', assigned value is " + valueKind + value.infoString(); + value.errorPath.emplace_back(tok, std::move(info)); + } + } + + if (tokenlist.isCPP() && vars.size() == 1 && Token::Match(vars.front()->typeStartToken(), "bool|_Bool")) { + for (ValueFlow::Value& value : values) { + if (value.isImpossible()) + continue; + if (value.isIntValue()) + value.intvalue = (value.intvalue != 0); + if (value.isTokValue()) + value.intvalue = (value.tokvalue != nullptr); + } + } + + // Static variable initialisation? + if (vars.size() == 1 && vars.front()->isStatic() && init) + lowerToPossible(values); + + // is volatile + if (std::any_of(vars.cbegin(), vars.cend(), [&](const Variable* var) { + return var->isVolatile(); + })) + lowerToPossible(values); + + // Skip RHS + Token* nextExpression = tok->astParent() ? nextAfterAstRightmostLeaf(tok->astParent()) : tok->next(); + if (!nextExpression) + return; + + for (ValueFlow::Value& value : values) { + if (value.isSymbolicValue()) + continue; + if (value.isTokValue()) + continue; + value.tokvalue = tok; + } + // Const variable + if (expr->variable() && expr->variable()->isConst() && !expr->variable()->isReference()) { + auto it = std::remove_if(values.begin(), values.end(), [](const ValueFlow::Value& value) { + if (!value.isKnown()) + return false; + if (value.isIntValue()) + return true; + if (value.isFloatValue()) + return true; + if (value.isContainerSizeValue()) + return true; + if (value.isIteratorValue()) + return true; + return false; + }); + std::list constValues; + constValues.splice(constValues.end(), values, it, values.end()); + valueFlowForwardConst(nextExpression, endOfVarScope, expr->variable(), constValues, settings); + } + if (isInitialVarAssign(expr)) { + // Check if variable is only incremented or decremented + ValueFlow::Value::Bound b = findVarBound(expr->variable(), nextExpression, endOfVarScope, settings); + if (b != ValueFlow::Value::Bound::Point) { + auto knownValueIt = std::find_if(values.begin(), values.end(), [](const ValueFlow::Value& value) { + if (!value.isKnown()) + return false; + return value.isIntValue(); + }); + if (knownValueIt != values.end()) { + ValueFlow::Value value = *knownValueIt; + value.bound = b; + value.invertRange(); + value.setImpossible(); + valueFlowForwardConst(nextExpression, endOfVarScope, expr->variable(), {std::move(value)}, settings); + } + } + } + valueFlowForward(nextExpression, endOfVarScope, expr, std::move(values), tokenlist, errorLogger, settings); +} + +static void valueFlowForwardAssign(Token* const tok, + const Variable* const var, + const std::list& values, + const bool /*unused*/, + const bool init, + TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings) +{ + valueFlowForwardAssign(tok, var->nameToken(), {var}, values, init, tokenlist, errorLogger, settings); +} + +static std::list truncateValues(std::list values, + const ValueType* dst, + const ValueType* src, + const Settings& settings) +{ + if (!dst || !dst->isIntegral()) + return values; + + const size_t sz = ValueFlow::getSizeOf(*dst, settings); + + if (src) { + const size_t osz = ValueFlow::getSizeOf(*src, settings); + if (osz >= sz && dst->sign == ValueType::Sign::SIGNED && src->sign == ValueType::Sign::UNSIGNED) { + values.remove_if([&](const ValueFlow::Value& value) { + if (!value.isIntValue()) + return false; + if (!value.isImpossible()) + return false; + if (value.bound != ValueFlow::Value::Bound::Upper) + return false; + if (osz == sz && value.intvalue < 0) + return true; + if (osz > sz) + return true; + return false; + }); + } + } + + for (ValueFlow::Value &value : values) { + // Don't truncate impossible values since those can be outside of the valid range + if (value.isImpossible()) + continue; + if (value.isFloatValue()) { + value.intvalue = value.floatValue; + value.valueType = ValueFlow::Value::ValueType::INT; + } + + if (value.isIntValue() && sz > 0 && sz < 8) { + const MathLib::biguint unsignedMaxValue = (1ULL << (sz * 8)) - 1ULL; + const MathLib::biguint signBit = 1ULL << (sz * 8 - 1); + value.intvalue &= unsignedMaxValue; + if (dst->sign == ValueType::Sign::SIGNED && (value.intvalue & signBit)) + value.intvalue |= ~unsignedMaxValue; + } + } + return values; +} + +static bool isVariableInit(const Token *tok) +{ + return (tok->str() == "(" || tok->str() == "{") && + (tok->isBinaryOp() || (tok->astOperand1() && tok->link() == tok->next())) && + tok->astOperand1()->variable() && + tok->astOperand1()->variable()->nameToken() == tok->astOperand1() && + tok->astOperand1()->variable()->valueType() && + tok->astOperand1()->variable()->valueType()->type >= ValueType::Type::VOID && + !Token::simpleMatch(tok->astOperand2(), ","); +} + +// Return true if two associative containers intersect +template +static bool intersects(const C1& c1, const C2& c2) +{ + if (c1.size() > c2.size()) + return intersects(c2, c1); + // NOLINTNEXTLINE(readability-use-anyofallof) - TODO: fix if possible / also Cppcheck false negative + for (auto&& x : c1) { + if (c2.find(x) != c2.end()) + return true; + } + return false; +} + +static void valueFlowAfterAssign(TokenList &tokenlist, + const SymbolDatabase& symboldatabase, + ErrorLogger &errorLogger, + const Settings &settings, + const std::set& skippedFunctions) +{ + for (const Scope * scope : symboldatabase.functionScopes) { + if (skippedFunctions.count(scope)) + continue; + std::unordered_map> backAssigns; + for (auto* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { + // Assignment + bool isInit = false; + if (tok->str() != "=" && !(isInit = isVariableInit(tok))) + continue; + + if (tok->astParent() && !((tok->astParent()->str() == ";" && astIsLHS(tok)) || tok->astParent()->str() == "*")) + continue; + + // Lhs should be a variable + if (!tok->astOperand1() || !tok->astOperand1()->exprId()) + continue; + std::vector vars = getLHSVariables(tok); + + // Rhs values.. + Token* rhs = tok->astOperand2(); + if (!rhs && isInit) + rhs = tok; + if (!rhs || rhs->values().empty()) + continue; + + std::list values = truncateValues( + rhs->values(), tok->astOperand1()->valueType(), rhs->valueType(), settings); + // Remove known values + std::set types; + if (tok->astOperand1()->hasKnownValue()) { + for (const ValueFlow::Value& value:tok->astOperand1()->values()) { + if (value.isKnown() && !value.isSymbolicValue()) + types.insert(value.valueType); + } + } + values.remove_if([&](const ValueFlow::Value& value) { + return types.count(value.valueType) > 0; + }); + // Remove container size if its not a container + if (!astIsContainer(tok->astOperand2())) + values.remove_if([&](const ValueFlow::Value& value) { + return value.valueType == ValueFlow::Value::ValueType::CONTAINER_SIZE; + }); + // Remove symbolic values that are the same as the LHS + values.remove_if([&](const ValueFlow::Value& value) { + if (value.isSymbolicValue() && value.tokvalue) + return value.tokvalue->exprId() == tok->astOperand1()->exprId(); + return false; + }); + // Find references to LHS in RHS + auto isIncremental = [&](const Token* tok2) -> bool { + return findAstNode(tok2, + [&](const Token* child) { + return child->exprId() == tok->astOperand1()->exprId(); + }); + }; + // Check symbolic values as well + const bool incremental = isIncremental(tok->astOperand2()) || + std::any_of(values.cbegin(), values.cend(), [&](const ValueFlow::Value& value) { + if (!value.isSymbolicValue()) + return false; + return isIncremental(value.tokvalue); + }); + // Remove values from the same assignment if it is incremental + if (incremental) { + values.remove_if([&](const ValueFlow::Value& value) { + if (value.tokvalue) + return value.tokvalue == tok->astOperand2(); + return false; + }); + } + // If assignment copy by value, remove Uninit values.. + if ((tok->astOperand1()->valueType() && tok->astOperand1()->valueType()->pointer == 0) || + (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isReference() && tok->astOperand1()->variable()->nameToken() == tok->astOperand1())) + values.remove_if([&](const ValueFlow::Value& value) { + return value.isUninitValue(); + }); + if (values.empty()) + continue; + const bool init = vars.size() == 1 && (vars.front()->nameToken() == tok->astOperand1() || tok->isSplittedVarDeclEq()); + valueFlowForwardAssign( + rhs, tok->astOperand1(), std::move(vars), values, init, tokenlist, errorLogger, settings); + // Back propagate symbolic values + if (tok->astOperand1()->exprId() > 0) { + Token* start = nextAfterAstRightmostLeaf(tok); + const Token* end = scope->bodyEnd; + // Collect symbolic ids + std::unordered_set ids; + for (const ValueFlow::Value& value : values) { + if (!value.isSymbolicValue()) + continue; + if (!value.tokvalue) + continue; + if (value.tokvalue->exprId() == 0) + continue; + ids.insert(value.tokvalue->exprId()); + } + for (ValueFlow::Value value : values) { + if (!value.isSymbolicValue()) + continue; + const Token* expr = value.tokvalue; + value.intvalue = -value.intvalue; + value.tokvalue = tok->astOperand1(); + + // Skip if it intersects with an already assigned symbol + auto& s = backAssigns[value.tokvalue->exprId()]; + if (intersects(s, ids)) + continue; + s.insert(expr->exprId()); + + value.errorPath.emplace_back(tok, + tok->astOperand1()->expressionString() + " is assigned '" + + tok->astOperand2()->expressionString() + "' here."); + valueFlowForward(start, end, expr, std::move(value), tokenlist, errorLogger, settings); + } + } + } + } +} + +static std::vector getVariables(const Token* tok) +{ + std::vector result; + visitAstNodes(tok, [&](const Token* child) { + if (child->variable()) + result.push_back(child->variable()); + return ChildrenToVisit::op1_and_op2; + }); + return result; +} + +static void valueFlowAfterSwap(TokenList& tokenlist, + const SymbolDatabase& symboldatabase, + ErrorLogger& errorLogger, + const Settings& settings) +{ + for (const Scope* scope : symboldatabase.functionScopes) { + for (auto* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { + if (!Token::simpleMatch(tok, "swap (")) + continue; + if (!Token::simpleMatch(tok->next()->astOperand2(), ",")) + continue; + std::vector args = astFlatten(tok->next()->astOperand2(), ","); + if (args.size() != 2) + continue; + if (args[0]->exprId() == 0) + continue; + if (args[1]->exprId() == 0) + continue; + for (int i = 0; i < 2; i++) { + std::vector vars = getVariables(args[0]); + const std::list& values = args[0]->values(); + valueFlowForwardAssign(args[0], args[1], std::move(vars), values, false, tokenlist, errorLogger, settings); + std::swap(args[0], args[1]); + } + } + } +} + +static void valueFlowSetConditionToKnown(const Token* tok, std::list& values, bool then) +{ + if (values.empty()) + return; + if (then && !Token::Match(tok, "==|!|(")) + return; + if (!then && !Token::Match(tok, "!=|%var%|(")) + return; + if (isConditionKnown(tok, then)) + changePossibleToKnown(values); +} + +static bool isBreakScope(const Token* const endToken) +{ + if (!Token::simpleMatch(endToken, "}")) + return false; + if (!Token::simpleMatch(endToken->link(), "{")) + return false; + return Token::findmatch(endToken->link(), "break|goto", endToken); +} + +ValueFlow::Value ValueFlow::asImpossible(ValueFlow::Value v) +{ + v.invertRange(); + v.setImpossible(); + return v; +} + +static void insertImpossible(std::list& values, const std::list& input) +{ + std::transform(input.cbegin(), input.cend(), std::back_inserter(values), &ValueFlow::asImpossible); +} + +static void insertNegateKnown(std::list& values, const std::list& input) +{ + for (ValueFlow::Value value:input) { + if (!value.isIntValue() && !value.isContainerSizeValue()) + continue; + value.intvalue = !value.intvalue; + value.setKnown(); + values.push_back(std::move(value)); + } +} + +struct ConditionHandler { + struct Condition { + const Token* vartok{}; + std::list true_values; + std::list false_values; + bool inverted = false; + // Whether to insert impossible values for the condition or only use possible values + bool impossible = true; + + bool isBool() const { + return astIsBool(vartok); + } + + static MathLib::bigint findPath(const std::list& values) + { + auto it = std::find_if(values.cbegin(), values.cend(), [](const ValueFlow::Value& v) { + return v.path > 0; + }); + if (it == values.end()) + return 0; + assert(std::all_of(it, values.end(), [&](const ValueFlow::Value& v) { + return v.path == 0 || v.path == it->path; + })); + return it->path; + } + + MathLib::bigint getPath() const + { + assert(std::abs(findPath(true_values) - findPath(false_values)) == 0); + return findPath(true_values) | findPath(false_values); + } + + Token* getContextAndValues(Token* condTok, + std::list& thenValues, + std::list& elseValues, + bool known = false) const + { + const MathLib::bigint path = getPath(); + const bool allowKnown = path == 0; + const bool allowImpossible = impossible && allowKnown; + + bool inverted2 = inverted; + Token* ctx = skipNotAndCasts(condTok, &inverted2); + bool then = !inverted || !inverted2; + + if (!Token::Match(condTok, "!=|=|(|.") && condTok != vartok) { + thenValues.insert(thenValues.end(), true_values.cbegin(), true_values.cend()); + if (allowImpossible && (known || isConditionKnown(ctx, !then))) + insertImpossible(elseValues, false_values); + } + if (!Token::Match(condTok, "==|!")) { + elseValues.insert(elseValues.end(), false_values.cbegin(), false_values.cend()); + if (allowImpossible && (known || isConditionKnown(ctx, then))) { + insertImpossible(thenValues, true_values); + if (isBool()) + insertNegateKnown(thenValues, true_values); + } + } + + if (inverted2) + std::swap(thenValues, elseValues); + + return ctx; + } + }; + + virtual std::vector parse(const Token* tok, const Settings& settings) const = 0; + + virtual Analyzer::Result forward(Token* start, + const Token* stop, + const Token* exprTok, + const std::list& values, + TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) const + { + return valueFlowForward(start->next(), stop, exprTok, values, tokenlist, errorLogger, settings, loc); + } + + virtual Analyzer::Result forward(Token* top, + const Token* exprTok, + const std::list& values, + TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) const + { + return valueFlowForwardRecursive(top, exprTok, values, tokenlist, errorLogger, settings, loc); + } + + virtual void reverse(Token* start, + const Token* endToken, + const Token* exprTok, + const std::list& values, + TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + SourceLocation loc = SourceLocation::current()) const + { + valueFlowReverse(start, endToken, exprTok, values, tokenlist, errorLogger, settings, loc); + } + + void traverseCondition(const SymbolDatabase& symboldatabase, + const Settings& settings, + const std::set& skippedFunctions, + const std::function& f) const + { + for (const Scope *scope : symboldatabase.functionScopes) { + if (skippedFunctions.count(scope)) + continue; + for (auto *tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "if|while|for (")) + continue; + if (Token::Match(tok, ":|;|,")) + continue; + + const Token* top = tok->astTop(); + if (!top) + continue; + + if (!Token::Match(top->previous(), "if|while|for (") && !Token::Match(tok->astParent(), "&&|%oror%|?|!")) + continue; + for (const Condition& cond : parse(tok, settings)) { + if (!cond.vartok) + continue; + if (cond.vartok->exprId() == 0) + continue; + if (cond.vartok->hasKnownIntValue()) + continue; + if (cond.true_values.empty() || cond.false_values.empty()) + continue; + if (!isConstExpression(cond.vartok, settings.library)) + continue; + f(cond, tok, scope); + } + } + } + } + + void beforeCondition(TokenList& tokenlist, + const SymbolDatabase& symboldatabase, + ErrorLogger& errorLogger, + const Settings& settings, + const std::set& skippedFunctions) const { + traverseCondition(symboldatabase, settings, skippedFunctions, [&](const Condition& cond, Token* tok, const Scope*) { + if (cond.vartok->exprId() == 0) + return; + + // If condition is known then don't propagate value + if (tok->hasKnownIntValue()) + return; + + Token* top = tok->astTop(); + + if (Token::Match(top, "%assign%")) + return; + if (Token::Match(cond.vartok->astParent(), "%assign%|++|--")) + return; + + if (Token::simpleMatch(tok->astParent(), "?") && tok->astParent()->isExpandedMacro()) { + if (settings.debugwarnings) + bailout(tokenlist, + errorLogger, + tok, + "variable '" + cond.vartok->expressionString() + "', condition is defined in macro"); + return; + } + + // if,macro => bailout + if (Token::simpleMatch(top->previous(), "if (") && top->previous()->isExpandedMacro()) { + if (settings.debugwarnings) + bailout(tokenlist, + errorLogger, + tok, + "variable '" + cond.vartok->expressionString() + "', condition is defined in macro"); + return; + } + + std::list values = cond.true_values; + if (cond.true_values != cond.false_values) + values.insert(values.end(), cond.false_values.cbegin(), cond.false_values.cend()); + + // extra logic for unsigned variables 'i>=1' => possible value can also be 0 + if (Token::Match(tok, "<|>|<=|>=")) { + values.remove_if([](const ValueFlow::Value& v) { + if (v.isIntValue()) + return v.intvalue != 0; + return false; + }); + if (cond.vartok->valueType() && cond.vartok->valueType()->sign != ValueType::Sign::UNSIGNED) + return; + } + if (values.empty()) + return; + + // bailout: for/while-condition, variable is changed in while loop + if (Token::Match(top->previous(), "for|while (") && Token::simpleMatch(top->link(), ") {")) { + + // Variable changed in 3rd for-expression + if (Token::simpleMatch(top->previous(), "for (")) { + if (top->astOperand2() && top->astOperand2()->astOperand2() && + findExpressionChanged( + cond.vartok, top->astOperand2()->astOperand2(), top->link(), &settings)) { + if (settings.debugwarnings) + bailout(tokenlist, + errorLogger, + tok, + "variable '" + cond.vartok->expressionString() + "' used in loop"); + return; + } + } + + // Variable changed in loop code + const Token* const start = top; + const Token* const block = top->link()->next(); + const Token* const end = block->link(); + + if (findExpressionChanged(cond.vartok, start, end, &settings)) { + // If its reassigned in loop then analyze from the end + if (!Token::Match(tok, "%assign%|++|--") && + findExpression(cond.vartok->exprId(), start, end, [&](const Token* tok2) { + return Token::Match(tok2->astParent(), "%assign%") && astIsLHS(tok2); + })) { + // Start at the end of the loop body + Token* bodyTok = top->link()->next(); + reverse(bodyTok->link(), bodyTok, cond.vartok, values, tokenlist, errorLogger, settings); + } + if (settings.debugwarnings) + bailout(tokenlist, + errorLogger, + tok, + "variable '" + cond.vartok->expressionString() + "' used in loop"); + return; + } + } + + Token* startTok = nullptr; + if (astIsRHS(tok)) + startTok = tok->astParent(); + else if (astIsLHS(tok)) + startTok = previousBeforeAstLeftmostLeaf(tok->astParent()); + if (!startTok) + startTok = tok->previous(); + + reverse(startTok, nullptr, cond.vartok, values, tokenlist, errorLogger, settings); + }); + } + + static Token* skipNotAndCasts(Token* tok, bool* inverted = nullptr) + { + for (; tok->astParent(); tok = tok->astParent()) { + if (Token::simpleMatch(tok->astParent(), "!")) { + if (inverted) + *inverted ^= true; + continue; + } + if (Token::Match(tok->astParent(), "==|!=")) { + const Token* sibling = tok->astSibling(); + if (sibling->hasKnownIntValue() && (astIsBool(tok) || astIsBool(sibling))) { + const bool value = sibling->values().front().intvalue; + if (inverted) + *inverted ^= value == Token::simpleMatch(tok->astParent(), "!="); + continue; + } + } + if (tok->astParent()->isCast() && astIsBool(tok->astParent())) + continue; + return tok; + } + return tok; + } + + static void fillFromPath(ProgramMemory& pm, const Token* top, MathLib::bigint path) + { + if (path < 1) + return; + visitAstNodes(top, [&](const Token* tok) { + const ValueFlow::Value* v = ValueFlow::findValue(tok->values(), nullptr, [&](const ValueFlow::Value& v) { + return v.path == path && isNonConditionalPossibleIntValue(v); + }); + if (v == nullptr) + return ChildrenToVisit::op1_and_op2; + pm.setValue(tok, *v); + return ChildrenToVisit::op1_and_op2; + }); + } + + void afterCondition(TokenList& tokenlist, + const SymbolDatabase& symboldatabase, + ErrorLogger& errorLogger, + const Settings& settings, + const std::set& skippedFunctions) const { + traverseCondition(symboldatabase, settings, skippedFunctions, [&](const Condition& cond, Token* condTok, const Scope* scope) { + Token* top = condTok->astTop(); + + const MathLib::bigint path = cond.getPath(); + const bool allowKnown = path == 0; + + std::list thenValues; + std::list elseValues; + + Token* ctx = cond.getContextAndValues(condTok, thenValues, elseValues); + + if (Token::Match(ctx->astParent(), "%oror%|&&")) { + Token* parent = ctx->astParent(); + if (astIsRHS(ctx) && astIsLHS(parent) && parent->astParent() && + parent->str() == parent->astParent()->str()) + parent = parent->astParent(); + else if (!astIsLHS(ctx)) { + parent = nullptr; + } + if (parent) { + std::vector nextExprs = {parent->astOperand2()}; + if (astIsLHS(parent) && parent->astParent() && parent->astParent()->str() == parent->str()) { + nextExprs.push_back(parent->astParent()->astOperand2()); + } + std::list andValues; + std::list orValues; + cond.getContextAndValues(condTok, andValues, orValues, true); + + const std::string& op(parent->str()); + std::list values; + if (op == "&&") + values = std::move(andValues); + else if (op == "||") + values = std::move(orValues); + if (allowKnown && (Token::Match(condTok, "==|!=") || cond.isBool())) + changePossibleToKnown(values); + if (astIsFloat(cond.vartok, false) || + (!cond.vartok->valueType() && + std::all_of(values.cbegin(), values.cend(), [](const ValueFlow::Value& v) { + return v.isIntValue() || v.isFloatValue(); + }))) + values.remove_if([&](const ValueFlow::Value& v) { + return v.isImpossible(); + }); + for (Token* start:nextExprs) { + Analyzer::Result r = forward(start, cond.vartok, values, tokenlist, errorLogger, settings); + if (r.terminate != Analyzer::Terminate::None || r.action.isModified()) + return; + } + } + } + + { + const Token* tok2 = condTok; + std::string op; + bool mixedOperators = false; + while (tok2->astParent()) { + const Token* parent = tok2->astParent(); + if (Token::Match(parent, "%oror%|&&")) { + if (op.empty()) { + op = parent->str(); + } else if (op != parent->str()) { + mixedOperators = true; + break; + } + } + if (parent->str() == "!") { + op = (op == "&&" ? "||" : "&&"); + } + tok2 = parent; + } + + if (mixedOperators) { + return; + } + } + + if (!top) + return; + + if (top->previous()->isExpandedMacro()) { + for (std::list* values : {&thenValues, &elseValues}) { + for (ValueFlow::Value& v : *values) + v.macro = true; + } + } + + Token* condTop = ctx->astParent(); + { + bool inverted2 = false; + while (Token::Match(condTop, "%oror%|&&")) { + Token* parent = skipNotAndCasts(condTop, &inverted2)->astParent(); + if (!parent) + break; + condTop = parent; + } + if (inverted2) + std::swap(thenValues, elseValues); + } + + if (!condTop) + return; + + if (Token::simpleMatch(condTop, "?")) { + Token* colon = condTop->astOperand2(); + forward(colon->astOperand1(), cond.vartok, thenValues, tokenlist, errorLogger, settings); + forward(colon->astOperand2(), cond.vartok, elseValues, tokenlist, errorLogger, settings); + // TODO: Handle after condition + return; + } + + if (condTop != top && condTop->str() != ";") + return; + + if (!Token::Match(top->previous(), "if|while|for (")) + return; + + if (top->previous()->str() == "for") { + if (!Token::Match(condTok, "%comp%")) + return; + if (!Token::simpleMatch(condTok->astParent(), ";")) + return; + const Token* stepTok = getStepTok(top); + if (cond.vartok->varId() == 0) + return; + if (!cond.vartok->variable()) + return; + if (!Token::Match(stepTok, "++|--")) + return; + std::set bounds; + for (const ValueFlow::Value& v : thenValues) { + if (v.bound != ValueFlow::Value::Bound::Point && v.isImpossible()) + continue; + bounds.insert(v.bound); + } + if (Token::simpleMatch(stepTok, "++") && bounds.count(ValueFlow::Value::Bound::Lower) > 0) + return; + if (Token::simpleMatch(stepTok, "--") && bounds.count(ValueFlow::Value::Bound::Upper) > 0) + return; + const Token* childTok = condTok->astOperand1(); + if (!childTok) + childTok = condTok->astOperand2(); + if (!childTok) + return; + if (childTok->varId() != cond.vartok->varId()) + return; + const Token* startBlock = top->link()->next(); + if (isVariableChanged(startBlock, + startBlock->link(), + cond.vartok->varId(), + cond.vartok->variable()->isGlobal(), + &settings)) + return; + // Check if condition in for loop is always false + const Token* initTok = getInitTok(top); + ProgramMemory pm; + fillFromPath(pm, initTok, path); + fillFromPath(pm, condTok, path); + execute(initTok, pm, nullptr, nullptr, nullptr); + MathLib::bigint result = 1; + execute(condTok, pm, &result, nullptr, nullptr); + if (result == 0) + return; + // Remove condition since for condition is not redundant + for (std::list* values : {&thenValues, &elseValues}) { + for (ValueFlow::Value& v : *values) { + v.condition = nullptr; + v.conditional = true; + } + } + } + + bool deadBranch[] = {false, false}; + // start token of conditional code + Token* startTokens[] = {nullptr, nullptr}; + // determine startToken(s) + if (Token::simpleMatch(top->link(), ") {")) + startTokens[0] = top->link()->next(); + if (Token::simpleMatch(top->link()->linkAt(1), "} else {")) + startTokens[1] = top->link()->linkAt(1)->tokAt(2); + + int changeBlock = -1; + int bailBlock = -1; + + for (int i = 0; i < 2; i++) { + const Token* const startToken = startTokens[i]; + if (!startToken) + continue; + std::list& values = (i == 0 ? thenValues : elseValues); + if (allowKnown) + valueFlowSetConditionToKnown(condTok, values, i == 0); + + Analyzer::Result r = forward(startTokens[i], startTokens[i]->link(), cond.vartok, values, tokenlist, errorLogger, settings); + deadBranch[i] = r.terminate == Analyzer::Terminate::Escape; + if (r.action.isModified() && !deadBranch[i]) + changeBlock = i; + if (r.terminate != Analyzer::Terminate::None && r.terminate != Analyzer::Terminate::Escape && + r.terminate != Analyzer::Terminate::Modified) + bailBlock = i; + changeKnownToPossible(values); + } + if (changeBlock >= 0 && !Token::simpleMatch(top->previous(), "while (")) { + if (settings.debugwarnings) + bailout(tokenlist, + errorLogger, + startTokens[changeBlock]->link(), + "valueFlowAfterCondition: " + cond.vartok->expressionString() + + " is changed in conditional block"); + return; + } + if (bailBlock >= 0) { + if (settings.debugwarnings) + bailout(tokenlist, + errorLogger, + startTokens[bailBlock]->link(), + "valueFlowAfterCondition: bailing in conditional block"); + return; + } + + // After conditional code.. + if (Token::simpleMatch(top->link(), ") {")) { + Token* after = top->link()->linkAt(1); + bool dead_if = deadBranch[0]; + bool dead_else = deadBranch[1]; + const Token* unknownFunction = nullptr; + if (condTok->astParent() && Token::Match(top->previous(), "while|for (")) + dead_if = !isBreakScope(after); + else if (!dead_if) + dead_if = isReturnScope(after, settings.library, &unknownFunction); + + if (!dead_if && unknownFunction) { + if (settings.debugwarnings) + bailout(tokenlist, errorLogger, unknownFunction, "possible noreturn scope"); + return; + } + + if (Token::simpleMatch(after, "} else {")) { + after = after->linkAt(2); + unknownFunction = nullptr; + if (!dead_else) + dead_else = isReturnScope(after, settings.library, &unknownFunction); + if (!dead_else && unknownFunction) { + if (settings.debugwarnings) + bailout(tokenlist, errorLogger, unknownFunction, "possible noreturn scope"); + return; + } + } + + if (dead_if && dead_else) + return; + + std::list values; + if (dead_if) { + values = std::move(elseValues); + } else if (dead_else) { + values = std::move(thenValues); + } else { + std::copy_if(thenValues.cbegin(), + thenValues.cend(), + std::back_inserter(values), + std::mem_fn(&ValueFlow::Value::isPossible)); + std::copy_if(elseValues.cbegin(), + elseValues.cend(), + std::back_inserter(values), + std::mem_fn(&ValueFlow::Value::isPossible)); + } + + if (values.empty()) + return; + + if (dead_if || dead_else) { + const Token* parent = condTok->astParent(); + // Skip the not operator + while (Token::simpleMatch(parent, "!")) + parent = parent->astParent(); + bool possible = false; + if (Token::Match(parent, "&&|%oror%")) { + const std::string& op(parent->str()); + while (parent && parent->str() == op) + parent = parent->astParent(); + if (Token::simpleMatch(parent, "!") || Token::simpleMatch(parent, "== false")) + possible = op == "||"; + else + possible = op == "&&"; + } + if (possible) { + values.remove_if(std::mem_fn(&ValueFlow::Value::isImpossible)); + changeKnownToPossible(values); + } else if (allowKnown) { + valueFlowSetConditionToKnown(condTok, values, true); + valueFlowSetConditionToKnown(condTok, values, false); + } + } + if (values.empty()) + return; + const bool isKnown = std::any_of(values.cbegin(), values.cend(), [&](const ValueFlow::Value& v) { + return v.isKnown() || v.isImpossible(); + }); + if (isKnown && isBreakOrContinueScope(after)) { + const Scope* loopScope = getLoopScope(cond.vartok); + if (loopScope) { + Analyzer::Result r = forward(after, loopScope->bodyEnd, cond.vartok, values, tokenlist, errorLogger, settings); + if (r.terminate != Analyzer::Terminate::None) + return; + if (r.action.isModified()) + return; + auto* start = const_cast(loopScope->bodyEnd); + if (Token::simpleMatch(start, "} while (")) { + start = start->tokAt(2); + forward(start, start->link(), cond.vartok, values, tokenlist, errorLogger, settings); + start = start->link(); + } + values.remove_if(std::mem_fn(&ValueFlow::Value::isImpossible)); + changeKnownToPossible(values); + } + } + forward(after, ValueFlow::getEndOfExprScope(cond.vartok, scope), cond.vartok, values, tokenlist, errorLogger, settings); + } + }); + } + virtual ~ConditionHandler() = default; + ConditionHandler(const ConditionHandler&) = default; +protected: + ConditionHandler() = default; +}; + +static void valueFlowCondition(const ValuePtr& handler, + TokenList& tokenlist, + SymbolDatabase& symboldatabase, + ErrorLogger& errorLogger, + const Settings& settings, + const std::set& skippedFunctions) +{ + handler->beforeCondition(tokenlist, symboldatabase, errorLogger, settings, skippedFunctions); + handler->afterCondition(tokenlist, symboldatabase, errorLogger, settings, skippedFunctions); +} + +struct SimpleConditionHandler : ConditionHandler { + std::vector parse(const Token* tok, const Settings& /*settings*/) const override { + + std::vector conds; + parseCompareEachInt(tok, [&](const Token* vartok, ValueFlow::Value true_value, ValueFlow::Value false_value) { + if (vartok->hasKnownIntValue()) + return; + if (vartok->str() == "=" && vartok->astOperand1() && vartok->astOperand2()) + vartok = vartok->astOperand1(); + Condition cond; + cond.true_values.push_back(std::move(true_value)); + cond.false_values.push_back(std::move(false_value)); + cond.vartok = vartok; + conds.push_back(std::move(cond)); + }); + if (!conds.empty()) + return conds; + + const Token* vartok = nullptr; + + if (tok->str() == "!") { + vartok = tok->astOperand1(); + + } else if (tok->astParent() && (Token::Match(tok->astParent(), "%oror%|&&|?") || + Token::Match(tok->astParent()->previous(), "if|while ("))) { + if (Token::simpleMatch(tok, "=")) + vartok = tok->astOperand1(); + else if (!Token::Match(tok, "%comp%|%assign%")) + vartok = tok; + } + + if (!vartok) + return {}; + Condition cond; + cond.true_values.emplace_back(tok, 0LL); + cond.false_values.emplace_back(tok, 0LL); + cond.vartok = vartok; + + return {std::move(cond)}; + } +}; + +struct IntegralInferModel : InferModel { + bool match(const ValueFlow::Value& value) const override { + return value.isIntValue(); + } + ValueFlow::Value yield(MathLib::bigint value) const override + { + ValueFlow::Value result(value); + result.valueType = ValueFlow::Value::ValueType::INT; + result.setKnown(); + return result; + } +}; + +ValuePtr ValueFlow::makeIntegralInferModel() { + return IntegralInferModel{}; +} + +static ValueFlow::Value inferCondition(const std::string& op, const Token* varTok, MathLib::bigint val) +{ + if (!varTok) + return ValueFlow::Value{}; + if (varTok->hasKnownIntValue()) + return ValueFlow::Value{}; + std::vector r = infer(IntegralInferModel{}, op, varTok->values(), val); + if (r.size() == 1 && r.front().isKnown()) + return r.front(); + return ValueFlow::Value{}; +} + +struct IteratorInferModel : InferModel { + virtual ValueFlow::Value::ValueType getType() const = 0; + bool match(const ValueFlow::Value& value) const override { + return value.valueType == getType(); + } + ValueFlow::Value yield(MathLib::bigint value) const override + { + ValueFlow::Value result(value); + result.valueType = getType(); + result.setKnown(); + return result; + } +}; + +struct EndIteratorInferModel : IteratorInferModel { + ValueFlow::Value::ValueType getType() const override { + return ValueFlow::Value::ValueType::ITERATOR_END; + } +}; + +struct StartIteratorInferModel : IteratorInferModel { + ValueFlow::Value::ValueType getType() const override { + return ValueFlow::Value::ValueType::ITERATOR_END; + } +}; + +static bool isIntegralOnlyOperator(const Token* tok) { + return Token::Match(tok, "%|<<|>>|&|^|~|%or%"); +} + +static bool isIntegralOrPointer(const Token* tok) +{ + if (!tok) + return false; + if (astIsIntegral(tok, false)) + return true; + if (astIsPointer(tok)) + return true; + if (Token::Match(tok, "NULL|nullptr")) + return true; + if (tok->valueType()) + return false; + // These operators only work on integers + if (isIntegralOnlyOperator(tok)) + return true; + if (isIntegralOnlyOperator(tok->astParent())) + return true; + if (Token::Match(tok, "+|-|*|/") && tok->isBinaryOp()) + return isIntegralOrPointer(tok->astOperand1()) && isIntegralOrPointer(tok->astOperand2()); + return false; +} + +static void valueFlowInferCondition(TokenList& tokenlist, + const Settings& settings) +{ + for (Token* tok = tokenlist.front(); tok; tok = tok->next()) { + if (!tok->astParent()) + continue; + if (tok->hasKnownIntValue()) + continue; + if (Token::Match(tok, "%comp%|-") && tok->astOperand1() && tok->astOperand2()) { + if (astIsIterator(tok->astOperand1()) || astIsIterator(tok->astOperand2())) { + static const std::array, 2> iteratorModels = {EndIteratorInferModel{}, + StartIteratorInferModel{}}; + for (const ValuePtr& model : iteratorModels) { + std::vector result = + infer(model, tok->str(), tok->astOperand1()->values(), tok->astOperand2()->values()); + for (ValueFlow::Value value : result) { + value.valueType = ValueFlow::Value::ValueType::INT; + setTokenValue(tok, std::move(value), settings); + } + } + } else if (isIntegralOrPointer(tok->astOperand1()) && isIntegralOrPointer(tok->astOperand2())) { + std::vector result = + infer(IntegralInferModel{}, tok->str(), tok->astOperand1()->values(), tok->astOperand2()->values()); + for (ValueFlow::Value& value : result) { + setTokenValue(tok, std::move(value), settings); + } + } + } else if (Token::Match(tok->astParent(), "?|&&|!|%oror%") || + Token::Match(tok->astParent()->previous(), "if|while (") || + (astIsPointer(tok) && isUsedAsBool(tok, &settings))) { + std::vector result = infer(IntegralInferModel{}, "!=", tok->values(), 0); + if (result.size() != 1) + continue; + ValueFlow::Value value = result.front(); + setTokenValue(tok, std::move(value), settings); + } + } +} + +struct SymbolicConditionHandler : SimpleConditionHandler { + + static bool isNegatedBool(const Token* tok) + { + if (!Token::simpleMatch(tok, "!")) + return false; + return (astIsBool(tok->astOperand1())); + } + + static const Token* skipNot(const Token* tok) + { + if (!Token::simpleMatch(tok, "!")) + return tok; + return tok->astOperand1(); + } + + std::vector parse(const Token* tok, const Settings& settings) const override + { + if (!Token::Match(tok, "%comp%")) + return {}; + if (tok->hasKnownIntValue()) + return {}; + if (!tok->astOperand1() || tok->astOperand1()->hasKnownIntValue() || tok->astOperand1()->isLiteral()) + return {}; + if (!tok->astOperand2() || tok->astOperand2()->hasKnownIntValue() || tok->astOperand2()->isLiteral()) + return {}; + if (!isConstExpression(tok, settings.library)) + return {}; + + std::vector result; + auto addCond = [&](const Token* lhsTok, const Token* rhsTok, bool inverted) { + for (int i = 0; i < 2; i++) { + const bool lhs = i == 0; + const Token* vartok = lhs ? lhsTok : rhsTok; + const Token* valuetok = lhs ? rhsTok : lhsTok; + if (valuetok->exprId() == 0) + continue; + if (valuetok->hasKnownSymbolicValue(vartok)) + continue; + if (vartok->hasKnownSymbolicValue(valuetok)) + continue; + ValueFlow::Value true_value; + ValueFlow::Value false_value; + setConditionalValues(tok, !lhs, 0, true_value, false_value); + setSymbolic(true_value, valuetok); + setSymbolic(false_value, valuetok); + + Condition cond; + cond.true_values = {std::move(true_value)}; + cond.false_values = {std::move(false_value)}; + cond.vartok = vartok; + cond.inverted = inverted; + result.push_back(std::move(cond)); + } + }; + addCond(tok->astOperand1(), tok->astOperand2(), false); + if (Token::Match(tok, "==|!=") && (isNegatedBool(tok->astOperand1()) || isNegatedBool(tok->astOperand2()))) { + const Token* lhsTok = skipNot(tok->astOperand1()); + const Token* rhsTok = skipNot(tok->astOperand2()); + addCond(lhsTok, rhsTok, !(isNegatedBool(tok->astOperand1()) && isNegatedBool(tok->astOperand2()))); + } + return result; + } +}; + +static bool valueFlowForLoop2(const Token *tok, + ProgramMemory *memory1, + ProgramMemory *memory2, + ProgramMemory *memoryAfter) +{ + // for ( firstExpression ; secondExpression ; thirdExpression ) + const Token *firstExpression = tok->next()->astOperand2()->astOperand1(); + const Token *secondExpression = tok->next()->astOperand2()->astOperand2()->astOperand1(); + const Token *thirdExpression = tok->next()->astOperand2()->astOperand2()->astOperand2(); + + ProgramMemory programMemory; + MathLib::bigint result(0); + bool error = false; + execute(firstExpression, programMemory, &result, &error, nullptr); + if (error) + return false; + execute(secondExpression, programMemory, &result, &error, nullptr); + if (result == 0) // 2nd expression is false => no looping + return false; + if (error) { + // If a variable is reassigned in second expression, return false + bool reassign = false; + visitAstNodes(secondExpression, + [&](const Token *t) { + if (t->str() == "=" && t->astOperand1() && programMemory.hasValue(t->astOperand1()->varId())) + // TODO: investigate what variable is assigned. + reassign = true; + return reassign ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; + }); + if (reassign) + return false; + } + + ProgramMemory startMemory(programMemory); + ProgramMemory endMemory; + + int maxcount = 10000; + while (result != 0 && !error && --maxcount > 0) { + endMemory = programMemory; + execute(thirdExpression, programMemory, &result, &error, nullptr); + if (!error) + execute(secondExpression, programMemory, &result, &error, nullptr); + } + + if (memory1) + memory1->swap(startMemory); + if (!error) { + if (memory2) + memory2->swap(endMemory); + if (memoryAfter) + memoryAfter->swap(programMemory); + } + + return true; +} + +static void valueFlowForLoopSimplify(Token* const bodyStart, + const Token* expr, + bool globalvar, + const MathLib::bigint value, + const TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings) +{ + // TODO: Refactor this to use arbitrary expressions + assert(expr->varId() > 0); + const Token * const bodyEnd = bodyStart->link(); + + // Is variable modified inside for loop + if (isVariableChanged(bodyStart, bodyEnd, expr->varId(), globalvar, &settings)) + return; + + for (Token *tok2 = bodyStart->next(); tok2 != bodyEnd; tok2 = tok2->next()) { + if (tok2->varId() == expr->varId()) { + const Token * parent = tok2->astParent(); + while (parent) { + const Token * const p = parent; + parent = parent->astParent(); + if (!parent || parent->str() == ":") + break; + if (parent->str() == "?") { + if (parent->astOperand2() != p) + parent = nullptr; + break; + } + } + if (parent) { + if (settings.debugwarnings) + bailout(tokenlist, errorLogger, tok2, "For loop variable " + tok2->str() + " stopping on ?"); + continue; + } + + ValueFlow::Value value1(value); + value1.varId = tok2->varId(); + setTokenValue(tok2, std::move(value1), settings); + } + + if (Token::Match(tok2, "%oror%|&&")) { + const ProgramMemory programMemory(getProgramMemory(tok2->astTop(), expr, ValueFlow::Value(value), settings)); + if ((tok2->str() == "&&" && !conditionIsTrue(tok2->astOperand1(), programMemory, &settings)) || + (tok2->str() == "||" && !conditionIsFalse(tok2->astOperand1(), programMemory, &settings))) { + // Skip second expression.. + Token *parent = tok2; + while (parent && parent->str() == tok2->str()) + parent = parent->astParent(); + // Jump to end of condition + if (parent && parent->str() == "(") { + tok2 = parent->link(); + // cast + if (Token::simpleMatch(tok2, ") (")) + tok2 = tok2->linkAt(1); + } + } + } + const Token* vartok = expr; + const Token* rml = nextAfterAstRightmostLeaf(vartok); + if (rml) + vartok = rml->str() == "]" ? rml : rml->previous(); + if (vartok->str() == "]" && vartok->link()->previous()) + vartok = vartok->link()->previous(); + + if ((tok2->str() == "&&" && + conditionIsFalse(tok2->astOperand1(), + getProgramMemory(tok2->astTop(), expr, ValueFlow::Value(value), settings), + &settings)) || + (tok2->str() == "||" && + conditionIsTrue(tok2->astOperand1(), + getProgramMemory(tok2->astTop(), expr, ValueFlow::Value(value), settings), + &settings))) + break; + + if (Token::simpleMatch(tok2, ") {")) { + if (vartok->varId() && Token::findmatch(tok2->link(), "%varid%", tok2, vartok->varId())) { + if (Token::findmatch(tok2, "continue|break|return", tok2->linkAt(1), vartok->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), vartok->varId())) { + if (settings.debugwarnings) + bailout(tokenlist, errorLogger, tok2, "For loop variable bailout on conditional continue|break|return"); + break; + } + tok2 = tok2->linkAt(2); + } + } + else { + if (settings.debugwarnings) + bailout(tokenlist, errorLogger, tok2, "For loop skipping {} code"); + tok2 = tok2->linkAt(1); + if (Token::simpleMatch(tok2, "} else {")) + tok2 = tok2->linkAt(2); + } + } + } +} + +static void valueFlowForLoopSimplifyAfter(Token* fortok, nonneg int varid, const MathLib::bigint num, const TokenList& tokenlist, ErrorLogger & errorLogger, const Settings& settings) +{ + const Token *vartok = nullptr; + for (const Token *tok = fortok; tok; tok = tok->next()) { + if (tok->varId() == varid) { + vartok = tok; + break; + } + } + if (!vartok || !vartok->variable()) + return; + + const Variable *var = vartok->variable(); + const Token *endToken = nullptr; + if (var->isLocal()) + endToken = var->scope()->bodyEnd; + else + endToken = fortok->scope()->bodyEnd; + + Token* blockTok = fortok->linkAt(1)->linkAt(1); + if (blockTok != endToken) { + ValueFlow::Value v{num}; + v.errorPath.emplace_back(fortok,"After for loop, " + var->name() + " has value " + v.infoString()); + + valueFlowForward(blockTok->next(), endToken, vartok, std::move(v), tokenlist, errorLogger, settings); + } +} + +static void valueFlowForLoop(TokenList &tokenlist, const SymbolDatabase& symboldatabase, ErrorLogger &errorLogger, const Settings &settings) +{ + for (const Scope &scope : symboldatabase.scopeList) { + if (scope.type != Scope::eFor) + continue; + + auto* tok = const_cast(scope.classDef); + auto* const bodyStart = const_cast(scope.bodyStart); + + if (!Token::simpleMatch(tok->next()->astOperand2(), ";") || + !Token::simpleMatch(tok->next()->astOperand2()->astOperand2(), ";")) + continue; + + nonneg int varid; + bool knownInitValue, partialCond; + MathLib::bigint initValue, stepValue, lastValue; + + if (extractForLoopValues(tok, varid, knownInitValue, initValue, partialCond, stepValue, lastValue)) { + const bool executeBody = !knownInitValue || initValue <= lastValue; + const Token* vartok = Token::findmatch(tok, "%varid%", bodyStart, varid); + if (executeBody && vartok) { + std::list initValues; + initValues.emplace_back(initValue, ValueFlow::Value::Bound::Lower); + initValues.push_back(ValueFlow::asImpossible(initValues.back())); + Analyzer::Result result = valueFlowForward(bodyStart, bodyStart->link(), vartok, std::move(initValues), tokenlist, errorLogger, settings); + + if (!result.action.isModified()) { + std::list lastValues; + lastValues.emplace_back(lastValue, ValueFlow::Value::Bound::Upper); + lastValues.back().conditional = true; + lastValues.push_back(ValueFlow::asImpossible(lastValues.back())); + if (stepValue != 1) + lastValues.pop_front(); + valueFlowForward(bodyStart, bodyStart->link(), vartok, std::move(lastValues), tokenlist, errorLogger, settings); + } + } + const MathLib::bigint afterValue = executeBody ? lastValue + stepValue : initValue; + valueFlowForLoopSimplifyAfter(tok, varid, afterValue, tokenlist, errorLogger, settings); + } else { + ProgramMemory mem1, mem2, memAfter; + if (valueFlowForLoop2(tok, &mem1, &mem2, &memAfter)) { + for (const auto& p : mem1) { + if (!p.second.isIntValue()) + continue; + if (p.second.isImpossible()) + continue; + if (p.first.tok->varId() == 0) + continue; + valueFlowForLoopSimplify(bodyStart, p.first.tok, false, p.second.intvalue, tokenlist, errorLogger, settings); + } + for (const auto& p : mem2) { + if (!p.second.isIntValue()) + continue; + if (p.second.isImpossible()) + continue; + if (p.first.tok->varId() == 0) + continue; + valueFlowForLoopSimplify(bodyStart, p.first.tok, false, p.second.intvalue, tokenlist, errorLogger, settings); + } + for (const auto& p : memAfter) { + if (!p.second.isIntValue()) + continue; + if (p.second.isImpossible()) + continue; + if (p.first.tok->varId() == 0) + continue; + valueFlowForLoopSimplifyAfter(tok, p.first.getExpressionId(), p.second.intvalue, tokenlist, errorLogger, settings); + } + } + } + } +} + +struct MultiValueFlowAnalyzer : ValueFlowAnalyzer { + std::unordered_map values; + std::unordered_map vars; + + MultiValueFlowAnalyzer(const std::unordered_map& args, const TokenList& t, const Settings& set) + : ValueFlowAnalyzer(t, set) { + for (const auto& p:args) { + values[p.first->declarationId()] = p.second; + vars[p.first->declarationId()] = p.first; + } + } + + virtual const std::unordered_map& getVars() const { + return vars; + } + + const ValueFlow::Value* getValue(const Token* tok) const override { + if (tok->varId() == 0) + return nullptr; + auto it = values.find(tok->varId()); + if (it == values.end()) + return nullptr; + return &it->second; + } + ValueFlow::Value* getValue(const Token* tok) override { + if (tok->varId() == 0) + return nullptr; + auto it = values.find(tok->varId()); + if (it == values.end()) + return nullptr; + return &it->second; + } + + void makeConditional() override { + for (auto&& p:values) { + p.second.conditional = true; + } + } + + void addErrorPath(const Token* tok, const std::string& s) override { + for (auto&& p:values) { + p.second.errorPath.emplace_back(tok, s); + } + } + + bool isAlias(const Token* tok, bool& inconclusive) const override { + const auto range = SelectValueFromVarIdMapRange(&values); + + for (const auto& p:getVars()) { + nonneg int const varid = p.first; + const Variable* var = p.second; + if (tok->varId() == varid) + return true; + if (isAliasOf(var, tok, varid, range, &inconclusive)) + return true; + } + return false; + } + + bool lowerToPossible() override { + for (auto&& p:values) { + if (p.second.isImpossible()) + return false; + p.second.changeKnownToPossible(); + } + return true; + } + bool lowerToInconclusive() override { + for (auto&& p:values) { + if (p.second.isImpossible()) + return false; + p.second.setInconclusive(); + } + return true; + } + + bool isConditional() const override { + for (auto&& p:values) { + if (p.second.conditional) + return true; + if (p.second.condition) + return !p.second.isImpossible(); + } + return false; + } + + bool stopOnCondition(const Token* condTok) const override { + if (isConditional()) + return true; + if (!condTok->hasKnownIntValue() && values.count(condTok->varId()) == 0) { + const auto& values_ = condTok->values(); + return std::any_of(values_.cbegin(), values_.cend(), [](const ValueFlow::Value& v) { + return v.isSymbolicValue() && Token::Match(v.tokvalue, "%oror%|&&"); + }); + } + return false; + } + + bool updateScope(const Token* endBlock, bool /*modified*/) const override { + const Scope* scope = endBlock->scope(); + if (!scope) + return false; + if (scope->type == Scope::eLambda) { + return std::all_of(values.cbegin(), values.cend(), [](const std::pair& p) { + return p.second.isLifetimeValue(); + }); + } + if (scope->type == Scope::eIf || scope->type == Scope::eElse || scope->type == Scope::eWhile || + scope->type == Scope::eFor) { + auto pred = [](const ValueFlow::Value& value) { + if (value.isKnown()) + return true; + if (value.isImpossible()) + return true; + if (value.isLifetimeValue()) + return true; + return false; + }; + if (std::all_of(values.cbegin(), values.cend(), std::bind(pred, std::bind(SelectMapValues{}, std::placeholders::_1)))) + return true; + if (isConditional()) + return false; + const Token* condTok = getCondTokFromEnd(endBlock); + std::set varids; + std::transform(getVars().cbegin(), getVars().cend(), std::inserter(varids, varids.begin()), SelectMapKeys{}); + return bifurcate(condTok, varids, getSettings()); + } + + return false; + } + + bool match(const Token* tok) const override { + return values.count(tok->varId()) > 0; + } + + ProgramState getProgramState() const override { + ProgramState ps; + for (const auto& p : values) { + const Variable* var = vars.at(p.first); + if (!var) + continue; + ps[var->nameToken()] = p.second; + } + return ps; + } +}; + +template +static bool productParams(const Settings& settings, const std::unordered_map>& vars, F f) +{ + using Args = std::vector>; + Args args(1); + // Compute cartesian product of all arguments + for (const auto& p:vars) { + if (p.second.empty()) + continue; + args.back()[p.first] = p.second.front(); + } + bool bail = false; + int max = settings.performanceValueFlowMaxSubFunctionArgs; + for (const auto& p:vars) { + if (args.size() > max) { + bail = true; + break; + } + if (p.second.empty()) + continue; + std::for_each(std::next(p.second.begin()), p.second.end(), [&](const ValueFlow::Value& value) { + Args new_args; + for (auto arg:args) { + if (value.path != 0) { + for (const auto& q:arg) { + if (q.first == p.first) + continue; + if (q.second.path == 0) + continue; + if (q.second.path != value.path) + return; + } + } + arg[p.first] = value; + new_args.push_back(std::move(arg)); + } + std::copy(new_args.cbegin(), new_args.cend(), std::back_inserter(args)); + }); + } + + if (args.size() > max) { + bail = true; + args.resize(max); + } + + for (const auto& arg:args) { + if (arg.empty()) + continue; + // Make sure all arguments are the same path + const MathLib::bigint path = arg.cbegin()->second.path; + if (std::any_of(arg.cbegin(), arg.cend(), [&](const std::pair& p) { + return p.second.path != path; + })) + continue; + f(arg); + } + return !bail; +} + +static void valueFlowInjectParameter(TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + const Scope* functionScope, + const std::unordered_map>& vars) +{ + const bool r = productParams(settings, vars, [&](const std::unordered_map& arg) { + MultiValueFlowAnalyzer a(arg, tokenlist, settings); + valueFlowGenericForward(const_cast(functionScope->bodyStart), functionScope->bodyEnd, a, tokenlist, errorLogger, settings); + }); + if (!r) { + std::string fname = ""; + if (const Function* f = functionScope->function) + fname = f->name(); + if (settings.debugwarnings) + bailout(tokenlist, errorLogger, functionScope->bodyStart, "Too many argument passed to " + fname); + } +} + +static void valueFlowInjectParameter(const TokenList& tokenlist, + ErrorLogger& errorLogger, + const Settings& settings, + const Variable* arg, + const Scope* functionScope, + const std::list& argvalues) +{ + // Is argument passed by value or const reference, and is it a known non-class type? + if (arg->isReference() && !arg->isConst() && !arg->isClass()) + return; + + // Set value in function scope.. + const nonneg int varid2 = arg->declarationId(); + if (!varid2) + return; + + valueFlowForward(const_cast(functionScope->bodyStart->next()), + functionScope->bodyEnd, + arg->nameToken(), + argvalues, + tokenlist, + errorLogger, + settings); +} + +static void valueFlowSwitchVariable(const TokenList &tokenlist, const SymbolDatabase& symboldatabase, ErrorLogger &errorLogger, const Settings &settings) +{ + for (const Scope &scope : symboldatabase.scopeList) { + if (scope.type != Scope::ScopeType::eSwitch) + continue; + if (!Token::Match(scope.classDef, "switch ( %var% ) {")) + continue; + const Token *vartok = scope.classDef->tokAt(2); + const Variable *var = vartok->variable(); + if (!var) + continue; + + // bailout: global non-const variables + if (!(var->isLocal() || var->isArgument()) && !var->isConst()) { + if (settings.debugwarnings) + bailout(tokenlist, errorLogger, vartok, "switch variable " + var->name() + " is global"); + continue; + } + + for (const Token *tok = scope.bodyStart->next(); tok != scope.bodyEnd; tok = tok->next()) { + if (tok->str() == "{") { + tok = tok->link(); + continue; + } + if (Token::Match(tok, "case %num% :")) { + std::list values; + values.emplace_back(MathLib::toBigNumber(tok->next()->str())); + values.back().condition = tok; + values.back().errorPath.emplace_back(tok, "case " + tok->next()->str() + ": " + vartok->str() + " is " + tok->next()->str() + " here."); + bool known = false; + if ((Token::simpleMatch(tok->previous(), "{") || Token::simpleMatch(tok->tokAt(-2), "break ;")) && !Token::Match(tok->tokAt(3), ";| case")) + known = true; + while (Token::Match(tok->tokAt(3), ";| case %num% :")) { + known = false; + tok = tok->tokAt(3); + if (!tok->isName()) + tok = tok->next(); + values.emplace_back(MathLib::toBigNumber(tok->next()->str())); + values.back().condition = tok; + values.back().errorPath.emplace_back(tok, "case " + tok->next()->str() + ": " + vartok->str() + " is " + tok->next()->str() + " here."); + } + for (std::list::const_iterator val = values.cbegin(); val != values.cend(); ++val) { + valueFlowReverse(tokenlist, + const_cast(scope.classDef), + vartok, + *val, + errorLogger, + settings); + } + if (vartok->variable()->scope()) { + if (known) + values.back().setKnown(); + + // FIXME We must check if there is a return. See #9276 + /* + valueFlowForwardVariable(tok->tokAt(3), + vartok->variable()->scope()->bodyEnd, + vartok->variable(), + vartok->varId(), + values, + values.back().isKnown(), + false, + tokenlist, + errorLogger, + settings); + */ + } + } + } + } +} + +static std::list getFunctionArgumentValues(const Token *argtok) +{ + std::list argvalues(argtok->values()); + removeImpossible(argvalues); + if (argvalues.empty() && Token::Match(argtok, "%comp%|%oror%|&&|!")) { + argvalues.emplace_back(0); + argvalues.emplace_back(1); + } + return argvalues; +} + +static void valueFlowLibraryFunction(Token *tok, const std::string &returnValue, const Settings &settings) +{ + std::unordered_map> argValues; + int argn = 1; + for (const Token *argtok : getArguments(tok->previous())) { + argValues[argn] = getFunctionArgumentValues(argtok); + argn++; + } + if (returnValue.find("arg") != std::string::npos && argValues.empty()) + return; + productParams(settings, argValues, [&](const std::unordered_map& arg) { + ValueFlow::Value value = evaluateLibraryFunction(arg, returnValue, &settings, tok->isCpp()); + if (value.isUninitValue()) + return; + ValueFlow::Value::ValueKind kind = ValueFlow::Value::ValueKind::Known; + for (auto&& p : arg) { + if (p.second.isPossible()) + kind = p.second.valueKind; + if (p.second.isInconclusive()) { + kind = p.second.valueKind; + break; + } + } + if (value.isImpossible() && kind != ValueFlow::Value::ValueKind::Known) + return; + if (!value.isImpossible()) + value.valueKind = kind; + setTokenValue(tok, std::move(value), settings); + }); +} + +template +struct IteratorRange +{ + Iterator mBegin; + Iterator mEnd; + + Iterator begin() const { + return mBegin; + } + + Iterator end() const { + return mEnd; + } +}; + +template +static IteratorRange MakeIteratorRange(Iterator start, Iterator last) +{ + return {start, last}; +} + +static void valueFlowSubFunction(TokenList& tokenlist, SymbolDatabase& symboldatabase, ErrorLogger& errorLogger, const Settings& settings) +{ + int id = 0; + for (const Scope* scope : MakeIteratorRange(symboldatabase.functionScopes.crbegin(), symboldatabase.functionScopes.crend())) { + const Function* function = scope->function; + if (!function) + continue; + for (Token* tok = const_cast(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { + if (tok->isKeyword() || !Token::Match(tok, "%name% (")) + continue; + + const Function * const calledFunction = tok->function(); + if (!calledFunction) { + // library function? + const std::string& returnValue(settings.library.returnValue(tok)); + if (!returnValue.empty()) + valueFlowLibraryFunction(tok->next(), returnValue, settings); + continue; + } + + const Scope * const calledFunctionScope = calledFunction->functionScope; + if (!calledFunctionScope) + continue; + + id++; + std::unordered_map> argvars; + // TODO: Rewrite this. It does not work well to inject 1 argument at a time. + const std::vector &callArguments = getArguments(tok); + for (int argnr = 0U; argnr < callArguments.size(); ++argnr) { + const Token *argtok = callArguments[argnr]; + // Get function argument + const Variable * const argvar = calledFunction->getArgumentVar(argnr); + if (!argvar) + break; + + // passing value(s) to function + std::list argvalues(getFunctionArgumentValues(argtok)); + + // Remove non-local lifetimes + argvalues.remove_if([](const ValueFlow::Value& v) { + if (v.isLifetimeValue()) + return !v.isLocalLifetimeValue() && !v.isSubFunctionLifetimeValue(); + return false; + }); + // Remove uninit values if argument is passed by value + if (argtok->variable() && !argtok->variable()->isPointer() && argvalues.size() == 1 && argvalues.front().isUninitValue()) { + if (CheckUninitVar::isVariableUsage(argtok, settings.library, false, CheckUninitVar::Alloc::NO_ALLOC, 0)) + continue; + } + + if (argvalues.empty()) + continue; + + // Error path.. + for (ValueFlow::Value &v : argvalues) { + const std::string nr = std::to_string(argnr + 1) + getOrdinalText(argnr + 1); + + v.errorPath.emplace_back(argtok, + "Calling function '" + + calledFunction->name() + + "', " + + nr + + " argument '" + + argtok->expressionString() + + "' value is " + + v.infoString()); + v.path = 256 * v.path + id % 256; + // Change scope of lifetime values + if (v.isLifetimeValue()) + v.lifetimeScope = ValueFlow::Value::LifetimeScope::SubFunction; + } + + // passed values are not "known".. + lowerToPossible(argvalues); + + argvars[argvar] = std::move(argvalues); + } + valueFlowInjectParameter(tokenlist, errorLogger, settings, calledFunctionScope, argvars); + } + } +} + +static void valueFlowFunctionDefaultParameter(const TokenList& tokenlist, const SymbolDatabase& symboldatabase, ErrorLogger& errorLogger, const Settings& settings) +{ + if (!tokenlist.isCPP()) + return; + + for (const Scope* scope : symboldatabase.functionScopes) { + const Function* function = scope->function; + if (!function) + continue; + for (std::size_t arg = function->minArgCount(); arg < function->argCount(); arg++) { + const Variable* var = function->getArgumentVar(arg); + if (var && var->hasDefault() && Token::Match(var->nameToken(), "%var% = %num%|%str% [,)]")) { + const std::list &values = var->nameToken()->tokAt(2)->values(); + std::list argvalues; + for (const ValueFlow::Value &value : values) { + ValueFlow::Value v(value); + v.defaultArg = true; + v.changeKnownToPossible(); + if (v.isPossible()) + argvalues.push_back(std::move(v)); + } + if (!argvalues.empty()) + valueFlowInjectParameter(tokenlist, errorLogger, settings, var, scope, argvalues); + } + } + } +} + +static const ValueFlow::Value* getKnownValueFromToken(const Token* tok) +{ + if (!tok) + return nullptr; + auto it = std::find_if(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& v) { + return (v.isIntValue() || v.isContainerSizeValue() || v.isFloatValue()) && v.isKnown(); + }); + if (it == tok->values().end()) + return nullptr; + return std::addressof(*it); +} + +static const ValueFlow::Value* getKnownValueFromTokens(const std::vector& toks) +{ + if (toks.empty()) + return nullptr; + const ValueFlow::Value* result = getKnownValueFromToken(toks.front()); + if (!result) + return nullptr; + if (!std::all_of(std::next(toks.begin()), toks.end(), [&](const Token* tok) { + return std::any_of(tok->values().begin(), tok->values().end(), [&](const ValueFlow::Value& v) { + return v.equalValue(*result) && v.valueKind == result->valueKind; + }); + })) + return nullptr; + return result; +} + +static void setFunctionReturnValue(const Function* f, Token* tok, ValueFlow::Value v, const Settings& settings) +{ + if (f->hasVirtualSpecifier()) { + if (v.isImpossible()) + return; + v.setPossible(); + } else if (!v.isImpossible()) { + v.setKnown(); + } + v.errorPath.emplace_back(tok, "Calling function '" + f->name() + "' returns " + v.toString()); + setTokenValue(tok, std::move(v), settings); +} + +static void valueFlowFunctionReturn(TokenList &tokenlist, ErrorLogger &errorLogger, const Settings& settings) +{ + for (Token *tok = tokenlist.back(); tok; tok = tok->previous()) { + if (tok->str() != "(" || !tok->astOperand1() || tok->isCast()) + continue; + + const Function* function = nullptr; + if (Token::Match(tok->previous(), "%name% (")) + function = tok->previous()->function(); + else + function = tok->astOperand1()->function(); + if (!function) + continue; + // TODO: Check if member variable is a pointer or reference + if (function->isImplicitlyVirtual() && !function->hasFinalSpecifier()) + continue; + + if (tok->hasKnownValue()) + continue; + + std::vector returns = Function::findReturns(function); + if (returns.empty()) + continue; + + if (const ValueFlow::Value* v = getKnownValueFromTokens(returns)) { + setFunctionReturnValue(function, tok, *v, settings); + continue; + } + + // Arguments.. + std::vector arguments = getArguments(tok); + + ProgramMemory programMemory; + for (std::size_t i = 0; i < arguments.size(); ++i) { + const Variable * const arg = function->getArgumentVar(i); + if (!arg) { + if (settings.debugwarnings) + bailout(tokenlist, errorLogger, tok, "function return; unhandled argument type"); + programMemory.clear(); + break; + } + const ValueFlow::Value* v = getKnownValueFromToken(arguments[i]); + if (!v) + continue; + programMemory.setValue(arg->nameToken(), *v); + } + if (programMemory.empty() && !arguments.empty()) + continue; + std::vector values = execute(function->functionScope, programMemory, settings); + for (const ValueFlow::Value& v : values) { + if (v.isUninitValue()) + continue; + setFunctionReturnValue(function, tok, v, settings); + } + } +} + +static bool needsInitialization(const Variable* var) +{ + if (!var) + return false; + if (var->hasDefault()) + return false; + if (var->isPointer()) + return true; + if (var->type() && var->type()->isUnionType()) + return false; + if (!var->nameToken()->isCpp()) + return true; + if (var->type() && var->type()->needInitialization == Type::NeedInitialization::True) + return true; + if (var->valueType()) { + if (var->valueType()->isPrimitive()) + return true; + if (var->valueType()->type == ValueType::Type::POD) + return true; + if (var->valueType()->type == ValueType::Type::ITERATOR) + return true; + } + return false; +} + +static void addToErrorPath(ValueFlow::Value& value, const ValueFlow::Value& from) +{ + std::unordered_set locations; + std::transform(value.errorPath.cbegin(), + value.errorPath.cend(), + std::inserter(locations, locations.begin()), + [](const ErrorPathItem& e) { + return e.first; + }); + if (from.condition && !value.condition) + value.condition = from.condition; + std::copy_if(from.errorPath.cbegin(), + from.errorPath.cend(), + std::back_inserter(value.errorPath), + [&](const ErrorPathItem& e) { + return locations.insert(e.first).second; + }); +} + +static std::vector findAllUsages(const Variable* var, + Token* start, // cppcheck-suppress constParameterPointer // FP + const Library& library) +{ + // std::vector result; + const Scope* scope = var->scope(); + if (!scope) + return {}; + return findTokensSkipDeadCode(library, start, scope->bodyEnd, [&](const Token* tok) { + return tok->varId() == var->declarationId(); + }); +} + +static Token* findStartToken(const Variable* var, Token* start, const Library& library) +{ + std::vector uses = findAllUsages(var, start, library); + if (uses.empty()) + return start; + Token* first = uses.front(); + if (Token::findmatch(start, "goto|asm|setjmp|longjmp", first)) + return start; + if (first != var->nameToken()) { + // if this is lhs in assignment then set first to the first token in LHS expression + Token* temp = first; + while (Token::Match(temp->astParent(), "[&*(]") && precedes(temp->astParent(), temp)) + temp = temp->astParent(); + if (Token::simpleMatch(temp->astParent(), "=") && precedes(temp, temp->astParent())) + first = temp; + } + // If there is only one usage + if (uses.size() == 1) + return first->previous(); + const Scope* scope = first->scope(); + // If first usage is in variable scope + if (scope == var->scope()) { + bool isLoopExpression = false; + for (const Token* parent = first; parent; parent = parent->astParent()) { + if (Token::simpleMatch(parent->astParent(), ";") && + Token::simpleMatch(parent->astParent()->astParent(), ";") && + Token::simpleMatch(parent->astParent()->astParent()->astParent(), "(") && + Token::simpleMatch(parent->astParent()->astParent()->astParent()->astOperand1(), "for (") && + parent == parent->astParent()->astParent()->astParent()->astOperand2()->astOperand2()->astOperand2()) { + isLoopExpression = true; + } + } + return isLoopExpression ? start : first->previous(); + } + // If all uses are in the same scope + if (std::all_of(uses.begin() + 1, uses.end(), [&](const Token* tok) { + return tok->scope() == scope; + })) + return first->previous(); + // Compute the outer scope + while (scope && scope->nestedIn != var->scope()) + scope = scope->nestedIn; + if (!scope) + return start; + Token* tok = const_cast(scope->bodyStart); + if (!tok) + return start; + if (Token::simpleMatch(tok->tokAt(-2), "} else {")) + tok = tok->linkAt(-2); + if (Token::simpleMatch(tok->previous(), ") {")) + return tok->linkAt(-1)->previous(); + return tok; +} + +static void valueFlowUninit(TokenList& tokenlist, ErrorLogger& errorLogger, const Settings& settings) +{ + for (Token *tok = tokenlist.front(); tok; tok = tok->next()) { + if (!tok->scope()->isExecutable()) + continue; + if (!Token::Match(tok, "%var% ;|[")) + continue; + const Variable* var = tok->variable(); + if (!var) + continue; + if (var->nameToken() != tok || var->isInit()) + continue; + if (!needsInitialization(var)) + continue; + if (!var->isLocal() || var->isStatic() || var->isExtern() || var->isReference() || var->isThrow()) + continue; + + ValueFlow::Value uninitValue; + uninitValue.setKnown(); + uninitValue.valueType = ValueFlow::Value::ValueType::UNINIT; + uninitValue.tokvalue = tok; + if (var->isArray()) + uninitValue.indirect = var->dimensions().size(); + + bool partial = false; + + Token* start = findStartToken(var, tok->next(), settings.library); + + std::map partialReads; + if (const Scope* scope = var->typeScope()) { + if (Token::findsimplematch(scope->bodyStart, "union", scope->bodyEnd)) + continue; + for (const Variable& memVar : scope->varlist) { + if (!memVar.isPublic()) + continue; + // Skip array since we can't track partial initialization from nested subexpressions + if (memVar.isArray()) + continue; + if (!needsInitialization(&memVar)) { + if (!var->isPointer()) + partial = true; + continue; + } + MemberExpressionAnalyzer analyzer(memVar.nameToken()->str(), tok, uninitValue, tokenlist, settings); + valueFlowGenericForward(start, tok->scope()->bodyEnd, analyzer, tokenlist, errorLogger, settings); + + for (auto&& p : *analyzer.partialReads) { + Token* tok2 = p.first; + const ValueFlow::Value& v = p.second; + // Try to insert into map + auto pp = partialReads.insert(std::make_pair(tok2, v)); + ValueFlow::Value& v2 = pp.first->second; + const bool inserted = pp.second; + // Merge the two values if it is already in map + if (!inserted) { + if (v.valueType != v2.valueType) + continue; + addToErrorPath(v2, v); + } + v2.subexpressions.push_back(memVar.nameToken()->str()); + } + } + } + + for (auto&& p : partialReads) { + Token* tok2 = p.first; + ValueFlow::Value& v = p.second; + + setTokenValue(tok2, std::move(v), settings); + } + + if (partial) + continue; + + valueFlowForward(start, tok->scope()->bodyEnd, var->nameToken(), std::move(uninitValue), tokenlist, errorLogger, settings); + } +} + +static bool isContainerSizeChanged(const Token* expr, + const Token* start, + const Token* end, + int indirect, + const Settings& settings, + int depth = 20); + +static bool isContainerSizeChangedByFunction(const Token* tok, + int indirect, + const Settings& settings, + int depth = 20) +{ + if (!tok->valueType()) + return false; + if (!astIsContainer(tok)) + return false; + // If we are accessing an element then we are not changing the container size + if (Token::Match(tok, "%name% . %name% (")) { + const Library::Container::Yield yield = getLibraryContainer(tok)->getYield(tok->strAt(2)); + if (yield != Library::Container::Yield::NO_YIELD) + return false; + } + if (Token::simpleMatch(tok->astParent(), "[")) + return false; + + // address of variable + const bool addressOf = tok->valueType()->pointer || (tok->astParent() && tok->astParent()->isUnaryOp("&")); + + int narg; + const Token * ftok = getTokenArgumentFunction(tok, narg); + if (!ftok) + return false; // not a function => variable not changed + const Function * fun = ftok->function(); + if (fun && !fun->isImplicitlyVirtual()) { + const Variable *arg = fun->getArgumentVar(narg); + if (arg) { + const bool isPointer = addressOf || indirect > 0; + if (!arg->isReference() && !isPointer) + return false; + if (!isPointer && arg->isConst()) + return false; + if (arg->valueType() && arg->valueType()->constness == 1) + return false; + const Scope * scope = fun->functionScope; + if (scope) { + // Argument not used + if (!arg->nameToken()) + return false; + if (depth > 0) + return isContainerSizeChanged(arg->nameToken(), + scope->bodyStart, + scope->bodyEnd, + addressOf ? indirect + 1 : indirect, + settings, + depth - 1); + } + // Don't know => Safe guess + return true; + } + } + + bool inconclusive = false; + const bool isChanged = isVariableChangedByFunctionCall(tok, indirect, &settings, &inconclusive); + return (isChanged || inconclusive); +} + +struct ContainerExpressionAnalyzer : ExpressionAnalyzer { + ContainerExpressionAnalyzer(const Token* expr, ValueFlow::Value val, const TokenList& t, const Settings& s) + : ExpressionAnalyzer(expr, std::move(val), t, s) + {} + + bool match(const Token* tok) const override { + return tok->exprId() == expr->exprId() || (astIsIterator(tok) && isAliasOf(tok, expr->exprId())); + } + + Action isWritable(const Token* tok, Direction /*d*/) const override + { + if (astIsIterator(tok)) + return Action::None; + if (!getValue(tok)) + return Action::None; + if (!tok->valueType()) + return Action::None; + if (!astIsContainer(tok)) + return Action::None; + const Token* parent = tok->astParent(); + const Library::Container* container = getLibraryContainer(tok); + + if (container->stdStringLike && Token::simpleMatch(parent, "+=") && astIsLHS(tok) && parent->astOperand2()) { + const Token* rhs = parent->astOperand2(); + if (rhs->tokType() == Token::eString) + return Action::Read | Action::Write | Action::Incremental; + const Library::Container* rhsContainer = getLibraryContainer(rhs); + if (rhsContainer && rhsContainer->stdStringLike) { + if (std::any_of(rhs->values().cbegin(), rhs->values().cend(), [&](const ValueFlow::Value &rhsval) { + return rhsval.isKnown() && rhsval.isContainerSizeValue(); + })) + return Action::Read | Action::Write | Action::Incremental; + } + } else if (astIsLHS(tok) && Token::Match(tok->astParent(), ". %name% (")) { + const Library::Container::Action action = container->getAction(tok->astParent()->strAt(1)); + if (action == Library::Container::Action::PUSH || action == Library::Container::Action::POP) { + std::vector args = getArguments(tok->tokAt(3)); + if (args.size() < 2) + return Action::Read | Action::Write | Action::Incremental; + } + } + return Action::None; + } + + void writeValue(ValueFlow::Value* val, const Token* tok, Direction d) const override { + if (!val) + return; + if (!tok->astParent()) + return; + if (!tok->valueType()) + return; + if (!astIsContainer(tok)) + return; + const Token* parent = tok->astParent(); + const Library::Container* container = getLibraryContainer(tok); + int n = 0; + + if (container->stdStringLike && Token::simpleMatch(parent, "+=") && parent->astOperand2()) { + const Token* rhs = parent->astOperand2(); + const Library::Container* rhsContainer = getLibraryContainer(rhs); + if (rhs->tokType() == Token::eString) + n = Token::getStrLength(rhs); + else if (rhsContainer && rhsContainer->stdStringLike) { + auto it = std::find_if(rhs->values().begin(), rhs->values().end(), [&](const ValueFlow::Value& rhsval) { + return rhsval.isKnown() && rhsval.isContainerSizeValue(); + }); + if (it != rhs->values().end()) + n = it->intvalue; + } + } else if (astIsLHS(tok) && Token::Match(tok->astParent(), ". %name% (")) { + const Library::Container::Action action = container->getAction(tok->astParent()->strAt(1)); + if (action == Library::Container::Action::PUSH) + n = 1; + if (action == Library::Container::Action::POP) + n = -1; + } + if (d == Direction::Reverse) + val->intvalue -= n; + else + val->intvalue += n; + } + + int getIndirect(const Token* tok) const override + { + if (tok->valueType()) { + return tok->valueType()->pointer; + } + return ValueFlowAnalyzer::getIndirect(tok); + } + + Action isModified(const Token* tok) const override { + Action read = Action::Read; + // An iterator won't change the container size + if (astIsIterator(tok)) + return read; + if (Token::Match(tok->astParent(), "%assign%") && astIsLHS(tok)) + return Action::Invalid; + if (isLikelyStreamRead(tok->astParent())) + return Action::Invalid; + if (astIsContainer(tok) && ValueFlow::isContainerSizeChanged(tok, getIndirect(tok), getSettings())) + return read | Action::Invalid; + return read; + } +}; + +static const Token* parseBinaryIntOp(const Token* expr, + const std::function(const Token*)>& eval, + MathLib::bigint& known) +{ + if (!expr) + return nullptr; + if (!expr->astOperand1() || !expr->astOperand2()) + return nullptr; + if (expr->astOperand1()->exprId() == 0 && expr->astOperand2()->exprId() == 0) + return nullptr; + std::vector x1 = eval(expr->astOperand1()); + std::vector x2 = eval(expr->astOperand2()); + if (expr->astOperand1()->exprId() == 0 && x1.empty()) + return nullptr; + if (expr->astOperand2()->exprId() == 0 && x2.empty()) + return nullptr; + const Token* varTok = nullptr; + if (!x1.empty() && x2.empty()) { + varTok = expr->astOperand2(); + known = x1.front(); + } else if (x1.empty() && !x2.empty()) { + varTok = expr->astOperand1(); + known = x2.front(); + } + return varTok; +} + +const Token* ValueFlow::solveExprValue(const Token* expr, + const std::function(const Token*)>& eval, + ValueFlow::Value& value) +{ + if (!value.isIntValue() && !value.isIteratorValue() && !value.isSymbolicValue()) + return expr; + if (value.isSymbolicValue() && !Token::Match(expr, "+|-")) + return expr; + MathLib::bigint intval; + const Token* binaryTok = parseBinaryIntOp(expr, eval, intval); + const bool rhs = astIsRHS(binaryTok); + // If its on the rhs, then -1 multiplication is needed, which is not possible with simple delta analysis used currently for symbolic values + if (value.isSymbolicValue() && rhs && Token::simpleMatch(expr, "-")) + return expr; + if (binaryTok && expr->str().size() == 1) { + switch (expr->str()[0]) { + case '+': { + value.intvalue -= intval; + return ValueFlow::solveExprValue(binaryTok, eval, value); + } + case '-': { + if (rhs) + value.intvalue = intval - value.intvalue; + else + value.intvalue += intval; + return ValueFlow::solveExprValue(binaryTok, eval, value); + } + case '*': { + if (intval == 0) + break; + value.intvalue /= intval; + return ValueFlow::solveExprValue(binaryTok, eval, value); + } + case '^': { + value.intvalue ^= intval; + return ValueFlow::solveExprValue(binaryTok, eval, value); + } + } + } + return expr; +} + +static const Token* solveExprValue(const Token* expr, ValueFlow::Value& value) +{ + return ValueFlow::solveExprValue( + expr, + [](const Token* tok) -> std::vector { + if (tok->hasKnownIntValue()) + return {tok->values().front().intvalue}; + return {}; + }, + value); +} + +static ValuePtr makeAnalyzer(const Token* exprTok, ValueFlow::Value value, const TokenList& tokenlist, const Settings& settings) +{ + if (value.isContainerSizeValue()) + return ContainerExpressionAnalyzer(exprTok, std::move(value), tokenlist, settings); + const Token* expr = solveExprValue(exprTok, value); + return ExpressionAnalyzer(expr, std::move(value), tokenlist, settings); +} + +static ValuePtr makeReverseAnalyzer(const Token* exprTok, ValueFlow::Value value, const TokenList& tokenlist, const Settings& settings) +{ + if (value.isContainerSizeValue()) + return ContainerExpressionAnalyzer(exprTok, std::move(value), tokenlist, settings); + return ExpressionAnalyzer(exprTok, std::move(value), tokenlist, settings); +} + +bool ValueFlow::isContainerSizeChanged(const Token* tok, int indirect, const Settings& settings, int depth) +{ + if (!tok) + return false; + if (!tok->valueType() || !tok->valueType()->container) + return true; + if (astIsLHS(tok) && Token::Match(tok->astParent(), "%assign%|<<")) + return true; + if (astIsLHS(tok) && Token::simpleMatch(tok->astParent(), "[")) + return tok->valueType()->container->stdAssociativeLike; + const Library::Container::Action action = astContainerAction(tok); + switch (action) { + case Library::Container::Action::RESIZE: + case Library::Container::Action::CLEAR: + case Library::Container::Action::PUSH: + case Library::Container::Action::POP: + case Library::Container::Action::CHANGE: + case Library::Container::Action::INSERT: + case Library::Container::Action::ERASE: + return true; + case Library::Container::Action::NO_ACTION: + // Is this an unknown member function call? + if (astIsLHS(tok) && Token::Match(tok->astParent(), ". %name% (")) { + const Library::Container::Yield yield = astContainerYield(tok); + return yield == Library::Container::Yield::NO_YIELD; + } + break; + case Library::Container::Action::FIND: + case Library::Container::Action::FIND_CONST: + case Library::Container::Action::CHANGE_CONTENT: + case Library::Container::Action::CHANGE_INTERNAL: + break; + } + return isContainerSizeChangedByFunction(tok, indirect, settings, depth); +} + +static bool isContainerSizeChanged(const Token* expr, + const Token* start, + const Token* end, + int indirect, + const Settings& settings, + int depth) +{ + for (const Token *tok = start; tok != end; tok = tok->next()) { + if (tok->exprId() != expr->exprId() && !isAliasOf(tok, expr)) + continue; + if (ValueFlow::isContainerSizeChanged(tok, indirect, settings, depth)) + return true; + } + return false; +} + +static void valueFlowSmartPointer(TokenList &tokenlist, ErrorLogger & errorLogger, const Settings &settings) +{ + for (Token *tok = tokenlist.front(); tok; tok = tok->next()) { + if (!tok->scope()) + continue; + if (!tok->scope()->isExecutable()) + continue; + if (!astIsSmartPointer(tok)) + continue; + if (tok->variable() && Token::Match(tok, "%var% (|{|;")) { + const Variable* var = tok->variable(); + if (!var->isSmartPointer()) + continue; + if (var->nameToken() == tok) { + if (Token::Match(tok, "%var% (|{") && tok->next()->astOperand2() && + tok->next()->astOperand2()->str() != ",") { + Token* inTok = tok->next()->astOperand2(); + const std::list& values = inTok->values(); + const bool constValue = inTok->isNumber(); + valueFlowForwardAssign(inTok, var, values, constValue, true, tokenlist, errorLogger, settings); + + } else if (Token::Match(tok, "%var% ;")) { + ValueFlow::Value v(0); + v.setKnown(); + valueFlowForwardAssign(tok, var, {std::move(v)}, false, true, tokenlist, errorLogger, settings); + } + } + } else if (astIsLHS(tok) && Token::Match(tok->astParent(), ". %name% (") && + tok->astParent()->originalName() != "->") { + std::vector vars = getVariables(tok); + Token* ftok = tok->astParent()->tokAt(2); + if (Token::simpleMatch(tok->astParent(), ". reset (")) { + if (Token::simpleMatch(ftok, "( )")) { + ValueFlow::Value v(0); + v.setKnown(); + valueFlowForwardAssign(ftok, tok, std::move(vars), {std::move(v)}, false, tokenlist, errorLogger, settings); + } else { + tok->removeValues(std::mem_fn(&ValueFlow::Value::isIntValue)); + Token* inTok = ftok->astOperand2(); + if (!inTok) + continue; + const std::list& values = inTok->values(); + valueFlowForwardAssign(inTok, tok, std::move(vars), values, false, tokenlist, errorLogger, settings); + } + } else if (Token::simpleMatch(tok->astParent(), ". release ( )")) { + const Token* parent = ftok->astParent(); + bool hasParentReset = false; + while (parent) { + if (Token::Match(parent->tokAt(-2), ". release|reset (") && + parent->tokAt(-2)->astOperand1()->exprId() == tok->exprId()) { + hasParentReset = true; + break; + } + parent = parent->astParent(); + } + if (hasParentReset) + continue; + ValueFlow::Value v(0); + v.setKnown(); + valueFlowForwardAssign(ftok, tok, std::move(vars), {std::move(v)}, false, tokenlist, errorLogger, settings); + } else if (Token::simpleMatch(tok->astParent(), ". get ( )")) { + ValueFlow::Value v = makeSymbolic(tok); + setTokenValue(tok->astParent()->tokAt(2), std::move(v), settings); + } + } else if (Token::Match(tok->previous(), "%name%|> (|{") && astIsSmartPointer(tok) && + astIsSmartPointer(tok->astOperand1())) { + std::vector args = getArguments(tok); + if (args.empty()) + continue; + for (const ValueFlow::Value& v : args.front()->values()) + setTokenValue(tok, v, settings); + } + } +} + +static Library::Container::Yield findIteratorYield(Token* tok, const Token** ftok, const Settings &settings) +{ + auto yield = astContainerYield(tok, ftok); + if (ftok && *ftok) + return yield; + + if (!tok->astParent()) + return yield; + + //begin/end free functions + return astFunctionYield(tok->astParent()->previous(), settings, ftok); +} + +static void valueFlowIterators(TokenList &tokenlist, const Settings &settings) +{ + for (Token *tok = tokenlist.front(); tok; tok = tok->next()) { + if (!tok->scope()) + continue; + if (!tok->scope()->isExecutable()) + continue; + if (!astIsContainer(tok)) + continue; + Token* ftok = nullptr; + const Library::Container::Yield yield = findIteratorYield(tok, const_cast(&ftok), settings); + if (ftok) { + ValueFlow::Value v(0); + v.setKnown(); + if (yield == Library::Container::Yield::START_ITERATOR) { + v.valueType = ValueFlow::Value::ValueType::ITERATOR_START; + setTokenValue(ftok->next(), std::move(v), settings); + } else if (yield == Library::Container::Yield::END_ITERATOR) { + v.valueType = ValueFlow::Value::ValueType::ITERATOR_END; + setTokenValue(ftok->next(), std::move(v), settings); + } + } + } +} + +static std::list getIteratorValues(std::list values, const ValueFlow::Value::ValueKind* kind = nullptr) +{ + values.remove_if([&](const ValueFlow::Value& v) { + if (kind && v.valueKind != *kind) + return true; + return !v.isIteratorValue(); + }); + return values; +} + +struct IteratorConditionHandler : SimpleConditionHandler { + std::vector parse(const Token* tok, const Settings& /*settings*/) const override { + Condition cond; + + if (Token::Match(tok, "==|!=")) { + if (!tok->astOperand1() || !tok->astOperand2()) + return {}; + + constexpr ValueFlow::Value::ValueKind kind = ValueFlow::Value::ValueKind::Known; + std::list values = getIteratorValues(tok->astOperand1()->values(), &kind); + if (!values.empty()) { + cond.vartok = tok->astOperand2(); + } else { + values = getIteratorValues(tok->astOperand2()->values(), &kind); + if (!values.empty()) + cond.vartok = tok->astOperand1(); + } + for (ValueFlow::Value& v:values) { + v.setPossible(); + v.assumeCondition(tok); + } + cond.true_values = values; + cond.false_values = std::move(values); + } + + return {std::move(cond)}; + } +}; + +static void valueFlowIteratorInfer(TokenList &tokenlist, const Settings &settings) +{ + for (Token *tok = tokenlist.front(); tok; tok = tok->next()) { + if (!tok->scope()) + continue; + if (!tok->scope()->isExecutable()) + continue; + std::list values = getIteratorValues(tok->values()); + values.remove_if([&](const ValueFlow::Value& v) { + if (!v.isImpossible()) + return true; + if (!v.condition) + return true; + if (v.bound != ValueFlow::Value::Bound::Point) + return true; + if (v.isIteratorEndValue() && v.intvalue <= 0) + return true; + if (v.isIteratorStartValue() && v.intvalue >= 0) + return true; + return false; + }); + for (ValueFlow::Value& v:values) { + v.setPossible(); + if (v.isIteratorStartValue()) + v.intvalue++; + if (v.isIteratorEndValue()) + v.intvalue--; + setTokenValue(tok, std::move(v), settings); + } + } +} + +static std::vector getContainerValues(const Token* tok) +{ + std::vector values; + if (tok) { + std::copy_if(tok->values().cbegin(), + tok->values().cend(), + std::back_inserter(values), + std::mem_fn(&ValueFlow::Value::isContainerSizeValue)); + } + return values; +} + +static ValueFlow::Value makeContainerSizeValue(std::size_t s, bool known = true) +{ + ValueFlow::Value value(s); + value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + if (known) + value.setKnown(); + return value; +} + +static std::vector makeContainerSizeValue(const Token* tok, bool known = true) +{ + if (tok->hasKnownIntValue()) + return {makeContainerSizeValue(tok->values().front().intvalue, known)}; + return {}; +} + +static std::vector getContainerSizeFromConstructorArgs(const std::vector& args, + const Library::Container* container, + bool known) +{ + if (astIsIntegral(args[0], false)) { // { count, i } or { count } + if (args.size() == 1 || (args.size() > 1 && !astIsIntegral(args[1], false))) + return {makeContainerSizeValue(args[0], known)}; + } else if (astIsContainer(args[0]) && args.size() == 1) { // copy constructor + return getContainerValues(args[0]); + } else if (isIteratorPair(args)) { + std::vector result = getContainerValues(args[0]); + if (!result.empty()) + return result; + // (ptr, ptr + size) + if (astIsPointer(args[0]) && args[0]->exprId() != 0) { + // (ptr, ptr) is empty + // TODO: Use lifetime values to check if it points to the same address + if (args[0]->exprId() == args[1]->exprId()) + return {makeContainerSizeValue(std::size_t{0}, known)}; + // TODO: Insert iterator positions for pointers + if (Token::simpleMatch(args[1], "+")) { + nonneg int const eid = args[0]->exprId(); + const Token* vartok = args[1]->astOperand1(); + const Token* sizetok = args[1]->astOperand2(); + if (sizetok->exprId() == eid) + std::swap(vartok, sizetok); + if (vartok->exprId() == eid && sizetok->hasKnownIntValue()) + return {makeContainerSizeValue(sizetok, known)}; + } + } + } else if (container->stdStringLike) { + if (astIsPointer(args[0])) { + // TODO: Try to read size of string literal { "abc" } + if (args.size() == 2 && astIsIntegral(args[1], false)) // { char*, count } + return {makeContainerSizeValue(args[1], known)}; + } else if (astIsContainer(args[0])) { + if (args.size() == 1) // copy constructor { str } + return getContainerValues(args[0]); + if (args.size() == 3) // { str, pos, count } + return {makeContainerSizeValue(args[2], known)}; + // TODO: { str, pos }, { ..., alloc } + } + } + return {}; +} + +static bool valueFlowIsSameContainerType(const ValueType& contType, const Token* tok, const Settings& settings) +{ + if (!tok || !tok->valueType() || !tok->valueType()->containerTypeToken) + return true; + + const ValueType tokType = ValueType::parseDecl(tok->valueType()->containerTypeToken, settings); + return contType.isTypeEqual(&tokType) || tokType.type == ValueType::Type::UNKNOWN_TYPE; +} + +static std::vector getInitListSize(const Token* tok, + const ValueType* valueType, + const Settings& settings, + bool known = true) +{ + std::vector args = getArguments(tok); + if (args.empty()) + return {makeContainerSizeValue(std::size_t{0}, known)}; + bool initList = true; + // Try to disambiguate init list from constructor + if (args.size() < 4) { + initList = !isIteratorPair(args) && !(args.size() < 3 && astIsIntegral(args[0], false)); + const Token* containerTypeToken = valueType->containerTypeToken; + if (valueType->container->stdStringLike) { + initList = astIsGenericChar(args[0]) && !astIsPointer(args[0]); + } else if (containerTypeToken) { + ValueType vt = ValueType::parseDecl(containerTypeToken, settings); + if (vt.pointer > 0 && astIsPointer(args[0])) + initList = true; + else if (vt.type == ValueType::ITERATOR && astIsIterator(args[0])) + initList = true; + else if (vt.isIntegral() && astIsIntegral(args[0], false)) + initList = true; + else if (args.size() == 1 && valueFlowIsSameContainerType(vt, tok->astOperand2(), settings)) + initList = false; // copy ctor + } + } + if (!initList) + return getContainerSizeFromConstructorArgs(args, valueType->container, known); + return {makeContainerSizeValue(args.size(), known)}; +} + +static std::vector getContainerSizeFromConstructor(const Token* tok, + const ValueType* valueType, + const Settings& settings, + bool known = true) +{ + std::vector args = getArguments(tok); + if (args.empty()) + return {makeContainerSizeValue(std::size_t{0}, known)}; + // Init list in constructor + if (args.size() == 1 && Token::simpleMatch(args[0], "{")) + return getInitListSize(args[0], valueType, settings, known); + return getContainerSizeFromConstructorArgs(args, valueType->container, known); +} + +static void valueFlowContainerSetTokValue(TokenList& tokenlist, ErrorLogger& errorLogger, const Settings& settings, const Token* tok, Token* initList) +{ + ValueFlow::Value value; + value.valueType = ValueFlow::Value::ValueType::TOK; + value.tokvalue = initList; + if (astIsContainerString(tok) && Token::simpleMatch(initList, "{") && Token::Match(initList->astOperand2(), "%str%")) { + value.tokvalue = initList->astOperand2(); + } + value.setKnown(); + Token* start = initList->link() ? initList->link() : initList->next(); + if (tok->variable() && tok->variable()->isConst()) { + valueFlowForwardConst(start, tok->variable()->scope()->bodyEnd, tok->variable(), {std::move(value)}, settings); + } else { + valueFlowForward(start, tok, std::move(value), tokenlist, errorLogger, settings); + } +} + +static const Scope* getFunctionScope(const Scope* scope) { + while (scope && scope->type != Scope::ScopeType::eFunction) + scope = scope->nestedIn; + return scope; +} + +static MathLib::bigint valueFlowGetStrLength(const Token* tok) +{ + if (tok->tokType() == Token::eString) + return Token::getStrLength(tok); + if (astIsGenericChar(tok) || tok->tokType() == Token::eChar) + return 1; + if (const ValueFlow::Value* v = tok->getKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE)) + return v->intvalue; + if (const ValueFlow::Value* v = tok->getKnownValue(ValueFlow::Value::ValueType::TOK)) { + if (v->tokvalue != tok) + return valueFlowGetStrLength(v->tokvalue); + } + return 0; +} + +static void valueFlowContainerSize(TokenList& tokenlist, + const SymbolDatabase& symboldatabase, + ErrorLogger& errorLogger, + const Settings& settings, + const std::set& skippedFunctions) +{ + // declaration + for (const Variable *var : symboldatabase.variableList()) { + if (!var) + continue; + if (!var->scope() || !var->scope()->bodyEnd || !var->scope()->bodyStart) + continue; + if (!var->valueType() || !var->valueType()->container) + continue; + if (!astIsContainer(var->nameToken())) + continue; + if (skippedFunctions.count(getFunctionScope(var->scope()))) + continue; + + bool known = true; + int size = 0; + const bool nonLocal = !var->isLocal() || var->isPointer() || var->isReference() || var->isStatic(); + bool constSize = var->isConst() && !nonLocal; + bool staticSize = false; + if (var->valueType()->container->size_templateArgNo >= 0) { + staticSize = true; + constSize = true; + size = -1; + if (var->dimensions().size() == 1) { + const Dimension& dim = var->dimensions().front(); + if (dim.known) { + size = dim.num; + } else if (dim.tok && dim.tok->hasKnownIntValue()) { + size = dim.tok->values().front().intvalue; + } + } + if (size < 0) + continue; + } + if (!staticSize && nonLocal) + continue; + Token* nameToken = const_cast(var->nameToken()); + if (nameToken->hasKnownValue(ValueFlow::Value::ValueType::CONTAINER_SIZE)) + continue; + if (!staticSize) { + if (!Token::Match(nameToken, "%name% ;") && + !(Token::Match(nameToken, "%name% {") && Token::simpleMatch(nameToken->next()->link(), "} ;")) && + !Token::Match(nameToken, "%name% (")) + continue; + } + if (nameToken->astTop() && Token::Match(nameToken->astTop()->previous(), "for|while")) + known = !isVariableChanged(var, &settings); + std::vector values{ValueFlow::Value{size}}; + values.back().valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + if (known) + values.back().setKnown(); + if (!staticSize) { + if (Token::simpleMatch(nameToken->next(), "{")) { + Token* initList = nameToken->next(); + valueFlowContainerSetTokValue(tokenlist, errorLogger, settings, nameToken, initList); + values = getInitListSize(initList, var->valueType(), settings, known); + } else if (Token::simpleMatch(nameToken->next(), "(")) { + const Token* constructorArgs = nameToken->next(); + values = getContainerSizeFromConstructor(constructorArgs, var->valueType(), settings, known); + } + } + + if (constSize) { + valueFlowForwardConst(nameToken->next(), var->scope()->bodyEnd, var, values, settings); + continue; + } + + for (const ValueFlow::Value& value : values) { + valueFlowForward(nameToken->next(), var->nameToken(), value, tokenlist, errorLogger, settings); + } + } + + // after assignment + for (const Scope *functionScope : symboldatabase.functionScopes) { + for (auto* tok = const_cast(functionScope->bodyStart); tok != functionScope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "%name%|;|{|} %var% = %str% ;")) { + Token* containerTok = tok->next(); + if (containerTok->exprId() == 0) + continue; + if (containerTok->valueType() && containerTok->valueType()->container && + containerTok->valueType()->container->stdStringLike) { + valueFlowContainerSetTokValue(tokenlist, errorLogger, settings, containerTok, containerTok->tokAt(2)); + ValueFlow::Value value(Token::getStrLength(containerTok->tokAt(2))); + value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + value.setKnown(); + valueFlowForward(containerTok->next(), containerTok, value, tokenlist, errorLogger, settings); + } + } else if (Token::Match(tok->previous(), ">|return (|{") && astIsContainer(tok) && getLibraryContainer(tok)->size_templateArgNo < 0) { + std::vector values; + if (Token::simpleMatch(tok, "{")) { + values = getInitListSize(tok, tok->valueType(), settings, true); + ValueFlow::Value value; + value.valueType = ValueFlow::Value::ValueType::TOK; + value.tokvalue = tok; + value.setKnown(); + values.push_back(value); + } else if (Token::simpleMatch(tok, "(")) { + const Token* constructorArgs = tok; + values = getContainerSizeFromConstructor(constructorArgs, tok->valueType(), settings, true); + } + for (const ValueFlow::Value& value : values) + setTokenValue(tok, value, settings); + } else if (Token::Match(tok, "%name%|;|{|}|> %var% = {") && Token::simpleMatch(tok->linkAt(3), "} ;")) { + Token* containerTok = tok->next(); + if (containerTok->exprId() == 0) + continue; + if (astIsContainer(containerTok) && containerTok->valueType()->container->size_templateArgNo < 0) { + std::vector values = + getInitListSize(tok->tokAt(3), containerTok->valueType(), settings); + valueFlowContainerSetTokValue(tokenlist, errorLogger, settings, containerTok, tok->tokAt(3)); + for (const ValueFlow::Value& value : values) + valueFlowForward(containerTok->next(), containerTok, value, tokenlist, errorLogger, settings); + } + } else if (Token::Match(tok, ". %name% (") && tok->astOperand1() && tok->astOperand1()->valueType() && + tok->astOperand1()->valueType()->container) { + const Token* containerTok = tok->astOperand1(); + if (containerTok->exprId() == 0) + continue; + const Library::Container::Action action = containerTok->valueType()->container->getAction(tok->strAt(1)); + if (action == Library::Container::Action::CLEAR) { + ValueFlow::Value value(0); + value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + value.setKnown(); + valueFlowForward(tok->next(), containerTok, std::move(value), tokenlist, errorLogger, settings); + } else if (action == Library::Container::Action::RESIZE && tok->tokAt(2)->astOperand2() && + tok->tokAt(2)->astOperand2()->hasKnownIntValue()) { + ValueFlow::Value value(tok->tokAt(2)->astOperand2()->values().front()); + value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + value.setKnown(); + valueFlowForward(tok->linkAt(2), containerTok, std::move(value), tokenlist, errorLogger, settings); + } else if (action == Library::Container::Action::PUSH && !isIteratorPair(getArguments(tok->tokAt(2)))) { + ValueFlow::Value value(0); + value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + value.setImpossible(); + valueFlowForward(tok->linkAt(2), containerTok, std::move(value), tokenlist, errorLogger, settings); + } + } else if (Token::simpleMatch(tok, "+=") && astIsContainer(tok->astOperand1())) { + const Token* containerTok = tok->astOperand1(); + const Token* valueTok = tok->astOperand2(); + MathLib::bigint size = valueFlowGetStrLength(valueTok); + if (size == 0) + continue; + ValueFlow::Value value(size - 1); + value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + value.bound = ValueFlow::Value::Bound::Upper; + value.setImpossible(); + Token* next = nextAfterAstRightmostLeaf(tok); + if (!next) + next = tok->next(); + valueFlowForward(next, containerTok, value, tokenlist, errorLogger, settings); + } + } + } +} + +struct ContainerConditionHandler : ConditionHandler { + std::vector parse(const Token* tok, const Settings& settings) const override + { + std::vector conds; + parseCompareEachInt(tok, [&](const Token* vartok, ValueFlow::Value true_value, ValueFlow::Value false_value) { + vartok = settings.library.getContainerFromYield(vartok, Library::Container::Yield::SIZE); + if (!vartok) + return; + true_value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + false_value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + Condition cond; + cond.true_values.push_back(std::move(true_value)); + cond.false_values.push_back(std::move(false_value)); + cond.vartok = vartok; + conds.push_back(std::move(cond)); + }); + if (!conds.empty()) + return conds; + + const Token* vartok = nullptr; + + // Empty check + if (tok->str() == "(") { + vartok = settings.library.getContainerFromYield(tok, Library::Container::Yield::EMPTY); + // TODO: Handle .size() + if (!vartok) + return {}; + const Token *parent = tok->astParent(); + while (parent) { + if (Token::Match(parent, "%comp%")) + return {}; + parent = parent->astParent(); + } + ValueFlow::Value value(tok, 0LL); + value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + Condition cond; + cond.true_values.emplace_back(value); + cond.false_values.emplace_back(std::move(value)); + cond.vartok = vartok; + cond.inverted = true; + return {std::move(cond)}; + } + // String compare + if (Token::Match(tok, "==|!=")) { + const Token *strtok = nullptr; + if (Token::Match(tok->astOperand1(), "%str%")) { + strtok = tok->astOperand1(); + vartok = tok->astOperand2(); + } else if (Token::Match(tok->astOperand2(), "%str%")) { + strtok = tok->astOperand2(); + vartok = tok->astOperand1(); + } + if (!strtok) + return {}; + if (!astIsContainer(vartok)) + return {}; + ValueFlow::Value value(tok, Token::getStrLength(strtok)); + value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + Condition cond; + cond.false_values.emplace_back(value); + cond.true_values.emplace_back(std::move(value)); + cond.vartok = vartok; + cond.impossible = false; + return {std::move(cond)}; + } + return {}; + } +}; + +static void valueFlowDynamicBufferSize(const TokenList& tokenlist, const SymbolDatabase& symboldatabase, ErrorLogger& errorLogger, const Settings& settings) +{ + auto getBufferSizeFromAllocFunc = [&](const Token* funcTok) -> MathLib::bigint { + MathLib::bigint sizeValue = -1; + const Library::AllocFunc* allocFunc = settings.library.getAllocFuncInfo(funcTok); + if (!allocFunc) + allocFunc = settings.library.getReallocFuncInfo(funcTok); + if (!allocFunc || allocFunc->bufferSize == Library::AllocFunc::BufferSize::none) + return sizeValue; + + const std::vector args = getArguments(funcTok); + + const Token* const arg1 = (args.size() >= allocFunc->bufferSizeArg1) ? args[allocFunc->bufferSizeArg1 - 1] : nullptr; + const Token* const arg2 = (args.size() >= allocFunc->bufferSizeArg2) ? args[allocFunc->bufferSizeArg2 - 1] : nullptr; + + switch (allocFunc->bufferSize) { + case Library::AllocFunc::BufferSize::none: + break; + case Library::AllocFunc::BufferSize::malloc: + if (arg1 && arg1->hasKnownIntValue()) + sizeValue = arg1->getKnownIntValue(); + break; + case Library::AllocFunc::BufferSize::calloc: + if (arg1 && arg2 && arg1->hasKnownIntValue() && arg2->hasKnownIntValue()) + sizeValue = arg1->getKnownIntValue() * arg2->getKnownIntValue(); + break; + case Library::AllocFunc::BufferSize::strdup: + if (arg1 && arg1->hasKnownValue()) { + const ValueFlow::Value& value = arg1->values().back(); + if (value.isTokValue() && value.tokvalue->tokType() == Token::eString) + sizeValue = Token::getStrLength(value.tokvalue) + 1; // Add one for the null terminator + } + break; + } + return sizeValue; + }; + + auto getBufferSizeFromNew = [&](const Token* newTok) -> MathLib::bigint { + MathLib::bigint sizeValue = -1, numElem = -1; + + if (newTok && newTok->astOperand1()) { // number of elements + const Token* bracTok = nullptr, *typeTok = nullptr; + if (newTok->astOperand1()->str() == "[") + bracTok = newTok->astOperand1(); + else if (Token::Match(newTok->astOperand1(), "(|{")) { + if (newTok->astOperand1()->astOperand1() && newTok->astOperand1()->astOperand1()->str() == "[") + bracTok = newTok->astOperand1()->astOperand1(); + else + typeTok = newTok->astOperand1()->astOperand1(); + } + else + typeTok = newTok->astOperand1(); + if (bracTok && bracTok->astOperand2() && bracTok->astOperand2()->hasKnownIntValue()) + numElem = bracTok->astOperand2()->getKnownIntValue(); + else if (Token::Match(typeTok, "%type%")) + numElem = 1; + } + + if (numElem >= 0 && newTok->astParent() && newTok->astParent()->isAssignmentOp()) { // size of the allocated type + const Token* typeTok = newTok->astParent()->astOperand1(); // TODO: implement fallback for e.g. "auto p = new Type;" + if (!typeTok || !typeTok->varId()) + typeTok = newTok->astParent()->previous(); // hack for "int** z = ..." + if (typeTok && typeTok->valueType()) { + const MathLib::bigint typeSize = typeTok->valueType()->typeSize(settings.platform, typeTok->valueType()->pointer > 1); + if (typeSize >= 0) + sizeValue = numElem * typeSize; + } + } + return sizeValue; + }; + + for (const Scope *functionScope : symboldatabase.functionScopes) { + for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { + if (!Token::Match(tok, "[;{}] %var% =")) + continue; + + if (!tok->next()->variable()) + continue; + + const Token *rhs = tok->tokAt(2)->astOperand2(); + while (rhs && rhs->isCast()) + rhs = rhs->astOperand2() ? rhs->astOperand2() : rhs->astOperand1(); + if (!rhs) + continue; + + const bool isNew = rhs->isCpp() && rhs->str() == "new"; + if (!isNew && !Token::Match(rhs->previous(), "%name% (")) + continue; + + const MathLib::bigint sizeValue = isNew ? getBufferSizeFromNew(rhs) : getBufferSizeFromAllocFunc(rhs->previous()); + if (sizeValue < 0) + continue; + + ValueFlow::Value value(sizeValue); + value.errorPath.emplace_back(tok->tokAt(2), "Assign " + tok->strAt(1) + ", buffer with size " + std::to_string(sizeValue)); + value.valueType = ValueFlow::Value::ValueType::BUFFER_SIZE; + value.setKnown(); + valueFlowForward(const_cast(rhs), functionScope->bodyEnd, tok->next(), std::move(value), tokenlist, errorLogger, settings); + } + } +} + +static bool getMinMaxValues(const ValueType *vt, const Platform &platform, MathLib::bigint &minValue, MathLib::bigint &maxValue) +{ + if (!vt || !vt->isIntegral() || vt->pointer) + return false; + + int bits; + switch (vt->type) { + case ValueType::Type::BOOL: + bits = 1; + break; + case ValueType::Type::CHAR: + bits = platform.char_bit; + break; + case ValueType::Type::SHORT: + bits = platform.short_bit; + break; + case ValueType::Type::INT: + bits = platform.int_bit; + break; + case ValueType::Type::LONG: + bits = platform.long_bit; + break; + case ValueType::Type::LONGLONG: + bits = platform.long_long_bit; + break; + default: + return false; + } + + if (bits == 1) { + minValue = 0; + maxValue = 1; + } else if (bits < 62) { + if (vt->sign == ValueType::Sign::UNSIGNED) { + minValue = 0; + maxValue = (1LL << bits) - 1; + } else { + minValue = -(1LL << (bits - 1)); + maxValue = (1LL << (bits - 1)) - 1; + } + } else if (bits == 64) { + if (vt->sign == ValueType::Sign::UNSIGNED) { + minValue = 0; + maxValue = LLONG_MAX; // todo max unsigned value + } else { + minValue = LLONG_MIN; + maxValue = LLONG_MAX; + } + } else { + return false; + } + + return true; +} + +static bool getMinMaxValues(const std::string &typestr, const Settings &settings, bool cpp, MathLib::bigint &minvalue, MathLib::bigint &maxvalue) +{ + TokenList typeTokens(&settings); + std::istringstream istr(typestr+";"); + if (!typeTokens.createTokens(istr, cpp ? Standards::Language::CPP : Standards::Language::C)) + return false; + typeTokens.simplifyPlatformTypes(); + typeTokens.simplifyStdType(); + const ValueType &vt = ValueType::parseDecl(typeTokens.front(), settings); + return getMinMaxValues(&vt, settings.platform, minvalue, maxvalue); +} + +static void valueFlowSafeFunctions(TokenList& tokenlist, const SymbolDatabase& symboldatabase, ErrorLogger& errorLogger, const Settings& settings) +{ + for (const Scope *functionScope : symboldatabase.functionScopes) { + if (!functionScope->bodyStart) + continue; + const Function *function = functionScope->function; + if (!function) + continue; + + const bool safe = function->isSafe(settings); + const bool all = safe && settings.platform.type != Platform::Type::Unspecified; + + for (const Variable &arg : function->argumentList) { + if (!arg.nameToken() || !arg.valueType()) + continue; + + if (arg.valueType()->type == ValueType::Type::CONTAINER) { + if (!safe) + continue; + std::list argValues; + argValues.emplace_back(0); + argValues.back().valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + argValues.back().errorPath.emplace_back(arg.nameToken(), "Assuming " + arg.name() + " is empty"); + argValues.back().safe = true; + argValues.emplace_back(1000000); + argValues.back().valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + argValues.back().errorPath.emplace_back(arg.nameToken(), "Assuming " + arg.name() + " size is 1000000"); + argValues.back().safe = true; + for (const ValueFlow::Value &value : argValues) + valueFlowForward(const_cast(functionScope->bodyStart), arg.nameToken(), value, tokenlist, errorLogger, settings); + continue; + } + + MathLib::bigint low, high; + bool isLow = arg.nameToken()->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::LOW, low); + bool isHigh = arg.nameToken()->getCppcheckAttribute(TokenImpl::CppcheckAttributes::Type::HIGH, high); + + if (!isLow && !isHigh && !all) + continue; + + const bool safeLow = !isLow; + const bool safeHigh = !isHigh; + + if ((!isLow || !isHigh) && all) { + MathLib::bigint minValue, maxValue; + if (getMinMaxValues(arg.valueType(), settings.platform, minValue, maxValue)) { + if (!isLow) + low = minValue; + if (!isHigh) + high = maxValue; + isLow = isHigh = true; + } else if (arg.valueType()->type == ValueType::Type::FLOAT || arg.valueType()->type == ValueType::Type::DOUBLE || arg.valueType()->type == ValueType::Type::LONGDOUBLE) { + std::list argValues; + argValues.emplace_back(0); + argValues.back().valueType = ValueFlow::Value::ValueType::FLOAT; + argValues.back().floatValue = isLow ? low : -1E25; + argValues.back().errorPath.emplace_back(arg.nameToken(), "Safe checks: Assuming argument has value " + MathLib::toString(argValues.back().floatValue)); + argValues.back().safe = true; + argValues.emplace_back(0); + argValues.back().valueType = ValueFlow::Value::ValueType::FLOAT; + argValues.back().floatValue = isHigh ? high : 1E25; + argValues.back().errorPath.emplace_back(arg.nameToken(), "Safe checks: Assuming argument has value " + MathLib::toString(argValues.back().floatValue)); + argValues.back().safe = true; + valueFlowForward(const_cast(functionScope->bodyStart->next()), + functionScope->bodyEnd, + arg.nameToken(), + std::move(argValues), + tokenlist, + errorLogger, + settings); + continue; + } + } + + std::list argValues; + if (isLow) { + argValues.emplace_back(low); + argValues.back().errorPath.emplace_back(arg.nameToken(), std::string(safeLow ? "Safe checks: " : "") + "Assuming argument has value " + std::to_string(low)); + argValues.back().safe = safeLow; + } + if (isHigh) { + argValues.emplace_back(high); + argValues.back().errorPath.emplace_back(arg.nameToken(), std::string(safeHigh ? "Safe checks: " : "") + "Assuming argument has value " + std::to_string(high)); + argValues.back().safe = safeHigh; + } + + if (!argValues.empty()) + valueFlowForward(const_cast(functionScope->bodyStart->next()), + functionScope->bodyEnd, + arg.nameToken(), + std::move(argValues), + tokenlist, + errorLogger, + settings); + } + } +} + +static void valueFlowUnknownFunctionReturn(TokenList &tokenlist, const Settings &settings) +{ + if (settings.checkUnknownFunctionReturn.empty()) + return; + for (Token *tok = tokenlist.front(); tok; tok = tok->next()) { + if (!tok->astParent() || tok->str() != "(" || !tok->previous()->isName()) + continue; + if (settings.checkUnknownFunctionReturn.find(tok->previous()->str()) == settings.checkUnknownFunctionReturn.end()) + continue; + std::vector unknownValues = settings.library.unknownReturnValues(tok->astOperand1()); + if (unknownValues.empty()) + continue; + + // Get min/max values for return type + const std::string &typestr = settings.library.returnValueType(tok->previous()); + MathLib::bigint minvalue, maxvalue; + if (!getMinMaxValues(typestr, settings, tok->isCpp(), minvalue, maxvalue)) + continue; + + for (MathLib::bigint value : unknownValues) { + if (value < minvalue) + value = minvalue; + else if (value > maxvalue) + value = maxvalue; + setTokenValue(tok, ValueFlow::Value(value), settings); + } + } +} + +static void valueFlowDebug(TokenList& tokenlist, ErrorLogger& errorLogger, const Settings& settings) +{ + if (!settings.debugnormal && !settings.debugwarnings) + return; + for (Token* tok = tokenlist.front(); tok; tok = tok->next()) { + if (tok->getTokenDebug() != TokenDebug::ValueFlow) + continue; + if (tok->astParent() && tok->astParent()->getTokenDebug() == TokenDebug::ValueFlow) + continue; + for (const ValueFlow::Value& v : tok->values()) { + std::string msg = "The value is " + debugString(v); + ErrorPath errorPath = v.errorPath; + errorPath.insert(errorPath.end(), v.debugPath.cbegin(), v.debugPath.cend()); + errorPath.emplace_back(tok, ""); + errorLogger.reportErr({errorPath, &tokenlist, Severity::debug, "valueFlow", msg, CWE{0}, Certainty::normal}); + } + } +} + +const ValueFlow::Value *ValueFlow::valueFlowConstantFoldAST(Token *expr, const Settings &settings) +{ + if (expr && expr->values().empty()) { + valueFlowConstantFoldAST(expr->astOperand1(), settings); + valueFlowConstantFoldAST(expr->astOperand2(), settings); + valueFlowSetConstantValue(expr, settings); + } + return expr && expr->hasKnownValue() ? &expr->values().front() : nullptr; +} + +struct ValueFlowState { + explicit ValueFlowState(TokenList& tokenlist, + SymbolDatabase& symboldatabase, + ErrorLogger& errorLogger, + const Settings& settings) + : tokenlist(tokenlist), symboldatabase(symboldatabase), errorLogger(errorLogger), settings(settings) + {} + + TokenList& tokenlist; + SymbolDatabase& symboldatabase; + ErrorLogger& errorLogger; + const Settings& settings; + std::set skippedFunctions; +}; + +struct ValueFlowPass { + ValueFlowPass() = default; + ValueFlowPass(const ValueFlowPass&) = default; + // Name of pass + virtual const char* name() const = 0; + // Run the pass + virtual void run(const ValueFlowState& state) const = 0; + // Returns true if pass needs C++ + virtual bool cpp() const = 0; + virtual ~ValueFlowPass() noexcept = default; +}; + +struct ValueFlowPassRunner { + using Clock = std::chrono::steady_clock; + using TimePoint = std::chrono::time_point; + explicit ValueFlowPassRunner(ValueFlowState state, TimerResultsIntf* timerResults = nullptr) + : state(std::move(state)), stop(TimePoint::max()), timerResults(timerResults) + { + setSkippedFunctions(); + setStopTime(); + } + + bool run_once(std::initializer_list> passes) const + { + return std::any_of(passes.begin(), passes.end(), [&](const ValuePtr& pass) { + return run(pass); + }); + } + + bool run(std::initializer_list> passes) const + { + std::size_t values = 0; + std::size_t n = state.settings.valueFlowMaxIterations; + while (n > 0 && values != getTotalValues()) { + values = getTotalValues(); + if (std::any_of(passes.begin(), passes.end(), [&](const ValuePtr& pass) { + return run(pass); + })) + return true; + --n; + } + if (state.settings.debugwarnings) { + if (n == 0 && values != getTotalValues()) { + ErrorMessage::FileLocation loc(state.tokenlist.getFiles()[0], 0, 0); + ErrorMessage errmsg({std::move(loc)}, + emptyString, + Severity::debug, + "ValueFlow maximum iterations exceeded", + "valueFlowMaxIterations", + Certainty::normal); + state.errorLogger.reportErr(errmsg); + } + } + return false; + } + + bool run(const ValuePtr& pass) const + { + auto start = Clock::now(); + if (start > stop) + return true; + if (!state.tokenlist.isCPP() && pass->cpp()) + return false; + if (timerResults) { + Timer t(pass->name(), state.settings.showtime, timerResults); + pass->run(state); + } else { + pass->run(state); + } + return false; + } + + std::size_t getTotalValues() const + { + std::size_t n = 1; + for (Token* tok = state.tokenlist.front(); tok; tok = tok->next()) + n += tok->values().size(); + return n; + } + + void setSkippedFunctions() + { + if (state.settings.performanceValueFlowMaxIfCount > 0) { + for (const Scope* functionScope : state.symboldatabase.functionScopes) { + int countIfScopes = 0; + std::vector scopes{functionScope}; + while (!scopes.empty()) { + const Scope* s = scopes.back(); + scopes.pop_back(); + for (const Scope* s2 : s->nestedList) { + scopes.emplace_back(s2); + if (s2->type == Scope::ScopeType::eIf) + ++countIfScopes; + } + } + if (countIfScopes > state.settings.performanceValueFlowMaxIfCount) { + state.skippedFunctions.emplace(functionScope); + + if (state.settings.severity.isEnabled(Severity::information)) { + const std::string& functionName = functionScope->className; + const std::list callstack( + 1, + ErrorMessage::FileLocation(functionScope->bodyStart, &state.tokenlist)); + const ErrorMessage errmsg(callstack, + state.tokenlist.getSourceFilePath(), + Severity::information, + "Limiting ValueFlow analysis in function '" + functionName + "' since it is too complex. " + "Please specify --check-level=exhaustive to perform full analysis.", + "checkLevelNormal", + Certainty::normal); + state.errorLogger.reportErr(errmsg); + } + } + } + } + } + + void setStopTime() + { + if (state.settings.performanceValueFlowMaxTime >= 0) + stop = Clock::now() + std::chrono::seconds{state.settings.performanceValueFlowMaxTime}; + } + + ValueFlowState state; + TimePoint stop; + TimerResultsIntf* timerResults; +}; + +template +struct ValueFlowPassAdaptor : ValueFlowPass { + const char* mName = nullptr; + bool mCPP = false; + F mRun; + ValueFlowPassAdaptor(const char* pname, bool pcpp, F prun) : ValueFlowPass(), mName(pname), mCPP(pcpp), mRun(prun) {} + const char* name() const override { + return mName; + } + void run(const ValueFlowState& state) const override + { + mRun(state.tokenlist, state.symboldatabase, state.errorLogger, state.settings, state.skippedFunctions); + } + bool cpp() const override { + return mCPP; + } +}; + +template +static ValueFlowPassAdaptor makeValueFlowPassAdaptor(const char* name, bool cpp, F run) +{ + return {name, cpp, run}; +} + +#define VALUEFLOW_ADAPTOR(cpp, ...) \ + makeValueFlowPassAdaptor(#__VA_ARGS__, \ + (cpp), \ + [](TokenList& tokenlist, \ + SymbolDatabase& symboldatabase, \ + ErrorLogger& errorLogger, \ + const Settings& settings, \ + const std::set& skippedFunctions) { \ + (void)tokenlist; \ + (void)symboldatabase; \ + (void)errorLogger; \ + (void)settings; \ + (void)skippedFunctions; \ + __VA_ARGS__; \ + }) + +#define VFA(...) VALUEFLOW_ADAPTOR(false, __VA_ARGS__) +#define VFA_CPP(...) VALUEFLOW_ADAPTOR(true, __VA_ARGS__) + +void ValueFlow::setValues(TokenList& tokenlist, + SymbolDatabase& symboldatabase, + ErrorLogger& errorLogger, + const Settings& settings, + TimerResultsIntf* timerResults) +{ + for (Token* tok = tokenlist.front(); tok; tok = tok->next()) + tok->clearValueFlow(); + + // commas in init.. + for (Token* tok = tokenlist.front(); tok; tok = tok->next()) { + if (tok->str() != "{" || !tok->astOperand1()) + continue; + for (Token* tok2 = tok->next(); tok2 != tok->link(); tok2 = tok2->next()) { + if (tok2->link() && Token::Match(tok2, "[{[(<]")) + tok2 = tok2->link(); + else if (tok2->str() == ",") + tok2->isInitComma(true); + } + } + + ValueFlowPassRunner runner{ValueFlowState{tokenlist, symboldatabase, errorLogger, settings}, timerResults}; + runner.run_once({ + VFA(valueFlowEnumValue(symboldatabase, settings)), + VFA(valueFlowNumber(tokenlist, settings)), + VFA(valueFlowString(tokenlist, settings)), + VFA(valueFlowArray(tokenlist, settings)), + VFA(valueFlowUnknownFunctionReturn(tokenlist, settings)), + VFA(valueFlowGlobalConstVar(tokenlist, settings)), + VFA(valueFlowEnumValue(symboldatabase, settings)), + VFA(valueFlowGlobalStaticVar(tokenlist, settings)), + VFA(valueFlowPointerAlias(tokenlist, settings)), + VFA(valueFlowLifetime(tokenlist, errorLogger, settings)), + VFA(valueFlowSymbolic(tokenlist, symboldatabase, errorLogger, settings)), + VFA(valueFlowBitAnd(tokenlist, settings)), + VFA(valueFlowSameExpressions(tokenlist, settings)), + VFA(valueFlowConditionExpressions(tokenlist, symboldatabase, errorLogger, settings)), + }); + + runner.run({ + VFA(valueFlowImpossibleValues(tokenlist, settings)), + VFA(valueFlowSymbolicOperators(symboldatabase, settings)), + VFA(valueFlowCondition(SymbolicConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions)), + VFA(valueFlowSymbolicInfer(symboldatabase, settings)), + VFA(valueFlowArrayBool(tokenlist, settings)), + VFA(valueFlowArrayElement(tokenlist, settings)), + VFA(valueFlowRightShift(tokenlist, settings)), + VFA(valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings, skippedFunctions)), + VFA_CPP(valueFlowAfterSwap(tokenlist, symboldatabase, errorLogger, settings)), + VFA(valueFlowCondition(SimpleConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions)), + VFA(valueFlowInferCondition(tokenlist, settings)), + VFA(valueFlowSwitchVariable(tokenlist, symboldatabase, errorLogger, settings)), + VFA(valueFlowForLoop(tokenlist, symboldatabase, errorLogger, settings)), + VFA(valueFlowSubFunction(tokenlist, symboldatabase, errorLogger, settings)), + VFA(valueFlowFunctionReturn(tokenlist, errorLogger, settings)), + VFA(valueFlowLifetime(tokenlist, errorLogger, settings)), + VFA(valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, errorLogger, settings)), + VFA(valueFlowUninit(tokenlist, errorLogger, settings)), + VFA_CPP(valueFlowAfterMove(tokenlist, symboldatabase, errorLogger, settings)), + VFA_CPP(valueFlowSmartPointer(tokenlist, errorLogger, settings)), + VFA_CPP(valueFlowIterators(tokenlist, settings)), + VFA_CPP( + valueFlowCondition(IteratorConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions)), + VFA_CPP(valueFlowIteratorInfer(tokenlist, settings)), + VFA_CPP(valueFlowContainerSize(tokenlist, symboldatabase, errorLogger, settings, skippedFunctions)), + VFA_CPP( + valueFlowCondition(ContainerConditionHandler{}, tokenlist, symboldatabase, errorLogger, settings, skippedFunctions)), + VFA(valueFlowSafeFunctions(tokenlist, symboldatabase, errorLogger, settings)), + }); + + runner.run_once({ + VFA(valueFlowDynamicBufferSize(tokenlist, symboldatabase, errorLogger, settings)), + VFA(valueFlowDebug(tokenlist, errorLogger, settings)), + }); +} + +std::string ValueFlow::eitherTheConditionIsRedundant(const Token *condition) +{ + if (!condition) + return "Either the condition is redundant"; + if (condition->str() == "case") { + std::string expr; + for (const Token *tok = condition; tok && tok->str() != ":"; tok = tok->next()) { + expr += tok->str(); + if (Token::Match(tok, "%name%|%num% %name%|%num%")) + expr += ' '; + } + return "Either the switch case '" + expr + "' is redundant"; + } + return "Either the condition '" + condition->expressionString() + "' is redundant"; +} + +const ValueFlow::Value* ValueFlow::findValue(const std::list& values, + const Settings* settings, + const std::function &pred) +{ + const ValueFlow::Value* ret = nullptr; + for (const ValueFlow::Value& v : values) { + if (pred(v)) { + if (!ret || ret->isInconclusive() || (ret->condition && !v.isInconclusive())) + ret = &v; + if (!ret->isInconclusive() && !ret->condition) + break; + } + } + if (settings && ret) { + if (ret->isInconclusive() && !settings->certainty.isEnabled(Certainty::inconclusive)) + return nullptr; + if (ret->condition && !settings->severity.isEnabled(Severity::warning)) + return nullptr; + } + return ret; +} + +// TODO: returns a single value at most - no need for std::vector +static std::vector isOutOfBoundsImpl(const ValueFlow::Value& size, + const Token* indexTok, + bool condition) +{ + if (!indexTok) + return {}; + const ValueFlow::Value* indexValue = indexTok->getMaxValue(condition, size.path); + if (!indexValue) + return {}; + if (indexValue->intvalue >= size.intvalue) + return {*indexValue}; + if (!condition) + return {}; + // TODO: Use a better way to decide if the variable in unconstrained + if (!indexTok->variable() || !indexTok->variable()->isArgument()) + return {}; + if (std::any_of(indexTok->values().cbegin(), indexTok->values().cend(), [&](const ValueFlow::Value& v) { + return v.isSymbolicValue() && v.isPossible() && v.bound == ValueFlow::Value::Bound::Upper; + })) + return {}; + if (indexValue->bound != ValueFlow::Value::Bound::Lower) + return {}; + if (size.bound == ValueFlow::Value::Bound::Lower) + return {}; + // Checking for underflow doesn't mean it could be out of bounds + if (indexValue->intvalue == 0) + return {}; + ValueFlow::Value value = inferCondition(">=", indexTok, indexValue->intvalue); + if (!value.isKnown()) + return {}; + if (value.intvalue == 0) + return {}; + value.intvalue = size.intvalue; + value.bound = ValueFlow::Value::Bound::Lower; + return {std::move(value)}; +} + +// TODO: return single value at most - no need for std::vector +std::vector ValueFlow::isOutOfBounds(const Value& size, const Token* indexTok, bool possible) +{ + ValueFlow::Value inBoundsValue = inferCondition("<", indexTok, size.intvalue); + if (inBoundsValue.isKnown() && inBoundsValue.intvalue != 0) + return {}; + std::vector result = isOutOfBoundsImpl(size, indexTok, false); + if (!result.empty()) + return result; + if (!possible) + return result; + return isOutOfBoundsImpl(size, indexTok, true); +} diff --git a/cppcheck-2.14.0/lib/valueflow.h b/cppcheck-2.14.0/lib/valueflow.h new file mode 100644 index 00000000..e51670c9 --- /dev/null +++ b/cppcheck-2.14.0/lib/valueflow.h @@ -0,0 +1,137 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef valueflowH +#define valueflowH +//--------------------------------------------------------------------------- + +#include "config.h" +#include "mathlib.h" +#include "vfvalue.h" + +#include +#include +#include +#include +#include +#include + +class ErrorLogger; +struct InferModel; +class Settings; +class SymbolDatabase; +class TimerResultsIntf; +class Token; +class TokenList; +class ValueType; +class Variable; +class Scope; + +template +class ValuePtr; + +namespace ValueFlow { + /// Constant folding of expression. This can be used before the full ValueFlow has been executed (ValueFlow::setValues). + const Value * valueFlowConstantFoldAST(Token *expr, const Settings &settings); + + /// Perform valueflow analysis. + void setValues(TokenList& tokenlist, + SymbolDatabase& symboldatabase, + ErrorLogger& errorLogger, + const Settings& settings, + TimerResultsIntf* timerResults); + + std::string eitherTheConditionIsRedundant(const Token *condition); + + size_t getSizeOf(const ValueType &vt, const Settings &settings, int maxRecursion = 0); + + const Value* findValue(const std::list& values, + const Settings* settings, + const std::function &pred); + + std::vector isOutOfBounds(const Value& size, const Token* indexTok, bool possible = true); + + Value asImpossible(Value v); + + bool isContainerSizeChanged(const Token* tok, int indirect, const Settings& settings, int depth = 20); + + struct LifetimeToken { + const Token* token{}; + Value::ErrorPath errorPath; + bool addressOf{}; + bool inconclusive{}; + + LifetimeToken() = default; + + LifetimeToken(const Token* token, Value::ErrorPath errorPath) + : token(token), errorPath(std::move(errorPath)) + {} + + LifetimeToken(const Token* token, bool addressOf, Value::ErrorPath errorPath) + : token(token), errorPath(std::move(errorPath)), addressOf(addressOf) + {} + + static std::vector setAddressOf(std::vector v, bool b) { + for (LifetimeToken& x : v) + x.addressOf = b; + return v; + } + + static std::vector setInconclusive(std::vector v, bool b) { + for (LifetimeToken& x : v) + x.inconclusive = b; + return v; + } + }; + + const Token *parseCompareInt(const Token *tok, Value &true_value, Value &false_value, const std::function(const Token*)>& evaluate); + const Token *parseCompareInt(const Token *tok, Value &true_value, Value &false_value); + + CPPCHECKLIB ValuePtr makeIntegralInferModel(); + + const Token* solveExprValue(const Token* expr, + const std::function(const Token*)>& eval, + Value& value); + + std::vector getLifetimeTokens(const Token* tok, + bool escape = false, + Value::ErrorPath errorPath = Value::ErrorPath{}); + + bool hasLifetimeToken(const Token* tok, const Token* lifetime); + + const Variable* getLifetimeVariable(const Token* tok, Value::ErrorPath& errorPath, bool* addressOf = nullptr); + + const Variable* getLifetimeVariable(const Token* tok); + + bool isLifetimeBorrowed(const Token *tok, const Settings &settings); + + std::string lifetimeMessage(const Token *tok, const Value *val, Value::ErrorPath &errorPath); + + CPPCHECKLIB Value getLifetimeObjValue(const Token *tok, bool inconclusive = false); + + CPPCHECKLIB std::vector getLifetimeObjValues(const Token* tok, + bool inconclusive = false, + MathLib::bigint path = 0); + + const Token* getEndOfExprScope(const Token* tok, const Scope* defaultScope = nullptr, bool smallest = true); + + void combineValueProperties(const Value& value1, const Value& value2, Value& result); +} + +#endif // valueflowH diff --git a/cppcheck-2.14.0/lib/valueptr.h b/cppcheck-2.14.0/lib/valueptr.h new file mode 100644 index 00000000..3bb3f71e --- /dev/null +++ b/cppcheck-2.14.0/lib/valueptr.h @@ -0,0 +1,106 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef valueptrH +#define valueptrH +//--------------------------------------------------------------------------- + +#include "config.h" + +#include + +template +class CPPCHECKLIB ValuePtr { + template + struct cloner { + static T* apply(const T* x) { + return new U(*static_cast(x)); + } + }; + +public: + using pointer = T*; + using element_type = T; + using cloner_type = decltype(&cloner::apply); + + ValuePtr() : mPtr(nullptr), mClone() {} + + template + // cppcheck-suppress noExplicitConstructor + // NOLINTNEXTLINE(google-explicit-constructor) + ValuePtr(const U& value) : mPtr(cloner::apply(&value)), mClone(&cloner::apply) + {} + + ValuePtr(const ValuePtr& rhs) : mPtr(nullptr), mClone(rhs.mClone) { + if (rhs) { + mPtr.reset(mClone(rhs.get())); + } + } + ValuePtr(ValuePtr&& rhs) NOEXCEPT : mPtr(std::move(rhs.mPtr)), mClone(std::move(rhs.mClone)) {} + + /** + * Releases the shared_ptr's ownership of the managed object using the .reset() function + */ + void release() { + mPtr.reset(); + } + + T* get() NOEXCEPT { + return mPtr.get(); + } + const T* get() const NOEXCEPT { + return mPtr.get(); + } + + T& operator*() { + return *get(); + } + const T& operator*() const { + return *get(); + } + + T* operator->() NOEXCEPT { + return get(); + } + const T* operator->() const NOEXCEPT { + return get(); + } + + void swap(ValuePtr& rhs) { + using std::swap; + swap(mPtr, rhs.mPtr); + swap(mClone, rhs.mClone); + } + + ValuePtr& operator=(ValuePtr rhs) { + swap(rhs); + return *this; + } + + // NOLINTNEXTLINE(google-explicit-constructor) + operator bool() const NOEXCEPT { + return !!mPtr; + } + +private: + std::shared_ptr mPtr; + cloner_type mClone; +}; + +#endif diff --git a/cppcheck-2.14.0/lib/version.h b/cppcheck-2.14.0/lib/version.h new file mode 100644 index 00000000..d99b949b --- /dev/null +++ b/cppcheck-2.14.0/lib/version.h @@ -0,0 +1,23 @@ +// For a release version x.y.z the MAJOR should be x and both MINOR and DEVMINOR should be y. +// After a release the DEVMINOR is incremented. MAJOR=x MINOR=y, DEVMINOR=y+1 + +#ifndef versionH +#define versionH + +#define CPPCHECK_MAJOR_VERSION 2 +#define CPPCHECK_MINOR_VERSION 14 +#define CPPCHECK_DEVMINOR_VERSION 14 +#define CPPCHECK_BUGFIX_VERSION 0 + +#define STRINGIFY(x) STRING(x) +#define STRING(VER) #VER +#if CPPCHECK_BUGFIX_VERSION < 99 +#define CPPCHECK_VERSION_STRING STRINGIFY(CPPCHECK_MAJOR_VERSION) "." STRINGIFY(CPPCHECK_MINOR_VERSION) "." STRINGIFY(CPPCHECK_BUGFIX_VERSION) +#define CPPCHECK_VERSION CPPCHECK_MAJOR_VERSION,CPPCHECK_MINOR_VERSION,CPPCHECK_BUGFIX_VERSION,0 +#else +#define CPPCHECK_VERSION_STRING STRINGIFY(CPPCHECK_MAJOR_VERSION) "." STRINGIFY(CPPCHECK_DEVMINOR_VERSION) " dev" +#define CPPCHECK_VERSION CPPCHECK_MAJOR_VERSION,CPPCHECK_MINOR_VERSION,99,0 +#endif +#define LEGALCOPYRIGHT L"Copyright (C) 2007-2024 Cppcheck team." + +#endif diff --git a/cppcheck-2.14.0/lib/version.rc b/cppcheck-2.14.0/lib/version.rc new file mode 100644 index 00000000..bf438f31 --- /dev/null +++ b/cppcheck-2.14.0/lib/version.rc @@ -0,0 +1,34 @@ +#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 diff --git a/cppcheck-2.14.0/lib/vfvalue.cpp b/cppcheck-2.14.0/lib/vfvalue.cpp new file mode 100644 index 00000000..4aade2ca --- /dev/null +++ b/cppcheck-2.14.0/lib/vfvalue.cpp @@ -0,0 +1,195 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "vfvalue.h" + +#include "errortypes.h" +#include "token.h" + +#include +#include + +namespace ValueFlow { + Value::Value(const Token *c, long long val, Bound b) + : bound(b), + intvalue(val), + varvalue(val), + condition(c) { + errorPath.emplace_back(c, "Assuming that condition '" + c->expressionString() + "' is not redundant"); + } + + void Value::assumeCondition(const Token *tok) { + condition = tok; + errorPath.emplace_back(tok, "Assuming that condition '" + tok->expressionString() + "' is not redundant"); + } + + std::string Value::toString() const { + std::stringstream ss; + if (this->isImpossible()) + ss << "!"; + if (this->bound == Bound::Lower) + ss << ">="; + if (this->bound == Bound::Upper) + ss << "<="; + switch (this->valueType) { + case ValueType::INT: + ss << this->intvalue; + break; + case ValueType::TOK: + ss << this->tokvalue->str(); + break; + case ValueType::FLOAT: + ss << this->floatValue; + break; + case ValueType::MOVED: + ss << toString(this->moveKind); + break; + case ValueType::UNINIT: + ss << "Uninit"; + break; + case ValueType::BUFFER_SIZE: + case ValueType::CONTAINER_SIZE: + ss << "size=" << this->intvalue; + break; + case ValueType::ITERATOR_START: + ss << "start=" << this->intvalue; + break; + case ValueType::ITERATOR_END: + ss << "end=" << this->intvalue; + break; + case ValueType::LIFETIME: + ss << "lifetime[" << toString(this->lifetimeKind) << "]=(" + << this->tokvalue->expressionString() << ")"; + break; + case ValueType::SYMBOLIC: + ss << "symbolic=(" << this->tokvalue->expressionString(); + if (this->intvalue > 0) + ss << "+" << this->intvalue; + else if (this->intvalue < 0) + ss << "-" << -this->intvalue; + ss << ")"; + break; + } + if (this->indirect > 0) + for (int i = 0; i < this->indirect; i++) + ss << "*"; + if (this->path > 0) + ss << "@" << this->path; + return ss.str(); + } + + std::string Value::infoString() const { + switch (valueType) { + case ValueType::INT: + return std::to_string(intvalue); + case ValueType::TOK: + return tokvalue->str(); + case ValueType::FLOAT: + return MathLib::toString(floatValue); + case ValueType::MOVED: + return ""; + case ValueType::UNINIT: + return ""; + case ValueType::BUFFER_SIZE: + case ValueType::CONTAINER_SIZE: + return "size=" + std::to_string(intvalue); + case ValueType::ITERATOR_START: + return "start=" + std::to_string(intvalue); + case ValueType::ITERATOR_END: + return "end=" + std::to_string(intvalue); + case ValueType::LIFETIME: + return "lifetime=" + tokvalue->str(); + case ValueType::SYMBOLIC: + { + std::string result = "symbolic=" + tokvalue->expressionString(); + if (intvalue > 0) + result += "+" + std::to_string(intvalue); + else if (intvalue < 0) + result += "-" + std::to_string(-intvalue); + return result; + } + } + throw InternalError(nullptr, "Invalid ValueFlow Value type"); + } + + const char *Value::toString(MoveKind moveKind) { + switch (moveKind) { + case MoveKind::NonMovedVariable: + return "NonMovedVariable"; + case MoveKind::MovedVariable: + return "MovedVariable"; + case MoveKind::ForwardedVariable: + return "ForwardedVariable"; + } + return ""; + } + + const char *Value::toString(LifetimeKind lifetimeKind) { + switch (lifetimeKind) { + case LifetimeKind::Object: + return "Object"; + case LifetimeKind::SubObject: + return "SubObject"; + case LifetimeKind::Lambda: + return "Lambda"; + case LifetimeKind::Iterator: + return "Iterator"; + case LifetimeKind::Address: + return "Address"; + } + return ""; + } + + bool Value::sameToken(const Token *tok1, const Token *tok2) { + if (tok1 == tok2) + return true; + if (!tok1) + return false; + if (tok1->exprId() == 0 || tok2->exprId() == 0) + return false; + return tok1->exprId() == tok2->exprId(); + } + + const char *Value::toString(LifetimeScope lifetimeScope) { + switch (lifetimeScope) { + case LifetimeScope::Local: + return "Local"; + case LifetimeScope::Argument: + return "Argument"; + case LifetimeScope::SubFunction: + return "SubFunction"; + case LifetimeScope::ThisPointer: + return "ThisPointer"; + case LifetimeScope::ThisValue: + return "ThisValue"; + } + return ""; + } + + const char *Value::toString(Bound bound) { + switch (bound) { + case Bound::Point: + return "Point"; + case Bound::Upper: + return "Upper"; + case Bound::Lower: + return "Lower"; + } + return ""; + } +} diff --git a/cppcheck-2.14.0/lib/vfvalue.h b/cppcheck-2.14.0/lib/vfvalue.h new file mode 100644 index 00000000..b3a7598c --- /dev/null +++ b/cppcheck-2.14.0/lib/vfvalue.h @@ -0,0 +1,416 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +//--------------------------------------------------------------------------- +#ifndef vfvalueH +#define vfvalueH +//--------------------------------------------------------------------------- + +#include "config.h" +#include "mathlib.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +class Token; + +namespace ValueFlow +{ + class CPPCHECKLIB Value { + public: + using ErrorPathItem = std::pair; + using ErrorPath = std::list; + enum class Bound { Upper, Lower, Point }; + + explicit Value(long long val = 0, Bound b = Bound::Point) : + bound(b), + intvalue(val), + varvalue(val), + wideintvalue(val) + {} + Value(const Token* c, long long val, Bound b = Bound::Point); + + static Value unknown() { + Value v; + v.valueType = ValueType::UNINIT; + return v; + } + + bool equalValue(const ValueFlow::Value& rhs) const { + if (valueType != rhs.valueType) + return false; + switch (valueType) { + case ValueType::INT: + case ValueType::CONTAINER_SIZE: + case ValueType::BUFFER_SIZE: + case ValueType::ITERATOR_START: + case ValueType::ITERATOR_END: + if (intvalue != rhs.intvalue) + return false; + break; + case ValueType::TOK: + if (tokvalue != rhs.tokvalue) + return false; + break; + case ValueType::FLOAT: + if (floatValue > rhs.floatValue || floatValue < rhs.floatValue || std::signbit(floatValue) != std::signbit(rhs.floatValue)) + return false; + break; + case ValueType::MOVED: + if (moveKind != rhs.moveKind) + return false; + break; + case ValueType::UNINIT: + break; + case ValueType::LIFETIME: + if (tokvalue != rhs.tokvalue) + return false; + break; + case ValueType::SYMBOLIC: + if (!sameToken(tokvalue, rhs.tokvalue)) + return false; + if (intvalue != rhs.intvalue) + return false; + break; + } + return true; + } + + template + static void visitValue(T& self, F f) { + switch (self.valueType) { + case ValueType::INT: + case ValueType::SYMBOLIC: + case ValueType::BUFFER_SIZE: + case ValueType::CONTAINER_SIZE: + case ValueType::ITERATOR_START: + case ValueType::ITERATOR_END: { + f(self.intvalue); + break; + } + case ValueType::FLOAT: { + f(self.floatValue); + break; + } + case ValueType::UNINIT: + case ValueType::TOK: + case ValueType::LIFETIME: + case ValueType::MOVED: + break; + } + } + + struct compareVisitor { + struct innerVisitor { + template + void operator()(bool& result, Compare compare, T x, U y) const { + result = compare(x, y); + } + }; + template + void operator()(bool& result, const Value& rhs, Compare compare, T x) const { + visitValue(rhs, + std::bind(innerVisitor{}, std::ref(result), std::move(compare), x, std::placeholders::_1)); + } + }; + + template + bool compareValue(const Value& rhs, Compare compare) const { + assert((!this->isSymbolicValue() && !rhs.isSymbolicValue()) || + (this->valueType == rhs.valueType && sameToken(this->tokvalue, rhs.tokvalue))); + bool result = false; + visitValue( + *this, + std::bind(compareVisitor{}, std::ref(result), std::ref(rhs), std::move(compare), std::placeholders::_1)); + return result; + } + + bool operator==(const Value &rhs) const { + if (!equalValue(rhs)) + return false; + + return varvalue == rhs.varvalue && + condition == rhs.condition && + varId == rhs.varId && + conditional == rhs.conditional && + defaultArg == rhs.defaultArg && + indirect == rhs.indirect && + valueKind == rhs.valueKind; + } + + bool operator!=(const Value &rhs) const { + return !(*this == rhs); + } + + template )> + bool equalTo(const T& x) const { + bool result = false; + visitValue(*this, std::bind(equalVisitor{}, std::ref(result), x, std::placeholders::_1)); + return result; + } + + void decreaseRange() { + if (bound == Bound::Lower) + visitValue(*this, increment{}); + else if (bound == Bound::Upper) + visitValue(*this, decrement{}); + } + + void invertBound() { + if (bound == Bound::Lower) + bound = Bound::Upper; + else if (bound == Bound::Upper) + bound = Bound::Lower; + } + + void invertRange() { + invertBound(); + decreaseRange(); + } + + void assumeCondition(const Token* tok); + + std::string infoString() const; + + std::string toString() const; + + enum class ValueType { + INT, + TOK, + FLOAT, + MOVED, + UNINIT, + CONTAINER_SIZE, + LIFETIME, + BUFFER_SIZE, + ITERATOR_START, + ITERATOR_END, + SYMBOLIC + } valueType = ValueType::INT; + bool isIntValue() const { + return valueType == ValueType::INT; + } + bool isTokValue() const { + return valueType == ValueType::TOK; + } + bool isFloatValue() const { + return valueType == ValueType::FLOAT; + } + bool isMovedValue() const { + return valueType == ValueType::MOVED; + } + bool isUninitValue() const { + return valueType == ValueType::UNINIT; + } + bool isContainerSizeValue() const { + return valueType == ValueType::CONTAINER_SIZE; + } + bool isLifetimeValue() const { + return valueType == ValueType::LIFETIME; + } + bool isBufferSizeValue() const { + return valueType == ValueType::BUFFER_SIZE; + } + bool isIteratorValue() const { + return valueType == ValueType::ITERATOR_START || valueType == ValueType::ITERATOR_END; + } + bool isIteratorStartValue() const { + return valueType == ValueType::ITERATOR_START; + } + bool isIteratorEndValue() const { + return valueType == ValueType::ITERATOR_END; + } + bool isSymbolicValue() const { + return valueType == ValueType::SYMBOLIC; + } + + bool isLocalLifetimeValue() const { + return valueType == ValueType::LIFETIME && lifetimeScope == LifetimeScope::Local; + } + + bool isArgumentLifetimeValue() const { + return valueType == ValueType::LIFETIME && lifetimeScope == LifetimeScope::Argument; + } + + bool isSubFunctionLifetimeValue() const { + return valueType == ValueType::LIFETIME && lifetimeScope == LifetimeScope::SubFunction; + } + + bool isNonValue() const { + return isMovedValue() || isUninitValue() || isLifetimeValue(); + } + + /** The value bound */ + Bound bound = Bound::Point; + + /** int value (or sometimes bool value?) */ + long long intvalue{}; + + /** token value - the token that has the value. this is used for pointer aliases, strings, etc. */ + const Token* tokvalue{}; + + /** float value */ + double floatValue{}; + + /** For calculated values - variable value that calculated value depends on */ + long long varvalue{}; + + /** Condition that this value depends on */ + const Token* condition{}; + + ErrorPath errorPath; + + ErrorPath debugPath; + + /** For calculated values - varId that calculated value depends on */ + nonneg int varId{}; + + /** value relies on safe checking */ + bool safe{}; + + /** Conditional value */ + bool conditional{}; + + /** Value is is from an expanded macro */ + bool macro{}; + + /** Is this value passed as default parameter to the function? */ + bool defaultArg{}; + + int indirect{}; + + /** kind of moved */ + enum class MoveKind { NonMovedVariable, MovedVariable, ForwardedVariable } moveKind = MoveKind::NonMovedVariable; + + /** Path id */ + MathLib::bigint path{}; + + /** int value before implicit truncation */ + long long wideintvalue{}; + + std::vector subexpressions; + + // Set to where a lifetime is captured by value + const Token* capturetok{}; + + enum class LifetimeKind { + // Pointer points to a member of lifetime + Object, + // A member of object points to the lifetime + SubObject, + // Lambda has captured lifetime(similar to SubObject) + Lambda, + // Iterator points to the lifetime of a container(similar to Object) + Iterator, + // A pointer that holds the address of the lifetime + Address + } lifetimeKind = LifetimeKind::Object; + + enum class LifetimeScope { Local, Argument, SubFunction, ThisPointer, ThisValue } lifetimeScope = LifetimeScope::Local; + + static const char* toString(MoveKind moveKind); + static const char* toString(LifetimeKind lifetimeKind); + static const char* toString(LifetimeScope lifetimeScope); + static const char* toString(Bound bound); + + /** How known is this value */ + enum class ValueKind { + /** This value is possible, other unlisted values may also be possible */ + Possible, + /** Only listed values are possible */ + Known, + /** Inconclusive */ + Inconclusive, + /** Listed values are impossible */ + Impossible + } valueKind = ValueKind::Possible; + + void setKnown() { + valueKind = ValueKind::Known; + } + + bool isKnown() const { + return valueKind == ValueKind::Known; + } + + void setPossible() { + valueKind = ValueKind::Possible; + } + + bool isPossible() const { + return valueKind == ValueKind::Possible; + } + + bool isImpossible() const { + return valueKind == ValueKind::Impossible; + } + + void setImpossible() { + valueKind = ValueKind::Impossible; + } + + void setInconclusive(bool inconclusive = true) { + if (inconclusive) + valueKind = ValueKind::Inconclusive; + } + + bool isInconclusive() const { + return valueKind == ValueKind::Inconclusive; + } + + void changeKnownToPossible() { + if (isKnown()) + valueKind = ValueKind::Possible; + } + + bool errorSeverity() const { + return !condition && !defaultArg; + } + + static bool sameToken(const Token* tok1, const Token* tok2); + + private: + struct equalVisitor { + template + void operator()(bool& result, T x, U y) const { + result = !(x > y || x < y); + } + }; + + struct increment { + template + void operator()(T& x) const { + x++; + } + }; + struct decrement { + template + void operator()(T& x) const { + x--; + } + }; + }; +} + +#endif // vfvalueH diff --git a/cppcheck-2.14.0/lib/xml.h b/cppcheck-2.14.0/lib/xml.h new file mode 100644 index 00000000..c8c258d9 --- /dev/null +++ b/cppcheck-2.14.0/lib/xml.h @@ -0,0 +1,34 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef xmlH +#define xmlH + +#include "config.h" + +SUPPRESS_WARNING_CLANG_PUSH("-Wzero-as-null-pointer-constant") +SUPPRESS_WARNING_CLANG_PUSH("-Wsuggest-destructor-override") +SUPPRESS_WARNING_CLANG_PUSH("-Winconsistent-missing-destructor-override") + +#include // IWYU pragma: export + +SUPPRESS_WARNING_CLANG_POP +SUPPRESS_WARNING_CLANG_POP +SUPPRESS_WARNING_CLANG_POP + +#endif // xmlH diff --git a/cppcheck-2.14.0/man/CMakeLists.txt b/cppcheck-2.14.0/man/CMakeLists.txt new file mode 100644 index 00000000..be9720d2 --- /dev/null +++ b/cppcheck-2.14.0/man/CMakeLists.txt @@ -0,0 +1,7 @@ +if (BUILD_MANPAGE) + find_program(XSLTPROC NAMES xsltproc REQUIRED) + set(DB2MAN "/usr/share/sgml/docbook/stylesheet/xsl/nwalsh/manpages/docbook.xsl") + set(MAN_SOURCE "${CMAKE_CURRENT_SOURCE_DIR}/cppcheck.1.xml") + add_custom_target(man ${XSLTPROC} --nonet --param man.charmap.use.subset "0" ${DB2MAN} ${MAN_SOURCE} + DEPENDS ${MAN_SOURCE}) +endif() \ No newline at end of file diff --git a/cppcheck-2.14.0/man/build-html.sh b/cppcheck-2.14.0/man/build-html.sh new file mode 100644 index 00000000..d9c4aa1c --- /dev/null +++ b/cppcheck-2.14.0/man/build-html.sh @@ -0,0 +1,3 @@ +#!/bin/sh +echo Building $1.html +pandoc $1.md -o $1.html -s --number-sections --toc --css manual.css diff --git a/cppcheck-2.14.0/man/build-pdf.sh b/cppcheck-2.14.0/man/build-pdf.sh new file mode 100644 index 00000000..648b0106 --- /dev/null +++ b/cppcheck-2.14.0/man/build-pdf.sh @@ -0,0 +1,32 @@ +#!/bin/sh +# This uses installed fonts, which vary between systems +# Segoe UI and Consolas are standard in Windows 10, DejaVu is more common on Linux +echo Building $1.pdf + +MainFont="Segoe UI" +MonoFont="Consolas" + +is_font_installed() { + fontname=$1 + fc-list | grep -i "$fontname" >/dev/null +} + +if ! is_font_installed "$MainFont"; then + MainFont="DejaVu Sans" +fi +if ! is_font_installed "$MonoFont"; then + MonoFont="DejaVu Sans Mono" +fi + +# echo Using $MainFont / $MonoFont + +pandoc $1.md -o $1.pdf -s --number-sections --toc \ + --pdf-engine=xelatex \ + --listings \ + -f markdown \ + -V mainfont="$MainFont" \ + -V monofont="$MonoFont" \ + -V geometry:a4paper \ + -V geometry:margin=2.4cm \ + -V subparagraph \ + -H manual-style.tex diff --git a/cppcheck-2.14.0/man/buildman.sh b/cppcheck-2.14.0/man/buildman.sh new file mode 100644 index 00000000..cd198071 --- /dev/null +++ b/cppcheck-2.14.0/man/buildman.sh @@ -0,0 +1,10 @@ +#!/bin/sh +# To install required tools in debian: +# sudo apt-get install pandoc texlive-latex-base texlive-fonts-recommended texlive-latex-extra +# For Windows you can use the MiKTeX installer https://miktex.org/download + +./build-pdf.sh manual +./build-html.sh manual + +./build-pdf.sh reference-cfg-format +./build-html.sh reference-cfg-format diff --git a/cppcheck-2.14.0/man/cppcheck-design.docbook b/cppcheck-2.14.0/man/cppcheck-design.docbook new file mode 100644 index 00000000..82eadfcf --- /dev/null +++ b/cppcheck-2.14.0/man/cppcheck-design.docbook @@ -0,0 +1,190 @@ + + + + Cppcheck Design + + + DanielMarjamäki + + + Cppcheck + + + + 2010 + + +
+ Introduction + + The goal with this article is to give users an idea of how Cppcheck + works. + + Cppcheck is a static analysis tool that tries to completely avoid + false warnings. A false warning is when the tool reports that there is an + error even though there is no error. + + Cppcheck is a relatively simple tool. I hope that this article will + highlight that it is possible to avoid false warnings even with simple + analysis. +
+ +
+ Limitations of static analysis + + There are many bugs in programs that are really hard to detect for + tools. Here is an example: + + // calculate the number of days + int days = hours / 23; + + A human programmer knows that there are 24 hours in a day and + therefore he could see that "23" is wrong. A tool will probably not know + that there are 24 hours in a day. + + A tool that tries to detect all bugs could write a warning message + for every calculation in the program. Then it will correctly report that + "hours / 23" is wrong but incorrectly warn about "hours / 24". + + Cppcheck will only write a warning message if it can determine that + the calculation is wrong. In this case, no error will be written. +
+ +
+ Control flow analysis + + When you review code you will probably use "control flow analysis" + in your head to determine if there are bugs or not. + + Control flow analysis is when you try to determine what the possible + execution paths are. + + The control flow analysis in Cppcheck is quite simple. +
+ +
+ Buffer overflows + + This is a simple description of how buffer overflows are detected by + Cppcheck. + + If an array is accessed out of bounds somewhere in its scope then an + error message will be written. An example code: + + void f() +{ + char a[10]; + if (x + y == 2) { + a[20] = 0; + } +} + + Cppcheck will report this message: + + Array 'a[10]' index 20 out of bounds + + No control flow analysis is used. Cppcheck will not try to determine + how execution can reach the "a[20] = 0;" statement. It is assumed that all + statements are reachable. Cppcheck will detect the error even if it is + really impossible that "x + y == 2" is true. I still claim that this is a + correct warning because the statement is there and it has the + error. + + Cppcheck will also investigate function calls. But then control flow + analysis can be needed to avoid false warnings. Here is an example that + logically is the same as the previous example: + + void f1(char *s) +{ + s[20] = 0; +} + +void f2() +{ + char a[10]; + if (x + y == 2) { + f1(a); + } +} + + Cppcheck will report this message: + + Array 'a[10]' index 20 out of bounds + + If the execution reaches the function call then there will be an + error. + + But if the condition is moved into "f1" then it will be necessary to + prove that "x+y==2" can be true when the function is called from "f2". No + error message is reported for this code: + + void f1(char *s) +{ + if (x + y == 2) { + s[20] = 0; + } +} + +void f2() +{ + char a[10]; + f1(a); +} +
+ +
+ Memory leaks + + The check uses simple control-flow analysis. The control flow + analysis assumes that all conditions can always be either true or false. + It is assumed that all statements are reachable. Here is an + example: + + void f() +{ + char *a = malloc(10); + if (x + y == 2) { + return; + } + free(a); +} + + Cppcheck will determine that there is a leak at the "return;" + statement: + + Memory leak: a + + Cppcheck doesn't try to determine how the execution reaches the + "return;" statement. It will only see that if the execution reaches the + "return;" then there will be a memory leak. + + Lack of advanced control-flow analysis means that many bugs are not + detected: + + void f(int x) +{ + char *a = 0; + + if (x == 10) + a = malloc(10); + + if (x == 20) + free(a); +} + + Cppcheck doesn't detect any error. The "all conditions can be either + true/false" means that cppcheck doesn't know that "if (x==20)" is always + false when "if (x==10)" is true. So Cppcheck can't establish that there is + a leak. Many other static analysis tools will probably detect that there + will be a leak if x is 10. +
+ +
+ Final thoughts + + You cannot trust that Cppcheck will detect all bugs. + + Cppcheck will just find some bugs. It is likely that you won't find + these bugs unless you use Cppcheck. +
+
diff --git a/cppcheck-2.14.0/man/cppcheck.1.xml b/cppcheck-2.14.0/man/cppcheck.1.xml new file mode 100644 index 00000000..f6838587 --- /dev/null +++ b/cppcheck-2.14.0/man/cppcheck.1.xml @@ -0,0 +1,673 @@ + +.
will be generated. You may view the +manual page with: nroff -man .
| less'. A typical entry +in a Makefile or Makefile.am is: + +DB2MAN = /usr/share/sgml/docbook/stylesheet/xsl/nwalsh/manpages/docbook.xsl +XP = xsltproc -''-nonet -''-param man.charmap.use.subset "0" + +manpage.1: manpage.xml + $(XP) $(DB2MAN) $< + +The xsltproc binary is found in the xsltproc package. The XSL files are in +docbook-xsl. A description of the parameters you can use can be found in the +docbook-xsl-doc-* packages. Please remember that if you create the nroff +version in one of the debian/rules file targets (such as build), you will need +to include xsltproc and docbook-xsl in your Build-Depends control field. +Alternatively use the xmlto command/package. That will also automatically +pull in xsltproc and docbook-xsl. + +Notes for using docbook2x: docbook2x-man does not automatically create the +AUTHOR(S) and COPYRIGHT sections. In this case, please add them manually as + ... . + +To disable the automatic creation of the AUTHOR(S) and COPYRIGHT sections +read /usr/share/doc/docbook-xsl/doc/manpages/authors.html. This file can be +found in the docbook-xsl-doc-html package. + +Validation can be done using: `xmllint -''-noout -''-valid manpage.xml` + +General documentation about man-pages and man-page-formatting: +man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/ + +--> + + + + + + + +]> + + + &dhtitle; + &dhpackage; + + + &dhfirstname; + &dhsurname; + Wrote this manpage for the Debian system. +
+ &dhemail; +
+
+
+ + 2009 - 2016 + &dhusername; + + + This manual page was written for the Debian system + (but may be used by others). + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU General Public License, + Version 3 or (at your option) any later version published by + the Free Software Foundation. + On Debian systems, the complete text of the GNU General Public + License can be found in + /usr/share/common-licenses/GPL-3. + +
+ + &dhucpackage; + &dhsection; + + + &dhpackage; + Tool for static C/C++ code analysis + + + + &dhpackage; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DESCRIPTION + Cppcheck is a command-line tool that tries to detect bugs that your C/C++ + compiler doesn't see. It is versatile, and can check non-standard code + including various compiler extensions, inline assembly code, etc. + Its internal preprocessor can handle includes, macros, and several + preprocessor commands. While Cppcheck is highly configurable, + you can start using it just by giving it a path to the source code. + + + + OPTIONS + Analyze given C/C++ files for common errors. + + + + + + + + Check Cppcheck configuration. The normal code analysis is disabled by this flag. + + + + + + + + Show information messages when library files have incomplete info. + + + + + + + + By default Cppcheck checks all configurations. Use -D to limit the checking. When -D is used the checking is limited to the given configuration. +Example: -DDEBUG=1 -D__cplusplus + + + + + + + + By default Cppcheck checks all configurations. Use '-U' to explicitly hide certain #ifdef <id> code paths from checking. +Example: '-UDEBUG' + + + + + + + + Disable individual checks. Please refer to the documentation of --enable=<id> for further details. + + + + + + + + + Enable additional checks. The available ids are: + allEnable all checks. It is recommended to only use + --enable=all when the whole program is scanned, because this + enables unusedFunction.warningEnable warning messagesstyleEnable all coding style checks. All messages with the + severities 'style', 'performance' and 'portability' are + enabled.performanceEnable performance messagesportabilityEnable portability messagesinformationEnable information messagesunusedFunctionCheck for unused functions. It is recommend to only + enable this when the whole program is scannedmissingIncludeWarn if there are missing includes + 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. + + + + + + + + Treat char type as signed. This overrides previous --platform options and is overridden by following ones. + + + + + + + + Treat char type as unsigned. This overrides previous --platform options and is overridden by following ones. + + + + + + + + + + + Print help text. + + + + + + + + Give path to search for include files. Give several -I parameters to give several paths. First given path is +searched for contained header files first. If paths are relative to source files, this is not needed. + + + + + + + + Specify directory paths to search for included header files in a text file. Add one include path per line. +First given path is searched for contained header files first. If paths are relative to source files, this is not needed. + + + + + + + + Path (prefix) to be excluded from configuration checking. Preprocessor configurations defined in headers +(but not sources) matching the prefix will not be considered for evaluation of configuration alternatives. + + + + + + + + A file that contains a list of config-excludes. + + + + + + + + Force inclusion of a file before the checked file. Can be used for example when checking the Linux kernel, +where autoconf.h needs to be included for every file compiled. Works the same way as the GCC -include option. + + + + + + + + Give path to ignore. Give several -i parameters to ignore several paths. Give directory name or filename with path as parameter. +Directory name is matched to all parts of the path. + + + + + + + + Allow that Cppcheck reports even though the analysis is inconclusive. +There are false positives with this option. Each result must be carefully investigated before you know if it is good or bad. + + + + + + + + Enable inline suppressions. Use them by placing comments in the form: // cppcheck-suppress memleak + before the line to suppress. + + + + + + + + Start <jobs> threads to do the checking work. + + + + + + + + Specifies that no new threads should be started if there are other threads running and the load average is at least +<load> (ignored on non UNIX-like systems) + + + + + + + + Forces cppcheck to check all files as the given language. Valid + values are: c, c++ + + + + + + + + Use library configuration. + + + + + + + + Maximum number of configurations to check in a file before skipping it. Default is 12. If used together with --force, the last option is + the one that is effective. + + + + + + + + Maximum depth in whole program analysis. Default is 2. + + + + + + + + Specifies platform specific types and sizes.The available platforms are: + unix3232 bit unix variantunix6464 bit unix variantwin32A32 bit Windows ASCII character encodingwin32W32 bit Windows UNICODE character encodingwin6464 bit Windows + By default the platform which was used to compile Cppcheck is used. + + + + + + + + + + + + Only print something when there is an error. + + + + + + + + + + + + + + + + + Use relative paths in output. When given, <paths> are used as base. You can separate multiple paths by ';'. Otherwise path where source files are searched is used. E.g. if given value is test, when checking test/test.cpp, the path in output will be test.cpp instead of test/test.cpp. The feature uses string comparison to create relative paths, so using e.g. ~ for home folder does not work. It is currently only possible to apply the base paths to files that are on a lower level in the directory tree. + + + + + + + + Report progress when checking a file. + + + + + + + + Match regular expression to create your own checks. E.g. rule "/ 0" can be used to check division by zero. This command is only available if cppcheck was compiled with HAVE_RULES=yes. + + + + + + + + Use given rule XML file. See https://sourceforge.net/projects/cppcheck/files/Articles/ for more info about the syntax. This command is only available if cppcheck was compiled with HAVE_RULES=yes. + + + + + + + + Show timing information. The available mode are: + noneShow nothing (default)fileShow for each processed filefile-totalShow total time only for each processed filesummaryShow a summary at the endtop5_fileShow the top 5 for each processed filetop5_summaryShow the top 5 summary at the endtop5Alias for top5_file (deprecated) + + + + + + + + + Set standard. The available options are: + c89C code is C89 compatiblec99C code is C99 compatiblec11C code is C11 compatible (default)c++03C++ code is C++03 compatiblec++11C++ code is C++11 compatiblec++14C++ code is C++14 compatiblec++17C++ code is C++17 compatiblec++20C++ code is C++20 compatible (default) + + + + + + + + + Suppress a specific warning. The format of <spec> is: [error id]:[filename]:[line]. + The [filename] and [line] are optional. + [error id] may be * to suppress all warnings (for a specified file or files). + [filename] may contain the wildcard characters * or ?. + + + + + + + + Suppress warnings listed in the file. + Each suppression is in the format of <spec> above. + + + + + + + + Use suppressions defined in xml as described in the manual + + + + + + + + Format the error messages. E.g. '{file}:{line},{severity},{id},{message}' or '{file}({line}):({severity}) {message}'. Pre-defined templates: gcc, vs + + + + + + + + + + + More detailed error reports + + + + + + + + Print out version information + + + + + + + + Write results in XML to error stream + + + + + + + + Select the XML file version. Also implies --xml. Currently only version 2 is available. The default version is 2. + + + + + + AUTHOR + The program was written by Daniel Marjamäki and Cppcheck team. See AUTHORS file for list of team members. + + + SEE ALSO + + Full list of features: https://sourceforge.net/p/cppcheck/wiki/Home/ + +
diff --git a/cppcheck-2.14.0/man/images/gui-newproject-addons.png b/cppcheck-2.14.0/man/images/gui-newproject-addons.png new file mode 100644 index 0000000000000000000000000000000000000000..e2d368b93236b25ad5f7965a810d1c1965c7b0a1 GIT binary patch literal 20633 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 literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/man/images/gui-newproject-project.png b/cppcheck-2.14.0/man/images/gui-newproject-project.png new file mode 100644 index 0000000000000000000000000000000000000000..5a921a3a1e117ae5f8370a73f482f414b3fc4fb6 GIT binary patch literal 33686 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 HcJKcIyt-Is literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/man/images/gui-newproject.png b/cppcheck-2.14.0/man/images/gui-newproject.png new file mode 100644 index 0000000000000000000000000000000000000000..5f824338518028be930645fce258b3576b26bfc3 GIT binary patch literal 35440 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 literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/man/images/gui-results.png b/cppcheck-2.14.0/man/images/gui-results.png new file mode 100644 index 0000000000000000000000000000000000000000..c24c8064ca74ef073a5bcd13a631814a9a114411 GIT binary patch literal 105686 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 KnRrt7*8c;%LAc}q literal 0 HcmV?d00001 diff --git a/cppcheck-2.14.0/man/manual-ja.docbook b/cppcheck-2.14.0/man/manual-ja.docbook new file mode 100644 index 00000000..1c13d3d0 --- /dev/null +++ b/cppcheck-2.14.0/man/manual-ja.docbook @@ -0,0 +1,1895 @@ + + + + + Cppcheck 1.87 + + 2018-04-23 + + + + イントロダクション + + Cppcheck は C/C++の静的解析ツールです。C/C++ コンパイラやその他の解析ツールとは異なり、シンタックスエラーを検出しません。 その代わりに、Cppcheckは、コンパイラが通常、検出に失敗するような種類のバグを検出します。このプロジェクトのゴールは、擬陽性 0 です。 + + サポートしているプログラムのソースコードとプラットフォーム: + + + + さまざまなコンパイラの拡張構文や、インラインアセンブル等を含む、非標準的なソースコードをチェックできます。 + + + + Cppcheck は 最新のC++規格をサポートしている、あらゆるC++コンパイラでコンパイルできるようにしています。 + + + + Cppcheck は 十分なCPUパワーとメモリーのある、あらゆるプラットフォームで動作するようにしています。 + + + + Cppcheckに限界があることをご理解ください。Cppcheckの報告しているエラーに稀に間違いのあることがあります。また、Cppcheck が検出しないバグが残っていることもあります。 + + ソフトウェアを注意深くテストすれば、Cppcheckを使うより、より多くのバグを検出できるでしょう。ソフトウェアを注意深く実装すれば、Cppcheckを使うより、より多くのバグを検出できるでしょう。しかし、あなたのソフトウェアを実装するときやテストするときに見逃したバグのいくつかを Cppcheckが検出できるでしょう。 + + + + GUIでのはじめ方 + + GUIの起動 + +
+ 新しいプロジェクト(New Project) + + 新しプロジェクトのファイルの作成は必要ではありませんが、最初のステップに最適です。ファイル(File)と新しいプロジェクトファイル(New project file)を通じて学べます。 +
+ +
+ 新しいプロジェクト(New Project) - パス(Paths)と定義(Defines) + + あなたのプロジェクトはどのようなプロジェクトでしょうか。あなたのプロジェクトがVisual Studioのプロジェクトの場合、または(cmake/qbs/等の)コンパイルデータベースqが精製できる場合、あなたはプロジェクトをインポート(import)できます。 + + そうではない場合には、そのプロジェクトのパスと定義をマニュアルで調整します。次の図は、Visual Studio のプロジェクトファイルをインポートした場合のスクリーンショットです。 + + + + + + +
+ +
+ 新しいプロジェクト(New Project) - プロジェクト(Project) + + プロジェクトタブ(Project tab)では、ビルドディレクトリ(Cppcheck build dir)を設定しましょう。これはCppcheckが様々な分析する情報を保管するために使用します。プログラム全体の解析、インクリメンタル解析、統計などです。それぞれのプロジェクトは、それぞれのビルドディレクトリを持ちます。次のスクリーンショットはビルドディレクトリをcppcheck-build-dirと設定しています。このパスはプロジェクトファイルからの相対パスです。 + + あなたは、あなた使用する全てのライブラリーを選択すべきです。次のスクリーンショットではmicrosoft_sal と windowsライブラリーを選択しています。ライブラリーについてはこのマニュアルを参照してして下さい。 + + + + + + +
+ +
+ 新しいプロジェクト(New Project) - アドオン(Addons) + + ここでは 除外タブ(Exclude)と抑制タブ(Suppressions)をスキップします。これは結果をあとで微調整するために使います。 + + アドオンタブ(Addons)であなたは別の分析を追加できます。このアドオンにはpythonが必要です。 + + + + + + +
+ +
+ 解析(Analyze) + + ダイアログのOKボタンをクリックします。解析がすぐに始まります。 + + + + + + + + 全ての警告が有効になり、やや賑やかになります。あなたが注意しない様々な警告があり得ます。これは簡単に修正できます。メッセージを右クリックして、隠す(Hide)または抑制( Suppress)を選びます。メッセージの隠しは永久ではありません。これは次の解析でまた表示されます。メッセージの抑制は、永久です。抑制されたidはプロジェクトファイルに保存されるので、これらは二度と表示されません。 +
+
+ + + コマンドラインでの始め方 + +
+ 最初のテスト + + これは単純なソースコードです。 + + int main() +{ + char a[10]; + a[10] = 0; + return 0; +} + + このソースコードをfile1.cに保存して次のコマンドを実行します。 + + cppcheck file1.c + + cppcheck は次のように出力するでしょう。 + + Checking file1.c... +[file1.c:4]: (error) Array 'a[10]' index 10 out of bounds +
+ +
+ フォルダ内の全てのファイルをチェックする + + 通常、プログラムは多くのソースファイルから構成されます。そして、それら全てをチェックしたいでしょう。Cppcheck は一つのディレクトリ以下の全てのソースファイルをチェックできます。 + + cppcheck path + + ここで"path"はディレクトリのパスです。このようにすれば cppcheck はディレクトリ以下の全てのファイルを再帰的にチェックします。 + + Checking path/file1.cpp... +1/2 files checked 50% done +Checking path/file2.cpp... +2/2 files checked 100% done +
+ +
+ マニュアルでファイルをチェックまたはプロジェクトファイルの使用 + + Cppcheckでは、ファイルやパスを指定する事でファイルチェックを指定できます。一方ででプロジェクトファイル(cmake/visual studio)を使用できます。 + + プロジェクトファイルの使用は早急に始められます、というのもあなたが設定してする項目が少なくなるからです。 + + マニュアルでのファイルチェックは、解析をより細かく制御できます。 + + どちらのアプローチが良い結果になるかはわかりません。両方の方法を試して下さい。両方のアプローチを使用するとより多くののバグを見つけられる結果が得られるかもしれません。 + + 以降の章でより詳細を説明します。 +
+ +
+ チェックからファイルやフォルダを除外する + + ファイルやフォルダをチェック対象から除外する方法は二つあります。最初の方法は、あなたがチェックしたいファイルやフォルダだけをcppcheckに指定することです。 + + cppcheck src/a src/b + + src/asrc/b 以下の全てのファイルだけをチェックします。 + + 第二の方法は、-iオプションと共に除外したいファイルやフォルダを指定することです。次のコマンドではsrc/c以下のファイルをチェックしません。 + + cppcheck -isrc/c src + + このオプションは現在--projectオプションと同時に使用できません。また、このオプションが有効なのは、インプットディレクトリが提供するされたときです。複数のディレクトリを無視するためには、-iを複数回使用します。次のコマンドではsrc/b と src/c 以下のファイルをチェックしません。 + + cppcheck -isrc/b -isrc/c +
+ +
+ Severities(厳格度) + + メッセージのseverities(厳格度)には次のものがあります。: + + + + error(エラー) + + + バグが検出されたときに使用します。 + + + + + warning(警告) + + + 防衛的プログラミングでバグを避けるための提案です。 + + + + + style + + + コードの可読性の向上に関連した、スタイル関連の指摘(未使用関数、冗長なコードなど) + + + + + performance + + + コードの高速化のための提案。これらの提案は、一般的な知識に基づいたものでしかありません。このメッセージの修正によって計測できるほど処理速度が向上するかどうかはわかりません。 + + + + + portability + + + 移植性についての警告。64 bit CPUへの移植性。コンパイラ依存(独自拡張)ソースコードについての警告など。 + + + + + information + + + 設定上の問題設定を変更している間だけ有効にすることをお勧めします。 + + + +
+ +
+ メッセージの表示 + + デフォルトではerrorのメッセージだけを表示します。--enableを使用すると他のチェックを有効にできます。 + + # warning のメッセージを有効にします。 +cppcheck --enable=warning file.c + +# performanceのメッセージを有効にします。 +cppcheck --enable=performance file.c + +# informationのメッセージを有効にします。 +cppcheck --enable=information file.c + +# 歴史的な理由により --enable=style を指定すると warning, performance, +# portability と styleのメッセージを有効にします。古いxml形式を使用しているときには、これらの厳格度を"style"として報告されます。 +cppcheck --enable=style file.c + +# warning と performance のメッセージを有効にします。 +cppcheck --enable=warning,performance file.c + +# unusedFunction のチェックを有効にします。今回は --enable=styleでは有効にできない。 +# というのは、これではライブラリではうまく動作しないからです。 +cppcheck --enable=unusedFunction file.c + +# 全てのメッセージを有効にします。 +cppcheck --enable=all + + --enable=unusedFunctionはプログラム全体をチェックするときにだけ有効にしてください。また、--enable=allもプログラム全体をチェックするときにだけ有効にしてください。というのは、unusedFunction チェックは、関数が呼び出されなかったときに警告するチェックだからです。関数呼び出しがチェック範囲にみつからなかったという可能性のノイズになります。 + +
+ 疑いのあるチェック + + Cppcheckはデフォルトで解析に疑いのない場合にだけエラーメッセージを表示します。しかし、--inconclusiveオプションを使用すると、解析に疑いのある場合であってもエラーメッセージを表示します。 + + cppcheck --inconclusive path + + これは、もちろん、実際に問題がないものに対しても、警告することになります。このオプションは、疑いのある警告を表示してもよい場合に限り、使用してください。 +
+
+ +
+ 結果をファイルに保存 + + 多くの場合、チェックの結果をファイルに保存したいと考えるでしょう。通常のシェルのリダイレクション機能を使って、エラー出力をファイルに保存することができます。 + + cppcheck file1.c 2> err.txt +
+ +
+ マルチスレッドチェック + + オプションの-j を使用してスレッド数を指定することができます。例えば、4スレッドを使ってフォルダ以下の全てのファイルをチェックする場合は次のように実行します。 + + cppcheck -j 4 path + + このチェックでは未使用関数の検出(unusedFunction checking)は無効になることに注意してください。 +
+ +
+ プラットフォーム + + あなたがターゲットとするプラットフォームの設定を使用すべきです。 + + デフォルトで、Cppcheckはネイティブのプラットフォームの設定を使用しますので、あなたのソースコードがローカルの環境でコンパイルし実行する場合には正常に動作するでしょう。 + + Cppcheck にはビルトインのプラットフォーム設定として、unixwindowsをターゲットにしたものがあります。コマンドラインオプションの--platformを使ってプラットフォーム設定を指定できます。 + + XMLファイルで自身のプラットフォームにあった設定ファイルを作成することもできます。ここに例をあげます。: + + <?xml version="1"?> +<platform> + <char_bit>8</char_bit> + <default-sign>signed</default-sign> + <sizeof> + <short>2</short> + <int>4</int> + <long>4</long> + <long-long>8</long-long> + <float>4</float> + <double>8</double> + <long-double>12</long-double> + <pointer>4</pointer> + <size_t>4</size_t> + <wchar_t>2</wchar_t> + </sizeof> +</platform> +
+
+ + + Project(プロジェクト) + + CMakeやVisual Studioを使っているとき、あなたは--projectを使ってプロジェクトを解析できます。 + + これでカンタンにチェックでき、結果も得られます。あなたに必要な設定項目はありません。しかしこれが最も良い結果を得る方法とは限りません。私たちは、このプロジェクトファイルを利用する方法と、--projectを利用しない方法を試してよいオプションを選ぶようにお勧めします。 + +
+ CMake + + Cppcheckはコンパイルデータベースを理解します。あなたはこれをCMakeで生成できます。 + + 例: + + $ cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON . + + compile_commands.jsonファイルが現在のディレクトリに生成されます。 + + それからCppcheckをこのように実行します。: + + $ cppcheck --project=compile_commands.json +
+ +
+ Visual Studio + + あなたは個々にのプロジェクトファイル(*.vcxproj)でCppcheckを実行できますし、ソルーション全体(*.sln)でも実行できます。 + + # run cppcheck on a whole solution +$ cppcheck --project=foobar.sln + +# run cppcheck on a individual project +$ cppcheck --project=foobar.vcxproj + + Visual Studio内でcppcheckを実行するための、Visual Studioプラグインもあります。 +
+
+ + + プリプロセッサの設定 + + あなたが --projectを使用した場合、Cppcheckはプロジェクトファイルからプリプロセッサーの設定を読み取ります。 + + そうでなければ、あなたはインクルードパスやディレクティブを設定したくなるでしょう。 + +
+ ディレクティブ + + ここに2つの設定があるファイルがあります(Aが定義された場合と定義されていない場合): + + #ifdef A + x = y; +#else + x = z; +#endif + + Cppcheckはデフォルトでプリプロセッサのデファインのコンパイルスイッチ設定の組み合わせを全てチェックします。(ただし、これらのうち #error を除く)そのため上のコードは、Aが定義された場合とAが定義されていない場合の両方を解析します。 + + これを変更するには -D を使います。また -D を使用した場合、cppcheckは与えられたコンパイルスイッチだけが有効でその他は設定されていないとしてチェックします。これは、コンパイラのように動作します。また、 --force--max-configs を使用すると、コンパイルスイッチの組み合わせの上限を上書きしてチェックすることができます。 + + # 全てのコンパイルスイッチの組み合わせをチェックする。 +cppcheck file.c + +# Aのコンパイルスイッチが有効になっている場合の組み合わせをチェックする +cppcheck -DA file.c + +# check all configurations when macro A is defined +cppcheck -DA --force file.c + + また、もう一つのオプションに-U があります。これはシンボルのundefとなります。使用例: + + cppcheck -UX file.c + + これはXが定義されていないことを意味します。Cppcheck は Xが定義されている組み合わせをチェックしません。 +
+ +
+ インクルードパス指定 + + インクルードパスを追加するには-Iオプションに続けてパスを指定します。 + + Cppcheckのプリプロセッサは基本的に他のプリプロセッサと同様にインクルードを扱います。しかし、その他のプリプロセッサはヘッダファイルが見つからない場合に停止するのとは違って、cppcheckはただ単に、メッセージ情報を表示してソースコードの解析を続けます。 + + cppcheckは常にソースコード全体を確認する必要がないので、このような仕様になっています。実際に、全てのインクルードパスを与えないことを推奨しています。もちろん、クラスのメンバーの実装を確認した上でクラスの宣言をCppcheckでチェックするのは有用ではありますが、標準ライブラリのヘッダーをCppcheckに確認させるのは有用ではありません。というのは、チェックにかかる時間が長くなり、あまりよくない結果が表示されるからです。そのような場合、.cfg ファイル (後述します)によってcppcheckに関数や型の実装の情報を提供する方がよいでしょう。 +
+
+ + + XML出力 + + Cppcheckは出力をXML形式に変更できます。--xml オプションでフォーマットを指定します。 + + ファイルをチェックし、XML形式で出力するコマンドのサンプルです。: + + cppcheck --xml file1.cpp + + これが出力例です。: + + <?xml version="1.0" encoding="UTF-8"?> +<results version="2"> + <cppcheck version="1.66"> + <errors> + <error id="someError" severity="error" msg="short error text" + verbose="long error text" inconclusive="true" cwe="312"> + <location file0="file.c" file="file.h" line="1"/> + </error> + </errors> +</results> + +
+ <error> 要素 + + それぞれのエラーは<error>要素に記載されます。属性: + + + + id + + + エラーのidこれは、妥当なシンボル名です。 + + + + + severity + + + 以下のいずれかです: error, warning, style, performance, portability, information + + + + + msg + + + 短い形式のエラーメッセージ + + + + + verbose + + + 長い形式のエラーメッセージ + + + + + inconclusive + + + この属性は、メッセージに疑いのある場合にのみ使用されます。 + + + + + cwe + + + メッセージのCWE ID。この属性は、メッセージのCWE IDが判明している場合のみ使用されます。 + + + +
+ +
+ <location>要素 + + エラーに関連する全ての位置情報は<location> 要素内にリストアップされます。主要な位置は、リストの最初の要素になります。 + + 属性: + + + + file + + + ファイル名相対パスまたは絶対パスのどちらかです。 + + + + + file0 + + + ソースファイルの名前(オプション) + + + + + line + + + + + + + + info + + + オプションの、それぞれの位置につiての短い情報 + + + +
+
+ + + 出力の形式の変更 + + もし、テンプレートを使用して、出力の形式を変更することができます。 + +
+ 事前定義した出力フォーマット + + Visual Studioに互換性のある形式が必要な場合には、--template=vsを使用します。 + + cppcheck --template=vs samples/arrayIndexOutOfBounds/bad.c + + このオプションは出力形式を次のように変更します。: + + Checking samples/arrayIndexOutOfBounds/bad.c ... +samples/arrayIndexOutOfBounds/bad.c(6): error: Array 'a[2]' accessed at index 2, which is out of bounds. + + gccに互換性のある出力が必要な場合には、--template=gccを使用します。: + + cppcheck --template=gcc samples/arrayIndexOutOfBounds/bad.c + + このオプションは出力形式を次のように変更します。: + + Checking samples/arrayIndexOutOfBounds/bad.c ... +samples/arrayIndexOutOfBounds/bad.c:6:6: warning: Array 'a[2]' accessed at index 2, which is out of bounds. [arrayIndexOutOfBounds] + a[2] = 0; + ^ +
+ +
+ ユーザー定義出力形式(1行) + + 自分で自身でパターンを作成できます。例えば古いgcc のよuな出力フォーマットで警告メッセージを出力してほしい場合次のように指定します。: + + cppcheck --template="{file}:{line}: {severity}: {message}" samples/arrayIndexOutOfBounds/bad.c + + このオプションは出力形式を次のように変更します。: + + Checking samples/arrayIndexOutOfBounds/bad.c ... +samples/arrayIndexOutOfBounds/bad.c:6: error: Array 'a[2]' accessed at index 2, which is out of bounds. + + コンマ区切りフォーマット: + + cppcheck --template="{file},{line},{severity},{id},{message}" samples/arrayIndexOutOfBounds/bad.c + + このオプションは出力形式を次のように変更します。: + + Checking samples/arrayIndexOutOfBounds/bad.c ... +samples/arrayIndexOutOfBounds/bad.c,6,error,arrayIndexOutOfBounds,Array 'a[2]' accessed at index 2, which is out of bounds. +
+ +
+ ユーザー定義出力形式(複数行) + + 多くの警告は、複数の位置を指定します。サンプルコード: + + void f(int *p) +{ + *p = 3; // line 3 +} + +int main() +{ + int *p = 0; // line 8 + f(p); // line 9 + return 0; +} + + 3行目でヌルポインタのデリファレンスの可能性があります。Cppcheckは追加の位置情報を表示してその結論がどこから発生したかを示すことができます。そのためには、コマンドラインで--template--template-locationの両方を使用する必要があります。 + + サンプルコマンド: + + cppcheck --template="{file}:{line}: {severity}: {message}\n{code}" --template-location="{file}:{line}: note: {info}\n{code}" multiline.c + + cppcheck は次のように出力します。 + + Checking multiline.c ... +multiline.c:3: warning: Possible null pointer dereference: p + *p = 3; + ^ +multiline.c:8: note: Assignment 'p=0', assigned value is 0 + int *p = 0; + ^ +multiline.c:9: note: Calling function 'f', 1st argument 'p' value is 0 + f(p); + ^ +multiline.c:3: note: Null pointer dereference + *p = 3; + ^ + + この警告の最初の行は--template で指定したフォーマットです。 + + この警告の残りの行は--template-locationで指定したフォーマットです。 +
+ +
+ --templateで指定するフォーマット + + --template では以下の要素が利用できます。: + + + + {file} + + + ファイル名 + + + + + {line} + + + 行数 + + + + + {column} + + + カラム番号 + + + + + {callstack} + + + 全ての位置。それぞれの位置は[{file}:{line}]のフォーマットで記載され、また->で位置を区切ります。例えば次のようになります。: [multiline.c:8] -> [multiline.c:9] -> [multiline.c:3] + + + + + {inconclusive:text} + + + 警告が確定的でない場合のメッセージを表示します。このメッセージは含まれていない場合もある、任意のテキストです。サンプル: {inconclusive:inconclusive,} + + + + + {severity} + + + エラー/警告/スタイル/性能/移植性/情報 + + + + + {message} + + + 警告メッセージ + + + + + {id} + + + 警告id + + + + + {code} + + + 実際のコード + + + + + \t + + + タブ + + + + + \n + + + 改行 + + + + + \r + + + キャリッジリターン + + + +
+ +
+ --template-location で指定するフォーマット + + --template-locationでは以下の要素が利用できます。: + + + + {file} + + + ファイル名 + + + + + {line} + + + 行数 + + + + + {column} + + + カラム番号 + + + + + {info} + + + 現在位置についての情報メッセージ + + + + + {code} + + + 実際のコード + + + + + \t + + + タブ + + + + + \t + + + 改行 + + + + + \r + + + キャリッジリターン + + + +
+
+ + + Misra + + CppcheckはMISRA C 2012 向けのチェッカのアドオンを持っています。 + +
+ 要求事項 + + 必要なもの: + + + + Python (2系 または 3系) + + + + MISRA C 2012の PDFこのPDFはhttp://www.misra.org.ukで購入できます (15-20 ポンド) + + +
+ +
+ MISRA テキストファイル + + MISRAルールテキストの公開は禁止されています。そのためMISRAルールテキストはこのアドオンから直接利用できません。代わりにこのアドオンはテキストファイルからルールのテキストを読み込みます。MISRA PDFの ”Appendix A Summary of guidelines"のテキストをコピーペーストした場合、それがルールのテキストになります。 + + もしあなたがxpdfを持っているなら、テキストファイルはコマンドラインから簡単に生成できます。 (pdftotextxpdfに含まれています。): + + pdftotext misra-c-2012.pdf output.txt + + この出力は100%完璧であるとは限りません。少し手直しする必要があることもあります。 + + その他のpdfからtextに変換するソフトでもうまくいくでしょう。 + + テキストファイルをマニュアルで作成してするには、MISRA PDFの Appendix A "Summary of guidelines" をコピーペーストします。フォーマット: + + Appendix A Summary of guidelines +Rule 1.1 +Rule text +Rule 1.2 +Rule text +... + + あなたが無効にしたいルールは、ルールテキストがなくても構いません。ルールテキストのないルールはアドオンによって抑制されます。 +
+
+ + + 出力の抑制 + + ある種のエラーをフィルタリングしたい場合、出力を抑制することができます。 + +
+ プレーンテキスト抑制 + + エラーの種類によって出力を抑制することができます。つぎのいずれかの形式で出力を抑制します。: + + [error id]:[filename]:[line] +[error id]:[filename2] +[error id] + + このerror id は抑制したいエラーのidです。このエラーのIDを簡単に調べるには、--xmlオプションをコマンドラインで与えます。そのXML出力から、idの文字列が取得できます。このエラーのIDに*を指定して全ての種類のメッセージを抑制することができます。(これは指定したファイルに限ることができます。) + + またfilenameにはワイルドキャラクターである、* または ?を含めることができます。前者には全ての文字列にマッチし、後者は任意の一文字にマッチします。またWindowsを含む全てのOSで、パス区切りに"/" を使うことをお勧めします。 + +
+ コマンドライン抑制 + + --suppress=のコマンドラインオプションを使用して、コマンドラインで抑制を指定することができます。例: + + cppcheck --suppress=memleak:src/file1.cpp src/ +
+ +
+ ファイルで抑制リストを指定 + + また、抑制ファイルを作成することもできます。例: + + // src/file1.cppのmemleak と exceptNew の エラーを抑制 +memleak:src/file1.cpp +exceptNew:src/file1.cpp + +// 全てのファイルのuninitvarエラーを抑制する。 +uninitvar + + 空行やコメント行を抑制ファイルに記載することができます。 + + そして、この抑制ファイルは次のようにして使用します。: + + cppcheck --suppressions-list=suppressions.txt src/ +
+
+ +
+ XML 抑制 + + XMLファイルで抑制を指定できます。サンプルファイル: + + <?xml version="1.0"?> +<suppressions> + <suppression> + <id>uninitvar</id> + <fileName>src/file1.c</fileName> + <lineNumber>10</lineNumber> + <symbolName>var</symbolName> + </suppression> +</suppressions> + + このXMLフォーマットは拡張可能であり、将来さらなる属性を加えるかもしれません。 +
+ +
+ インライン出力抑制 + + エラー出力の抑制をソースコード中に直接、コメントの形で記載することもできます。このコメントには特別なキーワードを含めて記載します。ただし、インライン出力を抑制するコメントをソースコードに追加すると、ソースコードの可読性が少し悪くなってしまうかもしれません。 + + このソースコードは通常エラメッセージを出力する例です。: + + void f() { + char arr[5]; + arr[10] = 0; +} + + 前のソースコードに対する出力は次のようになります。: + + # cppcheck test.c +Checking test.c... +[test.c:3]: (error) Array 'arr[5]' index 10 out of bounds + + このエラーメッセージを抑制するには次のようなコメントを追加します。: + + void f() { + char arr[5]; + + // cppcheck-suppress arrayIndexOutOfBounds + arr[10] = 0; +} + + これで、--inline-suppr オプションの準備ができました。次のようにcppcheckを起動するとエラーが抑制されます。: + + cppcheck --inline-suppr test.c + + 特定のシンボルにのみ適用するインライン抑制を指定できます。: + + // cppcheck-suppress arrayIndexOutOfBounds symbolName=arr + + 抑制のためにコメントを書きます。; や // を使って開始点を指定できます。 + + // cppcheck-suppress arrayIndexOutOfBounds ; some comment +// cppcheck-suppress arrayIndexOutOfBounds // some comment +
+
+ + + ライブラリ設定 + + WinAPI, POSIX, gtk, Qtなど他の外部のライブラリを使用した場合、Cppcheckは外部の関数がどのようなものであるかがわかりません。Cppcheck はそのため、メモリリークやバッファオーバーフロー、ヌルポインタのデリファレンスの可能性といったさまざまな問題が検出できません。これを解決するには設定ファイル(.cfg file)を使用します。 + + Cppcheckはいくつかのライブラリ用の設定を持っています。これらは次のようにしてロードできます。cppcheckは C または C++言語用の標準ライブラリの設定 std.cfgはいつもロードします。ご注意ください。もしあなたが有名なライブラリの設定ファイルを作成した場合や更新した場合には、私達のサイトにアップロードしてくれると非常に助かります。 + +
+ カスタム設定ファイル(.cfg file)の使用 + + あなたのプロジェクト専用の設定ファイルを作成し、使用することができます。そのためには、--check-library--enable=information を使用して設定のためのヒントを入手します。 + + 設定ファイルの編集に、Library Editorの使用をお勧めします、これはCppcheck GUIに含まれています。これはViewメニューで使用できます。すべての設定がこのマニュアルに載っていません。 + + この設定ファイル.cfgのフォーマットに質問がある場合、フォーラム(http://sourceforge.net/p/cppcheck/discussion/)で質問してください。 + + コマンドラインのcppcheck はカスタマイズした設定ファイル(.cfg files)を作業パスから読み込もうとします。作業パスはcppcheckを実行しているパスですでそこに設定ファイルがあると考えます。 + + GUIのcppcheckはプロジェクトのファイルパスから設定ファイルを読み込もうとします。カスタマイズした設定ファイル(.cfg file)は プロジェクトファイルの編集 ダイアログで確認できます。このタイアログを表示させるにはファイル メニューから開いてください。 +
+ +
+ メモリーリソースのリーク + + Cppcheck はリークのチェックが調整できます。言い換えれば、あなたはメモリーやリソースを割り当てる関数またはその割り当てを回収する関数を指定できます。 + +
+ alloc と dealloc + + ここにサンプルのプログラムがあります。: + + void test() +{ + HPEN pen = CreatePen(PS_SOLID, 1, RGB(255,0,0)); +} + + 上のコード例はリソースリークの欠陥があります。 - CreatePen() は WinAPI 関数でpenを作成します。しかし、Cppcheckは関数からの返り値が解放されていなければならないと仮定しません。そのためエラーメッセージは表示されません。: + + # cppcheck pen1.c +Checking pen1.c... + + もしあなたが設定ファイルを与えれば、Cppcheckはバグを検出します。: + + # cppcheck --library=windows.cfg pen1.c +Checking pen1.c... +[pen1.c:3]: (error) Resource leak: pen + + これが最小限のwindows.cfg ファイルです: + + <?xml version="1.0"?> +<def> + <resource> + <alloc>CreatePen</alloc> + <dealloc>DeleteObject</dealloc> + </resource> +</def> + + このアロケート関数とデアロケート関数はグループにまとめられています。それぞれのグループは<resource><memory> タグ中で定義されており、その<dealloc>関数によって特定されます。これは、<dealloc>タグでオーバーラップしたグループはマージされます。 +
+ +
+ leak-ignore とuse + + しばしば、割り当てられたポインタを関数に渡すことがあります。例: + + void test() +{ + char *p = malloc(100); + dostuff(p); +} + + もし設定ファイルがなく、Cppcheckがdostuffの仕様を把握していなければ、Cppcheckはdostuffがメモリーについて配慮しており、メモリーリークは発生しないと仮定します。 + + dostuffがメモリーについて配慮せず、解放などを行なっていないことを指定するためには、leak-ignore<function> タグ中で使います。: + + <?xml version="1.0"?> +<def> + <function name="dostuff"> + <leak-ignore/> + <arg nr="1"/> + </function> +</def> + + これとは逆にdostuffがメモリーについて配慮している場合には次のように設定します。: + + <?xml version="1.0"?> +<def> + <memory> + <dealloc>free</dealloc> + <use>dostuff</use> + </memory> +</def> + + なお、この<use>の設定は論理的に全く無意味です。この設定がない場合でも同じエラーが表示されます。これは--check-libraryのinformationメッセージを減らすために使用します。 +
+
+ +
+ 関数の動作 + + 関数の動作や関数の使用方法を指定するのに、<function>タグが使えます。関数は、その名前によって特定されます。この名前は、name 属性とその引数によって指定されます。この名前はコンマで区切られた関数名のリストです。名前空間やクラス中の関数の場合には、完全修飾名で指定されます。例: <function name="memcpy,std::memcpy">もしテンプレート関数がある場合、インスタンス化した名前を提供してします。<function name="dostuff<int>">. + +
+ 関数引き数 + + 関数がとる引数は、<arg>タグで指定できます。引数のそれぞれは、引数の順番(1始まり)をnr属性で示します。nr="any" は任意の引き数を表します。また、nr="variadic"は可変長引数を表します。オプション引数は、デフォルト値で指定します。: default="value". それぞれの引数に対する設定は、全ての引数に対する指定を上書きします。 + +
+ 非ブール + + ここで誤った比較のあるサンプルプログラムがあります。: + + void test() +{ + if (MemCmp(buffer1, buffer2, 1024==0)) {} +} + + Cppcheckは、この関数にブール値を渡してよいと仮定します。: + + # cppcheck notbool.c +Checking notbool.c... + + もしあなたが設定ファイルを与えれば、Cppcheckはバグを検出します。: + + # cppcheck --library=notbool.cfg notbool.c +Checking notbool.c... +[notbool.c:5]: (error) Invalid MemCmp() argument nr 3. 非ブール値が求められています。 + + ここで最小のnotbool.cfgを用意しました。 + + <?xml version="1.0"?> +<def> + <function name="MemCmp"> + <arg nr="1"/> + <arg nr="2"/> + <arg nr="3"> + <not-bool/> + </arg> + </function> +</def> +
+ +
+ 未初期化メモリ + + ここにサンプルのプログラムがあります。: + + void test() +{ + char buffer1[1024]; + char buffer2[1024]; + CopyMemory(buffer1, buffer2, 1024); +} + + このプログラムのバグは buffer2 が初期化されていないことです。CopyMemory 関数の第二引数は初期化されている必要があります。しかし、Cppcheckは関数に未初期化の変数を渡してもよいと仮定しています。: + + # cppcheck uninit.c +Checking uninit.c... + + もしあなたが設定ファイルを与えれば、Cppcheckはバグを検出します。: + + # cppcheck --library=windows.cfg uninit.c +Checking uninit.c... +[uninit.c:5]: (error) Uninitialized variable: buffer2 + + 注意:これは、ポインタが示すメモリ領域が初期化されていなければならないことを意味しています。 + + これが最小限のwindows.cfgファイルです。: + + <?xml version="1.0"?> +<def> + <function name="CopyMemory"> + <arg nr="1"/> + <arg nr="2"> + <not-uninit/> + </arg> + <arg nr="3"/> + </function> +</def> +
+ +
+ ヌルポインタ + + Cppcheckは、関数にヌルポインタを渡してもよいと仮定しています。ここにサンプルのプログラムがあります。: + + void test() +{ + CopyMemory(NULL, NULL, 1024); +} + + MSDNの文書はこれが問題あるかないかを明らかにしていません。しかし、ここでは問題ありと仮定します。Cppcheck は関数にヌルポインタを渡してもよいと仮定していますので、エラーを出力しません。: + + # cppcheck null.c +Checking null.c... + + もしあなたが設定ファイルを与えれば、Cppcheckはバグを検出します。: + + cppcheck --library=windows.cfg null.c +Checking null.c... +[null.c:3]: (error) Null pointer dereference + + 注意:<not-uninit>は値について意味しています。初期化されていないメモリが関数に渡されています。 + + これが最小限のwindows.cfg ファイルです: + + <?xml version="1.0"?> +<def> + <function name="CopyMemory"> + <arg nr="1"> + <not-null/> + </arg> + <arg nr="2"/> + <arg nr="3"/> + </function> +</def> +
+ +
+ フォーマット文字列 + + フォーマット文字列を扱う関数を定義できます。例: + + void test() +{ + do_something("%i %i\n", 1024); +} + + これについてもエラーは報告されません。: + + # cppcheck formatstring.c + Checking formatstring.c... + + 引数がフォーマット文字列であることを出力する設定ファイルが作成できます。設定ファイルの例です。: + + <?xml version="1.0"?> +<def> + <function name="do_something"> + <formatstr type="printf"/> + <arg nr="1"> + <formatstr/> + </arg> + </function> +</def>これで、Cppcheckはエラーを報告できるようになりました。: + + cppcheck --library=test.cfg formatstring.c +Checking formatstring.c... +[formatstring.c:3]: (error) do_something format string requires 2 parameters but only 1 is given. + + このフォーマット文字列のtype属性は次のどちらかになります。: + + + + printf - printf のルールに従うフォーマット文字列 + + + + scanf - scanf のルールに従うフォーマット文字列 + + +
+ +
+ 値の範囲 + + 有効な値の範囲が定義できます。想像してください。: + + void test() +{ + do_something(1024); +} + + これについてもエラーは報告されません。: + + # cppcheck valuerange.c +Checking valuerange.c... + + 1024 が 範囲外の値であることを出力する設定ファイルが作成できます。設定ファイルの例です。: + + <?xml version="1.0"?> +<def> + <function name="do_something"> + <arg nr="1"> + <valid>0:1023</valid> + </arg> + </function> +</def>これで、Cppcheckはエラーを報告できるようになりました。: + + cppcheck --library=test.cfg range.c +Checking range.c... +[range.c:3]: (error) Invalid do_something() argument nr 1. この値は1024ですが、妥当な値は0から1023までです。 + + validの要素で次のような表現が利用できます。: + + 0,3,5 => 0, 3 それに 5 だけが有効な値です。 +-10:20 => -10 から 20 までの値(両端含む)が有効な値です。 +:0 => 0または0未満の値が有効な値です。 +0: => 0または0以上の値が有効な値です。 +0,2:32 => 0 または2から32までの値(両端含む)が有効な値です。 +-1.5:5.6 => -1.5 から 5.6 までの値(両端含む)が有効な値です。 +
+ +
+ 最小サイズ + + いくつかの関数はバッファーを引数にとります。バッファの最小サイズを指定することができます。(要素数ではなくバイト数です。)想像してください。: + + void test() +{ + char str[5]; + do_something(str,"12345"); +} + + これについてもエラーは報告されません。: + + # cppcheck minsize.c +Checking minsize.c... + + 設定ファイルで、例えば、引数1のバッファのサイズが引数2の文字列長より大きくなればならないと警告するような設定ファイルを作成できます。例を挙げます。: + + <?xml version="1.0"?> +<def> + <function name="do_something"> + <arg nr="1"> + <minsize type="strlen" arg="2"/> + </arg> + <arg nr="2"/> + </function> +</def>これで、Cppcheckはこのエラーを報告できるようになりました。: + + cppcheck --library=1.cfg minsize.c +Checking minsize.c... +[minsize.c:4]: (error) Buffer is accessed out of bounds: str + + + minsizes はいくつかの種類があります。: + + + + strlen + + + バッファーのサイズが、その他の引数の文字列長より大きくなければなりません。例: std.cfg のstrcpyの設定を参照してください。 + + + + + argvalue + + + バッファーのサイズがその他の引数の値より大きくなればなりません。例: std.cfg のmemsetの設定を参照してください。 + + + + + sizeof + + + バッファーのサイズがその他の引数のバッファーのサイズより大きくなればなりません。例:posix.cfgのmemccpyの設定をみてください。 + + + + + mul + + + バッファーのサイズがその他の2つの引数の値の積より大きくなればなりません。典型的な使用例としては、一つの引数が構造体などの要素のサイズを指定し、もうひとつの引数が要素の個数を定義するような場合です。例: std.cfg のfreadの設定を参照してください + + + +
+ +
+ strz + + これを指定すると、数が0終端文字列でなければならないということができます。 + + <?xml version="1.0"?> +<def> + <function name="do_something"> + <arg nr="1"> + <strz/> + </arg> + </function> +</def> +
+
+ +
+ noreturn + + Cppcheck はこの関数がいつも値を返すとは仮定していません。ここにサンプルのプログラムがあります。: + + void test(int x) +{ + int data, buffer[1024]; + if (x == 1) + data = 123; + else + ZeroMemory(buffer, sizeof(buffer)); + buffer[0] = data; // <- error: xが1でないとき初期化されていない +} + + 理屈の上では、ZeroMemoryがプログラムを終了させてもバグはありません。そのため Cppcheckはエラーを報告しません。: + + # cppcheck noreturn.c +Checking noreturn.c... + + しかし、--check-library をつかうとエラーが出力されます。: + + # cppcheck --check-library noreturn.c +Checking noreturn.c... +[noreturn.c:7]: (information) --check-library: Function ZeroMemory() should have <noreturn> configuration + + + もし適切な windows.cfg が提供されていましたら、このバグは検出されます。: + + # cppcheck --library=windows.cfg noreturn.c +Checking noreturn.c... +[noreturn.c:8]: (error) Uninitialized variable: data + + これが最小限のwindows.cfg ファイルです: + + <?xml version="1.0"?> +<def> + <function name="ZeroMemory"> + <noreturn>false</noreturn> + <arg nr="1"/> + <arg nr="2"/> + </function> +</def> +
+ +
+ use-retval + + 他になにも指定されていない限り、cppcheckは関数が返り値を無視していても問題ないと仮定します。: + + bool test(const char* a, const char* b) +{ + strcmp(a, b); // <- bug: strcmp の呼び出しは副作用を持ちませんが返り値を無視している。 + return true; +} + + strcmp が副作用を持つ場合、パラメータが関数に渡されている結果を無視しても問題はなく、このような仮定は正しいといえます。: + + # cppcheck useretval.c +Checking useretval.c... + + もし適切なlib.cfg が提供されていましたら、このバグは検出されます。: + + # cppcheck --library=lib.cfg --enable=warning useretval.c +Checking useretval.c... +[useretval.c:3]: (warning) Return value of function strcmp() is not used. + + これが最小限のlib.cfg ファイルです。: + + <?xml version="1.0"?> +<def> + <function name="strcmp"> + <use-retval/> + <arg nr="1"/> + <arg nr="2"/> + </function> +</def> +
+ +
+ pure関数(pure)とconst関数 + + これらは、GCC関数属性のpureとconstに対応します。 + + pure関数は、値を返す以外の効果を持ちません。そしてその返り値はその関数の引数とグローバル変数によってのみ決まります。 + + const関数は、値を返す以外の効果を持ちません。そしてその返り値はその関数の引数によってのみ決まります。 + + ここにサンプルのプログラムがあります。: + + void f(int x) +{ + if (calculate(x) == 213) { + + } else if (calculate(x) == 213) { + // 到達不能コード + } +} + + もしcalculate() がconst関数であれば、calculate(x)は両方の条件で同じ値を返します。というのも、同じパラメータを引数にしているからです。 + + Cppcheck は通常、その結果が異なると仮定するため、Cppcheckはこのコード例に警告を出しません。: + + # cppcheck const.c +Checking const.c... + + もし適切なconst.cfg が提供されていましたら、このバグは検出されます。: + + # cppcheck --enable=style --library=const const.c +Checking const.c... +[const.c:7]: (style) Expression is always false because 'else if' condition matches previous condition at line 5. + + これが最小限のconst.cfg ファイルです。: + + <?xml version="1.0"?> +<def> + <function name="calculate"> + <const/> + <arg nr="1"/> + </function> +</def> +
+ +
+ 関数strcpyの設定例 + + 標準関数のstrcpyのための適切な設定は次のようになる。: + + <function name="strcpy"> + <leak-ignore/> + <noreturn>false</noreturn> + <arg nr="1"> + <not-null/> + </arg> + <arg nr="2"> + <not-null/> + <not-uninit/> + <strz/> + </arg> + </function> + + この<leak-ignore/> は、リークチェック中に関数呼び出しを無視するように、Cppcheckに伝えます。この関数は、割り当てられたメモリを解放しないことを意味しています。 + + この<noreturn> は、この関数が、返り値を返すかどうかをCppchecに伝えます。 + + この関数は第一引数にポインタを取ります。しかしこのポインタは、ヌルポインタであってはなりません。というのは<not-null>が使用されているからです。 + + この関数は第二引数にポインタを取ります。このポインタはヌルポインタであってはなりません。また、このポインタは初期化されたデータを指していなければなりません。<not-null><not-uninit> は正しく使用されています。さらにいえば、このポインタは0終端文字列(zero-terminated string)でなければなりません。そのため<strz>が使用されています。 +
+
+ +
+ define + + ライブラリはマクロプリプロセッサのdefineを使用することができます。例: + + <?xml version="1.0"?> +<def> + <define name="NULL_VALUE" value="0"/> +</def> + + プリプロセッサの段階でソースコード中に "NULL_VALUE" が現れるごとに、"0"で置き換えます。 +
+ +
+ podtype + + 多くのソースコードで、プラットフォームに依存しない型をtypedefによって定義しています。"podtype"のタグによって、cppcheckがこれらをサポートするために必要な情報を提供できます。このような情報のない場合、cppcheckは次の例でみるような "uint16_t" 型を理解できません。 + + void test() { + uint16_t a; +} + + そのため、未使用変数である、'a'が未使用であるとのメッセージが表示されません。 + + # cppcheck --enable=style unusedvar.cpp +Checking unusedvar.cpp... + + もし uint16_t が以下のように定義されていた場合、結果にメッセージが反映されます。 + + <?xml version="1.0"?> +<def> + <podtype name="uint16_t" sign="u" size="2"/> +</def> + + 型のサイズはバイトサイズで指定します。符号の "sign" 属性は 符号ありの "s" か 符号無し "u" のどちらかです。これらの属性はオプションです。このライブラリを使用しますと、cppcheckはメッセージを表示できるようになります。 + + # cppcheck --library=lib.cfg --enable=style unusedvar.cpp +Checking unusedvar.cpp... +[unusedvar.cpp:2]: (style) Unused variable: a +
+ +
+ コンテナ(container) + + C++ ライブラリの多くや STL 自身は、非常によく似た機能性をもつコンテナを提供します。ライブラリによってその動作をcppcheckに伝えられます。それぞれのコンテナの設定にはユニークなIDが必要とします。コンテナの設定には、startPatternを加えることができます(オプション)。この startPatternはToken::Match パターンとendPattern に有効でなけばなりません。また、このendPatternはリンクしているトークンと比較されるものです。オブション属性の"inherits"は事前に定義されたコンテナのIDをとります。 + + <container>タグの内部で、<size>、<access>、<other>を選択して使用して関数を定義できます。これらのタグはそれぞれ、"resize" やその結果を与えるような動作を指定することができます。その例 "end-iterator"を示します。 + + 次の例は、std::vectorの為の定義を示しています。std::vectorは"stdContainer"の定義に基づいていますが、ここには表示していません。: + + <?xml version="1.0"?> +<def> + <container id="stdVector" startPattern="std :: vector &lt;" inherits="stdContainer"> + <size> + <function name="push_back" action="push"/> + <function name="pop_back" action="pop"/> + </size> + <access indexOperator="array-like"> + <function name="at" yields="at_index"/> + <function name="front" yields="item"/> + <function name="back" yields="item"/> + </access> + </container> +</def> +
+
+ + + ルール(Rules) + + 正規表現を使用して、ユーザーがルール(rule)を定義することができます。 + + これらのカスタムルールは、ソースコードを高度に分析した結果を使用することができません。しかしソースコード中の非常にシンプルなパターンについて簡単にルールを作成することができます。 + + ルールの作成を始めるには次の関連記事を参照してください。: + + http://sourceforge.net/projects/cppcheck/files/Articles/ + + ルールのファイルフォーマットは次のとおりです。: + + <?xml version="1.0"?> +<rule> + <tokenlist>LIST</tokenlist> + <pattern>PATTERN</pattern> + <message> + <id>ID</id> + <severity>SEVERITY</severity> + <summary>SUMMARY</summary> + </message> +</rule> + + patternタグ中にCDATAを含めた場合、XMLに干渉する可能性がありますので使用時はご注意ください。: + + <![CDATA[some<strange>pattern]]> + +
+ <tokenlist> + + この<tokenlist> 要素はオプションです。この要素がある場合、どのトークンをチェックするかを指示することができます。このLISTdefine, raw, normal , simpleのいずれかです。 + + + + define + + + #define プリプロセッサの記述をチェックするために使用します。 + + + + + raw + + + プリプロセッサの出力をチェックするために使用します。 + + + + + normal + + + normal のトークンリストをチェックするために使用します。ソースコードをある程度、単純化した結果をチェックすることになります。 + + + + + simple + + + 単純なトークンリストをチェックするために使用します。ソースコードを完全に単純化した結果をチェックすることになります。ほとんどの Cppcheckのチェックには、この 単純ばトークンリストを使用します。 + + + + + もし<tokenlist>要素を省略した場合、simple が使用されます。 +
+ +
+ <pattern> + + このPATTERN にはPerlの正規表現と互換性のある正規表現 PCREを指定します。 +
+ +
+ <id> + + この ID にはユーザーが定義した message idを指定します。 +
+ +
+ <severity> + + このSEVERITYにはCppcheck の厳格度(severities)である、次のいずれかを指定します。: information, performance, portability, style, warning,error +
+ +
+ <summary> + + オプションです。メッセージのサマリーです。もしこのsummaryトークンが指定されていなければ、マッチしたトークンが出力されます。 +
+
+ + + Cppcheck アドオン + + Cppcheckのアドオンは、個別のスクリプトや個別のプログラムとして実装されています。Cppcheckのアドオンを使用すると次のような利点があります。 + + + + 洗練された分析の結果を使用した個別の、外部チェックを追加できます。 + + + + ソースコードが可視化できます。 + + + + その他 + + + +
+ Cppcheckアドオンの使用方法 + + 現在、アドオンを使用するには2段階の操作が必要です。: + + + + Cppcheckを実行し、ダンプファイルを生成します。 + + + + アドオンでダンプファイルを処理します。 + + + + --dumpフラグを使用するとダンプファイルを生成できます。foo/ フォルダ以下の全てのソースファイルからダンプファイルを生成するには次のようにします。 + + cppcheck --dump foo/ + + foo/ フォルダ以下の全てのダンプファイルをアドオンで処理するには次のようにします。 + + python addon.py foo/*.dump + +
+ Cppcheckアドオンの見つけ方 + + ダウンロードできる、アドオンがいくつかあります。 + + + + Cppcheck プロジェクトはいくつかのアドオンを以下の場所で提供しています。: http://github.com/danmar/cppcheck/blob/master/addons + + + + ublinterは規格で定義されていない未定義動作に注力した"lint"です。: http://github.com/danmar/ublinter + + + + あなたのアドオンの情報をご紹介ください。(商用、フリーを問いません。) +
+
+ +
+ Cppcheck アドオンの作成 + + Cppcheck は XML形式でダンプファイルを生成できます。このファイルには以下のようなものが含まれています。: + + + + トークンリスト(Token list) + + + + シンタックスツリー(Syntax trees) + + + + シンボルデータベース(関数、クラス、変数、スコープ) + + + + 既知の値(value flow analysis) + + + + Cppcheckはアドオンを直接実行することはできません。直接実行するためにインターフェースはありません。これは、次のような制限がないことを意味します。: + + + + アドオンを作成しリリースする際に、どのようなライセンスでも適用できます。 + + + + アドオンの作成に、どのようなスクリプト言語やプログラミング言語で作成できます。 + + + + アドオン作成者がユーザーインターフェースと出力を決定できます。 + + + + 警告の生成以外の目的にもアドオン使用できます。 + + + + アドオン作成者の利便性のために、Cppcheck プロジェクトは PythonからCppcheckのデータにアクセスするための cppcheckdata.pyを提供しています。cppcheckdata.pyの使用はオプションです。 + +
+ 使用例1 - 全トークンの表示 + + Script: + + import sys +import cppcheckdata + +def printtokens(data): + for token in data.tokenlist: + print(token.str) + +for arg in sys.argv[1:]: + printtokens(cppcheckdata.parse(arg)) +
+ +
+ 使用例2 - 全関数リストアップ + + Script: + + import sys +import cppcheckdata + +def printfunctions(data): + for scope in data.scopes: + if scope.type == 'Function': + print(scope.className) + +for arg in sys.argv[1:]: + printfunctions(cppcheckdata.parse(arg)) +
+ +
+ 使用例 3 - 全クラスリストアップ + + Script: + + import sys +import cppcheckdata + +def printclasses(data): + for scope in data.scopes: + if scope.type == 'Class': + print(scope.className) + +for arg in sys.argv[1:]: + printfunctions(cppcheckdata.parse(arg)) +
+
+
+ + + HTML 形式での報告 + + cppcheckのXML出力をHTML形式に変更できます。これを利用するには、Python と pygments module (http://pygments.org/) が必要です。Cppcheckのソースツリーにhtmlreportというフォルダがあります。このフォルダには、CppcheckのXMLファイルをHTML出力に変換するスクリプトがあります。 + + このコマンドでヘルプ画面を生成するには次のように実行します。 + + htmlreport/cppcheck-htmlreport -h + + 出力画面には次の内容が表示されます。: + + Usage: cppcheck-htmlreport [options] + +Options: + -h, --help show this help message and exit + --file=FILE The cppcheck xml output file to read defects from. + Default is reading from stdin. + --report-dir=REPORT_DIR + The directory where the html report content is written. + --source-dir=SOURCE_DIR + Base directory where source code files can be found. + + 使用例: + + ./cppcheck gui/test.cpp --xml 2> err.xml +htmlreport/cppcheck-htmlreport --file=err.xml --report-dir=test1 --source-dir=. + + + + グラフィカルインターフェースGUI + +
+ イントロダクション + + Cppcheck GUIが利用できます。 + + メイン画面は、このソフトを起動時に表示されます。 +
+ +
+ ソースコードのチェック + + Checkメニューを使用します。 +
+ +
+ 結果の確認 + + 結果はリスト表示されます。 + + View メニューを操作して、メッセージの種類毎に表示/非表示を切り替えできます。 + + 結果をXML ファイルに保存して、後で確認できます。Save results to fileOpen XMLを参照してください。 +
+ +
+ 設定 + + Languageメニューからいつでも使用言語を変更できます。 + + 設定は、 Edit Preferences で変更できます。 +
+ +
+ プロジェクトファイル + + プロジェクトファイルは、プロジェクト固有の設定を保存するのに使用します。固有の設定には次のものがあります。: + + + + インクルードパス + + + + プリプロセッサのdefine + + + + このマニュアルの3 章にあるように、全てのコンパイルスイッチの組み合わせをチェックします。コンパイルスイッチの組み合わせを制限したい場合にだけ、プリプロセッサのdefineを指定してください。 +
+
+
diff --git a/cppcheck-2.14.0/man/manual-premium.md b/cppcheck-2.14.0/man/manual-premium.md new file mode 100644 index 00000000..1b73d199 --- /dev/null +++ b/cppcheck-2.14.0/man/manual-premium.md @@ -0,0 +1,1152 @@ +--- +title: Cppcheck Premium manual +author: Cppcheck team, Cppcheck Solutions AB +lang: en +documentclass: report +--- + +# Introduction + +Cppcheck is an analysis tool for C/C++ code. It provides unique code analysis to detect bugs and focuses on detecting +undefined behaviour and dangerous coding constructs. The goal is to detect only real errors in the code, and generate +as few false positives (wrongly reported warnings) as possible. Cppcheck is designed to analyze your C/C++ code even +if it has non-standard syntax, as is common in for example embedded projects. + +Supported code and platforms: + +- Cppcheck checks non-standard code that contains various compiler extensions, inline assembly code, etc. +- Cppcheck should be compilable by any compiler that supports C++11 or later. +- Cppcheck is cross platform and is used in various posix/windows/etc environments. + +The checks in Cppcheck are not perfect. There are bugs that should be found, that Cppcheck fails to detect. + +## About static analysis + +The kinds of bugs that you can find with static analysis are: + +- Undefined behavior +- Using dangerous code patterns +- Coding style + +There are many bugs that you can not find with static analysis. Static analysis tools do not have human knowledge about +what your program is intended to do. If the output from your program is valid but unexpected then in most cases this is +not detected by static analysis tools. For instance, if your small program writes "Helo" on the screen instead of "Hello" +it is unlikely that any tool will complain about that. + +Static analysis should be used as a complement in your quality assurance. It does not replace any of; + +- Careful design +- Testing +- Dynamic analysis +- Fuzzing + +# Getting started + +## GUI + +It is not required but creating a new project file is a good first step. There are a few options you can tweak to get +good results. + +In the project settings dialog, the first option you see is "Import project". It is recommended that you use this +feature if you can. Cppcheck can import: + +- Visual studio solution / project +- Compile database, which can be generated from CMake/qbs/etc build files +- Borland C++ Builder 6 + +When you have filled out the project settings and clicked on OK, the Cppcheck analysis will start. + +## Command line + +### First test + +Here is some 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. Cppcheck can check all source files in a directory: + + cppcheck path + +If "path" is a folder, then Cppcheck will recursively check all source files in this folder: + + Checking path/file1.cpp... + 1/2 files checked 50% done + Checking path/file2.cpp... + 2/2 files checked 100% done + +### Check files manually or use project file + +With Cppcheck you can check files manually by specifying files/paths to check and settings. Or you can use a build +environment, such as CMake or Visual Studio. + +We don't know which approach (project file or manual configuration) will give you the best results. It is recommended +that you try both. It is possible that you will get different results so that to find the largest amount of bugs you +need to use both approaches. Later chapters will describe this in more detail. + +### Check files matching a given file filter + +With `--file-filter=` you can set a file filter and only those files matching the filter will be checked. + +For example: if you want to check only those files and folders starting from a subfolder src/ that start with "test" +you have to type: + + cppcheck src/ --file-filter=src/test* + +Cppcheck first collects all files in src/ and will apply the filter after that. So the filter must start with the given +start folder. + +### 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, which specifies the files/paths to ignore. With this command no files in src/c are +checked: + + cppcheck -isrc/c src + +This option is only valid when supplying an input directory. To ignore multiple directories supply the -i flag for each +directory individually. The following command ignores both the src/b and src/c directories: + + cppcheck -isrc/b -isrc/c + +### Clang parser (experimental) + +By default Cppcheck uses an internal C/C++ parser. However there is an experimental option to use the Clang parser instead. + +Install `clang`. Then use Cppcheck option `--clang`. + +Technically, Cppcheck will execute `clang` with its `-ast-dump` option. The Clang output is then imported and converted into +the normal Cppcheck format. And then normal Cppcheck analysis is performed on that. + +You can also pass a custom Clang executable to the option by using for example `--clang=clang-10`. You can also pass it +with a path. On Windows it will append the `.exe` extension unless you use a path. + +## Severities + +The possible severities for messages are: + +**error** + +when code is executed there is either undefined behavior or other error, such as a memory leak or resource leak + +**warning** + +when code is executed there might be undefined behavior + +**style** + +stylistic issues, such as unused functions, redundant code, constness, operator precedence, possible mistakes. + +**performance** + +run time performance suggestions based on common knowledge, though it is not certain any measurable speed difference +will be achieved by fixing these messages. + +**portability** + +portability warnings. Implementation defined behavior. 64-bit portability. Some undefined behavior that probably works +"as you want", etc. + +**information** + +configuration problems, which does not relate to the syntactical correctness, but the used Cppcheck configuration could +be improved. + +## Possible speedup analysis of template code + +Cppcheck instantiates the templates in your code. + +If your templates are recursive this can lead to slow analysis that uses a lot +of memory. Cppcheck will write information messages when there are potential +problems. + +Example code: + + template + void a() + { + a(); + } + + void foo() + { + a<0>(); + } + +Cppcheck output: + + test.cpp:4:5: information: TemplateSimplifier: max template recursion (100) reached for template 'a<101>'. You might want to limit Cppcheck recursion. [templateRecursion] + a(); + ^ + +As you can see Cppcheck has instantiated `a` until `a<101>` was reached +and then it bails out. + +To limit template recursion you can: + +- add template specialisation +- configure Cppcheck, which can be done in the GUI project file dialog + +Example code with template specialisation: + + template + void a() + { + a(); + } + + void foo() + { + a<0>(); + } + + #ifdef __cppcheck__ + template<> void a<3>() {} + #endif + +You can pass `-D__cppcheck__` when checking this code. + + +# Cppcheck build folder + +Using a Cppcheck build folder is not mandatory but it is recommended. + +Cppcheck save analyzer information in that folder. + +The advantages are; + +- It speeds up the analysis as it makes incremental analysis possible. Only changed files are analyzed when you recheck. +- Whole program analysis also when multiple threads are used. + +On the command line you configure that through `--cppcheck-build-dir=path`. Example: + + mkdir b + cppcheck --cppcheck-build-dir=b src # <- All files are analyzed + cppcheck --cppcheck-build-dir=b src # <- Faster! Results of unchanged files are reused + +In the GUI it is configured in the project settings. + +# Importing a project + +You can import some project files and build configurations into Cppcheck. + +## Cppcheck GUI project + +You can import and use Cppcheck GUI project files in the command line tool: + + cppcheck --project=foobar.cppcheck + +The Cppcheck GUI has a few options that are not available in the command line directly. To use these options you can import a GUI project file. +The command line tool usage is kept intentionally simple and the options are therefore limited. + +To ignore certain folders in the project you can use `-i`. This will skip the analysis of source files in the `foo` folder. + + cppcheck --project=foobar.cppcheck -ifoo + +## CMake + +Generate a compile database: + + cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON . + +The file `compile_commands.json` is created in the current folder. Now run Cppcheck like this: + + cppcheck --project=compile_commands.json + +To ignore certain folders you can use `-i`. This will skip analysis of source files in the `foo` folder. + + cppcheck --project=compile_commands.json -ifoo + + +## Visual Studio + +You can run Cppcheck on individual project files (\*.vcxproj) or on a whole solution (\*.sln) + +Running Cppcheck on an entire Visual Studio solution: + + cppcheck --project=foobar.sln + +Running Cppcheck on a Visual Studio project: + + cppcheck --project=foobar.vcxproj + +Both options will analyze all available configurations in the project(s). +Limiting on a single configuration: + + cppcheck --project=foobar.sln "--project-configuration=Release|Win32" + +In the `Cppcheck GUI` you have the option to only analyze a single debug configuration. If you want to use this option on the command line, then create a `Cppcheck GUI` project with this activated and then import the GUI project file on the command line. + +To ignore certain folders in the project you can use `-i`. This will skip analysis of source files in the `foo` folder. + + cppcheck --project=foobar.vcxproj -ifoo + +## C++ Builder 6 + +Running Cppcheck on a C++ Builder 6 project: + + cppcheck --project=foobar.bpr + + +To ignore certain folders in the project you can use `-i`. This will skip analysis of source files in the `foo` folder. + + cppcheck --project=foobar.bpr -ifoo + +## Other + +If you can generate a compile database, then it is possible to import that in Cppcheck. + +In Linux you can use for instance the `bear` (build ear) utility to generate a compile database from arbitrary build tools: + + bear -- make + +# Preprocessor Settings + +If you use `--project` then Cppcheck will automatically use the preprocessor settings in the imported project file and +likely you don't have to configure anything extra. + +If you don't use `--project` then a bit of manual preprocessor configuration might be required. However Cppcheck has +automatic configuration of defines. + +## Automatic configuration of preprocessor defines + +Cppcheck automatically test different combinations of preprocessor defines to achieve as high coverage in the analysis +as possible. + +Here is a file that has 3 bugs (when x,y,z are assigned). + + #ifdef A + x=100/0; + #ifdef B + y=100/0; + #endif + #else + z=100/0; + #endif + + #ifndef C + #error C must be defined + #endif + + +The flag `-D` tells Cppcheck that a name is defined. There will be no Cppcheck analysis without this define. +The flag `-U` tells Cppcheck that a name is not defined. There will be no Cppcheck analysis with this define. +The flag `--force` and `--max-configs` is used to control how many combinations are checked. When `-D` is used, +Cppcheck will only check 1 configuration unless these are used. + +Example: + + cppcheck test.c => test all configurations => all bugs are found + cppcheck -DA test.c => only test configuration "-DA" => No bug is found (#error) + cppcheck -DA -DC test.c => only test configuration "-DA -DC" => The first bug is found + cppcheck -UA test.c => The configuration "-DC" is tested => The last bug is found + cppcheck --force -DA test.c => All configurations with "-DA" are tested => The two first bugs are found + + +## Include paths + +To add an include path, use `-I`, followed by the path. + +Cppcheck's preprocessor basically handles includes like any other preprocessor. However, while other preprocessors +stop working when they encounter a missing header, Cppcheck will just print an information message and continues +parsing the code. + +The purpose of this behaviour is that Cppcheck is meant to work without necessarily seeing the entire code. +Actually, it is recommended to not give all include paths. +While it is useful for Cppcheck to see the declaration of a class when checking the implementation of its members, +passing standard library headers is discouraged, because the analysis will not work fully and lead to a longer checking +time. For such cases, .cfg files are the preferred way to provide information about the implementation of functions and +types to Cppcheck, see below for more information. + +# Platform + +You should use a platform configuration that matches your target environment. + +By default Cppcheck uses native platform configuration that works well if your code is compiled and executed locally. + +Cppcheck has builtin configurations for Unix and Windows targets. You can easily use these with the `--platform` command line flag. + +You can also create your own custom platform configuration in a XML file. Here is an example: + + + + 8 + signed + + 2 + 4 + 4 + 8 + 4 + 8 + 12 + 4 + 4 + 2 + + + +# C/C++ Standard + +Use `--std` on the command line to specify a C/C++ standard. + +Cppcheck assumes that the code is compatible with the latest C/C++ standard, but it is possible to override this. + +The available options are: + +- 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 +- c++14: C++ code is C++14 compatible +- c++17: C++ code is C++17 compatible +- c++20: C++ code is C++20 compatible (default) + +# Cppcheck build dir + +It's a good idea to use a Cppcheck build dir. On the command line use `--cppcheck-build-dir`. In +the GUI, the build dir is configured in the project options. + +Rechecking code will be much faster. Cppcheck does not analyse unchanged code. The old warnings are +loaded from the build dir and reported again. + +Whole program analysis does not work when multiple threads are used; unless you use a cppcheck +build dir. For instance, the unusedFunction warnings require whole program analysis. + +# Suppressions + +If you want to filter out certain errors from being generated, then it is possible to suppress these. + +If you encounter a false positive, then please report it to the Cppcheck team so that it can be fixed. + +## Plain text suppressions + +The format for an error 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 id of a warning is shown in brackets in the normal cppcheck text output. The suppression `error id` may contain \* to match any sequence of tokens. + +The filename may include the wildcard characters \* or ?, which matches any sequence of characters or any single character respectively. +It is recommended to use forward-slash `/` as path separator on all operating systems. The filename must match the filename in the reported warning exactly. +For instance, if the warning contains a relative path, then the suppression must match that relative path. + +## Command line suppression + +The `--suppress=` command line option is used to specify suppressions on the command line. Example: + + cppcheck --suppress=memleak:src/file1.cpp src/ + +## Suppressions in a file + +You can create a suppressions file for example as follows: + + // suppress memleak and exceptNew errors in the file src/file1.cpp + memleak:src/file1.cpp + exceptNew:src/file1.cpp + + uninitvar // suppress all uninitvar errors in all files + +Note that you may add empty lines and comments in the suppressions file. +Comments must start with `#` or `//` and be at the start of the line, or after the suppression line. + +The usage of the suppressions file is as follows: + + cppcheck --suppressions-list=suppressions.txt src/ + +## XML suppressions + +You can specify suppressions in a XML file, for example as follows: + + + + + uninitvar + src/file1.c + 10 + var + + + +The XML format is extensible and may be extended with further attributes in the future. + +The usage of the suppressions file is as follows: + + cppcheck --suppress-xml=suppressions.xml src/ + +## Inline suppressions + +Suppressions can also be added directly in the code by adding comments that contain special keywords. +Note that adding comments sacrifices the readability of the code somewhat. + +This code will normally generate an error message: + + void f() { + char arr[5]; + arr[10] = 0; + } + +The output is: + + cppcheck test.c + [test.c:3]: (error) Array 'arr[5]' index 10 out of bounds + +To activate inline suppressions: + + cppcheck --inline-suppr test.c + +### Format + +You can suppress a warning `aaaa` with: + + // cppcheck-suppress aaaa + +Suppressing multiple ids in one comment by using []: + + // cppcheck-suppress [aaaa, bbbb] + +Suppressing warnings `aaaa` on a block of code: + + // cppcheck-suppress-begin aaaa + ... + // cppcheck-suppress-end aaaa + +Suppressing multiple ids on a block of code: + + // cppcheck-suppress-begin [aaaa, bbbb] + ... + // cppcheck-suppress-end [aaaa, bbbb] + +Suppressing warnings `aaaa` for a whole file: + + // cppcheck-suppress-file aaaa + +Suppressing multiple ids for a whole file: + + // cppcheck-suppress-file [aaaa, bbbb] + +Suppressing warnings `aaaa` where macro is used: + + // cppcheck-suppress-macro aaaa + #define MACRO ... + ... + x = MACRO; // <- aaaa warnings are suppressed here + + +Suppressing multiple ids where macro is used: + + // cppcheck-suppress-macro [aaaa, bbbb] + #define MACRO ... + ... + x = MACRO; // <- aaaa and bbbb warnings are suppressed here + +### Comment before code or on same line + +The comment can be put before the code or at the same line as the code. + +Before the code: + + void f() { + char arr[5]; + + // cppcheck-suppress arrayIndexOutOfBounds + arr[10] = 0; + } + +Or at the same line as the code: + + void f() { + char arr[5]; + + arr[10] = 0; // cppcheck-suppress arrayIndexOutOfBounds + } + +In this example there are 2 lines with code and 1 suppression comment. The suppression comment only applies to 1 line: `a = b + c;`. + + void f() { + a = b + c; // cppcheck-suppress abc + d = e + f; + } + +As a special case for backwards compatibility, if you have a `{` on its own line and a suppression comment after that, then that will suppress warnings for both the current and next line. This example will suppress `abc` warnings both for `{` and for `a = b + c;`: + + void f() + { // cppcheck-suppress abc + a = b + c; + } + +### Multiple suppressions + +For a line of code there might be several warnings you want to suppress. + +There are several options; + +Using 2 suppression comments before code: + + void f() { + char arr[5]; + + // cppcheck-suppress arrayIndexOutOfBounds + // cppcheck-suppress zerodiv + arr[10] = arr[10] / 0; + } + +Using 1 suppression comment before the code: + + void f() { + char arr[5]; + + // cppcheck-suppress[arrayIndexOutOfBounds,zerodiv] + arr[10] = arr[10] / 0; + } + +Suppression comment on the same line as the code: + + void f() { + char arr[5]; + + arr[10] = arr[10] / 0; // cppcheck-suppress[arrayIndexOutOfBounds,zerodiv] + } + + +### Symbol name + +You can specify that the inline suppression only applies to a specific symbol: + + // cppcheck-suppress aaaa symbolName=arr + +Or: + + // cppcheck-suppress[aaaa symbolName=arr, bbbb] + +### Comment about suppression + +You can write comments about a suppression as follows: + + // cppcheck-suppress[warningid] some comment + // cppcheck-suppress warningid ; some comment + // cppcheck-suppress warningid // some comment + +# XML output + +Cppcheck can generate output in XML format. Use `--xml` to enable this format. + +A sample command to check a file and output errors in the XML format: + + cppcheck --xml file1.cpp + +Here is a sample report: + + + + + + + + + + + +## The `` element + +Each error is reported in a `` element. Attributes: + +**id** + +id of error, and which are valid symbolnames + +**severity** + +error/warning/style/performance/portability/information + +**msg** + +the error message in short format + +**verbose** + +the error message in long format + +**inconclusive** + +this attribute is only used when the error message is inconclusive + +**cwe** + +CWE ID for the problem; note that this attribute is only used when the CWE ID for the message is known + +## The `` element + +All locations related to an error are listed with `` elements. The primary location is listed first. + +Attributes: + +**file** + +filename, both relative and absolute paths are possible + +**file0** + +name of the source file (optional) + +**line** + +line number + +**info** + +short information for each location (optional) + +# Reformatting the text output + +If you want to reformat the output so that it looks different, then you can use templates. + +## Predefined output formats + +To get Visual Studio compatible output you can use --template=vs: + + cppcheck --template=vs samples/arrayIndexOutOfBounds/bad.c + +This output will look like this: + + Checking samples/arrayIndexOutOfBounds/bad.c ... + samples/arrayIndexOutOfBounds/bad.c(6): error: Array 'a[2]' accessed at index 2, which is out of bounds. + +To get gcc compatible output you can use --template=gcc: + + cppcheck --template=gcc samples/arrayIndexOutOfBounds/bad.c + +The output will look like this: + + Checking samples/arrayIndexOutOfBounds/bad.c ... + samples/arrayIndexOutOfBounds/bad.c:6:6: warning: Array 'a[2]' accessed at index 2, which is out of bounds. [arrayIndexOutOfBounds] + a[2] = 0; + ^ + +## User defined output format (single line) + +You can write your own pattern. For instance, to get warning messages that are formatted like traditional gcc, then the following format can be used: + + cppcheck --template="{file}:{line}: {severity}: {message}" samples/arrayIndexOutOfBounds/bad.c + +The output will then look like this: + + Checking samples/arrayIndexOutOfBounds/bad.c ... + samples/arrayIndexOutOfBounds/bad.c:6: error: Array 'a[2]' accessed at index 2, which is out of bounds. + +A comma separated format: + + cppcheck --template="{file},{line},{severity},{id},{message}" samples/arrayIndexOutOfBounds/bad.c + +The output will look like this: + + Checking samples/arrayIndexOutOfBounds/bad.c ... + samples/arrayIndexOutOfBounds/bad.c,6,error,arrayIndexOutOfBounds,Array 'a[2]' accessed at index 2, which is out of bounds. + +## User defined output format (multi line) + +Many warnings have multiple locations. Example code: + + void f(int *p) + { + *p = 3; // line 3 + } + + int main() + { + int *p = 0; // line 8 + f(p); // line 9 + return 0; + } + +There is a possible null pointer dereference at line 3. +Cppcheck can show how it came to that conclusion by showing extra location information. +You need to use both --template and --template-location at the command line, for example: + + cppcheck --template="{file}:{line}: {severity}: {message}\n{code}" --template-location="{file}:{line}: note: {info}\n{code}" multiline.c + +The output from Cppcheck is: + + Checking multiline.c ... + multiline.c:3: warning: Possible null pointer dereference: p + *p = 3; + ^ + multiline.c:8: note: Assignment 'p=0', assigned value is 0 + int *p = 0; + ^ + multiline.c:9: note: Calling function 'f', 1st argument 'p' value is 0 + f(p); + ^ + multiline.c:3: note: Null pointer dereference + *p = 3; + ^ + +The first line in the warning is formatted by the --template format. + +The other lines in the warning are formatted by the --template-location format. + +### Format specifiers for --template + +The available specifiers for --template are: + +**{file}** + +File name + +**{line}** + +Line number + +**{column}** + +Column number + +**{callstack}** + +Write all locations. Each location is written in [{file}:{line}] format and the locations are separated by ->. For instance it might look like: [multiline.c:8] -> [multiline.c:9] -> [multiline.c:3] + +**{inconclusive:text}** + +If warning is inconclusive, then the given text is written. The given text can be any text that does not contain }. Example: {inconclusive:inconclusive,} + +**{severity}** + +error/warning/style/performance/portability/information + +**{message}** + +The warning message + +**{id}** + +Warning id + +**{code}** + +The real code + +**\\t** + +Tab + +**\\n** + +Newline + +**\\r** + +Carriage return + +### Format specifiers for --template-location + +The available specifiers for `--template-location` are: + +**{file}** + +File name + +**{line}** + +Line number + +**{column}** + +Column number + +**{info}** + +Information message about the current location + +**{code}** + +The real code + +**\\t** + +Tab + +**\\n** + +Newline + +**\\r** + +Carriage return + +# Addons + +Addons are scripts that analyse Cppcheck dump files to check compatibility with secure coding standards and to locate issues. + +Cppcheck is distributed with a few addons which are listed below. + +## Supported addons + +### misra.py + +[misra.py](https://github.com/danmar/cppcheck/blob/main/addons/misra.py) is used to verify compliance with MISRA C 2012, a proprietary set of guidelines to avoid questionable code, developed for embedded systems. + +The full list of supported rules is available on: [https://files.cppchecksolutions.com/misrac2012.html](https://files.cppchecksolutions.com/misrac2012.html) + +### y2038.py + +[y2038.py](https://github.com/danmar/cppcheck/blob/main/addons/y2038.py) checks Linux systems for [year 2038 problem](https://en.wikipedia.org/wiki/Year_2038_problem) safety. This required [modified environment](https://github.com/3adev/y2038). See complete description [here](https://github.com/danmar/cppcheck/blob/main/addons/doc/y2038.txt). + +### threadsafety.py + +[threadsafety.py](https://github.com/danmar/cppcheck/blob/main/addons/threadsafety.py) analyses Cppcheck dump files to locate thread safety issues like static local objects used by multiple threads. + +## Running Addons + +Addons could be run through Cppcheck command line utility as follows: + + cppcheck --addon=misra.py somefile.c + +This will launch all Cppcheck checks and additionally calls specific checks provided by selected addon. + +Some addons need extra arguments. You can configure how you want to execute an addon in a json file. For example put this in misra.json: + + { + "script": "misra.py", + "args": [ + "--rule-texts=misra.txt" + ] + } + +And then the configuration can be executed on the Cppcheck command line: + + cppcheck --addon=misra.json somefile.c + +By default Cppcheck would search addon at the standard path which was specified +during the installation process. You also can set this path directly, for example: + + cppcheck --addon=/opt/cppcheck/configurations/my_misra.json somefile.c + +This allows you to create and manage multiple configuration files for different projects. + +# Library configuration + +When external libraries are used, such as WinAPI, POSIX, gtk, Qt, etc, Cppcheck has no information about functions, types, or macros contained in those libraries. Cppcheck then fails to detect various problems in the code, or might even abort the analysis. But this can be fixed by using the appropriate configuration files. + +Cppcheck already contains configurations for several libraries. They can be loaded as described below. Note that the configuration for the standard libraries of C and C++, std.cfg, is always loaded by cppcheck. If you create or update a configuration file for a popular library, we would appreciate if you supplied it to the cppcheck project. + +## Using a .cfg file + +To use a .cfg file shipped with cppcheck, pass the `--library=` option. The table below shows the currently existing libraries: +| .cfg file | Library | Comment | +| ------------- | ------------- | ------------- | +| avr.cfg | | +| bento4.cfg | [Bento4](http://www.bento4.com/) | +| boost.cfg | [Boost](http://www.boost.org/)| +| bsd.cfg | [BSD](https://www.freebsd.org/) | +| cairo.cfg | [cairo](https://www.cairographics.org/) | +| cppcheck-lib.cfg | [Cppcheck](http://cppcheck.net/) | Used in selfcheck of the Cppcheck code base +| cppunit.cfg | [CppUnit](https://sourceforge.net/projects/cppunit/) | +| dpdk.cfg | | +| embedded_sql.cfg | | +| emscripten.cfg | | +| ginac.cfg | | +| gnu.cfg | [GNU](https://www.gnu.org/) | +| googletest.cfg | [GoogleTest](https://github.com/google/googletest) | +| gtk.cfg | [GTK](https://www.gtk.org/) | +| icu.cfg | | +| kde.cfg | [KDE](https://kde.org/) | +| libcerror.cfg | [libcerror](https://github.com/libyal/libcerror) | +| libcurl.cfg | [libcurl](https://curl.se/libcurl/) | +| libsigc++.cfg | [libsigc++](https://github.com/libsigcplusplus/libsigcplusplus) | +| lua.cfg | | +| mfc.cfg | [MFC](https://learn.microsoft.com/en-us/cpp/mfc/mfc-desktop-applications) | +| microsoft_atl.cfg | [ATL](https://learn.microsoft.com/en-us/cpp/atl/active-template-library-atl-concepts) | +| microsoft_sal.cfg | [SAL annotations](https://learn.microsoft.com/en-us/cpp/c-runtime-library/sal-annotations) | +| microsoft_unittest.cfg | [CppUnitTest](https://learn.microsoft.com/en-us/visualstudio/test/microsoft-visualstudio-testtools-cppunittestframework-api-reference) | +| motif.cfg | | +| nspr.cfg | | +| ntl.cfg | | +| opencv2.cfg | [OpenCV](https://opencv.org/) | +| opengl.cfg | [OpenGL](https://opengl.org/) | +| openmp.cfg | [OpenMP](https://www.openmp.org/) | +| openssl.cfg | [OpenSSL](https://www.openssl.org/) | +| pcre.cfg | [PCRE](https://pcre.org/) | +| posix.cfg | [POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/) | +| python.cfg | | +| qt.cfg | [Qt](https://doc.qt.io/qt.html) | +| ruby.cfg | | +| sdl.cfg | | +| sfml.cfg | | +| sqlite3.cfg | [SQLite](https://www.sqlite.org/) | +| std.cfg | C/C++ standard library | Loaded by default +| tinyxml2.cfg | [TinyXML-2](https://github.com/leethomason/tinyxml2) | +| vcl.cfg | | +| windows.cfg | [Win32 API](https://learn.microsoft.com/en-us/windows/win32/) | +| wxsqlite3.cfg | | +| wxsvg.cfg | | +| wxwidgets.cfg | [wxWidgets](https://www.wxwidgets.org/) | +| zephyr.cfg | | +| zlib.cfg | [zlib](https://www.zlib.net) | + +## Creating a custom .cfg file + +You can create and use your own .cfg files for your projects. Use `--check-library` to get hints about what you should configure. + +You can use the `Library Editor` in the `Cppcheck GUI` to edit configuration files. It is available in the `View` menu. + +The .cfg file format is documented in the `Reference: Cppcheck .cfg format` (https://cppcheck.sourceforge.io/reference-cfg-format.pdf) document. + +# HTML Report + +You can convert the XML output from Cppcheck into a HTML report. You'll need Python and the pygments module () for this to work. In the Cppcheck source tree there is a folder htmlreport that contains a script that transforms a Cppcheck XML file into HTML output. + +This command generates the help screen: + + htmlreport/cppcheck-htmlreport -h + +The output screen says: + + Usage: cppcheck-htmlreport [options] + + Options: + -h, --help show this help message and exit + --file=FILE The cppcheck xml output file to read defects from. + Default is reading from stdin. + --report-dir=REPORT_DIR + The directory where the html report content is written. + --source-dir=SOURCE_DIR + Base directory where source code files can be found. + +Example usage: + + ./cppcheck gui/test.cpp --xml 2> err.xml + htmlreport/cppcheck-htmlreport --file=err.xml --report-dir=test1 --source-dir=. + +# Check Level + +## Normal + +The "normal" check level is chosen by default. Our aim is that this checking level will provide an effective checking in "reasonable" time. + +The "normal" check level should be useful during active development: + * checking files while you edit them. + * block changes to the repo + * etc + +## Exhaustive + +When you can wait longer for the results you can enable the "exhaustive" checking, by using the option `--check-level=exhaustive`. + +Exhaustive checking level should be useful for scenarios where you can wait for results. For instance: + * nightly builds + * etc + +# Speeding up analysis + +## Limit preprocessor configurations + +For performance reasons it might be a good idea to limit preprocessor configurations to check. + +## Limit ValueFlow: max if count + +The command line option `--performance-valueflow-max-if-count` adjusts the max count for number of if in a function. + +When that limit is exceeded there is a limitation of data flow in that function. It is not drastic: + * Analysis of other functions are not affected. + * It's only for some specific data flow analysis, we have data flow analysis that is always executed. + * All checks are always executed. There can still be plenty of warnings in the limited function. + +There is data flow analysis that slows down exponentially when number of if increase. And the limit is intended to avoid that +analysis time explodes. + +## GUI options + +In the GUI there are various options to limit analysis. + +In the GUI: + * Open the project dialog. + * In the "Analysis" tab there are several options. + +If you want to use these limitations on the command line also you can import the GUI project file with --project. + +# Cppcheck Premium + +## Bug hunting + +This is analysis that is more noisy than normal analysis. Most warnings will be false positives (cppcheck will wrongly claim that there are bugs). The design goal is to not have more than roughly 5 - 10 false positives in each file. + +It is not intended to be used in normal CI or regular static analysis by developers. The noise makes it useless for that. + +It is intended to be used when you are looking for bugs and you really can accept noise. For example: + * You have developed a brand new feature and want to ensure that there are no bugs. + * Maybe as part of release testing your product you can run bug hunting on modified files. + * Etc + +Technically, analysis that is "sound" will detect all bugs. Analysis that is "soundy" has the goal to detect most bugs and it tries to keep the noise at an reasonable level. + +The Cppcheck bug hunting analysis is "soundy". + +Command: + + cppcheck --premium=bughunting .... + +## Coding standards + +Command to active Autosar checkers: + + cppcheck --premium=autosar .... + +Command to active Cert C checkers: + + cppcheck --premium=cert-c .... + +Command to active Cert C++ checkers: + + cppcheck --premium=cert-c++ .... + +Command to active Misra C++ 2008 checkers: + + cppcheck --premium=misra-c++-2008 .... + +## Licenses + +### Individual license + +A license that is connected to your computer. You can check any code you want. + +### LOC license + +A license that allows you to run cppcheck on a limited number of lines of code. It can only be used for certain licensed paths in a repository. + +#### Running analysis + +Commands: + + cd check-path + + # Calculate lines of code and validate the license + premiumaddon --check-loc-license some-path/license-file > cppcheck-premium-loc + + # Run cppcheck analysis + cppcheck diff --git a/cppcheck-2.14.0/man/manual-style.tex b/cppcheck-2.14.0/man/manual-style.tex new file mode 100644 index 00000000..72187c11 --- /dev/null +++ b/cppcheck-2.14.0/man/manual-style.tex @@ -0,0 +1,32 @@ +\usepackage{titlesec} +\usepackage{fancyvrb,newverbs,xcolor} +\usepackage{xcolor} + +\titleformat{\chapter}[display] +{\normalfont\huge\bfseries}{\chaptertitlename\ \thechapter}{20pt}{\Huge} +\titlespacing*{\chapter}{0pt}{-20pt}{20pt} + + +\definecolor{Light}{HTML}{EEEEEE} +\let\oldtexttt\texttt +\renewcommand{\texttt}[1]{ + \colorbox{Light}{\oldtexttt{#1}} +} + +\lstset{ + basicstyle=\ttfamily, + backgroundcolor=\color{Light}, + xleftmargin=10pt, + frame=lrtb, + framesep=10pt, + framerule=0pt, + showspaces=false, + showstringspaces=false, + showtabs=false, + tabsize=2, + captionpos=b, + breaklines=true, + breakatwhitespace=true, + breakautoindent=true, + linewidth=\textwidth +} diff --git a/cppcheck-2.14.0/man/manual.css b/cppcheck-2.14.0/man/manual.css new file mode 100644 index 00000000..206be6cb --- /dev/null +++ b/cppcheck-2.14.0/man/manual.css @@ -0,0 +1,86 @@ +/* stylelint-disable */ +html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}main{display:block}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none} +/* stylelint-enable */ + +html { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; +} + +body { + background-color: #fff; + color: #333; + font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji; + font-size: 16px; + font-weight: 400; + line-height: 1.5; + max-width: 1012px; + margin: 20px auto; + border: 1px solid #eaecef; + padding: 20px 100px; + overflow-x: hidden; +} + +a { + color: #0366d6; + text-decoration: none; + +} + +a:hover, a:focus { + text-decoration: underline; + +} + +h1, h2, h3, h4, h5, h6 { + font-weight: 600; + line-height: 1.25; + margin-top: 24px; + margin-bottom: 16px; +} + +h1 { + font-size: 2em; +} + +h2 { + font-size: 1.5em; +} + +h3 { + font-size: 1.25em; +} + +h4, h5, h6 { + font-size: 1.1em; +} + +h1, h2 { + padding-bottom: 0.3em; + border-bottom: 1px solid #eaecef; +} + +pre { + padding: 16px; + background-color: #f6f8fa; + border-radius: 6px; + line-height: 1.45; + font-size: 85%; + margin-top: 0; + margin-bottom: 16px; + overflow: auto; +} + +code { + font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace; + background-color: #f6f8fa; + font-size: 100%; + word-break: normal; + white-space: pre; +} + +ol, ul { + padding-left: 2em; + margin-top: 0; + margin-bottom: 16px; +} diff --git a/cppcheck-2.14.0/man/manual.md b/cppcheck-2.14.0/man/manual.md new file mode 100644 index 00000000..8b54efb8 --- /dev/null +++ b/cppcheck-2.14.0/man/manual.md @@ -0,0 +1,1155 @@ +--- +title: Cppcheck manual +subtitle: Version 2.14 +author: Cppcheck team +lang: en +documentclass: report +--- + +# Introduction + +Cppcheck is an analysis tool for C/C++ code. It provides unique code analysis to detect bugs and focuses on detecting +undefined behaviour and dangerous coding constructs. The goal is to detect only real errors in the code, and generate +as few false positives (wrongly reported warnings) as possible. Cppcheck is designed to analyze your C/C++ code even +if it has non-standard syntax, as is common in for example embedded projects. + +Supported code and platforms: + +- Cppcheck checks non-standard code that contains various compiler extensions, inline assembly code, etc. +- Cppcheck should be compilable by any compiler that supports C++11 or later. +- Cppcheck is cross platform and is used in various posix/windows/etc environments. + +The checks in Cppcheck are not perfect. There are bugs that should be found, that Cppcheck fails to detect. + +## About static analysis + +The kinds of bugs that you can find with static analysis are: + +- Undefined behavior +- Using dangerous code patterns +- Coding style + +There are many bugs that you can not find with static analysis. Static analysis tools do not have human knowledge about +what your program is intended to do. If the output from your program is valid but unexpected then in most cases this is +not detected by static analysis tools. For instance, if your small program writes "Helo" on the screen instead of "Hello" +it is unlikely that any tool will complain about that. + +Static analysis should be used as a complement in your quality assurance. It does not replace any of; + +- Careful design +- Testing +- Dynamic analysis +- Fuzzing + +# Getting started + +## GUI + +It is not required but creating a new project file is a good first step. There are a few options you can tweak to get +good results. + +In the project settings dialog, the first option you see is "Import project". It is recommended that you use this +feature if you can. Cppcheck can import: + +- Visual studio solution / project +- Compile database, which can be generated from CMake/qbs/etc build files +- Borland C++ Builder 6 + +When you have filled out the project settings and clicked on OK, the Cppcheck analysis will start. + +## Command line + +### First test + +Here is some 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. Cppcheck can check all source files in a directory: + + cppcheck path + +If "path" is a folder, then Cppcheck will recursively check all source files in this folder: + + Checking path/file1.cpp... + 1/2 files checked 50% done + Checking path/file2.cpp... + 2/2 files checked 100% done + +### Check files manually or use project file + +With Cppcheck you can check files manually by specifying files/paths to check and settings. Or you can use a build +environment, such as CMake or Visual Studio. + +We don't know which approach (project file or manual configuration) will give you the best results. It is recommended +that you try both. It is possible that you will get different results so that to find the largest amount of bugs you +need to use both approaches. Later chapters will describe this in more detail. + +### Check files matching a given file filter + +With `--file-filter=` you can set a file filter and only those files matching the filter will be checked. + +For example: if you want to check only those files and folders starting from a subfolder src/ that start with "test" +you have to type: + + cppcheck src/ --file-filter=src/test* + +Cppcheck first collects all files in src/ and will apply the filter after that. So the filter must start with the given +start folder. + +### 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, which specifies the files/paths to ignore. With this command no files in src/c are +checked: + + cppcheck -isrc/c src + +This option is only valid when supplying an input directory. To ignore multiple directories supply the -i flag for each +directory individually. The following command ignores both the src/b and src/c directories: + + cppcheck -isrc/b -isrc/c + +### Clang parser (experimental) + +By default Cppcheck uses an internal C/C++ parser. However there is an experimental option to use the Clang parser instead. + +Install `clang`. Then use Cppcheck option `--clang`. + +Technically, Cppcheck will execute `clang` with its `-ast-dump` option. The Clang output is then imported and converted into +the normal Cppcheck format. And then normal Cppcheck analysis is performed on that. + +You can also pass a custom Clang executable to the option by using for example `--clang=clang-10`. You can also pass it +with a path. On Windows it will append the `.exe` extension unless you use a path. + +## Severities + +The possible severities for messages are: + +**error** + +when code is executed there is either undefined behavior or other error, such as a memory leak or resource leak + +**warning** + +when code is executed there might be undefined behavior + +**style** + +stylistic issues, such as unused functions, redundant code, constness, operator precedence, possible mistakes. + +**performance** + +run time performance suggestions based on common knowledge, though it is not certain any measurable speed difference +will be achieved by fixing these messages. + +**portability** + +portability warnings. Implementation defined behavior. 64-bit portability. Some undefined behavior that probably works +"as you want", etc. + +**information** + +configuration problems, which does not relate to the syntactical correctness, but the used Cppcheck configuration could +be improved. + +## Possible speedup analysis of template code + +Cppcheck instantiates the templates in your code. + +If your templates are recursive this can lead to slow analysis that uses a lot +of memory. Cppcheck will write information messages when there are potential +problems. + +Example code: + + template + void a() + { + a(); + } + + void foo() + { + a<0>(); + } + +Cppcheck output: + + test.cpp:4:5: information: TemplateSimplifier: max template recursion (100) reached for template 'a<101>'. You might want to limit Cppcheck recursion. [templateRecursion] + a(); + ^ + +As you can see Cppcheck has instantiated `a` until `a<101>` was reached +and then it bails out. + +To limit template recursion you can: + +- add template specialisation +- configure Cppcheck, which can be done in the GUI project file dialog + +Example code with template specialisation: + + template + void a() + { + a(); + } + + void foo() + { + a<0>(); + } + + #ifdef __cppcheck__ + template<> void a<3>() {} + #endif + +You can pass `-D__cppcheck__` when checking this code. + + +# Cppcheck build folder + +Using a Cppcheck build folder is not mandatory but it is recommended. + +Cppcheck save analyzer information in that folder. + +The advantages are; + +- It speeds up the analysis as it makes incremental analysis possible. Only changed files are analyzed when you recheck. +- Whole program analysis also when multiple threads are used. + +On the command line you configure that through `--cppcheck-build-dir=path`. Example: + + mkdir b + cppcheck --cppcheck-build-dir=b src # <- All files are analyzed + cppcheck --cppcheck-build-dir=b src # <- Faster! Results of unchanged files are reused + +In the GUI it is configured in the project settings. + +# Importing a project + +You can import some project files and build configurations into Cppcheck. + +## Cppcheck GUI project + +You can import and use Cppcheck GUI project files in the command line tool: + + cppcheck --project=foobar.cppcheck + +The Cppcheck GUI has a few options that are not available in the command line directly. To use these options you can import a GUI project file. +The command line tool usage is kept intentionally simple and the options are therefore limited. + +To ignore certain folders in the project you can use `-i`. This will skip the analysis of source files in the `foo` folder. + + cppcheck --project=foobar.cppcheck -ifoo + +## CMake + +Generate a compile database: + + cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON . + +The file `compile_commands.json` is created in the current folder. Now run Cppcheck like this: + + cppcheck --project=compile_commands.json + +To ignore certain folders you can use `-i`. This will skip analysis of source files in the `foo` folder. + + cppcheck --project=compile_commands.json -ifoo + + +## Visual Studio + +You can run Cppcheck on individual project files (\*.vcxproj) or on a whole solution (\*.sln) + +Running Cppcheck on an entire Visual Studio solution: + + cppcheck --project=foobar.sln + +Running Cppcheck on a Visual Studio project: + + cppcheck --project=foobar.vcxproj + +Both options will analyze all available configurations in the project(s). +Limiting on a single configuration: + + cppcheck --project=foobar.sln "--project-configuration=Release|Win32" + +In the `Cppcheck GUI` you have the option to only analyze a single debug configuration. If you want to use this option on the command line, then create a `Cppcheck GUI` project with this activated and then import the GUI project file on the command line. + +To ignore certain folders in the project you can use `-i`. This will skip analysis of source files in the `foo` folder. + + cppcheck --project=foobar.vcxproj -ifoo + +## C++ Builder 6 + +Running Cppcheck on a C++ Builder 6 project: + + cppcheck --project=foobar.bpr + + +To ignore certain folders in the project you can use `-i`. This will skip analysis of source files in the `foo` folder. + + cppcheck --project=foobar.bpr -ifoo + +## Other + +If you can generate a compile database, then it is possible to import that in Cppcheck. + +In Linux you can use for instance the `bear` (build ear) utility to generate a compile database from arbitrary build tools: + + bear -- make + +# Preprocessor Settings + +If you use `--project` then Cppcheck will automatically use the preprocessor settings in the imported project file and +likely you don't have to configure anything extra. + +If you don't use `--project` then a bit of manual preprocessor configuration might be required. However Cppcheck has +automatic configuration of defines. + +## Automatic configuration of preprocessor defines + +Cppcheck automatically test different combinations of preprocessor defines to achieve as high coverage in the analysis +as possible. + +Here is a file that has 3 bugs (when x,y,z are assigned). + + #ifdef A + x=100/0; + #ifdef B + y=100/0; + #endif + #else + z=100/0; + #endif + + #ifndef C + #error C must be defined + #endif + + +The flag `-D` tells Cppcheck that a name is defined. There will be no Cppcheck analysis without this define. +The flag `-U` tells Cppcheck that a name is not defined. There will be no Cppcheck analysis with this define. +The flag `--force` and `--max-configs` is used to control how many combinations are checked. When `-D` is used, +Cppcheck will only check 1 configuration unless these are used. + +Example: + + cppcheck test.c => test all configurations => all bugs are found + cppcheck -DA test.c => only test configuration "-DA" => No bug is found (#error) + cppcheck -DA -DC test.c => only test configuration "-DA -DC" => The first bug is found + cppcheck -UA test.c => The configuration "-DC" is tested => The last bug is found + cppcheck --force -DA test.c => All configurations with "-DA" are tested => The two first bugs are found + + +## Include paths + +To add an include path, use `-I`, followed by the path. + +Cppcheck's preprocessor basically handles includes like any other preprocessor. However, while other preprocessors +stop working when they encounter a missing header, Cppcheck will just print an information message and continues +parsing the code. + +The purpose of this behaviour is that Cppcheck is meant to work without necessarily seeing the entire code. +Actually, it is recommended to not give all include paths. +While it is useful for Cppcheck to see the declaration of a class when checking the implementation of its members, +passing standard library headers is discouraged, because the analysis will not work fully and lead to a longer checking +time. For such cases, .cfg files are the preferred way to provide information about the implementation of functions and +types to Cppcheck, see below for more information. + +# Platform + +You should use a platform configuration that matches your target environment. + +By default Cppcheck uses native platform configuration that works well if your code is compiled and executed locally. + +Cppcheck has builtin configurations for Unix and Windows targets. You can easily use these with the `--platform` command line flag. + +You can also create your own custom platform configuration in a XML file. Here is an example: + + + + 8 + signed + + 2 + 4 + 4 + 8 + 4 + 8 + 12 + 4 + 4 + 2 + + + +# C/C++ Standard + +Use `--std` on the command line to specify a C/C++ standard. + +Cppcheck assumes that the code is compatible with the latest C/C++ standard, but it is possible to override this. + +The available options are: + +- 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 +- c++14: C++ code is C++14 compatible +- c++17: C++ code is C++17 compatible +- c++20: C++ code is C++20 compatible (default) + +# Cppcheck build dir + +It's a good idea to use a Cppcheck build dir. On the command line use `--cppcheck-build-dir`. In +the GUI, the build dir is configured in the project options. + +Rechecking code will be much faster. Cppcheck does not analyse unchanged code. The old warnings are +loaded from the build dir and reported again. + +Whole program analysis does not work when multiple threads are used; unless you use a cppcheck +build dir. For instance, the unusedFunction warnings require whole program analysis. + +# Suppressions + +If you want to filter out certain errors from being generated, then it is possible to suppress these. + +If you encounter a false positive, then please report it to the Cppcheck team so that it can be fixed. + +## Plain text suppressions + +The format for an error 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 id of a warning is shown in brackets in the normal cppcheck text output. The suppression `error id` may contain \* to match any sequence of tokens. + +The filename may include the wildcard characters \* or ?, which matches any sequence of characters or any single character respectively. +It is recommended to use forward-slash `/` as path separator on all operating systems. The filename must match the filename in the reported warning exactly. +For instance, if the warning contains a relative path, then the suppression must match that relative path. + +## Command line suppression + +The `--suppress=` command line option is used to specify suppressions on the command line. Example: + + cppcheck --suppress=memleak:src/file1.cpp src/ + +## Suppressions in a file + +You can create a suppressions file for example as follows: + + // suppress memleak and exceptNew errors in the file src/file1.cpp + memleak:src/file1.cpp + exceptNew:src/file1.cpp + + uninitvar // suppress all uninitvar errors in all files + +Note that you may add empty lines and comments in the suppressions file. +Comments must start with `#` or `//` and be at the start of the line, or after the suppression line. + +The usage of the suppressions file is as follows: + + cppcheck --suppressions-list=suppressions.txt src/ + +## XML suppressions + +You can specify suppressions in a XML file, for example as follows: + + + + + uninitvar + src/file1.c + 10 + var + + + +The XML format is extensible and may be extended with further attributes in the future. + +The usage of the suppressions file is as follows: + + cppcheck --suppress-xml=suppressions.xml src/ + +## Inline suppressions + +Suppressions can also be added directly in the code by adding comments that contain special keywords. +Note that adding comments sacrifices the readability of the code somewhat. + +This code will normally generate an error message: + + void f() { + char arr[5]; + arr[10] = 0; + } + +The output is: + + cppcheck test.c + [test.c:3]: (error) Array 'arr[5]' index 10 out of bounds + +To activate inline suppressions: + + cppcheck --inline-suppr test.c + +### Format + +You can suppress a warning `aaaa` with: + + // cppcheck-suppress aaaa + +Suppressing multiple ids in one comment by using []: + + // cppcheck-suppress [aaaa, bbbb] + +Suppressing warnings `aaaa` on a block of code: + + // cppcheck-suppress-begin aaaa + ... + // cppcheck-suppress-end aaaa + +Suppressing multiple ids on a block of code: + + // cppcheck-suppress-begin [aaaa, bbbb] + ... + // cppcheck-suppress-end [aaaa, bbbb] + +Suppressing warnings `aaaa` for a whole file: + + // cppcheck-suppress-file aaaa + +Suppressing multiple ids for a whole file: + + // cppcheck-suppress-file [aaaa, bbbb] + +Suppressing warnings `aaaa` where macro is used: + + // cppcheck-suppress-macro aaaa + #define MACRO ... + ... + x = MACRO; // <- aaaa warnings are suppressed here + + +Suppressing multiple ids where macro is used: + + // cppcheck-suppress-macro [aaaa, bbbb] + #define MACRO ... + ... + x = MACRO; // <- aaaa and bbbb warnings are suppressed here + +### Comment before code or on same line + +The comment can be put before the code or at the same line as the code. + +Before the code: + + void f() { + char arr[5]; + + // cppcheck-suppress arrayIndexOutOfBounds + arr[10] = 0; + } + +Or at the same line as the code: + + void f() { + char arr[5]; + + arr[10] = 0; // cppcheck-suppress arrayIndexOutOfBounds + } + +In this example there are 2 lines with code and 1 suppression comment. The suppression comment only applies to 1 line: `a = b + c;`. + + void f() { + a = b + c; // cppcheck-suppress abc + d = e + f; + } + +As a special case for backwards compatibility, if you have a `{` on its own line and a suppression comment after that, then that will suppress warnings for both the current and next line. This example will suppress `abc` warnings both for `{` and for `a = b + c;`: + + void f() + { // cppcheck-suppress abc + a = b + c; + } + +### Multiple suppressions + +For a line of code there might be several warnings you want to suppress. + +There are several options; + +Using 2 suppression comments before code: + + void f() { + char arr[5]; + + // cppcheck-suppress arrayIndexOutOfBounds + // cppcheck-suppress zerodiv + arr[10] = arr[10] / 0; + } + +Using 1 suppression comment before the code: + + void f() { + char arr[5]; + + // cppcheck-suppress[arrayIndexOutOfBounds,zerodiv] + arr[10] = arr[10] / 0; + } + +Suppression comment on the same line as the code: + + void f() { + char arr[5]; + + arr[10] = arr[10] / 0; // cppcheck-suppress[arrayIndexOutOfBounds,zerodiv] + } + + +### Symbol name + +You can specify that the inline suppression only applies to a specific symbol: + + // cppcheck-suppress aaaa symbolName=arr + +Or: + + // cppcheck-suppress[aaaa symbolName=arr, bbbb] + +### Comment about suppression + +You can write comments about a suppression as follows: + + // cppcheck-suppress[warningid] some comment + // cppcheck-suppress warningid ; some comment + // cppcheck-suppress warningid // some comment + +# XML output + +Cppcheck can generate output in XML format. Use `--xml` to enable this format. + +A sample command to check a file and output errors in the XML format: + + cppcheck --xml file1.cpp + +Here is a sample report: + + + + + + + + + + + +## The `` element + +Each error is reported in a `` element. Attributes: + +**id** + +id of error, and which are valid symbolnames + +**severity** + +error/warning/style/performance/portability/information + +**msg** + +the error message in short format + +**verbose** + +the error message in long format + +**inconclusive** + +this attribute is only used when the error message is inconclusive + +**cwe** + +CWE ID for the problem; note that this attribute is only used when the CWE ID for the message is known + +## The `` element + +All locations related to an error are listed with `` elements. The primary location is listed first. + +Attributes: + +**file** + +filename, both relative and absolute paths are possible + +**file0** + +name of the source file (optional) + +**line** + +line number + +**info** + +short information for each location (optional) + +# Reformatting the text output + +If you want to reformat the output so that it looks different, then you can use templates. + +## Predefined output formats + +To get Visual Studio compatible output you can use --template=vs: + + cppcheck --template=vs samples/arrayIndexOutOfBounds/bad.c + +This output will look like this: + + Checking samples/arrayIndexOutOfBounds/bad.c ... + samples/arrayIndexOutOfBounds/bad.c(6): error: Array 'a[2]' accessed at index 2, which is out of bounds. + +To get gcc compatible output you can use --template=gcc: + + cppcheck --template=gcc samples/arrayIndexOutOfBounds/bad.c + +The output will look like this: + + Checking samples/arrayIndexOutOfBounds/bad.c ... + samples/arrayIndexOutOfBounds/bad.c:6:6: warning: Array 'a[2]' accessed at index 2, which is out of bounds. [arrayIndexOutOfBounds] + a[2] = 0; + ^ + +## User defined output format (single line) + +You can write your own pattern. For instance, to get warning messages that are formatted like traditional gcc, then the following format can be used: + + cppcheck --template="{file}:{line}: {severity}: {message}" samples/arrayIndexOutOfBounds/bad.c + +The output will then look like this: + + Checking samples/arrayIndexOutOfBounds/bad.c ... + samples/arrayIndexOutOfBounds/bad.c:6: error: Array 'a[2]' accessed at index 2, which is out of bounds. + +A comma separated format: + + cppcheck --template="{file},{line},{severity},{id},{message}" samples/arrayIndexOutOfBounds/bad.c + +The output will look like this: + + Checking samples/arrayIndexOutOfBounds/bad.c ... + samples/arrayIndexOutOfBounds/bad.c,6,error,arrayIndexOutOfBounds,Array 'a[2]' accessed at index 2, which is out of bounds. + +## User defined output format (multi line) + +Many warnings have multiple locations. Example code: + + void f(int *p) + { + *p = 3; // line 3 + } + + int main() + { + int *p = 0; // line 8 + f(p); // line 9 + return 0; + } + +There is a possible null pointer dereference at line 3. +Cppcheck can show how it came to that conclusion by showing extra location information. +You need to use both --template and --template-location at the command line, for example: + + cppcheck --template="{file}:{line}: {severity}: {message}\n{code}" --template-location="{file}:{line}: note: {info}\n{code}" multiline.c + +The output from Cppcheck is: + + Checking multiline.c ... + multiline.c:3: warning: Possible null pointer dereference: p + *p = 3; + ^ + multiline.c:8: note: Assignment 'p=0', assigned value is 0 + int *p = 0; + ^ + multiline.c:9: note: Calling function 'f', 1st argument 'p' value is 0 + f(p); + ^ + multiline.c:3: note: Null pointer dereference + *p = 3; + ^ + +The first line in the warning is formatted by the --template format. + +The other lines in the warning are formatted by the --template-location format. + +### Format specifiers for --template + +The available specifiers for --template are: + +**{file}** + +File name + +**{line}** + +Line number + +**{column}** + +Column number + +**{callstack}** + +Write all locations. Each location is written in [{file}:{line}] format and the locations are separated by ->. For instance it might look like: [multiline.c:8] -> [multiline.c:9] -> [multiline.c:3] + +**{inconclusive:text}** + +If warning is inconclusive, then the given text is written. The given text can be any text that does not contain }. Example: {inconclusive:inconclusive,} + +**{severity}** + +error/warning/style/performance/portability/information + +**{message}** + +The warning message + +**{id}** + +Warning id + +**{code}** + +The real code + +**\\t** + +Tab + +**\\n** + +Newline + +**\\r** + +Carriage return + +### Format specifiers for --template-location + +The available specifiers for `--template-location` are: + +**{file}** + +File name + +**{line}** + +Line number + +**{column}** + +Column number + +**{info}** + +Information message about the current location + +**{code}** + +The real code + +**\\t** + +Tab + +**\\n** + +Newline + +**\\r** + +Carriage return + +# Addons + +Addons are scripts that analyse Cppcheck dump files to check compatibility with secure coding standards and to locate issues. + +Cppcheck is distributed with a few addons which are listed below. + +## Supported addons + +### misra.py + +[misra.py](https://github.com/danmar/cppcheck/blob/main/addons/misra.py) is used to verify compliance with MISRA C 2012, a proprietary set of guidelines to avoid questionable code, developed for embedded systems. + +This standard is proprietary, and open source tools are not allowed to distribute the Misra rule texts. Therefore Cppcheck is not allowed to write the rule texts directly. Cppcheck is allowed to distribute the rules and display the id of each violated rule (for example, [c2012-21.3]). The corresponding rule text can also be written however you need to provide that. To get the rule texts, please buy the PDF from MISRA (https://www.misra.org.uk). If you copy the rule texts from "Appendix A - Summary of guidelines" in the PDF and write those in a text file, then by using that text file Cppcheck can write the proper warning messages. To see how the text file can be formatted, take a look at the files listed here: https://github.com/danmar/cppcheck/blob/main/addons/test/misra/. You can use the option `--rule-texts` to specify your rules text file. + +The full list of supported rules is available on [Cppcheck](https://cppcheck.sourceforge.io/misra.php) home page. + +### y2038.py + +[y2038.py](https://github.com/danmar/cppcheck/blob/main/addons/y2038.py) checks Linux systems for [year 2038 problem](https://en.wikipedia.org/wiki/Year_2038_problem) safety. This required [modified environment](https://github.com/3adev/y2038). See complete description [here](https://github.com/danmar/cppcheck/blob/main/addons/doc/y2038.txt). + +### threadsafety.py + +[threadsafety.py](https://github.com/danmar/cppcheck/blob/main/addons/threadsafety.py) analyses Cppcheck dump files to locate thread safety issues like static local objects used by multiple threads. + +## Running Addons + +Addons could be run through Cppcheck command line utility as follows: + + cppcheck --addon=misra.py somefile.c + +This will launch all Cppcheck checks and additionally calls specific checks provided by selected addon. + +Some addons need extra arguments. You can configure how you want to execute an addon in a json file. For example put this in misra.json: + + { + "script": "misra.py", + "args": [ + "--rule-texts=misra.txt" + ] + } + +And then the configuration can be executed on the Cppcheck command line: + + cppcheck --addon=misra.json somefile.c + +By default Cppcheck would search addon at the standard path which was specified +during the installation process. You also can set this path directly, for example: + + cppcheck --addon=/opt/cppcheck/configurations/my_misra.json somefile.c + +This allows you to create and manage multiple configuration files for different projects. + +# Library configuration + +When external libraries are used, such as WinAPI, POSIX, gtk, Qt, etc, Cppcheck has no information about functions, types, or macros contained in those libraries. Cppcheck then fails to detect various problems in the code, or might even abort the analysis. But this can be fixed by using the appropriate configuration files. + +Cppcheck already contains configurations for several libraries. They can be loaded as described below. Note that the configuration for the standard libraries of C and C++, std.cfg, is always loaded by cppcheck. If you create or update a configuration file for a popular library, we would appreciate if you supplied it to the cppcheck project. + +## Using a .cfg file + +To use a .cfg file shipped with cppcheck, pass the `--library=` option. The table below shows the currently existing libraries: +| .cfg file | Library | Comment | +| ------------- | ------------- | ------------- | +| avr.cfg | | +| bento4.cfg | [Bento4](http://www.bento4.com/) | +| boost.cfg | [Boost](http://www.boost.org/)| +| bsd.cfg | [BSD](https://www.freebsd.org/) | +| cairo.cfg | [cairo](https://www.cairographics.org/) | +| cppcheck-lib.cfg | [Cppcheck](http://cppcheck.net/) | Used in selfcheck of the Cppcheck code base +| cppunit.cfg | [CppUnit](https://sourceforge.net/projects/cppunit/) | +| dpdk.cfg | | +| embedded_sql.cfg | | +| emscripten.cfg | | +| ginac.cfg | | +| gnu.cfg | [GNU](https://www.gnu.org/) | +| googletest.cfg | [GoogleTest](https://github.com/google/googletest) | +| gtk.cfg | [GTK](https://www.gtk.org/) | +| icu.cfg | | +| kde.cfg | [KDE](https://kde.org/) | +| libcerror.cfg | [libcerror](https://github.com/libyal/libcerror) | +| libcurl.cfg | [libcurl](https://curl.se/libcurl/) | +| libsigc++.cfg | [libsigc++](https://github.com/libsigcplusplus/libsigcplusplus) | +| lua.cfg | | +| mfc.cfg | [MFC](https://learn.microsoft.com/en-us/cpp/mfc/mfc-desktop-applications) | +| microsoft_atl.cfg | [ATL](https://learn.microsoft.com/en-us/cpp/atl/active-template-library-atl-concepts) | +| microsoft_sal.cfg | [SAL annotations](https://learn.microsoft.com/en-us/cpp/c-runtime-library/sal-annotations) | +| microsoft_unittest.cfg | [CppUnitTest](https://learn.microsoft.com/en-us/visualstudio/test/microsoft-visualstudio-testtools-cppunittestframework-api-reference) | +| motif.cfg | | +| nspr.cfg | | +| ntl.cfg | | +| opencv2.cfg | [OpenCV](https://opencv.org/) | +| opengl.cfg | [OpenGL](https://opengl.org/) | +| openmp.cfg | [OpenMP](https://www.openmp.org/) | +| openssl.cfg | [OpenSSL](https://www.openssl.org/) | +| pcre.cfg | [PCRE](https://pcre.org/) | +| posix.cfg | [POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/) | +| python.cfg | | +| qt.cfg | [Qt](https://doc.qt.io/qt.html) | +| ruby.cfg | | +| sdl.cfg | | +| sfml.cfg | | +| sqlite3.cfg | [SQLite](https://www.sqlite.org/) | +| std.cfg | C/C++ standard library | Loaded by default +| tinyxml2.cfg | [TinyXML-2](https://github.com/leethomason/tinyxml2) | +| vcl.cfg | | +| windows.cfg | [Win32 API](https://learn.microsoft.com/en-us/windows/win32/) | +| wxsqlite3.cfg | | +| wxsvg.cfg | | +| wxwidgets.cfg | [wxWidgets](https://www.wxwidgets.org/) | +| zephyr.cfg | | +| zlib.cfg | [zlib](https://www.zlib.net) | + +## Creating a custom .cfg file + +You can create and use your own .cfg files for your projects. Use `--check-library` to get hints about what you should configure. + +You can use the `Library Editor` in the `Cppcheck GUI` to edit configuration files. It is available in the `View` menu. + +The .cfg file format is documented in the `Reference: Cppcheck .cfg format` (https://cppcheck.sourceforge.io/reference-cfg-format.pdf) document. + +# HTML Report + +You can convert the XML output from Cppcheck into a HTML report. You'll need Python and the pygments module () for this to work. In the Cppcheck source tree there is a folder htmlreport that contains a script that transforms a Cppcheck XML file into HTML output. + +This command generates the help screen: + + htmlreport/cppcheck-htmlreport -h + +The output screen says: + + Usage: cppcheck-htmlreport [options] + + Options: + -h, --help show this help message and exit + --file=FILE The cppcheck xml output file to read defects from. + Default is reading from stdin. + --report-dir=REPORT_DIR + The directory where the html report content is written. + --source-dir=SOURCE_DIR + Base directory where source code files can be found. + +Example usage: + + ./cppcheck gui/test.cpp --xml 2> err.xml + htmlreport/cppcheck-htmlreport --file=err.xml --report-dir=test1 --source-dir=. + +# Check Level + +## Normal + +The "normal" check level is chosen by default. Our aim is that this checking level will provide an effective checking in "reasonable" time. + +The "normal" check level should be useful during active development: + * checking files while you edit them. + * block changes to the repo + * etc + +## Exhaustive + +When you can wait longer for the results you can enable the "exhaustive" checking, by using the option `--check-level=exhaustive`. + +Exhaustive checking level should be useful for scenarios where you can wait for results. For instance: + * nightly builds + * etc + +# Speeding up analysis + +## Limit preprocessor configurations + +For performance reasons it might be a good idea to limit preprocessor configurations to check. + +## Limit ValueFlow: max if count + +The command line option `--performance-valueflow-max-if-count` adjusts the max count for number of if in a function. + +When that limit is exceeded there is a limitation of data flow in that function. It is not drastic: + * Analysis of other functions are not affected. + * It's only for some specific data flow analysis, we have data flow analysis that is always executed. + * All checks are always executed. There can still be plenty of warnings in the limited function. + +There is data flow analysis that slows down exponentially when number of if increase. And the limit is intended to avoid that +analysis time explodes. + +## GUI options + +In the GUI there are various options to limit analysis. + +In the GUI: + * Open the project dialog. + * In the "Analysis" tab there are several options. + +If you want to use these limitations on the command line also you can import the GUI project file with --project. + +# Cppcheck Premium + +## Bug hunting + +This is analysis that is more noisy than normal analysis. Most warnings will be false positives (cppcheck will wrongly claim that there are bugs). The design goal is to not have more than roughly 5 - 10 false positives in each file. + +It is not intended to be used in normal CI or regular static analysis by developers. The noise makes it useless for that. + +It is intended to be used when you are looking for bugs and you really can accept noise. For example: + * You have developed a brand new feature and want to ensure that there are no bugs. + * Maybe as part of release testing your product you can run bug hunting on modified files. + * Etc + +Technically, analysis that is "sound" will detect all bugs. Analysis that is "soundy" has the goal to detect most bugs and it tries to keep the noise at an reasonable level. + +The Cppcheck bug hunting analysis is "soundy". + +Command: + + cppcheck --premium=bughunting .... + +## Coding standards + +Command to active Autosar checkers: + + cppcheck --premium=autosar .... + +Command to active Cert C checkers: + + cppcheck --premium=cert-c .... + +Command to active Cert C++ checkers: + + cppcheck --premium=cert-c++ .... + +Command to active Misra C++ 2008 checkers: + + cppcheck --premium=misra-c++-2008 .... + +## Licenses + +### Individual license + +A license that is connected to your computer. You can check any code you want. + +### LOC license + +A license that allows you to run cppcheck on a limited number of lines of code. It can only be used for certain licensed paths in a repository. + +#### Running analysis + +Commands: + + cd check-path + + # Calculate lines of code and validate the license + premiumaddon --check-loc-license some-path/license-file > cppcheck-premium-loc + + # Run cppcheck analysis + cppcheck diff --git a/cppcheck-2.14.0/man/reference-cfg-format.md b/cppcheck-2.14.0/man/reference-cfg-format.md new file mode 100644 index 00000000..110e5f99 --- /dev/null +++ b/cppcheck-2.14.0/man/reference-cfg-format.md @@ -0,0 +1,613 @@ +--- +title: Cppcheck .cfg format +subtitle: Version 2.14 +author: Cppcheck team +lang: en +documentclass: report +--- + +# Introduction + +This is a reference for the .cfg file format that Cppcheck uses. + +# Memory and resource leaks + +Cppcheck has configurable checking for leaks, e.g. you can specify which functions allocate and free memory or resources and which functions do not affect the allocation at all. + +## ``, `` and `` + +Here is an example program: + + void test() + { + HPEN pen = CreatePen(PS_SOLID, 1, RGB(255,0,0)); + } + +The code example above has a resource leak - CreatePen() is a WinAPI function that creates a pen. However, Cppcheck doesn't assume that return values from functions must be freed. There is no error message: + + $ cppcheck pen1.c + Checking pen1.c... + +If you provide a configuration file then Cppcheck detects the bug: + + $ cppcheck --library=windows.cfg pen1.c + Checking pen1.c... + [pen1.c:3]: (error) Resource leak: pen + +Here is a minimal windows.cfg file: + + + + + CreatePen + DeleteObject + + + +Functions that reallocate memory can be configured using a `` tag. The input argument which points to the memory that shall be reallocated can also be configured (the default is the first argument). As an example, here is a configuration file for the fopen, freopen and fclose functions from the c standard library: + + + + + fopen + freopen + fclose + + + +The allocation and deallocation functions are organized in groups. Each group is defined in a `` or `` tag and is identified by its `` functions. This means, groups with overlapping `` tags are merged. + +## `` and `` + +Often the allocated pointer is passed to functions. Example: + + void test() + { + char *p = malloc(100); + dostuff(p); + } + +If Cppcheck doesn't know what `dostuff` does, without configuration it will assume that `dostuff` takes care of the memory so there is no memory leak. + +To specify that `dostuff` doesn't take care of the memory in any way, use `` in the `` tag (see next section): + + + + + + + + + +If instead `dostuff` takes care of the memory then this can be configured with: + + + + + free + dostuff + + + +The `` configuration has no logical purpose. You will get the same warnings without it. Use it to silence `--check-library` information messages. + +# Function behavior + +To specify the behaviour of functions and how they should be used, `` tags can be used. Functions are identified by their name, specified in the name attribute and their number of arguments. The name is a comma-separated list of function names. For functions in namespaces or classes, just provide their fully qualified name. For example: ``. If you have template functions then provide their instantiated names ``. + +## Function arguments + +The arguments a function takes can be specified by `` tags. Each of them takes the number of the argument (starting from 1) in the nr attribute, `nr="any"` for arbitrary arguments, or `nr="variadic"` for variadic arguments. Optional arguments can be specified by providing a default value: `default="value"`. The specifications for individual arguments override this setting. + +You can specify if an argument is an input or output argument. For example ``. The allowed directions are `in`, `out` and `inout`. + +### Not bool + +Here is an example program with misplaced comparison: + + void test() + { + if (MemCmp(buffer1, buffer2, 1024==0)) {} + } + +Cppcheck assumes that it is fine to pass boolean values to functions: + + $ cppcheck notbool.c + Checking notbool.c... + +If you provide a configuration file then Cppcheck detects the bug: + + $ cppcheck --library=notbool.cfg notbool.c + Checking notbool.c... + [notbool.c:5]: (error) Invalid MemCmp() argument nr 3. A non-boolean value is required. + +Here is the minimal notbool.cfg + + + + + + + + + + + + +### Uninitialized memory + +Here is an example program: + + void test() + { + char buffer1[1024]; + char buffer2[1024]; + CopyMemory(buffer1, buffer2, 1024); + } + +The bug here is that buffer2 is uninitialized. The second argument for CopyMemory needs to be initialized. However, Cppcheck assumes that it is fine to pass uninitialized variables to functions: + + $ cppcheck uninit.c + Checking uninit.c... + +If you provide a configuration file then Cppcheck detects the bug: + + $ cppcheck --library=windows.cfg uninit.c + Checking uninit.c... + [uninit.c:5]: (error) Uninitialized variable: buffer2 + +Below windows.cfg is shown: + +Version 1: + + + + + + + + + + + + + +Version 2: + + + + + + + + + + + + + +Version 1: If `indirect` attribute is not used then the level of indirection is determined automatically. The `` tells Cppcheck that the pointer must be initialized. The `` tells Cppcheck to check 1 extra level. This configuration means that both the pointer and the data must be initialized. + +Version 2: The `indirect` attribute can be set to explicitly control the level of indirection used in checking. Setting `indirect` to `0` means no uninitialized memory is allowed. Setting it to `1` allows a pointer to uninitialized memory. Setting it to `2` allows a pointer to pointer to uninitialized memory. + +### Null pointers + +Cppcheck assumes it's ok to pass NULL pointers to functions. Here is an example program: + + void test() + { + CopyMemory(NULL, NULL, 1024); + } + +The MSDN documentation is not clear if that is ok or not. But let's assume it's bad. Cppcheck assumes that it's ok to pass NULL to functions so no error is reported: + + $ cppcheck null.c + Checking null.c... + +If you provide a configuration file then Cppcheck detects the bug: + + $ cppcheck --library=windows.cfg null.c + Checking null.c... + [null.c:3]: (error) Null pointer dereference + +Note that this implies `` as far as values are concerned. Uninitialized memory might still be passed to the function. + +Here is a minimal windows.cfg file: + + + + + + + + + + + + +### Format string + +You can define that a function takes a format string. Example: + + void test() + { + do_something("%i %i\n", 1024); + } + +No error is reported for that: + + $ cppcheck formatstring.c + Checking formatstring.c... + +A configuration file can be created that says that the string is a format string. For instance: + + + + + + + + + + + +Now Cppcheck will report an error: + + $ cppcheck --library=test.cfg formatstring.c + Checking formatstring.c... + [formatstring.c:3]: (error) do_something format string requires 2 parameters but only 1 is given. + +The type attribute can be either: + +printf - format string follows the printf rules + +scanf - format string follows the scanf rules + +### Container inputs + +If this is a free function for containers(like for `std::size` or `std::erase_if`) then the `` tag can be used to specify the `yield` or `action`. Here is an example of `std::size`: + + + false + + + + + + + + + +### Value range + +The valid values can be defined. Imagine: + + void test() + { + do_something(1024); + } + +No error is reported for that: + + $ cppcheck valuerange.c + Checking valuerange.c... + +A configuration file can be created that says that 1024 is out of bounds. For instance: + + + + + + 0:1023 + + + + +Now Cppcheck will report an error: + + $ cppcheck --library=test.cfg range.c + Checking range.c... + [range.c:3]: (error) Invalid do_something() argument nr 1. The value is 1024 but the valid values are '0-1023'. + +Some example expressions you can use in the valid element: + +0,3,5 => only values 0, 3 and 5 are valid +-10:20 => all values between -10 and 20 are valid +:0 => all values that are less or equal to 0 are valid +0: => all values that are greater or equal to 0 are valid +0,2:32 => the value 0 and all values between 2 and 32 are valid +-1.5:5.6 => all values between -1.5 and 5.6 are valid +!0.0 => all values are accepted, except 0.0 + +### `` + +Some function arguments take a buffer. With minsize you can configure the min size of the buffer (in bytes, not elements). Imagine: + + void test() + { + char str[5]; + do_something(str,"12345"); + } + +No error is reported for that: + + $ cppcheck minsize.c + Checking minsize.c... + +A configuration file can for instance be created that says that the size of the buffer in argument 1 must be larger than the strlen of argument 2. For instance: + + + + + + + + + + + +Now Cppcheck will report this error: + + $ cppcheck --library=1.cfg minsize.c + Checking minsize.c... + [minsize.c:4]: (error) Buffer is accessed out of bounds: str + +There are different types of minsizes: + +strlen +buffer size must be larger than other arguments string length. Example: see strcpy configuration in std.cfg + +argvalue +buffer size must be larger than value in other argument. Example: see memset configuration in std.cfg + +sizeof +buffer size must be larger than other argument buffer size. Example: see memcpy configuration in posix.cfg + +mul +buffer size must be larger than multiplication result when multiplying values given in two other arguments. Typically one argument defines the element size and another element defines the number of elements. Example: see fread configuration in std.cfg + +strz +With this you can say that an argument must be a zero-terminated string. + + + + + + + + + + +### `` + +Cppcheck doesn't assume that functions always return. Here is an example code: + + void test(int x) + { + int data, buffer[1024]; + if (x == 1) + data = 123; + else + ZeroMemory(buffer, sizeof(buffer)); + buffer[0] = data; // <- error: data is uninitialized if x is not 1 + } + +In theory, if ZeroMemory terminates the program then there is no bug. Cppcheck therefore reports no error: + + $ cppcheck noreturn.c + Checking noreturn.c... + +However if you use `--check-library` you'll get this: + + $ cppcheck --check-library noreturn.c + Checking noreturn.c... + [noreturn.c:7]: (information) --check-library: Function ZeroMemory() should have configuration + +If a proper windows.cfg is provided, the bug is detected: + + $ cppcheck --library=windows.cfg noreturn.c + Checking noreturn.c... + [noreturn.c:8]: (error) Uninitialized variable: data + +Here is a minimal windows.cfg file: + + + + + false + + + + + +### `` + +As long as nothing else is specified, cppcheck assumes that ignoring the return value of a function is ok: + + bool test(const char* a, const char* b) + { + strcmp(a, b); // <- bug: The call of strcmp does not have side-effects, but the return value is ignored. + return true; + } + +In case strcmp has side effects, such as assigning the result to one of the parameters passed to it, nothing bad would happen: + + $ cppcheck useretval.c + Checking useretval.c... + +If a proper lib.cfg is provided, the bug is detected: + + $ cppcheck --library=lib.cfg --enable=warning useretval.c + Checking useretval.c... + [useretval.c:3]: (warning) Return value of function strcmp() is not used. + +Here is a minimal lib.cfg file: + + + + + + + + + + +### `` and `` + +These correspond to the GCC function attributes `` and ``. + +A pure function has no effects except to return a value, and its return value depends only on the parameters and global variables. + +A const function has no effects except to return a value, and its return value depends only on the parameters. + +Here is an example code: + + void f(int x) + { + if (calculate(x) == 213) { + } else if (calculate(x) == 213) { + // unreachable code + } + } + +If calculate() is a const function then the result of calculate(x) will be the same in both conditions, since the same parameter value is used. + +Cppcheck normally assumes that the result might be different, and reports no warning for the code: + + $ cppcheck const.c + Checking const.c... + +If a proper const.cfg is provided, the unreachable code is detected: + + $ cppcheck --enable=style --library=const const.c + Checking const.c... + [const.c:7]: (style) Expression is always false because 'else if' condition matches previous condition at line 5. + +Here is a minimal const.cfg file: + + + + + + + + + +### Example configuration for strcpy() + +The proper configuration for the standard strcpy() function would be: + + + + false + + + + + + + + + + +The `` tells Cppcheck to ignore this function call in the leaks checking. Passing allocated memory to this function won't mean it will be deallocated. + +The `` tells Cppcheck if this function returns or not. + +The first argument that the function takes is a pointer. It must not be a null pointer, therefore `` is used. + +The second argument the function takes is a pointer. It must not be null. And it must point at initialized data. Using `` and `` is correct. Moreover it must point at a zero-terminated string so `` is also used. + +# ``; check or suppress + +The ``configuration tells Cppcheck to show or suppress warnings for a certain type. + +Example: + + + + + + foo + bar + + + + +In the `unusedvar` checking the `foo` type will be checked. Warnings for `bar` type variables will be suppressed. + +# `` + +Libraries can be used to define preprocessor macros as well. For example: + + + + + + +Each occurrence of "NULL_VALUE" in the code would then be replaced by "0" at preprocessor stage. + +# `` + +Use this for integer/float/bool/pointer types. Not for structs/unions. + +Lots of code relies on typedefs providing platform independent types. "podtype"-tags can be used to provide necessary information to cppcheck to support them. Without further information, cppcheck does not understand the type "uint16_t" in the following example: + + void test() { + uint16_t a; + } + +No message about variable 'a' being unused is printed: + + $ cppcheck --enable=style unusedvar.cpp + Checking unusedvar.cpp... + +If uint16_t is defined in a library as follows, the result improves: + + + + + + +The size of the type is specified in bytes. Possible values for the "sign" attribute are "s" (signed) and "u" (unsigned). Both attributes are optional. Using this library, cppcheck prints: + + $ cppcheck --library=lib.cfg --enable=style unusedvar.cpp + Checking unusedvar.cpp... + [unusedvar.cpp:2]: (style) Unused variable: a + +# `` + +A lot of C++ libraries, among those the STL itself, provide containers with very similar functionality. Libraries can be used to tell cppcheck about their behaviour. Each container needs a unique ID. It can optionally have a startPattern, which must be a valid Token::Match pattern and an endPattern that is compared to the linked token of the first token with such a link. The optional attribute "inherits" takes an ID from a previously defined container. + +The `hasInitializerListConstructor` attribute can be set when the container has a constructor taking an initializer list. + +The `view` attribute can be set when the container is a view, which means it borrows the lifetime of another container. + +Inside the `` tag, functions can be defined inside of the tags ``, `` and `` (on your choice). Each of them can specify an action like "resize" and/or the result it yields, for example "end-iterator". + +The following example provides a definition for std::vector, based on the definition of "stdContainer" (not shown): + + + + + + + + + + + + + + + + +The tag `` can be added as well to provide more information about the type of container. Here is some of the attributes that can be set: + +* `string='std-like'` can be set for containers that match `std::string` interfaces. +* `associative='std-like'` can be set for containers that match C++'s `AssociativeContainer` interfaces. + +# `` + +Specify that a class is a smart pointer by using ``. + diff --git a/cppcheck-2.14.0/man/writing-addons.md b/cppcheck-2.14.0/man/writing-addons.md new file mode 100644 index 00000000..22a3b690 --- /dev/null +++ b/cppcheck-2.14.0/man/writing-addons.md @@ -0,0 +1,408 @@ +--- +title: Writing addons +subtitle: Version 2.14 +author: Cppcheck team +lang: en +documentclass: report +--- + +# Introduction + +This document provides an overview about writing Cppcheck addons. + + +# Overview of data + +## Tokens + +See class `Token` in cppcheckdata.py + +Cppcheck splits the code up in tokens: operators, numbers, identifiers, etc. + +Example C code: + + ab = a + b; + +Addon code: + + import cppcheck + + @cppcheck.checker + def func(cfg, data): + for token in cfg.tokenlist: + print(token.str) + +Output: + + $ cppcheck --dump test.c + $ python3 runaddon.py myaddon.py test.c.dump + Checking 1.c.dump... + Checking 1.c.dump, config ... + ab + = + a + + + b + ; + +The `cfg.tokenlist` does not always match the raw input code exactly. For instance: + * The `cfg.tokenlist` is preprocessed. + * There is no typedefs in `cfg.tokenlist`. + * C++ templates are instantiated when possible in `cfg.tokenlist`. + * Variable declarations are sometimes split up. + * If you don't write {} around the body for a if/else/while/for etc then those are inserted in the `cfg.tokenlist`. + * ... + +There are various properties in the `Token` class and some of those will be discussed below. + + +## AST - Abstract syntax tree + +Cppcheck creates a syntax tree for every expression. + +Example C code: + + ab = a + b; + +Addon code: + + import cppcheck + + @cppcheck.checker + def func(cfg, data): + for token in cfg.tokenlist: + out = token.str + if token.astParent: + out += f' parent:"{token.astParent.str}"' + if token.astOperand1: + out += f' astOperand1:"{token.astOperand1.str}"' + if token.astOperand2: + out += f' astOperand2:"{token.astOperand2.str}"' + print(out) + +Output: + + $ cppcheck --dump test.c + $ python3 runaddon.py myaddon.py test.c.dump + Checking 1.c.dump... + Checking 1.c.dump, config ... + ab parent:"=" + = astOperand1:"ab" astOperand2:"+" + a parent:"+" + + parent:"=" astOperand1:"a" astOperand2:"b" + b parent:"+" + ; + +## ValueType + +Data type of expressions are provided by `class ValueType` in cppcheckdata.py. + +Example C code: + + short a; + a = a + 10; + +Addon code: + + import cppcheck + + @cppcheck.checker + def func(cfg, data): + for token in cfg.tokenlist: + print(f'{token.str} : {token.valueType}') + +Output: + + $ cppcheck --dump test.c + $ python3 runaddon.py myaddon.py test.c.dump + Checking 1.c.dump... + Checking 1.c.dump, config ... + short : None + a : ValueType(type='short', sign='signed', bits=0, typeScopeId=None, originalTypeName=None, constness=0, pointer=0) + ; : None + a : ValueType(type='short', sign='signed', bits=0, typeScopeId=None, originalTypeName=None, constness=0, pointer=0) + = : ValueType(type='short', sign='signed', bits=0, typeScopeId=None, originalTypeName=None, constness=0, pointer=0) + a : ValueType(type='short', sign='signed', bits=0, typeScopeId=None, originalTypeName=None, constness=0, pointer=0) + + : ValueType(type='int', sign='signed', bits=0, typeScopeId=None, originalTypeName=None, constness=0, pointer=0) + 10 : ValueType(type='int', sign='signed', bits=0, typeScopeId=None, originalTypeName=None, constness=0, pointer=0) + ; : None + +The `pointer` property is a simple counter. + + int p => pointer=0 + int *p => pointer=1 + int **p => pointer=2 + +The `constness` property is a bitmask. + + int * * => constness=0 + const int * * => constness=1 + int * const * => constness=2 + int * * const => constness=4 + const int * const * => constness=3 + const int * const * const => constness=7 + + +## Variable + +Information about a variable is provided by `class Variable` in cppcheckdata.py. + +Example code: + + short a[10]; + a[0] = 0; + + +Addon code: + + import cppcheck + + @cppcheck.checker + def func(cfg, data): + for token in cfg.tokenlist: + print(f'{token.str} : {token.variable}') + +Output: + + $ cppcheck --dump test.c + $ python3 runaddon.py myaddon.py test.c.dump + Checking 1.c.dump... + Checking 1.c.dump, config ... + a : Variable(Id='0x55f432fc1580', nameTokenId='0x55f432fe0850', typeStartTokenId='0x55f432fc0eb0', typeEndTokenId='0x55f432fc0eb0', access='Global', scopeId='0x55f432fe0360', isArgument=False, isArray=True, isClass=False, isConst=False, isGlobal=True, isExtern=False, isLocal=False, isPointer=False, isReference=False, isStatic=False, constness=0) + [ : None + 10 : None + ] : None + ; : None + a : Variable(Id='0x55f432fc1580', nameTokenId='0x55f432fe0850', typeStartTokenId='0x55f432fc0eb0', typeEndTokenId='0x55f432fc0eb0', access='Global', scopeId='0x55f432fe0360', isArgument=False, isArray=True, isClass=False, isConst=False, isGlobal=True, isExtern=False, isLocal=False, isPointer=False, isReference=False, isStatic=False, constness=0) + [ : None + 0 : None + ] : None + = : None + 0 : None + ; : None + + +## Scope + +See `class Scope` in cppcheckdata.py. + +Every token is in some scope. + +If you see {} in the code then that is a scope of some kind. There is one scope that is not surrounded by {}; the global scope. + +Example c code: + + int x; + void foo() + { + if (x) + { + x = 0; + } + } + +Example addon #1 (list all scopes): + + import cppcheck + + @cppcheck.checker + def func(cfg, data): + for scope in cfg.scopes: + print(scope.type) + +Output: + + $ cppcheck --dump test.c + $ python3 runaddon.py myaddon.py test.c.dump + Checking 1.c.dump... + Checking 1.c.dump, config ... + Global + Function + If + +Example addon #2 (show scope for each token): + + import cppcheck + + @cppcheck.checker + def func(cfg, data): + for token in cfg.tokenlist: + print(f'{token.str} : {scope.type}') + +Output: + + $ cppcheck --dump test.c + $ python3 runaddon.py myaddon.py test.c.dump + Checking 1.c.dump... + Checking 1.c.dump, config ... + int : Global + x : Global + ; : Global + void : Global + foo : Global + ( : Global + ) : Global + { : Function + if : Function + ( : Function + x : Function + ) : Function + { : If + x : If + = : If + 0 : If + ; : If + } : If + } : Function + +### Special tokenlist tweaks and else if + +The `cfg.tokenlist` has some tweaks. In C/C++ code it is optional to use `{` and `}` around the if/else/for/while body, +if the body is only a single statement. However Cppcheck adds "{" and "}" tokens if those are missing. + +One more tweak is that in cfg.tokenlist there is no "else if" scope. + +Example C code: + + void foo(int x) + { + if (x > 0) + --x; + else if (x < 0) + ++x; + } + +The tokens in the `cfg.tokenlist` will look like this: + + void foo(int x) + { + if (x > 0) + { + --x; + } + else + { + if (x < 0) + { + ++x; + } + } + } + +And there are 2 "If" scopes here. And 1 "Else" scope. + + +## Function + +The class `Function` in cppcheckdata.py represents a function that is declared somewhere. + +Example code: + + void foo(int x); + + void bar() + { + foo(1); + } + + +Example addon #1: + + import cppcheck + + @cppcheck.checker + def func(cfg, data): + for token in cfg.tokenlist: + print(f'{token.str} : {token.function}') + +Output: + + void : None + foo : Function(Id='0x55c5f9330f60', tokenId='0', tokenDefId='0x55c5f93510d0', name='foo', type='Function', isVirtual=False, isImplicitlyVirtual=False, isInlineKeyword=False, isStatic=False, argumentId={1: '0x55c5f9351260'}) + ( : None + int : None + x : None + ) : None + ; : None + void : None + bar : Function(Id='0x55c5f9331040', tokenId='0x55c5f93314a0', tokenDefId='0x55c5f93314a0', name='bar', type='Function', isVirtual=False, isImplicitlyVirtual=False, isInlineKeyword=False, isStatic=False, argumentId={}) + ( : None + ) : None + { : None + foo : Function(Id='0x55c5f9330f60', tokenId='0', tokenDefId='0x55c5f93510d0', name='foo', type='Function', isVirtual=False, isImplicitlyVirtual=False, isInlineKeyword=False, isStatic=False, argumentId={1: '0x55c5f9351260'}) + ( : None + 1 : None + ) : None + ; : None + } : None + + +## Value flow + +For Cppcheck, by default variables and expressions have "unknown" values. If the value is only constrained by the data type that is typically not enough information to write warnings with precision. Unless all the values possible in the data type are bad. + +In Cppcheck terminology an expression will have a "possible" value if Cppcheck can determine that there will be a specific value in some control flow paths. + +In Cppcheck terminology an expression will have a "known" value if Cppcheck can determine that there will be a specific value in all control flow paths. + +An expression can have several "possible" values. But it can't have several "known" values. + +Example code: + + void foo(int x) // <- values of x is only constrained by data type. there are no "possible" or "known" values here. + { + a = x; // <- assuming that condition below is not redundant, x can have value 2. + if (x == 2) + { + b = x + 2; // <- value of x is always 2 when this code is executed. It's "known". + } + else + { + c = x; + } + d = x + 10; // <- value of x can be 2 when this code is executed. It's "possible". + } + +Addon: + + import cppcheck + + @cppcheck.checker + def func(cfg, data): + for token in cfg.tokenlist: + if token.values: + print(f'line {token.linenr} str="{token.str}"') + for value in token.values: + print(f' {value}') + +Output: + + $ ../cppcheck --dump 1.c ; python3 runaddon.py myaddon.py 1.c.dump + Checking 1.c ... + Checking 1.c.dump... + Checking 1.c.dump, config ... + line 3 str="=" + Value(intvalue=2, tokvalue=None, floatvalue=None, containerSize=None, condition=4, valueKind='possible', inconclusive=False) + line 3 str="x" + Value(intvalue=2, tokvalue=None, floatvalue=None, containerSize=None, condition=4, valueKind='possible', inconclusive=False) + line 4 str="2" + Value(intvalue=2, tokvalue=None, floatvalue=None, containerSize=None, condition=None, valueKind='known', inconclusive=False) + line 6 str="=" + Value(intvalue=4, tokvalue=None, floatvalue=None, containerSize=None, condition=4, valueKind='known', inconclusive=False) + line 6 str="x" + Value(intvalue=2, tokvalue=None, floatvalue=None, containerSize=None, condition=4, valueKind='known', inconclusive=False) + line 6 str="+" + Value(intvalue=4, tokvalue=None, floatvalue=None, containerSize=None, condition=4, valueKind='known', inconclusive=False) + line 6 str="2" + Value(intvalue=2, tokvalue=None, floatvalue=None, containerSize=None, condition=None, valueKind='known', inconclusive=False) + line 12 str="=" + Value(intvalue=12, tokvalue=None, floatvalue=None, containerSize=None, condition=4, valueKind='possible', inconclusive=False) + line 12 str="x" + Value(intvalue=2, tokvalue=None, floatvalue=None, containerSize=None, condition=4, valueKind='possible', inconclusive=False) + line 12 str="+" + Value(intvalue=12, tokvalue=None, floatvalue=None, containerSize=None, condition=4, valueKind='possible', inconclusive=False) + line 12 str="10" + Value(intvalue=10, tokvalue=None, floatvalue=None, containerSize=None, condition=None, valueKind='known', inconclusive=False) + +If the value is determined from a condition then the attribute `condition` points out the line that has the condition. + diff --git a/cppcheck-2.14.0/man/writing-rules-1.docbook b/cppcheck-2.14.0/man/writing-rules-1.docbook new file mode 100644 index 00000000..26936d26 --- /dev/null +++ b/cppcheck-2.14.0/man/writing-rules-1.docbook @@ -0,0 +1,133 @@ + +
+ 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. +
+
+
diff --git a/cppcheck-2.14.0/man/writing-rules-2.docbook b/cppcheck-2.14.0/man/writing-rules-2.docbook new file mode 100644 index 00000000..5a246d6c --- /dev/null +++ b/cppcheck-2.14.0/man/writing-rules-2.docbook @@ -0,0 +1,339 @@ + +
+ 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: } +
+
+
+
diff --git a/cppcheck-2.14.0/man/writing-rules-3.docbook b/cppcheck-2.14.0/man/writing-rules-3.docbook new file mode 100644 index 00000000..76792cd0 --- /dev/null +++ b/cppcheck-2.14.0/man/writing-rules-3.docbook @@ -0,0 +1,229 @@ + +
+ 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::toBigNumber(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 + [(,]. +
+
diff --git a/cppcheck-2.14.0/naming.json b/cppcheck-2.14.0/naming.json new file mode 100644 index 00000000..ca8bfe8a --- /dev/null +++ b/cppcheck-2.14.0/naming.json @@ -0,0 +1,9 @@ +{ + "script": "addons/naming.py", + "args": [ + "--private-member-variable=m[A-Z].*", + "--var=[_a-z].*", + "--const=[_a-zA-Z].*", + "--function=[a-zA-Z].*" + ] +} diff --git a/cppcheck-2.14.0/oss-fuzz/Makefile b/cppcheck-2.14.0/oss-fuzz/Makefile new file mode 100644 index 00000000..6bfad47f --- /dev/null +++ b/cppcheck-2.14.0/oss-fuzz/Makefile @@ -0,0 +1,342 @@ +# This file is generated by dmake, do not edit. + +# make CXX=clang++ MATCHCOMPILER=yes CXXFLAGS="-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=address -fsanitize-address-use-after-scope -DHAVE_BOOST" LIB_FUZZING_ENGINE="-fsanitize=fuzzer" oss-fuzz-client + +MATCHCOMPILER=yes +ifndef MATCHCOMPILER + MATCHCOMPILER= +endif +# use match compiler +ifeq ($(MATCHCOMPILER),yes) + # Find available Python interpreter + ifeq ($(PYTHON_INTERPRETER),) + PYTHON_INTERPRETER := $(shell which python3) + endif + ifeq ($(PYTHON_INTERPRETER),) + PYTHON_INTERPRETER := $(shell which python) + endif + ifeq ($(PYTHON_INTERPRETER),) + $(error Did not find a Python interpreter) + endif + ifdef VERIFY + matchcompiler_S := $(shell $(PYTHON_INTERPRETER) ../tools/matchcompiler.py --read-dir ../lib --verify) + else + matchcompiler_S := $(shell $(PYTHON_INTERPRETER) ../tools/matchcompiler.py --read-dir ../lib) + endif + libcppdir:=build +else ifeq ($(MATCHCOMPILER),) + libcppdir:=lib +else + $(error invalid MATCHCOMPILER value '$(MATCHCOMPILER)') +endif + +INCS=-I../lib -isystem../externals/simplecpp -isystem../externals/tinyxml2 -isystem../externals/picojson +CPPFLAGS=-std=c++11 -g -w $(INCS) + +LIBOBJ = $(libcppdir)/valueflow.o \ + $(libcppdir)/tokenize.o \ + $(libcppdir)/symboldatabase.o \ + $(libcppdir)/addoninfo.o \ + $(libcppdir)/analyzerinfo.o \ + $(libcppdir)/astutils.o \ + $(libcppdir)/check.o \ + $(libcppdir)/check64bit.o \ + $(libcppdir)/checkassert.o \ + $(libcppdir)/checkautovariables.o \ + $(libcppdir)/checkbool.o \ + $(libcppdir)/checkboost.o \ + $(libcppdir)/checkbufferoverrun.o \ + $(libcppdir)/checkclass.o \ + $(libcppdir)/checkcondition.o \ + $(libcppdir)/checkers.o \ + $(libcppdir)/checkersreport.o \ + $(libcppdir)/checkexceptionsafety.o \ + $(libcppdir)/checkfunctions.o \ + $(libcppdir)/checkinternal.o \ + $(libcppdir)/checkio.o \ + $(libcppdir)/checkleakautovar.o \ + $(libcppdir)/checkmemoryleak.o \ + $(libcppdir)/checknullpointer.o \ + $(libcppdir)/checkother.o \ + $(libcppdir)/checkpostfixoperator.o \ + $(libcppdir)/checksizeof.o \ + $(libcppdir)/checkstl.o \ + $(libcppdir)/checkstring.o \ + $(libcppdir)/checktype.o \ + $(libcppdir)/checkuninitvar.o \ + $(libcppdir)/checkunusedfunctions.o \ + $(libcppdir)/checkunusedvar.o \ + $(libcppdir)/checkvaarg.o \ + $(libcppdir)/clangimport.o \ + $(libcppdir)/color.o \ + $(libcppdir)/cppcheck.o \ + $(libcppdir)/ctu.o \ + $(libcppdir)/errorlogger.o \ + $(libcppdir)/errortypes.o \ + $(libcppdir)/forwardanalyzer.o \ + $(libcppdir)/fwdanalysis.o \ + $(libcppdir)/importproject.o \ + $(libcppdir)/infer.o \ + $(libcppdir)/keywords.o \ + $(libcppdir)/library.o \ + $(libcppdir)/mathlib.o \ + $(libcppdir)/path.o \ + $(libcppdir)/pathanalysis.o \ + $(libcppdir)/pathmatch.o \ + $(libcppdir)/platform.o \ + $(libcppdir)/preprocessor.o \ + $(libcppdir)/programmemory.o \ + $(libcppdir)/reverseanalyzer.o \ + $(libcppdir)/settings.o \ + $(libcppdir)/summaries.o \ + $(libcppdir)/suppressions.o \ + $(libcppdir)/templatesimplifier.o \ + $(libcppdir)/timer.o \ + $(libcppdir)/token.o \ + $(libcppdir)/tokenlist.o \ + $(libcppdir)/utils.o \ + $(libcppdir)/vfvalue.o + +EXTOBJ = simplecpp.o \ + tinyxml2.o + +oss-fuzz-client: $(EXTOBJ) $(LIBOBJ) main.o type2.o + ${CXX} $(CPPFLAGS) ${CXXFLAGS} -o $@ $^ ${LIB_FUZZING_ENGINE} + +no-fuzz: $(EXTOBJ) $(LIBOBJ) main_nofuzz.o type2.o + ${CXX} $(CPPFLAGS) ${CXXFLAGS} -o $@ $^ + +translate: translate.o type2.o + ${CXX} -std=c++11 -g ${CXXFLAGS} -o $@ type2.cpp translate.cpp + +clean: + rm -f *.o build/*.o oss-fuzz-client no-fuzz translate + +preprare-samples: + rm -rf samples + mkdir -p samples + cp -R ../samples . + find ./samples -type f -name '*.txt' -exec rm -vf {} \; + +do-fuzz: oss-fuzz-client preprare-samples + mkdir -p corpus + ./oss-fuzz-client -only_ascii=1 -timeout=3 -detect_leaks=0 corpus samples ../test/cli/fuzz-crash ../test/cli/fuzz-timeout + +dedup-corpus: oss-fuzz-client preprare-samples + mv corpus corpus_ + mkdir -p corpus + ./oss-fuzz-client -only_ascii=1 -timeout=3 -detect_leaks=0 corpus corpus_ samples ../test/cli/fuzz-crash ../test/cli/fuzz-timeout -merge=1 + +# jobs: +# ./oss-fuzz-client -only_ascii=1 -timeout=3 -detect_leaks=0 corpus samples ../test/cli/fuzz-crash ../test/cli/fuzz-timeout -workers=12 -jobs=9 + +# minimize: +# ./oss-fuzz-client -only_ascii=1 -timeout=3 -detect_leaks=0 -minimize_crash=1 crash-0123456789abcdef + +simplecpp.o: ../externals/simplecpp/simplecpp.cpp ../externals/simplecpp/simplecpp.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -w -c -o $@ ../externals/simplecpp/simplecpp.cpp + +tinyxml2.o: ../externals/tinyxml2/tinyxml2.cpp ../externals/tinyxml2/tinyxml2.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -w -c -o $@ ../externals/tinyxml2/tinyxml2.cpp + +$(libcppdir)/valueflow.o: ../lib/valueflow.cpp ../lib/addoninfo.h ../lib/analyzer.h ../lib/astutils.h ../lib/calculate.h ../lib/check.h ../lib/checkuninitvar.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/findtoken.h ../lib/forwardanalyzer.h ../lib/infer.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/programmemory.h ../lib/reverseanalyzer.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/timer.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/valueptr.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/valueflow.cpp + +$(libcppdir)/tokenize.o: ../lib/tokenize.cpp ../externals/simplecpp/simplecpp.h ../lib/addoninfo.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/preprocessor.h ../lib/settings.h ../lib/sourcelocation.h ../lib/standards.h ../lib/summaries.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/timer.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/tokenize.cpp + +$(libcppdir)/symboldatabase.o: ../lib/symboldatabase.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/keywords.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/symboldatabase.cpp + +$(libcppdir)/addoninfo.o: ../lib/addoninfo.cpp ../externals/picojson/picojson.h ../lib/addoninfo.h ../lib/config.h ../lib/json.h ../lib/path.h ../lib/standards.h ../lib/utils.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/addoninfo.cpp + +$(libcppdir)/analyzerinfo.o: ../lib/analyzerinfo.cpp ../externals/tinyxml2/tinyxml2.h ../lib/analyzerinfo.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/filesettings.h ../lib/path.h ../lib/platform.h ../lib/standards.h ../lib/utils.h ../lib/xml.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/analyzerinfo.cpp + +$(libcppdir)/astutils.o: ../lib/astutils.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkclass.h ../lib/config.h ../lib/errortypes.h ../lib/findtoken.h ../lib/infer.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/valueptr.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/astutils.cpp + +$(libcppdir)/check.o: ../lib/check.cpp ../lib/addoninfo.h ../lib/check.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/standards.h ../lib/suppressions.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/check.cpp + +$(libcppdir)/check64bit.o: ../lib/check64bit.cpp ../lib/addoninfo.h ../lib/check.h ../lib/check64bit.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/check64bit.cpp + +$(libcppdir)/checkassert.o: ../lib/checkassert.cpp ../lib/addoninfo.h ../lib/check.h ../lib/checkassert.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkassert.cpp + +$(libcppdir)/checkautovariables.o: ../lib/checkautovariables.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkautovariables.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkautovariables.cpp + +$(libcppdir)/checkbool.o: ../lib/checkbool.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkbool.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkbool.cpp + +$(libcppdir)/checkboost.o: ../lib/checkboost.cpp ../lib/check.h ../lib/checkboost.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/sourcelocation.h ../lib/standards.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkboost.cpp + +$(libcppdir)/checkbufferoverrun.o: ../lib/checkbufferoverrun.cpp ../externals/tinyxml2/tinyxml2.h ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkbufferoverrun.h ../lib/color.h ../lib/config.h ../lib/ctu.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h ../lib/xml.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkbufferoverrun.cpp + +$(libcppdir)/checkclass.o: ../lib/checkclass.cpp ../externals/tinyxml2/tinyxml2.h ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkclass.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h ../lib/xml.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkclass.cpp + +$(libcppdir)/checkcondition.o: ../lib/checkcondition.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkcondition.h ../lib/checkother.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkcondition.cpp + +$(libcppdir)/checkers.o: ../lib/checkers.cpp ../lib/checkers.h ../lib/config.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkers.cpp + +$(libcppdir)/checkersreport.o: ../lib/checkersreport.cpp ../lib/addoninfo.h ../lib/checkers.h ../lib/checkersreport.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/standards.h ../lib/suppressions.h ../lib/utils.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkersreport.cpp + +$(libcppdir)/checkexceptionsafety.o: ../lib/checkexceptionsafety.cpp ../lib/addoninfo.h ../lib/check.h ../lib/checkexceptionsafety.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkexceptionsafety.cpp + +$(libcppdir)/checkfunctions.o: ../lib/checkfunctions.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkfunctions.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkfunctions.cpp + +$(libcppdir)/checkinternal.o: ../lib/checkinternal.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkinternal.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkinternal.cpp + +$(libcppdir)/checkio.o: ../lib/checkio.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkio.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkio.cpp + +$(libcppdir)/checkleakautovar.o: ../lib/checkleakautovar.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkleakautovar.h ../lib/checkmemoryleak.h ../lib/checknullpointer.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkleakautovar.cpp + +$(libcppdir)/checkmemoryleak.o: ../lib/checkmemoryleak.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkmemoryleak.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkmemoryleak.cpp + +$(libcppdir)/checknullpointer.o: ../lib/checknullpointer.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checknullpointer.h ../lib/color.h ../lib/config.h ../lib/ctu.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checknullpointer.cpp + +$(libcppdir)/checkother.o: ../lib/checkother.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkother.h ../lib/config.h ../lib/errortypes.h ../lib/fwdanalysis.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkother.cpp + +$(libcppdir)/checkpostfixoperator.o: ../lib/checkpostfixoperator.cpp ../lib/addoninfo.h ../lib/check.h ../lib/checkpostfixoperator.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkpostfixoperator.cpp + +$(libcppdir)/checksizeof.o: ../lib/checksizeof.cpp ../lib/addoninfo.h ../lib/check.h ../lib/checksizeof.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checksizeof.cpp + +$(libcppdir)/checkstl.o: ../lib/checkstl.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checknullpointer.h ../lib/checkstl.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/pathanalysis.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkstl.cpp + +$(libcppdir)/checkstring.o: ../lib/checkstring.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkstring.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkstring.cpp + +$(libcppdir)/checktype.o: ../lib/checktype.cpp ../lib/addoninfo.h ../lib/check.h ../lib/checktype.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checktype.cpp + +$(libcppdir)/checkuninitvar.o: ../lib/checkuninitvar.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checknullpointer.h ../lib/checkuninitvar.h ../lib/color.h ../lib/config.h ../lib/ctu.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkuninitvar.cpp + +$(libcppdir)/checkunusedfunctions.o: ../lib/checkunusedfunctions.cpp ../externals/tinyxml2/tinyxml2.h ../lib/addoninfo.h ../lib/astutils.h ../lib/checkunusedfunctions.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h ../lib/xml.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkunusedfunctions.cpp + +$(libcppdir)/checkunusedvar.o: ../lib/checkunusedvar.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkunusedvar.h ../lib/config.h ../lib/errortypes.h ../lib/fwdanalysis.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkunusedvar.cpp + +$(libcppdir)/checkvaarg.o: ../lib/checkvaarg.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/checkvaarg.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkvaarg.cpp + +$(libcppdir)/clangimport.o: ../lib/clangimport.cpp ../lib/addoninfo.h ../lib/clangimport.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/clangimport.cpp + +$(libcppdir)/color.o: ../lib/color.cpp ../lib/color.h ../lib/config.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/color.cpp + +$(libcppdir)/cppcheck.o: ../lib/cppcheck.cpp ../externals/picojson/picojson.h ../externals/simplecpp/simplecpp.h ../externals/tinyxml2/tinyxml2.h ../lib/addoninfo.h ../lib/analyzerinfo.h ../lib/check.h ../lib/checkunusedfunctions.h ../lib/clangimport.h ../lib/color.h ../lib/config.h ../lib/cppcheck.h ../lib/ctu.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/filesettings.h ../lib/json.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/preprocessor.h ../lib/settings.h ../lib/standards.h ../lib/suppressions.h ../lib/templatesimplifier.h ../lib/timer.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/version.h ../lib/vfvalue.h ../lib/xml.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/cppcheck.cpp + +$(libcppdir)/ctu.o: ../lib/ctu.cpp ../externals/tinyxml2/tinyxml2.h ../lib/addoninfo.h ../lib/astutils.h ../lib/check.h ../lib/color.h ../lib/config.h ../lib/ctu.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h ../lib/xml.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/ctu.cpp + +$(libcppdir)/errorlogger.o: ../lib/errorlogger.cpp ../externals/tinyxml2/tinyxml2.h ../lib/addoninfo.h ../lib/analyzerinfo.h ../lib/check.h ../lib/color.h ../lib/config.h ../lib/cppcheck.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/settings.h ../lib/standards.h ../lib/suppressions.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h ../lib/xml.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/errorlogger.cpp + +$(libcppdir)/errortypes.o: ../lib/errortypes.cpp ../lib/config.h ../lib/errortypes.h ../lib/utils.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/errortypes.cpp + +$(libcppdir)/forwardanalyzer.o: ../lib/forwardanalyzer.cpp ../lib/addoninfo.h ../lib/analyzer.h ../lib/astutils.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/forwardanalyzer.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueptr.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/forwardanalyzer.cpp + +$(libcppdir)/fwdanalysis.o: ../lib/fwdanalysis.cpp ../lib/astutils.h ../lib/config.h ../lib/errortypes.h ../lib/fwdanalysis.h ../lib/library.h ../lib/mathlib.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/fwdanalysis.cpp + +$(libcppdir)/importproject.o: ../lib/importproject.cpp ../externals/picojson/picojson.h ../externals/tinyxml2/tinyxml2.h ../lib/addoninfo.h ../lib/config.h ../lib/errortypes.h ../lib/filesettings.h ../lib/importproject.h ../lib/json.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/settings.h ../lib/standards.h ../lib/suppressions.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h ../lib/xml.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/importproject.cpp + +$(libcppdir)/infer.o: ../lib/infer.cpp ../lib/calculate.h ../lib/config.h ../lib/errortypes.h ../lib/infer.h ../lib/mathlib.h ../lib/valueptr.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/infer.cpp + +$(libcppdir)/keywords.o: ../lib/keywords.cpp ../lib/config.h ../lib/keywords.h ../lib/standards.h ../lib/utils.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/keywords.cpp + +$(libcppdir)/library.o: ../lib/library.cpp ../externals/tinyxml2/tinyxml2.h ../lib/astutils.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h ../lib/xml.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/library.cpp + +$(libcppdir)/mathlib.o: ../lib/mathlib.cpp ../externals/simplecpp/simplecpp.h ../lib/config.h ../lib/errortypes.h ../lib/mathlib.h ../lib/utils.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/mathlib.cpp + +$(libcppdir)/path.o: ../lib/path.cpp ../externals/simplecpp/simplecpp.h ../lib/config.h ../lib/path.h ../lib/standards.h ../lib/utils.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/path.cpp + +$(libcppdir)/pathanalysis.o: ../lib/pathanalysis.cpp ../lib/astutils.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/pathanalysis.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/pathanalysis.cpp + +$(libcppdir)/pathmatch.o: ../lib/pathmatch.cpp ../lib/config.h ../lib/path.h ../lib/pathmatch.h ../lib/standards.h ../lib/utils.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/pathmatch.cpp + +$(libcppdir)/platform.o: ../lib/platform.cpp ../externals/tinyxml2/tinyxml2.h ../lib/config.h ../lib/path.h ../lib/platform.h ../lib/standards.h ../lib/utils.h ../lib/xml.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/platform.cpp + +$(libcppdir)/preprocessor.o: ../lib/preprocessor.cpp ../externals/simplecpp/simplecpp.h ../lib/addoninfo.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/preprocessor.h ../lib/settings.h ../lib/standards.h ../lib/suppressions.h ../lib/utils.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/preprocessor.cpp + +$(libcppdir)/programmemory.o: ../lib/programmemory.cpp ../lib/addoninfo.h ../lib/astutils.h ../lib/calculate.h ../lib/config.h ../lib/errortypes.h ../lib/infer.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/programmemory.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/utils.h ../lib/valueflow.h ../lib/valueptr.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/programmemory.cpp + +$(libcppdir)/reverseanalyzer.o: ../lib/reverseanalyzer.cpp ../lib/addoninfo.h ../lib/analyzer.h ../lib/astutils.h ../lib/config.h ../lib/errortypes.h ../lib/forwardanalyzer.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/reverseanalyzer.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/utils.h ../lib/valueptr.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/reverseanalyzer.cpp + +$(libcppdir)/settings.o: ../lib/settings.cpp ../externals/picojson/picojson.h ../lib/addoninfo.h ../lib/config.h ../lib/errortypes.h ../lib/json.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/settings.h ../lib/standards.h ../lib/summaries.h ../lib/suppressions.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/settings.cpp + +$(libcppdir)/summaries.o: ../lib/summaries.cpp ../lib/addoninfo.h ../lib/analyzerinfo.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/sourcelocation.h ../lib/standards.h ../lib/summaries.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/summaries.cpp + +$(libcppdir)/suppressions.o: ../lib/suppressions.cpp ../externals/tinyxml2/tinyxml2.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/mathlib.h ../lib/path.h ../lib/standards.h ../lib/suppressions.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h ../lib/xml.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/suppressions.cpp + +$(libcppdir)/templatesimplifier.o: ../lib/templatesimplifier.cpp ../lib/addoninfo.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/standards.h ../lib/suppressions.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenize.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/templatesimplifier.cpp + +$(libcppdir)/timer.o: ../lib/timer.cpp ../lib/config.h ../lib/timer.h ../lib/utils.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/timer.cpp + +$(libcppdir)/token.o: ../lib/token.cpp ../externals/simplecpp/simplecpp.h ../lib/addoninfo.h ../lib/astutils.h ../lib/config.h ../lib/errortypes.h ../lib/library.h ../lib/mathlib.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/tokenrange.h ../lib/utils.h ../lib/valueflow.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/token.cpp + +$(libcppdir)/tokenlist.o: ../lib/tokenlist.cpp ../externals/simplecpp/simplecpp.h ../lib/addoninfo.h ../lib/astutils.h ../lib/color.h ../lib/config.h ../lib/errorlogger.h ../lib/errortypes.h ../lib/keywords.h ../lib/library.h ../lib/mathlib.h ../lib/path.h ../lib/platform.h ../lib/settings.h ../lib/smallvector.h ../lib/sourcelocation.h ../lib/standards.h ../lib/suppressions.h ../lib/symboldatabase.h ../lib/templatesimplifier.h ../lib/token.h ../lib/tokenlist.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/tokenlist.cpp + +$(libcppdir)/utils.o: ../lib/utils.cpp ../lib/config.h ../lib/utils.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/utils.cpp + +$(libcppdir)/vfvalue.o: ../lib/vfvalue.cpp ../lib/config.h ../lib/errortypes.h ../lib/mathlib.h ../lib/templatesimplifier.h ../lib/token.h ../lib/utils.h ../lib/vfvalue.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/vfvalue.cpp + + +type2.o: type2.cpp type2.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ type2.cpp + +translate.o: translate.cpp type2.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ translate.cpp + +main.o: main.cpp type2.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ main.cpp + +main_nofuzz.o: main.cpp type2.h + $(CXX) ${LIB_FUZZING_ENGINE} $(CPPFLAGS) $(CXXFLAGS) -DNO_FUZZ -c -o $@ main.cpp diff --git a/cppcheck-2.14.0/oss-fuzz/main.cpp b/cppcheck-2.14.0/oss-fuzz/main.cpp new file mode 100644 index 00000000..27b0ca23 --- /dev/null +++ b/cppcheck-2.14.0/oss-fuzz/main.cpp @@ -0,0 +1,83 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "type2.h" + +#ifdef NO_FUZZ +#include +#include +#include +#endif + +enum class Color; + +class DummyErrorLogger : public ErrorLogger { +public: + void reportOut(const std::string& /*outmsg*/, Color /*c*/) override {} + void reportErr(const ErrorMessage& /*msg*/) override {} + void reportProgress(const std::string& /*filename*/, + const char /*stage*/[], + const std::size_t /*value*/) override {} // FN +}; + +static DummyErrorLogger s_errorLogger; + +static void doCheck(const std::string& code) +{ + CppCheck cppcheck(s_errorLogger, false, nullptr); + cppcheck.settings().addEnabled("all"); + cppcheck.settings().certainty.setEnabled(Certainty::inconclusive, true); + cppcheck.check("test.cpp", code); +} + +#ifndef NO_FUZZ +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize); + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) +{ + if (dataSize < 10000) { + const std::string code = generateCode2(data, dataSize); + doCheck(code); + } + return 0; +} +#else +int main(int argc, char * argv[]) +{ + if (argc != 2) + return EXIT_FAILURE; + + std::ifstream f(argv[1]); + if (!f.is_open()) + return EXIT_FAILURE; + + std::ostringstream oss; + oss << f.rdbuf(); + + if (!f.good()) + return EXIT_FAILURE; + + const std::string code = oss.str(); + doCheck(code); + + return EXIT_SUCCESS; +} +#endif + + diff --git a/cppcheck-2.14.0/oss-fuzz/translate.cpp b/cppcheck-2.14.0/oss-fuzz/translate.cpp new file mode 100644 index 00000000..5ebddd26 --- /dev/null +++ b/cppcheck-2.14.0/oss-fuzz/translate.cpp @@ -0,0 +1,46 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "type2.h" + +int main(int argc, char **argv) +{ + const char *filename = argc==2 ? argv[1] : nullptr; + + if (!filename) { + std::cout << "Invalid args, no filename\n"; + return 1; + } + + std::ifstream f(filename); + if (!f.is_open()) { + std::cout << "failed to open file:" << filename << "\n"; + return 1; + } + + std::string str((std::istreambuf_iterator(f)), + std::istreambuf_iterator()); + + std::cout << generateCode2(reinterpret_cast(str.data()), str.size()) << std::endl; + + return 0; +} + diff --git a/cppcheck-2.14.0/oss-fuzz/type2.cpp b/cppcheck-2.14.0/oss-fuzz/type2.cpp new file mode 100644 index 00000000..e337864e --- /dev/null +++ b/cppcheck-2.14.0/oss-fuzz/type2.cpp @@ -0,0 +1,231 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "type2.h" + +#include + +static int getValue(const uint8_t *data, size_t dataSize, uint8_t maxValue, bool *done = nullptr) +{ + static size_t pos; // current "data" position + static int dataValue; // value extracted from data + static int ones; // ones. This variable tracks if we need to add more stuff in "dataValue". + + // Shift more bits from "data" into "dataValue" if needed + while (pos < dataSize && ones < 0xFFFF) { + ones = (ones << 8) | 0xff; + dataValue = (dataValue << 8) | data[pos]; + pos++; + } + + if (done) + *done = (ones == 0); + + if (maxValue == 0) + return 0; + + // Shift out info from "dataValue" using % . Using & and >> would work but then we are limited to "power of 2" max value. + const int ret = dataValue % maxValue; + ones /= maxValue; + dataValue /= maxValue; + return ret; +} + +static std::string generateExpression2_lvalue(const uint8_t *data, size_t dataSize) +{ + return "var" + std::to_string(1 + getValue(data, dataSize, 5)); +} + +static std::string generateExpression2_Op(const uint8_t *data, size_t dataSize, uint8_t numberOfGlobalConstants) +{ + std::string code; + switch (getValue(data, dataSize, 3)) { + case 0: + code += generateExpression2_lvalue(data, dataSize); + break; + case 1: + code += "globalconstant"; + code += (1 + getValue(data, dataSize, numberOfGlobalConstants)); + break; + case 2: + code += (getValue(data, dataSize, 0x80) * 0x80 + getValue(data, dataSize, 0x80)); + break; + } + return code; +} + +static std::string generateExpression2_Expr(const uint8_t *data, size_t dataSize, uint8_t numberOfGlobalConstants, int depth=0) +{ + ++depth; + const int type = (depth > 3) ? 0 : getValue(data, dataSize, 3); + const char binop[] = "=<>+-*/%&|^"; + const char *unop[] = {"++","--","()","~"}; + + switch (type) { + case 0: + return generateExpression2_Op(data, dataSize, numberOfGlobalConstants); + case 1: { + const char op = binop[getValue(data,dataSize,sizeof(binop)-1)]; + const std::string lhs = (op == '=') ? + generateExpression2_lvalue(data, dataSize) : + generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth); + const std::string rhs = generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth); + + std::string ret = lhs + op + rhs; + if (depth > 1 && op == '=') + ret = "(" + ret + ")"; + + return ret; + } + case 2: { + const char *u = unop[getValue(data,dataSize,sizeof(unop)/sizeof(*unop))]; + if (strcmp(u, "()") == 0) + return "(" + generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth) + ")"; + else if (strcmp(u, "++") == 0 || strcmp(u, "--") == 0) + return u + generateExpression2_lvalue(data, dataSize); + return u + generateExpression2_Expr(data, dataSize, numberOfGlobalConstants, depth); + } + default: + break; + } + + return "0"; +} + + +static std::string generateExpression2_Cond(const uint8_t *data, size_t dataSize, uint8_t numberOfGlobalConstants) +{ + const char *comp[] = {"==", "!=", "<", "<=", ">", ">="}; + const int i = getValue(data, dataSize, 6); + const std::string lhs = generateExpression2_Expr(data, dataSize, numberOfGlobalConstants); + const std::string rhs = generateExpression2_Expr(data, dataSize, numberOfGlobalConstants); + return lhs + comp[i] + rhs; +} + + +static std::string functionStart() +{ + static int functionNumber; + return "int f" + std::to_string(++functionNumber) + "()\n" + "{\n"; +} + +static std::string generateExpression2_conditionalCode(const std::string &indent, + const uint8_t *data, + size_t dataSize, + uint8_t numberOfGlobalConstants) +{ + std::string code; + + if (indent.empty()) + code += functionStart(); + else { + code += indent; + code += "{\n"; + } + + for (int line = 0; line < 4 || indent.empty(); ++line) { + bool done = false; + const int type1 = getValue(data, dataSize, 8, &done); + if (done) + break; + + const int mostLikelyType = (line >= 2) ? 4 : 0; // should var assignment or return be more likely? + + const int type2 = (indent.size() >= 12) ? + mostLikelyType : // max indentation, no inner conditions + ((type1 >= 5) ? mostLikelyType : type1); + + if (type2 == 0) { + code += indent; + code += " var"; + code += getValue(data, dataSize, 5); + code += "="; + code += generateExpression2_Expr(data, dataSize, numberOfGlobalConstants); + code += ";\n"; + } else if (type2 == 1) { + code += indent; + code += " if ("; + code += generateExpression2_Cond(data, dataSize, numberOfGlobalConstants); + code += ")\n"; + code += generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); + } else if (type2 == 2) { + code += indent; + code += " if ("; + code += generateExpression2_Cond(data, dataSize, numberOfGlobalConstants); + code += ")\n"; + code += generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); + code += indent; + code += " else\n"; + code += generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); + } else if (type2 == 3) { + code += indent; + code += " while ("; + code += generateExpression2_Cond(data, dataSize, numberOfGlobalConstants); + code += ")\n"; + code += generateExpression2_conditionalCode(indent + " ", data, dataSize, numberOfGlobalConstants); + } else if (type2 == 4) { + code += indent; + code += " return "; + code += generateExpression2_Expr(data, dataSize, numberOfGlobalConstants); + code += ";\n"; + if (indent.empty()) { + code += "}\n\n"; + code += functionStart(); + } + else + break; + } + } + + if (!indent.empty()) { + code += indent; + code += "}\n"; + } + else { + code += " return 0;\n}\n"; + } + return code; +} + +std::string generateCode2(const uint8_t *data, size_t dataSize) +{ + std::string code; + + // create global constants + constexpr uint8_t numberOfGlobalConstants = 0; + /* + const int numberOfGlobalConstants = getValue(data, dataSize, 5); + for (int nr = 1; nr <= numberOfGlobalConstants; nr++) { + const char *types[4] = {"char", "int", "long long", "float"}; + code << "const " << types[getValue(data, dataSize, 4)] << " globalconstant" << nr << " = " << generateExpression2_Expr(data, dataSize, nr - 1) << ";\n"; + } + */ + + code += "int var1 = 1;\n" + "int var2 = 0;\n" + "int var3 = 1;\n" + "int var4 = 0;\n" + "int var5 = -1;\n\n"; + + code += generateExpression2_conditionalCode("", data, dataSize, numberOfGlobalConstants); + + return code; +} + + diff --git a/cppcheck-2.14.0/oss-fuzz/type2.h b/cppcheck-2.14.0/oss-fuzz/type2.h new file mode 100644 index 00000000..8a7997b1 --- /dev/null +++ b/cppcheck-2.14.0/oss-fuzz/type2.h @@ -0,0 +1,25 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2022 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include + +std::string generateCode2(const uint8_t *data, size_t dataSize); + diff --git a/cppcheck-2.14.0/philosophy.md b/cppcheck-2.14.0/philosophy.md new file mode 100644 index 00000000..af61a1cd --- /dev/null +++ b/cppcheck-2.14.0/philosophy.md @@ -0,0 +1,64 @@ + +# Cppcheck Philosophy + +It is important that everybody in the Cppcheck team has a consistent idea about how this tool should work. + +This is a static analyzer tool. + +## Usability + +Usability is very important. It's more important that Cppcheck is usable than finding all bugs. + - We don't want to have tons of configurations options. + - It's very important that warning messages are well written and with enough details. + - Speed is very important. --check-level=exhaustive can be used when user accept slow analysis. + +## Normal analysis - No false positives + +A fundamental goal is "no false positives". + +It is not possible to achieve "no false positives" completely. One case where false positives are OK is when the code is garbage. + +If the code is written as it is by design, then our goal is to not show any false positives. + +If it is not known if there is a problem, then in general we need to bailout to avoid false positives. We can only warn when we see that there is a problem. + +Stylistic checks are much more prone to false positives and therefore we should avoid writing stylistic checks mostly. + +Reporting issues in Trac: + - If you see a false negative; report that as an enhancement. + - If you see a false positive; report that as a defect. + +### Inconclusive messages + +If cppcheck can't determine that there is a problem or not, then the analysis is inconclusive. + +If the user enables inconclusive warnings and we guess that the probability there is a real problem is at least 50-50 then it's OK to write a inconclusive warning. + +Inconclusive messages shall not be used for new checks which are just being developed. There `settings.experimental` can be used. + + +## No configuration + +We want that a user can run Cppcheck without explicit -D and -I configuration. + +When this happens the false positives should be avoided. The user can reduce false negatives with configuration. + + +## Allow compiler extensions + +This is not just a tool for mainstream gcc/msvc c/c++ developers. If you can compile the code with a C/C++ compiler then our goal is that Cppcheck can check it. + + +## C++ language + +Our goal is to be highly portable. Users must be able to compile Cppcheck with GCC 4.8 or Visual Studio 2013. + +No C++14 is allowed. A subset of C++11 is allowed. + + +## Avoid dependencies + +We are very careful about dependencies. + + + diff --git a/cppcheck-2.14.0/platforms/aix_ppc64.xml b/cppcheck-2.14.0/platforms/aix_ppc64.xml new file mode 100644 index 00000000..3abf639e --- /dev/null +++ b/cppcheck-2.14.0/platforms/aix_ppc64.xml @@ -0,0 +1,18 @@ + + + 8 + unsigned + + 1 + 2 + 4 + 8 + 8 + 4 + 8 + 8 + 8 + 8 + 2 + + diff --git a/cppcheck-2.14.0/platforms/arm32-wchar_t2.xml b/cppcheck-2.14.0/platforms/arm32-wchar_t2.xml new file mode 100644 index 00000000..218a08cc --- /dev/null +++ b/cppcheck-2.14.0/platforms/arm32-wchar_t2.xml @@ -0,0 +1,18 @@ + + + 8 + unsigned + + 1 + 2 + 4 + 4 + 8 + 4 + 8 + 8 + 4 + 4 + 2 + + diff --git a/cppcheck-2.14.0/platforms/arm32-wchar_t4.xml b/cppcheck-2.14.0/platforms/arm32-wchar_t4.xml new file mode 100644 index 00000000..6a7d2e38 --- /dev/null +++ b/cppcheck-2.14.0/platforms/arm32-wchar_t4.xml @@ -0,0 +1,18 @@ + + + 8 + unsigned + + 1 + 2 + 4 + 4 + 8 + 4 + 8 + 8 + 4 + 4 + 4 + + diff --git a/cppcheck-2.14.0/platforms/arm64-wchar_t2.xml b/cppcheck-2.14.0/platforms/arm64-wchar_t2.xml new file mode 100644 index 00000000..2b0cff49 --- /dev/null +++ b/cppcheck-2.14.0/platforms/arm64-wchar_t2.xml @@ -0,0 +1,18 @@ + + + 8 + unsigned + + 1 + 2 + 4 + 4 + 8 + 4 + 8 + 8 + 8 + 4 + 2 + + diff --git a/cppcheck-2.14.0/platforms/arm64-wchar_t4.xml b/cppcheck-2.14.0/platforms/arm64-wchar_t4.xml new file mode 100644 index 00000000..35183bb0 --- /dev/null +++ b/cppcheck-2.14.0/platforms/arm64-wchar_t4.xml @@ -0,0 +1,18 @@ + + + 8 + unsigned + + 1 + 2 + 4 + 4 + 8 + 4 + 8 + 8 + 8 + 4 + 4 + + diff --git a/cppcheck-2.14.0/platforms/avr8.xml b/cppcheck-2.14.0/platforms/avr8.xml new file mode 100644 index 00000000..a5cd45e3 --- /dev/null +++ b/cppcheck-2.14.0/platforms/avr8.xml @@ -0,0 +1,18 @@ + + + 8 + unsigned + + 1 + 2 + 2 + 4 + 8 + 4 + 4 + 4 + 2 + 2 + 2 + + diff --git a/cppcheck-2.14.0/platforms/cppcheck-platforms.rng b/cppcheck-2.14.0/platforms/cppcheck-platforms.rng new file mode 100644 index 00000000..a0c99247 --- /dev/null +++ b/cppcheck-2.14.0/platforms/cppcheck-platforms.rng @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cppcheck-2.14.0/platforms/cray_sv1.xml b/cppcheck-2.14.0/platforms/cray_sv1.xml new file mode 100644 index 00000000..1c26bc21 --- /dev/null +++ b/cppcheck-2.14.0/platforms/cray_sv1.xml @@ -0,0 +1,18 @@ + + + 8 + unsigned + + 1 + 2 + 8 + 8 + 8 + 8 + 8 + 16 + 8 + 8 + 8 + + diff --git a/cppcheck-2.14.0/platforms/elbrus-e1cp.xml b/cppcheck-2.14.0/platforms/elbrus-e1cp.xml new file mode 100644 index 00000000..818c2841 --- /dev/null +++ b/cppcheck-2.14.0/platforms/elbrus-e1cp.xml @@ -0,0 +1,18 @@ + + + 8 + unsigned + + 1 + 2 + 4 + 8 + 8 + 4 + 8 + 16 + 8 + 8 + 4 + + diff --git a/cppcheck-2.14.0/platforms/mips32.xml b/cppcheck-2.14.0/platforms/mips32.xml new file mode 100644 index 00000000..c8fb46d4 --- /dev/null +++ b/cppcheck-2.14.0/platforms/mips32.xml @@ -0,0 +1,18 @@ + + + 8 + signed + + 1 + 2 + 4 + 4 + 8 + 4 + 8 + 8 + 4 + 4 + 4 + + diff --git a/cppcheck-2.14.0/platforms/msp430_eabi_large_datamodel.xml b/cppcheck-2.14.0/platforms/msp430_eabi_large_datamodel.xml new file mode 100644 index 00000000..18465323 --- /dev/null +++ b/cppcheck-2.14.0/platforms/msp430_eabi_large_datamodel.xml @@ -0,0 +1,18 @@ + + + 8 + signed + + 1 + 2 + 2 + 4 + 8 + 4 + 8 + 8 + 4 + 4 + 2 + + diff --git a/cppcheck-2.14.0/platforms/pic16.xml b/cppcheck-2.14.0/platforms/pic16.xml new file mode 100644 index 00000000..44fa44ca --- /dev/null +++ b/cppcheck-2.14.0/platforms/pic16.xml @@ -0,0 +1,18 @@ + + + 8 + unsigned + + 1 + 2 + 2 + 4 + 8 + 4 + 4 + 8 + 2 + 2 + 2 + + diff --git a/cppcheck-2.14.0/platforms/pic8-enhanced.xml b/cppcheck-2.14.0/platforms/pic8-enhanced.xml new file mode 100644 index 00000000..a5cd45e3 --- /dev/null +++ b/cppcheck-2.14.0/platforms/pic8-enhanced.xml @@ -0,0 +1,18 @@ + + + 8 + unsigned + + 1 + 2 + 2 + 4 + 8 + 4 + 4 + 4 + 2 + 2 + 2 + + diff --git a/cppcheck-2.14.0/platforms/pic8.xml b/cppcheck-2.14.0/platforms/pic8.xml new file mode 100644 index 00000000..b855cbef --- /dev/null +++ b/cppcheck-2.14.0/platforms/pic8.xml @@ -0,0 +1,18 @@ + + + 8 + unsigned + + 1 + 2 + 2 + 4 + 4 + 4 + 4 + 4 + 2 + 2 + 2 + + diff --git a/cppcheck-2.14.0/platforms/unix32-unsigned.xml b/cppcheck-2.14.0/platforms/unix32-unsigned.xml new file mode 100644 index 00000000..b2a56502 --- /dev/null +++ b/cppcheck-2.14.0/platforms/unix32-unsigned.xml @@ -0,0 +1,18 @@ + + + 8 + unsigned + + 1 + 2 + 4 + 4 + 8 + 4 + 8 + 12 + 4 + 4 + 2 + + diff --git a/cppcheck-2.14.0/platforms/unix64-unsigned.xml b/cppcheck-2.14.0/platforms/unix64-unsigned.xml new file mode 100644 index 00000000..818c2841 --- /dev/null +++ b/cppcheck-2.14.0/platforms/unix64-unsigned.xml @@ -0,0 +1,18 @@ + + + 8 + unsigned + + 1 + 2 + 4 + 8 + 8 + 4 + 8 + 16 + 8 + 8 + 4 + + diff --git a/cppcheck-2.14.0/pylintrc_travis b/cppcheck-2.14.0/pylintrc_travis new file mode 100644 index 00000000..54741737 --- /dev/null +++ b/cppcheck-2.14.0/pylintrc_travis @@ -0,0 +1,23 @@ +[MESSAGES CONTROL] +disable=C,R,W +enable=mixed-indentation, + trailing-whitespace, + print-statement, + literal-comparison, + unnecessary-semicolon, + mixed-line-endings, + bad-open-mode, + redundant-unittest-assert, + boolean-datetime, + deprecated-method, + anomalous-unicode-escape-in-string, + anomalous-backslash-in-string, + bad-indentation +[REPORTS] +reports=no +[TYPECHECK] +# See https://stackoverflow.com/questions/10300082/how-to-prevent-python-pylint-complaining-about-socket-class-sendall-method +ignored-classes=SQLObject,_socketobject +[MASTER] +init-hook='import sys; sys.path.append("./addons")' +suggestion-mode=yes diff --git a/cppcheck-2.14.0/readme.md b/cppcheck-2.14.0/readme.md new file mode 100644 index 00000000..45c616ed --- /dev/null +++ b/cppcheck-2.14.0/readme.md @@ -0,0 +1,275 @@ +# **Cppcheck** + +OSS-Fuzz|Coverity Scan Build Status|License| +|:--:|:--:|:--:| +[![OSS-Fuzz](https://oss-fuzz-build-logs.storage.googleapis.com/badges/cppcheck.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:cppcheck)|[![Coverity Scan Build Status](https://img.shields.io/coverity/scan/512.svg)](https://scan.coverity.com/projects/512)|[![License](https://img.shields.io/badge/license-GPL3.0-blue.svg)](https://opensource.org/licenses/GPL-3.0) + +## About the name + +The original name of this program was "C++check", but it was later changed to "Cppcheck". + +Despite the name, Cppcheck is designed for both C and C++. + +## Manual + +A manual is available [online](https://cppcheck.sourceforge.io/manual.pdf). + +## Donate CPU + +Cppcheck is a hobby project with limited resources. You can help us by donating CPU (1 core or as many as you like). It is simple: + + 1. Download (and extract) Cppcheck source code. + 2. Run script: `python cppcheck/tools/donate-cpu.py`. + +The script will analyse debian source code and upload the results to a cppcheck server. We need these results both to improve Cppcheck and to detect regressions. + +You can stop the script whenever you like with Ctrl C. + +## Compiling + +Cppcheck requires a C++ compiler with (partial) C++11 support. Minimum required versions are GCC 5.1 / Clang 3.5 / Visual Studio 2015. + +To build the GUI application, you need to use the CMake or qmake (deprecated) build system. + +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 (deprecated) +* CMake - cross platform build tool +* Windows: Visual Studio +* Windows: Qt Creator + MinGW +* GNU make +* GCC (g++) +* Clang (clang++) + +### CMake + +The minimum required version is CMake 3.5. + +Example, compiling Cppcheck with cmake: + +```shell +mkdir build +cd build +cmake .. +cmake --build . +``` + +If you want to compile the GUI you can use the flag. +-DBUILD_GUI=ON + +For rules support (requires pcre) use the flag. +-DHAVE_RULES=ON + +For release builds it is recommended that you use: +-DUSE_MATCHCOMPILER=ON + +For building the tests use the flag. +-DBUILD_TESTS=ON + +Using cmake you can generate project files for Visual Studio,XCode,etc. + +#### Building a specific configuration + +For single-configuration generators (like "Unix Makefiles") you can generate and build a specific configuration (e.g. "RelWithDebInfo") using: + +```shell +mkdir build_RelWithDebInfo +cd build_RelWithDebInfo +cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo .. +cmake --build . --config RelWithDebInfo +``` + +For multi-configuration generators (like "Visual Studio 17 2022") the same is achieved using: + +```shell +mkdir build +cd build +cmake .. +cmake --build . --config RelWithDebInfo +``` + +### qmake + +NOTE: This has been deprecated and will be removed in a future version. Please use CMake instead. + +You can use the gui/gui.pro file to build the GUI. + +```shell +cd gui +qmake +make +``` + +### Visual Studio + +Use the cppcheck.sln file. The file is configured for Visual Studio 2019, but the platform toolset can be changed easily to older or newer versions. The solution contains platform targets for both x86 and x64. + +To compile with rules, select "Release-PCRE" or "Debug-PCRE" configuration. pcre.lib (pcre64.lib for x64 builds) and pcre.h are expected to be in /externals then. A current version of PCRE for Visual Studio can be obtained using [vcpkg](https://github.com/microsoft/vcpkg). + +### Visual Studio (from command line) + +If you do not wish to use the Visual Studio IDE, you can compile cppcheck from the command line the following command. + +```shell +msbuild cppcheck.sln +``` + +### VS Code (on Windows) + +Install MSYS2 to get GNU toolchain with g++ and gdb (https://www.msys2.org/). +Create a settings.json file in the .vscode folder with the following content (adjust path as necessary): + +``` +{ + "terminal.integrated.shell.windows": "C:\\msys64\\usr\\bin\\bash.exe", + "terminal.integrated.shellArgs.windows": [ + "--login", + ], + "terminal.integrated.env.windows": { + "CHERE_INVOKING": "1", + "MSYSTEM": "MINGW64", + } +} +``` + +Run "make" in the terminal to build cppcheck. + +For debugging create a launch.json file in the .vscode folder with the following content, which covers configuration for debugging cppcheck and misra.py: + +``` +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "cppcheck", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/cppcheck.exe", + "args": [ + "--dump", + "${workspaceFolder}/addons/test/misra/misra-test.c" + ], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [], + "externalConsole": true, + "MIMode": "gdb", + "miDebuggerPath": "C:/msys64/mingw64/bin/gdb.exe", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + }, + { + "name": "misra.py", + "type": "python", + "request": "launch", + "program": "${workspaceFolder}/addons/misra.py", + "console": "integratedTerminal", + "args": [ + "${workspaceFolder}/addons/test/misra/misra-test.c.dump" + ] + } + ] +} +``` + +### Qt Creator + MinGW + +The PCRE dll is needed to build the CLI. It can be downloaded here: +http://software-download.name/pcre-library-windows/ + +### GNU make + +Simple, unoptimized build (no dependencies): + +```shell +make +``` + +The recommended release build is: + +```shell +make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck HAVE_RULES=yes CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function" +``` + +Flags: + +1. `MATCHCOMPILER=yes` + Python is used to optimise cppcheck. The Token::Match patterns are converted into C++ code at compile time. + +2. `FILESDIR=/usr/share/cppcheck` + Specify folder where cppcheck files are installed (addons, cfg, platform) + +3. `HAVE_RULES=yes` + Enable rules (PCRE is required if this is used) + +4. `CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function"` + Enables most compiler optimizations, disables cppcheck-internal debugging code and enables basic compiler warnings. + +### g++ (for experts) + +If you just want to build Cppcheck without dependencies then you can use this command: + +```shell +g++ -o cppcheck -std=c++11 -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml2 -Iexternals/picojson -Ilib cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml2/*.cpp +``` + +If you want to use `--rule` and `--rule-file` then dependencies are needed: + +```shell +g++ -o cppcheck -std=c++11 -lpcre -DHAVE_RULES -Ilib -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml2 cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml2/*.cpp +``` + +### MinGW + +```shell +mingw32-make +``` + +If you encounter the following error with `MATCHCOMPILER=yes` you need to specify your Python interpreter via `PYTHON_INTERPRETER`. + +``` +process_begin: CreateProcess(NULL, which python3, ...) failed. +makefile:24: pipe: No error +process_begin: CreateProcess(NULL, which python, ...) failed. +makefile:27: pipe: No error +makefile:30: *** Did not find a Python interpreter. Stop. +``` + +### Other Compiler/IDE + +1. Create an empty project file / makefile. +2. Add all cpp files in the cppcheck cli and lib folders to the project file / makefile. +3. Add all cpp files in the externals folders to the project file / makefile. +4. Compile. + +### Cross compiling Win32 (CLI) version of Cppcheck in Linux + +```shell +sudo apt-get install mingw32 +make CXX=i586-mingw32msvc-g++ LDFLAGS="-lshlwapi" RDYNAMIC="" +mv cppcheck cppcheck.exe +``` + +## Packages + +Besides building yourself on the platform of your choice there are also several ways to obtain pre-built packages.
+*Note:* The non-Windows packages are not maintained by the Cppcheck team but by the respective packagers instead. + +- (Windows) An official Windows installer is available via the official Cppcheck SourceForge page: https://cppcheck.sourceforge.io/. +- (Linux/Unix) Many major distros offer Cppcheck packages via their integrated package managers (`yum`, `apt`, `pacman`, etc.). See https://pkgs.org/search/?q=cppcheck for an overview. +- (Linux/Unix) Unless you are using a "rolling" distro, it is likely that they are not carrying the latest version. There are several external (mainly unsupported) repositories like AUR (ArchLinux), PPA (ubuntu), EPEL (CentOS/Fedora) etc. which provide up-to-date packages. +- (Linux/Unix) The Canonical Snapcraft package is unmaintained and contains a very old version. Please refrain from using it! See https://trac.cppcheck.net/ticket/11641 for more details. +- (MacOS) A package is available via Homebrew (`brew`). See https://formulae.brew.sh/formula/cppcheck#default. + +## Webpage + +https://cppcheck.sourceforge.io/ diff --git a/cppcheck-2.14.0/readme.txt b/cppcheck-2.14.0/readme.txt new file mode 100644 index 00000000..06b24b7f --- /dev/null +++ b/cppcheck-2.14.0/readme.txt @@ -0,0 +1,123 @@ +========= +Cppcheck +========= + + +About + + The original name of this program is "C++check" but it was later changed to "cppcheck". + +Manual + + A manual is available online: + https://cppcheck.sourceforge.io/manual.pdf + +Compiling + + Any C++11 compiler should work. For compilers with partial C++11 support it may work. If + your compiler has the C++11 features that are available in Visual Studio 2013 / GCC 4.8 + then it will work. + + To build the GUI, you need Qt. + + While building the command line tool, PCRE is optional. It is used if you build with rules. + + There are multiple compilation choices: + * qmake - cross platform build tool + * cmake - cross platform build tool + * Windows: Visual Studio + * Windows: Qt Creator + mingw + * gnu make + * g++ 4.8 (or later) + * clang++ + + cmake + ===== + Example, compiling Cppcheck with cmake: + mkdir build + cd build + cmake .. + cmake --build . + + If you want to compile the GUI you can use the flag + -DBUILD_GUI=ON + + For rules support (requires pcre) use the flag + -DHAVE_RULES=ON + + For release builds it is recommended that you use: + -DUSE_MATCHCOMPILER=ON + + qmake + ===== + You can use the gui/gui.pro file to build the GUI. + cd gui + qmake + make + + Visual Studio + ============= + Use the cppcheck.sln file. The file is configured for Visual Studio 2019, but the platform + toolset can be changed easily to older or newer versions. The solution contains platform + targets for both x86 and x64. + + To compile with rules, select "Release-PCRE" or "Debug-PCRE" configuration. + pcre.lib (pcre64.lib for x64 builds) and pcre.h are expected to be in /externals then. + A current version of PCRE for Visual Studio can be obtained using vcpkg: + https://github.com/microsoft/vcpkg + + Qt Creator + mingw + ================== + The PCRE dll is needed to build the CLI. It can be downloaded here: + http://software-download.name/pcre-library-windows/ + + gnu make + ======== + Simple build (no dependencies): + make + + The recommended release build is: + make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck HAVE_RULES=yes + + Flags: + MATCHCOMPILER=yes : Python is used to optimise cppcheck at compile time + FILESDIR=/usr/share/cppcheck : Specify folder where cppcheck files are installed + HAVE_RULES=yes : Enable rules (pcre is required if this is used) + + g++ (for experts) + ================= + If you just want to build Cppcheck without dependencies then you can use this command: + g++ -o cppcheck -std=c++11 -Iexternals -Iexternals/picojson -Iexternals/simplecpp -Iexternals/tinyxml2 -Ilib cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml2/*.cpp + + If you want to use --rule and --rule-file then dependencies are needed: + g++ -o cppcheck -std=c++11 -lpcre -DHAVE_RULES -Ilib -Iexternals -Iexternals/picojson -Iexternals/simplecpp -Iexternals/tinyxml2 cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml2/*.cpp + + mingw + ===== + The "LDFLAGS=-lshlwapi" is needed when building with mingw + mingw32-make LDFLAGS=-lshlwapi + + other compilers/ide + =================== + + 1. Create a empty project file / makefile. + 2. Add all cpp files in the cppcheck cli and lib folders to the project file / makefile. + 3. Add all cpp files in the externals folders to the project file / makefile. + 4. Compile. + + Cross compiling Win32 (CLI) version of Cppcheck in Linux + + sudo apt-get install mingw32 + make CXX=i586-mingw32msvc-g++ LDFLAGS="-lshlwapi" + mv cppcheck cppcheck.exe + +Packages + + You can install Cppcheck with yum/apt/brew/etc. + + The official rpms are built with these files: + https://src.fedoraproject.org/rpms/cppcheck/tree/master + +Webpage + + https://cppcheck.sourceforge.io/ diff --git a/cppcheck-2.14.0/readmeja.md b/cppcheck-2.14.0/readmeja.md new file mode 100644 index 00000000..27770202 --- /dev/null +++ b/cppcheck-2.14.0/readmeja.md @@ -0,0 +1,140 @@ +# Cppcheck + +| Linux ビルド状態 | Windows ビルド状態 | Coverity Scan Build 状態 | +|:--:|:--:|:--:| +| [![Linux ビルド状態](https://img.shields.io/travis/danmar/cppcheck/master.svg?label=Linux%20build)](https://travis-ci.org/danmar/cppcheck) | [![Windows ビルド状態](https://img.shields.io/appveyor/ci/danmar/cppcheck/master.svg?label=Windows%20build)](https://ci.appveyor.com/project/danmar/cppcheck/branch/master) | [![Coverity Scan Build 状態](https://img.shields.io/coverity/scan/512.svg)](https://scan.coverity.com/projects/512) | + +## 名前について + +このプログラムは元々、"C++check"という名前でしたが後に"Cppcheck"に変更されました。 + +このような名前ですが、Cppcheckは CとC++の両方に対して設計されています。 + +## マニュアル + +マニュアルは[オンライン上に](https://cppcheck.sourceforge.io/manual.pdf)あります。 + +## ビルド + +C++11に対応したコンパイラが利用できます。部分的にC++11にサポートしたコンパイラも利用できるかもしれません。もし、あなたのコンパイラがVisual Studio 2013や GCC 4.8で利用できるC++11機能がサポートされているなら、そのコンパイラが利用できます。 + +GUIも利用する場合、Qtライブラリが必要です。 + +コマンドラインツールをビルドする場合、[PCRE](http://www.pcre.org/)はオプションです。これはルールを作成するために利用します。 + +コンパイル上の選択肢がいくつかあります。 +* qmake - クロスプラットフォームのビルドツール +* cmake - クロスプラットフォームのビルドツール +* Windows: Visual Studio (VS 2013 またはそれ以上) +* Windows: Qt Creator + mingw +* gnu make +* g++ 4.8 (またはそれ以上) +* clang++ + +### cmake + +cmakeでCppcheckをコンパイルする例 + +```shell +mkdir build +cd build +cmake .. +cmake --build . +``` + +C++標準を指定する必要がある場合次のオプションを指定します。 +-DCMAKE_CXX_STANDARD=11 + +CppcheckのGUIが必要な場合次のフラグを指定します。 +-DBUILD_GUI=ON + +pcreが必要になりますが、正規表現のルールサポートが必要な場合次のフラグを指定します。 +-DHAVE_RULES=ON + +### qmake + +GUIをビルドするには、gui/gui.proファイルが利用できます。 + +```shell +cd gui +qmake +make +``` + +### Visual Studio + +cppcheck.slnファイルが利用できます。このファイルは、Visual Studio 2019向けです。しかし、このプラットフォームツールセットはこれより新しいバージョンまたは古いバージョン向けに変更できます。このソルーションには、プラットフォームターゲットとしてx86とx64があります。 + +ルールをコンパイルするためには、"Release-PCRE" または "Debug-PCRE" 設定を選択してください。pcre.lib (または pcre64.lib x64ビルド向け) と pcre.h を /externals にコピーしてください。Visual Studio のための PCRE の最新バージョンは [vcpkg](https://github.com/microsoft/vcpkg) から取得できます。 + +### Qt Creator + MinGW + +コマンドラインツールをビルドするには、PCRE.dllが必要です。これは以下のURLからダウンロードできます。: +http://software-download.name/pcre-library-windows/ + +### GNU make + +単純で最適化しないビルド(依存関係なし): + +```shell +make +``` + +推奨するリリースビルド方法: + +```shell +make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck HAVE_RULES=yes CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function" +``` + +フラグ: + +1. `MATCHCOMPILER=yes` +cppcheckの最適化にPythonを使用します。Token::Match パターンはコンパイル時にlC++コードに変換されます。 + +2. `FILESDIR=/usr/share/cppcheck` +cppcheckの設定ファイル(addon や cfg や platform)を置くディレクトリを指定します。 + +3. `HAVE_RULES=yes` +ルール機能の有効化 (ルール機能には PCRE が必要です)設定です。 + +4. `CXXFLAGS="-O2 -DNDEBUG -Wall -Wno-sign-compare -Wno-unused-function"` +ほとんどのコンパイラの最適化オプション、cppcheckの内部デバッグコードの無効化、基本的なコンパイラ警告の有効化 + +### g++ (エキスパート向け) + +依存関係なく Cppcheckをビルドしたい場合、次のコマンドを利用できます。 + +```shell +g++ -o cppcheck -std=c++11 -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml2 -Ilib cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml2/*.cpp +``` + +`--rule` や `--rule-file` を利用する場合、依存ライブラリが必要です。 + +```shell +g++ -o cppcheck -std=c++11 -lpcre -DHAVE_RULES -Iexternals -Iexternals/simplecpp -Iexternals/tinyxml2 -Ilib cli/*.cpp lib/*.cpp externals/simplecpp/simplecpp.cpp externals/tinyxml2/*.cpp +``` + +### MinGW + +```shell +mingw32-make +``` + +### その他のコンパイラ/IDE + +1. 空のプロジェクトファイル /makefileの作成 +2. cppcheck cli それに lib ディレクトリに含まれる全てのcppファイルをそのプロジェクトファイルまたはmakefileに加えます。 +3. externalsフォルダの全てのcppファイルをプロジェクトファイル / makefileに追加します。 +4. ビルド + +### Linux で Win32 コマンドラインバージョンをクロスコンパイル + +```shell +sudo apt-get install mingw32 +make CXX=i586-mingw32msvc-g++ LDFLAGS="-lshlwapi" RDYNAMIC="" +mv cppcheck cppcheck.exe +``` + +## Webページ + +https://cppcheck.sourceforge.io/ diff --git a/cppcheck-2.14.0/releasenotes.txt b/cppcheck-2.14.0/releasenotes.txt new file mode 100644 index 00000000..c76811fe --- /dev/null +++ b/cppcheck-2.14.0/releasenotes.txt @@ -0,0 +1,34 @@ +Release Notes for Cppcheck 2.14 + +Summary: +- Fixed 23 "crash" tickets +- Fixed 69 "false positive" tickets +- Fixed 36 "improve check" tickets + +New checks: +- eraseIteratorOutOfBounds: warns when erase() is called on an iterator that is out of bounds +- returnByReference: warns when a large class member is returned by value from a getter function + +GUI: +-Make it possible to suppress warnings in all files in a folder + +Changed interface: +- Fixed crash with '--rule-file=' if some data was missing. +- '--rule-file' will now bail out if a rule could not be added or a file contains unexpected data. +- Add option '--check-version', you can use it to pin the cppcheck version in a script. +- Added '--template=simple'. It is expands to '{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]' without any additional location details. +- Removed deprecated platform type 'Unspecified'. Please use 'unspecified' instead. +- Add --file-filter=- option that reads file filters from stdin. Added for a plugin. + +Other: +- Added CMake option 'EXTERNALS_AS_SYSTEM' to treat external includes as 'SYSTEM' ones. +- The minimum required compiler versions have been bumped to GCC 5.1 / Clang 3.5 / Visual Studio 2015 +- The minimum required CMake version has been bumped to 3.5 +- Using Visual Studio with CMake now checks if the CMake version is at least 3.13. This was always required but was not checked explicitly. +- Removed deprecated 'Makefile' option 'SRCDIR'. +- Added CMake option 'DISALLOW_THREAD_EXECUTOR' to control the inclusion of the executor which performs the analysis within a thread of the main process. +- Removed CMake option 'USE_THREADS' in favor of 'DISALLOW_THREAD_EXECUTOR'. +- misra-config will not be treated as a critical error anymore + +Safety critical: +- #12440 : Misra violations found but cppcheck exited with 0 even after specifying exit code diff --git a/cppcheck-2.14.0/requirements.txt b/cppcheck-2.14.0/requirements.txt new file mode 100644 index 00000000..2ecb4308 --- /dev/null +++ b/cppcheck-2.14.0/requirements.txt @@ -0,0 +1 @@ +pcre,pfultz2/pcre@8.45 -H sha256:d6f7182602a775a7d500a0cedca6449af0400c6493951513046d17615ed0bf11 diff --git a/cppcheck-2.14.0/rules/empty-catch-block.xml b/cppcheck-2.14.0/rules/empty-catch-block.xml new file mode 100644 index 00000000..a0f31c59 --- /dev/null +++ b/cppcheck-2.14.0/rules/empty-catch-block.xml @@ -0,0 +1,9 @@ + + + normal + + + style + Empty catch block found. + + diff --git a/cppcheck-2.14.0/rules/error-reporting.xml b/cppcheck-2.14.0/rules/error-reporting.xml new file mode 100644 index 00000000..2813c9bb --- /dev/null +++ b/cppcheck-2.14.0/rules/error-reporting.xml @@ -0,0 +1,10 @@ + + + Severity :: fromString \( "\w+" \) + + ConstantSeverityFromString + style + Constant severity lookups should be done via +Severity::constant. + + diff --git a/cppcheck-2.14.0/rules/show-all-defines.rule b/cppcheck-2.14.0/rules/show-all-defines.rule new file mode 100644 index 00000000..5607d9dd --- /dev/null +++ b/cppcheck-2.14.0/rules/show-all-defines.rule @@ -0,0 +1,11 @@ + + + define + .* + + showalldefines + information +
+ + + diff --git a/cppcheck-2.14.0/rules/stl.xml b/cppcheck-2.14.0/rules/stl.xml new file mode 100644 index 00000000..e98d5583 --- /dev/null +++ b/cppcheck-2.14.0/rules/stl.xml @@ -0,0 +1,11 @@ + + + + \. find \( "[^"]+?" \) == \d+ + + UselessSTDStringFind + performance + When looking for a string at a fixed position compare +is faster. + + diff --git a/cppcheck-2.14.0/rules/strlen-empty-str.xml b/cppcheck-2.14.0/rules/strlen-empty-str.xml new file mode 100644 index 00000000..eb45ccfd --- /dev/null +++ b/cppcheck-2.14.0/rules/strlen-empty-str.xml @@ -0,0 +1,9 @@ + + + if \( ([!] )*?(strlen) \( \w+? \) ([>] [0] )*?\) { + + StrlenEmptyString + performance + Using strlen() to check if a string is empty is not efficient. + + diff --git a/cppcheck-2.14.0/rules/suggest_nullptr.xml b/cppcheck-2.14.0/rules/suggest_nullptr.xml new file mode 100644 index 00000000..dc7f68a0 --- /dev/null +++ b/cppcheck-2.14.0/rules/suggest_nullptr.xml @@ -0,0 +1,10 @@ + + + raw + + + modernizeUseNullPtr + style + Prefer to use a 'nullptr' instead of initializing a pointer with 0. + + diff --git a/cppcheck-2.14.0/rules/token-matching.xml b/cppcheck-2.14.0/rules/token-matching.xml new file mode 100644 index 00000000..8b7823c0 --- /dev/null +++ b/cppcheck-2.14.0/rules/token-matching.xml @@ -0,0 +1,43 @@ + + + + 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() + + + + + + TokenMatchVariable + error + Simplify 'Token :: Match ( expr , %var% ) && expr->variable()' to 'expr->variable()' + + + diff --git a/cppcheck-2.14.0/rules/unused-deref.xml b/cppcheck-2.14.0/rules/unused-deref.xml new file mode 100644 index 00000000..b0e7dd44 --- /dev/null +++ b/cppcheck-2.14.0/rules/unused-deref.xml @@ -0,0 +1,9 @@ + + + [;{}] [*] \w+? (\+\+|\-\-) ; + + UnusedDeref + style + Redundant * found, "*p++" is the same as "*(p++)". + + diff --git a/cppcheck-2.14.0/runformat b/cppcheck-2.14.0/runformat new file mode 100644 index 00000000..60ad1b2a --- /dev/null +++ b/cppcheck-2.14.0/runformat @@ -0,0 +1,54 @@ +#!/bin/bash +# +# uncrustify-0.72 is used to format cppcheck source code. +# +# 1. Download source code: https://github.com/uncrustify/uncrustify/archive/refs/tags/uncrustify-0.72.0.zip +# It's important that all Cppcheck developers use the exact same version so we don't get a "format battle". +# 2. Building: +# - Linux: mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release .. && make +# - Windows: mkdir build && cd build && cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release .. && nmake +# 3. Ensure that the binary "uncrustify" is found by runformat. Either: +# - you can put uncrustify in your PATH +# - you can create an environment variable UNCRUSTIFY that has the full path of the binary + +UNCRUSTIFY_VERSION="0.72.0" +UNCRUSTIFY="${UNCRUSTIFY-uncrustify}" + +DETECTED_VERSION=$("$UNCRUSTIFY" --version 2>&1 | grep -o -E '[0-9.]+') +if [ "$DETECTED_VERSION" != "${UNCRUSTIFY_VERSION}" ]; then + echo "You should use version: ${UNCRUSTIFY_VERSION}" + echo "Detected version: ${DETECTED_VERSION}" + exit 1 +fi + +# OS variables +[ $(uname -s) = "Darwin" ] && export OSX=1 && export UNIX=1 +[ $(uname -s) = "Linux" ] && export LINUX=1 && export UNIX=1 +uname -s | grep -q "_NT-" && export WINDOWS=1 + +if [ $OSX ] +then + export CPUCOUNT=$(sysctl -n hw.ncpu) +elif [ $LINUX ] +then + export CPUCOUNT=$(nproc) +else + export CPUCOUNT="1" +fi + +function formatCplusplus { + find $1 -iname '*.h' \ + -o -iname '*.c' \ + -o -iname '*.cpp' \ + | xargs -n 1 -P $CPUCOUNT -I{} -t $UNCRUSTIFY -c .uncrustify.cfg --no-backup {} + +} + +formatCplusplus cli/ +formatCplusplus democlient/ +formatCplusplus gui/ +formatCplusplus lib/ +formatCplusplus oss-fuzz/ +formatCplusplus test/ +formatCplusplus tools/ +formatCplusplus samples/ diff --git a/cppcheck-2.14.0/samples/AssignmentAddressToInteger/bad.c b/cppcheck-2.14.0/samples/AssignmentAddressToInteger/bad.c new file mode 100644 index 00000000..dcef286d --- /dev/null +++ b/cppcheck-2.14.0/samples/AssignmentAddressToInteger/bad.c @@ -0,0 +1,12 @@ +int foo(int *p) +{ + int a = p; + return a + 4; +} + +int main() +{ + int i[10]; + foo(i); + return 0; +} diff --git a/cppcheck-2.14.0/samples/AssignmentAddressToInteger/good.c b/cppcheck-2.14.0/samples/AssignmentAddressToInteger/good.c new file mode 100644 index 00000000..e52e11c1 --- /dev/null +++ b/cppcheck-2.14.0/samples/AssignmentAddressToInteger/good.c @@ -0,0 +1,11 @@ +int* foo(int *p) +{ + return p + 4; +} + +int main() +{ + int i[10]; + foo(i); + return 0; +} diff --git a/cppcheck-2.14.0/samples/AssignmentAddressToInteger/out.txt b/cppcheck-2.14.0/samples/AssignmentAddressToInteger/out.txt new file mode 100644 index 00000000..32ff2ca3 --- /dev/null +++ b/cppcheck-2.14.0/samples/AssignmentAddressToInteger/out.txt @@ -0,0 +1,3 @@ +samples\AssignmentAddressToInteger\bad.c:3:11: portability: Assigning a pointer to an integer is not portable. [AssignmentAddressToInteger] + int a = p; + ^ diff --git a/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_1/bad.c b/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_1/bad.c new file mode 100644 index 00000000..ac99ea21 --- /dev/null +++ b/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_1/bad.c @@ -0,0 +1,9 @@ +int a[2]; + +int main() +{ + a[0] = 0; + a[1] = 0; + a[2] = 0; + return a[0]; +} diff --git a/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_1/good.c b/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_1/good.c new file mode 100644 index 00000000..cbce96de --- /dev/null +++ b/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_1/good.c @@ -0,0 +1,9 @@ +int a[3]; + +int main() +{ + a[0] = 0; + a[1] = 0; + a[2] = 0; + return 0; +} diff --git a/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_1/out.txt b/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_1/out.txt new file mode 100644 index 00000000..c1ed0477 --- /dev/null +++ b/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_1/out.txt @@ -0,0 +1,3 @@ +samples\arrayIndexOutOfBounds_1\bad.c:7:6: error: Array 'a[2]' accessed at index 2, which is out of bounds. [arrayIndexOutOfBounds] + a[2] = 0; + ^ diff --git a/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_2/bad.c b/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_2/bad.c new file mode 100644 index 00000000..169a2d1d --- /dev/null +++ b/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_2/bad.c @@ -0,0 +1,8 @@ +int main() +{ + int a[2]; + int i; + for (i = 0; i < 3; i++) + a[i] = 0; + return a[0]; +} diff --git a/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_2/good.c b/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_2/good.c new file mode 100644 index 00000000..62fa9eb5 --- /dev/null +++ b/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_2/good.c @@ -0,0 +1,8 @@ +int main() +{ + int a[3]; + int i; + for (i = 0; i < 3; i++) + a[i] = 0; + return a[0]; +} diff --git a/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_2/out.txt b/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_2/out.txt new file mode 100644 index 00000000..9fcc517d --- /dev/null +++ b/cppcheck-2.14.0/samples/arrayIndexOutOfBounds_2/out.txt @@ -0,0 +1,9 @@ +samples\arrayIndexOutOfBounds_2\bad.c:6:10: error: Array 'a[2]' accessed at index 2, which is out of bounds. [arrayIndexOutOfBounds] + a[i] = 0; + ^ +samples\arrayIndexOutOfBounds_2\bad.c:5:19: note: Assuming that condition 'i<3' is not redundant + for (i = 0; i < 3; i++) + ^ +samples\arrayIndexOutOfBounds_2\bad.c:6:10: note: Array index out of bounds + a[i] = 0; + ^ diff --git a/cppcheck-2.14.0/samples/autoVariables/bad.c b/cppcheck-2.14.0/samples/autoVariables/bad.c new file mode 100644 index 00000000..0b4287d2 --- /dev/null +++ b/cppcheck-2.14.0/samples/autoVariables/bad.c @@ -0,0 +1,12 @@ +void foo(int **a) +{ + int b = 1; + *a = &b; +} + +int main() +{ + int *c; + foo(&c); + return 0; +} diff --git a/cppcheck-2.14.0/samples/autoVariables/good.c b/cppcheck-2.14.0/samples/autoVariables/good.c new file mode 100644 index 00000000..6e74a8e6 --- /dev/null +++ b/cppcheck-2.14.0/samples/autoVariables/good.c @@ -0,0 +1,13 @@ +void foo(int **a) +{ + int b = 1; + **a = b; +} + +int main() +{ + int b; + int *c = &b; + foo(&c); + return 0; +} diff --git a/cppcheck-2.14.0/samples/autoVariables/out.txt b/cppcheck-2.14.0/samples/autoVariables/out.txt new file mode 100644 index 00000000..ba302b8a --- /dev/null +++ b/cppcheck-2.14.0/samples/autoVariables/out.txt @@ -0,0 +1,3 @@ +samples\autoVariables\bad.c:4:5: error: Address of local auto-variable assigned to a function parameter. [autoVariables] + *a = &b; + ^ diff --git a/cppcheck-2.14.0/samples/bufferAccessOutOfBounds/bad.c b/cppcheck-2.14.0/samples/bufferAccessOutOfBounds/bad.c new file mode 100644 index 00000000..e52bdb6a --- /dev/null +++ b/cppcheck-2.14.0/samples/bufferAccessOutOfBounds/bad.c @@ -0,0 +1,7 @@ +#include +int main() +{ + char str[5]; + strcpy(str, "0123456789abcdef"); + return 0; +} diff --git a/cppcheck-2.14.0/samples/bufferAccessOutOfBounds/good.c b/cppcheck-2.14.0/samples/bufferAccessOutOfBounds/good.c new file mode 100644 index 00000000..21bcabab --- /dev/null +++ b/cppcheck-2.14.0/samples/bufferAccessOutOfBounds/good.c @@ -0,0 +1,7 @@ +#include +int main() +{ + char str[10]; + snprintf(str, 10, "%s", "abc"); + return 0; +} diff --git a/cppcheck-2.14.0/samples/bufferAccessOutOfBounds/out.txt b/cppcheck-2.14.0/samples/bufferAccessOutOfBounds/out.txt new file mode 100644 index 00000000..11c9d04a --- /dev/null +++ b/cppcheck-2.14.0/samples/bufferAccessOutOfBounds/out.txt @@ -0,0 +1,3 @@ +samples\bufferAccessOutOfBounds\bad.c:5:12: error: Buffer is accessed out of bounds: str [bufferAccessOutOfBounds] + strcpy(str, "0123456789abcdef"); + ^ diff --git a/cppcheck-2.14.0/samples/invalidContainer/bad.cpp b/cppcheck-2.14.0/samples/invalidContainer/bad.cpp new file mode 100644 index 00000000..a863ceaf --- /dev/null +++ b/cppcheck-2.14.0/samples/invalidContainer/bad.cpp @@ -0,0 +1,14 @@ +#include +int main() +{ + std::vector items; + items.push_back(1); + items.push_back(2); + items.push_back(3); + std::vector::iterator iter; + for (iter = items.begin(); iter != items.end(); ++iter) { + if (*iter == 2) { + items.erase(iter); + } + } +} diff --git a/cppcheck-2.14.0/samples/invalidContainer/good.cpp b/cppcheck-2.14.0/samples/invalidContainer/good.cpp new file mode 100644 index 00000000..0d84af96 --- /dev/null +++ b/cppcheck-2.14.0/samples/invalidContainer/good.cpp @@ -0,0 +1,16 @@ +#include +int main() +{ + std::vector items; + items.push_back(1); + items.push_back(2); + items.push_back(3); + std::vector::iterator iter; + for (iter = items.begin(); iter != items.end();) { + if (*iter == 2) { + iter = items.erase(iter); + } else { + ++iter; + } + } +} diff --git a/cppcheck-2.14.0/samples/invalidContainer/out.txt b/cppcheck-2.14.0/samples/invalidContainer/out.txt new file mode 100644 index 00000000..767cf898 --- /dev/null +++ b/cppcheck-2.14.0/samples/invalidContainer/out.txt @@ -0,0 +1,24 @@ +samples\invalidContainer\bad.cpp:9:32: error: inconclusive: Using iterator to local container 'items' that may be invalid. [invalidContainer] + for (iter = items.begin(); iter != items.end(); ++iter) { + ^ +samples\invalidContainer\bad.cpp:9:28: note: Iterator to container is created here. + for (iter = items.begin(); iter != items.end(); ++iter) { + ^ +samples\invalidContainer\bad.cpp:10:19: note: Assuming condition is true. + if (*iter == 2) { + ^ +samples\invalidContainer\bad.cpp:10:19: note: Assuming condition is true. + if (*iter == 2) { + ^ +samples\invalidContainer\bad.cpp:9:37: note: Assuming condition is true. + for (iter = items.begin(); iter != items.end(); ++iter) { + ^ +samples\invalidContainer\bad.cpp:11:19: note: After calling 'erase', iterators or references to the container's data may be invalid . + items.erase(iter); + ^ +samples\invalidContainer\bad.cpp:4:22: note: Variable created here. + std::vector items; + ^ +samples\invalidContainer\bad.cpp:9:32: note: Using iterator to local container 'items' that may be invalid. + for (iter = items.begin(); iter != items.end(); ++iter) { + ^ diff --git a/cppcheck-2.14.0/samples/memleak/bad.c b/cppcheck-2.14.0/samples/memleak/bad.c new file mode 100644 index 00000000..c61a8309 --- /dev/null +++ b/cppcheck-2.14.0/samples/memleak/bad.c @@ -0,0 +1,9 @@ +#include +int main() +{ + int result; + char *a = malloc(10); + a[0] = 0; + result = a[0]; + return result; +} diff --git a/cppcheck-2.14.0/samples/memleak/good.c b/cppcheck-2.14.0/samples/memleak/good.c new file mode 100644 index 00000000..862f35fc --- /dev/null +++ b/cppcheck-2.14.0/samples/memleak/good.c @@ -0,0 +1,10 @@ +#include +int main() +{ + int result; + char *a = malloc(10); + a[0] = 0; + result = a[0]; + free(a); + return result; +} diff --git a/cppcheck-2.14.0/samples/memleak/out.txt b/cppcheck-2.14.0/samples/memleak/out.txt new file mode 100644 index 00000000..819ee8bf --- /dev/null +++ b/cppcheck-2.14.0/samples/memleak/out.txt @@ -0,0 +1,3 @@ +samples\memleak\bad.c:8:5: error: Memory leak: a [memleak] + return result; + ^ diff --git a/cppcheck-2.14.0/samples/resourceLeak/bad.c b/cppcheck-2.14.0/samples/resourceLeak/bad.c new file mode 100644 index 00000000..4595f10f --- /dev/null +++ b/cppcheck-2.14.0/samples/resourceLeak/bad.c @@ -0,0 +1,9 @@ +#include +int main() +{ + const FILE *a = fopen("good.c", "r"); + if (!a) + return 0; + + return 0; +} diff --git a/cppcheck-2.14.0/samples/resourceLeak/good.c b/cppcheck-2.14.0/samples/resourceLeak/good.c new file mode 100644 index 00000000..32859756 --- /dev/null +++ b/cppcheck-2.14.0/samples/resourceLeak/good.c @@ -0,0 +1,9 @@ +#include +int main() +{ + FILE *a = fopen("good.c", "r"); + if (!a) + return 0; + fclose(a); + return 0; +} diff --git a/cppcheck-2.14.0/samples/resourceLeak/out.txt b/cppcheck-2.14.0/samples/resourceLeak/out.txt new file mode 100644 index 00000000..b7c9ad9b --- /dev/null +++ b/cppcheck-2.14.0/samples/resourceLeak/out.txt @@ -0,0 +1,3 @@ +samples\resourceLeak\bad.c:8:5: error: Resource leak: a [resourceLeak] + return 0; + ^ diff --git a/cppcheck-2.14.0/samples/syntaxError/bad.c b/cppcheck-2.14.0/samples/syntaxError/bad.c new file mode 100644 index 00000000..25b30b69 --- /dev/null +++ b/cppcheck-2.14.0/samples/syntaxError/bad.c @@ -0,0 +1,6 @@ +int main() +{ + return 0; +#ifdef A +} +#endif diff --git a/cppcheck-2.14.0/samples/syntaxError/good.c b/cppcheck-2.14.0/samples/syntaxError/good.c new file mode 100644 index 00000000..ecec51ba --- /dev/null +++ b/cppcheck-2.14.0/samples/syntaxError/good.c @@ -0,0 +1,6 @@ +int main() +{ +#ifndef A +#endif + return 0; +} diff --git a/cppcheck-2.14.0/samples/syntaxError/out.txt b/cppcheck-2.14.0/samples/syntaxError/out.txt new file mode 100644 index 00000000..32539db8 --- /dev/null +++ b/cppcheck-2.14.0/samples/syntaxError/out.txt @@ -0,0 +1,3 @@ +samples\syntaxError\bad.c:2:1: error: Unmatched '{'. Configuration: ''. [syntaxError] +{ +^ diff --git a/cppcheck-2.14.0/snap/gui/cppcheck-gui.desktop b/cppcheck-2.14.0/snap/gui/cppcheck-gui.desktop new file mode 100644 index 00000000..bd540376 --- /dev/null +++ b/cppcheck-2.14.0/snap/gui/cppcheck-gui.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Name=Cppcheck +Comment=A tool for static C/C++ code analysis +Exec=cppcheck-gui +Icon=${SNAP}/meta/gui/cppcheck-gui.png +Terminal=true +StartupNotify=true +Categories=Development;Debugger;Qt; diff --git a/cppcheck-2.14.0/snap/gui/cppcheck-gui.png b/cppcheck-2.14.0/snap/gui/cppcheck-gui.png new file mode 100644 index 0000000000000000000000000000000000000000..25d7aef5f31c1b84351df253ff310a63274d842c GIT binary patch literal 2543 zcmV?=cpjhPFPyw?>MD+H_#NVdpRgMPkbs+_3Sb*9 zK_D?PxujODJ~Cjyc1cQlMrO|ZNR}`EQ#NltBkR{6Q~y10-d8ed(yMBl7AeT5kvuA%IJ9l1_UAx3%+qYkq88hCO?%h{NnKDgi zGbn8!Cs7>%Q$bh^fz+(oTNW<-&eaJL&Zp9{Obk*ceBLB<a@VC5XOyrOzPDe>?nlAPzU6s_b*(ynr^f|JgG+@?c2`|xDcXn80rz`E`T|6KGO{z zKo~r!M&JojB;Z0wgu2H$D7X6c+oBu18=h8>4t&v~9|9_bk0Im7KjA2Zx^)NY2FJsj zdKfZv=`)Q|=ZhD&^W{wxonEu?hm#*T-- zj0Sasaqy;kLu?Ge$#gE-gmA%TyAd{jcG)I`b2i&qOO4*zjSU-)>&8yO+scnb8_gkh z%n)pigV-@cut7cNNq?|l!MA$HAK>q*1a-S@+<3yUZQDh4yYkTg6ew0}u6f9WD@vX^Z%?N>cjHCT_?6_it@H1rb;-4Jt zJay_@ddIi2QwT?(E)OR=PnhtM-tnc(3L$UaGK?X9G_Y8sj*eXgTJc`eoE(< zO$bM9w(D>6XQypK*l)9)^)F=Q%72`Mz`EgUy0O>b|H`7l($`>N^h-w}OrQQ<>Ka0J z3gJztZlV6WT1Ff zgir}Aj~8?l0@4mui@}@P2g$&KtOy|pN5QD^XZInP^Z#yMzQ>fXut=uT)ir%}2^!gj z(5zXa)UQ7@qY%Pfh0vwT5~)z3joU?)2O;g+&2$n1+s)Dqu7;;I1g!q{NNsP;nuEH% zvk3EG$vp%caR?I;?#q&UJxF9^JTt75ZbL{)dRjO50X(hU`tsK84Ih4&Zts;0`mDFo z8HxnF2+{Bth9el1wZvxMHpsBvf_hKrE(C(o3oF-A22Lv;2cX6qx;5~}aLC5+d zBsPJ|eK_-tLPi+hqX#^$j{42h(xtz;4We=5(fR{-!f9=TI%)M5${cqbM>O13YaE9Z zDq+d&otCVA#FCW{S<)wo4nDUL#z$xg&)*5qQL%t=-c<-{7RI`yCmm!vT%!XtMyHi4 zchGIDhr3F_6-br(mMpzj1z=cy|BWjnCf*3+71V*Fio-8ZJ+Zr&FuYFFA95K}5Q+2^ zjl3%PaSCn)mXxh# z9LL>=hhM7%nxi)oF&|4X8N<*Hx#6yA;2%h?e3nev;CldrCNuDhNj>?a=#7A_-Z%#- zRmqYCxBGVTi|;Z!KfH*7{#*%;fj?^@u0!gzQN6cM0gPQ_44cm(8GfrooW}vAARN9_ z6dneHC`-DH^8Up2P`POrPCN*f@(IepFO>&RLDKmHLlF#5t1v!;x@X5M@GXEz>ln}# zwbDL%7b6sYsW#XLiHfyk==2*sc7b9>7~i2goK}78hvY5Hbb5<#kG@VrEC~rsJ(gck z4}Pg|tkd^u>B{O+V|;l3391+Objmsy7a`?ps1D5Q0A}rAI$qKE{9TBKU#dJl*Y|4s zff>)%Ry?5YWfnEuPFaJo3Ib`;Sy4gar|fc3Zp36D?*#ig0~0nFHHNwKoVn&5gwAj_&Wo`67&$@@>xk4*pTHC_d- z1Na*)5HQsOUqbX!SMqZIo&5Y;EvZ=B==|OJ0Bf049|c}M=#lRu9X+|<) + if (NOT BUILD_CORE_DLL) + list(APPEND testrunner_SOURCES $ $) + if(USE_BUNDLED_TINYXML2) + list(APPEND testrunner_SOURCES $) + endif() + endif() + + add_executable(testrunner ${testrunner_SOURCES}) + target_include_directories(testrunner PRIVATE ${PROJECT_SOURCE_DIR}/lib/ ${PROJECT_SOURCE_DIR}/cli/) + if(USE_BUNDLED_TINYXML2) + target_externals_include_directories(testrunner PRIVATE ${PROJECT_SOURCE_DIR}/externals/tinyxml2) + else() + target_include_directories(testrunner SYSTEM PRIVATE ${tinyxml2_INCLUDE_DIRS}) + endif() + target_externals_include_directories(testrunner PRIVATE ${PROJECT_SOURCE_DIR}/externals/simplecpp/) + if (HAVE_RULES) + target_link_libraries(testrunner ${PCRE_LIBRARY}) + endif() + if (WIN32 AND NOT BORLAND) + if(NOT MINGW) + target_link_libraries(testrunner Shlwapi.lib) + else() + target_link_libraries(testrunner shlwapi) + endif() + endif() + if(tinyxml2_FOUND AND NOT USE_BUNDLED_TINYXML2) + target_link_libraries(testrunner ${tinyxml2_LIBRARIES}) + endif() + target_link_libraries(testrunner ${CMAKE_THREAD_LIBS_INIT}) + if (BUILD_CORE_DLL) + target_compile_definitions(testrunner PRIVATE CPPCHECKLIB_IMPORT SIMPLECPP_IMPORT) + target_link_libraries(testrunner cppcheck-core) + endif() + if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # $ is used in dinit() designated initialization helper + target_compile_options_safe(testrunner -Wno-dollar-in-identifier-extension) + endif() + + if (NOT CMAKE_DISABLE_PRECOMPILE_HEADERS) + target_precompile_headers(testrunner PRIVATE precompiled.h) + endif() + + add_dependencies(testrunner copy_cfg) + add_dependencies(testrunner copy_addons) + add_dependencies(testrunner copy_platforms) + if (NOT DISABLE_DMAKE) + add_dependencies(testrunner run-dmake) + endif() + + if (LIBXML2_XMLLINT_EXECUTABLE) + # TODO: run the CMake implementation of the tests + # TODO: get rid of the copy + add_custom_target(checkcfg ${CMAKE_COMMAND} -E copy $ ${CMAKE_SOURCE_DIR} + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/cfg/runtests.sh + DEPENDS cppcheck validateCFG) + endif() + + if (REGISTER_TESTS) + # CMAKE_MATCH_ usage for if (MATCHES) requires CMake 3.9 + + find_package(Threads REQUIRED) + include(ProcessorCount) + ProcessorCount(N) + set(CTEST_PARALLEL_LEVEL ${N} CACHE STRING "CTest parallel level") + set(CTEST_TIMEOUT 90 CACHE STRING "CTest timeout") + add_custom_target(check ${CMAKE_CTEST_COMMAND} --output-on-failure -j ${CTEST_PARALLEL_LEVEL} -C ${CMAKE_CFG_INTDIR} --timeout ${CTEST_TIMEOUT} + DEPENDS testrunner cppcheck) + + set(SKIP_TESTS "" CACHE STRING "A list of tests to skip") + + function(add_fixture NAME) + if (${NAME} IN_LIST SKIP_TESTS) + elseif(TEST ${NAME}) + else() + add_test(NAME ${NAME} COMMAND testrunner ${NAME} WORKING_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) + endif() + endfunction() + + foreach(SRC ${srcs}) + file(STRINGS ${SRC} FIXTURE_LINE REGEX "^REGISTER_TEST\\([a-zA-z0-9]+\\)$") + foreach(_fixture_line ${FIXTURE_LINE}) + if(_fixture_line MATCHES "^REGISTER_TEST\\(([a-zA-z0-9]+)\\)$") + add_fixture(${CMAKE_MATCH_1}) + endif() + endforeach() + endforeach() + + function(add_cfg CFG_TEST) + set(oneValueArgs PLATFORM NAME) + set(multiValueArgs ADD_LIBRARY) + + cmake_parse_arguments(PARSE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + get_filename_component(LIBRARY ${CFG_TEST} NAME_WE) + # TODO: get rid of this + if(PARSE_ADD_LIBRARY) + string(REPLACE ";" "," ADD_LIBRARY "${PARSE_ADD_LIBRARY}") + set(LIBRARY "${ADD_LIBRARY},${LIBRARY}") + endif() + set(PLATFORM unix64) + if(PARSE_PLATFORM) + set(PLATFORM ${PARSE_PLATFORM}) + endif() + if(PARSE_NAME) + set(TEST_NAME ${PARSE_NAME}) + else() + string(MAKE_C_IDENTIFIER ${CFG_TEST} TEST_NAME) + endif() + if ("cfg-${TEST_NAME}" IN_LIST SKIP_TESTS) + else() + # TODO: remove missingInclude disabling when it no longer is implied by --enable=information + # TODO: add syntax check + add_test(NAME cfg-${TEST_NAME} + COMMAND $ + --library=${LIBRARY} + --check-library + --check-level=exhaustive + --platform=${PLATFORM} + --enable=style,information + --inconclusive + --force + --error-exitcode=1 + --disable=missingInclude + --inline-suppr + --debug-warnings + --suppress=checkersReport + ${CMAKE_CURRENT_SOURCE_DIR}/cfg/${CFG_TEST} + ) + endif() + endfunction() + # TODO: glob this + add_cfg(boost.cpp) + add_cfg(bsd.c) + add_cfg(cairo.c) + add_cfg(cppunit.cpp) + # TODO: posix needs to specified first or it has a different mmap() config + # TODO: get rid of posix dependency + add_cfg(gnu.c ADD_LIBRARY posix) + add_cfg(googletest.cpp) + add_cfg(gtk.c) + add_cfg(kde.cpp) + add_cfg(libcurl.c) + add_cfg(libsigc++.cpp) + add_cfg(lua.c) + add_cfg(mfc.cpp) + add_cfg(opencv2.cpp) + add_cfg(openmp.c) + add_cfg(openssl.c) + add_cfg(posix.c) + add_cfg(python.c) + add_cfg(qt.cpp) + add_cfg(sqlite3.c) + add_cfg(std.c) + add_cfg(std.cpp) + add_cfg(windows.cpp NAME windows32A PLATFORM win32A) + add_cfg(windows.cpp NAME windows32W PLATFORM win32W) + add_cfg(windows.cpp NAME windows64 PLATFORM win64) + add_cfg(wxwidgets.cpp) + + function(fixture_cost NAME COST) + if(TEST ${NAME}) + set_tests_properties(${NAME} PROPERTIES COST ${COST}) + endif() + endfunction() + + # Set cost of the more expensive tests to help improve parallel scheduling + # of tests + # + # To collect data to update this list remove "/Testing/Temporary/CTestCostData.txt", + # disable the fixture_cost() statements below and run a Debug build with "ctest -j1" several times. + # Afterwards run it with "ctest -j11" and immediately cancel the run and update the list accordingly to the + # first eleven tests chosen. + # + # NOTE: The TestProcessExecutor* tests are not the slowest but they invoke processes which max out the system + # and negatively impact the run-time of the other tests + if (TRUE) + fixture_cost(TestProcessExecutorFiles 1.10) + fixture_cost(TestProcessExecutorFS 1.09) + fixture_cost(cfg-std_c 1.08) + fixture_cost(TestIO 1.07) + fixture_cost(cfg-std_cpp 1.06) + fixture_cost(TestCondition 1.05) + fixture_cost(TestValueFlow 1.04) + fixture_cost(TestLeakAutoVarRecursiveCountLimit 1.03) + fixture_cost(TestBufferOverrun 1.02) + fixture_cost(TestClass 1.01) + fixture_cost(TestStl 1.00) + endif() + endif() +endif() diff --git a/cppcheck-2.14.0/test/cfg/boost.cpp b/cppcheck-2.14.0/test/cfg/boost.cpp new file mode 100644 index 00000000..d68d5a7f --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/boost.cpp @@ -0,0 +1,107 @@ + +// Test library configuration for boost.cfg +// +// Usage: +// $ cppcheck --check-library --library=boost --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/boost.cpp +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +// cppcheck-suppress-file valueFlowBailout + +#include +#include +#include +#include +#include +#include +#include +#include + +BOOST_FORCEINLINE void boost_forceinline_test() +{} + +BOOST_NOINLINE void boost_noinline_test() +{} + +BOOST_NORETURN void boost_noreturn_test() +{} + +void print_hello() +{ + printf("hello"); +} + +void valid_code(boost::function &pf_print_hello) +{ + if (BOOST_LIKELY(1)) {} + if (BOOST_UNLIKELY(0)) {} + + int int1 = 5; + boost::endian::endian_reverse_inplace(int1); + boost::bind(print_hello)(); + pf_print_hello = boost::bind(print_hello); +} + +void ignoredReturnValue() +{ + // cppcheck-suppress ignoredReturnValue + boost::math::round(1.5); + // cppcheck-suppress ignoredReturnValue + boost::math::iround(1.5); + // cppcheck-suppress ignoredReturnValue + boost::math::lround(1.5); + // cppcheck-suppress ignoredReturnValue + boost::math::llround(1.5); + // cppcheck-suppress ignoredReturnValue + boost::endian::endian_reverse(1); +} + +void uninitvar() +{ + int intUninit1; + int intUninit2; + // cppcheck-suppress uninitvar + boost::endian::endian_reverse_inplace(intUninit1); + // cppcheck-suppress uninitvar + (void)boost::math::round(intUninit2); +} + +void throwexception(int * buf) +{ + if (!buf) + boost::throw_exception(std::bad_alloc()); + *buf = 0; +} + +void throwexception2(int * buf) +{ + if (!buf) + BOOST_THROW_EXCEPTION(std::bad_alloc()); + *buf = 0; +} + +void macros() +{ +#define DECL(z, n, text) text ## n = n; + BOOST_PP_REPEAT(5, DECL, int x) + + BOOST_SCOPED_ENUM_DECLARE_BEGIN(future_errc) { + // cppcheck-suppress valueFlowBailoutIncompleteVar + no_state + } + BOOST_SCOPED_ENUM_DECLARE_END(future_errc) +} + +void containerOutOfBounds_scoped_array(std::size_t n) // #12356 +{ + boost::scoped_array d(new int[n] {}); + for (std::size_t i = 0; i < n; i++) + if (d[i]) {} +} + +void lock_guard_finiteLifetime(boost::mutex& m) +{ + // cppcheck-suppress unusedScopedObject + boost::lock_guard{ m }; +} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cfg/bsd.c b/cppcheck-2.14.0/test/cfg/bsd.c new file mode 100644 index 00000000..6652129c --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/bsd.c @@ -0,0 +1,175 @@ +// Test library configuration for bsd.cfg +// +// Usage: +// $ cppcheck --check-library --library=bsd --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/bsd.c +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +// cppcheck-suppress-file valueFlowBailout + +#include +#include +#include +#include +#include + +void nullPointer_setbuffer(FILE *stream, char *buf, size_t size) +{ + // cppcheck-suppress nullPointer + (void) setbuffer(NULL, buf, size); + (void) setbuffer(stream, NULL, size); + (void) setbuffer(stream, buf, size); +} + +void nullPointer_setlinebuf(FILE *stream) +{ + // cppcheck-suppress nullPointer + (void)setlinebuf(NULL); + (void)setlinebuf(stream); +} + +// #9323, #9331 +void verify_timercmp(struct timeval t) +{ + (void)timercmp(&t, &t, <); + (void)timercmp(&t, &t, <=); + (void)timercmp(&t, &t, ==); + (void)timercmp(&t, &t, !=); + (void)timercmp(&t, &t, >=); + (void)timercmp(&t, &t, >); +} + +ssize_t nullPointer_readv(int fd, const struct iovec *iov, int iovcnt) +{ + // cppcheck-suppress nullPointer + (void)readv(fd,NULL,iovcnt); + return readv(fd,iov,iovcnt); +} + +ssize_t nullPointer_writev(int fd, const struct iovec *iov, int iovcnt) +{ + // cppcheck-suppress nullPointer + (void)writev(fd,NULL,iovcnt); + return writev(fd,iov,iovcnt); +} + +ssize_t nullPointer_preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset) +{ + // cppcheck-suppress nullPointer + (void)preadv(fd,NULL,iovcnt,offset); + return preadv(fd,iov,iovcnt,offset); +} + +ssize_t nullPointer_pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset) +{ + // cppcheck-suppress nullPointer + (void)pwritev(fd,NULL,iovcnt,offset); + return pwritev(fd,iov,iovcnt,offset); +} + +// False negative: #9346 +void uninitvar_timercmp(struct timeval t) +{ + struct timeval uninit; + (void)timercmp(&t, &uninit, <); + (void)timercmp(&uninit, &t, <=); + (void)timercmp(&uninit, &uninit, ==); +} + +void nullPointer_timercmp(struct timeval t) +{ + // cppcheck-suppress constVariablePointer + struct timeval *p=0; + // cppcheck-suppress nullPointer + (void)timercmp(&t, p, <); + // cppcheck-suppress nullPointer + (void)timercmp(p, &t, <=); + // cppcheck-suppress nullPointer + (void)timercmp(p, p, ==); +} + +// size_t strlcat(char *dst, const char *src, size_t size); +void uninitvar_strlcat(char *Ct, const char *S, size_t N) +{ + char *ct1, *ct2; + char *s1, *s2; + size_t n1, n2; + // cppcheck-suppress uninitvar + (void)strlcat(ct1,s1,n1); + // cppcheck-suppress uninitvar + (void)strlcat(ct2,S,N); + // cppcheck-suppress uninitvar + (void)strlcat(Ct,s2,N); + // cppcheck-suppress uninitvar + (void)strlcat(Ct,S,n2); + + // no warning is expected for + (void)strlcat(Ct,S,N); +} + +void bufferAccessOutOfBounds(void) +{ + uint16_t uint16Buf[4]; + // cppcheck-suppress bufferAccessOutOfBounds + arc4random_buf(uint16Buf, 9); + // valid + arc4random_buf(uint16Buf, 8); +} + +void ignoredReturnValue(void) +{ + // cppcheck-suppress ignoredReturnValue + arc4random(); + // cppcheck-suppress ignoredReturnValue + arc4random_uniform(10); +} + +void invalidFunctionArg() +{ + // cppcheck-suppress invalidFunctionArg + (void) arc4random_uniform(1); + // valid + (void) arc4random_uniform(2); +} + +void nullPointer(void) +{ + // cppcheck-suppress nullPointer + arc4random_buf(NULL, 5); +} + +void uninitvar(void) +{ + uint32_t uint32Uninit; + + // cppcheck-suppress uninitvar + (void) arc4random_uniform(uint32Uninit); +} + +void arrayIndexOutOfBounds(void) +{ + char * pAlloc = calloc(2, 3); + pAlloc[5] = 'a'; + // cppcheck-suppress arrayIndexOutOfBounds + pAlloc[6] = 1; + // cppcheck-suppress memleakOnRealloc + pAlloc = reallocarray(pAlloc, 3, 3); + pAlloc[8] = 'a'; + // cppcheck-suppress arrayIndexOutOfBounds + pAlloc[9] = 1; + free(pAlloc); +} + +void reallocarray_memleak(void) { + char *a = (char *)malloc(10); + // cppcheck-suppress [memleakOnRealloc, unreadVariable] + a = reallocarray(a, 100, 2); + // cppcheck-suppress memleak +} + +void reallocarray_notused(void) +{ + // cppcheck-suppress [leakReturnValNotUsed, ignoredReturnValue] + reallocarray(NULL, 10, 10); +} diff --git a/cppcheck-2.14.0/test/cfg/cairo.c b/cppcheck-2.14.0/test/cfg/cairo.c new file mode 100644 index 00000000..c544a454 --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/cairo.c @@ -0,0 +1,26 @@ + +// Test library configuration for cairo.cfg +// +// Usage: +// $ cppcheck --check-library --library=cairo --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/cairo.c +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +#include + +void validCode(cairo_surface_t *target) +{ + cairo_t * cairo1 = cairo_create(target); + cairo_move_to(cairo1, 1.0, 2.0); + cairo_line_to(cairo1, 5.0, 6.0); + cairo_destroy(cairo1); +} + +void ignoredReturnValue(cairo_surface_t *target) +{ + // cppcheck-suppress ignoredReturnValue + cairo_create(target); + // cppcheck-suppress ignoredReturnValue + cairo_status_to_string(CAIRO_STATUS_READ_ERROR); +} diff --git a/cppcheck-2.14.0/test/cfg/cppunit.cpp b/cppcheck-2.14.0/test/cfg/cppunit.cpp new file mode 100644 index 00000000..1eaf0b66 --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/cppunit.cpp @@ -0,0 +1,55 @@ +// Test library configuration for cppunit.cfg +// +// Usage: +// $ cppcheck --check-library --library=cppunit --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/cppunit.cpp +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +// cppcheck-suppress-file valueFlowBailout + +#include + +void cppunit_assert_equal(int x, double y) +{ + CPPUNIT_ASSERT(true); + CPPUNIT_ASSERT(false); + CPPUNIT_ASSERT(1 < 2); + + CPPUNIT_ASSERT_MESSAGE("Test failed", true); + CPPUNIT_ASSERT_MESSAGE("Test failed", false); + CPPUNIT_ASSERT_MESSAGE("Test failed", 1 < 2); + + CPPUNIT_ASSERT_EQUAL(1, 2); + CPPUNIT_ASSERT_EQUAL(true, 3 == x); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("Test failed", 1, 4); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Test failed", true, 4 == x); + + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, 2.0, 1e-7); + CPPUNIT_ASSERT_DOUBLES_EQUAL(1.0, y, 1e-7); + + CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Test failed", 1.0, 2.0, 1e-7); + CPPUNIT_ASSERT_DOUBLES_EQUAL_MESSAGE("Test failed", 1.0, y, 1e-7); +} + +void cppunit_throw() +{ + CPPUNIT_ASSERT_NO_THROW(1 + 1); + CPPUNIT_ASSERT_NO_THROW_MESSAGE("Unexpected throw", 1 + 1); + CPPUNIT_ASSERT_THROW(1 + 1, CPPUNIT_NS::Exception); + CPPUNIT_ASSERT_THROW_MESSAGE("Did not throw", 1 + 1, CPPUNIT_NS::Exception); +} + +void cppunit_assertion_assert() +{ + CPPUNIT_ASSERT_ASSERTION_FAIL(true); + CPPUNIT_ASSERT_ASSERTION_FAIL_MESSAGE("hello", false); + CPPUNIT_ASSERT_ASSERTION_PASS(false); + CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE("goodbye", true); +} + +void cppunit_assert_fail() +{ + CPPUNIT_FAIL("This fails"); +} diff --git a/cppcheck-2.14.0/test/cfg/gnu.c b/cppcheck-2.14.0/test/cfg/gnu.c new file mode 100644 index 00000000..296a3cc0 --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/gnu.c @@ -0,0 +1,475 @@ + +// Test library configuration for gnu.cfg +// +// Usage: +// $ cppcheck --check-library --library=gnu --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/gnu.c +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +// cppcheck-suppress-file valueFlowBailout + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if !defined(__CYGWIN__) && !(defined(__APPLE__) && defined(__MACH__)) +#include +#endif +#include +#ifdef __gnu_linux__ +#include +#endif +#include +#include + +#ifdef __gnu_linux__ +void unreachableCode_error(void) // #11197 +{ + error(1, 0, ""); // will call exit() if the first parameter is non-zero + // cppcheck-suppress unusedVariable + // TODO cppcheck-suppress unreachableCode + int i; +} +#endif + +int nullPointer_gethostbyname2_r(const char* name, int af, struct hostent* ret, const char* buf, size_t buflen, struct hostent** result, const int* h_errnop) +{ + // cppcheck-suppress nullPointer + (void) gethostbyname2_r(NULL, af, ret, buf, buflen, result, h_errnop); + // cppcheck-suppress nullPointer + (void) gethostbyname2_r(name, af, NULL, buf, buflen, result, h_errnop); + // cppcheck-suppress nullPointer + (void) gethostbyname2_r(name, af, ret, NULL, buflen, result, h_errnop); + // cppcheck-suppress nullPointer + (void) gethostbyname2_r(name, af, ret, buf, buflen, NULL, h_errnop); + // cppcheck-suppress nullPointer + (void) gethostbyname2_r(name, af, ret, buf, buflen, result, NULL); + return gethostbyname2_r(name, af, ret, buf, buflen, result, h_errnop); +} + +int nullPointer_gethostbyname_r(const char* name, struct hostent* ret, const char* buf, size_t buflen, struct hostent** result, const int* h_errnop) +{ + // cppcheck-suppress nullPointer + (void) gethostbyname_r(NULL, ret, buf, buflen, result, h_errnop); + // cppcheck-suppress nullPointer + (void) gethostbyname_r(name, NULL, buf, buflen, result, h_errnop); + // cppcheck-suppress nullPointer + (void) gethostbyname_r(name, ret, NULL, buflen, result, h_errnop); + // cppcheck-suppress nullPointer + (void) gethostbyname_r(name, ret, buf, buflen, NULL, h_errnop); + // cppcheck-suppress nullPointer + (void) gethostbyname_r(name, ret, buf, buflen, result, NULL); + return gethostbyname_r(name, ret, buf, buflen, result, h_errnop); +} + + +int nullPointer_gethostbyaddr_r(const void* addr, socklen_t len, int type, struct hostent* ret, const char* buf, size_t buflen, struct hostent** result, const int* h_errnop) +{ + // cppcheck-suppress nullPointer + (void) gethostbyaddr_r(NULL, len, type, ret, buf, buflen, result, h_errnop); + // cppcheck-suppress nullPointer + (void) gethostbyaddr_r(addr, len, type, NULL, buf, buflen, result, h_errnop); + // cppcheck-suppress nullPointer + (void) gethostbyaddr_r(addr, len, type, ret, NULL, buflen, result, h_errnop); + // cppcheck-suppress nullPointer + (void) gethostbyaddr_r(addr, len, type, ret, buf, buflen, NULL, h_errnop); + // cppcheck-suppress nullPointer + (void) gethostbyaddr_r(addr, len, type, ret, buf, buflen, result, NULL); + return gethostbyaddr_r(addr, len, type, ret, buf, buflen, result, h_errnop); +} + +int nullPointer_getopt_long(int argc, char **argv, const char *optstring, + const struct option *longopts, int *longindex) +{ + // cppcheck-suppress nullPointer + (void) getopt_long(argc, argv, NULL, longopts, longindex); + // cppcheck-suppress nullPointer + (void) getopt_long(argc, argv, optstring, NULL, longindex); + // cppcheck-suppress nullPointer + (void) getopt_long(argc, NULL, optstring, longopts, longindex); + return getopt_long(argc, argv, optstring, longopts, longindex); +} + +int nullPointer_getopt_long_only(int argc, char* const* argv, const char* optstring, + const struct option* longopts, int* longindex) +{ + // cppcheck-suppress nullPointer + (void) getopt_long_only(argc, NULL, optstring, longopts, longindex); + // cppcheck-suppress nullPointer + (void) getopt_long_only(argc, argv, NULL, longopts, longindex); + // cppcheck-suppress nullPointer + (void) getopt_long_only(argc, argv, optstring, NULL, longindex); + return getopt_long_only(argc, argv, optstring, longopts, longindex); +} + +int nullPointer_getservent_r(struct servent *restrict result_buf, const char *restrict buf, size_t buflen, struct servent **restrict result) +{ + // cppcheck-suppress nullPointer + (void) getservent_r(NULL, buf, buflen, result); + // cppcheck-suppress nullPointer + (void) getservent_r(result_buf, NULL, buflen, result); + // cppcheck-suppress nullPointer + (void) getservent_r(result_buf, buf, buflen, NULL); + return getservent_r(result_buf, buf, buflen, result); +} + +void *bufferAccessOutOfBounds_memrchr(const void *s, int c, size_t n) +{ + const char buf[42]={0}; + (void)memrchr(buf,c,42); + // cppcheck-suppress bufferAccessOutOfBounds + (void)memrchr(buf,c,43); + return memrchr(s,c,n); +} + +void knownConditionTrueFalse_ffsl(long i) +{ + // ffs() returns the position of the first bit set, or 0 if no bits are set in i. + const int x = ffsl(0); + // cppcheck-suppress knownConditionTrueFalse + if (x == 0) {} + if (ffsl(i) == 0) {} +} + +void knownConditionTrueFalse_ffsll(long long i) +{ + // ffs() returns the position of the first bit set, or 0 if no bits are set in i. + const int x = ffsll(0); + // cppcheck-suppress knownConditionTrueFalse + if (x == 0) {} + if (ffsll(i) == 0) {} +} + +int nullPointer_semtimedop(int semid, struct sembuf *sops, size_t nsops, const struct timespec *timeout) +{ + (void) semtimedop(semid, sops, nsops, NULL); // If the timeout argument is NULL, then semtimedop() behaves exactly like semop(). + (void) semtimedop(semid, sops, nsops, timeout); + // cppcheck-suppress nullPointer + return semtimedop(semid, NULL, nsops, timeout); +} + +void *nullPointer_mempcpy(void *dest, const void *src, size_t n) +{ + // cppcheck-suppress nullPointer + (void) mempcpy(NULL,src,n); + // cppcheck-suppress nullPointer + (void) mempcpy(dest,NULL,n); + return mempcpy(dest,src,n); +} + +wchar_t *nullPointer_wmempcpy(wchar_t *dest, const wchar_t *src, size_t n) +{ + // cppcheck-suppress nullPointer + (void) wmempcpy(NULL,src,n); + // cppcheck-suppress nullPointer + (void) wmempcpy(dest,NULL,n); + return wmempcpy(dest,src,n); +} + +int uninitvar_getpw(uid_t uid, char *buf) +{ + uid_t someUid; + // cppcheck-suppress getpwCalled + (void)getpw(uid, buf); + // cppcheck-suppress getpwCalled + // cppcheck-suppress uninitvar + return getpw(someUid, buf); +} + +// Declaration necessary because there is no specific / portable header. +extern void *xcalloc(size_t nmemb, size_t size); +extern void *xmalloc(size_t size); +extern void *xrealloc(void *block, size_t newsize); +extern void xfree(void *ptr); + +void resourceLeak_mkostemps(char *template, int suffixlen, int flags) +{ + // cppcheck-suppress unreadVariable + int fp = mkostemps(template, suffixlen, flags); + // cppcheck-suppress resourceLeak +} + +void no_resourceLeak_mkostemps_01(char *template, int suffixlen, int flags) +{ + int fp = mkostemps(template, suffixlen, flags); + close(fp); +} + +int no_resourceLeak_mkostemps_02(char *template, int suffixlen, int flags) +{ + return mkostemps(template, suffixlen, flags); +} + +void resourceLeak_mkstemps(char *template, int suffixlen) +{ + // cppcheck-suppress unreadVariable + int fp = mkstemps(template, suffixlen); + // cppcheck-suppress resourceLeak +} + +void no_resourceLeak_mkstemps_01(char *template, int suffixlen) +{ + int fp = mkstemps(template, suffixlen); + close(fp); +} + +int no_resourceLeak_mkstemps_02(char *template, int suffixlen) +{ + return mkstemps(template, suffixlen); +} + +void resourceLeak_mkostemp(char *template, int flags) +{ + // cppcheck-suppress unreadVariable + int fp = mkostemp(template, flags); + // cppcheck-suppress resourceLeak +} + +void no_resourceLeak_mkostemp_01(char *template, int flags) +{ + int fp = mkostemp(template, flags); + close(fp); +} + +int no_resourceLeak_mkostemp_02(char *template, int flags) +{ + return mkostemp(template, flags); +} + +void valid_code(int argInt1, va_list valist_arg, const int * parg) +{ + char *p; + + if (__builtin_expect(argInt1, 0)) {} + if (__builtin_expect_with_probability(argInt1 + 1, 2, 0.5)) {} + if (__glibc_unlikely(argInt1 != 0)) {} + if (__glibc_likely(parg != NULL)) {} + const void *ax1 = __builtin_assume_aligned(parg, 16); + printf("%p", ax1); + const void *ax2 = __builtin_assume_aligned(parg, 32, 8); + printf("%p", ax2); + + p = (char *)malloc(10); + free(p); + p = (char *)malloc(5); + xfree(p); + p = (char *)xmalloc(10); + free(p); + p = (char *)xmalloc(5); + xfree(p); + + // cppcheck-suppress allocaCalled + p = __builtin_alloca(5); + p[0] = 1; + // TODO cppcheck-suppress arrayIndexOutOfBounds + p[5] = 1; + __builtin_prefetch(p, 0, 1); + + if (__builtin_types_compatible_p(int, char)) {} + + char * pStr = NULL; + if (vasprintf(&pStr, "%d %d", valist_arg) != -1) { + free(pStr); + } + + printf("%d", 0b010); + printf("%d", __extension__ 0b10001000); + + if (__alignof__(int) == 4) {} + + // cppcheck-suppress valueFlowBailoutIncompleteVar + const void * p_mmap = mmap(NULL, 1, PROT_NONE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); + printf("%p", p_mmap); + munmap(p_mmap, 1); + + uint16_t i16_1 = 0, i16_2; + // cppcheck-suppress unreadVariable + i16_2 = __builtin_bswap16(i16_1++); + uint32_t i32_1 = 0, i32_2; + // cppcheck-suppress unreadVariable + i32_2 = __builtin_bswap32(i32_1++); + uint64_t i64_1 = 0, i64_2; + // cppcheck-suppress unreadVariable + i64_2 = __builtin_bswap64(i64_1++); + + // cppcheck-suppress zerodiv + // cppcheck-suppress unreadVariable + i16_1 /= bswap_16(0x1234) - 0x3412; + // cppcheck-suppress zerodiv + // cppcheck-suppress unreadVariable + i32_1 /= bswap_32(0x12345678) - 0x78563412; + // cppcheck-suppress zerodiv + // cppcheck-suppress unreadVariable + i64_1 /= bswap_64(0x023456789abcde0f) - 0x0fdebc9a78563402; +} + +void ignoreleak(void) +{ + char *p = (char *)malloc(10); + __builtin_memset(&(p[0]), 0, 10); + // cppcheck-suppress memleak +} + +void memleak_asprintf(char **ptr, const char *fmt, const int arg) +{ + // No warning is expected for + if (-1 != asprintf(ptr,fmt,arg)) { + free(ptr); + } + if (-1 != asprintf(ptr,fmt,arg)) {} +} + +void memleak_asprintf2() { // #12186 + char* p = malloc(5); + // cppcheck-suppress memleak + (void)asprintf(&p, "%s", "test"); + // cppcheck-suppress memleak +} + +void memleak_asprintf3() { + char* p = malloc(5); + // cppcheck-suppress memleak + asprintf(&p, "%s", "test"); + free(p); +} + +void memleak_asprintf4(char** p) { + asprintf(p, "%s", "test"); +} + +void memleak_asprintf5(char* p) { + asprintf(&p, "%s", "test"); + // cppcheck-suppress memleak +} + +void memleak_asprintf6(const char* fmt, const int arg) { + char* ptr; + if (-1 == asprintf(&ptr, fmt, arg)) + return; + printf("%s", ptr); + free(ptr); +} + +void memleak_asprintf7(const char* fmt, const int arg) { + char* ptr; + if (asprintf(&ptr, fmt, arg) != -1) { + printf("%s", ptr); + free(ptr); + } + else + return; +} + +void memleak_asprintf8(const char *fmt, const int arg) // #12204 +{ + char* ptr; + int ret = asprintf(&ptr, fmt, arg); + if (-1 == ret) { + return; + } + printf("%s", ptr); + free(ptr); +} + +void memleak_xmalloc() +{ + char *p = (char*)xmalloc(10); + p[9] = 0; + // cppcheck-suppress memleak +} + +void memleak_mmap() +{ + // cppcheck-suppress valueFlowBailoutIncompleteVar + const void * p_mmap = mmap(NULL, 1, PROT_NONE, MAP_ANONYMOUS | MAP_SHARED, -1, 0); + printf("%p", p_mmap); + // cppcheck-suppress memleak +} + +void uninitvar__builtin_memset(void) +{ + void *s; + int c; + size_t n; + // cppcheck-suppress uninitvar + (void)__builtin_memset(s,c,n); +} + +void bufferAccessOutOfBounds__builtin_memset(void) +{ + uint8_t buf[42]; + // cppcheck-suppress bufferAccessOutOfBounds + (void)__builtin_memset(buf,0,1000); +} + +void bufferAccessOutOfBounds() +{ + const char buf[2] = "a"; + // This is valid + sethostname(buf, 2); + // cppcheck-suppress bufferAccessOutOfBounds + sethostname(buf, 4); + + char * pAlloc1 = xcalloc(2, 4); + memset(pAlloc1, 0, 8); + // cppcheck-suppress bufferAccessOutOfBounds + memset(pAlloc1, 0, 9); + free(pAlloc1); + + char * pAlloc2 = xmalloc(4); + memset(pAlloc2, 0, 4); + // cppcheck-suppress bufferAccessOutOfBounds + memset(pAlloc2, 0, 5); + + pAlloc2 = xrealloc(pAlloc2, 10); + memset(pAlloc2, 0, 10); + // cppcheck-suppress bufferAccessOutOfBounds + memset(pAlloc2, 0, 11); + + free(pAlloc2); +} + +void leakReturnValNotUsed() +{ + // cppcheck-suppress [unreadVariable, constVariablePointer] + char* ptr = (char*)strdupa("test"); + // cppcheck-suppress ignoredReturnValue + strdupa("test"); + // cppcheck-suppress [unreadVariable, constVariablePointer] + char* ptr2 = (char*)strndupa("test", 1); + // cppcheck-suppress ignoredReturnValue + strndupa("test", 1); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + strcasestr("test", NULL); + + // FIXME cppcheck-suppress knownConditionTrueFalse + // cppcheck-suppress duplicateExpression + if (42 == __builtin_expect(42, 0)) + return; +} + +#if !defined(__CYGWIN__) && !(defined(__APPLE__) && defined(__MACH__)) +int nullPointer_epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) +{ + // no warning is expected + (void)epoll_ctl(epfd, op, fd, event); + + // No nullpointer warning is expected in case op is set to EPOLL_CTL_DEL + // EPOLL_CTL_DEL + // Remove (deregister) the target file descriptor fd from the + // epoll instance referred to by epfd. The event is ignored and + // can be NULL. + // cppcheck-suppress valueFlowBailoutIncompleteVar + return epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL); +} +#endif diff --git a/cppcheck-2.14.0/test/cfg/googletest.cpp b/cppcheck-2.14.0/test/cfg/googletest.cpp new file mode 100644 index 00000000..c13db349 --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/googletest.cpp @@ -0,0 +1,85 @@ + +// Test library configuration for googletest.cfg +// +// Usage: +// $ cppcheck --check-library --library=googletest --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/googletest.cpp +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +// cppcheck-suppress-file valueFlowBailout + +#include +#include + + +namespace ExampleNamespace { + constexpr long long TOLERANCE = 10; + + // #9397 syntaxError when MATCHER_P is not known + // cppcheck-suppress symbolDatabaseWarning + MATCHER_P(ExampleMatcherPTest, expected, "") + { + // cppcheck-suppress valueFlowBailoutIncompleteVar + return ((arg <= (expected + TOLERANCE)) && (arg >= (expected - TOLERANCE))); + } + + // syntaxError when MATCHER is not known + // cppcheck-suppress symbolDatabaseWarning + MATCHER(ExampleMatcherTest, "") + { + return (arg == TOLERANCE); + } +} + +TEST(ASSERT, ASSERT) +{ + int *a = (int*)calloc(10,sizeof(int)); // cppcheck-suppress cstyleCast + ASSERT_TRUE(a != nullptr); + + a[0] = 10; + + free(a); +} + +// Avoid syntax error: https://sourceforge.net/p/cppcheck/discussion/general/thread/6ccc7283e2/ +TEST(test_cppcheck, cppcheck) +{ + TestStruct it; + ASSERT_THROW(it.operator->(), std::out_of_range); +} + +// #9964 - avoid compareBoolExpressionWithInt false positive +TEST(Test, assert_false_fp) +{ + // cppcheck-suppress valueFlowBailoutIncompleteVar + ASSERT_FALSE(errno < 0); +} + +// Check that conditions in the ASSERT_* macros are processed correctly. +TEST(Test, warning_in_assert_macros) +{ + int i = 5; + int j = 6; + + // cppcheck-suppress knownConditionTrueFalse + ASSERT_TRUE(i == 5); + // cppcheck-suppress knownConditionTrueFalse + ASSERT_FALSE(i != 5); + // cppcheck-suppress duplicateExpression + ASSERT_EQ(i, i); + ASSERT_NE(i, j); // expected knownConditionTrueFalse... + ASSERT_LT(i, j); // expected knownConditionTrueFalse... + // cppcheck-suppress duplicateExpression + ASSERT_LE(i, i); + ASSERT_GT(j, i); // expected knownConditionTrueFalse + // cppcheck-suppress duplicateExpression + ASSERT_GE(i, i); + + // cppcheck-suppress valueFlowBailoutIncompleteVar + unsigned int u = errno; + // cppcheck-suppress [unsignedPositive] + ASSERT_GE(u, 0); + // cppcheck-suppress [unsignedLessThanZero] + ASSERT_LT(u, 0); +} diff --git a/cppcheck-2.14.0/test/cfg/gtk.c b/cppcheck-2.14.0/test/cfg/gtk.c new file mode 100644 index 00000000..6d8b2258 --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/gtk.c @@ -0,0 +1,456 @@ + +// Test library configuration for gtk.cfg +// +// Usage: +// $ cppcheck --check-library --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr --library=gtk test/cfg/gtk.c +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +#include +#include +#include + + +void validCode(int argInt, GHashTableIter * hash_table_iter, GHashTable * hash_table) +{ + g_assert_cmpint(4 + 1, >=, 5); + g_assert_cmpstr("test", ==, "test"); + + // if G_UNLIKELY is not defined this results in a syntax error + if G_UNLIKELY(argInt == 1) {} else if (G_UNLIKELY(argInt == 2)) {} + + if G_LIKELY(argInt == 0) {} else if (G_LIKELY(argInt == -1)) {} + + printf("%s", _("test")); + printf("%s", Q_("a|test")); + printf("%s", N_("test")); + + gpointer gpt = g_malloc(4); + printf("%p", gpt); + g_free(gpt); + g_assert(gpt); + if (!gpt) { + // cppcheck-suppress checkLibraryNoReturn + g_assert_not_reached(); + } + gpointer p = GINT_TO_POINTER(1); + int i = GPOINTER_TO_INT(p); + // cppcheck-suppress knownConditionTrueFalse + if (i == 1) {} + + g_print("test"); + g_print("%d", 1); + g_printerr("err"); + + GString * pGStr1 = g_string_new("test"); + g_string_append(pGStr1, "a"); + g_string_free(pGStr1, TRUE); + + gchar * pGchar1 = g_strconcat("a", "b", NULL); + printf("%s", pGchar1); + g_free(pGchar1); + + // cppcheck-suppress unusedAllocatedMemory + GError * pGerror = g_error_new(1, -2, "a %d", 1); + g_error_free(pGerror); + + static gsize init_val = 0; + if (g_once_init_enter(&init_val)) { + gsize result_val = 1; + g_once_init_leave(&init_val, result_val); + } + + g_hash_table_iter_replace(hash_table_iter, g_strdup("test")); + g_hash_table_insert(hash_table, g_strdup("key"), g_strdup("value")); + g_hash_table_replace(hash_table, g_strdup("key"), g_strdup("value")); + + // NULL is handled graciously + char* str = g_strdup(NULL); + if (g_strcmp0(str, NULL) || g_strcmp0(NULL, str)) + printf("%s", str); + g_free(str); +} + +void g_malloc_test() +{ + // cppcheck-suppress leakReturnValNotUsed + g_malloc(8); + + gconstpointer gpt = g_malloc(1); + + printf("%p", gpt); + + // cppcheck-suppress memleak +} + +void g_malloc0_test() +{ + // cppcheck-suppress leakReturnValNotUsed + g_malloc0(8); + + gconstpointer gpt = g_malloc0(1); + + printf("%p", gpt); + + // cppcheck-suppress memleak +} + +void g_malloc_n_test() +{ + // cppcheck-suppress leakReturnValNotUsed + g_malloc_n(8, 1); + + gconstpointer gpt = g_malloc_n(1, 2); + + printf("%p", gpt); + + // cppcheck-suppress memleak +} + +void g_malloc0_n_test() +{ + // cppcheck-suppress leakReturnValNotUsed + g_malloc0_n(8, 1); + + gconstpointer gpt = g_malloc0_n(1, 2); + + printf("%p", gpt); + + // cppcheck-suppress memleak +} + +void g_try_malloc_test() +{ + // cppcheck-suppress leakReturnValNotUsed + g_try_malloc(8); + + gconstpointer gpt = g_try_malloc(1); + + printf("%p", gpt); + + // cppcheck-suppress memleak +} + +void g_try_malloc0_test() +{ + // cppcheck-suppress leakReturnValNotUsed + g_try_malloc0(8); + + gconstpointer gpt = g_try_malloc0(1); + + printf("%p", gpt); + + // cppcheck-suppress memleak +} + +void g_try_malloc_n_test() +{ + // cppcheck-suppress leakReturnValNotUsed + g_try_malloc_n(8, 1); + + gconstpointer gpt = g_try_malloc_n(1, 2); + + printf("%p", gpt); + + // cppcheck-suppress memleak +} + +void g_try_malloc0_n_test() +{ + // cppcheck-suppress leakReturnValNotUsed + g_try_malloc0_n(8, 1); + + gconstpointer gpt = g_try_malloc0_n(1, 2); + + printf("%p", gpt); + + // cppcheck-suppress memleak +} + +void g_realloc_test() +{ + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress leakReturnValNotUsed + g_realloc(NULL, 1); + + gpointer gpt = g_malloc(1); + gpt = g_realloc(gpt, 2); // No memleakOnRealloc since g_realloc aborts if it fails + printf("%p", gpt); + + // cppcheck-suppress memleak +} + +void g_realloc_n_test() +{ + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress leakReturnValNotUsed + g_realloc_n(NULL, 1, 2); + + gpointer gpt = g_malloc_n(1, 2); + gpt = g_realloc_n(gpt, 2, 3); // No memleakOnRealloc since g_realloc_n aborts if it fails + printf("%p", gpt); + + // cppcheck-suppress memleak +} + +void g_try_realloc_test() +{ + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress leakReturnValNotUsed + g_try_realloc(NULL, 1); + + gpointer gpt = g_try_malloc(1); + // cppcheck-suppress memleakOnRealloc + gpt = g_try_realloc(gpt, 2); + printf("%p", gpt); + + // cppcheck-suppress memleak +} + +void g_try_realloc_n_test() +{ + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress leakReturnValNotUsed + g_try_realloc_n(NULL, 1, 2); + + gpointer gpt = g_try_malloc_n(1, 2); + // cppcheck-suppress memleakOnRealloc + gpt = g_try_realloc_n(gpt, 2, 3); + printf("%p", gpt); + + // cppcheck-suppress memleak +} + +void g_assert_test() +{ + int a; + // cppcheck-suppress checkLibraryNoReturn + // cppcheck-suppress assignmentInAssert + g_assert(a = 5); +} + +void g_assert_true_false_test() +{ + gboolean t = TRUE; + gboolean f = FALSE; + g_assert_true(t); + // cppcheck-suppress checkLibraryNoReturn + g_assert_false(f); +} + +void g_assert_null_nonnull_test() +{ + char * gpt = g_malloc(1); + g_assert_nonnull(gpt); + gpt[0] = 0; + g_free(gpt); + // cppcheck-suppress checkLibraryNoReturn + g_assert_null(NULL); +} + +void g_print_test() +{ + // cppcheck-suppress invalidPrintfArgType_uint + g_print("%u", -1); + // cppcheck-suppress invalidPrintfArgType_uint + g_printerr("%x", "a"); +} + +void g_alloca_test() +{ + // cppcheck-suppress allocaCalled + char * pBuf1 = g_alloca(5); + pBuf1[0] = '\0'; +} + +void g_new_test() +{ + struct a { + int b; + }; + // valid + struct a * pNew1 = g_new(struct a, 5); + printf("%p", pNew1); + g_free(pNew1); + + // cppcheck-suppress leakReturnValNotUsed + g_new(struct a, 1); + + const struct a * pNew2 = g_new(struct a, 2); + printf("%p", pNew2); + // cppcheck-suppress memleak +} + +void g_new_if_test() +{ + struct a { + int b; + }; + + const struct a * pNew3; + // cppcheck-suppress valueFlowBailoutIncompleteVar + if (pNew3 = g_new(struct a, 6)) { + printf("%p", pNew3); + } + // cppcheck-suppress memleak +} + +void g_new0_test() +{ + struct a { + int b; + }; + // valid + // cppcheck-suppress valueFlowBailoutIncompleteVar + struct a * pNew1 = g_new0(struct a, 5); + printf("%p", pNew1); + g_free(pNew1); + + // cppcheck-suppress leakReturnValNotUsed + g_new0(struct a, 1); + + const struct a * pNew2 = g_new0(struct a, 2); + printf("%p", pNew2); + // cppcheck-suppress memleak +} + +void g_try_new_test() +{ + struct a { + int b; + }; + // valid + // cppcheck-suppress valueFlowBailoutIncompleteVar + struct a * pNew1 = g_try_new(struct a, 5); + printf("%p", pNew1); + g_free(pNew1); + + // cppcheck-suppress leakReturnValNotUsed + g_try_new(struct a, 1); + + const struct a * pNew2 = g_try_new(struct a, 2); + printf("%p", pNew2); + // cppcheck-suppress memleak +} +void g_try_new0_test() +{ + struct a { + int b; + }; + // valid + // cppcheck-suppress valueFlowBailoutIncompleteVar + struct a * pNew1 = g_try_new0(struct a, 5); + printf("%p", pNew1); + g_free(pNew1); + + // cppcheck-suppress leakReturnValNotUsed + g_try_new0(struct a, 1); + + const struct a * pNew2 = g_try_new0(struct a, 2); + printf("%p", pNew2); + // cppcheck-suppress memleak +} + +void g_renew_test() +{ + struct a { + int b; + }; + // cppcheck-suppress [leakReturnValNotUsed,valueFlowBailoutIncompleteVar] + g_renew(struct a, NULL, 1); + + struct a * pNew = g_new(struct a, 1); + pNew = g_renew(struct a, pNew, 2); // No memleakOnRealloc since g_renew aborts if it fails + printf("%p", pNew); + + // cppcheck-suppress memleak +} + +void g_try_renew_test() +{ + struct a { + int b; + }; + // cppcheck-suppress [leakReturnValNotUsed,valueFlowBailoutIncompleteVar] + g_try_renew(struct a, NULL, 1); + + struct a * pNew = g_try_new(struct a, 1); + // cppcheck-suppress memleakOnRealloc + pNew = g_try_renew(struct a, pNew, 2); + printf("%p", pNew); + + // cppcheck-suppress memleak +} + +void g_error_new_test() +{ + // valid + GError * pNew1 = g_error_new(1, -2, "a %d", 1); + printf("%p", pNew1); + g_error_free(pNew1); + + // cppcheck-suppress leakReturnValNotUsed + g_error_new(1, -2, "a %d", 1); + + const GError * pNew2 = g_error_new(1, -2, "a %d", 1); + printf("%p", pNew2); + // cppcheck-suppress memleak +} + +void g_once_init_enter_leave_test() +{ + static gsize init_val; + if (g_once_init_enter(&init_val)) { + gsize result_val = 0; + // cppcheck-suppress invalidFunctionArg + g_once_init_leave(&init_val, result_val); + } + + gsize init_val2; + // cppcheck-suppress uninitvar + // cppcheck-suppress ignoredReturnValue + g_once_init_enter(&init_val2); + + gsize * init_val3 = NULL; + // cppcheck-suppress nullPointer + if (g_once_init_enter(init_val3)) { + gsize* init_val31 = NULL; + // cppcheck-suppress nullPointer + g_once_init_leave(init_val31, 1); + } + + gsize * init_val4; + // cppcheck-suppress uninitvar + if (g_once_init_enter(init_val4)) { + gsize * init_val5; + // cppcheck-suppress uninitvar + g_once_init_leave(init_val5, 1); + } +} + +void g_strchug_g_strchomp_test(gchar * str1) +{ + g_strchug(str1); + g_strchomp(str1); + g_strchug(g_strchomp(str1)); + gchar * str2; + // cppcheck-suppress uninitvar + g_strchug(str2); + gchar * str3; + // cppcheck-suppress uninitvar + g_strchomp(str3); +} + +void g_abort_test() +{ + g_abort(); + //cppcheck-suppress unreachableCode + printf("Never reached"); +} + +gchar* g_strchug_string_free_test(GString* t) // #12301 +{ + gchar* p = g_strchug(g_string_free(t, FALSE)); + return p; +} diff --git a/cppcheck-2.14.0/test/cfg/kde.cpp b/cppcheck-2.14.0/test/cfg/kde.cpp new file mode 100644 index 00000000..9a272094 --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/kde.cpp @@ -0,0 +1,32 @@ + +// Test library configuration for kde.cfg +// +// Usage: +// $ cppcheck --check-library --library=kde --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/kde.cpp +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +#include +#include +#include + +class k_global_static_testclass1 {}; +K_GLOBAL_STATIC(k_global_static_testclass1, k_global_static_testinstance1); + +void valid_code(const KConfigGroup& cfgGroup) +{ + const k_global_static_testclass1 * pk_global_static_testclass1 = k_global_static_testinstance1; + printf("%p", pk_global_static_testclass1); + + bool entryTest = cfgGroup.readEntry("test", false); + if (entryTest) {} +} + +void ignoredReturnValue(const KConfigGroup& cfgGroup) +{ + // cppcheck-suppress ignoredReturnValue + cfgGroup.readEntry("test", "default"); + // cppcheck-suppress ignoredReturnValue + cfgGroup.readEntry("test"); +} diff --git a/cppcheck-2.14.0/test/cfg/libcurl.c b/cppcheck-2.14.0/test/cfg/libcurl.c new file mode 100644 index 00000000..9e91eefc --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/libcurl.c @@ -0,0 +1,90 @@ + +// Test library configuration for libcurl.cfg +// +// Usage: +// $ cppcheck --check-library --library=libcurl --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/libcurl.c +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +// cppcheck-suppress-file valueFlowBailout + +#include +#include + +void validCode() +{ + CURL *curl = curl_easy_init(); + if (curl) { + CURLcode res; + // cppcheck-suppress valueFlowBailoutIncompleteVar + curl_easy_setopt(curl, CURLOPT_URL, "http://example.com"); + res = curl_easy_perform(curl); + if (res != CURLE_OK) { + printf("error"); + } else { + long response_code; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); + printf("%ld", response_code); + char * pStr = curl_easy_escape(curl, "a", 1); + if (pStr) + printf("%s", pStr); + curl_free(pStr); + curl_easy_reset(curl); + } + curl_easy_cleanup(curl); + } +} + +// cppcheck-suppress constParameterPointer +void ignoredReturnValue(CURL * handle) +{ + // cppcheck-suppress ignoredReturnValue + curl_easy_strerror(1); +} + +void resourceLeak_curl_easy_init() +{ + const CURL *curl = curl_easy_init(); + printf("%p", curl); + // cppcheck-suppress resourceLeak +} + +void resourceLeak_curl_easy_duphandle(CURL * handle) +{ + const CURL *curl = curl_easy_duphandle(handle); + printf("%p", curl); + // cppcheck-suppress resourceLeak +} + +void memleak_curl_easy_escape(CURL * handle) +{ + const char * pStr = curl_easy_escape(handle, "a", 1); + if (pStr) + printf("%s", pStr); + // cppcheck-suppress memleak +} + +void nullPointer(CURL * handle) +{ + char * buf[10] = {0}; + size_t len; + + curl_easy_recv(handle, buf, 10, &len); + // cppcheck-suppress nullPointer + curl_easy_recv(handle, buf, 10, NULL); + curl_easy_send(handle, buf, 10, &len); + // cppcheck-suppress nullPointer + curl_easy_send(handle, buf, 10, NULL); +} + +void uninitvar(CURL * handle) +{ + const char * bufInit[10] = {0}; + const char * bufUninit; + size_t len; + + curl_easy_send(handle, bufInit, 10, &len); + // cppcheck-suppress uninitvar + curl_easy_send(handle, bufUninit, 10, &len); +} diff --git a/cppcheck-2.14.0/test/cfg/libsigc++.cpp b/cppcheck-2.14.0/test/cfg/libsigc++.cpp new file mode 100644 index 00000000..031f2dfa --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/libsigc++.cpp @@ -0,0 +1,28 @@ + +// Test library configuration for libsigc++.cfg +// +// Usage: +// $ cppcheck --check-library --library=libsigc++ --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/libsigc++.cpp +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +#include + +struct struct1 : public sigc::trackable { + void func1(int) const {} +}; + +void valid_code() +{ + const struct1 my_sruct1; + sigc::slot sl = sigc::mem_fun(my_sruct1, &struct1::func1); + if (sl) {} +} + +void ignoredReturnValue() +{ + const struct1 my_sruct1; + // cppcheck-suppress ignoredReturnValue + sigc::mem_fun(my_sruct1, &struct1::func1); +} diff --git a/cppcheck-2.14.0/test/cfg/lua.c b/cppcheck-2.14.0/test/cfg/lua.c new file mode 100644 index 00000000..de222d55 --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/lua.c @@ -0,0 +1,31 @@ + +// Test library configuration for lua.cfg +// +// Usage: +// $ cppcheck --check-library --library=lua --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/lua.c +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +#include +#include + +void validCode(lua_State *L) +{ + int a = lua_gettop(L); + printf("%d", a); + lua_pushnil(L); + lua_pop(L, 1); +} + +void ignoredReturnValue(lua_State *L) +{ + // cppcheck-suppress ignoredReturnValue + lua_tonumber(L, 1); + // cppcheck-suppress ignoredReturnValue + lua_tostring(L, 1); + // cppcheck-suppress ignoredReturnValue + lua_isboolean(L, 1); + // cppcheck-suppress ignoredReturnValue + lua_isnil(L, 1); +} diff --git a/cppcheck-2.14.0/test/cfg/mfc.cpp b/cppcheck-2.14.0/test/cfg/mfc.cpp new file mode 100644 index 00000000..84a7d868 --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/mfc.cpp @@ -0,0 +1,26 @@ + +// Test library configuration for mfc.cfg + +#include + + +class MyClass1 : public CObject { + DECLARE_DYNAMIC(MyClass1) +public: + MyClass1() {} +}; +IMPLEMENT_DYNAMIC(MyClass1, CObject) + +class MyClass2 : public CObject { + DECLARE_DYNCREATE(MyClass2) +public: + MyClass2() {} +}; +IMPLEMENT_DYNCREATE(MyClass2, CObject) + +class MyClass3 : public CObject { + DECLARE_SERIAL(MyClass3) +public: + MyClass3() {} +}; +IMPLEMENT_SERIAL(MyClass3, CObject, 42) diff --git a/cppcheck-2.14.0/test/cfg/opencv2.cpp b/cppcheck-2.14.0/test/cfg/opencv2.cpp new file mode 100644 index 00000000..de0f3731 --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/opencv2.cpp @@ -0,0 +1,51 @@ + +// Test library configuration for opencv2.cfg +// +// Usage: +// $ cppcheck --check-library --library=opencv2 --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/opencv2.cpp +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +// cppcheck-suppress-file valueFlowBailout + +#include +#include + + +void validCode(const char* argStr) +{ + cv::Mat image; + // cppcheck-suppress valueFlowBailoutIncompleteVar + image = cv::imread(argStr, cv::IMREAD_COLOR); + if (!image.data) { + printf("No image data \n"); + return; + } + cv::namedWindow("Display Image", cv::WINDOW_AUTOSIZE); + cv::imshow("Display Image", image); + cv::waitKey(0); + + cv::String cvStr("Hello"); + cvStr += " World"; + std::cout << cvStr; + + // cppcheck-suppress [cstyleCast, unusedAllocatedMemory] + char * pBuf = (char *)cv::fastMalloc(20); + cv::fastFree(pBuf); +} + +void ignoredReturnValue() +{ + // cppcheck-suppress ignoredReturnValue + cv::imread("42.png"); +} + +void memleak() +{ + // cppcheck-suppress cstyleCast + const char * pBuf = (char *)cv::fastMalloc(1000); + // cppcheck-suppress [uninitdata, valueFlowBailoutIncompleteVar] + std::cout << pBuf; + // cppcheck-suppress memleak +} diff --git a/cppcheck-2.14.0/test/cfg/openmp.c b/cppcheck-2.14.0/test/cfg/openmp.c new file mode 100644 index 00000000..1bde120b --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/openmp.c @@ -0,0 +1,32 @@ + +// Test library configuration for openmp.cfg +// +// Usage: +// $ cppcheck --check-library --library=openmp --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/openmp.c +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +#include +#include + +void validCode() +{ + int arr[20] = { 0 }; + #pragma omp parallel for + for (int i = 0; i < 20; ++i) { + // cppcheck-suppress unreadVariable + arr[i] = i * i; + } + + char * pChars = (char *) omp_target_alloc(4, 1); + printf("pChars: %p", pChars); + omp_target_free(pChars, 1); +} + +void memleak_omp_target_alloc() +{ + const char * pChars = (char *) omp_target_alloc(2, 0); + printf("pChars: %p", pChars); + // cppcheck-suppress memleak +} diff --git a/cppcheck-2.14.0/test/cfg/openssl.c b/cppcheck-2.14.0/test/cfg/openssl.c new file mode 100644 index 00000000..03af9478 --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/openssl.c @@ -0,0 +1,68 @@ + +// Test library configuration for openssl.cfg +// +// Usage: +// $ cppcheck --check-library --library=openssl --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/openssl.c +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +#include +#include +#include + +void valid_code(BIO * bio) +{ + BIO_printf(bio, "%d\n", 1); +} + +// Example for encrypting a string using IDEA (from https://www.openssl.org/docs/man1.1.1/man3/EVP_CIPHER_CTX_new.html) +int valid_code_do_crypt(const char *outfile) +{ + unsigned char outbuf[1024]; + int outlen, tmplen; + const unsigned char key[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + const unsigned char iv[] = {1,2,3,4,5,6,7,8}; + const char intext[] = "Some Crypto Text"; + EVP_CIPHER_CTX *ctx; + FILE *out; + + ctx = EVP_CIPHER_CTX_new(); + // cppcheck-suppress checkLibraryFunction + EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key, iv); + + if (!EVP_EncryptUpdate(ctx, outbuf, &outlen, intext, strlen(intext))) { + /* Error */ + EVP_CIPHER_CTX_free(ctx); + return 0; + } + if (!EVP_EncryptFinal_ex(ctx, outbuf + outlen, &tmplen)) { + /* Error */ + EVP_CIPHER_CTX_free(ctx); + return 0; + } + outlen += tmplen; + EVP_CIPHER_CTX_free(ctx); + + out = fopen(outfile, "wb"); + if (out == NULL) { + /* Error */ + return 0; + } + fwrite(outbuf, 1, outlen, out); + fclose(out); + return 1; +} + +void invalidPrintfArgType_test(BIO * bio) +{ + // cppcheck-suppress invalidPrintfArgType_sint + BIO_printf(bio, "%d\n", 5U); +} + +void EVP_CIPHER_CTX_new_test() +{ + const EVP_CIPHER_CTX * ctx = EVP_CIPHER_CTX_new(); + printf("%p", ctx); + // cppcheck-suppress resourceLeak +} diff --git a/cppcheck-2.14.0/test/cfg/posix.c b/cppcheck-2.14.0/test/cfg/posix.c new file mode 100644 index 00000000..b4b8c9e4 --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/posix.c @@ -0,0 +1,1465 @@ + +// Test library configuration for posix.cfg +// +// Usage: +// $ cppcheck --check-library --library=posix --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/posix.c +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +// cppcheck-suppress-file [valueFlowBailout,purgedConfiguration] + +#define _BSD_SOURCE + +#include +#include // <- FILE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include // unavailable on some linux systems +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__APPLE__) +#include +#endif +#if !(defined(__APPLE__) && defined(__MACH__)) +#include +#endif +#include +#include +#include + + +#if !(defined(__APPLE__) && defined(__MACH__)) +void nullPointer_mq_timedsend(mqd_t mqdes, const char* msg_ptr, size_t msg_len, unsigned msg_prio, const struct timespec* abs_timeout) { + // cppcheck-suppress nullPointer + (void) mq_timedsend(mqdes, NULL, msg_len, msg_prio, abs_timeout); + // cppcheck-suppress nullPointer + (void) mq_timedsend(mqdes, msg_ptr, msg_len, msg_prio, NULL); +} +#endif + +#if __TRACE_H__ // + +void nullPointer_posix_trace_event(trace_event_id_t event_id, const void* restrictdata_ptr, size_t data_len) +{ + // cppcheck-suppress nullPointer + (void) posix_trace_event(event_id, NULL, data_len); + (void) posix_trace_event(event_id, restrictdata_ptr, 0); +} + +void nullPointer_posix_trace_trygetnext_event(trace_id_t trid, + struct posix_trace_event_info *event, + void *data, size_t num_bytes, + size_t *data_len, int *unavailable) +{ + // cppcheck-suppress nullPointer + (void) posix_trace_trygetnext_event(trid, NULL, data, num_bytes, data_len, unavailable); + // cppcheck-suppress nullPointer + (void) posix_trace_trygetnext_event(trid, event, NULL, num_bytes, data_len, unavailable); + // cppcheck-suppress nullPointer + (void) posix_trace_trygetnext_event(trid, event, data, num_bytes, NULL, unavailable); + // cppcheck-suppress nullPointer + (void) posix_trace_trygetnext_event(trid, event, data, num_bytes, data_len, NULL); +} + +int nullPointer_posix_trace_timedgetnext_event(trace_id_t trid, struct posix_trace_event_info *restrict event, void *restrict data, size_t num_bytes, size_t *restrict data_len, int *restrict unavailable, const struct timespec *restrict abstime) +{ + // cppcheck-suppress nullPointer + (void) posix_trace_timedgetnext_event(trid, NULL, data, num_bytes, data_len, unavailable, abstime); + // cppcheck-suppress nullPointer + (void) posix_trace_timedgetnext_event(trid, event, NULL, num_bytes, data_len, unavailable, abstime); + // cppcheck-suppress nullPointer + (void) posix_trace_timedgetnext_event(trid, event, data, num_bytes, NULL, unavailable, abstime); + // cppcheck-suppress nullPointer + (void) posix_trace_timedgetnext_event(trid, event, data, num_bytes, data_len, NULL, abstime); + // cppcheck-suppress nullPointer + (void) posix_trace_timedgetnext_event(trid, event, data, num_bytes, data_len, unavailable, NULL); + return posix_trace_timedgetnext_event(trid, event, data, num_bytes, data_len, unavailable, abstime); +} + +int nullPointer_posix_trace_getnext_event(trace_id_t trid, struct posix_trace_event_info *restrict event, const void *restrict data, size_t num_bytes, size_t *restrict data_len, int *restrict unavailable) +{ + // cppcheck-suppress nullPointer + (void) posix_trace_getnext_event(trid, NULL, data, num_bytes, data_len, unavailable); + // cppcheck-suppress nullPointer + (void) posix_trace_getnext_event(trid, event, NULL, num_bytes, data_len, unavailable); + // cppcheck-suppress nullPointer + (void) posix_trace_getnext_event(trid, event, data, num_bytes, NULL, unavailable); + // cppcheck-suppress nullPointer + (void) posix_trace_getnext_event(trid, event, data, num_bytes, data_len, NULL); + return posix_trace_getnext_event(trid, event, data, num_bytes, data_len, unavailable); +} +#endif // __TRACE_H__ + +size_t nullPointer_strxfrm_l(char *restrict dest, const char *restrict src, size_t count, locale_t locale) +{ + (void)strxfrm_l(dest, src, count, locale); + // In case the 3rd argument is 0, the 1st argument is permitted to be a null pointer. (#6306) + (void)strxfrm_l(NULL, src, 0, locale); + (void)strxfrm_l(NULL, src, 1, locale); + (void)strxfrm_l(NULL, src, count, locale); + // cppcheck-suppress nullPointer + return strxfrm_l(dest, NULL, count, locale); +} + +void nullPointer_pthread_attr_getstack(const pthread_attr_t *attr, void *stackaddr, size_t stacksize) { + // cppcheck-suppress nullPointer + (void) pthread_attr_getstack(NULL, &stackaddr, &stacksize); + // cppcheck-suppress nullPointer + (void) pthread_attr_getstack(attr, NULL, &stacksize); + // cppcheck-suppress nullPointer + (void) pthread_attr_getstack(attr, &stackaddr, NULL); + // cppcheck-suppress nullPointer + (void) pthread_attr_getstack(NULL, NULL, &stacksize); + // cppcheck-suppress nullPointer + (void) pthread_attr_getstack(NULL, &stackaddr, NULL); + // cppcheck-suppress nullPointer + (void) pthread_attr_getstack(attr, NULL, NULL); + // cppcheck-suppress nullPointer + (void) pthread_attr_getstack(NULL, NULL, NULL); +} + +void nullPointer_pthread_attr_setstack(const pthread_attr_t *attr) { + // cppcheck-suppress nullPointer + (void) pthread_attr_setstack(NULL, NULL, 0); + (void) pthread_attr_setstack(attr, NULL, 0); + // cppcheck-suppress nullPointer + (void) pthread_attr_setstack(NULL, (void*) 1, 0); +} + +void nullPointer_setkey(const char *key) +{ + // cppcheck-suppress nullPointer + setkey(NULL); +} + +void nullPointer_encrypt(const char block[64], int edflag) +{ + // cppcheck-suppress nullPointer + encrypt(NULL, edflag); + encrypt(block, edflag); +} + +int nullPointer_getopt(int argc, char* const argv[], const char* optstring) +{ + // cppcheck-suppress nullPointer + (void) getopt(argc, NULL, optstring); + // cppcheck-suppress nullPointer + (void) getopt(argc, argv, NULL); + return getopt(argc, argv, optstring); +} + +#if !(defined(__APPLE__) && defined(__MACH__)) +int invalidFunctionArgStr_mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio) +{ + // No warning is expected for: + const char msg = '0'; + (void) mq_send(mqdes, &msg, 1, 0); + return mq_send(mqdes, msg_ptr, msg_len, 0); +} +#endif + +void invalidFunctionArgStr_mbsnrtowcs(void) +{ + wchar_t wenough[10]; + mbstate_t s; + memset (&s, '\0', sizeof (s)); + const char* cp = "ABC"; + wcscpy (wenough, L"DEF"); + // No warning is expected for - #11119 + if (mbsnrtowcs (wenough, &cp, 1, 10, &s) != 1 || wcscmp (wenough, L"AEF") != 0) {} +} + +struct tm * ignoredReturnValue_localtime(const time_t *tp) +{ + // cppcheck-suppress [ignoredReturnValue,localtimeCalled] + localtime(tp); + // cppcheck-suppress localtimeCalled + return localtime(tp); +} + +int nullPointer_getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) +{ + // cppcheck-suppress nullPointer + (void) getpwuid_r(uid, NULL, buffer, bufsize, result); + // cppcheck-suppress nullPointer + (void) getpwuid_r(uid, pwd, NULL, bufsize, result); + // cppcheck-suppress nullPointer + (void) getpwuid_r(uid, pwd, buffer, bufsize, NULL); + return getpwuid_r(uid, pwd, buffer, bufsize, result); +} + +int nullPointer_getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result) +{ + // cppcheck-suppress nullPointer + (void) getpwnam_r(NULL, pwd, buffer, bufsize, result); + // cppcheck-suppress nullPointer + (void) getpwnam_r(name, NULL, buffer, bufsize, result); + // cppcheck-suppress nullPointer + (void) getpwnam_r(name, pwd, NULL, bufsize, result); + // cppcheck-suppress nullPointer + (void) getpwnam_r(name, pwd, buffer, bufsize, NULL); + return getpwnam_r(name, pwd, buffer, bufsize, result); +} + +int nullPointer_fgetpwent_r(FILE *restrict stream, const struct passwd *restrict pwbuf, char *restrict buf, size_t buflen, struct passwd **restrict pwbufp) +{ + // cppcheck-suppress nullPointer + (void) fgetpwent_r(NULL, pwbuf, buf, buflen, pwbufp); + // cppcheck-suppress nullPointer + (void) fgetpwent_r(stream, NULL, buf, buflen, pwbufp); + // cppcheck-suppress nullPointer + (void) fgetpwent_r(stream, pwbuf, NULL, buflen, pwbufp); + // cppcheck-suppress nullPointer + (void) fgetpwent_r(stream, pwbuf, buf, buflen, NULL); + return fgetpwent_r(stream, pwbuf, buf, buflen, pwbufp); +} + +int nullPointer_getpwent_r(const struct passwd *restrict pwbuf, char *restrict buf, size_t buflen, struct passwd **restrict pwbufp) +{ + // cppcheck-suppress nullPointer + (void) getpwent_r(NULL, buf, buflen, pwbufp); + // cppcheck-suppress nullPointer + (void) getpwent_r(pwbuf, NULL, buflen, pwbufp); + // cppcheck-suppress nullPointer + (void) getpwent_r(pwbuf, buf, buflen, NULL); + return getpwent_r(pwbuf, buf, buflen, pwbufp); +} + +int nullPointer_getgrgid_r(gid_t gid, struct group *restrict grp, char *restrict buf, size_t buflen, struct group **restrict result) +{ + // cppcheck-suppress nullPointer + (void) getgrgid_r(gid, NULL, buf, buflen, result); + // cppcheck-suppress nullPointer + (void) getgrgid_r(gid, grp, NULL, buflen, result); + // cppcheck-suppress nullPointer + (void) getgrgid_r(gid, grp, buf, buflen, NULL); + return getgrgid_r(gid, grp, buf, buflen, result); +} + +int nullPointer_getgrnam_r(const char *restrict name, struct group *restrict grp, char *restrict buf, size_t buflen, struct group **restrict result) +{ + // cppcheck-suppress nullPointer + (void) getgrnam_r(NULL, grp, buf, buflen, result); + // cppcheck-suppress nullPointer + (void) getgrnam_r(name, NULL, buf, buflen, result); + // cppcheck-suppress nullPointer + (void) getgrnam_r(name, grp, NULL, buflen, result); + // cppcheck-suppress nullPointer + (void) getgrnam_r(name, grp, buf, buflen, NULL); + return getgrnam_r(name, grp, buf, buflen, result); +} + +void knownConditionTrueFalse_ffs(int i) +{ + // ffs() returns the position of the first bit set, or 0 if no bits are set in i. + const int x = ffs(0); + // cppcheck-suppress knownConditionTrueFalse + if (x == 0) {} // always true + // cppcheck-suppress knownConditionTrueFalse + if (x == 1) {} // always false + if (ffs(i) == 0) {} +} + +ssize_t nullPointer_readlink(const char *path, char *buf, size_t bufsiz) +{ + // cppcheck-suppress nullPointer + (void)readlink(NULL, buf, bufsiz); + // cppcheck-suppress nullPointer + (void)readlink(path, NULL, bufsiz); + return readlink(path, buf, bufsiz); +} + +int nullPointer_readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz) +{ + // cppcheck-suppress nullPointer + (void) readlinkat(dirfd, NULL, buf, bufsiz); + // cppcheck-suppress nullPointer + (void) readlinkat(dirfd, pathname, NULL, bufsiz); + return readlinkat(dirfd, pathname, buf, bufsiz); +} + +ssize_t nullPointer_recv(int sockfd, void *buf, size_t len, int flags) +{ + // cppcheck-suppress nullPointer + (void) recv(sockfd, NULL, len, flags); + return recv(sockfd, buf, len, flags); +} + +ssize_t nullPointer_recvfrom(int sockfd, void *buf, size_t len, int flags, + struct sockaddr *src_addr, socklen_t *addrlen) +{ + // If src_addr is not NULL, and the underlying protocol provides the source address, this source address is filled in. + (void) recvfrom(sockfd, buf, len, flags, NULL, addrlen); + (void) recvfrom(sockfd, buf, len, flags, src_addr, NULL); + (void) recvfrom(sockfd, buf, len, flags, NULL, NULL); + // cppcheck-suppress nullPointer + (void) recvfrom(sockfd, NULL, len, flags, src_addr, addrlen); + return recvfrom(sockfd, buf, len, flags, src_addr, addrlen); +} +int nullPointer_semop(int semid, struct sembuf *sops, size_t nsops) +{ + // cppcheck-suppress nullPointer + (void)semop(semid, NULL, nsops); + return semop(semid, sops, nsops); +} + +int nullPointer_socketpair(int domain, int t, int protocol, int sv[2]) +{ + // cppcheck-suppress nullPointer + (void) socketpair(domain, t, protocol, NULL); + return socketpair(domain, t, protocol, sv); +} + +void nullPointer_lcong48(const unsigned short param[7]) +{ + // cppcheck-suppress nullPointer + (void) lcong48(NULL); + return lcong48(param); +} + +long int nullPointer_jrand48(unsigned short xsubi[3]) +{ + // cppcheck-suppress nullPointer + (void) jrand48(NULL); + return jrand48(xsubi); +} + +long int nullPointer_nrand48(unsigned short xsubi[3]) +{ + // cppcheck-suppress nullPointer + (void) nrand48(NULL); + return nrand48(xsubi); +} + +double nullPointer_erand48(unsigned short xsubi[3]) +{ + // cppcheck-suppress nullPointer + (void) erand48(NULL); + return erand48(xsubi); +} + +struct non_const_parameter_erand48_struct { unsigned short xsubi[3]; }; +// No warning is expected that dat can be const +double non_const_parameter_erand48(struct non_const_parameter_erand48_struct *dat) +{ + return erand48(dat->xsubi); +} + +unsigned short *nullPointer_seed48(const unsigned short seed16v[3]) +{ + // cppcheck-suppress nullPointer + (void) seed48(NULL); + return seed48(seed16v); +} + +int nullPointer_getlogin_r(char *buf, size_t bufsize) +{ + // cppcheck-suppress nullPointer + (void)getlogin_r(NULL,bufsize); + return getlogin_r(buf,bufsize); +} + +ssize_t uninitvar_pread(int fd, void *buf, size_t nbyte, off_t offset) +{ + int Fd; + // cppcheck-suppress uninitvar + (void)pread(Fd,buf,nbyte,offset); + size_t Nbyte; + // cppcheck-suppress uninitvar + (void)pread(fd,buf,Nbyte,offset); + off_t Offset; + // cppcheck-suppress uninitvar + (void)pread(fd,buf,nbyte,Offset); + return pread(fd,buf,nbyte,offset); +} + +ssize_t nullPointer_pwrite(int fd, const void *buf, size_t nbyte, off_t offset) +{ + // cppcheck-suppress nullPointer + (void)pwrite(fd,NULL,nbyte,offset); + return pwrite(fd,buf,nbyte,offset); +} + +int nullPointer_ttyname_r(int fd, char *buf, size_t buflen) +{ + // cppcheck-suppress nullPointer + (void)ttyname_r(fd,NULL,buflen); + return ttyname_r(fd,buf,buflen); +} + +size_t bufferAccessOutOfBounds_wcsnrtombs(char *restrict dest, const wchar_t **restrict src, size_t nwc, size_t len, mbstate_t *restrict ps) +{ + char buf[42]; + (void)wcsnrtombs(buf,src,nwc,42,ps); + // cppcheck-suppress bufferAccessOutOfBounds + (void)wcsnrtombs(buf,src,nwc,43,ps); + return wcsnrtombs(dest,src,nwc,len,ps); +} + +size_t nullPointer_wcsnrtombs(char *restrict dest, const wchar_t **restrict src, size_t nwc, size_t len, mbstate_t *restrict ps) +{ + // It is allowed to set the first arg to NULL + (void)wcsnrtombs(NULL,src,nwc,len,ps); + // cppcheck-suppress nullPointer + (void)wcsnrtombs(dest,NULL,nwc,len,ps); + // It is allowed to set the last arg to NULL + (void)wcsnrtombs(dest,src,nwc,len,NULL); + return wcsnrtombs(dest,src,nwc,len,ps); +} + +int nullPointer_wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n) +{ + // cppcheck-suppress nullPointer + (void)wcsncasecmp(NULL,s2,n); + // cppcheck-suppress nullPointer + (void)wcsncasecmp(s1,NULL,n); + return wcsncasecmp(s1,s2,n); +} + +int uninitvar_wcwidth(const wchar_t c) +{ + wchar_t wc; + // cppcheck-suppress uninitvar + (void)wcwidth(wc); + // No warning is expected + return wcwidth(c); +} + +int nullPointer_wcsnlen(const wchar_t *s, size_t n) +{ + // cppcheck-suppress nullPointer + (void)wcsnlen(NULL, n); + // No warning is expected + return wcsnlen(s, n); +} + +size_t bufferAccessOutOfBounds_wcsnlen(void) // #10997 +{ + const wchar_t buf[2]={L'4',L'2'}; + size_t len = wcsnlen(buf,2); + // TODO cppcheck-suppress bufferAccessOutOfBounds + len+=wcsnlen(buf,3); + return len; +} + +int nullPointer_gethostname(char *s, size_t n) +{ + // cppcheck-suppress nullPointer + (void)gethostname(NULL, n); + // No warning is expected + return gethostname(s, n); +} + +int nullPointer_wcswidth(const wchar_t *s, size_t n) +{ + // cppcheck-suppress nullPointer + (void)wcswidth(NULL, n); + // No warning is expected + return wcswidth(s, n); +} + +int nullPointer_aio_cancel(int fd, struct aiocb *aiocbp) +{ + // No warning is expected + (void)aio_cancel(fd, NULL); + // No warning is expected + return aio_cancel(fd, aiocbp); +} + +int nullPointer_aio_fsync(int op, struct aiocb *aiocbp) +{ + // cppcheck-suppress nullPointer + (void)aio_fsync(op, NULL); + // No warning is expected + return aio_fsync(op, aiocbp); +} + +ssize_t nullPointer_aio_return(struct aiocb *aiocbp) +{ + // cppcheck-suppress nullPointer + (void)aio_return(NULL); + // No warning is expected + return aio_return(aiocbp); +} + +int nullPointer_aio_error(const struct aiocb *aiocbp) +{ + // cppcheck-suppress nullPointer + (void)aio_error(NULL); + // No warning is expected + return aio_error(aiocbp); +} + +int nullPointer_aio_read(struct aiocb *aiocbp) +{ + // cppcheck-suppress nullPointer + (void)aio_read(NULL); + // No warning is expected + return aio_read(aiocbp); +} + +int nullPointer_aio_write(struct aiocb *aiocbp) +{ + // cppcheck-suppress nullPointer + (void)aio_write(NULL); + // No warning is expected + return aio_write(aiocbp); +} + +int nullPointer_aio_suspend(const struct aiocb *const aiocb_list[], int nitems, const struct timespec *restrict timeout) +{ + // cppcheck-suppress nullPointer + (void)aio_suspend(NULL, nitems, timeout); + // No warning is expected + return aio_suspend(aiocb_list, nitems, timeout); +} + +#ifdef __linux__ +// Note: Since glibc 2.28, this function symbol is no longer available to newly linked applications. +void invalidFunctionArg_llseek(int fd, loff_t offset, int origin) +{ + // cppcheck-suppress llseekCalled + // cppcheck-suppress invalidFunctionArg + (void)llseek(-1, offset, SEEK_SET); + // cppcheck-suppress llseekCalled + // cppcheck-suppress invalidFunctionArg + (void)llseek(fd, offset, -1); + // cppcheck-suppress llseekCalled + // cppcheck-suppress invalidFunctionArg + (void)llseek(fd, offset, 3); + // cppcheck-suppress llseekCalled + // cppcheck-suppress invalidFunctionArg + (void)llseek(fd, offset, 42+SEEK_SET); + // cppcheck-suppress llseekCalled + // cppcheck-suppress invalidFunctionArg + (void)llseek(fd, offset, SEEK_SET+42); + // No invalidFunctionArg warning is expected for + // cppcheck-suppress llseekCalled + (void)llseek(0, offset, origin); + // cppcheck-suppress llseekCalled + (void)llseek(fd, offset, origin); + // cppcheck-suppress llseekCalled + (void)llseek(fd, offset, SEEK_SET); + // cppcheck-suppress llseekCalled + (void)llseek(fd, offset, SEEK_CUR); + // cppcheck-suppress llseekCalled + (void)llseek(fd, offset, SEEK_END); +} +#endif + +void invalidFunctionArg_lseek64(int fd, off_t offset, int origin) +{ + // cppcheck-suppress invalidFunctionArg + (void)lseek64(-1, offset, SEEK_SET); + // cppcheck-suppress invalidFunctionArg + (void)lseek64(fd, offset, -1); + // cppcheck-suppress invalidFunctionArg + (void)lseek64(fd, offset, 3); + // cppcheck-suppress invalidFunctionArg + (void)lseek64(fd, offset, 42+SEEK_SET); + // cppcheck-suppress invalidFunctionArg + (void)lseek64(fd, offset, SEEK_SET+42); + // No warning is expected for + (void)lseek64(0, offset, origin); + (void)lseek64(fd, offset, origin); + (void)lseek64(fd, offset, SEEK_SET); + (void)lseek64(fd, offset, SEEK_CUR); + (void)lseek64(fd, offset, SEEK_END); +} + +void invalidFunctionArg_lseek(int fd, off_t offset, int origin) +{ + // cppcheck-suppress invalidFunctionArg + (void)lseek(-1, offset, SEEK_SET); + // cppcheck-suppress invalidFunctionArg + (void)lseek(fd, offset, -1); + // cppcheck-suppress invalidFunctionArg + (void)lseek(fd, offset, 3); + // cppcheck-suppress invalidFunctionArg + (void)lseek(fd, offset, 42+SEEK_SET); + // cppcheck-suppress invalidFunctionArg + (void)lseek(fd, offset, SEEK_SET+42); + // No warning is expected for + (void)lseek(0, offset, origin); + (void)lseek(fd, offset, origin); + (void)lseek(fd, offset, SEEK_SET); + (void)lseek(fd, offset, SEEK_CUR); + (void)lseek(fd, offset, SEEK_END); +} + +void invalidFunctionArg_fseeko(FILE* stream, off_t offset, int origin) +{ + // cppcheck-suppress invalidFunctionArg + (void)fseeko(stream, offset, -1); + // cppcheck-suppress invalidFunctionArg + (void)fseeko(stream, offset, 3); + // cppcheck-suppress invalidFunctionArg + (void)fseeko(stream, offset, 42+SEEK_SET); + // cppcheck-suppress invalidFunctionArg + (void)fseeko(stream, offset, SEEK_SET+42); + // No warning is expected for + (void)fseeko(stream, offset, origin); + (void)fseeko(stream, offset, SEEK_SET); + (void)fseeko(stream, offset, SEEK_CUR); + (void)fseeko(stream, offset, SEEK_END); +} + +wchar_t *nullPointer_wcpncpy(wchar_t *dest, const wchar_t *src, size_t n) +{ + // cppcheck-suppress nullPointer + (void)wcpncpy(NULL, src, n); + // cppcheck-suppress nullPointer + (void)wcpncpy(dest, NULL, n); + return wcpncpy(dest, src, n); +} + +int nullPointer_utimes(const char *path, const struct timeval times[2]) +{ + // cppcheck-suppress nullPointer + // cppcheck-suppress utimesCalled + (void)utimes(NULL, times); + // cppcheck-suppress utimesCalled + return utimes(path, times); +} + +char * overlappingWriteFunction_stpcpy(char *src, char *dest) +{ + // No warning shall be shown: + (void) stpcpy(dest, src); + // cppcheck-suppress overlappingWriteFunction + return stpcpy(src, src); +} + +int nullPointer_strcasecmp(const char *a, const char *b) +{ + // No warning shall be shown: + (void) strcasecmp(a, b); + // cppcheck-suppress nullPointer + (void) strcasecmp(a, NULL); + // cppcheck-suppress nullPointer + return strcasecmp(NULL, b); +} + +int nullPointer_strncasecmp(const char *a, const char *b, size_t n) +{ + // No warning shall be shown: + (void) strncasecmp(a, b, n); + // cppcheck-suppress nullPointer + (void) strncasecmp(a, NULL, n); + // cppcheck-suppress nullPointer + return strncasecmp(NULL, b, n); +} + +int nullPointer_bcmp(const void *a, const void *b, size_t n) +{ + // No nullPointer warning shall be shown: + // cppcheck-suppress bcmpCalled + (void) bcmp(a, b, n); + // cppcheck-suppress nullPointer + // cppcheck-suppress bcmpCalled + (void) bcmp(a, NULL, n); + // cppcheck-suppress nullPointer + // cppcheck-suppress bcmpCalled + return bcmp(NULL, b, n); +} + +void nullPointer_bzero(void *s, size_t n) +{ + // cppcheck-suppress nullPointer + // cppcheck-suppress bzeroCalled + bzero(NULL,n); + // No nullPointer-warning shall be shown: + // cppcheck-suppress bzeroCalled + bzero(s,n); +} + +void bufferAccessOutOfBounds_bzero(void *s, size_t n) +{ + char buf[42]; + // cppcheck-suppress bufferAccessOutOfBounds + // cppcheck-suppress bzeroCalled + bzero(buf,43); + // cppcheck-suppress bzeroCalled + bzero(buf,42); + // No nullPointer-warning shall be shown: + // cppcheck-suppress bzeroCalled + bzero(s,n); +} + +size_t bufferAccessOutOfBounds_strnlen(const char *s, size_t maxlen) +{ + const char buf[2]={'4','2'}; + size_t len = strnlen(buf,2); + // cppcheck-suppress bufferAccessOutOfBounds + len+=strnlen(buf,3); + return len; +} + +void bufferAccessOutOfBounds_wcpncpy() +{ + wchar_t s[16]; + wcpncpy(s, L"abc", 16); + // cppcheck-suppress bufferAccessOutOfBounds + wcpncpy(s, L"abc", 17); +} + +size_t nullPointer_strnlen(const char *s, size_t maxlen) +{ + // No warning shall be shown: + (void) strnlen(s, maxlen); + // cppcheck-suppress nullPointer + return strnlen(NULL, maxlen); +} + +char * nullPointer_stpcpy(const char *src, char *dest) +{ + // No warning shall be shown: + (void) stpcpy(dest, src); + // cppcheck-suppress nullPointer + (void) stpcpy(dest, NULL); + // cppcheck-suppress nullPointer + return stpcpy(NULL, src); +} + +char * nullPointer_strsep(char **stringptr, char *delim) +{ + // No warning shall be shown: + (void) strsep(stringptr, delim); + // cppcheck-suppress nullPointer + (void) strsep(stringptr, NULL); + // cppcheck-suppress nullPointer + return strsep(NULL, delim); +} + +void overlappingWriteFunction_bcopy(char *buf, const size_t count) +{ + // No warning shall be shown: + // cppcheck-suppress bcopyCalled + bcopy(&buf[0], &buf[3], count); // size is not known + // cppcheck-suppress bcopyCalled + bcopy(&buf[0], &buf[3], 3U); // no-overlap + // cppcheck-suppress bcopyCalled + bcopy(&buf[0], &buf[3], 4U); // The result is correct, even when both areas overlap. +} + +void nullPointer_bcopy(const void *src, void *dest, size_t n) +{ + // No warning shall be shown: + // cppcheck-suppress bcopyCalled + bcopy(src, dest, n); + // cppcheck-suppress bcopyCalled + // cppcheck-suppress nullPointer + bcopy(NULL, dest, n); + // cppcheck-suppress bcopyCalled + // cppcheck-suppress nullPointer + bcopy(src, NULL, n); +} + +void overlappingWriteFunction_memccpy(const unsigned char *src, unsigned char *dest, int c, size_t count) +{ + // No warning shall be shown: + (void)memccpy(dest, src, c, count); + (void)memccpy(dest, src, 42, count); + // cppcheck-suppress overlappingWriteFunction + (void)memccpy(dest, dest, c, 4); + // cppcheck-suppress overlappingWriteFunction + (void)memccpy(dest, dest+3, c, 4); +} + +void overlappingWriteFunction_stpncpy(char *src, char *dest, ssize_t n) +{ + // No warning shall be shown: + (void) stpncpy(dest, src, n); + // cppcheck-suppress overlappingWriteFunction + (void)stpncpy(src, src+3, 4); +} + +wchar_t* overlappingWriteFunction_wcpncpy(wchar_t *src, wchar_t *dest, ssize_t n) +{ + // No warning shall be shown: + (void) wcpncpy(dest, src, n); + // cppcheck-suppress overlappingWriteFunction + return wcpncpy(src, src+3, 4); +} + +void overlappingWriteFunction_swab(char *src, char *dest, ssize_t n) +{ + // No warning shall be shown: + swab(src, dest, n); + // cppcheck-suppress overlappingWriteFunction + swab(src, src+3, 4); +} + +void bufferAccessOutOfBounds_swab(char *src, char *dest, ssize_t n) +{ + // No warning shall be shown: + swab(dest, src, n); + const char srcBuf[42] = {0}; + char destBuf[42] = {0}; + swab(srcBuf, dest, 42); + // cppcheck-suppress bufferAccessOutOfBounds + swab(srcBuf, dest, 43); + swab(src, destBuf, 42); + // cppcheck-suppress bufferAccessOutOfBounds + swab(src, destBuf, 43); +} + +void nullPointer_swab(char *src, char *dest, ssize_t n) +{ + // No warning shall be shown: + swab(dest, src, n); + // cppcheck-suppress nullPointer + swab(NULL, dest, n); + // cppcheck-suppress nullPointer + swab(src, NULL, n); +} + +bool invalidFunctionArgBool_isascii(bool b, int c) +{ + // cppcheck-suppress invalidFunctionArgBool + (void)isascii(b); + // cppcheck-suppress invalidFunctionArgBool + return isascii(c != 0); +} + +void uninitvar_putenv(char * envstr) +{ + // No warning is expected + (void)putenv(envstr); + + char * p; + // cppcheck-suppress uninitvar + (void)putenv(p); +} + +void nullPointer_putenv(char * envstr) +{ + // No warning is expected + (void)putenv(envstr); + + char * p=NULL; + // cppcheck-suppress nullPointer + (void)putenv(p); +} + +void memleak_scandir(void) +{ + struct dirent **namelist; + int n = scandir(".", &namelist, NULL, alphasort); + if (n == -1) { + return; + } + + // http://man7.org/linux/man-pages/man3/scandir.3.html + /* The scandir() function scans the directory dirp, calling filter() on + each directory entry. Entries for which filter() returns nonzero are + stored in strings allocated via malloc(3), sorted using qsort(3) with + the comparison function compar(), and collected in array namelist + which is allocated via malloc(3). If filter is NULL, all entries are + selected.*/ + + // cppcheck-suppress memleak +} + +void no_memleak_scandir(void) +{ + struct dirent **namelist; + int n = scandir(".", &namelist, NULL, alphasort); + if (n == -1) { + return; + } + while (n--) { + free(namelist[n]); + } + free(namelist); +} + +void validCode(va_list valist_arg1, va_list valist_arg2) +{ + void *ptr; + if (posix_memalign(&ptr, sizeof(void *), sizeof(void *)) == 0) + free(ptr); + // cppcheck-suppress valueFlowBailoutIncompleteVar + syslog(LOG_ERR, "err %u", 0U); + syslog(LOG_WARNING, "warn %d %d", 5, 1); + vsyslog(LOG_EMERG, "emerg %d", valist_arg1); + vsyslog(LOG_INFO, "test %s %d %p", valist_arg2); + + void* handle = dlopen("/lib.so", RTLD_NOW); + if (handle) { + dlclose(handle); + } +} + +typedef struct { + size_t N; + int* data; +} S_memalign; + +S_memalign* posix_memalign_memleak(size_t n) { // #12248 + S_memalign* s = malloc(sizeof(*s)); + s->N = n; + if (0 != posix_memalign((void**)&s->data, 16, n * sizeof(int))) { + free(s); + return NULL; + } + memset(s->data, 0, n * sizeof(int)); + return s; +} + +ssize_t nullPointer_send(int socket, const void *buf, size_t len, int flags) +{ + // cppcheck-suppress nullPointer + (void) send(socket, NULL, len, flags); + return send(socket, buf, len, flags); +} + +ssize_t nullPointer_sendto(int socket, const void *message, size_t length, + int flags, const struct sockaddr *dest_addr, + socklen_t dest_len) +{ + // cppcheck-suppress nullPointer + (void) sendto(socket, NULL, length, flags, dest_addr, dest_len); + (void) sendto(socket, message, length, flags, NULL, dest_len); + return sendto(socket, message, length, flags, dest_addr, dest_len); +} + +void bufferAccessOutOfBounds(int fd) +{ + char a[5]; + read(fd,a,5); + // cppcheck-suppress bufferAccessOutOfBounds + read(fd,a,6); + write(fd,a,5); + // cppcheck-suppress bufferAccessOutOfBounds + write(fd,a,6); + recv(fd,a,5,0); + // cppcheck-suppress bufferAccessOutOfBounds + recv(fd,a,6,0); + recvfrom(fd,a,5,0,0x0,0x0); + // cppcheck-suppress bufferAccessOutOfBounds + recvfrom(fd,a,6,0,0x0,0x0); + send(fd,a,5,0); + // cppcheck-suppress bufferAccessOutOfBounds + send(fd,a,6,0); + sendto(fd,a,5,0,0x0,0x0); + // cppcheck-suppress bufferAccessOutOfBounds + sendto(fd,a,6,0,0x0,0x0); + // cppcheck-suppress constStatement + 0; + readlink("path", a, 5); + // cppcheck-suppress bufferAccessOutOfBounds + readlink("path", a, 6); + readlinkat(1, "path", a, 5); + // cppcheck-suppress bufferAccessOutOfBounds + readlinkat(1, "path", a, 6); + // This is valid + gethostname(a, 5); + // cppcheck-suppress bufferAccessOutOfBounds + gethostname(a, 6); +} + +void nullPointer(char *p, int fd, pthread_mutex_t mutex) +{ + // cppcheck-suppress ignoredReturnValue + isatty(0); + mkdir(p, 0); + getcwd(0, 0); + // cppcheck-suppress nullPointer + // cppcheck-suppress readdirCalled + readdir(0); + // cppcheck-suppress nullPointer + // cppcheck-suppress utimeCalled + utime(NULL, NULL); + // not implemented yet: cppcheck-suppress nullPointer + read(fd,NULL,42); + read(fd,NULL,0); + // not implemented yet: cppcheck-suppress nullPointer + write(fd,NULL,42); + write(fd,NULL,0); + // cppcheck-suppress leakReturnValNotUsed + // cppcheck-suppress nullPointer + open(NULL, 0); + // cppcheck-suppress leakReturnValNotUsed + // cppcheck-suppress nullPointer + open(NULL, 0, 0); + // cppcheck-suppress unreadVariable + // cppcheck-suppress nullPointer + int ret = access(NULL, 0); + // cppcheck-suppress leakReturnValNotUsed + // cppcheck-suppress nullPointer + fdopen(fd, NULL); + // cppcheck-suppress strtokCalled + // cppcheck-suppress nullPointer + strtok(p, NULL); + + // cppcheck-suppress nullPointer + pthread_mutex_init(NULL, NULL); + // Second argument can be NULL + pthread_mutex_init(&mutex, NULL); + // cppcheck-suppress nullPointer + pthread_mutex_destroy(NULL); + // cppcheck-suppress nullPointer + pthread_mutex_lock(NULL); + // cppcheck-suppress nullPointer + (void)pthread_mutex_trylock(NULL); + // cppcheck-suppress nullPointer + pthread_mutex_unlock(NULL); +} + +// cppcheck-suppress constParameterCallback +void* f_returns_NULL(void* arg) +{ + return NULL; +} + +void nullPointer_pthread_create() // #12396 +{ + pthread_t thread; + pthread_create(&thread, NULL, (void* (*)(void*))f_returns_NULL, NULL); +} + +void memleak_getaddrinfo() // #6994 +{ + struct addrinfo * res=NULL; + getaddrinfo("node", NULL, NULL, &res); + freeaddrinfo(res); + getaddrinfo("node", NULL, NULL, &res); + // cppcheck-suppress memleak +} + +void memleak_mmap(int fd) +{ + // cppcheck-suppress [unusedAllocatedMemory, unreadVariable, constVariablePointer] + void *addr = mmap(NULL, 255, PROT_NONE, MAP_PRIVATE, fd, 0); + // cppcheck-suppress memleak +} + +void * memleak_mmap2() // #8327 +{ + void * data = mmap(NULL, 10, PROT_READ, MAP_PRIVATE, 1, 0); + if (data != MAP_FAILED) + return data; + return NULL; +} + +void memleak_getline() { // #11043 + char *line = NULL; + size_t size = 0; + // cppcheck-suppress valueFlowBailoutIncompleteVar + getline(&line, &size, stdin); + // cppcheck-suppress memleak + line = NULL; + getline(&line, &size, stdin); + // cppcheck-suppress memleak + line = NULL; +} + +void memleak_getline_array(FILE* stream) { // #12498 + char* a[2] = { 0 }; + size_t n; + getline(&a[0], &n, stream); + getline(&a[1], &n, stream); + free(a[0]); + free(a[1]); +} + +void memleak_getdelim(int delim) { + char *line = NULL; + size_t size = 0; + // cppcheck-suppress valueFlowBailoutIncompleteVar + getdelim(&line, &size, delim, stdin); + // cppcheck-suppress memleak + line = NULL; + getdelim(&line, &size, delim, stdin); + // cppcheck-suppress memleak + line = NULL; +} + +void memleak_getdelim_array(FILE* stream, int delim) { + char* a[2] = { 0 }; + size_t n; + getdelim(&a[0], &n, delim, stream); + getdelim(&a[1], &n, delim, stream); + free(a[0]); + free(a[1]); +} + +void * identicalCondition_mmap(int fd, size_t size) // #9940 +{ + // cppcheck-suppress valueFlowBailoutIncompleteVar + void* buffer = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (buffer == MAP_FAILED) { + return NULL; + } + return buffer; +} + +int munmap_no_double_free(int tofd, // #11396 + int fromfd, + size_t len) +{ + int rc; + // cppcheck-suppress valueFlowBailoutIncompleteVar + const void* fptr = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fromfd,(off_t)0); + if (fptr == MAP_FAILED) { + return -1; + } + + void* tptr = mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,tofd,(off_t)0); + if (tptr == MAP_FAILED) { + // cppcheck-suppress memleak + return -1; + } + + memcpy(tptr,fptr,len); + + if ((rc = munmap(fptr,len)) != 0) { + // cppcheck-suppress memleak + return -1; + } + + if ((rc = munmap(tptr,len)) != 0) { + return -1; + } + + return rc; +} + +void resourceLeak_fdopen(int fd) +{ + // cppcheck-suppress [unreadVariable, constVariablePointer] + FILE *f = fdopen(fd, "r"); + // cppcheck-suppress resourceLeak +} + +void resourceLeak_fdopen2(const char* fn) // #2767 +{ + // cppcheck-suppress valueFlowBailoutIncompleteVar + int fi = open(fn, O_RDONLY); + FILE* fd = fdopen(fi, "r"); + fclose(fd); +} + +void resourceLeak_mkstemp(char *template) +{ + // cppcheck-suppress unreadVariable + int fp = mkstemp(template); + // cppcheck-suppress resourceLeak +} + +void no_resourceLeak_mkstemp_01(char *template) +{ + int fp = mkstemp(template); + close(fp); +} + +int no_resourceLeak_mkstemp_02(char *template) +{ + return mkstemp(template); +} + +void resourceLeak_fdopendir(int fd) +{ + // cppcheck-suppress [unreadVariable, constVariablePointer] + DIR* leak1 = fdopendir(fd); + // cppcheck-suppress resourceLeak +} + +void resourceLeak_opendir(void) +{ + // cppcheck-suppress [unreadVariable, constVariablePointer] + DIR* leak1 = opendir("abc"); + // cppcheck-suppress resourceLeak +} + +void resourceLeak_socket(void) +{ + // cppcheck-suppress unreadVariable + int s = socket(AF_INET, SOCK_STREAM, 0); + // cppcheck-suppress resourceLeak +} + +void resourceLeak_open1(void) +{ + // cppcheck-suppress [unreadVariable,valueFlowBailoutIncompleteVar] + int fd = open("file", O_RDWR | O_CREAT); + // cppcheck-suppress resourceLeak +} + +void resourceLeak_open2(void) +{ + // cppcheck-suppress [unreadVariable,valueFlowBailoutIncompleteVar] + int fd = open("file", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + // cppcheck-suppress resourceLeak +} + +void noleak(int x, int y, int z) +{ + DIR *p1 = fdopendir(x); + closedir(p1); + DIR *p2 = opendir("abc"); + closedir(p2); + int s = socket(AF_INET,SOCK_STREAM,0); + close(s); + // cppcheck-suppress valueFlowBailoutIncompleteVar + int fd1 = open("a", O_RDWR | O_CREAT); + close(fd1); + int fd2 = open("a", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + close(fd2); +} + + +// unused return value + +void ignoredReturnValue(const void *addr, int fd) +{ + // cppcheck-suppress leakReturnValNotUsed + mmap(addr, 255, PROT_NONE, MAP_PRIVATE, fd, 0); + // cppcheck-suppress ignoredReturnValue + getuid(); + // cppcheck-suppress ignoredReturnValue + access("filename", 1); + // no ignoredReturnValue shall be shown for + setuid(42); +} + + +// valid range + +void invalidFunctionArg() +{ + // cppcheck-suppress invalidFunctionArg + // cppcheck-suppress usleepCalled + usleep(-1); + // cppcheck-suppress usleepCalled + usleep(0); + // cppcheck-suppress usleepCalled + usleep(999999); + // cppcheck-suppress invalidFunctionArg + // cppcheck-suppress usleepCalled + usleep(1000000); +} + +void invalidFunctionArg_close(int fd) +{ + if (fd < 0) { + // cppcheck-suppress invalidFunctionArg + (void)close(fd); + } +} + +void uninitvar(int fd) +{ + int x1, x2, x3, x4; + char buf[2]; + int decimal, sign; + double d; + const void *p; + pthread_mutex_t mutex, mutex1, mutex2, mutex3; + // cppcheck-suppress uninitvar + write(x1,"ab",2); + // TODO cppcheck-suppress uninitvar + write(fd,buf,2); // #6325 + // cppcheck-suppress uninitvar + write(fd,"ab",x2); + // cppcheck-suppress uninitvar + write(fd,p,2); + + + /* int regcomp(regex_t *restrict preg, const char *restrict pattern, int cflags); */ + regex_t reg; + const char * pattern; + int cflags1, cflags2; + // cppcheck-suppress uninitvar + regcomp(®, pattern, cflags1); + pattern=""; + // cppcheck-suppress uninitvar + regcomp(®, pattern, cflags2); + regerror(0, ®, 0, 0); +#ifndef __CYGWIN__ + // cppcheck-suppress [uninitvar, unreadVariable, ecvtCalled, constVariablePointer] + char *buffer = ecvt(d, 11, &decimal, &sign); +#endif + // cppcheck-suppress gcvtCalled + gcvt(3.141, 2, buf); + + const char *filename1, *filename2; + const struct utimbuf *times; + // cppcheck-suppress uninitvar + // cppcheck-suppress utimeCalled + utime(filename1, times); + // cppcheck-suppress constVariable + struct timeval times1[2]; + // cppcheck-suppress uninitvar + // cppcheck-suppress utimeCalled + utime(filename2, times1); + + // cppcheck-suppress unreadVariable + // cppcheck-suppress uninitvar + int access_ret = access("file", x3); + + // cppcheck-suppress leakReturnValNotUsed + // cppcheck-suppress uninitvar + fdopen(x4, "rw"); + + char *strtok_arg1; + // cppcheck-suppress strtokCalled + // cppcheck-suppress uninitvar + strtok(strtok_arg1, ";"); + + // cppcheck-suppress uninitvar + pthread_mutex_lock(&mutex1); + // cppcheck-suppress uninitvar + (void)pthread_mutex_trylock(&mutex2); + // cppcheck-suppress uninitvar + pthread_mutex_unlock(&mutex3); + // after initialization it must be OK to call lock, trylock and unlock for this mutex + pthread_mutex_init(&mutex, NULL); + pthread_mutex_lock(&mutex); + (void)pthread_mutex_trylock(&mutex); + pthread_mutex_unlock(&mutex); +} + +void uninitvar_getcwd(void) +{ + char *buf; + size_t size; + // cppcheck-suppress uninitvar + (void)getcwd(buf,size); +} + + +void uninitvar_types(void) +{ + // cppcheck-suppress unassignedVariable + blkcnt_t b; + // cppcheck-suppress [uninitvar,constStatement] + b + 1; + + struct dirent d; + // cppcheck-suppress constStatement - TODO: uninitvar + d.d_ino + 1; +} + +void timet_h(const struct timespec* ptp1) +{ + clockid_t clk_id1, clk_id2, clk_id3; + // cppcheck-suppress constVariablePointer + struct timespec* ptp; + // cppcheck-suppress [uninitvar,valueFlowBailoutIncompleteVar] + clock_settime(CLOCK_REALTIME, ptp); + // cppcheck-suppress uninitvar + clock_settime(clk_id1, ptp); + // cppcheck-suppress uninitvar + clock_settime(clk_id2, ptp1); + + struct timespec tp; + // FIXME cppcheck-suppress uninitvar + clock_settime(CLOCK_REALTIME, &tp); // #6577 - false negative + // cppcheck-suppress uninitvar + clock_settime(clk_id3, &tp); + + time_t clock = time(0); + char buf[26]; + // cppcheck-suppress ctime_rCalled + ctime_r(&clock, buf); +} + +void dl(const char* libname, const char* func) +{ + void* lib = dlopen(libname, RTLD_NOW); + // cppcheck-suppress redundantInitialization + // cppcheck-suppress resourceLeak + lib = dlopen(libname, RTLD_LAZY); + const char* funcname; + // cppcheck-suppress [uninitvar, unreadVariable, constVariablePointer] + void* sym = dlsym(lib, funcname); + // cppcheck-suppress ignoredReturnValue + dlsym(lib, "foo"); + // cppcheck-suppress unassignedVariable + void* uninit; + // cppcheck-suppress uninitvar + dlclose(uninit); + // cppcheck-suppress resourceLeak +} + +void asctime_r_test(const struct tm * tm, char * bufSizeUnknown) +{ + struct tm tm_uninit_data; + const struct tm * tm_uninit_pointer; + char bufSize5[5]; + char bufSize25[25]; + char bufSize26[26]; + char bufSize100[100]; + + // cppcheck-suppress asctime_rCalled + // cppcheck-suppress bufferAccessOutOfBounds + asctime_r(tm, bufSize5); + // cppcheck-suppress asctime_rCalled + // cppcheck-suppress bufferAccessOutOfBounds + asctime_r(tm, bufSize25); + // cppcheck-suppress asctime_rCalled + asctime_r(tm, bufSize26); + // cppcheck-suppress asctime_rCalled + asctime_r(tm, bufSize100); + + // cppcheck-suppress asctime_rCalled + // cppcheck-suppress uninitvar + asctime_r(&tm_uninit_data, bufSize100); + // cppcheck-suppress asctime_rCalled + // cppcheck-suppress uninitvar + asctime_r(tm_uninit_pointer, bufSize100); + + // cppcheck-suppress asctime_rCalled + asctime_r(tm, bufSizeUnknown); +} + +void ctime_r_test(const time_t * timep, char * bufSizeUnknown) +{ + time_t time_t_uninit_data; + const time_t * time_t_uninit_pointer; + char bufSize5[5]; + char bufSize25[25]; + char bufSize26[26]; + char bufSize100[100]; + + // cppcheck-suppress ctime_rCalled + // cppcheck-suppress bufferAccessOutOfBounds + ctime_r(timep, bufSize5); + // cppcheck-suppress ctime_rCalled + // cppcheck-suppress bufferAccessOutOfBounds + ctime_r(timep, bufSize25); + // cppcheck-suppress ctime_rCalled + ctime_r(timep, bufSize26); + // cppcheck-suppress ctime_rCalled + ctime_r(timep, bufSize100); + + // cppcheck-suppress ctime_rCalled + // cppcheck-suppress uninitvar + ctime_r(&time_t_uninit_data, bufSize100); + // cppcheck-suppress ctime_rCalled + // cppcheck-suppress uninitvar + ctime_r(time_t_uninit_pointer, bufSize100); + + // cppcheck-suppress ctime_rCalled + ctime_r(timep, bufSizeUnknown); +} diff --git a/cppcheck-2.14.0/test/cfg/python.c b/cppcheck-2.14.0/test/cfg/python.c new file mode 100644 index 00000000..12771c5d --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/python.c @@ -0,0 +1,64 @@ + +// Test library configuration for python.cfg +// +// Usage: +// $ cppcheck --check-library --library=python --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/python.c +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +#define PY_SSIZE_T_CLEAN +#include // should be the first include +#include + +void validCode(PyObject * pPyObjArg) +{ + PyObject * pPyObjNULL = NULL; + Py_Initialize(); + Py_INCREF(pPyObjArg); + Py_DECREF(pPyObjArg); + Py_XINCREF(pPyObjArg); + Py_XINCREF(pPyObjNULL); + Py_XDECREF(pPyObjArg); + Py_XDECREF(pPyObjNULL); + Py_CLEAR(pPyObjArg); + Py_CLEAR(pPyObjNULL); + (void)PyErr_NewException("text", NULL, NULL); + + // cppcheck-suppress unusedAllocatedMemory + char * pBuf1 = PyMem_Malloc(5); + PyMem_Free(pBuf1); + // cppcheck-suppress unusedAllocatedMemory + int * pIntBuf1 = PyMem_New(int, 10); + PyMem_Free(pIntBuf1); +} + +void nullPointer() +{ + // cppcheck-suppress nullPointer + Py_INCREF(NULL); + // cppcheck-suppress nullPointer + Py_DECREF(NULL); +} + +void PyMem_Malloc_memleak() +{ + const char * pBuf1 = PyMem_Malloc(1); + printf("%p", pBuf1); + // cppcheck-suppress memleak +} + +void PyMem_Malloc_mismatchAllocDealloc() +{ + // cppcheck-suppress unusedAllocatedMemory + char * pBuf1 = PyMem_Malloc(10); + // cppcheck-suppress mismatchAllocDealloc + free(pBuf1); +} + +void PyMem_New_memleak() +{ + const char * pBuf1 = PyMem_New(char, 5); + printf("%p", pBuf1); + // cppcheck-suppress memleak +} diff --git a/cppcheck-2.14.0/test/cfg/qt.cpp b/cppcheck-2.14.0/test/cfg/qt.cpp new file mode 100644 index 00000000..d89fc4a8 --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/qt.cpp @@ -0,0 +1,777 @@ + +// Test library configuration for qt.cfg +// +// Usage: +// $ cppcheck --check-library --library=qt --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/qt.cpp +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +// cppcheck-suppress-file valueFlowBailout + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int ignoredReturnValue_QSize_height(const QSize &s) +{ + // cppcheck-suppress ignoredReturnValue + s.height(); + return s.height(); +} + +int ignoredReturnValue_QSize_width(const QSize &s) +{ + // cppcheck-suppress ignoredReturnValue + s.width(); + return s.width(); +} + +void unusedVariable_QTransform() +{ + // cppcheck-suppress unusedVariable + QTransform a; +} + +void unreadVariable_QRegion(const int x, const QRegion::RegionType type, const QPolygon &polygon, const QBitmap &bm, const QRegion ®ion, const Qt::FillRule fillRule) +{ + // cppcheck-suppress unusedVariable + QRegion a; + // cppcheck-suppress unreadVariable + QRegion b{}; + // cppcheck-suppress unreadVariable + QRegion c{x,x,x,x}; + // cppcheck-suppress unreadVariable + QRegion d{x,x,x,x, type}; + // cppcheck-suppress unreadVariable + QRegion e{polygon, fillRule}; + // cppcheck-suppress unreadVariable + QRegion f{bm}; + // cppcheck-suppress unreadVariable + QRegion g{region}; +} + +void unreadVariable_QPoint(const QPoint &s) +{ + // cppcheck-suppress unusedVariable + QPoint a; + // cppcheck-suppress unreadVariable + QPoint b{}; + // cppcheck-suppress unreadVariable + QPoint c{4, 2}; + // cppcheck-suppress unreadVariable + QPoint d(4, 2); + // cppcheck-suppress unreadVariable + QPoint e(s); +} + +void unreadVariable_QPointF(const QPointF &s) +{ + // cppcheck-suppress unusedVariable + QPointF a; + // cppcheck-suppress unreadVariable + QPointF b{}; + // cppcheck-suppress unreadVariable + QPointF c{4.2, 4.2}; + // cppcheck-suppress unreadVariable + QPointF d(4.2, 4.2); + // cppcheck-suppress unreadVariable + QPointF e(s); +} + +void unreadVariable_QSizeF(const QSize &s) +{ + // cppcheck-suppress unusedVariable + QSizeF a; + // cppcheck-suppress unreadVariable + QSizeF b{}; + // cppcheck-suppress unreadVariable + QSizeF c{4.2, 4.2}; + // cppcheck-suppress unreadVariable + QSizeF d(4.2, 4.2); + // cppcheck-suppress unreadVariable + QSizeF e(s); +} + +void unreadVariable_QSize(const QSize &s) +{ + // cppcheck-suppress unusedVariable + QSize a; + // cppcheck-suppress unreadVariable + QSize b{}; + // cppcheck-suppress unreadVariable + QSize c{4, 2}; + // cppcheck-suppress unreadVariable + QSize d(4, 2); + // cppcheck-suppress unreadVariable + QSize e(s); +} + +void unreadVariable_QRect(const QPoint &topLeft, const QSize &size, const QPoint &bottomRight, const int x) { + // cppcheck-suppress unusedVariable + QRect a; + // cppcheck-suppress unreadVariable + QRect b{}; + // cppcheck-suppress unreadVariable + QRect c(0, 0, 100, 50); + // cppcheck-suppress unreadVariable + QRect d(x, x, x, x); + // cppcheck-suppress unreadVariable + QRect e(topLeft, size); + // cppcheck-suppress unreadVariable + QRect f(topLeft, bottomRight); +} + +void unreadVariable_QRectF(const QPointF &topLeft, const QSizeF &size, const QPointF &bottomRight, const QRectF &rect, const qreal x) { + // cppcheck-suppress unusedVariable + QRectF a; + // cppcheck-suppress unreadVariable + QRectF b{}; + // cppcheck-suppress unreadVariable + QRectF c(0.0, 0.0, 100.0, 50.0); + // cppcheck-suppress unreadVariable + QRectF d(x, x, x, x); + // cppcheck-suppress unreadVariable + QRectF e(topLeft, size); + // cppcheck-suppress unreadVariable + QRectF f(topLeft, bottomRight); + // cppcheck-suppress unreadVariable + QRectF g(rect); +} + +void QString1(QString s) +{ + for (int i = 0; i <= s.size(); ++i) { + // cppcheck-suppress stlOutOfBounds + s[i] = 'x'; + } +} + +bool QString2() +{ + QString s; + // cppcheck-suppress knownConditionTrueFalse + return s.size(); +} + +QString::iterator QString3() +{ + QString qstring1; + QString qstring2; + // cppcheck-suppress mismatchingContainers + for (QString::iterator it = qstring1.begin(); it != qstring2.end(); ++it) + {} + + QString::iterator it = qstring1.begin(); + // cppcheck-suppress returnDanglingLifetime + return it; +} + +void QString4() +{ + // cppcheck-suppress unusedVariable + QString qs; +} + +// cppcheck-suppress passedByValue +bool QString5(QString s) { // #10710 + return s.isEmpty(); +} + +// cppcheck-suppress passedByValue +QStringList QString6(QString s) { + return QStringList{ "*" + s + "*" }; +} + +// cppcheck-suppress passedByValue +bool QString7(QString s, const QString& l) { + return l.startsWith(s); +} + +namespace NTestStd // #12355 +{ + using namespace std; + QString QString_std(QString s) + { + s.replace("abc", "def"); + return s; + } +} + +void QByteArray1(QByteArray byteArrayArg) +{ + for (int i = 0; i <= byteArrayArg.size(); ++i) { + // cppcheck-suppress stlOutOfBounds + byteArrayArg[i] = 'x'; + } + + // cppcheck-suppress containerOutOfBoundsIndexExpression + byteArrayArg[byteArrayArg.length()] = 'a'; + // cppcheck-suppress containerOutOfBoundsIndexExpression + byteArrayArg[byteArrayArg.count()] = 'b'; + // cppcheck-suppress containerOutOfBoundsIndexExpression + printf("val: %c\n", byteArrayArg[byteArrayArg.size()]); + + QByteArray byteArray1{'a', 'b'}; + (void)byteArray1[1]; + // cppcheck-suppress ignoredReturnValue + byteArray1.at(1); +} + +void QList1(QList intListArg) +{ + for (int i = 0; i <= intListArg.size(); ++i) { + // cppcheck-suppress stlOutOfBounds + intListArg[i] = 1; + } + // cppcheck-suppress containerOutOfBoundsIndexExpression + intListArg[intListArg.length()] = 5; + // cppcheck-suppress containerOutOfBoundsIndexExpression + intListArg[intListArg.count()] = 10; + // cppcheck-suppress containerOutOfBoundsIndexExpression + printf("val: %d\n", intListArg[intListArg.size()]); + + QList qstringList1{"one", "two"}; + (void)qstringList1[1]; + + QList qstringList2 = {"one", "two"}; + (void)qstringList2[1]; + qstringList2.clear(); + // cppcheck-suppress containerOutOfBounds + (void)qstringList2[1]; + + QList qstringList3; + qstringList3 << "one" << "two"; + (void)qstringList3[1]; + // FIXME: The following should have a containerOutOfBounds suppression #9242 + (void)qstringList3[3]; + // cppcheck-suppress ignoredReturnValue + qstringList3.startsWith("one"); + // cppcheck-suppress ignoredReturnValue + qstringList3.endsWith("one"); + // cppcheck-suppress ignoredReturnValue + qstringList3.count(); + // cppcheck-suppress ignoredReturnValue + qstringList3.length(); + // cppcheck-suppress ignoredReturnValue + qstringList3.size(); + // cppcheck-suppress ignoredReturnValue + qstringList3.at(5); + // cppcheck-suppress invalidFunctionArg + (void)qstringList3.at(-5); + + QList qstringList4; + // cppcheck-suppress containerOutOfBounds + (void)qstringList4[0]; + qstringList4.append("a"); + (void)qstringList4[0]; + qstringList4.clear(); + // cppcheck-suppress containerOutOfBounds + (void)qstringList4[0]; +} + +QList QList2() { // #10556 + QList v; + + for (int i = 0; i < 4; ++i) + { + v.append(i); + (void)v.at(i); + } + return v; +} + +QList::iterator QList3() +{ + QList qlist1; + QList qlist2; + // cppcheck-suppress mismatchingContainers + for (QList::iterator it = qlist1.begin(); it != qlist2.end(); ++it) + {} + + QList::iterator it = qlist1.begin(); + // cppcheck-suppress returnDanglingLifetime + return it; +} + +void QLinkedList1() +{ + // cppcheck-suppress unreadVariable + QLinkedList qstringLinkedList1{"one", "two"}; + + QLinkedList qstringLinkedList2 = {"one", "two"}; + qstringLinkedList2.clear(); + + QLinkedList qstringLinkedList3; + qstringLinkedList3 << "one" << "two"; + // cppcheck-suppress ignoredReturnValue + qstringLinkedList3.startsWith("one"); + // cppcheck-suppress ignoredReturnValue + qstringLinkedList3.endsWith("one"); + // cppcheck-suppress ignoredReturnValue + qstringLinkedList3.count(); + // cppcheck-suppress ignoredReturnValue + qstringLinkedList3.size(); + + QLinkedList qstringLinkedList4; + qstringLinkedList4.append("a"); + qstringLinkedList4.clear(); +} + +QLinkedList::iterator QLinkedList3() +{ + QLinkedList intQLinkedList1; + QLinkedList intQLinkedList2; + // cppcheck-suppress mismatchingContainers + for (QLinkedList::iterator it = intQLinkedList1.begin(); it != intQLinkedList2.end(); ++it) + {} + + QLinkedList::iterator it = intQLinkedList1.begin(); + // cppcheck-suppress returnDanglingLifetime + return it; +} + +void QStringList1(QStringList stringlistArg) +{ + for (int i = 0; i <= stringlistArg.size(); ++i) { + // cppcheck-suppress stlOutOfBounds + stringlistArg[i] = "abc"; + } + // cppcheck-suppress containerOutOfBoundsIndexExpression + stringlistArg[stringlistArg.length()] = "ab"; + stringlistArg[stringlistArg.length() - 1] = "ab"; // could be valid + // cppcheck-suppress containerOutOfBoundsIndexExpression + stringlistArg[stringlistArg.count()] = "12"; + stringlistArg[stringlistArg.count() - 1] = "12"; // could be valid + // cppcheck-suppress containerOutOfBoundsIndexExpression + (void)stringlistArg[stringlistArg.size()]; + (void)stringlistArg[stringlistArg.size() - 1]; // could be valid + + QStringList qstringlist1{"one", "two"}; + (void)qstringlist1[1]; + + QStringList qstringlist2 = {"one", "two"}; + (void)qstringlist2[1]; + + QStringList qstringlist3; + qstringlist3 << "one" << "two"; + (void)qstringlist3[1]; + // FIXME: The following should have a containerOutOfBounds suppression #9242 + (void)qstringlist3[3]; + // cppcheck-suppress ignoredReturnValue + qstringlist3.startsWith("one"); + // cppcheck-suppress ignoredReturnValue + qstringlist3.endsWith("one"); + // cppcheck-suppress ignoredReturnValue + qstringlist3.count(); + // cppcheck-suppress ignoredReturnValue + qstringlist3.length(); + // cppcheck-suppress ignoredReturnValue + qstringlist3.size(); + // cppcheck-suppress ignoredReturnValue + qstringlist3.at(5); + // cppcheck-suppress invalidFunctionArg + (void)qstringlist3.at(-5); +} + +QStringList::iterator QStringList2() +{ + QStringList qstringlist1; + QStringList qstringlist2; + // cppcheck-suppress mismatchingContainers + for (QStringList::iterator it = qstringlist1.begin(); it != qstringlist2.end(); ++it) + {} + + QStringList::iterator it = qstringlist1.begin(); + // cppcheck-suppress returnDanglingLifetime + return it; +} + +void QVector1(QVector intVectorArg) +{ + for (int i = 0; i <= intVectorArg.size(); ++i) { + // cppcheck-suppress stlOutOfBounds + intVectorArg[i] = 1; + } + // cppcheck-suppress containerOutOfBoundsIndexExpression + intVectorArg[intVectorArg.length()] = 5; + // cppcheck-suppress containerOutOfBoundsIndexExpression + intVectorArg[intVectorArg.count()] = 10; + // cppcheck-suppress containerOutOfBoundsIndexExpression + printf("val: %d\n", intVectorArg[intVectorArg.size()]); + + QVector qstringVector1{"one", "two"}; + (void)qstringVector1[1]; + + QVector qstringVector2 = {"one", "two"}; + (void)qstringVector2[1]; + + QVector qstringVector3; + qstringVector3 << "one" << "two"; + (void)qstringVector3[1]; + // FIXME: The following should have a containerOutOfBounds suppression #9242 + (void)qstringVector3[3]; + // cppcheck-suppress ignoredReturnValue + qstringVector3.startsWith("one"); + // cppcheck-suppress ignoredReturnValue + qstringVector3.endsWith("one"); + // cppcheck-suppress ignoredReturnValue + qstringVector3.count(); + // cppcheck-suppress ignoredReturnValue + qstringVector3.length(); + // cppcheck-suppress ignoredReturnValue + qstringVector3.size(); + // cppcheck-suppress ignoredReturnValue + qstringVector3.at(5); + // cppcheck-suppress invalidFunctionArg + (void)qstringVector3.at(-5); +} + +QVector::iterator QVector2() +{ + QVector qvector1; + QVector qvector2; + // cppcheck-suppress mismatchingContainers + for (QVector::iterator it = qvector1.begin(); it != qvector2.end(); ++it) + {} + + QVector::iterator it = qvector1.begin(); + // cppcheck-suppress returnDanglingLifetime + return it; +} + +// cppcheck-suppress passedByValue +void duplicateExpression_QString_Compare(QString style) //#8723 +{ + // cppcheck-suppress [duplicateExpression,valueFlowBailoutIncompleteVar] + if (style.compare( "x", Qt::CaseInsensitive ) == 0 || style.compare( "x", Qt::CaseInsensitive ) == 0) + {} +} + +void QVector_uninit() +{ + int i; + // cppcheck-suppress [uninitvar, unreadVariable] + QVector v(i); +} + +void QStack1(QStack intStackArg) +{ + for (int i = 0; i <= intStackArg.size(); ++i) { + // cppcheck-suppress stlOutOfBounds + intStackArg[i] = 1; + } + // cppcheck-suppress containerOutOfBoundsIndexExpression + intStackArg[intStackArg.length()] = 5; + // cppcheck-suppress containerOutOfBoundsIndexExpression + intStackArg[intStackArg.count()] = 10; + // cppcheck-suppress containerOutOfBoundsIndexExpression + printf("val: %d\n", intStackArg[intStackArg.size()]); + + QStack qstringStack1; + qstringStack1.push("one"); + qstringStack1.push("two"); + (void)qstringStack1[1]; + + QStack qstringStack2; + qstringStack2 << "one" << "two"; + (void)qstringStack2[1]; + // FIXME: The following should have a containerOutOfBounds suppression #9242 + (void)qstringStack2[3]; + // cppcheck-suppress ignoredReturnValue + qstringStack2.startsWith("one"); + // cppcheck-suppress ignoredReturnValue + qstringStack2.endsWith("one"); + // cppcheck-suppress ignoredReturnValue + qstringStack2.count(); + // cppcheck-suppress ignoredReturnValue + qstringStack2.length(); + // cppcheck-suppress ignoredReturnValue + qstringStack2.size(); + // cppcheck-suppress ignoredReturnValue + qstringStack2.at(5); + // cppcheck-suppress invalidFunctionArg + (void)qstringStack2.at(-5); +} + +QStack::iterator QStack2() +{ + QStack qstack1; + QStack qstack2; + // cppcheck-suppress mismatchingContainers + for (QStack::iterator it = qstack1.begin(); it != qstack2.end(); ++it) + {} + + QStack::iterator it = qstack1.begin(); + // cppcheck-suppress returnDanglingLifetime + return it; +} + +void QStack3() +{ + QStack intStack; + intStack.push(1); + // cppcheck-suppress ignoredReturnValue + intStack.top(); + intStack.pop(); +} + +// Verify that Qt macros do not result in syntax errors, false positives or other issues. +class MacroTest1 : public QObject { + Q_OBJECT + Q_PLUGIN_METADATA(IID "com.foo.bar" FILE "test.json") + +public: + explicit MacroTest1(QObject *parent = 0); + ~MacroTest1(); +}; + +class MacroTest2 { + Q_DECLARE_TR_FUNCTIONS(MacroTest2) + +public: + MacroTest2(); + ~MacroTest2(); +}; + +void MacroTest2_test() +{ + QString str = MacroTest2::tr("hello"); + QByteArray ba = str.toLatin1(); + printf(ba.data()); + +#ifndef QT_NO_DEPRECATED + str = MacroTest2::trUtf8("test2"); + ba = str.toLatin1(); + printf(ba.data()); +#endif +} + +void MacroTest3() +{ + QByteArray message = QByteArrayLiteral("Test1"); + message += QByteArrayLiteral("Test2"); + QVERIFY2(2 >= 0, message.constData()); +} + +// cppcheck-suppress constParameterReference +void validCode(int * pIntPtr, QString & qstrArg, double d) +{ + Q_UNUSED(d) + if (QFile::exists("test")) {} + + if (pIntPtr != Q_NULLPTR) { + *pIntPtr = 5; + } + + if (pIntPtr && *pIntPtr == 1) { + forever { + } + } else if (pIntPtr && *pIntPtr == 2) { + Q_FOREVER {} + } + + if (Q_LIKELY(pIntPtr)) {} + if (Q_UNLIKELY(!pIntPtr)) {} + + printf(QT_TR_NOOP("Hi")); + + // cppcheck-suppress valueFlowBailoutIncompleteVar + Q_DECLARE_LOGGING_CATEGORY(logging_category_test); + QT_FORWARD_DECLARE_CLASS(forwardDeclaredClass); + QT_FORWARD_DECLARE_STRUCT(forwardDeclaredStruct); + + //#9650 + QString qstr1(qstrArg); + if (qstr1.length() == 1) {} else { + qstr1.chop(1); + if (qstr1.length() == 1) {} + } + if (qstr1.length() == 1) {} else { + qstr1.remove(1); + if (qstr1.length() == 1) {} + } +} + +void ignoredReturnValue() +{ + // cppcheck-suppress ignoredReturnValue + QFile::exists("test"); + QFile file1("test"); + // cppcheck-suppress ignoredReturnValue + file1.exists(); +} + +void nullPointer(int * pIntPtr) +{ + int * pNullPtr = Q_NULLPTR; + // cppcheck-suppress nullPointer + *pNullPtr = 1; + + if (pIntPtr != Q_NULLPTR) { + *pIntPtr = 2; + } else { + // cppcheck-suppress nullPointerRedundantCheck + *pIntPtr = 3; + } +} + +namespace { + class C : public QObject { + Q_OBJECT + public: + explicit C(QObject* parent = nullptr) : QObject(parent) {} + void signal() {} + }; + class D : public QObject { + Q_OBJECT + public: + D() { + connect(new C(this), &C::signal, this, &D::slot); + } + void slot() {}; + }; + + // findFunction11 + class Fred : public QObject { + Q_OBJECT + private slots: + void foo(); + }; + void Fred::foo() {} + + // bitfields14 + class X { + signals: + }; + + // simplifyQtSignalsSlots1 + class Counter1 : public QObject { + Q_OBJECT + public: + Counter1() { + 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 Counter1::setValue(int value) { + if (value != m_value) { + m_value = value; + emit valueChanged(value); + } + } + + class Counter2 : public QObject { + Q_OBJECT + public: + Counter2() { + 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 Counter2::setValue(int value) { + if (value != m_value) { + m_value = value; + emit valueChanged(value); + } + } + + class MyObject1 : public QObject { + MyObject1() {} + ~MyObject1() {} + public slots: + signals: + void test() {} + }; + + class MyObject2 : public QObject { + Q_OBJECT + public slots: + }; + + // simplifyQtSignalsSlots2 + namespace Foo { class Bar; } + class Foo::Bar : public QObject { private slots: }; + + // Q_PROPERTY with templates inducing a ',' should not produce a preprocessorErrorDirective + class AssocProperty : public QObject { + public: + Q_PROPERTY(QHash hash READ hash WRITE setHash) + }; +} + +struct SEstimateSize { + inline const QString& get() const { + return m; + } + QString m; +}; + +class QString; + +void dontCrashEstimateSize(const SEstimateSize& s) { + // cppcheck-suppress redundantCopyLocalConst + QString q = s.get(); + if (!q.isNull()) {} +} + +bool knownConditionTrueFalse_QString_count(const QString& s) // #11036 +{ + if (!s.isEmpty() && s.count("abc") == 0) + return false; + return true; +} + +void unusedVariable_qtContainers() // #10689 +{ + // cppcheck-suppress unusedVariable + QMap qm; + // cppcheck-suppress unusedVariable + QSet qs; + // cppcheck-suppress unusedVariable + QMultiMap qmm; + // cppcheck-suppress unusedVariable + QQueue qq; + // cppcheck-suppress unusedVariable + QLatin1String ql1s; +} + diff --git a/cppcheck-2.14.0/test/cfg/runtests.sh b/cppcheck-2.14.0/test/cfg/runtests.sh new file mode 100644 index 00000000..7169886e --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/runtests.sh @@ -0,0 +1,593 @@ +#!/bin/bash +set -e # abort on error +#set -x # be verbose + +# TODO: do not bail out on first error - always run all tests and return error instead + +function exit_if_strict { + if [ -n "${STRICT}" ] && [ "${STRICT}" -eq 1 ]; then + exit 1 + fi +} + +echo "Checking for pkg-config..." +if pkg-config --version; then + HAS_PKG_CONFIG=1 + echo "pkg-config found." +else + HAS_PKG_CONFIG=0 + echo "pkg-config is not available, skipping all syntax checks." + exit_if_strict +fi + +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"/ +CPPCHECK="$DIR"../../cppcheck +CFG="$DIR"../../cfg/ + +# TODO: remove missingInclude disabling when it no longer is implied by --enable=information +# Cppcheck options +# need to suppress unmatchedSuppression in case valueFlowBailout is not reported +CPPCHECK_OPT=( + "--check-library" + "--platform=unix64" + "--enable=style,information" + "--inconclusive" + "--force" + "--check-level=exhaustive" + "--error-exitcode=-1" + "--disable=missingInclude" + "--inline-suppr" + "--template=\"{file}:{line}:{severity}:{id}:{message}\"" + "--debug-warnings" + "--suppress=checkersReport") + +# Compiler settings +CXX=g++ +CXX_OPT=("-fsyntax-only" "-w" "-std=c++2a") +CC=gcc +CC_OPT=("-fsyntax-only" "-w" "-std=c11") + +function get_pkg_config_cflags { + # TODO: get rid of the error enabling/disabling? + set +e + PKGCONFIG=$(pkg-config --cflags "$@") + PKGCONFIG_RETURNCODE=$? + set -e + if [ $PKGCONFIG_RETURNCODE -ne 0 ]; then + PKGCONFIG= + else + # make sure the config is not empty when no flags were found - happens with e.g. libssl and sqlite3 + if [ -z "$PKGCONFIG" ]; then + PKGCONFIG=" " + fi + fi + echo "$PKGCONFIG" +} + +# posix.c +function posix_fn { + ${CC} "${CC_OPT[@]}" ${DIR}posix.c +} + +# gnu.c +function gnu_fn { + ${CC} "${CC_OPT[@]}" -D_GNU_SOURCE ${DIR}gnu.c +} + +# qt.cpp +function qt_fn { + if [ $HAS_PKG_CONFIG -eq 1 ]; then + QTCONFIG=$(get_pkg_config_cflags Qt5Core Qt5Test Qt5Gui) + if [ -n "$QTCONFIG" ]; then + QTBUILDCONFIG=$(pkg-config --variable=qt_config Qt5Core Qt5Test Qt5Gui) + [[ $QTBUILDCONFIG =~ (^|[[:space:]])reduce_relocations($|[[:space:]]) ]] && QTCONFIG="${QTCONFIG} -fPIC" + # TODO: get rid of the error enabling/disabling? + set +e + echo -e "#include " | ${CXX} "${CXX_OPT[@]}" ${QTCONFIG} -x c++ - + QTCHECK_RETURNCODE=$? + set -e + if [ $QTCHECK_RETURNCODE -ne 0 ]; then + echo "Qt not completely present or not working, skipping syntax check with ${CXX}." + exit_if_strict + else + echo "Qt found and working, checking syntax with ${CXX} now." + ${CXX} "${CXX_OPT[@]}" ${QTCONFIG} ${DIR}qt.cpp + fi + else + echo "Qt not present, skipping syntax check with ${CXX}." + exit_if_strict + fi + fi +} + +# bsd.c +function bsd_fn { + true +} + +# std.c +function std_c_fn { + ${CC} "${CC_OPT[@]}" "${DIR}"std.c +} + +# std.cpp +function std_cpp_fn { + ${CXX} "${CXX_OPT[@]}" "${DIR}"std.cpp +} + +# windows.cpp +function windows_fn { + # TODO: Syntax check via g++ does not work because it can not find a valid windows.h + #${CXX} "${CXX_OPT[@]}" ${DIR}windows.cpp + true +} + +# mfc.cpp +function mfc_fn { + # TODO: Add syntax check + true +} + +# wxwidgets.cpp +function wxwidgets_fn { + # TODO: get rid of the error enabling/disabling? + set +e + WXCONFIG=$(wx-config --cxxflags) + WXCONFIG_RETURNCODE=$? + set -e + if [ $WXCONFIG_RETURNCODE -ne 0 ]; then + echo "wx-config does not work, skipping syntax check for wxWidgets tests." + exit_if_strict + else + # TODO: get rid of the error enabling/disabling? + set +e + echo -e "#include \n#include \n#include \n#include \n#if wxVERSION_NUMBER<2950\n#error \"Old version\"\n#endif" | ${CXX} "${CXX_OPT[@]}" ${WXCONFIG} -x c++ - + WXCHECK_RETURNCODE=$? + set -e + if [ $WXCHECK_RETURNCODE -ne 0 ]; then + echo "wxWidgets not completely present (with GUI classes) or not working, skipping syntax check with ${CXX}." + exit_if_strict + else + echo "wxWidgets found, checking syntax with ${CXX} now." + ${CXX} "${CXX_OPT[@]}" ${WXCONFIG} -Wno-deprecated-declarations "${DIR}"wxwidgets.cpp + fi + fi +} + +# gtk.c +function gtk_fn { + if [ $HAS_PKG_CONFIG -eq 1 ]; then + GTKCONFIG=$(get_pkg_config_cflags gtk+-3.0) + if [ -z "$GTKCONFIG" ]; then + GTKCONFIG=$(get_pkg_config_cflags gtk+-2.0) + fi + if [ -n "$GTKCONFIG" ]; then + # TODO: get rid of the error enabling/disabling? + set +e + echo -e "#include " | ${CC} "${CC_OPT[@]}" ${GTKCONFIG} -x c - + GTKCHECK_RETURNCODE=$? + set -e + if [ $GTKCHECK_RETURNCODE -ne 0 ]; then + echo "GTK+ not completely present or not working, skipping syntax check with ${CXX}." + exit_if_strict + else + echo "GTK+ found and working, checking syntax with ${CXX} now." + ${CC} "${CC_OPT[@]}" ${GTKCONFIG} "${DIR}"gtk.c + fi + else + echo "GTK+ not present, skipping syntax check with ${CXX}." + exit_if_strict + fi + fi +} + +# boost.cpp +function boost_fn { + # TODO: get rid of the error enabling/disabling? + set +e + echo -e "#include " | ${CXX} "${CXX_OPT[@]}" -x c++ - + BOOSTCHECK_RETURNCODE=$? + set -e + if [ ${BOOSTCHECK_RETURNCODE} -ne 0 ]; then + echo "Boost not completely present or not working, skipping syntax check with ${CXX}." + exit_if_strict + else + echo "Boost found and working, checking syntax with ${CXX} now." + ${CXX} "${CXX_OPT[@]}" "${DIR}"boost.cpp + fi +} + +# sqlite3.c +function sqlite3_fn { + if [ $HAS_PKG_CONFIG -eq 1 ]; then + SQLITE3CONFIG=$(get_pkg_config_cflags sqlite3) + if [ -n "$SQLITE3CONFIG" ]; then + # TODO: get rid of the error enabling/disabling? + set +e + echo -e "#include " | ${CC} "${CC_OPT[@]}" ${SQLITE3CONFIG} -x c - + SQLITE3CHECK_RETURNCODE=$? + set -e + if [ $SQLITE3CHECK_RETURNCODE -ne 0 ]; then + echo "SQLite3 not completely present or not working, skipping syntax check with ${CC}." + exit_if_strict + else + echo "SQLite3 found and working, checking syntax with ${CC} now." + ${CC} "${CC_OPT[@]}" ${SQLITE3CONFIG} "${DIR}"sqlite3.c + fi + else + echo "SQLite3 not present, skipping syntax check with ${CC}." + exit_if_strict + fi + fi +} + +# openmp.c +function openmp_fn { + # MacOS compiler has no OpenMP by default + if ! command -v sw_vers; then + ${CC} "${CC_OPT[@]}" -fopenmp ${DIR}openmp.c + fi +} + +# python.c +function python_fn { + if [ $HAS_PKG_CONFIG -eq 1 ]; then + PYTHON3CONFIG=$(get_pkg_config_cflags python3) + if [ -n "$PYTHON3CONFIG" ]; then + # TODO: get rid of the error enabling/disabling? + set +e + echo -e "#include " | ${CC} "${CC_OPT[@]}" ${PYTHON3CONFIG} -x c - + PYTHON3CONFIG_RETURNCODE=$? + set -e + if [ $PYTHON3CONFIG_RETURNCODE -ne 0 ]; then + echo "Python 3 not completely present or not working, skipping syntax check with ${CC}." + exit_if_strict + else + echo "Python 3 found and working, checking syntax with ${CC} now." + ${CC} "${CC_OPT[@]}" ${PYTHON3CONFIG} "${DIR}"python.c + fi + else + echo "Python 3 not present, skipping syntax check with ${CC}." + exit_if_strict + fi + fi +} + +# lua.c +function lua_fn { + if [ $HAS_PKG_CONFIG -eq 1 ]; then + LUACONFIG=$(get_pkg_config_cflags lua-5.3) + if [ -n "$LUACONFIG" ]; then + # TODO: get rid of the error enabling/disabling? + set +e + echo -e "#include " | ${CC} "${CC_OPT[@]}" ${LUACONFIG} -x c - + LUACONFIG_RETURNCODE=$? + set -e + if [ $LUACONFIG_RETURNCODE -ne 0 ]; then + echo "Lua not completely present or not working, skipping syntax check with ${CC}." + exit_if_strict + else + echo "Lua found and working, checking syntax with ${CC} now." + ${CC} "${CC_OPT[@]}" ${LUACONFIG} "${DIR}"lua.c + fi + else + echo "Lua not present, skipping syntax check with ${CC}." + exit_if_strict + fi + fi +} + +# libcurl.c +function libcurl_fn { + if [ $HAS_PKG_CONFIG -eq 1 ]; then + LIBCURLCONFIG=$(get_pkg_config_cflags libcurl) + if [ -n "$LIBCURLCONFIG" ]; then + # TODO: get rid of the error enabling/disabling? + set +e + echo -e "#include " | ${CC} "${CC_OPT[@]}" ${LIBCURLCONFIG} -x c - + LIBCURLCONFIG_RETURNCODE=$? + set -e + if [ $LIBCURLCONFIG_RETURNCODE -ne 0 ]; then + echo "libcurl not completely present or not working, skipping syntax check with ${CC}." + exit_if_strict + else + echo "libcurl found and working, checking syntax with ${CC} now." + ${CC} "${CC_OPT[@]}" ${LIBCURLCONFIG} "${DIR}"libcurl.c + fi + else + echo "libcurl not present, skipping syntax check with ${CC}." + exit_if_strict + fi + fi +} + +# cairo.c +function cairo_fn { + if [ $HAS_PKG_CONFIG -eq 1 ]; then + CAIROCONFIG=$(get_pkg_config_cflags cairo) + if [ -n "$CAIROCONFIG" ]; then + # TODO: get rid of the error enabling/disabling? + set +e + echo -e "#include " | ${CC} "${CC_OPT[@]}" ${CAIROCONFIG} -x c - + CAIROCONFIG_RETURNCODE=$? + set -e + if [ $CAIROCONFIG_RETURNCODE -ne 0 ]; then + echo "cairo not completely present or not working, skipping syntax check with ${CC}." + exit_if_strict + else + echo "cairo found and working, checking syntax with ${CC} now." + ${CC} "${CC_OPT[@]}" ${CAIROCONFIG} "${DIR}"cairo.c + fi + else + echo "cairo not present, skipping syntax check with ${CC}." + exit_if_strict + fi + fi +} + +# googletest.cpp +function googletest_fn { + true +} + +# kde.cpp +function kde_fn { + # TODO: get rid of the error enabling/disabling? + set +e + KDECONFIG=$(kde4-config --path include) + KDECONFIG_RETURNCODE=$? + set -e + if [ $KDECONFIG_RETURNCODE -ne 0 ]; then + echo "kde4-config does not work, skipping syntax check." + exit_if_strict + else + KDEQTCONFIG=$(get_pkg_config_cflags QtCore) + if [ -n "$KDEQTCONFIG" ]; then + echo "Suitable Qt not present, Qt is necessary for KDE. Skipping syntax check." + exit_if_strict + else + # TODO: get rid of the error enabling/disabling? + set +e + echo -e "#include \n" | ${CXX} "${CXX_OPT[@]}" -I${KDECONFIG} ${KDEQTCONFIG} -x c++ - + KDECHECK_RETURNCODE=$? + set -e + if [ $KDECHECK_RETURNCODE -ne 0 ]; then + echo "KDE headers not completely present or not working, skipping syntax check with ${CXX}." + exit_if_strict + else + echo "KDE found, checking syntax with ${CXX} now." + ${CXX} "${CXX_OPT[@]}" -I${KDECONFIG} ${KDEQTCONFIG} "${DIR}"kde.cpp + fi + fi + fi +} + +# libsigc++.cpp +function libsigcpp_fn { + if [ $HAS_PKG_CONFIG -eq 1 ]; then + LIBSIGCPPCONFIG=$(get_pkg_config_cflags sigc++-2.0) + if [ -n "$LIBSIGCPPCONFIG" ]; then + # TODO: get rid of the error enabling/disabling? + set +e + echo -e "#include \n" | ${CXX} "${CXX_OPT[@]}" ${LIBSIGCPPCONFIG} -x c++ - + LIBSIGCPPCONFIG_RETURNCODE=$? + set -e + if [ $LIBSIGCPPCONFIG_RETURNCODE -ne 0 ]; then + echo "libsigc++ not completely present or not working, skipping syntax check with ${CXX}." + exit_if_strict + else + echo "libsigc++ found and working, checking syntax with ${CXX} now." + ${CXX} "${CXX_OPT[@]}" ${LIBSIGCPPCONFIG} "${DIR}"libsigc++.cpp + fi + else + echo "libsigc++ not present, skipping syntax check with ${CXX}." + exit_if_strict + fi + fi +} + +# openssl.c +function openssl_fn { + if [ $HAS_PKG_CONFIG -eq 1 ]; then + OPENSSLCONFIG=$(get_pkg_config_cflags libssl) + if [ -n "$OPENSSLCONFIG" ]; then + # TODO: get rid of the error enabling/disabling? + set +e + echo -e "#include " | ${CC} "${CC_OPT[@]}" ${OPENSSLCONFIG} -x c - + OPENSSLCONFIG_RETURNCODE=$? + set -e + if [ $OPENSSLCONFIG_RETURNCODE -ne 0 ]; then + echo "OpenSSL not completely present or not working, skipping syntax check with ${CC}." + exit_if_strict + else + echo "OpenSSL found and working, checking syntax with ${CC} now." + ${CC} "${CC_OPT[@]}" ${OPENSSLCONFIG} "${DIR}"openssl.c + fi + else + echo "OpenSSL not present, skipping syntax check with ${CC}." + exit_if_strict + fi + fi +} + +# opencv2.cpp +function opencv2_fn { + if [ $HAS_PKG_CONFIG -eq 1 ]; then + OPENCVCONFIG=$(get_pkg_config_cflags opencv) + if [ -n "$OPENCVCONFIG" ]; then + # TODO: get rid of the error enabling/disabling? + set +e + echo -e "#include \n" | ${CXX} "${CXX_OPT[@]}" ${OPENCVCONFIG} -x c++ - + OPENCVCONFIG_RETURNCODE=$? + set -e + if [ $OPENCVCONFIG_RETURNCODE -ne 0 ]; then + echo "OpenCV not completely present or not working, skipping syntax check with ${CXX}." + exit_if_strict + else + echo "OpenCV found and working, checking syntax with ${CXX} now." + ${CXX} "${CXX_OPT[@]}" ${OPENCVCONFIG} "${DIR}"opencv2.cpp + fi + else + echo "OpenCV not present, skipping syntax check with ${CXX}." + exit_if_strict + fi + fi +} + +# cppunit.cpp +function cppunit_fn { + if [ $HAS_PKG_CONFIG -eq 1 ]; then + if ! pkg-config cppunit; then + echo "cppunit not found, skipping syntax check for cppunit" + exit_if_strict + else + echo "cppunit found, checking syntax with ${CXX} now." + ${CXX} "${CXX_OPT[@]}" -Wno-deprecated-declarations "${DIR}"cppunit.cpp + fi + fi +} + +function check_file { + f=$(basename "$1") + lib="${f%%.*}" + case $f in + boost.cpp) + boost_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + bsd.c) + bsd_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + cairo.c) + cairo_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + cppunit.cpp) + cppunit_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + gnu.c) + gnu_fn + # TODO: posix needs to specified first or it has a different mmap() config + # TODO: get rid of posix dependency + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library=posix,"$lib" "${DIR}"gnu.c + ;; + googletest.cpp) + googletest_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + gtk.c) + gtk_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + kde.cpp) + # TODO: "kde-4config" is no longer commonly available in recent distros + #kde_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + libcurl.c) + libcurl_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + libsigc++.cpp) + libsigcpp_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + lua.c) + lua_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + mfc.cpp) + mfc_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --platform=win64 --library="$lib" "${DIR}""$f" + ;; + opencv2.cpp) + # TODO: "opencv.pc" is not commonly available in distros + #opencv2_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + openmp.c) + openmp_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + openssl.c) + openssl_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + posix.c) + posix_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + python.c) + python_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + qt.cpp) + qt_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + sqlite3.c) + sqlite3_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + std.c) + std_c_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" "${DIR}""$f" + ;; + std.cpp) + std_cpp_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" "${DIR}""$f" + ;; + windows.cpp) + windows_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --platform=win32A --library="$lib" "${DIR}""$f" + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --platform=win32W --library="$lib" "${DIR}""$f" + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --platform=win64 --library="$lib" "${DIR}""$f" + ;; + wxwidgets.cpp) + wxwidgets_fn + "${CPPCHECK}" "${CPPCHECK_OPT[@]}" --library="$lib" "${DIR}""$f" + ;; + *) + echo "Unhandled file $f" + exit_if_strict + esac +} + +function check_files +{ +for f in "$@" +do + check_file "$f" +done +} + +# Check the syntax of the defines in the configuration files +function check_defines_syntax +{ + if ! xmlstarlet --version; then + echo "xmlstarlet needed to extract defines, skipping defines check." + exit_if_strict + else + for configfile in "${CFG}"*.cfg; do + echo "Checking defines in $configfile" + # Disable debugging output temporarily since there could be many defines + set +x + # XMLStarlet returns 1 if no elements were found which is no problem here + EXTRACTED_DEFINES=$(xmlstarlet sel -t -m '//define' -c . -n <"$configfile" || true) + EXTRACTED_DEFINES=$(echo "$EXTRACTED_DEFINES" | sed 's///g') + echo "$EXTRACTED_DEFINES" | gcc -fsyntax-only -xc -Werror - + done + fi +} + +if [ $# -eq 0 ] +then + check_files "${DIR}"*.{c,cpp} + check_defines_syntax +else + check_files "$@" +fi + +echo SUCCESS diff --git a/cppcheck-2.14.0/test/cfg/sqlite3.c b/cppcheck-2.14.0/test/cfg/sqlite3.c new file mode 100644 index 00000000..d9480d5a --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/sqlite3.c @@ -0,0 +1,57 @@ + +// Test library configuration for sqlite3.cfg +// +// Usage: +// $ cppcheck --check-library --library=sqlite3 --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/sqlite3.c +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +#include +#include + +void validCode() +{ + sqlite3 * db; + + int rc = sqlite3_open("/db", &db); + if (rc != SQLITE_OK) { + printf("Error opening sqlite3 db: %s\n", sqlite3_errmsg(db)); + sqlite3_close(db); + } else { + sqlite3_close(db); + } + + { + char * buf = sqlite3_malloc(10); + printf("size: %ull\n", sqlite3_msize(buf)); + sqlite3_free(buf); + } +} + +void memleak_sqlite3_malloc() +{ + char * buf = sqlite3_malloc(10); + if (buf) { + buf[0] = 0; + } + // cppcheck-suppress memleak +} + +void resourceLeak_sqlite3_open() +{ + sqlite3 * db; + + sqlite3_open("/db", &db); + // cppcheck-suppress resourceLeak +} + +void ignoredReturnValue(const char * buf) +{ + // cppcheck-suppress leakReturnValNotUsed + sqlite3_malloc(10); + // cppcheck-suppress leakReturnValNotUsed + sqlite3_malloc64(5); + // cppcheck-suppress ignoredReturnValue + sqlite3_msize(buf); +} diff --git a/cppcheck-2.14.0/test/cfg/std.c b/cppcheck-2.14.0/test/cfg/std.c new file mode 100644 index 00000000..f795cdc5 --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/std.c @@ -0,0 +1,5038 @@ + +// Test library configuration for std.cfg +// +// Usage: +// $ cppcheck --check-library --library=std --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/std.c +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +// cppcheck-suppress-file valueFlowBailout + +#include +#include +#include +#include // frexp +#include +#if defined(__STD_UTF_16__) || defined(__STD_UTF_32__) +#include +#endif +#include +#include +#include +#include +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#ifndef __STDC_NO_THREADS__ +#include +#endif +#include +#include +#include +#include + +size_t invalidFunctionArgStr_wcslen(void) +{ + const wchar_t terminated0[] = L"ABCDEF49620910"; + const wchar_t terminated1[3] = { L'a', L'b', L'\0' }; + const wchar_t notTerminated[3] = { L'a', L'b', L'c' }; + // cppcheck-suppress invalidFunctionArgStr + (void) wcslen(notTerminated); + (void) wcslen(terminated0); + return wcslen(terminated1); +} + +int invalidFunctionArgStr_strcpn(void) +{ + const char str1[] = "ABCDEF49620910"; + const char str2[] = "42"; + const char pattern[3] = { -42, -43, -44 }; + // cppcheck-suppress invalidFunctionArgStr + (void) strcspn(str1, pattern); + return strcspn(str1, str2); +} + +void invalidFunctionArgStr_strncat(void) +{ + char str1[20]; + strcpy (str1,"test"); + const char src = '/'; + // No warning is expected for + strncat (str1, &src, 1); + puts (str1); +} + +char * invalidFunctionArgStr_strpbrk( const char *p ) +{ + const char search[] = { -42, -43, -44 }; + const char pattern[3] = { -42, -43, -44 }; + (void) strpbrk( "abc42", "42" ); + // cppcheck-suppress invalidFunctionArgStr + (void) strpbrk( search, "42" ); + // cppcheck-suppress invalidFunctionArgStr + (void) strpbrk( search, pattern ); + // cppcheck-suppress invalidFunctionArgStr + return strpbrk( p, pattern ); +} + +int invalidFunctionArgStr_strncmp( const char *p ) +{ + const char string[] = "foo"; + char other[5] = { 0 }; + memcpy(other, "foo", 4); + if (strncmp(other, string, 5) != 0) {} + + // No warning is expected for: + const char emdash[3] = { -42, -43, -44 }; + return strncmp( p, emdash, 3 ); +} + +float invalidFunctionArg_float_remquo (float x, float y, int* quo ) +{ + // cppcheck-suppress invalidFunctionArg + (void) remquo(x,0.0f,quo); + // cppcheck-suppress invalidFunctionArg + (void) remquof(x,0.0f,quo); + return remquo(x,y,quo); +} + +double invalidFunctionArg_double_remquo (double x, double y, int* quo ) +{ + // cppcheck-suppress invalidFunctionArg + (void) remquo(x,0.0,quo); + // cppcheck-suppress invalidFunctionArg + (void) remquo(x,0.0f,quo); + // cppcheck-suppress invalidFunctionArg + (void) remquo(x,0.0L,quo); + return remquo(x,y,quo); +} + +double invalidFunctionArg_long_double_remquo (long double x, long double y, int* quo ) +{ + // cppcheck-suppress invalidFunctionArg + (void) remquo(x,0.0L,quo); + // cppcheck-suppress invalidFunctionArg + (void) remquol(x,0.0L,quo); + return remquo(x,y,quo); +} + +void invalidFunctionArg_remainderl(long double f1, long double f2) +{ + // cppcheck-suppress invalidFunctionArg + (void)remainderl(f1,0.0); + // cppcheck-suppress invalidFunctionArg + (void)remainderl(f1,0.0L); + (void)remainderl(f1,f2); +} + +void invalidFunctionArg_remainder(double f1, double f2) +{ + // cppcheck-suppress invalidFunctionArg + (void)remainder(f1,0.0); + (void)remainder(f1,f2); +} + +void invalidFunctionArg_remainderf(float f1, float f2) +{ + // cppcheck-suppress invalidFunctionArg + (void)remainderf(f1,0.0); + // cppcheck-suppress invalidFunctionArg + (void)remainderf(f1,0.0f); + (void)remainderf(f1,f2); +} + +int qsort_cmpfunc (const void * a, const void * b) { + return (*(int*)a - *(int*)b); +} +void nullPointer_qsort(void *base, size_t n, size_t size, int (*cmp)(const void *, const void *)) +{ + // cppcheck-suppress nullPointer + qsort(NULL, n, size, qsort_cmpfunc); + // cppcheck-suppress nullPointer + qsort(base, n, size, NULL); + qsort(base, n, size, qsort_cmpfunc); +} + +// As with all bounds-checked functions, localtime_s is only guaranteed to be available if __STDC_LIB_EXT1__ is defined by the implementation and if the user defines __STDC_WANT_LIB_EXT1__ to the integer constant 1 before including time.h. +#ifdef __STDC_LIB_EXT1__ +void uninitvar_localtime_s(const time_t *restrict time, struct tm *restrict result) +{ + const time_t *restrict Time; + // cppcheck-suppress uninitvar + (void)localtime_s(Time, result); + (void)localtime_s(time, result); +} + +void nullPointer_localtime_s(const time_t *restrict time, struct tm *restrict result) +{ + // cppcheck-suppress nullPointer + (void)localtime_s(NULL, result); + // cppcheck-suppress nullPointer + (void)localtime_s(time, NULL); + (void)localtime_s(time, result); +} +#endif // __STDC_LIB_EXT1__ + +size_t bufferAccessOutOfBounds_wcsrtombs(char * dest, const wchar_t ** src, size_t len, mbstate_t * ps) +{ + char buf[42]; + (void)wcsrtombs(buf,src,42,ps); + // cppcheck-suppress bufferAccessOutOfBounds + (void)wcsrtombs(buf,src,43,ps); + return wcsrtombs(dest,src,len,ps); +} + +void bufferAccessOutOfBounds(void) +{ + char a[5]; + // cppcheck-suppress valueFlowBailoutIncompleteVar + fgets(a,5,stdin); + // cppcheck-suppress bufferAccessOutOfBounds + fgets(a,6,stdin); + sprintf(a, "ab%s", "cd"); + // cppcheck-suppress bufferAccessOutOfBounds + // TODO cppcheck-suppress redundantCopy + sprintf(a, "ab%s", "cde"); + // TODO cppcheck-suppress redundantCopy + snprintf(a, 5, "abcde%i", 1); + // TODO cppcheck-suppress redundantCopy + // cppcheck-suppress bufferAccessOutOfBounds + snprintf(a, 6, "abcde%i", 1); + // TODO cppcheck-suppress redundantCopy + strcpy(a,"abcd"); + // cppcheck-suppress bufferAccessOutOfBounds + // TODO cppcheck-suppress redundantCopy + strcpy(a, "abcde"); + // cppcheck-suppress bufferAccessOutOfBounds + strcpy_s(a, 10, "abcdefghij"); + // TODO cppcheck-suppress redundantCopy + // cppcheck-suppress terminateStrncpy + strncpy(a,"abcde",5); + // cppcheck-suppress bufferAccessOutOfBounds + // TODO cppcheck-suppress redundantCopy + strncpy(a,"abcde",6); + // cppcheck-suppress bufferAccessOutOfBounds + // TODO cppcheck-suppress redundantCopy + strncpy(a,"a",6); + // TODO cppcheck-suppress redundantCopy + strncpy(a,"abcdefgh",4); + // valid call + strncpy_s(a,5,"abcd",5); + // string will be truncated, error is returned, but no buffer overflow + strncpy_s(a,5,"abcde",6); + // TODO cppcheck-suppress bufferAccessOutOfBounds + strncpy_s(a,5,"a",6); + strncpy_s(a,5,"abcdefgh",4); + // valid call + strncat_s(a,5,"1",2); + // cppcheck-suppress bufferAccessOutOfBounds + strncat_s(a,10,"1",2); + // TODO cppcheck-suppress bufferAccessOutOfBounds + strncat_s(a,5,"1",5); + fread(a,1,5,stdin); + // cppcheck-suppress bufferAccessOutOfBounds + fread(a,1,6,stdin); + fwrite(a,1,5,stdout); + // cppcheck-suppress bufferAccessOutOfBounds + fread(a,1,6,stdout); + + char * pAlloc1 = aligned_alloc(8, 16); + memset(pAlloc1, 0, 16); + // cppcheck-suppress bufferAccessOutOfBounds + memset(pAlloc1, 0, 17); + free(pAlloc1); +} + +wchar_t* nullPointer_fgetws(wchar_t* buffer, int n, FILE* stream) +{ + // cppcheck-suppress nullPointer + (void)fgetws(NULL,n,stream); + // cppcheck-suppress nullPointer + (void)fgetws(buffer,n,NULL); + // No warning is expected + return fgetws(buffer, n, stream); +} + +char* nullPointer_fgets(char *buffer, int n, FILE *stream) +{ + // cppcheck-suppress nullPointer + (void)fgets(NULL,n,stream); + // cppcheck-suppress nullPointer + (void)fgets(buffer,n,NULL); + // No warning is expected + return fgets(buffer, n, stream); +} + +void memleak_aligned_alloc(void) +{ + // cppcheck-suppress [unusedAllocatedMemory, unreadVariable, constVariablePointer] + char * alignedBuf = aligned_alloc(8, 16); + // cppcheck-suppress memleak +} + +void pointerLessThanZero_aligned_alloc(void) +{ + char * alignedBuf = aligned_alloc(8, 16); + // cppcheck-suppress pointerLessThanZero + if (alignedBuf < 0) return; + free(alignedBuf); + + // no warning is expected for + alignedBuf = aligned_alloc(8, 16); + if (alignedBuf == 0) return; + free(alignedBuf); + + // no warning is expected for + alignedBuf = aligned_alloc(8, 16); + if (alignedBuf) free(alignedBuf); +} + +void unusedRetVal_aligned_alloc(void) +{ + // cppcheck-suppress leakReturnValNotUsed + aligned_alloc(8, 16); +} + +void uninitvar_aligned_alloc(size_t alignment, size_t size) +{ + size_t uninitVar1, uninitVar2, uninitVar3; + // cppcheck-suppress uninitvar + free(aligned_alloc(uninitVar1, size)); + // cppcheck-suppress uninitvar + free(aligned_alloc(alignment, uninitVar2)); + // cppcheck-suppress uninitvar + free(aligned_alloc(uninitVar3, uninitVar3)); + // no warning is expected + free(aligned_alloc(alignment, size)); +} + +void bufferAccessOutOfBounds_libraryDirectionConfiguration(void) +{ + // This tests whether the argument to isdigit() is configured with direction "in". This allows + // Cppcheck to report the error without marking it as inconclusive. + char arr[10]; + char c = 'A'; + (void)isdigit(c); + // cppcheck-suppress arrayIndexOutOfBounds + // cppcheck-suppress unreadVariable + arr[c] = 'x'; +} + +void arrayIndexOutOfBounds() +{ + char * pAlloc1 = aligned_alloc(8, 16); + pAlloc1[15] = '\0'; + // cppcheck-suppress arrayIndexOutOfBounds + pAlloc1[16] = '1'; + free(pAlloc1); + + char * pAlloc2 = malloc(9); + pAlloc2[8] = 'a'; + // cppcheck-suppress arrayIndexOutOfBounds + pAlloc2[9] = 'a'; + + // #1379 + // cppcheck-suppress memleakOnRealloc + pAlloc2 = realloc(pAlloc2, 8); + pAlloc2[7] = 'b'; + // cppcheck-suppress arrayIndexOutOfBounds + pAlloc2[8] = 0; + // cppcheck-suppress memleakOnRealloc + pAlloc2 = realloc(pAlloc2, 20); + pAlloc2[19] = 'b'; + // cppcheck-suppress arrayIndexOutOfBounds + pAlloc2[20] = 0; + free(pAlloc2); + + char * pAlloc3 = calloc(2,3); + pAlloc3[5] = 'a'; + // cppcheck-suppress arrayIndexOutOfBounds + pAlloc3[6] = 1; + free(pAlloc3); +} + +void resourceLeak_tmpfile(void) +{ + // cppcheck-suppress [unreadVariable, constVariablePointer] + FILE * fp = tmpfile(); + // cppcheck-suppress resourceLeak +} + +// memory leak + +void ignoreleak(void) +{ + char *p = (char *)malloc(10); + memset(&(p[0]), 0, 10); + // cppcheck-suppress memleak +} + +// null pointer + +void nullpointer(int value) +{ + int res = 0; + FILE *fp; + wchar_t *pWcsUninit; + +#ifndef __CYGWIN__ + // cppcheck-suppress nullPointer + clearerr(0); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + feof(0); +#endif + // cppcheck-suppress nullPointer + (void)fgetc(0); + // cppcheck-suppress nullPointer + fclose(0); +#ifndef __CYGWIN__ + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + ferror(0); +#endif + // cppcheck-suppress nullPointer + (void)ftell(0); + // cppcheck-suppress nullPointer + puts(0); + // cppcheck-suppress nullPointer + fp=fopen(0,0); + fclose(fp); + fp = 0; + // No FP + fflush(0); // If stream is a null pointer, all streams are flushed. + // cppcheck-suppress valueFlowBailoutIncompleteVar + fp = freopen(0,"abc",stdin); + fclose(fp); + fp = NULL; + // cppcheck-suppress nullPointer + fputc(0,0); + // cppcheck-suppress nullPointer + fputs(0,0); + // cppcheck-suppress nullPointer + fgetpos(0,0); + // cppcheck-suppress nullPointer + frexp(1.0,0); + // cppcheck-suppress nullPointer + fsetpos(0,0); + // cppcheck-suppress nullPointer + itoa(123,0,10); + putchar(0); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + strchr(0,0); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + wcschr(0,0); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + strlen(0); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + wcslen(0); + // cppcheck-suppress nullPointer + strcpy(0,0); + // cppcheck-suppress nullPointer + wcscpy(0,0); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + strspn(0,0); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + wcsspn(0,0); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + strcspn(0,0); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + wcscspn(0,0); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + strcoll(0,0); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + wcscoll(0,0); + // cppcheck-suppress nullPointer + strcat(0,0); + // cppcheck-suppress nullPointer + wcscat(0,0); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + strcmp(0,0); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + wcscmp(0,0); + // cppcheck-suppress nullPointer + strcpy_s(0,1,1); + // cppcheck-suppress nullPointer + strcpy_s(1,1,0); + // cppcheck-suppress nullPointer + strncpy(0,0,1); + // cppcheck-suppress nullPointer + strncpy_s(0,1,1,1); + // cppcheck-suppress nullPointer + strncpy_s(1,1,0,1); + // cppcheck-suppress nullPointer + wcsncpy(0,0,1); + // cppcheck-suppress nullPointer + strncat(0,0,1); + // cppcheck-suppress nullPointer + strncat_s(0,1,1,1); + // cppcheck-suppress nullPointer + strncat_s(1,1,0,1); + // cppcheck-suppress nullPointer + wcsncat(0,0,1); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + strncmp(0,0,1); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + wcsncmp(0,0,1); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + strstr(0,0); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress nullPointer + wcsstr(0,0); + // cppcheck-suppress nullPointer + strtoul(0,0,0); + // cppcheck-suppress nullPointer + wcstoul(0,0,0); + // cppcheck-suppress nullPointer + strtoull(0,0,0); + // cppcheck-suppress nullPointer + wcstoull(0,0,0); + // cppcheck-suppress nullPointer + strtol(0,0,0); + // cppcheck-suppress nullPointer + wcstol(0,0,0); + + // #6100 False positive nullPointer - calling mbstowcs(NULL,) + res += mbstowcs(0,"",0); + res += wcstombs(0,L"",0); + + strtok(NULL,"xyz"); + wcstok(NULL,L"xyz",&pWcsUninit); + + strxfrm(0,"foo",0); + // TODO: error message (#6306 and http://trac.cppcheck.net/changeset/d11eb4931aea51cf2cb74faccdcd2a3289b818d6/) + strxfrm(0,"foo",42); + wcsxfrm(0,L"foo",0); + // TODO: error message when arg1==NULL and arg3!=0 #6306: https://trac.cppcheck.net/ticket/6306#comment:2 + wcsxfrm(0,L"foo",42); + + snprintf(NULL, 0, "someformatstring"); // legal + // cppcheck-suppress nullPointer + snprintf(NULL, 42, "someformatstring"); // not legal + + scanf("%i", &res); + // cppcheck-suppress nullPointer + scanf("%i", NULL); + wscanf(L"%i", &res); + // cppcheck-suppress nullPointer + wscanf(L"%i", NULL); +} + +void nullPointer_wcsftime(wchar_t* ptr, size_t maxsize, const wchar_t* format, const struct tm* timeptr) +{ + // cppcheck-suppress nullPointer + (void)wcsftime(NULL, maxsize, format, timeptr); + // cppcheck-suppress nullPointer + (void)wcsftime(ptr, maxsize, NULL, timeptr); + // cppcheck-suppress nullPointer + (void)wcsftime(ptr, maxsize, format, NULL); + (void)wcsftime(ptr, maxsize, format, timeptr); +} + +void bufferAccessOutOfBounds_wcsftime(wchar_t* ptr, size_t maxsize, const wchar_t* format, const struct tm* timeptr) +{ + wchar_t buf[42]; + (void)wcsftime(buf, 42, format, timeptr); + // TODO cppcheck-suppress bufferAccessOutOfBounds + (void)wcsftime(buf, 43, format, timeptr); + (void)wcsftime(ptr, maxsize, format, timeptr); +} + +void bufferAccessOutOfBounds_wcsncpy() +{ + wchar_t s[16]; + wcsncpy(s, L"abc", 16); + // cppcheck-suppress bufferAccessOutOfBounds + wcsncpy(s, L"abc", 17); +} + +int nullPointer_wcsncmp(const wchar_t* s1, const wchar_t* s2, size_t n) +{ + // cppcheck-suppress nullPointer + (void) wcsncmp(NULL,s2,n); + // cppcheck-suppress nullPointer + (void) wcsncmp(s1,NULL,n); + return wcsncmp(s1,s2,n); +} + +wchar_t* nullPointer_wcsncpy(wchar_t *s, const wchar_t *cs, size_t n) +{ + // cppcheck-suppress nullPointer + (void) wcsncpy(NULL,cs,n); + // cppcheck-suppress nullPointer + (void) wcsncpy(s,NULL,n); + return wcsncpy(s,cs,n); +} + +size_t nullPointer_strlen(const char *s) +{ + // cppcheck-suppress nullPointer + (void) strlen(NULL); + return strlen(s); +} + +void nullpointerMemchr1(char *p, const char *s) +{ + p = memchr(s, 'p', strlen(s)); + (void)p; +} + +void nullpointerMemchr2(char *p, const char *s) +{ + p = memchr(s, 0, strlen(s)); + (void)p; +} + +void nullPointer_memchr(char *p) +{ + const char *s = 0; + // cppcheck-suppress nullPointer + p = memchr(s, 0, strlen(s)); + (void)p; +} + +void nullPointer_vsnprintf(const char * format, ...) +{ + va_list args; + // valid + char buffer[256]; + va_start(args, format); + vsnprintf(buffer, 256, format, args); + printf("%s", buffer); + va_end(args); + // valid + va_start(args, format); + vsnprintf(NULL, 0, format, args); + va_end(args); + // invalid + va_start(args, format); + // TODO #9410 cppcheck-suppress nullPointer + vsnprintf(NULL, 10, format, args); + va_end(args); +} + +// uninit pointers + +void uninitvar_abs(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)abs(i); +} + +void uninitvar_clearerr(void) +{ + FILE *fp; + // cppcheck-suppress uninitvar + clearerr(fp); +} + +void uninitvar_fclose(void) +{ + // cppcheck-suppress unassignedVariable + FILE *fp; + // cppcheck-suppress uninitvar + fclose(fp); +} + +void uninitvar_fopen(void) +{ + const char *filename, *mode; + FILE *fp; + // cppcheck-suppress uninitvar + fp = fopen(filename, "rt"); + fclose(fp); + // cppcheck-suppress uninitvar + fp = fopen("filename.txt", mode); + fclose(fp); +} + +void uninitvar_feof(void) +{ + FILE *fp1, *fp2; + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress uninitvar + feof(fp1); + + // cppcheck-suppress uninitvar + (void)feof(fp2); +} + +void uninitvar_ferror(void) +{ + FILE *fp1, *fp2; + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress uninitvar + ferror(fp1); + + // cppcheck-suppress uninitvar + (void)ferror(fp2); +} + +void uninitvar_fflush(void) +{ + FILE *fp; + // cppcheck-suppress uninitvar + fflush(fp); +} + +void uninitvar_fgetc(void) +{ + FILE *fp; + // cppcheck-suppress uninitvar + (void)fgetc(fp); +} + +void uninitvar_fgetpos(void) +{ + FILE *fp; + fpos_t pos; + fpos_t *ppos; + // cppcheck-suppress uninitvar + fgetpos(fp,&pos); + + fp = fopen("filename","rt"); + // cppcheck-suppress uninitvar + fgetpos(fp,ppos); + fclose(fp); +} + +void uninitvar_fsetpos(void) +{ + FILE *fp; + fpos_t pos; + const fpos_t *ppos; + // cppcheck-suppress uninitvar + fsetpos(fp,&pos); + + fp = fopen("filename","rt"); + // cppcheck-suppress uninitvar + fsetpos(fp,ppos); + fclose(fp); +} + +void uninitvar_fgets(void) +{ + FILE *fp; + char buf[10]; + char *str; + int n; + + // cppcheck-suppress valueFlowBailoutIncompleteVar + fgets(buf,10,stdin); + + // cppcheck-suppress uninitvar + fgets(str,10,stdin); + + // cppcheck-suppress uninitvar + fgets(buf,10,fp); + + // cppcheck-suppress uninitvar + fgets(buf,n,stdin); +} + +void uninitvar_fputc(void) +{ + int i; + FILE *fp; + + // cppcheck-suppress valueFlowBailoutIncompleteVar + fputc('a', stdout); + + // cppcheck-suppress uninitvar + fputc(i, stdout); + + // cppcheck-suppress uninitvar + fputc('a', fp); +} + +void uninitvar_fputs(void) +{ + const char *s; + FILE *fp; + + // cppcheck-suppress valueFlowBailoutIncompleteVar + fputs("a", stdout); + + // cppcheck-suppress uninitvar + fputs(s, stdout); + + // cppcheck-suppress uninitvar + fputs("a", fp); +} + +void uninitvar_ftell(void) +{ + FILE *fp; + // cppcheck-suppress uninitvar + (void)ftell(fp); +} + +void uninitvar_puts(void) +{ + const char *s; + // cppcheck-suppress uninitvar + puts(s); +} + +void uninitvar_putchar(void) +{ + char c; + // cppcheck-suppress uninitvar + putchar(c); +} + +void uninitvar_cproj(void) // #6939 +{ + float complex fc; + // cppcheck-suppress uninitvar + (void)cprojf(fc); + + double complex dc; + // cppcheck-suppress uninitvar + (void)cproj(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)cprojl(ldc); +} + +void uninitvar_creal(void) +{ + float complex fc; + // cppcheck-suppress uninitvar + (void)crealf(fc); + + double complex dc; + // cppcheck-suppress uninitvar + (void)creal(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)creall(ldc); +} + +void uninitvar_acos(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)acosf(f); + + double d; + // cppcheck-suppress uninitvar + (void)acos(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)acosl(ld); +} + +void uninitvar_acosh(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)acoshf(f); + + double d; + // cppcheck-suppress uninitvar + (void)acosh(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)acoshl(ld); +} + +void invalidFunctionArg_acosh(void) +{ + float f = .999f; + // cppcheck-suppress invalidFunctionArg + (void)acoshf(f); + f = 1.0f; + (void)acoshf(f); + + double d = .999; + // cppcheck-suppress invalidFunctionArg + (void)acosh(d); + d = 1.0; + (void)acosh(d); + + long double ld = .999L; + // cppcheck-suppress invalidFunctionArg + (void)acoshl(ld); + ld = 1.0; + (void)acoshl(ld); +} + +void invalidFunctionArg_atanh(void) +{ + float f = 1.00001f; + // cppcheck-suppress invalidFunctionArg + (void)atanhf(f); + f = 1.0f; + (void)atanhf(f); + f = -1.0f; + (void)atanhf(f); + f = -1.00001f; + // cppcheck-suppress invalidFunctionArg + (void)atanhf(f); + + double d = 1.00001; + // cppcheck-suppress invalidFunctionArg + (void)atanh(d); + d = 1.0; + (void)atanh(d); + d = -1.0; + (void)atanh(d); + d = -1.00001; + // cppcheck-suppress invalidFunctionArg + (void)atanh(d); + + long double ld = 1.00001L; + // cppcheck-suppress invalidFunctionArg + (void)atanhl(ld); + ld = 1.0L; + (void)atanhl(ld); + ld = -1.0L; + (void)atanhl(ld); + ld = -1.00001L; + // cppcheck-suppress invalidFunctionArg + (void)atanhl(ld); +} + +void uninitvar_asctime(void) +{ + const struct tm *tm; + // cppcheck-suppress uninitvar + // cppcheck-suppress asctimeCalled + (void)asctime(tm); +} + +void uninitvar_asctime_s(void) +{ + const struct tm *tm; + char buf[26]; + // cppcheck-suppress uninitvar + // cppcheck-suppress asctime_sCalled + asctime_s(buf, sizeof(buf), tm); +} + +void uninitvar_assert(void) +{ + int i; + // cppcheck-suppress checkLibraryNoReturn + // cppcheck-suppress uninitvar + assert(i); +} + +void uninitvar_sqrt(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)sqrtf(f); + + double d; + // cppcheck-suppress uninitvar + (void)sqrt(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)sqrtl(ld); +} + +void uninitvar_csqrt(void) +{ + float complex fc; + // cppcheck-suppress uninitvar + (void)csqrtf(fc); + + double complex dc; + // cppcheck-suppress uninitvar + (void)csqrt(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)csqrtl(ldc); +} + +void uninitvar_sinh(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)sinhf(f); + + double d; + // cppcheck-suppress uninitvar + (void)sinh(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)sinhl(ld); +} + +void uninitvar_sin(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)sinf(f); + + double d; + // cppcheck-suppress uninitvar + (void)sin(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)sinl(ld); +} + +void uninitvar_csin(void) +{ + float complex fd; + // cppcheck-suppress uninitvar + (void)csinf(fd); + + double complex dc; + // cppcheck-suppress uninitvar + (void)csin(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)csinl(ldc); +} + +void uninitvar_csinh(void) +{ + float complex fd; + // cppcheck-suppress uninitvar + (void)csinhf(fd); + + double complex dc; + // cppcheck-suppress uninitvar + (void)csinh(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)csinhl(ldc); +} + +void uninitvar_asin(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)asinf(f); + + double d; + // cppcheck-suppress uninitvar + (void)asin(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)asinl(ld); +} + +void uninitvar_casin(void) +{ + float complex fd; + // cppcheck-suppress uninitvar + (void)casinf(fd); + + double complex dc; + // cppcheck-suppress uninitvar + (void)casin(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)casinl(ldc); +} + +void uninitvar_asinh(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)asinhf(f); + + double d; + // cppcheck-suppress uninitvar + (void)asinh(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)asinhl(ld); +} + +void uninitvar_casinh(void) +{ + float complex fd; + // cppcheck-suppress uninitvar + (void)casinhf(fd); + + double complex dc; + // cppcheck-suppress uninitvar + (void)casinh(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)casinhl(ldc); +} + +void uninitvar_wcsftime(wchar_t* ptr) +{ + size_t maxsize; + const wchar_t* format; + const struct tm* timeptr; + // cppcheck-suppress uninitvar + (void)wcsftime(ptr, maxsize, format, timeptr); +} + +void uninitvar_tan(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)tanf(f); + + double d; + // cppcheck-suppress uninitvar + (void)tan(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)tanl(ld); +} + +void uninitvar_ctan(void) +{ + float complex fd; + // cppcheck-suppress uninitvar + (void)ctanf(fd); + + double complex dc; + // cppcheck-suppress uninitvar + (void)ctan(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)ctanl(ldc); +} + +void uninitvar_tanh(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)tanhf(f); + + double d; + // cppcheck-suppress uninitvar + (void)tanh(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)tanhl(ld); +} + +void uninitvar_ctanh(void) +{ + float complex fd; + // cppcheck-suppress uninitvar + (void)ctanhf(fd); + + double complex dc; + // cppcheck-suppress uninitvar + (void)ctanh(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)ctanhl(ldc); +} + +void uninitvar_feclearexcept(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)feclearexcept(i); +} + +void uninitvar_fegetexceptflag(fexcept_t* flagp) +{ + int excepts; + // cppcheck-suppress uninitvar + (void)fegetexceptflag(flagp, excepts); +} + +void uninitvar_feraiseexcept(void) +{ + int excepts; + // cppcheck-suppress uninitvar + (void)feraiseexcept(excepts); +} + +void uninitvar_fesetenv(void) +{ + const fenv_t* envp; + // cppcheck-suppress uninitvar + (void)fesetenv(envp); +} + +void uninitvar_fesetround(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)fesetround(i); +} + +void uninitvar_fetestexcept(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)fetestexcept(i); +} + +void uninitvar_feupdateenv(void) +{ + const fenv_t* envp; + // cppcheck-suppress uninitvar + (void)feupdateenv(envp); +} + +void uninitvar_atan(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)atanf(f); + + double d; + // cppcheck-suppress uninitvar + (void)atan(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)atanl(ld); +} + +void uninitvar_catan(void) +{ + float complex fd; + // cppcheck-suppress uninitvar + (void)catanf(fd); + + double complex dc; + // cppcheck-suppress uninitvar + (void)catan(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)catanl(ldc); +} + +void uninitvar_tgamma(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)tgammaf(f); + + double d; + // cppcheck-suppress uninitvar + (void)tgamma(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)tgammal(ld); +} + +void uninitvar_trunc(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)truncf(f); + + double d; + // cppcheck-suppress uninitvar + (void)trunc(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)truncl(ld); +} + +void uninitvar_atanh(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)atanhf(f); + + double d; + // cppcheck-suppress uninitvar + (void)atanh(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)atanhl(ld); +} + +void uninitvar_catanh(void) +{ + float complex fd; + // cppcheck-suppress uninitvar + (void)catanhf(fd); + + double complex dc; + // cppcheck-suppress uninitvar + (void)catanh(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)catanhl(ldc); +} + +void uninitvar_atan2(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)atan2f(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)atan2(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)atan2l(ld1,ld2); +} + +void uninitvar_atof(void) +{ + const char * c; + // cppcheck-suppress uninitvar + (void)atof(c); +} + +void uninitvar_atol(void) +{ + const char * c1, *c2, *c3; + // cppcheck-suppress uninitvar + (void)atoi(c1); + + // cppcheck-suppress uninitvar + (void)atol(c2); + + // cppcheck-suppress uninitvar + (void)atoll(c3); +} + +void uninitvar_calloc(void) +{ + size_t nitems; + size_t size; + // cppcheck-suppress unusedAllocatedMemory + // cppcheck-suppress uninitvar + int * p = (int*) calloc(nitems, size); + free(p); +} + +void uninitvar_ceil(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)ceilf(f); + + double d; + // cppcheck-suppress uninitvar + (void)ceil(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)ceill(ld); +} + +void uninitvar_copysign(void) +{ + float f1, f2; + // cppcheck-suppress uninitvar + (void)copysignf(f1, f2); + + double d1, d2; + // cppcheck-suppress uninitvar + (void)copysign(d1, d2); + + long double ld1, ld2; + // cppcheck-suppress uninitvar + (void)copysignl(ld1, ld2); +} + +void uninitvar_cbrt(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)cbrtf(f); + + double d; + // cppcheck-suppress uninitvar + (void)cbrt(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)cbrtl(ld); +} + +void uninitvar_cos(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)cosf(f); + + double d; + // cppcheck-suppress uninitvar + (void)cos(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)cosl(ld); +} + +void uninitvar_ccos(void) +{ + float complex fd; + // cppcheck-suppress uninitvar + (void)ccosf(fd); + + double complex dc; + // cppcheck-suppress uninitvar + (void)ccos(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)ccosl(ldc); +} + +void uninitvar_cosh(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)coshf(f); + + double d; + // cppcheck-suppress uninitvar + (void)cosh(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)coshl(ld); +} + +void uninitvar_ccosh(void) +{ + float complex fd; + // cppcheck-suppress uninitvar + (void)ccoshf(fd); + + double complex dc; + // cppcheck-suppress uninitvar + (void)ccosh(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)ccoshl(ldc); +} + +void uninitvar_ctime(void) +{ + const time_t *tp; + // cppcheck-suppress uninitvar + (void)ctime(tp); +} + +void uninitvar_difftime(void) +{ + time_t t1,t2; + // cppcheck-suppress uninitvar + (void)difftime(t1, t2); +} + +void uninitvar_div(void) +{ + int num; + int denom; + // cppcheck-suppress uninitvar + (void)div(num,denom); +} + +void uninitvar_exit(void) +{ + int i; + // cppcheck-suppress uninitvar + exit(i); +} + +void uninitvar_erf(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)erff(f); + + double d; + // cppcheck-suppress uninitvar + (void)erf(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)erfl(ld); +} + +void uninitvar_erfc(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)erfcf(f); + + double d; + // cppcheck-suppress uninitvar + (void)erfc(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)erfcl(ld); +} + +void uninitvar_carg(void) +{ + float complex fd; + // cppcheck-suppress uninitvar + (void)cargf(fd); + + double complex dc; + // cppcheck-suppress uninitvar + (void)carg(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)cargl(ldc); +} + +void uninitvar_exp(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)expf(f); + + double d; + // cppcheck-suppress uninitvar + (void)exp(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)expl(ld); +} + +void uninitvar_cexp(void) +{ + float complex fd; + // cppcheck-suppress uninitvar + (void)cexpf(fd); + + double complex dc; + // cppcheck-suppress uninitvar + (void)cexp(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)cexpl(ldc); +} + +void uninitvar_cimag(void) +{ + float complex fd; + // cppcheck-suppress uninitvar + (void)cimagf(fd); + + double complex dc; + // cppcheck-suppress uninitvar + (void)cimag(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)cimagl(ldc); +} + +void uninitvar_exp2(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)exp2f(f); + + double d; + // cppcheck-suppress uninitvar + (void)exp2(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)exp2l(ld); +} + +void uninitvar_expm1(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)expm1f(f); + + double d; + // cppcheck-suppress uninitvar + (void)expm1(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)expm1l(ld); +} + +void uninitvar_fabs(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)fabsf(f); + + double d; + // cppcheck-suppress uninitvar + (void)fabs(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)fabsl(ld); +} + +void uninitvar_fdim(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)fdimf(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)fdim(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)fdiml(ld1,ld2); +} + +void uninitvar_fgetwc(void) +{ + FILE *stream; + // cppcheck-suppress uninitvar + (void)fgetwc(stream); +} + +void uninitvar_floor(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)floorf(f); + + double d; + // cppcheck-suppress uninitvar + (void)floor(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)floorl(ld); +} + +void uninitvar_fma(void) +{ + float f1,f2,f3; + // cppcheck-suppress uninitvar + (void)fmaf(f1,f2,f3); + + double d1,d2,d3; + // cppcheck-suppress uninitvar + (void)fma(d1,d2,d3); + + long double ld1,ld2,ld3; + // cppcheck-suppress uninitvar + (void)fmal(ld1,ld2,ld3); +} + +void uninitvar_fmax(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)fmaxf(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)fmax(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)fmaxl(ld1,ld2); +} + +void uninitvar_fmin(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)fminf(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)fmin(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)fminl(ld1,ld2); +} + +void uninitvar_fmod(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)fmodf(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)fmod(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)fmodl(ld1,ld2); +} + +void nullPointer_fprintf(FILE *Stream, const char *Format, int Argument) +{ + // cppcheck-suppress nullPointer + (void)fprintf(Stream, NULL, Argument); + // no warning is expected + (void)fprintf(Stream, Format, Argument); +} + +void uninitvar_fprintf(FILE *Stream, const char *Format, int Argument) +{ + FILE *stream1, *stream2; + const char *format1, *format2; + int argument1, argument2; + // cppcheck-suppress uninitvar + (void)fprintf(stream1, format1, argument1); + // cppcheck-suppress uninitvar + (void)fprintf(stream2, Format, Argument); + // cppcheck-suppress uninitvar + (void)fprintf(Stream, format2, Argument); + // cppcheck-suppress uninitvar + (void)fprintf(Stream, Format, argument2); + + // no warning is expected + (void)fprintf(Stream, Format, Argument); +} + +void nullPointer_vfprintf(FILE *Stream, const char *Format, va_list Arg) +{ + // cppcheck-suppress nullPointer + (void)vfprintf(Stream, NULL, Arg); + (void)vfprintf(Stream, Format, Arg); +} + +void uninitvar_vfprintf(FILE *Stream, const char *Format, va_list Arg) +{ + FILE *stream1, *stream2; + const char *format1, *format2; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)vfprintf(stream1, format1, arg); + // cppcheck-suppress uninitvar + (void)vfprintf(stream2, Format, Arg); + // cppcheck-suppress uninitvar + (void)vfprintf(Stream, format2, Arg); + + // no warning is expected + (void)vfprintf(Stream, Format, Arg); + // cppcheck-suppress va_list_usedBeforeStarted + (void)vfprintf(Stream, Format, arg); +} + +void nullPointer_vfwprintf(FILE *Stream, const wchar_t *Format, va_list Arg) +{ + // cppcheck-suppress nullPointer + (void)vfwprintf(Stream, NULL, Arg); + (void)vfwprintf(Stream, Format, Arg); +} + +void uninitvar_vfwprintf(FILE *Stream, const wchar_t *Format, va_list Arg) +{ + FILE *stream1, *stream2; + const wchar_t *format1, *format2; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)vfwprintf(stream1, format1, arg); + // cppcheck-suppress uninitvar + (void)vfwprintf(stream2, Format, Arg); + // cppcheck-suppress uninitvar + (void)vfwprintf(Stream, format2, Arg); + + // no warning is expected + (void)vfwprintf(Stream, Format, Arg); + // cppcheck-suppress va_list_usedBeforeStarted + (void)vfwprintf(Stream, Format, arg); +} + +void uninitvar_fputwc(void) +{ + wchar_t c; + FILE *stream; + // cppcheck-suppress uninitvar + (void)fputwc(c,stream); +} + +void uninitvar_fputws(void) +{ + const wchar_t *string; + FILE *stream; + // cppcheck-suppress uninitvar + (void)fputws(string,stream); +} + +void uninitvar_fread(void) +{ + void *ptr; + size_t size; + size_t nobj; + FILE *stream; + // cppcheck-suppress uninitvar + (void)fread(ptr,size,nobj,stream); +} + +void uninitvar_free(void) +{ + // cppcheck-suppress unassignedVariable + const void *block; + // cppcheck-suppress uninitvar + free(block); +} + +void uninitvar_freopen(void) +{ + const char *filename; + const char *mode; + FILE *stream; + // cppcheck-suppress uninitvar + FILE * p = freopen(filename,mode,stream); + fclose(p); +} + +void uninitvar_frexp(void) +{ + float f1; + int *i1; + // cppcheck-suppress uninitvar + (void)frexpf(f1,i1); + + double d1; + int *i2; + // cppcheck-suppress uninitvar + (void)frexp(d1,i2); + + long double ld1; + int *i3; + // cppcheck-suppress uninitvar + (void)frexpl(ld1,i3); +} + +void uninitvar_hypot(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)hypotf(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)hypot(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)hypotl(ld1,ld2); +} + +void uninitvar_fscanf(void) +{ + FILE *stream; + const char *format; + int i; + // cppcheck-suppress uninitvar + (void)fscanf(stream,format,i); +} + +void uninitvar_vfscanf(void) +{ + FILE *stream; + const char * format; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)vfscanf(stream,format,arg); +} + +void uninitvar_vfwscanf(void) +{ + FILE *stream; + const wchar_t *format; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)vfwscanf(stream,format,arg); +} + +void uninitvar_fseek(void) +{ + FILE* stream; + long int offset; + int origin; + // cppcheck-suppress uninitvar + (void)fseek(stream,offset,origin); +} + +void uninitvar_fgetws(void) +{ + wchar_t *buffer; + int n; + FILE *stream; + // cppcheck-suppress uninitvar + (void)fgetws(buffer,n,stream); +} + +void uninitvar_fwide(void) +{ + FILE *stream; + int mode; + // cppcheck-suppress uninitvar + (void)fwide(stream,mode); +} + +void uninitvar_fwrite(void) +{ + const void *ptr; + size_t size; + size_t nobj; + FILE *stream; + // cppcheck-suppress uninitvar + (void)fwrite(ptr,size,nobj,stream); +} + +void uninitvar_mblen(void) +{ + const char *string; + size_t size; + // cppcheck-suppress uninitvar + (void)mblen(string,size); +} + +void uninitvar_mbtowc(void) +{ + wchar_t* pwc; + const char* pmb; + size_t max; + // cppcheck-suppress uninitvar + (void)mbtowc(pwc,pmb,max); +} + +void uninitvar_mbrlen(const char* p, size_t m, mbstate_t* s) +{ + const char* pmb1, *pmb2; + size_t max1, max2; + mbstate_t* ps1, *ps2; + // cppcheck-suppress uninitvar + (void)mbrlen(pmb1,max1,ps1); + // cppcheck-suppress uninitvar + (void)mbrlen(pmb2,m,s); + // cppcheck-suppress uninitvar + (void)mbrlen(p,max2,s); + // cppcheck-suppress uninitvar + (void)mbrlen(p,m,ps2); + // no warning is expected + (void)mbrlen(p,m,s); +} + +void nullPointer_mbrlen(const char* p, size_t m, mbstate_t* s) +{ + /* no warning is expected: A call to the function with a null pointer as pmb resets the shift state (and ignores parameter max). */ + (void)mbrlen(NULL,m,s); + (void)mbrlen(NULL,0,s); + /* cppcheck-suppress nullPointer */ + (void)mbrlen(p,m,NULL); +} + +void uninitvar_btowc(void) +{ + int c; + // cppcheck-suppress uninitvar + (void)btowc(c); +} + +void uninitvar_mbsinit(void) +{ + const mbstate_t* ps; + // cppcheck-suppress uninitvar + (void)mbsinit(ps); +} + +void uninitvar_mbstowcs(wchar_t* d, const char* s, size_t m) +{ + wchar_t *dest; + const char *src; + size_t max; + + // cppcheck-suppress uninitvar + (void)mbstowcs(dest,s,m); + // cppcheck-suppress uninitvar + (void)mbstowcs(d,src,m); + // cppcheck-suppress uninitvar + (void)mbstowcs(d,s,max); + + // No warning is expected + (void)mbstowcs(d,s,m); + + wchar_t buf[100]; + (void)mbstowcs(buf,s,100); +} + +void uninitvar_mbsrtowcs(wchar_t* d, const char** s, size_t m, mbstate_t *p) +{ + wchar_t* dest; + const char* src; + size_t max; + mbstate_t* ps; + + // cppcheck-suppress uninitvar + (void)mbsrtowcs(dest,s,m,p); + // cppcheck-suppress uninitvar + (void)mbsrtowcs(d,&src,m,p); + // cppcheck-suppress uninitvar + (void)mbsrtowcs(d,s,max,p); + // cppcheck-suppress uninitvar + (void)mbsrtowcs(d,s,m,ps); + + // No warning is expected + (void)mbsrtowcs(d,s,m,p); +} + +void uninitvar_wctob(void) +{ + wint_t wc; + // cppcheck-suppress uninitvar + (void)wctob(wc); +} + +void uninitvar_wctomb(void) +{ + char *s; + wchar_t wc; + // cppcheck-suppress uninitvar + (void)wctomb(s,wc); +} + +void uninitvar_wcstombs(void) +{ + char *mbstr; + const wchar_t *wcstr; + size_t n; + // cppcheck-suppress uninitvar + (void)wcstombs(mbstr,wcstr,n); +} + +void uninitvar_getc(void) +{ + FILE *stream; + // cppcheck-suppress uninitvar + (void)getc(stream); +} + +void uninitvar_getwc(void) +{ + FILE *stream; + // cppcheck-suppress uninitvar + (void)getwc(stream); +} + +void uninitvar_ungetc(void) +{ + int c; + FILE *stream; + // cppcheck-suppress uninitvar + (void)ungetc(c,stream); +} + +void uninitvar_ungetwc(void) +{ + wint_t c; + FILE *stream; + // cppcheck-suppress uninitvar + (void)ungetwc(c,stream); +} + +void uninitvar_getenv(void) +{ + const char *name; + // cppcheck-suppress uninitvar + (void)getenv(name); +} + +void uninitvar_gets(void) +{ + char *buffer; + // cppcheck-suppress getsCalled + // cppcheck-suppress uninitvar + (void)gets(buffer); +} + +void uninitvar_gmtime(void) +{ + const time_t *tp; + // cppcheck-suppress uninitvar + (void)gmtime(tp); +} + +void uninitvar_isalnum(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)isalnum(i); +} + +void uninitvar_iswalnum(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)iswalnum(i); +} + +void uninitvar_isalpha(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)isalpha(i); +} + +void uninitvar_iswalpha(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)iswalpha(i); +} + +void uninitvar_isblank(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)isblank(i); +} + +void uninitvar_iswblank(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)iswblank(i); +} + +void uninitvar_iscntrl(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)iscntrl(i); +} + +void uninitvar_iswcntrl(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)iswcntrl(i); +} + +void uninitvar_iswctype(void) +{ + wint_t c; + wctype_t desc; + // cppcheck-suppress uninitvar + (void)iswctype(c,desc); +} + +void uninitvar_isdigit(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)isdigit(i); +} + +void uninitvar_iswdigit(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)iswdigit(i); +} + +void uninitvar_isgraph(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)isgraph(i); +} + +void uninitvar_iswgraph(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)iswgraph(i); +} + +void uninitvar_islower(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)islower(i); +} + +void uninitvar_iswlower(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)iswlower(i); +} + +void uninitvar_isprint(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)isprint(i); +} + +void uninitvar_iswprint(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)iswprint(i); +} + +void uninitvar_ispunct(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)ispunct(i); +} + +void uninitvar_iswpunct(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)iswpunct(i); +} + +void uninitvar_isspace(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)isspace(i); +} + +void uninitvar_iswspace(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)iswspace(i); +} + +void uninitvar_isupper(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)isupper(i); +} + +void uninitvar_iswupper(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)iswupper(i); +} + +void uninitvar_isxdigit(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)isxdigit(i); +} + +void uninitvar_iswxdigit(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)iswxdigit(i); +} + +void uninitvar_towctrans(void) +{ + wint_t c; + wctrans_t desc; + // cppcheck-suppress uninitvar + (void)towctrans(c,desc); +} + +void uninitvar_towlower(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)towlower(i); +} + +void uninitvar_towupper(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)towupper(i); +} + +void uninitvar_wctrans(void) +{ + const char* property; + // cppcheck-suppress uninitvar + (void)wctrans(property); +} + +void uninitvar_wctype(void) +{ + const char* property; + // cppcheck-suppress uninitvar + (void)wctype(property); +} + +void ignorereturn(void) +{ + const char szNumbers[] = "2001 60c0c0 -1101110100110100100000 0x6fffff"; + char * pEnd; + strtol(szNumbers,&pEnd,10); +} + +void uninitvar_cabs(void) +{ + float complex fd; + // cppcheck-suppress uninitvar + (void)cabsf(fd); + + double complex dc; + // cppcheck-suppress uninitvar + (void)cabs(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)cabsl(ldc); +} + +void uninitvar_cacos(void) +{ + float complex fd; + // cppcheck-suppress uninitvar + (void)cacosf(fd); + + double complex dc; + // cppcheck-suppress uninitvar + (void)cacos(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)cacosl(ldc); +} + +void uninitvar_cacosh(void) +{ + float complex fd; + // cppcheck-suppress uninitvar + (void)cacoshf(fd); + + double complex dc; + // cppcheck-suppress uninitvar + (void)cacosh(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)cacoshl(ldc); +} + +void uninitvar_labs(void) +{ + long int li; + // cppcheck-suppress uninitvar + (void)labs(li); + + long long int lli; + // cppcheck-suppress uninitvar + (void)llabs(lli); +} + +void uninitvar_ldexp(void) +{ + float f; + int e1; + // cppcheck-suppress uninitvar + (void)ldexpf(f,e1); + + double d; + int e2; + // cppcheck-suppress uninitvar + (void)ldexp(d,e2); + + long double ld; + int e3; + // cppcheck-suppress uninitvar + (void)ldexpl(ld,e3); +} + +void uninitvar_lgamma(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)lgammaf(f); + + double d; + // cppcheck-suppress uninitvar + (void)lgamma(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)lgammal(ld); +} + +void uninitvar_rint(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)rintf(f); + + double d; + // cppcheck-suppress uninitvar + (void)rint(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)rintl(ld); +} + +void uninitvar_lrint(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)lrintf(f); + + double d; + // cppcheck-suppress uninitvar + (void)lrint(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)lrintl(ld); +} + +void uninitvar_llrint(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)llrintf(f); + + double d; + // cppcheck-suppress uninitvar + (void)llrint(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)llrintl(ld); +} + +void uninitvar_lround(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)lroundf(f); + + double d; + // cppcheck-suppress uninitvar + (void)lround(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)lroundl(ld); +} + +void uninitvar_llround(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)llroundf(f); + + double d; + // cppcheck-suppress uninitvar + (void)llround(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)llroundl(ld); +} + +void uninitvar_srand(void) +{ + unsigned int seed; + // cppcheck-suppress uninitvar + (void)srand(seed); +} + +void uninitvar_ldiv(void) +{ + long int l1; + long int l2; + // cppcheck-suppress uninitvar + (void)ldiv(l1,l2); + + long long int ll1; + long long int ll2; + // cppcheck-suppress uninitvar + (void)lldiv(ll1,ll2); +} + +void uninitvar_localtime(void) +{ + const time_t *tp; + // cppcheck-suppress uninitvar + (void)localtime(tp); +} + +void uninitvar_log(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)logf(f); + + double d; + // cppcheck-suppress uninitvar + (void)log(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)logl(ld); +} + +void uninitvar_clog(void) +{ + float complex fc; + // cppcheck-suppress uninitvar + (void)clogf(fc); + + double complex dc; + // cppcheck-suppress uninitvar + (void)clog(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)clogl(ldc); +} + +void uninitvar_conj(void) +{ + float complex fc; + // cppcheck-suppress uninitvar + (void)conjf(fc); + + double complex dc; + // cppcheck-suppress uninitvar + (void)conj(dc); + + long double complex ldc; + // cppcheck-suppress uninitvar + (void)conjl(ldc); +} + +void uninitvar_fpclassify(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)fpclassify(f); + + double d; + // cppcheck-suppress uninitvar + (void)fpclassify(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)fpclassify(ld); +} + +void uninitvar_isfinite(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)isfinite(f); + + double d; + // cppcheck-suppress uninitvar + (void)isfinite(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)isfinite(ld); +} + +void uninitvar_isgreater(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)isgreater(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)isgreater(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)isgreater(ld1,ld2); +} + +void uninitvar_isgreaterequal(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)isgreaterequal(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)isgreaterequal(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)isgreaterequal(ld1,ld2); +} + +void uninitvar_isinf(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)isinf(f); + + double d; + // cppcheck-suppress uninitvar + (void)isinf(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)isinf(ld); +} + +void uninitvar_logb(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)logbf(f); + + double d; + // cppcheck-suppress uninitvar + (void)logb(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)logbl(ld); +} + +void uninitvar_isless(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)isless(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)isless(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)isless(ld1,ld2); +} + +void uninitvar_islessequal(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)islessequal(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)islessequal(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)islessequal(ld1,ld2); +} + +void uninitvar_islessgreater(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)islessgreater(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)islessgreater(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)islessgreater(ld1,ld2); +} + +void uninitvar_nan(void) +{ + const char *tagp1, *tagp2, *tagp3; + // cppcheck-suppress uninitvar + (void)nanf(tagp1); + // cppcheck-suppress uninitvar + (void)nan(tagp2); + // cppcheck-suppress uninitvar + (void)nanl(tagp3); +} + +void uninitvar_isnan(void) +{ + double d; + // cppcheck-suppress uninitvar + (void)isnan(d); +} + +void uninitvar_isnormal(void) +{ + double d; + // cppcheck-suppress uninitvar + (void)isnormal(d); +} + +void uninitvar_isunordered(void) +{ + double d1,d2; + // cppcheck-suppress uninitvar + (void)isunordered(d1,d2); +} + +void uninitvar_ilogb(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)ilogbf(f); + + double d; + // cppcheck-suppress uninitvar + (void)ilogb(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)ilogbl(ld); +} + +void uninitvar_log10(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)log10f(f); + + double d; + // cppcheck-suppress uninitvar + (void)log10(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)log10l(ld); +} + +void uninitvar_log1p(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)log1pf(f); + + double d; + // cppcheck-suppress uninitvar + (void)log1p(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)log1pl(ld); +} + +void uninitvar_log2(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)log2f(f); + + double d; + // cppcheck-suppress uninitvar + (void)log2(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)log2l(ld); +} + +void uninitvar_nearbyint(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)nearbyintf(f); + + double d; + // cppcheck-suppress uninitvar + (void)nearbyint(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)nearbyintl(ld); +} + +void uninitvar_nextafter(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)nextafterf(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)nextafter(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)nextafterl(ld1,ld2); +} + +void uninitvar_nexttoward(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)nexttowardf(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)nexttoward(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)nexttowardl(ld1,ld2); +} + +void uninitvar_longjmp(void) +{ + jmp_buf env; + int val; + // cppcheck-suppress uninitvar + (void)longjmp(env,val); +} + +void uninitvar_malloc(void) +{ + size_t size; + // cppcheck-suppress unusedAllocatedMemory + // cppcheck-suppress uninitvar + int *p = (int*)malloc(size); + free(p); +} + +void uninitvar_alloca(void) +{ + size_t size; + // cppcheck-suppress allocaCalled + // cppcheck-suppress uninitvar + (void)alloca(size); +} + +void uninitvar_memchr(void) +{ + const void *cs; + int c; + size_t n; + // cppcheck-suppress uninitvar + (void)memchr(cs,c,n); +} + +void *bufferAccessOutOfBounds_memchr(const void *s, int c, size_t n) +{ + const char buf[42]={0}; + (void)memchr(buf,c,42); + // cppcheck-suppress bufferAccessOutOfBounds + (void)memchr(buf,c,43); + return memchr(s,c,n); +} + +void uninitvar_wmemchr(void) +{ + const wchar_t *cs; + wchar_t c; + size_t n; + // cppcheck-suppress uninitvar + (void)wmemchr(cs,c,n); +} + +void uninitvar_memcmp(void) +{ + const void *s1; + const void *s2; + size_t n; + // cppcheck-suppress uninitvar + (void)memcmp(s1,s2,n); +} + +void uninitvar_wmemcmp(void) +{ + const wchar_t *s1; + const wchar_t *s2; + size_t n; + // cppcheck-suppress uninitvar + (void)wmemcmp(s1,s2,n); +} + +void uninitvar_memcpy(void) +{ + void *ct; + const void *cs; + size_t n; + // cppcheck-suppress uninitvar + (void)memcpy(ct,cs,n); +} + +void uninitvar_wmemcpy(void) +{ + wchar_t *cs; + const wchar_t *c; + size_t n; + // cppcheck-suppress uninitvar + (void)wmemcpy(cs,c,n); +} + +void uninitvar_memmove(void) +{ + void *ct; + void *cs; + size_t n; + // cppcheck-suppress uninitvar + (void)memmove(ct,cs,n); +} + +void uninitvar_wmemmove(void) +{ + wchar_t *cs; + wchar_t *c; + size_t n; + // cppcheck-suppress uninitvar + (void)wmemmove(cs,c,n); +} + +void uninitvar_memset(void) +{ + void *s; + int c; + size_t n; + // cppcheck-suppress uninitvar + (void)memset(s,c,n); +} + +void uninitvar_wmemset(void) +{ + wchar_t *cs; + wchar_t c; + size_t n; + // cppcheck-suppress uninitvar + (void)wmemset(cs,c,n); +} + +void uninitvar_mktime(void) +{ + struct tm *tp; + // cppcheck-suppress uninitvar + (void)mktime(tp); + + struct tmx *tpx; + // cppcheck-suppress uninitvar + (void)mkxtime(tpx); +} + +void uninitvar_modf(void) +{ + float f1; + float *f2; + // cppcheck-suppress uninitvar + (void)modff(f1,f2); + + double d1; + double *d2; + // cppcheck-suppress uninitvar + (void)modf(d1,d2); + + long double ld1; + long double *ld2; + // cppcheck-suppress uninitvar + (void)modfl(ld1,ld2); +} + +void uninitvar_perror(void) +{ + const char *string; + // cppcheck-suppress uninitvar + (void)perror(string); +} + +void uninitvar_pow(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)powf(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)pow(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)powl(ld1,ld2); +} + +void uninitvar_cpow(void) +{ + float complex f1,f2; + // cppcheck-suppress uninitvar + (void)cpowf(f1,f2); + + double complex d1,d2; + // cppcheck-suppress uninitvar + (void)cpow(d1,d2); + + long double complex ld1,ld2; + // cppcheck-suppress uninitvar + (void)cpowl(ld1,ld2); +} + +void uninitvar_remainder(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)remainderf(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)remainder(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)remainderl(ld1,ld2); +} + +void uninitvar_remquo(void) +{ + float f1,f2; + int *i1; + // cppcheck-suppress uninitvar + (void)remquof(f1,f2,i1); + + double d1,d2; + int *i2; + // cppcheck-suppress uninitvar + (void)remquo(d1,d2,i2); + + long double ld1,ld2; + int *i3; + // cppcheck-suppress uninitvar + (void)remquol(ld1,ld2,i3); +} + +void uninitvar_printf(const char *Format, int Argument) +{ + const char * format_1, * format_2, * format_3; + int argument1, argument2; + // no warning is expected + (void)printf("x"); + // cppcheck-suppress uninitvar + (void)printf(format_1,argument1); + // cppcheck-suppress uninitvar + (void)printf(Format,argument2); + // cppcheck-suppress uninitvar + (void)printf(format_2,Argument); + // cppcheck-suppress uninitvar + (void)printf(format_3,1); + + // no warning is expected + (void)printf(Format,Argument); +} + +void uninitvar_vprintf(const char *Format, va_list Arg) +{ + const char * format1, *format2; + va_list arg1, arg2; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)vprintf(format1,arg1); + // cppcheck-suppress uninitvar + (void)vprintf(format2,Arg); + + // no warning is expected + (void)vprintf(Format,Arg); + // cppcheck-suppress va_list_usedBeforeStarted + (void)vprintf(Format,arg2); +} + +void memleak_strdup (const char *s) // #9328 +{ + const char *s1 = strdup(s); + printf("%s",s1); + free(s); // s1 is not freed + // cppcheck-suppress memleak +} + +void uninitvar_vwprintf(const wchar_t *Format, va_list Arg) +{ + const wchar_t * format1, * format2; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)vwprintf(format1,arg); + // cppcheck-suppress uninitvar + (void)vwprintf(format2,Arg); + + // no warning is expected + (void)vwprintf(Format,Arg); + // cppcheck-suppress va_list_usedBeforeStarted + (void)vwprintf(Format,arg); +} + +void nullPointer_bsearch(const void* key, const void* base, size_t num, size_t size) +{ + // cppcheck-suppress nullPointer + (void)bsearch(NULL,base,num,size,(int (*)(const void*,const void*))strcmp); + // cppcheck-suppress nullPointer + (void)bsearch(key,NULL,num,size,(int (*)(const void*,const void*))strcmp); + // No warning is expected + (void)bsearch(key,base,num,size,(int (*)(const void*,const void*))strcmp); +} + +void uninitvar_bsearch(void) +{ + const void* key; + const void* base; + size_t num; + size_t size; + // cppcheck-suppress uninitvar + (void)bsearch(key,base,num,size,(int (*)(const void*,const void*))strcmp); +} + +void uninitvar_qsort(void) +{ + void *base; + size_t n; + size_t size; + // cppcheck-suppress uninitvar + (void)qsort(base,n,size, (int (*)(const void*,const void*))strcmp); +} + +void uninitvar_putc(void) +{ + int c; + FILE *stream; + // cppcheck-suppress uninitvar + (void)putc(c,stream); +} + +void uninitvar_putwc(void) +{ + wchar_t c; + FILE *stream; + // cppcheck-suppress uninitvar + (void)putc(c,stream); +} + +void uninitvar_putwchar(void) +{ + wchar_t c; + // cppcheck-suppress uninitvar + (void)putwchar(c); +} + +void uninitvar_realloc(void) +{ + void *block; + size_t newsize; + // cppcheck-suppress uninitvar + void *p = realloc(block, newsize); + free(p); +} + +void uninitvar_remove(void) +{ + const char *s; + // cppcheck-suppress uninitvar + (void)remove(s); +} + +void uninitvar_rename(void) +{ + const char *s1; + const char *s2; + // cppcheck-suppress uninitvar + (void)rename(s1,s2); +} + +void uninitvar_rewind(void) +{ + FILE *f; + // cppcheck-suppress uninitvar + (void)rewind(f); +} + +void uninitvar_round(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)roundf(f); + + double d; + // cppcheck-suppress uninitvar + (void)round(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)roundl(ld); +} + +void uninitvar_scalbn(void) +{ + float f; + int i1; + // cppcheck-suppress uninitvar + (void)scalbnf(f,i1); + + double d; + int i2; + // cppcheck-suppress uninitvar + (void)scalbn(d,i2); + + long double ld; + int i3; + // cppcheck-suppress uninitvar + (void)scalbnl(ld,i3); +} + +void uninitvar_scalbln(void) +{ + float f; + long int i1; + // cppcheck-suppress uninitvar + (void)scalblnf(f,i1); + + double d; + long int i2; + // cppcheck-suppress uninitvar + (void)scalbln(d,i2); + + long double ld; + long int i3; + // cppcheck-suppress uninitvar + (void)scalblnl(ld,i3); +} + +void uninitvar_signbit(void) +{ + double d; + // cppcheck-suppress uninitvar + (void)signbit(d); +} + +void uninitvar_signal(void) +{ + int i; + // cppcheck-suppress uninitvar + signal(i, exit); +} + +void uninitvar_raise(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)raise(i); +} + +void uninitvar_scanf(void) +{ + const char *format; + char str[42]; + // cppcheck-suppress uninitvar + (void)scanf(format, str); + + // no warning is expected (#9347) + int i; + sscanf("0", "%d", &i); +} + +void uninitvar_vsscanf(void) +{ + const char *s; + const char *format; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)vsscanf(s,format,arg); +} + +void uninitvar_vswscanf(void) +{ + const wchar_t *s; + const wchar_t *format; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)vswscanf(s,format,arg); +} + +void uninitvar_vscanf(void) +{ + const char *format; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)vscanf(format,arg); +} + +void uninitvar_vwscanf(void) +{ + const wchar_t *format; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)vwscanf(format,arg); +} + +void nullPointer_setbuf(FILE *stream, char *buf) +{ + // cppcheck-suppress nullPointer + setbuf(NULL,buf); + setbuf(stream,NULL); + setbuf(stream,buf); +} + +int bufferAccessOutOfBounds_setvbuf(FILE* stream, int mode, size_t size) +{ + char buf[42]={0}; + // cppcheck-suppress bufferAccessOutOfBounds + (void) setvbuf(stream, buf, mode, 43); + return setvbuf(stream, buf, mode, 42); +} + +int nullPointer_setvbuf(FILE* stream, char *buf, int mode, size_t size) +{ + // cppcheck-suppress nullPointer + (void) setvbuf(NULL, buf, mode, size); + (void) setvbuf(stream, NULL, mode, size); + return setvbuf(stream, buf, mode, size); +} + +void uninitvar_setbuf(void) +{ + FILE *stream; + char *buf; + // cppcheck-suppress uninitvar + setbuf(stream,buf); +} + +void uninitvar_setvbuf(void) +{ + FILE *stream; + char *buf; + int mode; + size_t size; + // cppcheck-suppress uninitvar + (void)setvbuf(stream,buf,mode,size); +} + +void uninitvar_strcat(char *dest, const char * const source) +{ + char *deststr1, *deststr2; + const char *srcstr1, *srcstr2; + // cppcheck-suppress uninitvar + (void)strcat(deststr1,srcstr1); + // cppcheck-suppress uninitvar + (void)strcat(dest,srcstr2); + // cppcheck-suppress uninitvar + (void)strcat(deststr2,source); + + // no warning shall be shown for + (void)strcat(dest,source); +} + +void nullPointer_strcpy(char *dest, const char * const source) +{ + // cppcheck-suppress nullPointer + (void)strcpy(NULL,source); + // cppcheck-suppress nullPointer + (void)strcpy(dest,NULL); + + // no warning shall be shown for + (void)strcpy(dest,source); +} + +void nullPointer_strcat(char *dest, const char * const source) +{ + // cppcheck-suppress nullPointer + (void)strcat(NULL,source); + // cppcheck-suppress nullPointer + (void)strcat(dest,NULL); + + // no warning shall be shown for + (void)strcat(dest,source); +} + +void bufferAccessOutOfBounds_strcat(char *dest, const char * const source) +{ + char buf4[4] = {0}; + const char * const srcstr3 = "123"; + const char * const srcstr4 = "1234"; + // @todo #8599 cppcheck-suppress bufferAccessOutOfBounds + (void)strcat(buf4,srcstr4); // off by one issue: strcat is appends \0' at the end + + // no warning shall be shown for + (void)strcat(dest,source); + (void)strcat(buf4,srcstr3); // strcat appends '\0' at the end + (void)strcat(dest,srcstr4); // Cppcheck does not know the length of 'dest' +} + +void uninitvar_wcscat(wchar_t *dest, const wchar_t * const source) +{ + wchar_t *deststr_1, *deststr_2; + const wchar_t *srcstr_1, *srcstr_2; + // cppcheck-suppress uninitvar + (void)wcscat(deststr_1,srcstr_1); + // cppcheck-suppress uninitvar + (void)wcscat(dest,srcstr_2); + // cppcheck-suppress uninitvar + (void)wcscat(deststr_2,source); + + // no warning shall be shown for + (void)wcscat(dest,source); +} + +void uninitvar_wcrtomb(void) +{ + char *s; + wchar_t wc; + mbstate_t *ps; + // cppcheck-suppress uninitvar + (void)wcrtomb(s,wc,ps); +} + +void uninitvar_strchr(void) +{ + const char *cs; + int c; + // cppcheck-suppress uninitvar + (void)strchr(cs,c); +} + +void invalidFunctionArg_strchr(const char *cs, int c) +{ + // cppcheck-suppress invalidFunctionArg + (void)strchr(cs,-1); + + // No warning shall be issued for + (void)strchr(cs, 0); + (void)strchr(cs, 255); + + // cppcheck-suppress invalidFunctionArg + (void)strchr(cs, 256); +} + +void invalidFunctionArg_log10(float f, double d, const long double ld) +{ + // cppcheck-suppress invalidFunctionArg + // cppcheck-suppress wrongmathcall + (void)log10f(0.0f); + (void)log10f(1.4013e-45f); // note: calculated by nextafterf(0.0f, 1.0f); + (void)log10f(f); + // cppcheck-suppress valueFlowBailoutIncompleteVar + (void)log10f(FLT_MAX); + + // cppcheck-suppress invalidFunctionArg + // cppcheck-suppress wrongmathcall + (void)log10(0.0); + (void)log10(4.94066e-324); // note: calculated by nextafterf(0.0, 1.0); + (void)log10(d); + (void)log10(DBL_MAX); + + // cppcheck-suppress invalidFunctionArg + // cppcheck-suppress wrongmathcall + (void)log10l(0.0L); + (void)log10l(4.94066e-324L); // note: calculated by nextafterf(0.0L, 1.0L); + (void)log10l(ld); + (void)log10l(LDBL_MAX); +} + +void invalidFunctionArg_log(float f, double d, const long double ld) +{ + // cppcheck-suppress invalidFunctionArg + // cppcheck-suppress wrongmathcall + (void)logf(0.0f); + (void)logf(1.4013e-45f); // note: calculated by nextafterf(0.0f, 1.0f); + (void)logf(f); + // cppcheck-suppress valueFlowBailoutIncompleteVar + (void)logf(FLT_MAX); + + // cppcheck-suppress invalidFunctionArg + // cppcheck-suppress wrongmathcall + (void)log(0.0); + (void)log(4.94066e-324); // note: calculated by nextafterf(0.0, 1.0); + (void)log(d); + (void)log(DBL_MAX); + + // cppcheck-suppress invalidFunctionArg + // cppcheck-suppress wrongmathcall + (void)logl(0.0L); + (void)logl(4.94066e-324L); // note: calculated by nextafterf(0.0L, 1.0L); + (void)logl(ld); + (void)logl(LDBL_MAX); +} + +void invalidFunctionArg_log2(float f, double d, const long double ld) +{ + // cppcheck-suppress invalidFunctionArg + // cppcheck-suppress wrongmathcall + (void)log2f(0.0f); + (void)log2f(1.4013e-45f); // note: calculated by nextafterf(0.0f, 1.0f); + (void)log2f(f); + // cppcheck-suppress valueFlowBailoutIncompleteVar + (void)log2f(FLT_MAX); + + // cppcheck-suppress invalidFunctionArg + // cppcheck-suppress wrongmathcall + (void)log2(0.0); + (void)log2(4.94066e-324); // note: calculated by nextafterf(0.0, 1.0); + (void)log2(d); + (void)log2(DBL_MAX); + + // cppcheck-suppress invalidFunctionArg + // cppcheck-suppress wrongmathcall + (void)log2l(0.0L); + (void)log2l(4.94066e-324L); // note: calculated by nextafterf(0.0L, 1.0L); + (void)log2l(ld); + (void)log2l(LDBL_MAX); +} + +void uninitvar_wcschr(void) +{ + const wchar_t *cs; + wchar_t c; + // cppcheck-suppress uninitvar + (void)wcschr(cs,c); +} + +void nullPointer_strcmp(const char *s1, const char *s2) +{ + // cppcheck-suppress nullPointer + (void)strcmp(NULL,s2); + // cppcheck-suppress nullPointer + (void)strcmp(s1,NULL); + (void)strcmp(s1,s2); +} + +void uninitvar_strcmp(const char *s1, const char *s2) +{ + const char *str1; + const char *str2; + const char *str3; + const char *str4; + + // cppcheck-suppress uninitvar + (void)strcmp(str1,s2); + // cppcheck-suppress uninitvar + (void)strcmp(s1,str2); + // cppcheck-suppress uninitvar + (void)strcmp(str3,str4); + + // No warning is expected + (void)strcmp(s1,s2); +} + +void uninitvar_wcscmp(const wchar_t *s1, const wchar_t *s2) +{ + const wchar_t *str1; + const wchar_t *str2; + const wchar_t *str3; + const wchar_t *str4; + + // cppcheck-suppress uninitvar + (void)wcscmp(str1,s2); + // cppcheck-suppress uninitvar + (void)wcscmp(s1,str2); + // cppcheck-suppress uninitvar + (void)wcscmp(str3,str4); + + // No warning is expected + (void)wcscmp(s1,s2); +} + +void uninitvar_strcpy(char *d, const char *s) +{ + char *dest1, *dest2; + const char *src1, *src2; + + // cppcheck-suppress uninitvar + (void)strcpy(dest1,s); + // cppcheck-suppress uninitvar + (void)strcpy(d,src1); + // cppcheck-suppress uninitvar + (void)strcpy(dest2,src2); + + // No warning is expected + (void)strcpy(d,s); +} + +void uninitvar_strcpy_s(char * strDest, ssize_t s, const char *source) +{ + char *strUninit1; + const char *strUninit2; + ssize_t size; + + // cppcheck-suppress uninitvar + (void)strcpy_s(strUninit1, 1, "a"); + // cppcheck-suppress uninitvar + (void)strcpy_s(strDest, 1, strUninit2); + // cppcheck-suppress uninitvar + (void)strcpy_s(strDest, size, "a"); + + // No warning is expected + (void)strcpy_s(strDest, s, source); +} + +void uninitvar_wcscpy(wchar_t *d, const wchar_t*s) +{ + wchar_t *dest1, *dest2; + const wchar_t *src1, *src2; + + // cppcheck-suppress uninitvar + (void)wcscpy(dest1,s); + // cppcheck-suppress uninitvar + (void)wcscpy(d,src1); + // cppcheck-suppress uninitvar + (void)wcscpy(dest2,src2); + + // No warning is expected + (void)wcscpy(d,s); +} + +size_t bufferAccessOutOfBounds_strftime(char *s, size_t max, const char *fmt, const struct tm *p) +{ + char buf[42] = {0}; + // cppcheck-suppress bufferAccessOutOfBounds + (void) strftime(buf,43,fmt,p); + (void) strftime(buf,max,fmt,p); + return strftime(buf,42,fmt,p); +} + +size_t nullPointer_strftime(char *s, size_t max, const char *fmt, const struct tm *p) +{ + // cppcheck-suppress nullPointer + (void) strftime(NULL,max,fmt,p); + // cppcheck-suppress nullPointer + (void) strftime(s,max,NULL,p); + // cppcheck-suppress nullPointer + (void) strftime(s,max,fmt,NULL); + return strftime(s,max,fmt,p); +} + +void uninitvar_strftime(void) +{ + char *s; + size_t max; + const char *fmt; + const struct tm *p; + // cppcheck-suppress uninitvar + (void)strftime(s,max,fmt,p); + + const struct tmx *px; + // cppcheck-suppress uninitvar + (void)strfxtime(s,max,fmt,px); +} + +void uninitvar_strlen(const char *str) +{ + const char *s; + // cppcheck-suppress uninitvar + (void)strlen(s); + + const char x; + const char *xPtr = &x; + // cppcheck-suppress uninitvar + (void)strlen(xPtr); + + // No warning is expected + (void)strlen(str); +} + +void uninitvar_wcslen(void) +{ + const wchar_t *s; + // cppcheck-suppress uninitvar + (void)wcslen(s); +} + +//char * strncpy ( char * destination, const char * source, size_t num ); +void uninitvar_strncpy(char * dest, const char * src, size_t num) +{ + char *d; + const char *s; + size_t n; + + // cppcheck-suppress uninitvar + (void)strncpy(d,src,num); + // cppcheck-suppress uninitvar + (void)strncpy(dest,s,num); + // cppcheck-suppress uninitvar + (void)strncpy(dest,src,n); + + // No warning is expected for + (void)strncpy(dest,src,num); +} + +void uninitvar_strncpy_s(char *Ct, size_t N1, const char *S, size_t N2) +{ + char dest[42]; + const char *s1, *s2; + size_t n1; + size_t n2; + size_t n3; + size_t n4; + + // cppcheck-suppress uninitvar + (void)strncpy_s(dest,n1,s1,n2); + // cppcheck-suppress uninitvar + (void)strncpy_s(Ct,n3,S,N2); + // cppcheck-suppress uninitvar + (void)strncpy_s(Ct,N1,s2,N2); + // cppcheck-suppress uninitvar + (void)strncpy_s(Ct,N1,S,n4); + + // no warning is expected for + (void)strncpy_s(Ct,N1,S,N2); + (void)strncpy_s(dest,N1,S,N2); +} + +void uninitvar_strpbrk(void) +{ + const char *cs; + const char *ct; + // cppcheck-suppress uninitvar + (void)strpbrk(cs,ct); +} + +// char * strncat ( char * destination, const char * source, size_t num ); +void uninitvar_strncat(char *d, const char *s, size_t n) +{ + char *dest; + const char *src; + size_t num; + + // cppcheck-suppress uninitvar + (void)strncat(dest,s,n); + // cppcheck-suppress uninitvar + (void)strncat(d,src,n); + // cppcheck-suppress uninitvar + (void)strncat(d,s,num); + + // no warning is expected for + (void)strncat(d,s,n); +} + +void nullPointer_strncat(char *d, const char *s, size_t n) +{ + // cppcheck-suppress nullPointer + (void)strncat(NULL,s,n); + // cppcheck-suppress nullPointer + (void)strncat(d,NULL,n); + // no warning is expected for + (void)strncat(d,s,n); +} + +void nullPointer_strncpy(char *d, const char *s, size_t n) +{ + // cppcheck-suppress nullPointer + (void)strncpy(NULL,s,n); + // cppcheck-suppress nullPointer + (void)strncpy(d,NULL,n); + // no warning is expected for + (void)strncpy(d,s,n); +} + +// errno_t strcat_s(char *restrict dest, rsize_t destsz, const char *restrict src); // since C11 +void uninitvar_strcat_s(char *Ct, size_t N, const char *S) +{ + char *ct_1, *ct_2; + const char *s1, *s2; + size_t n1, n2; + // cppcheck-suppress uninitvar + (void)strcat_s(ct_1,n1,s1); + // cppcheck-suppress uninitvar + (void)strcat_s(ct_2,N,S); + // cppcheck-suppress uninitvar + (void)strcat_s(Ct,N,s2); + // cppcheck-suppress uninitvar + (void)strcat_s(Ct,n2,S); + + // no warning is expected for + (void) strcat_s(Ct,N,S); +} + +// errno_t wcscat_s(wchar_t *restrict dest, rsize_t destsz, const wchar_t *restrict src); // since C11 +void uninitvar_wcscat_s(wchar_t *Ct, size_t N, const wchar_t *S) +{ + wchar_t *ct_1, *ct_2; + const wchar_t *s1, *s2; + size_t n1, n2; + // cppcheck-suppress uninitvar + (void)wcscat_s(ct_1,n1,s1); + // cppcheck-suppress uninitvar + (void)wcscat_s(ct_2,N,S); + // cppcheck-suppress uninitvar + (void)wcscat_s(Ct,N,s2); + // cppcheck-suppress uninitvar + (void)wcscat_s(Ct,n2,S); + + // no warning is expected for + (void) wcscat_s(Ct,N,S); +} + +void uninitvar_strncat_s(char *Ct, size_t N1, const char *S, size_t N2) +{ + char *ct_1, *ct_2; + const char *s1, *s2; + size_t n1; + size_t n2; + size_t n3; + size_t n4; + + // cppcheck-suppress uninitvar + (void)strncat_s(ct_1,n1,s1,n2); + // cppcheck-suppress uninitvar + (void)strncat_s(ct_2,N1,S,N2); + // cppcheck-suppress uninitvar + (void)strncat_s(Ct,n3,S,N2); + // cppcheck-suppress uninitvar + (void)strncat_s(Ct,N1,s2,N2); + // cppcheck-suppress uninitvar + (void)strncat_s(Ct,N1,S,n4); + + // no warning is expected for + (void)strncat_s(Ct,N1,S,N2); +} + +void uninitvar_wcsncat(wchar_t *Ct, const wchar_t *S, size_t N) +{ + wchar_t *ct_1, *ct_2; + const wchar_t *s1, *s2; + size_t n1, n2; + // cppcheck-suppress uninitvar + (void)wcsncat(ct_1,s1,n1); + // cppcheck-suppress uninitvar + (void)wcsncat(ct_2,S,N); + // cppcheck-suppress uninitvar + (void)wcsncat(Ct,s2,N); + // cppcheck-suppress uninitvar + (void)wcsncat(Ct,S,n2); + + // no warning is expected for + (void)wcsncat(Ct,S,N); +} + +void uninitvar_strncmp(const char *Ct, const char *S, size_t N) +{ + const char *ct; + const char *s; + size_t n1; + + // cppcheck-suppress uninitvar + (void)strncmp(ct,S,N); + // cppcheck-suppress uninitvar + (void)strncmp(Ct,s,N); + // cppcheck-suppress uninitvar + (void)strncmp(Ct,S,n1); + + // no warning is expected for + (void)strncmp(Ct,S,N); +} + +void uninitvar_wcsncmp(const wchar_t *Ct, const wchar_t *S, size_t N) +{ + const wchar_t *ct1, *ct2; + const wchar_t *s1, *s2; + size_t n1, n2; + // cppcheck-suppress uninitvar + (void)wcsncmp(ct1,s1,n1); + // cppcheck-suppress uninitvar + (void)wcsncmp(ct2,S,N); + // cppcheck-suppress uninitvar + (void)wcsncmp(Ct,s2,N); + // cppcheck-suppress uninitvar + (void)wcsncmp(Ct,S,n2); + + // no warning is expected for + (void)wcsncmp(Ct,S,N); +} + +void uninitvar_strstr(void) +{ + const char *cs; + const char *ct; + // cppcheck-suppress uninitvar + (void)strstr(cs,ct); +} + +void uninitvar_wcsstr(void) +{ + const wchar_t *cs; + const wchar_t *ct; + // cppcheck-suppress uninitvar + (void)wcsstr(cs,ct); +} + +void uninitvar_strspn(void) +{ + const char *cs; + const char *ct; + // cppcheck-suppress uninitvar + (void)strspn(cs,ct); +} + +void uninitvar_strxfrm(void) +{ + char *ds; + const char *ss; + size_t n; + // cppcheck-suppress uninitvar + (void)strxfrm(ds,ss,n); +} + +void bufferAccessOutOfBounds_strxfrm(void) +{ + const char src[3] = "abc"; + char dest[1] = "a"; + // cppcheck-suppress invalidFunctionArgStr + (void)strxfrm(dest,src,1); + // cppcheck-suppress [bufferAccessOutOfBounds,invalidFunctionArgStr] + (void)strxfrm(dest,src,2); + // cppcheck-suppress [bufferAccessOutOfBounds,invalidFunctionArgStr] + (void)strxfrm(dest,src,3); +} + +void bufferAccessOutOfBounds_strncmp(void) +{ + const char src[3] = "abc"; + const char dest[1] = "a"; + (void)strncmp(dest,src,1); + (void)strncmp(dest,src,2); + (void)strncmp(dest,src,3); +} + +void nullPointer_wmemcmp(const wchar_t* s1, const wchar_t* s2, size_t n) +{ + // cppcheck-suppress nullPointer + (void)wmemcmp(NULL,s2,n); + // cppcheck-suppress nullPointer + (void)wmemcmp(s1,NULL,n); + (void)wmemcmp(s1,s2,n); +} + +void nullPointer_wmemmove(wchar_t* s1, const wchar_t* s2, size_t n) +{ + // cppcheck-suppress nullPointer + (void)wmemmove(NULL,s2,n); + // cppcheck-suppress nullPointer + (void)wmemmove(s1,NULL,n); + (void)wmemmove(s1,s2,n); +} + +void nullPointer_wmemset(wchar_t* s, wchar_t c, size_t n) +{ + // cppcheck-suppress nullPointer + (void)wmemset(NULL,c,n); + (void)wmemset(s,c,n); +} + +void nullPointer_memmove(void *s1, void *s2, size_t n) +{ + // cppcheck-suppress nullPointer + (void)memmove(NULL,s2,n); + // cppcheck-suppress nullPointer + (void)memmove(s1,NULL,n); + (void)memmove(s1,s2,n); +} + +void nullPointer_memcmp(const void *s1, const void *s2, size_t n) +{ + // cppcheck-suppress nullPointer + (void)memcmp(NULL,s2,n); + // cppcheck-suppress nullPointer + (void)memcmp(s1,NULL,n); + (void)memcmp(s1,s2,n); +} + +void nullPointer_memcpy(void *s1, const void *s2, size_t n) +{ + // cppcheck-suppress nullPointer + (void)memcpy(NULL,s2,n); + // cppcheck-suppress nullPointer + (void)memcpy(s1,NULL,n); + (void)memcpy(s1,s2,n); +} + +void nullPointer_strncmp(const char *s1, const char *s2, size_t n) +{ + // cppcheck-suppress nullPointer + (void)strncmp(NULL,s2,n); + // cppcheck-suppress nullPointer + (void)strncmp(s1,NULL,n); + (void)strncmp(s1,s2,n); +} + +void uninitvar_wcsxfrm(void) +{ + wchar_t *ds; + const wchar_t *ss; + size_t n; + // cppcheck-suppress uninitvar + (void)wcsxfrm(ds,ss,n); +} + +void uninitvar_wcsspn(void) +{ + const wchar_t *ds; + const wchar_t *ss; + // cppcheck-suppress uninitvar + (void)wcsspn(ds,ss); +} + +void uninitvar_setlocale(void) +{ + int category; + const char* locale; + // cppcheck-suppress uninitvar + (void)setlocale(category,locale); +} + +void uninitvar_strerror(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)strerror(i); +} + +void uninitvar_strcspn(void) +{ + const char *cs; + const char *ct; + // cppcheck-suppress uninitvar + (void)strcspn(cs,ct); +} + +void uninitvar_wcscspn(void) +{ + const wchar_t *cs; + const wchar_t *ct; + // cppcheck-suppress uninitvar + (void)wcscspn(cs,ct); +} + +void uninitvar_wcspbrk(void) +{ + const wchar_t *cs; + const wchar_t *ct; + // cppcheck-suppress uninitvar + (void)wcspbrk(cs,ct); +} + +void uninitvar_wcsncpy(void) +{ + wchar_t *cs; + const wchar_t *ct; + size_t n; + // cppcheck-suppress uninitvar + (void)wcsncpy(cs,ct,n); +} + +void uninitvar_strcoll(void) +{ + const char *cs; + const char *ct; + // cppcheck-suppress uninitvar + (void)strcoll(cs,ct); +} + +void uninitvar_wcscoll(void) +{ + const wchar_t *cs; + const wchar_t *ct; + // cppcheck-suppress uninitvar + (void)wcscoll(cs,ct); +} + +//const char * strrchr ( const char * str, int character ); +// char * strrchr ( char * str, int character ); +void uninitvar_strrchr(const char * s, int c) +{ + const char * str; + int character; + + // cppcheck-suppress uninitvar + (void)strrchr(str,c); + // cppcheck-suppress uninitvar + (void)strrchr(s,character); + + // No warning is expected for + (void)strrchr(s,c); +} + +void uninitvar_wcsrchr(void) +{ + const wchar_t* ws; + wchar_t wc; + // cppcheck-suppress uninitvar + (void)wcsrchr(ws,wc); +} + +void uninitvar_wcsrtombs(void) +{ + char *dst; + const wchar_t * p;; + size_t len; + mbstate_t *ps; + // cppcheck-suppress uninitvar + (void)wcsrtombs(dst,&p,len,ps); +} + +void uninitvar_strtok(void) +{ + char *s; + const char *ct; + // cppcheck-suppress uninitvar + (void)strtok(s,ct); +} + +void uninitvar_strtoimax(void) +{ + const char *s1, *s2; + char **endp1, **endp2; + int base1, base2; + // cppcheck-suppress uninitvar + (void)strtoimax(s1,endp1,base1); + // cppcheck-suppress uninitvar + (void)strtoumax(s2,endp2,base2); +} + +void uninitvar_strtof(void) +{ + const char *s1, *s2, *s3; + char **endp1, **endp2, **endp3; + // cppcheck-suppress uninitvar + (void)strtof(s1,endp1); + // cppcheck-suppress uninitvar + (void)strtod(s2,endp2); + // cppcheck-suppress uninitvar + (void)strtold(s3,endp3); +} + +void uninitvar_strtol(void) +{ + const char *s1, *s2, *s3, *s4; + char **endp1, **endp2, **endp3, **endp4; + int base1, base2, base3, base4; + // cppcheck-suppress uninitvar + (void)strtol(s1,endp1,base1); + // cppcheck-suppress uninitvar + (void)strtoll(s2,endp2,base2); + // cppcheck-suppress uninitvar + (void)strtoul(s3,endp3,base3); + // cppcheck-suppress uninitvar + (void)strtoull(s4,endp4,base4); +} + +void uninitvar_time(void) +{ + time_t *tp; + // cppcheck-suppress uninitvar + (void)time(tp); +} + +void uninitvar_tmpnam(void) +{ + char *s; + // cppcheck-suppress uninitvar + (void)tmpnam(s); +} + +void uninitvar_tolower(int character) +{ + int c1; + // cppcheck-suppress uninitvar + (void)tolower(c1); + + int c2; + // cppcheck-suppress constVariablePointer + int *pc=&c2; + // cppcheck-suppress uninitvar + (void)tolower(*pc); + + // No warning is expected + (void)tolower(character); + + // cppcheck-suppress constVariablePointer + int *pChar = &character; + // No warning is expected + (void)tolower(*pChar); +} + +void uninitvar_toupper(int character) +{ + int c1; + // cppcheck-suppress uninitvar + (void)toupper(c1); + + int c2; + // cppcheck-suppress constVariablePointer + int *pc=&c2; + // cppcheck-suppress uninitvar + (void)toupper(*pc); + + // No warning is expected + (void)toupper(character); + + // cppcheck-suppress constVariablePointer + int *pChar = &character; + // No warning is expected + (void)toupper(*pChar); +} + +void uninitvar_wcstof(void) +{ + const wchar_t *s1, *s2, *s3; + wchar_t **endp1, **endp2, **endp3; + // cppcheck-suppress uninitvar + (void)wcstof(s1,endp1); + // cppcheck-suppress uninitvar + (void)wcstod(s2,endp2); + // cppcheck-suppress uninitvar + (void)wcstold(s3,endp3); +} + +void uninitvar_mbrtowc(void) +{ + wchar_t* pwc; + const char* pmb; + size_t max; + mbstate_t* ps; + // cppcheck-suppress uninitvar + (void)mbrtowc(pwc,pmb,max,ps); +} + +void uninitvar_wcstok(void) +{ + wchar_t *s; + const wchar_t *ct; + wchar_t **ptr; + // cppcheck-suppress uninitvar + (void)wcstok(s,ct,ptr); +} + +void uninitvar_wcstoimax(void) +{ + const wchar_t *s1, *s2; + wchar_t ** endp1, **endp2; + int base1, base2; + // cppcheck-suppress uninitvar + (void)wcstoimax(s1,endp1,base1); + // cppcheck-suppress uninitvar + (void)wcstoumax(s2,endp2,base2); +} + +void uninitvar_wcstol(void) +{ + const wchar_t *s1, *s2, *s3, *s4; + wchar_t ** endp; + int base1, base2, base3, base4; + // cppcheck-suppress uninitvar + (void)wcstol(s1,endp,base1); + // cppcheck-suppress uninitvar + (void)wcstoll(s2,endp,base2); + // cppcheck-suppress uninitvar + (void)wcstoul(s3,endp,base3); + // cppcheck-suppress uninitvar + (void)wcstoull(s4,endp,base4); +} + +void uninitvar_wprintf(const wchar_t *Format, int Argument) +{ + const wchar_t *format1, *format2, *format3; + int argument1, argument2; + // cppcheck-suppress uninitvar + (void)wprintf(format1,argument1); + // cppcheck-suppress uninitvar + (void)wprintf(format2); + // cppcheck-suppress uninitvar + (void)wprintf(Format,argument2); + // cppcheck-suppress uninitvar + (void)wprintf(format3,Argument); + // no warning is expected + (void)wprintf(Format,Argument); + (void)wprintf(Format); +} + +void uninitvar_sprintf(char *S, const char *Format, int Argument) +{ + char *s1, *s2; + const char *format1, *format2; + int argument1, argument2; + // cppcheck-suppress uninitvar + (void)sprintf(s1,format1,argument1); + // cppcheck-suppress uninitvar + (void)sprintf(s2,Format,Argument); + // cppcheck-suppress uninitvar + (void)sprintf(S,format2,Argument); + // cppcheck-suppress uninitvar + (void)sprintf(S,Format,argument2); + + // no warning is expected for + (void)sprintf(S,Format,Argument); +} + +void uninitvar_swprintf(void) +{ + wchar_t *s; + size_t n; + const wchar_t *format; + // cppcheck-suppress uninitvar + (void)swprintf(s,n,format); +} + +void uninitvar_vsprintf(void) +{ + char *s; + const char *format; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)vsprintf(s,format,arg); +} + +void valid_vsprintf_helper(const char * format, ...) +{ + char buffer[2]; + va_list args; + va_start(args, format); + vsprintf(buffer, format, args); + printf(buffer); + va_end(args); +} + +void valid_vsprintf() +{ + // buffer will contain "2\0" => no bufferAccessOutOfBounds + valid_vsprintf_helper("%1.0f", 2.0f); +} + +int nullPointer_vswprintf(wchar_t* restrict ws, size_t s, const wchar_t* restrict format, va_list ap) +{ + // cppcheck-suppress nullPointer + vswprintf(NULL, s,format, ap); + // cppcheck-suppress nullPointer + vswprintf(ws, s,NULL, ap); + return vswprintf(ws, s,format, ap); +} + +void uninitvar_vswprintf(void) +{ + wchar_t *s; + size_t n; + const wchar_t *format; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)vswprintf(s,n,format,arg); +} + +void uninitvar_fwprintf(void) +{ + FILE* stream; + const wchar_t* format; + int i; + // cppcheck-suppress uninitvar + (void)fwprintf(stream,format,i); +} + +void uninitvar_snprintf(char *S, size_t N, const char *Format, int Int) +{ + size_t n1,n2; + const char *format1, *format2; + int i1, i2; + char *s1, *s2; + // cppcheck-suppress uninitvar + (void)snprintf(s1,n1,format1,i1); + // cppcheck-suppress uninitvar + (void)snprintf(S,n2,Format,Int); // n is uninitialized + // cppcheck-suppress uninitvar + (void)snprintf(S,N,format2,Int); // format is uninitialized + // cppcheck-suppress uninitvar + (void)snprintf(S,N,Format,i2); // i is uninitialized + // cppcheck-suppress uninitvar + (void)snprintf(s2,N,Format,Int); + + // no warning is expected for + (void)snprintf(S,N,Format,Int); +} + +void uninitvar_vsnprintf(char *S, size_t N, const char *Format, va_list Arg) +{ + char *s1, *s2; + size_t n1, n2; + const char *format1, *format2; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)vsnprintf(s1,n1,format1,arg); + // cppcheck-suppress uninitvar + (void)vsnprintf(s2,N,Format,Arg); + // cppcheck-suppress uninitvar + (void)vsnprintf(S,n2,Format,Arg); + // cppcheck-suppress uninitvar + (void)vsnprintf(S,N,format2,Arg); + + // no warning is expected for + (void)vsnprintf(S,N,Format,Arg); + // cppcheck-suppress va_list_usedBeforeStarted + (void)vsnprintf(S,N,Format,arg); +} + +void uninitvar_wscanf(void) +{ + const wchar_t *format1, *format2; + int i; + // cppcheck-suppress uninitvar + (void)wscanf(format1); + // cppcheck-suppress uninitvar + (void)wscanf(format2,&i); +} + +void uninitvar_sscanf(const char *s, const char *f, int i, int *ip) +{ + const char *string1, *string2, *string3; + const char * format; + int *pInteger; + + // cppcheck-suppress uninitvar + (void)sscanf(string1,f); + // cppcheck-suppress uninitvar + (void)sscanf(string2,f,i); + // cppcheck-suppress uninitvar + (void)sscanf(string3,f,ip); + // cppcheck-suppress uninitvar + (void)sscanf(s,format,&i); + // cppcheck-suppress uninitvar + (void)sscanf(s,f,pInteger); + + // No warning is expected + (void)sscanf(s,f,&i); + (void)sscanf(s,f,ip); +} + +void uninitvar_fwscanf(void) +{ + FILE* stream; + const wchar_t* format1, *format2; + int i; + // cppcheck-suppress uninitvar + (void)fwscanf(stream,format1); + // cppcheck-suppress uninitvar + (void)fwscanf(stream,format2,&i); +} + +void uninitvar_swscanf(void) +{ + const wchar_t* s; + const wchar_t* format1, *format2; + int i; + // cppcheck-suppress uninitvar + (void)swscanf(s,format1); + // cppcheck-suppress uninitvar + (void)swscanf(s,format2,&i); +} + +void uninitvar_system(void) +{ + const char *c; + // cppcheck-suppress uninitvar + (void)system(c); +} + +void nullPointer_system(const char *c) +{ + // If a null pointer is given, command processor is checked for existence + (void)system(NULL); + (void)system(c); +} + +#ifndef __STDC_NO_THREADS__ +int nullPointer_mtx_timedlock( mtx_t *restrict mutex, const struct timespec *restrict time_point ) +{ + // cppcheck-suppress nullPointer + (void) mtx_timedlock(NULL, time_point); + // cppcheck-suppress nullPointer + (void) mtx_timedlock(mutex, NULL); + return mtx_timedlock(mutex, time_point); +} +#endif + +void uninitvar_zonetime(void) +{ + const time_t *tp; + int zone; + // cppcheck-suppress uninitvar + (void)zonetime(tp,zone); +} + +void uninitvar_itoa(void) +{ + int value; + char * str; + int base; + // cppcheck-suppress uninitvar + (void)itoa(value,str,base); +} + +#ifdef __STD_UTF_16__ +void uninitvar_c16rtomb(void) +{ + char * pmb; + char16_t c16; + mbstate_t * ps; + // cppcheck-suppress uninitvar + (void)c16rtomb(pmb,c16,ps); +} + +void uninitvar_mbrtoc16(void) +{ + char16_t * pc16; + const char * pmb; + size_t max; + mbstate_t * ps; + // cppcheck-suppress uninitvar + (void)mbrtoc16(pc16,pmb,max,ps); +} +#endif // __STD_UTF_16__ + +#ifdef __STD_UTF_32__ +void uninitvar_c32rtomb(void) +{ + char * pmb; + char32_t c32; + mbstate_t * ps; + // cppcheck-suppress uninitvar + (void)c32rtomb(pmb,c32,ps); +} + +void uninitvar_mbrtoc32(void) +{ + char32_t * pc32; + const char * pmb; + size_t max; + mbstate_t * ps; + // cppcheck-suppress uninitvar + (void)mbrtoc32(pc32,pmb,max,ps); +} +#endif // __STD_UTF_32__ + +void invalidFunctionArgBool_abs(bool b, double x, double y) +{ + // cppcheck-suppress invalidFunctionArgBool + (void)abs(true); // #6990 + // cppcheck-suppress invalidFunctionArgBool + (void)abs(b); // #6990 + // cppcheck-suppress invalidFunctionArgBool + (void)abs(x +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +// cppcheck-suppress-file valueFlowBailout + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define __STDC_WANT_LIB_EXT1__ 1 +#include +#include +#include +#include +#include +#include +#include +#ifndef __STDC_NO_THREADS__ + #include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __cpp_lib_span +#include +#endif + +bool ignoredReturnValue_std_filesystem_exists(const std::filesystem::path &path, std::error_code& ec) +{ + // cppcheck-suppress ignoredReturnValue + std::filesystem::exists(path); + // cppcheck-suppress ignoredReturnValue + std::filesystem::exists(path, ec); + const bool b {std::filesystem::exists(path)}; + return b && std::filesystem::exists(path, ec); +} + +// https://en.cppreference.com/w/cpp/io/manip/quoted +void uninitvar_std_quoted(std::stringstream &ss, const std::string &input, const char delim, const char escape) +{ + ss << std::quoted(input); + ss << std::quoted(input, delim); + ss << std::quoted(input, delim, escape); + char delimChar; + // cppcheck-suppress uninitvar + ss << std::quoted(input, delimChar); + char escapeChar; + // cppcheck-suppress uninitvar + ss << std::quoted(input, delim, escapeChar); +} + +int zerodiv_ldexp() +{ + int i = std::ldexp(0.0, 42.0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_ilogb() +{ + int i = std::ilogb(1.0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_hypot() +{ + int i = std::hypot(0.0, 0.0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_fmod() +{ + int i = std::fmod(0.0, 42.0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_fmin() +{ + int i = std::fmin(0.0, 0.0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_fmax() +{ + int i = std::fmax(0.0, 0.0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_floor() +{ + int i = std::floor(0.0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_fabs() +{ + int i = std::fabs(-0.0) + std::fabs(+0.0) + std::fabs(0.0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_fdim() +{ + int i = std::fdim(1.0, 1.0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_trunc() +{ + int i = std::trunc(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_ceil() +{ + int i = std::ceil(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_sqrt() +{ + int i = std::sqrt(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_cbrt() +{ + int i = std::cbrt(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_erf() +{ + int i = std::erf(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_erfc() +{ + int i = std::erfc(42); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_asin() +{ + int i = std::asin(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_acos() +{ + int i = std::acos(1); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_asinh() +{ + int i = std::asinh(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_acosh() +{ + int i = std::acosh(1); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_log1p() +{ + int i = std::log1p(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_nearbyint() +{ + int i = std::nearbyint(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_round() +{ + int i = std::round(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_sinh() +{ + int i = std::sinh(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_tanh() +{ + int i = std::tanh(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_atanh() +{ + int i = std::atanh(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_atan() +{ + int i = std::atan(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_sin() +{ + int i = std::sin(0)+std::sin(M_PI)+std::sin(2*M_PI); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int zerodiv_expm1() +{ + int i = std::expm1(0); + // cppcheck-suppress zerodiv + return 42 / i; +} + +int moduloofone_cos() +{ + int i = std::cos(0); + // cppcheck-suppress moduloofone + return 42 % i; +} + +int moduloofone_exp() +{ + int i = std::exp(0); + // cppcheck-suppress moduloofone + return 42 % i; +} + +int moduloofone_exp2() +{ + int i = std::exp2(0); + // cppcheck-suppress moduloofone + return 42 % i; +} + +int moduloofone_pow() +{ + int i = std::pow(2, 0); + // cppcheck-suppress moduloofone + return 42 % i; +} + +char* invalidFunctionArgStr_strncpy(char * destination) +{ + // Copies the first num characters of source to destination. + // If the end of the source C string (which is signaled by a null-character) + // is found before num characters have been copied, destination + // is padded with zeros until a total of num characters have been written to it. + const char source = 'x'; + const std::size_t num = 1U; + return strncpy(destination, &source, num); +} + +void invalidFunctionArgStr_fprintf(FILE *stream, const char *format) +{ + const char formatBuf[] = {'%','d'}; + // cppcheck-suppress invalidFunctionArgStr + (void)fprintf(stream,formatBuf,42); + (void)fprintf(stream,format,42); +} + +void invalidFunctionArgStr_fopen(const char * const fileName, const char * const mode) +{ + const char fileNameBuf[] = {'f','i','l','e'}; + const char modeBuf[] = {'r'}; + // cppcheck-suppress invalidFunctionArgStr + FILE *fp = fopen(fileName, modeBuf); + fclose(fp); + // cppcheck-suppress invalidFunctionArgStr + fp = fopen(fileNameBuf, mode); + fclose(fp); +} + +float invalidFunctionArg_remquo (float x, float y, int* quo ) +{ + // cppcheck-suppress invalidFunctionArg + (void) std::remquo(x,0.0f,quo); + // cppcheck-suppress invalidFunctionArg + (void) std::remquof(x,0.0f,quo); + return std::remquo(x,y,quo); +} + +double invalidFunctionArg_remquo (double x, double y, int* quo ) +{ + // cppcheck-suppress invalidFunctionArg + (void) std::remquo(x,0.0,quo); + // cppcheck-suppress invalidFunctionArg + (void) std::remquo(x,0.0f,quo); + // cppcheck-suppress invalidFunctionArg + (void) std::remquo(x,0.0L,quo); + return std::remquo(x,y,quo); +} + +double invalidFunctionArg_remquo (long double x, long double y, int* quo ) +{ + // cppcheck-suppress invalidFunctionArg + (void) std::remquo(x,0.0L,quo); + // cppcheck-suppress invalidFunctionArg + (void) std::remquol(x,0.0L,quo); + return std::remquo(x,y,quo); +} + +void invalidFunctionArg_remainderl(long double f1, long double f2) +{ + // cppcheck-suppress invalidFunctionArg + (void)std::remainderl(f1,0.0); + // cppcheck-suppress invalidFunctionArg + (void)std::remainderl(f1,0.0L); + (void)std::remainderl(f1,f2); +} + +void invalidFunctionArg_remainder(double f1, double f2) +{ + // cppcheck-suppress invalidFunctionArg + (void)std::remainder(f1,0.0); + (void)std::remainder(f1,f2); +} + +void invalidFunctionArg_remainderf(float f1, float f2) +{ + // cppcheck-suppress invalidFunctionArg + (void)std::remainderf(f1,0.0); + // cppcheck-suppress invalidFunctionArg + (void)std::remainderf(f1,0.0f); + (void)std::remainderf(f1,f2); +} + +void uninitvar_std_fstream_open(std::fstream &fs, const std::string &strFileName, const char* filename, std::ios_base::openmode mode) +{ + std::string s; + const char *ptr; + std::ios_base::openmode m; + + fs.open(s, mode); + // cppcheck-suppress uninitvar + fs.open(ptr, mode); + // TODO cppcheck-suppress uninitvar + fs.open(filename, m); + // TODO cppcheck-suppress uninitvar + fs.open(strFileName, m); + fs.open(s); + // TODO cppcheck-suppress uninitvar + fs.open(ptr); +} + +void uninitvar_std_ifstream_open(std::ifstream &ifs, const std::string &strFileName, const char* filename, std::ios_base::openmode mode) +{ + std::string s; + const char *ptr; + std::ios_base::openmode m; + + ifs.open(s, mode); + // cppcheck-suppress uninitvar + ifs.open(ptr, mode); + // TODO cppcheck-suppress uninitvar + ifs.open(filename, m); + // TODO cppcheck-suppress uninitvar + ifs.open(strFileName, m); + ifs.open(s); + // TODO cppcheck-suppress uninitvar + ifs.open(ptr); +} + +void uninitvar_std_ofstream_open(std::ofstream &os, const std::string &strFileName, const char* filename, std::ios_base::openmode mode) +{ + std::string s; + const char *ptr; + std::ios_base::openmode m; + + os.open(s, mode); + // cppcheck-suppress uninitvar + os.open(ptr, mode); + // TODO cppcheck-suppress uninitvar + os.open(filename, m); + // TODO cppcheck-suppress uninitvar + os.open(strFileName, m); + os.open(s); + // TODO cppcheck-suppress uninitvar + os.open(ptr); +} + +void uninitvar_std_ofstream_precision(std::ofstream& os) +{ + std::streamsize s; + // cppcheck-suppress uninitvar + os.precision(s); +} + +void nullPointer_std_filebuf_open(std::filebuf &fb, const std::string &strFileName, const char* filename, std::ios_base::openmode mode) +{ + // cppcheck-suppress nullPointer + (void)fb.open(nullptr, mode); + (void)fb.open(filename, mode); + (void)fb.open(strFileName, mode); +} + +void nullPointer_std_ofstream_open(std::ofstream &os, const std::string &strFileName, const char* filename, std::ios_base::openmode mode) +{ + // cppcheck-suppress nullPointer + os.open(nullptr, mode); + os.open(filename, mode); + os.open(strFileName, mode); + // cppcheck-suppress nullPointer + os.open(nullptr); + os.open(filename); + os.open(strFileName); +} + +void nullPointer_std_fstream_open(std::fstream &fs, const std::string &strFileName, const char* filename, std::ios_base::openmode mode) +{ + // cppcheck-suppress nullPointer + fs.open(nullptr, mode); + fs.open(filename, mode); + fs.open(strFileName, mode); + // cppcheck-suppress nullPointer + fs.open(nullptr); + fs.open(filename); + fs.open(strFileName); +} + +void nullPointer_std_ifstream_open(std::ifstream &is, const std::string &strFileName, const char* filename, std::ios_base::openmode mode) +{ + // cppcheck-suppress nullPointer + is.open(nullptr, mode); + is.open(filename, mode); + is.open(strFileName, mode); + // cppcheck-suppress nullPointer + is.open(nullptr); + is.open(filename); + is.open(strFileName); +} + +void bufferAccessOutOfBounds_std_fstream_write(std::fstream &fs, const char* s, std::streamsize n) +{ + const char buf[42] = {0}; + (void)fs.write(buf,42); + // cppcheck-suppress bufferAccessOutOfBounds + (void)fs.write(buf,43); + (void)fs.write(buf,n); + (void)fs.write(s,n); +} + +void bufferAccessOutOfBounds_std_ostream_write(std::ostream &os, const char* s, std::streamsize n) +{ + const char buf[42] = {0}; + (void)os.write(buf,42); + // cppcheck-suppress bufferAccessOutOfBounds + (void)os.write(buf,43); + (void)os.write(buf,n); + (void)os.write(s,n); +} + +void bufferAccessOutOfBounds_std_ostringstream_write(std::ostringstream &oss, const char* s, std::streamsize n) +{ + const char buf[42] = {0}; + (void)oss.write(buf,42); + // cppcheck-suppress bufferAccessOutOfBounds + (void)oss.write(buf,43); + (void)oss.write(buf,n); + (void)oss.write(s,n); +} + +void bufferAccessOutOfBounds_std_ofstream_write(std::ofstream &os, const char* s, std::streamsize n) +{ + const char buf[42] = {0}; + (void)os.write(buf,42); + // cppcheck-suppress bufferAccessOutOfBounds + (void)os.write(buf,43); + (void)os.write(buf,n); + (void)os.write(s,n); +} + +// cppcheck-suppress constParameterReference // TODO: FP +void bufferAccessOutOfBounds_std_ifstream_get(std::ifstream& in, std::streambuf& sb) +{ + char cBuf[10]; + // cppcheck-suppress bufferAccessOutOfBounds + in.getline(cBuf, 100); + // cppcheck-suppress bufferAccessOutOfBounds + in.read(cBuf, 100); + // cppcheck-suppress bufferAccessOutOfBounds + in.readsome(cBuf, 100); + // cppcheck-suppress bufferAccessOutOfBounds + in.get(cBuf, 100); + // cppcheck-suppress bufferAccessOutOfBounds + in.get(cBuf, 100, 'a'); + // cppcheck-suppress bufferAccessOutOfBounds + in.getline(cBuf, 100, 'a'); + + in.get(sb, 'a'); + + in.close(); +} + +void invalidFunctionArg_fesetexceptflag(const fexcept_t* flagp, int excepts) +{ + (void)std::fesetexceptflag(flagp, excepts); + // cppcheck-suppress invalidFunctionArg + (void)std::fesetexceptflag(flagp, 0); + (void)std::fesetexceptflag(flagp, FE_DIVBYZERO); + (void)std::fesetexceptflag(flagp, FE_INEXACT); + (void)std::fesetexceptflag(flagp, FE_INVALID); + (void)std::fesetexceptflag(flagp, FE_OVERFLOW); + (void)std::fesetexceptflag(flagp, FE_UNDERFLOW); + (void)std::fesetexceptflag(flagp, FE_ALL_EXCEPT); + // cppcheck-suppress invalidFunctionArg + (void)std::fesetexceptflag(flagp, FE_ALL_EXCEPT+1); +} + +void invalidFunctionArg_fetestexcept(int excepts) +{ + (void)std::fetestexcept(excepts); + // cppcheck-suppress invalidFunctionArg + (void)std::fetestexcept(0); + (void)std::fetestexcept(FE_DIVBYZERO); + (void)std::fetestexcept(FE_INEXACT); + (void)std::fetestexcept(FE_INVALID); + (void)std::fetestexcept(FE_OVERFLOW); + (void)std::fetestexcept(FE_UNDERFLOW); + (void)std::fetestexcept(FE_ALL_EXCEPT); + // cppcheck-suppress invalidFunctionArg + (void)std::fetestexcept(FE_ALL_EXCEPT+1); +} + +void nullPointer_fprintf(FILE *Stream, const char *Format, int Argument) +{ + // cppcheck-suppress nullPointer + (void)std::fprintf(Stream, nullptr, Argument); + // no warning is expected + (void)std::fprintf(Stream, Format, Argument); +} + +void bufferAccessOutOfBounds_wcsftime(wchar_t* ptr, size_t maxsize, const wchar_t* format, const struct tm* timeptr) +{ + wchar_t buf[42]; + (void)std::wcsftime(buf, 42, format, timeptr); + // TODO cppcheck-suppress bufferAccessOutOfBounds + (void)std::wcsftime(buf, 43, format, timeptr); + (void)std::wcsftime(ptr, maxsize, format, timeptr); +} + +int qsort_cmpfunc (const void * a, const void * b) { + return (*static_cast(a) - *static_cast(b)); +} +void nullPointer_qsort(void *base, std::size_t n, std::size_t size, int (*cmp)(const void *, const void *)) +{ + // cppcheck-suppress nullPointer + std::qsort(nullptr, n, size, qsort_cmpfunc); + // cppcheck-suppress nullPointer + std::qsort(base, n, size, nullptr); + std::qsort(base, n, size, qsort_cmpfunc); +} + +void nullPointer_vfprintf(FILE *Stream, const char *Format, va_list Arg) +{ + // cppcheck-suppress nullPointer + (void)std::vfprintf(Stream, nullptr, Arg); + (void)std::vfprintf(Stream, Format, Arg); +} + +void nullPointer_vfwprintf(FILE *Stream, const wchar_t *Format, va_list Arg) +{ + // cppcheck-suppress nullPointer + (void)std::vfwprintf(Stream, nullptr, Arg); + (void)std::vfwprintf(Stream, Format, Arg); +} + +void *bufferAccessOutOfBounds_memchr(void *s, int c, size_t n) +{ + char buf[42]={0}; + (void)std::memchr(buf,c,42); + // cppcheck-suppress bufferAccessOutOfBounds + (void)std::memchr(buf,c,43); + return std::memchr(s,c,n); +} + +// As with all bounds-checked functions, localtime_s is only guaranteed to be available if __STDC_LIB_EXT1__ is defined by the implementation and if the user defines __STDC_WANT_LIB_EXT1__ to the integer constant 1 before including time.h. +#ifdef __STDC_LIB_EXT1__ +void uninitvar_localtime_s(const std::time_t *restrict time, struct tm *restrict result) +{ + // cppcheck-suppress valueFlowBailoutIncompleteVar + const std::time_t *restrict Time; + // TODO cppcheck-suppress uninitvar + (void)std::localtime_s(Time, result); + (void)std::localtime_s(time, result); +} + +void nullPointer_localtime_s(const std::time_t *restrict time, struct tm *restrict result) +{ + // cppcheck-suppress nullPointer + (void)std::localtime_s(NULL, result); + // cppcheck-suppress nullPointer + (void)std::localtime_s(time, NULL); + (void)std::localtime_s(time, result); +} + +void memleak_localtime_s(const std::time_t *restrict time, struct tm *restrict result) // #9258 +{ + const time_t t = time(0); + const struct tm* const now = new tm(); + if (localtime_s(now, &t) == 0) { + // cppcheck-suppress valueFlowBailoutIncompleteVar + std::cout << now->tm_mday << std::endl; + } + // cppcheck-suppress memleak +} +#endif // __STDC_LIB_EXT1__ + +size_t nullPointer_strftime(char *s, size_t max, const char *fmt, const struct tm *p) +{ + // cppcheck-suppress nullPointer + (void) std::strftime(NULL,max,fmt,p); + // cppcheck-suppress nullPointer + (void) std::strftime(s,max,NULL,p); + // cppcheck-suppress nullPointer + (void) std::strftime(s,max,fmt,NULL); + return std::strftime(s,max,fmt,p); +} + +size_t bufferAccessOutOfBounds_wcsrtombs(char * dest, const wchar_t ** src, size_t len, mbstate_t * ps) +{ + char buf[42]; + (void)std::wcsrtombs(buf,src,42,ps); + // cppcheck-suppress bufferAccessOutOfBounds + (void)std::wcsrtombs(buf,src,43,ps); + return std::wcsrtombs(dest,src,len,ps); +} + +void invalidFunctionArg_std_string_substr(const std::string &str, std::size_t pos, std::size_t len) { + // cppcheck-suppress invalidFunctionArg + (void)str.substr(-1,len); + // cppcheck-suppress invalidFunctionArg + (void)str.substr(pos,-1); + // no warning is expected for + (void)str.substr(pos,len); + // cppcheck-suppress valueFlowBailoutIncompleteVar + (void)str.substr(pos, std::string::npos); +} + +void invalidFunctionArg_std_wstring_substr(const std::wstring &str, std::size_t pos, std::size_t len) { + // cppcheck-suppress invalidFunctionArg + (void)str.substr(-1,len); + // cppcheck-suppress invalidFunctionArg + (void)str.substr(pos,-1); + // no warning is expected for + (void)str.substr(pos,len); + // cppcheck-suppress valueFlowBailoutIncompleteVar + (void)str.substr(pos, std::wstring::npos); +} + +double invalidFunctionArg_log10(double d = 0.0) { + // cppcheck-suppress invalidFunctionArg + return log10(d); +} + +void uninitvar_std_next(const std::vector &v, int count) +{ + // No warning shall be shown: + if (std::next(v.begin()) != v.end()) {} + if (std::next(v.begin(), count) != v.end()) {} + + std::vector::iterator it; + // cppcheck-suppress uninitvar + if (std::next(it) != v.end()) {} + + std::vector::const_iterator const_it; + // cppcheck-suppress uninitvar + if (std::next(const_it) != v.end()) {} + + std::vector::reverse_iterator rit; + // cppcheck-suppress uninitvar + if (std::next(rit) != v.rend()) {} + + std::vector::const_reverse_iterator const_rit; + // cppcheck-suppress uninitvar + if (std::next(const_rit) != v.rend()) {} +} + +void uninitvar_std_prev(const std::vector &v, int count) +{ + // No warning shall be shown: + if (std::prev(v.begin()) != v.end()) {} + if (std::prev(v.begin(), count) != v.end()) {} + + std::vector::iterator it; + // cppcheck-suppress uninitvar + if (std::prev(it) != v.end()) {} + + std::vector::const_iterator const_it; + // cppcheck-suppress uninitvar + if (std::prev(const_it) != v.end()) {} + + std::vector::reverse_iterator rit; + // cppcheck-suppress uninitvar + if (std::prev(rit) != v.rend()) {} + + std::vector::const_reverse_iterator const_rit; + // cppcheck-suppress uninitvar + if (std::prev(const_rit) != v.rend()) {} +} + +void overlappingWriteFunction_wcscat(wchar_t *src, wchar_t *dest) +{ + // No warning shall be shown: + (void)wcscat(dest, src); + // cppcheck-suppress overlappingWriteFunction + (void)wcscat(src, src); +} + +void overlappingWriteFunction_wcsxfrm(wchar_t *s1, const wchar_t *s2, size_t n) +{ + // No warning shall be shown: + (void)wcsxfrm(s1, s2, n); +} + +char * overlappingWriteFunction_strcat(char *src, char *dest) +{ + // No warning shall be shown: + (void)strcat(dest, src); + // cppcheck-suppress overlappingWriteFunction + return strcat(src, src); +} + +int nullPointer_wcsncmp(const wchar_t* s1, const wchar_t* s2, size_t n) +{ + // cppcheck-suppress nullPointer + (void) std::wcsncmp(NULL,s2,n); + // cppcheck-suppress nullPointer + (void) std::wcsncmp(s1,NULL,n); + return std::wcsncmp(s1,s2,n); +} + +wchar_t* nullPointer_wcsncpy(wchar_t *s, const wchar_t *cs, size_t n) +{ + // cppcheck-suppress nullPointer + (void) std::wcsncpy(NULL,cs,n); + // cppcheck-suppress nullPointer + (void) std::wcsncpy(s,NULL,n); + return std::wcsncpy(s,cs,n); +} + +char * overlappingWriteFunction_strncat(const char *src, char *dest, const std::size_t count) +{ + // No warning shall be shown: + (void)strncat(dest, src, 42); + (void)strncat(dest, src, count); + (void)strncat(dest, dest, count); + // cppcheck-suppress overlappingWriteFunction + (void)strncat(dest, dest+1, 2); + char buffer[] = "strncat"; + // cppcheck-suppress overlappingWriteFunction + return strncat(buffer, buffer + 1, 3); +} + +wchar_t * overlappingWriteFunction_wcsncat(const wchar_t *src, wchar_t *dest, const std::size_t count) +{ + // No warning shall be shown: + (void)wcsncat(dest, src, 42); + (void)wcsncat(dest, src, count); + (void)wcsncat(dest, dest, count); + // cppcheck-suppress overlappingWriteFunction + (void)wcsncat(dest, dest+1, 2); + wchar_t buffer[] = L"strncat"; + // cppcheck-suppress overlappingWriteFunction + return wcsncat(buffer, buffer + 1, 3); +} + +wchar_t * overlappingWriteFunction_wcscpy(wchar_t *src, wchar_t *dest) +{ + // No warning shall be shown: + (void)wcscpy(dest, src); + const wchar_t * destBuf = dest; + // TODO-cppcheck-suppress overlappingWriteFunction #10355 + (void)wcscpy(dest, destBuf); + // cppcheck-suppress overlappingWriteFunction + return wcscpy(src, src); +} + +wchar_t * overlappingWriteFunction_wcsncpy(wchar_t *buf, const std::size_t count) +{ + // No warning shall be shown: + (void)wcsncpy(&buf[0], &buf[3], count); // size is not known + (void)wcsncpy(&buf[0], &buf[3], 3U); // no-overlap + // cppcheck-suppress overlappingWriteFunction + return wcsncpy(&buf[0], &buf[3], 4U); +} + +char * overlappingWriteFunction_strncpy(char *buf, const std::size_t count) +{ + // No warning shall be shown: + (void)strncpy(&buf[0], &buf[3], count); // size is not known + (void)strncpy(&buf[0], &buf[3], 3U); // no-overlap + // cppcheck-suppress overlappingWriteFunction + return strncpy(&buf[0], &buf[3], 4U); +} + +void * overlappingWriteFunction_memmove(void) +{ + // No warning shall be shown: + char str[] = "memmove handles overlapping data well"; + return memmove(str,str+3,4); +} + +std::bitset<10> std_bitset_test_ignoredReturnValue() +{ + std::bitset<10> b1("1111010000"); + // cppcheck-suppress ignoredReturnValue + b1.test(2); + return b1; +} + +std::bitset<10> std_bitset_all_ignoredReturnValue() +{ + std::bitset<10> b1("1111010000"); + // cppcheck-suppress ignoredReturnValue + b1.all(); + return b1; +} + +std::bitset<10> std_bitset_none_ignoredReturnValue() +{ + std::bitset<10> b1("1111010000"); + // cppcheck-suppress ignoredReturnValue + b1.none(); + return b1; +} + +std::bitset<10> std_bitset_any_ignoredReturnValue() +{ + std::bitset<10> b1("1111010000"); + // cppcheck-suppress ignoredReturnValue + b1.any(); + return b1; +} + +std::bitset<10> std_bitset_size_ignoredReturnValue() +{ + std::bitset<10> b1("1111010000"); + // cppcheck-suppress ignoredReturnValue + b1.size(); + return b1; +} + +std::bitset<10> std_bitset_count_ignoredReturnValue() +{ + std::bitset<10> b1("1111010000"); + // cppcheck-suppress ignoredReturnValue + b1.count(); + return b1; +} + +void std_unordered_set_count_ignoredReturnValue(const std::unordered_set& u) +{ + int i; + // cppcheck-suppress [uninitvar, ignoredReturnValue] + u.count(i); +} + +void std_unordered_map_count_ignoredReturnValue(const std::unordered_map& u) +{ + int i; + // cppcheck-suppress [uninitvar, ignoredReturnValue] + u.count(i); +} + +void std_multimap_count_ignoredReturnValue(const std::multimap& m) +{ + int i; + // cppcheck-suppress [uninitvar, ignoredReturnValue] + m.count(i); +} + +void std_unordered_map_insert_unnitvar(std::unordered_set& u) +{ + int i; + // cppcheck-suppress uninitvar + u.insert(i); +} + +void std_unordered_map_emplace_unnitvar(std::unordered_set& u) +{ + int i; + // cppcheck-suppress uninitvar + u.emplace(i); +} + +int std_map_find_constref(std::map& m) // #11857 +{ + std::map& r = m; + std::map::iterator it = r.find(42); + int* p = &it->second; + return ++*p; +} + +void std_queue_front_ignoredReturnValue(const std::queue& q) { + // cppcheck-suppress ignoredReturnValue + q.front(); +} + +void std_priority_queue_top_ignoredReturnValue(const std::priority_queue& pq) { + // cppcheck-suppress ignoredReturnValue + pq.top(); +} + +void std_tie_ignoredReturnValue(int a, int b) +{ + std::set s; + std::set::iterator it; + bool success; + std::tie(it, success) = s.insert(1); + // cppcheck-suppress ignoredReturnValue + std::tie(); + // cppcheck-suppress ignoredReturnValue + std::tie(a, b); +} + +void std_exception_ignoredReturnValue(const std::exception& e) +{ + // cppcheck-suppress ignoredReturnValue + e.what(); +} + +void valid_code() +{ + std::vector vecInt{0, 1, 2}; + std::fill_n(vecInt.begin(), 2, 0); + vecInt.push_back(1); + vecInt.pop_back(); +} + +void returnValue_std_isgreater(void) +{ + // cppcheck-suppress knownConditionTrueFalse + if (std::isgreater(4,2) == 0) {} + // @todo support floats + if (std::isgreater(4.0f,2.0f) == 0) {} +} + +void returnValue_std_isgreaterequal(void) +{ + // cppcheck-suppress knownConditionTrueFalse + if (std::isgreaterequal(4,2) == 0) {} + // @todo support floats + if (std::isgreaterequal(4.0f,2.0f) == 0) {} +} + +void returnValue_std_isless(void) +{ + // cppcheck-suppress knownConditionTrueFalse + if (std::isless(4,2) == 0) {} + // @todo support floats + if (std::isless(4.0f,2.0f) == 0) {} +} + +void returnValue_std_islessequal(void) +{ + // cppcheck-suppress knownConditionTrueFalse + if (std::islessequal(4,2) == 0) {} + // @todo support floats + if (std::islessequal(4.0f,2.0f) == 0) {} +} + +void returnValue_std_islessgreater(void) +{ + // cppcheck-suppress knownConditionTrueFalse + if (std::islessgreater(4,2) == 0) {} + // cppcheck-suppress knownConditionTrueFalse + if (std::islessgreater(2,4) == 0) {} + + if (std::islessgreater(4.0f,2.0f) == 0) {} // @todo support floats + if (std::islessgreater(2.0f,4.0f) == 0) {} // @todo support floats +} + +void bufferAccessOutOfBounds(void) +{ + char a[5]; + std::strcpy(a,"abcd"); + // cppcheck-suppress bufferAccessOutOfBounds + // TODO cppcheck-suppress redundantCopy + std::strcpy(a, "abcde"); + // TODO cppcheck-suppress redundantCopy + // cppcheck-suppress terminateStrncpy + std::strncpy(a,"abcde",5); + // cppcheck-suppress bufferAccessOutOfBounds + // TODO cppcheck-suppress redundantCopy + std::strncpy(a,"abcde",6); +} + +void uninitvar_abs(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::abs(i); +} + +void uninivar_imaxabs(void) +{ + intmax_t i1, i2; + // cppcheck-suppress uninitvar + (void)std::imaxabs(i1); + // cppcheck-suppress uninitvar + (void)imaxabs(i2); +} + +void uninitvar_isalnum(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::isalnum(i); +} + +void uninitvar_isalpha(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::isalpha(i); +} + +void uninitvar_iscntrl(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::iscntrl(i); +} + +void uninitvar_isdigit(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::isdigit(i); +} + +void uninitvar_isgraph(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::isgraph(i); +} + +void uninitvar_islower(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::islower(i); +} + +void uninitvar_isprint(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::isprint(i); +} + +void uninitvar_isspace(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::isspace(i); +} + +void uninitvar_isupper(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::isupper(i); +} + +void uninitvar_isxdigit(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::isxdigit(i); +} + +void uninitvar_proj(void) +{ + double d; + // cppcheck-suppress uninitvar + const std::complex dc(d,d); + (void)std::proj(dc); +} + +void uninitvar_acos(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::acos(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::acos(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::acos(ld); +} + +void uninitvar_acosh(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::acoshf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::acosh(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::acoshl(ld); +} + +void uninitvar_asctime(void) +{ + const struct tm *tm; + // cppcheck-suppress uninitvar + // cppcheck-suppress asctimeCalled + (void)std::asctime(tm); +} + +void uninitvar_sqrt(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::sqrt(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::sqrt(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::sqrt(ld); +} + +void uninitvar_sinh(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::sinh(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::sinh(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::sinh(ld); +} + +void uninitvar_sin(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::sin(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::sin(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::sin(ld); +} + +void uninitvar_asin(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::asin(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::asin(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::asin(ld); +} + +void uninitvar_asinh(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::asinhf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::asinh(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::asinhl(ld); +} + +void uninitvar_wcsftime(wchar_t* ptr) +{ + size_t maxsize; + const wchar_t* format; + const struct tm* timeptr; + // cppcheck-suppress uninitvar + (void)std::wcsftime(ptr, maxsize, format, timeptr); +} + +void uninitvar_tan(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::tan(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::tan(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::tan(ld); +} + +void uninitvar_tanh(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::tanh(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::tanh(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::tanh(ld); +} + +void uninitvar_atan(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::atan(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::atan(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::atan(ld); +} + +void uninitvar_tgamma(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::tgammaf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::tgamma(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::tgammal(ld); +} + +void uninitvar_trunc(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::truncf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::trunc(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::truncl(ld); +} + +void uninitvar_atanh(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::atanhf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::atanh(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::atanhl(ld); +} + +void uninitvar_atan2(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)std::atan2(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)std::atan2(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)std::atan2(ld1,ld2); +} + +void uninitvar_atof(void) +{ + const char * c; + // cppcheck-suppress uninitvar + (void)std::atof(c); +} + +void uninitvar_atol(void) +{ + const char * c1, *c2, *c3; + // cppcheck-suppress uninitvar + (void)std::atoi(c1); + + // cppcheck-suppress uninitvar + (void)std::atol(c2); + + // cppcheck-suppress uninitvar + (void)std::atoll(c3); +} + +void uninitvar_ceil(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::ceil(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::ceil(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::ceil(ld); +} + +void uninitvar_copysign(void) +{ + float f1, f2; + // cppcheck-suppress uninitvar + (void)std::copysignf(f1, f2); + + double d1, d2; + // cppcheck-suppress uninitvar + (void)std::copysign(d1, d2); + + long double ld1, ld2; + // cppcheck-suppress uninitvar + (void)std::copysignl(ld1, ld2); +} + +void uninitvar_cbrt(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::cbrtf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::cbrt(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::cbrtl(ld); +} + +void uninitvar_cos(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::cos(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::cos(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::cos(ld); +} + +void uninitvar_clearerr(void) +{ + FILE * stream; + // cppcheck-suppress uninitvar + std::clearerr(stream); +} + +void uninitvar_cosh(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::cosh(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::cosh(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::cosh(ld); +} + +void uninitvar_feraiseexcept(void) +{ + int expects; + // cppcheck-suppress uninitvar + (void)std::feraiseexcept(expects); +} + +void uninitvar_fesetexceptflag(const fexcept_t* flagp) +{ + int expects; + // cppcheck-suppress uninitvar + (void)std::fesetexceptflag(flagp, expects); +} + +void uninitvar_feclearexcept(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::feclearexcept(i); +} + +void uninitvar_fesetenv(void) +{ + const fenv_t* envp; + // cppcheck-suppress uninitvar + (void)std::fesetenv(envp); +} + +void uninitvar_fesetround(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::fesetround(i); +} + +void uninitvar_fetestexcept(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::fetestexcept(i); +} + +void uninitvar_feupdateenv(void) +{ + const fenv_t* envp; + // cppcheck-suppress uninitvar + (void)std::feupdateenv(envp); +} + +void uninitvar_ctime(void) +{ + const time_t *tp; + // cppcheck-suppress uninitvar + (void)std::ctime(tp); +} + +void uninitvar_difftime(void) +{ + time_t t1,t2; + // cppcheck-suppress uninitvar + (void)std::difftime(t1, t2); +} + +void uninitvar_div(void) +{ + int num; + int denom; + // cppcheck-suppress uninitvar + (void)std::div(num,denom); +} + +void uninitvar_imaxdiv(void) +{ + intmax_t numer1, numer2; + intmax_t denom1, denom2; + // cppcheck-suppress uninitvar + (void)std::imaxdiv(numer1,denom1); + // cppcheck-suppress uninitvar + (void)imaxdiv(numer2,denom2); +} + +void uninitvar_exit(void) +{ + int i; + // cppcheck-suppress uninitvar + std::exit(i); +} + +void uninitvar_erf(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::erff(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::erf(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::erfl(ld); +} + +void uninitvar_erfc(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::erfcf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::erfc(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::erfcl(ld); +} + +void uninitvar_exp(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::exp(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::exp(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::exp(ld); +} + +void uninitvar_exp2(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::exp2f(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::exp2(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::exp2l(ld); +} + +void uninitvar_expm1(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::expm1f(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::expm1(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::expm1l(ld); +} + +void uninitvar_fabs(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::fabs(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::fabs(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::fabs(ld); +} + +void uninitvar_fdim(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)std::fdimf(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)std::fdim(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)std::fdiml(ld1,ld2); +} + +void uninitvar_fclose(void) +{ + // cppcheck-suppress unassignedVariable + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::fclose(stream); +} + +void uninitvar_ferror(void) +{ + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::ferror(stream); +} + +void uninitvar_feof(void) +{ + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::feof(stream); +} + +void uninitvar_fflush(void) +{ + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::fflush(stream); +} + +void uninitvar_fgetc(void) +{ + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::fgetc(stream); +} + +void uninitvar_fgetwc(void) +{ + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::fgetwc(stream); +} + +void uninitvar_fgetpos(void) +{ + FILE* stream; + fpos_t *ptr; + // cppcheck-suppress uninitvar + (void)std::fgetpos(stream,ptr); +} + +void uninitvar_floor(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::floor(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::floor(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::floor(ld); +} + +void uninitvar_fma(void) +{ + float f1,f2,f3; + // cppcheck-suppress uninitvar + (void)std::fmaf(f1,f2,f3); + + double d1,d2,d3; + // cppcheck-suppress uninitvar + (void)std::fma(d1,d2,d3); + + long double ld1,ld2,ld3; + // cppcheck-suppress uninitvar + (void)std::fmal(ld1,ld2,ld3); +} + +void uninitvar_fmax(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)std::fmaxf(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)std::fmax(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)std::fmaxl(ld1,ld2); +} + +void uninitvar_fmin(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)std::fminf(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)std::fmin(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)std::fminl(ld1,ld2); +} + +void uninitvar_fmod(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)std::fmod(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)std::fmod(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)std::fmod(ld1,ld2); +} + +void uninitar_fopen(void) +{ + const char *filename; + const char *mode; + // cppcheck-suppress uninitvar + FILE * fp = std::fopen(filename, mode); + fclose(fp); +} + +void uninitar_fprintf(FILE *Stream, const char *Format, int Argument) +{ + FILE *stream1, *stream2; + const char *format1, *format2; + int argument1, argument2; + // cppcheck-suppress uninitvar + (void)std::fprintf(stream1, format1, argument1); + // cppcheck-suppress uninitvar + (void)std::fprintf(stream2, Format, Argument); + // cppcheck-suppress uninitvar + (void)std::fprintf(Stream, format2, Argument); + // cppcheck-suppress uninitvar + (void)std::fprintf(Stream, Format, argument2); + + // no warning is expected + (void)std::fprintf(Stream, Format, Argument); +} + +void uninitar_vfprintf(FILE *Stream, const char *Format, va_list Arg) +{ + FILE *stream1, *stream2; + const char *format1, *format2; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)std::vfprintf(stream1, format1, arg); + // cppcheck-suppress uninitvar + (void)std::vfprintf(stream2, Format, Arg); + // cppcheck-suppress uninitvar + (void)std::vfprintf(Stream, format2, Arg); + + // no warning is expected + (void)std::vfprintf(Stream, Format, Arg); + // cppcheck-suppress va_list_usedBeforeStarted + (void)std::vfprintf(Stream, Format, arg); +} + +void uninitar_vfwprintf(FILE *Stream, const wchar_t *Format, va_list Arg) +{ + FILE *stream1, *stream2; + const wchar_t *format1, *format2; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)std::vfwprintf(stream1, format1, arg); + // cppcheck-suppress uninitvar + (void)std::vfwprintf(stream2, Format, Arg); + // cppcheck-suppress uninitvar + (void)std::vfwprintf(Stream, format2, Arg); + + // no warning is expected + (void)std::vfwprintf(Stream, Format, Arg); + // cppcheck-suppress va_list_usedBeforeStarted + (void)std::vfwprintf(Stream, Format, arg); +} + +void uninitvar_fputc(void) +{ + int c; + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::fputc(c,stream); +} + +void uninitvar_fputwc(void) +{ + wchar_t c; + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::fputwc(c,stream); +} + +void uninitvar_fputs(void) +{ + const char *string; + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::fputs(string,stream); +} + +void uninitvar_fputws(void) +{ + const wchar_t *string; + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::fputws(string,stream); +} + +void uninitvar_fread(void) +{ + void *ptr; + size_t size; + size_t nobj; + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::fread(ptr,size,nobj,stream); +} + +void uninitvar_free(void) +{ + // cppcheck-suppress unassignedVariable + void *block; + // cppcheck-suppress uninitvar + std::free(block); +} + +void uninitvar_freopen(void) +{ + const char *filename; + const char *mode; + FILE *stream; + // cppcheck-suppress uninitvar + FILE * p = std::freopen(filename,mode,stream); + std::fclose(p); +} + +void uninitvar_frexp(void) +{ + float f1; + int *i1; + // cppcheck-suppress uninitvar + (void)std::frexp(f1,i1); + + double d1; + int *i2; + // cppcheck-suppress uninitvar + (void)std::frexp(d1,i2); + + long double ld1; + int *i3; + // cppcheck-suppress uninitvar + (void)std::frexp(ld1,i3); +} + +void uninitvar_hypot(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)std::hypotf(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)std::hypot(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)std::hypotl(ld1,ld2); +} + +void uninitvar_fscanf(void) +{ + FILE *stream; + const char *format; + int i; + // cppcheck-suppress uninitvar + (void)std::fscanf(stream,format,i); +} + +void uninitvar_vfscanf(void) +{ + FILE *stream; + const char *format; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)std::vfscanf(stream,format,arg); +} + +void uninitvar_vfwscanf(void) +{ + FILE *stream; + const wchar_t *format; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)std::vfwscanf(stream,format,arg); +} + +void uninitvar_fseek(void) +{ + FILE* stream; + long int offset; + int origin; + // cppcheck-suppress uninitvar + (void)std::fseek(stream,offset,origin); +} + +void invalidFunctionArg_fseek(FILE* stream, long int offset, int origin) +{ + // cppcheck-suppress invalidFunctionArg + (void)std::fseek(stream, offset, -1); + // cppcheck-suppress invalidFunctionArg + (void)std::fseek(stream, offset, 3); + // cppcheck-suppress invalidFunctionArg + (void)std::fseek(stream, offset, 42+SEEK_SET); + // cppcheck-suppress invalidFunctionArg + (void)std::fseek(stream, offset, SEEK_SET+42); + // No warning is expected for + (void)std::fseek(stream, offset, origin); + (void)std::fseek(stream, offset, SEEK_SET); + (void)std::fseek(stream, offset, SEEK_CUR); + (void)std::fseek(stream, offset, SEEK_END); +} + +void invalidFunctionArgBool_fseek(FILE* stream, long int offset, int origin) +{ + // cppcheck-suppress invalidFunctionArgBool + (void)std::fseek(stream, offset, true); + // cppcheck-suppress invalidFunctionArgBool + (void)std::fseek(stream, offset, false); +} + +void uninitvar_fsetpos(void) +{ + FILE* stream; + const fpos_t *ptr; + // cppcheck-suppress uninitvar + (void)std::fsetpos(stream,ptr); +} + +wchar_t* nullPointer_fgetws(wchar_t* buffer, int n, FILE* stream) +{ + // cppcheck-suppress nullPointer + (void)std::fgetws(NULL,n,stream); + // cppcheck-suppress nullPointer + (void)std::fgetws(buffer,n,NULL); + // No warning is expected + return std::fgetws(buffer, n, stream); +} + +void nullPointer_wmemcmp(const wchar_t* s1, const wchar_t* s2, size_t n) +{ + // cppcheck-suppress nullPointer + (void)std::wmemcmp(NULL,s2,n); + // cppcheck-suppress nullPointer + (void)std::wmemcmp(s1,NULL,n); + (void)std::wmemcmp(s1,s2,n); +} + + +void nullPointer_memcmp(const void *s1, const void *s2, size_t n) +{ + // cppcheck-suppress nullPointer + (void)std::memcmp(NULL,s2,n); + // cppcheck-suppress nullPointer + (void)std::memcmp(s1,NULL,n); + (void)std::memcmp(s1,s2,n); +} + +void nullPointer_strncat(char *d, const char *s, size_t n) +{ + // cppcheck-suppress nullPointer + (void)std::strncat(NULL,s,n); + // cppcheck-suppress nullPointer + (void)std::strncat(d,NULL,n); + // no warning is expected for + (void)std::strncat(d,s,n); +} + +void nullPointer_strcpy(char *dest, const char * const source) +{ + // cppcheck-suppress nullPointer + (void)std::strcpy(NULL,source); + // cppcheck-suppress nullPointer + (void)std::strcpy(dest,NULL); + + // no warning shall be shown for + (void)std::strcpy(dest,source); +} + +void nullPointer_strcat(char *dest, const char * const source) +{ + // cppcheck-suppress nullPointer + (void)std::strcat(NULL,source); + // cppcheck-suppress nullPointer + (void)std::strcat(dest,NULL); + + // no warning shall be shown for + (void)std::strcat(dest,source); +} + +void nullPointer_strncpy(char *d, const char *s, size_t n) +{ + // cppcheck-suppress nullPointer + (void)std::strncpy(NULL,s,n); + // cppcheck-suppress nullPointer + (void)std::strncpy(d,NULL,n); + // no warning is expected for + (void)std::strncpy(d,s,n); +} + +void nullPointer_strncmp(const char *s1, const char *s2, size_t n) +{ + // cppcheck-suppress nullPointer + (void)std::strncmp(NULL,s2,n); + // cppcheck-suppress nullPointer + (void)std::strncmp(s1,NULL,n); + (void)std::strncmp(s1,s2,n); +} + +char* nullPointer_fgets(char *buffer, int n, FILE *stream) +{ + // cppcheck-suppress nullPointer + (void)std::fgets(NULL,n,stream); + // cppcheck-suppress nullPointer + (void)std::fgets(buffer,n,NULL); + // No warning is expected + return std::fgets(buffer, n, stream); +} + +void uninitvar_fgets(void) +{ + char *buffer; + int n; + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::fgets(buffer,n,stream); +} + +void uninitvar_fgetws(void) +{ + wchar_t *buffer; + int n; + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::fgetws(buffer,n,stream); +} + +void uninitvar_ftell(void) +{ + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::ftell(stream); +} + +void uninitvar_fwide(void) +{ + FILE *stream; + int mode; + // cppcheck-suppress uninitvar + (void)std::fwide(stream,mode); +} + +void uninitvar_fwrite(void) +{ + const void *ptr; + size_t size; + size_t nobj; + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::fwrite(ptr,size,nobj,stream); +} + +void uninitvar_mblen(void) +{ + const char *string; + size_t size; + // cppcheck-suppress uninitvar + (void)std::mblen(string,size); +} + +void uninitvar_mbtowc(void) +{ + wchar_t* pwc; + const char* pmb; + size_t max; + // cppcheck-suppress uninitvar + (void)std::mbtowc(pwc,pmb,max); +} + +void uninitvar_mbrlen(const char* p, size_t m, mbstate_t* s) +{ + const char* pmb1, *pmb2; + size_t max1, max2; + mbstate_t* ps1, *ps2; + // cppcheck-suppress uninitvar + (void)std::mbrlen(pmb1,max1,ps1); + // cppcheck-suppress uninitvar + (void)std::mbrlen(pmb2,m,s); + // cppcheck-suppress uninitvar + (void)std::mbrlen(p,max2,s); + // cppcheck-suppress uninitvar + (void)std::mbrlen(p,m,ps2); + // no warning is expected + (void)std::mbrlen(p,m,s); +} + +void nullPointer_mbrlen(const char* p, size_t m, mbstate_t* s) +{ + // no warning is expected: A call to the function with a null pointer as pmb resets the shift state (and ignores parameter max). + (void)std::mbrlen(NULL,m,s); + (void)std::mbrlen(NULL,0,s); + // cppcheck-suppress nullPointer + (void)std::mbrlen(p,m,NULL); +} + +void uninitvar_btowc(void) +{ + int c; + // cppcheck-suppress uninitvar + (void)std::btowc(c); +} + +void uninitvar_mbsinit(void) +{ + const mbstate_t* ps; + // cppcheck-suppress uninitvar + (void)std::mbsinit(ps); +} + +void uninitvar_mbstowcs(void) +{ + wchar_t *ws; + const char *s; + size_t n; + // cppcheck-suppress uninitvar + (void)std::mbstowcs(ws,s,n); +} + +void uninitvar_mbsrtowcs(void) +{ + wchar_t* dest; + const char* src; + size_t max; + mbstate_t* ps; + // cppcheck-suppress uninitvar + (void)std::mbsrtowcs(dest,&src,max,ps); +} + +void uninitvar_wctob(void) +{ + wint_t wc; + // cppcheck-suppress uninitvar + (void)std::wctob(wc); +} + +void uninitvar_wctomb(void) +{ + char *s; + wchar_t wc; + // cppcheck-suppress uninitvar + (void)std::wctomb(s,wc); +} + +void uninitvar_wcstombs(void) +{ + char *mbstr; + const wchar_t *wcstr; + size_t n; + // cppcheck-suppress uninitvar + (void)std::wcstombs(mbstr,wcstr,n); +} + +void uninitvar_getc(void) +{ + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::getc(stream); +} + +void uninitvar_getwc(void) +{ + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::getwc(stream); +} + +void uninitvar_ungetc(void) +{ + int c; + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::ungetc(c,stream); +} + +void uninitvar_ungetwc(void) +{ + wint_t c; + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::ungetwc(c,stream); +} + +void uninitvar_getenv(void) +{ + const char *name; + // cppcheck-suppress uninitvar + (void)std::getenv(name); +} + +void uninitvar_gmtime(void) +{ + const time_t *tp; + // cppcheck-suppress uninitvar + (void)std::gmtime(tp); +} + +void uninitvar_iswalnum(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)std::iswalnum(i); +} + +void uninitvar_iswalpha(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)std::iswalpha(i); +} + +void uninitvar_isblank(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::isblank(i); +} + +void uninitvar_iswblank(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)std::iswblank(i); +} + +void uninitvar_iswcntrl(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)std::iswcntrl(i); +} + +void uninitvar_iswctype(void) +{ + wint_t c; + wctype_t desc; + // cppcheck-suppress uninitvar + (void)std::iswctype(c,desc); +} + +void uninitvar_iswdigit(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)std::iswdigit(i); +} + +void uninitvar_iswgraph(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)std::iswgraph(i); +} + +void uninitvar_iswlower(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)std::iswlower(i); +} + +void uninitvar_iswprint(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)std::iswprint(i); +} + +void uninitvar_ispunct(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::ispunct(i); +} + +void uninitvar_iswpunct(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)std::iswpunct(i); +} + +void uninitvar_iswspace(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)std::iswspace(i); +} + +void uninitvar_iswupper(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)std::iswupper(i); +} + +void uninitvar_iswxdigit(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)std::iswxdigit(i); +} + +void uninitvar_towctrans(void) +{ + wint_t c; + wctrans_t desc; + // cppcheck-suppress uninitvar + (void)std::towctrans(c,desc); +} + +void uninitvar_towlower(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)std::towlower(i); +} + +void uninitvar_towupper(void) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)std::towupper(i); +} + +void uninitvar_wctrans(void) +{ + const char* property; + // cppcheck-suppress uninitvar + (void)std::wctrans(property); +} + +void uninitvar_wctype(void) +{ + const char* property; + // cppcheck-suppress uninitvar + (void)std::wctype(property); +} + +void uninitvar_labs(void) +{ + long int li; + // cppcheck-suppress uninitvar + (void)std::labs(li); + + long long int lli; + // cppcheck-suppress uninitvar + (void)std::llabs(lli); +} + +void uninitvar_ldexp(void) +{ + float fd; + int e1; + // cppcheck-suppress uninitvar + (void)std::ldexp(fd,e1); + + double dc; + int e2; + // cppcheck-suppress uninitvar + (void)std::ldexp(dc,e2); + + long double ldc; + int e3; + // cppcheck-suppress uninitvar + (void)std::ldexp(ldc,e3); +} + +void invalidFunctionArg_lgamma(float f, double d, long double ld) +{ + (void)lgamma(d); + // TODO cppcheck-suppress invalidFunctionArg + (void)lgamma(-0.1); + // cppcheck-suppress invalidFunctionArg + (void)lgamma(0.0); + (void)lgamma(0.1); + + (void)lgammaf(f); + // TODO cppcheck-suppress invalidFunctionArg + (void)lgammaf(-0.1f); + // cppcheck-suppress invalidFunctionArg + (void)lgammaf(0.0f); + (void)lgammaf(0.1f); + + (void)lgammal(ld); + // TODO cppcheck-suppress invalidFunctionArg + (void)lgammal(-0.1L); + // cppcheck-suppress invalidFunctionArg + (void)lgammal(0.0L); + (void)lgammal(0.1L); +} + +void invalidFunctionArg_tgamma(float f, double d, long double ld) +{ + (void)tgamma(d); + // TODO cppcheck-suppress invalidFunctionArg + (void)tgamma(-0.1); + // cppcheck-suppress invalidFunctionArg + (void)tgamma(0.0); + (void)tgamma(0.1); + + (void)tgammaf(f); + // TODO cppcheck-suppress invalidFunctionArg + (void)tgammaf(-0.1f); + // cppcheck-suppress invalidFunctionArg + (void)tgammaf(0.0f); + (void)tgammaf(0.1f); + + (void)tgammal(ld); + // TODO cppcheck-suppress invalidFunctionArg + (void)tgammal(-0.1L); + // cppcheck-suppress invalidFunctionArg + (void)tgammal(0.0L); + (void)tgammal(0.1L); +} + +void uninitvar_lgamma(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::lgammaf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::lgamma(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::lgammal(ld); +} + +void uninitvar_rint(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::rintf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::rint(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::rintl(ld); +} + +void uninitvar_lrint(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::lrintf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::lrint(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::lrintl(ld); +} + +void uninitvar_llrint(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::llrintf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::llrint(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::llrintl(ld); +} + +void uninitvar_lround(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::lroundf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::lround(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::lroundl(ld); +} + +void uninitvar_llround(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::llroundf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::llround(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::llroundl(ld); +} + +void uninitvar_srand(void) +{ + unsigned int seed; + // cppcheck-suppress uninitvar + (void)std::srand(seed); + // cppcheck-suppress ignoredReturnValue + std::rand(); +} + +void uninitvar_ldiv(void) +{ + long int l1; + long int l2; + // cppcheck-suppress uninitvar + (void)std::ldiv(l1,l2); + + long long int ll1; + long long int ll2; + // cppcheck-suppress uninitvar + (void)std::lldiv(ll1,ll2); +} + +void uninitvar_localtime(void) +{ + const time_t *tp; + // cppcheck-suppress uninitvar + (void)std::localtime(tp); +} + +void uninitvar_log(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::log(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::log(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::log(ld); +} + +void uninitvar_fpclassify(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::fpclassify(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::fpclassify(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::fpclassify(ld); +} + +void uninitvar_isfinite(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::isfinite(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::isfinite(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::isfinite(ld); +} + +void uninitvar_isgreater(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)std::isgreater(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)std::isgreater(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)std::isgreater(ld1,ld2); +} + +void uninitvar_isgreaterequal(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)std::isgreaterequal(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)std::isgreaterequal(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)std::isgreaterequal(ld1,ld2); +} + +void uninitvar_isinf(void) +{ + double d; + // cppcheck-suppress uninitvar + (void)std::isinf(d); +} + +void uninitvar_logb(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::logbf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::logb(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::logbl(ld); +} + +void uninitvar_isless(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)std::isless(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)std::isless(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)std::isless(ld1,ld2); +} + +void uninitvar_islessequal(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)std::islessequal(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)std::islessequal(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)std::islessequal(ld1,ld2); +} + +void uninitvar_islessgreater(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)std::islessgreater(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)std::islessgreater(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)std::islessgreater(ld1,ld2); +} + +void uninitvar_nan(void) +{ + const char *tagp1, *tagp2, *tagp3; + // cppcheck-suppress uninitvar + (void)std::nanf(tagp1); + // cppcheck-suppress uninitvar + (void)std::nan(tagp2); + // cppcheck-suppress uninitvar + (void)std::nanl(tagp3); +} + +void uninitvar_isnan(void) +{ + double d; + // cppcheck-suppress uninitvar + (void)std::isnan(d); +} + +void uninitvar_isnormal(void) +{ + double d; + // cppcheck-suppress uninitvar + (void)std::isnormal(d); +} + +void uninitvar_isunordered(void) +{ + double d1,d2; + // cppcheck-suppress uninitvar + (void)std::isunordered(d1,d2); +} + +void uninitvar_ilogb(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::ilogb(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::ilogb(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::ilogb(ld); +} + +void uninitvar_log10(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::log10(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::log10(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::log10(ld); +} + +void uninitvar_log1p(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::log1pf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::log1p(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::log1pl(ld); +} + +void uninitvar_log2(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::log2f(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::log2(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::log2l(ld); +} + +void uninitvar_nearbyint(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::nearbyintf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::nearbyint(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::nearbyintl(ld); +} + +void uninitvar_nextafter(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)std::nextafterf(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)std::nextafter(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)std::nextafterl(ld1,ld2); +} + +void uninitvar_nexttoward(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)std::nexttowardf(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)std::nexttoward(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)std::nexttowardl(ld1,ld2); +} + +void uninitvar_longjmp(void) +{ + jmp_buf env; + int val; + // cppcheck-suppress uninitvar + (void)std::longjmp(env,val); +} + +void uninitvar_malloc(void) +{ + size_t size; + // cppcheck-suppress [uninitvar, cstyleCast, unusedAllocatedMemory] + int *p = (int*)std::malloc(size); + free(p); +} + +void uninitvar_memchr(void) +{ + void *cs; + int c; + size_t n; + // cppcheck-suppress uninitvar + (void)std::memchr(cs,c,n); +} + +void uninitvar_wmemchr(void) +{ + wchar_t *cs; + wchar_t c; + size_t n; + // cppcheck-suppress uninitvar + (void)std::wmemchr(cs,c,n); +} + +void uninitvar_memcmp(void) +{ + const void *s1; + const void *s2; + size_t n; + // cppcheck-suppress uninitvar + (void)std::memcmp(s1,s2,n); +} + +void uninitvar_wmemcmp(void) +{ + const wchar_t *s1; + const wchar_t *s2; + size_t n; + // cppcheck-suppress uninitvar + (void)std::wmemcmp(s1,s2,n); +} + +void uninitvar_memcpy(void) +{ + void *ct; + const void *cs; + size_t n; + // cppcheck-suppress uninitvar + (void)std::memcpy(ct,cs,n); +} + +void uninitvar_wmemcpy(void) +{ + wchar_t *cs; + const wchar_t *c; + size_t n; + // cppcheck-suppress uninitvar + (void)std::wmemcpy(cs,c,n); +} + +void uninitvar_memmove(void) +{ + void *ct; + const void *cs; + size_t n; + // cppcheck-suppress uninitvar + (void)std::memmove(ct,cs,n); +} + +void uninitvar_wmemmove(void) +{ + wchar_t *cs; + wchar_t *c; + size_t n; + // cppcheck-suppress uninitvar + (void)std::wmemmove(cs,c,n); +} + +void uninitvar_memset(void) +{ + void *s; + int c; + size_t n; + // cppcheck-suppress uninitvar + (void)std::memset(s,c,n); +} + +void uninitvar_wmemset(void) +{ + wchar_t *cs; + wchar_t c; + size_t n; + // cppcheck-suppress uninitvar + (void)std::wmemset(cs,c,n); +} + +void uninitvar_mktime(void) +{ + struct tm *tp; + // cppcheck-suppress uninitvar + (void)std::mktime(tp); +} + +void uninivar_modf(void) +{ + float f1; + float *f2; + // cppcheck-suppress uninitvar + (void)std::modf(f1,f2); + + double d1; + double *d2; + // cppcheck-suppress uninitvar + (void)std::modf(d1,d2); + + long double ld1; + long double *ld2; + // cppcheck-suppress uninitvar + (void)std::modf(ld1,ld2); +} + +void uninivar_perror(void) +{ + const char *string; + // cppcheck-suppress uninitvar + (void)std::perror(string); +} + +void uninitvar_pow(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)std::pow(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)std::pow(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)std::pow(ld1,ld2); +} + +void uninitvar_remainder(void) +{ + float f1,f2; + // cppcheck-suppress uninitvar + (void)std::remainderf(f1,f2); + + double d1,d2; + // cppcheck-suppress uninitvar + (void)std::remainder(d1,d2); + + long double ld1,ld2; + // cppcheck-suppress uninitvar + (void)std::remainderl(ld1,ld2); +} + +void uninitvar_remquo(void) +{ + float f1,f2; + int *i1; + // cppcheck-suppress uninitvar + (void)std::remquof(f1,f2,i1); + + double d1,d2; + int *i2; + // cppcheck-suppress uninitvar + (void)std::remquo(d1,d2,i2); + + long double ld1,ld2; + int *i3; + // cppcheck-suppress uninitvar + (void)std::remquol(ld1,ld2,i3); +} + +void uninivar_printf(const char *Format, int Argument) +{ + const char * format_1, * format_2, * format_3; + int argument1, argument2; + // no warning is expected + (void)std::printf("x"); + // cppcheck-suppress uninitvar + (void)std::printf(format_1,argument1); + // cppcheck-suppress uninitvar + (void)std::printf(Format,argument2); + // cppcheck-suppress uninitvar + (void)std::printf(format_2,Argument); + // cppcheck-suppress uninitvar + (void)std::printf(format_3,1); + + // no warning is expected + (void)std::printf(Format,Argument); +} + +void uninivar_vprintf(const char *Format, va_list Arg) +{ + const char * format1, *format2; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)std::vprintf(format1,arg); + // cppcheck-suppress uninitvar + (void)std::vprintf(format2,Arg); + + // no warning is expected + (void)std::vprintf(Format,Arg); + // cppcheck-suppress va_list_usedBeforeStarted + (void)std::vprintf(Format,arg); +} + +void uninivar_vwprintf(const wchar_t *Format, va_list Arg) +{ + const wchar_t * format1, *format2; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)std::vwprintf(format1,arg); + // cppcheck-suppress uninitvar + (void)std::vwprintf(format2,Arg); + + // no warning is expected + (void)std::vwprintf(Format,Arg); + // cppcheck-suppress va_list_usedBeforeStarted + (void)std::vwprintf(Format,arg); +} + +void uninivar_bsearch(void) +{ + const void* key; + const void* base; + size_t num; + size_t size; + // cppcheck-suppress [uninitvar, cstyleCast] + (void)std::bsearch(key,base,num,size,(int (*)(const void*,const void*))strcmp); +} + +void minsize_bsearch(const void* key, const void* base, + size_t num, size_t size, + int (*compar)(const void*,const void*)) +{ + const int Base[3] = {42, 43, 44}; + + (void)std::bsearch(key,Base,2,size,(int (*)(const void*,const void*))strcmp); // cppcheck-suppress cstyleCast + (void)std::bsearch(key,Base,3,size,(int (*)(const void*,const void*))strcmp); // cppcheck-suppress cstyleCast + (void)std::bsearch(key,Base,4,size,(int (*)(const void*,const void*))strcmp); // cppcheck-suppress cstyleCast + + (void)std::bsearch(key,base,2,size,(int (*)(const void*,const void*))strcmp); // cppcheck-suppress cstyleCast +} + +void uninitvar_qsort(void) +{ + void *base; + size_t n; + size_t size; + // cppcheck-suppress uninitvar + (void)std::qsort(base,n,size, (int (*)(const void*,const void*))strcmp); // cppcheck-suppress cstyleCast +} + +void uninitvar_stable_sort(std::vector& v) +{ + std::vector::iterator end; + // cppcheck-suppress uninitvar + std::stable_sort(v.begin(), end); +} + +void uninitvar_merge(const std::vector& a, const std::vector& b) +{ + std::vector::iterator dst; + // cppcheck-suppress uninitvar + std::merge(a.begin(), a.end(), b.begin(), b.end(), dst); +} + +void uninitvar_push_heap(std::vector& v) +{ + std::vector::iterator end; + // cppcheck-suppress uninitvar + std::push_heap(v.begin(), end); +} + +void uninitvar_copy_n(const std::vector& v) +{ + std::vector::iterator dst; + // cppcheck-suppress [uninitvar, invalidFunctionArg] + std::copy_n(v.begin(), -1, dst); +} + +void uninitvar_iota(std::vector& v) +{ + int i; + // cppcheck-suppress uninitvar + std::iota(v.begin(), v.end(), i); +} + +void uninitvar_putc(void) +{ + int c; + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::putc(c,stream); +} + +void uninitvar_putwc(void) +{ + wchar_t c; + FILE *stream; + // cppcheck-suppress uninitvar + (void)std::putc(c,stream); +} + +void uninitvar_putchar(void) +{ + int c; + // cppcheck-suppress uninitvar + (void)std::putchar(c); +} + +void uninitvar_putwchar(void) +{ + wchar_t c; + // cppcheck-suppress uninitvar + (void)std::putwchar(c); +} + +void uninitvar_puts(void) +{ + const char *s; + // cppcheck-suppress uninitvar + (void)std::puts(s); +} + +void uninitvar_realloc(void) +{ + void *block; + size_t newsize; + // cppcheck-suppress uninitvar + void *p = std::realloc(block, newsize); + free(p); +} + +void uninitvar_remove(void) +{ + const char *s; + // cppcheck-suppress uninitvar + (void)std::remove(s); +} + +void uninitvar_rename(void) +{ + const char *s1; + const char *s2; + // cppcheck-suppress uninitvar + (void)std::rename(s1,s2); +} + +void uninitvar_rewind(void) +{ + FILE *f; + // cppcheck-suppress uninitvar + (void)std::rewind(f); +} + +void uninitvar_round(void) +{ + float f; + // cppcheck-suppress uninitvar + (void)std::roundf(f); + + double d; + // cppcheck-suppress uninitvar + (void)std::round(d); + + long double ld; + // cppcheck-suppress uninitvar + (void)std::roundl(ld); +} + +void uninivar_scalbn(void) +{ + float f; + int i1; + // cppcheck-suppress uninitvar + (void)std::scalbnf(f,i1); + + double d; + int i2; + // cppcheck-suppress uninitvar + (void)std::scalbn(d,i2); + + long double ld; + int i3; + // cppcheck-suppress uninitvar + (void)std::scalbnl(ld,i3); +} + +void uninivar_scalbln(void) +{ + float f; + long int i1; + // cppcheck-suppress uninitvar + (void)std::scalblnf(f,i1); + + double d; + long int i2; + // cppcheck-suppress uninitvar + (void)std::scalbln(d,i2); + + long double ld; + long int i3; + // cppcheck-suppress uninitvar + (void)std::scalblnl(ld,i3); +} + +void uninitvar_signbit(void) +{ + double d; + // cppcheck-suppress uninitvar + (void)std::signbit(d); +} + +void uninivar_signal(void) +{ + int i; + // cppcheck-suppress uninitvar + std::signal(i, exit); +} + +void uninivar_raise(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::raise(i); +} + +void uninivar_scanf(void) +{ + const char *format; + char str[42]; + // cppcheck-suppress uninitvar + (void)std::scanf(format, str); +} + +void uninivar_vsscanf(void) +{ + const char *s; + const char *format; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)std::vsscanf(s,format,arg); +} + +void uninivar_vswscanf(void) +{ + const wchar_t *s; + const wchar_t *format; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)std::vswscanf(s,format,arg); +} + +void uninivar_vscanf(void) +{ + const char *format; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)std::vscanf(format,arg); +} + +void uninivar_vwscanf(void) +{ + const wchar_t *format; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)std::vwscanf(format,arg); +} + +void uninivar_setbuf(void) +{ + FILE *stream; + char *buf; + // cppcheck-suppress uninitvar + (void)std::setbuf(stream,buf); +} + +void nullPointer_setbuf(FILE *stream, char *buf) +{ + // cppcheck-suppress nullPointer + std::setbuf(NULL,buf); + std::setbuf(stream,NULL); + std::setbuf(stream,buf); +} + +int bufferAccessOutOfBounds_setvbuf(FILE* stream, int mode, size_t size) +{ + char buf[42]={0}; + // cppcheck-suppress bufferAccessOutOfBounds + (void) std::setvbuf(stream, buf, mode, 43); + return std::setvbuf(stream, buf, mode, 42); +} + +int nullPointer_setvbuf(FILE* stream, char *buf, int mode, size_t size) +{ + // cppcheck-suppress nullPointer + (void) std::setvbuf(NULL, buf, mode, size); + (void) std::setvbuf(stream, NULL, mode, size); + return std::setvbuf(stream, buf, mode, size); +} + +void uninivar_setvbuf(void) +{ + FILE *stream; + char *buf; + int mode; + size_t size; + // cppcheck-suppress uninitvar + (void)std::setvbuf(stream,buf,mode,size); +} + +void uninitvar_strcat(char *dest, const char * const source) +{ + char *deststr1, *deststr2; + const char *srcstr1, *srcstr2; + // cppcheck-suppress uninitvar + (void)std::strcat(deststr1,srcstr1); + // cppcheck-suppress uninitvar + (void)std::strcat(dest,srcstr2); + // cppcheck-suppress uninitvar + (void)std::strcat(deststr2,source); + + // no warning shall be shown for + (void)std::strcat(dest,source); +} + +void uninitvar_wcscat(wchar_t *dest, const wchar_t * const source) +{ + wchar_t *deststr_1, *deststr_2; + const wchar_t *srcstr1, *srcstr2; + // cppcheck-suppress uninitvar + (void)std::wcscat(deststr_1,srcstr1); + // cppcheck-suppress uninitvar + (void)std::wcscat(dest,srcstr2); + // cppcheck-suppress uninitvar + (void)std::wcscat(deststr_2,source); + + // no warning shall be shown for + (void)std::wcscat(dest,source); +} + +void uninivar_wcrtomb(void) +{ + char *s; + wchar_t wc; + mbstate_t *ps; + // cppcheck-suppress uninitvar + (void)std::wcrtomb(s,wc,ps); +} + +void uninivar_strchr(void) +{ + const char *cs; + int c; + // cppcheck-suppress uninitvar + (void)std::strchr(cs,c); +} + +void uninivar_wcschr(void) +{ + const wchar_t *cs; + wchar_t c; + // cppcheck-suppress uninitvar + (void)std::wcschr(cs,c); +} + +void uninivar_strcmp(void) +{ + const char *str1; + const char *str2; + // cppcheck-suppress uninitvar + (void)std::strcmp(str1,str2); +} + +void uninivar_wcscmp(void) +{ + const wchar_t *str1; + const wchar_t *str2; + // cppcheck-suppress uninitvar + (void)std::wcscmp(str1,str2); +} + +void uninivar_strcpy(void) +{ + char *str1; + const char *str2; + // cppcheck-suppress uninitvar + (void)std::strcpy(str1,str2); +} + +void uninivar_wcscpy(void) +{ + wchar_t *str1; + const wchar_t *str2; + // cppcheck-suppress uninitvar + (void)std::wcscpy(str1,str2); +} + +void uninivar_strftime(void) +{ + char *s; + size_t max; + const char *fmt; + const struct tm *p; + // cppcheck-suppress uninitvar + (void)std::strftime(s,max,fmt,p); +} + +void uninivar_strlen(void) +{ + const char *s; + // cppcheck-suppress uninitvar + (void)std::strlen(s); +} + +void uninivar_wcslen(void) +{ + const wchar_t *s; + // cppcheck-suppress uninitvar + (void)std::wcslen(s); +} + +void uninivar_strncpy(void) +{ + char *s; + const char *ct; + size_t n; + // cppcheck-suppress uninitvar + (void)std::strncpy(s,ct,n); +} + +void uninivar_strpbrk(void) +{ + const char *cs; + const char *ct; + // cppcheck-suppress uninitvar + (void)std::strpbrk(cs,ct); +} + +void uninivar_strncat(char *Ct, const char *S, size_t N) +{ + char *ct_1, *ct_2; + const char *s1, *s2; + size_t n1, n2; + // cppcheck-suppress uninitvar + (void)std::strncat(ct_1,s1,n1); + // cppcheck-suppress uninitvar + (void)std::strncat(ct_2,S,N); + // cppcheck-suppress uninitvar + (void)std::strncat(Ct,s2,N); + // cppcheck-suppress uninitvar + (void)std::strncat(Ct,S,n2); + + // no warning is expected for + (void)std::strncat(Ct,S,N); +} + +void uninivar_wcsncat(wchar_t *Ct, const wchar_t *S, size_t N) +{ + wchar_t *ct_1, *ct_2; + const wchar_t *s1, *s2; + size_t n1, n2; + // cppcheck-suppress uninitvar + (void)std::wcsncat(ct_1,s1,n1); + // cppcheck-suppress uninitvar + (void)std::wcsncat(ct_2,S,N); + // cppcheck-suppress uninitvar + (void)std::wcsncat(Ct,s2,N); + // cppcheck-suppress uninitvar + (void)std::wcsncat(Ct,S,n2); + + // no warning is expected for + (void)std::wcsncat(Ct,S,N); +} + +void uninivar_strncmp(const char *Ct, const char *S, size_t N) +{ + const char *ct1, *ct2; + const char *s1, *s2; + size_t n1, n2; + // cppcheck-suppress uninitvar + (void)std::strncmp(ct1,s1,n1); + // cppcheck-suppress uninitvar + (void)std::strncmp(ct2,S,N); + // cppcheck-suppress uninitvar + (void)std::strncmp(Ct,s2,N); + // cppcheck-suppress uninitvar + (void)std::strncmp(Ct,S,n2); + + // no warning is expected for + (void)std::strncmp(Ct,S,N); +} + +void uninivar_wcsncmp(const wchar_t *Ct, const wchar_t *S, size_t N) +{ + const wchar_t *ct1, *ct2; + const wchar_t *s1, *s2; + size_t n1, n2; + // cppcheck-suppress uninitvar + (void)std::wcsncmp(ct1,s1,n1); + // cppcheck-suppress uninitvar + (void)std::wcsncmp(ct2,S,N); + // cppcheck-suppress uninitvar + (void)std::wcsncmp(Ct,s2,N); + // cppcheck-suppress uninitvar + (void)std::wcsncmp(Ct,S,n2); + + // no warning is expected for + (void)std::wcsncmp(Ct,S,N); +} + +void uninivar_strstr(void) +{ + char *cs; + const char *ct; + // cppcheck-suppress uninitvar + (void)std::strstr(cs,ct); +} + +void uninivar_wcsstr(void) +{ + wchar_t *cs; + const wchar_t *ct; + // cppcheck-suppress uninitvar + (void)std::wcsstr(cs,ct); +} + +void uninivar_strspn(void) +{ + const char *cs; + const char *ct; + // cppcheck-suppress uninitvar + (void)std::strspn(cs,ct); +} + +void uninivar_strxfrm(void) +{ + char *ds; + const char *ss; + size_t n; + // cppcheck-suppress uninitvar + (void)std::strxfrm(ds,ss,n); +} + +void uninivar_wcsxfrm(void) +{ + wchar_t *ds; + const wchar_t *ss; + size_t n; + // cppcheck-suppress uninitvar + (void)std::wcsxfrm(ds,ss,n); +} + +void uninivar_wcsspn(void) +{ + const wchar_t *ds; + const wchar_t *ss; + // cppcheck-suppress uninitvar + (void)std::wcsspn(ds,ss); +} + +void uninivar_setlocale(void) +{ + int category; + const char* locale; + // cppcheck-suppress uninitvar + (void)std::setlocale(category,locale); +} +void uninivar_strerror(void) +{ + int i; + // cppcheck-suppress uninitvar + (void)std::strerror(i); +} + +void uninivar_strcspn(void) +{ + const char *cs; + const char *ct; + // cppcheck-suppress uninitvar + (void)std::strcspn(cs,ct); +} + +void uninivar_wcscspn(void) +{ + const wchar_t *cs; + const wchar_t *ct; + // cppcheck-suppress uninitvar + (void)std::wcscspn(cs,ct); +} + +void uninivar_wcspbrk(void) +{ + const wchar_t *cs; + const wchar_t *ct; + // cppcheck-suppress uninitvar + (void)std::wcspbrk(cs,ct); +} + +void uninivar_wcsncpy(void) +{ + wchar_t *cs; + const wchar_t *ct; + size_t n; + // cppcheck-suppress uninitvar + (void)std::wcsncpy(cs,ct,n); +} + +void uninivar_strcoll(void) +{ + const char *cs; + const char *ct; + // cppcheck-suppress uninitvar + (void)std::strcoll(cs,ct); +} + +void uninivar_wcscoll(void) +{ + const wchar_t *cs; + const wchar_t *ct; + // cppcheck-suppress uninitvar + (void)std::wcscoll(cs,ct); +} + +void uninivar_strrchr(void) +{ + const char * str; + int c; + // cppcheck-suppress uninitvar + (void)std::strrchr(str,c); +} + +void uninivar_wcsrchr(void) +{ + wchar_t* ws; + wchar_t wc; + // cppcheck-suppress uninitvar + (void)std::wcsrchr(ws,wc); +} + +void uninivar_wcsrtombs(void) +{ + char *dst; + const wchar_t * p;; + size_t len; + mbstate_t *ps; + // cppcheck-suppress uninitvar + (void)std::wcsrtombs(dst,&p,len,ps); +} + +void uninivar_strtok(void) +{ + char *s; + const char *ct; + // cppcheck-suppress uninitvar + (void)std::strtok(s,ct); +} + +void uninivar_strtoimax(void) +{ + const char *s1, *s2; + char **endp1, **endp2; + int base1, base2; + // cppcheck-suppress uninitvar + (void)std::strtoimax(s1,endp1,base1); + // cppcheck-suppress uninitvar + (void)std::strtoumax(s2,endp2,base2); +} + +void uninivar_strtof(void) +{ + const char *s1, *s2, *s3; + char **endp1, **endp2, **endp3; + // cppcheck-suppress uninitvar + (void)std::strtof(s1,endp1); + // cppcheck-suppress uninitvar + (void)std::strtod(s2,endp2); + // cppcheck-suppress uninitvar + (void)std::strtold(s3,endp3); +} + +void uninivar_strtol(void) +{ + const char *s1,*s2,*s3,*s4,*s5,*s6,*s7,*s8; + char **endp1, **endp2, **endp3, **endp4, **endp5, **endp6, **endp7, **endp8; + int base1, base2, base3, base4, base5, base6, base7, base8; + + // cppcheck-suppress uninitvar + (void)std::strtol(s1,endp1,base1); + // cppcheck-suppress uninitvar + (void)std::strtoll(s2,endp2,base2); + // cppcheck-suppress uninitvar + (void)std::strtoul(s3,endp3,base3); + // cppcheck-suppress uninitvar + (void)std::strtoull(s4,endp4,base4); + // cppcheck-suppress uninitvar + (void)std::strtoimax(s5,endp5,base5); + // cppcheck-suppress uninitvar + (void)strtoimax(s6,endp6,base6); + // cppcheck-suppress uninitvar + (void)std::strtoumax(s7,endp7,base7); + // cppcheck-suppress uninitvar + (void)strtoumax(s8,endp8,base8); +} + +void uninitvar_time(void) +{ + time_t *tp; + // cppcheck-suppress uninitvar + (void)std::time(tp); +} + +void uninitvar_tmpnam(void) +{ + char *s; + // cppcheck-suppress uninitvar + (void)std::tmpnam(s); +} + +void uninivar_tolower(void) +{ + int c; + // cppcheck-suppress uninitvar + (void)std::tolower(c); +} + +void uninivar_toupper(void) +{ + int c; + // cppcheck-suppress uninitvar + (void)std::toupper(c); +} + +void uninivar_wcstof(void) +{ + const wchar_t *s1, *s2, *s3; + wchar_t **endp1, **endp2, **endp3; + // cppcheck-suppress uninitvar + (void)std::wcstof(s1,endp1); + // cppcheck-suppress uninitvar + (void)std::wcstod(s2,endp2); + // cppcheck-suppress uninitvar + (void)std::wcstold(s3,endp3); +} + +void uninivar_stoX(void) +{ + std::string str; + std::wstring wstr; + size_t* idx1; + size_t* idx2; + size_t* idx3; + size_t* idx4; + size_t* idx5; + size_t* idx6; + size_t* idx7; + size_t* idx8; + size_t* idx9; + size_t* idx10; + size_t* idx11; + size_t* idx12; + size_t* idx13; + size_t* idx14; + size_t* idx15; + size_t* idx16; + // cppcheck-suppress uninitvar + (void)std::stod(str,idx1); + // cppcheck-suppress uninitvar + (void)std::stod(wstr,idx2); + // cppcheck-suppress uninitvar + (void)std::stof(str,idx3); + // cppcheck-suppress uninitvar + (void)std::stof(wstr,idx4); + // cppcheck-suppress uninitvar + (void)std::stoi(str,idx5); + // cppcheck-suppress uninitvar + (void)std::stoi(wstr,idx6); + // cppcheck-suppress uninitvar + (void)std::stol(str,idx7); + // cppcheck-suppress uninitvar + (void)std::stol(wstr,idx8); + // cppcheck-suppress uninitvar + (void)std::stold(str,idx9); + // cppcheck-suppress uninitvar + (void)std::stold(wstr,idx10); + // cppcheck-suppress uninitvar + (void)std::stoll(str,idx11); + // cppcheck-suppress uninitvar + (void)std::stoll(wstr,idx12); + // cppcheck-suppress uninitvar + (void)std::stoul(str,idx13); + // cppcheck-suppress uninitvar + (void)std::stoul(wstr,idx14); + // cppcheck-suppress uninitvar + (void)std::stoull(str,idx15); + // cppcheck-suppress uninitvar + (void)std::stoull(wstr,idx16); +} + +void uninivar_to_string(void) +{ + int i; + long l; + long long ll; + unsigned u; + unsigned long ul; + unsigned long long ull; + float f; + double d; + long double ld; + // cppcheck-suppress uninitvar + (void)std::to_string(i); + // cppcheck-suppress uninitvar + (void)std::to_string(l); + // cppcheck-suppress uninitvar + (void)std::to_string(ll); + // cppcheck-suppress uninitvar + (void)std::to_string(u); + // cppcheck-suppress uninitvar + (void)std::to_string(ul); + // cppcheck-suppress uninitvar + (void)std::to_string(ull); + // cppcheck-suppress uninitvar + (void)std::to_string(f); + // cppcheck-suppress uninitvar + (void)std::to_string(d); + // cppcheck-suppress uninitvar + (void)std::to_string(ld); +} + +void uninivar_to_wstring(void) +{ + int i; + long l; + long long ll; + unsigned u; + unsigned long ul; + unsigned long long ull; + float f; + double d; + long double ld; + // cppcheck-suppress uninitvar + (void)std::to_wstring(i); + // cppcheck-suppress uninitvar + (void)std::to_wstring(l); + // cppcheck-suppress uninitvar + (void)std::to_wstring(ll); + // cppcheck-suppress uninitvar + (void)std::to_wstring(u); + // cppcheck-suppress uninitvar + (void)std::to_wstring(ul); + // cppcheck-suppress uninitvar + (void)std::to_wstring(ull); + // cppcheck-suppress uninitvar + (void)std::to_wstring(f); + // cppcheck-suppress uninitvar + (void)std::to_wstring(d); + // cppcheck-suppress uninitvar + (void)std::to_wstring(ld); +} + +void uninivar_mbrtowc(void) +{ + wchar_t* pwc; + const char* pmb; + size_t max; + mbstate_t* ps; + // cppcheck-suppress uninitvar + (void)std::mbrtowc(pwc,pmb,max,ps); +} + +void uninivar_wcstok(void) +{ + wchar_t *s; + const wchar_t *ct; + wchar_t **ptr; + // cppcheck-suppress uninitvar + (void)std::wcstok(s,ct,ptr); +} + +void uninivar_wcstoimax(void) +{ + const wchar_t *s1, *s2; + wchar_t ** endp1, **endp2; + int base1, base2; + // cppcheck-suppress uninitvar + (void)std::wcstoimax(s1,endp1,base1); + // cppcheck-suppress uninitvar + (void)std::wcstoumax(s2,endp2,base2); +} + +void uninivar_wcstol(void) +{ + const wchar_t *s1,*s2,*s3,*s4,*s5,*s6,*s7,*s8; + wchar_t **endp1, **endp2, **endp3, **endp4, **endp5, **endp6, **endp7, **endp8; + int base1, base2, base3, base4, base5, base6, base7, base8; + // cppcheck-suppress uninitvar + (void)std::wcstol(s1,endp1,base1); + // cppcheck-suppress uninitvar + (void)std::wcstoll(s2,endp2,base2); + // cppcheck-suppress uninitvar + (void)std::wcstoul(s3,endp3,base3); + // cppcheck-suppress uninitvar + (void)std::wcstoull(s4,endp4,base4); + // cppcheck-suppress uninitvar + (void)std::wcstoimax(s5,endp5,base5); + // cppcheck-suppress uninitvar + (void)wcstoimax(s6,endp6,base6); + // cppcheck-suppress uninitvar + (void)std::wcstoumax(s7,endp7,base7); + // cppcheck-suppress uninitvar + (void)wcstoumax(s8,endp8,base8); +} + +void uninitvar_wprintf(const wchar_t *Format, int Argument) +{ + const wchar_t *format1, *format2, *format3; + int argument1, argument2; + // cppcheck-suppress uninitvar + (void)std::wprintf(format1,argument1); + // cppcheck-suppress uninitvar + (void)std::wprintf(format2); + // cppcheck-suppress uninitvar + (void)std::wprintf(Format,argument2); + // cppcheck-suppress uninitvar + (void)std::wprintf(format3,Argument); + // no warning is expected + (void)std::wprintf(Format,Argument); + (void)std::wprintf(Format); +} + +void uninitvar_sprintf(void) +{ + char *s; + const char *format; + int i; + // cppcheck-suppress uninitvar + (void)std::sprintf(s,format,i); +} + +void uninitvar_swprintf(void) +{ + wchar_t *s; + size_t n; + const wchar_t *format; + // cppcheck-suppress uninitvar + (void)std::swprintf(s,n,format); +} + +void uninitvar_vsprintf(void) +{ + char *s; + const char *format; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)std::vsprintf(s,format,arg); +} + +void nullPointer_vsprintf(va_list arg,const char *format) +{ + char *s = NULL; + (void)std::vsprintf(s,format,arg); // Its allowed to provide 's' as NULL pointer + // cppcheck-suppress nullPointer + (void)std::vsprintf(s,NULL,arg); +} + +void uninitvar_vswprintf(void) +{ + wchar_t *s; + size_t n; + const wchar_t *format; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)std::vswprintf(s,n,format,arg); +} + +void uninivar_fwprintf(void) +{ + FILE* stream; + const wchar_t* format; + int i; + // cppcheck-suppress uninitvar + (void)std::fwprintf(stream,format,i); +} + +void uninivar_snprintf(char *S, size_t N, const char *Format, int Int) +{ + size_t n1, n2; + const char *format1, *format2; + int i1, i2; + char *s1, *s2; + // cppcheck-suppress uninitvar + (void)std::snprintf(s1,n1,format1,i1); + // cppcheck-suppress uninitvar + (void)std::snprintf(S,n2,Format,Int); // n is uninitialized + // cppcheck-suppress uninitvar + (void)std::snprintf(S,N,format2,Int); // format is uninitialized + // cppcheck-suppress uninitvar + (void)std::snprintf(S,N,Format,i2); // i is uninitialized + // cppcheck-suppress uninitvar + (void)std::snprintf(s2,N,Format,Int); + + // no warning is expected for + (void)std::snprintf(S,N,Format,Int); +} + +void uninivar_vsnprintf(char *S, size_t N, const char *Format, va_list Arg) +{ + char *s1, *s2; + size_t n1, n2; + const char *format1, *format2; + va_list arg; + // cppcheck-suppress va_list_usedBeforeStarted + // cppcheck-suppress uninitvar + (void)std::vsnprintf(s1,n1,format1,arg); + // cppcheck-suppress uninitvar + (void)std::vsnprintf(s2,N,Format,Arg); + // cppcheck-suppress uninitvar + (void)std::vsnprintf(S,n2,Format,Arg); + // cppcheck-suppress uninitvar + (void)std::vsnprintf(S,N,format2,Arg); + + // no warning is expected for + (void)std::vsnprintf(S,N,Format,Arg); + // cppcheck-suppress va_list_usedBeforeStarted + (void)std::vsnprintf(S,N,Format,arg); +} + +void uninivar_wscanf(void) +{ + const wchar_t *format1, *format2; + int i; + // cppcheck-suppress uninitvar + (void)std::wscanf(format1); + // cppcheck-suppress uninitvar + (void)std::wscanf(format2,&i); +} + +void uninivar_sscanf(void) +{ + const char *string1, *string2; + const char * format; + int i; + // cppcheck-suppress uninitvar + (void)std::sscanf(string1,format); + // cppcheck-suppress uninitvar + (void)std::sscanf(string2,format,&i); +} + +void uninivar_fwscanf(void) +{ + FILE* stream; + const wchar_t* format1, *format2; + int i; + // cppcheck-suppress uninitvar + (void)std::fwscanf(stream,format1); + // cppcheck-suppress uninitvar + (void)std::fwscanf(stream,format2,&i); +} + +void uninivar_swscanf(void) +{ + const wchar_t* s; + const wchar_t* format1, *format2; + int i; + // cppcheck-suppress uninitvar + (void)std::swscanf(s,format1); + // cppcheck-suppress uninitvar + (void)std::swscanf(s,format2,&i); +} + +void uninitvar_system(void) +{ + const char *c; + // cppcheck-suppress uninitvar + (void)std::system(c); +} + +#ifndef __STDC_NO_THREADS__ + +void nullPointer_mtx_destroy(mtx_t *mutex ) +{ + // cppcheck-suppress nullPointer + mtx_destroy(nullptr); + mtx_destroy(mutex); +} + +void nullPointer_mtx_lock( mtx_t *mutex ) +{ + // cppcheck-suppress nullPointer + mtx_lock(nullptr); + mtx_lock(mutex); +} + +void nullPointer_mtx_trylock( mtx_t *mutex ) +{ + // cppcheck-suppress nullPointer + mtx_trylock(nullptr); + mtx_trylock(mutex); +} + +int nullPointer_mtx_timedlock( mtx_t *mutex, const struct timespec *time_point ) +{ + // cppcheck-suppress nullPointer + (void) mtx_timedlock(nullptr, time_point); + // cppcheck-suppress nullPointer + (void) mtx_timedlock(mutex, nullptr); + return mtx_timedlock(mutex, time_point); +} +#endif + +void nullPointer_system(const char *c) +{ + // If a null pointer is given, command processor is checked for existence + (void)std::system(NULL); + (void)std::system(c); +} + +void uninitvar_setw(void) +{ + int i; + // cppcheck-suppress [uninitvar,valueFlowBailoutIncompleteVar] + std::cout << std::setw(i); +} + +void uninitvar_setiosflags(void) +{ + std::ios_base::fmtflags mask; + // TODO cppcheck-suppress uninitvar + // cppcheck-suppress valueFlowBailoutIncompleteVar + std::cout << std::setiosflags(mask); // #6987 - false negative +} + +void uninitvar_resetiosflags(void) +{ + std::ios_base::fmtflags mask; + // TODO cppcheck-suppress uninitvar + // cppcheck-suppress valueFlowBailoutIncompleteVar + std::cout << std::resetiosflags(mask); // #6987 - false negative +} + +void uninitvar_setfill(void) +{ + char c; + // cppcheck-suppress [uninitvar,valueFlowBailoutIncompleteVar] + std::cout << std::setfill(c); + + wchar_t wc; + // cppcheck-suppress uninitvar + std::wcout << std::setfill(wc); +} + +void uninitvar_setprecision(void) +{ + int p; + // cppcheck-suppress [uninitvar,valueFlowBailoutIncompleteVar] + std::cout << std::setprecision(p); +} + +void uninitvar_setbase(void) +{ + int p; + // cppcheck-suppress [uninitvar,valueFlowBailoutIncompleteVar] + std::cout << std::setbase(p); +} + +// cppcheck-suppress passedByValue +void uninitvar_find(std::string s) +{ + // testing of size_t find (const string& str, size_t pos = 0) + size_t pos1, pos2, pos3, pos4, pos5, pos6, pos7; + // cppcheck-suppress uninitvar + (void)s.find("find",pos1); // #6991 + + // testing of size_t find (const char* s, size_t pos = 0) const; + char *pc, *pc2; + // cppcheck-suppress uninitvar + (void)s.find(pc,0); + // cppcheck-suppress uninitvar + (void)s.find(pc,pos2); + // cppcheck-suppress uninitvar + (void)s.find("test",pos3); + + // testing of size_t find (char c, size_t pos = 0) const; + char c; + // cppcheck-suppress uninitvar + (void)s.find(c,pos4); + + // testing of size_t find (const char* pc, size_t pos, size_t n) const; + size_t n1,n2,n3; + // cppcheck-suppress uninitvar + (void)s.find(pc,pos5,n1); // #6991 + // cppcheck-suppress uninitvar + (void)s.find("test",pos6,n2); + // cppcheck-suppress uninitvar + (void)s.find("test",1,n3); + // cppcheck-suppress uninitvar + (void)s.find("test",pos7,1); + // cppcheck-suppress uninitvar + (void)s.find(pc2,1,1); +} + +void uninivar_ifstream_read(std::ifstream &f) +{ + int size; + char buffer[10]; + // cppcheck-suppress uninitvar + f.read(buffer, size); +} + +void uninivar_istream_read(std::istream &f) +{ + int size; + char buffer[10]; + // cppcheck-suppress uninitvar + f.read(buffer, size); +} + +void uninitvar_string_compare(const std::string &teststr, const std::wstring &testwstr) +{ + const char *pStrUninit; + // cppcheck-suppress uninitvar + (void)teststr.compare(pStrUninit); + + const wchar_t *pWStrUninit; + // cppcheck-suppress uninitvar + (void)testwstr.compare(pWStrUninit); +} + +void invalidFunctionArgBool_abs(bool b, double x, double y) +{ + // cppcheck-suppress invalidFunctionArgBool + (void)std::abs(true); // #6990 + // cppcheck-suppress invalidFunctionArgBool + (void)std::abs(b); // #6990 + // cppcheck-suppress invalidFunctionArgBool + (void)std::abs(x& v) +{ + // cppcheck-suppress ignoredReturnValue + s.begin(); + // cppcheck-suppress ignoredReturnValue + v.end(); + // cppcheck-suppress ignoredReturnValue + sv.front(); + // cppcheck-suppress ignoredReturnValue + s.at(0); +} + +void ignoredReturnValue_locale_global(const std::locale& loc) +{ + // no ignoredReturnValue shall be shown for + std::locale::global(loc); +} + +void ignoredReturnValue_make_pair() +{ + // cppcheck-suppress ignoredReturnValue + std::make_pair(1, 2); +} + +void nullPointer_ifstream_read(std::ifstream &f) +{ + // cppcheck-suppress nullPointer + f.read(NULL, 10); +} + +void nullPointer_istream_read(std::istream &f) +{ + // cppcheck-suppress nullPointer + f.read(NULL, 10); +} + +std::size_t nullPointer_strxfrm(char *dest, const char *src, std::size_t count) +{ + (void)strxfrm(dest, src, count); + // In case the 3rd argument is 0, the 1st argument is permitted to be a null pointer. (#6306) + (void)strxfrm(nullptr, src, 0); + (void)strxfrm(nullptr, src, 1); + (void)strxfrm(nullptr, src, count); + // cppcheck-suppress nullPointer + return strxfrm(dest, nullptr, count); +} + +std::size_t nullPointer_wcsxfrm(wchar_t *dest, const wchar_t *src, std::size_t count) +{ + (void)wcsxfrm(dest, src, count); + // In case the 3rd argument is 0, the 1st argument is permitted to be a null pointer. (#6306) + (void)wcsxfrm(nullptr, src, 0); + (void)wcsxfrm(nullptr, src, 1); + (void)wcsxfrm(nullptr, src, count); + // cppcheck-suppress nullPointer + return wcsxfrm(dest, nullptr, count); +} + +void nullPointer_asctime(void) +{ + const struct tm *tm = 0; + // cppcheck-suppress asctimeCalled + // cppcheck-suppress nullPointer + (void)std::asctime(tm); + // cppcheck-suppress asctimeCalled + // cppcheck-suppress nullPointer + (void)std::asctime(0); +} + +void nullPointer_wcsftime(wchar_t* ptr, size_t maxsize, const wchar_t* format, const struct tm* timeptr) +{ + // cppcheck-suppress nullPointer + (void)std::wcsftime(NULL, maxsize, format, timeptr); + // cppcheck-suppress nullPointer + (void)std::wcsftime(ptr, maxsize, NULL, timeptr); + // cppcheck-suppress nullPointer + (void)std::wcsftime(ptr, maxsize, format, NULL); + (void)std::wcsftime(ptr, maxsize, format, timeptr); +} + +void nullPointer_fegetenv(void) +{ + fenv_t* envp = 0; + // cppcheck-suppress nullPointer + (void)std::fegetenv(envp); + // cppcheck-suppress nullPointer + (void)std::fegetenv(0); +} + +void nullPointer_fegetexceptflag(int expects) +{ + fexcept_t* flagp = 0; + // cppcheck-suppress nullPointer + (void)std::fegetexceptflag(flagp,expects); + // cppcheck-suppress nullPointer + (void)std::fegetexceptflag(0,expects); +} + +void nullPointer_feholdexcept(void) +{ + fenv_t* envp = 0; + // cppcheck-suppress nullPointer + (void)std::feholdexcept(envp); + // cppcheck-suppress nullPointer + (void)std::feholdexcept(0); +} + +void nullPointer_fesetenv(void) +{ + const fenv_t* envp = 0; + // cppcheck-suppress nullPointer + (void)std::fesetenv(envp); + // cppcheck-suppress nullPointer + (void)std::fesetenv(0); +} + +void nullPointer_fesetexceptflag(int expects) +{ + const fexcept_t* flagp = 0; + // cppcheck-suppress nullPointer + (void)std::fesetexceptflag(flagp,expects); + // cppcheck-suppress nullPointer + (void)std::fesetexceptflag(0,expects); +} + +void nullPointer_feupdateenv(void) +{ + const fenv_t* envp = 0; + // cppcheck-suppress nullPointer + (void)std::feupdateenv(envp); + // cppcheck-suppress nullPointer + (void)std::feupdateenv(0); +} + +void nullPointer_atexit(void) +{ + // cppcheck-suppress nullPointer + (void)std::atexit(0); +} + +void nullPointer_atof(void) +{ + const char * c = 0; + // cppcheck-suppress nullPointer + (void)std::atof(c); + // cppcheck-suppress nullPointer + (void)std::atof(0); +} + +void nullPointer_memcpy(void *s1, const void *s2, size_t n) +{ + // cppcheck-suppress nullPointer + (void)std::memcpy(NULL,s2,n); + // cppcheck-suppress nullPointer + (void)std::memcpy(s1,NULL,n); + (void)std::memcpy(s1,s2,n); +} + +void nullPointer_memmove(void *s1, void *s2, size_t n) +{ + // cppcheck-suppress nullPointer + (void)std::memmove(NULL,s2,n); + // cppcheck-suppress nullPointer + (void)std::memmove(s1,NULL,n); + (void)std::memmove(s1,s2,n); +} + +void nullPointer_wmemmove(wchar_t* s1, const wchar_t* s2, size_t n) +{ + // cppcheck-suppress nullPointer + (void)std::wmemmove(NULL,s2,n); + // cppcheck-suppress nullPointer + (void)std::wmemmove(s1,NULL,n); + (void)std::wmemmove(s1,s2,n); +} + +void nullPointer_wmemset(wchar_t* s, wchar_t c, size_t n) +{ + // cppcheck-suppress nullPointer + (void)std::wmemset(NULL,c,n); + (void)std::wmemset(s,c,n); +} + +/////////////////////////////////////////////////////////////////////// +// +/////////////////////////////////////////////////////////////////////// + +#include +#include + +#define pred [] (int i) {return i==0;} + + +void stdalgorithm(const std::list &ints1, const std::list &ints2) +{ + // + // cppcheck-suppress mismatchingContainers + // cppcheck-suppress ignoredReturnValue + std::find(ints1.begin(), ints2.end(), 123); + // cppcheck-suppress mismatchingContainers + if (std::find(ints1.begin(), ints1.end(), 123) == ints2.end()) {} + + // #9455 + std::list::const_iterator uninitItBegin; + std::list::const_iterator uninitItEnd; + // cppcheck-suppress uninitvar + if (std::find(uninitItBegin, uninitItEnd, 123) == uninitItEnd) {} + + // + // cppcheck-suppress mismatchingContainers + // cppcheck-suppress ignoredReturnValue + std::find_if(ints1.begin(), ints2.end(), pred); + // cppcheck-suppress mismatchingContainers + if (std::find_if(ints1.begin(), ints1.end(), pred) == ints2.end()) {} + + // + // cppcheck-suppress mismatchingContainers + // cppcheck-suppress ignoredReturnValue + std::find_if_not(ints1.begin(), ints2.end(), pred); + // cppcheck-suppress mismatchingContainers + if (std::find_if_not(ints1.begin(), ints1.end(), pred) == ints2.end()) {} + + // + // cppcheck-suppress mismatchingContainers + // cppcheck-suppress ignoredReturnValue + std::all_of(ints1.begin(), ints2.end(), pred); + + // + // cppcheck-suppress mismatchingContainers + // cppcheck-suppress ignoredReturnValue + std::any_of(ints1.begin(), ints2.end(), pred); + + // + // cppcheck-suppress mismatchingContainers + // cppcheck-suppress ignoredReturnValue + std::none_of(ints1.begin(), ints2.end(), pred); + + // + // cppcheck-suppress mismatchingContainers + // cppcheck-suppress ignoredReturnValue + std::count(ints1.begin(), ints2.end(), 123); + + // + // cppcheck-suppress mismatchingContainers + // cppcheck-suppress ignoredReturnValue + std::count_if(ints1.begin(), ints2.end(), pred); + + // + // cppcheck-suppress mismatchingContainers + std::for_each(ints1.begin(), ints2.end(), [](int i) {}); +} + +void getline() +{ + // #837 + std::ifstream in("test1.txt"); + + char cBuf[10]; + // cppcheck-suppress bufferAccessOutOfBounds + in.getline(cBuf, 100); + // cppcheck-suppress bufferAccessOutOfBounds + in.read(cBuf, 100); + // cppcheck-suppress bufferAccessOutOfBounds + in.readsome(cBuf, 100); + // cppcheck-suppress bufferAccessOutOfBounds + in.get(cBuf, 100); + // cppcheck-suppress bufferAccessOutOfBounds + in.get(cBuf, 100, 'a'); + // cppcheck-suppress bufferAccessOutOfBounds + in.getline(cBuf, 100, 'a'); + + in.close(); +} + +// cppcheck-suppress passedByValue +void stream_write(std::ofstream& s, std::vector v) { + if (v.empty()) {} + s.write(v.data(), v.size()); +} + +void stdstring() +{ + std::string s; + // cppcheck-suppress ignoredReturnValue + s.size(); + + // valid + s.assign("a"); + +#ifdef __cpp_lib_starts_ends_with + // cppcheck-suppress ignoredReturnValue + s.starts_with("abc"); +#endif +} + +void stdvector() +{ + int uninit1, uninit2, uninit3; + std::vector v; + // cppcheck-suppress ignoredReturnValue + v.size(); + // cppcheck-suppress ignoredReturnValue + v.capacity(); + // cppcheck-suppress uselessCallsEmpty + // cppcheck-suppress ignoredReturnValue + v.empty(); + // cppcheck-suppress ignoredReturnValue + v.max_size(); + // cppcheck-suppress uninitvar + v.push_back(uninit1); + // cppcheck-suppress uninitvar + v.reserve(uninit2); + // cppcheck-suppress invalidFunctionArg + v.reserve(-1); + // no warning is expected for capacity 0 as it simply has no effect + v.reserve(0); + // cppcheck-suppress uninitvar + v.resize(uninit3); + // cppcheck-suppress invalidFunctionArg + v.resize(-1); + + v.clear(); + v.shrink_to_fit(); + + // no warning is expected for pop_back() + v.push_back(42); + v.pop_back(); + + v.push_back(42); + // cppcheck-suppress ignoredReturnValue + v.back(); + // cppcheck-suppress ignoredReturnValue + v.front(); +} + +void stdbind_helper(int a) +{ + printf("%d", a); +} + +void stdbind() +{ + // cppcheck-suppress valueFlowBailoutIncompleteVar + using namespace std::placeholders; + + // TODO cppcheck-suppress ignoredReturnValue #9369 + std::bind(stdbind_helper, 1); + + // TODO cppcheck-suppress unreadVariable + // cppcheck-suppress autoNoType + auto f1 = std::bind(stdbind_helper, _1); + // TODO cppcheck-suppress unreadVariable + // cppcheck-suppress autoNoType + auto f2 = std::bind(stdbind_helper, 10); +} + +int stdexchange() { + int i; + // cppcheck-suppress uninitvar + int j = std::exchange(i, 5); + return j; +} + +class A +{ + std::vector m_str; + +public: + + A() {} + + // cppcheck-suppress functionConst + void begin_const_iterator(void) + { + for (std::vector::const_iterator it = m_str.begin(); it != m_str.end(); ++it) {;} + } + // cppcheck-suppress functionConst + void cbegin_const_iterator(void) + { + for (std::vector::const_iterator it = m_str.cbegin(); it != m_str.cend(); ++it) {;} + } + // cppcheck-suppress functionConst + void crbegin_const_iterator(void) + { + for (std::vector::const_reverse_iterator it = m_str.crbegin(); it != m_str.crend(); ++it) {;} + } + // cppcheck-suppress functionConst + void rbegin_const_iterator(void) + { + for (std::vector::const_reverse_iterator it = m_str.rbegin(); it != m_str.rend(); ++it) {;} + } + // cppcheck-suppress functionConst + void cbegin_auto(void) + { + for (auto it = m_str.cbegin(); it != m_str.cend(); ++it) {;} + } + void baz_begin_no_const_iterator(void) + { + for (std::vector::iterator it = m_str.begin(); it != m_str.end(); ++it) {;} + } + void rbegin_no_const_iterator(void) + { + for (std::vector::reverse_iterator it = m_str.rbegin(); it != m_str.rend(); ++it) {;} + } +}; + +void addressof(int a) +{ + // cppcheck-suppress ignoredReturnValue + std::addressof(a); +} + +void string_view_unused(std::string_view v) +{ + // cppcheck-suppress ignoredReturnValue + v.substr(1, 3); +} + +// cppcheck-suppress passedByValue +void string_substr(std::string s) +{ + // cppcheck-suppress ignoredReturnValue + s.substr(1, 3); +} + +void stdspan() +{ +#ifndef __cpp_lib_span +#warning "This compiler does not support std::span" +#else + std::vector vec{1,2,3,4}; + std::span spn{vec}; + // cppcheck-suppress unreadVariable + std::span spn2 = spn; + + //cppcheck-suppress ignoredReturnValue + spn.begin(); + //cppcheck-suppress ignoredReturnValue + spn.end(); + //cppcheck-suppress ignoredReturnValue + spn.rbegin(); + + //cppcheck-suppress ignoredReturnValue + spn.front(); + //cppcheck-suppress ignoredReturnValue + spn.back(); + //cppcheck-suppress constStatement + spn[0]; + //cppcheck-suppress ignoredReturnValue + spn.data(); + //cppcheck-suppress ignoredReturnValue + spn.size(); + //cppcheck-suppress ignoredReturnValue + spn.size_bytes(); + //cppcheck-suppress ignoredReturnValue + spn.empty(); + //cppcheck-suppress ignoredReturnValue + spn.first(2); + //cppcheck-suppress ignoredReturnValue + spn.last(2); + //cppcheck-suppress ignoredReturnValue + spn.subspan(1, 2); + spn.subspan<1>(); + + static constexpr std::array arr{1, 2}; + constexpr std::span spn3{arr}; + spn3.first<1>(); + spn3.last<1>(); + spn3.subspan<1, 1>(); + #endif +} + +void beginEnd() +{ + std::vector v; + + //cppcheck-suppress ignoredReturnValue + std::begin(v); + //cppcheck-suppress ignoredReturnValue + std::rbegin(v); + //cppcheck-suppress ignoredReturnValue + std::cbegin(v); + //cppcheck-suppress ignoredReturnValue + std::crbegin(v); + + //cppcheck-suppress ignoredReturnValue + std::end(v); + //cppcheck-suppress ignoredReturnValue + std::rend(v); + //cppcheck-suppress ignoredReturnValue + std::cend(v); + //cppcheck-suppress ignoredReturnValue + std::crend(v); + + // cppcheck-suppress constVariable + int arr[4]; + + //cppcheck-suppress ignoredReturnValue + std::begin(arr); + //cppcheck-suppress ignoredReturnValue + std::rbegin(arr); + //cppcheck-suppress ignoredReturnValue + std::cbegin(arr); + //cppcheck-suppress ignoredReturnValue + std::crbegin(arr); + + //cppcheck-suppress ignoredReturnValue + std::end(arr); + //cppcheck-suppress ignoredReturnValue + std::rend(arr); + //cppcheck-suppress ignoredReturnValue + std::cend(arr); + //cppcheck-suppress ignoredReturnValue + std::crend(arr); +} + +void smartPtr_get() +{ + std::unique_ptr p; + //cppcheck-suppress ignoredReturnValue + p.get(); + //cppcheck-suppress nullPointer + *p = 1; +} + +void smartPtr_get2(std::vector>& v) +{ + // cppcheck-suppress autoNoType + for (auto& u : v) { + int* p = u.get(); + *p = 0; + } +} + +void smartPtr_reset() +{ + std::unique_ptr p(new int()); + p.reset(nullptr); + //cppcheck-suppress nullPointer + *p = 1; +} + +void smartPtr_release() +{ + std::unique_ptr p{ new int() }; + //cppcheck-suppress ignoredReturnValue + p.release(); + //cppcheck-suppress nullPointer + *p = 1; +} + +void std_vector_data_arithmetic() +{ + std::vector buf; + buf.resize(1); + memcpy(buf.data() + 0, "", 1); +} + +void memleak_std_malloc() // #12332 +{ + //cppcheck-suppress [unreadVariable, constVariablePointer, unusedAllocatedMemory] + void* p = std::malloc(1); + //cppcheck-suppress memleak +} + +void memleak_std_realloc(void* block, size_t newsize) +{ + //cppcheck-suppress [unreadVariable, constVariablePointer] + void* p = std::realloc(block, newsize); + //cppcheck-suppress memleak +} + +void unusedAllocatedMemory_std_free() +{ + // cppcheck-suppress unusedAllocatedMemory + void* p = std::malloc(1); + std::free(p); +} + +std::string global_scope_std() // #12355 +{ + ::std::stringstream ss; + return ss.str(); +} + +::std::size_t global_scope_std2() // #12378 +{ + std::vector<::std::size_t> v; + // cppcheck-suppress containerOutOfBounds + return v.front(); +} + +void unique_lock_const_ref(std::mutex& m) +{ + std::unique_lock lock(m); +} + +void eraseIteratorOutOfBounds_std_deque(std::deque& x) // #8690 +{ + // cppcheck-suppress eraseIteratorOutOfBounds + x.erase(x.end()); +} diff --git a/cppcheck-2.14.0/test/cfg/windows.cpp b/cppcheck-2.14.0/test/cfg/windows.cpp new file mode 100644 index 00000000..5f2e6555 --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/windows.cpp @@ -0,0 +1,1163 @@ + +// Test library configuration for windows.cfg +// +// Usage: +// $ cppcheck --check-library --library=windows --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/windows.cpp +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +// cppcheck-suppress-file [valueFlowBailout,purgedConfiguration] + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void resourceLeak_OpenThread(const DWORD dwDesiredAccess, const BOOL bInheritHandle, const DWORD dwThreadId) +{ + HANDLE proc = OpenThread(dwDesiredAccess, bInheritHandle, dwThreadId); + if (proc != INVALID_HANDLE_VALUE) {} + // cppcheck-suppress resourceLeak +} + +void resourceLeak_OpenProcess(const DWORD dwDesiredAccess, const BOOL bInheritHandle, const DWORD dwProcessId) +{ + HANDLE proc = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId); + if (proc != INVALID_HANDLE_VALUE) {} + // cppcheck-suppress resourceLeak +} + +/// https://learn.microsoft.com/en-us/windows/console/flushconsoleinputbuffer +BOOL unreachableCode_FlushConsoleInputBuffer(int &val) +{ + const BOOL retVal = FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)); + // still reachable after call FlushConsoleInputBuffer() + val = 42; + return retVal; +} + +/// https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulefilenamew +std::string constVariable_GetModuleFileName(void) { + char path[42]; + if (GetModuleFileNameA(NULL, path, sizeof(path))==0) + return std::string(); + return std::string{path}; +} + +int stringCompare_mbscmp(const unsigned char *string1, const unsigned char *string2) +{ + // cppcheck-suppress stringCompare + (void) _mbscmp(string1, string1); + const unsigned char x[] = "x"; + // cppcheck-suppress stringCompare + (void) _mbscmp(x, x); + return _mbscmp(string1, string2); +} + +int stringCompare_mbscmp_l(const unsigned char *string1, const unsigned char *string2, _locale_t locale) +{ + // cppcheck-suppress stringCompare + (void) _mbscmp_l(string1, string1, locale); + const unsigned char x[] = "x"; + // cppcheck-suppress stringCompare + (void) _mbscmp_l(x, x, locale); + return _mbscmp_l(string1, string2, locale); +} + +int ignoredReturnValue__wtoi_l(const wchar_t *str, _locale_t locale) +{ + // cppcheck-suppress ignoredReturnValue + _wtoi_l(str,locale); + return _wtoi_l(str,locale); +} + +int ignoredReturnValue__atoi_l(const char *str, _locale_t locale) +{ + // cppcheck-suppress ignoredReturnValue + _atoi_l(str,locale); + return _atoi_l(str,locale); +} + +void invalidFunctionArg__fseeki64(FILE* stream, __int64 offset, int origin) +{ + // cppcheck-suppress invalidFunctionArg + (void)_fseeki64(stream, offset, -1); + // cppcheck-suppress invalidFunctionArg + (void)_fseeki64(stream, offset, 3); + // cppcheck-suppress invalidFunctionArg + (void)_fseeki64(stream, offset, 42+SEEK_SET); + // cppcheck-suppress invalidFunctionArg + (void)_fseeki64(stream, offset, SEEK_SET+42); + // No warning is expected for + (void)_fseeki64(stream, offset, origin); + (void)_fseeki64(stream, offset, SEEK_SET); + (void)_fseeki64(stream, offset, SEEK_CUR); + (void)_fseeki64(stream, offset, SEEK_END); +} + +void invalidFunctionArgBool__fseeki64(FILE* stream, __int64 offset) +{ + // cppcheck-suppress invalidFunctionArgBool + (void)_fseeki64(stream, offset, true); + // cppcheck-suppress invalidFunctionArgBool + (void)_fseeki64(stream, offset, false); +} + +unsigned char * overlappingWriteFunction__mbscat(unsigned char *src, unsigned char *dest) +{ + // No warning shall be shown: + (void)_mbscat(dest, src); + // cppcheck-suppress overlappingWriteFunction + return _mbscat(src, src); +} + +void* overlappingWriteFunction__memccpy(const unsigned char *src, unsigned char *dest, int c, size_t count) +{ + // No warning shall be shown: + (void)_memccpy(dest, src, c, count); + (void)_memccpy(dest, src, 42, count); + // cppcheck-suppress overlappingWriteFunction + (void) _memccpy(dest, dest, c, 4); + // cppcheck-suppress overlappingWriteFunction + return _memccpy(dest, dest+3, c, 4); +} + +unsigned char * overlappingWriteFunction__mbscpy(unsigned char *src, unsigned char *dest) +{ + // No warning shall be shown: + (void)_mbscpy(dest, src); + // cppcheck-suppress overlappingWriteFunction + return _mbscpy(src, src); +} + +void overlappingWriteFunction__swab(char *src, char *dest, int n) +{ + // No warning shall be shown: + _swab(dest, src, n); + // cppcheck-suppress overlappingWriteFunction + _swab(src, src+3, 4); +} + +SYSTEM_INFO uninitvar_GetSystemInfo() +{ + // No warning is expected + SYSTEM_INFO SystemInfo; + GetSystemInfo(&SystemInfo); + return SystemInfo; +} + +void uninitvar__putenv(const char * envstr) +{ + // No warning is expected + (void)_putenv(envstr); + + const char * p; + // cppcheck-suppress uninitvar + (void)_putenv(p); +} + +void nullPointer__putenv(const char * envstr) +{ + // No warning is expected + (void)_putenv(envstr); + + const char * p=NULL; + // cppcheck-suppress nullPointer + (void)_putenv(p); +} + +void invalidFunctionArg__getcwd(char * buffer) +{ + // Passing NULL as the buffer forces getcwd to allocate + // memory for the path, which allows the code to support file paths + // longer than _MAX_PATH, which are supported by NTFS. + if ((buffer = _getcwd(NULL, 0)) == NULL) { + return; + } + free(buffer); +} +// DWORD GetPrivateProfileString( +// [in] LPCTSTR lpAppName, +// [in] LPCTSTR lpKeyName, +// [in] LPCTSTR lpDefault, +// [out] LPTSTR lpReturnedString, +// [in] DWORD nSize, +// [in] LPCTSTR lpFileName) +void nullPointer_GetPrivateProfileString(LPCTSTR lpAppName, + LPCTSTR lpKeyName, + LPCTSTR lpDefault, + LPTSTR lpReturnedString, + DWORD nSize, + LPCTSTR lpFileName) +{ + // No warning is expected + (void)GetPrivateProfileString(lpAppName, lpKeyName, lpDefault, lpReturnedString, nSize, lpFileName); + + // No warning is expected for 1st arg as nullptr + (void)GetPrivateProfileString(nullptr, lpKeyName, lpDefault, lpReturnedString, nSize, lpFileName); + // No warning is expected for 2nd arg as nullptr + (void)GetPrivateProfileString(lpAppName, nullptr, lpDefault, lpReturnedString, nSize, lpFileName); +} + +void nullPointer__get_timezone(long *sec) +{ + // No warning is expected + (void)_get_timezone(sec); + + long *pSec = NULL; + // cppcheck-suppress nullPointer + (void)_get_timezone(pSec); +} + +void nullPointer__get_daylight(int *h) +{ + // No warning is expected + (void)_get_daylight(h); + + int *pHours = NULL; + // cppcheck-suppress nullPointer + (void)_get_daylight(pHours); +} + +void validCode() +{ + DWORD dwordInit = 0; + WORD wordInit = 0; + BYTE byteInit = 0; + + // Valid Semaphore usage, no leaks, valid arguments + HANDLE hSemaphore1; + hSemaphore1 = CreateSemaphore(NULL, 0, 1, NULL); + CloseHandle(hSemaphore1); + HANDLE hSemaphore2; + // cppcheck-suppress valueFlowBailoutIncompleteVar + hSemaphore2 = CreateSemaphoreEx(NULL, 0, 1, NULL, 0, SEMAPHORE_ALL_ACCESS); + CloseHandle(hSemaphore2); + HANDLE hSemaphore3; + hSemaphore3 = OpenSemaphore(SEMAPHORE_ALL_ACCESS, TRUE, L"sem"); + CloseHandle(hSemaphore3); + + // Valid lstrcat usage, but with warning because it is deprecated + char buf[30] = "hello world"; + // cppcheck-suppress lstrcatACalled + lstrcatA(buf, "test"); + + // cppcheck-suppress strlwrCalled + strlwr(buf); + // cppcheck-suppress struprCalled + strupr(buf); + + // Valid Mutex usage, no leaks, valid arguments + HANDLE hMutex1; + hMutex1 = CreateMutex(NULL, TRUE, NULL); + if (hMutex1) { + ReleaseMutex(hMutex1); + } + CloseHandle(hMutex1); + HANDLE hMutex2; + hMutex2 = CreateMutexEx(NULL, NULL, 0, MUTEX_ALL_ACCESS); + CloseHandle(hMutex2); + HANDLE hMutex3; + hMutex3 = OpenMutex(MUTEX_ALL_ACCESS, FALSE, _T("sem")); + CloseHandle(hMutex3); + + // Valid Module usage, no leaks, valid arguments + HMODULE hModule = GetModuleHandle(L"My.dll"); + FreeLibrary(hModule); + hModule = GetModuleHandle(TEXT("somedll")); + FreeLibrary(hModule); + hModule = GetModuleHandle(NULL); + FreeLibrary(hModule); + + // Valid Event usage, no leaks, valid arguments + HANDLE event; + event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (NULL != event) { + SetEvent(event); + CloseHandle(event); + } + event = OpenEvent(EVENT_ALL_ACCESS, FALSE, L"testevent"); + if (NULL != event) { + PulseEvent(event); + SetEvent(event); + CloseHandle(event); + } + event = CreateEventEx(NULL, L"testevent3", CREATE_EVENT_INITIAL_SET, EVENT_MODIFY_STATE); + if (NULL != event) { + ResetEvent(event); + CloseHandle(event); + } + + // cppcheck-suppress unusedAllocatedMemory + void *pMem1 = _malloca(1); + _freea(pMem1); + // Memory from _alloca must not be freed + // cppcheck-suppress _allocaCalled + void *pMem2 = _alloca(10); + memset(pMem2, 0, 10); + + SYSTEMTIME st; + GetSystemTime(&st); + + DWORD lastError = GetLastError(); + SetLastError(lastError); + + PSID pEveryoneSID = NULL; + SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; + AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSID); + FreeSid(pEveryoneSID); + + LPVOID pMem = HeapAlloc(GetProcessHeap(), 0, 10); + pMem = HeapReAlloc(GetProcessHeap(), 0, pMem, 0); + HeapFree(GetProcessHeap(), 0, pMem); + + char bufC[50]; + sprintf_s(bufC, "Hello"); + printf("%s", bufC); + sprintf_s(bufC, "%s", "test"); + printf("%s", bufC); + sprintf_s(bufC, _countof(bufC), "%s", "test"); + printf("%s", bufC); + wchar_t bufWC[50]; + swprintf_s(bufWC, L"Hello"); + wprintf(L"%s\n", bufWC); + swprintf_s(bufWC, L"%s %d", L"swprintf_s", 3); + wprintf(L"%s\n", bufWC); + swprintf_s(bufWC, _countof(bufWC), L"%s %d", L"swprintf_s", 6); + wprintf(L"%s\n", bufWC); + TCHAR bufTC[50]; + _stprintf(bufTC, TEXT("Hello")); + _tprintf(TEXT("%s"), bufTC); + _stprintf(bufTC, TEXT("%d"), 1); + _tprintf(TEXT("%s"), bufTC); + _stprintf(bufTC, TEXT("%d"), 2); + _tprintf(TEXT("%s"), bufTC); + + GetUserName(NULL, &dwordInit); + dwordInit = 10; + GetUserName(bufTC, &dwordInit); + + WSADATA wsaData = {0}; + WSAStartup(2, &wsaData); + SOCKET sock = socket(1, 2, 3); + u_long ulongInit = 0; + ioctlsocket(sock, FIONBIO, &ulongInit); + if (sock != INVALID_SOCKET) { + closesocket(sock); + } + WSACleanup(); + + wordInit = MAKEWORD(1, 2); + // TODO cppcheck-suppress redundantAssignment + dwordInit = MAKELONG(1, 2); + // cppcheck-suppress redundantAssignment + wordInit = LOWORD(dwordInit); + byteInit = LOBYTE(wordInit); + wordInit = HIWORD(dwordInit); + // cppcheck-suppress redundantAssignment + byteInit = HIBYTE(wordInit); + // cppcheck-suppress knownConditionTrueFalse + if (byteInit) {} + + bool boolVar; + uint8_t byteBuf[5] = {0}; + uint8_t byteBuf2[10] = {0}; + boolVar = RtlEqualMemory(byteBuf, byteBuf2, sizeof(byteBuf)); + if (boolVar) {} + boolVar = RtlCompareMemory(byteBuf, byteBuf2, sizeof(byteBuf)); + if (boolVar) {} + RtlMoveMemory(byteBuf, byteBuf2, sizeof(byteBuf)); + RtlCopyMemory(byteBuf, byteBuf2, sizeof(byteBuf)); + RtlZeroMemory(byteBuf, sizeof(byteBuf)); + ZeroMemory(byteBuf, sizeof(byteBuf)); + RtlSecureZeroMemory(byteBuf, sizeof(byteBuf)); + SecureZeroMemory(byteBuf, sizeof(byteBuf)); + RtlFillMemory(byteBuf, sizeof(byteBuf), 0xff); + + // cppcheck-suppress [LocalAllocCalled, unusedAllocatedMemory] + HLOCAL pLocalAlloc = LocalAlloc(1, 2); + LocalFree(pLocalAlloc); + + // cppcheck-suppress lstrlenCalled + (void)lstrlen(bufTC); + // cppcheck-suppress lstrlenCalled + (void)lstrlen(NULL); + + // Intrinsics + __noop(); + __noop(1, "test", NULL); + __nop(); + + // cppcheck-suppress unusedAllocatedMemory + void * pAlloc1 = _aligned_malloc(100, 2); + _aligned_free(pAlloc1); + + ::PostMessage(nullptr, WM_QUIT, 0, 0); + + printf("%zu", __alignof(int)); + printf("%zu", _alignof(double)); + + // Valid Library usage, no leaks, valid arguments + HINSTANCE hInstLib = LoadLibrary(L"My.dll"); + FreeLibrary(hInstLib); + hInstLib = LoadLibraryA("My.dll"); + FreeLibrary(hInstLib); + hInstLib = LoadLibraryEx(L"My.dll", NULL, 0); + FreeLibrary(hInstLib); + hInstLib = LoadLibraryExW(L"My.dll", NULL, 0); + FreeLibrary(hInstLib); + hInstLib = ::LoadLibrary(L"My.dll"); + FreeLibraryAndExitThread(hInstLib, 0); // Does not return! Must be at the end! +} + +void bufferAccessOutOfBounds() +{ + wchar_t buf[10]; + // Verifying _countof macro configuration + // Valid loop over array + for (size_t i = 0; i < _countof(buf); ++i) { + buf[i] = L'\0'; + } + // Wrong loop over array accessing one element past the end + for (size_t i = 0; i <= _countof(buf); ++i) { + // cppcheck-suppress arrayIndexOutOfBounds + buf[i] = L'\0'; + } + + uint8_t byteBuf[5] = {0}; + uint8_t byteBuf2[10] = {0}; + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress bufferAccessOutOfBounds + RtlEqualMemory(byteBuf, byteBuf2, 20); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress bufferAccessOutOfBounds + RtlCompareMemory(byteBuf, byteBuf2, 20); + // cppcheck-suppress bufferAccessOutOfBounds + RtlMoveMemory(byteBuf, byteBuf2, 20); + // TODO cppcheck-suppress redundantCopy + // cppcheck-suppress bufferAccessOutOfBounds + MoveMemory(byteBuf, byteBuf2, 20); + // TODO cppcheck-suppress redundantCopy + // cppcheck-suppress bufferAccessOutOfBounds + RtlCopyMemory(byteBuf, byteBuf2, 20); + // TODO cppcheck-suppress redundantCopy + // cppcheck-suppress bufferAccessOutOfBounds + CopyMemory(byteBuf, byteBuf2, 20); + // cppcheck-suppress bufferAccessOutOfBounds + RtlZeroMemory(byteBuf, sizeof(byteBuf)+1); + // cppcheck-suppress bufferAccessOutOfBounds + ZeroMemory(byteBuf, sizeof(byteBuf)+1); + // cppcheck-suppress bufferAccessOutOfBounds + RtlSecureZeroMemory(byteBuf, sizeof(byteBuf)+1); + // cppcheck-suppress bufferAccessOutOfBounds + SecureZeroMemory(byteBuf, sizeof(byteBuf)+1); + // cppcheck-suppress bufferAccessOutOfBounds + RtlFillMemory(byteBuf, sizeof(byteBuf)+1, 0x01); + // cppcheck-suppress bufferAccessOutOfBounds + FillMemory(byteBuf, sizeof(byteBuf)+1, 0x01); + + char * pAlloc1 = static_cast(_malloca(32)); + memset(pAlloc1, 0, 32); + // cppcheck-suppress bufferAccessOutOfBounds + memset(pAlloc1, 0, 33); + _freea(pAlloc1); +} + +void mismatchAllocDealloc() +{ + char * pChar = static_cast(_aligned_malloc(100, 2)); + // cppcheck-suppress mismatchAllocDealloc + free(pChar); + + // cppcheck-suppress unusedAllocatedMemory + pChar = static_cast(_malloca(32)); + // cppcheck-suppress mismatchAllocDealloc + _aligned_free(pChar); +} + +void nullPointer() +{ + HANDLE hSemaphore; + // cppcheck-suppress [nullPointer,valueFlowBailoutIncompleteVar] + hSemaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS, FALSE, NULL); + CloseHandle(hSemaphore); + + // cppcheck-suppress lstrcatCalled + // cppcheck-suppress nullPointer + lstrcat(NULL, _T("test")); + TCHAR buf[10] = _T("\0"); + // cppcheck-suppress lstrcatCalled + // cppcheck-suppress nullPointer + lstrcat(buf, NULL); + + HANDLE hMutex; + // cppcheck-suppress nullPointer + hMutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, NULL); + CloseHandle(hMutex); + + //Incorrect: 1. parameter, must not be null + // cppcheck-suppress nullPointer + FARPROC pAddr = GetProcAddress(NULL, "name"); + (void)pAddr; + HMODULE * phModule = NULL; + // cppcheck-suppress nullPointer + GetModuleHandleEx(0, NULL, phModule); + + // cppcheck-suppress leakReturnValNotUsed + // cppcheck-suppress nullPointer + OpenEvent(EVENT_ALL_ACCESS, FALSE, NULL); + HANDLE hEvent = NULL; + // cppcheck-suppress nullPointer + PulseEvent(hEvent); + // cppcheck-suppress nullPointer + ResetEvent(hEvent); + // cppcheck-suppress nullPointer + SetEvent(hEvent); + + char *str = NULL; + // cppcheck-suppress strlwrCalled + // cppcheck-suppress nullPointer + strlwr(str); + // cppcheck-suppress struprCalled + // cppcheck-suppress nullPointer + strupr(str); + + // cppcheck-suppress nullPointer + GetSystemTime(NULL); + // cppcheck-suppress nullPointer + GetLocalTime(NULL); + + // TODO: error message: arg1 must not be nullptr if variable pointed to by arg2 is not 0 + DWORD dwordInit = 10; + GetUserName(NULL, &dwordInit); + TCHAR bufTC[10]; + // cppcheck-suppress nullPointer + GetUserName(bufTC, NULL); + + SOCKET socketInit = {0}; + sockaddr sockaddrUninit; + int intInit = 0; + int *pIntNull = NULL; + char charArray[] = "test"; + // cppcheck-suppress nullPointer + WSAStartup(1, NULL); + // cppcheck-suppress nullPointer + bind(socketInit, NULL, 5); + // cppcheck-suppress nullPointer + getpeername(socketInit, NULL, &intInit); + // cppcheck-suppress nullPointer + getpeername(socketInit, &sockaddrUninit, pIntNull); + // cppcheck-suppress nullPointer + getsockopt(socketInit, 1, 2, NULL, &intInit); + // cppcheck-suppress nullPointer + getsockopt(socketInit, 1, 2, charArray, pIntNull); +} + +void memleak_malloca() +{ + // cppcheck-suppress [unusedAllocatedMemory, unreadVariable, constVariablePointer] + void *pMem = _malloca(10); + // cppcheck-suppress memleak +} + +void memleak_AllocateAndInitializeSid() +{ + PSID pEveryoneSID = NULL; + SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_WORLD_SID_AUTHORITY; + AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pEveryoneSID); + // cppcheck-suppress memleak +} + +void memleak_HeapAlloc() +{ + LPVOID pMem; + pMem = HeapAlloc(GetProcessHeap(), 0, 10); + HeapValidate(GetProcessHeap(), 0, pMem); + // cppcheck-suppress unreadVariable + SIZE_T memSize = HeapSize(GetProcessHeap(), 0, pMem); + // cppcheck-suppress memleak +} + +void memleak_LocalAlloc() +{ + LPTSTR pszBuf; + // cppcheck-suppress [LocalAllocCalled, cstyleCast, valueFlowBailoutIncompleteVar] + pszBuf = (LPTSTR)LocalAlloc(LPTR, MAX_PATH*sizeof(TCHAR)); + (void)LocalSize(pszBuf); + (void)LocalFlags(pszBuf); + LocalLock(pszBuf); + LocalUnlock(pszBuf); + // cppcheck-suppress memleak +} + +void memleak_dupenv_s() // #10646 +{ + char* pValue; + size_t len; + errno_t err = _dupenv_s(&pValue, &len, "pathext"); + if (err) return; + printf("pathext = %s\n", pValue); + free(pValue); + err = _dupenv_s(&pValue, &len, "nonexistentvariable"); + if (err) return; + printf("nonexistentvariable = %s\n", pValue); + // cppcheck-suppress memleak +} + +void resourceLeak_CreateSemaphoreA() +{ + HANDLE hSemaphore; + // cppcheck-suppress unreadVariable + hSemaphore = CreateSemaphoreA(NULL, 0, 1, "sem1"); + // cppcheck-suppress resourceLeak +} + +void resourceLeak_CreateSemaphoreEx() +{ + HANDLE hSemaphore; + // cppcheck-suppress [unreadVariable,valueFlowBailoutIncompleteVar] + hSemaphore = CreateSemaphoreEx(NULL, 0, 1, NULL, 0, SEMAPHORE_ALL_ACCESS); + // cppcheck-suppress resourceLeak +} + +void resourceLeak_OpenSemaphore() +{ + HANDLE hSemaphore; + // cppcheck-suppress [unreadVariable,valueFlowBailoutIncompleteVar] + hSemaphore = OpenSemaphore(SEMAPHORE_ALL_ACCESS, TRUE, _T("sem")); + // cppcheck-suppress resourceLeak +} + +void resourceLeak_CreateMutexA() +{ + HANDLE hMutex; + // cppcheck-suppress unreadVariable + hMutex = CreateMutexA(NULL, TRUE, "sem1"); + // cppcheck-suppress resourceLeak +} + +void resourceLeak_CreateMutexEx() +{ + HANDLE hMutex; + // cppcheck-suppress [unreadVariable,valueFlowBailoutIncompleteVar] + hMutex = CreateMutexEx(NULL, _T("sem"), 0, MUTEX_ALL_ACCESS); + // cppcheck-suppress resourceLeak +} + +void resourceLeak_OpenMutex() +{ + HANDLE hMutex; + // cppcheck-suppress unreadVariable + hMutex = OpenMutex(MUTEX_ALL_ACCESS, TRUE, _T("sem")); + // cppcheck-suppress resourceLeak +} + +void resourceLeak_LoadLibrary() +{ + HINSTANCE hInstLib; + hInstLib = ::LoadLibrary(L"My.dll"); + typedef BOOL (WINAPI *fpFunc)(); + // cppcheck-suppress [unreadVariable, cstyleCast] + fpFunc pFunc = (fpFunc)GetProcAddress(hInstLib, "name"); + // cppcheck-suppress resourceLeak +} + +void resourceLeak_CreateEvent() +{ + HANDLE hEvent; + hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + SetEvent(hEvent); + // cppcheck-suppress resourceLeak +} + +void resourceLeak_CreateEventExA() +{ + HANDLE hEvent; + // cppcheck-suppress unreadVariable + hEvent = CreateEventExA(NULL, "test", CREATE_EVENT_INITIAL_SET, EVENT_MODIFY_STATE); + // cppcheck-suppress resourceLeak +} + +void resourceLeak_OpenEventW() +{ + HANDLE hEvent; + // cppcheck-suppress [unreadVariable,valueFlowBailoutIncompleteVar] + hEvent = OpenEventW(EVENT_ALL_ACCESS, TRUE, L"testevent"); + // cppcheck-suppress resourceLeak +} + +void resourceLeak_socket() +{ + SOCKET sock; + // cppcheck-suppress unreadVariable + sock = socket(1, 2, 3); + // cppcheck-suppress resourceLeak +} + +void ignoredReturnValue(FILE* fp) +{ + // cppcheck-suppress leakReturnValNotUsed + CreateSemaphoreW(NULL, 0, 1, NULL); + // cppcheck-suppress [leakReturnValNotUsed,valueFlowBailoutIncompleteVar] + CreateSemaphoreExA(NULL, 0, 1, NULL, 0, SEMAPHORE_ALL_ACCESS); + // cppcheck-suppress leakReturnValNotUsed + OpenSemaphoreA(SEMAPHORE_ALL_ACCESS, TRUE, "sem"); + + // cppcheck-suppress leakReturnValNotUsed + CreateMutexW(NULL, FALSE, NULL); + // cppcheck-suppress leakReturnValNotUsed + CreateMutexExA(NULL, NULL, 1, MUTEX_ALL_ACCESS); + // cppcheck-suppress leakReturnValNotUsed + OpenMutexA(MUTEX_ALL_ACCESS, TRUE, "sem"); + + // cppcheck-suppress leakReturnValNotUsed + LoadLibrary(L"My.dll"); + // cppcheck-suppress leakReturnValNotUsed + LoadLibraryEx(L"My.dll", NULL, 0); + + HINSTANCE hInstLib = LoadLibrary(L"My.dll"); + // cppcheck-suppress ignoredReturnValue + GetProcAddress(hInstLib, "name"); + FreeLibrary(hInstLib); + + // cppcheck-suppress leakReturnValNotUsed + CreateEvent(NULL, FALSE, FALSE, NULL); + // cppcheck-suppress leakReturnValNotUsed + OpenEvent(EVENT_ALL_ACCESS, FALSE, L"testevent"); + // cppcheck-suppress leakReturnValNotUsed + CreateEventEx(NULL, L"test", CREATE_EVENT_INITIAL_SET, EVENT_MODIFY_STATE); + + // cppcheck-suppress leakReturnValNotUsed + _malloca(10); + // cppcheck-suppress ignoredReturnValue + // cppcheck-suppress _allocaCalled + _alloca(5); + + // cppcheck-suppress ignoredReturnValue + GetLastError(); + + // cppcheck-suppress ignoredReturnValue + GetProcessHeap(); + // cppcheck-suppress leakReturnValNotUsed + HeapAlloc(GetProcessHeap(), 0, 10); + // cppcheck-suppress [leakReturnValNotUsed, nullPointer] + HeapReAlloc(GetProcessHeap(), 0, nullptr, 0); + + // cppcheck-suppress leakReturnValNotUsed + socket(1, 2, 3); + + // cppcheck-suppress ignoredReturnValue + _fileno(fp); + + // cppcheck-suppress lstrlenCalled + // cppcheck-suppress ignoredReturnValue + lstrlen(TEXT("test")); +} + +void invalidFunctionArg() +{ + HANDLE hSemaphore; + // cppcheck-suppress invalidFunctionArg + hSemaphore = CreateSemaphore(NULL, 0, 0, NULL); + CloseHandle(hSemaphore); + // cppcheck-suppress invalidFunctionArgBool + hSemaphore = CreateSemaphore(NULL, 0, 1, false); + CloseHandle(hSemaphore); + // cppcheck-suppress [invalidFunctionArg,valueFlowBailoutIncompleteVar] + hSemaphore = CreateSemaphoreEx(NULL, 0, 0, NULL, 0, SEMAPHORE_ALL_ACCESS); + CloseHandle(hSemaphore); + // cppcheck-suppress invalidFunctionArg + hSemaphore = CreateSemaphoreEx(NULL, 0, 1, NULL, 1, SEMAPHORE_ALL_ACCESS); + CloseHandle(hSemaphore); + + HANDLE hMutex; + // cppcheck-suppress invalidFunctionArgBool + hMutex = CreateMutex(NULL, TRUE, false); + CloseHandle(hMutex); + // cppcheck-suppress invalidFunctionArgBool + hMutex = CreateMutex(NULL, FALSE, false); + CloseHandle(hMutex); + // cppcheck-suppress invalidFunctionArg + hMutex = CreateMutexEx(NULL, NULL, 3, MUTEX_ALL_ACCESS); + CloseHandle(hMutex); + + //Incorrect: 2. parameter to LoadLibraryEx() must be NULL + // TODO cppcheck-suppress invalidFunctionArg + HINSTANCE hInstLib = LoadLibraryEx(L"My.dll", HANDLE(1), 0); + FreeLibrary(hInstLib); + + // cppcheck-suppress invalidFunctionArg + void *pMem = _malloca(-1); + _freea(pMem); + // FIXME cppcheck-suppress unreadVariable + // cppcheck-suppress invalidFunctionArg + // cppcheck-suppress _allocaCalled + pMem = _alloca(-5); +} + +void uninitvar() +{ + // cppcheck-suppress unassignedVariable + HANDLE hSemaphore; + // cppcheck-suppress uninitvar + CloseHandle(hSemaphore); + + TCHAR buf[10]; + // cppcheck-suppress lstrcatCalled + // cppcheck-suppress uninitvar + lstrcat(buf, _T("test")); + buf[0] = _T('\0'); + // TODO cppcheck-suppress constVariable + TCHAR buf2[2]; + // cppcheck-suppress lstrcatCalled + // cppcheck-suppress uninitvar + lstrcat(buf, buf2); + + // cppcheck-suppress unassignedVariable + HANDLE hMutex1, hMutex2; + // cppcheck-suppress uninitvar + ReleaseMutex(hMutex1); + // cppcheck-suppress uninitvar + CloseHandle(hMutex2); + + // cppcheck-suppress unassignedVariable + HANDLE hEvent1, hEvent2, hEvent3, hEvent4; + // cppcheck-suppress uninitvar + PulseEvent(hEvent1); + // cppcheck-suppress uninitvar + ResetEvent(hEvent2); + // cppcheck-suppress uninitvar + SetEvent(hEvent3); + // cppcheck-suppress uninitvar + CloseHandle(hEvent4); + + char buf_uninit1[10]; + char buf_uninit2[10]; + // cppcheck-suppress strlwrCalled + // cppcheck-suppress uninitvar + strlwr(buf_uninit1); + // cppcheck-suppress struprCalled + // cppcheck-suppress uninitvar + strupr(buf_uninit2); + + DWORD dwordUninit; + // cppcheck-suppress uninitvar + SetLastError(dwordUninit); + + DWORD dwordUninit2; + // cppcheck-suppress uninitvar + GetUserName(NULL, &dwordUninit2); + + FILE *pFileUninit; + // cppcheck-suppress uninitvar + // cppcheck-suppress ignoredReturnValue + _fileno(pFileUninit); +} + +void unreferencedParameter(int i) { + UNREFERENCED_PARAMETER(i); +} + +void errorPrintf() +{ + char bufC[50]; + // cppcheck-suppress wrongPrintfScanfArgNum + sprintf_s(bufC, _countof(bufC), "%s %d", "sprintf_s"); + printf("%s\n", bufC); + // cppcheck-suppress wrongPrintfScanfArgNum + sprintf_s(bufC, "%s %d", "sprintf_s"); + printf("%s\n", bufC); + // cppcheck-suppress wrongPrintfScanfArgNum + sprintf_s(bufC, _countof(bufC), "test", 0); + printf("%s\n", bufC); + // cppcheck-suppress wrongPrintfScanfArgNum + sprintf_s(bufC, "test", "sprintf_s"); + printf("%s\n", bufC); + // cppcheck-suppress invalidPrintfArgType_s + sprintf_s(bufC, _countof(bufC), "%s", 1); + printf("%s\n", bufC); + // cppcheck-suppress invalidPrintfArgType_s + sprintf_s(bufC, "%s", 1); + printf("%s\n", bufC); + + wchar_t bufWC[50]; + // cppcheck-suppress wrongPrintfScanfArgNum + swprintf_s(bufWC, _countof(bufWC), L"%s %d", L"swprintf_s"); + wprintf(L"%s\n", bufWC); + // cppcheck-suppress wrongPrintfScanfArgNum + swprintf_s(bufWC, L"%s %d", L"swprintf_s"); + wprintf(L"%s\n", bufWC); + // cppcheck-suppress wrongPrintfScanfArgNum + swprintf_s(bufWC, _countof(bufWC), L"test", 0); + wprintf(L"%s\n", bufWC); + // cppcheck-suppress wrongPrintfScanfArgNum + swprintf_s(bufWC, L"test", L"swprintf_s"); + wprintf(L"%s\n", bufWC); + // cppcheck-suppress invalidPrintfArgType_s + swprintf_s(bufWC, _countof(bufWC), L"%s", 1); + wprintf(L"%s\n", bufWC); + // cppcheck-suppress invalidPrintfArgType_s + swprintf_s(bufWC, L"%s", 1); + wprintf(L"%s\n", bufWC); + + TCHAR bufTC[50]; + // cppcheck-suppress wrongPrintfScanfArgNum + _stprintf_s(bufTC, _countof(bufTC), TEXT("%s %d"), TEXT("_stprintf_s")); + _tprintf(L"%s\n", bufTC); + // cppcheck-suppress wrongPrintfScanfArgNum + _stprintf_s(bufTC, TEXT("%s %d"), TEXT("_stprintf_s")); + _tprintf(TEXT("%s\n"), bufTC); + // cppcheck-suppress wrongPrintfScanfArgNum + _stprintf_s(bufTC, _countof(bufTC), TEXT("test"), 0); + _tprintf(TEXT("%s\n"), bufTC); + // cppcheck-suppress wrongPrintfScanfArgNum + _stprintf_s(bufTC, TEXT("test"), TEXT("_stprintf_s")); + _tprintf(TEXT("%s\n"), bufTC); + // cppcheck-suppress invalidPrintfArgType_s + _stprintf_s(bufTC, _countof(bufTC), TEXT("%s"), 1); + _tprintf(TEXT("%s\n"), bufTC); + // cppcheck-suppress invalidPrintfArgType_s + _stprintf_s(bufTC, TEXT("%s"), 1); + _tprintf(TEXT("%s\n"), bufTC); +} + +void allocDealloc_GetModuleHandleEx() +{ + // For GetModuleHandleEx it depends on the first argument if FreeLibrary + // must be called or is not allowed to be called. + // If the first argument is GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT + // (0x00000002), a call to FreeLibrary is not allowed, otherwise it is + // necessary. + // Since this is not possible to configure at the moment cppcheck should + // accept not calling FreeLibrary and also calling it for the handle. + // TODO: Enhance cppcheck to conditionally check for alloc/dealloc issues. + + // No warning because of correct FreeLibrary on 'hModule' should be issued. + HMODULE hModule1; + if (GetModuleHandleEx(0, NULL, &hModule1)) { + FreeLibrary(hModule1); + } + + //This is a false negative, but it is not detected to avoid false positives. + HMODULE hModule2; + GetModuleHandleEx(0, NULL, &hModule2); +} + +void uninitvar_tolower(_locale_t l) +{ + int c1, c2; + // cppcheck-suppress uninitvar + (void)_tolower(c1); + // cppcheck-suppress uninitvar + (void)_tolower_l(c2, l); +} + +void uninitvar_toupper(_locale_t l) +{ + int c1, c2; + // cppcheck-suppress uninitvar + (void)_toupper(c1); + // cppcheck-suppress uninitvar + (void)_toupper_l(c2, l); +} + +void uninitvar_towlower(_locale_t l) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)_towlower_l(i, l); +} + +void uninitvar_towupper(_locale_t l) +{ + wint_t i; + // cppcheck-suppress uninitvar + (void)_towupper_l(i, l); +} + +void oppositeInnerCondition_SUCCEEDED_FAILED(HRESULT hr) +{ + if (SUCCEEDED(hr)) { + // TODO ticket #8596 cppcheck-suppress oppositeInnerCondition + if (FAILED(hr)) {} + } +} + +/*HANDLE WINAPI CreateThread( + _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, + _In_ SIZE_T dwStackSize, + _In_ LPTHREAD_START_ROUTINE lpStartAddress, + _In_opt_ LPVOID lpParameter, + _In_ DWORD dwCreationFlags, + _Out_opt_ LPDWORD lpThreadId + );*/ +HANDLE test_CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, + SIZE_T dwStackSize, + LPTHREAD_START_ROUTINE lpStartAddress, + LPVOID lpParameter, + DWORD dwCreationFlags, + LPDWORD lpThreadId) +{ + // Create uninitialized variables + LPSECURITY_ATTRIBUTES uninit_lpThreadAttributes; + SIZE_T uninit_dwStackSize; + LPTHREAD_START_ROUTINE uninit_lpStartAddress; + LPVOID uninit_lpParameter; + DWORD uninit_dwCreationFlags; + + // cppcheck-suppress leakReturnValNotUsed + // cppcheck-suppress uninitvar + (void) CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, uninit_dwCreationFlags, lpThreadId); + // cppcheck-suppress leakReturnValNotUsed + // cppcheck-suppress uninitvar + (void) CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, uninit_lpParameter, dwCreationFlags, lpThreadId); + // @todo uninitvar shall be reported + // cppcheck-suppress leakReturnValNotUsed + (void) CreateThread(lpThreadAttributes, dwStackSize, uninit_lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); + // cppcheck-suppress leakReturnValNotUsed + // cppcheck-suppress uninitvar + (void) CreateThread(lpThreadAttributes, uninit_dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); + // @todo uninitvar shall be reported + // cppcheck-suppress leakReturnValNotUsed + (void) CreateThread(uninit_lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); + + // cppcheck-suppress leakReturnValNotUsed + // cppcheck-suppress nullPointer + (void) CreateThread(lpThreadAttributes, dwStackSize, 0, lpParameter, dwCreationFlags, lpThreadId); + + // cppcheck-suppress leakReturnValNotUsed + // cppcheck-suppress invalidFunctionArg + (void) CreateThread(lpThreadAttributes, -1, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); + + // no warning shall be shown for + return CreateThread(lpThreadAttributes, dwStackSize, lpStartAddress, lpParameter, dwCreationFlags, lpThreadId); +} + +// unsigned char *_mbscat(unsigned char *strDestination, const unsigned char *strSource); +unsigned char * uninitvar_mbscat(unsigned char *strDestination, const unsigned char *strSource) +{ + unsigned char *uninit_deststr; + const unsigned char *uninit_srcstr1, *uninit_srcstr2; + // cppcheck-suppress uninitvar + (void)_mbscat(uninit_deststr,uninit_srcstr1); + // cppcheck-suppress uninitvar + (void)_mbscat(strDestination,uninit_srcstr2); + + // no warning shall be shown for + return _mbscat(strDestination,strSource); +} + +// unsigned char *_mbscat(unsigned char *strDestination, const unsigned char *strSource); +unsigned char * nullPointer_mbscat(unsigned char *strDestination, const unsigned char *strSource) +{ + // cppcheck-suppress nullPointer + (void)_mbscat(0,strSource); + // cppcheck-suppress nullPointer + (void)_mbscat(strDestination,0); + + // no warning shall be shown for + return _mbscat(strDestination,strSource); +} + +// errno_t _mbscat_s(unsigned char *strDestination, size_t numberOfElements, const unsigned char *strSource ); +errno_t uninitvar_mbscat_s(unsigned char *strDestination, size_t numberOfElements, const unsigned char *strSource) +{ + unsigned char *uninit_strDestination; + size_t uninit_numberOfElements; + const unsigned char *uninit_strSource; + + // cppcheck-suppress uninitvar + (void)_mbscat_s(uninit_strDestination, numberOfElements, strSource); + // cppcheck-suppress uninitvar + (void)_mbscat_s(strDestination, uninit_numberOfElements, strSource); + // cppcheck-suppress uninitvar + (void)_mbscat_s(strDestination, numberOfElements, uninit_strSource); + + // no warning shall be shown for + return _mbscat_s(strDestination, numberOfElements, strSource); +} + +// errno_t _mbscat_s(unsigned char *strDestination, size_t numberOfElements, const unsigned char *strSource ); +errno_t nullPointer_mbscat_s(unsigned char *strDestination, size_t numberOfElements, const unsigned char *strSource) +{ + // cppcheck-suppress nullPointer + (void)_mbscat_s(0, numberOfElements, strSource); + // cppcheck-suppress nullPointer + (void)_mbscat_s(strDestination, numberOfElements, 0); + + // no warning shall be shown for + return _mbscat_s(strDestination, numberOfElements, strSource); +} + +#if !UNICODE +// errno_t _strncpy_s_l(char *strDest, size_t numberOfElements, const char *strSource, size_t count, _locale_t locale); +errno_t uninitvar__strncpy_s_l(char *strDest, size_t numberOfElements, const char *strSource, size_t count, _locale_t locale) +{ + size_t uninit_numberOfElements; + const char *uninit_strSource; + size_t uninit_count; + _locale_t uninit_locale; + + // cppcheck-suppress uninitvar + (void)_strncpy_s_l(strDest, uninit_numberOfElements, strSource, count, locale); + // cppcheck-suppress uninitvar + (void)_strncpy_s_l(strDest, numberOfElements, uninit_strSource, count, locale); + // cppcheck-suppress uninitvar + (void)_strncpy_s_l(strDest, numberOfElements, strSource, uninit_count, locale); + // cppcheck-suppress uninitvar + (void)_strncpy_s_l(strDest, numberOfElements, strSource, count, uninit_locale); + + // no warning shall be shown for + return _strncpy_s_l(strDest, numberOfElements, strSource, count, locale); +} + +errno_t nullPointer__strncpy_s_l(char *strDest, size_t numberOfElements, const char *strSource, size_t count, _locale_t locale) +{ + // cppcheck-suppress nullPointer + (void)_strncpy_s_l(0, numberOfElements, strSource, count, locale); + // cppcheck-suppress nullPointer + (void)_strncpy_s_l(strDest, numberOfElements, 0, count, locale); + + // no warning shall be shown for + return _strncpy_s_l(strDest, numberOfElements, strSource, count, locale); +} +#endif + +void GetShortPathName_validCode(const TCHAR* lpszPath) +{ + long length = GetShortPathName(lpszPath, NULL, 0); + if (length == 0) { + _tprintf(TEXT("error")); + return; + } + TCHAR* buffer = new TCHAR[length]; + length = GetShortPathName(lpszPath, buffer, length); + if (length == 0) { + delete[] buffer; + _tprintf(TEXT("error")); + return; + } + _tprintf(TEXT("long name = %s short name = %s"), lpszPath, buffer); + delete[] buffer; +} + +void invalidPrintfArgType_StructMember(double d) { // #9672 + typedef struct { CString st; } my_struct_t; + + my_struct_t my_struct; + // cppcheck-suppress invalidPrintfArgType_sint + my_struct.st.Format(_T("%d"), d); +} + +BOOL MyEnableWindow(HWND hWnd, BOOL bEnable) { + return EnableWindow(hWnd, bEnable); +} diff --git a/cppcheck-2.14.0/test/cfg/wxwidgets.cpp b/cppcheck-2.14.0/test/cfg/wxwidgets.cpp new file mode 100644 index 00000000..7623206f --- /dev/null +++ b/cppcheck-2.14.0/test/cfg/wxwidgets.cpp @@ -0,0 +1,1289 @@ + +// Test library configuration for wxwidgets.cfg +// +// Usage: +// $ ./cppcheck --check-library --library=wxwidgets --enable=style,information --inconclusive --error-exitcode=1 --disable=missingInclude --inline-suppr test/cfg/wxwidgets.cpp +// => +// No warnings about bad library configuration, unmatched suppressions, etc. exitcode=0 +// + +// cppcheck-suppress-file valueFlowBailout +// cppcheck-suppress-file purgedConfiguration + +#include +#include +#include +#include +#include +#include +#include +#if wxCHECK_VERSION(3, 1, 6) // wxWidets-3.1.6 or higher +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__WXMSW__) +#include +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if wxCHECK_VERSION(3, 1, 6) // wxWidets-3.1.6 or higher +void unreadVariable_wxBitmapBundle(const wxBitmap &bmp, const wxIcon &icon, const wxImage &image, const char *const * xpm, const wxBitmapBundle &bundle) +{ + // cppcheck-suppress unusedVariable + wxBitmapBundle a; + // cppcheck-suppress unreadVariable + wxBitmapBundle b(bmp); + // cppcheck-suppress unreadVariable + wxBitmapBundle c(icon); + // cppcheck-suppress unreadVariable + wxBitmapBundle d(image); + // cppcheck-suppress unreadVariable + wxBitmapBundle e(xpm); + // cppcheck-suppress unreadVariable + wxBitmapBundle f(bundle); +} +#endif + +#if wxCHECK_VERSION(3, 1, 3) // wxWidets-3.1.3 or higher +void unreadVariable_wxDCTextBgModeChanger(wxDC &dc) +{ + // cppcheck-suppress unreadVariable + wxDCTextBgModeChanger a(dc); +} + +void unreadVariable_wxDCTextBgColourChanger(wxDC &dc, const wxColour &colour) +{ + // cppcheck-suppress unreadVariable + wxDCTextBgColourChanger a(dc); + // cppcheck-suppress unreadVariable + wxDCTextBgColourChanger b(dc, colour); +} +#endif + +void unreadVariable_wxZipEntry(const wxZipEntry &entry) +{ + // cppcheck-suppress unreadVariable + wxZipEntry a(entry); +} + +void unreadVariable_wxTarEntry(const wxTarEntry &entry) +{ + // cppcheck-suppress unreadVariable + wxTarEntry a(entry); +} + +void unreadVariable_wxDCTextColourChanger(wxDC &dc, const wxColour &colour) +{ + // cppcheck-suppress unreadVariable + wxDCTextColourChanger a(dc); + // cppcheck-suppress unreadVariable + wxDCTextColourChanger b(dc, colour); +} + +void unreadVariable_wxDCPenChanger(wxDC &dc, const wxPen &pen) +{ + // cppcheck-suppress unreadVariable + wxDCPenChanger a(dc, pen); +} + +void unreadVariable_wxDCFontChanger(wxDC &dc, const wxFont &font) +{ + // cppcheck-suppress unreadVariable + wxDCFontChanger a(dc); + // cppcheck-suppress unreadVariable + wxDCFontChanger b(dc, font); +} + +void unreadVariable_wxDCBrushChanger(wxDC &dc, const wxBrush &brush) +{ + // cppcheck-suppress unreadVariable + wxDCBrushChanger a(dc, brush); +} + +void unreadVariable_wxGBSpan(const int x) +{ + // cppcheck-suppress unusedVariable + wxGBSpan a; + // cppcheck-suppress unreadVariable + wxGBSpan b(x, x); +} + +void unreadVariable_wxGBPosition(const int x) +{ + // cppcheck-suppress unusedVariable + wxGBPosition a; + // cppcheck-suppress unreadVariable + wxGBPosition b(x, x); +} + +void unreadVariable_wxWrapSizer(const int x) +{ + // cppcheck-suppress unreadVariable + wxWrapSizer a(x, x); +} + +void unreadVariable_wxGridBagSizer(const int x) +{ + // cppcheck-suppress unreadVariable + wxGridBagSizer a(x, x); +} + +void unreadVariable_wxGBSizerItem(const int x, const wxGBPosition &pos) +{ + // cppcheck-suppress unreadVariable + wxGBSizerItem a(x, x, pos); +} + +void unreadVariable_wxSizerItem(const int x) +{ + // cppcheck-suppress unreadVariable + wxSizerItem a(x, x); +} + +void unreadVariable_wxFlexGridSizer(const int x) +{ + // cppcheck-suppress unreadVariable + wxFlexGridSizer a(x, x, x); +} + +void unreadVariable_wxBoxSizer(const int orient) +{ + // cppcheck-suppress unreadVariable + wxBoxSizer a(orient); +} + +void unreadVariable_wxGridSizer(int x) +{ + // cppcheck-suppress unreadVariable + wxGridSizer a(x, x, x); +} + +void unreadVariable_wxStaticBoxSizer(wxStaticBox *box, const int orient, wxWindow *parent, const wxString &label) +{ + // cppcheck-suppress unreadVariable + wxStaticBoxSizer a(box, orient); + // cppcheck-suppress unreadVariable + wxStaticBoxSizer b(orient, parent); + // cppcheck-suppress unreadVariable + wxStaticBoxSizer c(orient, parent, label); +} + +void unusedVariable_wxDelegateRendererNative() +{ + // cppcheck-suppress unusedVariable + wxDelegateRendererNative a; +} + +void unusedVariable_wxHeaderButtonParams() +{ + // cppcheck-suppress unusedVariable + wxHeaderButtonParams a; +} + +void unusedVariable_wxRegionIterator() +{ + // cppcheck-suppress unusedVariable + wxRegionIterator a; +} + +void unusedVariable_wxRegionContain() +{ + // cppcheck-suppress unusedVariable + wxRegionContain a; +} + +void unusedVariable_wxPalette() +{ + // cppcheck-suppress unusedVariable + wxPalette a; +} + +void unusedVariable_wxJPEGHandler() +{ + // cppcheck-suppress unusedVariable + wxJPEGHandler a; +} + +void unusedVariable_wxGIFHandler() +{ + // cppcheck-suppress unusedVariable + wxGIFHandler a; +} + +void unusedVariable_wxPCXHandler() +{ + // cppcheck-suppress unusedVariable + wxPCXHandler a; +} + +void unusedVariable_wxIFFHandler() +{ + // cppcheck-suppress unusedVariable + wxIFFHandler a; +} + +void unusedVariable_wxGraphicsBrush() +{ + // cppcheck-suppress unusedVariable + wxGraphicsBrush a; +} + +void unusedVariable_wxGraphicsMatrix() +{ + // cppcheck-suppress unusedVariable + wxGraphicsMatrix a; +} + +void unusedVariable_wxGraphicsFont() +{ + // cppcheck-suppress unusedVariable + wxGraphicsFont a; +} + +void unusedVariable_wxIconBundle() +{ + // cppcheck-suppress unusedVariable + wxIconBundle a; +} + +void unusedVariable_wxStdDialogButtonSizer() +{ + // cppcheck-suppress unusedVariable + wxStdDialogButtonSizer a; +} + +void unusedVariable_wxColourDatabase() +{ + // cppcheck-suppress unusedVariable + wxColourDatabase a; +} + +void unusedVariable_wxFontEnumerator() +{ + // cppcheck-suppress unusedVariable + wxFontEnumerator a; +} + +void unusedVariable_wxCursor() +{ + // cppcheck-suppress unusedVariable + wxCursor a; +} + +void unusedVariable_wxBitmapHandler() +{ + // cppcheck-suppress unusedVariable + wxBitmapHandler a; +} + +void unusedVariable_wxNativeFontInfo() +{ + // cppcheck-suppress unusedVariable + wxNativeFontInfo a; +} + +void unreadVariable_wxDCClipper(wxDC &dc, const wxRegion ®ion) +{ + // cppcheck-suppress unreadVariable + wxDCClipper a(dc, region); +} + +void unreadVariable_wxMask(const wxBitmap &bmp, int x, const wxColour & colour) +{ + // cppcheck-suppress unusedVariable + wxMask a; + // cppcheck-suppress unreadVariable + wxMask b(bmp); + // cppcheck-suppress unreadVariable + wxMask c(bmp, x); + // cppcheck-suppress unreadVariable + wxMask d(bmp, colour); +} + +void unreadVariable_wxGraphicsGradientStops() +{ + // cppcheck-suppress unusedVariable + wxGraphicsGradientStops a; + // cppcheck-suppress unreadVariable + wxGraphicsGradientStops b(wxTransparentColour); + // cppcheck-suppress unreadVariable + wxGraphicsGradientStops c(wxTransparentColour, wxTransparentColour); +} + +void unreadVariable_wxGraphicsGradientStop() +{ + // cppcheck-suppress unusedVariable + wxGraphicsGradientStop a; + // cppcheck-suppress unreadVariable + wxGraphicsGradientStop b(wxTransparentColour); + // cppcheck-suppress unreadVariable + wxGraphicsGradientStop c(wxTransparentColour, 0.42); +} + +void unusedVariable_wxFontMetrics() +{ + // cppcheck-suppress unusedVariable + wxFontMetrics a; +} + +void unusedVariable_wxIconLocation() +{ + // cppcheck-suppress unusedVariable + wxIconLocation a; +} + +void unreadVariable_wxIcon(const wxIcon &icon, const wxIconLocation &loc, const char *const *ptr) +{ + // cppcheck-suppress unusedVariable + wxIcon a; + // cppcheck-suppress unreadVariable + wxIcon b(icon); + // cppcheck-suppress unreadVariable + wxIcon c(loc); + // cppcheck-suppress unreadVariable + wxIcon d(ptr); +} + +void unreadVariable_wxImage(const wxImage &image, const int x) +{ + // cppcheck-suppress unusedVariable + wxImage a; + // cppcheck-suppress unreadVariable + wxImage b(image); + // cppcheck-suppress unreadVariable + wxImage c(x, x); + // cppcheck-suppress unreadVariable + wxImage d(x, x, true); +} + +void unreadVariable_wxUString(const wxUString &str, const wxChar32 *strPtr) +{ + // cppcheck-suppress unusedVariable + wxUString a; + // cppcheck-suppress unreadVariable + wxUString b(str); + // cppcheck-suppress unreadVariable + wxUString c(strPtr); +} + +void unreadVariable_wxAny(const wxVariant &variant, const wxAny &any) +{ + // cppcheck-suppress unusedVariable + wxAny a; + // cppcheck-suppress unreadVariable + wxAny b(42); + // cppcheck-suppress unreadVariable + wxAny c(variant); + // cppcheck-suppress unreadVariable + wxAny d(any); +} + +void unreadVariable_wxVariant(wxVariantData *data, + const wxString &name, + const wxVariant &variant, + const wxAny &any, + const wxChar *valuePtr, + const wxString &valueStr, + const wxChar charValue, + long lValue, + bool bvalue) +{ + // cppcheck-suppress unusedVariable + wxVariant a; + // cppcheck-suppress unreadVariable + wxVariant b(data); + // cppcheck-suppress unreadVariable + wxVariant c(data, name); + // cppcheck-suppress unreadVariable + wxVariant d(variant); + // cppcheck-suppress unreadVariable + wxVariant e(any); + // cppcheck-suppress unreadVariable + wxVariant f(valuePtr); + // cppcheck-suppress unreadVariable + wxVariant g(valuePtr, name); + // cppcheck-suppress unreadVariable + wxVariant h(valueStr); + // cppcheck-suppress unreadVariable + wxVariant i(valueStr, name); + // cppcheck-suppress unreadVariable + wxVariant j(charValue); + // cppcheck-suppress unreadVariable + wxVariant k(charValue, name); + // cppcheck-suppress unreadVariable + wxVariant l(lValue); + // cppcheck-suppress unreadVariable + wxVariant m(lValue, name); + // cppcheck-suppress unreadVariable + wxVariant n(bvalue); + // cppcheck-suppress unreadVariable + wxVariant o(bvalue, name); +} + +#if defined(__WXMSW__) + +void unusedVariable_wxMetafile() +{ + // cppcheck-suppress unusedVariable + wxMetafile a; +} + +void unusedVariable_wxVariantDataErrorCode() +{ + // cppcheck-suppress unusedVariable + wxVariantDataErrorCode a; +} +void unusedVariable_wxVariantDataCurrency() +{ + // cppcheck-suppress unusedVariable + wxVariantDataCurrency a; +} +void unusedVariable_wxVariantDataSafeArray() +{ + // cppcheck-suppress unusedVariable + wxVariantDataSafeArray a; +} +#endif + +void unreadVariable_wxBitmap(const wxBitmap &bmp, const char bits[], const int x, const wxSize &sz) +{ + // cppcheck-suppress unusedVariable + wxBitmap a; + // cppcheck-suppress unreadVariable + wxBitmap b(bmp); + // cppcheck-suppress unreadVariable + wxBitmap c(bits, x, x); + // cppcheck-suppress unreadVariable + wxBitmap d(bits, x, x, x); + // cppcheck-suppress unreadVariable + wxBitmap e(x, x); + // cppcheck-suppress unreadVariable + wxBitmap f(x, x, x); + // cppcheck-suppress unreadVariable + wxBitmap g(sz); + // cppcheck-suppress unreadVariable + wxBitmap h(sz, x); +} + +void unusedVariable_wxChar() +{ + // cppcheck-suppress unusedVariable + wxChar a; +} + +void unusedVariable_wxUniChar(const int c) +{ + // cppcheck-suppress unusedVariable + wxUniChar a; + // cppcheck-suppress unreadVariable + wxUniChar b(c); +} + +void unusedVariable_wxSystemOptions() +{ + // cppcheck-suppress unusedVariable + wxSystemOptions a; +} + +void unusedVariable_wxSystemSettings() +{ + // cppcheck-suppress unusedVariable + wxSystemSettings a; +} + +void unusedVariable_wxPenList() +{ + // cppcheck-suppress unusedVariable + wxPenList a; +} + +void unusedVariable_wxPen(const wxColour &colour, int width, const wxPenStyle style, const wxPen &pen) +{ + // cppcheck-suppress unusedVariable + wxPen a; + // cppcheck-suppress unreadVariable + wxPen b(colour, width); + // cppcheck-suppress unreadVariable + wxPen c(colour, width, style); + // cppcheck-suppress unreadVariable + wxPen d(pen); +} + +void unusedVariable_wxBrush(const wxColour &color, const wxBrushStyle style, const wxBitmap &bmp, const wxBrush &brush) +{ + // cppcheck-suppress unusedVariable + wxBrush a; + // cppcheck-suppress unreadVariable + wxBrush b(color, style); + // cppcheck-suppress unreadVariable + wxBrush c(bmp); + // cppcheck-suppress unreadVariable + wxBrush d(brush); +} + +void unusedVariable_wxFontList() +{ + // cppcheck-suppress unusedVariable + wxFontList a; +} + +void unusedVariable_wxFontInfo(const double pointSize, const wxSize &sz) +{ + // cppcheck-suppress unusedVariable + wxFontInfo a; + // cppcheck-suppress unreadVariable + wxFontInfo b(pointSize); + // cppcheck-suppress unreadVariable + wxFontInfo c(sz); +} + +void unusedVariable_wxFont(const wxFont &font, + const wxFontInfo &fontInfo, + const int pointSize, + const wxFontFamily family, + const wxFontStyle style, + const wxFontWeight weight, + const bool underline, + const wxString &faceName, + const wxFontEncoding encoding) +{ + // cppcheck-suppress unusedVariable + wxFont a; + // cppcheck-suppress unreadVariable + wxFont b(font); + // cppcheck-suppress unreadVariable + wxFont c(fontInfo); + // cppcheck-suppress unreadVariable + wxFont d(pointSize, family, style, weight); + // cppcheck-suppress unreadVariable + wxFont e(pointSize, family, style, weight, underline); + // cppcheck-suppress unreadVariable + wxFont f(pointSize, family, style, weight, underline, faceName); + // cppcheck-suppress unreadVariable + wxFont g(pointSize, family, style, weight, underline, faceName, encoding); +} + +void unusedVariable_wxVector() +{ + // cppcheck-suppress unusedVariable + wxVector a; +} + +void unusedVariable_wxArrayInt() +{ + // cppcheck-suppress unusedVariable + wxArrayInt a; +} + +void unusedVariable_wxArrayDouble() +{ + // cppcheck-suppress unusedVariable + wxArrayDouble a; +} + +void unusedVariable_wxArrayShort() +{ + // cppcheck-suppress unusedVariable + wxArrayShort a; +} + +void unusedVariable_wxArrayString() +{ + // cppcheck-suppress unusedVariable + wxArrayString a; +} + +void unusedVariable_wxArrayPtrVoid() +{ + // cppcheck-suppress unusedVariable + wxArrayPtrVoid a; +} + +void unreadVariable_wxColour(const unsigned char uc, const wxString &name, const unsigned long colRGB, const wxColour &colour) +{ + // cppcheck-suppress unusedVariable + wxColour a; + // cppcheck-suppress unreadVariable + wxColour b(uc, uc, uc); + // cppcheck-suppress unreadVariable + wxColour c(uc, uc, uc, uc); + // cppcheck-suppress unreadVariable + wxColour d(name); + // cppcheck-suppress unreadVariable + wxColour e(colRGB); + // cppcheck-suppress unreadVariable + wxColour f(colour); +} + +void unreadVariable_wxPoint2DInt(const wxInt32 x, const wxPoint2DInt& pti, const wxPoint &pt) +{ + // cppcheck-suppress unusedVariable + wxPoint2DInt a; + // cppcheck-suppress unreadVariable + wxPoint2DInt b(x, x); + // cppcheck-suppress unreadVariable + wxPoint2DInt c(pti); + // cppcheck-suppress unreadVariable + wxPoint2DInt d(pt); +} + +void unreadVariable_wxPoint2DDouble(const wxDouble x, const wxPoint2DDouble& ptd, const wxPoint2DInt& pti, const wxPoint &pt) +{ + // cppcheck-suppress unusedVariable + wxPoint2DDouble a; + // cppcheck-suppress unreadVariable + wxPoint2DDouble b(x, x); + // cppcheck-suppress unreadVariable + wxPoint2DDouble c(ptd); + // cppcheck-suppress unreadVariable + wxPoint2DDouble d(pti); + // cppcheck-suppress unreadVariable + wxPoint2DDouble e(pt); +} + +void unusedVariable_wxAcceleratorEntry() +{ + // cppcheck-suppress unusedVariable + wxAcceleratorEntry a; +} + +void unreadVariable_wxDateSpan(const int x) +{ + // cppcheck-suppress unusedVariable + wxDateSpan a; + // cppcheck-suppress unreadVariable + wxDateSpan b{x}; + // cppcheck-suppress unreadVariable + wxDateSpan c{x, x}; + // cppcheck-suppress unreadVariable + wxDateSpan d{x, x, x}; + // cppcheck-suppress unreadVariable + wxDateSpan e{x, x, x, x}; +} + +void unreadVariable_wxTimeSpan(const long x, const wxLongLong y) +{ + // cppcheck-suppress unusedVariable + wxTimeSpan a; + // cppcheck-suppress unreadVariable + wxTimeSpan b{}; + // cppcheck-suppress unreadVariable + wxTimeSpan c{x}; + // cppcheck-suppress unreadVariable + wxTimeSpan d{x, x}; + // cppcheck-suppress unreadVariable + wxTimeSpan e{x, x, y}; + // cppcheck-suppress unreadVariable + wxTimeSpan f{x, x, y, y}; +} + +void unreadVariable_wxFileType(const wxFileTypeInfo &info) +{ + // cppcheck-suppress unreadVariable + wxFileType a(info); +} + +void unreadVariable_wxPosition(const int x) +{ + // cppcheck-suppress unusedVariable + wxPosition a; + // cppcheck-suppress unreadVariable + wxPosition b{}; + // cppcheck-suppress unreadVariable + wxPosition c{x,x}; +} + +void unreadVariable_wxRegEx(const wxString &expr, const int flags) +{ + // cppcheck-suppress unusedVariable + wxRegEx a; + // cppcheck-suppress unreadVariable + wxRegEx b{expr}; + // cppcheck-suppress unreadVariable + wxRegEx c{expr, flags}; +} + +void unreadVariable_wxRegion(const wxCoord x, const wxPoint &pt, const wxRect &rect, const wxRegion ®ion, const wxBitmap &bmp) +{ + // cppcheck-suppress unusedVariable + wxRegion a; + // cppcheck-suppress unreadVariable + wxRegion b{}; + // cppcheck-suppress unreadVariable + wxRegion c{x,x,x,x}; + // cppcheck-suppress unreadVariable + wxRegion d{pt,pt}; + // cppcheck-suppress unreadVariable + wxRegion e{rect}; + // cppcheck-suppress unreadVariable + wxRegion f{region}; + // cppcheck-suppress unreadVariable + wxRegion g{bmp}; +} + +void unreadVariable_wxVersionInfo(const wxString &name, const int major, const int minor, const int micro, const wxString &description, const wxString ©right) +{ + // cppcheck-suppress unusedVariable + wxVersionInfo a; + // cppcheck-suppress unreadVariable + wxVersionInfo b(name); + // cppcheck-suppress unreadVariable + wxVersionInfo c(name, major); + // cppcheck-suppress unreadVariable + wxVersionInfo d(name, major, minor); + // cppcheck-suppress unreadVariable + wxVersionInfo e(name, major, minor, micro); + // cppcheck-suppress unreadVariable + wxVersionInfo f(name, major, minor, micro, description); + // cppcheck-suppress unreadVariable + wxVersionInfo g(name, major, minor, micro, description, copyright); +} + +void unreadVariable_wxSize(const wxSize &s) +{ + // cppcheck-suppress unusedVariable + wxSize a; + // cppcheck-suppress unreadVariable + wxSize b{}; + // cppcheck-suppress unreadVariable + wxSize c{4, 2}; + // cppcheck-suppress unreadVariable + wxSize d(4, 2); + // cppcheck-suppress unreadVariable + wxSize e(s); +} + +void unreadVariable_wxPoint(const wxRealPoint &rp, const int x, const int y) +{ + // cppcheck-suppress unusedVariable + wxPoint a; + // cppcheck-suppress unreadVariable + wxPoint b{}; + // cppcheck-suppress unreadVariable + wxPoint c{4, 2}; + // cppcheck-suppress unreadVariable + wxPoint d(4, 2); + // cppcheck-suppress unreadVariable + wxPoint e{x, 2}; + // cppcheck-suppress unreadVariable + wxPoint f(4, y); + // cppcheck-suppress unreadVariable + wxPoint g(rp); +} + +void unreadVariable_wxRealPoint(const wxPoint &pt, const double x, const double y) +{ + // cppcheck-suppress unusedVariable + wxRealPoint a; + // cppcheck-suppress unreadVariable + wxRealPoint b{}; + // cppcheck-suppress unreadVariable + wxRealPoint c{4.0, 2.0}; + // cppcheck-suppress unreadVariable + wxRealPoint d(4.0, 2.0); + // cppcheck-suppress unreadVariable + wxRealPoint e{x, 2.0}; + // cppcheck-suppress unreadVariable + wxRealPoint f(4.0, y); + // cppcheck-suppress unreadVariable + wxRealPoint g(pt); +} + +void unreadVariable_wxRect(const int x, const wxPoint &pt, const wxSize &sz) +{ + // cppcheck-suppress unusedVariable + wxRect a; + // cppcheck-suppress unreadVariable + wxRect b{}; + // cppcheck-suppress unreadVariable + wxRect c{x,x,x,x}; + // cppcheck-suppress unreadVariable + wxRect d{pt,sz}; + // cppcheck-suppress unreadVariable + wxRect e{sz}; + // cppcheck-suppress unreadVariable + wxRect f(x,x,x,x); + // cppcheck-suppress unreadVariable + wxRect g(pt,sz); + // cppcheck-suppress unreadVariable + wxRect h(sz); +} + +void uninitvar_wxRegEx_GetMatch(const wxRegEx &obj, size_t *start, size_t *len, size_t index) +{ + size_t s,l; + size_t *sPtr,*lPtr; + // cppcheck-suppress uninitvar + (void)obj.GetMatch(&s,lPtr); + // TODO cppcheck-suppress uninitvar + (void)obj.GetMatch(sPtr,&l); + (void)obj.GetMatch(&s,&l); + (void)obj.GetMatch(start,len); + (void)obj.GetMatch(start,len,0); + (void)obj.GetMatch(start,len,index); +} + +#ifdef __VISUALC__ +// Ensure no duplicateBreak warning is issued after wxLogApiError() calls. +// This function does not terminate execution. +bool duplicateBreak_wxLogApiError(const wxString &msg, const HRESULT &hr, wxString &str) +{ + if (hr) { + wxLogApiError(msg,hr); + str = "fail"; + return false; + } + return true; +} +#endif + +void argDirection_wxString_ToDouble(const wxString &str) +{ + // No warning is expected. Ensure both arguments are treated + // as output by library configuration + double value; + const bool convOk = str.ToDouble(&value); + if (convOk && value <= 42.0) {} +} + +void argDirection_wxString_ToCDouble(const wxString &str) +{ + // No warning is expected. Ensure both arguments are treated + // as output by library configuration + double value; + const bool convOk = str.ToCDouble(&value); + if (convOk && value <= 42.0) {} +} + +void argDirection_wxTextCtrl_GetSelection(const wxTextCtrl *const textCtrl) +{ + // No warning is expected. Ensure both arguments are treated + // as output by library configuration + long start; + long end; + textCtrl->GetSelection(&start, &end); + if (start > 0 && end > 0) {} +} + +void useRetval_wxString_MakeCapitalized(wxString &str) +{ + // No warning is expected for + str.MakeCapitalized(); +} + +void useRetval_wxString_MakeLower(wxString &str) +{ + // No warning is expected for + str.MakeLower(); +} + +void useRetval_wxString_MakeUpper(wxString &str) +{ + // No warning is expected for + str.MakeUpper(); +} + +wxString containerOutOfBounds_wxArrayString(void) +{ + wxArrayString a; + a.Add("42"); + a.Clear(); + // TODO: wxArrayString is defined to be a vector + // TODO: cppcheck-suppress containerOutOfBounds + return a[0]; +} + +int containerOutOfBounds_wxArrayInt(void) +{ + wxArrayInt a; + a.Add(42); + a.Clear(); + // TODO: wxArrayString is defined to be a vector + // TODO: cppcheck-suppress containerOutOfBounds + return a[0]; +} + +void ignoredReturnValue_wxDC_GetSize(const wxDC &dc, wxCoord *width, wxCoord *height) +{ + // No warning is expected for + dc.GetSize(width, height); + // No warning is expected for + (void)dc.GetSize(); +} + +void ignoredReturnValue_wxDC_GetSizeMM(const wxDC &dc, wxCoord *width, wxCoord *height) +{ + // No warning is expected for + dc.GetSizeMM(width, height); + // Now warning is expected for + (void)dc.GetSizeMM(); +} + +wxSizerItem* invalidFunctionArgBool_wxSizer_Add(wxSizer *sizer, wxWindow * window, const wxSizerFlags &flags) +{ + // No warning is expected for + return sizer->Add(window,flags); +} + +bool invalidFunctionArgBool_wxPGProperty_Hide(wxPGProperty *pg, bool hide, int flags) +{ + // cppcheck-suppress invalidFunctionArgBool + (void)pg->Hide(hide, true); + // No warning is expected for + return pg->Hide(hide, flags); +} + +wxTextCtrlHitTestResult nullPointer_wxTextCtrl_HitTest(const wxTextCtrl& txtCtrl, const wxPoint& pos) +{ + // no nullPointer-warning is expected + return txtCtrl.HitTest(pos, NULL); +} + +void validCode() +{ + wxString str = wxGetCwd(); + (void)str; + + wxLogGeneric(wxLOG_Message, "test %d", 0); + wxLogMessage("test %s", "str"); + + wxString translation1 = _("text"); + wxString translation2 = wxGetTranslation("text"); + wxString translation3 = wxGetTranslation("string", "domain"); + (void)translation1; + (void)translation2; + (void)translation3; +} + +#if wxUSE_GUI==1 +void validGuiCode() +{ +#if wxUSE_SPINCTRL==1 + extern wxSpinCtrl spinCtrlInstance; + spinCtrlInstance.SetBase(10); + spinCtrlInstance.SetBase(16); +#endif +} +#endif + +void nullPointer(const wxString &str) +{ + // cppcheck-suppress nullPointer + wxLogGeneric(wxLOG_Message, (char*)NULL); + // cppcheck-suppress nullPointer + wxLogMessage((char*)NULL); + + double *doublePtr = NULL; + // cppcheck-suppress nullPointer + (void)str.ToDouble(doublePtr); + double *doublePtr1 = NULL; + // cppcheck-suppress nullPointer + (void)str.ToCDouble(doublePtr1); + + long * longPtr = NULL; + // cppcheck-suppress nullPointer + (void)str.ToLong(longPtr); + long * longPtr1 = NULL; + // cppcheck-suppress nullPointer + (void)str.ToCLong(longPtr1); + + unsigned long * ulongPtr = NULL; + // cppcheck-suppress nullPointer + (void)str.ToULong(ulongPtr); + unsigned long * ulongPtr1 = NULL; + // cppcheck-suppress nullPointer + (void)str.ToCULong(ulongPtr1); + + long long * longLongPtr = NULL; + // cppcheck-suppress nullPointer + (void)str.ToLongLong(longLongPtr); + + unsigned long long * ulongLongPtr = NULL; + // cppcheck-suppress nullPointer + (void)str.ToULongLong(ulongLongPtr); +} + +void nullPointer_wxSizer_Add(wxSizer &sizer, wxWindow *w) +{ + wxWindow * const ptr = 0; + // @todo cppcheck-suppress nullPointer + sizer.Add(ptr); + // No warning shall be issued for + sizer.Add(w); +} + +void uninitvar_wxSizer_Add(wxSizer &sizer, wxWindow *w,wxObject* userData) +{ + int uninit1, uninit2, uninit3; + // cppcheck-suppress uninitvar + sizer.Add(w,uninit1); + // cppcheck-suppress uninitvar + sizer.Add(w,4,uninit2); + // cppcheck-suppress uninitvar + sizer.Add(w,4,2,uninit3,userData); +} + +void ignoredReturnValue(const wxString &s) +{ + // cppcheck-suppress ignoredReturnValue + wxGetCwd(); + // cppcheck-suppress ignoredReturnValue + wxAtoi(s); + // cppcheck-suppress ignoredReturnValue + wxAtol(s); + // cppcheck-suppress ignoredReturnValue + wxAtof(s); +} + +void invalidFunctionArg(const wxString &str) +{ +#if wxUSE_SPINCTRL==1 + extern wxSpinCtrl spinCtrlInstance; + // cppcheck-suppress invalidFunctionArg + spinCtrlInstance.SetBase(0); + // cppcheck-suppress invalidFunctionArg + spinCtrlInstance.SetBase(5); +#endif + + long l; + // cppcheck-suppress invalidFunctionArg + (void)str.ToLong(&l, -1); + // cppcheck-suppress invalidFunctionArg + (void)str.ToLong(&l, 1); + // cppcheck-suppress invalidFunctionArg + (void)str.ToLong(&l, 37); +} + +void uninitvar(wxWindow &w) +{ + wxLogLevel logLevelUninit; + // cppcheck-suppress constVariable + char cBufUninit[10]; + const char *pcUninit; + bool uninitBool; + // cppcheck-suppress uninitvar + wxLogGeneric(logLevelUninit, "test"); + // cppcheck-suppress uninitvar + wxLogMessage(cBufUninit); + // cppcheck-suppress uninitvar + wxLogMessage(pcUninit); + // cppcheck-suppress uninitvar + w.Close(uninitBool); +} + +void uninitvar_wxStaticText(wxStaticText &s) +{ + // no warning + s.Wrap(-1); + int uninitInt; + // cppcheck-suppress uninitvar + s.Wrap(uninitInt); +} + +void uninitvar_wxString_NumberConversion(const wxString &str, const int numberBase) +{ + int uninitInteger1; + int uninitInteger2; + int uninitInteger3; + int uninitInteger4; + int uninitInteger5; + int uninitInteger6; + long l; + long long ll; + unsigned long ul; + unsigned long long ull; + + // cppcheck-suppress uninitvar + (void)str.ToLong(&l, uninitInteger1); + // cppcheck-suppress uninitvar + (void)str.ToLongLong(&ll, uninitInteger2); + // cppcheck-suppress uninitvar + (void)str.ToULong(&ul, uninitInteger3); + // cppcheck-suppress uninitvar + (void)str.ToULongLong(&ull, uninitInteger4); + + // cppcheck-suppress uninitvar + (void)str.ToCLong(&l, uninitInteger5); + // cppcheck-suppress uninitvar + (void)str.ToCULong(&ul, uninitInteger6); +} + +void uninitvar_SetMenuBar(wxFrame * const framePtr, wxMenuBar * const menuBarPtr) +{ + wxMenuBar *menuBar; + // cppcheck-suppress uninitvar + framePtr->SetMenuBar(menuBar); + framePtr->SetMenuBar(menuBarPtr); +} + +void uninitvar_wxMenuBarAppend(wxMenuBar * const menuBarPtr, wxMenu * const menuPtr, const wxString &title) +{ + wxMenu *menu; + // cppcheck-suppress uninitvar + menuBarPtr->Append(menu, title); + menuBarPtr->Append(menuPtr, title); +} + +void deprecatedFunctions_wxDataViewCustomRenderer(wxDataViewCustomRenderer &dataViewCustomRenderer, wxPoint cursor, wxRect cell, wxDataViewModel *model, const wxDataViewItem &item, unsigned int col) +{ + // cppcheck-suppress ActivateCalled + dataViewCustomRenderer.Activate(cell, model, item, col); + // cppcheck-suppress LeftClickCalled + dataViewCustomRenderer.LeftClick(cursor, cell, model, item, col); +} + +void deprecatedFunctions([[maybe_unused]] wxApp &a, + const wxString &s, + [[maybe_unused]] wxArtProvider *artProvider, + [[maybe_unused]] wxCalendarCtrl &calenderCtrl, + wxComboCtrl &comboCtrl, + wxChar * path) +{ +#ifdef __WXOSX__ + // cppcheck-suppress MacOpenFileCalled + a.MacOpenFile(s); +#endif + +#if wxCHECK_VERSION(3, 1, 0) // wxWidets-3.1.0 or higher: + // Some functions are not available anymore in newer versions + + // @todo cppcheck-suppress ShowPopupCalled + comboCtrl.ShowPopup(); +#else + // cppcheck-suppress InsertCalled + wxArtProvider::Insert(artProvider); + + // cppcheck-suppress GetTextIndentCalled + // cppcheck-suppress ignoredReturnValue + comboCtrl.GetTextIndent(); + + // cppcheck-suppress HidePopupCalled + comboCtrl.HidePopup(true); + // cppcheck-suppress HidePopupCalled + comboCtrl.HidePopup(false); + // cppcheck-suppress HidePopupCalled + comboCtrl.HidePopup(/*default=false*/); + + // cppcheck-suppress SetTextIndentCalled + comboCtrl.SetTextIndent(0); + +#if wxUSE_DEBUG_CONTEXT==1 + // cppcheck-suppress GetLevelCalled + // cppcheck-suppress ignoredReturnValue + wxDebugContext::GetLevel(); + // cppcheck-suppress SetLevelCalled + wxDebugContext::SetLevel(42); +#endif + + // cppcheck-suppress wxDos2UnixFilenameCalled + wxDos2UnixFilename(path); + + // cppcheck-suppress wxFileNameFromPathCalled + // cppcheck-suppress ignoredReturnValue + wxFileNameFromPath(wxT_2("../test.c")); +#endif + +#if defined(__WXMSW__) || defined(__WXGTK__) + // EnableYearChange() is not available on these GUI systems +#else + // cppcheck-suppress EnableYearChangeCalled + calenderCtrl.EnableYearChange(false); + // cppcheck-suppress EnableYearChangeCalled + calenderCtrl.EnableYearChange(true); + // cppcheck-suppress EnableYearChangeCalled + calenderCtrl.EnableYearChange(/*default=yes*/); +#endif +} + +void wxString_test1(wxString s) +{ + for (int i = 0; i <= s.size(); ++i) { + // cppcheck-suppress stlOutOfBounds + s[i] = 'x'; + } +} + +void wxString_test2() +{ + wxString s; + // cppcheck-suppress containerOutOfBounds + s[1] = 'a'; + s.append("abc"); + s[1] = 'B'; + printf("%s", static_cast(s.c_str())); + wxPrintf("%s", s); + wxPrintf("%s", s.c_str()); + s.Clear(); +} + +wxString::iterator wxString_test3() +{ + wxString wxString1; + wxString wxString2; + // cppcheck-suppress mismatchingContainers + for (wxString::iterator it = wxString1.begin(); it != wxString2.end(); ++it) + {} + + wxString::iterator it = wxString1.begin(); + // cppcheck-suppress returnDanglingLifetime + return it; +} + +wxGrid::wxGridSelectionModes get_wxGridSelectionModes() +{ + // cppcheck-suppress valueFlowBailoutIncompleteVar // TODO: configure enum #8183 + return wxGrid::wxGridSelectCells; +} diff --git a/cppcheck-2.14.0/test/cli/QML-Samples-TableView/README b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/README new file mode 100644 index 00000000..fed52488 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/README @@ -0,0 +1,2 @@ +Downloaded from here: +https://github.com/HamedMasafi/QML-Samples diff --git a/cppcheck-2.14.0/test/cli/QML-Samples-TableView/TableColumn.qml b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/TableColumn.qml new file mode 100644 index 00000000..a29f2a7b --- /dev/null +++ b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/TableColumn.qml @@ -0,0 +1,11 @@ +import QtQuick 2.0 +import QtQuick.Layouts 1.12 +import QtQuick.Controls 2.12 +import QtQml.Models 2.12 + +QtObject { + property string title + property string role + property bool fillWidth: false + property int size: -1 +} diff --git a/cppcheck-2.14.0/test/cli/QML-Samples-TableView/TableRow.qml b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/TableRow.qml new file mode 100644 index 00000000..2b1c7b0c --- /dev/null +++ b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/TableRow.qml @@ -0,0 +1,25 @@ +import QtQuick 2.0 +import QtQuick.Layouts 1.12 +import QtQuick.Controls 2.12 +import QtQml.Models 2.12 + +ItemDelegate { + height: 40 + width: parent.width + property bool isHeader: false + RowLayout { + property var _d: model + anchors.fill: parent + anchors.leftMargin: 9 + + Repeater { + model: root.columns + Label { + text: parent.parent.isHeader ? modelData.title : parent._d[modelData.role] + Layout.fillWidth: modelData.fillWidth + Layout.preferredWidth: modelData.size !== '*' + ? modelData.size : 20 + } + } + } +} diff --git a/cppcheck-2.14.0/test/cli/QML-Samples-TableView/TableView.pro b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/TableView.pro new file mode 100644 index 00000000..4dc16d5d --- /dev/null +++ b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/TableView.pro @@ -0,0 +1,27 @@ +QT += quick + +CONFIG += c++11 + +# You can make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main.cpp \ + samplemodel.cpp + +RESOURCES += qml.qrc + +# Additional import path used to resolve QML modules in Qt Creator's code model +QML_IMPORT_PATH = + +# Additional import path used to resolve QML modules just for Qt Quick Designer +QML_DESIGNER_IMPORT_PATH = + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target + +HEADERS += \ + samplemodel.h diff --git a/cppcheck-2.14.0/test/cli/QML-Samples-TableView/TableView.qml b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/TableView.qml new file mode 100644 index 00000000..a30b7c8d --- /dev/null +++ b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/TableView.qml @@ -0,0 +1,30 @@ +import QtQuick 2.0 +import QtQuick.Layouts 1.12 +import QtQuick.Controls 2.12 +import QtQml.Models 2.12 + +Item { + id: root + + property alias model: tableView.model + default property list columns + + TableRow { + id: header + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + background: null + isHeader: true + height: 60 + } + + ListView { + id: tableView + clip: true + + anchors.fill: parent + anchors.topMargin: header.height + delegate: TableRow {} + } +} diff --git a/cppcheck-2.14.0/test/cli/QML-Samples-TableView/main.cpp b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/main.cpp new file mode 100644 index 00000000..1c577994 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/main.cpp @@ -0,0 +1,25 @@ +#include +#include +#include "samplemodel.h" + +int main(int argc, char *argv[]) +{ +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif + + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + qmlRegisterType("Test", 1, 0, "SampleModel"); + + const QUrl url(QStringLiteral("qrc:/main.qml")); + QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, + &app, [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, Qt::QueuedConnection); + engine.load(url); + + return app.exec(); +} diff --git a/cppcheck-2.14.0/test/cli/QML-Samples-TableView/main.qml b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/main.qml new file mode 100644 index 00000000..6a8cc43f --- /dev/null +++ b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/main.qml @@ -0,0 +1,37 @@ +import QtQuick 2.12 +import QtQuick.Window 2.12 +import Test 1.0 +import '.' as Here + +Window { + width: 640 + height: 480 + visible: true + title: qsTr("Hello World") + + Component.onCompleted: sampleModel.fillSampleData(50) + SampleModel { + id: sampleModel + } + + Here.TableView { + anchors.fill: parent + model: sampleModel + + TableColumn { + title: qsTr("Id") + role: 'id' + size: 30 + } + TableColumn { + title: qsTr("Name") + role: 'name' + fillWidth: true + } + TableColumn { + title: qsTr("Grade") + role: 'grade' + size: 50 + } + } +} diff --git a/cppcheck-2.14.0/test/cli/QML-Samples-TableView/qml.qrc b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/qml.qrc new file mode 100644 index 00000000..cf94b15b --- /dev/null +++ b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/qml.qrc @@ -0,0 +1,8 @@ + + + main.qml + TableView.qml + TableRow.qml + TableColumn.qml + + diff --git a/cppcheck-2.14.0/test/cli/QML-Samples-TableView/samplemodel.cpp b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/samplemodel.cpp new file mode 100644 index 00000000..7032d37a --- /dev/null +++ b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/samplemodel.cpp @@ -0,0 +1,66 @@ +#include "samplemodel.h" + +#include +#include + +SampleModel::SampleModel(QObject *parent) : QAbstractListModel(parent) +{} + +int SampleModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return _data.size(); +} + +QVariant SampleModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + if (index.row() < 0 || index.row() > _data.count() - 1) + return QVariant(); + + auto row = _data.at(index.row()); + + switch (role) { + case IdRole: + return index.row(); + + case NameRole: + return row.first; + + case GradeRole: + return row.second; + } + + return QVariant(); +} + +QHash SampleModel::roleNames() const +{ + return { + {IdRole, "id"}, + {NameRole, "name"}, + {GradeRole, "grade"} + }; +} + +void SampleModel::fillSampleData(int size) +{ + QString abs = "qwertyuiopasdfghjklzxcvbnm"; + QRandomGenerator r; + for (auto i = 0; i < size; i++) { + Row row; + auto nameLen = r.bounded(3, 8); + QString name; + for (int c = 0; c < nameLen; ++c) + name.append(abs.at(r.bounded(0, abs.size() - 1))); + + row.first = name; + row.second = r.bounded(0, 20); + _data.append(row); + } + + qDebug() << _data.size() << "item(s) added as sample data"; + beginInsertRows(QModelIndex(), 0, _data.size() - 1); + endInsertRows(); +} diff --git a/cppcheck-2.14.0/test/cli/QML-Samples-TableView/samplemodel.h b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/samplemodel.h new file mode 100644 index 00000000..2bb9107e --- /dev/null +++ b/cppcheck-2.14.0/test/cli/QML-Samples-TableView/samplemodel.h @@ -0,0 +1,30 @@ +#ifndef SAMPLEMODEL_H +#define SAMPLEMODEL_H + +#include + +class SampleModel : public QAbstractListModel +{ + Q_OBJECT + + typedef QPair Row; + QList _data; + +public: + enum Role { + IdRole = Qt::UserRole + 1, + NameRole, + GradeRole + }; + + SampleModel(QObject *parent = nullptr); + + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QHash roleNames() const; + +public slots: + void fillSampleData(int size); +}; + +#endif // SAMPLEMODEL_H diff --git a/cppcheck-2.14.0/test/cli/clang-import_test.py b/cppcheck-2.14.0/test/cli/clang-import_test.py new file mode 100644 index 00000000..13be3cd0 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/clang-import_test.py @@ -0,0 +1,153 @@ + +# python -m pytest test-clang-import.py + +import os +import re +import subprocess +import sys + +import pytest +from testutils import cppcheck, assert_cppcheck + +try: + subprocess.call(['clang', '--version']) +except OSError: + pytest.skip("'clang' does not exist", allow_module_level=True) + + +# the IDs differ with Visual Studio +if sys.platform == 'win32': + pytest.skip(allow_module_level=True) + + +def get_debug_section(title, stdout): + s = re.sub(r'0x[0-9a-fA-F]+', '0x12345678', stdout) + s = re.sub(r'nestedIn: Struct', 'nestedIn: Class', s) + s = re.sub(r'classDef: struct', 'classDef: class', s) + s = re.sub(r'isInline: [a-z]+', 'isInline: ---', s) + s = re.sub(r'needInitialization: .*', 'needInitialization: ---', s) + s = re.sub(r'functionOf: .*', 'functionOf: ---', s) + s = re.sub(r'0x12345678 Struct', '0x12345678 Class', s) + + if title == '##AST': + # TODO set types + s = re.sub(r"return '[a-zA-Z0-9: *]+'", "return", s) + + pos1 = s.find(title) + assert pos1 > 0, 'title not found' + pos1 = s.find('\n', pos1) + 1 + assert pos1 > 0 + pos2 = s.find("\n##", pos1) + if pos2 < 0: + return s[pos1:] + return s[pos1:pos2-1] + + +def check_symbol_database(code): + testfile = 'test.cpp' + with open(testfile, 'w+t') as f: + f.write(code) + ret1, stdout1, _ = cppcheck(['--clang', '--debug', '-v', testfile]) + ret2, stdout2, _ = cppcheck(['--debug', '-v', testfile]) + os.remove(testfile) + assert 0 == ret1, stdout1 + assert 0 == ret2, stdout2 + assert get_debug_section('### Symbol database', stdout1) == get_debug_section('### Symbol database', stdout2) + + +def check_ast(code): + testfile = 'test.cpp' + with open(testfile, 'w+t') as f: + f.write(code) + ret1, stdout1, _ = cppcheck(['--clang', '--debug', '-v', testfile]) + ret2, stdout2, _ = cppcheck(['--debug', '-v', testfile]) + os.remove(testfile) + assert 0 == ret1, stdout1 + assert 0 == ret2, stdout1 + assert get_debug_section('##AST', stdout1) == get_debug_section('##AST', stdout2) + + +def todo_check_ast(code): + testfile = 'test.cpp' + with open(testfile, 'w+t') as f: + f.write(code) + ret1, stdout1, _ = cppcheck(['--clang', '--debug', '-v', testfile]) + ret2, stdout2, _ = cppcheck(['--debug', '-v', testfile]) + os.remove(testfile) + assert 0 == ret1, stdout1 + assert 0 == ret2, stdout2 + assert get_debug_section('##AST', stdout1) != get_debug_section('##AST', stdout2) + + + +def test_symbol_database_1(): + check_symbol_database('int main(){return 0;}') + +def test_symbol_database_2(): + check_symbol_database('struct Foo { void f(); }; void Foo::f() {}') + +def test_symbol_database_3(): + check_symbol_database('struct Fred { int a; }; int b; void f(int c, int d) { int e; }') + +def test_symbol_database_4(): + check_symbol_database('void f(const int x) {}') + +def test_symbol_database_5(): + check_symbol_database('void f(int);') + +def test_symbol_database_6(): + check_symbol_database('inline static int foo(int x) { return x; }') + +def test_symbol_database_7(): + check_symbol_database('struct S {int x;}; void f(struct S *s) {}') + +def test_symbol_database_class_access_1(): + check_symbol_database('class Fred { void foo ( ) {} } ;') + +def test_symbol_database_class_access_2(): + check_symbol_database('class Fred { protected: void foo ( ) {} } ;') + +def test_symbol_database_class_access_3(): + check_symbol_database('class Fred { public: void foo ( ) {} } ;') + +def test_symbol_database_operator(): + check_symbol_database('struct Fred { void operator=(int x); };') + +def test_symbol_database_struct_1(): + check_symbol_database('struct S {};') + +def test_ast_calculations(): + check_ast('int x = 5; int y = (x + 4) * 2;') + check_ast('long long dostuff(int x) { return x ? 3 : 5; }') + +def test_ast_control_flow(): + check_ast('void foo(int x) { if (x > 5){} }') + check_ast('int dostuff() { for (int x = 0; x < 10; x++); }') + check_ast('void foo(int x) { switch (x) {case 1: break; } }') + check_ast('void foo(int a, int b, int c) { foo(a,b,c); }') + +def test_ast(): + check_ast('struct S { int x; }; S* foo() { return new S(); }') + +def test_log(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt'): + pass + + args = ['--clang', test_file] + out_lines = [ + 'Checking {} ...'.format(test_file).replace('\\', '/'), + ] + + assert_cppcheck(args, ec_exp=0, err_exp=[], out_exp=out_lines) + + +def test_warning(tmpdir): # #12424 + test_file = os.path.join(tmpdir, 'test_2') + with open(test_file, 'wt') as f: + f.write('''void f() {}''') + + exitcode, stdout, stderr = cppcheck(['-q', '--enable=warning', '--clang', test_file]) + assert exitcode == 0, stderr # do not assert + assert stdout == '' + assert stderr == '' diff --git a/cppcheck-2.14.0/test/cli/fuzz-crash/crash-0f0efd6e66bd115fc0aabbfe6503230b31de5f24 b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-0f0efd6e66bd115fc0aabbfe6503230b31de5f24 new file mode 100644 index 00000000..a011f109 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-0f0efd6e66bd115fc0aabbfe6503230b31de5f24 @@ -0,0 +1 @@ +d f(t*a){a[a[3]]} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/fuzz-crash/crash-15d71125ba17344f02417a9d46443cdaa30aa17f b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-15d71125ba17344f02417a9d46443cdaa30aa17f new file mode 100644 index 00000000..5d268fdf --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-15d71125ba17344f02417a9d46443cdaa30aa17f @@ -0,0 +1 @@ +t m(){int a[]i for(;;)} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/fuzz-crash/crash-19219d7e7dfe8202248cd22229cdd9a2fd87a78a b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-19219d7e7dfe8202248cd22229cdd9a2fd87a78a new file mode 100644 index 00000000..b0042052 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-19219d7e7dfe8202248cd22229cdd9a2fd87a78a @@ -0,0 +1 @@ +n::template u \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/fuzz-crash/crash-20efd8856e04ca1fced9daa93837ae3384bb5e62 b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-20efd8856e04ca1fced9daa93837ae3384bb5e62 new file mode 100644 index 00000000..bc4d567d --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-20efd8856e04ca1fced9daa93837ae3384bb5e62 @@ -0,0 +1 @@ +assert({:=;}) \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/fuzz-crash/crash-2490dbc1880f2d7883c1b634deee8da3805186f8 b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-2490dbc1880f2d7883c1b634deee8da3805186f8 new file mode 100644 index 00000000..60a55bed --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-2490dbc1880f2d7883c1b634deee8da3805186f8 @@ -0,0 +1 @@ +i a;m t(=a[]);m e(){a[]=0} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/fuzz-crash/crash-26edfe9761d3b681c841dfe80398847dee332f83 b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-26edfe9761d3b681c841dfe80398847dee332f83 new file mode 100644 index 00000000..ca4faea1 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-26edfe9761d3b681c841dfe80398847dee332f83 @@ -0,0 +1 @@ +[]>a{} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/fuzz-crash/crash-9ef938bba7d752386e24f2438c73cec66f6b972b b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-9ef938bba7d752386e24f2438c73cec66f6b972b new file mode 100644 index 00000000..57d23881 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-9ef938bba7d752386e24f2438c73cec66f6b972b @@ -0,0 +1 @@ +o n(){r<>items;t iter;for(&&){iter=items.g}} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/fuzz-crash/crash-d6609399a4398aed92f5ac9e53aa0554d9f8bbd6 b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-d6609399a4398aed92f5ac9e53aa0554d9f8bbd6 new file mode 100644 index 00000000..1563d4c9 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-d6609399a4398aed92f5ac9e53aa0554d9f8bbd6 @@ -0,0 +1 @@ +_(){w((char)=e)} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/fuzz-crash/crash-e000709d155e9c993795748ba31fddacbd5a86ac b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-e000709d155e9c993795748ba31fddacbd5a86ac new file mode 100644 index 00000000..6f94840a --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-e000709d155e9c993795748ba31fddacbd5a86ac @@ -0,0 +1 @@ +{for(()s)} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/fuzz-crash/crash-e4a26f2d7d0a73836bf086f54e48204d8914b95a b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-e4a26f2d7d0a73836bf086f54e48204d8914b95a new file mode 100644 index 00000000..ba39dbb8 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-e4a26f2d7d0a73836bf086f54e48204d8914b95a @@ -0,0 +1 @@ +t i(){for(;;f&++)} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/fuzz-crash/crash-f4ec019b9a1f357d036a9bc3c2cb6fb10a0c3ded b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-f4ec019b9a1f357d036a9bc3c2cb6fb10a0c3ded new file mode 100644 index 00000000..7f948841 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz-crash/crash-f4ec019b9a1f357d036a9bc3c2cb6fb10a0c3ded @@ -0,0 +1 @@ +o k(){t*data;{memcpy(data,,sizeof\)}} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/fuzz-crash/leak-9543188fae87abc4409106e1918d6424efe0d68a b/cppcheck-2.14.0/test/cli/fuzz-crash/leak-9543188fae87abc4409106e1918d6424efe0d68a new file mode 100644 index 00000000..58448756 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz-crash/leak-9543188fae87abc4409106e1918d6424efe0d68a @@ -0,0 +1 @@ +v f(*){e=t&&} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/fuzz-timeout/minimized-from-179e5b42a7b0ba41a088e7972bdb13dde18ef4d1 b/cppcheck-2.14.0/test/cli/fuzz-timeout/minimized-from-179e5b42a7b0ba41a088e7972bdb13dde18ef4d1 new file mode 100644 index 00000000..9118d58d --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz-timeout/minimized-from-179e5b42a7b0ba41a088e7972bdb13dde18ef4d1 @@ -0,0 +1 @@ +d f(*){*:b&&} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/fuzz-timeout/timeout-0ee5eed9abd34e9d23640a5b82dd724affd05b79 b/cppcheck-2.14.0/test/cli/fuzz-timeout/timeout-0ee5eed9abd34e9d23640a5b82dd724affd05b79 new file mode 100644 index 00000000..5f9465da --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz-timeout/timeout-0ee5eed9abd34e9d23640a5b82dd724affd05b79 @@ -0,0 +1 @@ +;new t() \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/fuzz-timeout/timeout-9598595ae3c480b58773e85cd4e4d52b1dc37038 b/cppcheck-2.14.0/test/cli/fuzz-timeout/timeout-9598595ae3c480b58773e85cd4e4d52b1dc37038 new file mode 100644 index 00000000..1fb47f83 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz-timeout/timeout-9598595ae3c480b58773e85cd4e4d52b1dc37038 @@ -0,0 +1 @@ +i f(n*a){n b=0;*a=b;%*a=b;--------------------b} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/fuzz-timeout/timeout-a0b9848dd6e98677a0a96c5fc50ad571ed5a7092 b/cppcheck-2.14.0/test/cli/fuzz-timeout/timeout-a0b9848dd6e98677a0a96c5fc50ad571ed5a7092 new file mode 100644 index 00000000..2f29da2e --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz-timeout/timeout-a0b9848dd6e98677a0a96c5fc50ad571ed5a7092 @@ -0,0 +1,2 @@ +t i(){int +t,?:0::::} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/fuzz_test.py b/cppcheck-2.14.0/test/cli/fuzz_test.py new file mode 100644 index 00000000..fe9b65d5 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/fuzz_test.py @@ -0,0 +1,34 @@ +import os +import subprocess + +from testutils import cppcheck + +__script_dir = os.path.dirname(os.path.abspath(__file__)) + + +def test_fuzz_crash(): + failures = {} + + fuzz_crash_dir = os.path.join(__script_dir, 'fuzz-crash') + for f in os.listdir(fuzz_crash_dir): + ret, stdout, _ = cppcheck(['-q', '--language=c++', '--enable=all', '--inconclusive', f], cwd=fuzz_crash_dir) + if ret != 0: + failures[f] = stdout + + assert failures == {} + + +def test_fuzz_timeout(): + failures = [] + + fuzz_timeout_dir = os.path.join(__script_dir, 'fuzz-timeout') + # TODO: remove check if we have test data + if not os.path.exists(fuzz_timeout_dir): + return + for f in os.listdir(fuzz_timeout_dir): + try: + ret, stdout, _ = cppcheck(['-q', '--language=c++', '--enable=all', '--inconclusive', f], cwd=fuzz_timeout_dir, timeout=5) + except subprocess.TimeoutExpired: + failures.append(f) + + assert failures == [] diff --git a/cppcheck-2.14.0/test/cli/helloworld/helloworld.cppcheck b/cppcheck-2.14.0/test/cli/helloworld/helloworld.cppcheck new file mode 100644 index 00000000..6e4455d2 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/helloworld/helloworld.cppcheck @@ -0,0 +1,6 @@ + + + + helloworld.sln + false + diff --git a/cppcheck-2.14.0/test/cli/helloworld/helloworld.sln b/cppcheck-2.14.0/test/cli/helloworld/helloworld.sln new file mode 100644 index 00000000..0ac036b3 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/helloworld/helloworld.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2027 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "helloworld", "helloworld.vcxproj", "{7319858B-261C-4F0D-B022-92BB896242DD}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7319858B-261C-4F0D-B022-92BB896242DD}.Debug|x64.ActiveCfg = Debug|x64 + {7319858B-261C-4F0D-B022-92BB896242DD}.Debug|x64.Build.0 = Debug|x64 + {7319858B-261C-4F0D-B022-92BB896242DD}.Debug|x86.ActiveCfg = Debug|Win32 + {7319858B-261C-4F0D-B022-92BB896242DD}.Debug|x86.Build.0 = Debug|Win32 + {7319858B-261C-4F0D-B022-92BB896242DD}.Release|x64.ActiveCfg = Release|x64 + {7319858B-261C-4F0D-B022-92BB896242DD}.Release|x64.Build.0 = Release|x64 + {7319858B-261C-4F0D-B022-92BB896242DD}.Release|x86.ActiveCfg = Release|Win32 + {7319858B-261C-4F0D-B022-92BB896242DD}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C787A78A-D377-4103-9389-2594C573ED55} + EndGlobalSection +EndGlobal diff --git a/cppcheck-2.14.0/test/cli/helloworld/helloworld.vcxproj b/cppcheck-2.14.0/test/cli/helloworld/helloworld.vcxproj new file mode 100644 index 00000000..170e34ef --- /dev/null +++ b/cppcheck-2.14.0/test/cli/helloworld/helloworld.vcxproj @@ -0,0 +1,123 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {7319858B-261C-4F0D-B022-92BB896242DD} + helloworld + 10.0.16299.0 + + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + true + + + + + Level3 + Disabled + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + + + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/helloworld/main.c b/cppcheck-2.14.0/test/cli/helloworld/main.c new file mode 100644 index 00000000..c5b94db7 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/helloworld/main.c @@ -0,0 +1,11 @@ +#include + +int main(void) { + (void)printf("Hello world!\n"); + int x = 3 / 0; (void)x; // ERROR + return 0; +} + +#ifdef SOME_CONFIG +void foo(); +#endif diff --git a/cppcheck-2.14.0/test/cli/helloworld_test.py b/cppcheck-2.14.0/test/cli/helloworld_test.py new file mode 100644 index 00000000..94f13602 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/helloworld_test.py @@ -0,0 +1,230 @@ + +# python -m pytest test-helloworld.py + +import os +import re +import tempfile +import pytest +import glob + +from testutils import create_gui_project_file, cppcheck + +# Run Cppcheck from project path +def cppcheck_local(args): + cwd = os.getcwd() + os.chdir('helloworld') + ret, stdout, stderr = cppcheck(args) + os.chdir(cwd) + return ret, stdout, stderr + +def getRelativeProjectPath(): + return 'helloworld' + +def getAbsoluteProjectPath(): + return os.path.join(os.getcwd(), 'helloworld') + +# Get Visual Studio configurations checking a file +# Checking {file} {config}... +def getVsConfigs(stdout, filename): + ret = [] + for line in stdout.split('\n'): + if not line.startswith('Checking %s ' % filename): + continue + if not line.endswith('...'): + continue + res = re.match(r'.* ([A-Za-z0-9|]+)...', line) + if res: + ret.append(res.group(1)) + ret.sort() + return ' '.join(ret) + +def test_relative_path(): + ret, stdout, stderr = cppcheck(['--template=cppcheck1', 'helloworld']) + filename = os.path.join('helloworld', 'main.c') + assert ret == 0, stdout + assert stderr == '[%s:5]: (error) Division by zero.\n' % filename + + +def test_local_path(): + ret, stdout, stderr = cppcheck_local(['--template=cppcheck1', '.']) + assert ret == 0, stdout + assert stderr == '[main.c:5]: (error) Division by zero.\n' + +def test_absolute_path(): + prjpath = getAbsoluteProjectPath() + ret, stdout, stderr = cppcheck(['--template=cppcheck1', prjpath]) + filename = os.path.join(prjpath, 'main.c') + assert ret == 0, stdout + assert stderr == '[%s:5]: (error) Division by zero.\n' % filename + +def test_addon_local_path(): + ret, stdout, stderr = cppcheck_local(['--addon=misra', '--enable=style', '--template=cppcheck1', '.']) + assert ret == 0, stdout + assert stderr == ('[main.c:5]: (error) Division by zero.\n' + '[main.c:1]: (style) misra violation (use --rule-texts= to get proper output)\n') + +def test_addon_local_path_not_enable(): + ret, stdout, stderr = cppcheck_local(['--addon=misra', '--template=cppcheck1', '.']) + assert ret == 0, stdout + assert stderr == '[main.c:5]: (error) Division by zero.\n' + +def test_addon_absolute_path(): + prjpath = getAbsoluteProjectPath() + ret, stdout, stderr = cppcheck(['--addon=misra', '--enable=style', '--template=cppcheck1', prjpath]) + filename = os.path.join(prjpath, 'main.c') + assert ret == 0, stdout + assert stderr == ('[%s:5]: (error) Division by zero.\n' + '[%s:1]: (style) misra violation (use --rule-texts= to get proper output)\n' % (filename, filename)) + +def test_addon_relative_path(): + prjpath = getRelativeProjectPath() + ret, stdout, stderr = cppcheck(['--addon=misra', '--enable=style', '--template=cppcheck1', prjpath]) + filename = os.path.join(prjpath, 'main.c') + assert ret == 0, stdout + assert stdout == ('Checking %s ...\n' + 'Checking %s: SOME_CONFIG...\n' % (filename, filename)) + assert stderr == ('[%s:5]: (error) Division by zero.\n' + '[%s:1]: (style) misra violation (use --rule-texts= to get proper output)\n' % (filename, filename)) + +def test_addon_with_gui_project(): + project_file = 'helloworld/test.cppcheck' + create_gui_project_file(project_file, paths=['.'], addon='misra') + ret, stdout, stderr = cppcheck(['--template=cppcheck1', '--enable=style', '--project=' + project_file]) + filename = os.path.join('helloworld', 'main.c') + assert ret == 0, stdout + assert stdout == 'Checking %s ...\n' % filename + assert stderr == ('[%s:5]: (error) Division by zero.\n' + '[%s:1]: (style) misra violation (use --rule-texts= to get proper output)\n' % (filename, filename)) + +def test_basepath_relative_path(): + prjpath = getRelativeProjectPath() + ret, stdout, stderr = cppcheck([prjpath, '--template=cppcheck1', '-rp=' + prjpath]) + assert ret == 0, stdout + assert stderr == '[main.c:5]: (error) Division by zero.\n' + +def test_basepath_absolute_path(): + prjpath = getAbsoluteProjectPath() + ret, stdout, stderr = cppcheck(['--template=cppcheck1', prjpath, '-rp=' + prjpath]) + assert ret == 0, stdout + assert stderr == '[main.c:5]: (error) Division by zero.\n' + +def test_vs_project_local_path(): + ret, stdout, stderr = cppcheck_local(['--template=cppcheck1', '--project=helloworld.vcxproj']) + assert ret == 0, stdout + assert getVsConfigs(stdout, 'main.c') == 'Debug|Win32 Debug|x64 Release|Win32 Release|x64' + assert stderr == '[main.c:5]: (error) Division by zero.\n' + +def test_vs_project_relative_path(): + prjpath = getRelativeProjectPath() + ret, stdout, stderr = cppcheck(['--template=cppcheck1', '--project=' + os.path.join(prjpath, 'helloworld.vcxproj')]) + filename = os.path.join(prjpath, 'main.c') + assert ret == 0, stdout + assert getVsConfigs(stdout, filename) == 'Debug|Win32 Debug|x64 Release|Win32 Release|x64' + assert stderr == '[%s:5]: (error) Division by zero.\n' % filename + +def test_vs_project_absolute_path(): + prjpath = getAbsoluteProjectPath() + ret, stdout, stderr = cppcheck(['--template=cppcheck1', '--project=' + os.path.join(prjpath, 'helloworld.vcxproj')]) + filename = os.path.join(prjpath, 'main.c') + assert ret == 0, stdout + assert getVsConfigs(stdout, filename) == 'Debug|Win32 Debug|x64 Release|Win32 Release|x64' + assert stderr == '[%s:5]: (error) Division by zero.\n' % filename + +def test_cppcheck_project_local_path(): + ret, stdout, stderr = cppcheck_local(['--template=cppcheck1', '--platform=win64', '--project=helloworld.cppcheck']) + assert ret == 0, stdout + assert getVsConfigs(stdout, 'main.c') == 'Debug|x64' + assert stderr == '[main.c:5]: (error) Division by zero.\n' + +def test_cppcheck_project_relative_path(): + prjpath = getRelativeProjectPath() + ret, stdout, stderr = cppcheck(['--template=cppcheck1', '--platform=win64', '--project=' + os.path.join(prjpath, 'helloworld.cppcheck')]) + filename = os.path.join(prjpath, 'main.c') + assert ret == 0, stdout + assert getVsConfigs(stdout, filename) == 'Debug|x64' + assert stderr == '[%s:5]: (error) Division by zero.\n' % filename + +def test_cppcheck_project_absolute_path(): + prjpath = getAbsoluteProjectPath() + ret, stdout, stderr = cppcheck(['--template=cppcheck1', '--platform=win64', '--project=' + os.path.join(prjpath, 'helloworld.cppcheck')]) + filename = os.path.join(prjpath, 'main.c') + assert ret == 0, stdout + assert getVsConfigs(stdout, filename) == 'Debug|x64' + assert stderr == '[%s:5]: (error) Division by zero.\n' % filename + +def test_suppress_command_line(): + prjpath = getRelativeProjectPath() + ret, stdout, stderr = cppcheck(['--suppress=zerodiv:' + os.path.join(prjpath, 'main.c'), prjpath]) + assert ret == 0, stdout + assert stderr == '' + + prjpath = getAbsoluteProjectPath() + ret, stdout, stderr = cppcheck(['--suppress=zerodiv:' + os.path.join(prjpath, 'main.c'), prjpath]) + assert ret == 0, stdout + assert stderr == '' + +def test_suppress_project(): + project_file = os.path.join('helloworld', 'test.cppcheck') + create_gui_project_file(project_file, + paths=['.'], + suppressions=[{'fileName':'main.c', 'id':'zerodiv'}]) + + # Relative path + ret, stdout, stderr = cppcheck(['--project=' + project_file]) + assert ret == 0, stdout + assert stderr == '' + + # Absolute path + ret, stdout, stderr = cppcheck(['--project=' + os.path.join(os.getcwd(), 'helloworld', 'test.cppcheck')]) + assert ret == 0, stdout + assert stderr == '' + + +def test_exclude(): + prjpath = getRelativeProjectPath() + ret, stdout, _ = cppcheck(['-i' + prjpath, '--platform=win64', '--project=' + os.path.join(prjpath, 'helloworld.cppcheck')]) + assert ret == 1 + lines = stdout.splitlines() + assert lines == [ + 'cppcheck: error: no C or C++ source files found.', + 'cppcheck: all paths were ignored' + ] + + +def test_build_dir_dump_output(): + with tempfile.TemporaryDirectory() as tempdir: + args = f'--cppcheck-build-dir={tempdir} --addon=misra helloworld' + + cppcheck(args.split()) + cppcheck(args.split()) + + filename = f'{tempdir}/main.a1.*.dump' + filelist = glob.glob(filename) + assert(len(filelist) == 0) + + +def test_checkers_report(): + with tempfile.TemporaryDirectory() as tempdir: + filename = os.path.join(tempdir, '1.txt') + args = f'--checkers-report={filename} helloworld' + + cppcheck(args.split()) + + with open(filename, 'rt') as f: + data = f.read() + assert 'No CheckAutoVariables::assignFunctionArg' in data + assert 'Yes CheckAutoVariables::autoVariables' in data + + args = '--enable=style ' + args + cppcheck(args.split()) + with open(filename, 'rt') as f: + data = f.read() + # checker has been activated by --enable=style + assert 'Yes CheckAutoVariables::assignFunctionArg' in data + + +def test_missing_include_system(): # #11283 + args = ['--enable=missingInclude', '--suppress=zerodiv', '--template=simple', 'helloworld'] + + _, _, stderr = cppcheck(args) + assert stderr.replace('\\', '/') == 'helloworld/main.c:1:0: information: Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results. [missingIncludeSystem]\n' diff --git a/cppcheck-2.14.0/test/cli/inline-suppress_test.py b/cppcheck-2.14.0/test/cli/inline-suppress_test.py new file mode 100644 index 00000000..144ba717 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/inline-suppress_test.py @@ -0,0 +1,284 @@ + +# python -m pytest test-inline-suppress.py + +import json +import os +import pytest +from testutils import cppcheck + +__script_dir = os.path.dirname(os.path.abspath(__file__)) +__proj_inline_suppres_path = 'proj-inline-suppress' + os.path.sep + + +def __create_unused_function_compile_commands(tmpdir): + prjpath = os.path.realpath(os.path.join(__script_dir, 'proj-inline-suppress-unusedFunction')) + j = [{'directory': prjpath, + 'command': '/usr/bin/c++ -I"' + prjpath + '" -o "' + os.path.join(prjpath, 'B.cpp.o') + '" -c "' + os.path.join(prjpath, 'B.cpp') + '"', + 'file': os.path.join(prjpath, 'B.cpp')}, + {'directory': prjpath, + 'command': '/usr/bin/c++ -I"' + prjpath + '" -o "' + os.path.join(prjpath, 'A.cpp.o') + '" -c "' + os.path.join(prjpath, 'A.cpp') + '"', + 'file': os.path.join(prjpath, 'A.cpp')}] + compdb_path = os.path.join(tmpdir, 'proj-inline-suppress-unusedFunction') + os.makedirs(compdb_path) + compile_commands = os.path.join(compdb_path, 'compile_commands.json') + with open(compile_commands, 'wt') as f: + f.write(json.dumps(j, indent=4)) + return compile_commands + + +def test1(): + args = [ + '-q', + '--template=simple', + '--inline-suppr', + 'proj-inline-suppress' + ] + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + assert stderr == '' + assert stdout == '' + assert ret == 0, stdout + + +def test2(): + args = [ + '-q', + '--template=simple', + 'proj-inline-suppress' + ] + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + lines = stderr.splitlines() + assert lines == [ + '{}3.cpp:4:19: error: Division by zero. [zerodiv]'.format(__proj_inline_suppres_path) + ] + assert stdout == '' + assert ret == 0, stdout + + +def test_unmatched_suppression(): + args = [ + '-q', + '--template=simple', + '--inline-suppr', + '--enable=information', + '--disable=missingInclude', + '--error-exitcode=1', + '{}2.c'.format(__proj_inline_suppres_path) + ] + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + lines = stderr.splitlines() + assert lines == [ + '{}2.c:2:0: information: Unmatched suppression: some_warning_id [unmatchedSuppression]'.format(__proj_inline_suppres_path) + ] + assert stdout == '' + assert ret == 1, stdout + + +def test_unmatched_suppression_path_with_extra_stuff(): + args = [ + '-q', + '--template=simple', + '--inline-suppr', + '--enable=information', + '--disable=missingInclude', + '--error-exitcode=1', + '{}2.c'.format(__proj_inline_suppres_path) + ] + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + lines = stderr.splitlines() + assert lines == [ + '{}2.c:2:0: information: Unmatched suppression: some_warning_id [unmatchedSuppression]'.format(__proj_inline_suppres_path) + ] + assert stdout == '' + assert ret == 1, stdout + + +def test_backwards_compatibility(): + args = [ + '-q', + '--template=simple', + '{}3.cpp'.format(__proj_inline_suppres_path) + ] + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + lines = stderr.splitlines() + assert lines == [ + '{}3.cpp:4:19: error: Division by zero. [zerodiv]'.format(__proj_inline_suppres_path) + ] + assert stdout == '' + assert ret == 0, stdout + + args = [ + '-q', + '--template=simple', + '--inline-suppr', + '{}3.cpp'.format(__proj_inline_suppres_path) + ] + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + lines = stderr.splitlines() + assert lines == [] + assert stdout == '' + assert ret == 0, stdout + + +def __test_compile_commands_unused_function(tmpdir, use_j): + compdb_file = __create_unused_function_compile_commands(tmpdir) + args = [ + '-q', + '--template=simple', + '--enable=all', + '--error-exitcode=1', + '--project={}'.format(compdb_file) + ] + if use_j: + args.append('-j2') + else: + args.append('-j1') + ret, stdout, stderr = cppcheck(args) + proj_path_sep = os.path.join(__script_dir, 'proj-inline-suppress-unusedFunction') + os.path.sep + lines = stderr.splitlines() + assert lines == [ + "{}B.cpp:6:0: style: The function 'unusedFunctionTest' is never used. [unusedFunction]".format(proj_path_sep) + ] + assert stdout == '' + assert ret == 1, stdout + + +def test_compile_commands_unused_function(tmpdir): + __test_compile_commands_unused_function(tmpdir, False) + + +@pytest.mark.skip # unusedFunction does not work with -j +def test_compile_commands_unused_function_j(tmpdir): + __test_compile_commands_unused_function(tmpdir, True) + + +def __test_compile_commands_unused_function_suppression(tmpdir, use_j): + compdb_file = __create_unused_function_compile_commands(tmpdir) + args = [ + '-q', + '--template=simple', + '--enable=all', + '--inline-suppr', + '--error-exitcode=1', + '--project={}'.format(compdb_file) + ] + if use_j: + args.append('-j2') + else: + args.append('-j1') + ret, stdout, stderr = cppcheck(args) + lines = stderr.splitlines() + assert lines == [] + assert stdout == '' + assert ret == 0, stdout + + +def test_compile_commands_unused_function_suppression(tmpdir): + __test_compile_commands_unused_function_suppression(tmpdir, False) + + +@pytest.mark.skip # unusedFunction does not work with -j +def test_compile_commands_unused_function_suppression_j(tmpdir): + __test_compile_commands_unused_function_suppression(tmpdir, True) + + +def test_unmatched_suppression_ifdef(): + args = [ + '-q', + '--template=simple', + '--enable=information', + '--disable=missingInclude', + '--inline-suppr', + '-DNO_ZERO_DIV', + 'trac5704/trac5704a.c' + ] + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + lines = stderr.splitlines() + assert lines == [] + assert stdout == '' + assert ret == 0, stdout + + +def test_unmatched_suppression_ifdef_0(): + args = [ + '-q', + '--template=simple', + '--enable=information', + '--disable=missingInclude', + '--inline-suppr', + 'trac5704/trac5704b.c' + ] + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + lines = stderr.splitlines() + assert lines == [] + assert stdout == '' + assert ret == 0, stdout + + +def test_build_dir(tmpdir): + args = [ + '-q', + '--template=simple', + '--cppcheck-build-dir={}'.format(tmpdir), + '--enable=all', + '--inline-suppr', + '{}4.c'.format(__proj_inline_suppres_path) + ] + + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + lines = stderr.splitlines() + assert lines == [] + assert stdout == '' + assert ret == 0, stdout + + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + lines = stderr.splitlines() + assert lines == [] + assert stdout == '' + assert ret == 0, stdout + + +def __test_build_dir_unused_template(tmpdir, use_j): + args = [ + '-q', + '--template=simple', + '--cppcheck-build-dir={}'.format(tmpdir), + '--enable=all', + '--inline-suppr', + '{}template.cpp'.format(__proj_inline_suppres_path) + ] + if use_j: + args.append('-j2') + else: + args.append('-j1') + + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + lines = stderr.splitlines() + assert lines == [] + assert stdout == '' + assert ret == 0, stdout + + +def test_build_dir_unused_template(tmpdir): + __test_build_dir_unused_template(tmpdir, False) + + +@pytest.mark.xfail(strict=True) +def test_build_dir_unused_template_j(tmpdir): + __test_build_dir_unused_template(tmpdir, True) + + +def test_suppress_unmatched_inline_suppression(): # 11172 + args = [ + '-q', + '--template=simple', + '--enable=information', + '--disable=missingInclude', + '--suppress=unmatchedSuppression', + '--inline-suppr', + '{}2.c'.format(__proj_inline_suppres_path) + ] + ret, stdout, stderr = cppcheck(args, cwd=__script_dir) + lines = stderr.splitlines() + assert lines == [] + assert stdout == '' + assert ret == 0, stdout diff --git a/cppcheck-2.14.0/test/cli/more-projects_test.py b/cppcheck-2.14.0/test/cli/more-projects_test.py new file mode 100644 index 00000000..c26bce8a --- /dev/null +++ b/cppcheck-2.14.0/test/cli/more-projects_test.py @@ -0,0 +1,639 @@ +# python -m pytest test-more-projects.py +import json +import os +import pytest +from testutils import cppcheck, assert_cppcheck + + +def test_project_force_U(tmpdir): + # 10018 + # -U does not work with compile_commands.json + with open(os.path.join(tmpdir, 'bug1.cpp'), 'wt') as f: + f.write(""" + int x = 123 / 0; + #ifdef MACRO1 + int y = 1000 / 0; + #endif + """) + + compile_commands = os.path.join(tmpdir, 'compile_commands.json') + + compilation_db = [ + {"directory": str(tmpdir), + "command": "c++ -o bug1.o -c bug1.cpp", + "file": "bug1.cpp", + "output": "bug1.o"} + ] + + with open(compile_commands, 'wt') as f: + f.write(json.dumps(compilation_db)) + + # Without -U => both bugs are found + ret, stdout, stderr = cppcheck(['--project=' + compile_commands, '--force', '-rp=' + str(tmpdir), '--template=cppcheck1']) + assert ret == 0, stdout + assert (stderr == '[bug1.cpp:2]: (error) Division by zero.\n' + '[bug1.cpp:4]: (error) Division by zero.\n') + + # With -U => only first bug is found + ret, stdout, stderr = cppcheck(['--project=' + compile_commands, '--force', '-UMACRO1', '-rp=' + str(tmpdir), '--template=cppcheck1']) + assert ret == 0, stdout + assert stderr == '[bug1.cpp:2]: (error) Division by zero.\n' + + +def __write_cppcheck_project_file(tmpdir, platform=None, importproject=None): + project_file = os.path.join(tmpdir, 'Project.cppcheck') + + if platform is not None: + platform = '{}'.format(platform) + if importproject is not None: + platform = '{}'.format(importproject) + + with open(project_file, 'wt') as f: + f.write( +""" + + {} + {} + + + +""".format(platform, importproject) + ) + + return project_file + + +def test_project_custom_platform(tmpdir): + """ + import cppcheck project that contains a custom platform file + """ + project_file = __write_cppcheck_project_file(tmpdir, platform='p1.xml') + + with open(os.path.join(tmpdir, 'p1.xml'), 'wt') as f: + f.write('\n') + + with open(os.path.join(tmpdir, '1.c'), 'wt') as f: + f.write("int x;") + + ret, stdout, stderr = cppcheck(['--project=' + project_file, '--template=cppcheck1', '-q']) + assert ret == 0, stdout + assert stdout == '' + assert stderr == '' + + +def test_project_empty_platform(tmpdir): + """ + import cppcheck project that contains an empty platform type + """ + project_file = __write_cppcheck_project_file(tmpdir, platform='') + + with open(os.path.join(tmpdir, '1.c'), 'wt') as f: + f.write("int x;") + + ret, stdout, stderr = cppcheck(['--project=' + project_file, '--template=cppcheck1', '-q']) + assert ret == 0, stdout + assert stdout == '' + assert stderr == '' + + +def test_project_unspecified_platform(tmpdir): + """ + import cppcheck project that contains the deprecated platform type "Unspecified" + """ + project_file = __write_cppcheck_project_file(tmpdir, platform='Unspecified') + + with open(os.path.join(tmpdir, '1.c'), 'wt') as f: + f.write("int x;") + + ret, stdout, stderr = cppcheck(['--project=' + project_file, '--template=cppcheck1', '-q']) + assert ret == 1, stdout + assert stdout == "cppcheck: error: unrecognized platform: 'Unspecified'.\n" + assert stderr == '' + + +def test_project_unknown_platform(tmpdir): + """ + import cppcheck project that contains an unknown platform type + """ + project_file = __write_cppcheck_project_file(tmpdir, platform='dummy') + + with open(os.path.join(tmpdir, '1.c'), 'wt') as f: + f.write("int x;") + + ret, stdout, stderr = cppcheck(['--project=' + project_file, '--template=cppcheck1']) + assert ret == 1, stdout + assert stdout == "cppcheck: error: unrecognized platform: 'dummy'.\n" + assert stderr == '' + + +def test_project_empty_fields(tmpdir): + """ + import cppcheck project that contains all empty fields - make sure there are no crashes + """ + project_file = os.path.join(tmpdir, 'Project.cppcheck') + + with open(project_file, 'wt') as f: + f.write( +""" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +""") + + ret, stdout, stderr = cppcheck(['--project=' + project_file, '--template=cppcheck1']) + assert ret == 1, stdout # do not crash + assert stdout == 'cppcheck: error: no C or C++ source files found.\n' + assert stderr == '' + + +def test_project_missing_subproject(tmpdir): + """ + import cppcheck project that contains an unknown platform type + """ + project_file = __write_cppcheck_project_file(tmpdir, importproject='dummy.json') + + ret, stdout, stderr = cppcheck(['--project=' + project_file, '--template=cppcheck1']) + assert ret == 1, stdout + assert stdout == "cppcheck: error: failed to open project '{}/dummy.json'. The file does not exist.\n".format(str(tmpdir).replace('\\', '/')) + assert stderr == '' + + +def test_project_std(tmpdir): + with open(os.path.join(tmpdir, 'bug1.cpp'), 'wt') as f: + f.write(""" + #if __cplusplus == 201402L + int x = 123 / 0; + #endif + """) + + compile_commands = os.path.join(tmpdir, 'compile_commands.json') + + compilation_db = [ + { + "directory": str(tmpdir), + "command": "c++ -o bug1.o -c bug1.cpp -std=c++14", + "file": "bug1.cpp", + "output": "bug1.o" + } + ] + + with open(compile_commands, 'wt') as f: + f.write(json.dumps(compilation_db)) + + ret, stdout, stderr = cppcheck(['--project=' + compile_commands, '--enable=all', '-rp=' + str(tmpdir), '--template=cppcheck1']) + assert ret == 0, stdout + assert (stderr == '[bug1.cpp:3]: (error) Division by zero.\n') + + + +@pytest.mark.skip() # clang-tidy is not available in all cases +def test_clang_tidy(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + f.write(""" + int main(int argc) + { + (void)argc; + } + """) + + project_file = os.path.join(tmpdir, 'test.cppcheck') + with open(project_file, 'wt') as f: + f.write( + """ + + + + > + + clang-tidy + +""".format(test_file)) + + args = ['--project={}'.format(project_file)] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0, stdout + lines = stdout.splitlines() + # TODO: should detect clang-tidy issue + assert len(lines) == 1 + assert lines == [ + 'Checking {} ...'.format(test_file) + ] + assert stderr == '' + + +def test_project_file_filter(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + pass + + project_file = os.path.join(tmpdir, 'test.cppcheck') + with open(project_file, 'wt') as f: + f.write( + """ + + + + +""".format(test_file)) + + args = ['--file-filter=*.cpp', '--project={}'.format(project_file)] + out_lines = [ + 'Checking {} ...'.format(test_file) + ] + + assert_cppcheck(args, ec_exp=0, err_exp=[], out_exp=out_lines) + + +def test_project_file_filter_2(tmpdir): + test_file_1 = os.path.join(tmpdir, 'test.cpp') + with open(test_file_1, 'wt') as f: + pass + test_file_2 = os.path.join(tmpdir, 'test.c') + with open(test_file_2, 'wt') as f: + pass + + project_file = os.path.join(tmpdir, 'test.cppcheck') + with open(project_file, 'wt') as f: + f.write( + """ + + + + + +""".format(test_file_1, test_file_2)) + + args = ['--file-filter=*.cpp', '--project={}'.format(project_file)] + out_lines = [ + 'Checking {} ...'.format(test_file_1) + ] + + assert_cppcheck(args, ec_exp=0, err_exp=[], out_exp=out_lines) + + +def test_project_file_filter_3(tmpdir): + test_file_1 = os.path.join(tmpdir, 'test.cpp') + with open(test_file_1, 'wt') as f: + pass + test_file_2 = os.path.join(tmpdir, 'test.c') + with open(test_file_2, 'wt') as f: + pass + + project_file = os.path.join(tmpdir, 'test.cppcheck') + with open(project_file, 'wt') as f: + f.write( + """ + + + + + +""".format(test_file_1, test_file_2)) + + args = ['--file-filter=*.c', '--project={}'.format(project_file)] + out_lines = [ + 'Checking {} ...'.format(test_file_2) + ] + + assert_cppcheck(args, ec_exp=0, err_exp=[], out_exp=out_lines) + + +def test_project_file_filter_no_match(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + pass + + project_file = os.path.join(tmpdir, 'test.cppcheck') + with open(project_file, 'wt') as f: + f.write( + """ + + + + +""".format(test_file)) + + args = ['--file-filter=*.c', '--project={}'.format(project_file)] + out_lines = [ + 'cppcheck: error: could not find any files matching the filter.' + ] + + assert_cppcheck(args, ec_exp=1, err_exp=[], out_exp=out_lines) + + +def test_project_file_order(tmpdir): + test_file_a = os.path.join(tmpdir, 'a.c') + with open(test_file_a, 'wt'): + pass + test_file_b = os.path.join(tmpdir, 'b.c') + with open(test_file_b, 'wt'): + pass + test_file_c = os.path.join(tmpdir, 'c.c') + with open(test_file_c, 'wt'): + pass + test_file_d = os.path.join(tmpdir, 'd.c') + with open(test_file_d, 'wt'): + pass + + project_file = os.path.join(tmpdir, 'test.cppcheck') + with open(project_file, 'wt') as f: + f.write( + """ + + + + + + + +""".format(test_file_c, test_file_d, test_file_b, test_file_a)) + + args = ['--project={}'.format(project_file), '-j1'] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file_c), + '1/4 files checked 0% done', + 'Checking {} ...'.format(test_file_d), + '2/4 files checked 0% done', + 'Checking {} ...'.format(test_file_b), + '3/4 files checked 0% done', + 'Checking {} ...'.format(test_file_a), + '4/4 files checked 0% done' + ] + assert stderr == '' + + +def test_project_file_duplicate(tmpdir): + test_file_a = os.path.join(tmpdir, 'a.c') + with open(test_file_a, 'wt'): + pass + + project_file = os.path.join(tmpdir, 'test.cppcheck') + with open(project_file, 'wt') as f: + f.write( + """ + + + + + + +""".format(test_file_a, test_file_a, tmpdir)) + + args = ['--project={}'.format(project_file)] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file_a) + ] + assert stderr == '' + + +def test_project_file_duplicate_2(tmpdir): + test_file_a = os.path.join(tmpdir, 'a.c') + with open(test_file_a, 'wt'): + pass + test_file_b = os.path.join(tmpdir, 'b.c') + with open(test_file_b, 'wt'): + pass + test_file_c = os.path.join(tmpdir, 'c.c') + with open(test_file_c, 'wt'): + pass + + project_file = os.path.join(tmpdir, 'test.cppcheck') + with open(project_file, 'wt') as f: + f.write( + """ + + + + + + + + + + + +""".format(test_file_c, test_file_a, test_file_b, tmpdir, test_file_b, test_file_c, test_file_a, tmpdir)) + + args = ['--project={}'.format(project_file), '-j1'] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file_c), + '1/3 files checked 0% done', + 'Checking {} ...'.format(test_file_a), + '2/3 files checked 0% done', + 'Checking {} ...'.format(test_file_b), + '3/3 files checked 0% done' + ] + assert stderr == '' + + +def test_project_file_ignore(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + pass + + project_file = os.path.join(tmpdir, 'test.cppcheck') + with open(project_file, 'wt') as f: + f.write( + """ + + + + +""".format(test_file)) + + args = ['-itest.cpp', '--project={}'.format(project_file)] + out_lines = [ + 'cppcheck: error: could not find or open any of the paths given.', + 'cppcheck: Maybe all paths were ignored?' + ] + + assert_cppcheck(args, ec_exp=1, err_exp=[], out_exp=out_lines) + + +def test_project_file_ignore_2(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + pass + + project_file = os.path.join(tmpdir, 'test.cppcheck') + with open(project_file, 'wt') as f: + f.write( + """ + + + + + + + +""".format(test_file)) + + args = ['--project={}'.format(project_file)] + out_lines = [ + 'cppcheck: error: could not find or open any of the paths given.', + 'cppcheck: Maybe all paths were ignored?' + ] + + assert_cppcheck(args, ec_exp=1, err_exp=[], out_exp=out_lines) + + +def test_project_file_ignore_3(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + pass + + project_file = os.path.join(tmpdir, 'test.cppcheck') + with open(project_file, 'wt') as f: + f.write( + """ + + + + + + + +""".format(test_file)) + + args = ['--project={}'.format(project_file)] + out_lines = [ + 'cppcheck: error: could not find or open any of the paths given.', + 'cppcheck: Maybe all paths were ignored?' + ] + + assert_cppcheck(args, ec_exp=1, err_exp=[], out_exp=out_lines) + + +@pytest.mark.xfail +def test_json_file_ignore(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + pass + + compilation_db = [ + {"directory": str(tmpdir), + "command": "c++ -o bug1.o -c bug1.cpp", + "file": "test.cpp", + "output": "test.o"} + ] + + project_file = os.path.join(tmpdir, 'test.json') + with open(project_file, 'wt') as f: + f.write(json.dumps(compilation_db)) + + args = ['-itest.cpp', '--project={}'.format(project_file)] + out_lines = [ + 'cppcheck: error: no C or C++ source files found.', + 'cppcheck: all paths were ignored' + ] + + assert_cppcheck(args, ec_exp=1, err_exp=[], out_exp=out_lines) + + +def test_json_file_ignore_2(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + pass + + compilation_db = [ + {"directory": str(tmpdir), + "command": "c++ -o bug1.o -c bug1.cpp", + "file": "test.cpp", + "output": "test.o"} + ] + + project_file = os.path.join(tmpdir, 'test.json') + with open(project_file, 'wt') as f: + f.write(json.dumps(compilation_db)) + + args = ['-i{}'.format(test_file), '--project={}'.format(project_file)] + out_lines = [ + 'cppcheck: error: no C or C++ source files found.', + 'cppcheck: all paths were ignored' + ] + + assert_cppcheck(args, ec_exp=1, err_exp=[], out_exp=out_lines) \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/other_test.py b/cppcheck-2.14.0/test/cli/other_test.py new file mode 100644 index 00000000..1ac8e4a3 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/other_test.py @@ -0,0 +1,1360 @@ + +# python -m pytest test-other.py + +import os +import sys +import pytest +import json + +from testutils import cppcheck, assert_cppcheck + + +def test_missing_include(tmpdir): # #11283 + test_file = os.path.join(tmpdir, 'test.c') + with open(test_file, 'wt') as f: + f.write(""" + #include "test.h" + """) + + args = ['--enable=missingInclude', '--template=simple', test_file] + + _, _, stderr = cppcheck(args) + assert stderr == '{}:2:0: information: Include file: "test.h" not found. [missingInclude]\n'.format(test_file) + + +def __test_missing_include_check_config(tmpdir, use_j): + test_file = os.path.join(tmpdir, 'test.c') + with open(test_file, 'wt') as f: + f.write(""" + #include "test.h" + """) + + # TODO: -rp is not working requiring the full path in the assert + args = '--check-config -rp={} {}'.format(tmpdir, test_file) + if use_j: + args = '-j2 ' + args + + _, _, stderr = cppcheck(args.split()) + assert stderr == '' # --check-config no longer reports the missing includes + + +def test_missing_include_check_config(tmpdir): + __test_missing_include_check_config(tmpdir, False) + + +def test_missing_include_check_config_j(tmpdir): + __test_missing_include_check_config(tmpdir, True) + + +def test_missing_include_inline_suppr(tmpdir): + test_file = os.path.join(tmpdir, 'test.c') + with open(test_file, 'wt') as f: + f.write(""" + // cppcheck-suppress missingInclude + #include "missing.h" + // cppcheck-suppress missingIncludeSystem + #include + """) + + args = ['--enable=missingInclude', '--inline-suppr', test_file] + + _, _, stderr = cppcheck(args) + assert stderr == '' + + +def test_preprocessor_error(tmpdir): + test_file = os.path.join(tmpdir, '10866.c') + with open(test_file, 'wt') as f: + f.write('#error test\nx=1;\n') + exitcode, _, stderr = cppcheck(['--error-exitcode=1', test_file]) + assert 'preprocessorErrorDirective' in stderr + assert exitcode != 0 + + +def test_invalid_library(tmpdir): + args = ['--library=none', '--library=posix', '--library=none2', 'file.c'] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 1 + assert (stdout == "cppcheck: Failed to load library configuration file 'none'. File not found\n" + "cppcheck: Failed to load library configuration file 'none2'. File not found\n") + assert stderr == "" + + +def test_message_j(tmpdir): + test_file = os.path.join(tmpdir, 'test.c') + with open(test_file, 'wt') as f: + f.write("") + + args = ['-j2', test_file] + + _, stdout, _ = cppcheck(args) + assert stdout == "Checking {} ...\n".format(test_file) # we were adding stray \0 characters at the end + +# TODO: test missing std.cfg + + +def test_progress(tmpdir): + test_file = os.path.join(tmpdir, 'test.c') + with open(test_file, 'wt') as f: + f.write(""" + int main(int argc) + { + } + """) + + args = ['--report-progress=0', '--enable=all', '--inconclusive', '-j1', test_file] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + pos = stdout.find('\n') + assert(pos != -1) + pos += 1 + assert stdout[:pos] == "Checking {} ...\n".format(test_file) + assert (stdout[pos:] == + "progress: Tokenize (typedef) 0%\n" + "progress: Tokenize (typedef) 12%\n" + "progress: Tokenize (typedef) 25%\n" + "progress: Tokenize (typedef) 37%\n" + "progress: Tokenize (typedef) 50%\n" + "progress: Tokenize (typedef) 62%\n" + "progress: Tokenize (typedef) 75%\n" + "progress: Tokenize (typedef) 87%\n" + "progress: SymbolDatabase 0%\n" + "progress: SymbolDatabase 12%\n" + "progress: SymbolDatabase 87%\n" + ) + assert stderr == "" + + +def test_progress_j(tmpdir): + test_file = os.path.join(tmpdir, 'test.c') + with open(test_file, 'wt') as f: + f.write(""" + int main(int argc) + { + } + """) + + args = ['--report-progress=0', '--enable=all', '--inconclusive', '-j2', '--disable=unusedFunction', test_file] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + assert stdout == "Checking {} ...\n".format(test_file) + assert stderr == "" + + +def test_execute_addon_failure(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + f.write(""" + void f(); + """) + + args = ['--addon=naming', test_file] + + # provide empty PATH environment variable so python is not found and execution of addon fails + env = {'PATH': ''} + _, _, stderr = cppcheck(args, env) + assert stderr == '{}:0:0: error: Bailing out from analysis: Checking file failed: Failed to auto detect python [internalError]\n\n^\n'.format(test_file) + + +def test_execute_addon_failure_2(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + f.write(""" + void f(); + """) + + # specify non-existent python executbale so execution of addon fails + args = ['--addon=naming', '--addon-python=python5.x', test_file] + + _, _, stderr = cppcheck(args) + ec = 1 if os.name == 'nt' else 127 + assert stderr == "{}:0:0: error: Bailing out from analysis: Checking file failed: Failed to execute addon 'naming' - exitcode is {} [internalError]\n\n^\n".format(test_file, ec) + + +def test_execute_addon_file0(tmpdir): + test_file = os.path.join(tmpdir, 'test.c') + with open(test_file, 'wt') as f: + f.write('void foo() {}\n') + + args = ['--xml', '--addon=misra', '--enable=style', test_file] + + _, _, stderr = cppcheck(args) + assert 'misra-c2012-8.2' in stderr + assert '.dump' not in stderr + + +# TODO: find a test case which always fails +@pytest.mark.skip +def test_internal_error(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + f.write(""" +#include + +void f() { + double gc = 3333.3333; + char stat[80]; + sprintf(stat,"'%2.1f'",gc); +} + """) + + args = [test_file] + + _, _, stderr = cppcheck(args) + assert stderr == '{}:0:0: error: Bailing from out analysis: Checking file failed: converting \'1f\' to integer failed - not an integer [internalError]\n\n^\n'.format(test_file) + + +def test_addon_ctu_exitcode(tmpdir): + """ #12440 - Misra ctu violations found => exit code should be non-zero """ + test_file = os.path.join(tmpdir, 'test.c') + with open(test_file, 'wt') as f: + f.write("""typedef enum { BLOCK = 0x80U, } E;""") + args = ['--addon=misra', '--enable=style', '--error-exitcode=1', test_file] + exitcode, stdout, stderr = cppcheck(args) + assert '2.3' in stderr, stderr + assert exitcode == 1 + + +# TODO: test with -j2 +def test_addon_misra(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + f.write(""" +typedef int MISRA_5_6_VIOLATION; + """) + + args = ['--addon=misra', '--enable=all', '--disable=unusedFunction', '-j1', test_file] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file) + ] + assert stderr == '{}:2:1: style: misra violation (use --rule-texts= to get proper output) [misra-c2012-2.3]\ntypedef int MISRA_5_6_VIOLATION;\n^\n'.format(test_file) + + +def test_addon_y2038(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + # TODO: trigger warning + with open(test_file, 'wt') as f: + f.write(""" +extern void f() +{ + time_t t = std::time(nullptr); + (void)t; +} + """) + + args = ['--addon=y2038', '--enable=all', '--disable=unusedFunction', '--template=simple', test_file] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file) + ] + assert stderr == '{}:4:21: warning: time is Y2038-unsafe [y2038-unsafe-call]\n'.format(test_file) + + +def test_addon_threadsafety(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + f.write(""" +extern const char* f() +{ + return strerror(1); +} + """) + + args = ['--addon=threadsafety', '--enable=all', '--disable=unusedFunction', '--template=simple', test_file] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file) + ] + assert stderr == '{}:4:12: warning: strerror is MT-unsafe [threadsafety-unsafe-call]\n'.format(test_file) + + +def test_addon_naming(tmpdir): + # the addon does nothing without a config + addon_file = os.path.join(tmpdir, 'naming1.json') + with open(addon_file, 'wt') as f: + f.write(""" +{ + "script": "addons/naming.py", + "args": [ + "--var=[_a-z].*" + ] +} + """) + + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + f.write(""" +int Var; + """) + + args = ['--addon={}'.format(addon_file), '--enable=all', '--disable=unusedFunction', '--template=simple', test_file] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file) + ] + assert stderr == '{}:2:1: style: Variable Var violates naming convention [naming-varname]\n'.format(test_file) + + +def test_addon_namingng(tmpdir): + addon_file = os.path.join(tmpdir, 'namingng.json') + addon_config_file = os.path.join(tmpdir, 'namingng.config.json') + with open(addon_file, 'wt') as f: + f.write(""" +{ + "script": "addons/namingng.py", + "args": [ + "--configfile=%s" + ] +} + """%(addon_config_file).replace('\\','\\\\')) + + with open(addon_config_file, 'wt') as f: + f.write(""" +{ + "RE_FILE": [ + "[^/]*[a-z][a-z0-9_]*[a-z0-9]\\.c\\Z" + ], + "RE_CLASS_NAME": ["[a-z][a-z0-9_]*[a-z0-9]\\Z"], + "RE_NAMESPACE": ["[a-z][a-z0-9_]*[a-z0-9]\\Z"], + "RE_VARNAME": ["[a-z][a-z0-9_]*[a-z0-9]\\Z"], + "RE_PUBLIC_MEMBER_VARIABLE": ["[a-z][a-z0-9_]*[a-z0-9]\\Z"], + "RE_PRIVATE_MEMBER_VARIABLE": { + ".*_tmp\\Z":[true,"illegal suffix _tmp"], + "priv_.*\\Z":[false,"required prefix priv_ missing"] + }, + "RE_GLOBAL_VARNAME": ["[a-z][a-z0-9_]*[a-z0-9]\\Z"], + "RE_FUNCTIONNAME": ["[a-z][a-z0-9_]*[a-z0-9]\\Z"], + "include_guard": { + "input": "basename", + "prefix": "_", + "suffix": "", + "case": "upper", + "max_linenr": 5, + "RE_HEADERFILE": ".*\\.h\\Z", + "required": true + }, + "var_prefixes": {"uint32_t": "ui32"}, + "function_prefixes": {"uint16_t": "ui16", + "uint32_t": "ui32"}, + "skip_one_char_variables": false +} + """.replace('\\','\\\\')) + + test_unguarded_include_file_basename = 'test_unguarded.h' + test_unguarded_include_file = os.path.join(tmpdir, test_unguarded_include_file_basename) + with open(test_unguarded_include_file, 'wt') as f: + f.write(""" +void InvalidFunctionUnguarded(); +""") + + test_include_file_basename = '_test.h' + test_include_file = os.path.join(tmpdir, test_include_file_basename) + with open(test_include_file, 'wt') as f: + f.write(""" +#ifndef TEST_H +#define TEST_H + +void InvalidFunction(); +extern int _invalid_extern_global; + +#include "{}" + +#endif +""".format(test_unguarded_include_file)) + + test_file_basename = 'test_.cpp' + test_file = os.path.join(tmpdir, test_file_basename) + with open(test_file, 'wt') as f: + f.write(""" +#include "%s" + +void invalid_function_(); +void _invalid_function(); +void valid_function1(); +void valid_function2(int _invalid_arg); +void valid_function3(int invalid_arg_); +void valid_function4(int valid_arg32); +void valid_function5(uint32_t invalid_arg32); +void valid_function6(uint32_t ui32_valid_arg); +uint16_t invalid_function7(int valid_arg); +uint16_t ui16_valid_function8(int valid_arg); + +int _invalid_global; +static int _invalid_static_global; + +class _clz { +public: + _clz() : _invalid_public(0), _invalid_private(0), priv_good(0), priv_bad_tmp(0) { } + int _invalid_public; +private: + char _invalid_private; + int priv_good; + int priv_bad_tmp; +}; + +namespace _invalid_namespace { } + """%(test_include_file_basename)) + + args = ['--addon='+addon_file, '--verbose', '--enable=all', '--disable=unusedFunction', test_file] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file), + 'Defines:', + 'Undefines:', + 'Includes:', + 'Platform:native' + ] + lines = [line for line in stderr.splitlines() if line != ''] + expect = [ + '{}:0:0: style: File name {} violates naming convention [namingng-namingConvention]'.format(test_include_file,test_include_file_basename), + '^', + '{}:2:9: style: include guard naming violation; TEST_H != _TEST_H [namingng-includeGuardName]'.format(test_include_file), + '#ifndef TEST_H', + ' ^', + '{}:5:6: style: Function InvalidFunction violates naming convention [namingng-namingConvention]'.format(test_include_file), + 'void InvalidFunction();', + ' ^', + '{}:6:12: style: Global variable _invalid_extern_global violates naming convention [namingng-namingConvention]'.format(test_include_file), + 'extern int _invalid_extern_global;', + ' ^', + + '{}:0:0: style: File name {} violates naming convention [namingng-namingConvention]'.format(test_unguarded_include_file,test_unguarded_include_file_basename), + '^', + '{}:0:0: style: Missing include guard [namingng-includeGuardMissing]'.format(test_unguarded_include_file), + '^', + '{}:2:6: style: Function InvalidFunctionUnguarded violates naming convention [namingng-namingConvention]'.format(test_unguarded_include_file), + 'void InvalidFunctionUnguarded();', + ' ^', + + '{}:0:0: style: File name {} violates naming convention [namingng-namingConvention]'.format(test_file,test_file_basename), + '^', + '{}:7:26: style: Variable _invalid_arg violates naming convention [namingng-namingConvention]'.format(test_file), + 'void valid_function2(int _invalid_arg);', + ' ^', + '{}:8:26: style: Variable invalid_arg_ violates naming convention [namingng-namingConvention]'.format(test_file), + 'void valid_function3(int invalid_arg_);', + ' ^', + '{}:10:31: style: Variable invalid_arg32 violates naming convention [namingng-namingConvention]'.format(test_file), + 'void valid_function5(uint32_t invalid_arg32);', + ' ^', + '{}:4:6: style: Function invalid_function_ violates naming convention [namingng-namingConvention]'.format(test_file), + 'void invalid_function_();', + ' ^', + '{}:5:6: style: Function _invalid_function violates naming convention [namingng-namingConvention]'.format(test_file), + 'void _invalid_function();', + ' ^', + '{}:12:10: style: Function invalid_function7 violates naming convention [namingng-namingConvention]'.format(test_file), + 'uint16_t invalid_function7(int valid_arg);', + ' ^', + '{}:15:5: style: Global variable _invalid_global violates naming convention [namingng-namingConvention]'.format(test_file), + 'int _invalid_global;', + ' ^', + '{}:16:12: style: Global variable _invalid_static_global violates naming convention [namingng-namingConvention]'.format(test_file), + 'static int _invalid_static_global;', + ' ^', + '{}:20:5: style: Class Constructor _clz violates naming convention [namingng-namingConvention]'.format(test_file), + ' _clz() : _invalid_public(0), _invalid_private(0), priv_good(0), priv_bad_tmp(0) { }', + ' ^', + '{}:21:9: style: Public member variable _invalid_public violates naming convention [namingng-namingConvention]'.format(test_file), + ' int _invalid_public;', + ' ^', + '{}:23:10: style: Private member variable _invalid_private violates naming convention: required prefix priv_ missing [namingng-namingConvention]'.format(test_file), + ' char _invalid_private;', + ' ^', + '{}:25:9: style: Private member variable priv_bad_tmp violates naming convention: illegal suffix _tmp [namingng-namingConvention]'.format(test_file), + ' int priv_bad_tmp;', + ' ^', + '{}:28:11: style: Namespace _invalid_namespace violates naming convention [namingng-namingConvention]'.format(test_file), + 'namespace _invalid_namespace { }', + ' ^', + ] + # test sorted lines; the order of messages may vary and is not of importance + lines.sort() + expect.sort() + assert lines == expect + + +# TODO: test with -j2 +def test_addon_namingng_config(tmpdir): + addon_file = os.path.join(tmpdir, 'namingng.json') + addon_config_file = os.path.join(tmpdir, 'namingng.config.json') + with open(addon_file, 'wt') as f: + f.write(""" +{ + "script": "addons/namingng.py", + "args": [ + "--configfile=%s" + ] +} + """%(addon_config_file).replace('\\','\\\\')) + + with open(addon_config_file, 'wt') as f: + f.write(""" +{ + "RE_FILE": "[^/]*[a-z][a-z0-9_]*[a-z0-9]\\.c\\Z", + "RE_NAMESPACE": false, + "RE_VARNAME": ["+bad pattern","[a-z]_good_pattern\\Z","(parentheses?"], + "RE_PRIVATE_MEMBER_VARIABLE": "[a-z][a-z0-9_]*[a-z0-9]\\Z", + "RE_PUBLIC_MEMBER_VARIABLE": { + "tmp_.*\\Z":[true,"illegal prefix tmp_"], + "bad_.*\\Z":true, + "public_.*\\Z":[false], + "pub_.*\\Z":[0,"required prefix pub_ missing"] + }, + "RE_GLOBAL_VARNAME": "[a-z][a-z0-9_]*[a-z0-9]\\Z", + "RE_FUNCTIONNAME": "[a-z][a-z0-9_]*[a-z0-9]\\Z", + "RE_CLASS_NAME": "[a-z][a-z0-9_]*[a-z0-9]\\Z", + "_comment1": "these should all be arrays, or null, or not set", + + "include_guard": true, + "var_prefixes": ["bad"], + "function_prefixes": false, + "_comment2": "these should all be dict", + + "skip_one_char_variables": "false", + "_comment3": "this should be bool", + + "RE_VAR_NAME": "typo" +} + """.replace('\\','\\\\')) + + test_file_basename = 'test.c' + test_file = os.path.join(tmpdir, test_file_basename) + with open(test_file, 'a') as f: + # only create the file + pass + + args = ['--addon='+addon_file, '--verbose', '--enable=all', '-j1', test_file] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file), + 'Defines:', + 'Undefines:', + 'Includes:', + 'Platform:native' + ] + lines = stderr.splitlines() + # ignore the first line, stating that the addon failed to run properly + lines.pop(0) + assert lines == [ + "Output:", + "config error: RE_FILE must be list (not str), or not set", + "config error: RE_NAMESPACE must be list or dict (not bool), or not set", + "config error: include_guard must be dict (not bool), or not set", + "config error: item '+bad pattern' of 'RE_VARNAME' is not a valid regular expression: nothing to repeat at position 0", + "config error: item '(parentheses?' of 'RE_VARNAME' is not a valid regular expression: missing ), unterminated subpattern at position 0", + "config error: var_prefixes must be dict (not list), or not set", + "config error: RE_PRIVATE_MEMBER_VARIABLE must be list or dict (not str), or not set", + "config error: item 'bad_.*\\Z' of 'RE_PUBLIC_MEMBER_VARIABLE' must be an array [bool,string]", + "config error: item 'public_.*\\Z' of 'RE_PUBLIC_MEMBER_VARIABLE' must be an array [bool,string]", + "config error: item 'pub_.*\\Z' of 'RE_PUBLIC_MEMBER_VARIABLE' must be an array [bool,string]", + "config error: RE_GLOBAL_VARNAME must be list or dict (not str), or not set", + "config error: RE_FUNCTIONNAME must be list or dict (not str), or not set", + "config error: function_prefixes must be dict (not bool), or not set", + "config error: RE_CLASS_NAME must be list or dict (not str), or not set", + "config error: skip_one_char_variables must be bool (not str), or not set", + "config error: unknown config key 'RE_VAR_NAME' [internalError]", + "", + "^", + ] + + +def test_addon_findcasts(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + f.write(""" + extern void f(char c) + { + int i = (int)c; + (void)i; + } + """) + + args = ['--addon=findcasts', '--enable=all', '--disable=unusedFunction', '--template=simple', test_file] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file) + ] + assert stderr == '{}:4:21: information: found a cast [findcasts-cast]\n'.format(test_file) + + +def test_addon_misc(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + f.write(""" +extern void f() +{ + char char* [] = {"a" "b"} +} + """) + + args = ['--addon=misc', '--enable=all', '--disable=unusedFunction', '--template=simple', test_file] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file) + ] + assert stderr == '{}:4:26: style: String concatenation in array initialization, missing comma? [misc-stringConcatInArrayInit]\n'.format(test_file) + + +def test_invalid_addon_json(tmpdir): + addon_file = os.path.join(tmpdir, 'addon1.json') + with open(addon_file, 'wt') as f: + f.write(""" + """) + + test_file = os.path.join(tmpdir, 'file.cpp') + with open(test_file, 'wt'): + pass + + args = ['--addon={}'.format(addon_file), test_file] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 1 + lines = stdout.splitlines() + assert lines == [ + 'Loading {} failed. syntax error at line 2 near: '.format(addon_file) + ] + assert stderr == '' + + +def test_invalid_addon_py(tmpdir): + addon_file = os.path.join(tmpdir, 'addon1.py') + with open(addon_file, 'wt') as f: + f.write(""" +raise Exception() + """) + + test_file = os.path.join(tmpdir, 'file.cpp') + with open(test_file, 'wt') as f: + f.write(""" +typedef int MISRA_5_6_VIOLATION; + """) + + args = ['--addon={}'.format(addon_file), '--enable=all', '--disable=unusedFunction', test_file] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 # TODO: needs to be 1 + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file) + ] + assert stderr == "{}:0:0: error: Bailing out from analysis: Checking file failed: Failed to execute addon 'addon1' - exitcode is 1 [internalError]\n\n^\n".format(test_file) + + +# TODO: test with -j2 +def test_invalid_addon_py_verbose(tmpdir): + addon_file = os.path.join(tmpdir, 'addon1.py') + with open(addon_file, 'wt') as f: + f.write(""" +raise Exception() + """) + + test_file = os.path.join(tmpdir, 'file.cpp') + with open(test_file, 'wt') as f: + f.write(""" +typedef int MISRA_5_6_VIOLATION; + """) + + args = ['--addon={}'.format(addon_file), '--enable=all', '--disable=unusedFunction', '--verbose', '-j1', test_file] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 # TODO: needs to be 1 + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file), + 'Defines:', + 'Undefines:', + 'Includes:', + 'Platform:native' + ] + """ +/tmp/pytest-of-user/pytest-11/test_invalid_addon_py_20/file.cpp:0:0: error: Bailing out from analysis: Checking file failed: Failed to execute addon 'addon1' - exitcode is 1: python3 /home/user/CLionProjects/cppcheck/addons/runaddon.py /tmp/pytest-of-user/pytest-11/test_invalid_addon_py_20/addon1.py --cli /tmp/pytest-of-user/pytest-11/test_invalid_addon_py_20/file.cpp.24762.dump +Output: +Traceback (most recent call last): + File "/home/user/CLionProjects/cppcheck/addons/runaddon.py", line 8, in + runpy.run_path(addon, run_name='__main__') + File "", line 291, in run_path + File "", line 98, in _run_module_code + File "", line 88, in _run_code + File "/tmp/pytest-of-user/pytest-11/test_invalid_addon_py_20/addon1.py", line 2, in + raise Exception() +Exceptio [internalError] + """ + # /tmp/pytest-of-user/pytest-10/test_invalid_addon_py_20/file.cpp:0:0: error: Bailing out from analysis: Checking file failed: Failed to execute addon 'addon1' - exitcode is 256.: python3 /home/user/CLionProjects/cppcheck/addons/runaddon.py /tmp/pytest-of-user/pytest-10/test_invalid_addon_py_20/addon1.py --cli /tmp/pytest-of-user/pytest-10/test_invalid_addon_py_20/file.cpp.24637.dump + assert stderr.startswith("{}:0:0: error: Bailing out from analysis: Checking file failed: Failed to execute addon 'addon1' - exitcode is 1: ".format(test_file)) + assert stderr.count('Output:\nTraceback') + assert stderr.endswith('raise Exception()\nException [internalError]\n\n^\n') + + +def test_addon_result(tmpdir): + addon_file = os.path.join(tmpdir, 'addon1.py') + with open(addon_file, 'wt') as f: + f.write(""" +print("Checking ...") +print("") +print('{"file": "test.cpp", "linenr": 1, "column": 1, "severity": "style", "message": "msg", "addon": "addon1", "errorId": "id", "extra": ""}') +print('{"loc": [{"file": "test.cpp", "linenr": 1, "column": 1, "info": ""}], "severity": "style", "message": "msg", "addon": "addon1", "errorId": "id", "extra": ""}') + """) + + test_file = os.path.join(tmpdir, 'file.cpp') + with open(test_file, 'wt') as f: + f.write(""" +typedef int MISRA_5_6_VIOLATION; + """) + + args = ['--addon={}'.format(addon_file), '--enable=all', '--disable=unusedFunction', test_file] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 # TODO: needs to be 1 + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file) + ] + assert stderr == 'test.cpp:1:1: style: msg [addon1-id]\n\n^\n' + + +# TODO: test with -j2 +# #11483 +def test_unused_function_include(tmpdir): + test_cpp_file = os.path.join(tmpdir, 'test.cpp') + with open(test_cpp_file, 'wt') as f: + f.write(""" + #include "test.h" + """) + + test_h_file = os.path.join(tmpdir, 'test.h') + with open(test_h_file, 'wt') as f: + f.write(""" + class A { + public: + void f() {} + // cppcheck-suppress unusedFunction + void f2() {} + }; + """) + + args = ['--enable=unusedFunction', '--inline-suppr', '--template=simple', '-j1', test_cpp_file] + + _, _, stderr = cppcheck(args) + assert stderr == "{}:4:0: style: The function 'f' is never used. [unusedFunction]\n".format(test_h_file) + + +# TODO: test with all other types +def test_showtime_top5_file(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + f.write(""" + int main(int argc) + { + } + """) + + args = ['--showtime=top5_file', '--quiet', test_file] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 # TODO: needs to be 1 + lines = stdout.splitlines() + assert len(lines) == 7 + assert lines[0] == '' + for i in range(1, 5): + if lines[i].startswith('valueFlowLifetime'): + assert lines[i].endswith(' - 2 result(s))') + elif lines[i].startswith('valueFlowEnumValue'): + assert lines[i].endswith(' - 2 result(s))') + else: + assert lines[i].endswith(' - 1 result(s))') + assert lines[6].startswith('Overall time:') + assert stderr == '' + + +def test_missing_addon(tmpdir): + args = ['--addon=misra3', '--addon=misra', '--addon=misra2', 'file.c'] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 1 + lines = stdout.splitlines() + lines.sort() + assert lines == [ + 'Did not find addon misra2.py', + 'Did not find addon misra3.py' + ] + assert stderr == "" + + +def test_file_filter(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + pass + + args = ['--file-filter=*.cpp', test_file] + out_lines = [ + 'Checking {} ...'.format(test_file) + ] + + assert_cppcheck(args, ec_exp=0, err_exp=[], out_exp=out_lines) + + +def test_file_filter_2(tmpdir): + test_file_1 = os.path.join(tmpdir, 'test.cpp') + with open(test_file_1, 'wt') as f: + pass + test_file_2 = os.path.join(tmpdir, 'test.c') + with open(test_file_2, 'wt') as f: + pass + + args = ['--file-filter=*.cpp', test_file_1, test_file_2] + out_lines = [ + 'Checking {} ...'.format(test_file_1) + ] + + assert_cppcheck(args, ec_exp=0, err_exp=[], out_exp=out_lines) + + +def test_file_filter_3(tmpdir): + test_file_1 = os.path.join(tmpdir, 'test.cpp') + with open(test_file_1, 'wt') as f: + pass + test_file_2 = os.path.join(tmpdir, 'test.c') + with open(test_file_2, 'wt') as f: + pass + + args = ['--file-filter=*.c', test_file_1, test_file_2] + out_lines = [ + 'Checking {} ...'.format(test_file_2) + ] + + assert_cppcheck(args, ec_exp=0, err_exp=[], out_exp=out_lines) + + +def test_file_filter_no_match(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt'): + pass + + args = ['--file-filter=*.c', test_file] + out_lines = [ + 'cppcheck: error: could not find any files matching the filter.' + ] + + assert_cppcheck(args, ec_exp=1, err_exp=[], out_exp=out_lines) + + +def test_file_order(tmpdir): + test_file_a = os.path.join(tmpdir, 'a.c') + with open(test_file_a, 'wt'): + pass + test_file_b = os.path.join(tmpdir, 'b.c') + with open(test_file_b, 'wt'): + pass + test_file_c = os.path.join(tmpdir, 'c.c') + with open(test_file_c, 'wt'): + pass + test_file_d = os.path.join(tmpdir, 'd.c') + with open(test_file_d, 'wt'): + pass + + args = [test_file_c, test_file_d, test_file_b, test_file_a, '-j1'] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file_c), + '1/4 files checked 0% done', + 'Checking {} ...'.format(test_file_d), + '2/4 files checked 0% done', + 'Checking {} ...'.format(test_file_b), + '3/4 files checked 0% done', + 'Checking {} ...'.format(test_file_a), + '4/4 files checked 0% done' + ] + assert stderr == '' + + +def test_markup(tmpdir): + test_file_1 = os.path.join(tmpdir, 'test_1.qml') + with open(test_file_1, 'wt') as f: + pass + test_file_2 = os.path.join(tmpdir, 'test_2.cpp') + with open(test_file_2, 'wt') as f: + pass + test_file_3 = os.path.join(tmpdir, 'test_3.qml') + with open(test_file_3, 'wt') as f: + pass + test_file_4 = os.path.join(tmpdir, 'test_4.cpp') + with open(test_file_4, 'wt') as f: + pass + + args = ['--library=qt', test_file_1, test_file_2, test_file_3, test_file_4, '-j1'] + out_lines = [ + 'Checking {} ...'.format(test_file_2), + '1/4 files checked 0% done', + 'Checking {} ...'.format(test_file_4), + '2/4 files checked 0% done', + 'Checking {} ...'.format(test_file_1), + '3/4 files checked 0% done', + 'Checking {} ...'.format(test_file_3), + '4/4 files checked 0% done' + ] + + assert_cppcheck(args, ec_exp=0, err_exp=[], out_exp=out_lines) + + +def test_markup_j(tmpdir): + test_file_1 = os.path.join(tmpdir, 'test_1.qml') + with open(test_file_1, 'wt') as f: + pass + test_file_2 = os.path.join(tmpdir, 'test_2.cpp') + with open(test_file_2, 'wt') as f: + pass + test_file_3 = os.path.join(tmpdir, 'test_3.qml') + with open(test_file_3, 'wt') as f: + pass + test_file_4 = os.path.join(tmpdir, 'test_4.cpp') + with open(test_file_4, 'wt') as f: + pass + + args = ['--library=qt', '-j2', test_file_1, test_file_2, test_file_3, test_file_4] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + lines = stdout.splitlines() + for i in range(1, 5): + lines.remove('{}/4 files checked 0% done'.format(i)) + + # this test started to fail in the -j2 injection run when using ThreadExecutor although it always specifies -j2. + # the order of the files in the output changed so just check for the file extentions + assert len(lines) == 4 + assert lines[0].endswith('.cpp ...') + assert lines[1].endswith('.cpp ...') + assert lines[2].endswith('.qml ...') + assert lines[3].endswith('.qml ...') + + #assert lines == [ + # 'Checking {} ...'.format(test_file_2), + # 'Checking {} ...'.format(test_file_4), + # 'Checking {} ...'.format(test_file_1), + # 'Checking {} ...'.format(test_file_3) + #] + assert stderr == '' + + +def test_valueflow_debug(tmpdir): + test_file_cpp = os.path.join(tmpdir, 'test_1.cpp') + with open(test_file_cpp, 'wt') as f: + f.write(""" +#include "test.h" + +void f() +{ + int i = 0; +} +""" + ) + test_file_h = os.path.join(tmpdir, 'test.h') + with open(test_file_h, 'wt') as f: + f.write(""" +#include "test2.h" +inline void f1() +{ + int i = 0; +} +""" + ) + pass + test_file_h_2 = os.path.join(tmpdir, 'test2.h') + with open(test_file_h_2, 'wt') as f: + f.write(""" +inline void f2() +{ + int i = 0; +} +""" + ) + + args = ['--debug', test_file_cpp] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + if sys.platform == "win32": + stdout = stdout.replace('/', '\\') + assert stdout == '''Checking {} ... + + +##file {} +2: void f2 ( ) +3: {{ +4: int i@var1 ; i@var1 = 0 ; +5: }} + +##file {} + +1: +2: +3: void f1 ( ) +4: {{ +5: int i@var2 ; i@var2 = 0 ; +6: }} + +##file {} + +1: +2: +3: +4: void f ( ) +5: {{ +6: int i@var3 ; i@var3 = 0 ; +7: }} + + + +##Value flow +File {} +Line 4 + = always 0 + 0 always 0 +File {} +Line 5 + = always 0 + 0 always 0 +File {} +Line 6 + = always 0 + 0 always 0 +'''.format(test_file_cpp, test_file_h_2, test_file_h, test_file_cpp, test_file_h_2, test_file_h, test_file_cpp) + assert stderr == '' + + +def test_file_duplicate(tmpdir): + test_file_a = os.path.join(tmpdir, 'a.c') + with open(test_file_a, 'wt'): + pass + + args = [test_file_a, test_file_a, str(tmpdir)] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file_a) + ] + assert stderr == '' + + +def test_file_duplicate_2(tmpdir): + test_file_a = os.path.join(tmpdir, 'a.c') + with open(test_file_a, 'wt'): + pass + test_file_b = os.path.join(tmpdir, 'b.c') + with open(test_file_b, 'wt'): + pass + test_file_c = os.path.join(tmpdir, 'c.c') + with open(test_file_c, 'wt'): + pass + + args = [test_file_c, test_file_a, test_file_b, str(tmpdir), test_file_b, test_file_c, test_file_a, str(tmpdir), '-j1'] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 0 + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file_c), + '1/3 files checked 0% done', + 'Checking {} ...'.format(test_file_a), + '2/3 files checked 0% done', + 'Checking {} ...'.format(test_file_b), + '3/3 files checked 0% done' + ] + assert stderr == '' + + +def test_file_ignore(tmpdir): + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt'): + pass + + args = ['-itest.cpp', test_file] + out_lines = [ + 'cppcheck: error: could not find or open any of the paths given.', + 'cppcheck: Maybe all paths were ignored?' + ] + + assert_cppcheck(args, ec_exp=1, err_exp=[], out_exp=out_lines) + + +def test_build_dir_j_memleak(tmpdir): #12111 + build_dir = os.path.join(tmpdir, 'build-dir') + os.mkdir(build_dir) + + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + f.write('int main() {}') + + args = ['--cppcheck-build-dir={}'.format(build_dir), '-j2', test_file] + out_lines = [ + 'Checking {} ...'.format(test_file) + ] + + assert_cppcheck(args, ec_exp=0, err_exp=[], out_exp=out_lines) + + +def __test_addon_json_invalid(tmpdir, addon_json, expected): + addon_file = os.path.join(tmpdir, 'invalid.json') + with open(addon_file, 'wt') as f: + f.write(addon_json) + + test_file = os.path.join(tmpdir, 'file.cpp') + with open(test_file, 'wt'): + pass + + args = ['--addon={}'.format(addon_file), test_file] + + exitcode, stdout, stderr = cppcheck(args) + assert exitcode == 1 + lines = stdout.splitlines() + assert len(lines) == 1 + assert lines == [ + 'Loading {} failed. {}'.format(addon_file, expected) + ] + assert stderr == '' + + +def test_addon_json_invalid_no_obj(tmpdir): + __test_addon_json_invalid(tmpdir, json.dumps([]), "JSON is not an object.") + + +def test_addon_json_invalid_args_1(tmpdir): + __test_addon_json_invalid(tmpdir, json.dumps({'args':0}), "'args' must be an array.") + + +def test_addon_json_invalid_args_2(tmpdir): + __test_addon_json_invalid(tmpdir, json.dumps({'args':[0]}), "'args' entry is not a string.") + + +def test_addon_json_invalid_ctu(tmpdir): + __test_addon_json_invalid(tmpdir, json.dumps({'ctu':0}), "'ctu' must be a boolean.") + + +def test_addon_json_invalid_python(tmpdir): + __test_addon_json_invalid(tmpdir, json.dumps({'python':0}), "'python' must be a string.") + + +def test_addon_json_invalid_executable(tmpdir): + __test_addon_json_invalid(tmpdir, json.dumps({'executable':0}), "'executable' must be a string.") + + +def test_addon_json_invalid_script_1(tmpdir): + __test_addon_json_invalid(tmpdir, json.dumps({'Script':''}), "'script' is missing.") + + +def test_addon_json_invalid_script_2(tmpdir): + __test_addon_json_invalid(tmpdir, json.dumps({'script':0}), "'script' must be a string.") + + +def test_unknown_extension(tmpdir): + test_file = os.path.join(tmpdir, 'test_2') + with open(test_file, 'wt') as f: + f.write(''' +void f() { } +''') + + exitcode, stdout, stderr = cppcheck(['-q', test_file]) + assert exitcode == 0, stderr + assert stdout == '' + assert stderr == '' + + +def test_rule_file_define_multiple(tmpdir): + rule_file = os.path.join(tmpdir, 'rule_file.xml') + with open(rule_file, 'wt') as f: + f.write(""" + + + define + DEF_1 + + error + ruleId1 + + + + define + DEF_2 + + error + ruleId2 + define2 + + +""") + + test_file = os.path.join(tmpdir, 'test.c') + with open(test_file, 'wt') as f: + f.write(''' +#define DEF_1 +#define DEF_2 +void f() { } +''') + + exitcode, stdout, stderr = cppcheck(['--template=simple', '--rule-file={}'.format(rule_file), '-DDEF_3', test_file]) + assert exitcode == 0, stderr + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file), + 'Processing rule: DEF_1', + 'Processing rule: DEF_2', + 'Checking {}: DEF_3=1...'.format(test_file) + ] + lines = stderr.splitlines() + assert lines == [ + "{}:2:0: error: found 'DEF_1' [ruleId1]".format(test_file), + "{}:3:0: error: define2 [ruleId2]".format(test_file) + ] + + +def test_rule_file_define(tmpdir): + rule_file = os.path.join(tmpdir, 'rule_file.xml') + with open(rule_file, 'wt') as f: + f.write(""" + + define + DEF_. + +""") + + test_file = os.path.join(tmpdir, 'test.c') + with open(test_file, 'wt') as f: + f.write(''' +#define DEF_1 +#define DEF_2 +void f() { } +''') + + exitcode, stdout, stderr = cppcheck(['--template=simple', '--rule-file={}'.format(rule_file), '-DDEF_3', test_file]) + assert exitcode == 0, stdout + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file), + 'Processing rule: DEF_.', + 'Checking {}: DEF_3=1...'.format(test_file) + ] + lines = stderr.splitlines() + assert lines == [ + "{}:2:0: style: found 'DEF_1' [rule]".format(test_file), + "{}:3:0: style: found 'DEF_2' [rule]".format(test_file) + ] + + +def test_rule_file_normal(tmpdir): + rule_file = os.path.join(tmpdir, 'rule_file.xml') + with open(rule_file, 'wt') as f: + f.write(""" + + int + +""") + + test_file = os.path.join(tmpdir, 'test.c') + with open(test_file, 'wt') as f: + f.write(''' +#define DEF_1 +#define DEF_2 +typedef int i32; +void f(i32) { } +''') + + exitcode, stdout, stderr = cppcheck(['--template=simple', '--rule-file={}'.format(rule_file), test_file]) + assert exitcode == 0, stdout + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file), + 'Processing rule: int', + ] + lines = stderr.splitlines() + assert lines == [ + "{}:5:0: style: found 'int' [rule]".format(test_file) + ] + + +def test_rule_file_raw(tmpdir): + rule_file = os.path.join(tmpdir, 'rule_file.xml') + with open(rule_file, 'wt') as f: + f.write(""" + + raw + i32 + +""") + + test_file = os.path.join(tmpdir, 'test.c') + with open(test_file, 'wt') as f: + f.write(''' +#define DEF_1 +#define DEF_2 +typedef int i32; +void f(i32) { } +''') + + exitcode, stdout, stderr = cppcheck(['--template=simple', '--rule-file={}'.format(rule_file), test_file]) + assert exitcode == 0, stdout + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file), + 'Processing rule: i32', + ] + lines = stderr.splitlines() + assert lines == [ + "{}:4:0: style: found 'i32' [rule]".format(test_file), + "{}:5:0: style: found 'i32' [rule]".format(test_file) + ] + + +def test_rule(tmpdir): + test_file = os.path.join(tmpdir, 'test.c') + with open(test_file, 'wt') as f: + f.write(''' +#define DEF_1 +#define DEF_2 +void f() { } +''') + + exitcode, stdout, stderr = cppcheck(['--template=simple', '--rule=f', test_file]) + assert exitcode == 0, stdout + lines = stdout.splitlines() + assert lines == [ + 'Checking {} ...'.format(test_file), + 'Processing rule: f', + ] + lines = stderr.splitlines() + assert lines == [ + "{}:4:0: style: found 'f' [rule]".format(test_file) + ] \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/performance_test.py b/cppcheck-2.14.0/test/cli/performance_test.py new file mode 100644 index 00000000..7311f036 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/performance_test.py @@ -0,0 +1,221 @@ + +# python -m pytest test-other.py + +import os +import sys +import pytest + +from testutils import cppcheck, assert_cppcheck + + + +@pytest.mark.timeout(10) +def test_slow_array_many_floats(tmpdir): + # 11649 + # cppcheck valueflow takes a long time when an array has many floats + filename = os.path.join(tmpdir, 'hang.c') + with open(filename, 'wt') as f: + f.write("const float f[] = {\n") + for i in range(20000): + f.write(' 13.6f,\n') + f.write("};\n") + cppcheck([filename]) # should not take more than ~1 second + + +@pytest.mark.timeout(10) +def test_slow_array_many_strings(tmpdir): + # 11901 + # cppcheck valueflow takes a long time when analyzing a file with many strings + filename = os.path.join(tmpdir, 'hang.c') + with open(filename, 'wt') as f: + f.write("const char *strings[] = {\n") + for i in range(20000): + f.write(' "abc",\n') + f.write("};\n") + cppcheck([filename]) # should not take more than ~1 second + + +@pytest.mark.timeout(10) +def test_slow_long_line(tmpdir): + # simplecpp #314 + filename = os.path.join(tmpdir, 'hang.c') + with open(filename, 'wt') as f: + f.write("#define A() static const int a[] = {\\\n") + for i in range(5000): + f.write(" -123, 456, -789,\\\n") + f.write("};\n") + cppcheck([filename]) # should not take more than ~1 second + + +@pytest.mark.timeout(60) +def test_slow_large_constant_expression(tmpdir): + # 12182 + filename = os.path.join(tmpdir, 'hang.c') + with open(filename, 'wt') as f: + f.write(""" +#define FLAG1 0 +#define FLAG2 0 +#define FLAG3 0 +#define FLAG4 0 +#define FLAG5 0 +#define FLAG6 0 +#define FLAG7 0 +#define FLAG8 0 +#define FLAG9 0 +#define FLAG10 0 +#define FLAG11 0 +#define FLAG12 0 +#define FLAG13 0 +#define FLAG14 0 +#define FLAG15 0 +#define FLAG16 0 +#define FLAG17 0 +#define FLAG18 0 +#define FLAG19 0 +#define FLAG20 0 +#define FLAG21 0 +#define FLAG22 0 +#define FLAG23 0 +#define FLAG24 0 + +#define maxval(x, y) ((x) > (y) ? (x) : (y)) + +#define E_SAMPLE_SIZE maxval( FLAG1, \ + maxval( FLAG2, \ + maxval( FLAG3, \ + maxval( FLAG4, \ + maxval( FLAG5, \ + maxval( FLAG6, \ + maxval( FLAG7, \ + maxval( FLAG8, \ + maxval( FLAG9, \ + maxval( FLAG10, \ + maxval( FLAG11, \ + maxval( FLAG12, \ + maxval( FLAG13, \ + maxval( FLAG14, \ + FLAG15 )))))))))))))) + +#define SAMPLE_SIZE maxval( E_SAMPLE_SIZE, \ + maxval( sizeof(st), \ + maxval( FLAG16, \ + maxval( FLAG17, \ + maxval( FLAG18, \ + maxval( FLAG19, \ + maxval( FLAG20, \ + maxval( FLAG21, \ + maxval( FLAG22, \ + maxval( FLAG23, \ + FLAG24 )))))))))) + +typedef struct { + int n; +} st; + +x = SAMPLE_SIZE; + """) + + cppcheck([filename]) + +@pytest.mark.timeout(10) +def test_slow_exprid(tmpdir): + # 11885 + filename = os.path.join(tmpdir, 'hang.c') + with open(filename, 'wt') as f: + f.write(""" +int foo(int a, int b) +{ +#define A0(a, b) ((a) + (b)) +#define A1(a, b) ((a) > (b)) ? A0((a) - (b), (b)) : A0((b) - (a), (a)) +#define A2(a, b) ((a) > (b)) ? A1((a) - (b), (b)) : A1((b) - (a), (a)) +#define A3(a, b) ((a) > (b)) ? A2((a) - (b), (b)) : A2((b) - (a), (a)) +#define A4(a, b) ((a) > (b)) ? A3((a) - (b), (b)) : A3((b) - (a), (a)) +#define A5(a, b) ((a) > (b)) ? A4((a) - (b), (b)) : A4((b) - (a), (a)) +#define A6(a, b) ((a) > (b)) ? A5((a) - (b), (b)) : A5((b) - (a), (a)) +#define A7(a, b) ((a) > (b)) ? A6((a) - (b), (b)) : A6((b) - (a), (a)) +#define A8(a, b) ((a) > (b)) ? A7((a) - (b), (b)) : A7((b) - (a), (a)) +#define A9(a, b) ((a) > (b)) ? A8((a) - (b), (b)) : A8((b) - (a), (a)) +#define A10(a, b) ((a) > (b)) ? A9((a) - (b), (b)) : A9((b) - (a), (a)) +#define A11(a, b) ((a) > (b)) ? A10((a) - (b), (b)) : A10((b) - (a), (a)) + return A8(a, b); +} + """) + + my_env = os.environ.copy() + my_env["DISABLE_VALUEFLOW"] = "1" + cppcheck([filename], env=my_env) + + +@pytest.mark.timeout(10) +def test_slow_initlist_varchanged(tmpdir): + # #12235 + filename = os.path.join(tmpdir, 'hang.cpp') + with open(filename, 'wt') as f: + f.write(r""" + struct T { + int* q; + int nx, ny; + }; + struct S { + void f(); + int n; + T* p; + }; + #define ROW 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , + #define ROW4 ROW ROW ROW ROW + #define ROW16 ROW4 ROW4 ROW4 ROW4 + #define ROW64 ROW16 ROW16 ROW16 ROW16 + #define ROW256 ROW64 ROW64 ROW64 ROW64 + #define ROW1K ROW256 ROW256 ROW256 ROW256 + #define ROW4K ROW1K ROW1K ROW1K ROW1K + const int A[] = { + ROW4K + }; + void S::f() { + for (int i = 0; i < n; ++i) { + T& t = p[i]; + for (int y = 0; y < t.ny; y += 4) { + int* row0 = t.q + y * t.nx; + for (int x = 0; x < t.nx; x += 4) { + int s[16] = {}; + memcpy(row0, &s[0], 4); + row0 += 4; + } + } + } + }""") + cppcheck([filename]) # should not take more than ~1 second + + +@pytest.mark.timeout(10) +def test_slow_many_scopes(tmpdir): + # #12038 + filename = os.path.join(tmpdir, 'hang.cpp') + with open(filename, 'wt') as f: + f.write(r""" + #define BLOCK {\ + char buf[sizeof("x") + 5 * 3 + 16];\ + int rc;\ + memset(buf, cmp[0], sizeof buf);\ + rc = sprintf(buf + 5, "x");\ + assert(rc == sizeof("x") - 1);\ + assert(memcmp(buf, cmp, 5) == 0);\ + if ((rc) >= 0) {\ + assert(memcmp(buf + 5, ("x"), (rc)) == 0);\ + assert(buf[5 + (rc)] == '\0');\ + }\ + assert(memcmp(buf + 5 + (rc) + 1, cmp, 5) == 0);\ + } + #define BLOCK2 BLOCK BLOCK + #define BLOCK4 BLOCK2 BLOCK2 + #define BLOCK8 BLOCK4 BLOCK4 + #define BLOCK16 BLOCK8 BLOCK8 + #define BLOCK32 BLOCK16 BLOCK16 + #define BLOCK64 BLOCK32 BLOCK32 + int main() { + char cmp[5]; + memset(cmp, '\376', sizeof cmp); + BLOCK64 + return EXIT_SUCCESS; + }""") + cppcheck([filename]) # should not take more than ~1 second diff --git a/cppcheck-2.14.0/test/cli/premium_test.py b/cppcheck-2.14.0/test/cli/premium_test.py new file mode 100644 index 00000000..82d8bc03 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/premium_test.py @@ -0,0 +1,51 @@ + +# python -m pytest premium_test.py + +import logging +import os +import shutil +import sys +import time +import pytest + +from testutils import cppcheck, assert_cppcheck, __lookup_cppcheck_exe + +PRODUCT_NAME = 'Cppcheck Premium ' + str(time.time()) + + +def copy_cppcheck_premium(tmpdir): + exe = shutil.copy2(__lookup_cppcheck_exe(), tmpdir) + + # add minimum cfg/std.cfg + test_cfg_folder = tmpdir.mkdir('cfg') + with open(test_cfg_folder.join('std.cfg'), 'wt') as f: + f.write('\n') + + # add simple cppcheck.cfg + with open(tmpdir.join('cppcheck.cfg'), 'wt') as f: + f.write(""" + { + "addons": [], + "productName": "NAME", + "about": "NAME", + "safety": false + } + """.replace('NAME', PRODUCT_NAME)) + + return exe + + +def test_misra_c_builtin_style_checks(tmpdir): + # FIXME this test does not work in ci-windows.yml (release build) + if sys.platform == 'win32': + return + + test_file = os.path.join(tmpdir, 'test.cpp') + with open(test_file, 'wt') as f: + f.write('void foo() { int x; y = 0; }') + + exe = copy_cppcheck_premium(tmpdir) + _, stdout, stderr = cppcheck(['--premium=autosar', test_file], cppcheck_exe=exe) + assert '' in stdout + assert '[unusedVariable]' in stderr + diff --git a/cppcheck-2.14.0/test/cli/proj-inline-suppress-unusedFunction/A.cpp b/cppcheck-2.14.0/test/cli/proj-inline-suppress-unusedFunction/A.cpp new file mode 100644 index 00000000..69235491 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj-inline-suppress-unusedFunction/A.cpp @@ -0,0 +1,7 @@ +#include "B.hpp" + +int main() +{ + B b(); + return 0; +} diff --git a/cppcheck-2.14.0/test/cli/proj-inline-suppress-unusedFunction/B.cpp b/cppcheck-2.14.0/test/cli/proj-inline-suppress-unusedFunction/B.cpp new file mode 100644 index 00000000..f8e512f3 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj-inline-suppress-unusedFunction/B.cpp @@ -0,0 +1,6 @@ +#include "B.hpp" + +B::B() {} + +// cppcheck-suppress unusedFunction +void B::unusedFunctionTest() {} diff --git a/cppcheck-2.14.0/test/cli/proj-inline-suppress-unusedFunction/B.hpp b/cppcheck-2.14.0/test/cli/proj-inline-suppress-unusedFunction/B.hpp new file mode 100644 index 00000000..aca79e88 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj-inline-suppress-unusedFunction/B.hpp @@ -0,0 +1,6 @@ +class B { +public: + B(); + + void unusedFunctionTest(); +}; diff --git a/cppcheck-2.14.0/test/cli/proj-inline-suppress/1.c b/cppcheck-2.14.0/test/cli/proj-inline-suppress/1.c new file mode 100644 index 00000000..490765b2 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj-inline-suppress/1.c @@ -0,0 +1 @@ +#include <1.h> diff --git a/cppcheck-2.14.0/test/cli/proj-inline-suppress/1.h b/cppcheck-2.14.0/test/cli/proj-inline-suppress/1.h new file mode 100644 index 00000000..9ca29fb8 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj-inline-suppress/1.h @@ -0,0 +1,5 @@ + +// cppcheck-suppress zerodiv +const int x = 10000 / 0; + + diff --git a/cppcheck-2.14.0/test/cli/proj-inline-suppress/2.c b/cppcheck-2.14.0/test/cli/proj-inline-suppress/2.c new file mode 100644 index 00000000..541b01cb --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj-inline-suppress/2.c @@ -0,0 +1,3 @@ +// cppcheck-suppress some_warning_id ; there should be a unmatchedSuppression warning about this +x; + diff --git a/cppcheck-2.14.0/test/cli/proj-inline-suppress/3.cpp b/cppcheck-2.14.0/test/cli/proj-inline-suppress/3.cpp new file mode 100644 index 00000000..246ffbfe --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj-inline-suppress/3.cpp @@ -0,0 +1,6 @@ +// From forum: https://sourceforge.net/p/cppcheck/discussion/general/thread/e67653efdb/ +void foo() +{ // cppcheck-suppress zerodiv + int x = 10000 / 0; +} + diff --git a/cppcheck-2.14.0/test/cli/proj-inline-suppress/4.c b/cppcheck-2.14.0/test/cli/proj-inline-suppress/4.c new file mode 100644 index 00000000..022c389b --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj-inline-suppress/4.c @@ -0,0 +1,6 @@ +int main() { + // cppcheck-suppress unreadVariable + int i = 0; + return 0; +} + diff --git a/cppcheck-2.14.0/test/cli/proj-inline-suppress/template.cpp b/cppcheck-2.14.0/test/cli/proj-inline-suppress/template.cpp new file mode 100644 index 00000000..62e504e7 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj-inline-suppress/template.cpp @@ -0,0 +1,15 @@ +namespace { + template + T f() { + return T(); + } +} + +// cppcheck-suppress unusedFunction +int g(int i) { + if (i == 0) + return f(); + if (i == 1) + return f(); + return f(); +} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/proj-suppress-syntaxError/1.c b/cppcheck-2.14.0/test/cli/proj-suppress-syntaxError/1.c new file mode 100644 index 00000000..c4521b0d --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj-suppress-syntaxError/1.c @@ -0,0 +1,8 @@ + + +void validCode(int argInt) +{ + // if G_UNLIKELY is not defined this results in a syntax error + if G_UNLIKELY(argInt == 1) {} else if (G_UNLIKELY(argInt == 2)) {} +} + diff --git a/cppcheck-2.14.0/test/cli/proj-suppress-syntaxError/2.c b/cppcheck-2.14.0/test/cli/proj-suppress-syntaxError/2.c new file mode 100644 index 00000000..12a8770e --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj-suppress-syntaxError/2.c @@ -0,0 +1 @@ +void f1(); diff --git a/cppcheck-2.14.0/test/cli/proj-suppress-syntaxError/3.c b/cppcheck-2.14.0/test/cli/proj-suppress-syntaxError/3.c new file mode 100644 index 00000000..d8eabefa --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj-suppress-syntaxError/3.c @@ -0,0 +1,2 @@ + +void f2(); diff --git a/cppcheck-2.14.0/test/cli/proj2/a/a.c b/cppcheck-2.14.0/test/cli/proj2/a/a.c new file mode 100644 index 00000000..2959aad8 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj2/a/a.c @@ -0,0 +1,5 @@ +x = 3 / 0; +#ifdef AAA +void aa; +#endif + diff --git a/cppcheck-2.14.0/test/cli/proj2/b/b.c b/cppcheck-2.14.0/test/cli/proj2/b/b.c new file mode 100644 index 00000000..a87462ab --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj2/b/b.c @@ -0,0 +1,2 @@ +x = 3 / 0; + diff --git a/cppcheck-2.14.0/test/cli/proj2/proj2.cppcheck b/cppcheck-2.14.0/test/cli/proj2/proj2.cppcheck new file mode 100644 index 00000000..079023c3 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj2/proj2.cppcheck @@ -0,0 +1,6 @@ + + + + compile_commands.json + + diff --git a/cppcheck-2.14.0/test/cli/proj2/proj2.sln b/cppcheck-2.14.0/test/cli/proj2/proj2.sln new file mode 100644 index 00000000..9c957ab7 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj2/proj2.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2027 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "proj2", "proj2.vcxproj", "{B9ED3CF9-9DB9-4876-9923-6FAD501885D5}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Debug|x64.ActiveCfg = Debug|x64 + {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Debug|x64.Build.0 = Debug|x64 + {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Debug|x86.ActiveCfg = Debug|Win32 + {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Debug|x86.Build.0 = Debug|Win32 + {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Release|x64.ActiveCfg = Release|x64 + {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Release|x64.Build.0 = Release|x64 + {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Release|x86.ActiveCfg = Release|Win32 + {B9ED3CF9-9DB9-4876-9923-6FAD501885D5}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DDBB0F1F-12C9-4A06-B09A-D55E5F0D0A43} + EndGlobalSection +EndGlobal diff --git a/cppcheck-2.14.0/test/cli/proj2/proj2.vcxproj b/cppcheck-2.14.0/test/cli/proj2/proj2.vcxproj new file mode 100644 index 00000000..c65dc35c --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj2/proj2.vcxproj @@ -0,0 +1,124 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {B9ED3CF9-9DB9-4876-9923-6FAD501885D5} + proj2 + 10.0.16299.0 + + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + true + + + + + Level3 + Disabled + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + + + + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/proj2_test.py b/cppcheck-2.14.0/test/cli/proj2_test.py new file mode 100644 index 00000000..868d832d --- /dev/null +++ b/cppcheck-2.14.0/test/cli/proj2_test.py @@ -0,0 +1,155 @@ + +# python -m pytest test-proj2.py + +import json +import os +from testutils import create_gui_project_file, cppcheck + +COMPILE_COMMANDS_JSON = 'compile_commands.json' + +ERR_A = ('%s:1:7: error: Division by zero. [zerodiv]\n' + + 'x = 3 / 0;\n' + + ' ^\n') % os.path.join('a', 'a.c') +ERR_B = ('%s:1:7: error: Division by zero. [zerodiv]\n' + + 'x = 3 / 0;\n' + + ' ^\n') % os.path.join('b', 'b.c') + +def realpath(s): + return os.path.realpath(s).replace('\\', '/') + +def create_compile_commands(): + j = [{'directory': realpath('proj2/a'), 'command': 'gcc -c a.c', 'file': 'a.c'}, + {'directory': realpath('proj2'), 'command': 'gcc -c b/b.c', 'file': 'b/b.c'}] + with open('proj2/' + COMPILE_COMMANDS_JSON, 'wt') as f: + f.write(json.dumps(j)) + +# Run Cppcheck from project path +def cppcheck_local(args): + cwd = os.getcwd() + os.chdir('proj2') + ret, stdout, stderr = cppcheck(args) + os.chdir(cwd) + return ret, stdout, stderr + +def test_file_filter(): + ret, stdout, stderr = cppcheck(['proj2/','--file-filter=proj2/a/*']) + file1 = os.path.join('proj2', 'a', 'a.c') + file2 = os.path.join('proj2', 'b', 'b.c') + assert ret == 0 + assert stdout.find('Checking %s ...' % file1) >= 0 + ret, stdout, stderr = cppcheck(['proj2/','--file-filter=proj2/b*']) + assert ret == 0, stdout + assert stdout.find('Checking %s ...' % file2) >= 0 + +def test_local_path(): + create_compile_commands() + ret, stdout, stderr = cppcheck_local(['--project=compile_commands.json']) + file1 = os.path.join('a', 'a.c') + file2 = os.path.join('b', 'b.c') + assert ret == 0, stdout + assert stdout.find('Checking %s ...' % file1) >= 0 + assert stdout.find('Checking %s ...' % file2) >= 0 + +def test_local_path_force(): + create_compile_commands() + ret, stdout, stderr = cppcheck_local(['--project=compile_commands.json', '--force']) + assert ret == 0, stdout + assert stdout.find('AAA') >= 0 + +def test_local_path_maxconfigs(): + create_compile_commands() + ret, stdout, stderr = cppcheck_local(['--project=compile_commands.json', '--max-configs=2']) + assert ret == 0, stdout + assert stdout.find('AAA') >= 0 + +def test_relative_path(): + create_compile_commands() + ret, stdout, stderr = cppcheck(['--project=proj2/' + COMPILE_COMMANDS_JSON]) + file1 = os.path.join('proj2', 'a', 'a.c') + file2 = os.path.join('proj2', 'b', 'b.c') + assert ret == 0, stdout + assert stdout.find('Checking %s ...' % file1) >= 0 + assert stdout.find('Checking %s ...' % file2) >= 0 + +def test_absolute_path(): + create_compile_commands() + ret, stdout, stderr = cppcheck(['--project=' + os.path.realpath('proj2/' + COMPILE_COMMANDS_JSON)]) + file1 = os.path.realpath('proj2/a/a.c') + file2 = os.path.realpath('proj2/b/b.c') + assert ret == 0, stdout + assert stdout.find('Checking %s ...' % file1) >= 0 + assert stdout.find('Checking %s ...' % file2) >= 0 + +def test_gui_project_loads_compile_commands_1(): + create_compile_commands() + ret, stdout, stderr = cppcheck(['--project=proj2/proj2.cppcheck']) + file1 = os.path.join('proj2', 'a', 'a.c') + file2 = os.path.join('proj2', 'b', 'b.c') + assert ret == 0, stdout + assert stdout.find('Checking %s ...' % file1) >= 0 + assert stdout.find('Checking %s ...' % file2) >= 0 + +def test_gui_project_loads_compile_commands_2(): + create_compile_commands() + exclude_path_1 = 'proj2/b' + create_gui_project_file('proj2/test.cppcheck', + import_project='compile_commands.json', + exclude_paths=[exclude_path_1]) + ret, stdout, stderr = cppcheck(['--project=proj2/test.cppcheck']) + file1 = os.path.join('proj2', 'a', 'a.c') + file2 = os.path.join('proj2', 'b', 'b.c') # Excluded by test.cppcheck + assert ret == 0, stdout + assert stdout.find('Checking %s ...' % file1) >= 0 + assert stdout.find('Checking %s ...' % file2) < 0 + + +def test_gui_project_loads_relative_vs_solution(): + create_gui_project_file('test.cppcheck', import_project='proj2/proj2.sln') + ret, stdout, stderr = cppcheck(['--project=test.cppcheck']) + file1 = os.path.join('proj2', 'a', 'a.c') + file2 = os.path.join('proj2', 'b', 'b.c') + assert ret == 0, stdout + assert stdout.find('Checking %s Debug|Win32...' % file1) >= 0 + assert stdout.find('Checking %s Debug|x64...' % file1) >= 0 + assert stdout.find('Checking %s Release|Win32...' % file1) >= 0 + assert stdout.find('Checking %s Release|x64...' % file1) >= 0 + assert stdout.find('Checking %s Debug|Win32...' % file2) >= 0 + assert stdout.find('Checking %s Debug|x64...' % file2) >= 0 + assert stdout.find('Checking %s Release|Win32...' % file2) >= 0 + assert stdout.find('Checking %s Release|x64...' % file2) >= 0 + +def test_gui_project_loads_absolute_vs_solution(): + create_gui_project_file('test.cppcheck', import_project=realpath('proj2/proj2.sln')) + ret, stdout, stderr = cppcheck(['--project=test.cppcheck']) + file1 = os.path.realpath('proj2/a/a.c') + file2 = os.path.realpath('proj2/b/b.c') + print(stdout) + assert ret == 0, stdout + assert stdout.find('Checking %s Debug|Win32...' % file1) >= 0 + assert stdout.find('Checking %s Debug|x64...' % file1) >= 0 + assert stdout.find('Checking %s Release|Win32...' % file1) >= 0 + assert stdout.find('Checking %s Release|x64...' % file1) >= 0 + assert stdout.find('Checking %s Debug|Win32...' % file2) >= 0 + assert stdout.find('Checking %s Debug|x64...' % file2) >= 0 + assert stdout.find('Checking %s Release|Win32...' % file2) >= 0 + assert stdout.find('Checking %s Release|x64...' % file2) >= 0 + +def test_gui_project_loads_relative_vs_solution_2(): + create_gui_project_file('test.cppcheck', root_path='proj2', import_project='proj2/proj2.sln') + ret, stdout, stderr = cppcheck(['--project=test.cppcheck']) + assert ret == 0, stdout + assert stderr == ERR_A + ERR_B + +def test_gui_project_loads_relative_vs_solution_with_exclude(): + create_gui_project_file('test.cppcheck', root_path='proj2', import_project='proj2/proj2.sln', exclude_paths=['b']) + ret, stdout, stderr = cppcheck(['--project=test.cppcheck']) + assert ret == 0, stdout + assert stderr == ERR_A + +def test_gui_project_loads_absolute_vs_solution_2(): + create_gui_project_file('test.cppcheck', + root_path=realpath('proj2'), + import_project=realpath('proj2/proj2.sln')) + ret, stdout, stderr = cppcheck(['--project=test.cppcheck']) + assert ret == 0, stdout + assert stderr == ERR_A + ERR_B diff --git a/cppcheck-2.14.0/test/cli/project_test.py b/cppcheck-2.14.0/test/cli/project_test.py new file mode 100644 index 00000000..8ac24300 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/project_test.py @@ -0,0 +1,151 @@ +# python -m pytest test-projects.py + +import pytest +import os +import json +import sys +from testutils import cppcheck + + +@pytest.mark.parametrize("project_ext", ["json", "sln", "vcxproj", "bpr", "cppcheck"]) +def test_missing_project(project_ext): + project_file = "file.{}".format(project_ext) + + ret, stdout, stderr = cppcheck(['--project=' + project_file, '--template=cppcheck1']) + assert 1 == ret + assert "cppcheck: error: failed to open project '{}'. The file does not exist.\n".format(project_file) == stdout + assert "" == stderr + + +def _test_project_error(tmpdir, ext, content, expected): + project_file = os.path.join(tmpdir, "file.{}".format(ext)) + + with open(project_file, 'w') as f: + if content is not None: + f.write(content) + + ret, stdout, stderr = cppcheck(['--project=' + str(project_file)]) + assert 1 == ret + assert "cppcheck: error: " + expected + "\ncppcheck: error: failed to load project '{}'. An error occurred.\n".format(project_file) == stdout + assert "" == stderr + + +@pytest.mark.parametrize("project_ext, expected", [ + ("json", "compilation database is not a JSON array"), + ("sln", "Visual Studio solution file is empty"), + ("vcxproj", "Visual Studio project file is not a valid XML - XML_ERROR_EMPTY_DOCUMENT"), + ("bpr", "Borland project file is not a valid XML - XML_ERROR_EMPTY_DOCUMENT"), + ("cppcheck", "Cppcheck GUI project file is not a valid XML - XML_ERROR_EMPTY_DOCUMENT") +]) +def test_empty_project(tmpdir, project_ext, expected): + _test_project_error(tmpdir, project_ext, None, expected) + + +def test_json_entry_file_not_found(tmpdir): + compilation_db = [ + {"directory": str(tmpdir), + "command": "c++ -o bug1.o -c bug1.cpp", + "file": "bug1.cpp", + "output": "bug1.o"} + ] + + expected = "'{}' from compilation database does not exist".format(os.path.join(tmpdir, "bug1.cpp")) + if sys.platform == "win32": + expected = expected.replace('\\', '/') + + _test_project_error(tmpdir, "json", json.dumps(compilation_db), expected) + + +def test_json_no_arguments(tmpdir): + compilation_db = [ + {"directory": str(tmpdir), + "file": "bug1.cpp", + "output": "bug1.o"} + ] + + expected = "no 'arguments' or 'command' field found in compilation database entry" + + _test_project_error(tmpdir, "json", json.dumps(compilation_db), expected) + + +def test_json_invalid_arguments(tmpdir): + compilation_db = [ + {"directory": str(tmpdir), + "arguments": "", + "file": "bug1.cpp", + "output": "bug1.o"} + ] + + expected = "'arguments' field in compilation database entry is not a JSON array" + + _test_project_error(tmpdir, "json", json.dumps(compilation_db), expected) + + +def test_sln_invalid_file(tmpdir): + content = "some file" + + expected = "Visual Studio solution file header not found" + + _test_project_error(tmpdir, "sln", content, expected) + + +def test_sln_no_header(tmpdir): + content = "\xEF\xBB\xBF\r\n" \ + "some header" + + expected = "Visual Studio solution file header not found" + + _test_project_error(tmpdir, "sln", content, expected) + + +def test_sln_no_projects(tmpdir): + content = "\xEF\xBB\xBF\r\n" \ + "Microsoft Visual Studio Solution File, Format Version 12.00\r\n" + + expected = "no projects found in Visual Studio solution file" + + _test_project_error(tmpdir, "sln", content, expected) + + +def test_sln_project_file_not_found(tmpdir): + content = "\xEF\xBB\xBF\r\n" \ + "Microsoft Visual Studio Solution File, Format Version 12.00\r\n" \ + "# Visual Studio Version 16\r\n" \ + "VisualStudioVersion = 16.0.29020.237\r\n" \ + "MinimumVisualStudioVersion = 10.0.40219.1\r\n" \ + 'Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cli", "cli\\cli.vcxproj", "{35CBDF51-2456-3EC3-99ED-113C30858883}"\r\n' \ + "ProjectSection(ProjectDependencies) = postProject\r\n" \ + "{C183DB5B-AD6C-423D-80CA-1F9549555A1A} = {C183DB5B-AD6C-423D-80CA-1F9549555A1A}\r\n" \ + "EndProjectSection\r\n" \ + "EndProject\r\n" + + expected = "Visual Studio project file is not a valid XML - XML_ERROR_FILE_NOT_FOUND\n" \ + "cppcheck: error: failed to load '{}' from Visual Studio solution".format(os.path.join(tmpdir, "cli/cli.vcxproj")) + if sys.platform == "win32": + expected = expected.replace('\\', '/') + + _test_project_error(tmpdir, "sln", content, expected) + + +def test_vcxproj_no_xml_root(tmpdir): + content = '' + + expected = "Visual Studio project file has no XML root node" + + _test_project_error(tmpdir, "vcxproj", content, expected) + + +def test_bpr_no_xml_root(tmpdir): + content = '' + + expected = "Borland project file has no XML root node" + + _test_project_error(tmpdir, "bpr", content, expected) + + +def test_cppcheck_no_xml_root(tmpdir): + content = '' + + expected = "Cppcheck GUI project file has no XML root node" + + _test_project_error(tmpdir, "cppcheck", content, expected) diff --git a/cppcheck-2.14.0/test/cli/qml_test.py b/cppcheck-2.14.0/test/cli/qml_test.py new file mode 100644 index 00000000..485ab597 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/qml_test.py @@ -0,0 +1,65 @@ + +# python3 -m pytest test-qml.py + +import os +from testutils import cppcheck + +__script_dir = os.path.dirname(os.path.abspath(__file__)) + +# there are unused functions. But fillSampleData is not unused because that is referenced from main.qml +__project_dir = os.path.join(__script_dir, 'QML-Samples-TableView') +__project_dir_sep = __project_dir + os.path.sep + + +def test_unused_functions(): + ret, stdout, stderr = cppcheck(['-q', '--template=simple', '--library=qt', '--enable=unusedFunction', '-j1', __project_dir]) + # there are unused functions. But fillSampleData is not unused because that is referenced from main.qml + assert stdout.splitlines() == [] + assert stderr.splitlines() == [ + "{}samplemodel.cpp:9:0: style: The function 'rowCount' is never used. [unusedFunction]".format(__project_dir_sep), + "{}samplemodel.cpp:15:0: style: The function 'data' is never used. [unusedFunction]".format(__project_dir_sep), + "{}samplemodel.cpp:38:0: style: The function 'roleNames' is never used. [unusedFunction]".format(__project_dir_sep) + ] + assert ret == 0, stdout + + +def test_unused_functions_j(): + ret, stdout, stderr = cppcheck(['-q', '--template=simple', '--library=qt', '--enable=unusedFunction', '-j2', __project_dir]) + assert stdout.splitlines() == [ + "cppcheck: unusedFunction check can't be used with '-j' option. Disabling unusedFunction check." + ] + assert stderr.splitlines() == [] + assert ret == 0, stdout # TODO: abil out on this + + +# TODO: fillSampleData is not unused +def test_unused_functions_builddir(tmpdir): + build_dir = os.path.join(tmpdir, 'b1') + os.mkdir(build_dir) + ret, stdout, stderr = cppcheck(['-q', '--template=simple', '--library=qt', '--enable=unusedFunction', '--cppcheck-build-dir={}'.format(build_dir), __project_dir]) + assert stdout.splitlines() == [] + assert stderr.splitlines() == [ + "{}samplemodel.cpp:15:0: style: The function 'data' is never used. [unusedFunction]".format(__project_dir_sep), + "{}samplemodel.cpp:47:0: style: The function 'fillSampleData' is never used. [unusedFunction]".format(__project_dir_sep), + "{}samplemodel.cpp:38:0: style: The function 'roleNames' is never used. [unusedFunction]".format(__project_dir_sep), + "{}samplemodel.cpp:9:0: style: The function 'rowCount' is never used. [unusedFunction]".format(__project_dir_sep), + ] + assert ret == 0, stdout + + +# TODO: fillSampleData is not unused +def test_unused_functions_builddir_j(tmpdir): + build_dir = os.path.join(tmpdir, 'b1') + os.mkdir(build_dir) + ret, stdout, stderr = cppcheck(['-q', '--template=simple', '--library=qt', '--enable=unusedFunction', '-j2', '--cppcheck-build-dir={}'.format(build_dir), __project_dir]) + assert stdout.splitlines() == [] + assert stderr.splitlines() == [ + "{}samplemodel.cpp:15:0: style: The function 'data' is never used. [unusedFunction]".format(__project_dir_sep), + "{}samplemodel.cpp:47:0: style: The function 'fillSampleData' is never used. [unusedFunction]".format(__project_dir_sep), + "{}samplemodel.cpp:38:0: style: The function 'roleNames' is never used. [unusedFunction]".format(__project_dir_sep), + "{}samplemodel.cpp:9:0: style: The function 'rowCount' is never used. [unusedFunction]".format(__project_dir_sep), + ] + assert ret == 0, stdout + +# TODO: test with project file +# TODO: test with FileSettings diff --git a/cppcheck-2.14.0/test/cli/readme.txt b/cppcheck-2.14.0/test/cli/readme.txt new file mode 100644 index 00000000..b237cfa2 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/readme.txt @@ -0,0 +1,17 @@ + +Systemtesting of Cppcheck CLI on some projects + +addons +base path +exclude folders +importing projects + * visual studio + * compile database + - different generators bear/cmake/.. + - different platforms +suppressions + +Different paths: + * relative + * absolute + diff --git a/cppcheck-2.14.0/test/cli/samples_test.py b/cppcheck-2.14.0/test/cli/samples_test.py new file mode 100644 index 00000000..71848936 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/samples_test.py @@ -0,0 +1,45 @@ +import os +import sys + +from testutils import cppcheck + +__script_dir = os.path.dirname(os.path.abspath(__file__)) +__root_dir = os.path.abspath(os.path.join(__script_dir, '..', '..')) + + +def test_samples(): + failures = {} + + samples_dir = os.path.join(__root_dir, 'samples') + for entry in os.listdir(samples_dir): + sample_dir = os.path.join(samples_dir, entry) + if not os.path.isdir(sample_dir): + continue + + with open(os.path.join(sample_dir, 'out.txt')) as out_in: + out_txt = out_in.read() + if not sys.platform == 'win32': + out_txt = out_txt.replace('\\', '/') + + if not os.path.exists(os.path.join(sample_dir, 'good.c')): + good_src = os.path.join('samples', entry, 'good.cpp') + bad_src = os.path.join('samples', entry, 'bad.cpp') + else: + good_src = os.path.join('samples', entry, 'good.c') + bad_src = os.path.join('samples', entry, 'bad.c') + + # check that good input does not produce any warnings + ret, stdout, stderr = cppcheck(['-q', '--enable=all', '--disable=missingInclude', '--inconclusive', '--check-level=exhaustive', '--error-exitcode=1', good_src], cwd=__root_dir) + if not ret == 0: + failures[good_src] = stderr + + # check that the bad inout produces a warning + ret, stdout, stderr = cppcheck(['-q', '--enable=all', '--disable=missingInclude', '--inconclusive', '--check-level=exhaustive', '--error-exitcode=1', bad_src], cwd=__root_dir) + if not ret == 1: + failures[bad_src] = stderr + + # check that the bad input procudes the expected output + if not stderr == out_txt: + failures[bad_src] = stderr + + assert failures == {} diff --git a/cppcheck-2.14.0/test/cli/suppress-syntaxError_test.py b/cppcheck-2.14.0/test/cli/suppress-syntaxError_test.py new file mode 100644 index 00000000..bc3248c8 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/suppress-syntaxError_test.py @@ -0,0 +1,29 @@ + +# python -m pytest test-suppress-syntaxError.py + +import pytest +from testutils import cppcheck + +def test_j(): + ret, stdout, stderr = cppcheck(['-q', '--error-exitcode=1', '-j2', '-q', 'proj-suppress-syntaxError']) + assert ret == 1, stdout + assert len(stderr) > 0 + +def test_suppress_j(): + ret, stdout, stderr = cppcheck(['-q', '--error-exitcode=1', '--suppress=*:proj-suppress-syntaxError/*', '-j2', '-q', 'proj-suppress-syntaxError']) + assert ret == 0, stdout + assert len(stderr) == 0 + +# TODO: test with -j2 +def test_safety_suppress_syntax_error_implicitly(tmpdir): + ret, stdout, stderr = cppcheck(['-q', '--safety', '--suppress=*', 'proj-suppress-syntaxError', '-j1'], remove_checkers_report=False) + assert ret == 1, stdout + assert '[syntaxError]' in stderr + assert 'Active checkers: There was critical errors' in stderr + +# TODO: test with -j2 +def test_safety_suppress_syntax_error_explicitly(): + ret, stdout, stderr = cppcheck(['-q', '--safety', '--suppress=syntaxError', 'proj-suppress-syntaxError', '-j1'], remove_checkers_report=False) + assert ret == 1, stdout + assert '[syntaxError]' not in stderr + assert 'Active checkers: There was critical errors' in stderr diff --git a/cppcheck-2.14.0/test/cli/testutils.py b/cppcheck-2.14.0/test/cli/testutils.py new file mode 100644 index 00000000..a6692bde --- /dev/null +++ b/cppcheck-2.14.0/test/cli/testutils.py @@ -0,0 +1,169 @@ +import logging +import os +import signal +import subprocess + +# Create Cppcheck project file +import sys + + +def create_gui_project_file(project_file, root_path=None, import_project=None, paths=None, exclude_paths=None, suppressions=None, addon=None): + cppcheck_xml = ('\n' + '\n') + if root_path: + cppcheck_xml += ' \n' + if import_project: + cppcheck_xml += ' ' + import_project + '\n' + if paths: + cppcheck_xml += ' \n' + for path in paths: + cppcheck_xml += ' \n' + cppcheck_xml += ' \n' + if exclude_paths: + cppcheck_xml += ' \n' + for path in exclude_paths: + cppcheck_xml += ' \n' + cppcheck_xml += ' \n' + if suppressions: + cppcheck_xml += ' \n' + for suppression in suppressions: + cppcheck_xml += ' \n' + cppcheck_xml += ' \n' + if addon: + cppcheck_xml += ' \n' + cppcheck_xml += ' %s\n' % addon + cppcheck_xml += ' \n' + cppcheck_xml += '\n' + + f = open(project_file, 'wt') + f.write(cppcheck_xml) + f.close() + + +def __lookup_cppcheck_exe(): + # path the script is located in + script_path = os.path.dirname(os.path.realpath(__file__)) + + exe_name = "cppcheck" + + if sys.platform == "win32": + exe_name += ".exe" + + exe_path = None + + if 'TEST_CPPCHECK_EXE_LOOKUP_PATH' in os.environ: + lookup_paths = [os.environ['TEST_CPPCHECK_EXE_LOOKUP_PATH']] + else: + lookup_paths = [os.path.join(script_path, '..', '..'), '.'] + + for base in lookup_paths: + for path in ('', 'bin', os.path.join('bin', 'debug')): + tmp_exe_path = os.path.join(base, path, exe_name) + if os.path.isfile(tmp_exe_path): + exe_path = tmp_exe_path + break + + if exe_path: + exe_path = os.path.abspath(exe_path) + print("using '{}'".format(exe_path)) + return exe_path + + +# Run Cppcheck with args +def cppcheck(args, env=None, remove_checkers_report=True, cwd=None, cppcheck_exe=None, timeout=None): + exe = cppcheck_exe if cppcheck_exe else __lookup_cppcheck_exe() + assert exe is not None, 'no cppcheck binary found' + + if 'TEST_CPPCHECK_INJECT_J' in os.environ: + found_j = False + for arg in args: + if arg.startswith('-j'): + found_j = True + break + if not found_j: + arg_j = '-j' + str(os.environ['TEST_CPPCHECK_INJECT_J']) + args.append(arg_j) + + if 'TEST_CPPCHECK_INJECT_CLANG' in os.environ: + found_clang = False + for arg in args: + if arg.startswith('--clang'): + found_clang = True + break + if not found_clang: + arg_clang = '--clang=' + str(os.environ['TEST_CPPCHECK_INJECT_CLANG']) + args.append(arg_clang) + + if 'TEST_CPPCHECK_INJECT_EXECUTOR' in os.environ: + found_jn = False + found_executor = False + for arg in args: + if arg.startswith('-j') and arg != '-j1': + found_jn = True + continue + if arg.startswith('--executor'): + found_executor = True + continue + # only add '--executor' if we are actually using multiple jobs + if found_jn and not found_executor: + arg_executor = '--executor=' + str(os.environ['TEST_CPPCHECK_INJECT_EXECUTOR']) + args.append(arg_executor) + + logging.info(exe + ' ' + ' '.join(args)) + p = subprocess.Popen([exe] + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, cwd=cwd) + try: + comm = p.communicate(timeout=timeout) + return_code = p.returncode + p = None + except subprocess.TimeoutExpired: + import psutil + # terminate all the child processes + child_procs = psutil.Process(p.pid).children(recursive=True) + if len(child_procs) > 0: + for child in child_procs: + child.terminate() + try: + # call with timeout since it might be stuck + p.communicate(timeout=5) + p = None + except subprocess.TimeoutExpired: + pass + raise + finally: + if p: + # sending the signal to the process groups causes the parent Python process to terminate as well + #os.killpg(os.getpgid(p.pid), signal.SIGTERM) # Send the signal to all the process groups + p.terminate() + comm = p.communicate() + stdout = comm[0].decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n') + stderr = comm[1].decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n') + if remove_checkers_report: + if stderr.find('[checkersReport]\n') > 0: + start_id = stderr.find('[checkersReport]\n') + start_line = stderr.rfind('\n', 0, start_id) + if start_line <= 0: + stderr = '' + else: + stderr = stderr[:start_line + 1] + elif stderr.find(': (information) Active checkers: ') >= 0: + pos = stderr.find(': (information) Active checkers: ') + if pos == 0: + stderr = '' + elif stderr[pos - 1] == '\n': + stderr = stderr[:pos] + return return_code, stdout, stderr + + +def assert_cppcheck(args, ec_exp=None, out_exp=None, err_exp=None, env=None): + exitcode, stdout, stderr = cppcheck(args, env) + if ec_exp is not None: + assert exitcode == ec_exp, stdout + if out_exp is not None: + out_lines = stdout.splitlines() + assert out_lines == out_exp, stdout + if err_exp is not None: + err_lines = stderr.splitlines() + assert err_lines == err_exp, stderr diff --git a/cppcheck-2.14.0/test/cli/trac5704/trac5704a.c b/cppcheck-2.14.0/test/cli/trac5704/trac5704a.c new file mode 100644 index 00000000..e2501041 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/trac5704/trac5704a.c @@ -0,0 +1,11 @@ +#include + +int main(int argc, char *argv[]) +{ +#ifdef INCLUDE_ZERO_DIV + // cppcheck-suppress zerodiv + int i = 1/0; + printf("i = %d\n", i); +#endif + return 0; +} diff --git a/cppcheck-2.14.0/test/cli/trac5704/trac5704b.c b/cppcheck-2.14.0/test/cli/trac5704/trac5704b.c new file mode 100644 index 00000000..17b28101 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/trac5704/trac5704b.c @@ -0,0 +1,11 @@ +#include + +int main(int argc, char *argv[]) +{ +#ifdef 0 + // cppcheck-suppress zerodiv + int i = 1/0; + printf("i = %d\n", i); +#endif + return 0; +} diff --git a/cppcheck-2.14.0/test/cli/unusedFunction/1.c b/cppcheck-2.14.0/test/cli/unusedFunction/1.c new file mode 100644 index 00000000..6e725814 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/unusedFunction/1.c @@ -0,0 +1,7 @@ +#include "3.h" + +// cppcheck-suppress unusedFunction +void f1() +{ + f3_1(); +} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/unusedFunction/2.c b/cppcheck-2.14.0/test/cli/unusedFunction/2.c new file mode 100644 index 00000000..c6bf77ce --- /dev/null +++ b/cppcheck-2.14.0/test/cli/unusedFunction/2.c @@ -0,0 +1,7 @@ +#include "3.h" + +// cppcheck-suppress unusedFunction +void f2() +{ + f3_2(); +} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/unusedFunction/3.c b/cppcheck-2.14.0/test/cli/unusedFunction/3.c new file mode 100644 index 00000000..3b517fe3 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/unusedFunction/3.c @@ -0,0 +1,3 @@ +void f3_1() {} +void f3_2() {} +void f3_3() {} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/unusedFunction/3.h b/cppcheck-2.14.0/test/cli/unusedFunction/3.h new file mode 100644 index 00000000..3b643b42 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/unusedFunction/3.h @@ -0,0 +1,3 @@ +void f3_1(); +void f3_2(); +void f3_3(); \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/unusedFunction/4.c b/cppcheck-2.14.0/test/cli/unusedFunction/4.c new file mode 100644 index 00000000..ae17ea51 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/unusedFunction/4.c @@ -0,0 +1,7 @@ +static void f4_0() {} + +// cppcheck-suppress unusedFunction +void f4_1() +{ + f4_0(); +} \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/unusedFunction/unusedFunction.cppcheck b/cppcheck-2.14.0/test/cli/unusedFunction/unusedFunction.cppcheck new file mode 100644 index 00000000..fd28f707 --- /dev/null +++ b/cppcheck-2.14.0/test/cli/unusedFunction/unusedFunction.cppcheck @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/test/cli/unused_function_test.py b/cppcheck-2.14.0/test/cli/unused_function_test.py new file mode 100644 index 00000000..4aa06ade --- /dev/null +++ b/cppcheck-2.14.0/test/cli/unused_function_test.py @@ -0,0 +1,214 @@ + +# python3 -m pytest test-unused_function_test.py + +import os +import json +from testutils import cppcheck + +__script_dir = os.path.dirname(os.path.abspath(__file__)) + +__project_dir = os.path.join(__script_dir, 'unusedFunction') +__project_dir_sep = __project_dir + os.path.sep + + +# TODO: make this a generic helper function +def __create_compdb(tmpdir, projpath): + compile_commands = os.path.join(tmpdir, 'compile_commands.json') + files = [] + for f in os.listdir(projpath): + files.append(f) + files.sort() + j = [] + for f in files: + j.append({ + 'directory': projpath, + 'file': os.path.join(projpath, f), + 'command': 'gcc -c {}'.format(f) + }) + with open(compile_commands, 'wt') as f: + f.write(json.dumps(j, indent=4)) + return compile_commands + + +def test_unused_functions(): + ret, stdout, stderr = cppcheck(['-q', '--template=simple', '--enable=unusedFunction', '--inline-suppr', '-j1', __project_dir]) + assert stdout.splitlines() == [] + assert stderr.splitlines() == [ + "{}3.c:3:0: style: The function 'f3_3' is never used. [unusedFunction]".format(__project_dir_sep) + ] + assert ret == 0, stdout + + +def test_unused_functions_j(): + ret, stdout, stderr = cppcheck(['-q', '--template=simple', '--enable=unusedFunction', '--inline-suppr', '-j2', __project_dir]) + assert stdout.splitlines() == [ + "cppcheck: unusedFunction check can't be used with '-j' option. Disabling unusedFunction check." + ] + assert stderr.splitlines() == [] + assert ret == 0, stdout + + +def test_unused_functions_project(): + ret, stdout, stderr = cppcheck(['-q', + '--template=simple', + '--enable=unusedFunction', + '--inline-suppr', + '--project={}'.format(os.path.join(__project_dir, 'unusedFunction.cppcheck')), + '-j1']) + assert stdout.splitlines() == [] + assert [ + "{}3.c:3:0: style: The function 'f3_3' is never used. [unusedFunction]".format(__project_dir_sep) + ] == stderr.splitlines() + assert ret == 0, stdout + + +def test_unused_functions_project_j(): + ret, stdout, stderr = cppcheck(['-q', + '--template=simple', + '--enable=unusedFunction', + '--inline-suppr', + '--project={}'.format(os.path.join(__project_dir, 'unusedFunction.cppcheck')), + '-j2']) + assert stdout.splitlines() == [ + "cppcheck: unusedFunction check can't be used with '-j' option. Disabling unusedFunction check." + ] + assert [] == stderr.splitlines() + assert ret == 0, stdout + + +def test_unused_functions_compdb(tmpdir): + compdb_file = __create_compdb(tmpdir, __project_dir) + ret, stdout, stderr = cppcheck(['-q', + '--template=simple', + '--enable=unusedFunction', + '--inline-suppr', + '--project={}'.format(compdb_file), + '-j1' + ]) + assert stdout.splitlines() == [] + assert stderr.splitlines() == [ + "{}3.c:3:0: style: The function 'f3_3' is never used. [unusedFunction]".format(__project_dir_sep) + ] + assert ret == 0, stdout + + +def test_unused_functions_compdb_j(tmpdir): + compdb_file = __create_compdb(tmpdir, __project_dir) + ret, stdout, stderr = cppcheck(['-q', + '--template=simple', + '--enable=unusedFunction', + '--inline-suppr', + '--project={}'.format(compdb_file), + '-j2' + ]) + assert stdout.splitlines() == [ + "cppcheck: unusedFunction check can't be used with '-j' option. Disabling unusedFunction check." + ] + assert stderr.splitlines() == [] + assert ret == 0, stdout + + +def test_unused_functions_builddir(tmpdir): + build_dir = os.path.join(tmpdir, 'b1') + os.mkdir(build_dir) + ret, stdout, stderr = cppcheck(['-q', '--template=simple', '--enable=unusedFunction', '--inline-suppr', '-j1', '--cppcheck-build-dir={}'.format(build_dir), __project_dir]) + assert stdout.splitlines() == [] + assert stderr.splitlines() == [ + "{}3.c:3:0: style: The function 'f3_3' is never used. [unusedFunction]".format(__project_dir_sep) + ] + assert ret == 0, stdout + + +# TODO: only f3_3 is unused +def test_unused_functions_builddir_j(tmpdir): + build_dir = os.path.join(tmpdir, 'b1') + os.mkdir(build_dir) + ret, stdout, stderr = cppcheck(['-q', '--template=simple', '--enable=unusedFunction', '--inline-suppr', '-j2', '--cppcheck-build-dir={}'.format(build_dir), __project_dir]) + assert stdout.splitlines() == [] + assert stderr.splitlines() == [ + "{}1.c:4:0: style: The function 'f1' is never used. [unusedFunction]".format(__project_dir_sep), + "{}2.c:4:0: style: The function 'f2' is never used. [unusedFunction]".format(__project_dir_sep), + "{}3.c:3:0: style: The function 'f3_3' is never used. [unusedFunction]".format(__project_dir_sep), + "{}4.c:4:0: style: The function 'f4_1' is never used. [unusedFunction]".format(__project_dir_sep) + ] + assert ret == 0, stdout + + +def test_unused_functions_builddir_project(tmpdir): + build_dir = os.path.join(tmpdir, 'b1') + os.mkdir(build_dir) + ret, stdout, stderr = cppcheck(['-q', + '--template=simple', + '--enable=unusedFunction', + '--inline-suppr', + '--project={}'.format(os.path.join(__project_dir, 'unusedFunction.cppcheck')), + '--cppcheck-build-dir={}'.format(build_dir), + '-j1']) + assert stdout.splitlines() == [] + assert stderr.splitlines() == [ + "{}3.c:3:0: style: The function 'f3_3' is never used. [unusedFunction]".format(__project_dir_sep) + ] + assert ret == 0, stdout + + +# TODO: only f3_3 is unused +def test_unused_functions_builddir_project_j(tmpdir): + build_dir = os.path.join(tmpdir, 'b1') + os.mkdir(build_dir) + ret, stdout, stderr = cppcheck(['-q', + '--template=simple', + '--enable=unusedFunction', + '--inline-suppr', + '--project={}'.format(os.path.join(__project_dir, 'unusedFunction.cppcheck')), + '--cppcheck-build-dir={}'.format(build_dir), + '-j2']) + assert stdout.splitlines() == [] + assert stderr.splitlines() == [ + "{}1.c:4:0: style: The function 'f1' is never used. [unusedFunction]".format(__project_dir_sep), + "{}2.c:4:0: style: The function 'f2' is never used. [unusedFunction]".format(__project_dir_sep), + "{}3.c:3:0: style: The function 'f3_3' is never used. [unusedFunction]".format(__project_dir_sep), + "{}4.c:4:0: style: The function 'f4_1' is never used. [unusedFunction]".format(__project_dir_sep) + ] + assert ret == 0, stdout + + +def test_unused_functions_builddir_compdb(tmpdir): + compdb_file = __create_compdb(tmpdir, __project_dir) + build_dir = os.path.join(tmpdir, 'b1') + os.mkdir(build_dir) + ret, stdout, stderr = cppcheck(['-q', + '--template=simple', + '--enable=unusedFunction', + '--inline-suppr', + '--project={}'.format(compdb_file), + '--cppcheck-build-dir={}'.format(build_dir), + '-j1' + ]) + assert stdout.splitlines() == [] + assert stderr.splitlines() == [ + "{}3.c:3:0: style: The function 'f3_3' is never used. [unusedFunction]".format(__project_dir_sep) + ] + assert ret == 0, stdout + + +# TODO: only f3_3 is unused +def test_unused_functions_builddir_compdb_j(tmpdir): + compdb_file = __create_compdb(tmpdir, __project_dir) + build_dir = os.path.join(tmpdir, 'b1') + os.mkdir(build_dir) + ret, stdout, stderr = cppcheck(['-q', + '--template=simple', + '--enable=unusedFunction', + '--inline-suppr', + '--project={}'.format(compdb_file), + '--cppcheck-build-dir={}'.format(build_dir), + '-j2' + ]) + assert stdout.splitlines() == [] + assert stderr.splitlines() == [ + "{}1.c:4:0: style: The function 'f1' is never used. [unusedFunction]".format(__project_dir_sep), + "{}2.c:4:0: style: The function 'f2' is never used. [unusedFunction]".format(__project_dir_sep), + "{}3.c:3:0: style: The function 'f3_3' is never used. [unusedFunction]".format(__project_dir_sep), + "{}4.c:4:0: style: The function 'f4_1' is never used. [unusedFunction]".format(__project_dir_sep) + ] + assert ret == 0, stdout diff --git a/cppcheck-2.14.0/test/fixture.cpp b/cppcheck-2.14.0/test/fixture.cpp new file mode 100644 index 00000000..484a5aeb --- /dev/null +++ b/cppcheck-2.14.0/test/fixture.cpp @@ -0,0 +1,492 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "fixture.h" + +#include "cppcheck.h" +#include "errortypes.h" +#include "options.h" +#include "redirect.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xml.h" + +/** + * TestRegistry + **/ +namespace { + struct CompareFixtures { + bool operator()(const TestFixture* lhs, const TestFixture* rhs) const { + return lhs->classname < rhs->classname; + } + }; +} +using TestSet = std::set; +namespace { + class TestRegistry { + TestSet _tests; + public: + + static TestRegistry &theInstance() { + static TestRegistry testreg; + return testreg; + } + + void addTest(TestFixture *t) { + _tests.insert(t); + } + + const TestSet &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; + +TestFixture::TestFixture(const char * const _name) + : classname(_name) +{ + TestRegistry::theInstance().addTest(this); +} + + +bool TestFixture::prepareTest(const char testname[]) +{ + mVerbose = false; + mTemplateFormat.clear(); + mTemplateLocation.clear(); + CppCheck::resetTimerResults(); + + prepareTestInternal(); + + // Check if tests should be executed + if (testToRun.empty() || testToRun == testname) { + // Tests will be executed - prepare them + mTestname = testname; + ++countTests; + if (quiet_tests) { + std::putchar('.'); // Use putchar to write through redirection of std::cout/cerr + std::fflush(stdout); + } else { + std::cout << classname << "::" << mTestname << std::endl; + } + return !dry_run; + } + return false; +} + +void TestFixture::teardownTest() +{ + teardownTestInternal(); + + { + const std::string s = errout_str(); + if (!s.empty()) + throw std::runtime_error("unconsumed ErrorLogger err: " + s); + } + { + const std::string s = output_str(); + if (!s.empty()) + throw std::runtime_error("unconsumed ErrorLogger out: " + s); + } +} + +std::string TestFixture::getLocationStr(const char * const filename, const unsigned int linenr) const +{ + return std::string(filename) + ':' + std::to_string(linenr) + '(' + classname + "::" + mTestname + ')'; +} + +static std::string writestr(const std::string &str, bool gccStyle = false) +{ + std::ostringstream ostr; + if (gccStyle) + ostr << '\"'; + for (std::string::const_iterator i = str.cbegin(); i != str.cend(); ++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 if (std::isprint(static_cast(*i))) + ostr << *i; + else + ostr << "\\x" << std::hex << short{*i}; + } + if (!str.empty() && !gccStyle) + ostr << std::endl; + else if (gccStyle) + ostr << '\"'; + return ostr.str(); +} + +bool TestFixture::assert_(const char * const filename, const unsigned int linenr, const bool condition) const +{ + if (!condition) { + ++fails_counter; + errmsg << getLocationStr(filename, linenr) << ": Assertion failed." << std::endl << "_____" << std::endl; + } + return condition; +} + +void TestFixture::assertEqualsFailed(const char* const filename, const unsigned int linenr, const std::string& expected, const std::string& actual, const std::string& msg) const +{ + ++fails_counter; + errmsg << getLocationStr(filename, linenr) << ": Assertion failed. " << std::endl + << "Expected: " << std::endl + << writestr(expected) << std::endl + << "Actual: " << std::endl + << writestr(actual) << std::endl; + if (!msg.empty()) + errmsg << "Hint:" << std::endl << msg << std::endl; + errmsg << "_____" << std::endl; +} + +bool TestFixture::assertEquals(const char * const filename, const unsigned int linenr, const std::string &expected, const std::string &actual, const std::string &msg) const +{ + if (expected != actual) { + assertEqualsFailed(filename, linenr, expected, actual, msg); + } + return expected == actual; +} + +std::string TestFixture::deleteLineNumber(const std::string &message) +{ + std::string result(message); + // delete line number in "...:NUMBER:..." + std::string::size_type pos = 0; + while ((pos = result.find(':', pos)) != std::string::npos) { + // get number + if (pos + 1 == result.find_first_of("0123456789", pos + 1)) { + const std::string::size_type after = result.find_first_not_of("0123456789", pos + 1); + if (after != std::string::npos + && result.at(after) == ':') { + // erase NUMBER + result.erase(pos + 1, after - pos - 1); + pos = after; + } else { + ++pos; + } + } else { + ++pos; + } + } + return result; +} + +void TestFixture::assertEqualsWithoutLineNumbers(const char * const filename, const unsigned int linenr, const std::string &expected, const std::string &actual, const std::string &msg) const +{ + assertEquals(filename, linenr, deleteLineNumber(expected), deleteLineNumber(actual), msg); +} + +bool TestFixture::assertEquals(const char * const filename, const unsigned int linenr, const char expected[], const std::string& actual, const std::string &msg) const +{ + return assertEquals(filename, linenr, std::string(expected), actual, msg); +} +bool TestFixture::assertEquals(const char * const filename, const unsigned int linenr, const char expected[], const char actual[], const std::string &msg) const +{ + return assertEquals(filename, linenr, std::string(expected), std::string(actual), msg); +} +bool TestFixture::assertEquals(const char * const filename, const unsigned int linenr, const std::string& expected, const char actual[], const std::string &msg) const +{ + return assertEquals(filename, linenr, expected, std::string(actual), msg); +} + +bool TestFixture::assertEquals(const char * const filename, const unsigned int linenr, const long long expected, const long long actual, const std::string &msg) const +{ + if (expected != actual) { + assertEquals(filename, linenr, std::to_string(expected), std::to_string(actual), msg); + } + return expected == actual; +} + +void TestFixture::assertEqualsDouble(const char * const filename, const unsigned int linenr, const double expected, const double actual, const double tolerance, const std::string &msg) const +{ + if (expected < (actual - tolerance) || expected > (actual + tolerance)) { + std::ostringstream ostr1; + ostr1 << expected; + std::ostringstream ostr2; + ostr2 << actual; + assertEquals(filename, linenr, ostr1.str(), ostr2.str(), msg); + } +} + +void TestFixture::todoAssertEquals(const char * const filename, const unsigned int linenr, + const std::string &wanted, + const std::string ¤t, + const std::string &actual) const +{ + if (wanted == actual) { + errmsg << getLocationStr(filename, linenr) << ": Assertion succeeded unexpectedly. " + << "Result: " << writestr(wanted, true) << std::endl << "_____" << std::endl; + + ++succeeded_todos_counter; + } else { + assertEquals(filename, linenr, current, actual); + ++todos_counter; + } +} + +void TestFixture::todoAssertEquals(const char* const filename, const unsigned int linenr, + const char wanted[], + const char current[], + const std::string& actual) const +{ + todoAssertEquals(filename, linenr, std::string(wanted), std::string(current), actual); +} + + +void TestFixture::todoAssertEquals(const char * const filename, const unsigned int linenr, const long long wanted, const long long current, const long long actual) const +{ + todoAssertEquals(filename, linenr, std::to_string(wanted), std::to_string(current), std::to_string(actual)); +} + +void TestFixture::assertThrow(const char * const filename, const unsigned int linenr) const +{ + ++fails_counter; + errmsg << getLocationStr(filename, linenr) << ": Assertion succeeded. " + << "The expected exception was thrown" << std::endl << "_____" << std::endl; + +} + +void TestFixture::assertThrowFail(const char * const filename, const unsigned int linenr) const +{ + ++fails_counter; + errmsg << getLocationStr(filename, linenr) << ": Assertion failed. " + << "The expected exception was not thrown" << std::endl << "_____" << std::endl; + +} + +void TestFixture::assertNoThrowFail(const char * const filename, const unsigned int linenr) const +{ + ++fails_counter; + + std::string ex_msg; + + try { + // cppcheck-suppress rethrowNoCurrentException + throw; + } + catch (const InternalError& e) { + ex_msg = e.errorMessage; + } + catch (const std::exception& e) { + ex_msg = e.what(); + } + catch (...) { + ex_msg = "unknown exception"; + } + + errmsg << getLocationStr(filename, linenr) << ": Assertion failed. " + << "Unexpected exception was thrown: " << ex_msg << std::endl << "_____" << std::endl; + +} + +void TestFixture::printHelp() +{ + std::cout << "Testrunner - run Cppcheck tests\n" + "\n" + "Syntax:\n" + " testrunner [OPTIONS] [TestClass::TestCase...]\n" + " run all test cases:\n" + " testrunner\n" + " run all test cases in TestClass:\n" + " testrunner TestClass\n" + " run TestClass::TestCase:\n" + " testrunner TestClass::TestCase\n" + " run all test cases in TestClass1 and TestClass2::TestCase:\n" + " testrunner TestClass1 TestClass2::TestCase\n" + "\n" + "Options:\n" + " -q Do not print the test cases that have run.\n" + " -h, --help Print this help.\n" + " -n Print no summaries.\n" + " -d Do not execute the tests.\n"; +} + +void TestFixture::run(const std::string &str) +{ + testToRun = str; + try { + if (quiet_tests) { + std::cout << '\n' << classname << ':'; + SUPPRESS; + run(); + } + else + run(); + } + catch (const InternalError& e) { + ++fails_counter; + errmsg << classname << "::" << mTestname << " - InternalError: " << e.errorMessage << std::endl; + } + catch (const std::exception& error) { + ++fails_counter; + errmsg << classname << "::" << mTestname << " - Exception: " << error.what() << std::endl; + } + catch (...) { + ++fails_counter; + errmsg << classname << "::" << mTestname << " - Unknown exception" << std::endl; + } +} + +void TestFixture::processOptions(const options& args) +{ + quiet_tests = args.quiet(); + dry_run = args.dry_run(); + exename = args.exe(); +} + +std::size_t TestFixture::runTests(const options& args) +{ + countTests = 0; + errmsg.str(""); + + for (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("::")); + } + + for (TestFixture * test : TestRegistry::theInstance().tests()) { + if (classname.empty() || test->classname == classname) { + test->processOptions(args); + test->run(testname); + } + } + } + + if (args.summary() && !args.dry_run()) { + 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(); + + if (args.summary() && !args.dry_run()) { + std::cerr << "Tests failed: " << fails_counter << std::endl << std::endl; + } + std::cerr << errmsg.str(); + + std::cerr.flush(); + return fails_counter + succeeded_todos_counter; +} + +void TestFixture::reportOut(const std::string & outmsg, Color /*c*/) +{ + mOutput << outmsg << std::endl; +} + +void TestFixture::reportErr(const ErrorMessage &msg) +{ + if (msg.severity == Severity::internal) + return; + if (msg.severity == Severity::information && msg.id == "normalCheckLevelMaxBranches") + return; + const std::string errormessage(msg.toString(mVerbose, mTemplateFormat, mTemplateLocation)); + mErrout << errormessage << std::endl; +} + +void TestFixture::setTemplateFormat(const std::string &templateFormat) +{ + if (templateFormat == "multiline") { + mTemplateFormat = "{file}:{line}:{severity}:{message}"; + mTemplateLocation = "{file}:{line}:note:{info}"; + } + else if (templateFormat == "simple") { // TODO: use the existing one in CmdLineParser + mTemplateFormat = "{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]"; + mTemplateLocation = ""; + } + else { + mTemplateFormat = templateFormat; + mTemplateLocation = ""; + } +} + +TestFixture::SettingsBuilder& TestFixture::SettingsBuilder::checkLevel(Settings::CheckLevel level) { + settings.setCheckLevel(level); + return *this; +} + +TestFixture::SettingsBuilder& TestFixture::SettingsBuilder::library(const char lib[]) { + if (REDUNDANT_CHECK && std::find(settings.libraries.cbegin(), settings.libraries.cend(), lib) != settings.libraries.cend()) + throw std::runtime_error("redundant setting: libraries (" + std::string(lib) + ")"); + // TODO: exename is not yet set + LOAD_LIB_2_EXE(settings.library, lib, fixture.exename.c_str()); + // strip extension + std::string lib_s(lib); + const std::string ext(".cfg"); + const auto pos = lib_s.find(ext); + if (pos != std::string::npos) + lib_s.erase(pos, ext.size()); + settings.libraries.emplace_back(lib_s); + return *this; +} + +TestFixture::SettingsBuilder& TestFixture::SettingsBuilder::platform(Platform::Type type) +{ + const std::string platformStr = Platform::toString(type); + + if (REDUNDANT_CHECK && settings.platform.type == type) + throw std::runtime_error("redundant setting: platform (" + platformStr + ")"); + + std::string errstr; + // TODO: exename is not yet set + if (!settings.platform.set(platformStr, errstr, {fixture.exename})) + throw std::runtime_error("platform '" + platformStr + "' not found"); + return *this; +} + +TestFixture::SettingsBuilder& TestFixture::SettingsBuilder::libraryxml(const char xmldata[], std::size_t len) +{ + tinyxml2::XMLDocument doc; + const tinyxml2::XMLError xml_error = doc.Parse(xmldata, len); + if (tinyxml2::XML_SUCCESS != xml_error) + throw std::runtime_error(std::string("loading XML data failed - ") + tinyxml2::XMLDocument::ErrorIDToName(xml_error)); + const Library::ErrorCode lib_error = settings.library.load(doc).errorcode; + if (lib_error != Library::ErrorCode::OK) + throw std::runtime_error("loading library XML failed - " + std::to_string(static_cast(lib_error))); + return *this; +} diff --git a/cppcheck-2.14.0/test/fixture.h b/cppcheck-2.14.0/test/fixture.h new file mode 100644 index 00000000..1defa6ac --- /dev/null +++ b/cppcheck-2.14.0/test/fixture.h @@ -0,0 +1,315 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#ifndef fixtureH +#define fixtureH + +#include "check.h" +#include "color.h" +#include "config.h" +#include "errorlogger.h" +#include "library.h" +#include "platform.h" +#include "settings.h" +#include "standards.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +class options; +class Tokenizer; +enum class Certainty; +enum class Severity; + +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; + bool mVerbose{}; + std::string mTemplateFormat; + std::string mTemplateLocation; + std::string mTestname; + +protected: + std::string exename; + std::string testToRun; + bool quiet_tests{}; + bool dry_run{}; + + virtual void run() = 0; + + bool prepareTest(const char testname[]); + virtual void prepareTestInternal() {} + void teardownTest(); + virtual void teardownTestInternal() {} + std::string getLocationStr(const char * const filename, const unsigned int linenr) const; + + bool assert_(const char * const filename, const unsigned int linenr, const bool condition) const; + + template + bool assertEquals(const char* const filename, const unsigned int linenr, const T& expected, const T& actual, const std::string& msg = emptyString) const { + if (expected != actual) { + std::ostringstream expectedStr; + expectedStr << expected; + std::ostringstream actualStr; + actualStr << actual; + + assertEqualsFailed(filename, linenr, expectedStr.str(), actualStr.str(), msg); + } + return expected == actual; + } + + template + bool assertEqualsEnum(const char* const filename, const unsigned int linenr, const T& expected, const T& actual, const std::string& msg = emptyString) const { + if (std::is_unsigned()) + return assertEquals(filename, linenr, static_cast(expected), static_cast(actual), msg); + return assertEquals(filename, linenr, static_cast(expected), static_cast(actual), msg); + } + + //Helper function to be called when an assertEquals assertion fails. + //Writes the appropriate failure message to errmsg and increments fails_counter + void assertEqualsFailed(const char* const filename, const unsigned int linenr, const std::string& expected, const std::string& actual, const std::string& msg) const; + + bool assertEquals(const char * const filename, const unsigned int linenr, const std::string &expected, const std::string &actual, const std::string &msg = emptyString) const; + void assertEqualsWithoutLineNumbers(const char * const filename, const unsigned int linenr, const std::string &expected, const std::string &actual, const std::string &msg = emptyString) const; + bool assertEquals(const char * const filename, const unsigned int linenr, const char expected[], const std::string& actual, const std::string &msg = emptyString) const; + bool assertEquals(const char * const filename, const unsigned int linenr, const char expected[], const char actual[], const std::string &msg = emptyString) const; + bool assertEquals(const char * const filename, const unsigned int linenr, const std::string& expected, const char actual[], const std::string &msg = emptyString) const; + bool assertEquals(const char * const filename, const unsigned int linenr, const long long expected, const long long actual, const std::string &msg = emptyString) const; + void assertEqualsDouble(const char * const filename, const unsigned int linenr, const double expected, const double actual, const double tolerance, const std::string &msg = emptyString) const; + + void todoAssertEquals(const char * const filename, const unsigned int linenr, const std::string &wanted, + const std::string ¤t, const std::string &actual) const; + void todoAssertEquals(const char * const filename, const unsigned int linenr, const char wanted[], + const char current[], const std::string &actual) const; + void todoAssertEquals(const char * const filename, const unsigned int linenr, const long long wanted, + const long long current, const long long actual) const; + void assertThrow(const char * const filename, const unsigned int linenr) const; + void assertThrowFail(const char * const filename, const unsigned int linenr) const; + void assertNoThrowFail(const char * const filename, const unsigned int linenr) const; + static std::string deleteLineNumber(const std::string &message); + + void setVerbose(bool v) { + mVerbose = v; + } + + void setTemplateFormat(const std::string &templateFormat); + + void setMultiline() { + setTemplateFormat("multiline"); + } + + void processOptions(const options& args); + + template + static T& getCheck() + { + for (Check *check : Check::instances()) { + //cppcheck-suppress useStlAlgorithm + if (T* c = dynamic_cast(check)) + return *c; + } + throw std::runtime_error("instance not found"); + } + + template + static void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) + { + Check& check = getCheck(); + check.runChecks(tokenizer, errorLogger); + } + + class SettingsBuilder + { + public: + explicit SettingsBuilder(const TestFixture &fixture) : fixture(fixture) {} + SettingsBuilder(const TestFixture &fixture, Settings settings) : fixture(fixture), settings(std::move(settings)) {} + + SettingsBuilder& severity(Severity sev, bool b = true) { + if (REDUNDANT_CHECK && settings.severity.isEnabled(sev) == b) + throw std::runtime_error("redundant setting: severity"); + settings.severity.setEnabled(sev, b); + return *this; + } + + SettingsBuilder& certainty(Certainty cert, bool b = true) { + if (REDUNDANT_CHECK && settings.certainty.isEnabled(cert) == b) + throw std::runtime_error("redundant setting: certainty"); + settings.certainty.setEnabled(cert, b); + return *this; + } + + SettingsBuilder& clang() { + if (REDUNDANT_CHECK && settings.clang) + throw std::runtime_error("redundant setting: clang"); + settings.clang = true; + return *this; + } + + SettingsBuilder& checkLibrary() { + if (REDUNDANT_CHECK && settings.checkLibrary) + throw std::runtime_error("redundant setting: checkLibrary"); + settings.checkLibrary = true; + return *this; + } + + SettingsBuilder& checkUnusedTemplates(bool b = true) { + if (REDUNDANT_CHECK && settings.checkUnusedTemplates == b) + throw std::runtime_error("redundant setting: checkUnusedTemplates"); + settings.checkUnusedTemplates = b; + return *this; + } + + SettingsBuilder& debugwarnings(bool b = true) { + if (REDUNDANT_CHECK && settings.debugwarnings == b) + throw std::runtime_error("redundant setting: debugwarnings"); + settings.debugwarnings = b; + return *this; + } + + SettingsBuilder& c(Standards::cstd_t std) { + // TODO: CLatest and C11 are the same - handle differently + //if (REDUNDANT_CHECK && settings.standards.c == std) + // throw std::runtime_error("redundant setting: standards.c"); + settings.standards.c = std; + return *this; + } + + SettingsBuilder& cpp(Standards::cppstd_t std) { + // TODO: CPPLatest and CPP20 are the same - handle differently + //if (REDUNDANT_CHECK && settings.standards.cpp == std) + // throw std::runtime_error("redundant setting: standards.cpp"); + settings.standards.cpp = std; + return *this; + } + + SettingsBuilder& checkLevel(Settings::CheckLevel level); + + SettingsBuilder& library(const char lib[]); + + SettingsBuilder& libraryxml(const char xmldata[], std::size_t len); + + SettingsBuilder& platform(Platform::Type type); + + SettingsBuilder& checkConfiguration() { + if (REDUNDANT_CHECK && settings.checkConfiguration) + throw std::runtime_error("redundant setting: checkConfiguration"); + settings.checkConfiguration = true; + return *this; + } + + SettingsBuilder& checkHeaders(bool b = true) { + if (REDUNDANT_CHECK && settings.checkHeaders == b) + throw std::runtime_error("redundant setting: checkHeaders"); + settings.checkHeaders = b; + return *this; + } + + Settings build() { + return std::move(settings); + } + private: + const TestFixture &fixture; + Settings settings; + + const bool REDUNDANT_CHECK = false; + }; + + SettingsBuilder settingsBuilder() const { + return SettingsBuilder(*this); + } + + SettingsBuilder settingsBuilder(Settings settings) const { + return SettingsBuilder(*this, std::move(settings)); + } + + std::string output_str() { + std::string s = mOutput.str(); + mOutput.str(""); + return s; + } + + std::string errout_str() { + std::string s = mErrout.str(); + mErrout.str(""); + return s; + } + + void ignore_errout() { + if (errout_str().empty()) + throw std::runtime_error("no errout to ignore"); + } + + const Settings settingsDefault; + +private: + std::ostringstream mOutput; + std::ostringstream mErrout; + + void reportOut(const std::string &outmsg, Color c = Color::Reset) override; + void reportErr(const ErrorMessage &msg) override; + void run(const std::string &str); + +public: + static void printHelp(); + const std::string classname; + + explicit TestFixture(const char * const _name); + + static std::size_t runTests(const options& args); +}; + +// TODO: most asserts do not actually assert i.e. do not return +#define TEST_CASE( NAME ) do { if (prepareTest(#NAME)) { setVerbose(false); try { NAME(); teardownTest(); } catch (...) { assertNoThrowFail(__FILE__, __LINE__); } } } while (false) +#define ASSERT( CONDITION ) if (!assert_(__FILE__, __LINE__, (CONDITION))) return +#define ASSERT_LOC( CONDITION, FILE_, LINE_ ) assert_(FILE_, LINE_, (CONDITION)) +#define CHECK_EQUALS( EXPECTED, ACTUAL ) assertEquals(__FILE__, __LINE__, (EXPECTED), (ACTUAL)) +// *INDENT-OFF* +#define ASSERT_EQUALS( EXPECTED, ACTUAL ) do { try { if (!assertEquals(__FILE__, __LINE__, (EXPECTED), (ACTUAL))) return; } catch (...) { assertNoThrowFail(__FILE__, __LINE__); } } while (false) +// *INDENT-ON* +#define ASSERT_EQUALS_WITHOUT_LINENUMBERS( EXPECTED, ACTUAL ) assertEqualsWithoutLineNumbers(__FILE__, __LINE__, EXPECTED, ACTUAL) +#define ASSERT_EQUALS_DOUBLE( EXPECTED, ACTUAL, TOLERANCE ) assertEqualsDouble(__FILE__, __LINE__, EXPECTED, ACTUAL, TOLERANCE) +#define ASSERT_EQUALS_MSG( EXPECTED, ACTUAL, MSG ) assertEquals(__FILE__, __LINE__, EXPECTED, ACTUAL, MSG) +#define ASSERT_EQUALS_ENUM( EXPECTED, ACTUAL ) if (!assertEqualsEnum(__FILE__, __LINE__, (EXPECTED), (ACTUAL))) return +#define ASSERT_THROW( CMD, EXCEPTION ) do { try { CMD; assertThrowFail(__FILE__, __LINE__); } catch (const EXCEPTION&) {} catch (...) { assertThrowFail(__FILE__, __LINE__); } } while (false) +#define ASSERT_THROW_EQUALS( CMD, EXCEPTION, EXPECTED ) do { try { CMD; assertThrowFail(__FILE__, __LINE__); } catch (const EXCEPTION&e) { assertEquals(__FILE__, __LINE__, EXPECTED, e.errorMessage); } catch (...) { assertThrowFail(__FILE__, __LINE__); } } while (false) +#define ASSERT_THROW_EQUALS_2( CMD, EXCEPTION, EXPECTED ) do { try { CMD; assertThrowFail(__FILE__, __LINE__); } catch (const EXCEPTION&e) { assertEquals(__FILE__, __LINE__, EXPECTED, e.what()); } catch (...) { assertThrowFail(__FILE__, __LINE__); } } while (false) +#define ASSERT_THROW_INTERNAL( CMD, TYPE ) do { try { CMD; assertThrowFail(__FILE__, __LINE__); } catch (const InternalError& e) { assertEqualsEnum(__FILE__, __LINE__, InternalError::TYPE, e.type); } catch (...) { assertThrowFail(__FILE__, __LINE__); } } while (false) +#define ASSERT_THROW_INTERNAL_EQUALS( CMD, TYPE, EXPECTED ) do { try { CMD; assertThrowFail(__FILE__, __LINE__); } catch (const InternalError& e) { assertEqualsEnum(__FILE__, __LINE__, InternalError::TYPE, e.type); assertEquals(__FILE__, __LINE__, EXPECTED, e.errorMessage); } catch (...) { assertThrowFail(__FILE__, __LINE__); } } while (false) +#define ASSERT_NO_THROW( CMD ) do { try { CMD; } catch (...) { assertNoThrowFail(__FILE__, __LINE__); } } while (false) +#define TODO_ASSERT_THROW( CMD, EXCEPTION ) do { try { CMD; } catch (const EXCEPTION&) {} catch (...) { assertThrow(__FILE__, __LINE__); } } while (false) +#define TODO_ASSERT( CONDITION ) do { const bool condition=(CONDITION); todoAssertEquals(__FILE__, __LINE__, true, false, condition); } while (false) +#define TODO_ASSERT_EQUALS( WANTED, CURRENT, ACTUAL ) todoAssertEquals(__FILE__, __LINE__, WANTED, CURRENT, ACTUAL) +#define EXPECT_EQ( EXPECTED, ACTUAL ) assertEquals(__FILE__, __LINE__, EXPECTED, ACTUAL) +#define REGISTER_TEST( CLASSNAME ) namespace { CLASSNAME instance_ ## CLASSNAME; } + +#define LOAD_LIB_2_EXE( LIB, NAME, EXE ) do { if (((LIB).load((EXE), NAME).errorcode != Library::ErrorCode::OK)) throw std::runtime_error("library '" + std::string(NAME) + "' not found"); } while (false) + +#define PLATFORM( P, T ) do { std::string errstr; assertEquals(__FILE__, __LINE__, true, P.set(Platform::toString(T), errstr, {exename}), errstr); } while (false) + +#endif // fixtureH diff --git a/cppcheck-2.14.0/test/helpers.cpp b/cppcheck-2.14.0/test/helpers.cpp new file mode 100644 index 00000000..a2fe44c0 --- /dev/null +++ b/cppcheck-2.14.0/test/helpers.cpp @@ -0,0 +1,183 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "helpers.h" + +#include "filelister.h" +#include "path.h" +#include "pathmatch.h" +#include "preprocessor.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#include + +class SuppressionList; + +const Settings SimpleTokenizer::s_settings; + +// TODO: better path-only usage +ScopedFile::ScopedFile(std::string name, const std::string &content, std::string path) + : mName(std::move(name)) + , mPath(Path::toNativeSeparators(std::move(path))) + , mFullPath(Path::join(mPath, mName)) +{ + if (!mPath.empty() && mPath != Path::getCurrentPath()) { + if (Path::isDirectory(mPath)) + throw std::runtime_error("ScopedFile(" + mFullPath + ") - directory already exists"); +#ifdef _WIN32 + if (!CreateDirectoryA(mPath.c_str(), nullptr)) + throw std::runtime_error("ScopedFile(" + mFullPath + ") - could not create directory"); +#else + if (mkdir(mPath.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) + throw std::runtime_error("ScopedFile(" + mFullPath + ") - could not create directory"); +#endif + } + + if (Path::isFile(mFullPath)) + throw std::runtime_error("ScopedFile(" + mFullPath + ") - file already exists"); + + std::ofstream of(mFullPath); + if (!of.is_open()) + throw std::runtime_error("ScopedFile(" + mFullPath + ") - could not open file"); + of << content; +} + +ScopedFile::~ScopedFile() { + const int remove_res = std::remove(mFullPath.c_str()); + if (remove_res != 0) { + std::cout << "ScopedFile(" << mFullPath + ") - could not delete file (" << remove_res << ")" << std::endl; + } + if (!mPath.empty() && mPath != Path::getCurrentPath()) { + // TODO: remove all files + // TODO: simplify the function call + // hack to be able to delete *.plist output files + std::list> files; + const std::string res = FileLister::addFiles(files, mPath, {".plist"}, false, PathMatch({})); + if (!res.empty()) { + std::cout << "ScopedFile(" << mPath + ") - generating file list failed (" << res << ")" << std::endl; + } + for (const auto &f : files) + { + const std::string &file = f.first; + const int rm_f_res = std::remove(file.c_str()); + if (rm_f_res != 0) { + std::cout << "ScopedFile(" << mPath + ") - could not delete '" << file << "' (" << rm_f_res << ")" << std::endl; + } + } + +#ifdef _WIN32 + if (!RemoveDirectoryA(mPath.c_str())) { + std::cout << "ScopedFile(" << mFullPath + ") - could not delete folder (" << GetLastError() << ")" << std::endl; + } +#else + const int rmdir_res = rmdir(mPath.c_str()); + if (rmdir_res == -1) { + const int err = errno; + std::cout << "ScopedFile(" << mFullPath + ") - could not delete folder (" << err << ")" << std::endl; + } +#endif + } +} + +// TODO: we should be using the actual Preprocessor implementation +std::string PreprocessorHelper::getcode(const Settings& settings, ErrorLogger& errorlogger, const std::string &filedata, const std::string &cfg, const std::string &filename, SuppressionList *inlineSuppression) +{ + std::map cfgcode = getcode(settings, errorlogger, filedata.c_str(), std::set{cfg}, filename, inlineSuppression); + const auto it = cfgcode.find(cfg); + if (it == cfgcode.end()) + return ""; + return it->second; +} + +std::map PreprocessorHelper::getcode(const Settings& settings, ErrorLogger& errorlogger, const char code[], const std::string &filename, SuppressionList *inlineSuppression) +{ + return getcode(settings, errorlogger, code, {}, filename, inlineSuppression); +} + +std::map PreprocessorHelper::getcode(const Settings& settings, ErrorLogger& errorlogger, const char code[], std::set cfgs, const std::string &filename, SuppressionList *inlineSuppression) +{ + simplecpp::OutputList outputList; + std::vector files; + + std::istringstream istr(code); + simplecpp::TokenList tokens(istr, files, Path::simplifyPath(filename), &outputList); + Preprocessor preprocessor(settings, errorlogger); + if (inlineSuppression) + preprocessor.inlineSuppressions(tokens, *inlineSuppression); + tokens.removeComments(); + preprocessor.simplifyPragmaAsm(&tokens); + preprocessor.removeComments(); + + preprocessor.reportOutput(outputList, true); + + if (Preprocessor::hasErrors(outputList)) + return {}; + + std::map cfgcode; + if (cfgs.empty()) + cfgs = preprocessor.getConfigs(tokens); + for (const std::string & config : cfgs) { + try { + // TODO: also preserve location information when #include exists - enabling that will fail since #line is treated like a regular token + cfgcode[config] = preprocessor.getcode(tokens, config, files, std::string(code).find("#file") != std::string::npos); + } catch (const simplecpp::Output &) { + cfgcode[config] = ""; + } + } + + return cfgcode; +} + +void PreprocessorHelper::preprocess(const char code[], std::vector &files, Tokenizer& tokenizer, ErrorLogger& errorlogger) +{ + preprocess(code, files, tokenizer, errorlogger, simplecpp::DUI()); +} + +void PreprocessorHelper::preprocess(const char code[], std::vector &files, Tokenizer& tokenizer, ErrorLogger& errorlogger, const simplecpp::DUI& dui) +{ + std::istringstream istr(code); + const simplecpp::TokenList tokens1(istr, files, files[0]); + + // Preprocess.. + simplecpp::TokenList tokens2(files); + std::map filedata; + simplecpp::preprocess(tokens2, tokens1, files, filedata, dui); + + // Tokenizer.. + tokenizer.list.createTokens(std::move(tokens2)); + + const Preprocessor preprocessor(tokenizer.getSettings(), errorlogger); + std::list directives = preprocessor.createDirectives(tokens1); + tokenizer.setDirectives(std::move(directives)); +} diff --git a/cppcheck-2.14.0/test/helpers.h b/cppcheck-2.14.0/test/helpers.h new file mode 100644 index 00000000..107db754 --- /dev/null +++ b/cppcheck-2.14.0/test/helpers.h @@ -0,0 +1,221 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef helpersH +#define helpersH + +#include "config.h" +#include "settings.h" +#include "standards.h" +#include "tokenize.h" +#include "tokenlist.h" + +#include +#include +#include +#include +#include +#include +#include + +class Token; +class SuppressionList; +class ErrorLogger; +namespace simplecpp { + struct DUI; +} + +// TODO: make Tokenizer private +class SimpleTokenizer : public Tokenizer { +public: + SimpleTokenizer(ErrorLogger& errorlogger, const char code[], bool cpp = true) + : Tokenizer{s_settings, errorlogger} + { + if (!tokenize(code, cpp)) + throw std::runtime_error("creating tokens failed"); + } + + SimpleTokenizer(const Settings& settings, ErrorLogger& errorlogger) + : Tokenizer{settings, errorlogger} + {} + + /* + Token* tokens() { + return Tokenizer::tokens(); + } + + const Token* tokens() const { + return Tokenizer::tokens(); + } + */ + + /** + * Tokenize code + * @param code The code + * @param cpp Indicates if the code is C++ + * @param configuration E.g. "A" for code where "#ifdef A" is true + * @return false if source code contains syntax errors + */ + bool tokenize(const char code[], + bool cpp = true, + const std::string &configuration = emptyString) + { + std::istringstream istr(code); + if (!list.createTokens(istr, cpp ? "test.cpp" : "test.c")) + return false; + + return simplifyTokens1(configuration); + } + +private: + // TODO. find a better solution + static const Settings s_settings; +}; + +class SimpleTokenList +{ +public: + + explicit SimpleTokenList(const char code[], Standards::Language lang = Standards::Language::CPP) + { + std::istringstream iss(code); + if (!list.createTokens(iss, lang)) + throw std::runtime_error("creating tokens failed"); + } + + Token* front() { + return list.front(); + } + + const Token* front() const { + return list.front(); + } + +private: + const Settings settings; + TokenList list{&settings}; +}; + + +class ScopedFile { +public: + ScopedFile(std::string name, const std::string &content, std::string path = ""); + ~ScopedFile(); + + const std::string& path() const + { + return mFullPath; + } + + const std::string& name() const { + return mName; + } + + ScopedFile(const ScopedFile&) = delete; + ScopedFile(ScopedFile&&) = delete; + ScopedFile& operator=(const ScopedFile&) = delete; + ScopedFile& operator=(ScopedFile&&) = delete; + +private: + const std::string mName; + const std::string mPath; + const std::string mFullPath; +}; + +class PreprocessorHelper +{ +public: + /** + * Get preprocessed code for a given configuration + * + * Note: for testing only. + * + * @param filedata file data including preprocessing 'if', 'define', etc + * @param cfg configuration to read out + * @param filename name of source file + * @param inlineSuppression the inline suppressions + */ + static std::string getcode(const Settings& settings, ErrorLogger& errorlogger, const std::string &filedata, const std::string &cfg, const std::string &filename, SuppressionList *inlineSuppression = nullptr); + static std::map getcode(const Settings& settings, ErrorLogger& errorlogger, const char code[], const std::string &filename = "file.c", SuppressionList *inlineSuppression = nullptr); + + static void preprocess(const char code[], std::vector &files, Tokenizer& tokenizer, ErrorLogger& errorlogger); + static void preprocess(const char code[], std::vector &files, Tokenizer& tokenizer, ErrorLogger& errorlogger, const simplecpp::DUI& dui); + +private: + static std::map getcode(const Settings& settings, ErrorLogger& errorlogger, const char code[], std::set cfgs, const std::string &filename = "file.c", SuppressionList *inlineSuppression = nullptr); +}; + +namespace cppcheck { + template + std::size_t count_all_of(const std::string& str, T sub) { + std::size_t n = 0; + std::string::size_type pos = 0; + while ((pos = str.find(sub, pos)) != std::string::npos) { + ++pos; + ++n; + } + return n; + } +} + +/* designated initialization helper + Usage: + struct S + { + int i; + }; + + const auto s = dinit(S, + $.i = 1 + ); + */ +#define dinit(T, ...) \ + ([&] { T ${}; __VA_ARGS__; return $; }()) + +// Default construct object to avoid bug in clang +// error: default member initializer for 'y' needed within definition of enclosing class 'X' outside of member functions +// see https://stackoverflow.com/questions/53408962 +struct make_default_obj +{ + template + operator T() const // NOLINT + { + return T{}; + } +}; + +inline std::string filter_valueflow(const std::string& s) { + bool filtered = false; + std::istringstream istr(s); + std::string ostr; + std::string errline; + while (std::getline(istr, errline)) { + if (errline.find("valueflow.cpp") != std::string::npos) + { + filtered = true; + continue; + } + ostr += errline; + ostr += '\n'; // TODO: last line might not contain a newline + } + if (!filtered) + throw std::runtime_error("no valueflow.cpp messages were filtered"); + return ostr; +} + +#endif // helpersH diff --git a/cppcheck-2.14.0/test/main.cpp b/cppcheck-2.14.0/test/main.cpp new file mode 100644 index 00000000..571b1269 --- /dev/null +++ b/cppcheck-2.14.0/test/main.cpp @@ -0,0 +1,44 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "color.h" +#include "options.h" +#include "preprocessor.h" +#include "fixture.h" + +#include + +int main(int argc, char *argv[]) +{ + // MS Visual C++ memory leak debug tracing +#if defined(_MSC_VER) && defined(_DEBUG) + _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF); +#endif + + Preprocessor::macroChar = '$'; // While macroChar is char(1) per default outside test suite, we require it to be a human-readable character here. + gDisableColors = true; + + options args(argc, argv); + + if (args.help()) { + TestFixture::printHelp(); + return EXIT_SUCCESS; + } + const std::size_t failedTestsCount = TestFixture::runTests(args); + return (failedTestsCount == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/cppcheck-2.14.0/test/options.cpp b/cppcheck-2.14.0/test/options.cpp new file mode 100644 index 00000000..c3667fcc --- /dev/null +++ b/cppcheck-2.14.0/test/options.cpp @@ -0,0 +1,67 @@ +// Cppcheck - A tool for static C/C++ code analysis +// Copyright (C) 2007-2024 Cppcheck team. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public 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* const argv[]) + : mWhichTests(argv + 1, argv + argc) + ,mQuiet(mWhichTests.count("-q") != 0) + ,mHelp(mWhichTests.count("-h") != 0 || mWhichTests.count("--help")) + ,mSummary(mWhichTests.count("-n") == 0) + ,mDryRun(mWhichTests.count("-d") != 0) + ,mExe(argv[0]) +{ + for (std::set::const_iterator it = mWhichTests.cbegin(); it != mWhichTests.cend();) { + if (!it->empty() && (((*it)[0] == '-') || (it->find("::") != std::string::npos && mWhichTests.count(it->substr(0, it->find("::")))))) + it = mWhichTests.erase(it); + else + ++it; + } + + if (mWhichTests.empty()) { + mWhichTests.insert(""); + } +} + +bool options::quiet() const +{ + return mQuiet; +} + +bool options::help() const +{ + return mHelp; +} + +bool options::summary() const +{ + return mSummary; +} + +bool options::dry_run() const +{ + return mDryRun; +} + +const std::set& options::which_test() const +{ + return mWhichTests; +} + +const std::string& options::exe() const +{ + return mExe; +} diff --git a/cppcheck-2.14.0/test/options.h b/cppcheck-2.14.0/test/options.h new file mode 100644 index 00000000..18df1dd7 --- /dev/null +++ b/cppcheck-2.14.0/test/options.h @@ -0,0 +1,58 @@ +// Cppcheck - A tool for static C/C++ code analysis +// Copyright (C) 2007-2024 Cppcheck team. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef OPTIONS_H +#define OPTIONS_H + +#include +#include + +/** + * @brief Class to parse command-line parameters for ./testrunner . + * Has getters for available switches and parameters. + * See test/testoptions.cpp for sample usage. + */ +class options { +public: + /** Call from main() to populate object */ + options(int argc, const char* const argv[]); + /** Don't print the name of each method being tested. */ + bool quiet() const; + /** Print help. */ + bool help() const; + /** Print summary. */ + bool summary() const; + /** Perform dry run. */ + bool dry_run() const; + /** Which test should be run. Empty string means 'all tests' */ + const std::set& which_test() const; + + const std::string& exe() const; + + options() = delete; + options(const options&) = delete; + options& operator =(const options&) = delete; + +private: + std::set mWhichTests; + const bool mQuiet; + const bool mHelp; + const bool mSummary; + const bool mDryRun; + std::string mExe; +}; + +#endif diff --git a/cppcheck-2.14.0/test/precompiled.h b/cppcheck-2.14.0/test/precompiled.h new file mode 100644 index 00000000..9fba4c2a --- /dev/null +++ b/cppcheck-2.14.0/test/precompiled.h @@ -0,0 +1,32 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +// IWYU pragma: begin_keep +#include "check.h" +#include "config.h" +#include "importproject.h" +#include "library.h" +#include "mathlib.h" +#include "settings.h" +#include "timer.h" +#include "token.h" +#include "tokenlist.h" +#include "tokenize.h" +// IWYU pragma: end_keep diff --git a/cppcheck-2.14.0/test/redirect.h b/cppcheck-2.14.0/test/redirect.h new file mode 100644 index 00000000..cf0d1d82 --- /dev/null +++ b/cppcheck-2.14.0/test/redirect.h @@ -0,0 +1,132 @@ +// Cppcheck - A tool for static C/C++ code analysis +// Copyright (C) 2007-2024 Cppcheck team. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#ifndef REDIRECT_H +#define REDIRECT_H + +#include +#include +#include +#include + +/** + * @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. + * NOTE: This is *not* thread-safe. + */ +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() noexcept(false) { + std::cout.rdbuf(_oldCout); // restore cout's original streambuf + std::cerr.rdbuf(_oldCerr); // restore cerrs's original streambuf + + { + const std::string s = _out.str(); + if (!s.empty()) + throw std::runtime_error("unconsumed stdout: " + s); // cppcheck-suppress exceptThrowInDestructor - FP #11031 + } + + { + const std::string s = _err.str(); + if (!s.empty()) + throw std::runtime_error("consumed stderr: " + s); + } + } + + /** Return what would be printed to cout. */ + std::string getOutput() { + std::string s = _out.str(); + _out.str(""); + return s; + } + + /** Return what would be printed to cerr. */ + std::string getErrout() { + std::string s = _err.str(); + _err.str(""); + return s; + } + +private: + std::ostringstream _out; + std::ostringstream _err; + std::streambuf *_oldCout; + std::streambuf *_oldCerr; +}; + +class SuppressOutput { +public: + /** Set up suppression, flushing anything in the pipes. */ + SuppressOutput() { + // 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(nullptr); // disable cout + std::cerr.rdbuf(nullptr); // disable cerr + } + + /** Revert cout and cerr behaviour */ + ~SuppressOutput() { + std::cout.rdbuf(_oldCout); // restore cout's original streambuf + std::cerr.rdbuf(_oldCerr); // restore cerrs's original streambuf + } + +private: + std::streambuf *_oldCout; + std::streambuf *_oldCerr; +}; + +#define REDIRECT RedirectOutputError redir +#define GET_REDIRECT_OUTPUT redir.getOutput() +#define GET_REDIRECT_ERROUT redir.getErrout() + +#define SUPPRESS SuppressOutput supprout + + +class RedirectInput { +public: + explicit RedirectInput(const std::string &input) : _in(input) { + _oldCin = std::cin.rdbuf(); // back up cin's streambuf + std::cin.rdbuf(_in.rdbuf()); // assign streambuf to cin + } + ~RedirectInput() noexcept { + std::cin.rdbuf(_oldCin); // restore cin's original streambuf + } +private: + std::istringstream _in; + std::streambuf* _oldCin; +}; + +#endif diff --git a/cppcheck-2.14.0/test/scripts/testrunner-single.sh b/cppcheck-2.14.0/test/scripts/testrunner-single.sh new file mode 100644 index 00000000..072779a2 --- /dev/null +++ b/cppcheck-2.14.0/test/scripts/testrunner-single.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +if [ -n "$1" ]; then + testrunner_bin=$1 +else + make -s -C "$SCRIPT_DIR/../.." -j"$(nproc)" testrunner # CXXFLAGS="-g -O2 -w -DHAVE_BOOST" + testrunner_bin=$SCRIPT_DIR/../../testrunner +fi + +ec=0 + +tests=$($testrunner_bin -d | cut -d'(' -f2 | cut -d')' -f1) +for test in $tests; do + $testrunner_bin -n "$test" || ec=1 +done + +exit $ec diff --git a/cppcheck-2.14.0/test/signal/CMakeLists.txt b/cppcheck-2.14.0/test/signal/CMakeLists.txt new file mode 100644 index 00000000..6bce413a --- /dev/null +++ b/cppcheck-2.14.0/test/signal/CMakeLists.txt @@ -0,0 +1,24 @@ +if (CMAKE_VERSION VERSION_EQUAL "3.13" OR CMAKE_VERSION VERSION_GREATER "3.13") + # target_link_options requires CMake 3.13 + + add_executable(test-signalhandler + test-signalhandler.cpp + ${PROJECT_SOURCE_DIR}/cli/signalhandler.cpp + ${PROJECT_SOURCE_DIR}/cli/stacktrace.cpp) + target_include_directories(test-signalhandler PRIVATE ${PROJECT_SOURCE_DIR}/cli ${PROJECT_SOURCE_DIR}/lib) + # names for static functions are omitted from trace + target_compile_options_safe(test-signalhandler -Wno-missing-declarations) + target_compile_options_safe(test-signalhandler -Wno-missing-prototypes) + # required for backtrace() to produce function names + target_link_options(test-signalhandler PRIVATE -rdynamic) + + add_executable(test-stacktrace + test-stacktrace.cpp + ${PROJECT_SOURCE_DIR}/cli/stacktrace.cpp) + target_include_directories(test-stacktrace PRIVATE ${PROJECT_SOURCE_DIR}/cli ${PROJECT_SOURCE_DIR}/lib) + # names for static functions are omitted from trace + target_compile_options_safe(test-stacktrace -Wno-missing-declarations) + target_compile_options_safe(test-stacktrace -Wno-missing-prototypes) + # required for backtrace() to produce function names + target_link_options(test-stacktrace PRIVATE -rdynamic) +endif() \ No newline at end of file diff --git a/cppcheck-2.14.0/test/signal/test-signalhandler.cpp b/cppcheck-2.14.0/test/signal/test-signalhandler.cpp new file mode 100644 index 00000000..023137c2 --- /dev/null +++ b/cppcheck-2.14.0/test/signal/test-signalhandler.cpp @@ -0,0 +1,78 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#if defined(USE_UNIX_SIGNAL_HANDLING) +#include "signalhandler.h" + +#include +#include +#include +#include + +// static functions are omitted from trace + +/*static*/ NORETURN void my_assert() +{ + assert(false); +} + +/*static*/ NORETURN void my_abort() +{ + abort(); +} + +/*static*/ void my_segv() +{ + // cppcheck-suppress nullPointer + ++*(int*)nullptr; +} + +/*static*/ void my_fpe() +{ +#if !defined(__APPLE__) + feenableexcept(FE_ALL_EXCEPT); // TODO: check result +#endif + std::feraiseexcept(FE_UNDERFLOW | FE_DIVBYZERO); // TODO: check result + // TODO: to generate this via code +} +#endif + +int main(int argc, const char * const argv[]) +{ +#if defined(USE_UNIX_SIGNAL_HANDLING) + if (argc != 2) + return 1; + + register_signal_handler(); + + if (strcmp(argv[1], "assert") == 0) + my_assert(); + else if (strcmp(argv[1], "abort") == 0) + my_abort(); + else if (strcmp(argv[1], "fpe") == 0) + my_fpe(); + else if (strcmp(argv[1], "segv") == 0) + my_segv(); + + return 0; +#else + return 1; +#endif +} diff --git a/cppcheck-2.14.0/test/signal/test-signalhandler.py b/cppcheck-2.14.0/test/signal/test-signalhandler.py new file mode 100644 index 00000000..2d9939f1 --- /dev/null +++ b/cppcheck-2.14.0/test/signal/test-signalhandler.py @@ -0,0 +1,84 @@ +import subprocess +import os +import sys +import pytest + +def _lookup_cppcheck_exe(exe_name): + # path the script is located in + script_path = os.path.dirname(os.path.realpath(__file__)) + + if sys.platform == "win32": + exe_name += ".exe" + + for base in (script_path + '/../../', './'): + for path in ('', 'bin/', 'bin/debug/'): + exe_path = base + path + exe_name + if os.path.isfile(exe_path): + print("using '{}'".format(exe_path)) + return exe_path + + return None + +def _call_process(arg): + exe = _lookup_cppcheck_exe('test-signalhandler') + if exe is None: + raise Exception('executable not found') + p = subprocess.Popen([exe, arg], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + comm = p.communicate() + stdout = comm[0].decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n') + stderr = comm[1].decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n') + return p.returncode, stdout, stderr + + +def test_assert(): + exitcode, stdout, stderr = _call_process('assert') + if sys.platform == "darwin": + assert stderr.startswith("Assertion failed: (false), function my_assert, file test-signalhandler.cpp, line "), stderr + else: + assert stderr.endswith("test-signalhandler.cpp:33: void my_assert(): Assertion `false' failed.\n"), stderr + lines = stdout.splitlines() + assert lines[0] == 'Internal error: cppcheck received signal SIGABRT - abort or assertion' + # no stacktrace of MacOs + if sys.platform != "darwin": + assert lines[1] == 'Callstack:' + assert lines[2].endswith('my_abort()'), lines[2] # TODO: wrong function + assert lines[len(lines)-1] == 'Please report this to the cppcheck developers!' + + +def test_abort(): + exitcode, stdout, stderr = _call_process('abort') + lines = stdout.splitlines() + assert lines[0] == 'Internal error: cppcheck received signal SIGABRT - abort or assertion' + # no stacktrace on MaCos + if sys.platform != "darwin": + assert lines[1] == 'Callstack:' + assert lines[2].endswith('my_segv()'), lines[2] # TODO: wrong function + assert lines[len(lines)-1] == 'Please report this to the cppcheck developers!' + + +def test_segv(): + exitcode, stdout, stderr = _call_process('segv') + assert stderr == '' + lines = stdout.splitlines() + if sys.platform == "darwin": + assert lines[0] == 'Internal error: cppcheck received signal SIGSEGV - SEGV_MAPERR (at 0x0).' + else: + assert lines[0] == 'Internal error: cppcheck received signal SIGSEGV - SEGV_MAPERR (reading at 0x0).' + # no stacktrace on MacOS + if sys.platform != "darwin": + assert lines[1] == 'Callstack:' + assert lines[2].endswith('my_segv()'), lines[2] # TODO: wrong function + assert lines[len(lines)-1] == 'Please report this to the cppcheck developers!' + + +# TODO: make this work +@pytest.mark.skip +def test_fpe(): + exitcode, stdout, stderr = _call_process('fpe') + assert stderr == '' + lines = stdout.splitlines() + assert lines[0].startswith('Internal error: cppcheck received signal SIGFPE - FPE_FLTDIV (at 0x7f'), lines[0] + assert lines[0].endswith(').'), lines[0] + assert lines[1] == 'Callstack:' + assert lines[2].endswith('my_fpe()'), lines[2] + assert lines[len(lines)-1] == 'Please report this to the cppcheck developers!' diff --git a/cppcheck-2.14.0/test/signal/test-stacktrace.cpp b/cppcheck-2.14.0/test/signal/test-stacktrace.cpp new file mode 100644 index 00000000..dd9a6848 --- /dev/null +++ b/cppcheck-2.14.0/test/signal/test-stacktrace.cpp @@ -0,0 +1,48 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#ifdef USE_UNIX_BACKTRACE_SUPPORT +#include "stacktrace.h" + +#include + +// static functions are omitted from trace + +/*static*/ void my_func_2() +{ + print_stacktrace(stdout, 0, true, -1, true); +} + +/*static*/ void my_func() +{ + my_func_2(); +} +#endif + +int main() +{ +#ifdef USE_UNIX_BACKTRACE_SUPPORT + my_func(); + + return 0; +#else + return 1; +#endif +} diff --git a/cppcheck-2.14.0/test/signal/test-stacktrace.py b/cppcheck-2.14.0/test/signal/test-stacktrace.py new file mode 100644 index 00000000..8f9e5763 --- /dev/null +++ b/cppcheck-2.14.0/test/signal/test-stacktrace.py @@ -0,0 +1,38 @@ +import subprocess +import os +import sys + +def _lookup_cppcheck_exe(exe_name): + # path the script is located in + script_path = os.path.dirname(os.path.realpath(__file__)) + + if sys.platform == "win32": + exe_name += ".exe" + + for base in (script_path + '/../../', './'): + for path in ('', 'bin/', 'bin/debug/'): + exe_path = base + path + exe_name + if os.path.isfile(exe_path): + print("using '{}'".format(exe_path)) + return exe_path + + return None + +def _call_process(): + exe = _lookup_cppcheck_exe('test-stacktrace') + if exe is None: + raise Exception('executable not found') + p = subprocess.Popen([exe], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + comm = p.communicate() + stdout = comm[0].decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n') + stderr = comm[1].decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n') + return p.returncode, stdout, stderr + + +def test_stack(): + exitcode, stdout, stderr = _call_process() + assert stderr == '' + lines = stdout.splitlines() + assert lines[0] == 'Callstack:' + assert lines[1].endswith('my_func_2()') + assert lines[2].endswith('my_func()') diff --git a/cppcheck-2.14.0/test/test64bit.cpp b/cppcheck-2.14.0/test/test64bit.cpp new file mode 100644 index 00000000..68c3b34b --- /dev/null +++ b/cppcheck-2.14.0/test/test64bit.cpp @@ -0,0 +1,303 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "check64bit.h" +#include "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "settings.h" + +class Test64BitPortability : public TestFixture { +public: + Test64BitPortability() : TestFixture("Test64BitPortability") {} + +private: + const Settings settings = settingsBuilder().severity(Severity::portability).library("std.cfg").build(); + + void run() override { + TEST_CASE(novardecl); + TEST_CASE(functionpar); + TEST_CASE(structmember); + TEST_CASE(ptrcompare); + TEST_CASE(ptrarithmetic); + TEST_CASE(returnIssues); + TEST_CASE(assignment); + } + +#define check(code) check_(code, __FILE__, __LINE__) + void check_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check char variable usage.. + Check64BitPortability check64BitPortability(&tokenizer, &settings, this); + check64BitPortability.pointerassignment(); + } + + void assignment() { + // #8631 + check("using CharArray = char[16];\n" + "void f() {\n" + " CharArray foo = \"\";\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct T { std::vector*a[2][2]; };\n" // #11560 + "void f(T& t, int i, int j) {\n" + " t.a[i][j] = new std::vector;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void novardecl() { + // if the variable declarations can't be seen then skip the warning + check("void foo()\n" + "{\n" + " a = p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void functionpar() { + check("int foo(int *p)\n" + "{\n" + " int a = p;\n" + " return a + 4;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (portability) Assigning a pointer to an integer is not portable.\n", errout_str()); + + check("int foo(int p[])\n" + "{\n" + " int a = p;\n" + " return a + 4;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (portability) Assigning a pointer to an integer is not portable.\n", errout_str()); + + check("int foo(int p[])\n" + "{\n" + " int *a = p;\n" + " return a;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (portability) Returning an address value in a function with integer return type is not portable.\n", errout_str()); + + check("void foo(int x)\n" + "{\n" + " int *p = x;\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (portability) Assigning an integer to a pointer is not portable.\n", errout_str()); + + check("int f(const char *p) {\n" // #4659 + " return 6 + p[2] * 256;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int foo(int *p) {\n" // #6096 + " bool a = p;\n" + " return a;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::array f();\n" + "void g() {\n" + " std::array a = f();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::array f(int x);\n" + "void g(int i) {\n" + " std::array a = f(i);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("typedef std::array Array;\n" + "Array f(int x);\n" + "void g(int i) {\n" + " Array a = f(i);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("typedef std::array Array;\n" + "Array f();\n" + "void g(int i) {\n" + " Array a = f();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" // #9951 + " enum E { E0 };\n" + " std::array g(S::E);\n" + "};\n" + "void f() {\n" + " std::array a = S::g(S::E::E0);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("char* f(char* p) {\n" + " return p ? p : 0;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void structmember() { + check("struct Foo { int *p; };\n" + "void f(struct Foo *foo) {\n" + " int i = foo->p;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (portability) Assigning a pointer to an integer is not portable.\n", errout_str()); + + check("struct S {\n" // #10145 + " enum class E { e1, e2 };\n" + " E e;\n" + " char* e1;\n" + "};\n" + "void f(S& s) {\n" + " s.e = S::E::e1;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void ptrcompare() { + // Ticket #2892 + check("void foo(int *p) {\n" + " int a = (p != NULL);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ptrarithmetic() { + // #3073 + check("void foo(int *p) {\n" + " int x = 10;\n" + " int *a = p + x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int *p) {\n" + " int x = 10;\n" + " int *a = x + p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int *p) {\n" + " int x = 10;\n" + " int *a = x * x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (portability) Assigning an integer to a pointer is not portable.\n", errout_str()); + + check("void foo(int *start, int *end) {\n" + " int len;\n" + " int len = end + 10 - start;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnIssues() { + check("void* foo(int i) {\n" + " return i;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (portability) Returning an integer in a function with pointer return type is not portable.\n", errout_str()); + + check("void* foo(int* i) {\n" + " return i;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void* foo() {\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int foo(int i) {\n" + " return i;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct Foo {};\n" + "\n" + "int* dostuff(Foo foo) {\n" + " return foo;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int foo(char* c) {\n" + " return c;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (portability) Returning an address value in a function with integer return type is not portable.\n", errout_str()); + + check("int foo(char* c) {\n" + " return 1+c;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (portability) Returning an address value in a function with integer return type is not portable.\n", errout_str()); + + check("std::string foo(char* c) {\n" + " return c;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int foo(char *a, char *b) {\n" // #4486 + " return a + 1 - b;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct s {\n" // 4642 + " int i;\n" + "};\n" + "int func(struct s *p) {\n" + " return 1 + p->i;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("static void __iomem *f(unsigned int port_no) {\n" + " void __iomem *mmio = hpriv->mmio;\n" + " return mmio + (port_no * 0x80);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #7247: don't check return statements in nested functions.. + check("int foo() {\n" + " struct {\n" + " const char * name() { return \"abc\"; }\n" + " } table;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #7451: Lambdas + check("const int* test(std::vector outputs, const std::string& text) {\n" + " auto it = std::find_if(outputs.begin(), outputs.end(),\n" + " [&](int ele) { return \"test\" == text; });\n" + " return nullptr;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" // #12159 + " std::future f() const {\n" + " return {};\n" + " }\n" + "};\n" + "int g() {\n" + " std::shared_ptr s = std::make_shared();\n" + " auto x = s->f();\n" + " return x.get();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } +}; + +REGISTER_TEST(Test64BitPortability) diff --git a/cppcheck-2.14.0/test/testanalyzerinformation.cpp b/cppcheck-2.14.0/test/testanalyzerinformation.cpp new file mode 100644 index 00000000..6727efb1 --- /dev/null +++ b/cppcheck-2.14.0/test/testanalyzerinformation.cpp @@ -0,0 +1,46 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "analyzerinfo.h" +#include "fixture.h" + +#include + +class TestAnalyzerInformation : public TestFixture, private AnalyzerInformation { +public: + TestAnalyzerInformation() : TestFixture("TestAnalyzerInformation") {} + +private: + + void run() override { + TEST_CASE(getAnalyzerInfoFile); + } + + void getAnalyzerInfoFile() const { + constexpr char filesTxt[] = "file1.a4::file1.c\n"; + std::istringstream f1(filesTxt); + ASSERT_EQUALS("file1.a4", getAnalyzerInfoFileFromFilesTxt(f1, "file1.c", "")); + std::istringstream f2(filesTxt); + ASSERT_EQUALS("file1.a4", getAnalyzerInfoFileFromFilesTxt(f2, "./file1.c", "")); + ASSERT_EQUALS("builddir/file1.c.analyzerinfo", AnalyzerInformation::getAnalyzerInfoFile("builddir", "file1.c", "")); + ASSERT_EQUALS("builddir/file1.c.analyzerinfo", AnalyzerInformation::getAnalyzerInfoFile("builddir", "some/path/file1.c", "")); + } +}; + +REGISTER_TEST(TestAnalyzerInformation) diff --git a/cppcheck-2.14.0/test/testassert.cpp b/cppcheck-2.14.0/test/testassert.cpp new file mode 100644 index 00000000..e8bb9e6a --- /dev/null +++ b/cppcheck-2.14.0/test/testassert.cpp @@ -0,0 +1,250 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "checkassert.h" +#include "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "settings.h" + +class TestAssert : public TestFixture { +public: + TestAssert() : TestFixture("TestAssert") {} + +private: + const Settings settings = settingsBuilder().severity(Severity::warning).build(); + +#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) + void check_(const char* file, int line, const char code[]) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check.. + runChecks(tokenizer, this); + } + + void run() override { + TEST_CASE(assignmentInAssert); + TEST_CASE(functionCallInAssert); + TEST_CASE(memberFunctionCallInAssert); + TEST_CASE(safeFunctionCallInAssert); + TEST_CASE(crash); + } + + + 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);"); + ASSERT_EQUALS("", errout_str()); + + check( + "int foo(int a) {\n" + " int b=a+1;\n" + " return b;\n" + "}\n" + "assert(foo(1) == 2);"); + ASSERT_EQUALS("", errout_str()); + } + + void functionCallInAssert() { + check( + "int a;\n" + "int foo() {\n" + " a = 1+2;\n" + " return a;\n" + "}\n" + "assert(foo() == 3);"); + 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" + "}"); + 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" + "}"); + 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" + "}"); + 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" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct Geometry {\n" + " int nbv;\n" + " int empty() { return (nbv == 0); }\n" + " void ReadGeometry();\n" + "};\n" + "\n" + "void Geometry::ReadGeometry() {\n" + " assert(empty());\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void memberFunctionCallInAssert() { + check("struct SquarePack {\n" + " void Foo();\n" + "};\n" + "void foo(SquarePack s) {\n" + " assert( s.Foo() );\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Assert statement calls a function which may have desired side effects: 'Foo'.\n", errout_str()); + + check("struct SquarePack {\n" + " int Foo() const;\n" + "};\n" + "void foo(SquarePack* s) {\n" + " assert( s->Foo() );\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct SquarePack {\n" + " static int Foo();\n" + "};\n" + "void foo(SquarePack* s) {\n" + " assert( s->Foo() );\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct SquarePack {\n" + "};\n" + "void foo(SquarePack* s) {\n" + " assert( s->Foo() );\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void assignmentInAssert() { + check("void f() {\n" + " int a; a = 0;\n" + " assert(a = 2);\n" + " return a;\n" + "}"); + 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" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int a, int b) {\n" + " assert(a == 2 && (b = 1));\n" + " return a;\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" + "}"); + 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" + "}"); + 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" + "}"); + 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" + "}"); + 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" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Assert statement modifies 'a'.\n", errout_str()); + + check("void f() {\n" + " assert(std::all_of(first, last, []() {\n" + " auto tmp = x.someValue();\n" + " auto const expected = someOtherValue;\n" + " return tmp == expected;\n" + " }));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void crash() { + check("void foo() {\n" + " assert(sizeof(struct { int a[x++]; })==sizeof(int));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" // #9790 + " assert(kad_bucket_hash(&(kad_guid) { .bytes = { 0 } }, & (kad_guid){.bytes = { 0 }}) == -1);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } +}; + +REGISTER_TEST(TestAssert) diff --git a/cppcheck-2.14.0/test/testastutils.cpp b/cppcheck-2.14.0/test/testastutils.cpp new file mode 100644 index 00000000..d7eb4541 --- /dev/null +++ b/cppcheck-2.14.0/test/testastutils.cpp @@ -0,0 +1,492 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "astutils.h" +#include "fixture.h" +#include "helpers.h" +#include "library.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenlist.h" + +#include + +class TestAstUtils : public TestFixture { +public: + TestAstUtils() : TestFixture("TestAstUtils") {} + +private: + + // TODO: test with C code + + void run() override { + TEST_CASE(findLambdaEndTokenTest); + TEST_CASE(findLambdaStartTokenTest); + TEST_CASE(isNullOperandTest); + TEST_CASE(isReturnScopeTest); + TEST_CASE(isSameExpressionCpp); + TEST_CASE(isSameExpressionC); + TEST_CASE(isVariableChangedTest); + TEST_CASE(isVariableChangedByFunctionCallTest); + TEST_CASE(isExpressionChangedTest); + TEST_CASE(nextAfterAstRightmostLeafTest); + TEST_CASE(isUsedAsBool); + } + +#define findLambdaEndToken(...) findLambdaEndToken_(__FILE__, __LINE__, __VA_ARGS__) + bool findLambdaEndToken_(const char* file, int line, const char code[], const char pattern[] = nullptr, bool checkNext = true) { + const Settings settings; + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + const Token* const tokStart = pattern ? Token::findsimplematch(tokenizer.tokens(), pattern, strlen(pattern)) : tokenizer.tokens(); + const Token * const tokEnd = (::findLambdaEndToken)(tokStart); + return tokEnd && (!checkNext || tokEnd->next() == nullptr); + } + + void findLambdaEndTokenTest() { + const Token* nullTok = nullptr; + ASSERT(nullptr == (::findLambdaEndToken)(nullTok)); + ASSERT_EQUALS(false, findLambdaEndToken("void f() { }")); + ASSERT_EQUALS(true, findLambdaEndToken("[]{ }")); + ASSERT_EQUALS(true, findLambdaEndToken("[]{ return 0; }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](){ }")); + ASSERT_EQUALS(true, findLambdaEndToken("[&](){ }")); + ASSERT_EQUALS(true, findLambdaEndToken("[&, i](){ }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) { return -1; }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](int a, int b) { return a + b; }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](int a, int b) mutable { return a + b; }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](int a, int b) constexpr { return a + b; }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) -> int { return -1; }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) mutable -> int { return -1; }")); + ASSERT_EQUALS(false, findLambdaEndToken("[](void) foo -> int { return -1; }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> int { return -1; }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> int* { return x; }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> const * int { return x; }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) mutable -> const * int { return x; }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> const ** int { return x; }")); + ASSERT_EQUALS(true, findLambdaEndToken("[](void) constexpr -> const * const* int { return x; }")); + ASSERT_EQUALS(false, findLambdaEndToken("int** a[] { new int*[2] { new int, new int} }", "[ ]")); + ASSERT_EQUALS(false, findLambdaEndToken("int** a[] { new int*[2] { new int, new int} }", "[ 2")); + ASSERT_EQUALS(false, findLambdaEndToken("shared_ptr sp{ new Type *[2] {new Type, new Type}, Deleter{ 2 } };", "[ 2")); + ASSERT_EQUALS(true, findLambdaEndToken("int i = 5 * []{ return 7; }();", "[", /*checkNext*/ false)); + } + +#define findLambdaStartToken(code) findLambdaStartToken_(code, __FILE__, __LINE__) + bool findLambdaStartToken_(const char code[], const char* file, int line) { + SimpleTokenizer tokenizer(settingsDefault, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + const Token * const tokStart = (::findLambdaStartToken)(tokenizer.list.back()); + return tokStart && tokStart == tokenizer.list.front(); + } + + void findLambdaStartTokenTest() { + ASSERT(nullptr == (::findLambdaStartToken)(nullptr)); + ASSERT_EQUALS(false, findLambdaStartToken("void f() { }")); + ASSERT_EQUALS(true, findLambdaStartToken("[]{ }")); + ASSERT_EQUALS(true, findLambdaStartToken("[]{ return 0; }")); + ASSERT_EQUALS(true, findLambdaStartToken("[](){ }")); + ASSERT_EQUALS(true, findLambdaStartToken("[&](){ }")); + ASSERT_EQUALS(true, findLambdaStartToken("[&, i](){ }")); + ASSERT_EQUALS(true, findLambdaStartToken("[](void) { return -1; }")); + ASSERT_EQUALS(true, findLambdaStartToken("[](int a, int b) { return a + b; }")); + ASSERT_EQUALS(true, findLambdaStartToken("[](int a, int b) mutable { return a + b; }")); + ASSERT_EQUALS(true, findLambdaStartToken("[](int a, int b) constexpr { return a + b; }")); + ASSERT_EQUALS(true, findLambdaStartToken("[](void) -> int { return -1; }")); + ASSERT_EQUALS(true, findLambdaStartToken("[](void) mutable -> int { return -1; }")); + ASSERT_EQUALS(false, findLambdaStartToken("[](void) foo -> int { return -1; }")); + ASSERT_EQUALS(true, findLambdaStartToken("[](void) constexpr -> int { return -1; }")); + ASSERT_EQUALS(true, findLambdaStartToken("[](void) constexpr -> int* { return x; }")); + ASSERT_EQUALS(true, findLambdaStartToken("[](void) constexpr -> const * int { return x; }")); + ASSERT_EQUALS(true, findLambdaStartToken("[](void) mutable -> const * int { return x; }")); + ASSERT_EQUALS(true, findLambdaStartToken("[](void) constexpr -> const ** int { return x; }")); + ASSERT_EQUALS(true, findLambdaStartToken("[](void) constexpr -> const * const* int { return x; }")); + } + +#define isNullOperand(code) isNullOperand_(code, __FILE__, __LINE__) + bool isNullOperand_(const char code[], const char* file, int line) { + SimpleTokenizer tokenizer(settingsDefault, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + return (::isNullOperand)(tokenizer.tokens()); + } + + void isNullOperandTest() { + ASSERT_EQUALS(true, isNullOperand("(void*)0;")); + ASSERT_EQUALS(true, isNullOperand("(void*)0U;")); + ASSERT_EQUALS(true, isNullOperand("(void*)0x0LL;")); + ASSERT_EQUALS(true, isNullOperand("NULL;")); + ASSERT_EQUALS(true, isNullOperand("nullptr;")); + ASSERT_EQUALS(true, isNullOperand("(void*)NULL;")); + ASSERT_EQUALS(true, isNullOperand("static_cast(0);")); + ASSERT_EQUALS(false, isNullOperand("0;")); + ASSERT_EQUALS(false, isNullOperand("(void*)0.0;")); + ASSERT_EQUALS(false, isNullOperand("(void*)1;")); + } + +#define isReturnScope(code, offset) isReturnScope_(code, offset, __FILE__, __LINE__) + bool isReturnScope_(const char code[], int offset, const char* file, int line) { + SimpleTokenizer tokenizer(settingsDefault, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + const Token * const tok = (offset < 0) + ? tokenizer.list.back()->tokAt(1+offset) + : tokenizer.tokens()->tokAt(offset); + return (isReturnScope)(tok, settingsDefault.library); + } + + void isReturnScopeTest() { + ASSERT_EQUALS(true, isReturnScope("void f() { if (a) { return; } }", -2)); + ASSERT_EQUALS(true, isReturnScope("int f() { if (a) { return {}; } }", -2)); // #8891 + ASSERT_EQUALS(true, isReturnScope("std::string f() { if (a) { return std::string{}; } }", -2)); // #8891 + ASSERT_EQUALS(true, isReturnScope("std::string f() { if (a) { return std::string{\"\"}; } }", -2)); // #8891 + ASSERT_EQUALS(true, isReturnScope("void f() { if (a) { return (ab){0}; } }", -2)); // #7103 + ASSERT_EQUALS(false, isReturnScope("void f() { if (a) { return (ab){0}; } }", -4)); // #7103 + ASSERT_EQUALS(true, isReturnScope("void f() { if (a) { {throw new string(x);}; } }", -4)); // #7144 + ASSERT_EQUALS(true, isReturnScope("void f() { if (a) { {throw new string(x);}; } }", -2)); // #7144 + ASSERT_EQUALS(false, isReturnScope("void f() { [=]() { return data; }; }", -1)); + ASSERT_EQUALS(true, isReturnScope("auto f() { return [=]() { return data; }; }", -1)); + ASSERT_EQUALS(true, isReturnScope("auto f() { return [=]() { return data; }(); }", -1)); + ASSERT_EQUALS(false, isReturnScope("auto f() { [=]() { return data; }(); }", -1)); + + ASSERT_EQUALS(true, isReturnScope("void negativeTokenOffset() { return; }", -1)); + ASSERT_EQUALS(false, isReturnScope("void zeroTokenOffset() { return; }", 0)); + ASSERT_EQUALS(true, isReturnScope("void positiveTokenOffset() { return; }", 7)); + } + +#define isSameExpression(...) isSameExpression_(__FILE__, __LINE__, __VA_ARGS__) + bool isSameExpression_(const char* file, int line, const char code[], const char tokStr1[], const char tokStr2[], bool cpp) { + Library library; + SimpleTokenizer tokenizer(settingsDefault, *this); + ASSERT_LOC(tokenizer.tokenize(code, cpp), file, line); + const Token * const tok1 = Token::findsimplematch(tokenizer.tokens(), tokStr1, strlen(tokStr1)); + const Token * const tok2 = Token::findsimplematch(tok1->next(), tokStr2, strlen(tokStr2)); + return (isSameExpression)(false, tok1, tok2, library, false, true, nullptr); + } + + void isSameExpressionTestInternal(bool cpp) { + ASSERT_EQUALS(true, isSameExpression("x = 1 + 1;", "1", "1", cpp)); + ASSERT_EQUALS(false, isSameExpression("x = 1 + 1u;", "1", "1u", cpp)); + ASSERT_EQUALS(true, isSameExpression("x = 1.0 + 1.0;", "1.0", "1.0", cpp)); + ASSERT_EQUALS(false, isSameExpression("x = 1.0f + 1.0;", "1.0f", "1.0", cpp)); + ASSERT_EQUALS(false, isSameExpression("x = 1L + 1;", "1L", "1", cpp)); + ASSERT_EQUALS(true, isSameExpression("x = 0.0f + 0x0p+0f;", "0.0f", "0x0p+0f", cpp)); + ASSERT_EQUALS(true, isSameExpression("x < x;", "x", "x", cpp)); + ASSERT_EQUALS(false, isSameExpression("x < y;", "x", "y", cpp)); + ASSERT_EQUALS(true, isSameExpression("(x + 1) < (x + 1);", "+", "+", cpp)); + ASSERT_EQUALS(false, isSameExpression("(x + 1) < (x + 1L);", "+", "+", cpp)); + ASSERT_EQUALS(!cpp, isSameExpression("(1 + x) < (x + 1);", "+", "+", cpp)); + ASSERT_EQUALS(false, isSameExpression("(1.0l + x) < (1.0 + x);", "+", "+", cpp)); + ASSERT_EQUALS(!cpp, isSameExpression("(0.0 + x) < (x + 0x0p+0);", "+", "+", cpp)); + ASSERT_EQUALS(true, isSameExpression("void f() {double y = 1e1; (x + y) < (x + 10.0); } ", "+", "+", cpp)); + ASSERT_EQUALS(!cpp, isSameExpression("void f() {double y = 1e1; (x + 10.0) < (y + x); } ", "+", "+", cpp)); + ASSERT_EQUALS(true, isSameExpression("void f() {double y = 1e1; double z = 10.0; (x + y) < (x + z); } ", "+", "+", cpp)); + ASSERT_EQUALS(true, isSameExpression("A + A", "A", "A", cpp)); + + // the remaining test cases are not valid C code + if (!cpp) + return; + + //https://trac.cppcheck.net/ticket/9700 + ASSERT_EQUALS(true, isSameExpression("A::B + A::B;", "::", "::", cpp)); + ASSERT_EQUALS(false, isSameExpression("A::B + A::C;", "::", "::", cpp)); + ASSERT_EQUALS(true, isSameExpression("A::B* get() { if(x) return new A::B(true); else return new A::B(true); }", "new", "new", cpp)); + ASSERT_EQUALS(false, isSameExpression("A::B* get() { if(x) return new A::B(true); else return new A::C(true); }", "new", "new", cpp)); + } + + void isSameExpressionCpp() { + isSameExpressionTestInternal(true); + } + + void isSameExpressionC() { + isSameExpressionTestInternal(false); + } + +#define isVariableChanged(code, startPattern, endPattern) isVariableChanged_(code, startPattern, endPattern, __FILE__, __LINE__) + bool isVariableChanged_(const char code[], const char startPattern[], const char endPattern[], const char* file, int line) { + SimpleTokenizer tokenizer(settingsDefault, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + const Token * const tok1 = Token::findsimplematch(tokenizer.tokens(), startPattern, strlen(startPattern)); + const Token * const tok2 = Token::findsimplematch(tokenizer.tokens(), endPattern, strlen(endPattern)); + return (isVariableChanged)(tok1, tok2, 1, false, &settingsDefault); + } + + void isVariableChangedTest() { + // #8211 - no lhs for >> , do not crash + isVariableChanged("void f() {\n" + " int b;\n" + " if (b) { (int)((INTOF(8))result >> b); }\n" + "}", "if", "}"); + // #9235 + ASSERT_EQUALS(false, + isVariableChanged("void f() {\n" + " int &a = a;\n" + "}\n", + "= a", + "}")); + + ASSERT_EQUALS(false, isVariableChanged("void f(const A& a) { a.f(); }", "{", "}")); + ASSERT_EQUALS(true, + isVariableChanged("void g(int*);\n" + "void f(int x) { g(&x); }\n", + "{", + "}")); + + ASSERT_EQUALS(false, isVariableChanged("const int A[] = { 1, 2, 3 };", "[", "]")); + } + +#define isVariableChangedByFunctionCall(code, pattern, inconclusive) isVariableChangedByFunctionCall_(code, pattern, inconclusive, __FILE__, __LINE__) + bool isVariableChangedByFunctionCall_(const char code[], const char pattern[], bool *inconclusive, const char* file, int line) { + SimpleTokenizer tokenizer(settingsDefault, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + const Token * const argtok = Token::findmatch(tokenizer.tokens(), pattern); + ASSERT_LOC(argtok, file, line); + int indirect = (argtok->variable() && argtok->variable()->isArray()); + return (isVariableChangedByFunctionCall)(argtok, indirect, &settingsDefault, inconclusive); + } + + void isVariableChangedByFunctionCallTest() { + const char *code; + bool inconclusive; + + // #8271 - template method + code = "void f(int x) {\n" + " a(x);\n" + "}"; + inconclusive = false; + ASSERT_EQUALS(false, isVariableChangedByFunctionCall(code, "x ) ;", &inconclusive)); + ASSERT_EQUALS(true, inconclusive); + + code = "int f(int x) {\n" + "return int(x);\n" + "}\n"; + inconclusive = false; + ASSERT_EQUALS(false, isVariableChangedByFunctionCall(code, "x ) ;", &inconclusive)); + ASSERT_EQUALS(false, inconclusive); + + code = "void g(int* p);\n" + "void f(int x) {\n" + " return g(&x);\n" + "}\n"; + inconclusive = false; + ASSERT_EQUALS(true, isVariableChangedByFunctionCall(code, "x ) ;", &inconclusive)); + ASSERT_EQUALS(false, inconclusive); + + code = "void g(const int* p);\n" + "void f(int x) {\n" + " return g(&x);\n" + "}\n"; + inconclusive = false; + ASSERT_EQUALS(false, isVariableChangedByFunctionCall(code, "x ) ;", &inconclusive)); + ASSERT_EQUALS(false, inconclusive); + + code = "void g(int** pp);\n" + "void f(int* p) {\n" + " return g(&p);\n" + "}\n"; + inconclusive = false; + ASSERT_EQUALS(true, isVariableChangedByFunctionCall(code, "p ) ;", &inconclusive)); + ASSERT_EQUALS(false, inconclusive); + + code = "void g(int* const* pp);\n" + "void f(int* p) {\n" + " return g(&p);\n" + "}\n"; + inconclusive = false; + ASSERT_EQUALS(false, isVariableChangedByFunctionCall(code, "p ) ;", &inconclusive)); + ASSERT_EQUALS(false, inconclusive); + + code = "void g(int a[2]);\n" + "void f() {\n" + " int b[2] = {};\n" + " return g(b);\n" + "}\n"; + inconclusive = false; + ASSERT_EQUALS(true, isVariableChangedByFunctionCall(code, "b ) ;", &inconclusive)); + ASSERT_EQUALS(false, inconclusive); + + code = "void g(const int a[2]);\n" + "void f() {\n" + " int b[2] = {};\n" + " return g(b);\n" + "}\n"; + inconclusive = false; + TODO_ASSERT_EQUALS(false, true, isVariableChangedByFunctionCall(code, "b ) ;", &inconclusive)); + ASSERT_EQUALS(false, inconclusive); + + code = "void g(std::array a);\n" + "void f() {\n" + " std::array b = {};\n" + " return g(b);\n" + "}\n"; + inconclusive = false; + ASSERT_EQUALS(false, isVariableChangedByFunctionCall(code, "b ) ;", &inconclusive)); + ASSERT_EQUALS(false, inconclusive); + + code = "void g(std::array& a);\n" + "void f() {\n" + " std::array b = {};\n" + " return g(b);\n" + "}\n"; + inconclusive = false; + ASSERT_EQUALS(true, isVariableChangedByFunctionCall(code, "b ) ;", &inconclusive)); + ASSERT_EQUALS(false, inconclusive); + + code = "void g(const std::array& a);\n" + "void f() {\n" + " std::array b = {};\n" + " return g(b);\n" + "}\n"; + inconclusive = false; + ASSERT_EQUALS(false, isVariableChangedByFunctionCall(code, "b ) ;", &inconclusive)); + ASSERT_EQUALS(false, inconclusive); + + code = "void g(std::array* p);\n" + "void f() {\n" + " std::array b = {};\n" + " return g(&b);\n" + "}\n"; + inconclusive = false; + ASSERT_EQUALS(true, isVariableChangedByFunctionCall(code, "b ) ;", &inconclusive)); + ASSERT_EQUALS(false, inconclusive); + + code = "struct S {};\n" + "void g(S);\n" + "void f(S* s) {\n" + " g(*s);\n" + "}\n"; + inconclusive = false; + ASSERT_EQUALS(false, isVariableChangedByFunctionCall(code, "s ) ;", &inconclusive)); + ASSERT_EQUALS(false, inconclusive); + + code = "struct S {};\n" + "void g(const S&);\n" + "void f(S* s) {\n" + " g(*s);\n" + "}\n"; + inconclusive = false; + ASSERT_EQUALS(false, isVariableChangedByFunctionCall(code, "s ) ;", &inconclusive)); + ASSERT_EQUALS(false, inconclusive); + } + +#define isExpressionChanged(code, var, startPattern, endPattern) \ + isExpressionChanged_(code, var, startPattern, endPattern, __FILE__, __LINE__) + bool isExpressionChanged_(const char code[], + const char var[], + const char startPattern[], + const char endPattern[], + const char* file, + int line) + { + const Settings settings = settingsBuilder().library("std.cfg").build(); + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + const Token* const start = Token::findsimplematch(tokenizer.tokens(), startPattern, strlen(startPattern)); + const Token* const end = Token::findsimplematch(start, endPattern, strlen(endPattern)); + const Token* const expr = Token::findsimplematch(tokenizer.tokens(), var, strlen(var)); + return (findExpressionChanged)(expr, start, end, &settings); + } + + void isExpressionChangedTest() + { + ASSERT_EQUALS(true, isExpressionChanged("void f(std::map& m) { m[0]; }", "m [", "{", "}")); + ASSERT_EQUALS(false, isExpressionChanged("void f(const A& a) { a.f(); }", "a .", "{", "}")); + + ASSERT_EQUALS(true, + isExpressionChanged("void g(int*);\n" + "void f(int x) { g(&x); }\n", + "x", + ") {", + "}")); + + ASSERT_EQUALS(true, + isExpressionChanged("struct S { void f(); int i; };\n" + "void call_f(S& s) { (s.*(&S::f))(); }\n", + "s .", + "{ (", + "}")); + } + +#define nextAfterAstRightmostLeaf(code, parentPattern, rightPattern) nextAfterAstRightmostLeaf_(code, parentPattern, rightPattern, __FILE__, __LINE__) + bool nextAfterAstRightmostLeaf_(const char code[], const char parentPattern[], const char rightPattern[], const char* file, int line) { + SimpleTokenizer tokenizer(settingsDefault, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + const Token * tok = Token::findsimplematch(tokenizer.tokens(), parentPattern, strlen(parentPattern)); + return Token::simpleMatch((::nextAfterAstRightmostLeaf)(tok), rightPattern, strlen(rightPattern)); + } + + void nextAfterAstRightmostLeafTest() { + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("void f(int a, int b) { int x = a + b; }", "=", "; }")); + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a); }", "=", "; }")); + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a)[b]; }", "=", "; }")); + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(g(a)[b]); }", "=", "; }")); + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(g(a)[b] + a); }", "=", "; }")); + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a)[b + 1]; }", "=", "; }")); + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("void f() { int a; int b; int x = [](int a){}; }", "=", "; }")); + + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = a + b; }", "+", "; }")); + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a)[b + 1]; }", "+", "] ; }")); + ASSERT_EQUALS(true, nextAfterAstRightmostLeaf("int * g(int); void f(int a, int b) { int x = g(a + 1)[b]; }", "+", ") [")); + } + + enum class Result {False, True, Fail}; + + Result isUsedAsBool(const char code[], const char pattern[]) { + SimpleTokenizer tokenizer(settingsDefault, *this); + if (!tokenizer.tokenize(code)) + return Result::Fail; + const Token * const argtok = Token::findmatch(tokenizer.tokens(), pattern); + if (!argtok) + return Result::Fail; + return ::isUsedAsBool(argtok) ? Result::True : Result::False; + } + + void isUsedAsBool() { + ASSERT(Result::True == isUsedAsBool("void f() { bool b = true; }", "b")); + ASSERT(Result::False ==isUsedAsBool("void f() { int i = true; }", "i")); + ASSERT(Result::True == isUsedAsBool("void f() { int i; if (i) {} }", "i )")); + ASSERT(Result::True == isUsedAsBool("void f() { int i; while (i) {} }", "i )")); + ASSERT(Result::True == isUsedAsBool("void f() { int i; for (;i;) {} }", "i ; )")); + ASSERT(Result::False == isUsedAsBool("void f() { int i; for (;;i) {} }", "i )")); + ASSERT(Result::False == isUsedAsBool("void f() { int i; for (i;;) {} }", "i ; ; )")); + ASSERT(Result::True == isUsedAsBool("void f() { int i; for (int j=0; i; ++j) {} }", "i ; ++")); + ASSERT(Result::False == isUsedAsBool("void f() { int i; if (i == 2) {} }", "i ==")); + ASSERT(Result::False == isUsedAsBool("void f() { int i; if (i == true) {} }", "i ==")); + ASSERT(Result::False == isUsedAsBool("void f() { int i,j; if (i == (j&&f())) {} }", "i ==")); + ASSERT(Result::True == isUsedAsBool("void f() { int i; if (!i == 0) {} }", "i ==")); + ASSERT(Result::True == isUsedAsBool("void f() { int i; if (!i) {} }", "i )")); + ASSERT(Result::True == isUsedAsBool("void f() { int i; if (!!i) {} }", "i )")); + ASSERT(Result::True == isUsedAsBool("void f() { int i; if (i && f()) {} }", "i &&")); + ASSERT(Result::True == isUsedAsBool("void f() { int i; int j = i && f(); }", "i &&")); + ASSERT(Result::False == isUsedAsBool("void f() { int i; if (i & f()) {} }", "i &")); + ASSERT(Result::True == isUsedAsBool("void f() { int i; if (static_cast(i)) {} }", "i )")); + ASSERT(Result::True == isUsedAsBool("void f() { int i; if ((bool)i) {} }", "i )")); + ASSERT(Result::True == isUsedAsBool("void f() { int i; if (1+static_cast(i)) {} }", "i )")); + ASSERT(Result::True == isUsedAsBool("void f() { int i; if (1+(bool)i) {} }", "i )")); + ASSERT(Result::False == isUsedAsBool("void f() { int i; if (1+static_cast(i)) {} }", "i )")); + ASSERT(Result::True == isUsedAsBool("void f() { int i; if (1+!static_cast(i)) {} }", "i )")); + ASSERT(Result::False == isUsedAsBool("void f() { int i; if (1+(int)i) {} }", "i )")); + ASSERT(Result::False == isUsedAsBool("void f() { int i; if (i + 2) {} }", "i +")); + ASSERT(Result::True == isUsedAsBool("void f() { int i; bool b = i; }", "i ; }")); + ASSERT(Result::True == isUsedAsBool("void f(bool b); void f() { int i; f(i); }","i )")); + ASSERT(Result::False == isUsedAsBool("void f() { int *i; if (*i) {} }", "i )")); + ASSERT(Result::True == isUsedAsBool("void f() { int *i; if (*i) {} }", "* i )")); + ASSERT(Result::True == isUsedAsBool("int g(); void h(bool); void f() { h(g()); }", "( ) )")); + ASSERT(Result::True == isUsedAsBool("int g(int); void h(bool); void f() { h(g(0)); }", "( 0 ) )")); + } +}; + +REGISTER_TEST(TestAstUtils) diff --git a/cppcheck-2.14.0/test/testautovariables.cpp b/cppcheck-2.14.0/test/testautovariables.cpp new file mode 100644 index 00000000..4951d8c0 --- /dev/null +++ b/cppcheck-2.14.0/test/testautovariables.cpp @@ -0,0 +1,4558 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "checkautovariables.h" +#include "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "settings.h" + +class TestAutoVariables : public TestFixture { +public: + TestAutoVariables() : TestFixture("TestAutoVariables") {} + +private: + const Settings settings = settingsBuilder().severity(Severity::warning).severity(Severity::style).library("std.cfg").library("qt.cfg").build(); + +#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) + void check_(const char* file, int line, const char code[], bool inconclusive = true, bool cpp = true) { + const Settings settings1 = settingsBuilder(settings).certainty(Certainty::inconclusive, inconclusive).build(); + + // Tokenize.. + SimpleTokenizer tokenizer(settings1, *this); + ASSERT_LOC(tokenizer.tokenize(code, cpp), file, line); + + runChecks(tokenizer, this); + } + + void run() override { + TEST_CASE(testautovar1); + TEST_CASE(testautovar2); + TEST_CASE(testautovar3); // ticket #2925 + TEST_CASE(testautovar4); // ticket #2928 + TEST_CASE(testautovar5); // ticket #2926 + TEST_CASE(testautovar6); // ticket #2931 + TEST_CASE(testautovar7); // ticket #3066 + TEST_CASE(testautovar8); + TEST_CASE(testautovar9); + TEST_CASE(testautovar10); // ticket #2930 - void f(char *p) { p = '\0'; } + TEST_CASE(testautovar11); // ticket #4641 - fp, assign local struct member address to function parameter + TEST_CASE(testautovar12); // ticket #5024 - crash + TEST_CASE(testautovar13); // ticket #5537 - crash + TEST_CASE(testautovar14); // ticket #4776 - assignment of function parameter, goto + TEST_CASE(testautovar15); // ticket #6538 + TEST_CASE(testautovar16); // ticket #8114 + TEST_CASE(testautovar_array1); + TEST_CASE(testautovar_array2); + TEST_CASE(testautovar_array3); + TEST_CASE(testautovar_normal); // "normal" token list that does not remove casts etc + TEST_CASE(testautovar_ptrptr); // ticket #6956 + TEST_CASE(testautovar_return1); + TEST_CASE(testautovar_return2); + TEST_CASE(testautovar_return3); + TEST_CASE(testautovar_return4); + TEST_CASE(testautovar_return5); + TEST_CASE(testautovar_extern); + TEST_CASE(testautovar_reassigned); + TEST_CASE(testinvaliddealloc); + TEST_CASE(testinvaliddealloc_input); // Ticket #10600 + TEST_CASE(testinvaliddealloc_string); + TEST_CASE(testinvaliddealloc_C); + TEST_CASE(testassign1); // Ticket #1819 + TEST_CASE(testassign2); // Ticket #2765 + + TEST_CASE(assignAddressOfLocalArrayToGlobalPointer); + TEST_CASE(assignAddressOfLocalVariableToGlobalPointer); + TEST_CASE(assignAddressOfLocalVariableToMemberVariable); + + TEST_CASE(returnLocalVariable1); + TEST_CASE(returnLocalVariable2); + TEST_CASE(returnLocalVariable3); // &x[0] + TEST_CASE(returnLocalVariable4); // x+y + TEST_CASE(returnLocalVariable5); // cast + TEST_CASE(returnLocalVariable6); // valueflow + + // return reference.. + TEST_CASE(returnReference1); + TEST_CASE(returnReference2); + TEST_CASE(returnReference3); + TEST_CASE(returnReference4); + TEST_CASE(returnReference5); + TEST_CASE(returnReference6); + TEST_CASE(returnReference7); + TEST_CASE(returnReference8); + TEST_CASE(returnReference9); + TEST_CASE(returnReference10); + TEST_CASE(returnReference11); + TEST_CASE(returnReference12); + TEST_CASE(returnReference13); + TEST_CASE(returnReference14); + TEST_CASE(returnReference15); // #9432 + TEST_CASE(returnReference16); // #9433 + TEST_CASE(returnReference16); // #9433 + TEST_CASE(returnReference17); // #9461 + TEST_CASE(returnReference18); // #9482 + TEST_CASE(returnReference19); // #9597 + TEST_CASE(returnReference20); // #9536 + TEST_CASE(returnReference21); // #9530 + TEST_CASE(returnReference22); + TEST_CASE(returnReference23); + TEST_CASE(returnReference24); // #10098 + TEST_CASE(returnReference25); // #10983 + TEST_CASE(returnReference26); + TEST_CASE(returnReference27); + TEST_CASE(returnReferenceFunction); + TEST_CASE(returnReferenceContainer); + TEST_CASE(returnReferenceLiteral); + TEST_CASE(returnReferenceCalculation); + TEST_CASE(returnReferenceLambda); + TEST_CASE(returnReferenceInnerScope); + TEST_CASE(returnReferenceRecursive); + TEST_CASE(extendedLifetime); + + TEST_CASE(danglingReference); + TEST_CASE(danglingTempReference); + + // global namespace + TEST_CASE(testglobalnamespace); + + TEST_CASE(returnParameterAddress); + + TEST_CASE(testconstructor); // ticket #5478 - crash + + TEST_CASE(variableIsUsedInScope); // ticket #5599 crash in variableIsUsedInScope() + + TEST_CASE(danglingLifetimeLambda); + TEST_CASE(danglingLifetimeContainer); + TEST_CASE(danglingLifetimeContainerView); + TEST_CASE(danglingLifetimeUniquePtr); + TEST_CASE(danglingLifetime); + TEST_CASE(danglingLifetimeFunction); + TEST_CASE(danglingLifetimeUserConstructor); + TEST_CASE(danglingLifetimeAggegrateConstructor); + TEST_CASE(danglingLifetimeInitList); + TEST_CASE(danglingLifetimeImplicitConversion); + TEST_CASE(danglingTemporaryLifetime); + TEST_CASE(danglingLifetimeBorrowedMembers); + TEST_CASE(danglingLifetimeClassMemberFunctions); + TEST_CASE(invalidLifetime); + TEST_CASE(deadPointer); + TEST_CASE(splitNamespaceAuto); // crash #10473 + } + + + + void testautovar1() { + check("void func1(int **res)\n" + "{\n" + " int num = 2;\n" + " *res = #\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Address of local auto-variable assigned to a function parameter.\n", errout_str()); + + check("void func1(int **res)\n" + "{\n" + " int num = 2;\n" + " res = #\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout_str()); + + check("void func1(int **res)\n" + "{\n" + " int num = 2;\n" + " foo.res = #\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testautovar2() { + check("class Fred {\n" + " void func1(int **res);\n" + "}\n" + "void Fred::func1(int **res)\n" + "{\n" + " int num = 2;\n" + " *res = #\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Address of local auto-variable assigned to a function parameter.\n", errout_str()); + + check("class Fred {\n" + " void func1(int **res);\n" + "}\n" + "void Fred::func1(int **res)\n" + "{\n" + " int num = 2;\n" + " res = #\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout_str()); + + check("class Fred {\n" + " void func1(int **res);\n" + "}\n" + "void Fred::func1(int **res)\n" + "{\n" + " int num = 2;\n" + " foo.res = #\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testautovar3() { // ticket #2925 + check("void foo(int **p)\n" + "{\n" + " int x[100];\n" + " *p = x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Address of local auto-variable assigned to a function parameter.\n", errout_str()); + } + + void testautovar4() { // ticket #2928 + check("void foo(int **p)\n" + "{\n" + " static int x[100];\n" + " *p = x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testautovar5() { // ticket #2926 + check("void foo(struct AB *ab)\n" + "{\n" + " char a;\n" + " ab->a = &a;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error, 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" + "}", false); + ASSERT_EQUALS("[test.cpp:6]: (error) Address of local auto-variable assigned to a function parameter.\n", errout_str()); + + 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 = &p_fp->i;\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + + 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 = &fp.f->i;\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + } + + void testautovar10() { // #2930 - assignment of function parameter + check("void foo(char* p) {\n" + " p = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout_str()); + + check("void foo(int b) {\n" + " b = foo(b);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Assignment of function parameter has no effect outside the function.\n", errout_str()); + + check("void foo(int b) {\n" + " b += 1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Assignment of function parameter has no effect outside the function.\n", errout_str()); + + check("void foo(std::string s) {\n" + " s = foo(b);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Assignment of function parameter has no effect outside the function.\n", errout_str()); + + check("void foo(char* p) {\n" // don't warn for self assignment, there is another warning for this + " p = p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(char* p) {\n" + " if (!p) p = buf;\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(char* p) {\n" + " if (!p) p = buf;\n" + " do_something(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(char* p) {\n" + " while (!p) p = buf;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(char* p) {\n" + " p = 0;\n" + " asm(\"somecmd\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(Foo* p) {\n" + " p = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout_str()); + + check("class Foo {};\n" + "void foo(Foo p) {\n" + " p = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Assignment of function parameter has no effect outside the function.\n", errout_str()); + + check("void foo(Foo p) {\n" + " p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int& p) {\n" + " p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("double foo(double d) {\n" // #5005 + " int i = d;\n" + " d = i;\n" + " return d;" + "}",false); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int* ptr) {\n" // #4793 + " ptr++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout_str()); + + check("void foo(int* ptr) {\n" // #3177 + " --ptr;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?\n", errout_str()); + + check("void foo(struct S* const x) {\n" // #7839 + " ++x->n;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testautovar11() { // #4641 - fp, assign local struct member address to function parameter + check("struct A {\n" + " char (*data)[10];\n" + "};\n" + "void foo(char** p) {\n" + " struct A a = bar();\n" + " *p = &(*a.data)[0];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " char data[10];\n" + "};\n" + "void foo(char** p) {\n" + " struct A a = bar();\n" + " *p = &a.data[0];\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Address of local auto-variable assigned to a function parameter.\n", errout_str()); + + check("void f(char **out) {\n" + " struct S *p = glob;\n" + " *out = &p->data;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #4998 + check("void f(s8**out) {\n" + " s8 *p;\n" // <- p is pointer => no error + " *out = &p[1];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(s8**out) {\n" + " s8 p[10];\n" // <- p is array => error + " *out = &p[1];\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n", errout_str()); + } + + void testautovar12() { // Ticket #5024, #5050 - Crash on invalid input + ASSERT_THROW_INTERNAL(check("void f(int* a) { a = }"), SYNTAX); + 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" + "};"); + } + + void testautovar14() { // Ticket #4776 + check("void f(int x) {\n" + "label:" + " if (x>0) {\n" + " x = x >> 1;\n" + " goto label;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testautovar15() { // Ticket #6538 + check("static const float4 darkOutline(0.05f, 0.05f, 0.05f, 0.95f);\n" + "static const float darkLuminosity = 0.05 +\n" + " 0.0722f * math::powf(darkOutline[2], 2.2);\n" + "const float4* ChooseOutlineColor(const float4& textColor) {\n" + " const float lumdiff = something;\n" + " if (lumdiff > 5.0f)\n" + " return &darkOutline;\n" + " return 0;\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + } + + void testautovar16() { // Ticket #8114 + check("void f(const void* ptr, bool* result) {\n" + " int dummy;\n" + " *result = (&dummy < ptr);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testautovar_array1() { + check("void func1(int* arr[2])\n" + "{\n" + " int num=2;" + " arr[0]=#\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n", errout_str()); + } + + void testautovar_array2() { + check("class Fred {\n" + " void func1(int* arr[2]);\n" + "}\n" + "void Fred::func1(int* arr[2])\n" + "{\n" + " int num=2;" + " arr[0]=#\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Address of local auto-variable assigned to a function parameter.\n", errout_str()); + } + + void testautovar_array3() { + check("int main(int argc, char* argv[]) {\n" // #11732 + " std::string a = \"abc\";\n" + " argv[0] = &a[0];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(char* c[]) {\n" + " char a[] = \"abc\";\n" + " c[0] = a;\n" + "}\n" + "void g(char* c[]) {\n" + " std::string a = \"abc\";\n" + " c[0] = a.data();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n" + "[test.cpp:7]: (error) Address of local auto-variable assigned to a function parameter.\n", + errout_str()); + + check("struct String {\n" + " String& operator=(const char* c) { m = c; return *this; }\n" + " std::string m;\n" + "};\n" + "struct T { String s; };\n" + "void f(T* t) {\n" + " char a[] = \"abc\";\n" + " t->s = &a[0];\n" + "}\n" + "void g(std::string* s) {\n" + " char a[] = \"abc\";\n" + " *s = &a[0];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void testautovar_normal() { + check("void f(XmDestinationCallbackStruct *ds)\n" + "{\n" + " XPoint DropPoint;\n" + " ds->location_data = (XtPointer *)&DropPoint;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error, inconclusive) Address of local auto-variable assigned to a function parameter.\n", errout_str()); + } + + void testautovar_ptrptr() { // #6596 + check("void remove_duplicate_matches (char **matches) {\n" + " char dead_slot;\n" + " matches[0] = (char *)&dead_slot;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Address of local auto-variable assigned to a function parameter.\n", errout_str()); + } + + void testautovar_return1() { + check("int* func1()\n" + "{\n" + " int num=2;" + " return #" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3] -> [test.cpp:3]: (error) Returning pointer to local variable 'num' that will be invalid when returning.\n", errout_str()); + } + + void testautovar_return2() { + check("class Fred {\n" + " int* func1();\n" + "}\n" + "int* Fred::func1()\n" + "{\n" + " int num=2;" + " return #" + "}"); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:6] -> [test.cpp:6]: (error) Returning pointer to local variable 'num' that will be invalid when returning.\n", errout_str()); + } + + void testautovar_return3() { + // #2975 - FP + check("void** f()\n" + "{\n" + " void *&value = tls[id];" + " return &value;" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testautovar_return4() { + // #8058 - FP ignore return in lambda + check("void foo() {\n" + " int cond2;\n" + " dostuff([&cond2]() { return &cond2; });\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testautovar_return5() { // #11465 + check("struct S {};\n" + "const std::type_info* f() {\n" + " return &typeid(S);\n" + "}\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 testautovar_reassigned() { + check("void foo(cb* pcb) {\n" + " int root0;\n" + " pcb->root0 = &root0;\n" + " dostuff(pcb);\n" + " pcb->root0 = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(cb* pcb) {\n" + " int root0;\n" + " pcb->root0 = &root0;\n" + " dostuff(pcb);\n" + " if (condition) return;\n" // <- not reassigned => error + " pcb->root0 = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error, inconclusive) Address of local auto-variable assigned to a function parameter.\n", errout_str()); + + check("void foo(cb* pcb) {\n" + " int root0;\n" + " pcb->root0 = &root0;\n" + " dostuff(pcb);\n" + " if (condition)\n" + " pcb->root0 = 0;\n" // <- conditional reassign => error + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error, inconclusive) Address of local auto-variable assigned to a function parameter.\n", errout_str()); + + check("struct S { int *p; };\n" + "void g(struct S* s) {\n" + " int a[10];\n" + " s->p = a;\n" + " a[0] = 0;\n" + "}\n" + "void f() {\n" + " struct S s;\n" + " g(&s);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4]: (error) Address of local auto-variable assigned to a function parameter.\n" + "[test.cpp:4]: (error) Address of local auto-variable assigned to a function parameter.\n", // duplicate + 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(char * ptr) {\n" + " free(ptr);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void func1() {\n" + " char* tmp1[256];\n" + " init(tmp1);\n" + " delete tmp1[34];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void func1() {\n" + " static char tmp1[256];\n" + " char *p = tmp1;\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Deallocation of a static variable (tmp1) results in undefined behaviour.\n", errout_str()); + + check("char tmp1[256];\n" + "void func1() {\n" + " char *p; if (x) p = tmp1;\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Deallocation of a global variable (tmp1) results in undefined behaviour.\n", errout_str()); + + check("void f()\n" + "{\n" + " char psz_title[10];\n" + " {\n" + " char *psz_title = 0;\n" + " abc(0, psz_title);\n" + " free(psz_title);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #2298 new check: passing stack-address to free() + check("int main() {\n" + " int *p = malloc(4);\n" + " free(&p);\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of an auto-variable results in undefined behaviour.\n", errout_str()); + check("int main() {\n" + " int i;\n" + " free(&i);\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of an auto-variable results in undefined behaviour.\n", errout_str()); + + // #5732 + check("int main() {\n" + " long (*pKoeff)[256] = new long[9][256];\n" + " delete[] pKoeff;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int main() {\n" + " long *pKoeff[256];\n" + " delete[] pKoeff;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of an auto-variable results in undefined behaviour.\n", errout_str()); + + check("int main() {\n" + " long *pKoeff[256];\n" + " free (pKoeff);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of an auto-variable results in undefined behaviour.\n", errout_str()); + + check("void foo() {\n" + " const intPtr& intref = Getter();\n" + " delete intref;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void test() {\n" + " MyObj& obj = *new MyObj;\n" + " delete &obj;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #6506 + check("struct F {\n" + " void free(void*) {}\n" + "};\n" + "void foo() {\n" + " char c1[1];\n" + " F().free(c1);\n" + " char *c2 = 0;\n" + " F().free(&c2);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class foo {\n" + " void free(void* );\n" + " void someMethod() {\n" + " char **dst_copy = NULL;\n" + " free(&dst_copy);\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // #6551 + check("bool foo( ) {\n" + " SwTxtFld * pTxtFld = GetFldTxtAttrAt();\n" + " delete static_cast(&pTxtFld->GetAttr());\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #8910 + check("void f() {\n" + " char stack[512];\n" + " RGNDATA *data;\n" + " if (data_size > sizeof (stack)) data = malloc (data_size);\n" + " else data = (RGNDATA *)stack;\n" + " if ((char *)data != stack) free (data);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #8923 + check("void f(char **args1, char *args2[]) {\n" + " free((char **)args1);\n" + " free((char **)args2);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #10097 + check("struct Array {\n" + " ~Array() { delete m_Arr; }\n" + " std::array* m_Arr{};\n" + "};\n" + "Array arr;\n"); + ASSERT_EQUALS("", errout_str()); + + // #8174 + check("struct S {};\n" + "void f() {\n" + " S s;\n" + " S* p = &s;\n" + " free(p);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Deallocation of an auto-variable (s) results in undefined behaviour.\n", errout_str()); + + check("void f(bool b, int* q) {\n" + " int i;\n" + " int* p = b ? &i : q;\n" + " if (!b)\n" + " free(p);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct E { int* i; };\n" // #11768 + "struct C { E e; };\n" + "int foo(C* cin) {\n" + " E* e = &cin->e;\n" + " e->i = new int[42];\n" + " delete[] e->i;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " S* p = &g();\n" + " delete p;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void testinvaliddealloc_input() { + // #10600 + check("void f(int* a[]) {\n" + " free(a);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int a[]) {\n" + " free(a);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int* a[]) {\n" + " int * p = *a;\n" + " free(p);\n" + " int ** q = a;\n" + " free(q);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int a[]) {\n" + " int * p = a;\n" + " free(p);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void testinvaliddealloc_string() { + // #7341 + check("void f() {\n" + " char *ptr = \"a\";\n" + " free(\"a\");\n" + " delete \"a\";\n" + " free(ptr);\n" + " delete ptr;\n" + " char * p = malloc(1000);\n" + " p = \"abc\";\n" + " free(p);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Deallocation of a string literal results in undefined behaviour.\n" + "[test.cpp:4]: (error) Deallocation of a string literal results in undefined behaviour.\n" + "[test.cpp:5]: (error) Deallocation of a pointer pointing to a string literal (\"a\") results in undefined behaviour.\n" + "[test.cpp:6]: (error) Deallocation of a pointer pointing to a string literal (\"a\") results in undefined behaviour.\n" + "[test.cpp:9]: (error) Deallocation of a pointer pointing to a string literal (\"abc\") results in undefined behaviour.\n", + errout_str()); + + check("void f() {\n" + " char *ptr = malloc(10);\n" + " char *empty_str = \"\";\n" + " if (ptr == NULL)\n" + " ptr = empty_str;\n" + " if (ptr != empty_str)\n" + " free(ptr);\n" + "}\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, /* cpp= */ false); + ASSERT_EQUALS("", errout_str()); + } + + void testassign1() { // Ticket #1819 + check("void f(EventPtr *eventP, ActionPtr **actionsP) {\n" + " EventPtr event = *eventP;\n" + " *actionsP = &event->actions;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testassign2() { // Ticket #2765 + check("static void function(unsigned long **datap) {\n" + " struct my_s *mr = global_structure_pointer;\n" + " *datap = &mr->value;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void assignAddressOfLocalArrayToGlobalPointer() { + check("int *p;\n" + "void f() {\n" + " int x[10];\n" + " p = x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Non-local variable 'p' will use pointer to local variable 'x'.\n", errout_str()); + + check("int *p;\n" + "void f() {\n" + " int x[10];\n" + " p = x;\n" + " p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void assignAddressOfLocalVariableToGlobalPointer() { + check("int *p;\n" + "void f() {\n" + " int x;\n" + " p = &x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Non-local variable 'p' will use pointer to local variable 'x'.\n", errout_str()); + + check("int *p;\n" + "void f() {\n" + " int x;\n" + " p = &x;\n" + " p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void assignAddressOfLocalVariableToMemberVariable() { + check("struct A {\n" + " void f() {\n" + " int x;\n" + " ptr = &x;\n" + " }\n" + " int *ptr;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Non-local variable 'ptr' will use pointer to local variable 'x'.\n", errout_str()); + + check("struct A {\n" + " void f() {\n" + " int x;\n" + " ptr = &x;\n" + " ptr = 0;\n" + " }\n" + " int *ptr;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void returnLocalVariable1() { + check("char *foo()\n" + "{\n" + " char str[100] = {0};\n" + " return str;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n", + errout_str()); + + check("char *foo()\n" // use ValueFlow + "{\n" + " char str[100] = {0};\n" + " char *p = str;\n" + " return p;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:3] -> [test.cpp:5]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n", + errout_str()); + + check("class Fred {\n" + " char *foo();\n" + "};\n" + "char *Fred::foo()\n" + "{\n" + " char str[100] = {0};\n" + " return str;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning pointer to local variable 'str' that will be invalid when returning.\n", + errout_str()); + + check("char * format_reg(char *outbuffer_start) {\n" + " return outbuffer_start;\n" + "}\n" + "void print_with_operands() {\n" + " char temp[42];\n" + " char *tp = temp;\n" + " tp = format_reg(tp);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnLocalVariable2() { + check("std::string foo()\n" + "{\n" + " char str[100] = {0};\n" + " return str;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class Fred {\n" + " std::string foo();\n" + "};\n" + "std::string Fred::foo()\n" + "{\n" + " char str[100] = {0};\n" + " return str;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + + void returnLocalVariable3() { // &x[..] + // #3030 + check("char *foo() {\n" + " char q[] = \"AAAAAAAAAAAA\";\n" + " return &q[1];\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'q' that will be invalid when returning.\n", errout_str()); + + check("char *foo()\n" + "{\n" + " static char q[] = \"AAAAAAAAAAAA\";\n" + " return &q[1];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("char *foo()\n" + "{\n" + "char q[] = \"AAAAAAAAAAAA\";\n" + "char *p;\n" + "p = &q[1];\n" + "return p;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3] -> [test.cpp:6]: (error) Returning pointer to local variable 'q' that will be invalid when returning.\n", errout_str()); + } + + void returnLocalVariable4() { // x+y + check("char *foo() {\n" + " char x[10] = {0};\n" + " return x+5;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", + errout_str()); + + check("char *foo(int y) {\n" + " char x[10] = {0};\n" + " return (x+8)-y;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", + errout_str()); + } + + void returnLocalVariable5() { // cast + check("char *foo() {\n" + " int x[10] = {0};\n" + " return (char *)x;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", + errout_str()); + } + + void returnLocalVariable6() { // valueflow + check("int *foo() {\n" + " int x = 123;\n" + " int p = &x;\n" + " return p;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'x' that will be invalid when returning.\n", errout_str()); + } + + void returnReference1() { + check("int &foo()\n" + "{\n" + " int s = 0;\n" + " int& x = s;\n" + " return x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (error) Reference to local variable returned.\n", errout_str()); + + check("std::string &foo()\n" + "{\n" + " std::string s;\n" + " return s;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Reference to local variable returned.\n", errout_str()); + + check("std::vector &foo()\n" + "{\n" + " std::vector v;\n" + " return v;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Reference to local variable returned.\n", errout_str()); + + check("std::vector &foo()\n" + "{\n" + " static std::vector v;\n" + " return v;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::vector &foo()\n" + "{\n" + " thread_local std::vector v;\n" + " return v;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::string hello()\n" + "{\n" + " return \"hello\";\n" + "}\n" + "\n" + "std::string &f()\n" + "{\n" + " return hello();\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (error) Reference to temporary returned.\n", errout_str()); + + // make sure scope is used in function lookup + check("class Fred {\n" + " std::string hello() {\n" + " return std::string();\n" + " }\n" + "};\n" + "std::string &f() {\n" + " return hello();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::string hello() {\n" + " return std::string();\n" + "}\n" + "\n" + "std::string &f() {\n" + " return hello();\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Reference to temporary returned.\n", errout_str()); + + check("std::string hello() {\n" + " return \"foo\";\n" + "}\n" + "\n" + "std::string &f() {\n" + " return hello().substr(1);\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Reference to temporary returned.\n", errout_str()); + + check("class Foo;\n" + "Foo hello() {\n" + " return Foo();\n" + "}\n" + "\n" + "Foo& f() {\n" + " return hello();\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Reference to temporary returned.\n", errout_str()); + + // make sure function overloads are handled properly + check("class Foo;\n" + "Foo & hello(bool) {\n" + " static Foo foo;\n" + " return foo;\n" + "}\n" + "Foo hello() {\n" + " return Foo();\n" + "}\n" + "\n" + "Foo& f() {\n" + " return hello(true);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("Foo hello() {\n" + " return Foo();\n" + "}\n" + "\n" + "Foo& f() {\n" // Unknown type - might be a reference + " return hello();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReference2() { + check("class Fred {\n" + " std::string &foo();\n" + "}\n" + "std::string &Fred::foo()\n" + "{\n" + " std::string s;\n" + " return s;\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Reference to local variable returned.\n", errout_str()); + + check("class Fred {\n" + " std::vector &foo();\n" + "};\n" + "std::vector &Fred::foo()\n" + "{\n" + " std::vector v;\n" + " return v;\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Reference to local variable returned.\n", errout_str()); + + check("class Fred {\n" + " std::vector &foo();\n" + "};\n" + "std::vector &Fred::foo()\n" + "{\n" + " static std::vector v;\n" + " return v;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class Fred {\n" + " std::string &f();\n" + "};\n" + "std::string hello()\n" + "{\n" + " return \"hello\";\n" + "}\n" + "std::string &Fred::f()\n" + "{\n" + " return hello();\n" + "}"); + ASSERT_EQUALS("[test.cpp:10]: (error) Reference to temporary returned.\n", errout_str()); + + check("class Fred {\n" + " std::string hello();\n" + " std::string &f();\n" + "};\n" + "std::string Fred::hello()\n" + "{\n" + " return \"hello\";\n" + "}\n" + "std::string &Fred::f()\n" + "{\n" + " return hello();\n" + "}"); + ASSERT_EQUALS("[test.cpp:11]: (error) Reference to temporary returned.\n", errout_str()); + + check("class Bar;\n" + "Bar foo() {\n" + " return something;\n" + "}\n" + "Bar& bar() {\n" + " return foo();\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Reference to temporary returned.\n", errout_str()); + + check("std::map foo() {\n" + " return something;\n" + "}\n" + "std::map& bar() {\n" + " return foo();\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Reference to temporary returned.\n", errout_str()); + + check("Bar foo() {\n" + " return something;\n" + "}\n" + "Bar& bar() {\n" // Unknown type - might be a typedef to a reference type + " return foo();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + // Don't crash with function in unknown scope (#4076) + check("X& a::Bar() {}" + "X& foo() {" + " return Bar();" + "}"); + } + + void returnReference3() { + check("double & f(double & rd) {\n" + " double ret = getValue();\n" + " rd = ret;\n" + " return rd;\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + } + + // Returning reference to global variable + void returnReference4() { + check("double a;\n" + "double & f() {\n" + " return a;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReference5() { + check("struct A {\n" + " int i;\n" + "};\n" + + "struct B {\n" + " A a;\n" + "};\n" + + "struct C {\n" + " B *b;\n" + " const A& a() const {\n" + " const B *pb = b;\n" + " const A &ra = pb->a;\n" + " return ra;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReference6() { + check("Fred & create() {\n" + " Fred &fred(*new Fred);\n" + " return fred;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReference7() { // 3791 - false positive for overloaded function + check("std::string a();\n" + "std::string &a(int);\n" + "std::string &b() {\n" + " return a(12);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::string &a(int);\n" + "std::string a();\n" + "std::string &b() {\n" + " return a(12);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReference8() { + check("int& f(std::vector &v) {\n" + " std::vector::iterator it = v.begin();\n" + " int& value = *it;\n" + " return value;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReference9() { + check("int& f(bool b, int& x, int& y) {\n" + " return b ? x : y;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReference10() { + check("class A { int f() const; };\n" + "int& g() {\n" + " A a;\n" + " return a.f();\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Reference to temporary returned.\n", errout_str()); + + check("class A { int& f() const; };\n" + "int& g() {\n" + " A a;\n" + " return a.f();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReference11() { + check("class A { static int f(); };\n" + "int& g() {\n" + " return A::f();\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Reference to temporary returned.\n", errout_str()); + + check("class A { static int& f(); };\n" + "int& g() {\n" + " return A::f();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("namespace A { int& f(); }\n" + "int& g() {\n" + " return A::f();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReference12() { + check("class A { static int& f(); };\n" + "auto g() {\n" + " return &A::f;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class A { static int& f(); };\n" + "auto g() {\n" + " auto x = &A::f;\n" + " return x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReference13() { + check("std::vector v;\n" + "void* vp = &v;\n" + "int& foo(size_t i) {\n" + " return ((std::vector*)vp)->at(i);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::vector v;\n" + "void* vp = &v;\n" + "int& foo(size_t i) {\n" + " return static_cast*>(vp)->at(i);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReference14() { + check("struct C { void* m; };\n" + "struct A { void* &f(); };\n" + "C* g() {\n" + " static C c;\n" + " return &c;\n" + "}\n" + "void* &A::f() {\n" + " return g()->m;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReference15() { + check("template \n" + "const int& f() {\n" + " static int s;\n" + " return s;\n" + "}\n" + "template \n" + "const int& f(const T&) {\n" + " return f();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("template \n" + "int g();\n" + "template \n" + "const int& f(const T&) {\n" + " return g();\n" + "}"); + TODO_ASSERT_EQUALS("error", "", errout_str()); + } + + void returnReference16() { + check("int& f(std::tuple& x) {\n" + " return std::get<0>(x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int& f(int x) {\n" + " return std::get<0>(std::make_tuple(x));\n" + "}"); + TODO_ASSERT_EQUALS("error", "", errout_str()); + } + + void returnReference17() { + check("auto g() -> int&;\n" + "int& f() {\n" + " return g();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReference18() { + check("template\n" + "auto f(T& x) -> decltype(x);\n" + "int& g(int* x) {\n" + " return f(*x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + // #9597 + void returnReference19() { + check("struct C : B {\n" + " const B &f() const { return (const B &)*this; }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + // #9536 + void returnReference20() { + check("struct a {\n" + " int& operator()() const;\n" + "};\n" + "int& b() {\n" + " return a()();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("auto a() {\n" + " return []() -> int& {\n" + " static int b;\n" + " return b;\n" + " };\n" + "}\n" + "const int& c() {\n" + " return a()();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::function a();\n" + "int& b() {\n" + " return a()();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9889 + check("int f(std::vector>& v, int i) {\n" + " auto& j = v[i]();\n" + " return j;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + // #9530 + void returnReference21() { + check("int& f(int& x) {\n" + " return {x};\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReference22() { + check("int& f() {\n" + " std::unique_ptr p = std::make_unique(1);\n" + " return *p;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Reference to local variable returned.\n", errout_str()); + + check("void g(const std::unique_ptr&);\n" + "int& f() {\n" + " std::unique_ptr p = std::make_unique(1);\n" + " g(p);\n" + " return *p;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Reference to local variable returned.\n", errout_str()); + + check("void g(std::shared_ptr);\n" + "int& f() {\n" + " std::shared_ptr p = std::make_shared(1);\n" + " g(p);\n" + " return *p;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("std::shared_ptr g();\n" + "int& f() {\n" + " return *g();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("std::unique_ptr g();\n" + "int& f() {\n" + " return *g();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Reference to temporary returned.\n", errout_str()); + + check("struct A { int x; };\n" + "int& f() {\n" + " std::unique_ptr p = std::make_unique();\n" + " return p->x;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Reference to local variable returned.\n", errout_str()); + } + + void returnReference23() { + check("const std::vector * g();\n" + "const std::vector& f() {\n" + " return *g();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReference24() + { + check("struct A {\n" + " A() {}\n" + "};\n" + "const A& a() {\n" + " return A();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Reference to temporary returned.\n", errout_str()); + } + + void returnReference25() + { + check("int& f();\n" // #10983 + "auto g() -> decltype(f()) {\n" + " return f();\n" + "}\n" + "int& h() {\n" + " return g();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReference26() + { + check("const int& f() {\n" // #12153 + " int x = 234;\n" + " int& s{ x };\n" + " return s;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Reference to local variable returned.\n", errout_str()); + } + + void returnReference27() + { + check("struct S {\n" // #12607 + " const std::string & f(const std::string& a, const std::string& b) {\n" + " auto status = m.emplace(a, b);\n" + " return status.first->second;\n" + " }\n" + " std::map m;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReferenceFunction() { + check("int& f(int& a) {\n" + " return a;\n" + "}\n" + "int& hello() {\n" + " int x = 0;\n" + " return f(x);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:1] -> [test.cpp:2] -> [test.cpp:6] -> [test.cpp:6]: (error) Reference to local variable returned.\n", + errout_str()); + + check("int& f(int& a) {\n" + " return a;\n" + "}\n" + "int* hello() {\n" + " int x = 0;\n" + " return &f(x);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:1] -> [test.cpp:2] -> [test.cpp:6] -> [test.cpp:6] -> [test.cpp:5] -> [test.cpp:6]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", + errout_str()); + + check("int* f(int * x) {\n" + " return x;\n" + "}\n" + "int * g(int x) {\n" + " return f(&x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:5]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout_str()); + + check("int* f(int * x) {\n" + " x = nullptr;\n" + " return x;\n" + "}\n" + "int * g(int x) {\n" + " return f(&x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int& a) {\n" + " return a;\n" + "}\n" + "int& hello() {\n" + " int x = 0;\n" + " return f(x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Reference to temporary returned.\n", errout_str()); + + check("int& f(int a) {\n" + " return a;\n" + "}\n" + "int& hello() {\n" + " int x = 0;\n" + " return f(x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Reference to local variable returned.\n", errout_str()); + + check("int f(int a) {\n" + " return a;\n" + "}\n" + "int& hello() {\n" + " int x = 0;\n" + " return f(x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Reference to temporary returned.\n", errout_str()); + + check("template\n" + "int& f(int& x, T y) {\n" + " x += y;\n" + " return x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReferenceContainer() { + check("auto& f() {\n" + " std::vector x;\n" + " return x[0];\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Reference to local variable returned.\n", errout_str()); + + check("auto& f() {\n" + " std::vector x;\n" + " return x.front();\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (error) Reference to local variable returned.\n", errout_str()); + + check("std::vector g();\n" + "auto& f() {\n" + " return g().front();\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (error) Reference to temporary returned.\n", errout_str()); + + check("auto& f() {\n" + " return std::vector{1}.front();\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (error) Reference to temporary returned.\n", errout_str()); + + check("struct A { int foo; };\n" + "int& f(std::vector v) {\n" + " auto it = v.begin();\n" + " return it->foo;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Reference to local variable returned.\n", errout_str()); + + check("template \n" + "const V& get_default(const T& t, const K& k, const V& v) {\n" + " auto it = t.find(k);\n" + " if (it == t.end()) return v;\n" + " return it->second;\n" + "}\n" + "const int& bar(const std::unordered_map& m, int k) {\n" + " auto x = 0;\n" + " return get_default(m, k, x);\n" + "}\n", + true); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:4] -> [test.cpp:9] -> [test.cpp:9]: (error, inconclusive) Reference to local variable returned.\n", + errout_str()); + + check("template \n" + "const V& get_default(const T& t, const K& k, const V& v) {\n" + " auto it = t.find(k);\n" + " if (it == t.end()) return v;\n" + " return it->second;\n" + "}\n" + "const int& bar(const std::unordered_map& m, int k) {\n" + " return get_default(m, k, 0);\n" + "}\n", + true); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:4] -> [test.cpp:8] -> [test.cpp:8]: (error, inconclusive) Reference to temporary returned.\n", + errout_str()); + + check("struct A { int foo; };\n" + "int& f(std::vector& v) {\n" + " auto it = v.begin();\n" + " return it->foo;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("static std::vector A[2];\n" + "static std::vector B;\n" + "std::vector& g(int i) {\n" + " return i ? A[i] : B;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReferenceLiteral() { + check("const std::string &a() {\n" + " return \"foo\";\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Reference to temporary returned.\n", errout_str()); + + check("const std::string a() {\n" + " return \"foo\";\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("const std::string& f(const std::string& x) { return x; }\n" + "const std::string &a() {\n" + " return f(\"foo\");\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:1] -> [test.cpp:1] -> [test.cpp:3] -> [test.cpp:3]: (error) Reference to temporary returned.\n", + errout_str()); + + check("const char * f(const char * x) { return x; }\n" + "const std::string &a() {\n" + " return f(\"foo\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Reference to temporary returned.\n", errout_str()); + } + + void returnReferenceCalculation() { + check("const std::string &a(const std::string& str) {\n" + " return \"foo\" + str;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Reference to temporary returned.\n", errout_str()); + + check("int& operator<<(int out, int path) {\n" + " return out << path;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Reference to temporary returned.\n", errout_str()); + + check("std::ostream& operator<<(std::ostream& out, const std::string& path) {\n" + " return out << path;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::ostream& operator<<(std::ostream* out, const std::string& path) {\n" + " return *out << path;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("Unknown1& operator<<(Unknown1 out, Unknown2 path) {\n" + " return out << path;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int& a(int b) {\n" + " return 2*(b+1);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Reference to temporary returned.\n", errout_str()); + + check("const std::string &a(const std::string& str) {\n" + " return str;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("const std::string &a(int bar) {\n" + " return foo(bar + 1);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("const std::string a(const std::string& str) {\n" + " return \"foo\" + str;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int& incValue(int& value) {\n" + " return ++value;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReferenceLambda() { + // #6787 + check("const Item& foo(const Container& items) const {\n" + " return bar(items.begin(), items.end(),\n" + " [](const Item& lhs, const Item& rhs) {\n" + " return false;\n" + " });\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #5844 + check("map const &getVariableTable() {\n" + "static map const s_var = []{\n" + " map var;\n" + " return var;\n" + " }();\n" + "return s_var;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #7583 + check("Command& foo() {\n" + " return f([]() -> int { return 1; });\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReferenceInnerScope() { + // #6951 + check("const Callback& make() {\n" + " struct _Wrapper {\n" + " static ulong call(void* o, const void* f, const void*[]) {\n" + " return 1;\n" + " }\n" + " };\n" + " return _make(_Wrapper::call, pmf);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnReferenceRecursive() { + check("int& f() { return f(); }"); + ASSERT_EQUALS("", errout_str()); + + check("int& g(int& i) { return i; }\n" + "int& f() { return g(f()); }"); + ASSERT_EQUALS("", errout_str()); + } + + void extendedLifetime() { + check("void g(int*);\n" + "int h();\n" + "auto f() {\n" + " const int& x = h();\n" + " return [&] { return x; };\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:5]: (error) Returning lambda that captures local variable 'x' that will be invalid when returning.\n", errout_str()); + + check("void g(int*);\n" + "int h();\n" + "int* f() {\n" + " const int& x = h();\n" + " return &x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:5]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", errout_str()); + + check("void g(int*);\n" + "int h();\n" + "void f() {\n" + " int& x = h();\n" + " g(&x);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:5]: (error) Using pointer that is a temporary.\n" + "[test.cpp:4] -> [test.cpp:5]: (error) Using reference to dangling temporary.\n", + errout_str()); + + check("void g(int*);\n" + "int h();\n" + "void f() {\n" + " const int& x = h();\n" + " g(&x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct Data {\n" + " std::string path;\n" + "};\n" + "const char* foo() {\n" + " const Data& data = getData();\n" + " return data.path.c_str();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void danglingReference() { + check("int f( int k )\n" + "{\n" + " static int &r = k;\n" + " return r;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (error) Non-local reference variable 'r' to local variable 'k'\n", + errout_str()); + + check("int &f( int & k )\n" + "{\n" + " static int &r = k;\n" + " return r;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void danglingTempReference() { + check("const std::string& g(const std::string& str_cref) {\n" + " return str_cref;\n" + "}\n" + "void f() {\n" + " const auto& str_cref2 = g(std::string(\"hello\"));\n" + " std::cout << str_cref2 << std::endl;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:1] -> [test.cpp:2] -> [test.cpp:5] -> [test.cpp:6]: (error) Using reference to dangling temporary.\n", errout_str()); + + // Lifetime extended + check("std::string g(const std::string& str_cref) {\n" + " return str_cref;\n" + "}\n" + "void f() {\n" + " const auto& str_cref2 = g(std::string(\"hello\"));\n" + " std::cout << str_cref2 << std::endl;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("char f() {\n" + " char c = 0;\n" + " char&& cr = std::move(c);\n" + " return cr;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #9987 + check("void g(std::vector);\n" + "void f() {\n" + " std::vector&& v = {};\n" + " g(std::move(v));\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void g(std::vector);\n" + "std::vector h();\n" + "void f() {\n" + " std::vector&& v = h();\n" + " g(std::move(v));\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #11087 + check("struct S1 {\n" + " int& get() { return val; }\n" + " int val{42};\n" + "};\n" + "void f() {\n" + " int& v = S1().get();\n" + " v += 1;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:6] -> [test.cpp:2] -> [test.cpp:6] -> [test.cpp:7]: (error) Using reference to dangling temporary.\n", + errout_str()); + + check("struct A {\n" + " const int& g() const { return i; }\n" + " int i;\n" + "};\n" + "A* a();\n" + "int f() {\n" + " const int& i = a()->g();\n" + " return i;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " const int& g() const { return i; }\n" + " int i;\n" + "};\n" + "std::unique_ptr a();\n" + "int f() {\n" + " const int& i = a()->g();\n" + " return i;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:7] -> [test.cpp:2] -> [test.cpp:7] -> [test.cpp:8]: (error) Using reference to dangling temporary.\n", + errout_str()); + + check("struct S1 {\n" + " auto get() -> auto& { return val; }\n" + " int val{42};\n" + "};\n" + "struct S2 {\n" + " auto get() -> S1 { return s; }\n" + " S1 s;\n" + "};\n" + "auto main() -> int {\n" + " S2 c{};\n" + " auto& v = c.get().get();\n" + " v += 1;\n" + " return c.s.val;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:11] -> [test.cpp:2] -> [test.cpp:11] -> [test.cpp:12]: (error) Using reference to dangling temporary.\n", + errout_str()); + + check("struct C {\n" + " std::vector> v;\n" + "};\n" + "struct P {\n" + " std::vector::const_iterator find() const { return pv.begin(); }\n" + " std::vector pv;\n" + "};\n" + "struct M {\n" + " const P* get() const { return p; }\n" + " P* p;\n" + "};\n" + "void f(const M* m) {\n" + " auto it = m->get()->find();\n" + " auto e = (*it)->v.begin();\n" + " const int& x = (*e)[1];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int* g();\n" // #11188 + "void f() {\n" + " const auto& p = g();\n" + " if (p != nullptr) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("template\n" + "void f(const std::vector& v) {\n" + " T a;\n" + " for (typename std::vector::iterator it = v.begin(); it != v.end(); ++it) {\n" + " const T& b = static_cast(it->find(1));\n" + " a = b;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("using namespace std;\n" // #10971 + "struct S { int i = 3; };\n" + "unique_ptr g() {\n" + " auto tp = make_unique();\n" + " return tp;\n" + "}\n" + "void f() {\n" + " const S& s = *g();\n" + " (void)s.i;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:9]: (error) Using reference to dangling temporary.\n", errout_str()); + + check("std::string f() {\n" // #12173 + " std::string s;\n" + " for (auto& c : { \"a\", \"b\", \"c\" }) {\n" + " s += c;\n" + " }\n" + " return s;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void testglobalnamespace() { + check("class SharedPtrHolder\n" + "{\n" + " ::std::tr1::shared_ptr pNum;\n" + "public:\n" + " void SetNum(const ::std::tr1::shared_ptr & apNum)\n" + " {\n" + " pNum = apNum;\n" + " }\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + } + + void returnParameterAddress() { + check("int* foo(int y)\n" + "{\n" + " return &y;\n" + "}"); + + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning pointer to local variable 'y' that will be invalid when returning.\n", errout_str()); + + check("int ** foo(int * y)\n" + "{\n" + " return &y;\n" + "}"); + + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning pointer to local variable 'y' that will be invalid when returning.\n", errout_str()); + + check("const int * foo(const int & y)\n" + "{\n" + " return &y;\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + + check("int * foo(int * y)\n" + "{\n" + " return y;\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + + check("struct s { void *p; };\n" + "extern struct s* f(void);\n" + "void g(void **q)\n" + "{\n" + " struct s *r = f();\n" + " *q = &r->p;\n" + "}"); + + 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" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (error) Reference to temporary returned.\n", errout_str()); + } + + void variableIsUsedInScope() { + check("void removed_cb (GList *uids) {\n" + "for (; uids; uids = uids->next) {\n" + "}\n" + "}\n" + "void opened_cb () {\n" + " g_signal_connect (G_CALLBACK (removed_cb));\n" + "}"); + } + + void danglingLifetimeLambda() { + check("auto f() {\n" + " int a = 1;\n" + " auto l = [&](){ return a; };\n" + " return l;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout_str()); + + check("auto f() {\n" + " int a = 1;\n" + " return [&](){ return a; };\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout_str()); + + check("auto f(int a) {\n" + " return [&](){ return a; };\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1] -> [test.cpp:2]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout_str()); + + check("auto f(int a) {\n" + " auto p = &a;\n" + " return [=](){ return p; };\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout_str()); + + check("auto g(int& a) {\n" + " int p = a;\n" + " return [&](){ return p; };\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'p' that will be invalid when returning.\n", errout_str()); + + check("auto f() {\n" + " return [=](){\n" + " int a = 1;\n" + " return [&](){ return a; };\n" + " };\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout_str()); + + check("auto f(int b) {\n" + " return [=](int a){\n" + " a += b;\n" + " return [&](){ return a; };\n" + " };\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout_str()); + + check("auto g(int& a) {\n" + " return [&](){ return a; };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("auto g(int a) {\n" + " auto p = a;\n" + " return [=](){ return p; };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("auto g(int& a) {\n" + " auto p = a;\n" + " return [=](){ return p; };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("auto g(int& a) {\n" + " int& p = a;\n" + " return [&](){ return p; };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("template\n" + "void g(F);\n" + "auto f() {\n" + " int x;\n" + " return g([&]() { return x; });\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("auto f() {\n" + " int i = 0;\n" + " return [&i] {};\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'i' that will be invalid when returning.\n", errout_str()); + + check("auto f() {\n" + " int i = 0;\n" + " return [i] {};\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("auto f() {\n" + " int i = 0;\n" + " return [=, &i] {};\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'i' that will be invalid when returning.\n", errout_str()); + + check("auto f() {\n" + " int i = 0;\n" + " int j = 0;\n" + " return [=, &i] { return j; };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'i' that will be invalid when returning.\n", errout_str()); + + check("auto f() {\n" + " int i = 0;\n" + " return [&, i] {};\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("auto f() {\n" + " int i = 0;\n" + " int j = 0;\n" + " return [&, i] { return j; };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'j' that will be invalid when returning.\n", errout_str()); + + check("auto f(int& i) {\n" + " int j = 0;\n" + " return [=, &i] { return j; };\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int*);\n" + "auto g(int y) {\n" + " int x = y;\n" + " return [=] {\n" + " g(&x);\n" + " };\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " int x;\n" + "};\n" + "auto f() {\n" + " A a;\n" + " return [=] {\n" + " const A* ap = &a;\n" + " ap->x;\n" + " };\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void danglingLifetimeContainer() { + check("auto f(const std::vector& x) {\n" + " auto it = x.begin();\n" + " return it;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::vector::iterator f() {\n" + " std::vector x;\n" + " auto it = x.begin();\n" + " return it;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning iterator to local container 'x' that will be invalid when returning.\n", errout_str()); + + check("auto f() {\n" + " std::vector x;\n" + " auto it = std::begin(x);\n" + " return it;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning iterator to local container 'x' that will be invalid when returning.\n", errout_str()); + + check("int* f() {\n" + " std::vector x;\n" + " auto p = x.data();\n" + " return p;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", + errout_str()); + + check("auto f() {\n" + " std::vector x;\n" + " auto p = &x[0];\n" + " return p;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning pointer to local variable 'x' that will be invalid when returning.\n", + errout_str()); + + check("struct A { int foo; };\n" + "int* f(std::vector v) {\n" + " auto it = v.begin();\n" + " return &it->foo;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning pointer to local variable 'v' that will be invalid when returning.\n", + errout_str()); + + check("std::vector::iterator f(std::vector x) {\n" + " auto it = x.begin();\n" + " return it;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning iterator to local container 'x' that will be invalid when returning.\n", errout_str()); + + check("auto f() {\n" + " std::vector x;\n" + " auto it = x.begin();\n" + " return std::next(it);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'x' that will be invalid when returning.\n", + errout_str()); + + check("auto f() {\n" + " std::vector x;\n" + " auto it = x.begin();\n" + " return it + 1;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning iterator to local container 'x' that will be invalid when returning.\n", + errout_str()); + + check("auto f() {\n" + " std::vector x;\n" + " auto it = x.begin();\n" + " return std::next(it + 1);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'x' that will be invalid when returning.\n", + errout_str()); + + check("std::vector f() {\n" + " int i = 0;\n" + " std::vector v;\n" + " v.push_back(&i);\n" + " return v;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:5]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", + errout_str()); + + check("std::vector f() {\n" + " std::vector r;\n" + " int i = 0;\n" + " std::vector v;\n" + " v.push_back(&i);\n" + " r.assign(v.begin(), v.end());\n" + " return r;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:5] -> [test.cpp:5] -> [test.cpp:5] -> [test.cpp:3] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " std::vector v;\n" + " void f() {\n" + " int i;\n" + " v.push_back(&i);\n" + " }\n" + "};"); + ASSERT_EQUALS( + "[test.cpp:5] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:5]: (error) Non-local variable 'v' will use object that points to local variable 'i'.\n", + errout_str()); + + check("struct A {\n" + " std::vector v;\n" + " void f() {\n" + " int i;\n" + " int * p = &i;\n" + " v.push_back(p);\n" + " }\n" + "};"); + ASSERT_EQUALS( + "[test.cpp:5] -> [test.cpp:6] -> [test.cpp:4] -> [test.cpp:6]: (error) Non-local variable 'v' will use object that points to local variable 'i'.\n", + errout_str()); + + check("struct A {\n" + " std::vector m;\n" + " void f() {\n" + " int x;\n" + " std::vector v;\n" + " v.push_back(&x);\n" + " m.insert(m.end(), v.begin(), v.end());\n" + " }\n" + "};"); + ASSERT_EQUALS( + "[test.cpp:6] -> [test.cpp:6] -> [test.cpp:6] -> [test.cpp:4] -> [test.cpp:7]: (error) Non-local variable 'm' will use object that points to local variable 'x'.\n" + "[test.cpp:6] -> [test.cpp:6] -> [test.cpp:6] -> [test.cpp:4] -> [test.cpp:7]: (error) Non-local variable 'm' will use object that points to local variable 'x'.\n", // duplicate + errout_str()); + + check("std::vector::iterator f(std::vector v) {\n" + " for(auto it = v.begin();it != v.end();it++) {\n" + " return it;\n" + " }\n" + " return {};\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning iterator to local container 'v' that will be invalid when returning.\n", + errout_str()); + + check("const char * f() {\n" + " std::string ba(\"hello\");\n" + " return ba.c_str();\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning pointer to local variable 'ba' that will be invalid when returning.\n", + errout_str()); + + check("template \n" + "const V* get_default(const T& t, const K& k, const V* v) {\n" + " auto it = t.find(k);\n" + " if (it == t.end()) return v;\n" + " return &it->second;\n" + "}\n" + "const int* bar(const std::unordered_map& m, int k) {\n" + " auto x = 0;\n" + " return get_default(m, k, &x);\n" + "}\n", + true); + ASSERT_EQUALS( + "[test.cpp:9] -> [test.cpp:9] -> [test.cpp:8] -> [test.cpp:9]: (error, inconclusive) Returning pointer to local variable 'x' that will be invalid when returning.\n", + errout_str()); + + check("std::vector g();\n" + "auto f() {\n" + " return g().begin();\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (error) Returning iterator that will be invalid when returning.\n", + errout_str()); + + check("std::vector g();\n" + "std::vector::iterator f() {\n" + " auto it = g().begin();\n" + " return it;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3] -> [test.cpp:4]: (error) Using iterator that is a temporary.\n" + "[test.cpp:3] -> [test.cpp:4]: (error) Returning iterator that will be invalid when returning.\n", + errout_str()); + + check("std::vector g();\n" + "int& f() {\n" + " return *g().begin();\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (error) Reference to temporary returned.\n", errout_str()); + + check("struct A {\n" + " std::vector v;\n" + " void f() {\n" + " char s[3];\n" + " v.push_back(s);\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("std::vector f() {\n" + " const char * s = \"hello\";\n" + " std::vector v;\n" + " v.push_back(s);\n" + " return v;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("auto f() {\n" + " static std::vector x;\n" + " return x.begin();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::string g() {\n" + " std::vector v;\n" + " return v.data();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::vector::iterator f(std::vector* v) {\n" + " return v->begin();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::vector::iterator f(std::vector* v) {\n" + " std::vector* v = new std::vector();\n" + " return v->begin();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(std::vector v) {\n" + " return *v.begin();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(std::vector v) {\n" + " return v.end() - v.begin();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("auto g() {\n" + " std::vector v;\n" + " return {v, [v]() { return v.data(); }};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("template\n" + "void g(F);\n" + "auto f() {\n" + " std::vector v;\n" + " return g([&]() { return v.data(); });\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::vector g();\n" + "struct A {\n" + " std::vector m;\n" + " void f() {\n" + " std::vector v = g();\n" + " m.insert(m.end(), v.begin(), v.end());\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool b) {\n" + " std::vector v = {1};\n" + " if (b) {\n" + " int a[] = {0};\n" + " v.insert(a, a+1);\n" + " }\n" + " return v.back() == 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class A {\n" + " int f( P p ) {\n" + " std::vector< S > maps;\n" + " m2.insert( m1.begin(), m1.end() );\n" + " }\n" + " struct B {};\n" + " std::map< S, B > m1;\n" + " std::map< S, B > m2;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " std::vector v;\n" + " int x;\n" + " void f() {\n" + " v.push_back(&x);\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("size_t f(const std::string& x) {\n" + " std::string y = \"x\";\n" + " return y.find(x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::string* f();\n" + "const char* g() {\n" + " std::string* var = f();\n" + " return var->c_str();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::string f() {\n" + " std::vector data{};\n" + " data.push_back('a');\n" + " return std::string{ data.data(), data.size() };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::vector f() {\n" + " char a = 0;\n" + " return std::vector{&a};\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'a' that will be invalid when returning.\n", errout_str()); + + check("std::vector* g();\n" + "int& f() {\n" + " auto* p = g();\n" + " return p->front();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("std::vector> g();\n" + "void f() {\n" + " for(auto& x:g())\n" + " std::sort(x.begin(), x.end());\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " std::vector v;\n" + " void add(int* i) {\n" + " v.push_back(i);\n" + " }\n" + " void f() {\n" + " int i = 0;\n" + " add(&i);\n" + " }\n" + "};\n"); + ASSERT_EQUALS( + "[test.cpp:8] -> [test.cpp:8] -> [test.cpp:4] -> [test.cpp:7] -> [test.cpp:4]: (error) Non-local variable 'v' will use object that points to local variable 'i'.\n", + errout_str()); + + check("struct A {\n" + " std::vector v;\n" + " void add(int* i) {\n" + " v.push_back(i);\n" + " }\n" + "};\n" + "void f() {\n" + " A a;\n" + " int i = 0;\n" + " a.add(&i);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " std::vector v;\n" + " void add(int* i) {\n" + " v.push_back(i);\n" + " }\n" + " void f() {\n" + " A a;\n" + " int i = 0;\n" + " a.add(&i);\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("int f() {\n" + " int i;\n" + " {\n" + " std::vector vec;\n" + " const auto iter = vec.begin();\n" + " i = (int)(iter - vec.begin());\n" + " }\n" + " return i;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int* get(std::vector& container) {\n" + " Sequence seq(container);\n" + " for (auto& r : seq) {\n" + " return &r;\n" + " }\n" + " return &*seq.begin();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("std::string f(std::string Str, int first, int last) {\n" + " return { Str.begin() + first, Str.begin() + last + 1 };\n" + "}\n", + true); + ASSERT_EQUALS("", errout_str()); + + check("std::string f(std::string s) {\n" + " std::string r = { s.begin(), s.end() };\n" + " return r;\n" + "}\n", + true); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " std::vector> mA;\n" + " void f(std::unique_ptr a) {\n" + " auto x = a.get();\n" + " mA.push_back(std::move(a));\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " std::map m;\n" + " int* f(std::string s) {\n" + " auto r = m.emplace(name, name);\n" + " return &(r.first->second);\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " std::queue q;\n" + " auto& h = q.emplace();\n" + " h = 1;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("std::string f(std::string s) {\n" + " std::string ss = (\":\" + s).c_str();\n" + " return ss;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #12568 + " std::string str;\n" + " {\n" + " std::unique_ptr b(new char[1]{});\n" + " str.assign(b.get());\n" + " }\n" + " std::cerr << str;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void danglingLifetimeContainerView() + { + check("std::string_view f() {\n" + " std::string s = \"\";\n" + " return s;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 's' that will be invalid when returning.\n", + errout_str()); + + check("std::string_view f() {\n" + " std::string s = \"\";\n" + " std::string_view sv = s;\n" + " return sv;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 's' that will be invalid when returning.\n", + errout_str()); + + check("std::string_view f() {\n" + " std::string s = \"\";\n" + " return std::string_view{s};\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 's' that will be invalid when returning.\n", + errout_str()); + + check("std::string_view f(std::string_view s) {\n" + " return s;\n" + "}\n" + "std::string_view g() {\n" + " std::string s = \"\";\n" + " return f(s);\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:6] -> [test.cpp:6] -> [test.cpp:5] -> [test.cpp:6]: (error) Returning object that points to local variable 's' that will be invalid when returning.\n", + errout_str()); + + check("const char * f() {\n" + " std::string s;\n" + " std::string_view sv = s;\n" + " return sv.begin();\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning iterator to local container 's' that will be invalid when returning.\n", + errout_str()); + + check("const char * f() {\n" + " std::string s;\n" + " return std::string_view{s}.begin();\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning iterator to local container 's' that will be invalid when returning.\n", + errout_str()); + + check("const char * f() {\n" + " std::string s;\n" + " return std::string_view(s).begin();\n" + "}\n"); + TODO_ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning iterator to local container 's' that will be invalid when returning.\n", + "", + errout_str()); + + check("const char * f(std::string_view sv) {\n" + " return sv.begin();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("const char * f(std::string s) {\n" + " std::string_view sv = s;\n" + " return sv.begin();\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning iterator to local container 's' that will be invalid when returning.\n", + errout_str()); + + check("std::string_view f(std::string s) {\n" + " return s;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:1] -> [test.cpp:2]: (error) Returning object that points to local variable 's' that will be invalid when returning.\n", + errout_str()); + + check("const char * f(const std::string& s) {\n" + " std::string_view sv = s;\n" + " return sv.begin();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("std::string_view f(const std::string_view& sv) {\n" + " return sv;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #10993 + " std::string_view v = std::string();\n" + " v.data();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2] -> [test.cpp:3]: (error) Using object that is a temporary.\n", errout_str()); + + check("std::string convert(std::string_view sv) { return std::string{ sv }; }\n" // #11374 + "auto f() {\n" + " std::vector v;\n" + " v.push_back(convert(\"foo\"));\n" + " return v[0];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10532 + check("std::string f(std::string ss) {\n" + " std::string_view sv = true ? \"\" : ss;\n" + " std::string s = sv;\n" + " return s;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2] -> [test.cpp:3]: (error) Using object that is a temporary.\n", + errout_str()); + + // #10833 + check("struct A { std::string s; };\n" + "const std::string& f(A* a) {\n" + " return a ? a->s : \"\";\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Reference to temporary returned.\n", errout_str()); + + check("std::span f() {\n" + " std::vector v{};\n" + " return v;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'v' that will be invalid when returning.\n", + errout_str()); + + check("std::span f() {\n" + " std::vector v;\n" + " std::span sp = v;\n" + " return sp;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'v' that will be invalid when returning.\n", + errout_str()); + + check("std::span f() {\n" + " std::vector v;\n" + " return std::span{v};\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'v' that will be invalid when returning.\n", + errout_str()); + + check("int f() {\n" + " std::span s;\n" + " {\n" + " std::vector v(1);" + " s = v;\n" + " }\n" + "return s.back()\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:4] -> [test.cpp:6]: (error) Using object that points to local variable 'v' that is out of scope.\n", + errout_str()); + + check("int f() {\n" + " std::span s;\n" + " {\n" + " std::vector v(1);" + " s = v;\n" + " }\n" + "return s.back()\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:4] -> [test.cpp:6]: (error) Using object that points to local variable 'v' that is out of scope.\n", + errout_str()); + + check("int f() {\n" + " std::span s;\n" + " {\n" + " std::vector v(1);" + " s = v;\n" + " }\n" + "return s.front()\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:4] -> [test.cpp:6]: (error) Using object that points to local variable 'v' that is out of scope.\n", + errout_str()); + + check("int f() {\n" + " std::span s;\n" + " {\n" + " std::vector v(1);" + " s = v;\n" + " }\n" + "return s.last(1)\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:4] -> [test.cpp:6]: (error) Using object that points to local variable 'v' that is out of scope.\n", + errout_str()); + + check("int f() {\n" + " std::span s;\n" + " {\n" + " std::vector v(1);" + " s = v;\n" + " }\n" + "return s.first(1)\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:4] -> [test.cpp:6]: (error) Using object that points to local variable 'v' that is out of scope.\n", + errout_str()); + } + + void danglingLifetimeUniquePtr() + { + check("int* f(std::unique_ptr p) {\n" + " int * rp = p.get();\n" + " return rp;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning pointer to local variable 'p' that will be invalid when returning.\n", + errout_str()); + + check("int* f();\n" // #11406 + "bool g() {\n" + " std::unique_ptr ptr(f());\n" + " return ptr.get();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int* f();\n" + "int* g() {\n" + " std::unique_ptr ptr(f());\n" + " return ptr.get();\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning pointer to local variable 'ptr' that will be invalid when returning.\n", + errout_str()); + + // #12600 + check("struct S { std::unique_ptr p; };\n" + "int* f(const S* s) {\n" + " return s[0].p.get();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + void danglingLifetime() { + check("auto f() {\n" + " std::vector a;\n" + " auto it = a.begin();\n" + " return [=](){ return it; };\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout_str()); + + check("auto f(std::vector a) {\n" + " auto it = a.begin();\n" + " return [=](){ return it; };\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:3]: (error) Returning lambda that captures local variable 'a' that will be invalid when returning.\n", errout_str()); + + check("struct e {};\n" + "e * j() {\n" + " e c[20];\n" + " return c;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:3] -> [test.cpp:4]: (error) Returning pointer to local variable 'c' that will be invalid when returning.\n", + errout_str()); + + check("auto f(std::vector& a) {\n" + " auto it = a.begin();\n" + " return [=](){ return it; };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int * f(int a[]) {\n" + " return a;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " struct b {\n" + " uint32_t f[6];\n" + " } d;\n" + " uint32_t *a = d.f;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Don't decay std::array + check("std::array f() {\n" + " std::array x;\n" + " return x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Make sure we don't hang + check("struct A;\n" + "void f() {\n" + " using T = A[3];\n" + " A &&a = T{1, 2, 3}[1];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Make sure we don't hang + check("struct A;\n" + "void f() {\n" + " using T = A[3];\n" + " A &&a = T{1, 2, 3}[1]();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Make sure we don't hang + check("struct A;\n" + "void f() {\n" + " using T = A[3];\n" + " A &&a = T{1, 2, 3}[1];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Make sure we don't hang + check("struct A;\n" + "void f() {\n" + " using T = A[3];\n" + " A &&a = T{1, 2, 3}[1]();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Crash #8872 + check("struct a {\n" + " void operator()(b c) override {\n" + " d(c, [&] { c->e });\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct a {\n" + " void operator()(b c) override {\n" + " d(c, [=] { c->e });\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct a {\n" + " a(char* b) {}\n" + "};\n" + "a f() {\n" + " char c[20];\n" + " return c;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct a {\n" + " a(char* b) {}\n" + "};\n" + "a g() {\n" + " char c[20];\n" + " a d = c;\n" + " return d;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " struct a {\n" + " std::vector v;\n" + " auto g() { return v.end(); }\n" + " };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int * f(std::vector& v) {\n" + " for(int & x : v)\n" + " return &x;\n" + " return nullptr;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9275 + check("struct S {\n" + " void f();\n" + " std::string m;\n" + "}\n" + "void S::f() {\n" + " char buf[1024];\n" + " const char* msg = buf;\n" + " m = msg;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9201 + check("int* f() {\n" + " struct a { int m; };\n" + " static a b{0};\n" + " return &b.m;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9453 + check("int *ptr;\n" + "void foo(int arr[]) {\n" + " ptr = &arr[2];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9639 + check("struct Fred {\n" + " std::string s;\n" + "};\n" + "const Fred &getFred();\n" + "const char * f() {\n" + " const Fred &fred = getFred();\n" + " return fred.s.c_str();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9534 + check("struct A {\n" + " int* x;\n" + "};\n" + "int* f(int i, std::vector& v) {\n" + " A& y = v[i];\n" + " return &y.x[i];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9712 + check("std::string f(const char *str) {\n" + " char value[256];\n" + " return value;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9770 + check("class C {\n" + " std::string f(const char*);\n" + "};\n" + "std::string C::f(const char*) {\n" + " const char data[] = \"x\";\n" + " return data;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #9899 + check("struct A {\n" + " std::vector v;\n" + " void f(std::vector w) {\n" + " v = std::move(w);\n" + " }\n" + " void g(std::vector w) {\n" + " f(std::move(w));\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + //Make sure we can still take the address of a reference without warning + check("int* foo() {\n" + " int& x = getX();\n" + " return &x;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + check("struct C {\n" + " int* m_x;\n" + " void foo() {\n" + " const int& x = getX();\n" + " m_x = &x;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10090 + check("struct a {\n" + " int b{};\n" + "};\n" + "struct c {\n" + " int* c{};\n" + " a* d{};\n" + "};\n" + "a* f();\n" + "c g() {\n" + " c e;\n" + " e.d = f();\n" + " if (e.d)\n" + " e.c = &e.d->b;\n" + " return e;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10214 + check("struct A {\n" + " std::string key;\n" + " const char *value;\n" + "};\n" + "const char *f(const std::string &key, const std::vector &lookup) {\n" + " const auto &entry =\n" + " std::find_if(lookup.begin(), lookup.end(),\n" + " [key](const auto &v) { return v.key == key; });\n" + " return (entry == lookup.end()) ? \"\" : entry->value;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #9811 + check("struct Base {\n" + " virtual auto get() -> int & = 0;\n" + "};\n" + "struct A : public Base {\n" + " int z = 42;\n" + " auto get() -> int & override { return z; }\n" + " auto getMore() -> int & { return get(); }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + // #10575 + check("struct Data {\n" + " int x=0;\n" + " int y=0;\n" + "};\n" + "struct MoreData {\n" + " Data *data1;\n" + "};\n" + "struct Fred {\n" + " Fred() {\n" + " Data data;\n" + " mMoreData.data1 = &data;\n" + " }\n" + " MoreData mMoreData;\n" + "};\n"); + ASSERT_EQUALS( + "[test.cpp:11] -> [test.cpp:10] -> [test.cpp:11]: (error) Non-local variable 'mMoreData.data1' will use pointer to local variable 'data'.\n", + errout_str()); + + // #10784 + check("template \n" + "auto f(int i, Ts&... xs) {\n" + " return std::tie(xs[i]...);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #11362 + check("int* f() {\n" + " static struct { int x; } a[] = { { 1 } };\n" + " return &a[0].x;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #11666 + check("template \n" + "struct Matrix;\n" + "template \n" + "struct Matrix {\n" + " std::array x;\n" + "private:\n" + " static constexpr decltype(&Matrix::x) members[] = {&Matrix::x};\n" + "};\n" + "template \n" + "struct Matrix {\n" + " std::array x;\n" + " std::array y;\n" + "private:\n" + " static constexpr decltype(&Matrix::x) members[] = {&Matrix::x, &Matrix::y};\n" + "};\n" + "template \n" + "Matrix O() {\n" + " return { {}, {} };\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #11729 + check("struct T {\n" + " void add(int* i) {\n" + " v.push_back(i);\n" + " }\n" + " void f() {\n" + " static int val = 1;\n" + " add(&val);\n" + " }\n" + " std::vector v;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void danglingLifetimeFunction() { + check("auto f() {\n" + " int a;\n" + " return std::ref(a);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'a' that will be invalid when returning.\n", + errout_str()); + + check("auto f() {\n" + " int a;\n" + " return std::make_tuple(std::ref(a));\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'a' that will be invalid when returning.\n", + errout_str()); + + check("template\n" + "auto by_value(T x) {\n" + " return [=] { return x; };\n" + "}\n" + "auto g() {\n" + " std::vector v;\n" + " return by_value(v.begin());\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:7] -> [test.cpp:7] -> [test.cpp:3] -> [test.cpp:3] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'v' that will be invalid when returning.\n", + errout_str()); + + check("template\n" + "auto by_value(const T& x) {\n" + " return [=] { return x; };\n" + "}\n" + "auto g() {\n" + " std::vector v;\n" + " return by_value(v.begin());\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:7] -> [test.cpp:7] -> [test.cpp:3] -> [test.cpp:3] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'v' that will be invalid when returning.\n", + errout_str()); + + check("auto by_ref(int& x) {\n" + " return [&] { return x; };\n" + "}\n" + "auto f() {\n" + " int i = 0;\n" + " return by_ref(i);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:1] -> [test.cpp:2] -> [test.cpp:6] -> [test.cpp:5] -> [test.cpp:6]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", + errout_str()); + + check("auto by_ref(const int& x) {\n" + " return [=] { return x; };\n" + "}\n" + "auto f() {\n" + " int i = 0;\n" + " return by_ref(i);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("auto f(int x) {\n" + " int a;\n" + " std::tie(a) = x;\n" + " return a;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::pair\n" + "str_pair(std::string const & a, std::string const & b) {\n" + " return std::make_pair(a, b);\n" + "}\n" + "std::vector > create_parameters() {\n" + " std::vector > par;\n" + " par.push_back(str_pair(\"param1\", \"prop_a\"));\n" + " par.push_back(str_pair(\"param2\", \"prop_b\"));\n" + " par.push_back(str_pair(\"param3\", \"prop_c\"));\n" + " return par;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void danglingLifetimeUserConstructor() + { + check("struct A {\n" + " int* i;\n" + " A(int& x)\n" + " : i(&x)\n" + " {}\n" + "};\n" + "A f() {\n" + " int i = 0;\n" + " A a{i};\n" + " return a;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:9] -> [test.cpp:8] -> [test.cpp:10]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " int* i;\n" + " A(int& x);\n" + "};\n" + "A f() {\n" + " int i = 0;\n" + " A a{i};\n" + " return a;\n" + "}\n", + true); + ASSERT_EQUALS( + "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:8]: (error, inconclusive) Returning object that points to local variable 'i' that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " int* i;\n" + " A(const int& x);\n" + "};\n" + "A f() {\n" + " int i = 0;\n" + " A a{i};\n" + " return a;\n" + "}\n", + true); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " int& i;\n" + " A(int& x)\n" + " : i(x)\n" + " {}\n" + "};\n" + "A f() {\n" + " int i = 0;\n" + " A a{i};\n" + " return a;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:9] -> [test.cpp:8] -> [test.cpp:10]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " int& i;\n" + " A(const std::vector& x)\n" + " : i(x[0])\n" + " {}\n" + "};\n" + "A f() {\n" + " std::vector v = {0};\n" + " A a{v};\n" + " return a;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:9] -> [test.cpp:8] -> [test.cpp:10]: (error) Returning object that points to local variable 'v' that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " int* i;\n" + " A(const std::vector& x)\n" + " : i(x.data())\n" + " {}\n" + "};\n" + "A f() {\n" + " std::vector v = {0};\n" + " A a{v};\n" + " return a;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:9] -> [test.cpp:8] -> [test.cpp:10]: (error) Returning object that points to local variable 'v' that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " const int* i;\n" + " A(const int& x)\n" + " : i(&x)\n" + " {}\n" + "};\n" + "A f() {\n" + " A a{0};\n" + " return a;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:9]: (error) Returning object that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " const int* i;\n" + " A(const int& x)\n" + " : i(&x)\n" + " {}\n" + "};\n" + "A f() {\n" + " int i = 0;\n" + " return A{i};\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:9] -> [test.cpp:8] -> [test.cpp:9]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " std::string v;\n" + " A(const std::string& s)\n" + " : v(s)\n" + " {}\n" + "};\n" + "A f() {\n" + " std::string s;\n" + " return A{s};\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " std::string_view v;\n" + " A(const std::string& s)\n" + " : v(s)\n" + " {}\n" + "};\n" + "A f() {\n" + " std::string s;\n" + " return A{s};\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:9] -> [test.cpp:8] -> [test.cpp:9]: (error) Returning object that points to local variable 's' that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " const int* i;\n" + " A(const int& x)\n" + " : i(&x)\n" + " {}\n" + "};\n" + "A f() {\n" + " return A{0};\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:8]: (error) Returning object that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " int n;\n" + " A(const int &x) : n(x) {}\n" + "};\n" + "A f() {\n" + " A m(4);\n" + " return m;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct B {};\n" + "struct A {\n" + " B n;\n" + " A(const B &x) : n(x) {}\n" + "};\n" + "A f() {\n" + " A m(B{});\n" + " return m;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " A(std::vector &filenames)\n" + " : files(filenames) {}\n" + " std::vector &files;\n" + "};\n" + "A f() {\n" + " std::vector files;\n" + " return A(files);\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:8] -> [test.cpp:7] -> [test.cpp:8]: (error) Returning object that points to local variable 'files' that will be invalid when returning.\n", + errout_str()); + + check("struct S {\n" + " explicit S(std::string& s);\n" + "}\n" + "S f() {\n" + " std::string m(\"abc\");\n" + " return S(m);\n" + "}\n", + true); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " std::string msg;\n" + " explicit S(const char* m) : msg(m) {}\n" + "};\n" + "S f() {\n" + " std::string s(\"abc\");\n" + " return S(s.c_str());\n" + "}\n", + true); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " explicit S(const char* p) { m = p; }\n" + " void g();\n" + " std::string m;\n" + " int* t{};\n" + "};\n" + "void f(const std::stringstream& buffer) {\n" + " S s(buffer.str().c_str());\n" + " s.g();\n" + "}\n", + true); + ASSERT_EQUALS("", errout_str()); + } + + void danglingLifetimeAggegrateConstructor() { + check("struct A {\n" + " const int& x;\n" + " int y;\n" + "};\n" + "A f() {\n" + " int i = 0;\n" + " return A{i, i};\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " const int& x;\n" + " int y;\n" + "};\n" + "A f() {\n" + " int i = 0;\n" + " return {i, i};\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " const int& x;\n" + " int y;\n" + "};\n" + "A f() {\n" + " int i = 0;\n" + " A r{i, i};\n" + " return r;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:8]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " const int& x;\n" + " int y;\n" + "};\n" + "A f() {\n" + " int i = 0;\n" + " A r = {i, i};\n" + " return r;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:8]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " const int& x;\n" + " int y;\n" + "};\n" + "A f(int& x) {\n" + " int i = 0;\n" + " return A{i, x};\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:7] -> [test.cpp:6] -> [test.cpp:7]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " const int& x;\n" + " int y;\n" + "};\n" + "A f(int& x) {\n" + " int i = 0;\n" + " return A{x, i};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " const int& x;\n" + " int y;\n" + "};\n" + "A f(int& x) {\n" + " return A{x, x};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int i; const int& j; };\n" + "A f(int& x) {\n" + " int y = 0;\n" + " return A{y, x};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct a {\n" + " std::string m;\n" + "};\n" + "a f() {\n" + " std::array m {};\n" + " return { m.data() };\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void danglingLifetimeInitList() { + check("std::vector f() {\n" + " int i = 0;\n" + " std::vector v = {&i, &i};\n" + " return v;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n" + "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", // duplicate + errout_str()); + + check("std::vector f() {\n" + " int i = 0;\n" + " std::vector v{&i, &i};\n" + " return v;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n" + "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", // duplicate + errout_str()); + + check("std::vector f() {\n" + " int i = 0;\n" + " return {&i, &i};\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n" + "[test.cpp:3] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:3]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", // duplicate + errout_str()); + + check("std::vector f(int& x) {\n" + " return {&x, &x};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::vector f() {\n" + " std::set x;\n" + " x.insert(\"1\");\n" + " x.insert(\"2\");\n" + " return { x.begin(), x.end() };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void danglingLifetimeImplicitConversion() { + check("struct A { A(const char *a); };\n" + "A f() {\n" + " std::string ba(\"hello\");\n" + " return ba.c_str();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { A(const char *a); };\n" + "A f() {\n" + " std::string ba(\"hello\");\n" + " A bp = ba.c_str();\n" + " return bp;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { A(const char *a); };\n" + "std::vector f() {\n" + " std::string ba(\"hello\");\n" + " std::vector v;\n" + " v.push_back(ba.c_str());\n" + " return v;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::string f(const std::string& x) {\n" + " const char c[] = \"\";\n" + " if (!x.empty())\n" + " return x + c;\n" + " return \"\";\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::string f(const std::string& x) {\n" + " const char c[] = \"123\";\n" + " if (!x.empty())\n" + " return c + 1;\n" + " return \"\";\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void danglingTemporaryLifetime() { + check("struct C {\n" // #11091 + " C(C& rhs);\n" + " explicit C(const S& n, const S& p = {});\n" + " bool f() const;\n" + " S m;\n" + "};\n" + "void f() {\n" + " C c(\"\");\n" + " while (c.f()) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("const int& g(const int& x) {\n" + " return x;\n" + "}\n" + "void f(int& i) {\n" + " int* x = &g(0);\n" + " i += *x;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:1] -> [test.cpp:2] -> [test.cpp:5] -> [test.cpp:5] -> [test.cpp:5] -> [test.cpp:6]: (error) Using pointer that is a temporary.\n", + errout_str()); + + check("QString f() {\n" + " QString a(\"dummyValue\");\n" + " const char* b = a.toStdString().c_str();\n" + " QString c = b;\n" + " return c;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3] -> [test.cpp:4]: (error) Using pointer that is a temporary.\n", + errout_str()); + + check("auto f(std::string s) {\n" + " const char *x = s.substr(1,2).c_str();\n" + " auto i = s.substr(4,5).begin();\n" + " return *i;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3] -> [test.cpp:4]: (error) Using iterator that is a temporary.\n", + errout_str()); + + check("std::string f() {\n" + " std::stringstream tmp;\n" + " const std::string &str = tmp.str();\n" + " return std::string(str.c_str(), 1);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int get_value();\n" + "const int &get_reference1() {\n" + " const int &x = get_value();\n" + " return x;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Reference to temporary returned.\n", errout_str()); + + check("int get_value();\n" + "const int &get_reference2() {\n" + " const int &x1 = get_value();\n" + " const int &x2 = x1;\n" + " return x2;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3] -> [test.cpp:5]: (error) Reference to temporary returned.\n", + errout_str()); + + check("const std::string& getState() {\n" + " static const std::string& state = \"\";\n" + " return state;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct var {\n" + " void fun();\n" + "}x;\n" + "var* T(const char*) {\n" + " return &x;\n" + "}\n" + "std::string GetTemp();\n" + "void f() {\n" + " auto a = T(GetTemp().c_str());\n" + " a->fun();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " std::map m_;\n" + "};\n" + "struct B {\n" + " A a_;\n" + "};\n" + "B func();\n" + "void f() {\n" + " const std::map::iterator& m = func().a_.m_.begin();\n" + " (void)m->first;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:9] -> [test.cpp:10]: (error) Using iterator that is a temporary.\n", + errout_str()); + + check("void f(bool b) {\n" + " std::vector ints = g();\n" + " auto *ptr = &ints;\n" + " if (b)\n" + " ptr = &ints;\n" + " for (auto it = ptr->begin(); it != ptr->end(); ++it)\n" + " {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct String {\n" // #10469 + " void Append(uint8_t Val);\n" + " String& operator+=(const char s[]);\n" + " String& operator+=(const std::string& Str) {\n" + " return operator+=(Str.c_str());\n" + " }\n" + " void operator+=(uint8_t Val) {\n" + " Append(Val);\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + // #11057 + check("struct S {\n" + " int& r;\n" + "};\n" + "void f(int i) {\n" + " const S a[] = { { i } };\n" + " for (const auto& s : a) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("std::vector f(const std::vector& args) {\n" // #9773 + " std::vector cargs;\n" + " for (const auto& a : args) {\n" + " cargs.push_back(const_cast(a.data()));\n" + " }\n" + " return cargs;\n" + "}\n" + "void g() {\n" + " std::vector cargs = f({ \"0\", \"0\" });\n" + " (void)cargs;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:4] -> [test.cpp:3] -> [test.cpp:1] -> [test.cpp:4] -> [test.cpp:9] -> [test.cpp:9] -> [test.cpp:10]: (error) Using object that is a temporary.\n", errout_str()); + + check("struct C {\n" // #9194 + " const int& m;\n" + " C(const int& i) : m(i) {}\n" + " int get() { return m; }\n" + "};\n" + "int f() {\n" + " C c(42);\n" + " return c.get();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:7] -> [test.cpp:8]: (error) Using object that is a temporary.\n", errout_str()); + + // #11298 + check("struct S {\n" + " std::string g(); \n" + "};\n" + "struct T {\n" + " void f(); \n" + " S* p = nullptr;\n" + "};\n" + "struct U {\n" + " explicit U(const char* s);\n" + " bool h();\n" + " int i;\n" + "};\n" + "void T::f() {\n" + " U u(p->g().c_str());\n" + " if (u.h()) {}\n" + "}\n", + true); + ASSERT_EQUALS("", errout_str()); + + // #11442 + check("const std::string& f(const P< std::string >& value) {\n" + " static const std::string empty;\n" + " return value.get() == nullptr ? empty : *value;\n" + "}\n", + true); + ASSERT_EQUALS("", errout_str()); + + // #11472 + check("namespace N {\n" + " struct T { int m; };\n" + " int i;\n" + " const T& f(const T* p) {\n" + " return p != nullptr ? *p : *reinterpret_cast(&i);\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #11609 + check("struct S {\n" + " void f(const std::string& s) {\n" + " auto it = m.find(s.substr(1,4));\n" + " if (it == m.end()) {}\n" + " }\n" + " std::map m;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + // #11628 + check("std::vector* g();\n" + "void f() {\n" + " std::unique_ptr> p(g());\n" + " if (!p) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void danglingLifetimeBorrowedMembers() + { + // #10585 + check("struct Info { int k; };\n" + "struct MoreInfo {\n" + " int* k;\n" + " char dat;\n" + "};\n" + "struct Fields {\n" + " Info info;\n" + "};\n" + "template void func1(T val){}\n" + "template void func2(T val){}\n" + "Fields* get();\n" + "void doit() {\n" + " MoreInfo rech;\n" + " rech.k = &get()->info.k;\n" + " func1(&rech.dat);\n" + " func2(rech.k);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int x; };\n" + "A* g();\n" + "void f() {\n" + " A** ap = &g();\n" + " (*ap)->x;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:4] -> [test.cpp:5]: (error) Using pointer that is a temporary.\n", + errout_str()); + + check("struct A { int* x; };\n" + "A g();\n" + "void f() {\n" + " int* x = g().x;\n" + " (void)*x + 1;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int x; };\n" + "struct B { A* a; }\n" + "B g();\n" + "void f() {\n" + " int* x = &g()->a.x;\n" + " (void)*x + 1;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int x; };\n" + "struct B { A* g(); };\n" + "A* g();\n" + "void f(B b) {\n" + " A** ap = &b.g();\n" + " (*ap)->x;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5] -> [test.cpp:6]: (error) Using pointer that is a temporary.\n", + errout_str()); + } + + void danglingLifetimeClassMemberFunctions() + { + check("struct S {\n" + " S(int i) : i(i) {}\n" + " int i;\n" + " int* ptr() { return &i; }\n" + "};\n" + "int* fun(int i) { \n" + " return S(i).ptr();\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:4] -> [test.cpp:7] -> [test.cpp:7]: (error) Returning pointer that will be invalid when returning.\n", + errout_str()); + + check("struct Fred\n" + "{\n" + " int x[2];\n" + " Fred() {\n" + " x[0] = 0x41;\n" + " x[1] = 0x42;\n" + " }\n" + " const int *get_x() {\n" + " return x;\n" + " }\n" + "};\n" + "static const int *foo() {\n" + " Fred fred;\n" + " return fred.get_x();\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:9] -> [test.cpp:9] -> [test.cpp:14] -> [test.cpp:13] -> [test.cpp:14]: (error) Returning pointer to local variable 'fred' that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " int i;\n" + " auto f() const {\n" + " return [=]{ return i; };\n" + " }\n" + "};\n" + "auto g() {\n" + " return A().f();\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:4] -> [test.cpp:8] -> [test.cpp:8]: (error) Returning object that will be invalid when returning.\n", + errout_str()); + + check("struct A {\n" + " int i;\n" + " auto f() const {\n" + " return [*this]{ return i; };\n" + " }\n" + "};\n" + "auto g() {\n" + " return A().f();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " int* i;\n" + " auto f() const {\n" + " return [*this]{ return i; };\n" + " }\n" + "};\n" + "auto g() {\n" + " int i = 0;\n" + " return A{&i}.f();\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:9] -> [test.cpp:9] -> [test.cpp:9] -> [test.cpp:4] -> [test.cpp:4] -> [test.cpp:8] -> [test.cpp:9]: (error) Returning object that points to local variable 'i' that will be invalid when returning.\n", + errout_str()); + + check("struct S {\n" + " int i{};\n" + "};\n" + "struct T {\n" + " S getS() const { return S{ j }; }\n" + " int j{};\n" + "};\n" + "void f(S* p) {\n" + " S ret;\n" + " {\n" + " T t;\n" + " ret = t.getS();\n" + " }\n" + " *p = ret;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void invalidLifetime() { + check("void foo(int a) {\n" + " std::function f;\n" + " if (a > 0) {\n" + " int b = a + 1;\n" + " f = [&]{ return b; };\n" + " }\n" + " f();\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4] -> [test.cpp:7]: (error) Using lambda that captures local variable 'b' that is out of scope.\n", errout_str()); + + check("void f(bool b) {\n" + " int* x;\n" + " if(b) {\n" + " int y[6] = {0,1,2,3,4,5};\n" + " x = y;\n" + " }\n" + " x[3];\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:5] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'y' that is out of scope.\n", + errout_str()); + + check("void foo(int a) {\n" + " std::function f;\n" + " if (a > 0) {\n" + " int b = a + 1;\n" + " f = [&]{ return b; };\n" + " f();\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct a {\n" + " b();\n" + " std::list c;\n" + "};\n" + "void a::b() {\n" + " c.end()\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void b(char f[], char c[]) {\n" + " std::string d(c); {\n" + " std::string e;\n" + " b(f, e.c_str())\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool b) {\n" + " std::string s;\n" + " if(b) {\n" + " char buf[3];\n" + " s = buf;\n" + " }\n" + " std::cout << s;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int &a[];\n" + "void b(){int *c = a};"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " int x;\n" + "};\n" + "struct B {\n" + " std::function x;\n" + " void f() {\n" + " this->x = [&] {\n" + " B y;\n" + " return y.x;\n" + " };\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("namespace test {\n" + "class Foo {};\n" + "struct Bar {\n" + " Foo *_foo;\n" + "};\n" + "\n" + "int f(Bar *bar);\n" + "\n" + "void g(Bar *bar) {\n" + " {\n" + " Foo foo;\n" + " bar->_foo = &foo;\n" + " bar->_foo = nullptr;\n" + " }\n" + " f(bar);\n" + "}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("class Foo {};\n" + "struct Bar {\n" + " Foo *_foo;\n" + "};\n" + "\n" + "int f(Bar *bar);\n" + "\n" + "void g(Bar *bar) {\n" + " {\n" + " Foo foo;\n" + " bar->_foo = &foo;\n" + " bar->_foo = nullptr;\n" + " }\n" + " f(bar);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("namespace test {\n" + "class Foo {};\n" + "struct Bar {\n" + " Foo *_foo;\n" + "};\n" + "\n" + "int f(Bar *bar);\n" + "\n" + "void g(Bar *bar) {\n" + " {\n" + " Foo foo;\n" + " bar->_foo = &foo;\n" + " }\n" + " f(bar);\n" + "}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:12]: (error) Address of local auto-variable assigned to a function parameter.\n", errout_str()); + + check("class Foo {};\n" + "struct Bar {\n" + " Foo *_foo;\n" + "};\n" + "\n" + "int f(Bar *bar);\n" + "\n" + "void g(Bar *bar) {\n" + " {\n" + " Foo foo;\n" + " bar->_foo = &foo;\n" + " }\n" + " f(bar);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:11]: (error) Address of local auto-variable assigned to a function parameter.\n", errout_str()); + + check("class Foo {};\n" // #10750 + "struct Bar {\n" + " Foo *_foo;\n" + "};\n" + "int f(Bar *bar);\n" + "void g(Bar *bar) {\n" + " {\n" + " Foo foo;\n" + " {\n" + " bar->_foo = &foo;\n" + " }\n" + " }\n" + " f(bar);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:10]: (error) Address of local auto-variable assigned to a function parameter.\n", errout_str()); + + check("void f(std::string_view text);\n" // #11508 + "void g() {\n" + " std::string teststr;\n" + " f(teststr);" + "}\n" + "void f(std::string_view text) {" + " g(text.data());\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::span data);\n" // #11508 + "void g() {\n" + " std::vector v;\n" + " f(v);" + "}\n" + "void f(std::span data) {" + " g(data.begin());\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + } + + void deadPointer() { + check("void f() {\n" + " int *p = p1;\n" + " if (cond) {\n" + " int x;\n" + " p = &x;\n" + " }\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'x' that is out of scope.\n", errout_str()); + + // FP: don't warn in subfunction + check("void f(struct KEY *key) {\n" + " key->x = 0;\n" + "}\n" + "\n" + "int main() {\n" + " struct KEY *tmp = 0;\n" + " struct KEY k;\n" + "\n" + " if (condition) {\n" + " tmp = &k;\n" + " } else {\n" + " }\n" + " f(tmp);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Don't warn about references (#6399) + check("void f() {\n" + " wxAuiToolBarItem* former_hover = NULL;\n" + " for (i = 0, count = m_items.GetCount(); i < count; ++i) {\n" + " wxAuiToolBarItem& item = m_items.Item(i);\n" + " former_hover = &item;\n" + " }\n" + " if (former_hover != pitem)\n" + " dosth();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " wxAuiToolBarItem* former_hover = NULL;\n" + " for (i = 0, count = m_items.GetCount(); i < count; ++i) {\n" + " wxAuiToolBarItem item = m_items.Item(i);\n" + " former_hover = &item;\n" + " }\n" + " if (former_hover != pitem)\n" + " dosth();\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:5] -> [test.cpp:3] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'item' that is out of scope.\n", + errout_str()); + + // #6575 + check("void trp_deliver_signal() {\n" + " union {\n" + " Uint32 theData[25];\n" + " EventReport repData;\n" + " };\n" + " EventReport * rep = &repData;\n" + " rep->setEventType(NDB_LE_Connected);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #8785 + check("int f(bool a, bool b) {\n" + " int *iPtr = 0;\n" + " if(b) {\n" + " int x = 42;\n" + " iPtr = &x;\n" + " }\n" + " if(b && a)\n" + " return *iPtr;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4] -> [test.cpp:8]: (error) Using pointer to local variable 'x' that is out of scope.\n", errout_str()); + + // #11753 + check("int main(int argc, const char *argv[]) {\n" + " const char* s = \"\";\n" + " if (argc > 0) {\n" + " char buff[32]{};\n" + " s = std::strncpy(buff, argv[0], 31);\n" + " }\n" + " std::cout << s;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4] -> [test.cpp:7]: (error) Using pointer to local variable 'buff' that is out of scope.\n", errout_str()); + + check("char* f(char* dst) {\n" + " const char* src = \"abc\";\n" + " return strcpy(dst, src);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void splitNamespaceAuto() { // #10473 + check("namespace ns\n" + "{\n" + " auto var{ 0 };\n" + "}\n" + "namespace ns\n" + "{\n" + " int i;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + +}; + +REGISTER_TEST(TestAutoVariables) diff --git a/cppcheck-2.14.0/test/testbool.cpp b/cppcheck-2.14.0/test/testbool.cpp new file mode 100644 index 00000000..b0fb5b25 --- /dev/null +++ b/cppcheck-2.14.0/test/testbool.cpp @@ -0,0 +1,1386 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "settings.h" + +class TestBool : public TestFixture { +public: + TestBool() : TestFixture("TestBool") {} + +private: + const Settings settings = settingsBuilder().severity(Severity::style).severity(Severity::warning).certainty(Certainty::inconclusive).build(); + + void run() override { + TEST_CASE(bitwiseOnBoolean); // if (bool & bool) + TEST_CASE(incrementBoolean); + TEST_CASE(assignBoolToPointer); + TEST_CASE(assignBoolToFloat); + + TEST_CASE(comparisonOfBoolExpressionWithInt1); + TEST_CASE(comparisonOfBoolExpressionWithInt2); + TEST_CASE(comparisonOfBoolExpressionWithInt3); + TEST_CASE(comparisonOfBoolExpressionWithInt4); + + TEST_CASE(comparisonOfBoolWithInt1); + TEST_CASE(comparisonOfBoolWithInt2); + TEST_CASE(comparisonOfBoolWithInt3); + TEST_CASE(comparisonOfBoolWithInt4); + TEST_CASE(comparisonOfBoolWithInt5); + TEST_CASE(comparisonOfBoolWithInt6); // #4224 - integer is casted to bool + TEST_CASE(comparisonOfBoolWithInt7); // #4846 - (!x == true) + TEST_CASE(comparisonOfBoolWithInt8); // #9165 + TEST_CASE(comparisonOfBoolWithInt9); // #9304 + TEST_CASE(comparisonOfBoolWithInt10); // #10935 + + TEST_CASE(checkComparisonOfFuncReturningBool1); + TEST_CASE(checkComparisonOfFuncReturningBool2); + TEST_CASE(checkComparisonOfFuncReturningBool3); + TEST_CASE(checkComparisonOfFuncReturningBool4); + TEST_CASE(checkComparisonOfFuncReturningBool5); + TEST_CASE(checkComparisonOfFuncReturningBool6); + TEST_CASE(checkComparisonOfFuncReturningBool7); // #7197 + TEST_CASE(checkComparisonOfFuncReturningBool8); // #4103 + // Integration tests.. + TEST_CASE(checkComparisonOfFuncReturningBoolIntegrationTest1); // #7798 overloaded functions + + TEST_CASE(checkComparisonOfBoolWithBool); + + // Converting pointer addition result to bool + TEST_CASE(pointerArithBool1); + + TEST_CASE(returnNonBool); + TEST_CASE(returnNonBoolLambda); + TEST_CASE(returnNonBoolLogicalOp); + TEST_CASE(returnNonBoolClass); + } + +#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) + void check_(const char* file, int line, const char code[], bool cpp = true) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code, cpp), file, line); + + // Check... + runChecks(tokenizer, this); + } + + + void assignBoolToPointer() { + check("void foo(bool *p) {\n" + " p = false;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Boolean value assigned to pointer.\n", errout_str()); + + check("void foo(bool *p) {\n" + " p = (x[rSize];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // ticket #6588 (c mode) + check("struct MpegEncContext { int *q_intra_matrix, *q_chroma_intra_matrix; };\n" + "void dnxhd_10bit_dct_quantize(MpegEncContext *ctx, int n, int qscale) {\n" + " const int *qmat = n < 4;\n" /* KO */ + " const int *rmat = n < 4 ? " /* OK */ + " ctx->q_intra_matrix :" + " ctx->q_chroma_intra_matrix;\n" + "}", false); + ASSERT_EQUALS("[test.c:3]: (error) Boolean value assigned to pointer.\n", errout_str()); + + // ticket #6588 (c++ mode) + check("struct MpegEncContext { int *q_intra_matrix, *q_chroma_intra_matrix; };\n" + "void dnxhd_10bit_dct_quantize(MpegEncContext *ctx, int n, int qscale) {\n" + " const int *qmat = n < 4;\n" /* KO */ + " const int *rmat = n < 4 ? " /* OK */ + " ctx->q_intra_matrix :" + " ctx->q_chroma_intra_matrix;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Boolean value assigned to pointer.\n", errout_str()); + + // ticket #6665 + check("void pivot_big(char *first, int compare(const void *, const void *)) {\n" + " char *a = first, *b = first + 1, *c = first + 2;\n" + " char* m1 = compare(a, b) < 0\n" + " ? (compare(b, c) < 0 ? b : (compare(a, c) < 0 ? c : a))\n" + " : (compare(a, c) < 0 ? a : (compare(b, c) < 0 ? c : b));\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + + // #7381 + check("void foo(bool *p, bool b) {\n" + " p = b;\n" + " p = &b;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Boolean value assigned to pointer.\n", errout_str()); + } + + void assignBoolToFloat() { + check("void foo1() {\n" + " double d = false;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Boolean value assigned to floating point variable.\n", errout_str()); + + check("void foo2() {\n" + " float d = true;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Boolean value assigned to floating point variable.\n", errout_str()); + + check("void foo3() {\n" + " long double d = (2>1);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Boolean value assigned to floating point variable.\n", errout_str()); + + // stability - don't crash: + check("void foo4() {\n" + " unknown = false;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " float p;\n" + "};\n" + "void f() {\n" + " S s = {0};\n" + " s.p = true;\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (style) Boolean value assigned to floating point variable.\n", errout_str()); + + check("struct S {\n" + " float* p[1];\n" + "};\n" + "void f() {\n" + " S s = {0};\n" + " *s.p[0] = true;\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (style) Boolean value assigned to floating point variable.\n", errout_str()); + } + + void comparisonOfBoolExpressionWithInt1() { + check("void f(int x) {\n" + " if ((x && 0x0f)==6)\n" + " a++;\n" + "}"); + 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" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if ((x || 0x0f)==6)\n" + " a++;\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" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if ((x & 0x0f)==6)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if ((x | 0x0f)==6)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + + check("void f(int x) {\n" + " if ((5 && x)==3)\n" + " a++;\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" + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n" + "[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", // duplicate + errout_str()); + + check("void f(int x) {\n" + " if ((5 && x)!=3)\n" + " a++;\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" + "}"); + 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" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if ((5 && x) < 0)\n" + " a++;\n" + "}" + ); + ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer.\n", errout_str()); + + check("void f(int x) {\n" + " if ((5 && x) < 1)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if ((5 && x) > 1)\n" + " a++;\n" + "}" + ); + ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer.\n", errout_str()); + + + check("void f(int x) {\n" + " if (0 < (5 && x))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if (0 > (5 && x))\n" + " a++;\n" + "}" + ); + ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer.\n", errout_str()); + + check("void f(int x) {\n" + " if (1 > (5 && x))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if (1 < (5 && x))\n" + " a++;\n" + "}" + ); + ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer.\n", errout_str()); + + check("void f(bool x ) {\n" + " if ( x > false )\n" + " a++;\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" + "}"); + 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" + "}"); + 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" + "}"); + 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" + "}"); + 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" + "}"); + 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" + "}"); + 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" + "}"); + 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.\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("", errout_str()); + + check("void f(int a, int b, int c) {\n" + " return x(a > b) < c;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int a, int b, int c) {\n" + " return a > b == c;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // templates + check("struct Tokenizer { TokenList list; };\n" + "void Tokenizer::f() {\n" + " std::list locationList;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #5063 - or + check("void f() {\n" + " return a > b or c < d;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f() {\n" + " return (a < b) != 0U;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("int f() {\n" + " return (a < b) != 0x0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("int f() {\n" + " return (a < b) != 42U;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout_str()); + } + + void checkComparisonOfFuncReturningBool1() { + check("void f(){\n" + " int temp = 4;\n" + " if(compare1(temp) > compare2(temp)){\n" + " printf(\"foo\");\n" + " }\n" + "}\n" + "bool compare1(int temp){\n" + " if(temp==4){\n" + " return true;\n" + " }\n" + " else\n" + " return false;\n" + "}\n" + "bool compare2(int temp){\n" + " if(temp==4){\n" + " return false;\n" + " }\n" + " else\n" + " return true;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n", errout_str()); + } + + void checkComparisonOfFuncReturningBool2() { + check("void leftOfComparison(){\n" + " int temp = 4;\n" + " bool a = true;\n" + " if(compare(temp) > a){\n" + " printf(\"foo\");\n" + " }\n" + "}\n" + "void rightOfComparison(){\n" + " int temp = 4;\n" + " bool a = true;\n" + " if(a < compare(temp)){\n" + " printf(\"foo\");\n" + " }\n" + "}\n" + "bool compare(int temp){\n" + " if(temp==4){\n" + " return true;\n" + " }\n" + " else\n" + " return false;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n" + "[test.cpp:11]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n", errout_str()); + } + + void checkComparisonOfFuncReturningBool3() { + check("void f(){\n" + " int temp = 4;\n" + " if(compare(temp) > temp){\n" + " printf(\"foo\");\n" + " }\n" + "}\n" + "bool compare(int temp);"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n" + "[test.cpp:3]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n", errout_str()); + } + + void checkComparisonOfFuncReturningBool4() { + check("void f(){\n" + " int temp = 4;\n" + " bool b = compare2(6);\n" + " if(compare1(temp)> b){\n" + " printf(\"foo\");\n" + " }\n" + "}\n" + "bool compare1(int temp){\n" + " if(temp==4){\n" + " return true;\n" + " }\n" + " else\n" + " return false;\n" + "}\n" + "bool compare2(int temp){\n" + " if(temp == 5){\n" + " return true;\n" + " }\n" + " else\n" + " return false;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n", errout_str()); + } + + void checkComparisonOfFuncReturningBool5() { + check("void f(){\n" + " int temp = 4;\n" + " if(compare1(temp) > !compare2(temp)){\n" + " printf(\"foo\");\n" + " }\n" + "}\n" + "bool compare1(int temp){\n" + " if(temp==4){\n" + " return true;\n" + " }\n" + " else\n" + " return false;\n" + "}\n" + "bool compare2(int temp){\n" + " if(temp==4){\n" + " return false;\n" + " }\n" + " else\n" + " return true;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n", errout_str()); + } + + void checkComparisonOfFuncReturningBool6() { + check("int compare1(int temp);\n" + "namespace Foo {\n" + " bool compare1(int temp);\n" + "}\n" + "void f(){\n" + " int temp = 4;\n" + " if(compare1(temp) > compare2(temp)){\n" + " printf(\"foo\");\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("namespace Foo {\n" + " bool compare1(int temp);\n" + "}\n" + "int compare1(int temp);\n" + "void f(){\n" + " int temp = 4;\n" + " if(compare1(temp) > compare2(temp)){\n" + " printf(\"foo\");\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int compare1(int temp);\n" + "namespace Foo {\n" + " bool compare1(int temp);\n" + " void f(){\n" + " int temp = 4;\n" + " if(compare1(temp) > compare2(temp)){\n" + " printf(\"foo\");\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (style) Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n", errout_str()); + + check("int compare1(int temp);\n" + "namespace Foo {\n" + " bool compare1(int temp);\n" + " void f(){\n" + " int temp = 4;\n" + " if(::compare1(temp) > compare2(temp)){\n" + " printf(\"foo\");\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool compare1(int temp);\n" + "void f(){\n" + " int temp = 4;\n" + " if(foo.compare1(temp) > compare2(temp)){\n" + " printf(\"foo\");\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void checkComparisonOfFuncReturningBool7() { // #7197 + check("struct C {\n" + " bool isEmpty();\n" + "};\n" + "void f() {\n" + " C c1, c2;\n" + " if ((c1.isEmpty()) < (c2.isEmpty())) {}\n" + " if (!c1.isEmpty() < !!c2.isEmpty()) {}\n" + " if ((int)c1.isEmpty() < (int)c2.isEmpty()) {}\n" + " if (static_cast(c1.isEmpty()) < static_cast(c2.isEmpty())) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:6]: (style) Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n" + "[test.cpp:7]: (style) Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n" + "[test.cpp:8]: (style) Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n" + "[test.cpp:9]: (style) Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n", + errout_str()); + } + + void checkComparisonOfFuncReturningBool8() { // #4103 + // op: > + check("int main(void){\n" + " bool a = true;\n" + " bool b = false;\n" + " if(b > a){ \n" // here warning should be displayed + " ;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Comparison of a variable having boolean value using relational (<, >, <= or >=) operator.\n", errout_str()); + // op: < + check("int main(void){\n" + " bool a = true;\n" + " bool b = false;\n" + " if(b < a){ \n" // here warning should be displayed + " ;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Comparison of a variable having boolean value using relational (<, >, <= or >=) operator.\n", errout_str()); + // op: >= + check("int main(void){\n" + " bool a = true;\n" + " bool b = false;\n" + " if(b >= a){ \n" // here warning should be displayed + " ;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Comparison of a variable having boolean value using relational (<, >, <= or >=) operator.\n", errout_str()); + // op: <= + check("int main(void){\n" + " bool a = true;\n" + " bool b = false;\n" + " if(b <= a){ \n" // here warning should be displayed + " ;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Comparison of a variable having boolean value using relational (<, >, <= or >=) operator.\n", errout_str()); + } + + void checkComparisonOfFuncReturningBoolIntegrationTest1() { // #7798 + check("bool eval(double *) { return false; }\n" + "double eval(char *) { return 1.0; }\n" + "int main(int argc, char *argv[])\n" + "{\n" + " if ( eval(argv[1]) > eval(argv[2]) )\n" + " return 1;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void checkComparisonOfBoolWithBool() { + const char code[] = "void f(){\n" + " int temp = 4;\n" + " bool b = compare2(6);\n" + " bool a = compare1(4);\n" + " if(b > a){\n" + " printf(\"foo\");\n" + " }\n" + "}\n" + "bool compare1(int temp){\n" + " if(temp==4){\n" + " return true;\n" + " }\n" + " else\n" + " return false;\n" + "}\n" + "bool compare2(int temp){\n" + " if(temp == 5){\n" + " return true;\n" + " }\n" + " else\n" + " return false;\n" + "}\n"; + check(code); + ASSERT_EQUALS("[test.cpp:5]: (style) Comparison of a variable having boolean value using relational (<, >, <= or >=) operator.\n", errout_str()); + } + + void bitwiseOnBoolean() { // 3062 + check("void f(_Bool a, _Bool b) {\n" + " if(a & b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '&&'?\n", errout_str()); + + check("void f(_Bool a, _Bool b) {\n" + " if(a | b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '||'?\n", errout_str()); + + check("void f(bool a, bool b) {\n" + " if(a & !b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '&&'?\n", errout_str()); + + check("void f(bool a, bool b) {\n" + " if(a | !b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '||'?\n", errout_str()); + + check("bool a, b;\n" + "void f() {\n" + " if(a & b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '&&'?\n", errout_str()); + + check("bool a, b;\n" + "void f() {\n" + " if(a & !b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '&&'?\n", errout_str()); + + check("bool a, b;\n" + "void f() {\n" + " if(a | b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '||'?\n", errout_str()); + + check("bool a, b;\n" + "void f() {\n" + " if(a | !b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '||'?\n", errout_str()); + + check("void f(bool a, int b) {\n" + " if(a & b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '&&'?\n", errout_str()); + + check("void f(int a, bool b) {\n" + " if(a & b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'b' is used in bitwise operation. Did you mean '&&'?\n", errout_str()); + + check("void f(int a, int b) {\n" + " if((a > 0) & (b < 0)) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a>0' is used in bitwise operation. Did you mean '&&'?\n", errout_str()); + + check("void f(bool a, int b) {\n" + " if(a | b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '||'?\n", errout_str()); + + check("void f(int a, bool b) {\n" + " if(a | b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'b' is used in bitwise operation. Did you mean '||'?\n", errout_str()); + + check("int f(bool a, int b) {\n" + " return a | b;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(bool a, int b) {\n" + " return a | b;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Boolean expression 'a' is used in bitwise operation. Did you mean '||'?\n", errout_str()); + + check("void f(int a, int b) {\n" + " if(a & b) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool b) {\n" + " foo(bar, &b);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool b) {\n" // #9405 + " class C { void foo(bool &b) {} };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f();\n" + "bool g() {\n" + " return f() | f();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("uint8 outcode(float p) {\n" + " float d = 0.;\n" + " return ((p - xm >= d) << 1) | (x - p > d);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int g();\n" // #10655 + "void f(bool b) {\n" + " if (g() | b) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Boolean expression 'b' is used in bitwise operation. Did you mean '||'?\n", errout_str()); + + check("int g();\n" + "void f(bool b) {\n" + " if (b | g()) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int g();\n" + "bool f(bool b, bool c) {\n" + " return b | g() | c;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Boolean expression 'c' is used in bitwise operation. Did you mean '||'?\n", errout_str()); + + check("void f(int i) {\n" // #4233 + " bool b = true, c = false;\n" + " b &= i;\n" + " c |= i;\n" + " if (b || c) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Boolean expression 'b' is used in bitwise operation.\n" + "[test.cpp:4]: (style, inconclusive) Boolean expression 'c' is used in bitwise operation.\n", + errout_str()); + + check("void f(int i, int j, bool b) {\n" + " i &= b;\n" + " j |= b;\n" + " if (b || c) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(bool b, int i) {\n" + " b &= (i == 5);\n" + " return b;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { bool b{}; };\n" // #12455 + "void f(const std::unordered_map m) {\n" + " for (const auto& e : m) {\n" + " S s;\n" + " s.b |= e.second.b;\n" + " (void)s.b;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void incrementBoolean() { + check("bool bValue = true;\n" + "void f() { bValue++; }"); + ASSERT_EQUALS("[test.cpp:2]: (style) Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n", errout_str()); + + check("void f(bool test){\n" + " test++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n", errout_str()); + + check("void f(bool* test){\n" + " (*test)++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n", errout_str()); + + check("void f(bool* test){\n" + " test[0]++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Incrementing a variable of type 'bool' with postfix operator++ is deprecated by the C++ Standard. You should assign it the value 'true' instead.\n", errout_str()); + + check("void f(int test){\n" + " test++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void comparisonOfBoolWithInt1() { + check("void f(bool x) {\n" + " if (x < 10) {\n" + " printf(\"foo\");\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout_str()); + + check("void f(bool x) {\n" + " if (10 >= x) {\n" + " printf(\"foo\");\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout_str()); + + check("void f(bool x) {\n" + " if (x != 0) {\n" + " printf(\"foo\");\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool x) {\n" // #3356 + " if (x == 1) {\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool x) {\n" + " if (x != 10) {\n" + " printf(\"foo\");\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout_str()); + + check("void f(bool x) {\n" + " if (x == 10) {\n" + " printf(\"foo\");\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout_str()); + + check("void f(bool x) {\n" + " if (x == 0) {\n" + " printf(\"foo\");\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("DensePropertyMap visited;"); // #4075 + ASSERT_EQUALS("", errout_str()); + } + + void comparisonOfBoolWithInt2() { + check("void f(bool x, int y) {\n" + " if (x == y) {\n" + " printf(\"foo\");\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x, bool y) {\n" + " if (x == y) {\n" + " printf(\"foo\");\n" + " }\n" + "}"); + ASSERT_EQUALS("", 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 value using relational operator (<, >, <= or >=).\n", errout_str()); + + check("void f(int y) {\n" + " if (true == y) {\n" + " printf(\"foo\");\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool y) {\n" + " if (y == true) {\n" + " printf(\"foo\");\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool y) {\n" + " if (false < 5) {\n" + " printf(\"foo\");\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of a boolean expression with an integer other than 0 or 1.\n", errout_str()); + } + + void comparisonOfBoolWithInt4() { + check("void f(int x) {\n" + " if (!x == 1) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void comparisonOfBoolWithInt5() { + check("void SetVisible(int index, bool visible) {\n" + " bool (SciTEBase::*ischarforsel)(char ch);\n" + " if (visible != GetVisible(index)) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void comparisonOfBoolWithInt6() { // #4224 - integer is casted to bool + check("void SetVisible(bool b, int i) {\n" + " if (b == (bool)i) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void comparisonOfBoolWithInt7() { // #4846 - (!x==true) + check("void f(int x) {\n" + " if (!x == true) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void comparisonOfBoolWithInt8() { // #9165 + check("bool Fun();\n" + "void Test(bool expectedResult) {\n" + " auto res = Fun();\n" + " if (expectedResult == res)\n" + " throw 2;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int Fun();\n" + "void Test(bool expectedResult) {\n" + " auto res = Fun();\n" + " if (expectedResult == res)\n" + " throw 2;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool Fun();\n" + "void Test(bool expectedResult) {\n" + " auto res = Fun();\n" + " if (5 + expectedResult == res)\n" + " throw 2;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int Fun();\n" + "void Test(bool expectedResult) {\n" + " auto res = Fun();\n" + " if (5 + expectedResult == res)\n" + " throw 2;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int Fun();\n" + "void Test(bool expectedResult) {\n" + " auto res = Fun();\n" + " if (expectedResult == res + 5)\n" + " throw 2;\n" + "}"); + TODO_ASSERT_EQUALS("error", "", errout_str()); + } + + void comparisonOfBoolWithInt9() { // #9304 + check("bool f(int a, bool b)\n" + "{\n" + " if ((a == 0 ? false : true) != b) {\n" + " b = !b;\n" + " }\n" + " return b;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void comparisonOfBoolWithInt10() { // #10935 + check("enum class E { H = 2 };\n" + "template \n" + "void f(bool v) {\n" + " if (v == H) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("namespace N {\n" + " enum class E { H = 2 };\n" + "}\n" + "void f(bool v) {\n" + " if (v == N::H) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + + void pointerArithBool1() { // #5126 + check("void f(char *p) {\n" + " if (p+1){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout_str()); + + check("void f(char *p) {\n" + " do {} while (p+1);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout_str()); + + check("void f(char *p) {\n" + " while (p-1) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout_str()); + + check("void f(char *p) {\n" + " for (int i = 0; p+1; i++) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout_str()); + + check("void f(char *p) {\n" + " if (p && p+1){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout_str()); + + check("void f(char *p) {\n" + " if (p+2 || p) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Converting pointer arithmetic result to bool. The bool is always true unless there is undefined behaviour.\n", errout_str()); + } + + void returnNonBool() { + check("bool f(void) {\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(void) {\n" + " return 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(void) {\n" + " return 2;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Non-boolean value returned from function returning bool\n", errout_str()); + + check("bool f(void) {\n" + " return -1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Non-boolean value returned from function returning bool\n", errout_str()); + + check("bool f(void) {\n" + " return 1 + 1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Non-boolean value returned from function returning bool\n", errout_str()); + + check("bool f(void) {\n" + " int x = 0;\n" + " return x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(void) {\n" + " int x = 10;\n" + " return x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout_str()); + + check("bool f(void) {\n" + " return 2 < 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(void) {\n" + " int ret = 0;\n" + " if (a)\n" + " ret = 1;\n" + " return ret;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(void) {\n" + " int ret = 0;\n" + " if (a)\n" + " ret = 3;\n" + " return ret;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) Non-boolean value returned from function returning bool\n", errout_str()); + + check("bool f(void) {\n" + " if (a)\n" + " return 3;\n" + " return 4;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n" + "[test.cpp:4]: (style) Non-boolean value returned from function returning bool\n", errout_str()); + + check("bool f(void) {\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnNonBoolLambda() { + check("bool f(void) {\n" + " auto x = [](void) { return -1; };\n" + " return false;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(void) {\n" + " auto x = [](void) { return -1; };\n" + " return 2;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout_str()); + + check("bool f(void) {\n" + " auto x = [](void) -> int { return -1; };\n" + " return false;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(void) {\n" + " auto x = [](void) -> int { return -1; };\n" + " return 2;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout_str()); + } + + void returnNonBoolLogicalOp() { + check("bool f(int x) {\n" + " return x & 0x4;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(int x, int y) {\n" + " return x | y;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(int x) {\n" + " return (x & 0x2);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void returnNonBoolClass() { + check("class X {\n" + " public:\n" + " bool f() { return -1;}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Non-boolean value returned from function returning bool\n", errout_str()); + + check("bool f() {\n" + " struct X {\n" + " public:\n" + " int f() { return -1;}\n" + " };\n" + " return false;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f() {\n" + " class X {\n" + " public:\n" + " int f() { return -1;}\n" + " };\n" + " return false;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f() {\n" + " class X {\n" + " public:\n" + " bool f() { return -1;}\n" + " };\n" + " return -1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (style) Non-boolean value returned from function returning bool\n" + "[test.cpp:4]: (style) Non-boolean value returned from function returning bool\n", errout_str()); + } +}; + +REGISTER_TEST(TestBool) diff --git a/cppcheck-2.14.0/test/testboost.cpp b/cppcheck-2.14.0/test/testboost.cpp new file mode 100644 index 00000000..b3641b1a --- /dev/null +++ b/cppcheck-2.14.0/test/testboost.cpp @@ -0,0 +1,98 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "settings.h" + +class TestBoost : public TestFixture { +public: + TestBoost() : TestFixture("TestBoost") {} + +private: + const Settings settings = settingsBuilder().severity(Severity::style).severity(Severity::performance).build(); + + void run() override { + TEST_CASE(BoostForeachContainerModification); + } + +#define check(code) check_(code, __FILE__, __LINE__) + void check_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check.. + runChecks(tokenizer, this); + } + + void BoostForeachContainerModification() { + check("void f() {\n" + " vector data;\n" + " BOOST_FOREACH(int i, data) {\n" + " data.push_back(123);\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) BOOST_FOREACH caches the end() iterator. It's undefined behavior if you modify the container inside.\n", errout_str()); + + check("void f() {\n" + " set data;\n" + " BOOST_FOREACH(int i, data) {\n" + " data.insert(123);\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) BOOST_FOREACH caches the end() iterator. It's undefined behavior if you modify the container inside.\n", errout_str()); + + check("void f() {\n" + " set data;\n" + " BOOST_FOREACH(const int &i, data) {\n" + " data.erase(123);\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) BOOST_FOREACH caches the end() iterator. It's undefined behavior if you modify the container inside.\n", errout_str()); + + // Check single line usage + check("void f() {\n" + " set data;\n" + " BOOST_FOREACH(const int &i, data)\n" + " data.clear();\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) BOOST_FOREACH caches the end() iterator. It's undefined behavior if you modify the container inside.\n", errout_str()); + + // Container returned as result of a function -> Be quiet + check("void f() {\n" + " BOOST_FOREACH(const int &i, get_data())\n" + " data.insert(i);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Break after modification (#4788) + check("void f() {\n" + " vector data;\n" + " BOOST_FOREACH(int i, data) {\n" + " data.push_back(123);\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } +}; + +REGISTER_TEST(TestBoost) diff --git a/cppcheck-2.14.0/test/testbufferoverrun.cpp b/cppcheck-2.14.0/test/testbufferoverrun.cpp new file mode 100644 index 00000000..b3d0bfaa --- /dev/null +++ b/cppcheck-2.14.0/test/testbufferoverrun.cpp @@ -0,0 +1,5641 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "check.h" +#include "checkbufferoverrun.h" +#include "ctu.h" +#include "errortypes.h" +#include "helpers.h" +#include "standards.h" +#include "platform.h" +#include "settings.h" +#include "fixture.h" +#include "tokenize.h" + +#include +#include +#include + +class TestBufferOverrun : public TestFixture { +public: + TestBufferOverrun() : TestFixture("TestBufferOverrun") {} + +private: + /*const*/ Settings settings0 = settingsBuilder().library("std.cfg").severity(Severity::warning).severity(Severity::style).severity(Severity::portability).build(); + +#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) + void check_(const char* file, int line, const char code[], bool cpp = true) { + const Settings settings = settingsBuilder(settings0).certainty(Certainty::inconclusive).build(); + + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code, cpp), file, line); + + // Check for buffer overruns.. + runChecks(tokenizer, this); + } + + void check_(const char* file, int line, const char code[], const Settings &settings, bool cpp = true) { + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code, cpp), file, line); + + // Check for buffer overruns.. + runChecks(tokenizer, this); + } + +#define checkP(...) checkP_(__FILE__, __LINE__, __VA_ARGS__) + void checkP_(const char* file, int line, const char code[], const char* filename = "test.cpp") + { + const Settings settings = settingsBuilder(settings0).severity(Severity::performance) + .c(Standards::CLatest).cpp(Standards::CPPLatest).certainty(Certainty::inconclusive).build(); + + std::vector files(1, filename); + Tokenizer tokenizer(settings, *this); + PreprocessorHelper::preprocess(code, files, tokenizer, *this); + + // Tokenizer.. + ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); + + // Check for buffer overruns.. + runChecks(tokenizer, this); + } + + void run() override { + TEST_CASE(noerr1); + TEST_CASE(noerr2); + TEST_CASE(noerr3); + TEST_CASE(noerr4); + + TEST_CASE(sizeof3); + + TEST_CASE(array_index_1); + TEST_CASE(array_index_2); + TEST_CASE(array_index_3); + TEST_CASE(array_index_4); + TEST_CASE(array_index_6); + TEST_CASE(array_index_7); + TEST_CASE(array_index_11); + TEST_CASE(array_index_12); + TEST_CASE(array_index_13); + TEST_CASE(array_index_14); + TEST_CASE(array_index_15); + TEST_CASE(array_index_16); + TEST_CASE(array_index_17); + TEST_CASE(array_index_18); + TEST_CASE(array_index_19); + TEST_CASE(array_index_20); + TEST_CASE(array_index_21); + TEST_CASE(array_index_22); + TEST_CASE(array_index_23); + TEST_CASE(array_index_24); // ticket #1492 and #1539 + TEST_CASE(array_index_25); // ticket #1536 + TEST_CASE(array_index_26); + TEST_CASE(array_index_27); + TEST_CASE(array_index_28); // ticket #1418 + TEST_CASE(array_index_29); // ticket #1734 + TEST_CASE(array_index_30); // ticket #2086 - out of bounds when type is unknown + TEST_CASE(array_index_31); // ticket #2120 - out of bounds in subfunction when type is unknown + TEST_CASE(array_index_32); + TEST_CASE(array_index_33); // ticket #3044 + TEST_CASE(array_index_34); // ticket #3063 + TEST_CASE(array_index_35); // ticket #2889 + TEST_CASE(array_index_36); // ticket #2960 + TEST_CASE(array_index_37); + TEST_CASE(array_index_38); // ticket #3273 + TEST_CASE(array_index_39); + TEST_CASE(array_index_40); // loop variable calculation, taking address + TEST_CASE(array_index_41); // structs with the same name + TEST_CASE(array_index_42); + TEST_CASE(array_index_43); // struct with array + TEST_CASE(array_index_44); // #3979 + TEST_CASE(array_index_45); // #4207 - calling function with variable number of parameters (...) + TEST_CASE(array_index_46); // #4840 - two-statement for loop + TEST_CASE(array_index_47); // #5849 + TEST_CASE(array_index_48); // #9478 + TEST_CASE(array_index_49); // #8653 + TEST_CASE(array_index_50); + TEST_CASE(array_index_51); // #3763 + TEST_CASE(array_index_52); // #7682 + TEST_CASE(array_index_53); // #4750 + TEST_CASE(array_index_54); // #10268 + TEST_CASE(array_index_55); // #10254 + TEST_CASE(array_index_56); // #10284 + TEST_CASE(array_index_57); // #10023 + TEST_CASE(array_index_58); // #7524 + TEST_CASE(array_index_59); // #10413 + TEST_CASE(array_index_60); // #10617, #9824 + TEST_CASE(array_index_61); // #10621 + TEST_CASE(array_index_62); // #7684 + TEST_CASE(array_index_63); // #10979 + TEST_CASE(array_index_64); // #10878 + TEST_CASE(array_index_65); // #11066 + TEST_CASE(array_index_66); // #10740 + TEST_CASE(array_index_67); // #1596 + TEST_CASE(array_index_68); // #6655 + TEST_CASE(array_index_69); // #6370 + TEST_CASE(array_index_70); // #11355 + TEST_CASE(array_index_71); // #11461 + TEST_CASE(array_index_72); // #11784 + TEST_CASE(array_index_73); // #11530 + TEST_CASE(array_index_74); // #11088 + TEST_CASE(array_index_75); + 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_bounds); + TEST_CASE(array_index_calculation); + TEST_CASE(array_index_negative1); + TEST_CASE(array_index_negative2); // ticket #3063 + TEST_CASE(array_index_negative3); + TEST_CASE(array_index_negative4); + TEST_CASE(array_index_negative5); // #10526 + TEST_CASE(array_index_negative6); // #11349 + TEST_CASE(array_index_negative7); // #5685 + TEST_CASE(array_index_negative8); // #11651 + TEST_CASE(array_index_negative9); + TEST_CASE(array_index_negative10); + TEST_CASE(array_index_for_decr); + TEST_CASE(array_index_varnames); // FP: struct member #1576, FN: #1586 + TEST_CASE(array_index_for_continue); // for,continue + TEST_CASE(array_index_for); // FN: for,if + TEST_CASE(array_index_for_neq); // #2211: Using != in condition + TEST_CASE(array_index_for_question); // #2561: for, ?: + TEST_CASE(array_index_for_andand_oror); // FN: using && or || in the for loop condition + TEST_CASE(array_index_for_varid0); // #4228: No varid for counter variable + TEST_CASE(array_index_vla_for); // #3221: access VLA inside for + TEST_CASE(array_index_extern); // FP when using 'extern'. #1684 + TEST_CASE(array_index_cast); // FP after cast. #2841 + TEST_CASE(array_index_string_literal); + TEST_CASE(array_index_same_struct_and_var_name); // #4751 - not handled well when struct name and var name is same + TEST_CASE(array_index_valueflow); + TEST_CASE(array_index_valueflow_pointer); + TEST_CASE(array_index_function_parameter); + TEST_CASE(array_index_enum_array); // #8439 + TEST_CASE(array_index_container); // #9386 + TEST_CASE(array_index_two_for_loops); + TEST_CASE(array_index_new); // #7690 + + TEST_CASE(buffer_overrun_2_struct); + TEST_CASE(buffer_overrun_3); + TEST_CASE(buffer_overrun_4); + TEST_CASE(buffer_overrun_5); + TEST_CASE(buffer_overrun_6); + TEST_CASE(buffer_overrun_7); + TEST_CASE(buffer_overrun_8); + TEST_CASE(buffer_overrun_9); + TEST_CASE(buffer_overrun_10); + TEST_CASE(buffer_overrun_11); + TEST_CASE(buffer_overrun_15); // ticket #1787 + TEST_CASE(buffer_overrun_16); + TEST_CASE(buffer_overrun_18); // ticket #2576 - for, calculation with loop variable + TEST_CASE(buffer_overrun_19); // #2597 - class member with unknown type + TEST_CASE(buffer_overrun_21); + TEST_CASE(buffer_overrun_24); // index variable is changed in for-loop + TEST_CASE(buffer_overrun_26); // #4432 (segmentation fault) + TEST_CASE(buffer_overrun_27); // #4444 (segmentation fault) + TEST_CASE(buffer_overrun_29); // #7083: false positive: typedef and initialization with strings + TEST_CASE(buffer_overrun_30); // #6367 + TEST_CASE(buffer_overrun_31); + TEST_CASE(buffer_overrun_32); //#10244 + TEST_CASE(buffer_overrun_33); //#2019 + TEST_CASE(buffer_overrun_34); //#11035 + TEST_CASE(buffer_overrun_35); //#2304 + TEST_CASE(buffer_overrun_36); + TEST_CASE(buffer_overrun_errorpath); + TEST_CASE(buffer_overrun_bailoutIfSwitch); // ticket #2378 : bailoutIfSwitch + TEST_CASE(buffer_overrun_function_array_argument); + TEST_CASE(possible_buffer_overrun_1); // #3035 + TEST_CASE(buffer_overrun_readSizeFromCfg); + + TEST_CASE(valueflow_string); // using ValueFlow string values in checking + + // It is undefined behaviour to point out of bounds of an array + // the address beyond the last element is in bounds + // char a[10]; + // char *p1 = a + 10; // OK + // char *p2 = a + 11 // UB + TEST_CASE(pointer_out_of_bounds_1); + TEST_CASE(pointer_out_of_bounds_2); + TEST_CASE(pointer_out_of_bounds_3); + TEST_CASE(pointer_out_of_bounds_4); + TEST_CASE(pointer_out_of_bounds_sub); + + TEST_CASE(strcat1); + + TEST_CASE(varid1); + TEST_CASE(varid2); // ticket #4764 + + TEST_CASE(assign1); + + TEST_CASE(alloc_new); // Buffer allocated with new + TEST_CASE(alloc_malloc); // Buffer allocated with malloc + TEST_CASE(alloc_string); // statically allocated buffer + TEST_CASE(alloc_alloca); // Buffer allocated with alloca + + // TODO TEST_CASE(countSprintfLength); + TEST_CASE(minsize_argvalue); + TEST_CASE(minsize_sizeof); + TEST_CASE(minsize_strlen); + TEST_CASE(minsize_mul); + TEST_CASE(unknownType); + + TEST_CASE(terminateStrncpy1); + TEST_CASE(terminateStrncpy2); + TEST_CASE(terminateStrncpy3); + TEST_CASE(terminateStrncpy4); + TEST_CASE(terminateStrncpy5); // #9944 + TEST_CASE(recursive_long_time); + + TEST_CASE(crash1); // Ticket #1587 - crash + TEST_CASE(crash2); // Ticket #3034 - crash + TEST_CASE(crash3); // Ticket #5426 - crash + TEST_CASE(crash4); // Ticket #8679 - crash + TEST_CASE(crash5); // Ticket #8644 - crash + TEST_CASE(crash6); // Ticket #9024 - crash + TEST_CASE(crash7); // Ticket #9073 - crash + + TEST_CASE(insecureCmdLineArgs); + TEST_CASE(checkBufferAllocatedWithStrlen); + + TEST_CASE(scope); // handling different scopes + + TEST_CASE(getErrorMessages); + + // Access array and then check if the used index is within bounds + TEST_CASE(arrayIndexThenCheck); + TEST_CASE(arrayIndexEarlyReturn); // #6884 + + TEST_CASE(bufferNotZeroTerminated); + + TEST_CASE(negativeMemoryAllocationSizeError); // #389 + TEST_CASE(negativeArraySize); + + TEST_CASE(pointerAddition1); + + TEST_CASE(ctu_malloc); + TEST_CASE(ctu_array); + TEST_CASE(ctu_variable); + TEST_CASE(ctu_arithmetic); + + TEST_CASE(objectIndex); + + TEST_CASE(checkPipeParameterSize); // ticket #3521 + } + + + + 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] = {0};\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] = {0};\n" + " return str[16];\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'str[16]' accessed at index 16, which is out of bounds.\n", errout_str()); + + // test stack array + check("int f()\n" + "{\n" + " int x[ 3 ] = { 0, 1, 2 };\n" + " int y;\n" + " y = x[ 4 ];\n" + " return y;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'x[3]' accessed at index 4, which is out of bounds.\n", errout_str()); + + check("int f()\n" + "{\n" + " int x[ 3 ] = { 0, 1, 2 };\n" + " int y;\n" + " y = x[ 2 ];\n" + " return y;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int x[5] = {0};\n" + "int a = x[10];"); + ASSERT_EQUALS("[test.cpp:2]: (error) Array 'x[5]' accessed at index 10, which is out of bounds.\n", errout_str()); + + check("int x[5] = {0};\n" + "int a = (x)[10];"); + ASSERT_EQUALS("[test.cpp:2]: (error) Array 'x[5]' accessed at index 10, which is out of bounds.\n", errout_str()); + } + + + void array_index_2() { + check("void a(int i)\n" // valueflow + "{\n" + " char *str = new char[0x10];\n" + " str[i] = 0;\n" + "}\n" + "void b() { a(16); }"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'str[16]' accessed at index 16, which is out of bounds.\n", errout_str()); + } + + void array_index_4() { + check("char c = \"abc\"[4];"); + ASSERT_EQUALS("[test.cpp:1]: (error) Array '\"abc\"[4]' accessed at index 4, which is out of bounds.\n", errout_str()); + + check("p = &\"abc\"[4];"); + ASSERT_EQUALS("", errout_str()); + + check("char c = \"\\0abc\"[2];"); + ASSERT_EQUALS("", errout_str()); + + check("char c = L\"abc\"[4];"); + ASSERT_EQUALS("[test.cpp:1]: (error) Array 'L\"abc\"[4]' accessed at index 4, which is out of bounds.\n", errout_str()); + } + + void array_index_3() { + check("void f()\n" + "{\n" + " int val[50];\n" + " int i, sum=0;\n" + " for (i = 0; i < 100; i++)\n" + " sum += val[i];\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Array 'val[50]' accessed at index 99, which is out of bounds.\n", errout_str()); + + check("void f()\n" + "{\n" + " int val[50];\n" + " int i, sum=0;\n" + " for (i = 1; i < 100; i++)\n" + " sum += val[i];\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Array 'val[50]' accessed at index 99, which is out of bounds.\n", errout_str()); + + check("void f(int a)\n" + "{\n" + " int val[50];\n" + " int i, sum=0;\n" + " for (i = a; i < 100; i++)\n" + " sum += val[i];\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Array 'val[50]' accessed at index 99, which is out of bounds.\n", errout_str()); + + check("typedef struct g g2[3];\n" + "void foo(char *a)\n" + "{\n" + " for (int i = 0; i < 4; i++)\n" + " {\n" + " a[i]=0;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int argc)\n" + "{\n" + " char a[2];\n" + " for (int i = 4; i < argc; i++){}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int a[10]) {\n" + " for (int i=0;i<50;++i) {\n" + " a[i] = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[10]' accessed at index 49, which is out of bounds.\n", errout_str()); + } + + void array_index_6() { + check("struct ABC\n" + "{\n" + " char str[10];\n" + "};\n" + "\n" + "static void f()\n" + "{\n" + " struct ABC abc;\n" + " abc.str[10] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:9]: (error) Array 'abc.str[10]' accessed at index 10, which is out of bounds.\n", errout_str()); + + check("struct ABC\n" + "{\n" + " char str[10];\n" + "};\n" + "\n" + "static char f()\n" + "{\n" + " struct ABC abc;\n" + " return abc.str[10];\n" + "}"); + ASSERT_EQUALS("[test.cpp:9]: (error) Array 'abc.str[10]' accessed at index 10, which is out of bounds.\n", errout_str()); + + // This is not out of bounds because it is a variable length array + check("struct ABC\n" + "{\n" + " char str[1];\n" + "};\n" + "\n" + "static void f()\n" + "{\n" + " struct ABC* x = malloc(sizeof(struct ABC) + 10);\n" + " x->str[1] = 0;" + "}"); + ASSERT_EQUALS("", errout_str()); + + // This is not out of bounds because it is not a variable length array + check("struct ABC\n" + "{\n" + " char str[1];\n" + " int x;\n" + "};\n" + "\n" + "static void f()\n" + "{\n" + " struct ABC* x = malloc(sizeof(struct ABC) + 10);\n" + " x->str[1] = 0;" + "}"); + TODO_ASSERT_EQUALS("error", "", errout_str()); + + // This is not out of bounds because it is a variable length array + // and the index is within the memory allocated. + /** @todo this works by accident because we ignore any access to this array */ + check("struct ABC\n" + "{\n" + " char str[1];\n" + "};\n" + "\n" + "static void f()\n" + "{\n" + " struct ABC* x = malloc(sizeof(struct ABC) + 10);\n" + " x->str[10] = 0;" + "}"); + ASSERT_EQUALS("", errout_str()); + + // This is out of bounds because it is outside the memory allocated. + /** @todo this doesn't work because of a bug in sizeof(struct) */ + check("struct ABC\n" + "{\n" + " char str[1];\n" + "};\n" + "\n" + "static void f()\n" + "{\n" + " struct ABC* x = malloc(sizeof(struct ABC) + 10);\n" + " x->str[11] = 0;" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:9]: (error) Array 'str[1]' accessed at index 11, which is out of bounds.\n", "", errout_str()); + + // This is out of bounds if 'sizeof(ABC)' is 1 (No padding) + check("struct ABC\n" + "{\n" + " char str[1];\n" + "};\n" + "\n" + "static void f()\n" + "{\n" + " struct ABC* x = malloc(sizeof(ABC) + 10);\n" + " x->str[11] = 0;" + "}"); + TODO_ASSERT_EQUALS("error", "", errout_str()); + + // This is out of bounds because it is outside the memory allocated + /** @todo this doesn't work because of a bug in sizeof(struct) */ + check("struct ABC\n" + "{\n" + " char str[1];\n" + "};\n" + "\n" + "static void f()\n" + "{\n" + " struct ABC* x = malloc(sizeof(struct ABC));\n" + " x->str[1] = 0;" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:9]: (error) Array 'str[1]' accessed at index 1, which is out of bounds.\n", "", errout_str()); + + // This is out of bounds because it is outside the memory allocated + // But only if 'sizeof(ABC)' is 1 (No padding) + check("struct ABC\n" + "{\n" + " char str[1];\n" + "};\n" + "\n" + "static void f()\n" + "{\n" + " struct ABC* x = malloc(sizeof(ABC));\n" + " x->str[1] = 0;" + "}"); + TODO_ASSERT_EQUALS("error", "", errout_str()); + + // This is out of bounds because it is not a variable array + check("struct ABC\n" + "{\n" + " char str[1];\n" + "};\n" + "\n" + "static void f()\n" + "{\n" + " struct ABC x;\n" + " x.str[1] = 0;" + "}"); + ASSERT_EQUALS("[test.cpp:9]: (error) Array 'x.str[1]' accessed at index 1, which is out of bounds.\n", errout_str()); + + check("struct foo\n" + "{\n" + " char str[10];\n" + "};\n" + "\n" + "void x()\n" + "{\n" + " foo f;\n" + " for ( unsigned int i = 0; i < 64; ++i )\n" + " f.str[i] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:10]: (error) Array 'f.str[10]' accessed at index 63, which is out of bounds.\n", errout_str()); + + check("struct AB { char a[NUM]; char b[NUM]; }\n" + "void f(struct AB *ab) {\n" + " ab->a[0] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("union { char a[1]; int b; } ab;\n" + "void f() {\n" + " ab.a[2] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'ab.a[1]' accessed at index 2, which is out of bounds.\n", errout_str()); + } + + + void array_index_7() { + check("struct ABC\n" + "{\n" + " char str[10];\n" + "};\n" + "\n" + "static void f(struct ABC *abc)\n" + "{\n" + " abc->str[10] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (error) Array 'abc->str[10]' accessed at index 10, which is out of bounds.\n", errout_str()); + } + + void array_index_11() { + check("class ABC\n" + "{\n" + "public:\n" + " ABC();\n" + " char *str[10];\n" + " struct ABC *next();\n" + "};\n" + "\n" + "static void f(ABC *abc1)\n" + "{\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("[test.cpp:6]: (error) Array 'a[5]' accessed at index 5, which is out of bounds.\n", errout_str()); + + check("void f()\n" + "{\n" + " int a[6];\n" + " for (int i = 0; i < 7; i++)\n" + " {\n" + " a[i] = i;\n" + " i+=1;\n" + " }\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:6]: (error) Buffer overrun\n", "", errout_str()); + } + + void array_index_19() { + // "One Past the End" is legal, as long as pointer is not dereferenced. + check("void f()\n" + "{\n" + " char a[2];\n" + " char *end = &(a[2]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Getting more than one past the end is not legal + check("void f()\n" + "{\n" + " char a[2];\n" + " char *end = &(a[3]);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2]' accessed at index 3, which is out of bounds.\n", errout_str()); + } + + void array_index_20() { + check("void f()\n" + "{\n" + " char a[8];\n" + " int b[10];\n" + " for ( int i = 0; i < 9; i++ )\n" + " b[i] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_21() { + check("class A {\n" + " int indices[2];\n" + " void foo(int indices[3]);\n" + "};\n" + "\n" + "void A::foo(int indices[3]) {\n" + " for(int j=0; j<3; ++j) {\n" + " int b = indices[j];\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_22() { + check("int main() {\n" + " size_t indices[2];\n" + " int b = indices[2];\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'indices[2]' accessed at index 2, which is out of bounds.\n", errout_str()); + } + + void array_index_23() { + check("void foo()\n" + "{\n" + " char c[10];\n" + " c[1<<23]='a';\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'c[10]' accessed at index 8388608, which is out of bounds.\n", errout_str()); + } + + void array_index_24() { + // ticket #1492 and #1539 + const std::string charMaxPlusOne(settings0.platform.defaultSign == 'u' ? "256" : "128"); + check(("void f(char n) {\n" + " int a[n];\n" // n <= CHAR_MAX + " a[-1] = 0;\n" // negative index + " a[" + charMaxPlusOne + "] = 0;\n" // 128/256 > CHAR_MAX + "}\n").c_str()); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[" + charMaxPlusOne + "]' accessed at index -1, which is out of bounds.\n" + "[test.cpp:4]: (error) Array 'a[" + charMaxPlusOne + "]' accessed at index " + charMaxPlusOne + ", which is out of bounds.\n", errout_str()); + + check("void f(signed char n) {\n" + " int a[n];\n" // n <= SCHAR_MAX + " a[-1] = 0;\n" // negative index + " a[128] = 0;\n" // 128 > SCHAR_MAX + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[128]' accessed at index -1, which is out of bounds.\n" + "[test.cpp:4]: (error) Array 'a[128]' accessed at index 128, which is out of bounds.\n", errout_str()); + + check("void f(unsigned char n) {\n" + " int a[n];\n" // n <= UCHAR_MAX + " a[-1] = 0;\n" // negative index + " a[256] = 0;\n" // 256 > UCHAR_MAX + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[256]' accessed at index -1, which is out of bounds.\n" + "[test.cpp:4]: (error) Array 'a[256]' accessed at index 256, which is out of bounds.\n", errout_str()); + + check("void f(short n) {\n" + " int a[n];\n" // n <= SHRT_MAX + " a[-1] = 0;\n" // negative index + " a[32768] = 0;\n" // 32768 > SHRT_MAX + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[32768]' accessed at index -1, which is out of bounds.\n" + "[test.cpp:4]: (error) Array 'a[32768]' accessed at index 32768, which is out of bounds.\n", errout_str()); + + check("void f(unsigned short n) {\n" + " int a[n];\n" // n <= USHRT_MAX + " a[-1] = 0;\n" // negative index + " a[65536] = 0;\n" // 65536 > USHRT_MAX + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[65536]' accessed at index -1, which is out of bounds.\n" + "[test.cpp:4]: (error) Array 'a[65536]' accessed at index 65536, which is out of bounds.\n", errout_str()); + + check("void f(signed short n) {\n" + " int a[n];\n" // n <= SHRT_MAX + " a[-1] = 0;\n" // negative index + " a[32768] = 0;\n" // 32768 > SHRT_MAX + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[32768]' accessed at index -1, which is out of bounds.\n" + "[test.cpp:4]: (error) Array 'a[32768]' accessed at index 32768, which is out of bounds.\n", errout_str()); + + check("void f(int n) {\n" + " int a[n];\n" // n <= INT_MAX + " a[-1] = 0;\n" // negative index + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[2147483648]' accessed at index -1, which is out of bounds.\n", errout_str()); + + check("void f(unsigned int n) {\n" + " int a[n];\n" // n <= UINT_MAX + " a[-1] = 0;\n" // negative index + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[4294967296]' accessed at index -1, which is out of bounds.\n", errout_str()); + + check("void f(signed int n) {\n" + " int a[n];\n" // n <= INT_MAX + " a[-1] = 0;\n" // negative index + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[2147483648]' accessed at index -1, which is out of bounds.\n", errout_str()); + } + + void array_index_25() { // #1536 + check("void foo()\n" + "{\n" + " long l[SOME_SIZE];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_26() { + check("void f()\n" + "{\n" + " int a[3];\n" + " for (int i = 3; 0 <= i; i--)\n" + " a[i] = i;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[3]' accessed at index 3, which is out of bounds.\n", errout_str()); + + check("void f()\n" + "{\n" + " int a[4];\n" + " for (int i = 3; 0 <= i; i--)\n" + " a[i] = i;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_27() { + check("void f()\n" + "{\n" + " int a[10];\n" + " for (int i = 0; i < 10; i++)\n" + " a[i-1] = a[i];\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index -1, which is out of bounds.\n", errout_str()); + } + + void array_index_28() { + // ticket #1418 + check("void f()\n" + "{\n" + " int i[2];\n" + " int *ip = i + 1;\n" + " ip[-10] = 1;\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Array ip[-10] out of bounds.\n", "", errout_str()); + } + + void array_index_29() { + // ticket #1724 + check("void f()\n" + "{\n" + " int iBuf[10];" + " int *i = iBuf + 9;" + " int *ii = i + -5;" + " ii[10] = 0;" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:6]: (error) Array ii[10] out of bounds.\n", "", errout_str()); + } + + void array_index_30() { + // ticket #2086 - unknown type + // extracttests.start: typedef unsigned char UINT8; + check("void f() {\n" + " UINT8 x[2];\n" + " x[5] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'x[2]' accessed at index 5, which is out of bounds.\n", errout_str()); + } + + void array_index_31() { + // ticket #2120 - sub function, unknown type + check("struct s1 {\n" + " unknown_type_t delay[3];\n" + "};\n" + "\n" + "void x(unknown_type_t *delay, const int *net) {\n" + " delay[0] = 0;\n" + "}\n" + "\n" + "void y() {\n" + " struct s1 obj;\n" + " x(obj.delay, 123);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct s1 {\n" + " unknown_type_t delay[3];\n" + "};\n" + "\n" + "void x(unknown_type_t *delay, const int *net) {\n" + " delay[4] = 0;\n" + "}\n" + "\n" + "void y() {\n" + " struct s1 obj;\n" + " x(obj.delay, 123);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:6]: (error) Array 'obj.delay[3]' accessed at index 4, which is out of bounds.\n", + "", + errout_str()); + + check("struct s1 {\n" + " float a[0];\n" + "};\n" + "\n" + "void f() {\n" + " struct s1 *obj;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_32() { + check("class X\n" + "{\n" + " public:\n" + " X()\n" + " {\n" + " m_x[0] = 0;\n" + " m_x[1] = 0;\n" + " }\n" + " int m_x[1];\n" + "};"); + ASSERT_EQUALS("[test.cpp:7]: (error) Array 'm_x[1]' accessed at index 1, which is out of bounds.\n", errout_str()); + } + + void array_index_33() { + check("void foo(char bar[][4]) {\n" + " baz(bar[5]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_34() { // ticket #3063 + check("void foo() {\n" + " int y[2][2][2];\n" + " y[0][2][0] = 0;\n" + " y[0][0][2] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'y[2][2][2]' accessed at index y[0][2][0], which is out of bounds.\n" + "[test.cpp:4]: (error) Array 'y[2][2][2]' accessed at index y[0][0][2], which is out of bounds.\n", errout_str()); + + check("struct TEST\n" + "{\n" + " char a[10];\n" + " char b[10][5];\n" + "};\n" + "void foo()\n" + "{\n" + " TEST test;\n" + " test.a[10] = 3;\n" + " test.b[10][2] = 4;\n" + " test.b[0][19] = 4;\n" + " TEST *ptest;\n" + " ptest = &test;\n" + " ptest->a[10] = 3;\n" + " ptest->b[10][2] = 4;\n" + " ptest->b[0][19] = 4;\n" + "}"); + ASSERT_EQUALS("[test.cpp:9]: (error) Array 'test.a[10]' accessed at index 10, which is out of bounds.\n" + "[test.cpp:10]: (error) Array 'test.b[10][5]' accessed at index test.b[10][2], which is out of bounds.\n" + "[test.cpp:11]: (error) Array 'test.b[10][5]' accessed at index test.b[0][19], which is out of bounds.\n" + "[test.cpp:14]: (error) Array 'ptest->a[10]' accessed at index 10, which is out of bounds.\n" + "[test.cpp:15]: (error) Array 'ptest->b[10][5]' accessed at index ptest->b[10][2], which is out of bounds.\n" + "[test.cpp:16]: (error) Array 'ptest->b[10][5]' accessed at index ptest->b[0][19], which is out of bounds.\n", errout_str()); + + check("struct TEST\n" + "{\n" + " char a[10][5];\n" + "};\n" + "void foo()\n" + "{\n" + " TEST test;\n" + " test.a[9][5] = 4;\n" + " test.a[0][50] = 4;\n" + " TEST *ptest;\n" + " ptest = &test;\n" + " ptest->a[9][5] = 4;\n" + " ptest->a[0][50] = 4;\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (error) Array 'test.a[10][5]' accessed at index test.a[9][5], which is out of bounds.\n" + "[test.cpp:9]: (error) Array 'test.a[10][5]' accessed at index test.a[0][50], which is out of bounds.\n" + "[test.cpp:12]: (error) Array 'ptest->a[10][5]' accessed at index ptest->a[9][5], which is out of bounds.\n" + "[test.cpp:13]: (error) Array 'ptest->a[10][5]' accessed at index ptest->a[0][50], which is out of bounds.\n", errout_str()); + } + + void array_index_35() { // ticket #2889 + check("void f() {\n" + " struct Struct { unsigned m_Var[1]; } s;\n" + " s.m_Var[1] = 1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 's.m_Var[1]' accessed at index 1, which is out of bounds.\n", errout_str()); + + check("struct Struct { unsigned m_Var[1]; };\n" + "void f() {\n" + " struct Struct s;\n" + " s.m_Var[1] = 1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 's.m_Var[1]' accessed at index 1, which is out of bounds.\n", errout_str()); + + check("struct Struct { unsigned m_Var[1]; };\n" + "void f() {\n" + " struct Struct * s = calloc(40);\n" + " s->m_Var[1] = 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_36() { // ticket #2960 + check("class Fred {\n" + " Fred(const Fred &);\n" + "private:\n" + " bool m_b[2];\n" + "};\n" + "Fred::Fred(const Fred & rhs) {\n" + " m_b[2] = rhs.m_b[2];\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Array 'm_b[2]' accessed at index 2, which is out of bounds.\n" + "[test.cpp:7]: (error) Array 'rhs.m_b[2]' accessed at index 2, which is out of bounds.\n", errout_str()); + } + + void array_index_37() { + check("class Fred {\n" + " char x[X];\n" + " Fred() {\n" + " for (unsigned int i = 0; i < 15; i++)\n" + " i;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_38() { //ticket #3273 + check("void aFunction() {\n" + " double aDoubleArray[ 10 ];\n" + " unsigned int i; i = 0;\n" + " for( i = 0; i < 6; i++ )\n" + " {\n" + " unsigned int j; j = 0;\n" + " for( j = 0; j < 5; j++ )\n" + " {\n" + " unsigned int x; x = 0;\n" + " for( x = 0; x < 4; x++ )\n" + " {\n" + " }\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_39() { // ticket 3387 + check("void aFunction()\n" + "{\n" + " char a[10];\n" + " a[10] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", errout_str()); + } + + void array_index_40() { + check("void f() {\n" + " char a[10];\n" + " for (int i = 0; i < 10; ++i)\n" + " f2(&a[i + 1]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_41() { + // Don't generate false positives when structs have the same name + check("void a() {\n" + " struct Fred { char data[6]; } fred;\n" + " fred.data[4] = 0;\n" // <- no error + "}\n" + "\n" + "void b() {\n" + " struct Fred { char data[3]; } fred;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void a() {\n" + " struct Fred { char data[6]; } fred;\n" + " fred.data[4] = 0;\n" // <- no error + "}\n" + "\n" + "void b() {\n" + " struct Fred { char data[3]; } fred;\n" + " fred.data[4] = 0;\n" // <- error + "}"); + ASSERT_EQUALS("[test.cpp:8]: (error) Array 'fred.data[3]' accessed at index 4, which is out of bounds.\n", errout_str()); + } + + void array_index_42() { // ticket #3569 + + check("void f()\n" + "{\n" + " char *p; p = (char *)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" + " float *p; p = (float *)malloc(10 * sizeof(float));\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 = (char *)malloc(10);\n" + " p[0] = 0;\n" + " p[9] = 9;\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " char *p; p = new char[10];\n" + " p[0] = 0;\n" + " p[9] = 9;\n" + " delete [] p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " char *p(new char[10]);\n" + " p[0] = 0;\n" + " p[9] = 9;\n" + " delete [] p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " char *p = NULL;" + " try{\n" + " p = new char[10];\n" + " }\n" + " catch(...){\n" + " return;\n" + " }" + " p[0] = 0;\n" + " p[9] = 9;\n" + " delete [] p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_43() { // #3838 + + check("int f( )\n" + "{\n" + " struct {\n" + " int arr[ 3 ];\n" + " } var[ 1 ];\n" + " int y;\n" + " var[ 0 ].arr[ 0 ] = 0;\n" + " var[ 0 ].arr[ 1 ] = 1;\n" + " var[ 0 ].arr[ 2 ] = 2;\n" + " y = var[ 0 ].arr[ 3 ];\n" // <-- array access out of bounds + " return y;\n" + "}"); + ASSERT_EQUALS("[test.cpp:10]: (error) Array 'var[0].arr[3]' accessed at index 3, which is out of bounds.\n", errout_str()); + + check("int f( )\n" + "{\n" + " struct {\n" + " int arr[ 3 ];\n" + " } var[ 1 ];\n" + " int y=1;\n" + " var[ 0 ].arr[ 0 ] = 0;\n" + " var[ 0 ].arr[ 1 ] = 1;\n" + " var[ 0 ].arr[ 2 ] = 2;\n" + " y = var[ 0 ].arr[ 2 ];\n" + " return y;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + + check("int f( ){\n" + "struct Struct{\n" + " int arr[ 3 ];\n" + "};\n" + "int y;\n" + "Struct var;\n" + "var.arr[ 0 ] = 0;\n" + "var.arr[ 1 ] = 1;\n" + "var.arr[ 2 ] = 2;\n" + "var.arr[ 3 ] = 3;\n" // <-- array access out of bounds + "y=var.arr[ 3 ];\n" // <-- array access out of bounds + "return y;\n" + "}"); + ASSERT_EQUALS("[test.cpp:10]: (error) Array 'var.arr[3]' accessed at index 3, which is out of bounds.\n" + "[test.cpp:11]: (error) Array 'var.arr[3]' accessed at index 3, which is out of bounds.\n", errout_str()); + + + check("void f( ) {\n" + "struct S{\n" + " int var[ 3 ];\n" + "} ;\n" + "S var[2];\n" + "var[0].var[ 0 ] = 0;\n" + "var[0].var[ 1 ] = 1;\n" + "var[0].var[ 2 ] = 2;\n" + "var[0].var[ 4 ] = 4;\n" // <-- array access out of bounds + "}"); + ASSERT_EQUALS("[test.cpp:9]: (error) Array 'var[0].var[3]' accessed at index 4, which is out of bounds.\n", errout_str()); + + check("void f( ) {\n" + "struct S{\n" + " int var[ 3 ];\n" + "} ;\n" + "S var[2];\n" + "var[0].var[ 0 ] = 0;\n" + "var[0].var[ 1 ] = 1;\n" + "var[0].var[ 2 ] = 2;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // avoid FPs (modified examples taken from #3838) + check("struct AB { int a[10]; int b[10]; };\n" + "int main() {\n" + " struct AB ab;\n" + " int * p = &ab.a[10];\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct AB { int a[10]; int b[10]; };\n" + "int main() {\n" + " struct AB ab[1];\n" + " int * p = &ab[0].a[10];\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct AB { int a[10]; int b[10]; };\n" + "int main() {\n" + " struct AB ab[1];\n" + " int * p = &ab[10].a[0];\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'ab[1]' accessed at index 10, which is out of bounds.\n", errout_str()); + } + + void array_index_44() { // #3979 (false positive) + + check("void f()\n" + "{\n" + " char buf[2];\n" + " int i;\n" + " for (i = 2; --i >= 0; )\n" + " {\n" + " buf[i] = 1;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " double buf[2];\n" + " for (int i = 2; i--; )\n" + " {\n" + " buf[i] = 2.;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_45() { // #4207 - handling of function with variable number of parameters / unnamed arguments + // Variable number of arguments + check("void f(const char *format, ...) {\n" + " va_args args;\n" + " va_start(args, format);\n" + "}\n" + "void test() {\n" + " CHAR buffer[1024];\n" + " f(\"%s\", buffer);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Unnamed argument + check("void f(char *) {\n" + " dostuff();\n" + "}\n" + "void test() {\n" + " char buffer[1024];\n" + " f(buffer);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + // Two statement for-loop + void array_index_46() { + // #4840 + check("void bufferAccessOutOfBounds2() {\n" + " char *buffer[]={\"a\",\"b\",\"c\"};\n" + " for(int i=3; i--;) {\n" + " printf(\"files(%i): %s\", 3-i, buffer[3-i]);\n" + " }\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Array 'buffer[3]' accessed at index 3, which is out of bounds.\n", "", errout_str()); + + check("void f() {\n" + " int buffer[9];\n" + " long int i;\n" + " for(i=10; i--;) {\n" + " buffer[i] = i;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'buffer[9]' accessed at index 9, which is out of bounds.\n", errout_str()); + + // Correct access limits -> i from 9 to 0 + check("void f() {\n" + " int buffer[10];\n" + " for(unsigned long int i=10; i--;) {\n" + " buffer[i] = i;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_47() { + // #5849 + check("int s[4];\n" + "void f() {\n" + " for (int i = 2; i < 0; i++)\n" + " s[i] = 5;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_48() { + // #9478 + check("void test(void)\n" + "{\n" + " int array[4] = { 1,2,3,4 };\n" + " for (int i = 1; i <= 4; i++) {\n" + " printf(\" %i\", i);\n" + " array[i] = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Array 'array[4]' accessed at index 4, which is out of bounds.\n", errout_str()); + + check("void test(void)\n" + "{\n" + " int array[4] = { 1,2,3,4 };\n" + " for (int i = 1; i <= 4; i++) {\n" + " scanf(\"%i\", &i);\n" + " array[i] = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_49() { + // #8653 + check("void f() {\n" + " int i, k;\n" + " int arr[34] = {};\n" + " i = 1;\n" + " for (k = 0; k < 34 && i < 34; k++) {\n" + " i++;\n" + " }\n" + " arr[k];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_50() { + check("void f(const char * str) {\n" + " int len = strlen(str);\n" + " (void)str[len - 1];\n" + "}\n" + "void g() {\n" + " f(\"12345678\");\n" + " f(\"12345\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_51() { + check("void f(void){\n" + " int k=0, dd, d[1U] = {1};\n" + " for (dd=d[k]; k<10; dd=d[++k]){;}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'd[1]' accessed at index 1, which is out of bounds.\n", errout_str()); + } + + void array_index_52() { + check("char f(void)\n" + "{\n" + " char buf[10];\n" + " for(int i = 0, j= 11; i < j; ++i)\n" + " buf[i] = 0;\n" + " return buf[0];\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'buf[10]' accessed at index 10, which is out of bounds.\n", errout_str()); + } + + void array_index_53() { + check("double M[3][1];\n" + " \n" + "void matrix()\n" + "{\n" + " for (int i=0; i < 3; i++)\n" + " for (int j = 0; j < 3; j++)\n" + " M[i][j]=0.0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Array 'M[3][1]' accessed at index M[*][2], which is out of bounds.\n", errout_str()); + } + + void array_index_54() { + check("void f() {\n" + " g(0);\n" + "}\n" + "void g(unsigned int x) {\n" + " int b[4];\n" + " for (unsigned int i = 0; i < 4; i += 2) {\n" + " b[i] = 0;\n" + " b[i+1] = 0;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_55() { + check("void make(const char* s, size_t len) {\n" + " for (size_t i = 0; i < len; ++i)\n" + " s[i];\n" + "}\n" + "void make(const char* s) {\n" + " make(s, strlen(s));\n" + "}\n" + "void f() {\n" + " make(\"my-utf8-payload\");\n" + "}\n" + "void f2() {\n" + " make(\"false\");\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_56() { + check("struct s {\n" + " int array[1];\n" + " int index;\n" + "};\n" + "void f(struct s foo) {\n" + " foo.array[foo.index++] = 1;\n" + " if (foo.index == 1) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_57() { + check("void f(std::vector& v) {\n" + " int a[3] = { 1, 2, 3 };\n" + " int i = 0;\n" + " for (auto& x : v) {\n" + " int c = a[i++];\n" + " if (i == 3)\n" + " i = 0;\n" + " x = c;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::vector& v) {\n" + " int a[3] = { 1, 2, 3 };\n" + " int i = 0;\n" + " for (auto& x : v) {\n" + " int c = a[i++];\n" + " if (i == 4)\n" + " i = 0;\n" + " x = c;\n" + " }\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:6] -> [test.cpp:5]: (warning) Either the condition 'i==4' is redundant or the array 'a[3]' is accessed at index 3, which is out of bounds.\n", + errout_str()); + } + + void array_index_58() + { + check("int f(int x, int y) {\n" + " int a[3]= {0,1,2};\n" + " if(x<2)\n" + " y = a[x] + 1;\n" + " else\n" + " y = a[x];\n" + " return y;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:6]: (warning) Either the condition 'x<2' is redundant or the array 'a[3]' is accessed at index 3, which is out of bounds.\n", + errout_str()); + + check("void f() {\n" // #2199 + " char a[5];\n" + " for (int i = 0; i < 5; i++) {\n" + " i += 8;\n" + " a[i] = 0;\n" + " }\n" + "}\n" + "void g() {\n" + " char a[5];\n" + " for (int i = 0; i < 5; i++) {\n" + " a[i + 7] = 0;\n" + " }\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:5]: (error) Array 'a[5]' accessed at index 8, which is out of bounds.\n" + "[test.cpp:11]: (error) Array 'a[5]' accessed at index 11, which is out of bounds.\n", + errout_str()); + } + + void array_index_59() // #10413 + { + check("long f(long b) {\n" + " const long a[] = { 0, 1, };\n" + " const long c = std::size(a);\n" + " if (b < 0 || b >= c)\n" + " return 0;\n" + " return a[b];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int a, int b) {\n" + " const int S[2] = {};\n" + " if (a < 0) {}\n" + " else {\n" + " if (b < 0) {}\n" + " else if (S[b] > S[a]) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int a[2] = {};\n" + "void f(int i) {\n" + " g(i < 0 ? 0 : a[i]);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_60() + { + checkP("#define CKR(B) if (!(B)) { return -1; }\n" + "int f(int i) {\n" + " const int A[3] = {};\n" + " CKR(i < 3);\n" + " if (i > 0)\n" + " i = A[i];\n" + " return i;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + checkP("#define ASSERT(expression, action) if (expression) {action;}\n" + "int array[5];\n" + "void func (int index) {\n" + " ASSERT(index > 5, return);\n" + " array[index]++;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:5]: (warning) Either the condition 'index>5' is redundant or the array 'array[5]' is accessed at index 5, which is out of bounds.\n", + errout_str()); + } + + void array_index_61() + { + check("int f(int i) {\n" + " const int M[] = { 0, 1, 2, 3 };\n" + " if (i > 4)\n" + " return -1;\n" + " if (i < 0 || i == std::size(M))\n" + " return 0; \n" + " return M[i];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { enum E { e0 }; };\n" + "const S::E M[4] = { S::E:e0, S::E:e0, S::E:e0, S::E:e0 };\n" + "int f(int i) {\n" + " if (i > std::size(M) + 1)\n" + " return -1;\n" + " if (i < 0 || i >= std::size(M))\n" + " return 0;\n" + " return M[i]; \n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_62() + { + check("struct X {\n" + " static int GetSize() {return 11;}\n" + "};\n" + "char f() {\n" + " char buf[10]= {0};\n" + " for(int i = 0; i < X::GetSize(); ++i) \n" + " buf[i] = 0;\n" + " return buf[0];\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7]: (error) Array 'buf[10]' accessed at index 10, which is out of bounds.\n", + errout_str()); + } + + void array_index_63() + { + check("int b[4];\n" // #10979 + "void f(int i) {\n" + " if (i >= 0 && i < sizeof(b) / sizeof(*(b)))\n" + " b[i] = 0;\n" + " if (i >= 0 && i < sizeof(b) / sizeof((b)[0]))\n" + " b[i] = 0;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_64() // #10878 + { + check("struct Array {\n" + " int x[10];\n" + " int& accessArrayRef(int a) { return x[a]; }\n" + "};\n" + "void f() {\n" + " Array array = {};\n" + " array.accessArrayRef(10);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'x[10]' accessed at index 10, which is out of bounds.\n", errout_str()); + + check("int i = 10;\n" + "struct Array {\n" + " int x[10];\n" + " int& accessArrayRef(int a) { return x[a]; }\n" + "};\n" + "void f() {\n" + " Array array = {};\n" + " array.accessArrayRef(i);\n" + "}\n"); + TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Array 'x[10]' accessed at index 10, which is out of bounds.\n", "", errout_str()); + } + + void array_index_65() // #11066 + { + check("char P[] = { 2, 1 };\n" + "char f[2];\n" + "void e(char* c) {\n" + " register j;\n" + " for (j = 0; j < 2; j++)\n" + " c[j] = f[P[j] - 1];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_66() + { + check("void foo(int j) {\n" + " int offsets[256];\n" + " while (x) {\n" + " if (j >= 256) break;\n" + " offsets[++j] = -1;\n" + " }\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:5]: (warning) Either the condition 'j>=256' is redundant or the array 'offsets[256]' is accessed at index 256, which is out of bounds.\n", + errout_str()); + } + + void array_index_67() { + check("void func(int i) {\n" // #1596 + " int types[3];\n" + " int type_cnt = 0;\n" + " if (i == 0) {\n" + " types[type_cnt] = 0;\n" + " type_cnt++;\n" + " types[type_cnt] = 0;\n" + " type_cnt++;\n" + " types[type_cnt] = 0;\n" + " type_cnt++;\n" + " } else {\n" + " types[type_cnt] = 1;\n" + " type_cnt++;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_68() { // #6655 + check("int ia[10];\n" + "void f(int len) {\n" + " for (int i = 0; i < len; i++)\n" + " ia[i] = 0;\n" + "}\n" + "int g() {\n" + " f(20);\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'ia[10]' accessed at index 19, which is out of bounds.\n", errout_str()); + } + + // #6370 + void array_index_69() + { + check("void f() {\n" + " const int e[] = {0,10,20,30};\n" + " int a[4];\n" + " for(int i = 0; i < 4; ++i)\n" + " a[e[i]] = 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[4]' accessed at index 30, which is out of bounds.\n", errout_str()); + } + + // #11355 + void array_index_70() { + check("void f() {\n" + " static const char a[] = ((\"test\"));\n" + " printf(\"%c\", a[5]);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[5]' accessed at index 5, which is out of bounds.\n", errout_str()); + } + + // #11461 + void array_index_71() + { + check("unsigned int f(unsigned int Idx) {\n" + " if (Idx < 64)\n" + " return 0;\n" + " Idx -= 64;\n" + " int arr[64] = { 0 };\n" + " return arr[Idx];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + // #11784 + void array_index_72() + { + check("char f(int i) {\n" + " char d[4] = {};\n" + " for (; i < 3; i++) {}\n" + " for (i++; i > 0;) {\n" + " d[--i] = 1;\n" + " break;\n" + " }\n" + " return d[3];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + // #11530 + void array_index_73() + { + check("void f() {\n" + " int k = 0;\n" + " std::function a[1] = {};\n" + " a[k++](0);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + // #11088 + void array_index_74() + { + check("void foo(const char *keys) {\n" + " const char *prefix = \"') {}\n" + "}\n" + "void bar() {\n" + " foo(\"q\");\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + // #1644 + void array_index_75() + { + check("void f() {\n" + " char buf[10];\n" + " int i = 10;\n" + " while (i >= 3)\n" + " buf[i--] = 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'buf[10]' accessed at index 10, which is out of bounds.\n", errout_str()); + } + + void array_index_multidim() { + check("void f()\n" + "{\n" + " char a[2][2];\n" + " a[1][1] = 'a';\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " char a[2][2][2];\n" + " a[1][1][1] = 'a';\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " char a[2][2];\n" + " a[2][1] = 'a';\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2]' accessed at index a[2][1], which is out of bounds.\n", errout_str()); + + check("void f()\n" + "{\n" + " char a[2][2];\n" + " a[1][2] = 'a';\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2]' accessed at index a[1][2], which is out of bounds.\n", errout_str()); + + check("void f()\n" + "{\n" + " char a[2][2][2];\n" + " a[2][1][1] = 'a';\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2][2]' accessed at index a[2][1][1], which is out of bounds.\n", errout_str()); + + check("void f()\n" + "{\n" + " char a[2][2][2];\n" + " a[1][2][1] = 'a';\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2][2]' accessed at index a[1][2][1], which is out of bounds.\n", errout_str()); + + check("void f()\n" + "{\n" + " char a[2][2][2][2];\n" + " a[1][2][1][1] = 'a';\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2][2][2]' accessed at index a[1][2][1][1], which is out of bounds.\n", errout_str()); + + check("void f()\n" + "{\n" + " char a[2][2][2];\n" + " a[1][1][2] = 'a';\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2][2][2]' accessed at index a[1][1][2], which is out of bounds.\n", errout_str()); + + check("void f()\n" + "{\n" + " char a[10][10][10];\n" + " a[2*3][4*3][2] = 'a';\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[10][10][10]' accessed at index a[6][12][2], which is out of bounds.\n", errout_str()); + + check("void f() {\n" + " char a[10][10][10];\n" + " a[6][40][10] = 'a';\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[10][10][10]' accessed at index a[6][40][10], which is out of bounds.\n", errout_str()); + + check("void f() {\n" + " char a[1][1][1];\n" + " a[2][2][2] = 'a';\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[1][1][1]' accessed at index a[2][2][2], which is out of bounds.\n", errout_str()); + + check("void f() {\n" + " char a[6][6][6];\n" + " a[6][6][2] = 'a';\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[6][6][6]' accessed at index a[6][6][2], which is out of bounds.\n", errout_str()); + + check("void f() {\n" + " int a[2][2];\n" + " p = &a[2][0];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // unknown dim.. + check("void f()\n" + "{\n" + " int a[2][countof(x)] = {{1,2},{3,4}};\n" + " a[0][0] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void draw_quad(float z) {\n" + " int i;\n" + " float (*vertices)[2][4];\n" + " vertices[0][0][0] = z;\n" + " vertices[0][0][1] = z;\n" + " vertices[1][0][0] = z;\n" + " vertices[1][0][1] = z;\n" + " vertices[2][0][0] = z;\n" + " vertices[2][0][1] = z;\n" + " vertices[3][0][0] = z;\n" + " vertices[3][0][1] = z;\n" + " for (i = 0; i < 4; i++) {\n" + " vertices[i][0][2] = z;\n" + " vertices[i][0][3] = 1.0;\n" + " vertices[i][1][0] = 2.0;\n" + " vertices[i][1][1] = 3.0;\n" + " vertices[i][1][2] = 4.0;\n" + " vertices[i][1][3] = 5.0;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + { + check("int foo() {\n" + " const size_t A = 4;\n" + " const size_t B = 2;\n" + " extern int stuff[A][B];\n" + " return stuff[0][1];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // TODO: better handling of VLAs in symboldatabase. should be + // possible to use ValueFlow values. + check("int foo() {\n" + " const size_t A = 4;\n" + " const size_t B = 2;\n" + " extern int stuff[A][B];\n" + " return stuff[0][1];\n" + "}"); + TODO_ASSERT_EQUALS("error", "", errout_str()); + } + } + + void array_index_switch_in_for() { + check("void f()\n" + "{\n" + " int ar[10];\n" + " for (int i = 0; i < 10; ++i)\n" + " {\n" + " switch(i)\n" + " {\n" + " case 9:\n" + " ar[i] = 0;\n" + " break;\n" + " default:\n" + " ar[i] = ar[i+1];\n" + " break;\n" + " };\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " int ar[10];\n" + " for (int i = 0; i < 10; ++i)\n" + " {\n" + " switch(i)\n" + " {\n" + " case 8:\n" + " ar[i] = 0;\n" + " break;\n" + " default:\n" + " ar[i] = ar[i+1];\n" + " break;\n" + " };\n" + " }\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:12]: (error) Array index out of bounds.\n", "", errout_str()); + } + + void array_index_for_in_for() { + check("void f() {\n" + " int a[5];\n" + " for (int i = 0; i < 10; ++i) {\n" + " for (int j = i; j < 5; ++j) {\n" + " a[i] = 0;\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_bounds() { + // #10275 + check("int a[10];\n" + "void f(int i) {\n" + " if (i >= 0 && i < 10) {}\n" + " a[i] = 1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'i<10' is redundant or the array 'a[10]' is accessed at index 10, which is out of bounds.\n" + "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'i>=0' is redundant or the array 'a[10]' is accessed at index -1, which is out of bounds.\n", + errout_str()); + } + + void array_index_calculation() { + // #1193 - false negative: array out of bounds in loop when there is calculation + check("void f()\n" + "{\n" + " char data[8];\n" + " for (int i = 19; i < 36; ++i) {\n" + " data[i/2] = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[8]' accessed at index 17, which is out of bounds.\n", errout_str()); + + // #2199 - false negative: array out of bounds in loop when there is calculation + check("void f()\n" + "{\n" + " char arr[5];\n" + " for (int i = 0; i < 5; ++i) {\n" + " arr[i + 7] = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'arr[5]' accessed at index 11, which is out of bounds.\n", errout_str()); + } + + void array_index_negative1() { + // #948 - array index out of bound not detected 'a[-1] = 0' + check("void f()\n" + "{\n" + " char data[8];\n" + " data[-1] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'data[8]' accessed at index -1, which is out of bounds.\n", errout_str()); + + check("void f()\n" + "{\n" + " char data[8][4];\n" + " data[5][-1] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'data[8][4]' accessed at index data[*][-1], which is out of bounds.\n", errout_str()); + + // #1614 - negative index is ok for pointers + check("void foo(char *p)\n" + "{\n" + " p[-1] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " char s[] = \"abc\";\n" + " char *p = s + strlen(s);\n" + " if (p[-1]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // ticket #1850 + check("int f(const std::map > &m)\n" + "{\n" + " return m[0][-1];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_negative2() { // ticket #3063 + check("struct TEST { char a[10]; };\n" + "void foo() {\n" + " TEST test;\n" + " test.a[-1] = 3;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'test.a[10]' accessed at index -1, which is out of bounds.\n", errout_str()); + } + + void array_index_negative3() { + check("int f(int i) {\n" + " int p[2] = {0, 0};\n" + " if(i >= 2)\n" + " return 0;\n" + " else if(i == 0)\n" + " return 0;\n" + " return p[i - 1];\n" + "}\n" + "void g(int i) {\n" + " if( i == 0 )\n" + " return f(i);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_negative4() + { + check("void f(void) {\n" + " int buf[64]={};\n" + " int i;\n" + " for(i=0; i <16; ++i){}\n" + " for(; i < 24; ++i){ buf[i] = buf[i-16];}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_negative5() // #10526 + { + check("int i;\n" + "std::vector v;\n" + "bool f() {\n" + " if (i != 0) {\n" + " if (v.begin() != v.end()) {\n" + " if (i < 0)\n" + " return false;\n" + " const int a[4] = { 0, 1, 2, 3 };\n" + " return a[i - 1] > 0;\n" + " }\n" + " }\n" + " return false;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + // #11349 + void array_index_negative6() + { + check("void f(int i) {\n" + " int j = i;\n" + " const int a[5] = {};\n" + " const int k = j < 0 ? 0 : j;\n" + " (void)a[k];\n" + " if (i == -3) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + // #5685 + void array_index_negative7() + { + check("void f() {\n" + " int i = -9;\n" + " int a[5];\n" + " for (; i < 5; i++)\n" + " a[i] = 1;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[5]' accessed at index -9, which is out of bounds.\n", errout_str()); + } + + // #11651 + void array_index_negative8() + { + check("unsigned g(char*);\n" + "void f() {\n" + " char buf[10];\n" + " unsigned u = g(buf);\n" + " for (int i = u, j = sizeof(i); --i >= 0;)\n" + " char c = buf[i];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + // #8075 + void array_index_negative9() + { + check("int g(int i) {\n" + " if (i < 10)\n" + " return -1;\n" + " return 0;\n" + "}\n" + "void f() {\n" + " int a[] = { 1, 2, 3 };\n" + " printf(\"%d\\n\", a[g(4)]);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:8]: (error) Array 'a[3]' accessed at index -1, which is out of bounds.\n", errout_str()); + } + + // #11844 + void array_index_negative10() + { + check("struct S { int a[4]; };\n" + "void f(S* p, int k) {\n" + " int m = 3;\n" + " if (k)\n" + " m = 2;\n" + " for (int j = m + 1; j <= 4; j++)\n" + " p->a[j-1];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_for_decr() { + check("void f()\n" + "{\n" + " char data[8];\n" + " for (int i = 10; i > 0; --i) {\n" + " data[i] = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[8]' accessed at index 10, which is out of bounds.\n", errout_str()); + + check("void f()\n" + "{\n" + " char val[5];\n" + " for (unsigned int i = 3; i < 5; --i) {\n" + " val[i+1] = val[i];\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " char val[5];\n" + " for (int i = 3; i < 5; --i) {\n" + " val[i+1] = val[i];\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'val[5]' accessed at index -9994, which is out of bounds.\n" + "[test.cpp:5]: (error) Array 'val[5]' accessed at index -9995, which is out of bounds.\n", errout_str()); + } + + + void array_index_varnames() { + check("struct A {\n" + " char data[4];\n" + " struct B { char data[3]; };\n" + " B b;\n" + "};\n" + "\n" + "void f()\n" + "{\n" + " A a;\n" + " a.data[3] = 0;\n" + " a.b.data[2] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #1586 + 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[4] = 0;\n" + " a.b.data[3] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:10]: (error) Array 'a.data[4]' accessed at index 4, which is out of bounds.\n" + "[test.cpp:11]: (error) Array 'a.b.data[3]' accessed at index 3, which is out of bounds.\n", errout_str()); + } + + void array_index_for_andand_oror() { // #3907 - using && or || + // extracttests.start: volatile int y; + + check("void f() {\n" + " char data[2];\n" + " int x;\n" + " for (x = 0; x < 10 && y; x++) {\n" + " data[x] = 0;\n" + " }\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" + " }\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" + " }\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" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'data[2]' accessed at index 10, which is out of bounds.\n", errout_str()); + + check("int f() {\n" // #9126 + " int i, c;\n" + " char* words[100] = {0};\n" + " g(words);\n" + " for (i = c = 0; (i < N) && (c < 1); i++) {\n" + " if (words[i][0] == '|')\n" + " c++;\n" + " }\n" + " return c;\n" + "}", false); + ASSERT_EQUALS("", 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()); + + // extracttests.start: int maybe(); + check("void f() {\n" + " int a[2];\n" + " for (int i = 0; i < 2; ++i) {\n" + " if (maybe()) {\n" + " continue;\n" + " }\n" + " a[i - 1] = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Array 'a[2]' accessed at index -1, which is out of bounds.\n", errout_str()); + } + + void array_index_for() { + // Ticket #2370 - No false negative when there is no "break" + check("void f() {\n" + " int a[10];\n" + " for (int i = 0; i < 20; ++i) {\n" + " if (i==1) {\n" + " }\n" + " a[i] = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Array 'a[10]' accessed at index 19, which is out of bounds.\n", errout_str()); + + // Ticket #2385 - No false positive + check("void f() {\n" + " int a[10];\n" + " for (int i = 0; i < 20; ++i) {\n" + " if (i<10) {\n" + " } else {\n" + " a[i-10] = 0;\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Ticket #3893 - start value out of bounds + // extracttests.start: int maybe(); int dostuff(); + check("void f() {\n" + " int a[10];\n" + " for (int i = 10; maybe(); dostuff()) {\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()); + + // #7686 + check("char f() {\n" + " char buf[10];\n" + " const bool a = true, b = true;\n" + " for (int i = 0; i < (a && b ? 11 : 10); ++i)\n" + " buf[i] = 0;\n" + " return buf[0];\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'buf[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 'a[10]' accessed at index -1, which is out of bounds.\n", + errout_str()); + + check("void f() {\n" + " int a[10];\n" + " for (int i = 0; i != 10; ++i) {\n" + " i==0 ? 0 : a[i-1];\n" + " a[i-1] = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'a[10]' accessed at index -1, which is out of bounds.\n", errout_str()); + } + + void array_index_for_varid0() { // #4228: No varid for counter variable + check("void f() {\n" + " char a[10];\n" + " for (i=0; i<10; i++);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_vla_for() { + // #3221 - access VLA inside for + check("void f(int len) {\n" + " char a[len];\n" + " for (int i=0; i<7; ++i) {\n" + " a[0] = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_extern() { + // Ticket #1684. FP when using 'extern'. + check("extern char arr[15];\n" + "char arr[15] = \"abc\";"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_cast() { + // Ticket #2841. FP when using cast. + + // Different types => no error + check("void f1(char *buf) {\n" + " buf[4] = 0;\n" + "}\n" + "void f2() {\n" + " int x[2];\n" + " f1(x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Same type => error + check("void f1(const char buf[]) {\n" + " char c = buf[4];\n" + "}\n" + "void f2() {\n" + " char x[2];\n" + " f1(x);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (error) Array 'x[2]' accessed at index 4, which is out of bounds.\n", + "", + errout_str()); + } + + void array_index_string_literal() { + check("void f() {\n" + " const char *str = \"abc\";\n" + " bar(str[10]);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'str[4]' accessed at index 10, which is out of bounds.\n", errout_str()); + + check("void f()\n" + "{\n" + " const char *str = \"abc\";\n" + " bar(str[4]);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'str[4]' accessed at index 4, which is out of bounds.\n", errout_str()); + + check("void f()\n" + "{\n" + " const char *str = \"abc\";\n" + " bar(str[3]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " const char *str = \"a\tc\";\n" + " bar(str[4]);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'str[4]' accessed at index 4, which is out of bounds.\n", errout_str()); + + check("void f() {\n" // #6973 + " const char *name = \"\";\n" + " if ( name[0] == 'U' ? name[1] : 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int main(int argc, char **argv) {\n" + " char str[6] = \"\\0\";\n" + " unsigned short port = 65535;\n" + " snprintf(str, sizeof(str), \"%hu\", port);\n" + "}", settings0, false); + ASSERT_EQUALS("", errout_str()); + + check("int f(int x) {\n" // #11020 + " const char* p = (x == 0 ? \"12345\" : \"ABC\");\n" + " int s = 0;\n" + " for (int i = 0; p[i]; i++)\n" + " s += p[i];\n" + " return s;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_same_struct_and_var_name() { + // don't throw internal error + check("struct tt {\n" + " char name[21];\n" + "} ;\n" + "void doswitch(struct tt *x)\n" + "{\n" + " struct tt *tt=x;\n" + " tt->name;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // detect error + check("struct tt {\n" + " char name[21];\n" + "} ;\n" + "void doswitch(struct tt *x)\n" + "{\n" + " struct tt *tt=x;\n" + " tt->name[22] = 123;\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Array 'tt->name[21]' accessed at index 22, which is out of bounds.\n", errout_str()); + } + + void array_index_valueflow() { + check("void f(int i) {\n" + " char str[3];\n" + " str[i] = 0;\n" + " if (i==10) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition 'i==10' is redundant or the array 'str[3]' is accessed at index 10, which is out of bounds.\n", errout_str()); + + check("void f(int i) {\n" + " char str[3];\n" + " str[i] = 0;\n" + " switch (i) {\n" + " case 10: break;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (warning) Either the switch case 'case 10' is redundant or the array 'str[3]' is accessed at index 10, which is out of bounds.\n", errout_str()); + + check("void f() {\n" + " char str[3];\n" + " str[((unsigned char)3) - 1] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #5416 FP + " char *str[3];\n" + " do_something(&str[0][5]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class X { static const int x[100]; };\n" // #6070 + "const int X::x[100] = {0};"); + ASSERT_EQUALS("", errout_str()); + + check("namespace { class X { static const int x[100]; };\n" // #6232 + "const int X::x[100] = {0}; }"); + ASSERT_EQUALS("", errout_str()); + + check("class ActorSprite { static ImageSet * targetCursorImages[2][10]; };\n" + "ImageSet *ActorSprite::targetCursorImages[2][10];"); + ASSERT_EQUALS("", errout_str()); + + check("int f(const std::size_t s) {\n" // #10130 + " const char a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };\n" + " return (s > sizeof(a)) ? 11 : (int)a[s];\n" + "}\n" + "int g() {\n" + " return f(16);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (warning) Either the condition 's>sizeof(a)' is redundant or the array 'a[16]' is accessed at index 16, which is out of bounds.\n", + errout_str()); + + } + + void array_index_valueflow_pointer() { + check("void f() {\n" + " int a[10];\n" + " int *p = a;\n" + " p[20] = 0;\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Array 'a[10]' accessed at index 20, which is out of bounds.\n", "", errout_str()); + + { + // address of + check("void f() {\n" + " int a[10];\n" + " int *p = a;\n" + " p[10] = 0;\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Array 'a[10]' accessed at index 10, which is out of bounds.\n", "", errout_str()); + + check("void f() {\n" + " int a[10];\n" + " int *p = a;\n" + " dostuff(&p[10]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + check("void f() {\n" + " int a[X];\n" // unknown size + " int *p = a;\n" + " p[20] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int a[2];\n" + " char *p = (char *)a;\n" // cast + " p[4] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_function_parameter() { + check("void f(char a[10]) {\n" + " a[20] = 0;\n" // <- cppcheck warn here even though it's not a definite access out of bounds + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Array 'a[10]' accessed at index 20, which is out of bounds.\n", errout_str()); + + check("void f(char a[10]) {\n" // #6353 - reassign 'a' + " a += 4;\n" + " a[-1] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(char a[10]) {\n" + " a[0] = 0;\n" + " a[-1] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'a[10]' accessed at index -1, which is out of bounds.\n", errout_str()); + } + + void array_index_enum_array() { // #8439 + check("enum E : unsigned int { e1, e2 };\n" + "void f() {\n" + " E arrE[] = { e1, e2 };\n" + " arrE[sizeof(arrE)] = e1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'arrE[2]' accessed at index 8, which is out of bounds.\n", errout_str()); + } + + void array_index_container() { // #9386 + check("constexpr int blockLen = 10;\n" + "void foo(std::array& a) {\n" + " a[2] = 2;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_two_for_loops() { + check("bool b();\n" + "void f()\n" + "{\n" + " int val[50];\n" + " int i, sum=0;\n" + " for (i = 1; b() && i < 50; i++)\n" + " sum += val[i];\n" + " if (i < 50)\n" + " sum -= val[i];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool b();\n" + "void f()\n" + "{\n" + " int val[50];\n" + " int i, sum=0;\n" + " for (i = 1; b() && i < 50; i++)\n" + " sum += val[i];\n" + " for (; i < 50;) {\n" + " sum -= val[i];\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool b();\n" + "void f()\n" + "{\n" + " int val[50];\n" + " int i, sum=0;\n" + " for (i = 1; b() && i < 50; i++)\n" + " sum += val[i];\n" + " for (; i < 50; i++)\n" + " sum -= val[i];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_new() { // #7690 + check("void f() {\n" + " int* z = new int;\n" + " for (int n = 0; n < 8; ++n)\n" + " z[n] = 0;\n" + " delete[] z;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'z[1]' accessed at index 7, which is out of bounds.\n", errout_str()); + + check("void f() {\n" + " int* z = new int(1);\n" + " for (int n = 0; n < 8; ++n)\n" + " z[n] = 0;\n" + " delete[] z;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'z[1]' accessed at index 7, which is out of bounds.\n", errout_str()); + + check("void f() {\n" + " int* z = new int{};\n" + " for (int n = 0; n < 8; ++n)\n" + " z[n] = 0;\n" + " delete[] z;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'z[1]' accessed at index 7, which is out of bounds.\n", errout_str()); + + check("void f() {\n" + " int* z = new int[5];\n" + " for (int n = 0; n < 8; ++n)\n" + " z[n] = 0;\n" + " delete[] z;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'z[5]' accessed at index 7, which is out of bounds.\n", errout_str()); + + check("void g() {\n" + " int* z = new int[5]();\n" + " for (int n = 0; n < 8; ++n)\n" + " z[n] = 1;\n" + " delete[] z;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'z[5]' accessed at index 7, which is out of bounds.\n", errout_str()); + + check("void h() {\n" + " int** z = new int* [5];\n" + " for (int n = 0; n < 8; ++n)\n" + " z[n] = nullptr;\n" + " delete[] z;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'z[5]' accessed at index 7, which is out of bounds.\n", errout_str()); + + check("void h() {\n" + " int** z = new int* [5]();\n" + " for (int n = 0; n < 8; ++n)\n" + " z[n] = nullptr;\n" + " delete[] z;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'z[5]' accessed at index 7, which is out of bounds.\n", errout_str()); + + check("void h() {\n" + " int** z = new int* [5]{};\n" + " for (int n = 0; n < 8; ++n)\n" + " z[n] = nullptr;\n" + " delete[] z;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'z[5]' accessed at index 7, which is out of bounds.\n", errout_str()); + + check("void h() {\n" + " int** z = new int* [5]{ 0 };\n" + " for (int n = 0; n < 8; ++n)\n" + " z[n] = nullptr;\n" + " delete[] z;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'z[5]' accessed at index 7, which is out of bounds.\n", 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()); + + check("struct S { int b; } static e[1];\n" // #11052 + "int f() { return e[1].b; }\n"); + ASSERT_EQUALS("[test.cpp:2]: (error) Array 'e[1]' accessed at index 1, 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"); + 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" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: n\n", "", errout_str()); + } + + void buffer_overrun_7() { + // ticket #731 + check("void f()\n" + "{\n" + " char a[2];\n" + " strcpy(a, \"a\\0\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void buffer_overrun_8() { + // ticket #714 + check("void f()\n" + "{\n" + " char a[5];\n" + " for (int i = 0; i < 20; i = i + 100)\n" + " {\n" + " a[i] = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " char a[5];\n" + " for (int i = 0; i < 20; i = 100 + i)\n" + " {\n" + " a[i] = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void buffer_overrun_9() { + // ticket #738 + check("void f()\n" + "{\n" + " char a[5];\n" + " for (int i = 0; i < 20; )\n" + " {\n" + " a[i] = 0;\n" + " i += 100;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void buffer_overrun_10() { + // ticket #740 + check("void f()\n" + "{\n" + " char a[4];\n" + " for (int i = 0; i < 4; i++)\n" + " {\n" + " char b = a[i];\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void buffer_overrun_11() { + check("void f()\n" + "{\n" + " char a[4];\n" + " for (float i=0; i<10.0;i=i+0.1)\n" + " {\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " char a[4];\n" + " for (float i=0; i<10.0;i=0.1+i)\n" + " {\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void buffer_overrun_15() { // ticket #1787 + check("class A : public B {\n" + " char val[2];\n" + " void f(int i, int ii);\n" + "};\n" + "void A::f(int i, int ii)\n" + "{\n" + " strcpy(val, \"ab\") ;\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Buffer is accessed out of bounds: val\n", errout_str()); + } + + void buffer_overrun_16() { + // unknown types + check("void f() {\n" + " struct Foo foo[5];\n" + " memset(foo, 0, sizeof(foo));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // ticket #2093 + " gchar x[3];\n" + " strcpy(x, \"12\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("extern char a[10];\n" + "void f() {\n" + " char b[25] = {0};\n" + " std::memcpy(b, a, 10);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void buffer_overrun_18() { // ticket #2576 + check("class A {\n" + " void foo();\n" + " bool b[7];\n" + "};\n" + "\n" + "void A::foo() {\n" + " for (int i=0; i<6; i++) {\n" + " b[i] = b[i+1];\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class A {\n" + " void foo();\n" + " bool b[7];\n" + "};\n" + "\n" + "void A::foo() {\n" + " for (int i=0; i<7; i++) {\n" + " b[i] = b[i+1];\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (error) Array 'b[7]' accessed at index 7, which is out of bounds.\n", errout_str()); + } + + void buffer_overrun_19() { // #2597 - class member with unknown type + check("class A {\n" + "public:\n" + " u8 buf[10];\n" + " A();" + "};\n" + "\n" + "A::A() {\n" + " memset(buf, 0, 10);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void buffer_overrun_21() { + check("void foo()\n" + "{ { {\n" + " char dst[4];\n" + " const char *src = \"AAAAAAAAAAAAAAAAAAAAA\";\n" + " for (size_t i = 0; i <= 4; i++)\n" + " dst[i] = src[i];\n" + "} } }"); + ASSERT_EQUALS("[test.cpp:6]: (error) Array 'dst[4]' accessed at index 4, which is out of bounds.\n", errout_str()); + } + + void buffer_overrun_24() { // index variable is changed in for-loop + // ticket #4106 + check("void main() {\n" + " int array[] = {1,2};\n" + " int x = 0;\n" + " for( int i = 0; i<6; ) {\n" + " x += array[i];\n" + " i++; }\n" + "}"); + TODO_ASSERT_EQUALS("error", "", errout_str()); + + // ticket #4096 + check("void main() {\n" + " int array[] = {1,2};\n" + " int x = 0;\n" + " for( int i = 0; i<6; ) {\n" + " x += array[i++];\n" + " }\n" + "}"); + TODO_ASSERT_EQUALS("error", "", errout_str()); + } + + void buffer_overrun_26() { // ticket #4432 (segmentation fault) + check("extern int split();\n" + "void regress() {\n" + " char inbuf[1000];\n" + " char *f[10];\n" + " split(inbuf, f, 10, \"\t\t\");\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + } + + void buffer_overrun_27() { // ticket #4444 (segmentation fault) + check("void abc(struct foobar[5]);\n" + "void main() {\n" + "struct foobar x[5];\n" + "abc(x);\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + } + + // #7083: false positive: typedef and initialization with strings + void buffer_overrun_29() { + check("typedef char testChar[10];\n" + "int main(){\n" + " testChar tc1 = \"\";\n" + " tc1[5]='a';\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + + // #6367 + void buffer_overrun_30() { + check("struct S { int m[9]; };\n" + "int f(S * s) {\n" + " return s->m[sizeof(s->m)];\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 's->m[9]' accessed at index 36, which is out of bounds.\n", errout_str()); + } + + void buffer_overrun_31() { + check("void f(WhereInfo *pWInfo, int *aiCur) {\n" + " memcpy(aiCur, pWInfo->aiCurOnePass, sizeof(int)*2);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void buffer_overrun_32() { + // destination size is too small + check("void f(void) {\n" + " const char src[3] = \"abc\";\n" + " char dest[1] = \"a\";\n" + " (void)strxfrm(dest,src,1);\n" + " (void)strxfrm(dest,src,2);\n"// << + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: dest\n", errout_str()); + // destination size is too small + check("void f(void) {\n" + " const char src[3] = \"abc\";\n" + " char dest[2] = \"ab\";\n" + " (void)strxfrm(dest,src,1);\n" + " (void)strxfrm(dest,src,2);\n" + " (void)strxfrm(dest,src,3);\n" // << + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Buffer is accessed out of bounds: dest\n", errout_str()); + // source size is too small + check("void f(void) {\n" + " const char src[2] = \"ab\";\n" + " char dest[3] = \"abc\";\n" + " (void)strxfrm(dest,src,1);\n" + " (void)strxfrm(dest,src,2);\n" + " (void)strxfrm(dest,src,3);\n" // << + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Buffer is accessed out of bounds: src\n", errout_str()); + // source size is too small + check("void f(void) {\n" + " const char src[1] = \"a\";\n" + " char dest[3] = \"abc\";\n" + " (void)strxfrm(dest,src,1);\n" + " (void)strxfrm(dest,src,2);\n" // << + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: src\n", errout_str()); + } + + void buffer_overrun_33() { // #2019 + check("int f() {\n" + " int z[16];\n" + " for (int i=0; i<20; i++)\n" + " for (int j=0; j<20; j++)\n" + " z[i] = 0;\n" + " return z[0];\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'z[16]' accessed at index 19, which is out of bounds.\n", errout_str()); + } + + void buffer_overrun_34() { // #11035 + check("struct S {\n" + " std::vector v;\n" + " int a[15] = {};\n" + " int g() const { return v.size(); }\n" + " int f(int i) const {\n" + " if (i < 0 || i >= g())\n" + " return 0;\n" + " return a[i];\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void buffer_overrun_35() { // #2304 + check("void f() {\n" + " char* q = \"0123456789\";\n" + " char* p = (char*)malloc(sizeof(q) + 1);\n" + " strcpy(p, q);\n" + " free(p);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: p\n", errout_str()); + + check("void f() {\n" + " char* q = \"0123456789\";\n" + " char* p = (char*)malloc(1);\n" + " strcpy(p, q);\n" + " free(p);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: p\n", errout_str()); + + check("typedef struct { char buf[1]; } S;\n" + "S* f() {\n" + " S* s = NULL;\n" + " s = (S*)malloc(sizeof(S) + 10);\n" + " sprintf((char*)s->buf, \"abc\");\n" + " return s;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void buffer_overrun_36() { // #11708 + check("void f(double d) {\n" + " char a[80];\n" + " sprintf(a, \"%2.1f\", d);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void buffer_overrun_errorpath() { + setMultiline(); + const Settings settingsOld = settings0; + settings0.templateLocation = "{file}:{line}:note:{info}"; + + check("void f() {\n" + " char *p = malloc(10);\n" + " memset(p, 0, 20);\n" + "}"); + ASSERT_EQUALS("test.cpp:3:error:Buffer is accessed out of bounds: p\n" + "test.cpp:2:note:Assign p, buffer with size 10\n" + "test.cpp:3:note:Buffer overrun\n", errout_str()); + + settings0 = settingsOld; + } + + void buffer_overrun_bailoutIfSwitch() { + // No false positive + check("void f1(char *s) {\n" + " if (x) s[100] = 0;\n" + "}\n" + "\n" + "void f2() {\n" + " char a[10];\n" + " f1(a);" + "}"); + ASSERT_EQUALS("", errout_str()); + + // No false positive + check("void f1(char *s) {\n" + " if (x) return;\n" + " s[100] = 0;\n" + "}\n" + "\n" + "void f2() {\n" + " char a[10];\n" + " f1(a);" + "}"); + ASSERT_EQUALS("", errout_str()); + + // No false negative + check("void f1(char *s) {\n" + " if (x) { }\n" + " s[100] = 0;\n" + "}\n" + "\n" + "void f2() {\n" + " char a[10];\n" + " f1(a);" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (error) Array 'a[10]' accessed at index 100, which is out of bounds.\n", "", errout_str()); + } + + void buffer_overrun_function_array_argument() { + setMultiline(); + + check("void f(char a[10]);\n" + "void g() {\n" + " char a[2];\n" + " f(a);\n" + "}"); + ASSERT_EQUALS("test.cpp:4:warning:Buffer 'a' is too small, the function 'f' expects a bigger buffer in 1st argument\n" + "test.cpp:4:note:Function 'f' is called\n" + "test.cpp:1:note:Declaration of 1st function argument.\n" + "test.cpp:3:note:Passing buffer 'a' to function that is declared here\n" + "test.cpp:4:note:Buffer 'a' is too small, the function 'f' expects a bigger buffer in 1st argument\n", errout_str()); + + check("void f(float a[10][3]);\n" + "void g() {\n" + " float a[2][3];\n" + " f(a);\n" + "}"); + ASSERT_EQUALS("test.cpp:4:warning:Buffer 'a' is too small, the function 'f' expects a bigger buffer in 1st argument\n" + "test.cpp:4:note:Function 'f' is called\n" + "test.cpp:1:note:Declaration of 1st function argument.\n" + "test.cpp:3:note:Passing buffer 'a' to function that is declared here\n" + "test.cpp:4:note:Buffer 'a' is too small, the function 'f' expects a bigger buffer in 1st argument\n", errout_str()); + + check("void f(int a[20]);\n" + "void g() {\n" + " int a[2];\n" + " f(a);\n" + "}"); + ASSERT_EQUALS("test.cpp:4:warning:Buffer 'a' is too small, the function 'f' expects a bigger buffer in 1st argument\n" + "test.cpp:4:note:Function 'f' is called\n" + "test.cpp:1:note:Declaration of 1st function argument.\n" + "test.cpp:3:note:Passing buffer 'a' to function that is declared here\n" + "test.cpp:4:note:Buffer 'a' is too small, the function 'f' expects a bigger buffer in 1st argument\n", errout_str()); + + check("void f(int a[]) {\n" + " switch (2) {\n" + " case 1:\n" + " a[1] = 1;\n" + " }\n" + "}\n" + "int a[1];\n" + "f(a);\n" + ""); + ASSERT_EQUALS("", errout_str()); + + check("void CreateLeafTex(unsigned char buf[256][2048][4]);\n" + "void foo() {\n" + " unsigned char(* tree)[2048][4] = new unsigned char[256][2048][4];\n" + " CreateLeafTex(tree);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int a[10]) {\n" // #10069 + " int i = 0;\n" + " for (i = 0; i < 10; i++)\n" + " a[i] = i * 2;\n" + "}\n" + "void g() {\n" + " int b[5];\n" + " f(b);\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("test.cpp:8:warning:Buffer 'b' is too small, the function 'f' expects a bigger buffer in 1st argument\n" + "test.cpp:8:note:Function 'f' is called\n" + "test.cpp:1:note:Declaration of 1st function argument.\n" + "test.cpp:7:note:Passing buffer 'b' to function that is declared here\n" + "test.cpp:8:note:Buffer 'b' is too small, the function 'f' expects a bigger buffer in 1st argument\n", + errout_str()); + } + + void possible_buffer_overrun_1() { // #3035 + check("void foo() {\n" + " char * data = (char *)alloca(50);\n" + " char src[100];\n" + " memset(src, 'C', 99);\n" + " src[99] = '\\0';\n" + " strcat(data, src);\n" + "}"); + TODO_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 = (char *)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 = (char *)alloca(50);\n" + " strcat(data, src);\n" + "}"); + TODO_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 = (char *)alloca(100);\n" + " strcat(data, src);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " char * data = (char *)alloca(50);\n" + " char src[100];\n" + " memset(src, 'C', 99);\n" + " src[99] = '\\0';\n" + " strcpy(data, src);\n" + "}"); + TODO_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 = (char *)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 = (char *)alloca(50);\n" + " strcpy(data, src);\n" + "}"); + TODO_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 = (char *)alloca(100);\n" + " strcpy(data, src);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void buffer_overrun_readSizeFromCfg() { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " \n" + " false\n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + const Settings settings = settingsBuilder().libraryxml(xmldata, sizeof(xmldata)).build(); + + // Attempt to get size from Cfg files, no false positives if size is not specified + check("void f() {\n" + " u8 str[256];\n" + " mystrcpy(str, \"abcd\");\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " u8 str[2];\n" + " mystrcpy(str, \"abcd\");\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout_str()); + + // The same for structs, where the message comes from a different check + check("void f() {\n" + " struct { u8 str[256]; } ms;\n" + " mystrcpy(ms.str, \"abcd\");\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " struct { u8 str[2]; } ms;\n" + " mystrcpy(ms.str, \"abcd\");\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: ms.str\n", errout_str()); + } + + void valueflow_string() { // using ValueFlow string values in checking + check("char f() {\n" + " const char *x = s;\n" + " if (cond) x = \"abcde\";\n" + " return x[20];\n" // <- array index out of bounds when x is "abcde" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'x[6]' accessed at index 20, which is out of bounds.\n", errout_str()); + } + + void pointer_out_of_bounds_1() { + // extracttests.start: void dostuff(char *); + + check("void f() {\n" + " char a[10];\n" + " char *p = a + 100;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 'a+100' is out of bounds.\n", errout_str()); + + check("char *f() {\n" + " char a[10];\n" + " return a + 100;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 'a+100' is out of bounds.\n", errout_str()); + + check("void f(int i) {\n" + " char x[10];\n" + " if (i == 123) {}\n" + " dostuff(x+i);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (portability) Undefined behaviour, when 'i' is 123 the pointer arithmetic 'x+i' is out of bounds.\n", errout_str()); + + check("void f(int i) {\n" + " char x[10];\n" + " if (i == -1) {}\n" + " dostuff(x+i);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (portability) Undefined behaviour, when 'i' is -1 the pointer arithmetic 'x+i' is out of bounds.\n", errout_str()); + + check("void f() {\n" // #6350 - fp when there is cast of buffer + " wchar_t buf[64];\n" + " p = (unsigned char *) buf + sizeof (buf);\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + + check("int f() {\n" + " const char d[] = \"0123456789\";\n" + " char *cp = d + 3;\n" + " return cp - d;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void pointer_out_of_bounds_2() { + check("void f() {\n" + " char *p = malloc(10);\n" + " p += 100;\n" + " free(p);" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 'p+100' is out of bounds.\n", "", errout_str()); + + check("void f() {\n" + " char *p = malloc(10);\n" + " p += 10;\n" + " *p = 0;\n" + " free(p);" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) p is out of bounds.\n", "", errout_str()); + + check("void f() {\n" + " char *p = malloc(10);\n" + " p += 10;\n" + " p -= 10;\n" + " *p = 0;\n" + " free(p);" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " char *p = malloc(10);\n" + " p += 10;\n" + " p = p - 1;\n" + " *p = 0;\n" + " free(p);" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void pointer_out_of_bounds_3() { + check("struct S { int a[10]; };\n" + "void f(struct S *s) {\n" + " int *p = s->a + 100;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 's->a+100' is out of bounds.\n", errout_str()); + + check("template class Vector\n" + "{\n" + "public:\n" + " void test() const;\n" + " T* data();\n" + "};\n" + "template \n" + "void Vector::test() const\n" + "{\n" + " const T* PDat = data();\n" + " const T* P2 = PDat + 1;\n" + " const T* P1 = P2 - 1;\n" + "}\n" + "Vector> Foo;\n"); + ASSERT_EQUALS("", errout_str()); + } + + void pointer_out_of_bounds_4() { + check("const char* f() {\n" + " g(\"Hello\" + 6);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("const char* f() {\n" + " g(\"Hello\" + 7);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (portability) Undefined behaviour, pointer arithmetic '\"Hello\"+7' is out of bounds.\n", errout_str()); + + check("const char16_t* f() {\n" + " g(u\"Hello\" + 6);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("const char16_t* f() {\n" + " g(u\"Hello\" + 7);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (portability) Undefined behaviour, pointer arithmetic 'u\"Hello\"+7' is out of bounds.\n", errout_str()); + + check("void f() {\n" // #4647 + " int val = 5;\n" + " std::string hi = \"hi\" + val;\n" + " std::cout << hi << std::endl;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic '\"hi\"+val' is out of bounds.\n", errout_str()); + + check("void f(const char* s, int len) {\n" // #11026 + " const char* end = s + len;\n" + " printf(\"%s, %d\\n\", s, *end);\n" + "}\n" + "void g() {\n" + " f(\"a\", 1);\n" + " f(\"bbb\", 3);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int i, const char* a) {\n" // #11140 + " (void)a[i];\n" + "}\n" + "void g() {\n" + " for (int i = 0; \"01234\"[i]; ++i)\n" + " f(i, \"56789\");\n" + "}\n" + "void h() {\n" + " for (int i = 0; \"012\"[i]; ++i)\n" + " f(i, \"345\");\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + + void pointer_out_of_bounds_sub() { + // extracttests.start: void dostuff(char *); + + check("char *f() {\n" + " char x[10];\n" + " return x-1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 'x-1' is out of bounds.\n", errout_str()); + + check("void f(int i) {\n" + " char x[10];\n" + " if (i == 123) {}\n" + " dostuff(x-i);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (portability) Undefined behaviour, when 'i' is 123 the pointer arithmetic 'x-i' is out of bounds.\n", errout_str()); + + check("void f(int i) {\n" + " char x[10];\n" + " if (i == -20) {}\n" + " dostuff(x-i);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (portability) Undefined behaviour, when 'i' is -20 the pointer arithmetic 'x-i' is out of bounds.\n", "", errout_str()); + + check("void f(const char *x[10]) {\n" + " return x-4;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void strcat1() { + check("struct Foo { char a[4]; };\n" + "void f() {\n" + " struct Foo x = {0};\n" + " strcat(x.a, \"aa\");\n" + " strcat(x.a, \"aa\");\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds.\n", "", errout_str()); + } + + void varid1() { + check("void foo()\n" + "{\n" + " char str[10];\n" + " if (str[0])\n" + " {\n" + " char str[50];\n" + " str[30] = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varid2() { // #4764 + check("struct foo {\n" + " void bar() { return; }\n" + " type<> member[1];\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void assign1() { + check("char str[3] = {'a', 'b', 'c'};\n" + "\n" + "void foo()\n" + "{\n" + " str[3] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'str[3]' accessed at index 3, which is out of bounds.\n", errout_str()); + } + + void alloc_new() { + check("void foo()\n" + "{\n" + " char *s; s = new char[10];\n" + " s[10] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[10]' accessed at index 10, which is out of bounds.\n", errout_str()); + + // ticket #1670 - false negative when using return + check("char f()\n" + "{\n" + " int *s; s = new int[10];\n" + " return s[10];\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[10]' accessed at index 10, which is out of bounds.\n", errout_str()); + + check("struct Fred { char c[10]; };\n" + "char f()\n" + "{\n" + " Fred *f; f = new Fred;\n" + " return f->c[10];\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'f->c[10]' accessed at index 10, which is out of bounds.\n", errout_str()); + + check("static const size_t MAX_SIZE = UNAVAILABLE_TO_CPPCHECK;\n" + "struct Thing { char data[MAX_SIZE]; };\n" + "char f4(const Thing& t) { return !t.data[0]; }"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " char * buf; buf = new char[8];\n" + " buf[7] = 0;\n" + " delete [] buf;\n" + " buf = new char[9];\n" + " buf[8] = 0;\n" + " delete [] buf;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " char * buf; buf = new char[8];\n" + " buf[7] = 0;\n" + " delete [] buf;\n" + " buf = new char[9];\n" + " buf[9] = 0;\n" + " delete [] buf;\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Array 'buf[9]' accessed at index 9, which is out of bounds.\n", errout_str()); + + check("void foo()\n" + "{\n" + " enum E { Size = 10 };\n" + " char *s; s = new char[Size];\n" + " s[Size] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 's[10]' accessed at index 10, which is out of bounds.\n", errout_str()); + + check("void foo()\n" + "{\n" + " enum E { ZERO };\n" + " E *e; e = new E[10];\n" + " e[10] = ZERO;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'e[10]' accessed at index 10, which is out of bounds.\n", errout_str()); + } + + // data is allocated with malloc + void alloc_malloc() { + check("void foo()\n" + "{\n" + " char *s; s = (char *)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 = (int *)malloc(20 * sizeof(int));\n" + " tab4[20] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Array 'tab4[20]' accessed at index 20, which is out of bounds.\n", errout_str()); + + // ticket #1478 + check("void foo() {\n" + " char *p = (char *)malloc(10);\n" + " free(p);\n" + " p = (char *)malloc(10);\n" + " p[10] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Array 'p[10]' accessed at index 10, which is out of bounds.\n", errout_str()); + + // ticket #1134 + check("void f() {\n" + " int *x, i;\n" + " x = (int *)malloc(10 * sizeof(int));\n" + " x[10] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'x[10]' accessed at index 10, which is out of bounds.\n", errout_str()); + + check("void f() {\n" + " int *tab4; tab4 = malloc(20 * sizeof(int));\n" + " tab4[19] = 0;\n" + " free(tab4);\n" + " tab4 = malloc(21 * sizeof(int));\n" + " tab4[20] = 0;\n" + " free(tab4);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int *tab4 = malloc(20 * sizeof(int));\n" + " tab4[19] = 0;\n" + " tab4 = realloc(tab4,21 * sizeof(int));\n" + " tab4[20] = 0;\n" + " free(tab4);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " enum E { Size = 20 };\n" + " E *tab4 = (E *)malloc(Size * 4);\n" + " tab4[Size] = Size;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'tab4[20]' accessed at index 20, which is out of bounds.\n", errout_str()); + + check("void f() {\n" + " enum E { Size = 20 };\n" + " E *tab4 = (E *)malloc(4 * Size);\n" + " tab4[Size] = Size;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'tab4[20]' accessed at index 20, which is out of bounds.\n", errout_str()); + + check("void f() {\n" + " enum E { ZERO };\n" + " E *tab4 = (E *)malloc(20 * sizeof(E));\n" + " tab4[20] = ZERO;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'tab4[20]' accessed at index 20, which is out of bounds.\n", errout_str()); + + check("void f() {\n" // #8721 + " unsigned char **cache = malloc(32);\n" + " cache[i] = malloc(65536);\n" + " cache[i][0xFFFF] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int **a = malloc(2 * sizeof(int*));\n" + " for (int i = 0; i < 3; i++)\n" + " a[i] = NULL;\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" + " int **a = new int*[2];\n" + " for (int i = 0; i < 3; i++)\n" + " a[i] = NULL;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 'a[2]' accessed at index 2, which is out of bounds.\n", errout_str()); + } + + // statically allocated buffer + void alloc_string() { + check("void foo()\n" + "{\n" + " const char *s = \"123\";\n" + " s[10] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[4]' accessed at index 10, which is out of bounds.\n", errout_str()); + + check("void foo()\n" + "{\n" + " char *s; s = \"\";\n" + " s[10] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[1]' accessed at index 10, which is out of bounds.\n", errout_str()); + + check("void foo() {\n" + " const char *s = \"\";\n" + " s = y();\n" + " s[10] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" // #7718 + "{\n" + " std::string s = \"123\";\n" + " s.resize(100);\n" + " s[10] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + // data is allocated with alloca + void alloc_alloca() { + check("void foo()\n" + "{\n" + " char *s = (char *)alloca(10);\n" + " s[10] = 0;\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Array 's[10]' accessed at index 10, which is out of bounds.\n", "", errout_str()); + } + /* + void countSprintfLength() const { + std::list unknownParameter(1, nullptr); + + ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("Hello", unknownParameter)); + ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("s", unknownParameter)); + ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("i", unknownParameter)); + ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("%d", unknownParameter)); + ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("%1d", unknownParameter)); + ASSERT_EQUALS(3, CheckBufferOverrun::countSprintfLength("%2.2d", unknownParameter)); + ASSERT_EQUALS(1, CheckBufferOverrun::countSprintfLength("%s", unknownParameter)); + ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("f%s", unknownParameter)); + ASSERT_EQUALS(1, CheckBufferOverrun::countSprintfLength("%-s", unknownParameter)); + ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%-5s", unknownParameter)); + ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("\\\"", unknownParameter)); + ASSERT_EQUALS(7, CheckBufferOverrun::countSprintfLength("Hello \\0Text", unknownParameter)); + ASSERT_EQUALS(1, CheckBufferOverrun::countSprintfLength("\\0", unknownParameter)); + ASSERT_EQUALS(2, CheckBufferOverrun::countSprintfLength("%%", unknownParameter)); + ASSERT_EQUALS(3, CheckBufferOverrun::countSprintfLength("%d%d", unknownParameter)); + ASSERT_EQUALS(3, CheckBufferOverrun::countSprintfLength("\\\\a%s\\0a", unknownParameter)); + ASSERT_EQUALS(10, CheckBufferOverrun::countSprintfLength("\\\\\\\\Hello%d \\0Text\\\\\\\\", unknownParameter)); + ASSERT_EQUALS(4, CheckBufferOverrun::countSprintfLength("%%%%%d", unknownParameter)); + + Token strTok; + std::list stringAsParameter(1, &strTok); + strTok.str("\"\""); + ASSERT_EQUALS(4, CheckBufferOverrun::countSprintfLength("str%s", stringAsParameter)); + strTok.str("\"12345\""); + ASSERT_EQUALS(9, CheckBufferOverrun::countSprintfLength("str%s", stringAsParameter)); + ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%-4s", stringAsParameter)); + ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%-5s", stringAsParameter)); + ASSERT_EQUALS(7, CheckBufferOverrun::countSprintfLength("%-6s", stringAsParameter)); + ASSERT_EQUALS(5, CheckBufferOverrun::countSprintfLength("%.4s", stringAsParameter)); + ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%.5s", stringAsParameter)); + ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%.6s", stringAsParameter)); + ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%5.6s", stringAsParameter)); + ASSERT_EQUALS(7, CheckBufferOverrun::countSprintfLength("%6.6s", stringAsParameter)); + + Token numTok; + numTok.str("12345"); + std::list intAsParameter(1, &numTok); + ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%02ld", intAsParameter)); + ASSERT_EQUALS(9, CheckBufferOverrun::countSprintfLength("%08ld", intAsParameter)); + ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%.2d", intAsParameter)); + ASSERT_EQUALS(9, CheckBufferOverrun::countSprintfLength("%08.2d", intAsParameter)); + TODO_ASSERT_EQUALS(5, 2, CheckBufferOverrun::countSprintfLength("%x", intAsParameter)); + ASSERT_EQUALS(5, CheckBufferOverrun::countSprintfLength("%4x", intAsParameter)); + ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%5x", intAsParameter)); + ASSERT_EQUALS(5, CheckBufferOverrun::countSprintfLength("%.4x", intAsParameter)); + ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%.5x", intAsParameter)); + ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%1.5x", intAsParameter)); + ASSERT_EQUALS(6, CheckBufferOverrun::countSprintfLength("%5.1x", intAsParameter)); + + Token floatTok; + floatTok.str("1.12345f"); + std::list floatAsParameter(1, &floatTok); + TODO_ASSERT_EQUALS(5, 3, CheckBufferOverrun::countSprintfLength("%.2f", floatAsParameter)); + ASSERT_EQUALS(9, CheckBufferOverrun::countSprintfLength("%8.2f", floatAsParameter)); + TODO_ASSERT_EQUALS(5, 3, CheckBufferOverrun::countSprintfLength("%2.2f", floatAsParameter)); + + Token floatTok2; + floatTok2.str("100.12345f"); + std::list floatAsParameter2(1, &floatTok2); + TODO_ASSERT_EQUALS(7, 3, CheckBufferOverrun::countSprintfLength("%2.2f", floatAsParameter2)); + TODO_ASSERT_EQUALS(7, 3, CheckBufferOverrun::countSprintfLength("%.2f", floatAsParameter)); + TODO_ASSERT_EQUALS(7, 5, CheckBufferOverrun::countSprintfLength("%4.2f", floatAsParameter)); + + std::list multipleParams = { &strTok, nullptr, &numTok }; + ASSERT_EQUALS(15, CheckBufferOverrun::countSprintfLength("str%s%d%d", multipleParams)); + ASSERT_EQUALS(26, CheckBufferOverrun::countSprintfLength("str%-6s%08ld%08ld", multipleParams)); + } + */ + + // extracttests.disable + + void minsize_argvalue() { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " false\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + /*const*/ Settings settings = settingsBuilder().libraryxml(xmldata, sizeof(xmldata)).severity(Severity::warning).build(); + settings.platform.sizeof_wchar_t = 4; + + check("void f() {\n" + " char c[10];\n" + " mymemset(c, 0, 10);\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " char c[10];\n" + " mymemset(c, 0, 11);\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", errout_str()); + + check("struct S {\n" + " char a[5];\n" + "};\n" + "void f() {\n" + " S s;\n" + " mymemset(s.a, 0, 10);\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:6]: (error) Buffer is accessed out of bounds: s.a\n", errout_str()); + + check("void foo() {\n" + " char s[10];\n" + " mymemset(s, 0, '*');\n" + "}", settings); + TODO_ASSERT_EQUALS("[test.cpp:3]: (warning) The size argument is given as a char constant.\n" + "[test.cpp:3]: (error) Buffer is accessed out of bounds: s\n", "[test.cpp:3]: (error) Buffer is accessed out of bounds: s\n", errout_str()); + + // ticket #836 + check("void f(void) {\n" + " char a[10];\n" + " mymemset(a+5, 0, 10);\n" + "}", settings); + TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: a\n", "", errout_str()); + + // Ticket #909 + check("void f(void) {\n" + " char str[] = \"abcd\";\n" + " mymemset(str, 0, 6);\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout_str()); + + check("void f(void) {\n" + " char str[] = \"abcd\";\n" + " mymemset(str, 0, 5);\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + + check("void f(void) {\n" + " wchar_t str[] = L\"abcd\";\n" + " mymemset(str, 0, 21);\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout_str()); + + check("void f(void) {\n" + " wchar_t str[] = L\"abcd\";\n" + " mymemset(str, 0, 20);\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + + // ticket #1659 - overflowing variable when using memcpy + check("void f(void) {\n" + " char c;\n" + " mymemset(&c, 0, 4);\n" + "}", settings); + TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", "", errout_str()); + + // ticket #2121 - buffer access out of bounds when using uint32_t + check("void f(void) {\n" + " unknown_type_t buf[4];\n" + " mymemset(buf, 0, 100);\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + + // #3124 - multidimensional array + check("int main() {\n" + " char b[5][6];\n" + " mymemset(b, 0, 5 * 6);\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + + check("int main() {\n" + " char b[5][6];\n" + " mymemset(b, 0, 6 * 6);\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: b\n", errout_str()); + + check("int main() {\n" + " char b[5][6];\n" + " mymemset(b, 0, 31);\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: b\n", errout_str()); + + // #4968 - not standard function + check("void f() {\n" + " char str[3];\n" + " foo.mymemset(str, 0, 100);\n" + " foo::mymemset(str, 0, 100);\n" + " std::mymemset(str, 0, 100);\n" + "}", settings); + TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: str\n", "", errout_str()); + + // #5257 - check strings + check("void f() {\n" + " mymemset(\"abc\", 0, 20);\n" + "}", settings); + TODO_ASSERT_EQUALS("[test.cpp:2]: (error) Buffer is accessed out of bounds.\n", + "", + errout_str()); + + check("void f() {\n" + " mymemset(temp, \"abc\", 4);\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #6816 - fp when array has known string value + " char c[10] = \"c\";\n" + " mymemset(c, 0, 10);\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + } + + void minsize_sizeof() { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " false\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + const Settings settings = settingsBuilder().libraryxml(xmldata, sizeof(xmldata)).build(); + + check("void f() {\n" + " char c[7];\n" + " mystrncpy(c, \"hello\", 7);\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " char c[6];\n" + " mystrncpy(c,\"hello\",6);\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " char c[5];\n" + " mystrncpy(c,\"hello\",6);\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", errout_str()); + + check("void f() {\n" + " char c[6];\n" + " mystrncpy(c,\"hello!\",7);\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", errout_str()); + + check("void f(unsigned int addr) {\n" + " memset((void *)addr, 0, 1000);\n" + "}", settings0); + ASSERT_EQUALS("", errout_str()); + + check("struct AB { char a[10]; };\n" + "void foo(AB *ab) {\n" + " mystrncpy(x, ab->a, 100);\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + + check("void a(char *p) { mystrncpy(p,\"hello world!\",10); }\n" // #3168 + "void b() {\n" + " char buf[5];\n" + " a(buf);" + "}", settings); + TODO_ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:1]: (error) Buffer is accessed out of bounds: buf\n", + "", + errout_str()); + } + + void minsize_strlen() { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " false\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + const Settings settings = settingsBuilder().libraryxml(xmldata, sizeof(xmldata)).build(); + + // formatstr.. + check("void f() {\n" + " char str[3];\n" + " mysprintf(str, \"test\");\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout_str()); + + check("void f() {\n" + " char str[5];\n" + " mysprintf(str, \"%s\", \"abcde\");\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout_str()); + + check("int getnumber();\n" + "void f()\n" + "{\n" + " char str[5];\n" + " mysprintf(str, \"%d: %s\", getnumber(), \"abcde\");\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:5]: (error) Buffer is accessed out of bounds: str\n", errout_str()); + + check("void f() {\n" + " char str[5];\n" + " mysprintf(str, \"test%s\", \"\");\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " char *str = new char[5];\n" + " mysprintf(str, \"abcde\");\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout_str()); + + check("void f(int condition) {\n" + " char str[5];\n" + " mysprintf(str, \"test%s\", condition ? \"12\" : \"34\");\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + + check("void f(int condition) {\n" + " char str[5];\n" + " mysprintf(str, \"test%s\", condition ? \"12\" : \"345\");\n" + "}", settings); + TODO_ASSERT_EQUALS("error", "", errout_str()); + + check("struct Foo { char a[1]; };\n" + "void f() {\n" + " struct Foo x;\n" + " mysprintf(x.a, \"aa\");\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: x.a\n", errout_str()); + + // ticket #900 + check("void f() {\n" + " char *a = new char(30);\n" + " mysprintf(a, \"a\");\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: a\n", errout_str()); + + check("void f(char value) {\n" + " char *a = new char(value);\n" + " mysprintf(a, \"a\");\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: a\n", errout_str()); + + // This is out of bounds if 'sizeof(ABC)' is 1 (No padding) + check("struct Foo { char a[1]; };\n" + "void f() {\n" + " struct Foo *x = malloc(sizeof(Foo));\n" + " mysprintf(x->a, \"aa\");\n" + "}", settings); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error, inconclusive) Buffer is accessed out of bounds: x.a\n", "", errout_str()); + + check("struct Foo { char a[1]; };\n" + "void f() {\n" + " struct Foo *x = malloc(sizeof(Foo) + 10);\n" + " mysprintf(x->a, \"aa\");\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + + check("struct Foo { char a[1]; };\n" + "void f() {\n" + " struct Foo x;\n" + " mysprintf(x.a, \"aa\");\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds: x.a\n", errout_str()); + + check("struct Foo {\n" // #6668 - unknown size + " char a[LEN];\n" + " void f();\n" + "};" + "void Foo::f() {\n" + " mysprintf(a, \"abcd\");\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + } + + void minsize_mul() { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + const Settings settings = settingsBuilder().libraryxml(xmldata, sizeof(xmldata)).build(); + + check("void f() {\n" + " char c[5];\n" + " myfread(c, 1, 5, stdin);\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " char c[5];\n" + " myfread(c, 1, 6, stdin);\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: c\n", errout_str()); + } + + // extracttests.enable + + void unknownType() { + check("void f()\n" + "{\n" + " UnknownType *a = malloc(4);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + // extracttests.disable + void terminateStrncpy1() { + check("void foo ( char *bar ) {\n" + " char baz[100];\n" + " strncpy(baz, bar, 100);\n" + " strncpy(baz, bar, 100);\n" + " baz[99] = 0;\n" + " strncpy(baz, bar, 100);\n" + " baz[99] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo ( char *bar ) {\n" + " char baz[100];\n" + " strncpy(baz, bar, 100);\n" + " baz[99] = '\\0';\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo ( char *bar ) {\n" + " char baz[100];\n" + " strncpy(baz, bar, 100);\n" + " baz[x+1] = '\\0';\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Test with invalid code that there is no segfault + check("char baz[100];\n" + "strncpy(baz, \"var\", 100)"); + 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 strdup(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"); + ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) The buffer 'str' may not be null-terminated after the call to strncpy().\n", errout_str()); + } + + void terminateStrncpy4() { + check("void bar() {\n" + " char buf[4];\n" + " strncpy(buf, \"ab\", 4);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void bar() {\n" + " char buf[4];\n" + " strncpy(buf, \"abcde\", 4);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'buf' may not be null-terminated after the call to strncpy().\n", errout_str()); + } + + void terminateStrncpy5() { // #9944 + check("void f(const std::string& buf) {\n" + " char v[255];\n" + " if (buf.size() >= sizeof(v))\n" + " return;\n" + " strncpy(v, buf.c_str(), sizeof(v));\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const std::string& buf) {\n" + " char v[255];\n" + " if (buf.size() >= sizeof(v))\n" + " strncpy(v, buf.c_str(), sizeof(v));\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) The buffer 'v' may not be null-terminated after the call to strncpy().\n", errout_str()); + } + // extracttests.enable + + void recursive_long_time() { + // Just test that recursive check doesn't take long time + check("char *f2 ( char *b )\n" + "{\n" + " f2( b );\n" + " f2( b );\n" + " f2( b );\n" + " f2( b );\n" + " f2( b );\n" + " f2( b );\n" + " f2( b );\n" + " f2( b );\n" + " f2( b );\n" + " f2( b );\n" + " f2( b );\n" + " f2( b );\n" + " f2( b );\n" + " f2( b );\n" + " f2( b );\n" + "}\n" + "void f()\n" + "{\n" + " char a[10];\n" + " f2(a);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + + // Ticket #1587 - crash + void crash1() { + check("struct struct A\n" + "{\n" + " int alloclen;\n" + "};\n" + "\n" + "void foo()\n" + "{\n" + " struct A *str;\n" + " str = malloc(4);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void crash2() { + check("void a(char *p) {\n" + " f( { if(finally_arg); } );\n" + "}\n" + "\n" + "void b() {\n" + " char arr[64];\n" + " a(arr);\n" + "}"); + } + + void crash3() { + check("struct b { unknown v[0]; };\n" + "void d() { struct b *f; f = malloc(108); }"); + } + + void crash4() { // #8679 + check("__thread void *thread_local_var; " + "int main() { " + " thread_local_var = malloc(1337); " + " return 0; " + "}"); + + check("thread_local void *thread_local_var; " + "int main() { " + " thread_local_var = malloc(1337); " + " return 0; " + "}"); + } + + void crash5() { // 8644 - token has varId() but variable() is null + check("int a() {\n" + " void b(char **dst) {\n" + " *dst = malloc(50);\n" + " }\n" + "}"); + } + + void crash6() { + check("void start(char* name) {\n" + "char snapname[64] = { 0 };\n" + "strncpy(snapname, \"snapshot\", arrayLength(snapname));\n" + "}"); + } + + void crash7() { // 9073 - [ has no astParent + check("char x[10];\n" + "void f() { x[10]; }"); + } + + void insecureCmdLineArgs() { + check("int main(int argc, char *argv[])\n" + "{\n" + " if(argc>1)\n" + " {\n" + " char buf[2];\n" + " char *p = strdup(argv[1]);\n" + " strcpy(buf,p);\n" + " free(p);\n" + " }\n" + " return 0;\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:7]: (error) Buffer overrun possible for long command line arguments.\n", "", errout_str()); + + check("int main(int argc, char *argv[])\n" + "{\n" + " if(argc>1)\n" + " {\n" + " char buf[2] = {'\\0','\\0'};\n" + " char *p = strdup(argv[1]);\n" + " strcat(buf,p);\n" + " free(p);\n" + " }\n" + " return 0;\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:7]: (error) Buffer overrun possible for long command line arguments.\n", "", errout_str()); + + check("int main(const int argc, char* argv[])\n" + "{\n" + " char prog[10];\n" + " strcpy(prog, argv[0]);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout_str()); + + check("int main(int argc, const char* argv[])\n" + "{\n" + " char prog[10];\n" + " strcpy(prog, argv[0]);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout_str()); + + check("int main(const int argc, const char* argv[])\n" + "{\n" + " char prog[10];\n" + " strcpy(prog, argv[0]);\n" + "}"); + TODO_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" + "}"); + TODO_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" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout_str()); + + check("int main(int argc, const char *const *const argv, char **envp)\n" + "{\n" + " char prog[10];\n" + " strcpy(prog, argv[0]);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout_str()); + + check("int main(const int argc, const char *const *const argv, const char *const *const envp)\n" + "{\n" + " char prog[10];\n" + " strcpy(prog, argv[0]);\n" + "}"); + TODO_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" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout_str()); + + check("int main(const int argc, const char **argv, char **envp)\n" + "{\n" + " char prog[10] = {'\\0'};\n" + " strcat(prog, argv[0]);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout_str()); + + check("int main(int argc, const char **argv, char **envp)\n" + "{\n" + " char prog[10] = {'\\0'};\n" + " strcat(prog, argv[0]);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout_str()); + + check("int main(const int argc, char **argv, char **envp)\n" + "{\n" + " char prog[10] = {'\\0'};\n" + " strcat(prog, argv[0]);\n" + "}"); + TODO_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" + "}"); + TODO_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" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout_str()); + + check("int main(int argc, char **options)\n" + "{\n" + " char prog[10];\n" + " strcpy(prog, *options);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout_str()); + + check("int main(int argc, char **options)\n" + "{\n" + " char prog[10];\n" + " strcpy(prog+3, *options);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout_str()); + + check("int main(int argc, char **argv, char **envp)\n" + "{\n" + " char prog[10];\n" + " if (strlen(argv[0]) < 10)\n" + " strcpy(prog, argv[0]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int main(int argc, char **argv, char **envp)\n" + "{\n" + " char prog[10] = {'\\0'};\n" + " if (10 > strlen(argv[0]))\n" + " strcat(prog, argv[0]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int main(int argc, char **argv, char **envp)\n" + "{\n" + " char prog[10];\n" + " argv[0][0] = '\\0';\n" + " strcpy(prog, argv[0]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #5835 + check("int main(int argc, char* argv[]) {\n" + " char prog[10];\n" + " strcpy(prog, argv[0]);\n" + " strcpy(prog, argv[0]);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer overrun possible for long command line arguments.\n" + "[test.cpp:4]: (error) Buffer overrun possible for long command line arguments.\n", "", errout_str()); + + // #7964 + check("int main(int argc, char *argv[]) {\n" + " char *strcpy();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("int main(int argc, char *argv[]) {\n" + " char *strcat();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void checkBufferAllocatedWithStrlen() { + check("void f(char *a) {\n" + " char *b = new char[strlen(a)];\n" + " strcpy(b, a);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", "", errout_str()); + + check("void f(char *a) {\n" + " char *b = new char[strlen(a) + 1];\n" + " strcpy(b, a);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(char *a) {\n" + " char *b = new char[strlen(a)];\n" + " a[0] = '\\0';\n" + " strcpy(b, a);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(char *a) {\n" + " char *b = (char *)malloc(strlen(a));\n" + " b = realloc(b, 10000);\n" + " strcpy(b, a);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(char *a) {\n" + " char *b = (char *)malloc(strlen(a));\n" + " strcpy(b, a);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", "", errout_str()); + + check("void f(char *a) {\n" + " char *b = (char *)malloc(strlen(a));\n" + " {\n" + " strcpy(b, a);\n" + " }\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Buffer is accessed out of bounds.\n", "", errout_str()); + + check("void f(char *a) {\n" + " char *b = (char *)malloc(strlen(a) + 1);\n" + " strcpy(b, a);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(char *a, char *c) {\n" + " char *b = (char *)realloc(c, strlen(a));\n" + " strcpy(b, a);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", "", errout_str()); + + check("void f(char *a, char *c) {\n" + " char *b = (char *)realloc(c, strlen(a) + 1);\n" + " strcpy(b, a);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(char *a) {\n" + " char *b = (char *)malloc(strlen(a));\n" + " strcpy(b, a);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds.\n", "", errout_str()); + } + + void scope() { + check("class A {\n" + "private:\n" + " struct X { char buf[10]; };\n" + "};\n" + "\n" + "void f()\n" + "{\n" + " X x;\n" + " x.buf[10] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class A {\n" + "public:\n" + " struct X { char buf[10]; };\n" + "};\n" + "\n" + "void f()\n" + "{\n" + " A::X x;\n" + " x.buf[10] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:9]: (error) Array 'x.buf[10]' accessed at index 10, which is out of bounds.\n", errout_str()); + } + + void getErrorMessages() { + // Ticket #2292: segmentation fault when using --errorlist + const Check& c = getCheck(); + c.getErrorMessages(this, nullptr); + // we are not interested in the output - just consume it + ignore_errout(); + } + + void arrayIndexThenCheck() { + // extracttests.start: volatile int y; + + 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()); + + // extracttests.start: int elen; + check("void f(char* e, int y) {\n" + " if (e[y] == '/' && elen > y + 1 && e[y + 1] == '?') {\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // extracttests.start: int foo(int); int func(int); + check("void f(const int a[], unsigned i) {\n" + " if(a[i] < func(i) && i <= 42) {\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Array index 'i' is used before limits check.\n", errout_str()); + + check("void f(const int a[], unsigned i) {\n" + " if (i <= 42 && a[i] < func(i)) {\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const int a[], unsigned i) {\n" + " if (foo(a[i] + 3) < func(i) && i <= 42) {\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Array index 'i' is used before limits check.\n", errout_str()); + + check("void f(int i) {\n" // sizeof + " sizeof(a)/sizeof(a[i]) && i < 10;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // extracttests.start: extern int buf[]; + check("void f(int i) {\n" // ?: + " if ((i < 10 ? buf[i] : 1) && (i < 5 ? buf[i] : 5)){}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void arrayIndexEarlyReturn() { // #6884 + check("extern const char *Names[2];\n" + "const char* getName(int value) {\n" + " if ((value < 0) || (value > 1))\n" + " return \"???\";\n" + " const char* name = Names[value]; \n" + " switch (value) {\n" + " case 2:\n" + " break; \n" + " }\n" + " return name;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void bufferNotZeroTerminated() { + check("void f() {\n" + " char c[6];\n" + " strncpy(c,\"hello!\",6);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'c' may not be null-terminated after the call to strncpy().\n", errout_str()); + + check("void f() {\n" + " char c[6];\n" + " memcpy(c,\"hello!\",6);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'c' may not be null-terminated after the call to memcpy().\n", "", errout_str()); + + check("void f() {\n" + " char c[6];\n" + " memmove(c,\"hello!\",6);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'c' may not be null-terminated after the call to memmove().\n", "", errout_str()); + } + + void negativeMemoryAllocationSizeError() { // #389 + check("void f()\n" + "{\n" + " int *a;\n" + " a = new int[-1];\n" + " delete [] a;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Memory allocation size is negative.\n", errout_str()); + + check("void f()\n" + "{\n" + " int *a;\n" + " a = (int *)malloc( -10 );\n" + " free(a);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Memory allocation size is negative.\n", "", errout_str()); + + check("void f()\n" + "{\n" + " int *a;\n" + " a = (int *)malloc( -10);\n" + " free(a);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Memory allocation size is negative.\n", "", errout_str()); + + check("void f()\n" + "{\n" + " int *a;\n" + " a = (int *)alloca( -10 );\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Memory allocation size is negative.\n", "", errout_str()); + + check("int* f(int n) {\n" // #11145 + " int d = -1;\n" + " for (int i = 0; i < n; ++i)\n" + " d = std::max(i, d);\n" + " int* p = new int[d];\n" + " return p;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (warning, inconclusive) Memory allocation size is negative.\n", errout_str()); + } + + void negativeArraySize() { + check("void f(int sz) {\n" // #1760 - VLA + " int a[sz];\n" + "}\n" + "void x() { f(-100); }"); + ASSERT_EQUALS("[test.cpp:2]: (error) Declaration of array 'a' with negative size is undefined behaviour\n", errout_str()); + + // don't warn for constant sizes -> this is a compiler error so this is used for static assertions for instance + check("int x, y;\n" + "int a[-1];\n" + "int b[x?1:-1];\n" + "int c[x?y:-1];"); + ASSERT_EQUALS("", errout_str()); + } + + void pointerAddition1() { + check("void f() {\n" + " char arr[10];\n" + " char *p = arr + 20;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (portability) Undefined behaviour, pointer arithmetic 'arr+20' is out of bounds.\n", errout_str()); + + check("char(*g())[1];\n" // #7950 + "void f() {\n" + " int a[2];\n" + " int* b = a + sizeof(*g());\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + +#define ctu(code) ctu_(code, __FILE__, __LINE__) + void ctu_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings0, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + CTU::FileInfo *ctu = CTU::getFileInfo(tokenizer); + + // Check code.. + std::list fileInfo; + Check& c = getCheck(); + fileInfo.push_back(c.getFileInfo(tokenizer, settings0)); + c.analyseWholeProgram(ctu, fileInfo, settings0, *this); + while (!fileInfo.empty()) { + delete fileInfo.back(); + fileInfo.pop_back(); + } + delete ctu; + } + + void ctu_malloc() { + ctu("void dostuff(char *p) {\n" + " p[-3] = 0;\n" + "}\n" + "\n" + "int main() {\n" + " char *s = malloc(4);\n" + " dostuff(s);\n" + "}"); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:7] -> [test.cpp:2]: (error) Array index out of bounds; buffer 'p' is accessed at offset -3.\n", errout_str()); + + ctu("void dostuff(char *p) {\n" + " p[4] = 0;\n" + "}\n" + "\n" + "int main() {\n" + " char *s = malloc(4);\n" + " dostuff(s);\n" + "}"); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:7] -> [test.cpp:2]: (error) Array index out of bounds; 'p' buffer size is 4 and it is accessed at offset 4.\n", errout_str()); + + ctu("void f(int* p) {\n" // #10415 + " int b[1];\n" + " b[0] = p[5];\n" + " std::cout << b[0];\n" + "}\n" + "void g() {\n" + " int* a = new int[1];\n" + " a[0] = 5;\n" + " f(a);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9] -> [test.cpp:3]: (error) Array index out of bounds; 'p' buffer size is 4 and it is accessed at offset 20.\n", errout_str()); + } + + void ctu_array() { + ctu("void dostuff(char *p) {\n" + " p[10] = 0;\n" + "}\n" + "int main() {\n" + " char str[4];\n" + " dostuff(str);\n" + "}"); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (error) Array index out of bounds; 'p' buffer size is 4 and it is accessed at offset 10.\n", errout_str()); + + ctu("static void memclr( char *data )\n" + "{\n" + " data[10] = 0;\n" + "}\n" + "\n" + "static void f()\n" + "{\n" + " char str[5];\n" + " memclr( str );\n" + "}"); + ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:3]: (error) Array index out of bounds; 'data' buffer size is 5 and it is accessed at offset 10.\n", errout_str()); + + ctu("static void memclr( int i, char *data )\n" + "{\n" + " data[10] = 0;\n" + "}\n" + "\n" + "static void f()\n" + "{\n" + " char str[5];\n" + " memclr( 0, str );\n" + "}"); + ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:3]: (error) Array index out of bounds; 'data' buffer size is 5 and it is accessed at offset 10.\n", errout_str()); + + ctu("static void memclr( int i, char *data )\n" + "{\n" + " data[i] = 0;\n" + "}\n" + "\n" + "static void f()\n" + "{\n" + " char str[5];\n" + " memclr( 10, str );\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:3]: (possible error) Array index out of bounds.\n", + "", errout_str()); + + // This is not an error + ctu("static void memclr( char *data, int size )\n" + "{\n" + " if( size > 10 )" + " data[10] = 0;\n" + "}\n" + "\n" + "static void f()\n" + "{\n" + " char str[5];\n" + " memclr( str, 5 );\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #2097 + ctu("void foo(int *p)\n" + "{\n" + " --p;\n" + " p[2] = 0;\n" + "}\n" + "\n" + "void bar()\n" + "{\n" + " int p[3];\n" + " foo(p+1);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9112 + ctu("static void get_mac_address(const u8 *strbuf)\n" + "{\n" + " (strbuf[2]);\n" + "}\n" + "\n" + "static void program_mac_address(u32 mem_base)\n" + "{\n" + " u8 macstrbuf[17] = { 0 };\n" + " get_mac_address(macstrbuf);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9788 + ctu("void f1(char *s) { s[2] = 'B'; }\n" + "void f2(char s[]) { s[2] = 'B'; }\n" + "void g() {\n" + " char str[2];\n" + " f1(str);\n" + " f2(str);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:1]: (error) Array index out of bounds; 's' buffer size is 2 and it is accessed at offset 2.\n" + "[test.cpp:6] -> [test.cpp:2]: (error) Array index out of bounds; 's' buffer size is 2 and it is accessed at offset 2.\n", + errout_str()); + + // #5140 + ctu("void g(const char* argv[]) { std::cout << \"argv: \" << argv[4] << std::endl; }\n" + "void f() {\n" + " const char* argv[] = { \"test\" };\n" + " g(argv);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:1]: (error) Array index out of bounds; 'argv' buffer size is 1 and it is accessed at offset 4.\n", + errout_str()); + + ctu("void g(const char* argv[]) { std::cout << \"argv: \" << argv[5] << std::endl; }\n" + "void f() {\n" + " const char* argv[1] = { \"test\" };\n" + " g(argv);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:1]: (error) Array index out of bounds; 'argv' buffer size is 1 and it is accessed at offset 5.\n", + errout_str()); + + ctu("void g(int *b) { b[0] = 0; }\n" + "void f() {\n" + " GLint a[1];\n" + " g(a);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + ctu("const int a[1] = { 1 };\n" // #11042 + "void g(const int* d) {\n" + " (void)d[2];\n" + "}\n" + "void f() {\n" + " g(a);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:3]: (error) Array index out of bounds; 'd' buffer size is 4 and it is accessed at offset 8.\n", + errout_str()); + } + + void ctu_variable() { + ctu("void dostuff(int *p) {\n" + " p[10] = 0;\n" + "}\n" + "int main() {\n" + " int x = 4;\n" + " dostuff(&x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (error) Array index out of bounds; 'p' buffer size is 4 and it is accessed at offset 40.\n", errout_str()); + } + + void ctu_arithmetic() { + ctu("void dostuff(int *p) { x = p + 10; }\n" + "int main() {\n" + " int x[3];\n" + " dostuff(x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:1]: (error) Pointer arithmetic overflow; 'p' buffer size is 12\n", errout_str()); + + ctu("void f(const char *p) {\n" // #11361 + " const char* c = p + 1;\n" + "}\n" + "void g() {\n" + " const char s[N] = \"ab\";\n" + " f(s);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void objectIndex() { + check("int f() {\n" + " int i;\n" + " return (&i)[1];\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:3]: (error) The address of variable 'i' is accessed at non-zero index.\n", + errout_str()); + + check("int f(int j) {\n" + " int i;\n" + " return (&i)[j];\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:3]: (warning) The address of variable 'i' might be accessed at non-zero index.\n", + errout_str()); + + check("int f() {\n" + " int i;\n" + " return (&i)[0];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int * i) {\n" + " return i[1];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(std::vector i) {\n" + " return i[1];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(std::vector i) {\n" + " return i.data()[1];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int* f(std::vector& i) {\n" + " return &(i[1]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int i; int j; };\n" + "int f() {\n" + " A x;\n" + " return (&x.i)[0];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int i; int j; };\n" + "int f() {\n" + " A x;\n" + " int * i = &x.i;\n" + " return i[0];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int x = 0;\n" + " std::map m;\n" + " m[0] = &x;\n" + " m[1] = &x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f() {\n" + " int x = 0;\n" + " std::map m;\n" + " m[0] = &x;\n" + " return m[0][1];\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:5]: (error) The address of variable 'x' is accessed at non-zero index.\n", + errout_str()); + + check("int x = 0;\n" + "int f() {\n" + " std::map m;\n" + " m[0] = &x;\n" + " return m[0][1];\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:5]: (error) The address of variable 'x' is accessed at non-zero index.\n", + errout_str()); + + check("int f(int * y) {\n" + " int x = 0;\n" + " std::map m;\n" + " m[0] = &x;\n" + " m[1] = y;\n" + " return m[1][1];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void print(char** test);\n" + "int main(){\n" + " char* test = \"abcdef\";\n" + " print(&test);\n" + " return 0;\n" + "}\n" + "void print(char** test){\n" + " for(int i=0;i [test.cpp:4] -> [test.cpp:9]: (warning) The address of local variable 'test' might be accessed at non-zero index.\n", + "", + errout_str()); + + check("void Bar(uint8_t data);\n" + "void Foo(const uint8_t * const data, const uint8_t length) {\n" + " for(uint8_t index = 0U; index < length ; ++index)\n" + " Bar(data[index]);\n" + "}\n" + "void test() {\n" + " const uint8_t data = 0U;\n" + " Foo(&data,1U);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int foo(int n, int* p) {\n" + " int res = 0;\n" + " for(int i = 0; i < n; i++ )\n" + " res += p[i];\n" + " return res;\n" + "}\n" + "int bar() {\n" + " int single_value = 0;\n" + " return foo(1, &single_value);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const char* app, size_t applen) {\n" // #10137 + " char* tmp_de = NULL;\n" + " char** str = &tmp_de;\n" + " char* tmp = (char*)realloc(*str, applen + 1);\n" + " if (tmp) {\n" + " *str = tmp;\n" + " memcpy(*str, app, applen);\n" + " (*str)[applen] = '\\0';\n" + " }\n" + " free(*str);\n" + "}\n", false); + ASSERT_EQUALS("", errout_str()); + + check("template \n" + "using vector = Eigen::Matrix;\n" + "template \n" + "void scharr(image2d>& out) {\n" + " vector* out_row = &out(r, 0);\n" + " out_row[c] = vector(1,2);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const uint8_t* d, const uint8_t L) {\n" // #10092 + " for (uint8_t i = 0U; i < L; ++i)\n" + " g(d[i]);\n" + "}\n" + "void h() {\n" + " const uint8_t u = 4;\n" + " f(&u, N);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("uint32_t f(uint32_t u) {\n" // #10154 + " return ((uint8_t*)&u)[3];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("uint32_t f(uint32_t u) {\n" + " return ((uint8_t*)&u)[4];\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (error) The address of variable 'u' is accessed at non-zero index.\n", errout_str()); + + check("uint32_t f(uint32_t u) {\n" + " return reinterpret_cast(&u)[3];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("uint32_t f(uint32_t u) {\n" + " return reinterpret_cast(&u)[4];\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (error) The address of variable 'u' is accessed at non-zero index.\n", errout_str()); + + check("uint32_t f(uint32_t u) {\n" + " uint8_t* p = (uint8_t*)&u;\n" + " return p[3];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("uint32_t f(uint32_t u) {\n" + " uint8_t* p = (uint8_t*)&u;\n" + " return p[4];\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) The address of variable 'u' is accessed at non-zero index.\n", errout_str()); + + check("uint32_t f(uint32_t* pu) {\n" + " uint8_t* p = (uint8_t*)pu;\n" + " return p[4];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { uint8_t padding[500]; };\n" // #10133 + "S s = { 0 };\n" + "uint8_t f() {\n" + " uint8_t* p = (uint8_t*)&s;\n" + " return p[10];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct X {\n" // #2654 + " int a;\n" + " char b;\n" + "};\n" + "void f() {\n" + " const X s;\n" + " const int* y = &s.a;\n" + " (void)y[0];\n" + " (void)y[1];\n" + " (void)y[2];\n" + "}\n"); + TODO_ASSERT_EQUALS("error", "", errout_str()); + } + + void checkPipeParameterSize() { // #3521 + + const Settings settings = settingsBuilder().library("posix.cfg").build(); + + check("void f(){\n" + "int pipefd[1];\n" // <-- array of two integers is needed + "if (pipe(pipefd) == -1) {\n" + " return;\n" + " }\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: pipefd\n", errout_str()); + + check("void f(){\n" + "int pipefd[2];\n" + "if (pipe(pipefd) == -1) {\n" + " return;\n" + " }\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + + check("void f(){\n" + "char pipefd[2];\n" + "if (pipe((int*)pipefd) == -1) {\n" + " return;\n" + " }\n" + "}", settings); + ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: (int*)pipefd\n", errout_str()); + + check("void f(){\n" + "char pipefd[20];\n" // Strange, but large enough + "if (pipe((int*)pipefd) == -1) {\n" + " return;\n" + " }\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + } +}; + +REGISTER_TEST(TestBufferOverrun) diff --git a/cppcheck-2.14.0/test/testcharvar.cpp b/cppcheck-2.14.0/test/testcharvar.cpp new file mode 100644 index 00000000..09e1befb --- /dev/null +++ b/cppcheck-2.14.0/test/testcharvar.cpp @@ -0,0 +1,196 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "platform.h" +#include "settings.h" + +class TestCharVar : public TestFixture { +public: + TestCharVar() : TestFixture("TestCharVar") {} + +private: + const Settings settings = settingsBuilder().severity(Severity::warning).severity(Severity::portability).platform(Platform::Type::Unspecified).build(); + + void run() override { + TEST_CASE(array_index_1); + TEST_CASE(array_index_2); + TEST_CASE(bitop); + } + +#define check(code) check_(code, __FILE__, __LINE__) + void check_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check char variable usage.. + CheckOther checkOther(&tokenizer, &settings, this); + checkOther.checkCharVariable(); + } + + void array_index_1() { + check("int buf[256];\n" + "void foo()\n" + "{\n" + " unsigned char ch = 0x80;\n" + " buf[ch] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int buf[256];\n" + "void foo()\n" + "{\n" + " char ch = 0x80;\n" + " buf[ch] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (portability) 'char' type used as array index.\n", errout_str()); + + check("int buf[256];\n" + "void foo()\n" + "{\n" + " char ch = 0;\n" + " buf[ch] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int buf[256];\n" + "void foo()\n" + "{\n" + " signed char ch = 0;\n" + " buf[ch] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int buf[256];\n" + "void foo()\n" + "{\n" + " char ch = 0x80;\n" + " buf[ch] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (portability) 'char' type used as array index.\n", errout_str()); + + check("int buf[256];\n" + "void foo(signed char ch)\n" + "{\n" + " buf[ch] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int buf[256];\n" + "void foo(char ch)\n" + "{\n" + " buf[ch] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(char* buf)\n" + "{\n" + " char ch = 0x80;" + " buf[ch] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (portability) 'char' type used as array index.\n", errout_str()); + + check("void foo(char* buf)\n" + "{\n" + " char ch = 0;" + " buf[ch] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(char* buf)\n" + "{\n" + " buf['A'] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(char* buf, char ch)\n" + "{\n" + " buf[ch] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int flags[256];\n" + "void foo(const char* str)\n" + "{\n" + " flags[*str] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int flags[256];\n" + "void foo(const char* str)\n" + "{\n" + " flags[(unsigned char)*str] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(const char str[])\n" + "{\n" + " map[str] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void array_index_2() { + // #3282 - False positive + check("void foo(char i);\n" + "void bar(int i) {\n" + " const char *s = \"abcde\";\n" + " foo(s[i]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void bitop() { + check("void foo(int *result) {\n" + " signed char ch = -1;\n" + " *result = a | ch;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) When using 'char' variables in bit operations, sign extension can generate unexpected results.\n", errout_str()); + + check("void foo(int *result) {\n" + " unsigned char ch = -1;\n" + " *result = a | ch;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(char *result) {\n" + " signed char ch = -1;\n" + " *result = a | ch;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // 0x03 & .. + check("void foo(int *result) {\n" + " signed char ch = -1;\n" + " *result = 0x03 | ch;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) When using 'char' variables in bit operations, sign extension can generate unexpected results.\n", errout_str()); + + check("void foo(int *result) {\n" + " signed char ch = -1;\n" + " *result = 0x03 & ch;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } +}; + +REGISTER_TEST(TestCharVar) diff --git a/cppcheck-2.14.0/test/testcheck.cpp b/cppcheck-2.14.0/test/testcheck.cpp new file mode 100644 index 00000000..e05c5e86 --- /dev/null +++ b/cppcheck-2.14.0/test/testcheck.cpp @@ -0,0 +1,58 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "check.h" +#include "fixture.h" + +#include +#include + +class TestCheck : public TestFixture { +public: + TestCheck() : TestFixture("TestCheck") {} + +private: + void run() override { + TEST_CASE(instancesSorted); + TEST_CASE(classInfoFormat); + } + + void instancesSorted() const { + for (std::list::const_iterator i = Check::instances().cbegin(); i != Check::instances().cend(); ++i) { + std::list::const_iterator j = i; + ++j; + if (j != Check::instances().cend()) { + ASSERT_EQUALS(true, (*i)->name() < (*j)->name()); + } + } + } + + void classInfoFormat() const { + for (std::list::const_iterator i = Check::instances().cbegin(); i != Check::instances().cend(); ++i) { + const std::string info = (*i)->classInfo(); + if (!info.empty()) { + ASSERT('\n' != info[0]); // No \n in the beginning + ASSERT('\n' == info.back()); // \n at end + if (info.size() > 1) + ASSERT('\n' != info[info.length()-2]); // Only one \n at end + } + } + } +}; + +REGISTER_TEST(TestCheck) diff --git a/cppcheck-2.14.0/test/testclangimport.cpp b/cppcheck-2.14.0/test/testclangimport.cpp new file mode 100644 index 00000000..00810de9 --- /dev/null +++ b/cppcheck-2.14.0/test/testclangimport.cpp @@ -0,0 +1,1370 @@ +// Cppcheck - A tool for static C/C++ code analysis +// Copyright (C) 2007-2024 Cppcheck team. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#include "clangimport.h" +#include "platform.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" +#include "tokenize.h" +#include "fixture.h" + +#include +#include +#include +#include +#include + + +class TestClangImport : public TestFixture { +public: + TestClangImport() + : TestFixture("TestClangImport") {} + + +private: + void run() override { + TEST_CASE(breakStmt); + TEST_CASE(callExpr); + TEST_CASE(caseStmt1); + TEST_CASE(characterLiteral); + TEST_CASE(class1); + TEST_CASE(classTemplateDecl1); + TEST_CASE(classTemplateDecl2); + TEST_CASE(conditionalExpr); + TEST_CASE(compoundAssignOperator); + TEST_CASE(continueStmt); + TEST_CASE(cstyleCastExpr); + TEST_CASE(cxxBoolLiteralExpr); + TEST_CASE(cxxConstructorDecl1); + TEST_CASE(cxxConstructorDecl2); + TEST_CASE(cxxConstructExpr1); + TEST_CASE(cxxConstructExpr2); + TEST_CASE(cxxConstructExpr3); + TEST_CASE(cxxDeleteExpr); + TEST_CASE(cxxDestructorDecl); + TEST_CASE(cxxForRangeStmt1); + TEST_CASE(cxxForRangeStmt2); + TEST_CASE(cxxFunctionalCastExpr); + TEST_CASE(cxxMemberCall); + TEST_CASE(cxxMethodDecl1); + TEST_CASE(cxxMethodDecl2); + TEST_CASE(cxxMethodDecl3); + TEST_CASE(cxxMethodDecl4); + TEST_CASE(cxxNewExpr1); + TEST_CASE(cxxNewExpr2); + TEST_CASE(cxxNullPtrLiteralExpr); + TEST_CASE(cxxOperatorCallExpr); + TEST_CASE(cxxRecordDecl1); + TEST_CASE(cxxRecordDecl2); + TEST_CASE(cxxRecordDeclDerived); + TEST_CASE(cxxStaticCastExpr1); + TEST_CASE(cxxStaticCastExpr2); + TEST_CASE(cxxStaticCastExpr3); + TEST_CASE(cxxStdInitializerListExpr); + TEST_CASE(cxxThrowExpr); + TEST_CASE(defaultStmt); + TEST_CASE(doStmt); + TEST_CASE(enumDecl1); + TEST_CASE(enumDecl2); + TEST_CASE(enumDecl3); + TEST_CASE(enumDecl4); + TEST_CASE(forStmt); + TEST_CASE(funcdecl1); + TEST_CASE(funcdecl2); + TEST_CASE(funcdecl3); + TEST_CASE(funcdecl4); + TEST_CASE(funcdecl5); + TEST_CASE(funcdecl6); + TEST_CASE(functionTemplateDecl1); + TEST_CASE(functionTemplateDecl2); + TEST_CASE(initListExpr); + TEST_CASE(ifelse); + TEST_CASE(ifStmt); + TEST_CASE(labelStmt); + TEST_CASE(memberExpr); + TEST_CASE(namespaceDecl1); + TEST_CASE(namespaceDecl2); + TEST_CASE(recordDecl1); + TEST_CASE(recordDecl2); + TEST_CASE(switchStmt); + TEST_CASE(typedefDecl1); + TEST_CASE(typedefDecl2); + TEST_CASE(typedefDecl3); + TEST_CASE(unaryExprOrTypeTraitExpr1); + TEST_CASE(unaryExprOrTypeTraitExpr2); + TEST_CASE(unaryOperator); + TEST_CASE(vardecl1); + TEST_CASE(vardecl2); + TEST_CASE(vardecl3); + TEST_CASE(vardecl4); + TEST_CASE(vardecl5); + TEST_CASE(vardecl6); + TEST_CASE(vardecl7); + TEST_CASE(whileStmt1); + TEST_CASE(whileStmt2); + + TEST_CASE(tokenIndex); + TEST_CASE(symbolDatabaseEnum1); + TEST_CASE(symbolDatabaseFunction1); + TEST_CASE(symbolDatabaseFunction2); + TEST_CASE(symbolDatabaseFunction3); + TEST_CASE(symbolDatabaseFunctionConst); + TEST_CASE(symbolDatabaseVariableRef); + TEST_CASE(symbolDatabaseVariableRRef); + TEST_CASE(symbolDatabaseVariablePointerRef); + TEST_CASE(symbolDatabaseNodeType1); + TEST_CASE(symbolDatabaseForVariable); + + TEST_CASE(valueFlow1); + TEST_CASE(valueFlow2); + + TEST_CASE(valueType1); + TEST_CASE(valueType2); + + TEST_CASE(crash); + } + + std::string parse(const char clang[]) { + const Settings settings = settingsBuilder().clang().build(); + Tokenizer tokenizer(settings, *this); + std::istringstream istr(clang); + clangimport::parseClangAstDump(tokenizer, istr); + if (!tokenizer.tokens()) { + return std::string(); + } + return tokenizer.tokens()->stringifyList(true, false, false, false, false); + } + + void breakStmt() { + const char clang[] = "`-FunctionDecl 0x2c31b18 <1.c:1:1, col:34> col:6 foo 'void ()'\n" + " `-CompoundStmt 0x2c31c40 \n" + " `-WhileStmt 0x2c31c20 \n" + " |-<<>>\n" + " |-IntegerLiteral 0x2c31bf8 'int' 0\n" + " `-BreakStmt 0x3687c18 "; + ASSERT_EQUALS("void foo ( ) { while ( 0 ) { break ; } }", parse(clang)); + } + + void callExpr() { + const char clang[] = "`-FunctionDecl 0x2444b60 <1.c:1:1, line:8:1> line:1:6 foo 'void (int)'\n" + " |-ParmVarDecl 0x2444aa0 col:14 used x 'int'\n" + " `-CompoundStmt 0x2444e00 \n" + " `-CallExpr 0x7f5a6c04b158 'bool'\n" + " |-ImplicitCastExpr 0x7f5a6c04b140 'bool (*)(const Token *, const char *, int)' \n" + " | `-DeclRefExpr 0x7f5a6c04b0a8 'bool (const Token *, const char *, int)' lvalue CXXMethod 0x43e5600 'Match' 'bool (const Token *, const char *, int)'\n" + " |-ImplicitCastExpr 0x7f5a6c04b1c8 'const Token *' \n" + " | `-ImplicitCastExpr 0x7f5a6c04b1b0 'Token *' \n" + " | `-DeclRefExpr 0x7f5a6c04b0e0 'Token *' lvalue Var 0x7f5a6c045968 'tokAfterCondition' 'Token *'\n" + " |-ImplicitCastExpr 0x7f5a6c04b1e0 'const char *' \n" + " | `-StringLiteral 0x7f5a6c04b108 'const char [11]' lvalue \"%name% : {\"\n" + " `-CXXDefaultArgExpr 0x7f5a6c04b1f8 <> 'int'\n"; + ASSERT_EQUALS("void foo ( int x@1 ) { Match ( tokAfterCondition , \"%name% : {\" ) ; }", parse(clang)); + } + + void caseStmt1() { + const char clang[] = "`-FunctionDecl 0x2444b60 <1.c:1:1, line:8:1> line:1:6 foo 'void (int)'\n" + " |-ParmVarDecl 0x2444aa0 col:14 used x 'int'\n" + " `-CompoundStmt 0x2444e00 \n" + " `-SwitchStmt 0x2444c88 \n" + " |-<<>>\n" + " |-<<>>\n" + " |-ImplicitCastExpr 0x2444c70 'int' \n" + " | `-DeclRefExpr 0x2444c48 'int' lvalue ParmVar 0x2444aa0 'x' 'int'\n" + " `-CompoundStmt 0x2444de0 \n" + " |-CaseStmt 0x2444cd8 \n" + " | |-IntegerLiteral 0x2444cb8 'int' 16\n" + " | |-<<>>\n" + " | `-CaseStmt 0x2444d30 \n" + " | |-IntegerLiteral 0x2444d10 'int' 32\n" + " | |-<<>>\n" + " | `-BinaryOperator 0x2444db0 'int' '='\n" + " | |-DeclRefExpr 0x2444d68 'int' lvalue ParmVar 0x2444aa0 'x' 'int'\n" + " | `-IntegerLiteral 0x2444d90 'int' 123\n" + " `-BreakStmt 0x2444dd8 "; + ASSERT_EQUALS("void foo ( int x@1 ) { switch ( x@1 ) { case 16 : case 32 : x@1 = 123 ; break ; } }", parse(clang)); + } + + void characterLiteral() { + const char clang[] = "`-VarDecl 0x3df8608 col:6 c 'char' cinit\n" + " `-CharacterLiteral 0x3df86a8 'char' 120"; + ASSERT_EQUALS("char c@1 = 'x' ;", parse(clang)); + } + + void class1() { + const char clang[] = "`-CXXRecordDecl 0x274c638 col:7 class C definition\n" + " |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init\n" + " | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr\n" + " | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param\n" + " | |-MoveConstructor exists simple trivial needs_implicit\n" + " | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param\n" + " | |-MoveAssignment exists simple trivial needs_implicit\n" + " | `-Destructor simple irrelevant trivial needs_implicit\n" + " |-CXXRecordDecl 0x274c758 col:7 implicit class C\n" + " `-CXXMethodDecl 0x274c870 col:16 foo 'void ()'\n" + " `-CompoundStmt 0x274c930 "; + ASSERT_EQUALS("class C { void foo ( ) { } } ;", parse(clang)); + } + + void classTemplateDecl1() { + const char clang[] = "`-ClassTemplateDecl 0x29d1748 col:25 C\n" + " |-TemplateTypeParmDecl 0x29d15f8 col:16 referenced class depth 0 index 0 T\n" + " `-CXXRecordDecl 0x29d16b0 col:25 class C definition\n" + " |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init\n" + " | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr\n" + " | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param\n" + " | |-MoveConstructor exists simple trivial needs_implicit\n" + " | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param\n" + " | |-MoveAssignment exists simple trivial needs_implicit\n" + " | `-Destructor simple irrelevant trivial needs_implicit\n" + " |-CXXRecordDecl 0x29d19b0 col:25 implicit class C\n" + " |-AccessSpecDecl 0x29d1a48 col:29 public\n" + " `-CXXMethodDecl 0x29d1b20 col:39 foo 'T ()'\n" + " `-CompoundStmt 0x29d1c18 \n" + " `-ReturnStmt 0x29d1c00 \n" + " `-IntegerLiteral 0x29d1be0 'int' 0"; + ASSERT_EQUALS("", parse(clang)); + } + + void classTemplateDecl2() { + const char clang[] = "|-ClassTemplateDecl 0x244e748 col:25 C\n" + "| |-TemplateTypeParmDecl 0x244e5f8 col:16 referenced class depth 0 index 0 T\n" + "| |-CXXRecordDecl 0x244e6b0 col:25 class C definition\n" + "| | |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init\n" + "| | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr\n" + "| | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param\n" + "| | | |-MoveConstructor exists simple trivial needs_implicit\n" + "| | | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param\n" + "| | | |-MoveAssignment exists simple trivial needs_implicit\n" + "| | | `-Destructor simple irrelevant trivial needs_implicit\n" + "| | |-CXXRecordDecl 0x244e9b0 col:25 implicit class C\n" + "| | |-AccessSpecDecl 0x244ea48 col:29 public\n" + "| | `-CXXMethodDecl 0x244eb20 col:39 foo 'T ()'\n" + "| | `-CompoundStmt 0x244ec18 \n" + "| | `-ReturnStmt 0x244ec00 \n" + "| | `-IntegerLiteral 0x244ebe0 'int' 0\n" + "| `-ClassTemplateSpecializationDecl 0x244ed78 col:25 class C definition\n" + "| |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init\n" + "| | |-DefaultConstructor exists trivial constexpr defaulted_is_constexpr\n" + "| | |-CopyConstructor simple trivial has_const_param implicit_has_const_param\n" + "| | |-MoveConstructor exists simple trivial\n" + "| | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param\n" + "| | |-MoveAssignment exists simple trivial needs_implicit\n" + "| | `-Destructor simple irrelevant trivial needs_implicit\n" + "| |-TemplateArgument type 'int'\n" + "| |-CXXRecordDecl 0x244eff0 prev 0x244ed78 col:25 implicit class C\n" + "| |-AccessSpecDecl 0x244f088 col:29 public\n" + "| |-CXXMethodDecl 0x244f160 col:39 used foo 'int ()'\n" + "| | `-CompoundStmt 0x247cb40 \n" + "| | `-ReturnStmt 0x247cb28 \n" + "| | `-IntegerLiteral 0x244ebe0 'int' 0\n" + "| |-CXXConstructorDecl 0x247c540 col:25 implicit used constexpr C 'void () noexcept' inline default trivial\n" + "| | `-CompoundStmt 0x247ca00 \n" + "| |-CXXConstructorDecl 0x247c658 col:25 implicit constexpr C 'void (const C &)' inline default trivial noexcept-unevaluated 0x247c658\n" + "| | `-ParmVarDecl 0x247c790 col:25 'const C &'\n" + "| `-CXXConstructorDecl 0x247c828 col:25 implicit constexpr C 'void (C &&)' inline default trivial noexcept-unevaluated 0x247c828\n" + "| `-ParmVarDecl 0x247c960 col:25 'C &&'\n"; + ASSERT_EQUALS("class C { int foo ( ) { return 0 ; } C ( ) { } C ( const C & ) = default ; C ( C && ) = default ; } ;", parse(clang)); + } + + void conditionalExpr() { + const char clang[] = "`-VarDecl 0x257cc88 col:5 x 'int' cinit\n" + " `-ConditionalOperator 0x257cda8 'int'\n" + " |-ImplicitCastExpr 0x257cd60 'int' \n" + " | `-DeclRefExpr 0x257cce8 'int' lvalue Var 0x257cae0 'a' 'int'\n" + " |-ImplicitCastExpr 0x257cd78 'int' \n" + " | `-DeclRefExpr 0x257cd10 'int' lvalue Var 0x257cb98 'b' 'int'\n" + " `-ImplicitCastExpr 0x257cd90 'int' \n" + " `-DeclRefExpr 0x257cd38 'int' lvalue Var 0x257cc10 'c' 'int'"; + ASSERT_EQUALS("int x@1 = a ? b : c ;", parse(clang)); + } + + void compoundAssignOperator() { + const char clang[] = "`-FunctionDecl 0x3570690 <1.cpp:2:1, col:25> col:6 f 'void ()'\n" + " `-CompoundStmt 0x3570880 \n" + " `-CompoundAssignOperator 0x3570848 'int' lvalue '+=' ComputeLHSTy='int' ComputeResultTy='int'\n" + " |-DeclRefExpr 0x3570800 'int' lvalue Var 0x3570788 'x' 'int'\n" + " `-IntegerLiteral 0x3570828 'int' 1"; + ASSERT_EQUALS("void f ( ) { x += 1 ; }", parse(clang)); + } + + void continueStmt() { + const char clang[] = "`-FunctionDecl 0x2c31b18 <1.c:1:1, col:34> col:6 foo 'void ()'\n" + " `-CompoundStmt 0x2c31c40 \n" + " `-WhileStmt 0x2c31c20 \n" + " |-<<>>\n" + " |-IntegerLiteral 0x2c31bf8 'int' 0\n" + " `-ContinueStmt 0x2c31c18 "; + ASSERT_EQUALS("void foo ( ) { while ( 0 ) { continue ; } }", parse(clang)); + } + + void cstyleCastExpr() { + const char clang[] = "`-VarDecl 0x2336aa0 <1.c:1:1, col:14> col:5 x 'int' cinit\n" + " `-CStyleCastExpr 0x2336b70 'int' \n" + " `-CharacterLiteral 0x2336b40 'int' 97"; + ASSERT_EQUALS("int x@1 = ( int ) 'a' ;", parse(clang)); + } + + void cxxBoolLiteralExpr() { + const char clang[] = "`-VarDecl 0x3940608 col:6 x 'bool' cinit\n" + " `-CXXBoolLiteralExpr 0x39406a8 'bool' true"; + ASSERT_EQUALS("bool x@1 = true ;", parse(clang)); + } + + void cxxConstructorDecl1() { + const char clang[] = "|-CXXConstructorDecl 0x428e890 col:11 C 'void ()'\n" + "| `-CompoundStmt 0x428ea58 \n" + "| `-BinaryOperator 0x428ea30 'int' lvalue '='\n" + "| |-MemberExpr 0x428e9d8 'int' lvalue ->x 0x428e958\n" + "| | `-CXXThisExpr 0x428e9c0 'C *' this\n" + "| `-IntegerLiteral 0x428ea10 'int' 0\n" + "`-FieldDecl 0x428e958 col:30 referenced x 'int'"; + ASSERT_EQUALS("C ( ) { this . x@1 = 0 ; } int x@1", parse(clang)); + } + + void cxxConstructorDecl2() { + const char clang[] = "`-CXXConstructorDecl 0x1c208c0 col:11 implicit constexpr basic_string 'void (std::basic_string &&)' inline default trivial noexcept-unevaluated 0x1c208c0\n" + " `-ParmVarDecl 0x1c209f0 col:11 'std::basic_string &&'"; + ASSERT_EQUALS("basic_string ( std::basic_string && ) = default ;", parse(clang)); + } + + void cxxConstructExpr1() { + const char clang[] = "`-FunctionDecl 0x2dd7940 col:5 f 'Foo (Foo)'\n" + " |-ParmVarDecl 0x2dd7880 col:11 used foo 'Foo'\n" + " `-CompoundStmt 0x2dd80c0 \n" + " `-ReturnStmt 0x2dd80a8 \n" + " `-CXXConstructExpr 0x2dd8070 'Foo' 'void (Foo &&) noexcept'\n" + " `-ImplicitCastExpr 0x2dd7f28 'Foo' xvalue \n" + " `-DeclRefExpr 0x2dd7a28 'Foo' lvalue ParmVar 0x2dd7880 'foo' 'Foo'"; + ASSERT_EQUALS("Foo f ( Foo foo@1 ) { return foo@1 ; }", parse(clang)); + } + + void cxxConstructExpr2() { + const char clang[] = "`-FunctionDecl 0x3e44180 <1.cpp:2:1, col:30> col:13 f 'std::string ()'\n" + " `-CompoundStmt 0x3e4cb80 \n" + " `-ReturnStmt 0x3e4cb68 \n" + " `-CXXConstructExpr 0x3e4cb38 'std::string':'std::__cxx11::basic_string' '....' list"; + ASSERT_EQUALS("std :: string f ( ) { return std :: string ( ) ; }", parse(clang)); + } + + void cxxConstructExpr3() { + const char clang[] = "`-FunctionDecl 0x2c585b8 <1.cpp:4:1, col:39> col:6 f 'void ()'\n" + " `-CompoundStmt 0x2c589d0 \n" + " |-DeclStmt 0x2c586d0 \n" + " | `-VarDecl 0x2c58670 col:18 used p 'char *'\n" + " `-DeclStmt 0x2c589b8 \n" + " `-VarDecl 0x2c58798 col:33 s 'std::string':'std::__cxx11::basic_string' callinit\n" + " `-ExprWithCleanups 0x2c589a0 'std::string':'std::__cxx11::basic_string'\n" + " `-CXXConstructExpr 0x2c58960 'std::string':'std::__cxx11::basic_string' 'void (const char *, const std::allocator &)'\n" + " |-ImplicitCastExpr 0x2c58870 'const char *' \n" + " | `-ImplicitCastExpr 0x2c58858 'char *' \n" + " | `-DeclRefExpr 0x2c58750 'char *' lvalue Var 0x2c58670 'p' 'char *'\n" + " `-CXXDefaultArgExpr 0x2c58940 <> 'const std::allocator':'const std::allocator' lvalue\n"; + ASSERT_EQUALS("void f ( ) { char * p@1 ; std :: string s@2 ( p@1 ) ; }", parse(clang)); + } + + void cxxDeleteExpr() { + const char clang[] = "|-FunctionDecl 0x2e0e740 <1.cpp:1:1, col:28> col:6 f 'void (int *)'\n" + "| |-ParmVarDecl 0x2e0e680 col:13 used p 'int *'\n" + "| `-CompoundStmt 0x2e0ee70 \n" + "| `-CXXDeleteExpr 0x2e0ee48 'void' Function 0x2e0ebb8 'operator delete' 'void (void *) noexcept'\n" + "| `-ImplicitCastExpr 0x2e0e850 'int *' \n" + "| `-DeclRefExpr 0x2e0e828 'int *' lvalue ParmVar 0x2e0e680 'p' 'int *'"; + ASSERT_EQUALS("void f ( int * p@1 ) { delete p@1 ; }", parse(clang)); + } + + void cxxDestructorDecl() { + const char clang[] = "`-CXXRecordDecl 0x8ecd60 <1.cpp:1:1, line:4:1> line:1:8 struct S definition\n" + " `-CXXDestructorDecl 0x8ed088 col:3 ~S 'void () noexcept'\n" + " `-CompoundStmt 0x8ed1a8 "; + ASSERT_EQUALS("struct S { ~S ( ) { } } ;", parse(clang)); + } + + void cxxForRangeStmt1() { + const char clang[] = "`-FunctionDecl 0x4280820 line:4:6 foo 'void ()'\n" + " `-CompoundStmt 0x42810f0 \n" + " `-CXXForRangeStmt 0x4281090 \n" + " |-DeclStmt 0x4280c30 \n" + " | `-VarDecl 0x42809c8 col:17 implicit referenced __range1 'char const (&)[6]' cinit\n" + " | `-DeclRefExpr 0x42808c0 'const char [6]' lvalue Var 0x4280678 'hello' 'const char [6]'\n" + " |-DeclStmt 0x4280ef8 \n" + " | `-VarDecl 0x4280ca8 col:15 implicit used __begin1 'const char *':'const char *' cinit\n" + " | `-ImplicitCastExpr 0x4280e10 'const char *' \n" + " | `-DeclRefExpr 0x4280c48 'char const[6]' lvalue Var 0x42809c8 '__range1' 'char const (&)[6]'\n" + " |-DeclStmt 0x4280f10 \n" + " | `-VarDecl 0x4280d18 col:15 implicit used __end1 'const char *':'const char *' cinit\n" + " | `-BinaryOperator 0x4280e60 'const char *' '+'\n" + " | |-ImplicitCastExpr 0x4280e48 'const char *' \n" + " | | `-DeclRefExpr 0x4280c70 'char const[6]' lvalue Var 0x42809c8 '__range1' 'char const (&)[6]'\n" + " | `-IntegerLiteral 0x4280e28 'long' 6\n" + " |-BinaryOperator 0x4280fa8 'bool' '!='\n" + " | |-ImplicitCastExpr 0x4280f78 'const char *':'const char *' \n" + " | | `-DeclRefExpr 0x4280f28 'const char *':'const char *' lvalue Var 0x4280ca8 '__begin1' 'const char *':'const char *'\n" + " | `-ImplicitCastExpr 0x4280f90 'const char *':'const char *' \n" + " | `-DeclRefExpr 0x4280f50 'const char *':'const char *' lvalue Var 0x4280d18 '__end1' 'const char *':'const char *'\n" + " |-UnaryOperator 0x4280ff8 'const char *':'const char *' lvalue prefix '++'\n" + " | `-DeclRefExpr 0x4280fd0 'const char *':'const char *' lvalue Var 0x4280ca8 '__begin1' 'const char *':'const char *'\n" + " |-DeclStmt 0x4280958 \n" + " | `-VarDecl 0x42808f8 col:13 c1 'char' cinit\n" + " | `-ImplicitCastExpr 0x4281078 'char' \n" + " | `-UnaryOperator 0x4281058 'const char' lvalue prefix '*' cannot overflow\n" + " | `-ImplicitCastExpr 0x4281040 'const char *':'const char *' \n" + " | `-DeclRefExpr 0x4281018 'const char *':'const char *' lvalue Var 0x4280ca8 '__begin1' 'const char *':'const char *'\n" + " `-CompoundStmt 0x42810e0 "; + ASSERT_EQUALS("void foo ( ) { for ( char c1@1 : hello ) { } }", + parse(clang)); + } + + void cxxForRangeStmt2() { + // clang 9 + const char clang[] = "`-FunctionDecl 0xc15d98 col:6 foo 'void ()'\n" + " `-CompoundStmt 0xc16668 \n" + " `-CXXForRangeStmt 0xc165f8 \n" + " |-<<>>\n" + " |-DeclStmt 0xc161c0 \n" + " | `-VarDecl 0xc15f48 col:25 implicit referenced __range1 'int const (&)[4]' cinit\n" + " | `-DeclRefExpr 0xc15e38 'const int [4]' lvalue Var 0xc15ac0 'values' 'const int [4]'\n" + " |-DeclStmt 0xc16498 \n" + " | `-VarDecl 0xc16228 col:24 implicit used __begin1 'const int *':'const int *' cinit\n" + " | `-ImplicitCastExpr 0xc163b0 'const int *' \n" + " | `-DeclRefExpr 0xc161d8 'int const[4]' lvalue Var 0xc15f48 '__range1' 'int const (&)[4]' non_odr_use_constant\n" + " |-DeclStmt 0xc164b0 \n" + " | `-VarDecl 0xc162a0 col:24 implicit used __end1 'const int *':'const int *' cinit\n" + " | `-BinaryOperator 0xc16400 'const int *' '+'\n" + " | |-ImplicitCastExpr 0xc163e8 'const int *' \n" + " | | `-DeclRefExpr 0xc161f8 'int const[4]' lvalue Var 0xc15f48 '__range1' 'int const (&)[4]' non_odr_use_constant\n" + " | `-IntegerLiteral 0xc163c8 'long' 4\n" + " |-BinaryOperator 0xc16538 'bool' '!='\n" + " | |-ImplicitCastExpr 0xc16508 'const int *':'const int *' \n" + " | | `-DeclRefExpr 0xc164c8 'const int *':'const int *' lvalue Var 0xc16228 '__begin1' 'const int *':'const int *'\n" + " | `-ImplicitCastExpr 0xc16520 'const int *':'const int *' \n" + " | `-DeclRefExpr 0xc164e8 'const int *':'const int *' lvalue Var 0xc162a0 '__end1' 'const int *':'const int *'\n" + " |-UnaryOperator 0xc16578 'const int *':'const int *' lvalue prefix '++'\n" + " | `-DeclRefExpr 0xc16558 'const int *':'const int *' lvalue Var 0xc16228 '__begin1' 'const int *':'const int *'\n" + " |-DeclStmt 0xc15ed8 \n" + " | `-VarDecl 0xc15e70 col:23 v 'int' cinit\n" + " | `-ImplicitCastExpr 0xc165e0 'int' \n" + " | `-UnaryOperator 0xc165c8 'const int' lvalue prefix '*' cannot overflow\n" + " | `-ImplicitCastExpr 0xc165b0 'const int *':'const int *' \n" + " | `-DeclRefExpr 0xc16590 'const int *':'const int *' lvalue Var 0xc16228 '__begin1' 'const int *':'const int *'\n" + " `-CompoundStmt 0xc16658 "; + ASSERT_EQUALS("void foo ( ) { for ( int v@1 : values ) { } }", + parse(clang)); + } + + void cxxFunctionalCastExpr() { + const char clang[] = "`-FunctionDecl 0x156fe98 line:1:5 main 'int (int, char **)'\n" + " |-ParmVarDecl 0x156fd00 col:14 argc 'int'\n" + " |-ParmVarDecl 0x156fdb8 col:27 argv 'char **'\n" + " `-CompoundStmt 0x1596410 \n" + " |-DeclStmt 0x15946a8 \n" + " | `-VarDecl 0x1570118 col:11 used setCode 'MyVar':'MyVar' cinit\n" + " | `-ExprWithCleanups 0x1594690 'MyVar':'MyVar'\n" + " | `-CXXConstructExpr 0x1594660 'MyVar':'MyVar' 'void (MyVar &&) noexcept' elidable\n" + " | `-MaterializeTemporaryExpr 0x1592b68 'MyVar':'MyVar' xvalue\n" + " | `-CXXFunctionalCastExpr 0x1592b40 'MyVar':'MyVar' functional cast to MyVar \n" + " | `-CXXConstructExpr 0x15929f0 'MyVar':'MyVar' 'void (int)'\n" + " | `-IntegerLiteral 0x1570248 'int' 5\n"; + ASSERT_EQUALS("int main ( int argc@1 , char * * argv@2 ) { MyVar setCode@3 = MyVar ( 5 ) ; }", + parse(clang)); + } + + void cxxMemberCall() { + const char clang[] = "`-FunctionDecl 0x320dc80 col:6 bar 'void ()'\n" + " `-CompoundStmt 0x323bb08 \n" + " |-DeclStmt 0x323ba40 \n" + " | `-VarDecl 0x320df28 col:21 used c 'C':'C' callinit\n" + " | `-CXXConstructExpr 0x323ba10 'C':'C' 'void () noexcept'\n" + " `-CXXMemberCallExpr 0x323bab8 'int':'int'\n" + " `-MemberExpr 0x323ba80 '' .foo 0x320e160\n" + " `-DeclRefExpr 0x323ba58 'C':'C' lvalue Var 0x320df28 'c' 'C':'C'"; + ASSERT_EQUALS("void bar ( ) { C c@1 ( C ( ) ) ; c@1 . foo ( ) ; }", parse(clang)); + } + + void cxxMethodDecl1() { + const char clang[] = "|-CXXMethodDecl 0x55c786f5ad60 col:10 analyzeFile '_Bool (const std::string &, const std::string &, const std::string &, unsigned long long, std::list *)'\n" + "| |-ParmVarDecl 0x55c786f5a4c8 col:41 buildDir 'const std::string &'\n" + "| |-ParmVarDecl 0x55c786f5a580 col:70 sourcefile 'const std::string &'\n" + "| |-ParmVarDecl 0x55c786f5a638 col:101 cfg 'const std::string &'\n" + "| |-ParmVarDecl 0x55c786f5a6a8 col:125 checksum 'unsigned long long'\n" + "| |-ParmVarDecl 0x55c786f5ac00 col:173 errors 'std::list *'\n" + " `-CompoundStmt 0x0 <>"; + ASSERT_EQUALS("_Bool analyzeFile ( const std :: string & buildDir@1 , const std :: string & sourcefile@2 , const std :: string & cfg@3 , unsigned long long checksum@4 , std::list * errors@5 ) { }", parse(clang)); + } + + void cxxMethodDecl2() { // "unexpanded" template method + const char clang[] = "`-CXXMethodDecl 0x220ecb0 parent 0x21e4c28 prev 0x21e5338 line:14:1 find 'const typename char_traits<_CharT>::char_type *(const char_traits::char_type *, int, const char_traits::char_type &)'\n" + " `-CompoundStmt 0x220ede0 \n" + " `-ReturnStmt 0x220edd0 \n" + " `-IntegerLiteral 0x220edb0 'int' 0"; + ASSERT_EQUALS("", parse(clang)); + } + + void cxxMethodDecl3() { + const char clang[] = "|-CXXRecordDecl 0x21cca40 <2.cpp:2:1, line:4:1> line:2:7 class Fred definition\n" + "| |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init\n" + "| | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr\n" + "| | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param\n" + "| | |-MoveConstructor exists simple trivial needs_implicit\n" + "| | |-CopyAssignment trivial has_const_param needs_implicit implicit_has_const_param\n" + "| | |-MoveAssignment exists simple trivial needs_implicit\n" + "| | `-Destructor simple irrelevant trivial needs_implicit\n" + "| |-CXXRecordDecl 0x21ccb58 col:7 implicit class Fred\n" + "| `-CXXMethodDecl 0x21ccc68 col:6 foo 'void ()'\n" + "`-CXXMethodDecl 0x21ccd60 parent 0x21cca40 prev 0x21ccc68 col:12 foo 'void ()'\n" + " `-CompoundStmt 0x21cce50 "; + ASSERT_EQUALS("class Fred { void foo ( ) ; } ; void Fred :: foo ( ) { }", parse(clang)); + } + + void cxxMethodDecl4() { + const char clang[] = "|-ClassTemplateSpecializationDecl 0x15d82f8 line:8:12 struct char_traits definition\n" + "| |-TemplateArgument type 'char'\n" + "| | `-BuiltinType 0x15984c0 'char'\n" + "| |-CXXRecordDecl 0x15d8520 col:12 implicit struct char_traits\n" + "| |-CXXMethodDecl 0x15d8738 line:13:7 move 'char *(char *)' static\n" + "| | |-ParmVarDecl 0x15d8630 col:18 used __s1 'char *'\n" + "| | `-CompoundStmt 0x15d88e8 \n"; + ASSERT_EQUALS("struct char_traits { static char * move ( char * __s1@1 ) { } } ;", + parse(clang)); + } + + void cxxNewExpr1() { + const char clang[] = "|-VarDecl 0x3a97680 <1.cpp:2:1, col:14> col:6 i 'int *' cinit\n" + "| `-CXXNewExpr 0x3a97d18 'int *' Function 0x3a97778 'operator new' 'void *(unsigned long)'\n" + "`-VarDecl 0x3a97d80 col:6 j 'int *' cinit\n" + " `-CXXNewExpr 0x3a97e68 'int *' array Function 0x3a978c0 'operator new[]' 'void *(unsigned long)'\n" + " `-ImplicitCastExpr 0x3a97e18 'unsigned long' \n" + " `-IntegerLiteral 0x3a97de0 'int' 100"; + ASSERT_EQUALS("int * i@1 = new int ; int * j@2 = new int [ 100 ] ;", + parse(clang)); + } + + void cxxNewExpr2() { + const char clang[] = "|-FunctionDecl 0x59a188 line:7:11 f 'struct S *()'\n" + "| `-CompoundStmt 0x5c4318 \n" + "| `-ReturnStmt 0x5c4308 \n" + "| `-CXXNewExpr 0x5c42c8 'S *' Function 0x59a378 'operator new' 'void *(unsigned long)'\n" + "| `-CXXConstructExpr 0x5c42a0 'S' 'void () noexcept'"; + ASSERT_EQUALS("struct S * f ( ) { return new S ( ) ; }", + parse(clang)); + } + + void cxxNullPtrLiteralExpr() { + const char clang[] = "`-VarDecl 0x2a7d650 <1.cpp:1:1, col:17> col:13 p 'const char *' cinit\n" + " `-ImplicitCastExpr 0x2a7d708 'const char *' \n" + " `-CXXNullPtrLiteralExpr 0x2a7d6f0 'nullptr_t'"; + ASSERT_EQUALS("const char * p@1 = nullptr ;", parse(clang)); + } + + void cxxOperatorCallExpr() { + const char clang[] = "`-FunctionDecl 0x3c099f0 col:6 foo 'void ()'\n" + " `-CompoundStmt 0x3c37308 \n" + " |-DeclStmt 0x3c0a060 \n" + " | `-VarDecl 0x3c09ae0 col:16 used c 'C' callinit\n" + " | `-CXXConstructExpr 0x3c0a030 'C' 'void () noexcept'\n" + " `-CXXOperatorCallExpr 0x3c372c0 'void'\n" + " |-ImplicitCastExpr 0x3c372a8 'void (*)(int)' \n" + " | `-DeclRefExpr 0x3c37250 'void (int)' lvalue CXXMethod 0x3c098c0 'operator=' 'void (int)'\n" + " |-DeclRefExpr 0x3c0a078 'C' lvalue Var 0x3c09ae0 'c' 'C'\n" + " `-IntegerLiteral 0x3c0a0a0 'int' 4"; + ASSERT_EQUALS("void foo ( ) { C c@1 ( C ( ) ) ; c@1 . operator= ( 4 ) ; }", parse(clang)); + } + + void cxxRecordDecl1() { + const char* clang = "`-CXXRecordDecl 0x34cc5f8 <1.cpp:2:1, col:7> col:7 class Foo"; + ASSERT_EQUALS("class Foo ;", parse(clang)); + + clang = "`-CXXRecordDecl 0x34cc5f8 col:7 class Foo"; + ASSERT_EQUALS("class Foo ;", parse(clang)); + + clang = "`-CXXRecordDecl 0x34cc5f8 col:7 class Foo"; + ASSERT_EQUALS("class Foo ;", parse(clang)); + } + + void cxxRecordDecl2() { + const char clang[] = "`-CXXRecordDecl 0x34cc5f8 <1.cpp:2:1, col:7> col:7 struct Foo definition"; + ASSERT_EQUALS("struct Foo { } ;", parse(clang)); + } + + void cxxRecordDeclDerived() { + const char clang[] = "|-CXXRecordDecl 0x19ccd38 line:4:8 referenced struct base definition\n" + "| `-VarDecl 0x19ccf00 col:27 value 'const bool' static constexpr cinit\n" + "| |-value: Int 0\n" + "| `-CXXBoolLiteralExpr 0x19ccf68 'bool' false\n" + "`-CXXRecordDecl 0x19ccfe8 col:8 struct derived definition\n" + " |-public 'base'\n" + " `-CXXRecordDecl 0x19cd150 col:8 implicit struct derived"; + + ASSERT_EQUALS("struct base { static const bool value@1 = false ; } ; struct derived : public base { } ;", parse(clang)); + } + + void cxxStaticCastExpr1() { + const char clang[] = "`-VarDecl 0x2e0e650 col:5 a 'int' cinit\n" + " `-CXXStaticCastExpr 0x2e0e728 'int' static_cast \n" + " `-IntegerLiteral 0x2e0e6f0 'int' 0"; + ASSERT_EQUALS("int a@1 = static_cast ( 0 ) ;", parse(clang)); + } + + void cxxStaticCastExpr2() { + const char clang[] = "`-VarDecl 0x2e0e650 col:5 a 'int' cinit\n" + " `-CXXStaticCastExpr 0x3e453e8 'std::_Rb_tree_iterator, Library::AllocFunc> >' xvalue static_cast, struct Library::AllocFunc> > &&> \n" + " `-DeclRefExpr 0x3e453b0 'std::_Rb_tree_iterator, Library::AllocFunc> >' lvalue ParmVar 0x3e45250 '' 'std::_Rb_tree_iterator, Library::AllocFunc> > &&'"; + ASSERT_EQUALS("int a@1 = static_cast,structLibrary::AllocFunc>>&&> ( ) ;", parse(clang)); + } + + void cxxStaticCastExpr3() { + const char clang[] = "`-ClassTemplateSpecializationDecl 0xd842d8 line:4:21 struct char_traits definition\n" + " |-TemplateArgument type 'char'\n" + " | `-BuiltinType 0xd444c0 'char'\n" + " |-CXXRecordDecl 0xd84500 col:21 implicit struct char_traits\n" + " |-TypedefDecl 0xd845a0 col:20 referenced char_type 'char'\n" + " | `-BuiltinType 0xd444c0 'char'\n" + " `-CXXMethodDecl 0xd847b0 col:18 assign 'char_traits::char_type *(char_traits::char_type *)'\n" + " |-ParmVarDecl 0xd84670 col:36 used __s 'char_traits::char_type *'\n" + " `-CompoundStmt 0xd848f8 \n" + " `-ReturnStmt 0xd848e8 \n" + " `-CXXStaticCastExpr 0xd848b8 'char_traits::char_type *' static_cast::char_type *> \n" + " `-ImplicitCastExpr 0xd848a0 'char_traits::char_type *' part_of_explicit_cast\n" + " `-DeclRefExpr 0xd84870 'char_traits::char_type *' lvalue ParmVar 0xd84670 '__s' 'char_traits::char_type *'\n"; + + ASSERT_EQUALS("struct char_traits { typedef char char_type ; char_traits::char_type * assign ( char_traits::char_type * __s@1 ) { return static_cast::char_type*> ( __s@1 ) ; } } ;", parse(clang)); + } + + void cxxStdInitializerListExpr() { + const char clang[] = "`-VarDecl 0x2f92060 <1.cpp:3:1, col:25> col:18 x 'std::vector':'std::vector >' listinit\n" + " `-ExprWithCleanups 0x2fb0b40 'std::vector':'std::vector >'\n" + " `-CXXConstructExpr 0x2fb0b00 'std::vector':'std::vector >' 'void (initializer_list >::value_type>, const std::vector >::allocator_type &)' list std::initializer_list\n" + " |-CXXStdInitializerListExpr 0x2fb0928 'initializer_list >::value_type>':'std::initializer_list'\n" + " | `-MaterializeTemporaryExpr 0x2fb0910 'const int [3]' xvalue\n" + " | `-InitListExpr 0x2fb08b8 'const int [3]'\n" + " | |-IntegerLiteral 0x2f920c0 'int' 1\n" + " | |-IntegerLiteral 0x2f920e0 'int' 2\n" + " | `-IntegerLiteral 0x2f92100 'int' 3\n" + " `-CXXDefaultArgExpr 0x2fb0ae0 <> 'const std::vector >::allocator_type':'const std::allocator' lvalue"; + ASSERT_EQUALS("std :: vector x@1 { 1 , 2 , 3 } ;", parse(clang)); + } + + void cxxThrowExpr() { + const char clang[] = "`-FunctionDecl 0x3701690 <1.cpp:2:1, col:23> col:6 foo 'void ()'\n" + " `-CompoundStmt 0x37017b0 \n" + " `-CXXThrowExpr 0x3701790 'void'\n" + " `-IntegerLiteral 0x3701770 'int' 1"; + ASSERT_EQUALS("void foo ( ) { throw 1 ; }", parse(clang)); + } + + void defaultStmt() { + const char clang[] = "`-FunctionDecl 0x18476b8 <1.c:3:1, line:9:1> line:3:5 foo 'int (int)'\n" + " |-ParmVarDecl 0x18475e0 col:13 used rc 'int'\n" + " `-CompoundStmt 0x1847868 \n" + " `-SwitchStmt 0x18477e0 \n" + " |-ImplicitCastExpr 0x18477c8 'int' \n" + " | `-DeclRefExpr 0x18477a8 'int' lvalue ParmVar 0x18475e0 'rc' 'int'\n" + " `-CompoundStmt 0x1847850 \n" + " `-DefaultStmt 0x1847830 \n" + " `-ReturnStmt 0x1847820 \n" + " `-IntegerLiteral 0x1847800 'int' 1"; + ASSERT_EQUALS("int foo ( int rc@1 ) { switch ( rc@1 ) { default : return 1 ; } }", parse(clang)); + } + + void doStmt() { + const char clang[] = "`-FunctionDecl 0x27fbbc8 col:6 foo 'void ()'\n" + " `-CompoundStmt 0x27fbd08 \n" + " `-DoStmt 0x27fbce8 \n" + " |-CompoundStmt 0x27fbcb0 \n" + " | `-UnaryOperator 0x27fbc90 'int' postfix '++'\n" + " | `-DeclRefExpr 0x27fbc68 'int' lvalue Var 0x27fbae0 'x' 'int'\n" + " `-IntegerLiteral 0x27fbcc8 'int' 1"; + ASSERT_EQUALS("void foo ( ) { do { ++ x ; } while ( 1 ) ; }", parse(clang)); + } + + void enumDecl1() { + const char clang[] = "`-EnumDecl 0x2660660 col:6 referenced abc\n" + " |-EnumConstantDecl 0x2660720 col:11 referenced a 'abc'\n" + " |-EnumConstantDecl 0x2660768 col:13 b 'abc'\n" + " `-EnumConstantDecl 0x26607b0 col:15 c 'abc'"; + ASSERT_EQUALS("enum abc { a , b , c }", parse(clang)); + } + + void enumDecl2() { + const char clang[] = "`-EnumDecl 0xb55d50 <2.cpp:4:3, col:44> col:8 syntax_option_type 'unsigned int'"; + ASSERT_EQUALS("enum syntax_option_type : unsigned int { }", parse(clang)); + } + + void enumDecl3() { + const char clang[] = "|-EnumDecl 0x1586e48 <2.cpp:1:3, line:5:3> line:1:8 __syntax_option\n" + "| |-EnumConstantDecl 0x1586f18 col:5 referenced _S_polynomial '__syntax_option'\n" + "| `-EnumConstantDecl 0x1586f68 col:5 _S_syntax_last '__syntax_option'"; + ASSERT_EQUALS("enum __syntax_option { _S_polynomial , _S_syntax_last }", parse(clang)); + } + + void enumDecl4() { + const char clang[] = "|-EnumDecl 0xace1f8 col:1\n" + "| |-EnumConstantDecl 0xace2c8 col:7 A '(anonymous enum at e1.cpp:3:1)'\n" + "| |-EnumConstantDecl 0xace318 col:16 B '(anonymous enum at e1.cpp:3:1)'\n" + "| `-EnumConstantDecl 0xace3b8 col:46 referenced C '(anonymous enum at e1.cpp:3:1)'\n" + "`-VarDecl 0xace470 col:53 x 'enum (anonymous enum at e1.cpp:3:1)':'(anonymous enum at e1.cpp:3:1)' cinit\n" + " `-DeclRefExpr 0xace520 '(anonymous enum at e1.cpp:3:1)' EnumConstant 0xace3b8 'C' '(anonymous enum at e1.cpp:3:1)'"; + ASSERT_EQUALS("enum { A , B , C } x@1 = C ;", parse(clang)); + } + + void forStmt() { + const char clang[] = "`-FunctionDecl 0x2f93ae0 <1.c:1:1, col:56> col:5 main 'int ()'\n" + " `-CompoundStmt 0x2f93dc0 \n" + " |-ForStmt 0x2f93d50 \n" + " | |-DeclStmt 0x2f93c58 \n" + " | | `-VarDecl 0x2f93bd8 col:23 used i 'int' cinit\n" + " | | `-IntegerLiteral 0x2f93c38 'int' 0\n" + " | |-<<>>\n" + " | |-BinaryOperator 0x2f93cd0 'int' '<'\n" + " | | |-ImplicitCastExpr 0x2f93cb8 'int' \n" + " | | | `-DeclRefExpr 0x2f93c70 'int' lvalue Var 0x2f93bd8 'i' 'int'\n" + " | | `-IntegerLiteral 0x2f93c98 'int' 10\n" + " | |-UnaryOperator 0x2f93d20 'int' postfix '++'\n" + " | | `-DeclRefExpr 0x2f93cf8 'int' lvalue Var 0x2f93bd8 'i' 'int'\n" + " | `-CompoundStmt 0x2f93d40 \n" + " `-ReturnStmt 0x2f93da8 \n" + " `-IntegerLiteral 0x2f93d88 'int' 0"; + ASSERT_EQUALS("int main ( ) { for ( int i@1 = 0 ; i@1 < 10 ; ++ i@1 ) { } return 0 ; }", parse(clang)); + } + + void funcdecl1() { + const char clang[] = "`-FunctionDecl 0x3122c30 <1.c:1:1, col:22> col:6 foo 'void (int, int)'\n" + " |-ParmVarDecl 0x3122ae0 col:14 x 'int'\n" + " `-ParmVarDecl 0x3122b58 col:21 y 'int'"; + ASSERT_EQUALS("void foo ( int x@1 , int y@2 ) ;", parse(clang)); + } + + void funcdecl2() { + const char clang[] = "`-FunctionDecl 0x24b2c38 <1.c:1:1, line:4:1> line:1:5 foo 'int (int, int)'\n" + " |-ParmVarDecl 0x24b2ae0 col:13 used x 'int'\n" + " |-ParmVarDecl 0x24b2b58 col:20 used y 'int'\n" + " `-CompoundStmt 0x24b2de8 \n" + " `-ReturnStmt 0x24b2dd0 \n" + " `-BinaryOperator 0x24b2da8 'int' '/'\n" + " |-ImplicitCastExpr 0x24b2d78 'int' \n" + " | `-DeclRefExpr 0x24b2d28 'int' lvalue ParmVar 0x24b2ae0 'x' 'int'\n" + " `-ImplicitCastExpr 0x24b2d90 'int' \n" + " `-DeclRefExpr 0x24b2d50 'int' lvalue ParmVar 0x24b2b58 'y' 'int'"; + ASSERT_EQUALS("int foo ( int x@1 , int y@2 ) { return x@1 / y@2 ; }", parse(clang)); + } + + void funcdecl3() { + const char clang[] = "|-FunctionDecl 0x27cb6b8 col:12 __overflow 'int (FILE *, int)' extern\n" + "| |-ParmVarDecl 0x27cb528 col:30 'FILE *'\n" + "| `-ParmVarDecl 0x27cb5a0 col:35 'int'"; + ASSERT_EQUALS("int __overflow ( FILE * , int ) ;", parse(clang)); + } + + void funcdecl4() { + const char clang[] = "|-FunctionDecl 0x272bb60 col:15 implicit fwrite 'unsigned long (const void *, unsigned long, unsigned long, FILE *)' extern\n" + "| |-ParmVarDecl 0x272cc40 <> 'const void *'\n" + "| |-ParmVarDecl 0x272cca0 <> 'unsigned long'\n" + "| |-ParmVarDecl 0x272cd00 <> 'unsigned long'\n" + "| `-ParmVarDecl 0x272cd60 <> 'FILE *'"; + ASSERT_EQUALS("unsigned long fwrite ( const void * , unsigned long , unsigned long , FILE * ) ;", parse(clang)); + } + + void funcdecl5() { + const char clang[] = "`-FunctionDecl 0x59d670 <1.c:1:1, col:28> col:20 foo 'void (void)' static inline"; + ASSERT_EQUALS("static inline void foo ( ) ;", parse(clang)); + } + + void funcdecl6() { + const char clang[] = "`-FunctionDecl 0x196eea8 <1.cpp:3:5, col:27> col:12 foo 'void **(int)'\n" + " `-ParmVarDecl 0x196eda0 col:21 count 'int'"; + ASSERT_EQUALS("void * * foo ( int count@1 ) ;", parse(clang)); + } + + void functionTemplateDecl1() { + const char clang[] = "`-FunctionTemplateDecl 0x3242860 col:21 foo"; + ASSERT_EQUALS("", parse(clang)); + } + + void functionTemplateDecl2() { + const char clang[] = "|-FunctionTemplateDecl 0x333a860 col:21 foo\n" + "| |-TemplateTypeParmDecl 0x333a5f8 col:16 referenced class depth 0 index 0 T\n" + "| |-FunctionDecl 0x333a7c0 col:21 foo 'T (T)'\n" + "| | |-ParmVarDecl 0x333a6c0 col:27 referenced t 'T'\n" + "| | `-CompoundStmt 0x333a980 \n" + "| | `-ReturnStmt 0x333a968 \n" + "| | `-BinaryOperator 0x333a940 '' '+'\n" + "| | |-DeclRefExpr 0x333a8f8 'T' lvalue ParmVar 0x333a6c0 't' 'T'\n" + "| | `-IntegerLiteral 0x333a920 'int' 1\n" + "| `-FunctionDecl 0x333ae00 col:21 used foo 'int (int)'\n" + "| |-TemplateArgument type 'int'\n" + "| |-ParmVarDecl 0x333ad00 col:27 used t 'int':'int'\n" + "| `-CompoundStmt 0x333b0a8 \n" + "| `-ReturnStmt 0x333b090 \n" + "| `-BinaryOperator 0x333b068 'int' '+'\n" + "| |-ImplicitCastExpr 0x333b050 'int':'int' \n" + "| | `-DeclRefExpr 0x333b028 'int':'int' lvalue ParmVar 0x333ad00 't' 'int':'int'\n" + "| `-IntegerLiteral 0x333a920 'int' 1\n" + "`-FunctionDecl 0x333a9f8 col:1 invalid bar 'int ()'\n" + " `-CompoundStmt 0x333b010 \n" + " `-CallExpr 0x333afe0 'int':'int'\n" + " |-ImplicitCastExpr 0x333afc8 'int (*)(int)' \n" + " | `-DeclRefExpr 0x333af00 'int (int)' lvalue Function 0x333ae00 'foo' 'int (int)' (FunctionTemplate 0x333a860 'foo')\n" + " `-IntegerLiteral 0x333ab48 'int' 1"; + ASSERT_EQUALS("int foo ( int t@1 ) { return t@1 + 1 ; } int bar ( ) { foo ( 1 ) ; }", parse(clang)); + } + + void ifelse() { + const char clang[] = "`-FunctionDecl 0x2637ba8 <1.c:1:1, line:4:1> line:1:5 foo 'int (int)'\n" + " |-ParmVarDecl 0x2637ae0 col:13 used x 'int'\n" + " `-CompoundStmt 0x2637d70 \n" + " `-IfStmt 0x2637d38 \n" + " |-<<>>\n" + " |-<<>>\n" + " |-BinaryOperator 0x2637cf0 'int' '>'\n" + " | |-ImplicitCastExpr 0x2637cd8 'int' \n" + " | | `-DeclRefExpr 0x2637c90 'int' lvalue ParmVar 0x2637ae0 'x' 'int'\n" + " | `-IntegerLiteral 0x2637cb8 'int' 10\n" + " |-CompoundStmt 0x2637d18 \n" + " `-CompoundStmt 0x2637d28 "; + ASSERT_EQUALS("int foo ( int x@1 ) { if ( x@1 > 10 ) { } else { } }", parse(clang)); + } + + void ifStmt() { + // Clang 8 in cygwin + const char clang[] = "`-FunctionDecl 0x41d0690 <2.cpp:1:1, col:24> col:6 foo 'void ()'\n" + " `-CompoundStmt 0x41d07f0 \n" + " `-IfStmt 0x41d07b8 \n" + " |-ImplicitCastExpr 0x41d0790 'bool' \n" + " | `-IntegerLiteral 0x41d0770 'int' 1\n" + " |-CompoundStmt 0x41d07a8 \n"; + ASSERT_EQUALS("void foo ( ) { if ( 1 ) { } }", parse(clang)); + } + + void initListExpr() { + const char clang[] = "|-VarDecl 0x397c680 <1.cpp:2:1, col:26> col:11 used ints 'const int [3]' cinit\n" + "| `-InitListExpr 0x397c7d8 'const int [3]'\n" + "| |-IntegerLiteral 0x397c720 'int' 1\n" + "| |-IntegerLiteral 0x397c740 'int' 2\n" + "| `-IntegerLiteral 0x397c760 'int' 3"; + ASSERT_EQUALS("const int [3] ints@1 = { 1 , 2 , 3 } ;", parse(clang)); + } + + void labelStmt() { + const char clang[] = "`-FunctionDecl 0x2ed1ba0 <1.c:1:1, col:36> col:6 foo 'void (int)'\n" + " `-CompoundStmt 0x2ed1d00 \n" + " `-LabelStmt 0x2ed1ce8 'loop'\n" + " `-GotoStmt 0x2ed1cd0 'loop' 0x2ed1c88"; + ASSERT_EQUALS("void foo ( ) { loop : goto loop ; }", parse(clang)); + } + + void memberExpr() { + // C code: + // struct S { int x }; + // int foo(struct S s) { return s.x; } + const char clang[] = "|-RecordDecl 0x2441a88 <1.c:1:1, col:18> col:8 struct S definition\n" + "| `-FieldDecl 0x2441b48 col:16 referenced x 'int'\n" + "`-FunctionDecl 0x2441cf8 col:5 foo 'int (struct S)'\n" + " |-ParmVarDecl 0x2441be8 col:18 used s 'struct S':'struct S'\n" + " `-CompoundStmt 0x2441e70 \n" + " `-ReturnStmt 0x2441e58 \n" + " `-ImplicitCastExpr 0x2441e40 'int' \n" + " `-MemberExpr 0x2441e08 'int' lvalue .x 0x2441b48\n" + " `-DeclRefExpr 0x2441de0 'struct S':'struct S' lvalue ParmVar 0x2441be8 's' 'struct S':'struct S'"; + ASSERT_EQUALS("struct S { int x@1 ; } ; int foo ( struct S s@2 ) { return s@2 . x@1 ; }", + parse(clang)); + } + + void namespaceDecl1() { + const char clang[] = "`-NamespaceDecl 0x2e5f658 col:11 x\n" + " `-VarDecl 0x2e5f6d8 col:19 var 'int'"; + ASSERT_EQUALS("namespace x { int var@1 ; }", + parse(clang)); + } + + void namespaceDecl2() { + const char clang[] = "`-NamespaceDecl 0x1753e60 <1.cpp:1:1, line:4:1> line:1:11 std\n" + " |-VisibilityAttr 0x1753ed0 Default\n" + " `-VarDecl 0x1753f40 col:9 x 'int'"; + ASSERT_EQUALS("namespace std { int x@1 ; }", + parse(clang)); + } + + void recordDecl1() { + const char clang[] = "`-RecordDecl 0x354eac8 <1.c:1:1, line:4:1> line:1:8 struct S definition\n" + " |-FieldDecl 0x354eb88 col:7 x 'int'\n" + " `-FieldDecl 0x354ebe8 col:7 y 'int'"; + ASSERT_EQUALS("struct S { int x@1 ; int y@2 ; } ;", + parse(clang)); + } + + void recordDecl2() { + const char clang[] = "`-RecordDecl 0x3befac8 <2.c:2:1, col:22> col:1 struct definition\n" + " `-FieldDecl 0x3befbf0 col:14 val 'int'"; + ASSERT_EQUALS("struct { int val@1 ; } ;", + parse(clang)); + } + + void switchStmt() { + const char clang[] = "`-FunctionDecl 0x2796ba0 <1.c:1:1, col:35> col:6 foo 'void (int)'\n" + " |-ParmVarDecl 0x2796ae0 col:14 used x 'int'\n" + " `-CompoundStmt 0x2796d18 \n" + " |-SwitchStmt 0x2796cc8 \n" + " | |-<<>>\n" + " | |-<<>>\n" + " | |-ImplicitCastExpr 0x2796cb0 'int' \n" + " | | `-DeclRefExpr 0x2796c88 'int' lvalue ParmVar 0x2796ae0 'x' 'int'\n" + " | `-CompoundStmt 0x2796cf8 \n" + " `-NullStmt 0x2796d08 "; + ASSERT_EQUALS("void foo ( int x@1 ) { switch ( x@1 ) { } ; }", + parse(clang)); + } + + void typedefDecl1() { + const char clang[] = "|-TypedefDecl 0x2d60180 <> implicit __int128_t '__int128'\n" + "| `-BuiltinType 0x2d5fe80 '__int128'"; + ASSERT_EQUALS("typedef __int128 __int128_t ;", parse(clang)); + } + + void typedefDecl2() { + const char clang[] = "|-TypedefDecl 0x2d604a8 <> implicit __NSConstantString 'struct __NSConstantString_tag'\n" + "| `-RecordType 0x2d602c0 'struct __NSConstantString_tag'\n" + "| `-Record 0x2d60238 '__NSConstantString_tag'"; + ASSERT_EQUALS("typedef struct __NSConstantString_tag __NSConstantString ;", parse(clang)); + } + + void typedefDecl3() { + const char clang[] = "|-TypedefDecl 0x2d60540 <> implicit __builtin_ms_va_list 'char *'\n" + "| `-PointerType 0x2d60500 'char *'\n" + "| `-BuiltinType 0x2d5f980 'char'"; + ASSERT_EQUALS("typedef char * __builtin_ms_va_list ;", parse(clang)); + } + + void unaryExprOrTypeTraitExpr1() { + const char clang[] = "`-VarDecl 0x24cc610 col:5 x 'int' cinit\n" + " `-ImplicitCastExpr 0x24cc6e8 'int' \n" + " `-UnaryExprOrTypeTraitExpr 0x24cc6c8 'unsigned long' sizeof 'int'\n"; + ASSERT_EQUALS("int x@1 = sizeof ( int ) ;", parse(clang)); + } + + void unaryExprOrTypeTraitExpr2() { + const char clang[] = "`-VarDecl 0x27c6c00 col:9 x 'int' cinit\n" + " `-ImplicitCastExpr 0x27c6cc8 'int' \n" + " `-UnaryExprOrTypeTraitExpr 0x27c6ca8 'unsigned long' sizeof\n" + " `-ParenExpr 0x27c6c88 'char [10]' lvalue\n" + " `-DeclRefExpr 0x27c6c60 'char [10]' lvalue Var 0x27c6b48 'buf' 'char [10]'"; + ASSERT_EQUALS("int x@1 = sizeof ( buf ) ;", parse(clang)); + } + + void unaryOperator() { + const char clang[] = "`-FunctionDecl 0x2dd9748 <1.cpp:2:1, col:44> col:5 foo 'int (int *)'\n" + " |-ParmVarDecl 0x2dd9680 col:19 used p 'int *'\n" + " `-CompoundStmt 0x2dd9908 \n" + " `-ReturnStmt 0x2dd98f0 \n" + " `-BinaryOperator 0x2dd98c8 'int' '/'\n" + " |-IntegerLiteral 0x2dd9830 'int' 100000\n" + " `-ImplicitCastExpr 0x2dd98b0 'int' \n" + " `-UnaryOperator 0x2dd9890 'int' lvalue prefix '*' cannot overflow\n" + " `-ImplicitCastExpr 0x2dd9878 'int *' \n" + " `-DeclRefExpr 0x2dd9850 'int *' lvalue ParmVar 0x2dd9680 'p' 'int *'"; + ASSERT_EQUALS("int foo ( int * p@1 ) { return 100000 / * p@1 ; }", parse(clang)); + } + + void vardecl1() { + const char clang[] = "|-VarDecl 0x32b8aa0 <1.c:1:1, col:9> col:5 used a 'int' cinit\n" + "| `-IntegerLiteral 0x32b8b40 'int' 1\n" + "`-VarDecl 0x32b8b78 col:5 b 'int' cinit\n" + " `-ImplicitCastExpr 0x32b8c00 'int' \n" + " `-DeclRefExpr 0x32b8bd8 'int' lvalue Var 0x32b8aa0 'a' 'int'"; + + ASSERT_EQUALS("int a@1 = 1 ; int b@2 = a@1 ;", + parse(clang)); + } + + void vardecl2() { + const char clang[] = "|-VarDecl 0x3873b50 <1.c:1:1, col:9> col:5 used a 'int [10]'\n" + "`-FunctionDecl 0x3873c38 line:3:6 foo 'void ()'\n" + " `-CompoundStmt 0x3873dd0 \n" + " `-BinaryOperator 0x3873da8 'int' '='\n" + " |-ArraySubscriptExpr 0x3873d60 'int' lvalue\n" + " | |-ImplicitCastExpr 0x3873d48 'int *' \n" + " | | `-DeclRefExpr 0x3873cd8 'int [10]' lvalue Var 0x3873b50 'a' 'int [10]'\n" + " | `-IntegerLiteral 0x3873d00 'int' 0\n" + " `-IntegerLiteral 0x3873d88 'int' 0\n"; + + ASSERT_EQUALS("int [10] a@1 ; void foo ( ) { a@1 [ 0 ] = 0 ; }", + parse(clang)); + } + + void vardecl3() { + const char clang[] = "`-VarDecl 0x25a8aa0 <1.c:1:1, col:12> col:12 p 'const int *'"; + ASSERT_EQUALS("const int * p@1 ;", parse(clang)); + } + + void vardecl4() { + const char clang[] = "|-VarDecl 0x23d6c78 col:14 stdin 'FILE *' extern"; + ASSERT_EQUALS("FILE * stdin@1 ;", parse(clang)); + } + + void vardecl5() { + const char clang[] = "|-VarDecl 0x2e31fc0 col:26 sys_errlist 'const char *const []' extern"; + ASSERT_EQUALS("const char * const [] sys_errlist@1 ;", parse(clang)); + } + + void vardecl6() { + const char clang[] = "`-VarDecl 0x278e170 <1.c:1:1, col:16> col:12 x 'int' static cinit\n" + " `-IntegerLiteral 0x278e220 'int' 3"; + ASSERT_EQUALS("static int x@1 = 3 ;", parse(clang)); + } + + void vardecl7() { + const char clang[] = "`-VarDecl 0x2071f20 <1.cpp:2:1, col:23> col:9 start 'void *(*)(void *)'"; + ASSERT_EQUALS("void * * start@1 ;", parse(clang)); + } + + void whileStmt1() { + const char clang[] = "`-FunctionDecl 0x3d45b18 <1.c:1:1, line:3:1> line:1:6 foo 'void ()'\n" + " `-CompoundStmt 0x3d45c48 \n" + " `-WhileStmt 0x3d45c28 \n" + " |-<<>>\n" + " |-IntegerLiteral 0x3d45bf8 'int' 0\n" + " `-NullStmt 0x3d45c18 "; + ASSERT_EQUALS("void foo ( ) { while ( 0 ) { ; } }", + parse(clang)); + } + + void whileStmt2() { + // clang 9 + const char clang[] = "`-FunctionDecl 0x1c99ac8 <1.cpp:1:1, col:27> col:6 foo 'void ()'\n" + " `-CompoundStmt 0x1c99c10 \n" + " `-WhileStmt 0x1c99bf8 \n" + " |-ImplicitCastExpr 0x1c99bd0 'bool' \n" + " | `-IntegerLiteral 0x1c99bb0 'int' 1\n" + " `-CompoundStmt 0x1c99be8 "; + ASSERT_EQUALS("void foo ( ) { while ( 1 ) { } }", parse(clang)); + } + + +#define GET_SYMBOL_DB(AST) \ + const Settings settings = settingsBuilder().clang().platform(Platform::Type::Unix64).build(); \ + Tokenizer tokenizer(settings, *this); \ + { \ + std::istringstream istr(AST); \ + clangimport::parseClangAstDump(tokenizer, istr); \ + } \ + const SymbolDatabase *db = tokenizer.getSymbolDatabase(); \ + ASSERT(db) + + void tokenIndex() { + const char clang[] = "`-FunctionDecl 0x1e07dd0 <67.cpp:1:1, col:13> col:6 foo 'void ()'\n" + " `-CompoundStmt 0x1e07eb8 "; + ASSERT_EQUALS("void foo ( ) { }", parse(clang)); + + GET_SYMBOL_DB(clang); + const Token *tok = tokenizer.tokens(); + ASSERT_EQUALS(tok->index() + 1, tok->next()->index()); + } + + void symbolDatabaseEnum1() { + const char clang[] = "|-NamespaceDecl 0x29ad5f8 <1.cpp:1:1, line:3:1> line:1:11 ns\n" + "| `-EnumDecl 0x29ad660 col:6 referenced abc\n" + "| |-EnumConstantDecl 0x29ad720 col:11 a 'ns::abc'\n" + "| |-EnumConstantDecl 0x29ad768 col:13 b 'ns::abc'\n" + "| `-EnumConstantDecl 0x29ad7b0 col:15 referenced c 'ns::abc'\n" + "`-VarDecl 0x29ad898 col:9 x 'ns::abc':'ns::abc' cinit\n" + " `-DeclRefExpr 0x29ad998 'ns::abc' EnumConstant 0x29ad7b0 'c' 'ns::abc'\n"; + + ASSERT_EQUALS("namespace ns { enum abc { a , b , c } } ns :: abc x@1 = c ;", parse(clang)); + + GET_SYMBOL_DB(clang); + + // Enum scope and type + ASSERT_EQUALS(3, db->scopeList.size()); + const Scope &enumScope = db->scopeList.back(); + ASSERT_EQUALS(Scope::ScopeType::eEnum, enumScope.type); + ASSERT_EQUALS("abc", enumScope.className); + const Type *enumType = enumScope.definedType; + ASSERT_EQUALS("abc", enumType->name()); + + // Variable + const Token *vartok = Token::findsimplematch(tokenizer.tokens(), "x"); + ASSERT(vartok); + ASSERT(vartok->variable()); + ASSERT(vartok->variable()->valueType()); + ASSERT_EQUALS(uintptr_t(&enumScope), uintptr_t(vartok->variable()->valueType()->typeScope)); + } + + void symbolDatabaseFunction1() { + const char clang[] = "|-FunctionDecl 0x3aea7a0 <1.cpp:2:1, col:22> col:6 used foo 'void (int, int)'\n" + " |-ParmVarDecl 0x3aea650 col:14 x 'int'\n" + " |-ParmVarDecl 0x3aea6c8 col:21 y 'int'\n" + " `-CompoundStmt 0x3d45c48 \n"; + + GET_SYMBOL_DB(clang); + + // There is a function foo that has 2 arguments + ASSERT_EQUALS(1, db->functionScopes.size()); + const Scope *scope = db->functionScopes[0]; + const Function *func = scope->function; + ASSERT_EQUALS(2, func->argCount()); + ASSERT_EQUALS("x", func->getArgumentVar(0)->name()); + ASSERT_EQUALS("y", func->getArgumentVar(1)->name()); + ASSERT_EQUALS(ValueType::Type::INT, func->getArgumentVar(0)->valueType()->type); + ASSERT_EQUALS(ValueType::Type::INT, func->getArgumentVar(1)->valueType()->type); + } + + void symbolDatabaseFunction2() { + const char clang[] = "|-FunctionDecl 0x3aea7a0 <1.cpp:2:1, col:22> col:6 used foo 'void (int, int)'\n" + "| |-ParmVarDecl 0x3aea650 col:14 'int'\n" + "| |-ParmVarDecl 0x3aea6c8 col:21 'int'\n" + " `-CompoundStmt 0x3d45c48 \n"; + + GET_SYMBOL_DB(clang); + + // There is a function foo that has 2 arguments + ASSERT_EQUALS(1, db->functionScopes.size()); + const Scope *scope = db->functionScopes[0]; + const Function *func = scope->function; + ASSERT_EQUALS(2, func->argCount()); + ASSERT_EQUALS(0, (long long)func->getArgumentVar(0)->nameToken()); + ASSERT_EQUALS(0, (long long)func->getArgumentVar(1)->nameToken()); + } + + void symbolDatabaseFunction3() { // #9640 + const char clang[] = "`-FunctionDecl 0x238fcd8 <9640.cpp:1:1, col:26> col:6 used bar 'bool (const char, int &)'\n" + " |-ParmVarDecl 0x238fb10 col:20 'const char'\n" + " |-ParmVarDecl 0x238fbc0 col:26 'int &'\n" + " `-CompoundStmt 0x3d45c48 \n"; + + GET_SYMBOL_DB(clang); + + // There is a function foo that has 2 arguments + ASSERT_EQUALS(1, db->functionScopes.size()); + const Scope *scope = db->functionScopes[0]; + const Function *func = scope->function; + ASSERT_EQUALS(2, func->argCount()); + ASSERT_EQUALS(false, func->getArgumentVar(0)->isReference()); + ASSERT_EQUALS(true, func->getArgumentVar(1)->isReference()); + } + + void symbolDatabaseFunctionConst() { + const char clang[] = "`-CXXRecordDecl 0x7e2d98 <1.cpp:2:1, line:5:1> line:2:7 class foo definition\n" + " `-CXXMethodDecl 0x7e3000 col:8 f 'void () const'"; + + GET_SYMBOL_DB(clang); + + // There is a function f that is const + ASSERT_EQUALS(2, db->scopeList.size()); + ASSERT_EQUALS(1, db->scopeList.back().functionList.size()); + const Function &func = db->scopeList.back().functionList.back(); + ASSERT(func.isConst()); + } + + void symbolDatabaseVariableRef() { + const char clang[] = "`-FunctionDecl 0x1593df0 <3.cpp:1:1, line:4:1> line:1:6 foo 'void ()'\n" + " `-CompoundStmt 0x15940b0 \n" + " |-DeclStmt 0x1593f58 \n" + " | `-VarDecl 0x1593ef0 col:7 used x 'int'\n" + " `-DeclStmt 0x1594098 \n" + " `-VarDecl 0x1593fb8 col:8 ref 'int &' cinit\n" + " `-DeclRefExpr 0x1594020 'int' lvalue Var 0x1593ef0 'x' 'int'"; + GET_SYMBOL_DB(clang); + const Variable *refVar = db->variableList().back(); + ASSERT(refVar->isReference()); + } + + void symbolDatabaseVariableRRef() { + const char clang[] = "`-FunctionDecl 0x1a40df0 <3.cpp:1:1, line:4:1> line:1:6 foo 'void ()'\n" + " `-CompoundStmt 0x1a41180 \n" + " |-DeclStmt 0x1a40f58 \n" + " | `-VarDecl 0x1a40ef0 col:7 used x 'int'\n" + " `-DeclStmt 0x1a41168 \n" + " `-VarDecl 0x1a40fb8 col:9 ref 'int &&' cinit\n" + " `-ExprWithCleanups 0x1a410f8 'int' xvalue\n" + " `-MaterializeTemporaryExpr 0x1a41098 'int' xvalue extended by Var 0x1a40fb8 'ref' 'int &&'\n" + " `-BinaryOperator 0x1a41078 'int' '+'\n" + " |-ImplicitCastExpr 0x1a41060 'int' \n" + " | `-DeclRefExpr 0x1a41020 'int' lvalue Var 0x1a40ef0 'x' 'int'\n" + " `-IntegerLiteral 0x1a41040 'int' 1\n"; + + ASSERT_EQUALS("void foo ( ) { int x@1 ; int && ref@2 = x@1 + 1 ; }", parse(clang)); + + GET_SYMBOL_DB(clang); + const Variable *refVar = db->variableList().back(); + ASSERT(refVar->isReference()); + ASSERT(refVar->isRValueReference()); + } + + void symbolDatabaseVariablePointerRef() { + const char clang[] = "`-FunctionDecl 0x9b4f10 <3.cpp:1:1, col:17> col:6 used foo 'void (int *&)'\n" + " `-ParmVarDecl 0x9b4e40 col:16 p 'int *&'\n"; + + ASSERT_EQUALS("void foo ( int * & p@1 ) ;", parse(clang)); + + GET_SYMBOL_DB(clang); + const Variable *p = db->variableList().back(); + ASSERT(p->isPointer()); + ASSERT(p->isReference()); + } + + void symbolDatabaseNodeType1() { + const char clang[] = "`-FunctionDecl 0x32438c0 line:5:6 foo 'a::b (a::b)'\n" + " |-ParmVarDecl 0x32437b0 col:15 used i 'a::b':'long'\n" + " `-CompoundStmt 0x3243a60 \n" + " `-ReturnStmt 0x3243a48 \n" + " `-BinaryOperator 0x3243a20 'long' '+'\n" + " |-ImplicitCastExpr 0x32439f0 'a::b':'long' \n" + " | `-DeclRefExpr 0x32439a8 'a::b':'long' lvalue ParmVar 0x32437b0 'i' 'a::b':'long'\n" + " `-ImplicitCastExpr 0x3243a08 'long' \n" + " `-IntegerLiteral 0x32439d0 'int' 1\n"; + + GET_SYMBOL_DB(clang); + + const Token *tok = Token::findsimplematch(tokenizer.tokens(), "i + 1"); + ASSERT(!!tok); + ASSERT(!!tok->valueType()); + ASSERT_EQUALS("signed long", tok->valueType()->str()); + } + + void symbolDatabaseForVariable() { + const char clang[] = "`-FunctionDecl 0x38f8bb0 <10100.c:2:1, line:4:1> line:2:6 f 'void (void)'\n" + " `-CompoundStmt 0x38f8d88 \n" + " `-ForStmt 0x38f8d50 \n" + " |-DeclStmt 0x38f8d28 \n" + " | `-VarDecl 0x38f8ca8 col:14 i 'int' cinit\n" + " | `-IntegerLiteral 0x38f8d08 'int' 0\n" + " |-<<>>\n" + " |-<<>>\n" + " |-<<>>\n" + " `-CompoundStmt 0x38f8d40 "; + + ASSERT_EQUALS("void f ( ) { for ( int i@1 = 0 ; ; ) { } }", parse(clang)); + + GET_SYMBOL_DB(clang); + + const Token *tok = Token::findsimplematch(tokenizer.tokens(), "i"); + ASSERT(!!tok); + ASSERT(!!tok->variable()); + ASSERT_EQUALS(Scope::ScopeType::eFor, tok->variable()->scope()->type); + } + + void valueFlow1() { + // struct S { int x; int buf[10]; } ; int sz = sizeof(struct S); + const char clang[] = "|-RecordDecl 0x2fc5a88 <1.c:1:1, line:4:1> line:1:8 struct S definition\n" + "| |-FieldDecl 0x2fc5b48 col:7 x 'int'\n" + "| `-FieldDecl 0x2fc5c10 col:7 buf 'int [10]'\n" + "`-VarDecl 0x2fc5c70 col:5 sz 'int' cinit\n" + " `-ImplicitCastExpr 0x2fc5d88 'int' \n" + " `-UnaryExprOrTypeTraitExpr 0x2fc5d68 'unsigned long' sizeof 'struct S':'struct S'"; + GET_SYMBOL_DB(clang); + + const Token *tok = Token::findsimplematch(tokenizer.tokens(), "sizeof ("); + ASSERT(!!tok); + tok = tok->next(); + ASSERT(tok->hasKnownIntValue()); + ASSERT_EQUALS(44, tok->getKnownIntValue()); + } + + void valueFlow2() { + // int buf[42]; + // int x = sizeof(buf); + const char clang[] = "|-VarDecl 0x10f6de8 <66.cpp:3:1, col:11> col:5 referenced buf 'int [42]'\n" + "`-VarDecl 0x10f6eb0 col:5 x 'int' cinit\n" + " `-ImplicitCastExpr 0x10f6f78 'int' \n" + " `-UnaryExprOrTypeTraitExpr 0x10f6f58 'unsigned long' sizeof\n" + " `-ParenExpr 0x10f6f38 'int [42]' lvalue\n" + " `-DeclRefExpr 0x10f6f18 'int [42]' lvalue Var 0x10f6de8 'buf' 'int [42]' non_odr_use_unevaluated"; + + GET_SYMBOL_DB(clang); + + const Token *tok = Token::findsimplematch(tokenizer.tokens(), "sizeof ("); + ASSERT(!!tok); + tok = tok->next(); + TODO_ASSERT_EQUALS(true, false, tok->hasKnownIntValue() && tok->getKnownIntValue() == 10); + } + + void valueType1() { + const char clang[] = "`-FunctionDecl 0x32438c0 line:5:6 foo 'a::b (a::b)'\n" + " |-ParmVarDecl 0x32437b0 col:15 used i 'a::b':'long'\n" + " `-CompoundStmt 0x3243a60 \n" + " `-ReturnStmt 0x3243a48 \n" + " `-ImplicitCastExpr 0x2176ca8 'int' \n" + " `-ImplicitCastExpr 0x2176c90 'bool' \n" + " `-DeclRefExpr 0x2176c60 'bool' lvalue Var 0x2176bd0 'e' 'bool'\n"; + + GET_SYMBOL_DB(clang); + + const Token *tok = Token::findsimplematch(tokenizer.tokens(), "e"); + ASSERT(!!tok); + ASSERT(!!tok->valueType()); + ASSERT_EQUALS("bool", tok->valueType()->str()); + } + + void valueType2() { + const char clang[] = "`-VarDecl 0xc9eda0 <1.cpp:2:1, col:17> col:13 s 'const char *' cinit\n" + " `-ImplicitCastExpr 0xc9eef0 'const char *' \n" + " `-StringLiteral 0xc9eed0 'const char [6]' lvalue \"hello\"\n"; + + GET_SYMBOL_DB(clang); + + const Token *tok = Token::findsimplematch(tokenizer.tokens(), "\"hello\""); + ASSERT(!!tok); + ASSERT(!!tok->valueType()); + ASSERT_EQUALS("const signed char *", tok->valueType()->str()); + } + + void crash() { + const char* clang = "TranslationUnitDecl 0x56037914f998 <> \n" + "|-TypedefDecl 0x560379150200 <> implicit __int128_t '__int128'\n" + "| `-BuiltinType 0x56037914ff60 '__int128'\n" + "|-TypedefDecl 0x560379150270 <> implicit __uint128_t 'unsigned __int128'\n" + "| `-BuiltinType 0x56037914ff80 'unsigned __int128'\n" + "|-TypedefDecl 0x5603791505e8 <> implicit __NSConstantString '__NSConstantString_tag'\n" + "| `-RecordType 0x560379150360 '__NSConstantString_tag'\n" + "| `-CXXRecord 0x5603791502c8 '__NSConstantString_tag'\n" + "|-TypedefDecl 0x560379150680 <> implicit __builtin_ms_va_list 'char *'\n" + "| `-PointerType 0x560379150640 'char *'\n" + "| `-BuiltinType 0x56037914fa40 'char'\n" + "|-TypedefDecl 0x5603791968f8 <> implicit __builtin_va_list '__va_list_tag[1]'\n" + "| `-ConstantArrayType 0x5603791968a0 '__va_list_tag[1]' 1 \n" + "| `-RecordType 0x560379150770 '__va_list_tag'\n" + "| `-CXXRecord 0x5603791506d8 '__va_list_tag'\n" + "|-ClassTemplateDecl 0x560379196b58 col:37 A\n" + "| |-TemplateTypeParmDecl 0x560379196950 col:19 typename depth 0 index 0\n" + "| |-TemplateTypeParmDecl 0x5603791969f8 col:29 typename depth 0 index 1\n" + "| `-CXXRecordDecl 0x560379196ac8 col:37 class A definition\n" + "| |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init\n" + "| | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr\n" + "| | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param\n" + "| | |-MoveConstructor exists simple trivial needs_implicit\n" + "| | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param\n" + "| | |-MoveAssignment exists simple trivial needs_implicit\n" + "| | `-Destructor simple irrelevant trivial needs_implicit\n" + "| |-CXXRecordDecl 0x560379196de0 col:37 implicit referenced class A\n" + "| `-CXXRecordDecl 0x560379196e70 col:47 class b\n" + "|-CXXRecordDecl 0x560379197110 parent 0x560379196ac8 prev 0x560379196e70 col:50 class b definition\n" + "| |-DefinitionData empty standard_layout trivially_copyable has_user_declared_ctor can_const_default_init\n" + "| | |-DefaultConstructor defaulted_is_constexpr\n" + "| | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param\n" + "| | |-MoveConstructor exists simple trivial needs_implicit\n" + "| | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param\n" + "| | |-MoveAssignment exists simple trivial needs_implicit\n" + "| | `-Destructor simple irrelevant trivial needs_implicit\n" + "| |-CXXRecordDecl 0x560379197250 col:50 implicit referenced class b\n" + "| `-CXXConstructorDecl 0x5603791974b8 col:54 b 'void (A &)'\n" + "| `-ParmVarDecl 0x560379197380 col:59 a 'A &'\n" + "`-CXXConstructorDecl 0x5603791b5600 parent 0x560379197110 prev 0x5603791974b8 col:47 b 'void (A &)'\n" + " |-ParmVarDecl 0x5603791b5570 col:52 'A &'\n" + " `-CompoundStmt 0x5603791b5700 \n"; + parse(clang); // don't crash + } +}; + +REGISTER_TEST(TestClangImport) diff --git a/cppcheck-2.14.0/test/testclass.cpp b/cppcheck-2.14.0/test/testclass.cpp new file mode 100644 index 00000000..6f281658 --- /dev/null +++ b/cppcheck-2.14.0/test/testclass.cpp @@ -0,0 +1,8970 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "check.h" +#include "checkclass.h" +#include "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "settings.h" +#include "tokenize.h" +#include "tokenlist.h" + +#include +#include +#include +#include + +class TestClass : public TestFixture { +public: + TestClass() : TestFixture("TestClass") {} + +private: + const Settings settings0 = settingsBuilder().severity(Severity::style).library("std.cfg").build(); + const Settings settings1 = settingsBuilder().severity(Severity::warning).library("std.cfg").build(); + const Settings settings2 = settingsBuilder().severity(Severity::style).library("std.cfg").certainty(Certainty::inconclusive).build(); + const Settings settings3 = settingsBuilder().severity(Severity::style).library("std.cfg").severity(Severity::warning).build(); + + void run() override { + TEST_CASE(virtualDestructor1); // Base class not found => no error + TEST_CASE(virtualDestructor2); // Base class doesn't have a destructor + TEST_CASE(virtualDestructor3); // Base class has a destructor, but it's not virtual + TEST_CASE(virtualDestructor4); // Derived class doesn't have a destructor => no error + TEST_CASE(virtualDestructor5); // Derived class has empty destructor => no error + TEST_CASE(virtualDestructor6); // only report error if base class pointer that points at derived class is deleted + TEST_CASE(virtualDestructorProtected); + TEST_CASE(virtualDestructorInherited); + TEST_CASE(virtualDestructorTemplate); + + TEST_CASE(virtualDestructorInconclusive); // ticket # 5807 + + TEST_CASE(copyConstructor1); + TEST_CASE(copyConstructor2); // ticket #4458 + TEST_CASE(copyConstructor3); // defaulted/deleted + TEST_CASE(copyConstructor4); // base class with private constructor + TEST_CASE(copyConstructor5); // multiple inheritance + TEST_CASE(copyConstructor6); // array of pointers + TEST_CASE(noOperatorEq); // class with memory management should have operator eq + TEST_CASE(noDestructor); // class with memory management should have destructor + + TEST_CASE(operatorEqRetRefThis1); + TEST_CASE(operatorEqRetRefThis2); // ticket #1323 + TEST_CASE(operatorEqRetRefThis3); // ticket #1405 + TEST_CASE(operatorEqRetRefThis4); // ticket #1451 + TEST_CASE(operatorEqRetRefThis5); // ticket #1550 + TEST_CASE(operatorEqRetRefThis6); // ticket #2479 + TEST_CASE(operatorEqRetRefThis7); // ticket #5782 endless recursion + TEST_CASE(operatorEqToSelf1); // single class + TEST_CASE(operatorEqToSelf2); // nested class + TEST_CASE(operatorEqToSelf3); // multiple inheritance + TEST_CASE(operatorEqToSelf4); // nested class with multiple inheritance + TEST_CASE(operatorEqToSelf5); // ticket # 1233 + TEST_CASE(operatorEqToSelf6); // ticket # 1550 + TEST_CASE(operatorEqToSelf7); + TEST_CASE(operatorEqToSelf8); // ticket #2179 + TEST_CASE(operatorEqToSelf9); // ticket #2592 + + TEST_CASE(memsetOnStruct); + TEST_CASE(memsetVector); + TEST_CASE(memsetOnClass); + TEST_CASE(memsetOnInvalid); // Ticket #5425: Crash upon invalid + TEST_CASE(memsetOnStdPodType); // Ticket #5901 - std::uint8_t + TEST_CASE(memsetOnFloat); // Ticket #5421 + TEST_CASE(memsetOnUnknown); // Ticket #7183 + TEST_CASE(mallocOnClass); + + TEST_CASE(this_subtraction); // warn about "this-x" + + // can member function be made const + TEST_CASE(const1); + TEST_CASE(const2); + TEST_CASE(const3); + TEST_CASE(const4); + TEST_CASE(const5); // ticket #1482 + TEST_CASE(const6); // ticket #1491 + TEST_CASE(const7); + TEST_CASE(const8); // ticket #1517 + TEST_CASE(const9); // ticket #1515 + TEST_CASE(const10); // ticket #1522 + TEST_CASE(const11); // ticket #1529 + TEST_CASE(const12); // ticket #1552 + TEST_CASE(const13); // ticket #1519 + TEST_CASE(const14); + TEST_CASE(const15); + TEST_CASE(const16); // ticket #1551 + TEST_CASE(const17); // ticket #1552 + TEST_CASE(const18); + TEST_CASE(const19); // ticket #1612 + TEST_CASE(const20); // ticket #1602 + TEST_CASE(const21); // ticket #1683 + TEST_CASE(const22); + TEST_CASE(const23); // ticket #1699 + TEST_CASE(const24); // ticket #1708 + TEST_CASE(const25); // ticket #1724 + TEST_CASE(const26); // ticket #1847 + TEST_CASE(const27); // ticket #1882 + TEST_CASE(const28); // ticket #1883 + TEST_CASE(const29); // ticket #1922 + TEST_CASE(const30); + TEST_CASE(const31); + TEST_CASE(const32); // ticket #1905 - member array is assigned + TEST_CASE(const33); + TEST_CASE(const34); // ticket #1964 + TEST_CASE(const35); // ticket #2001 + TEST_CASE(const36); // ticket #2003 + TEST_CASE(const37); // ticket #2081 and #2085 + TEST_CASE(const38); // ticket #2135 + TEST_CASE(const39); + TEST_CASE(const40); // ticket #2228 + TEST_CASE(const41); // ticket #2255 + TEST_CASE(const42); // ticket #2282 + TEST_CASE(const43); // ticket #2377 + TEST_CASE(const44); // ticket #2595 + TEST_CASE(const45); // ticket #2664 + TEST_CASE(const46); // ticket #2636 + TEST_CASE(const47); // ticket #2670 + TEST_CASE(const48); // ticket #2672 + TEST_CASE(const49); // ticket #2795 + TEST_CASE(const50); // ticket #2943 + TEST_CASE(const51); // ticket #3040 + TEST_CASE(const52); // ticket #3048 + TEST_CASE(const53); // ticket #3049 + TEST_CASE(const54); // ticket #3052 + TEST_CASE(const55); + TEST_CASE(const56); // ticket #3149 + TEST_CASE(const57); // tickets #2669 and #2477 + TEST_CASE(const58); // ticket #2698 + TEST_CASE(const59); // ticket #4646 + TEST_CASE(const60); // ticket #3322 + TEST_CASE(const61); // ticket #5606 + TEST_CASE(const62); // ticket #5701 + TEST_CASE(const63); // ticket #5983 + TEST_CASE(const64); // ticket #6268 + TEST_CASE(const65); // ticket #8693 + TEST_CASE(const66); // ticket #7714 + TEST_CASE(const67); // ticket #9193 + TEST_CASE(const68); // ticket #6471 + TEST_CASE(const69); // ticket #9806 + TEST_CASE(const70); // variadic template can receive more arguments than in its definition + TEST_CASE(const71); // ticket #10146 + TEST_CASE(const72); // ticket #10520 + TEST_CASE(const73); // ticket #10735 + TEST_CASE(const74); // ticket #10671 + TEST_CASE(const75); // ticket #10065 + TEST_CASE(const76); // ticket #10825 + TEST_CASE(const77); // ticket #10307, #10311 + TEST_CASE(const78); // ticket #10315 + TEST_CASE(const79); // ticket #9861 + TEST_CASE(const80); // ticket #11328 + TEST_CASE(const81); // ticket #11330 + TEST_CASE(const82); // ticket #11513 + TEST_CASE(const83); + TEST_CASE(const84); + TEST_CASE(const85); + TEST_CASE(const86); + TEST_CASE(const87); + TEST_CASE(const88); + TEST_CASE(const89); + TEST_CASE(const90); + TEST_CASE(const91); + TEST_CASE(const92); + TEST_CASE(const93); + + TEST_CASE(const_handleDefaultParameters); + TEST_CASE(const_passThisToMemberOfOtherClass); + TEST_CASE(assigningPointerToPointerIsNotAConstOperation); + TEST_CASE(assigningArrayElementIsNotAConstOperation); + TEST_CASE(constoperator1); // operator< can often be const + TEST_CASE(constoperator2); // operator<< + TEST_CASE(constoperator3); + TEST_CASE(constoperator4); + TEST_CASE(constoperator5); // ticket #3252 + TEST_CASE(constoperator6); // ticket #8669 + TEST_CASE(constincdec); // increment/decrement => non-const + TEST_CASE(constassign1); + TEST_CASE(constassign2); + TEST_CASE(constincdecarray); // increment/decrement array element => non-const + TEST_CASE(constassignarray); + TEST_CASE(constReturnReference); + TEST_CASE(constDelete); // delete member variable => not const + TEST_CASE(constLPVOID); // a function that returns LPVOID can't be const + TEST_CASE(constFunc); // a function that calls const functions can be const + TEST_CASE(constVirtualFunc); + TEST_CASE(constIfCfg); // ticket #1881 - fp when there are #if + TEST_CASE(constFriend); // ticket #1921 - fp for friend function + TEST_CASE(constUnion); // ticket #2111 - fp when there is a union + TEST_CASE(constArrayOperator); // #4406 + TEST_CASE(constRangeBasedFor); // #5514 + TEST_CASE(const_shared_ptr); + TEST_CASE(constPtrToConstPtr); + TEST_CASE(constTrailingReturnType); + TEST_CASE(staticArrayPtrOverload); + TEST_CASE(qualifiedNameMember); // #10872 + + TEST_CASE(initializerListOrder); + TEST_CASE(initializerListArgument); + TEST_CASE(initializerListUsage); + TEST_CASE(selfInitialization); + + TEST_CASE(virtualFunctionCallInConstructor); + TEST_CASE(pureVirtualFunctionCall); + TEST_CASE(pureVirtualFunctionCallOtherClass); + TEST_CASE(pureVirtualFunctionCallWithBody); + TEST_CASE(pureVirtualFunctionCallPrevented); + + TEST_CASE(duplInheritedMembers); + TEST_CASE(explicitConstructors); + TEST_CASE(copyCtorAndEqOperator); + + TEST_CASE(override1); + TEST_CASE(overrideCVRefQualifiers); + + TEST_CASE(uselessOverride); + + TEST_CASE(thisUseAfterFree); + + TEST_CASE(unsafeClassRefMember); + + TEST_CASE(ctuOneDefinitionRule); + + TEST_CASE(testGetFileInfo); + + TEST_CASE(returnByReference); + } + +#define checkCopyCtorAndEqOperator(code) checkCopyCtorAndEqOperator_(code, __FILE__, __LINE__) + void checkCopyCtorAndEqOperator_(const char code[], const char* file, int line) { + const Settings settings = settingsBuilder().severity(Severity::warning).build(); + + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check.. + CheckClass checkClass(&tokenizer, &settings, this); + (checkClass.checkCopyCtorAndEqOperator)(); + } + + void copyCtorAndEqOperator() { + checkCopyCtorAndEqOperator("class A\n" + "{\n" + " A(const A& other) { }\n" + " A& operator=(const A& other) { return *this; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + + checkCopyCtorAndEqOperator("class A\n" + "{\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkCopyCtorAndEqOperator("class A\n" + "{\n" + " A(const A& other) { }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkCopyCtorAndEqOperator("class A\n" + "{\n" + " A& operator=(const A& other) { return *this; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + + checkCopyCtorAndEqOperator("class A\n" + "{\n" + " A(const A& other) { }\n" + " int x;\n" + "};"); + TODO_ASSERT_EQUALS("[test.cpp:1]: (warning) The class 'A' has 'copy constructor' but lack of 'operator='.\n", "", errout_str()); + // TODO the error message should be clarified. It should say something like 'copy constructor is empty and will not assign i and therefore the behaviour is different to the default assignment operator' + + checkCopyCtorAndEqOperator("class A\n" + "{\n" + " A& operator=(const A& other) { return *this; }\n" + " int x;\n" + "};"); + TODO_ASSERT_EQUALS("[test.cpp:1]: (warning) The class 'A' has 'operator=' but lack of 'copy constructor'.\n", "", errout_str()); + // TODO the error message should be clarified. It should say something like 'assignment operator does not assign i and therefore the behaviour is different to the default copy constructor' + + checkCopyCtorAndEqOperator("class A\n" + "{\n" + " A& operator=(const int &x) { this->x = x; return *this; }\n" + " int x;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkCopyCtorAndEqOperator("class A {\n" + "public:\n" + " A() : x(0) { }\n" + " A(const A & a) { x = a.x; }\n" + " A & operator = (const A & a) {\n" + " x = a.x;\n" + " return *this;\n" + " }\n" + "private:\n" + " int x;\n" + "};\n" + "class B : public A {\n" + "public:\n" + " B() { }\n" + " B(const B & b) :A(b) { }\n" + "private:\n" + " static int i;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // #7987 - Don't show warning when there is a move constructor + checkCopyCtorAndEqOperator("struct S {\n" + " std::string test;\n" + " S(S&& s) : test(std::move(s.test)) { }\n" + " S& operator = (S &&s) {\n" + " test = std::move(s.test);\n" + " return *this;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // #8337 - False positive in copy constructor detection + checkCopyCtorAndEqOperator("struct StaticListNode {\n" + " StaticListNode(StaticListNode*& prev) : m_next(0) {}\n" + " StaticListNode* m_next;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + +#define checkExplicitConstructors(code) checkExplicitConstructors_(code, __FILE__, __LINE__) + void checkExplicitConstructors_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings0, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check.. + CheckClass checkClass(&tokenizer, &settings0, this); + (checkClass.checkExplicitConstructors)(); + } + + void explicitConstructors() { + checkExplicitConstructors("class Class {\n" + " Class() = delete;\n" + " Class(const Class& other) { }\n" + " Class(Class&& other) { }\n" + " explicit Class(int i) { }\n" + " explicit Class(const std::string&) { }\n" + " Class(int a, int b) { }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkExplicitConstructors("class Class {\n" + " Class() = delete;\n" + " explicit Class(const Class& other) { }\n" + " explicit Class(Class&& other) { }\n" + " virtual int i() = 0;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkExplicitConstructors("class Class {\n" + " Class() = delete;\n" + " Class(const Class& other) = delete;\n" + " Class(Class&& other) = delete;\n" + " virtual int i() = 0;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkExplicitConstructors("class Class {\n" + " Class(int i) { }\n" + "};"); + ASSERT_EQUALS("[test.cpp:2]: (style) Class 'Class' has a constructor with 1 argument that is not explicit.\n", errout_str()); + + checkExplicitConstructors("class Class {\n" + " Class(const Class& other) { }\n" + " virtual int i() = 0;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkExplicitConstructors("class Class {\n" + " Class(Class&& other) { }\n" + " virtual int i() = 0;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // #6585 + checkExplicitConstructors("class Class {\n" + " private: Class(const Class&);\n" + " virtual int i() = 0;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkExplicitConstructors("class Class {\n" + " public: Class(const Class&);\n" + " virtual int i() = 0;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // #7465: Error properly reported in templates + checkExplicitConstructors("template struct Test {\n" + " Test(int) : fData(0) {}\n" + " T fData;\n" + "};\n" + "int main() {\n" + " Test test;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Struct 'Test < int >' has a constructor with 1 argument that is not explicit.\n", errout_str()); + + // #7465: No error for copy or move constructors + checkExplicitConstructors("template struct Test {\n" + " Test() : fData(0) {}\n" + " Test (const Test& aOther) : fData(aOther.fData) {}\n" + " Test (Test&& aOther) : fData(std::move(aOther.fData)) {}\n" + " T fData;\n" + "};\n" + "int main() {\n" + " Test test;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #8600 + checkExplicitConstructors("struct A { struct B; };\n" + "struct A::B {\n" + " B() = default;\n" + " B(const B&) {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkExplicitConstructors("struct A{" + " A(int, int y=2) {}" + "};"); + ASSERT_EQUALS("[test.cpp:1]: (style) Struct 'A' has a constructor with 1 argument that is not explicit.\n", errout_str()); + + checkExplicitConstructors("struct Foo {\n" // #10515 + " template \n" + " explicit constexpr Foo(T) {}\n" + "};\n" + "struct Bar {\n" + " template \n" + " constexpr explicit Bar(T) {}\n" + "};\n" + "struct Baz {\n" + " explicit constexpr Baz(int) {}\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkExplicitConstructors("class Token;\n" // #11126 + "struct Branch {\n" + " Branch(Token* tok = nullptr) : endBlock(tok) {}\n" + " Token* endBlock = nullptr;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Struct 'Branch' has a constructor with 1 argument that is not explicit.\n", errout_str()); + + checkExplicitConstructors("struct S {\n" + " S(std::initializer_list il) : v(il) {}\n" + " std::vector v;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkExplicitConstructors("template\n" // #10977 + "struct A {\n" + " template\n" + " A(Ts&&... ts) {}\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkExplicitConstructors("class Color {\n" // #7176 + "public:\n" + " Color(unsigned int rgba);\n" + " Color(std::uint8_t r = 0, std::uint8_t g = 0, std::uint8_t b = 0, std::uint8_t a = 255);\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Class 'Color' has a constructor with 1 argument that is not explicit.\n" + "[test.cpp:4]: (style) Class 'Color' has a constructor with 1 argument that is not explicit.\n", + errout_str()); + } + +#define checkDuplInheritedMembers(code) checkDuplInheritedMembers_(code, __FILE__, __LINE__) + void checkDuplInheritedMembers_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings1, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check.. + CheckClass checkClass(&tokenizer, &settings1, this); + (checkClass.checkDuplInheritedMembers)(); + } + + void duplInheritedMembers() { + checkDuplInheritedMembers("class Base {\n" + " int x;\n" + "};\n" + "struct Derived : Base {\n" + " int x;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkDuplInheritedMembers("class Base {\n" + " protected:\n" + " int x;\n" + "};\n" + "struct Derived : Base {\n" + " int x;\n" + "};"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (warning) The struct 'Derived' defines member variable with name 'x' also defined in its parent class 'Base'.\n", errout_str()); + + checkDuplInheritedMembers("class Base {\n" + " protected:\n" + " int x;\n" + "};\n" + "struct Derived : public Base {\n" + " int x;\n" + "};"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (warning) The struct 'Derived' defines member variable with name 'x' also defined in its parent class 'Base'.\n", errout_str()); + + checkDuplInheritedMembers("class Base0 {\n" + " int x;\n" + "};\n" + "class Base1 {\n" + " int x;\n" + "};\n" + "struct Derived : Base0, Base1 {\n" + " int x;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkDuplInheritedMembers("class Base0 {\n" + " protected:\n" + " int x;\n" + "};\n" + "class Base1 {\n" + " int x;\n" + "};\n" + "struct Derived : Base0, Base1 {\n" + " int x;\n" + "};"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:9]: (warning) The struct 'Derived' defines member variable with name 'x' also defined in its parent class 'Base0'.\n", errout_str()); + + checkDuplInheritedMembers("class Base0 {\n" + " protected:\n" + " int x;\n" + "};\n" + "class Base1 {\n" + " public:\n" + " int x;\n" + "};\n" + "struct Derived : Base0, Base1 {\n" + " int x;\n" + "};"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:10]: (warning) The struct 'Derived' defines member variable with name 'x' also defined in its parent class 'Base0'.\n" + "[test.cpp:7] -> [test.cpp:10]: (warning) The struct 'Derived' defines member variable with name 'x' also defined in its parent class 'Base1'.\n", errout_str()); + + checkDuplInheritedMembers("class Base {\n" + " int x;\n" + "};\n" + "struct Derived : Base {\n" + " int y;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkDuplInheritedMembers("class A {\n" + " int x;\n" + "};\n" + "struct B {\n" + " int x;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // Unknown 'Base' class + checkDuplInheritedMembers("class Derived : public UnknownBase {\n" + " int x;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkDuplInheritedMembers("class Base {\n" + " int x;\n" + "};\n" + "class Derived : public Base {\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // #6692 + checkDuplInheritedMembers("namespace test1 {\n" + " struct SWibble{};\n" + " typedef SWibble wibble;\n" + "}\n" + "namespace test2 {\n" + " struct SWibble : public test1::wibble {\n" + " int Value;\n" + " };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9957 + checkDuplInheritedMembers("class Base {\n" + " public:\n" + " int i;\n" + "};\n" + "class Derived1: public Base {\n" + " public:\n" + " int j;\n" + "};\n" + "class Derived2 : public Derived1 {\n" + " int i;\n" + "};"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:10]: (warning) The class 'Derived2' defines member variable with name 'i' also defined in its parent class 'Base'.\n", errout_str()); + + // don't crash on recursive template + checkDuplInheritedMembers("template\n" + "struct BitInt : public BitInt { };"); + ASSERT_EQUALS("", errout_str()); + + // don't crash on recursive template + checkDuplInheritedMembers("namespace _impl {\n" + " template \n" + " struct fn_traits;\n" + "}\n" + "template \n" + "struct function_traits\n" + " : public _impl::fn_traits> {};\n" + "namespace _impl {\n" + " template \n" + " struct fn_traits\n" + " : public fn_traits {};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #10594 + checkDuplInheritedMembers("template struct A { bool a = true; };\n" + "struct B { bool a; };\n" + "template<> struct A<1> : B {};\n"); + ASSERT_EQUALS("", errout_str()); + + checkDuplInheritedMembers("struct B {\n" + " int g() const;\n" + " virtual int f() const { return g(); }\n" + "};\n" + "struct D : B {\n" + " int g() const;\n" + " int f() const override { return g(); }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:6]: (warning) The struct 'D' defines member function with name 'g' also defined in its parent struct 'B'.\n", + errout_str()); + + checkDuplInheritedMembers("struct B {\n" + " int g() const;\n" + "};\n" + "struct D : B {\n" + " int g(int) const;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkDuplInheritedMembers("struct S {\n" + " struct T {\n" + " T() {}\n" + " };\n" + "};\n" + "struct T : S::T {\n" + " T() : S::T() {}\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkDuplInheritedMembers("struct S {};\n" // #11827 + "struct SPtr {\n" + " virtual S* operator->() const { return p; }\n" + " S* p = nullptr;\n" + "};\n" + "struct T : public S {};\n" + "struct TPtr : public SPtr {\n" + " T* operator->() const { return (T*)p; }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkDuplInheritedMembers("struct B { virtual int& get() = 0; };\n" // #12311 + "struct D : B {\n" + " int i{};\n" + " int& get() override { return i; }\n" + " const int& get() const { return i; }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkDuplInheritedMembers("class Base {\n" // #12353 + " public:\n" + " void One();\n" + " void Two();\n" + "};\n" + "class Derived : public Base {\n" + "public:\n" + " void Two() = delete;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + +#define checkCopyConstructor(code) checkCopyConstructor_(code, __FILE__, __LINE__) + void checkCopyConstructor_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings3, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check.. + CheckClass checkClass(&tokenizer, &settings3, this); + checkClass.copyconstructors(); + } + + void copyConstructor1() { + checkCopyConstructor("class F\n" + "{\n" + " public:\n" + " char *c,*p,*d;\n" + " F(const F &f) : p(f.p), c(f.c)\n" + " {\n" + " p=(char *)malloc(strlen(f.p)+1);\n" + " strcpy(p,f.p);\n" + " }\n" + " F(char *str)\n" + " {\n" + " p=(char *)malloc(strlen(str)+1);\n" + " strcpy(p,str);\n" + " }\n" + " F&operator=(const F&);\n" + " ~F();\n" + "};"); + TODO_ASSERT_EQUALS("[test.cpp:5]: (warning) Value of pointer 'p', which points to allocated memory, is copied in copy constructor instead of allocating new memory.\n", "", errout_str()); + + checkCopyConstructor("class F {\n" + " char *p;\n" + " F(const F &f) {\n" + " p = f.p;\n" + " }\n" + " F(char *str) {\n" + " p = malloc(strlen(str)+1);\n" + " }\n" + " ~F();\n" + " F& operator=(const F&f);\n" + "};"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) Value of pointer 'p', which points to allocated memory, is copied in copy constructor instead of allocating new memory.\n" + "[test.cpp:3] -> [test.cpp:7]: (warning) Copy constructor does not allocate memory for member 'p' although memory has been allocated in other constructors.\n", + "[test.cpp:4]: (warning) Value of pointer 'p', which points to allocated memory, is copied in copy constructor instead of allocating new memory.\n" + , errout_str()); + + checkCopyConstructor("class F\n" + "{\n" + " public:\n" + " char *c,*p,*d;\n" + " F(const F &f) :p(f.p)\n" + " {\n" + " }\n" + " F(char *str)\n" + " {\n" + " p=(char *)malloc(strlen(str)+1);\n" + " strcpy(p,str);\n" + " }\n" + " ~F();\n" + " F& operator=(const F&f);\n" + "};"); + TODO_ASSERT_EQUALS("[test.cpp:5]: (warning) Value of pointer 'p', which points to allocated memory, is copied in copy constructor instead of allocating new memory.\n" + "[test.cpp:5] -> [test.cpp:10]: (warning) Copy constructor does not allocate memory for member 'p' although memory has been allocated in other constructors.\n", + "" + , errout_str()); + + checkCopyConstructor("class kalci\n" + "{\n" + " public:\n" + " char *c,*p,*d;\n" + " kalci()\n" + " {\n" + " p=(char *)malloc(100);\n" + " strcpy(p,\"hello\");\n" + " c=(char *)malloc(100);\n" + " strcpy(p,\"hello\");\n" + " d=(char *)malloc(100);\n" + " strcpy(p,\"hello\");\n" + " }\n" + " kalci(const kalci &f)\n" + " {\n" + " p=(char *)malloc(strlen(str)+1);\n" + " strcpy(p,f.p);\n" + " c=(char *)malloc(strlen(str)+1);\n" + " strcpy(p,f.p);\n" + " d=(char *)malloc(strlen(str)+1);\n" + " strcpy(p,f.p);\n" + " }\n" + " ~kalci();\n" + " kalci& operator=(const kalci&kalci);\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkCopyConstructor("class F\n" + "{\n" + " public:\n" + " char *c,*p,*d;\n" + " F(char *str,char *st,char *string)\n" + " {\n" + " p=(char *)malloc(100);\n" + " strcpy(p,str);\n" + " c=(char *)malloc(100);\n" + " strcpy(p,st);\n" + " d=(char *)malloc(100);\n" + " strcpy(p,string);\n" + " }\n" + " F(const F &f)\n" + " {\n" + " p=(char *)malloc(strlen(str)+1);\n" + " strcpy(p,f.p);\n" + " c=(char *)malloc(strlen(str)+1);\n" + " strcpy(p,f.p);\n" + " }\n" + " ~F();\n" + " F& operator=(const F&f);\n" + "};"); + TODO_ASSERT_EQUALS("[test.cpp:14] -> [test.cpp:11]: (warning) Copy constructor does not allocate memory for member 'd' although memory has been allocated in other constructors.\n", "", errout_str()); + + checkCopyConstructor("class F {\n" + " char *c;\n" + " F(char *str,char *st,char *string) {\n" + " p=(char *)malloc(100);\n" + " }\n" + " F(const F &f)\n" + " : p(malloc(size))\n" + " {\n" + " }\n" + " ~F();\n" + " F& operator=(const F&f);\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkCopyConstructor("class F {\n" + " char *c;\n" + " F(char *str,char *st,char *string)\n" + " : p(malloc(size))\n" + " {\n" + " }\n" + " F(const F &f)\n" + " {\n" + " }\n" + " ~F();\n" + " F& operator=(const F&f);\n" + "};"); + TODO_ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:4]: (warning) Copy constructor does not allocate memory for member 'd' although memory has been allocated in other constructors.\n", "", errout_str()); + + checkCopyConstructor("class F\n" + "{\n" + " public:\n" + " char *c,*p,*d;\n" + " F()\n" + " {\n" + " p=(char *)malloc(100);\n" + " c=(char *)malloc(100);\n" + " d=(char*)malloc(100);\n" + " }\n" + " ~F();\n" + " F& operator=(const F&f);\n" + "};"); + TODO_ASSERT_EQUALS("[test.cpp:8]: (warning) Class 'F' does not have a copy constructor which is recommended since it has dynamic memory/resource allocation(s).\n", "", errout_str()); + + checkCopyConstructor("class F\n" + "{\n" + " public:\n" + " char *c;\n" + " const char *p,*d;\n" + " F(char *str,char *st,char *string)\n" + " {\n" + " p=str;\n" + " d=st;\n" + " c=(char *)malloc(strlen(string)+1);\n" + " strcpy(d,string);\n" + " }\n" + " F(const F &f)\n" + " {\n" + " p=f.p;\n" + " d=f.d;\n" + " c=(char *)malloc(strlen(str)+1);\n" + " strcpy(d,f.p);\n" + " }\n" + " ~F();\n" + " F& operator=(const F&f);\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkCopyConstructor("class F : E\n" + "{\n" + " char *p;\n" + " F() {\n" + " p = malloc(100);\n" + " }\n" + " ~F();\n" + " F& operator=(const F&f);\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkCopyConstructor("class E { E(E&); };\n" // non-copyable + "class F : E\n" + "{\n" + " char *p;\n" + " F() {\n" + " p = malloc(100);\n" + " }\n" + " ~F();\n" + " F& operator=(const F&f);\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkCopyConstructor("class E {};\n" + "class F : E {\n" + " char *p;\n" + " F() {\n" + " p = malloc(100);\n" + " }\n" + " ~F();\n" + " F& operator=(const F&f);\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Class 'F' does not have a copy constructor which is recommended since it has dynamic memory/resource allocation(s).\n", errout_str()); + + checkCopyConstructor("class F {\n" + " char *p;\n" + " F() {\n" + " p = malloc(100);\n" + " }\n" + " F(F& f);\n" + " ~F();\n" + " F& operator=(const F&f);\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkCopyConstructor("class F {\n" + " char *p;\n" + " F() : p(malloc(100)) {}\n" + " ~F();\n" + " F& operator=(const F&f);\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Class 'F' does not have a copy constructor which is recommended since it has dynamic memory/resource allocation(s).\n", errout_str()); + + // #7198 + checkCopyConstructor("struct F {\n" + " static char* c;\n" + " F() {\n" + " p = malloc(100);\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void copyConstructor2() { // ticket #4458 + checkCopyConstructor("template \n" + "class Vector\n" + "{\n" + "public:\n" + " Vector() {\n" + " _M_finish = new _Tp[ 42 ];\n" + " }\n" + " Vector( const Vector<_Tp>& v ) {\n" + " }\n" + " ~Vector();\n" + " Vector& operator=(const Vector&v);\n" + " _Tp* _M_finish;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void copyConstructor3() { + checkCopyConstructor("struct F {\n" + " char* c;\n" + " F() { c = malloc(100); }\n" + " F(const F &f) = delete;\n" + " F&operator=(const F &f);\n" + " ~F();\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkCopyConstructor("struct F {\n" + " char* c;\n" + " F() { c = malloc(100); }\n" + " F(const F &f) = default;\n" + " F&operator=(const F &f);\n" + " ~F();\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' has dynamic memory/resource allocation(s). The copy constructor is explicitly defaulted but the default copy constructor does not work well. It is recommended to define or delete the copy constructor.\n", errout_str()); + } + + void copyConstructor4() { + checkCopyConstructor("class noncopyable {\n" + "protected:\n" + " noncopyable() {}\n" + " ~noncopyable() {}\n" + "\n" + "private:\n" + " noncopyable( const noncopyable& );\n" + " const noncopyable& operator=( const noncopyable& );\n" + "};\n" + "\n" + "class Base : private noncopyable {};\n" + "\n" + "class Foo : public Base {\n" + "public:\n" + " Foo() : m_ptr(new int) {}\n" + " ~Foo() { delete m_ptr; }\n" + "private:\n" + " int* m_ptr;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void copyConstructor5() { + checkCopyConstructor("class Copyable {};\n" + "\n" + "class Foo : public Copyable, public UnknownType {\n" + "public:\n" + " Foo() : m_ptr(new int) {}\n" + " ~Foo() { delete m_ptr; }\n" + "private:\n" + " int* m_ptr;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkCopyConstructor("class Copyable {};\n" + "\n" + "class Foo : public UnknownType, public Copyable {\n" + "public:\n" + " Foo() : m_ptr(new int) {}\n" + " ~Foo() { delete m_ptr; }\n" + "private:\n" + " int* m_ptr;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void copyConstructor6() { + checkCopyConstructor("struct S {\n" + " S() {\n" + " for (int i = 0; i < 5; i++)\n" + " a[i] = new char[3];\n" + " }\n" + " char* a[5];\n" + "};\n"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) Struct 'S' does not have a copy constructor which is recommended since it has dynamic memory/resource allocation(s).\n" + "[test.cpp:4]: (warning) Struct 'S' does not have a operator= which is recommended since it has dynamic memory/resource allocation(s).\n" + "[test.cpp:4]: (warning) Struct 'S' does not have a destructor which is recommended since it has dynamic memory/resource allocation(s).\n", + "", + errout_str()); + } + + void noOperatorEq() { + checkCopyConstructor("struct F {\n" + " char* c;\n" + " F() { c = malloc(100); }\n" + " F(const F &f);\n" + " ~F();\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' does not have a operator= which is recommended since it has dynamic memory/resource allocation(s).\n", errout_str()); + + // defaulted operator= + checkCopyConstructor("struct F {\n" + " char* c;\n" + " F() { c = malloc(100); }\n" + " F(const F &f);\n" + " F &operator=(const F &f) = default;\n" + " ~F();\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' has dynamic memory/resource allocation(s). The operator= is explicitly defaulted but the default operator= does not work well. It is recommended to define or delete the operator=.\n", errout_str()); + + // deleted operator= + checkCopyConstructor("struct F {\n" + " char* c;\n" + " F() { c = malloc(100); }\n" + " F(const F &f);\n" + " F &operator=(const F &f) = delete;\n" + " ~F();\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // base class deletes operator= + checkCopyConstructor("struct F : NonCopyable {\n" + " char* c;\n" + " F() { c = malloc(100); }\n" + " F(const F &f);\n" + " ~F();\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void noDestructor() { + checkCopyConstructor("struct F {\n" + " char* c;\n" + " F() { c = malloc(100); }\n" + " F(const F &f);\n" + " F&operator=(const F&);" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' does not have a destructor which is recommended since it has dynamic memory/resource allocation(s).\n", errout_str()); + + checkCopyConstructor("struct F {\n" + " C* c;\n" + " F() { c = new C; }\n" + " F(const F &f);\n" + " F&operator=(const F&);" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkCopyConstructor("struct F {\n" + " int* i;\n" + " F() { i = new int(); }\n" + " F(const F &f);\n" + " F& operator=(const F&);" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' does not have a destructor which is recommended since it has dynamic memory/resource allocation(s).\n", errout_str()); + + checkCopyConstructor("struct Data { int x; int y; };\n" + "struct F {\n" + " Data* c;\n" + " F() { c = new Data; }\n" + " F(const F &f);\n" + " F&operator=(const F&);" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Struct 'F' does not have a destructor which is recommended since it has dynamic memory/resource allocation(s).\n", errout_str()); + + // defaulted destructor + checkCopyConstructor("struct F {\n" + " char* c;\n" + " F() { c = malloc(100); }\n" + " F(const F &f);\n" + " F &operator=(const F &f);\n" + " ~F() = default;\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Struct 'F' has dynamic memory/resource allocation(s). The destructor is explicitly defaulted but the default destructor does not work well. It is recommended to define the destructor.\n", errout_str()); + + // deleted destructor + checkCopyConstructor("struct F {\n" + " char* c;\n" + " F() { c = malloc(100); }\n" + " F(const F &f);\n" + " F &operator=(const F &f);\n" + " ~F() = delete;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + // Check that operator Equal returns reference to this +#define checkOpertorEqRetRefThis(code) checkOpertorEqRetRefThis_(code, __FILE__, __LINE__) + void checkOpertorEqRetRefThis_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings0, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check.. + CheckClass checkClass(&tokenizer, &settings0, this); + checkClass.operatorEqRetRefThis(); + } + + void operatorEqRetRefThis1() { + checkOpertorEqRetRefThis( + "class A\n" + "{\n" + "public:\n" + " A & operator=(const A &a) { return *this; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkOpertorEqRetRefThis( + "class A\n" + "{\n" + "public:\n" + " A & operator=(const A &a) { return a; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style) 'operator=' should return reference to 'this' instance.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A\n" + "{\n" + "public:\n" + " A & operator=(const A &);\n" + "};\n" + "A & A::operator=(const A &a) { return *this; }"); + ASSERT_EQUALS("", errout_str()); + + checkOpertorEqRetRefThis( + "class A\n" + "{\n" + "public:\n" + " A & operator=(const A &a);\n" + "};\n" + "A & A::operator=(const A &a) { return *this; }"); + ASSERT_EQUALS("", errout_str()); + + checkOpertorEqRetRefThis( + "class A\n" + "{\n" + "public:\n" + " A & operator=(const A &);\n" + "};\n" + "A & A::operator=(const A &a) { return a; }"); + ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A\n" + "{\n" + "public:\n" + " A & operator=(const A &a);\n" + "};\n" + "A & A::operator=(const A &a) { return a; }"); + ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A\n" + "{\n" + "public:\n" + " class B\n" + " {\n" + " public:\n" + " B & operator=(const B &b) { return *this; }\n" + " };\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkOpertorEqRetRefThis( + "class A\n" + "{\n" + "public:\n" + " class B\n" + " {\n" + " public:\n" + " B & operator=(const B &b) { return b; }\n" + " };\n" + "};"); + ASSERT_EQUALS("[test.cpp:7]: (style) 'operator=' should return reference to 'this' instance.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A\n" + "{\n" + "public:\n" + " class B\n" + " {\n" + " public:\n" + " B & operator=(const B &);\n" + " };\n" + "};\n" + "A::B & A::B::operator=(const A::B &b) { return *this; }"); + ASSERT_EQUALS("", errout_str()); + + checkOpertorEqRetRefThis( + "class A\n" + "{\n" + "public:\n" + " class B\n" + " {\n" + " public:\n" + " B & operator=(const B &);\n" + " };\n" + "};\n" + "A::B & A::B::operator=(const A::B &b) { return b; }"); + ASSERT_EQUALS("[test.cpp:10]: (style) 'operator=' should return reference to 'this' instance.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + " class B;\n" + "};\n" + "class A::B\n" + "{\n" + " B & operator=(const B & b) { return b; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + " class B;\n" + "};\n" + "class A::B\n" + "{\n" + " B & operator=(const B &);\n" + "};\n" + "A::B & A::B::operator=(const A::B & b) { return b; }"); + ASSERT_EQUALS("[test.cpp:8]: (style) 'operator=' should return reference to 'this' instance.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + " class B;\n" + "};\n" + "class A::B\n" + "{\n" + " A::B & operator=(const A::B & b) { return b; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + " class B;\n" + "};\n" + "class A::B\n" + "{\n" + " A::B & operator=(const A::B &);\n" + "};\n" + "A::B & A::B::operator=(const A::B & b) { return b; }"); + ASSERT_EQUALS("[test.cpp:8]: (style) 'operator=' should return reference to 'this' instance.\n", errout_str()); + + checkOpertorEqRetRefThis( + "namespace A {\n" + " class B;\n" + "}\n" + "class A::B\n" + "{\n" + " B & operator=(const B & b) { return b; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout_str()); + + checkOpertorEqRetRefThis( + "namespace A {\n" + " class B;\n" + "}\n" + "class A::B\n" + "{\n" + " B & operator=(const B &);\n" + "};\n" + "A::B & A::B::operator=(const A::B & b) { return b; }"); + ASSERT_EQUALS("[test.cpp:8]: (style) 'operator=' should return reference to 'this' instance.\n", errout_str()); + + checkOpertorEqRetRefThis( + "namespace A {\n" + " class B;\n" + "}\n" + "class A::B\n" + "{\n" + " A::B & operator=(const A::B & b) { return b; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (style) 'operator=' should return reference to 'this' instance.\n", errout_str()); + + checkOpertorEqRetRefThis( + "namespace A {\n" + " class B;\n" + "}\n" + "class A::B\n" + "{\n" + " A::B & operator=(const A::B &);\n" + "};\n" + "A::B & A::B::operator=(const A::B & b) { return b; }"); + ASSERT_EQUALS("[test.cpp:8]: (style) 'operator=' should return reference to 'this' instance.\n", errout_str()); + + checkOpertorEqRetRefThis( // #11380 + "struct S {\n" + " S& operator=(const S& other) {\n" + " i = []() { return 42; }();\n" + " return *this;\n" + " }\n" + " int i;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void operatorEqRetRefThis2() { + // ticket # 1323 + checkOpertorEqRetRefThis( + "class szp\n" + "{\n" + " szp &operator =(int *other) {}\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class szp\n" + "{\n" + " szp &operator =(int *other);\n" + "};\n" + "szp &szp::operator =(int *other) {}"); + ASSERT_EQUALS("[test.cpp:5]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout_str()); + + checkOpertorEqRetRefThis( + "namespace NS {\n" + " class szp;\n" + "}\n" + "class NS::szp\n" + "{\n" + " szp &operator =(int *other) {}\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout_str()); + + checkOpertorEqRetRefThis( + "namespace NS {\n" + " class szp;\n" + "}\n" + "class NS::szp\n" + "{\n" + " szp &operator =(int *other);\n" + "};\n" + "NS::szp &NS::szp::operator =(int *other) {}"); + ASSERT_EQUALS("[test.cpp:8]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout_str()); + + checkOpertorEqRetRefThis( + "namespace NS {\n" + " class szp;\n" + "}\n" + "class NS::szp\n" + "{\n" + " NS::szp &operator =(int *other) {}\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout_str()); + + checkOpertorEqRetRefThis( + "namespace NS {\n" + " class szp;\n" + "}\n" + "class NS::szp\n" + "{\n" + " NS::szp &operator =(int *other);\n" + "};\n" + "NS::szp &NS::szp::operator =(int *other) {}"); + ASSERT_EQUALS("[test.cpp:8]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + " class szp;\n" + "};\n" + "class A::szp\n" + "{\n" + " szp &operator =(int *other) {}\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + " class szp;\n" + "};\n" + "class A::szp\n" + "{\n" + " szp &operator =(int *other);\n" + "};\n" + "A::szp &A::szp::operator =(int *other) {}"); + ASSERT_EQUALS("[test.cpp:8]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + " class szp;\n" + "};\n" + "class A::szp\n" + "{\n" + " A::szp &operator =(int *other) {}\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + " class szp;\n" + "};\n" + "class A::szp\n" + "{\n" + " A::szp &operator =(int *other);\n" + "};\n" + "A::szp &A::szp::operator =(int *other) {}"); + ASSERT_EQUALS("[test.cpp:8]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout_str()); + } + + void operatorEqRetRefThis3() { + // ticket # 1405 + checkOpertorEqRetRefThis( + "class A {\n" + "public:\n" + " inline A &operator =(int *other) { return (*this); };\n" + " inline A &operator =(long *other) { return (*this = 0); };\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + "public:\n" + " A &operator =(int *other);\n" + " A &operator =(long *other);\n" + "};\n" + "A &A::operator =(int *other) { return (*this); };\n" + "A &A::operator =(long *other) { return (*this = 0); };"); + ASSERT_EQUALS("", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + "public:\n" + " inline A &operator =(int *other) { return (*this); };\n" + " inline A &operator =(long *other) { return operator = (*(int *)other); };\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + "public:\n" + " A &operator =(int *other);\n" + " A &operator =(long *other);\n" + "};\n" + "A &A::operator =(int *other) { return (*this); };\n" + "A &A::operator =(long *other) { return operator = (*(int *)other); };"); + ASSERT_EQUALS("", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + "public:\n" + " A &operator =(int *other);\n" + " A &operator =(long *other);\n" + "};\n" + "A &A::operator =(int *other) { return (*this); };\n" + "A &A::operator =(long *other) { return this->operator = (*(int *)other); };"); + ASSERT_EQUALS("", errout_str()); + + checkOpertorEqRetRefThis( // #9045 + "class V {\n" + "public:\n" + " V& operator=(const V& r) {\n" + " if (this == &r) {\n" + " return ( *this );\n" + " }\n" + " return *this;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void operatorEqRetRefThis4() { + // ticket # 1451 + checkOpertorEqRetRefThis( + "P& P::operator = (const P& pc)\n" + "{\n" + " return (P&)(*this += pc);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void operatorEqRetRefThis5() { + // ticket # 1550 + checkOpertorEqRetRefThis( + "class A {\n" + "public:\n" + " A & operator=(const A &a) { }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + "protected:\n" + " A & operator=(const A &a) {}\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should return reference to 'this' instance.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + "private:\n" + " A & operator=(const A &a) {}\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should return reference to 'this' instance.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + "public:\n" + " A & operator=(const A &a) {\n" + " rand();\n" + " throw std::exception();\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should either return reference to 'this' instance or be declared private and left unimplemented.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + "public:\n" + " A & operator=(const A &a) {\n" + " rand();\n" + " abort();\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style) 'operator=' should either return reference to 'this' instance or be declared private and left unimplemented.\n", errout_str()); + + checkOpertorEqRetRefThis( + "class A {\n" + "public:\n" + " A & operator=(const A &a);\n" + "};\n" + "A & A :: operator=(const A &a) { }"); + ASSERT_EQUALS("[test.cpp:5]: (error) No 'return' statement in non-void function causes undefined behavior.\n", errout_str()); + } + + void operatorEqRetRefThis6() { // ticket #2478 (segmentation fault) + checkOpertorEqRetRefThis( + "class UString {\n" + "public:\n" + " UString& assign( const char* c_str );\n" + " UString& operator=( const UString& s );\n" + "};\n" + "UString& UString::assign( const char* c_str ) {\n" + " std::string tmp( c_str );\n" + " return assign( tmp );\n" + "}\n" + "UString& UString::operator=( const UString& s ) {\n" + " return assign( s );\n" + "}"); + } + + void operatorEqRetRefThis7() { // ticket #5782 Endless recursion in CheckClass::checkReturnPtrThis() + checkOpertorEqRetRefThis( + "class basic_fbstring {\n" + " basic_fbstring& operator=(int il) {\n" + " return assign();\n" + " }\n" + " basic_fbstring& assign() {\n" + " return replace();\n" + " }\n" + " basic_fbstring& replaceImplDiscr() {\n" + " return replace();\n" + " }\n" + " basic_fbstring& replace() {\n" + " return replaceImplDiscr();\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + // Check that operator Equal checks for assignment to self +#define checkOpertorEqToSelf(code) checkOpertorEqToSelf_(code, __FILE__, __LINE__) + void checkOpertorEqToSelf_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings1, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check.. + CheckClass checkClass(&tokenizer, &settings1, this); + checkClass.operatorEqToSelf(); + } + + void operatorEqToSelf1() { + // this test has an assignment test but it is not needed + checkOpertorEqToSelf( + "class A\n" + "{\n" + "public:\n" + " A & operator=(const A &a) { if (&a != this) { } return *this; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // this test doesn't have an assignment test but it is not needed + checkOpertorEqToSelf( + "class A\n" + "{\n" + "public:\n" + " A & operator=(const A &a) { return *this; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // this test needs an assignment test and has it + checkOpertorEqToSelf( + "class A\n" + "{\n" + "public:\n" + " char *s;\n" + " A & operator=(const A &a)\n" + " {\n" + " if (&a != this)\n" + " {\n" + " free(s);\n" + " s = strdup(a.s);\n" + " }\n" + " return *this;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // this class needs an assignment test but doesn't have it + checkOpertorEqToSelf( + "class A\n" + "{\n" + "public:\n" + " char *s;\n" + " A & operator=(const A &a)\n" + " {\n" + " free(s);\n" + " s = strdup(a.s);\n" + " return *this;\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout_str()); + + // this test has an assignment test but doesn't need it + checkOpertorEqToSelf( + "class A\n" + "{\n" + "public:\n" + " A & operator=(const A &);\n" + "};\n" + "A & A::operator=(const A &a) { if (&a != this) { } return *this; }"); + ASSERT_EQUALS("", errout_str()); + + // this test doesn't have an assignment test but doesn't need it + checkOpertorEqToSelf( + "class A\n" + "{\n" + "public:\n" + " A & operator=(const A &);\n" + "};\n" + "A & A::operator=(const A &a) { return *this; }"); + ASSERT_EQUALS("", errout_str()); + + // this test needs an assignment test and has it + checkOpertorEqToSelf( + "class A\n" + "{\n" + "public:\n" + " char *s;\n" + " A & operator=(const A &);\n" + "};\n" + "A & A::operator=(const A &a)\n" + "{\n" + " if (&a != this)\n" + " {\n" + " free(s);\n" + " s = strdup(a.s);\n" + " }\n" + " return *this;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // this test needs an assignment test and has the inverse test + 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("[test.cpp:7]: (warning) 'operator=' should check for assignment to self to avoid problems with dynamic memory.\n", errout_str()); + + // this test needs an assignment test and has the inverse test + 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) == true)\n" + " {\n" + " free(s);\n" + " s = strdup(a.s);\n" + " }\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()); + + // this test needs an assignment test and has the inverse test + 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) != false)\n" + " {\n" + " free(s);\n" + " s = strdup(a.s);\n" + " }\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()); + + // this test needs an assignment test and has the inverse test + 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) == false))\n" + " {\n" + " free(s);\n" + " s = strdup(a.s);\n" + " }\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()); + + // this test needs an assignment test and has the inverse test + 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) == false)\n" + " {\n" + " free(s);\n" + " s = strdup(a.s);\n" + " }\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()); + + // this test needs an assignment test and has the inverse test + 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" + " }\n" + " else\n" + " {\n" + " free(s);\n" + " s = strdup(a.s);\n" + " }\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()); + + // this test needs an assignment test and has the inverse test + 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" + " free(s);\n" + " else\n" + " {\n" + " free(s);\n" + " s = strdup(a.s);\n" + " }\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()); + + + // 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 +#define checkVirtualDestructor(...) checkVirtualDestructor_(__FILE__, __LINE__, __VA_ARGS__) + void checkVirtualDestructor_(const char* file, int line, const char code[], bool inconclusive = false) { + const Settings s = settingsBuilder(settings0).certainty(Certainty::inconclusive, inconclusive).severity(Severity::warning).build(); + + // Tokenize.. + SimpleTokenizer tokenizer(s, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check.. + CheckClass checkClass(&tokenizer, &s, 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()); + + // #9104 + checkVirtualDestructor("struct A\n" + "{\n" + " A() { cout << \"A is constructing\\n\"; }\n" + " ~A() { cout << \"A is destructing\\n\"; }\n" + "};\n" + " \n" + "struct Base {};\n" + " \n" + "struct Derived : Base\n" + "{\n" + " A a;\n" + "};\n" + " \n" + "int main(void)\n" + "{\n" + " Base* p = new Derived();\n" + " delete p;\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout_str()); + + checkVirtualDestructor("using namespace std;\n" + "struct A\n" + "{\n" + " A() { cout << \"A is constructing\\n\"; }\n" + " ~A() { cout << \"A is destructing\\n\"; }\n" + "};\n" + " \n" + "struct Base {};\n" + " \n" + "struct Derived : Base\n" + "{\n" + " A a;\n" + "};\n" + " \n" + "int main(void)\n" + "{\n" + " Base* p = new Derived();\n" + " delete p;\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout_str()); + } + + void virtualDestructor3() { + // Base class has a destructor, but it's not virtual + + checkVirtualDestructor("class Base { public: ~Base(); };\n" + "class Derived : public Base { public: ~Derived() { (void)11; } };" + "Base *base = new Derived;\n" + "delete base;"); + ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout_str()); + + checkVirtualDestructor("class Base { public: ~Base(); };\n" + "class Derived : protected Base { public: ~Derived() { (void)11; } };" + "Base *base = new Derived;\n" + "delete base;"); + ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout_str()); + + checkVirtualDestructor("class Base { public: ~Base(); };\n" + "class Derived : private Fred, public Base { public: ~Derived() { (void)11; } };" + "Base *base = new Derived;\n" + "delete base;"); + ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout_str()); + } + + void virtualDestructor4() { + // Derived class doesn't have a destructor => undefined behaviour according to paragraph 3 in [expr.delete] + + checkVirtualDestructor("class Base { public: ~Base(); };\n" + "class Derived : public Base { };" + "Base *base = new Derived;\n" + "delete base;"); + ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout_str()); + + checkVirtualDestructor("class Base { public: ~Base(); };\n" + "class Derived : private Fred, public Base { };" + "Base *base = new Derived;\n" + "delete base;"); + ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout_str()); + } + + void virtualDestructor5() { + // Derived class has empty destructor => undefined behaviour according to paragraph 3 in [expr.delete] + + checkVirtualDestructor("class Base { public: ~Base(); };\n" + "class Derived : public Base { public: ~Derived() {} };" + "Base *base = new Derived;\n" + "delete base;"); + ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout_str()); + + checkVirtualDestructor("class Base { public: ~Base(); };\n" + "class Derived : public Base { public: ~Derived(); }; Derived::~Derived() {}" + "Base *base = new Derived;\n" + "delete base;"); + ASSERT_EQUALS("[test.cpp:1]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout_str()); + } + + void virtualDestructor6() { + // Only report error if base class pointer is deleted that + // points at derived class + + checkVirtualDestructor("class Base { public: ~Base(); };\n" + "class Derived : public Base { public: ~Derived() { (void)11; } };"); + ASSERT_EQUALS("", errout_str()); + } + + void virtualDestructorProtected() { + // Base class has protected destructor, it makes Base *p = new Derived(); fail + // during compilation time, so error is not possible. => no error + checkVirtualDestructor("class A\n" + "{\n" + "protected:\n" + " ~A() { }\n" + "};\n" + "\n" + "class B : public A\n" + "{\n" + "public:\n" + " ~B() { int a; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void virtualDestructorInherited() { + // class A inherits virtual destructor from class Base -> no error + checkVirtualDestructor("class Base\n" + "{\n" + "public:\n" + "virtual ~Base() {}\n" + "};\n" + "class A : private Base\n" + "{\n" + "public:\n" + " ~A() { }\n" + "};\n" + "\n" + "class B : public A\n" + "{\n" + "public:\n" + " ~B() { int a; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // class A inherits virtual destructor from struct Base -> no error + // also notice that public is not given, but destructor is public, because + // we are using struct instead of class + checkVirtualDestructor("struct Base\n" + "{\n" + "virtual ~Base() {}\n" + "};\n" + "class A : public Base\n" + "{\n" + "};\n" + "\n" + "class B : public A\n" + "{\n" + "public:\n" + " ~B() { int a; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // Unknown Base class -> it could have virtual destructor, so ignore + checkVirtualDestructor("class A : private Base\n" + "{\n" + "public:\n" + " ~A() { }\n" + "};\n" + "\n" + "class B : public A\n" + "{\n" + "public:\n" + " ~B() { int a; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // Virtual destructor is inherited -> no error + checkVirtualDestructor("class Base2\n" + "{\n" + "virtual ~Base2() {}\n" + "};\n" + "class Base : public Base2\n" + "{\n" + "};\n" + "class A : private Base\n" + "{\n" + "public:\n" + " ~A() { }\n" + "};\n" + "\n" + "class B : public A\n" + "{\n" + "public:\n" + " ~B() { int a; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // class A doesn't inherit virtual destructor from class Base -> error + checkVirtualDestructor("class Base\n" + "{\n" + "public:\n" + " ~Base() {}\n" + "};\n" + "class A : private Base\n" + "{\n" + "public:\n" + " ~A() { }\n" + "};\n" + "\n" + "class B : public A\n" + "{\n" + "public:\n" + " ~B() { int a; }\n" + "};"); + TODO_ASSERT_EQUALS("[test.cpp:7]: (error) Class 'Base' which is inherited by class 'B' does not have a virtual destructor.\n", + "", errout_str()); + } + + void virtualDestructorTemplate() { + checkVirtualDestructor("template class A\n" + "{\n" + " public:\n" + " virtual ~A(){}\n" + "};\n" + "template class AA\n" + "{\n" + " public:\n" + " ~AA(){}\n" + "};\n" + "class B : public A, public AA\n" + "{\n" + " public:\n" + " ~B(){int a;}\n" + "};\n" + "\n" + "AA *p = new B; delete p;"); + ASSERT_EQUALS("[test.cpp:9]: (error) Class 'AA < double >' which is inherited by class 'B' does not have a virtual destructor.\n", errout_str()); + } + + void virtualDestructorInconclusive() { + checkVirtualDestructor("class Base {\n" + "public:\n" + " ~Base(){}\n" + " virtual void foo(){}\n" + "};\n", true); + ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Class 'Base' which has virtual members does not have a virtual destructor.\n", errout_str()); + + checkVirtualDestructor("class Base {\n" + "public:\n" + " ~Base(){}\n" + " virtual void foo(){}\n" + "};\n" + "class Derived : public Base {\n" + "public:\n" + " ~Derived() { bar(); }\n" + "};\n" + "void foo() {\n" + " Base * base = new Derived();\n" + " delete base;\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:3]: (error) Class 'Base' which is inherited by class 'Derived' does not have a virtual destructor.\n", errout_str()); + + // class Base destructor is not virtual but protected -> no error + checkVirtualDestructor("class Base {\n" + "public:\n" + " virtual void foo(){}\n" + "protected:\n" + " ~Base(){}\n" + "};\n", true); + ASSERT_EQUALS("", errout_str()); + + checkVirtualDestructor("class C {\n" + "private:\n" + " C();\n" + " virtual ~C();\n" + "};\n", true); + ASSERT_EQUALS("", errout_str()); + } + + +#define checkNoMemset(...) checkNoMemset_(__FILE__, __LINE__, __VA_ARGS__) + void checkNoMemset_(const char* file, int line, const char code[]) { + const Settings settings = settingsBuilder().severity(Severity::warning).severity(Severity::portability).library("std.cfg").library("posix.cfg").build(); + checkNoMemset_(file, line, code, settings); + } + + void checkNoMemset_(const char* file, int line, const char code[], const Settings &settings) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check.. + CheckClass checkClass(&tokenizer, &settings, this); + checkClass.checkMemset(); + } + + void memsetOnClass() { + checkNoMemset("class Fred\n" + "{\n" + "};\n" + "void f()\n" + "{\n" + " Fred fred;\n" + " memset(&fred, 0, sizeof(Fred));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkNoMemset("class Fred\n" + "{\n" + " static std::string b;\n" + "};\n" + "void f()\n" + "{\n" + " Fred fred;\n" + " memset(&fred, 0, sizeof(Fred));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkNoMemset("class Fred\n" + "{\n" + " std::string * b;\n" + "};\n" + "void f()\n" + "{\n" + " Fred fred;\n" + " memset(&fred, 0, sizeof(Fred));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkNoMemset("class Fred\n" + "{\n" + " std::string b;\n" + "};\n" + "void f()\n" + "{\n" + " Fred fred;\n" + " memset(&fred, 0, sizeof(Fred));\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout_str()); + + checkNoMemset("class Fred\n" + "{\n" + " mutable std::string b;\n" + "};\n" + "void f()\n" + "{\n" + " Fred fred;\n" + " memset(&fred, 0, sizeof(Fred));\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout_str()); + + checkNoMemset("class Fred {\n" + " std::string b;\n" + " void f();\n" + "};\n" + "void Fred::f() {\n" + " memset(this, 0, sizeof(*this));\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout_str()); + + checkNoMemset("class Fred\n" + "{\n" + "};\n" + "void f()\n" + "{\n" + " Fred fred;\n" + " memset(&fred, 0, sizeof(fred));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkNoMemset("class Fred\n" + "{\n" + " std::string s;\n" + "};\n" + "void f()\n" + "{\n" + " Fred fred;\n" + " memset(&fred, 0, sizeof(fred));\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout_str()); + + checkNoMemset("class Fred\n" + "{\n" + " std::string s;\n" + "};\n" + "class Pebbles: public Fred {};\n" + "void f()\n" + "{\n" + " Pebbles pebbles;\n" + " memset(&pebbles, 0, sizeof(pebbles));\n" + "}"); + ASSERT_EQUALS("[test.cpp:9]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout_str()); + + checkNoMemset("class Fred\n" + "{\n" + " virtual ~Fred();\n" + "};\n" + "void f()\n" + "{\n" + " Fred fred;\n" + " memset(&fred, 0, sizeof(fred));\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a virtual function.\n", errout_str()); + + checkNoMemset("class Fred\n" + "{\n" + " virtual ~Fred();\n" + "};\n" + "void f()\n" + "{\n" + " static Fred fred;\n" + " memset(&fred, 0, sizeof(fred));\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on class that contains a virtual function.\n", errout_str()); + + checkNoMemset("class Fred\n" + "{\n" + "};\n" + "class Wilma\n" + "{\n" + " virtual ~Wilma();\n" + "};\n" + "class Pebbles: public Fred, Wilma {};\n" + "void f()\n" + "{\n" + " Pebbles pebbles;\n" + " memset(&pebbles, 0, sizeof(pebbles));\n" + "}"); + ASSERT_EQUALS("[test.cpp:12]: (error) Using 'memset' on class that contains a virtual function.\n", errout_str()); + + // Fred not defined in scope + checkNoMemset("namespace n1 {\n" + " class Fred\n" + " {\n" + " std::string b;\n" + " };\n" + "}\n" + "void f()\n" + "{\n" + " Fred fred;\n" + " memset(&fred, 0, sizeof(Fred));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Fred with namespace qualifier + checkNoMemset("namespace n1 {\n" + " class Fred\n" + " {\n" + " std::string b;\n" + " };\n" + "}\n" + "void f()\n" + "{\n" + " n1::Fred fred;\n" + " memset(&fred, 0, sizeof(n1::Fred));\n" + "}"); + ASSERT_EQUALS("[test.cpp:10]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout_str()); + + // Fred with namespace qualifier + checkNoMemset("namespace n1 {\n" + " class Fred\n" + " {\n" + " std::string b;\n" + " };\n" + "}\n" + "void f()\n" + "{\n" + " n1::Fred fred;\n" + " memset(&fred, 0, sizeof(fred));\n" + "}"); + ASSERT_EQUALS("[test.cpp:10]: (error) Using 'memset' on class that contains a 'std::string'.\n", errout_str()); + + checkNoMemset("class A {\n" + " virtual ~A() { }\n" + " std::string s;\n" + "};\n" + "int f() {\n" + " const int N = 10;\n" + " A** arr = new A*[N];\n" + " memset(arr, 0, N * sizeof(A*));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkNoMemset("class A {\n" // #5116 - nested class data is mixed in the SymbolDatabase + " std::string s;\n" + " struct B { int x; };\n" + "};\n" + "void f(A::B *b) {\n" + " memset(b,0,4);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #4461 Warn about memset/memcpy on class with references as members + checkNoMemset("class A {\n" + " std::string &s;\n" + "};\n" + "void f() {\n" + " A a;\n" + " memset(&a, 0, sizeof(a));\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Using 'memset' on class that contains a reference.\n", errout_str()); + checkNoMemset("class A {\n" + " const B&b;\n" + "};\n" + "void f() {\n" + " A a;\n" + " memset(&a, 0, sizeof(a));\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Using 'memset' on class that contains a reference.\n", errout_str()); + + // #7456 + checkNoMemset("struct A {\n" + " A() {}\n" + " virtual ~A() {}\n" + "};\n" + "struct B {\n" + " A* arr[4];\n" + "};\n" + "void func() {\n" + " B b[4];\n" + " memset(b, 0, sizeof(b));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #8619 + checkNoMemset("struct S { std::vector m; };\n" + "void f() {\n" + " std::vector v(5);\n" + " memset(&v[0], 0, sizeof(S) * v.size());\n" + " memset(&v[0], 0, v.size() * sizeof(S));\n" + " memset(&v[0], 0, 5 * sizeof(S));\n" + " memset(&v[0], 0, sizeof(S) * 5);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Using 'memset' on struct that contains a 'std::vector'.\n" + "[test.cpp:5]: (error) Using 'memset' on struct that contains a 'std::vector'.\n" + "[test.cpp:6]: (error) Using 'memset' on struct that contains a 'std::vector'.\n" + "[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", + errout_str()); + + // #1655 + const Settings s = settingsBuilder().library("std.cfg").build(); + checkNoMemset("void f() {\n" + " char c[] = \"abc\";\n" + " std::string s;\n" + " memcpy(&s, c, strlen(c) + 1);\n" + "}\n", s); + ASSERT_EQUALS("[test.cpp:4]: (error) Using 'memcpy' on std::string.\n", errout_str()); + + checkNoMemset("template \n" + " void f(T* dst, const T* src, int N) {\n" + " std::memcpy(dst, src, N * sizeof(T));\n" + "}\n" + "void g() {\n" + " typedef std::vector* P;\n" + " P Src[2]{};\n" + " P Dst[2];\n" + " f

(Dst, Src, 2);\n" + "}\n", s); + ASSERT_EQUALS("", errout_str()); + + checkNoMemset("void f() {\n" + " std::array a;\n" + " std::memset(&a, 0, 4);\n" + "}\n", s); + ASSERT_EQUALS("", errout_str()); + } + + void memsetOnInvalid() { // Ticket #5425 + checkNoMemset("union ASFStreamHeader {\n" + " struct AVMPACKED {\n" + " union {\n" + " struct AVMPACKED {\n" + " int width;\n" + " } vid;\n" + " };\n" + " } hdr;\n" + "};" + "void parseHeader() {\n" + " ASFStreamHeader strhdr;\n" + " memset(&strhdr, 0, sizeof(strhdr));\n" + "}"); + } + + void memsetOnStruct() { + checkNoMemset("struct A\n" + "{\n" + "};\n" + "void f()\n" + "{\n" + " A a;\n" + " memset(&a, 0, sizeof(A));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkNoMemset("struct A\n" + "{\n" + "};\n" + "void f()\n" + "{\n" + " struct A a;\n" + " memset(&a, 0, sizeof(struct A));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkNoMemset("struct A\n" + "{\n" + "};\n" + "void f()\n" + "{\n" + " struct A a;\n" + " memset(&a, 0, sizeof(A));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkNoMemset("void f()\n" + "{\n" + " struct sockaddr_in6 fail;\n" + " memset(&fail, 0, sizeof(struct sockaddr_in6));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkNoMemset("struct A\n" + "{\n" + " void g( struct sockaddr_in6& a);\n" + "private:\n" + " std::string b;\n" + "};\n" + "void f()\n" + "{\n" + " struct A fail;\n" + " memset(&fail, 0, sizeof(struct A));\n" + "}"); + ASSERT_EQUALS("[test.cpp:10]: (error) Using 'memset' on struct that contains a 'std::string'.\n", errout_str()); + + checkNoMemset("struct Fred\n" + "{\n" + " std::string s;\n" + "};\n" + "void f()\n" + "{\n" + " Fred fred;\n" + " memset(&fred, 0, sizeof(fred));\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (error) Using 'memset' on struct that contains a 'std::string'.\n", errout_str()); + + checkNoMemset("struct Stringy {\n" + " std::string inner;\n" + "};\n" + "struct Foo {\n" + " Stringy s;\n" + "};\n" + "int main() {\n" + " Foo foo;\n" + " memset(&foo, 0, sizeof(Foo));\n" + "}"); + + ASSERT_EQUALS("[test.cpp:9]: (error) Using 'memset' on struct that contains a 'std::string'.\n", errout_str()); + } + + void memsetVector() { + checkNoMemset("class A\n" + "{ std::vector ints; };\n" + "\n" + "void f()\n" + "{\n" + " A a;\n" + " memset(&a, 0, sizeof(A));\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on class that contains a 'std::vector'.\n", errout_str()); + + checkNoMemset("struct A\n" + "{ std::vector ints; };\n" + "\n" + "void f()\n" + "{\n" + " A a;\n" + " memset(&a, 0, sizeof(A));\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout_str()); + + checkNoMemset("struct A\n" + "{ std::vector ints; };\n" + "\n" + "void f()\n" + "{\n" + " A a;\n" + " memset(&a, 0, sizeof(struct A));\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout_str()); + + checkNoMemset("struct A\n" + "{ std::vector ints; };\n" + "\n" + "void f()\n" + "{\n" + " A a;\n" + " memset(&a, 0, sizeof(a));\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout_str()); + + checkNoMemset("class A\n" + "{ std::vector< std::vector > ints; };\n" + "\n" + "void f()\n" + "{\n" + " A a;\n" + " memset(&a, 0, sizeof(A));\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on class that contains a 'std::vector'.\n", errout_str()); + + checkNoMemset("struct A\n" + "{ std::vector< std::vector > ints; };\n" + "\n" + "void f()\n" + "{\n" + " A a;\n" + " memset(&a, 0, sizeof(A));\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout_str()); + + checkNoMemset("struct A\n" + "{ std::vector< std::vector > ints; };\n" + "\n" + "void f()\n" + "{\n" + " A a;\n" + " memset(&a, 0, sizeof(a));\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout_str()); + + checkNoMemset("struct A\n" + "{ std::vector ints; };\n" + "\n" + "void f()\n" + "{\n" + " A a;\n" + " memset(&a, 0, sizeof(A));\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Using 'memset' on struct that contains a 'std::vector'.\n", errout_str()); + + checkNoMemset("struct A {\n" + " std::vector buf;\n" + " operator int*() {return &buf[0];}\n" + "};\n" + "void f() {\n" + " A a;\n" + " memset(a, 0, 100);\n" + "}"); + ASSERT_EQUALS("", errout_str()); // #4460 + + checkNoMemset("struct C {\n" + " std::string s;\n" + "};\n" + "int foo() {\n" + " C* c1[10][10];\n" + " C* c2[10];\n" + " C c3[10][10];\n" + " C** c4 = new C*[10];\n" + " memset(**c1, 0, 10);\n" + " memset(*c1, 0, 10);\n" + " memset(*c2, 0, 10);\n" + " memset(*c3, 0, 10);\n" + " memset(*c4, 0, 10);\n" + " memset(c2, 0, 10);\n" + " memset(c3, 0, 10);\n" + "}"); + ASSERT_EQUALS("[test.cpp:9]: (error) Using 'memset' on struct that contains a 'std::string'.\n" + "[test.cpp:11]: (error) Using 'memset' on struct that contains a 'std::string'.\n" + "[test.cpp:12]: (error) Using 'memset' on struct that contains a 'std::string'.\n" + "[test.cpp:13]: (error) Using 'memset' on struct that contains a 'std::string'.\n", errout_str()); + + // Ticket #6953 + checkNoMemset("typedef float realnum;\n" + "struct multilevel_data {\n" + " realnum *GammaInv;\n" + " realnum data[1];\n" + "};\n" + "void *new_internal_data() const {\n" + " multilevel_data *d = (multilevel_data *) malloc(sizeof(multilevel_data));\n" + " memset(d, 0, sizeof(multilevel_data));\n" + " return (void*) d;\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (portability) Using memset() on struct which contains a floating point number.\n", errout_str()); + } + + void memsetOnStdPodType() { // Ticket #5901 + constexpr char xmldata[] = "\n" + "\n" + " \n" + " \n" + ""; + const Settings settings = settingsBuilder().libraryxml(xmldata, sizeof(xmldata)).build(); + + checkNoMemset("class A {\n" + " std::array ints;\n" + "};\n" + "void f() {\n" + " A a;\n" + " memset(&a, 0, sizeof(A));\n" + "}"); + ASSERT_EQUALS("", errout_str()); // std::array is POD (#5481) + + checkNoMemset("struct st {\n" + " std::uint8_t a;\n" + " std::atomic_bool b;\n" + "};\n" + "\n" + "void f() {\n" + " st s;\n" + " std::memset(&s, 0, sizeof(st));\n" + "}", settings); + ASSERT_EQUALS("", errout_str()); + } + + void memsetOnFloat() { + checkNoMemset("struct A {\n" + " float f;\n" + "};\n" + "void f() {\n" + " A a;\n" + " memset(&a, 0, sizeof(A));\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (portability) Using memset() on struct which contains a floating point number.\n", errout_str()); + + checkNoMemset("struct A {\n" + " float f[4];\n" + "};\n" + "void f() {\n" + " A a;\n" + " memset(&a, 0, sizeof(A));\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (portability) Using memset() on struct which contains a floating point number.\n", errout_str()); + + checkNoMemset("struct A {\n" + " float f[4];\n" + "};\n" + "void f(const A& b) {\n" + " A a;\n" + " memcpy(&a, &b, sizeof(A));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkNoMemset("struct A {\n" + " float* f;\n" + "};\n" + "void f() {\n" + " A a;\n" + " memset(&a, 0, sizeof(A));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void memsetOnUnknown() { + checkNoMemset("void clang_tokenize(CXToken **Tokens) {\n" + " *Tokens = (CXToken *)malloc(sizeof(CXToken) * CXTokens.size());\n" + " memmove(*Tokens, CXTokens.data(), sizeof(CXToken) * CXTokens.size());\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void mallocOnClass() { + checkNoMemset("class C { C() {} };\n" + "void foo(C*& p) {\n" + " p = malloc(sizeof(C));\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (warning) Memory for class instance allocated with malloc(), but class provides constructors.\n", errout_str()); + + checkNoMemset("class C { C(int z, Foo bar) { bar(); } };\n" + "void foo(C*& p) {\n" + " p = malloc(sizeof(C));\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (warning) Memory for class instance allocated with malloc(), but class provides constructors.\n", errout_str()); + + checkNoMemset("struct C { C() {} };\n" + "void foo(C*& p) {\n" + " p = realloc(p, sizeof(C));\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (warning) Memory for class instance allocated with realloc(), but class provides constructors.\n", errout_str()); + + checkNoMemset("struct C { virtual void bar(); };\n" + "void foo(C*& p) {\n" + " p = malloc(sizeof(C));\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (error) Memory for class instance allocated with malloc(), but class contains a virtual function.\n", errout_str()); + + checkNoMemset("struct C { std::string s; };\n" + "void foo(C*& p) {\n" + " p = malloc(sizeof(C));\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (error) Memory for class instance allocated with malloc(), but class contains a 'std::string'.\n", errout_str()); + + checkNoMemset("class C { };\n" // C-Style class/struct + "void foo(C*& p) {\n" + " p = malloc(sizeof(C));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkNoMemset("struct C { C() {} };\n" + "void foo(C*& p) {\n" + " p = new C();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkNoMemset("class C { C() {} };\n" + "void foo(D*& p) {\n" // Unknown type + " p = malloc(sizeof(C));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkNoMemset("class AutoCloseFD {\n" + " int fd;\n" + "public:\n" + " AutoCloseFD(int fd);\n" + " ~AutoCloseFD();\n" + "};\n" + "void f() {\n" + " AutoCloseFD fd = open(\"abc\", O_RDONLY | O_CLOEXEC);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkNoMemset("struct C {\n" // #12313 + " char* p;\n" + " C(char* ptr) : p(ptr) {}\n" + "};\n" + "void f() {\n" + " C c = strdup(\"abc\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + +#define checkThisSubtraction(code) checkThisSubtraction_(code, __FILE__, __LINE__) + void checkThisSubtraction_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings1, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check.. + CheckClass checkClass(&tokenizer, &settings1, this); + checkClass.thisSubtraction(); + } + + void this_subtraction() { + checkThisSubtraction("; this-x ;"); + ASSERT_EQUALS("[test.cpp:1]: (warning) Suspicious pointer subtraction. Did you intend to write '->'?\n", errout_str()); + + checkThisSubtraction("; *this = *this-x ;"); + ASSERT_EQUALS("", errout_str()); + + checkThisSubtraction("; *this = *this-x ;\n" + "this-x ;"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Suspicious pointer subtraction. Did you intend to write '->'?\n", errout_str()); + + checkThisSubtraction("; *this = *this-x ;\n" + "this-x ;\n" + "this-x ;"); + 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()); + } + +#define checkConst(...) checkConst_(__FILE__, __LINE__, __VA_ARGS__) + void checkConst_(const char* file, int line, const char code[], const Settings *s = nullptr, bool inconclusive = true) { + const Settings settings = settingsBuilder(s ? *s : settings0).certainty(Certainty::inconclusive, inconclusive).build(); + + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + 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 (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("class Fred {\n" + " std::string s;\n" + " const std::string & foo() { return \"\"; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + // constructors can't be const.. + checkConst("class Fred {\n" + " int a;\n" + "public:\n" + " Fred() { }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // assignment through |=.. + checkConst("class Fred {\n" + " int a;\n" + " int setA() { a |= true; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // functions with a call to a member function can only be const, if that member function is const, too.. (#1305) + checkConst("class foo {\n" + "public:\n" + " int x;\n" + " void a() { x = 1; }\n" + " void b() { a(); }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + "public:\n" + " int x;\n" + " int a() const { return x; }\n" + " void b() { a(); }\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'Fred::b' can be const.\n", errout_str()); + + checkConst("class Fred {\n" + "public:\n" + " int x;\n" + " void b() { a(); }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::b' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + // static functions can't be const.. + checkConst("class foo\n" + "{\n" + "public:\n" + " static unsigned get()\n" + " { return 0; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " const std::string foo() const throw() { return \"\"; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + } + + void const2() { + // ticket 1344 + // assignment to variable can't be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo() { s = \"\"; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // assignment to function argument reference can be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string & a) { a = s; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout_str()); + + // assignment to variable can't be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string & a) { s = a; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // assignment to function argument references can be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string & a, std::string & b) { a = s; b = s; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout_str()); + + // assignment to variable, can't be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string & a, std::string & b) { s = a; s = b; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // assignment to variable, can't be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string & a, std::string & b) { s = a; b = a; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // assignment to variable, can't be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string & a, std::string & b) { a = s; s = b; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const3() { + // assignment to function argument pointer can be const + checkConst("class Fred {\n" + " int s;\n" + " void foo(int * a) { *a = s; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout_str()); + + // assignment to variable, can't be const + checkConst("class Fred {\n" + " int s;\n" + " void foo(int * a) { s = *a; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // assignment to function argument pointers can be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string * a, std::string * b) { *a = s; *b = s; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout_str()); + + // assignment to variable, can't be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string * a, std::string * b) { s = *a; s = *b; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // assignment to variable, can't be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string * a, std::string * b) { s = *a; *b = s; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // assignment to variable, can't be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string * a, std::string * b) { *a = s; s = b; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const4() { + checkConst("class Fred {\n" + " int a;\n" + " int getA();\n" + "};\n" + "int Fred::getA() { return a; }"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::getA' can be const.\n", errout_str()); + + checkConst("class Fred {\n" + " std::string s;\n" + " const std::string & foo();\n" + "};\n" + "const std::string & Fred::foo() { return \"\"; }"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + // functions with a function call to a non-const member can't be const.. (#1305) + checkConst("class Fred\n" + "{\n" + "public:\n" + " int x;\n" + " void a() { x = 1; }\n" + " void b();\n" + "};\n" + "void Fred::b() { a(); }"); + ASSERT_EQUALS("", errout_str()); + + // static functions can't be const.. + checkConst("class Fred\n" + "{\n" + "public:\n" + " static unsigned get();\n" + "};\n" + "static unsigned Fred::get() { return 0; }"); + ASSERT_EQUALS("", errout_str()); + + // assignment to variable can't be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo();\n" + "};\n" + "void Fred::foo() { s = \"\"; }"); + ASSERT_EQUALS("", errout_str()); + + // assignment to function argument reference can be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string & a);\n" + "};\n" + "void Fred::foo(std::string & a) { a = s; }"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout_str()); + + // assignment to variable can't be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string & a);\n" + "};\n" + "void Fred::foo(std::string & a) { s = a; }"); + ASSERT_EQUALS("", errout_str()); + + // assignment to function argument references can be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string & a, std::string & b);\n" + "};\n" + "void Fred::foo(std::string & a, std::string & b) { a = s; b = s; }"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout_str()); + + // assignment to variable, can't be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string & a, std::string & b);\n" + "};\n" + "void Fred::foo(std::string & a, std::string & b) { s = a; s = b; }"); + ASSERT_EQUALS("", errout_str()); + + // assignment to variable, can't be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string & a, std::string & b);\n" + "};\n" + "void Fred::foo(std::string & a, std::string & b) { s = a; b = a; }"); + ASSERT_EQUALS("", errout_str()); + + // assignment to variable, can't be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string & a, std::string & b);\n" + "};\n" + "void Fred::foo(std::string & a, std::string & b) { a = s; s = b; }"); + ASSERT_EQUALS("", errout_str()); + + // assignment to function argument pointer can be const + checkConst("class Fred {\n" + " int s;\n" + " void foo(int * a);\n" + "};\n" + "void Fred::foo(int * a) { *a = s; }"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout_str()); + + // assignment to variable, can't be const + checkConst("class Fred {\n" + " int s;\n" + " void foo(int * a);\n" + "};\n" + "void Fred::foo(int * a) { s = *a; }"); + ASSERT_EQUALS("", errout_str()); + + // assignment to function argument pointers can be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string * a, std::string * b);\n" + "};\n" + "void Fred::foo(std::string * a, std::string * b) { *a = s; *b = s; }"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout_str()); + + // assignment to variable, can't be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string * a, std::string * b);\n" + "};\n" + "void Fred::foo(std::string * a, std::string * b) { s = *a; s = *b; }"); + ASSERT_EQUALS("", errout_str()); + + // assignment to variable, can't be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string * a, std::string * b);\n" + "};\n" + "void Fred::foo(std::string * a, std::string * b) { s = *a; *b = s; }"); + ASSERT_EQUALS("", errout_str()); + + // assignment to variable, can't be const + checkConst("class Fred {\n" + " std::string s;\n" + " void foo(std::string * a, std::string * b);\n" + "};\n" + "void Fred::foo(std::string * a, std::string * b) { *a = s; s = b; }"); + ASSERT_EQUALS("", errout_str()); + + // check functions with same name + checkConst("class Fred {\n" + " std::string s;\n" + " void foo();\n" + " void foo(std::string & a);\n" + " void foo(const std::string & a);\n" + "};\n" + "void Fred::foo() { }" + "void Fred::foo(std::string & a) { a = s; }" + "void Fred::foo(const std::string & a) { s = a; }"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:7] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::foo' can be const.\n", errout_str()); + + // check functions with different or missing parameter names + checkConst("class Fred {\n" + " std::string s;\n" + " void foo1(int, int);\n" + " void foo2(int a, int b);\n" + " void foo3(int, int b);\n" + " void foo4(int a, int);\n" + " void foo5(int a, int b);\n" + "};\n" + "void Fred::foo1(int a, int b) { }\n" + "void Fred::foo2(int c, int d) { }\n" + "void Fred::foo3(int a, int b) { }\n" + "void Fred::foo4(int a, int b) { }\n" + "void Fred::foo5(int, int) { }"); + ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::foo1' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:10] -> [test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::foo2' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:11] -> [test.cpp:5]: (performance, inconclusive) Technically the member function 'Fred::foo3' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:12] -> [test.cpp:6]: (performance, inconclusive) Technically the member function 'Fred::foo4' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:13] -> [test.cpp:7]: (performance, inconclusive) Technically the member function 'Fred::foo5' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + // check nested classes + checkConst("class Fred {\n" + " class A {\n" + " int a;\n" + " int getA() { return a; }\n" + " };\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::A::getA' can be const.\n", errout_str()); + + checkConst("class Fred {\n" + " class A {\n" + " int a;\n" + " int getA();\n" + " };\n" + " int A::getA() { return a; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::A::getA' can be const.\n", errout_str()); + + checkConst("class Fred {\n" + " class A {\n" + " int a;\n" + " int getA();\n" + " };\n" + "};\n" + "int Fred::A::getA() { return a; }"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::A::getA' can be const.\n", errout_str()); + + // check deeply nested classes + checkConst("class Fred {\n" + " class B {\n" + " int b;\n" + " int getB() { return b; }\n" + " class A {\n" + " int a;\n" + " int getA() { return a; }\n" + " };\n" + " };\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::B::getB' can be const.\n" + "[test.cpp:7]: (style, inconclusive) Technically the member function 'Fred::B::A::getA' can be const.\n" + , errout_str()); + + checkConst("class Fred {\n" + " class B {\n" + " int b;\n" + " int getB();\n" + " class A {\n" + " int a;\n" + " int getA();\n" + " };\n" + " int A::getA() { return a; }\n" + " };\n" + " int B::getB() { return b; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::B::getB' can be const.\n" + "[test.cpp:9] -> [test.cpp:7]: (style, inconclusive) Technically the member function 'Fred::B::A::getA' can be const.\n", errout_str()); + + checkConst("class Fred {\n" + " class B {\n" + " int b;\n" + " int getB();\n" + " class A {\n" + " int a;\n" + " int getA();\n" + " };\n" + " };\n" + " int B::A::getA() { return a; }\n" + " int B::getB() { return b; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::B::getB' can be const.\n" + "[test.cpp:10] -> [test.cpp:7]: (style, inconclusive) Technically the member function 'Fred::B::A::getA' can be const.\n", errout_str()); + + checkConst("class Fred {\n" + " class B {\n" + " int b;\n" + " int getB();\n" + " class A {\n" + " int a;\n" + " int getA();\n" + " };\n" + " };\n" + "};\n" + "int Fred::B::A::getA() { return a; }\n" + "int Fred::B::getB() { return b; }"); + ASSERT_EQUALS("[test.cpp:12] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::B::getB' can be const.\n" + "[test.cpp:11] -> [test.cpp:7]: (style, inconclusive) Technically the member function 'Fred::B::A::getA' can be const.\n", errout_str()); + } + + // operator< can often be const + void constoperator1() { + checkConst("struct Fred {\n" + " int a;\n" + " bool operator<(const Fred &f) { return a < f.a; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::operator<' can be const.\n", errout_str()); + } + + // operator<< + void constoperator2() { + checkConst("struct Foo {\n" + " void operator<<(int);\n" + "};\n" + "struct Fred {\n" + " Foo foo;\n" + " void x()\n" + " {\n" + " foo << 123;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct Foo {\n" + " void operator<<(int);\n" + "};\n" + "struct Fred {\n" + " Foo foo;\n" + " void x()\n" + " {\n" + " std::cout << foo << 123;\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (style, inconclusive) Technically the member function 'Fred::x' can be const.\n", errout_str()); + } + + void constoperator3() { + checkConst("struct Fred {\n" + " int array[10];\n" + " int const & operator [] (unsigned int index) const { return array[index]; }\n" + " int & operator [] (unsigned int index) { return array[index]; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct Fred {\n" + " int array[10];\n" + " int const & operator [] (unsigned int index) { return array[index]; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::operator[]' can be const.\n", errout_str()); + } + + void constoperator4() { + // #7953 + checkConst("class A {\n" + " int c;\n" + "public:\n" + " operator int*() { return &c; };\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class A {\n" + " int c;\n" + "public:\n" + " operator const int*() { return &c; };\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::operatorconstint*' can be const.\n", errout_str()); + + // #2375 + checkConst("struct Fred {\n" + " int array[10];\n" + " typedef int* (Fred::*UnspecifiedBoolType);\n" + " operator UnspecifiedBoolType() { };\n" + "};"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::operatorint**' can be const.\n", "", errout_str()); + + checkConst("struct Fred {\n" + " int array[10];\n" + " typedef int* (Fred::*UnspecifiedBoolType);\n" + " operator UnspecifiedBoolType() { array[0] = 0; };\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void constoperator5() { // ticket #3252 + checkConst("class A {\n" + " int c;\n" + "public:\n" + " operator int& () {return c}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class A {\n" + " int c;\n" + "public:\n" + " operator const int& () {return c}\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::operatorconstint&' can be const.\n", errout_str()); + + checkConst("class A {\n" + " int c;\n" + "public:\n" + " operator int () {return c}\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::operatorint' can be const.\n", errout_str()); + } + + void constoperator6() { // ticket #8669 + checkConst("class A {\n" + " int c;\n" + " void f() { os >> *this; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const5() { + // ticket #1482 + checkConst("class A {\n" + " int a;\n" + " bool foo(int i)\n" + " {\n" + " bool same;\n" + " same = (i == a);\n" + " return same;\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::foo' can be const.\n", errout_str()); + } + + void const6() { + // ticket #1491 + checkConst("class foo {\n" + "public:\n" + "};\n" + "void bar() {}"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred\n" + "{\n" + "public:\n" + " void foo() { }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::foo' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("struct fast_string\n" + "{\n" + " union\n" + " {\n" + " char buff[100];\n" + " };\n" + " void set_type(char t);\n" + "};\n" + "inline void fast_string::set_type(char t)\n" + "{\n" + " buff[10] = t;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void const7() { + checkConst("class foo {\n" + " int a;\n" + "public:\n" + " void set(int i) { a = i; }\n" + " void set(const foo & f) { *this = f; }\n" + "};\n" + "void bar() {}"); + ASSERT_EQUALS("", errout_str()); + } + + void const8() { + // ticket #1517 + checkConst("class A {\n" + "public:\n" + " A():m_strValue(\"\"){}\n" + " std::string strGetString() { return m_strValue; }\n" + "private:\n" + " std::string m_strValue;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::strGetString' can be const.\n", errout_str()); + } + + void const9() { + // ticket #1515 + checkConst("class wxThreadInternal {\n" + "public:\n" + " void SetExitCode(wxThread::ExitCode exitcode) { m_exitcode = exitcode; }\n" + "private:\n" + " wxThread::ExitCode m_exitcode;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const10() { + // ticket #1522 + checkConst("class A {\n" + "public:\n" + " int foo() { return x = 0; }\n" + "private:\n" + " int x;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class A {\n" + "public:\n" + " int foo() { return x ? x : x = 0; }\n" + "private:\n" + " int x;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class A {\n" + "public:\n" + " int foo() { return x ? x = 0 : x; }\n" + "private:\n" + " int x;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const11() { + // ticket #1529 + checkConst("class A {\n" + "public:\n" + " void set(struct tm time) { m_time = time; }\n" + "private:\n" + " struct tm m_time;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const12() { + // ticket #1525 + checkConst("class A {\n" + "public:\n" + " int foo() { x = 0; }\n" + "private:\n" + " mutable int x;\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::foo' can be const.\n", errout_str()); + } + + void const13() { + // ticket #1519 + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " std::vector GetVec() {return m_vec;}\n" + " std::pair GetPair() {return m_pair;}\n" + "private:\n" + " std::vector m_vec;\n" + " std::pair m_pair;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetVec' can be const.\n" + "[test.cpp:5]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout_str()); + + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " const std::vector & GetVec() {return m_vec;}\n" + " const std::pair & GetPair() {return m_pair;}\n" + "private:\n" + " std::vector m_vec;\n" + " std::pair m_pair;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetVec' can be const.\n" + "[test.cpp:5]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout_str()); + } + + void const14() { + // extends ticket 1519 + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " std::pair,double> GetPair() {return m_pair;}\n" + "private:\n" + " std::pair,double> m_pair;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout_str()); + + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " const std::pair,double>& GetPair() {return m_pair;}\n" + "private:\n" + " std::pair,double> m_pair;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout_str()); + + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " std::pair,double>& GetPair() {return m_pair;}\n" + "private:\n" + " std::pair,double> m_pair;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + + checkConst("using namespace std;" + "class A {\n" + "public:\n" + " A(){}\n" + " pair GetPair() {return m_pair;}\n" + "private:\n" + " pair m_pair;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout_str()); + + checkConst("using namespace std;" + "class A {\n" + "public:\n" + " A(){}\n" + " const pair & GetPair() {return m_pair;}\n" + "private:\n" + " pair m_pair;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout_str()); + + checkConst("using namespace std;" + "class A {\n" + "public:\n" + " A(){}\n" + " pair & GetPair() {return m_pair;}\n" + "private:\n" + " pair m_pair;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " std::pair< int,std::vector > GetPair() {return m_pair;}\n" + "private:\n" + " std::pair< int,std::vector > m_pair;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout_str()); + + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " const std::pair< int,std::vector >& GetPair() {return m_pair;}\n" + "private:\n" + " std::pair< int,std::vector > m_pair;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout_str()); + + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " std::pair< int,std::vector >& GetPair() {return m_pair;}\n" + "private:\n" + " std::pair< int,std::vector > m_pair;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + + checkConst("using namespace std;" + "class A {\n" + "public:\n" + " A(){}\n" + " pair< vector, int > GetPair() {return m_pair;}\n" + "private:\n" + " pair< vector, int > m_pair;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout_str()); + + checkConst("using namespace std;" + "class A {\n" + "public:\n" + " A(){}\n" + " const pair< vector, int >& GetPair() {return m_pair;}\n" + "private:\n" + " pair< vector, int > m_pair;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout_str()); + + checkConst("using namespace std;" + "class A {\n" + "public:\n" + " A(){}\n" + " pair< vector, int >& GetPair() {return m_pair;}\n" + "private:\n" + " pair< vector, int > m_pair;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " std::pair< std::vector,std::vector > GetPair() {return m_pair;}\n" + "private:\n" + " std::pair< std::vector,std::vector > m_pair;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout_str()); + + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " const std::pair< std::vector,std::vector >& GetPair() {return m_pair;}\n" + "private:\n" + " std::pair< std::vector,std::vector > m_pair;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout_str()); + + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " std::pair< std::vector,std::vector >& GetPair() {return m_pair;}\n" + "private:\n" + " std::pair< std::vector,std::vector > m_pair;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + + + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " std::pair< std::pair < int, char > , int > GetPair() {return m_pair;}\n" + "private:\n" + " std::pair< std::pair < int, char > , int > m_pair;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout_str()); + + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " const std::pair< std::pair < int, char > , int > & GetPair() {return m_pair;}\n" + "private:\n" + " std::pair< std::pair < int, char > , int > m_pair;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout_str()); + + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " std::pair< std::pair < int, char > , int > & GetPair() {return m_pair;}\n" + "private:\n" + " std::pair< std::pair < int, char > , int > m_pair;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " std::pair< int , std::pair < int, char > > GetPair() {return m_pair;}\n" + "private:\n" + " std::pair< int , std::pair < int, char > > m_pair;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout_str()); + + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " const std::pair< int , std::pair < int, char > >& GetPair() {return m_pair;}\n" + "private:\n" + " std::pair< int , std::pair < int, char > > m_pair;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetPair' can be const.\n", errout_str()); + + checkConst("class A {\n" + "public:\n" + " A(){}\n" + " std::pair< int , std::pair < int, char > >& GetPair() {return m_pair;}\n" + "private:\n" + " std::pair< int , std::pair < int, char > > m_pair;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + + checkConst("using namespace std;" + "class A {\n" + "public:\n" + " A(){}\n" + " vector GetVec() {return m_Vec;}\n" + "private:\n" + " vector m_Vec;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetVec' can be const.\n", errout_str()); + + checkConst("using namespace std;" + "class A {\n" + "public:\n" + " A(){}\n" + " const vector& GetVec() {return m_Vec;}\n" + "private:\n" + " vector m_Vec;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'A::GetVec' can be const.\n", errout_str()); + + checkConst("using namespace std;" + "class A {\n" + "public:\n" + " A(){}\n" + " vector& GetVec() {return m_Vec;}\n" + "private:\n" + " vector m_Vec;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + + checkConst("class A {\n" + "public:\n" + " int * * foo() { return &x; }\n" + "private:\n" + " const int * x;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class A {\n" + "public:\n" + " const int ** foo() { return &x; }\n" + "private:\n" + " const int * x;\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::foo' can be const.\n", errout_str()); + } + + void const15() { + checkConst("class Fred {\n" + " unsigned long long int a;\n" + " unsigned long long int getA() { return a; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::getA' can be const.\n", errout_str()); + + // constructors can't be const.. + checkConst("class Fred {\n" + " unsigned long long int a;\n" + "public:\n" + " Fred() { }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // assignment through |=.. + checkConst("class Fred {\n" + " unsigned long long int a;\n" + " unsigned long long int setA() { a |= true; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // static functions can't be const.. + checkConst("class foo\n" + "{\n" + "public:\n" + " static unsigned long long int get()\n" + " { return 0; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const16() { + // ticket #1551 + checkConst("class Fred {\n" + " int a;\n" + " void set(int i) { Fred::a = i; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const17() { + // ticket #1552 + checkConst("class Fred {\n" + "public:\n" + " void set(int i, int j) { a[i].k = i; }\n" + "private:\n" + " struct { int k; } a[4];\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const18() { + checkConst("class Fred {\n" + "static int x;\n" + "public:\n" + " void set(int i) { x = i; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Fred::set' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + } + + void const19() { + // ticket #1612 + checkConst("using namespace std;\n" + "class Fred {\n" + "private:\n" + " std::string s;\n" + "public:\n" + " void set(std::string ss) { s = ss; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const20() { + // ticket #1602 + checkConst("class Fred {\n" + " int x : 3;\n" + "public:\n" + " void set(int i) { x = i; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " list x;\n" + "public:\n" + " list get() { return x; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " list x;\n" + "public:\n" + " list get() { return x; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::get' can be const.\n", errout_str()); + + checkConst("class Fred {\n" + " std::list x;\n" + "public:\n" + " std::list get() { return x; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " std::list x;\n" + "public:\n" + " std::list get() { return x; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::get' can be const.\n", errout_str()); + } + + void const21() { + // ticket #1683 + checkConst("class A\n" + "{\n" + "private:\n" + " const char * l1[10];\n" + "public:\n" + " A()\n" + " {\n" + " for (int i = 0 ; i < 10; l1[i] = NULL, i++);\n" + " }\n" + " void f1() { l1[0] = \"Hello\"; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const22() { + checkConst("class A\n" + "{\n" + "private:\n" + " B::C * v1;\n" + "public:\n" + " void f1() { v1 = 0; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class A\n" + "{\n" + "private:\n" + " B::C * v1[0];\n" + "public:\n" + " void f1() { v1[0] = 0; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const23() { + checkConst("class Class {\n" + "public:\n" + " typedef Template Type;\n" + " typedef Template2 Type2;\n" + " void set_member(Type2 m) { _m = m; }\n" + "private:\n" + " Type2 _m;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const24() { + checkConst("class Class {\n" + "public:\n" + "void Settings::SetSetting(QString strSetting, QString strNewVal)\n" + "{\n" + " (*m_pSettings)[strSetting] = strNewVal;\n" + "}\n" + "private:\n" + " std::map *m_pSettings;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + + void const25() { // ticket #1724 + checkConst("class A{\n" + "public:\n" + "A(){m_strVal=\"\";}\n" + "std::string strGetString() const\n" + "{return m_strVal.c_str();}\n" + "const std::string strGetString1() const\n" + "{return m_strVal.c_str();}\n" + "private:\n" + "std::string m_strVal;\n" + "};"); + 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" + "};"); + 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" + "};"); + 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" + "};"); + 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" + "};"); + 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" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct DelayBase {\n" + " float swapSpecificDelays(int index1) {\n" + " return delays_[index1];\n" + " }\n" + " float delays_[4];\n" + "};"); + ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Technically the member function 'DelayBase::swapSpecificDelays' can be const.\n", errout_str()); + } + + void const27() { // ticket #1882 + checkConst("class A {\n" + "public:\n" + " A(){m_d=1.0; m_iRealVal=2.0;}\n" + " double dGetValue();\n" + "private:\n" + " double m_d;\n" + " double m_iRealVal;\n" + "};\n" + "double A::dGetValue() {\n" + " double dRet = m_iRealVal;\n" + " if( m_d != 0 )\n" + " return m_iRealVal / m_d;\n" + " return dRet;\n" + "};", nullptr, true); + ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'A::dGetValue' can be const.\n", errout_str()); + } + + void const28() { // ticket #1883 + checkConst("class P {\n" + "public:\n" + " P() { x=0.0; y=0.0; }\n" + " double x,y;\n" + "};\n" + "class A : public P {\n" + "public:\n" + " A():P(){}\n" + " void SetPos(double xPos, double yPos) {\n" + " x=xPos;\n" + " y=yPos;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class AA : public P {\n" + "public:\n" + " AA():P(){}\n" + " inline void vSetXPos(int x_)\n" + " {\n" + " UnknownScope::x = x_;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class AA {\n" + "public:\n" + " AA():P(){}\n" + " inline void vSetXPos(int x_)\n" + " {\n" + " UnknownScope::x = x_;\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'AA::vSetXPos' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + } + + void const29() { // ticket #1922 + checkConst("class test {\n" + " public:\n" + " test();\n" + " const char* get() const;\n" + " char* get();\n" + " private:\n" + " char* value_;\n" + "};\n" + "test::test()\n" + "{\n" + " value_ = 0;\n" + "}\n" + "const char* test::get() const\n" + "{\n" + " return value_;\n" + "}\n" + "char* test::get()\n" + "{\n" + " return value_;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void const30() { + // check for false negatives + checkConst("class Base {\n" + "public:\n" + " int a;\n" + "};\n" + "class Derived : public Base {\n" + "public:\n" + " int get() {\n" + " return a;\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'Derived::get' can be const.\n", errout_str()); + + checkConst("class Base1 {\n" + "public:\n" + " int a;\n" + "};\n" + "class Base2 {\n" + "public:\n" + " int b;\n" + "};\n" + "class Derived : public Base1, public Base2 {\n" + "public:\n" + " int getA() {\n" + " return a;\n" + " }\n" + " int getB() {\n" + " return b;\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:11]: (style, inconclusive) Technically the member function 'Derived::getA' can be const.\n" + "[test.cpp:14]: (style, inconclusive) Technically the member function 'Derived::getB' can be const.\n", errout_str()); + + checkConst("class Base {\n" + "public:\n" + " int a;\n" + "};\n" + "class Derived1 : public Base { };\n" + "class Derived2 : public Derived1 {\n" + "public:\n" + " int get() {\n" + " return a;\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:8]: (style, inconclusive) Technically the member function 'Derived2::get' can be const.\n", errout_str()); + + checkConst("class Base {\n" + "public:\n" + " int a;\n" + "};\n" + "class Derived1 : public Base { };\n" + "class Derived2 : public Derived1 { };\n" + "class Derived3 : public Derived2 { };\n" + "class Derived4 : public Derived3 {\n" + "public:\n" + " int get() {\n" + " return a;\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:10]: (style, inconclusive) Technically the member function 'Derived4::get' can be const.\n", errout_str()); + + // check for false positives + checkConst("class Base {\n" + "public:\n" + " int a;\n" + "};\n" + "class Derived : public Base {\n" + "public:\n" + " int get() const {\n" + " return a;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Base1 {\n" + "public:\n" + " int a;\n" + "};\n" + "class Base2 {\n" + "public:\n" + " int b;\n" + "};\n" + "class Derived : public Base1, public Base2 {\n" + "public:\n" + " int getA() const {\n" + " return a;\n" + " }\n" + " int getB() const {\n" + " return b;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Base {\n" + "public:\n" + " int a;\n" + "};\n" + "class Derived1 : public Base { };\n" + "class Derived2 : public Derived1 {\n" + "public:\n" + " int get() const {\n" + " return a;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Base {\n" + "public:\n" + " int a;\n" + "};\n" + "class Derived1 : public Base { };\n" + "class Derived2 : public Derived1 { };\n" + "class Derived3 : public Derived2 { };\n" + "class Derived4 : public Derived3 {\n" + "public:\n" + " int get() const {\n" + " return a;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const31() { + checkConst("namespace std { }\n" + "class Fred {\n" + "public:\n" + " int a;\n" + " int get() { return a; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'Fred::get' can be const.\n", errout_str()); + } + + void const32() { + checkConst("class Fred {\n" + "public:\n" + " std::string a[10];\n" + " void seta() { a[0] = \"\"; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const33() { + checkConst("class derived : public base {\n" + "public:\n" + " void f(){}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const34() { // ticket #1964 + checkConst("class Bar {\n" + " void init(Foo * foo) {\n" + " foo.bar = this;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const35() { // ticket #2001 + checkConst("namespace N\n" + "{\n" + " class Base\n" + " {\n" + " };\n" + "}\n" + "namespace N\n" + "{\n" + " class Derived : public Base\n" + " {\n" + " public:\n" + " int getResourceName() { return var; }\n" + " int var;\n" + " };\n" + "}"); + ASSERT_EQUALS("[test.cpp:12]: (style, inconclusive) Technically the member function 'N::Derived::getResourceName' can be const.\n", errout_str()); + + checkConst("namespace N\n" + "{\n" + " class Base\n" + " {\n" + " public:\n" + " int getResourceName();\n" + " int var;\n" + " };\n" + "}\n" + "int N::Base::getResourceName() { return var; }"); + ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:6]: (style, inconclusive) Technically the member function 'N::Base::getResourceName' can be const.\n", errout_str()); + + checkConst("namespace N\n" + "{\n" + " class Base\n" + " {\n" + " public:\n" + " int getResourceName();\n" + " int var;\n" + " };\n" + "}\n" + "namespace N\n" + "{\n" + " int Base::getResourceName() { return var; }\n" + "}"); + ASSERT_EQUALS("[test.cpp:12] -> [test.cpp:6]: (style, inconclusive) Technically the member function 'N::Base::getResourceName' can be const.\n", errout_str()); + + checkConst("namespace N\n" + "{\n" + " class Base\n" + " {\n" + " public:\n" + " int getResourceName();\n" + " int var;\n" + " };\n" + "}\n" + "using namespace N;\n" + "int Base::getResourceName() { return var; }"); + ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:6]: (style, inconclusive) Technically the member function 'N::Base::getResourceName' can be const.\n", errout_str()); + } + + void const36() { // ticket #2003 + checkConst("class Foo {\n" + "public:\n" + " Blue::Utility::Size m_MaxQueueSize;\n" + " void SetMaxQueueSize(Blue::Utility::Size a_MaxQueueSize)\n" + " {\n" + " m_MaxQueueSize = a_MaxQueueSize;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const37() { // ticket #2081 and #2085 + checkConst("class A\n" + "{\n" + "public:\n" + " A(){};\n" + " std::string operator+(const char *c)\n" + " {\n" + " return m_str+std::string(c);\n" + " }\n" + "private:\n" + " std::string m_str;\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'A::operator+' can be const.\n", errout_str()); + + checkConst("class Fred\n" + "{\n" + "private:\n" + " long x;\n" + "public:\n" + " Fred() {\n" + " x = 0;\n" + " }\n" + " bool isValid() {\n" + " return (x == 0x11224488);\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:9]: (style, inconclusive) Technically the member function 'Fred::isValid' can be const.\n", errout_str()); + } + + void const38() { // ticket #2135 + checkConst("class Foo {\n" + "public:\n" + " ~Foo() { delete oArq; }\n" + " Foo(): oArq(new std::ofstream(\"...\")) {}\n" + " void MyMethod();\n" + "private:\n" + " std::ofstream *oArq;\n" + "};\n" + "void Foo::MyMethod()\n" + "{\n" + " (*oArq) << \"\";\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void const39() { + checkConst("class Foo\n" + "{\n" + " int * p;\n" + "public:\n" + " Foo () : p(0) { }\n" + " int * f();\n" + " const int * f() const;\n" + "};\n" + "const int * Foo::f() const\n" + "{\n" + " return p;\n" + "}\n" + "int * Foo::f()\n" + "{\n" + " return p;\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + } + + void const40() { // ticket #2228 + checkConst("class SharedPtrHolder\n" + "{\n" + " private:\n" + " std::tr1::shared_ptr pView;\n" + " public:\n" + " SharedPtrHolder()\n" + " { }\n" + " void SetView(const std::shared_ptr & aView)\n" + " {\n" + " pView = aView;\n" + " }\n" + "};"); + + ASSERT_EQUALS("", errout_str()); + } + + void const41() { // ticket #2255 + checkConst("class Fred\n" + "{\n" + " ::std::string m_name;\n" + "public:\n" + " void SetName(const ::std::string & name)\n" + " {\n" + " m_name = name;\n" + " }\n" + "};"); + + ASSERT_EQUALS("", errout_str()); + + checkConst("class SharedPtrHolder\n" + "{\n" + " ::std::tr1::shared_ptr pNum;\n" + " public :\n" + " void SetNum(const ::std::tr1::shared_ptr & apNum)\n" + " {\n" + " pNum = apNum;\n" + " }\n" + "};"); + + ASSERT_EQUALS("", errout_str()); + + checkConst("class SharedPtrHolder2\n" + "{\n" + " public:\n" + " typedef ::std::tr1::shared_ptr IntSharedPtr;\n" + " private:\n" + " IntSharedPtr pNum;\n" + " public :\n" + " void SetNum(const IntSharedPtr & apNum)\n" + " {\n" + " pNum = apNum;\n" + " }\n" + "};"); + + ASSERT_EQUALS("", errout_str()); + + checkConst("struct IntPtrTypes\n" + "{\n" + " typedef ::std::tr1::shared_ptr Shared;\n" + "};\n" + "class SharedPtrHolder3\n" + "{\n" + " private:\n" + " IntPtrTypes::Shared pNum;\n" + " public :\n" + " void SetNum(const IntPtrTypes::Shared & apNum)\n" + " {\n" + " pNum = apNum;\n" + " }\n" + "};"); + + ASSERT_EQUALS("", errout_str()); + + checkConst("template \n" + "struct PtrTypes\n" + "{\n" + " typedef ::std::tr1::shared_ptr Shared;\n" + "};\n" + "class SharedPtrHolder4\n" + "{\n" + " private:\n" + " PtrTypes::Shared pNum;\n" + " public :\n" + " void SetNum(const PtrTypes::Shared & apNum)\n" + " {\n" + " pNum = apNum;\n" + " }\n" + "};"); + + ASSERT_EQUALS("", errout_str()); + } + + void const42() { // ticket #2282 + checkConst("class Fred\n" + "{\n" + "public:\n" + " struct AB { };\n" + " bool f(AB * ab);\n" + "};\n" + "bool Fred::f(Fred::AB * ab)\n" + "{\n" + "}"); + + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:5]: (performance, inconclusive) Technically the member function 'Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("class Fred\n" + "{\n" + "public:\n" + " struct AB {\n" + " struct CD { };\n" + " };\n" + " bool f(AB::CD * cd);\n" + "};\n" + "bool Fred::f(Fred::AB::CD * cd)\n" + "{\n" + "}"); + + ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:7]: (performance, inconclusive) Technically the member function 'Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("namespace NS {\n" + " class Fred\n" + " {\n" + " public:\n" + " struct AB {\n" + " struct CD { };\n" + " };\n" + " bool f(AB::CD * cd);\n" + " };\n" + " bool Fred::f(Fred::AB::CD * cd)\n" + " {\n" + " }\n" + "}"); + + ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:8]: (performance, inconclusive) Technically the member function 'NS::Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("namespace NS {\n" + " class Fred\n" + " {\n" + " public:\n" + " struct AB {\n" + " struct CD { };\n" + " };\n" + " bool f(AB::CD * cd);\n" + " };\n" + "}\n" + "bool NS::Fred::f(NS::Fred::AB::CD * cd)\n" + "{\n" + "}"); + + ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:8]: (performance, inconclusive) Technically the member function 'NS::Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("class Foo {\n" + " class Fred\n" + " {\n" + " public:\n" + " struct AB {\n" + " struct CD { };\n" + " };\n" + " bool f(AB::CD * cd);\n" + " };\n" + "};\n" + "bool Foo::Fred::f(Foo::Fred::AB::CD * cd)\n" + "{\n" + "}"); + + ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:8]: (performance, inconclusive) Technically the member function 'Foo::Fred::f' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + } + + void const43() { // ticket 2377 + checkConst("class A\n" + "{\n" + "public:\n" + " void foo( AA::BB::CC::DD b );\n" + " AA::BB::CC::DD a;\n" + "};\n" + "void A::foo( AA::BB::CC::DD b )\n" + "{\n" + " a = b;\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + + checkConst("namespace AA\n" + "{\n" + " namespace BB\n" + " {\n" + " namespace CC\n" + " {\n" + " struct DD\n" + " {};\n" + " }\n" + " }\n" + "}\n" + "class A\n" + "{\n" + " public:\n" + "\n" + " AA::BB::CC::DD a;\n" + " void foo(AA::BB::CC::DD b)\n" + " {\n" + " a = b;\n" + " }\n" + "};"); + + ASSERT_EQUALS("", errout_str()); + + checkConst("namespace ZZ\n" + "{\n" + " namespace YY\n" + " {\n" + " struct XX\n" + " {};\n" + " }\n" + "}\n" + "class B\n" + "{\n" + " public:\n" + " ZZ::YY::XX a;\n" + " void foo(ZZ::YY::XX b)\n" + " {\n" + " a = b;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const44() { // ticket 2595 + checkConst("class A\n" + "{\n" + "public:\n" + " bool bOn;\n" + " bool foo()\n" + " {\n" + " return 0 != (bOn = bOn);\n" + " }\n" + "};"); + + ASSERT_EQUALS("", errout_str()); + } + + void const45() { // ticket 2664 + checkConst("namespace wraps {\n" + " class BaseLayout {};\n" + "}\n" + "namespace tools {\n" + " class WorkspaceControl :\n" + " public wraps::BaseLayout\n" + " {\n" + " int toGrid(int _value)\n" + " {\n" + " }\n" + " };\n" + "}"); + + ASSERT_EQUALS("[test.cpp:8]: (performance, inconclusive) Technically the member function 'tools::WorkspaceControl::toGrid' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + } + + void const46() { // ticket 2663 + checkConst("class Altren {\n" + "public:\n" + " int fun1() {\n" + " int a;\n" + " a++;\n" + " }\n" + " int fun2() {\n" + " b++;\n" + " }\n" + "};"); + + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Altren::fun1' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:7]: (performance, inconclusive) Technically the member function 'Altren::fun2' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + } + + void const47() { // ticket 2670 + checkConst("class Altren {\n" + "public:\n" + " void foo() { delete this; }\n" + " void foo(int i) const { }\n" + " void bar() { foo(); }\n" + "};"); + + ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Altren::foo' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("class Altren {\n" + "public:\n" + " void foo() { delete this; }\n" + " void foo(int i) const { }\n" + " void bar() { foo(1); }\n" + "};"); + + ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'Altren::foo' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:5]: (style, inconclusive) Technically the member function 'Altren::bar' can be const.\n", errout_str()); + } + + void const48() { // ticket 2672 + checkConst("class S0 {\n" + " class S1 {\n" + " class S2 {\n" + " class S3 {\n" + " class S4 { };\n" + " };\n" + " };\n" + " };\n" + "};\n" + "class TextIterator {\n" + " S0::S1::S2::S3::S4 mCurrent, mSave;\n" + "public:\n" + " bool setTagColour();\n" + "};\n" + "bool TextIterator::setTagColour() {\n" + " mSave = mCurrent;\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + } + + void const49() { // ticket 2795 + checkConst("class A {\n" + " private:\n" + " std::map _hash;\n" + " public:\n" + " A() : _hash() {}\n" + " unsigned int fetch(unsigned int key)\n" // cannot be 'const' + " {\n" + " return _hash[key];\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const50() { // ticket 2943 + checkConst("class Altren\n" + "{\n" + " class SubClass : public std::vector\n" + " {\n" + " };\n" + "};\n" + "void _setAlign()\n" + "{\n" + " if (mTileSize.height > 0) return;\n" + " if (mEmptyView) return;\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + } + + void const51() { // ticket 3040 + checkConst("class PSIPTable {\n" + "public:\n" + " PSIPTable() : _pesdata(0) { }\n" + " const unsigned char* pesdata() const { return _pesdata; }\n" + " unsigned char* pesdata() { return _pesdata; }\n" + " void SetSection(uint num) { pesdata()[6] = num; }\n" + "private:\n" + " unsigned char *_pesdata;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class PESPacket {\n" + "public:\n" + " PESPacket() : _pesdata(0) { }\n" + " const unsigned char* pesdata() const { return _pesdata; }\n" + " unsigned char* pesdata() { return _pesdata; }\n" + "private:\n" + " unsigned char *_pesdata;\n" + "};\n" + "class PSIPTable : public PESPacket\n" + "{\n" + "public:\n" + " void SetSection(uint num) { pesdata()[6] = num; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const52() { // ticket 3048 + checkConst("class foo {\n" + " void DoSomething(int &a) const { a = 1; }\n" + " void DoSomethingElse() { DoSomething(bar); }\n" + "private:\n" + " int bar;\n" + "};"); + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'foo::DoSomething' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + } + + void const53() { // ticket 3049 + checkConst("class A {\n" + " public:\n" + " A() : foo(false) {};\n" + " virtual bool One(bool b = false) { foo = b; return false; }\n" + " private:\n" + " bool foo;\n" + "};\n" + "class B : public A {\n" + " public:\n" + " B() {};\n" + " bool One(bool b = false) { return false; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const54() { // ticket 3052 + checkConst("class Example {\n" + " public:\n" + " void Clear(void) { Example tmp; (*this) = tmp; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const55() { + checkConst("class MyObject {\n" + " int tmp;\n" + " MyObject() : tmp(0) {}\n" + "public:\n" + " void set(std::stringstream &in) { in >> tmp; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const56() { // ticket #3149 + checkConst("class MyObject {\n" + "public:\n" + " void foo(int x) {\n" + " switch (x) { }\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("class A\n" + "{\n" + " protected:\n" + " unsigned short f (unsigned short X);\n" + " public:\n" + " A ();\n" + "};\n" + "\n" + "unsigned short A::f (unsigned short X)\n" + "{\n" + " enum ERetValues {RET_NOK = 0, RET_OK = 1};\n" + " enum ETypes {FLOAT_TYPE = 1, INT_TYPE = 2};\n" + "\n" + " try\n" + " {\n" + " switch (X)\n" + " {\n" + " case FLOAT_TYPE:\n" + " {\n" + " return RET_OK;\n" + " }\n" + " case INT_TYPE:\n" + " {\n" + " return RET_OK;\n" + " }\n" + " default:\n" + " {\n" + " return RET_NOK;\n" + " }\n" + " }\n" + " }\n" + " catch (...)\n" + " {\n" + " return RET_NOK;\n" + " }\n" + "\n" + " return RET_NOK;\n" + "}"); + ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:4]: (performance, inconclusive) Technically the member function 'A::f' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("class MyObject {\n" + "public:\n" + " void foo(int x) {\n" + " for (int i = 0; i < 5; i++) { }\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + } + + void const57() { // tickets #2669 and #2477 + checkConst("namespace MyGUI\n" + "{\n" + " namespace types\n" + " {\n" + " struct TSize {};\n" + " struct TCoord {\n" + " TSize size() const { }\n" + " };\n" + " }\n" + " typedef types::TSize IntSize;\n" + " typedef types::TCoord IntCoord;\n" + "}\n" + "class SelectorControl\n" + "{\n" + " MyGUI::IntSize getSize()\n" + " {\n" + " return mCoordValue.size();\n" + " }\n" + "private:\n" + " MyGUI::IntCoord mCoordValue;\n" + "};"); + TODO_ASSERT_EQUALS("[test.cpp:7]: (performance, inconclusive) Technically the member function 'MyGUI::types::TCoord::size' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:15]: (style, inconclusive) Technically the member function 'SelectorControl::getSize' can be const.\n", + "[test.cpp:7]: (performance, inconclusive) Technically the member function 'MyGUI::types::TCoord::size' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("struct Foo {\n" + " Bar b;\n" + " void foo(Foo f) {\n" + " b.run();\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct Bar {\n" + " int i = 0;\n" + " void run() { i++; }\n" + "};\n" + "struct Foo {\n" + " Bar b;\n" + " void foo(Foo f) {\n" + " b.run();\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct Bar {\n" + " void run() const { }\n" + "};\n" + "struct Foo {\n" + " Bar b;\n" + " void foo(Foo f) {\n" + " b.run();\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Bar::run' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:6]: (style, inconclusive) Technically the member function 'Foo::foo' can be const.\n", errout_str()); + } + + void const58() { + checkConst("struct MyObject {\n" + " void foo(Foo f) {\n" + " f.clear();\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("struct MyObject {\n" + " int foo(Foo f) {\n" + " return f.length();\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'MyObject::foo' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("struct MyObject {\n" + " Foo f;\n" + " int foo() {\n" + " return f.length();\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct MyObject {\n" + " std::string f;\n" + " int foo() {\n" + " return f.length();\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'MyObject::foo' can be const.\n", errout_str()); + } + + void const59() { // ticket #4646 + checkConst("class C {\n" + "public:\n" + " inline void operator += (const int &x ) { re += x; }\n" + " friend inline void exp(C & c, const C & x) { }\n" + "protected:\n" + " int re;\n" + " int im;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const60() { // ticket #3322 + checkConst("class MyString {\n" + "public:\n" + " MyString() : m_ptr(0){}\n" + " MyString& operator+=( const MyString& rhs ) {\n" + " delete m_ptr;\n" + " m_ptr = new char[42];\n" + " }\n" + " MyString append( const MyString& str )\n" + " { return operator+=( str ); }\n" + " char *m_ptr;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + checkConst("class MyString {\n" + "public:\n" + " MyString() : m_ptr(0){}\n" + " MyString& operator+=( const MyString& rhs );\n" + " MyString append( const MyString& str )\n" + " { return operator+=( str ); }\n" + " char *m_ptr;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const61() { // ticket #5606 - don't crash + // this code is invalid so a false negative is OK + checkConst("class MixerParticipant : public MixerParticipant {\n" + " int GetAudioFrame();\n" + "};\n" + "int MixerParticipant::GetAudioFrame() {\n" + " return 0;\n" + "}"); + + // this code is invalid so a false negative is OK + checkConst("class MixerParticipant : public MixerParticipant {\n" + " bool InitializeFileReader() {\n" + " printf(\"music\");\n" + " }\n" + "};"); + + // Based on an example from SVN source code causing an endless recursion within CheckClass::isConstMemberFunc() + // A more complete example including a template declaration like + // template class Hash{/* ... */}; + // didn't . + checkConst("template<>\n" + "class Hash {\n" + "protected:\n" + " typedef Key::key_type key_type;\n" + " void set(const Key& key);\n" + "};\n" + "template\n" + "class Hash : private Hash {\n" + " typedef Hash inherited;\n" + " void set(const Key& key) {\n" + " inherited::set(inherited::Key(key));\n" + " }\n" + "};\n", nullptr, false); + ASSERT_EQUALS("", errout_str()); + } + + void const62() { + checkConst("class A {\n" + " private:\n" + " std::unordered_map _hash;\n" + " public:\n" + " A() : _hash() {}\n" + " unsigned int fetch(unsigned int key)\n" // cannot be 'const' + " {\n" + " return _hash[key];\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const63() { + checkConst("struct A {\n" + " std::string s;\n" + " void clear() {\n" + " std::string* p = &s;\n" + " p->clear();\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct A {\n" + " std::string s;\n" + " void clear() {\n" + " std::string& r = s;\n" + " r.clear();\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct A {\n" + " std::string s;\n" + " void clear() {\n" + " std::string& r = sth; r = s;\n" + " r.clear();\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::clear' can be const.\n", errout_str()); + + checkConst("struct A {\n" + " std::string s;\n" + " void clear() {\n" + " const std::string* p = &s;\n" + " p->somefunction();\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::clear' can be const.\n", errout_str()); + + checkConst("struct A {\n" + " std::string s;\n" + " void clear() {\n" + " const std::string& r = s;\n" + " r.somefunction();\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'A::clear' can be const.\n", errout_str()); + } + + void const64() { + checkConst("namespace B {\n" + " namespace D {\n" + " typedef int DKIPtr;\n" + " }\n" + " class ZClass {\n" + " void set(const ::B::D::DKIPtr& p) {\n" + " membervariable = p;\n" + " }\n" + " ::B::D::DKIPtr membervariable;\n" + " };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void const65() { + checkConst("template \n" + "class TemplateClass {\n" + "public:\n" + " TemplateClass() { }\n" + "};\n" + "template <>\n" + "class TemplateClass {\n" + "public:\n" + " TemplateClass() { }\n" + "};\n" + "int main() {\n" + " TemplateClass a;\n" + " TemplateClass b;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void const66() { + checkConst("struct C {\n" + " C() : n(0) {}\n" + " void f(int v) { g((char *) &v); }\n" + " void g(char *) { n++; }\n" + " int n;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const67() { // #9193 + checkConst("template >\n" + "class TestList {\n" + "public:\n" + " LIST_T m_list;\n" + "};\n" + "class Test {\n" + "public:\n" + " const std::list>& get() { return m_test.m_list; }\n" + " TestList> m_test;\n" + "};"); + ASSERT_EQUALS("[test.cpp:8]: (style, inconclusive) Technically the member function 'Test::get' can be const.\n", errout_str()); + } + + void const68() { // #6471 + checkConst("class MyClass {\n" + " void clear() {\n" + " SVecPtr v = (SVecPtr) m_data;\n" + " v->clear();\n" + " }\n" + " void* m_data;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void const69() { // #9806 + checkConst("struct A {\n" + " int a = 0;\n" + " template void call(const Args &... args) { a = 1; }\n" + " template auto call(const Args &... args) -> T {\n" + " a = 2;\n" + " return T{};\n" + " }\n" + "};\n" + "\n" + "struct B : public A {\n" + " void test() {\n" + " call();\n" + " call(1, 2, 3);\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const70() { + checkConst("struct A {\n" + " template void call(Args ... args) {\n" + " func(this);\n" + " }\n" + "\n" + " void test() {\n" + " call(1, 2);\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void const71() { // #10146 + checkConst("struct Bar {\n" + " int j = 5;\n" + " void f(int& i) const { i += j; }\n" + "};\n" + "struct Foo {\n" + " Bar bar;\n" + " int k{};\n" + " void g() { bar.f(k); }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct S {\n" + " A a;\n" + " void f(int j, int*& p) {\n" + " p = &(((a[j])));\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void const72() { // #10520 + checkConst("struct S {\n" + " explicit S(int* p) : mp(p) {}\n" + " int* mp{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() { return S{ &i }; }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct S {\n" + " explicit S(int* p) : mp(p) {}\n" + " int* mp{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() { return S(&i); }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct S {\n" + " int* mp{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() { return S{ &i }; }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct S {\n" + " int* mp{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() { return { &i }; }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct S {\n" + " explicit S(const int* p) : mp(p) {}\n" + " const int* mp{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() { return S{ &i }; }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'C::f' can be const.\n", errout_str()); + + checkConst("struct S {\n" + " explicit S(const int* p) : mp(p) {}\n" + " const int* mp{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() { return S(&i); }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'C::f' can be const.\n", errout_str()); + + checkConst("struct S {\n" + " const int* mp{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() { return S{ &i }; }\n" + "};\n"); + TODO_ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'C::f' can be const.\n", "", errout_str()); + + checkConst("struct S {\n" + " const int* mp{};\n" + "};\n" + "struct C {\n" + " int i{};\n" + " S f() { return { &i }; }\n" + "};\n"); + TODO_ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'C::f' can be const.\n", "", errout_str()); + } + + void const73() { + checkConst("struct A {\n" + " int* operator[](int i);\n" + " const int* operator[](int i) const;\n" + "};\n" + "struct S {\n" + " A a;\n" + " void f(int j) {\n" + " int* p = a[j];\n" + " *p = 0;\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct S {\n" // #10758 + " T* h;\n" + " void f(); \n" + "};\n" + "void S::f() {\n" + " char* c = h->x[y];\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Technically the member function 'S::f' can be const.\n", errout_str()); + } + + void const74() { // #10671 + checkConst("class A {\n" + " std::vector m_str;\n" + "public:\n" + " A() {}\n" + " void bar(void) {\n" + " for(std::vector::const_iterator it = m_str.begin(); it != m_str.end(); ++it) {;}\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'A::bar' can be const.\n", errout_str()); + + // Don't crash + checkConst("struct S {\n" + " std::vector v;\n" + " void f() const;\n" + "};\n" + "void S::f() const {\n" + " for (std::vector::const_iterator it = v.begin(), end = v.end(); it != end; ++it) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void const75() { // #10065 + checkConst("namespace N { int i = 0; }\n" + "struct S {\n" + " int i;\n" + " void f() {\n" + " if (N::i) {}\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'S::f' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("int i = 0;\n" + "struct S {\n" + " int i;\n" + " void f() {\n" + " if (::i) {}\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4]: (performance, inconclusive) Technically the member function 'S::f' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("namespace N {\n" + " struct S {\n" + " int i;\n" + " void f() {\n" + " if (N::S::i) {}\n" + " }\n" + " };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'N::S::f' can be const.\n", errout_str()); + } + + void const76() { // #10825 + checkConst("struct S {\n" + " enum E {};\n" + " void f(const T* t);\n" + " E e;\n" + "};\n" + "struct T { void e(); };\n" + "void S::f(const T* t) {\n" + " const_cast(t)->e();\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'S::f' can be static (but you may consider moving to unnamed namespace).\n", + errout_str()); + } + + void const77() { + checkConst("template \n" // #10307 + "struct S {\n" + " std::vector const* f() const { return p; }\n" + " std::vector const* p;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct S {\n" // #10311 + " std::vector v;\n" + " std::vector& f() { return v; }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void const78() { // #10315 + checkConst("struct S {\n" + " typedef void(S::* F)();\n" + " void g(F f);\n" + "};\n" + "void S::g(F f) {\n" + " (this->*f)();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct S {\n" + " using F = void(S::*)();\n" + " void g(F f);\n" + "};\n" + "void S::g(F f) {\n" + " (this->*f)();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void const79() { // #9861 + checkConst("class A {\n" + "public:\n" + " char* f() {\n" + " return nullptr;\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'A::f' can be static (but you may consider moving to unnamed namespace).\n", + errout_str()); + } + + void const80() { // #11328 + checkConst("struct B { static void b(); };\n" + "struct S : B {\n" + " static void f() {}\n" + " void g() const;\n" + " void h();\n" + " void k();\n" + " void m();\n" + " void n();\n" + " int i;\n" + "};\n" + "void S::g() const {\n" + " this->f();\n" + "}\n" + "void S::h() {\n" + " this->f();\n" + "}\n" + "void S::k() {\n" + " if (i)\n" + " this->f();\n" + "}\n" + "void S::m() {\n" + " this->B::b();\n" + "}\n" + "void S::n() {\n" + " this->h();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:4]: (performance, inconclusive) Technically the member function 'S::g' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:14] -> [test.cpp:5]: (performance, inconclusive) Technically the member function 'S::h' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:17] -> [test.cpp:6]: (style, inconclusive) Technically the member function 'S::k' can be const.\n" + "[test.cpp:21] -> [test.cpp:7]: (performance, inconclusive) Technically the member function 'S::m' can be static (but you may consider moving to unnamed namespace).\n", + errout_str()); + } + + void const81() { + checkConst("struct A {\n" // #11330 + " bool f() const;\n" + "};\n" + "struct S {\n" + " std::shared_ptr a;\n" + " void g() {\n" + " if (a->f()) {}\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:6]: (style, inconclusive) Technically the member function 'S::g' can be const.\n", + errout_str()); + + checkConst("struct A {\n" // #11499 + " void f() const;\n" + "};\n" + "template\n" + "struct P {\n" + " T* operator->();\n" + " const T* operator->() const;\n" + "};\n" + "struct S {\n" + " P p;\n" + " void g() { p->f(); }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:11]: (style, inconclusive) Technically the member function 'S::g' can be const.\n", + errout_str()); + + checkConst("struct A {\n" + " void f(int) const;\n" + "};\n" + "template\n" + "struct P {\n" + " T* operator->();\n" + " const T* operator->() const;\n" + "};\n" + "struct S {\n" + " P p;\n" + " void g() { p->f(1); }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:11]: (style, inconclusive) Technically the member function 'S::g' can be const.\n", errout_str()); + + checkConst("struct A {\n" + " void f(void*) const;\n" + "};\n" + "template\n" + "struct P {\n" + " T* operator->();\n" + " const T* operator->() const;\n" + " P& operator=(P) {\n" + " return *this;\n" + " }\n" + "};\n" + "struct S {\n" + " P p;\n" + " std::vector g() { p->f(nullptr); return {}; }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:14]: (style, inconclusive) Technically the member function 'S::g' can be const.\n", errout_str()); + + checkConst("struct A {\n" + " void f();\n" + "};\n" + "template\n" + "struct P {\n" + " T* operator->();\n" + " const T* operator->() const;\n" + "};\n" + "struct S {\n" + " P p;\n" + " void g() { p->f(); }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct A {\n" + " void f() const;\n" + "};\n" + "template\n" + "struct P {\n" + " T* operator->();\n" + "};\n" + "struct S {\n" + " P p;\n" + " void g() { p->f(); }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct A {\n" + " void f(int&) const;\n" + "};\n" + "template\n" + "struct P {\n" + " T* operator->();\n" + " const T* operator->() const;\n" + "};\n" + "struct S {\n" + " P p;\n" + " int i;\n" + " void g() { p->f(i); }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct A {\n" // #11501 + " enum E { E1 };\n" + " virtual void f(E) const = 0;\n" + "};\n" + "struct F {\n" + " A* a;\n" + " void g() { a->f(A::E1); }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:7]: (style, inconclusive) Technically the member function 'F::g' can be const.\n", errout_str()); + } + + void const82() { // #11513 + checkConst("struct S {\n" + " int i;\n" + " void h(bool) const;\n" + " void g() { h(i == 1); }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'S::g' can be const.\n", + errout_str()); + + checkConst("struct S {\n" + " int i;\n" + " void h(int, int*) const;\n" + " void g() { int a; h(i, &a); }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'S::g' can be const.\n", + errout_str()); + } + + void const83() { + checkConst("struct S {\n" + " int i1, i2;\n" + " void f(bool b);\n" + " void g(bool b, int j);\n" + "};\n" + "void S::f(bool b) {\n" + " int& r = b ? i1 : i2;\n" + " r = 5;\n" + "}\n" + "void S::g(bool b, int j) {\n" + " int& r = b ? j : i2;\n" + " r = 5;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void const84() { + checkConst("class S {};\n" // #11616 + "struct T {\n" + " T(const S*);\n" + " T(const S&);\n" + "};\n" + "struct C {\n" + " const S s;\n" + " void f1() {\n" + " T t(&s);\n" + " }\n" + " void f2() {\n" + " T t(s);\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:8]: (style, inconclusive) Technically the member function 'C::f1' can be const.\n" + "[test.cpp:11]: (style, inconclusive) Technically the member function 'C::f2' can be const.\n", + errout_str()); + } + + void const85() { // #11618 + checkConst("struct S {\n" + " int a[2], b[2];\n" + " void f() { f(a, b); }\n" + " static void f(const int p[2], int q[2]);\n" + "};\n" + "void S::f(const int p[2], int q[2]) {\n" + " q[0] = p[0];\n" + " q[1] = p[1];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void const86() { // #11621 + checkConst("struct S { int* p; };\n" + "struct T { int m; int* p; };\n" + "struct U {\n" + " int i;\n" + " void f() { S s = { &i }; }\n" + " void g() { int* a[] = { &i }; }\n" + " void h() { T t = { 1, &i }; }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void const87() { + checkConst("struct Tokenizer {\n" // #11720 + " bool isCPP() const {\n" + " return cpp;\n" + " }\n" + " bool cpp;\n" + "};\n" + "struct Check {\n" + " const Tokenizer* const mTokenizer;\n" + " const int* const mSettings;\n" + "};\n" + "struct CheckA : Check {\n" + " static bool test(const std::string& funcname, const int* settings, bool cpp);\n" + "};\n" + "struct CheckB : Check {\n" + " bool f(const std::string& s);\n" + "};\n" + "bool CheckA::test(const std::string& funcname, const int* settings, bool cpp) {\n" + " return !funcname.empty() && settings && cpp;\n" + "}\n" + "bool CheckB::f(const std::string& s) {\n" + " return CheckA::test(s, mSettings, mTokenizer->isCPP());\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:20] -> [test.cpp:15]: (style, inconclusive) Technically the member function 'CheckB::f' can be const.\n", errout_str()); + + checkConst("void g(int&);\n" + "struct S {\n" + " struct { int i; } a[1];\n" + " void f() { g(a[0].i); }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct S {\n" + " const int& g() const { return i; }\n" + " int i;\n" + "};\n" + "void h(int, const int&);\n" + "struct T {\n" + " S s;\n" + " int j;\n" + " void f() { h(j, s.g()); }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:9]: (style, inconclusive) Technically the member function 'T::f' can be const.\n", errout_str()); + + checkConst("struct S {\n" + " int& g() { return i; }\n" + " int i;\n" + "};\n" + "void h(int, int&);\n" + "struct T {\n" + " S s;\n" + " int j;\n" + " void f() { h(j, s.g()); }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct S {\n" + " const int& g() const { return i; }\n" + " int i;\n" + "};\n" + "void h(int, const int*);\n" + "struct T {\n" + " S s;\n" + " int j;\n" + " void f() { h(j, &s.g()); }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:9]: (style, inconclusive) Technically the member function 'T::f' can be const.\n", errout_str()); + + checkConst("struct S {\n" + " int& g() { return i; }\n" + " int i;\n" + "};\n" + "void h(int, int*);\n" + "struct T {\n" + " S s;\n" + " int j;\n" + " void f() { h(j, &s.g()); }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkConst("void j(int** x);\n" + "void k(int* const* y);\n" + "struct S {\n" + " int* p;\n" + " int** q;\n" + " int* const* r;\n" + " void f1() { j(&p); }\n" + " void f2() { j(q); }\n" + " void g1() { k(&p); }\n" + " void g2() { k(q); }\n" + " void g3() { k(r); }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkConst("void m(int*& r);\n" + "void n(int* const& s);\n" + "struct T {\n" + " int i;\n" + " int* p;\n" + " void f1() { m(p); }\n" + " void f2() { n(&i); }\n" + " void f3() { n(p); }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void const88() { // #11626 + checkConst("struct S {\n" + " bool f() { return static_cast(p); }\n" + " const int* g() { return const_cast(p); }\n" + " const int* h() { return (const int*)p; }\n" + " char* j() { return reinterpret_cast(p); }\n" + " char* k() { return (char*)p; }\n" + " int* p;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Technically the member function 'S::f' can be const.\n" + "[test.cpp:3]: (style, inconclusive) Technically the member function 'S::g' can be const.\n" + "[test.cpp:4]: (style, inconclusive) Technically the member function 'S::h' can be const.\n", + errout_str()); + + checkConst("struct S {\n" + " bool f() { return p != nullptr; }\n" + " std::shared_ptr p;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Technically the member function 'S::f' can be const.\n", + errout_str()); + } + + void const89() { + checkConst("struct S {\n" // #11654 + " void f(bool b);\n" + " int i;\n" + "};\n" + "void S::f(bool b) {\n" + " if (i && b)\n" + " f(false);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:2]: (style, inconclusive) Technically the member function 'S::f' can be const.\n", errout_str()); + + checkConst("struct S {\n" + " void f(int& r);\n" + " int i;\n" + "};\n" + "void S::f(int& r) {\n" + " r = 0;\n" + " if (i)\n" + " f(i);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct S {\n" // #11744 + " S* p;\n" + " int f() {\n" + " if (p)\n" + " return 1 + p->f();\n" + " return 1;\n" + " }\n" + " int g(int i) {\n" + " if (i > 0)\n" + " return i + g(i - 1);\n" + " return 0;\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3]: (style, inconclusive) Technically the member function 'S::f' can be const.\n" + "[test.cpp:8]: (performance, inconclusive) Technically the member function 'S::g' can be static (but you may consider moving to unnamed namespace).\n", + errout_str()); + + checkConst("class C {\n" // #11653 + "public:\n" + " void f(bool b) const;\n" + "};\n" + "void C::f(bool b) const {\n" + " if (b)\n" + " f(false);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (performance, inconclusive) Technically the member function 'C::f' can be static (but you may consider moving to unnamed namespace).\n", + errout_str()); + } + + void const90() { // #11637 + checkConst("class S {};\n" + "struct C {\n" + " C(const S*);\n" + " C(const S&);\n" + "};\n" + "class T {\n" + " S s;\n" + " void f1() { C c = C{ &s }; }\n" + " void f2() { C c = C{ s }; }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:8]: (style, inconclusive) Technically the member function 'T::f1' can be const.\n" + "[test.cpp:9]: (style, inconclusive) Technically the member function 'T::f2' can be const.\n", + errout_str()); + } + + void const91() { // #11790 + checkConst("struct S {\n" + " char* p;\n" + " template \n" + " T* get() {\n" + " return reinterpret_cast(p);\n" + " }\n" + "};\n" + "const int* f(S& s) {\n" + " return s.get();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void const92() { // #11886 + checkConst("void g(int);\n" + "template\n" + "struct S : public S {\n" + " void f() {\n" + " g(n - 1);\n" + " }\n" + "};\n" + "template<>\n" + "struct S<0> {};\n" + "struct D : S<150> {};\n"); + // don't hang + // we are not interested in the output - just consume it + ignore_errout(); + } + + void const93() { // #12162 + checkConst("struct S {\n" + " bool f() {\n" + " return m.cbegin()->first == 0;\n" + " }\n" + " bool g() {\n" + " return m.count(0);\n" + " }\n" + " std::map m;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2]: (style, inconclusive) Technically the member function 'S::f' can be const.\n" + "[test.cpp:5]: (style, inconclusive) Technically the member function 'S::g' can be const.\n", + errout_str()); + } + + void const_handleDefaultParameters() { + checkConst("struct Foo {\n" + " void foo1(int i, int j = 0) {\n" + " return func(this);\n" + " }\n" + " int bar1() {\n" + " return foo1(1);\n" + " }\n" + " int bar2() {\n" + " return foo1(1, 2);\n" + " }\n" + " int bar3() {\n" + " return foo1(1, 2, 3);\n" + " }\n" + " int bar4() {\n" + " return foo1();\n" + " }\n" + " void foo2(int i = 0) {\n" + " return func(this);\n" + " }\n" + " int bar5() {\n" + " return foo2();\n" + " }\n" + " void foo3() {\n" + " return func(this);\n" + " }\n" + " int bar6() {\n" + " return foo3();\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:11]: (performance, inconclusive) Technically the member function 'Foo::bar3' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:14]: (performance, inconclusive) Technically the member function 'Foo::bar4' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + } + + void const_passThisToMemberOfOtherClass() { + checkConst("struct Foo {\n" + " void foo() {\n" + " Bar b;\n" + " b.takeFoo(this);\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct Foo {\n" + " void foo() {\n" + " Foo f;\n" + " f.foo();\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Foo::foo' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("struct A;\n" // #5839 - operator() + "struct B {\n" + " void operator()(A *a);\n" + "};\n" + "struct A {\n" + " void dostuff() {\n" + " B()(this);\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void assigningPointerToPointerIsNotAConstOperation() { + checkConst("struct s\n" + "{\n" + " int** v;\n" + " void f()\n" + " {\n" + " v = 0;\n" + " }\n" + "};"); + 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" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + // increment/decrement => not const + void constincdec() { + checkConst("class Fred {\n" + " int a;\n" + " void nextA() { return ++a; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " int a;\n" + " void nextA() { return --a; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " int a;\n" + " void nextA() { return a++; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " int a;\n" + " void nextA() { return a--; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("int a;\n" + "class Fred {\n" + " void nextA() { return ++a; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("int a;\n" + "class Fred {\n" + " void nextA() { return --a; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("int a;\n" + "class Fred {\n" + " void nextA() { return a++; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("int a;\n" + "class Fred {\n" + " void nextA() { return a--; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("struct S {\n" // #10077 + " int i{};\n" + " S& operator ++() { ++i; return *this; }\n" + " S operator ++(int) { S s = *this; ++(*this); return s; }\n" + " void f() { (*this)--; }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void constassign1() { + checkConst("class Fred {\n" + " int a;\n" + " void nextA() { return a=1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " int a;\n" + " void nextA() { return a-=1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " int a;\n" + " void nextA() { return a+=1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " int a;\n" + " void nextA() { return a*=-1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " int a;\n" + " void nextA() { return a/=-2; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("int a;\n" + "class Fred {\n" + " void nextA() { return a=1; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("int a;\n" + "class Fred {\n" + " void nextA() { return a-=1; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("int a;\n" + "class Fred {\n" + " void nextA() { return a+=1; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("int a;\n" + "class Fred {\n" + " void nextA() { return a*=-1; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("int a;\n" + "class Fred {\n" + " void nextA() { return a/=-2; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + } + + void constassign2() { + checkConst("class Fred {\n" + " struct A { int a; } s;\n" + " void nextA() { return s.a=1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " struct A { int a; } s;\n" + " void nextA() { return s.a-=1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " struct A { int a; } s;\n" + " void nextA() { return s.a+=1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " struct A { int a; } s;\n" + " void nextA() { return s.a*=-1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct A { int a; } s;\n" + "class Fred {\n" + " void nextA() { return s.a=1; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("struct A { int a; } s;\n" + "class Fred {\n" + " void nextA() { return s.a-=1; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("struct A { int a; } s;\n" + "class Fred {\n" + " void nextA() { return s.a+=1; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("struct A { int a; } s;\n" + "class Fred {\n" + " void nextA() { return s.a*=-1; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("struct A { int a; } s;\n" + "class Fred {\n" + " void nextA() { return s.a/=-2; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("struct A { int a; };\n" + "class Fred {\n" + " A s;\n" + " void nextA() { return s.a=1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct A { int a; };\n" + "class Fred {\n" + " A s;\n" + " void nextA() { return s.a-=1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct A { int a; };\n" + "class Fred {\n" + " A s;\n" + " void nextA() { return s.a+=1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct A { int a; };\n" + "class Fred {\n" + " A s;\n" + " void nextA() { return s.a*=-1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("struct A { int a; };\n" + "class Fred {\n" + " A s;\n" + " void nextA() { return s.a/=-2; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + // increment/decrement array element => not const + void constincdecarray() { + checkConst("class Fred {\n" + " int a[2];\n" + " void nextA() { return ++a[0]; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " int a[2];\n" + " void nextA() { return --a[0]; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " int a[2];\n" + " void nextA() { return a[0]++; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " int a[2];\n" + " void nextA() { return a[0]--; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("int a[2];\n" + "class Fred {\n" + " void nextA() { return ++a[0]; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("int a[2];\n" + "class Fred {\n" + " void nextA() { return --a[0]; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("int a[2];\n" + "class Fred {\n" + " void nextA() { return a[0]++; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("int a[2];\n" + "class Fred {\n" + " void nextA() { return a[0]--; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + } + + void constassignarray() { + checkConst("class Fred {\n" + " int a[2];\n" + " void nextA() { return a[0]=1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " int a[2];\n" + " void nextA() { return a[0]-=1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " int a[2];\n" + " void nextA() { return a[0]+=1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " int a[2];\n" + " void nextA() { return a[0]*=-1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class Fred {\n" + " int a[2];\n" + " void nextA() { return a[0]/=-2; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("int a[2];\n" + "class Fred {\n" + " void nextA() { return a[0]=1; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("int a[2];\n" + "class Fred {\n" + " void nextA() { return a[0]-=1; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("int a[2];\n" + "class Fred {\n" + " void nextA() { return a[0]+=1; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("int a[2];\n" + "class Fred {\n" + " void nextA() { return a[0]*=-1; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst("int a[2];\n" + "class Fred {\n" + " void nextA() { return a[0]/=-2; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'Fred::nextA' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + } + + // return pointer/reference => not const + void constReturnReference() { + checkConst("class Fred {\n" + " int a;\n" + " int &getR() { return a; }\n" + " int *getP() { return &a; }" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + // delete member variable => not const (but technically it can, it compiles without errors) + void constDelete() { + checkConst("class Fred {\n" + " int *a;\n" + " void clean() { delete a; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + // A function that returns unknown types can't be const (#1579) + void constLPVOID() { + checkConst("class Fred {\n" + " UNKNOWN a() { return 0; };\n" + "};"); + TODO_ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::a' can be static.\n", "", errout_str()); + + // #1579 - HDC + checkConst("class Fred {\n" + " foo bar;\n" + " UNKNOWN a() { return b; };\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + // a function that calls const functions can be const + void constFunc() { + checkConst("class Fred {\n" + " void f() const { };\n" + " void a() { f(); };\n" + "};"); + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Technically the member function 'Fred::f' can be static (but you may consider moving to unnamed namespace).\n" + "[test.cpp:3]: (style, inconclusive) Technically the member function 'Fred::a' can be const.\n", errout_str()); + + // ticket #1593 + checkConst("class A\n" + "{\n" + " std::vector m_v;\n" + "public:\n" + " A(){}\n" + " unsigned int GetVecSize() {return m_v.size();}\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (style, inconclusive) Technically the member function 'A::GetVecSize' can be const.\n", errout_str()); + + checkConst("class A\n" + "{\n" + " std::vector m_v;\n" + "public:\n" + " A(){}\n" + " bool GetVecEmpty() {return m_v.empty();}\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (style, inconclusive) Technically the member function 'A::GetVecEmpty' can be const.\n", errout_str()); + } + + void constVirtualFunc() { + // base class has no virtual function + checkConst("class A { };\n" + "class B : public A {\n" + " int b;\n" + "public:\n" + " B() : b(0) { }\n" + " int func() { return b; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (style, inconclusive) Technically the member function 'B::func' can be const.\n", errout_str()); + + checkConst("class A { };\n" + "class B : public A {\n" + " int b;\n" + "public:\n" + " B() : b(0) { }\n" + " int func();\n" + "};\n" + "int B::func() { return b; }"); + ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:6]: (style, inconclusive) Technically the member function 'B::func' can be const.\n", errout_str()); + + // base class has no virtual function + checkConst("class A {\n" + "public:\n" + " int func();\n" + "};\n" + "class B : public A {\n" + " int b;\n" + "public:\n" + " B() : b(0) { }\n" + " int func() { return b; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:9]: (style, inconclusive) Technically the member function 'B::func' can be const.\n", errout_str()); + + checkConst("class A {\n" + "public:\n" + " int func();\n" + "};\n" + "class B : public A {\n" + " int b;\n" + "public:\n" + " B() : b(0) { }\n" + " int func();\n" + "};\n" + "int B::func() { return b; }"); + ASSERT_EQUALS("[test.cpp:11] -> [test.cpp:9]: (style, inconclusive) Technically the member function 'B::func' can be const.\n", errout_str()); + + // base class has virtual function + checkConst("class A {\n" + "public:\n" + " virtual int func();\n" + "};\n" + "class B : public A {\n" + " int b;\n" + "public:\n" + " B() : b(0) { }\n" + " int func() { return b; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class A {\n" + "public:\n" + " virtual int func();\n" + "};\n" + "class B : public A {\n" + " int b;\n" + "public:\n" + " B() : b(0) { }\n" + " int func();\n" + "};\n" + "int B::func() { return b; }"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class A {\n" + "public:\n" + " virtual int func() = 0;\n" + "};\n" + "class B : public A {\n" + " int b;\n" + "public:\n" + " B() : b(0) { }\n" + " int func();\n" + "};\n" + "int B::func() { return b; }"); + ASSERT_EQUALS("", errout_str()); + + // base class has no virtual function + checkConst("class A {\n" + " int a;\n" + "public:\n" + " A() : a(0) { }\n" + " int func() { return a; }\n" + "};\n" + "class B : public A {\n" + " int b;\n" + "public:\n" + " B() : b(0) { }\n" + " int func() { return b; }\n" + "};\n" + "class C : public B {\n" + " int c;\n" + "public:\n" + " C() : c(0) { }\n" + " int func() { return c; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'A::func' can be const.\n" + "[test.cpp:11]: (style, inconclusive) Technically the member function 'B::func' can be const.\n" + "[test.cpp:17]: (style, inconclusive) Technically the member function 'C::func' can be const.\n", errout_str()); + + checkConst("class A {\n" + " int a;\n" + "public:\n" + " A() : a(0) { }\n" + " int func();\n" + "};\n" + "int A::func() { return a; }\n" + "class B : public A {\n" + " int b;\n" + "public:\n" + " B() : b(0) { }\n" + " int func();\n" + "};\n" + "int B::func() { return b; }\n" + "class C : public B {\n" + " int c;\n" + "public:\n" + " C() : c(0) { }\n" + " int func();\n" + "};\n" + "int C::func() { return c; }"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:5]: (style, inconclusive) Technically the member function 'A::func' can be const.\n" + "[test.cpp:14] -> [test.cpp:12]: (style, inconclusive) Technically the member function 'B::func' can be const.\n" + "[test.cpp:21] -> [test.cpp:19]: (style, inconclusive) Technically the member function 'C::func' can be const.\n", errout_str()); + + // base class has virtual function + checkConst("class A {\n" + " int a;\n" + "public:\n" + " A() : a(0) { }\n" + " virtual int func() { return a; }\n" + "};\n" + "class B : public A {\n" + " int b;\n" + "public:\n" + " B() : b(0) { }\n" + " int func() { return b; }\n" + "};\n" + "class C : public B {\n" + " int c;\n" + "public:\n" + " C() : c(0) { }\n" + " int func() { return c; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkConst("class A {\n" + " int a;\n" + "public:\n" + " A() : a(0) { }\n" + " virtual int func();\n" + "};\n" + "int A::func() { return a; }\n" + "class B : public A {\n" + " int b;\n" + "public:\n" + " B() : b(0) { }\n" + " int func();\n" + "};\n" + "int B::func() { return b; }\n" + "class C : public B {\n" + " int c;\n" + "public:\n" + " C() : c(0) { }\n" + " int func();\n" + "};\n" + "int C::func() { return c; }"); + ASSERT_EQUALS("", errout_str()); + + // ticket #1311 + checkConst("class X {\n" + " int x;\n" + "public:\n" + " X(int x) : x(x) { }\n" + " int getX() { return x; }\n" + "};\n" + "class Y : public X {\n" + " int y;\n" + "public:\n" + " Y(int x, int y) : X(x), y(y) { }\n" + " int getY() { return y; }\n" + "};\n" + "class Z : public Y {\n" + " int z;\n" + "public:\n" + " Z(int x, int y, int z) : Y(x, y), z(z) { }\n" + " int getZ() { return z; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Technically the member function 'X::getX' can be const.\n" + "[test.cpp:11]: (style, inconclusive) Technically the member function 'Y::getY' can be const.\n" + "[test.cpp:17]: (style, inconclusive) Technically the member function 'Z::getZ' can be const.\n", errout_str()); + + checkConst("class X {\n" + " int x;\n" + "public:\n" + " X(int x) : x(x) { }\n" + " int getX();\n" + "};\n" + "int X::getX() { return x; }\n" + "class Y : public X {\n" + " int y;\n" + "public:\n" + " Y(int x, int y) : X(x), y(y) { }\n" + " int getY();\n" + "};\n" + "int Y::getY() { return y; }\n" + "class Z : public Y {\n" + " int z;\n" + "public:\n" + " Z(int x, int y, int z) : Y(x, y), z(z) { }\n" + " int getZ();\n" + "};\n" + "int Z::getZ() { return z; }"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:5]: (style, inconclusive) Technically the member function 'X::getX' can be const.\n" + "[test.cpp:14] -> [test.cpp:12]: (style, inconclusive) Technically the member function 'Y::getY' can be const.\n" + "[test.cpp:21] -> [test.cpp:19]: (style, inconclusive) Technically the member function 'Z::getZ' can be const.\n", errout_str()); + } + + void constIfCfg() { + const char code[] = "struct foo {\n" + " int i;\n" + " void f() {\n" + //"#ifdef ABC\n" + //" i = 4;\n" + //"endif\n" + " }\n" + "};"; + + checkConst(code, &settings0, true); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Technically the member function 'foo::f' can be static (but you may consider moving to unnamed namespace).\n", errout_str()); + + checkConst(code, &settings0, false); // TODO: Set inconclusive to true (preprocess it) + ASSERT_EQUALS("", errout_str()); + } + + void constFriend() { // ticket #1921 + const char code[] = "class foo {\n" + " friend void f() { }\n" + "};"; + checkConst(code); + ASSERT_EQUALS("", errout_str()); + } + + void constUnion() { // ticket #2111 + checkConst("class foo {\n" + "public:\n" + " union {\n" + " int i;\n" + " float f;\n" + " } d;\n" + " void setf(float x) {\n" + " d.f = x;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void constArrayOperator() { + checkConst("struct foo {\n" + " int x;\n" + " int y[5][724];\n" + " T a() {\n" + " return y[x++][6];\n" + " }\n" + " T b() {\n" + " return y[1][++x];\n" + " }\n" + " T c() {\n" + " return y[1][6];\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:10]: (style, inconclusive) Technically the member function 'foo::c' can be const.\n", errout_str()); + } + + void constRangeBasedFor() { // #5514 + checkConst("class Fred {\n" + " int array[256];\n" + "public:\n" + " void f1() {\n" + " for (auto & e : array)\n" + " foo(e);\n" + " }\n" + " void f2() {\n" + " for (const auto & e : array)\n" + " foo(e);\n" + " }\n" + " void f3() {\n" + " for (decltype(auto) e : array)\n" + " foo(e);\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:8]: (style, inconclusive) Technically the member function 'Fred::f2' can be const.\n", errout_str()); + } + + void const_shared_ptr() { // #8674 + checkConst("class Fred {\n" + "public:\n" + " std::shared_ptr getData();\n" + "private:\n" + " std::shared_ptr data;\n" + "};\n" + "\n" + "std::shared_ptr Fred::getData() { return data; }"); + ASSERT_EQUALS("", errout_str()); + } + + void constPtrToConstPtr() { + checkConst("class Fred {\n" + "public:\n" + " const char *const *data;\n" + " const char *const *getData() { return data; }\n}"); + ASSERT_EQUALS("[test.cpp:4]: (style, inconclusive) Technically the member function 'Fred::getData' can be const.\n", errout_str()); + } + + void constTrailingReturnType() { // #9814 + checkConst("struct A {\n" + " int x = 1;\n" + " auto get() -> int & { return x; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void staticArrayPtrOverload() { + checkConst("struct S {\n" + " template\n" + " void f(const std::array& sv);\n" + " template\n" + " void f(const char* const (&StrArr)[N]);\n" + "};\n" + "template\n" + "void S::f(const std::array& sv) {\n" + " const char* ptrs[N]{};\n" + " return f(ptrs);\n" + "}\n" + "template void S::f(const std::array& sv);\n"); + ASSERT_EQUALS("", errout_str()); + } + + void qualifiedNameMember() { // #10872 + const Settings s = settingsBuilder().severity(Severity::style).debugwarnings().library("std.cfg").build(); + checkConst("struct data {};\n" + " struct S {\n" + " std::vector std;\n" + " void f();\n" + "};\n" + "void S::f() {\n" + " std::vector::const_iterator end = std.end();\n" + "}\n", &s); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:4]: (style, inconclusive) Technically the member function 'S::f' can be const.\n", errout_str()); + } + +#define checkInitializerListOrder(code) checkInitializerListOrder_(code, __FILE__, __LINE__) + void checkInitializerListOrder_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings2, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + CheckClass checkClass(&tokenizer, &settings2, this); + checkClass.initializerListOrder(); + } + + void initializerListOrder() { + checkInitializerListOrder("class Fred {\n" + " int a, b, c;\n" + "public:\n" + " Fred() : c(0), b(0), a(0) { }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Member variable 'Fred::b' is in the wrong place in the initializer list.\n" + "[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Member variable 'Fred::a' is in the wrong place in the initializer list.\n", errout_str()); + + checkInitializerListOrder("class Fred {\n" + " int a, b, c;\n" + "public:\n" + " Fred() : c{0}, b{0}, a{0} { }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Member variable 'Fred::b' is in the wrong place in the initializer list.\n" + "[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Member variable 'Fred::a' is in the wrong place in the initializer list.\n", errout_str()); + + checkInitializerListOrder("struct S {\n" + " S() : b(a = 1) {}\n" + " int a, b;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializerListOrder("struct S {\n" + " int nCols() const;\n" + " int nRows() const;\n" + "};\n" + "struct B {\n" + " const char* m_name;\n" + " int nCols;\n" + " int nRows;\n" + " B(const char* p_name, int nR, int nC)\n" + " : m_name(p_name)\n" + " , nCols(nC)\n" + " , nRows(nR)\n" + " {}\n" + "};\n" + "struct D : public B {\n" + " const int m_i;\n" + " D(const S& s, int _i)\n" + " : B(\"abc\", s.nRows(), s.nCols())\n" + " , m_i(_i)\n" + " {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void initializerListArgument() { + checkInitializerListOrder("struct A { A(); };\n" // #12322 + "struct B { explicit B(const A* a); };\n" + "struct C {\n" + " C() : b(&a) {}\n" + " B b;\n" + " const A a;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (style, inconclusive) Member variable 'C::b' uses an uninitialized argument 'a' due to the order of declarations.\n", + errout_str()); + + checkInitializerListOrder("struct S {\n" + " S(const std::string& f, std::string i, int b, int c) : a(0), b(b), c(c) {}\n" + " int a, b, c;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializerListOrder("struct S {\n" + " S() : p(a) {}\n" + " int* p;\n" + " int a[1];\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializerListOrder("struct S {\n" + " S() : p(&i) {}\n" + " int* p;\n" + " int i;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializerListOrder("struct S {\n" + " S() : a(b = 1) {}\n" + " int a, b;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializerListOrder("struct S {\n" + " S() : r(i) {}\n" + " int& r;\n" + " int i{};\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializerListOrder("struct B {\n" + " int a{}, b{};\n" + "};\n" + "struct D : B {\n" + " D() : B(), j(b) {}\n" + " int j;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializerListOrder("struct S {\n" + " S() : a(i) {}\n" + " int a;\n" + " static int i;\n" + "};\n" + "int S::i = 0;"); + ASSERT_EQUALS("", errout_str()); + + checkInitializerListOrder("struct S {\n" + " S(int b) : a(b) {}\n" + " int a, b{};\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializerListOrder("class Foo {\n" // #3524 + "public:\n" + " Foo(int arg) : a(b), b(arg) {}\n" + " int a;\n" + " int b;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Member variable 'Foo::a' uses an uninitialized argument 'b' due to the order of declarations.\n", + errout_str()); + } + +#define checkInitializationListUsage(code) checkInitializationListUsage_(code, __FILE__, __LINE__) + void checkInitializationListUsage_(const char code[], const char* file, int line) { + // Check.. + const Settings settings = settingsBuilder().severity(Severity::performance).build(); + + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + CheckClass checkClass(&tokenizer, &settings, this); + checkClass.initializationListUsage(); + } + + void initializerListUsage() { + checkInitializationListUsage("enum Enum { C = 0 };\n" + "class Fred {\n" + " int a;\n" // No message for builtin types: No performance gain + " int* b;\n" // No message for pointers: No performance gain + " Enum c;\n" // No message for enums: No performance gain + " Fred() { a = 0; b = 0; c = C; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializationListUsage("class Fred {\n" + " std::string s;\n" + " Fred() { a = 0; s = \"foo\"; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance) Variable 's' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout_str()); + + checkInitializationListUsage("class Fred {\n" + " std::string& s;\n" // Message is invalid for references, since their initialization in initializer list is required anyway and behaves different from assignment (#5004) + " Fred(const std::string& s_) : s(s_) { s = \"foo\"; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializationListUsage("class Fred {\n" + " std::vector v;\n" + " Fred() { v = unknown; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance) Variable 'v' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout_str()); + + checkInitializationListUsage("class C { std::string s; };\n" + "class Fred {\n" + " C c;\n" + " Fred() { c = unknown; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (performance) Variable 'c' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout_str()); + + checkInitializationListUsage("class C;\n" + "class Fred {\n" + " C c;\n" + " Fred() { c = unknown; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (performance) Variable 'c' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout_str()); + + checkInitializationListUsage("class C;\n" + "class Fred {\n" + " C c;\n" + " Fred(Fred const & other) { c = other.c; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (performance) Variable 'c' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout_str()); + + checkInitializationListUsage("class C;\n" + "class Fred {\n" + " C c;\n" + " Fred(Fred && other) { c = other.c; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (performance) Variable 'c' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout_str()); + + checkInitializationListUsage("class C;\n" + "class Fred {\n" + " C a;\n" + " Fred() { initB(); a = b; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializationListUsage("class C;\n" + "class Fred {\n" + " C a;\n" + " Fred() : a(0) { if(b) a = 0; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializationListUsage("class C;\n" + "class Fred {\n" + " C a[5];\n" + " Fred() { for(int i = 0; i < 5; i++) a[i] = 0; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializationListUsage("class C;\n" + "class Fred {\n" + " C a; int b;\n" + " Fred() : b(5) { a = b; }\n" // Don't issue a message here: You actually could move it to the initialization list, but it would cause problems if you change the order of the variable declarations. + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializationListUsage("class C;\n" + "class Fred {\n" + " C a;\n" + " Fred() { try { a = new int; } catch(...) {} }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializationListUsage("class Fred {\n" + " std::string s;\n" + " Fred() { s = toString((size_t)this); }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializationListUsage("class Fred {\n" + " std::string a;\n" + " std::string foo();\n" + " Fred() { a = foo(); }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializationListUsage("class Fred {\n" + " std::string a;\n" + " Fred() { a = foo(); }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (performance) Variable 'a' is assigned in constructor body. Consider performing initialization in initialization list.\n", errout_str()); + + checkInitializationListUsage("class Fred {\n" // #4332 + " static std::string s;\n" + " Fred() { s = \"foo\"; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializationListUsage("class Fred {\n" // #5640 + " std::string s;\n" + " Fred() {\n" + " char str[2];\n" + " str[0] = c;\n" + " str[1] = 0;\n" + " s = str;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializationListUsage("class B {\n" // #5640 + " std::shared_ptr _d;\n" + " B(const B& other) : _d(std::make_shared()) {\n" + " *_d = *other._d;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializationListUsage("class Bar {\n" // #8466 + "public:\n" + " explicit Bar(const Bar &bar) : Bar{bar.s} {}\n" + " explicit Bar(const char s) : s{s} {}\n" + "private:\n" + " char s;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializationListUsage("unsigned bar(std::string);\n" // #8291 + "class Foo {\n" + "public:\n" + " int a_, b_;\n" + " Foo(int a, int b) : a_(a), b_(b) {}\n" + " Foo(int a, const std::string& b) : Foo(a, bar(b)) {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializationListUsage("class Fred {\n" // #8111 + " std::string a;\n" + " Fred() {\n" + " std::ostringstream ostr;\n" + " ostr << x;\n" + " a = ostr.str();\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // bailout: multi line lambda in rhs => do not warn + checkInitializationListUsage("class Fred {\n" + " std::function f;\n" + " Fred() {\n" + " f = [](){\n" + " return 1;\n" + " };\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // don't warn if some other instance's members are assigned to + checkInitializationListUsage("class C {\n" + "public:\n" + " C(C& c) : m_i(c.m_i) { c.m_i = (Foo)-1; }\n" + "private:\n" + " Foo m_i;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkInitializationListUsage("class A {\n" // #9821 - delegate constructor + "public:\n" + " A() : st{} {}\n" + "\n" + " explicit A(const std::string &input): A() {\n" + " st = input;\n" + " }\n" + "\n" + "private:\n" + " std::string st;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + +#define checkSelfInitialization(code) checkSelfInitialization_(code, __FILE__, __LINE__) + void checkSelfInitialization_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings0, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + CheckClass checkClass(&tokenizer, &settings0, this); + (checkClass.checkSelfInitialization)(); + } + + void selfInitialization() { + checkSelfInitialization("class Fred {\n" + " int i;\n" + " Fred() : i(i) {\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (error) Member variable 'i' is initialized by itself.\n", errout_str()); + + checkSelfInitialization("class Fred {\n" + " int i;\n" + " Fred() : i{i} {\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (error) Member variable 'i' is initialized by itself.\n", errout_str()); + + checkSelfInitialization("class Fred {\n" + " int i;\n" + " Fred();\n" + "};\n" + "Fred::Fred() : i(i) {\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Member variable 'i' is initialized by itself.\n", errout_str()); + + checkSelfInitialization("class A {\n" // #10427 + "public:\n" + " explicit A(int x) : _x(static_cast(_x)) {}\n" + "private:\n" + " int _x;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Member variable '_x' is initialized by itself.\n", errout_str()); + + checkSelfInitialization("class A {\n" + "public:\n" + " explicit A(int x) : _x((int)(_x)) {}\n" + "private:\n" + " int _x;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Member variable '_x' is initialized by itself.\n", errout_str()); + + checkSelfInitialization("class Fred {\n" + " std::string s;\n" + " Fred() : s(s) {\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (error) Member variable 's' is initialized by itself.\n", errout_str()); + + checkSelfInitialization("class Fred {\n" + " int x;\n" + " Fred(int x);\n" + "};\n" + "Fred::Fred(int x) : x(x) { }"); + ASSERT_EQUALS("", errout_str()); + + checkSelfInitialization("class Fred {\n" + " int x;\n" + " Fred(int x);\n" + "};\n" + "Fred::Fred(int x) : x{x} { }"); + ASSERT_EQUALS("", errout_str()); + + checkSelfInitialization("class Fred {\n" + " std::string s;\n" + " Fred(const std::string& s) : s(s) {\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkSelfInitialization("class Fred {\n" + " std::string s;\n" + " Fred(const std::string& s) : s{s} {\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkSelfInitialization("struct Foo : Bar {\n" + " int i;\n" + " Foo(int i)\n" + " : Bar(\"\"), i(i) {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkSelfInitialization("struct Foo : std::Bar {\n" // #6073 + " int i;\n" + " Foo(int i)\n" + " : std::Bar(\"\"), i(i) {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkSelfInitialization("struct Foo : std::Bar {\n" // #6073 + " int i;\n" + " Foo(int i)\n" + " : std::Bar(\"\"), i{i} {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + +#define checkVirtualFunctionCall(...) checkVirtualFunctionCall_(__FILE__, __LINE__, __VA_ARGS__) + void checkVirtualFunctionCall_(const char* file, int line, const char code[], bool inconclusive = true) { + // Check.. + const Settings settings = settingsBuilder().severity(Severity::warning).severity(Severity::style).certainty(Certainty::inconclusive, inconclusive).build(); + + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + CheckClass checkClass(&tokenizer, &settings, this); + checkClass.checkVirtualFunctionCallInConstructor(); + } + + void virtualFunctionCallInConstructor() { + checkVirtualFunctionCall("class A\n" + "{\n" + " virtual int f() { return 1; }\n" + " A();\n" + "};\n" + "A::A()\n" + "{f();}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (style) Virtual function 'f' is called from constructor 'A()' at line 7. Dynamic binding is not used.\n", errout_str()); + + checkVirtualFunctionCall("class A {\n" + " virtual int f();\n" + " A() {f();}\n" + "};\n" + "int A::f() { return 1; }"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (style) Virtual function 'f' is called from constructor 'A()' at line 3. Dynamic binding is not used.\n", errout_str()); + + checkVirtualFunctionCall("class A : B {\n" + " int f() override;\n" + " A() {f();}\n" + "};\n" + "int A::f() { return 1; }"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (style) Virtual function 'f' is called from constructor 'A()' at line 3. Dynamic binding is not used.\n", errout_str()); + + checkVirtualFunctionCall("class B {\n" + " virtual int f() = 0;\n" + "};\n" + "class A : B {\n" + " int f();\n" // <- not explicitly virtual + " A() {f();}\n" + "};\n" + "int A::f() { return 1; }"); + ASSERT_EQUALS("", errout_str()); + + checkVirtualFunctionCall("class A\n" + "{\n" + " A() { A::f(); }\n" + " virtual void f() {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkVirtualFunctionCall("class A : B {\n" + " int f() final { return 1; }\n" + " A() { f(); }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkVirtualFunctionCall("class B {\n" + "public:" + " virtual void f() {}\n" + "};\n" + "class A : B {\n" + "public:" + " void f() override final {}\n" + " A() { f(); }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkVirtualFunctionCall("class Base {\n" + "public:\n" + " virtual void Copy(const Base& Src) = 0;\n" + "};\n" + "class Derived : public Base {\n" + "public:\n" + " Derived() : i(0) {}\n" + " Derived(const Derived& Src);\n" + " void Copy(const Base& Src) override;\n" + " int i;\n" + "};\n" + "Derived::Derived(const Derived& Src) {\n" + " Copy(Src);\n" + "}\n" + "void Derived::Copy(const Base& Src) {\n" + " auto d = dynamic_cast(Src);\n" + " i = d.i;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:13] -> [test.cpp:9]: (style) Virtual function 'Copy' is called from copy constructor 'Derived(const Derived&Src)' at line 13. Dynamic binding is not used.\n", + errout_str()); + + checkVirtualFunctionCall("struct B {\n" + " B() { auto pf = &f; }\n" + " virtual void f() {}\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkVirtualFunctionCall("struct B {\n" + " B() { auto pf = &B::f; }\n" + " virtual void f() {}\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkVirtualFunctionCall("struct B {\n" + " B() { (f)(); }\n" + " virtual void f() {}\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Virtual function 'f' is called from constructor 'B()' at line 2. Dynamic binding is not used.\n", errout_str()); + + checkVirtualFunctionCall("class S {\n" // don't crash + " ~S();\n" + "public:\n" + " S();\n" + "};\n" + "S::S() {\n" + " typeid(S);\n" + "}\n" + "S::~S() = default;\n"); + ASSERT_EQUALS("", errout_str()); + + checkVirtualFunctionCall("struct Base: { virtual void wibble() = 0; virtual ~Base() {} };\n" // #11167 + "struct D final : public Base {\n" + " void wibble() override;\n" + " D() {}\n" + " virtual ~D() { wibble(); }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void pureVirtualFunctionCall() { + checkVirtualFunctionCall("class A\n" + "{\n" + " virtual void pure()=0;\n" + " A();\n" + "};\n" + "A::A()\n" + "{pure();}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout_str()); + + checkVirtualFunctionCall("class A\n" + "{\n" + " virtual int pure()=0;\n" + " A();\n" + " int m;\n" + "};\n" + "A::A():m(A::pure())\n" + "{}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout_str()); + + checkVirtualFunctionCall("namespace N {\n" + " class A\n" + " {\n" + " virtual int pure() = 0;\n" + " A();\n" + " int m;\n" + " };\n" + "}\n" + "N::A::A() : m(N::A::pure()) {}\n"); + ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:4]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout_str()); + + checkVirtualFunctionCall("class A\n" + " {\n" + " virtual void pure()=0;\n" + " virtual ~A();\n" + " int m;\n" + "};\n" + "A::~A()\n" + "{pure();}"); + ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout_str()); + + checkVirtualFunctionCall("class A\n" + " {\n" + " virtual void pure()=0;\n" + " void nonpure()\n" + " {pure();}\n" + " A();\n" + "};\n" + "A::A()\n" + "{nonpure();}"); + ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:5] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout_str()); + + checkVirtualFunctionCall("class A\n" + " {\n" + " virtual int pure()=0;\n" + " int nonpure()\n" + " {return pure();}\n" + " A();\n" + " int m;\n" + "};\n" + "A::A():m(nonpure())\n" + "{}"); + TODO_ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:5] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", "", errout_str()); + + checkVirtualFunctionCall("class A\n" + " {\n" + " virtual void pure()=0;\n" + " void nonpure()\n" + " {pure();}\n" + " virtual ~A();\n" + " int m;\n" + "};\n" + "A::~A()\n" + "{nonpure();}"); + ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:5] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout_str()); + + checkVirtualFunctionCall("class A\n" + "{\n" + " virtual void pure()=0;\n" + " A(bool b);\n" + "};\n" + "A::A(bool b)\n" + "{if (b) pure();}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in constructor.\n", errout_str()); + + checkVirtualFunctionCall("class A\n" + "{\n" + " virtual void pure()=0;\n" + " virtual ~A();\n" + " int m;\n" + "};\n" + "A::~A()\n" + "{if (b) pure();}"); + ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (warning) Call of pure virtual function 'pure' in destructor.\n", errout_str()); + + // #5831 + checkVirtualFunctionCall("class abc {\n" + "public:\n" + " virtual ~abc() throw() {}\n" + " virtual void def(void* g) throw () = 0;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // #4992 + checkVirtualFunctionCall("class CMyClass {\n" + " std::function< void(void) > m_callback;\n" + "public:\n" + " CMyClass() {\n" + " m_callback = [this]() { return VirtualMethod(); };\n" + " }\n" + " virtual void VirtualMethod() = 0;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // #10559 + checkVirtualFunctionCall("struct S {\n" + " S(const int x) : m(std::bind(&S::f, this, x, 42)) {}\n" + " virtual int f(const int x, const int y) = 0;\n" + " std::function m;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void pureVirtualFunctionCallOtherClass() { + checkVirtualFunctionCall("class A\n" + "{\n" + " virtual void pure()=0;\n" + " A(const A & a);\n" + "};\n" + "A::A(const A & a)\n" + "{a.pure();}"); + ASSERT_EQUALS("", errout_str()); + + checkVirtualFunctionCall("class A\n" + "{\n" + " virtual void pure()=0;\n" + " A();\n" + "};\n" + "class B\n" + "{\n" + " virtual void pure()=0;\n" + "};\n" + "A::A()\n" + "{B b; b.pure();}"); + ASSERT_EQUALS("", errout_str()); + } + + void pureVirtualFunctionCallWithBody() { + checkVirtualFunctionCall("class A\n" + "{\n" + " virtual void pureWithBody()=0;\n" + " A();\n" + "};\n" + "A::A()\n" + "{pureWithBody();}\n" + "void A::pureWithBody()\n" + "{}"); + ASSERT_EQUALS("", errout_str()); + + checkVirtualFunctionCall("class A\n" + " {\n" + " virtual void pureWithBody()=0;\n" + " void nonpure()\n" + " {pureWithBody();}\n" + " A();\n" + "};\n" + "A::A()\n" + "{nonpure();}\n" + "void A::pureWithBody()\n" + "{}"); + ASSERT_EQUALS("", errout_str()); + + } + + void pureVirtualFunctionCallPrevented() { + checkVirtualFunctionCall("class A\n" + " {\n" + " virtual void pure()=0;\n" + " void nonpure(bool bCallPure)\n" + " { if (bCallPure) pure();}\n" + " A();\n" + "};\n" + "A::A()\n" + "{nonpure(false);}"); + ASSERT_EQUALS("", errout_str()); + + checkVirtualFunctionCall("class A\n" + " {\n" + " virtual void pure()=0;\n" + " void nonpure(bool bCallPure)\n" + " { if (!bCallPure) ; else pure();}\n" + " A();\n" + "};\n" + "A::A()\n" + "{nonpure(false);}"); + ASSERT_EQUALS("", errout_str()); + + checkVirtualFunctionCall("class A\n" + " {\n" + " virtual void pure()=0;\n" + " void nonpure(bool bCallPure)\n" + " {\n" + " switch (bCallPure) {\n" + " case true: pure(); break;\n" + " }\n" + " }\n" + " A();\n" + "};\n" + "A::A()\n" + "{nonpure(false);}"); + ASSERT_EQUALS("", errout_str()); + } + + +#define checkOverride(code) checkOverride_(code, __FILE__, __LINE__) + void checkOverride_(const char code[], const char* file, int line) { + const Settings settings = settingsBuilder().severity(Severity::style).build(); + + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check.. + CheckClass checkClass(&tokenizer, &settings, this); + (checkClass.checkOverride)(); + } + + void override1() { + checkOverride("class Base { virtual void f(); };\n" + "class Derived : Base { virtual void f(); };"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2]: (style) The function 'f' overrides a function in a base class but is not marked with a 'override' specifier.\n", errout_str()); + + checkOverride("class Base { virtual void f(); };\n" + "class Derived : Base { virtual void f() override; };"); + ASSERT_EQUALS("", errout_str()); + + checkOverride("class Base { virtual void f(); };\n" + "class Derived : Base { virtual void f() final; };"); + ASSERT_EQUALS("", errout_str()); + + checkOverride("class Base {\n" + "public:\n" + " virtual auto foo( ) const -> size_t { return 1; }\n" + " virtual auto bar( ) const -> size_t { return 1; }\n" + "};\n" + "class Derived : public Base {\n" + "public :\n" + " auto foo( ) const -> size_t { return 0; }\n" + " auto bar( ) const -> size_t override { return 0; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:8]: (style) The function 'foo' overrides a function in a base class but is not marked with a 'override' specifier.\n", errout_str()); + + checkOverride("namespace Test {\n" + " class C {\n" + " public:\n" + " virtual ~C();\n" + " };\n" + "}\n" + "class C : Test::C {\n" + "public:\n" + " ~C();\n" + "};"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:9]: (style) The destructor '~C' overrides a destructor in a base class but is not marked with a 'override' specifier.\n", errout_str()); + + checkOverride("struct Base {\n" + " virtual void foo();\n" + "};\n" + "\n" + "struct Derived: public Base {\n" + " void foo() override;\n" + " void foo(int);\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkOverride("struct B {\n" // #9092 + " virtual int f(int i) const = 0;\n" + "};\n" + "namespace N {\n" + " struct D : B {\n" + " virtual int f(int i) const;\n" + " };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:6]: (style) The function 'f' overrides a function in a base class but is not marked with a 'override' specifier.\n", errout_str()); + + checkOverride("struct A {\n" + " virtual void f(int);\n" + "};\n" + "struct D : A {\n" + " void f(double);\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkOverride("struct A {\n" + " virtual void f(int);\n" + "};\n" + "struct D : A {\n" + " void f(int);\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (style) The function 'f' overrides a function in a base class but is not marked with a 'override' specifier.\n", errout_str()); + + checkOverride("struct A {\n" + " virtual void f(char, int);\n" + "};\n" + "struct D : A {\n" + " void f(char, int);\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (style) The function 'f' overrides a function in a base class but is not marked with a 'override' specifier.\n", errout_str()); + + checkOverride("struct A {\n" + " virtual void f(char, int);\n" + "};\n" + "struct D : A {\n" + " void f(char, double);\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkOverride("struct A {\n" + " virtual void f(char, int);\n" + "};\n" + "struct D : A {\n" + " void f(char c = '\\0', double);\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkOverride("struct A {\n" + " virtual void f(char, int);\n" + "};\n" + "struct D : A {\n" + " void f(char c = '\\0', int);\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (style) The function 'f' overrides a function in a base class but is not marked with a 'override' specifier.\n", errout_str()); + + checkOverride("struct A {\n" + " virtual void f(char c, std::vector);\n" + "};\n" + "struct D : A {\n" + " void f(char c, std::vector);\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkOverride("struct A {\n" + " virtual void f(char c, std::vector);\n" + "};\n" + "struct D : A {\n" + " void f(char c, std::set);\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkOverride("struct A {\n" + " virtual void f(char c, std::vector v);\n" + "};\n" + "struct D : A {\n" + " void f(char c, std::vector w = {});\n" + "};\n"); + TODO_ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (style) The function 'f' overrides a function in a base class but is not marked with a 'override' specifier.\n", "", errout_str()); + + checkOverride("struct T {};\n" // #10920 + "struct B {\n" + " virtual T f() = 0;\n" + "};\n" + "struct D : B {\n" + " friend T f();\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkOverride("struct S {};\n" // #11827 + "struct SPtr {\n" + " virtual S* operator->() const { return p; }\n" + " S* p = nullptr;\n" + "};\n" + "struct T : public S {};\n" + "struct TPtr : public SPtr {\n" + " T* operator->() const { return (T*)p; }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:8]: (style) The function 'operator->' overrides a function in a base class but is not marked with a 'override' specifier.\n", + errout_str()); + + checkOverride("class Base {\n" // #12131 + " virtual int Calculate(int arg) = 0;\n" + "};\n" + "class Derived : public Base {\n" + " int Calculate(int arg = 0) {\n" + " return arg * 2;\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (style) The function 'Calculate' overrides a function in a base class but is not marked with a 'override' specifier.\n", errout_str()); + + checkOverride("struct S {\n" // #12439 + " virtual ~S() = default;\n" + "};\n" + "struct D : S {\n" + " ~D() {}\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (style) The destructor '~D' overrides a destructor in a base class but is not marked with a 'override' specifier.\n", + errout_str()); + } + + void overrideCVRefQualifiers() { + checkOverride("class Base { virtual void f(); };\n" + "class Derived : Base { void f() const; }"); + ASSERT_EQUALS("", errout_str()); + + checkOverride("class Base { virtual void f(); };\n" + "class Derived : Base { void f() volatile; }"); + ASSERT_EQUALS("", errout_str()); + + checkOverride("class Base { virtual void f(); };\n" + "class Derived : Base { void f() &; }"); + ASSERT_EQUALS("", errout_str()); + + checkOverride("class Base { virtual void f(); };\n" + "class Derived : Base { void f() &&; }"); + ASSERT_EQUALS("", errout_str()); + } + + #define checkUselessOverride(...) checkUselessOverride_(__FILE__, __LINE__, __VA_ARGS__) + void checkUselessOverride_(const char* file, int line, const char code[]) { + const Settings settings = settingsBuilder().severity(Severity::style).build(); + + std::vector files(1, "test.cpp"); + Tokenizer tokenizer(settings, *this); + PreprocessorHelper::preprocess(code, files, tokenizer, *this); + + ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); + + // Check.. + CheckClass checkClass(&tokenizer, &settings, this); + (checkClass.checkUselessOverride)(); + } + + void uselessOverride() { + checkUselessOverride("struct B { virtual int f() { return 5; } };\n" // #11757 + "struct D : B {\n" + " int f() override { return B::f(); }\n" + "};"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:3]: (style) The function 'f' overrides a function in a base class but just delegates back to the base class.\n", errout_str()); + + checkUselessOverride("struct B { virtual void f(); };\n" + "struct D : B {\n" + " void f() override { B::f(); }\n" + "};"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:3]: (style) The function 'f' overrides a function in a base class but just delegates back to the base class.\n", errout_str()); + + checkUselessOverride("struct B { virtual int f() = 0; };\n" + "int B::f() { return 5; }\n" + "struct D : B {\n" + " int f() override { return B::f(); }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkUselessOverride("struct B { virtual int f(int i); };\n" + "struct D : B {\n" + " int f(int i) override { return B::f(i); }\n" + "};"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:3]: (style) The function 'f' overrides a function in a base class but just delegates back to the base class.\n", errout_str()); + + checkUselessOverride("struct B { virtual int f(int i); };\n" + "struct D : B {\n" + " int f(int i) override { return B::f(i + 1); }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkUselessOverride("struct B { virtual int f(int i, int j); };\n" + "struct D : B {\n" + " int f(int i, int j) override { return B::f(j, i); }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkUselessOverride("struct B { virtual int f(); };\n" + "struct I { virtual int f() = 0; };\n" + "struct D : B, I {\n" + " int f() override { return B::f(); }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkUselessOverride("struct S { virtual void f(); };\n" + "struct D : S {\n" + " void f() final { S::f(); }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkUselessOverride("struct S {\n" + "protected:\n" + " virtual void f();\n" + "};\n" + "struct D : S {\n" + "public:\n" + " void f() override { S::f(); }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkUselessOverride("struct B { virtual void f(int, int, int) const; };\n" // #11799 + "struct D : B {\n" + " int m = 42;\n" + " void f(int a, int b, int c) const override;\n" + "};\n" + "void D::f(int a, int b, int c) const {\n" + " B::f(a, b, m);\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkUselessOverride("struct B {\n" // #11803 + " virtual void f();\n" + " virtual void f(int i);\n" + "};\n" + "struct D : B {\n" + " void f() override { B::f(); }\n" + " void f(int i) override;\n" + " void g() { f(); }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkUselessOverride("struct B { virtual void f(); };\n" // #11808 + "struct D : B { void f() override {} };\n" + "struct D2 : D {\n" + " void f() override {\n" + " B::f();\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkUselessOverride("struct B {\n" + " virtual int f() { return 1; }\n" + " virtual int g() { return 7; }\n" + " virtual int h(int i, int j) { return i + j; }\n" + " virtual int j(int i, int j) { return i + j; }\n" + "};\n" + "struct D : B {\n" + " int f() override { return 2; }\n" + " int g() override { return 7; }\n" + " int h(int j, int i) override { return i + j; }\n" + " int j(int i, int j) override { return i + j; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:9]: (style) The function 'g' overrides a function in a base class but is identical to the overridden function\n" + "[test.cpp:5] -> [test.cpp:11]: (style) The function 'j' overrides a function in a base class but is identical to the overridden function\n", + errout_str()); + + checkUselessOverride("struct B : std::exception {\n" + " virtual void f() { throw *this; }\n" + "};\n" + "struct D : B {\n" + " void f() override { throw *this; }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkUselessOverride("#define MACRO virtual void f() {}\n" + "struct B {\n" + " MACRO\n" + "};\n" + "struct D : B {\n" + " MACRO\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkUselessOverride("struct B {\n" + " B() = default;\n" + " explicit B(int i) : m(i) {}\n" + " int m{};\n" + " virtual int f() const { return m; }\n" + "};\n" + "struct D : B {\n" + " explicit D(int i) : m(i) {}\n" + " int m{};\n" + " int f() const override { return m; }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkUselessOverride("struct B {\n" + " int g() const;\n" + " virtual int f() const { return g(); }\n" + "};\n" + "struct D : B {\n" + " int g() const;\n" + " int f() const override { return g(); }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkUselessOverride("#define MACRO 1\n" + "struct B { virtual int f() { return 1; } };\n" + "struct D : B {\n" + " int f() override { return MACRO; }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + +#define checkUnsafeClassRefMember(code) checkUnsafeClassRefMember_(code, __FILE__, __LINE__) + void checkUnsafeClassRefMember_(const char code[], const char* file, int line) { + /*const*/ Settings settings = settingsBuilder().severity(Severity::warning).build(); + settings.safeChecks.classes = true; + + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check.. + CheckClass checkClass(&tokenizer, &settings, this); + (checkClass.checkUnsafeClassRefMember)(); + } + + void unsafeClassRefMember() { + checkUnsafeClassRefMember("class C { C(const std::string &s) : s(s) {} const std::string &s; };"); + ASSERT_EQUALS("[test.cpp:1]: (warning) Unsafe class: The const reference member 'C::s' is initialized by a const reference constructor argument. You need to be careful about lifetime issues.\n", errout_str()); + } + + +#define checkThisUseAfterFree(code) checkThisUseAfterFree_(code, __FILE__, __LINE__) + void checkThisUseAfterFree_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings1, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check.. + CheckClass checkClass(&tokenizer, &settings1, this); + (checkClass.checkThisUseAfterFree)(); + } + + void thisUseAfterFree() { + setMultiline(); + + // Calling method.. + checkThisUseAfterFree("class C {\n" + "public:\n" + " void dostuff() { delete mInstance; hello(); }\n" + "private:\n" + " static C *mInstance;\n" + " void hello() {}\n" + "};"); + ASSERT_EQUALS("test.cpp:3:warning:Calling method 'hello()' when 'this' might be invalid\n" + "test.cpp:5:note:Assuming 'mInstance' is used as 'this'\n" + "test.cpp:3:note:Delete 'mInstance', invalidating 'this'\n" + "test.cpp:3:note:Call method when 'this' is invalid\n", + errout_str()); + + checkThisUseAfterFree("class C {\n" + "public:\n" + " void dostuff() { mInstance.reset(); hello(); }\n" + "private:\n" + " static std::shared_ptr mInstance;\n" + " void hello() {}\n" + "};"); + ASSERT_EQUALS("test.cpp:3:warning:Calling method 'hello()' when 'this' might be invalid\n" + "test.cpp:5:note:Assuming 'mInstance' is used as 'this'\n" + "test.cpp:3:note:Delete 'mInstance', invalidating 'this'\n" + "test.cpp:3:note:Call method when 'this' is invalid\n", + errout_str()); + + checkThisUseAfterFree("class C {\n" + "public:\n" + " void dostuff() { reset(); hello(); }\n" + "private:\n" + " static std::shared_ptr mInstance;\n" + " void hello();\n" + " void reset() { mInstance.reset(); }\n" + "};"); + ASSERT_EQUALS("test.cpp:3:warning:Calling method 'hello()' when 'this' might be invalid\n" + "test.cpp:5:note:Assuming 'mInstance' is used as 'this'\n" + "test.cpp:7:note:Delete 'mInstance', invalidating 'this'\n" + "test.cpp:3:note:Call method when 'this' is invalid\n", + errout_str()); + + // Use member.. + checkThisUseAfterFree("class C {\n" + "public:\n" + " void dostuff() { delete self; x = 123; }\n" + "private:\n" + " static C *self;\n" + " int x;\n" + "};"); + ASSERT_EQUALS("test.cpp:3:warning:Using member 'x' when 'this' might be invalid\n" + "test.cpp:5:note:Assuming 'self' is used as 'this'\n" + "test.cpp:3:note:Delete 'self', invalidating 'this'\n" + "test.cpp:3:note:Call method when 'this' is invalid\n", + errout_str()); + + checkThisUseAfterFree("class C {\n" + "public:\n" + " void dostuff() { delete self; x[1] = 123; }\n" + "private:\n" + " static C *self;\n" + " std::map x;\n" + "};"); + ASSERT_EQUALS("test.cpp:3:warning:Using member 'x' when 'this' might be invalid\n" + "test.cpp:5:note:Assuming 'self' is used as 'this'\n" + "test.cpp:3:note:Delete 'self', invalidating 'this'\n" + "test.cpp:3:note:Call method when 'this' is invalid\n", + errout_str()); + + // Assign 'shared_from_this()' to non-static smart pointer + checkThisUseAfterFree("class C {\n" + "public:\n" + " void hold() { mInstance = shared_from_this(); }\n" + " void dostuff() { mInstance.reset(); hello(); }\n" + "private:\n" + " std::shared_ptr mInstance;\n" + " void hello() {}\n" + "};"); + ASSERT_EQUALS("test.cpp:4:warning:Calling method 'hello()' when 'this' might be invalid\n" + "test.cpp:6:note:Assuming 'mInstance' is used as 'this'\n" + "test.cpp:4:note:Delete 'mInstance', invalidating 'this'\n" + "test.cpp:4:note:Call method when 'this' is invalid\n", + errout_str()); + + // Avoid FP.. + checkThisUseAfterFree("class C {\n" + "public:\n" + " void dostuff() { delete self; x = 123; }\n" + "private:\n" + " C *self;\n" + " int x;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkThisUseAfterFree("class C {\n" + "public:\n" + " void hold() { mInstance = shared_from_this(); }\n" + " void dostuff() { if (x) { mInstance.reset(); return; } hello(); }\n" + "private:\n" + " std::shared_ptr mInstance;\n" + " void hello() {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + checkThisUseAfterFree("class C\n" + "{\n" + "public:\n" + " explicit C(const QString& path) : mPath( path ) {}\n" + "\n" + " static void initialize(const QString& path) {\n" // <- avoid fp in static method + " if (instanceSingleton)\n" + " delete instanceSingleton;\n" + " instanceSingleton = new C(path);\n" + " }\n" + "private:\n" + " static C* instanceSingleton;\n" + "};\n" + "\n" + "C* C::instanceSingleton;"); + ASSERT_EQUALS("", errout_str()); + + // Avoid false positive when pointer is deleted in lambda + checkThisUseAfterFree("class C {\n" + "public:\n" + " void foo();\n" + " void set() { p = this; }\n" + " void dostuff() {}\n" + " C* p;\n" + "};\n" + "\n" + "void C::foo() {\n" + " auto done = [this] () { delete p; };\n" + " dostuff();\n" + " done();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + + void ctu(const std::vector &code) { + Check &check = getCheck(); + + // getFileInfo + std::list fileInfo; + for (const std::string& c: code) { + Tokenizer tokenizer(settingsDefault, *this); + std::istringstream istr(c); + const std::string filename = std::to_string(fileInfo.size()) + ".cpp"; + ASSERT(tokenizer.list.createTokens(istr, filename)); + ASSERT(tokenizer.simplifyTokens1("")); + fileInfo.push_back(check.getFileInfo(tokenizer, settingsDefault)); + } + + // Check code.. + check.analyseWholeProgram(nullptr, fileInfo, settingsDefault, *this); + + while (!fileInfo.empty()) { + delete fileInfo.back(); + fileInfo.pop_back(); + } + } + + void ctuOneDefinitionRule() { + ctu({"class C { C() { std::cout << 0; } };", "class C { C() { std::cout << 1; } };"}); + ASSERT_EQUALS("[1.cpp:1] -> [0.cpp:1]: (error) The one definition rule is violated, different classes/structs have the same name 'C'\n", errout_str()); + + ctu({"class C { C(); }; C::C() { std::cout << 0; }", "class C { C(); }; C::C() { std::cout << 1; }"}); + ASSERT_EQUALS("[1.cpp:1] -> [0.cpp:1]: (error) The one definition rule is violated, different classes/structs have the same name 'C'\n", errout_str()); + + ctu({"class C { C() {} };\n", "class C { C() {} };\n"}); + ASSERT_EQUALS("", errout_str()); + + ctu({"class C { C(); }; C::C(){}", "class C { C(); }; C::C(){}"}); + ASSERT_EQUALS("", errout_str()); + + ctu({"class A::C { C() { std::cout << 0; } };", "class B::C { C() { std::cout << 1; } };"}); + ASSERT_EQUALS("", errout_str()); + + // 11435 - template specialisations + const std::string header = "template struct Test {};\n"; + ctu({header + "template struct Test {};\n", + header + "template struct Test {};\n"}); + ASSERT_EQUALS("", errout_str()); + } + + +#define getFileInfo(code) getFileInfo_(code, __FILE__, __LINE__) + void getFileInfo_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings1, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check.. + const Check& c = getCheck(); + Check::FileInfo * fileInfo = (c.getFileInfo)(tokenizer, settings1); + + delete fileInfo; + } + + void testGetFileInfo() { + getFileInfo("void foo() { union { struct { }; }; }"); // don't crash + getFileInfo("struct sometype { sometype(); }; sometype::sometype() = delete;"); // don't crash + } + +#define checkReturnByReference(...) checkReturnByReference_(__FILE__, __LINE__, __VA_ARGS__) + void checkReturnByReference_(const char* file, int line, const char code[]) { + const Settings settings = settingsBuilder().severity(Severity::performance).library("std.cfg").build(); + + std::vector files(1, "test.cpp"); + Tokenizer tokenizer(settings, *this); + PreprocessorHelper::preprocess(code, files, tokenizer, *this); + + ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); + + // Check.. + CheckClass checkClass(&tokenizer, &settings, this); + (checkClass.checkReturnByReference)(); + } + + void returnByReference() { + checkReturnByReference("struct T { int a[10]; };\n" // #12546 + "struct S {\n" + " T t;\n" + " int i;\n" + " std::string s;\n" + " T getT() const { return t; }\n" + " int getI() const { return i; }\n" + " std::string getS() const { return s; }\n" + " unknown_t f() { return; }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:6]: (performance) Function 'getT()' should return member 't' by const reference.\n" + "[test.cpp:8]: (performance) Function 'getS()' should return member 's' by const reference.\n", + errout_str()); + + checkReturnByReference("struct B {\n" // #12608 + " virtual std::string f() { return \"abc\"; }\n" + "};\n" + "struct D : B {\n" + " std::string f() override { return s; }\n" + " std::string s;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkReturnByReference("struct S {\n" + " std::string f(std::string s) { return s; }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + checkReturnByReference("struct S { S(); };\n" // #12620 + "S::S() = delete;\n"); + ASSERT_EQUALS("", errout_str()); // don't crash + } +}; + +REGISTER_TEST(TestClass) diff --git a/cppcheck-2.14.0/test/testcmdlineparser.cpp b/cppcheck-2.14.0/test/testcmdlineparser.cpp new file mode 100644 index 00000000..cde8d238 --- /dev/null +++ b/cppcheck-2.14.0/test/testcmdlineparser.cpp @@ -0,0 +1,2749 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "cmdlinelogger.h" +#include "cmdlineparser.h" +#include "config.h" +#include "cppcheckexecutor.h" +#include "errorlogger.h" +#include "errortypes.h" +#include "helpers.h" +#include "path.h" +#include "platform.h" +#include "redirect.h" +#include "settings.h" +#include "standards.h" +#include "suppressions.h" +#include "fixture.h" +#include "timer.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +class TestCmdlineParser : public TestFixture { +public: + TestCmdlineParser() : TestFixture("TestCmdlineParser") + {} + +private: + class CmdLineLoggerTest : public CmdLineLogger + { + public: + CmdLineLoggerTest() = default; + + void printMessage(const std::string &message) override + { + printInternal("cppcheck: " + message + '\n'); + } + + void printError(const std::string &message) override + { + printMessage("error: " + message); + } + + void printRaw(const std::string &message) override + { + printInternal(message + '\n'); + } + + std::string str() + { + std::string s; + std::swap(buf, s); + return s; + } + + void destroy() + { + if (!buf.empty()) + throw std::runtime_error("unconsumed messages: " + buf); + } + + private: + void printInternal(const std::string &message) + { + buf += message; + } + + std::string buf; + }; + + std::unique_ptr logger; + std::unique_ptr settings; + std::unique_ptr parser; + + void prepareTestInternal() override { + logger.reset(new CmdLineLoggerTest()); + settings.reset(new Settings()); + parser.reset(new CmdLineParser(*logger, *settings, settings->supprs)); + } + + void teardownTestInternal() override { + logger->destroy(); + } + + void asPremium() { + // set this so we think it is the premium + settings->cppcheckCfgProductName = "Cppcheck Premium 0.0.0"; + } + + void run() override { + TEST_CASE(nooptions); + TEST_CASE(helpshort); + TEST_CASE(helpshortExclusive); + TEST_CASE(helplong); + TEST_CASE(helplongExclusive); + TEST_CASE(version); + TEST_CASE(versionWithCfg); + TEST_CASE(versionExclusive); + TEST_CASE(versionWithInvalidCfg); + TEST_CASE(checkVersionCorrect); + TEST_CASE(checkVersionIncorrect); + TEST_CASE(onefile); + TEST_CASE(onepath); + TEST_CASE(optionwithoutfile); + TEST_CASE(verboseshort); + TEST_CASE(verboselong); + TEST_CASE(debugSimplified); + TEST_CASE(debugwarnings); + TEST_CASE(forceshort); + TEST_CASE(forcelong); + TEST_CASE(relativePaths1); + TEST_CASE(relativePaths2); + TEST_CASE(relativePaths3); + TEST_CASE(relativePaths4); + 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(enforceLanguage1); + TEST_CASE(enforceLanguage2); + TEST_CASE(enforceLanguage3); + TEST_CASE(enforceLanguage4); + TEST_CASE(enforceLanguage5); + TEST_CASE(enforceLanguage6); + TEST_CASE(enforceLanguage7); + TEST_CASE(includesnopath); + TEST_CASE(includes); + TEST_CASE(includesslash); + TEST_CASE(includesbackslash); + TEST_CASE(includesnospace); + TEST_CASE(includes2); + TEST_CASE(includesFile); + TEST_CASE(includesFileNoFile); + TEST_CASE(configExcludesFile); + TEST_CASE(configExcludesFileNoFile); + TEST_CASE(enabledAll); + TEST_CASE(enabledStyle); + TEST_CASE(enabledPerformance); + TEST_CASE(enabledPortability); + TEST_CASE(enabledInformation); + TEST_CASE(enabledUnusedFunction); + TEST_CASE(enabledMissingInclude); + TEST_CASE(disabledMissingIncludeWithInformation); + TEST_CASE(enabledMissingIncludeWithInformation); + TEST_CASE(enabledMissingIncludeWithInformationReverseOrder); +#ifdef CHECK_INTERNAL + TEST_CASE(enabledInternal); +#endif + TEST_CASE(enabledMultiple); + TEST_CASE(enabledInvalid); + TEST_CASE(enabledError); + TEST_CASE(enabledEmpty); + TEST_CASE(disableAll); + TEST_CASE(disableMultiple); + TEST_CASE(disableStylePartial); + TEST_CASE(disableInformationPartial); + TEST_CASE(disableInformationPartial2); + TEST_CASE(disableInvalid); + TEST_CASE(disableError); + TEST_CASE(disableEmpty); + TEST_CASE(inconclusive); + TEST_CASE(errorExitcode); + TEST_CASE(errorExitcodeMissing); + TEST_CASE(errorExitcodeStr); + TEST_CASE(exitcodeSuppressionsOld); + TEST_CASE(exitcodeSuppressions); + TEST_CASE(exitcodeSuppressionsNoFile); + TEST_CASE(fileFilterStdin); + TEST_CASE(fileList); + TEST_CASE(fileListNoFile); + TEST_CASE(fileListStdin); + TEST_CASE(fileListInvalid); + TEST_CASE(inlineSuppr); + TEST_CASE(jobs); + TEST_CASE(jobs2); + TEST_CASE(jobsMissingCount); + TEST_CASE(jobsInvalid); + TEST_CASE(jobsNoJobs); + TEST_CASE(jobsTooBig); + TEST_CASE(maxConfigs); + TEST_CASE(maxConfigsMissingCount); + TEST_CASE(maxConfigsInvalid); + TEST_CASE(maxConfigsTooSmall); + TEST_CASE(premiumOptions1); + TEST_CASE(premiumOptions2); + TEST_CASE(premiumOptions3); + TEST_CASE(premiumOptions4); + TEST_CASE(premiumOptions5); + TEST_CASE(premiumOptionsInvalid1); + TEST_CASE(premiumOptionsInvalid2); + TEST_CASE(premiumSafety); + TEST_CASE(reportProgress1); + TEST_CASE(reportProgress2); + TEST_CASE(reportProgress3); + TEST_CASE(reportProgress4); + TEST_CASE(reportProgress5); + TEST_CASE(stdc99); + TEST_CASE(stdcpp11); + TEST_CASE(stdunknown1); + TEST_CASE(stdunknown2); + TEST_CASE(platformWin64); + TEST_CASE(platformWin32A); + TEST_CASE(platformWin32W); + TEST_CASE(platformUnix32); + TEST_CASE(platformUnix32Unsigned); + TEST_CASE(platformUnix64); + TEST_CASE(platformUnix64Unsigned); + TEST_CASE(platformNative); + TEST_CASE(platformUnspecified); + TEST_CASE(platformPlatformFile); + TEST_CASE(platformUnknown); + TEST_CASE(plistEmpty); + TEST_CASE(plistDoesNotExist); + TEST_CASE(suppressionsOld); + TEST_CASE(suppressions); + TEST_CASE(suppressionsNoFile1); + TEST_CASE(suppressionsNoFile2); + TEST_CASE(suppressionsNoFile3); + 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(templatesCppcheck1); + TEST_CASE(templatesDaca2); + TEST_CASE(templatesSelfcheck); + TEST_CASE(templatesSimple); + TEST_CASE(templatesNoPlaceholder); + TEST_CASE(templateFormatInvalid); + TEST_CASE(templateFormatEmpty); + TEST_CASE(templateLocationInvalid); + TEST_CASE(templateLocationEmpty); + TEST_CASE(xml); + TEST_CASE(xmlver2); + TEST_CASE(xmlver2both); + TEST_CASE(xmlver2both2); + TEST_CASE(xmlverunknown); + TEST_CASE(xmlverinvalid); + TEST_CASE(doc); + TEST_CASE(docExclusive); + TEST_CASE(showtimeFile); + TEST_CASE(showtimeFileTotal); + TEST_CASE(showtimeTop5); + TEST_CASE(showtimeTop5File); + TEST_CASE(showtimeTop5Summary); + TEST_CASE(showtimeNone); + TEST_CASE(showtimeEmpty); + TEST_CASE(showtimeInvalid); + TEST_CASE(errorlist); + TEST_CASE(errorlistWithCfg); + TEST_CASE(errorlistExclusive); + TEST_CASE(errorlistWithInvalidCfg); + TEST_CASE(ignorepathsnopath); +#if defined(USE_WINDOWS_SEH) || defined(USE_UNIX_SIGNAL_HANDLING) + TEST_CASE(exceptionhandling); + TEST_CASE(exceptionhandling2); + TEST_CASE(exceptionhandling3); + TEST_CASE(exceptionhandlingInvalid); + TEST_CASE(exceptionhandlingInvalid2); +#else + TEST_CASE(exceptionhandlingNotSupported); + TEST_CASE(exceptionhandlingNotSupported2); +#endif + TEST_CASE(clang); + TEST_CASE(clang2); + TEST_CASE(clangInvalid); + TEST_CASE(valueFlowMaxIterations); + TEST_CASE(valueFlowMaxIterations2); + TEST_CASE(valueFlowMaxIterationsInvalid); + TEST_CASE(valueFlowMaxIterationsInvalid2); + TEST_CASE(valueFlowMaxIterationsInvalid3); + TEST_CASE(checksMaxTime); + TEST_CASE(checksMaxTime2); + TEST_CASE(checksMaxTimeInvalid); +#ifdef HAS_THREADING_MODEL_FORK + TEST_CASE(loadAverage); + TEST_CASE(loadAverage2); + TEST_CASE(loadAverageInvalid); +#else + TEST_CASE(loadAverageNotSupported); +#endif + TEST_CASE(maxCtuDepth); + TEST_CASE(maxCtuDepthInvalid); + TEST_CASE(performanceValueflowMaxTime); + TEST_CASE(performanceValueflowMaxTimeInvalid); + TEST_CASE(performanceValueFlowMaxIfCount); + TEST_CASE(performanceValueFlowMaxIfCountInvalid); + TEST_CASE(templateMaxTime); + TEST_CASE(templateMaxTimeInvalid); + TEST_CASE(templateMaxTimeInvalid2); + TEST_CASE(typedefMaxTime); + TEST_CASE(typedefMaxTimeInvalid); + TEST_CASE(typedefMaxTimeInvalid2); + TEST_CASE(templateMaxTime); + TEST_CASE(templateMaxTime); + TEST_CASE(project); + TEST_CASE(projectMultiple); + TEST_CASE(projectAndSource); + TEST_CASE(projectEmpty); + TEST_CASE(projectMissing); + TEST_CASE(projectNoPaths); + TEST_CASE(addon); + TEST_CASE(addonMissing); +#ifdef HAVE_RULES + TEST_CASE(rule); + TEST_CASE(ruleMissingPattern); +#else + TEST_CASE(ruleNotSupported); +#endif +#ifdef HAVE_RULES + TEST_CASE(ruleFileMulti); + TEST_CASE(ruleFileSingle); + TEST_CASE(ruleFileEmpty); + TEST_CASE(ruleFileMissing); + TEST_CASE(ruleFileInvalid); + TEST_CASE(ruleFileNoRoot); + TEST_CASE(ruleFileEmptyElements1); + TEST_CASE(ruleFileEmptyElements2); + TEST_CASE(ruleFileUnknownElement1); + TEST_CASE(ruleFileUnknownElement2); + TEST_CASE(ruleFileMissingTokenList); + TEST_CASE(ruleFileUnknownTokenList); + TEST_CASE(ruleFileMissingId); + TEST_CASE(ruleFileInvalidSeverity1); + TEST_CASE(ruleFileInvalidSeverity2); +#else + TEST_CASE(ruleFileNotSupported); +#endif + TEST_CASE(signedChar); + TEST_CASE(signedChar2); + TEST_CASE(unsignedChar); + TEST_CASE(unsignedChar2); + TEST_CASE(signedCharUnsignedChar); + TEST_CASE(library); + TEST_CASE(libraryMissing); + TEST_CASE(suppressXml); + TEST_CASE(suppressXmlEmpty); + TEST_CASE(suppressXmlMissing); + TEST_CASE(suppressXmlInvalid); + TEST_CASE(suppressXmlNoRoot); + TEST_CASE(executorDefault); + TEST_CASE(executorAuto); + TEST_CASE(executorAutoNoJobs); +#if defined(HAS_THREADING_MODEL_THREAD) + TEST_CASE(executorThread); + TEST_CASE(executorThreadNoJobs); +#else + TEST_CASE(executorThreadNotSupported); +#endif +#if defined(HAS_THREADING_MODEL_FORK) + TEST_CASE(executorProcess); + TEST_CASE(executorProcessNoJobs); +#else + TEST_CASE(executorProcessNotSupported); +#endif + TEST_CASE(checkLevelDefault); + TEST_CASE(checkLevelNormal); + TEST_CASE(checkLevelExhaustive); + TEST_CASE(checkLevelUnknown); + + TEST_CASE(ignorepaths1); + TEST_CASE(ignorepaths2); + TEST_CASE(ignorepaths3); + TEST_CASE(ignorepaths4); + TEST_CASE(ignorefilepaths1); + TEST_CASE(ignorefilepaths2); + TEST_CASE(ignorefilepaths3); + + TEST_CASE(checkconfig); + TEST_CASE(unknownParam); + + TEST_CASE(undefs_noarg); + TEST_CASE(undefs_noarg2); + TEST_CASE(undefs_noarg3); + TEST_CASE(undefs); + TEST_CASE(undefs2); + + TEST_CASE(cppcheckBuildDirExistent); + TEST_CASE(cppcheckBuildDirNonExistent); + TEST_CASE(cppcheckBuildDirEmpty); + + TEST_CASE(invalidCppcheckCfg); + } + + void nooptions() { + REDIRECT; + const char * const argv[] = {"cppcheck"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Exit, parser->parseFromArgs(1, argv)); + ASSERT(startsWith(logger->str(), "Cppcheck - A tool for static C/C++ code analysis")); + } + + void helpshort() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-h"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Exit, parser->parseFromArgs(2, argv)); + ASSERT(startsWith(logger->str(), "Cppcheck - A tool for static C/C++ code analysis")); + } + + void helpshortExclusive() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--library=missing", "-h"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Exit, parser->parseFromArgs(3, argv)); + ASSERT(startsWith(logger->str(), "Cppcheck - A tool for static C/C++ code analysis")); + } + + void helplong() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--help"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Exit, parser->parseFromArgs(2, argv)); + ASSERT(startsWith(logger->str(), "Cppcheck - A tool for static C/C++ code analysis")); + } + + void helplongExclusive() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--library=missing", "--help"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Exit, parser->parseFromArgs(3, argv)); + ASSERT(startsWith(logger->str(), "Cppcheck - A tool for static C/C++ code analysis")); + } + + void version() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--version"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Exit, parser->parseFromArgs(2, argv)); + ASSERT(logger->str().compare(0, 11, "Cppcheck 2.") == 0); + } + + void versionWithCfg() { + REDIRECT; + ScopedFile file(Path::join(Path::getPathFromFilename(Path::getCurrentExecutablePath("")), "cppcheck.cfg"), + "{\n" + "\"productName\": \"The Product\"" + "}\n"); + const char * const argv[] = {"cppcheck", "--version"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Exit, parser->parseFromArgs(2, argv)); + // TODO: somehow the config is not loaded on some systems + (void)logger->str(); //ASSERT_EQUALS("The Product\n", logger->str()); // TODO: include version? + } + + // TODO: test --version with extraVersion + + void versionExclusive() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--library=missing", "--version"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Exit, parser->parseFromArgs(3, argv)); + ASSERT(logger->str().compare(0, 11, "Cppcheck 2.") == 0); + } + + void versionWithInvalidCfg() { + REDIRECT; + ScopedFile file(Path::join(Path::getPathFromFilename(Path::getCurrentExecutablePath("")), "cppcheck.cfg"), + "{\n"); + const char * const argv[] = {"cppcheck", "--version"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: could not load cppcheck.cfg - not a valid JSON - syntax error at line 2 near: \n", logger->str()); + } + + void checkVersionCorrect() { + REDIRECT; + const std::string currentVersion = parser->getVersion(); + const std::string checkVersion = "--check-version=" + currentVersion; + const char * const argv[] = {"cppcheck", checkVersion.c_str(), "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + } + + void checkVersionIncorrect() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--check-version=Cppcheck 2.0", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: --check-version check failed. Aborting.\n", logger->str()); + } + + void onefile() { + REDIRECT; + const char * const argv[] = {"cppcheck", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS(1, (int)parser->getPathNames().size()); + ASSERT_EQUALS("file.cpp", parser->getPathNames().at(0)); + } + + void onepath() { + REDIRECT; + const char * const argv[] = {"cppcheck", "src"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS(1, (int)parser->getPathNames().size()); + ASSERT_EQUALS("src", parser->getPathNames().at(0)); + } + + void optionwithoutfile() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-v"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS(0, (int)parser->getPathNames().size()); + ASSERT_EQUALS("cppcheck: error: no C or C++ source files found.\n", logger->str()); + } + + void verboseshort() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-v", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->verbose); + } + + void verboselong() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--verbose", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->verbose); + } + + void debugSimplified() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--debug-simplified", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->debugSimplified); + } + + void debugwarnings() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--debug-warnings", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->debugwarnings); + } + + void forceshort() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-f", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->force); + } + + void forcelong() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--force", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->force); + } + + void relativePaths1() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-rp", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->relativePaths); + } + + void relativePaths2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--relative-paths", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->relativePaths); + } + + void relativePaths3() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-rp=C:/foo;C:\\bar", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + 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 relativePaths4() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--relative-paths=C:/foo;C:\\bar", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->relativePaths); + ASSERT_EQUALS(2, settings->basePaths.size()); + ASSERT_EQUALS("C:/foo", settings->basePaths[0]); + ASSERT_EQUALS("C:/bar", settings->basePaths[1]); + } + + void quietshort() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-q", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->quiet); + } + + void quietlong() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--quiet", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->quiet); + } + + void defines_noarg() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-D"}; + // Fails since -D has no param + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '-D' is missing.\n", logger->str()); + } + + void defines_noarg2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-D", "-v", "file.cpp"}; + // Fails since -D has no param + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '-D' is missing.\n", logger->str()); + } + + void defines_noarg3() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-D", "--quiet", "file.cpp"}; + // Fails since -D has no param + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '-D' is missing.\n", logger->str()); + } + + void defines() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-D_WIN32", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("_WIN32=1", settings->userDefines); + } + + void defines2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-D_WIN32", "-DNODEBUG", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("_WIN32=1;NODEBUG=1", settings->userDefines); + } + + void defines3() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-D", "DEBUG", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("DEBUG=1", settings->userDefines); + } + + void defines4() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-DDEBUG=", "file.cpp"}; // #5137 - defining empty macro + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("DEBUG=", settings->userDefines); + } + + void enforceLanguage1() { + REDIRECT; + const char * const argv[] = {"cppcheck", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS(Standards::Language::None, settings->enforcedLang); + } + + void enforceLanguage2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-x", "c++", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS(Standards::Language::CPP, settings->enforcedLang); + } + + void enforceLanguage3() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-x"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: no language given to '-x' option.\n", logger->str()); + } + + void enforceLanguage4() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-x", "--inconclusive", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: no language given to '-x' option.\n", logger->str()); + } + + void enforceLanguage5() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--language=c++", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(Standards::Language::CPP, settings->enforcedLang); + } + + void enforceLanguage6() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--language=c", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(Standards::Language::C, settings->enforcedLang); + } + + void enforceLanguage7() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--language=unknownLanguage", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unknown language 'unknownLanguage' enforced.\n", logger->str()); + } + + void includesnopath() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-I"}; + // Fails since -I has no param + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '-I' is missing.\n", logger->str()); + } + + void includes() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-I", "include", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("include/", settings->includePaths.front()); + } + + void includesslash() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-I", "include/", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("include/", settings->includePaths.front()); + } + + void includesbackslash() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-I", "include\\", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("include/", settings->includePaths.front()); + } + + void includesnospace() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-Iinclude", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("include/", settings->includePaths.front()); + } + + void includes2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-I", "include/", "-I", "framework/", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(6, argv)); + ASSERT_EQUALS("include/", settings->includePaths.front()); + settings->includePaths.pop_front(); + ASSERT_EQUALS("framework/", settings->includePaths.front()); + } + + void includesFile() { + REDIRECT; + ScopedFile file("includes.txt", + "path/sub\n" + "path2/sub1\n"); + const char * const argv[] = {"cppcheck", "--includes-file=includes.txt", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(2, settings->includePaths.size()); + auto it = settings->includePaths.cbegin(); + ASSERT_EQUALS("path/sub/", *it++); + ASSERT_EQUALS("path2/sub1/", *it); + } + + void includesFileNoFile() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--includes-file=fileThatDoesNotExist.txt", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unable to open includes file at 'fileThatDoesNotExist.txt'\n", logger->str()); + } + + void configExcludesFile() { + REDIRECT; + ScopedFile file("excludes.txt", + "path/sub\n" + "path2/sub1\n"); + const char * const argv[] = {"cppcheck", "--config-excludes-file=excludes.txt", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(2, settings->configExcludePaths.size()); + auto it = settings->configExcludePaths.cbegin(); + ASSERT_EQUALS("path/sub/", *it++); + ASSERT_EQUALS("path2/sub1/", *it); + } + + void configExcludesFileNoFile() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--config-excludes-file=fileThatDoesNotExist.txt", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unable to open config excludes file at 'fileThatDoesNotExist.txt'\n", logger->str()); + } + + void enabledAll() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=all", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT(settings->severity.isEnabled(Severity::style)); + ASSERT(settings->severity.isEnabled(Severity::warning)); + ASSERT(settings->checks.isEnabled(Checks::unusedFunction)); + ASSERT(settings->checks.isEnabled(Checks::missingInclude)); + ASSERT(!settings->checks.isEnabled(Checks::internalCheck)); + } + + void enabledStyle() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=style", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT(settings->severity.isEnabled(Severity::style)); + ASSERT(settings->severity.isEnabled(Severity::warning)); + ASSERT(settings->severity.isEnabled(Severity::performance)); + ASSERT(settings->severity.isEnabled(Severity::portability)); + ASSERT(!settings->checks.isEnabled(Checks::unusedFunction)); + ASSERT(!settings->checks.isEnabled(Checks::internalCheck)); + } + + void enabledPerformance() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=performance", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT(!settings->severity.isEnabled(Severity::style)); + ASSERT(!settings->severity.isEnabled(Severity::warning)); + ASSERT(settings->severity.isEnabled(Severity::performance)); + ASSERT(!settings->severity.isEnabled(Severity::portability)); + ASSERT(!settings->checks.isEnabled(Checks::unusedFunction)); + ASSERT(!settings->checks.isEnabled(Checks::missingInclude)); + } + + void enabledPortability() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=portability", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT(!settings->severity.isEnabled(Severity::style)); + ASSERT(!settings->severity.isEnabled(Severity::warning)); + ASSERT(!settings->severity.isEnabled(Severity::performance)); + ASSERT(settings->severity.isEnabled(Severity::portability)); + ASSERT(!settings->checks.isEnabled(Checks::unusedFunction)); + ASSERT(!settings->checks.isEnabled(Checks::missingInclude)); + } + + void enabledInformation() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=information", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT(settings->severity.isEnabled(Severity::information)); + ASSERT(settings->checks.isEnabled(Checks::missingInclude)); + ASSERT_EQUALS("cppcheck: '--enable=information' will no longer implicitly enable 'missingInclude' starting with 2.16. Please enable it explicitly if you require it.\n", logger->str()); + } + + void enabledUnusedFunction() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=unusedFunction", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT(settings->checks.isEnabled(Checks::unusedFunction)); + } + + void enabledMissingInclude() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=missingInclude", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT(settings->checks.isEnabled(Checks::missingInclude)); + } + + void disabledMissingIncludeWithInformation() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--disable=missingInclude", "--enable=information", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT(settings->severity.isEnabled(Severity::information)); + ASSERT(!settings->checks.isEnabled(Checks::missingInclude)); + ASSERT_EQUALS("", logger->str()); + } + + void enabledMissingIncludeWithInformation() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=information", "--enable=missingInclude", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT(settings->severity.isEnabled(Severity::information)); + ASSERT(settings->checks.isEnabled(Checks::missingInclude)); + ASSERT_EQUALS("", logger->str()); + } + + void enabledMissingIncludeWithInformationReverseOrder() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=missingInclude", "--enable=information", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT(settings->severity.isEnabled(Severity::information)); + ASSERT(settings->checks.isEnabled(Checks::missingInclude)); + ASSERT_EQUALS("", logger->str()); + } + +#ifdef CHECK_INTERNAL + void enabledInternal() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=internal", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT(settings->checks.isEnabled(Checks::internalCheck)); + } +#endif + + void enabledMultiple() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=missingInclude,portability,warning", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT(!settings->severity.isEnabled(Severity::style)); + ASSERT(settings->severity.isEnabled(Severity::warning)); + ASSERT(!settings->severity.isEnabled(Severity::performance)); + ASSERT(settings->severity.isEnabled(Severity::portability)); + ASSERT(!settings->checks.isEnabled(Checks::unusedFunction)); + ASSERT(settings->checks.isEnabled(Checks::missingInclude)); + } + + void enabledInvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=warning,missingIncludeSystem,style", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: --enable parameter with the unknown name 'missingIncludeSystem'\n", logger->str()); + } + + void enabledError() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=error", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: --enable parameter with the unknown name 'error'\n", logger->str()); + } + + void enabledEmpty() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: --enable parameter is empty\n", logger->str()); + } + + + void disableAll() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=all", "--disable=all", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS(true, settings->severity.isEnabled(Severity::error)); + ASSERT_EQUALS(false, settings->severity.isEnabled(Severity::warning)); + ASSERT_EQUALS(false, settings->severity.isEnabled(Severity::style)); + ASSERT_EQUALS(false, settings->severity.isEnabled(Severity::performance)); + ASSERT_EQUALS(false, settings->severity.isEnabled(Severity::portability)); + ASSERT_EQUALS(false, settings->severity.isEnabled(Severity::debug)); + ASSERT_EQUALS(false, settings->checks.isEnabled(Checks::unusedFunction)); + ASSERT_EQUALS(false, settings->checks.isEnabled(Checks::missingInclude)); + ASSERT_EQUALS(false, settings->checks.isEnabled(Checks::internalCheck)); + } + + void disableMultiple() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=all", "--disable=style", "--disable=unusedFunction", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(5, argv)); + ASSERT_EQUALS(true, settings->severity.isEnabled(Severity::error)); + ASSERT_EQUALS(true, settings->severity.isEnabled(Severity::warning)); + ASSERT_EQUALS(false, settings->severity.isEnabled(Severity::style)); + ASSERT_EQUALS(true, settings->severity.isEnabled(Severity::performance)); + ASSERT_EQUALS(true, settings->severity.isEnabled(Severity::portability)); + ASSERT_EQUALS(true, settings->severity.isEnabled(Severity::debug)); + ASSERT_EQUALS(false, settings->checks.isEnabled(Checks::unusedFunction)); + ASSERT_EQUALS(true, settings->checks.isEnabled(Checks::missingInclude)); + ASSERT_EQUALS(false, settings->checks.isEnabled(Checks::internalCheck)); + } + + // make sure the implied "style" checks are not added when "--enable=style" is specified + void disableStylePartial() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=style", "--disable=performance", "--enable=unusedFunction", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(5, argv)); + ASSERT_EQUALS(true, settings->severity.isEnabled(Severity::error)); + ASSERT_EQUALS(true, settings->severity.isEnabled(Severity::warning)); + ASSERT_EQUALS(true, settings->severity.isEnabled(Severity::style)); + ASSERT_EQUALS(false, settings->severity.isEnabled(Severity::performance)); + ASSERT_EQUALS(true, settings->severity.isEnabled(Severity::portability)); + ASSERT_EQUALS(false, settings->severity.isEnabled(Severity::debug)); + ASSERT_EQUALS(true, settings->checks.isEnabled(Checks::unusedFunction)); + ASSERT_EQUALS(false, settings->checks.isEnabled(Checks::missingInclude)); + ASSERT_EQUALS(false, settings->checks.isEnabled(Checks::internalCheck)); + } + + void disableInformationPartial() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=information", "--disable=missingInclude", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT(settings->severity.isEnabled(Severity::information)); + ASSERT(!settings->checks.isEnabled(Checks::missingInclude)); + ASSERT_EQUALS("", logger->str()); + } + + void disableInformationPartial2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--enable=missingInclude", "--disable=information", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT(!settings->severity.isEnabled(Severity::information)); + ASSERT(settings->checks.isEnabled(Checks::missingInclude)); + } + + void disableInvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--disable=leaks", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: --disable parameter with the unknown name 'leaks'\n", logger->str()); + } + + void disableError() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--disable=error", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: --disable parameter with the unknown name 'error'\n", logger->str()); + } + + void disableEmpty() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--disable=", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: --disable parameter is empty\n", logger->str()); + } + + void inconclusive() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--inconclusive", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->certainty.isEnabled(Certainty::inconclusive)); + } + + void errorExitcode() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--error-exitcode=5", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(5, settings->exitCode); + } + + void errorExitcodeMissing() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--error-exitcode=", "file.cpp"}; + // Fails since exit code not given + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--error-exitcode=' is not valid - not an integer.\n", logger->str()); + } + + void errorExitcodeStr() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--error-exitcode=foo", "file.cpp"}; + // Fails since invalid exit code + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--error-exitcode=' is not valid - not an integer.\n", logger->str()); + } + + void exitcodeSuppressionsOld() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--exitcode-suppressions", "suppr.txt", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: unrecognized command line option: \"--exitcode-suppressions\".\n", logger->str()); + } + + void exitcodeSuppressions() { + REDIRECT; + ScopedFile file("suppr.txt", + "uninitvar\n" + "unusedFunction\n"); + const char * const argv[] = {"cppcheck", "--exitcode-suppressions=suppr.txt", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(2, settings->supprs.nofail.getSuppressions().size()); + auto it = settings->supprs.nofail.getSuppressions().cbegin(); + ASSERT_EQUALS("uninitvar", (it++)->errorId); + ASSERT_EQUALS("unusedFunction", it->errorId); + } + + void exitcodeSuppressionsNoFile() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--exitcode-suppressions", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unrecognized command line option: \"--exitcode-suppressions\".\n", logger->str()); + } + + void fileFilterStdin() { + REDIRECT; + RedirectInput input("file1.c\nfile2.cpp\n"); + const char * const argv[] = {"cppcheck", "--file-filter=-"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: no C or C++ source files found.\n", logger->str()); + ASSERT_EQUALS(2U, settings->fileFilters.size()); + ASSERT_EQUALS("file1.c", settings->fileFilters[0]); + ASSERT_EQUALS("file2.cpp", settings->fileFilters[1]); + } + + void fileList() { + REDIRECT; + ScopedFile file("files.txt", + "file1.c\n" + "file2.cpp\n"); + const char * const argv[] = {"cppcheck", "--file-list=files.txt", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(3, parser->getPathNames().size()); + auto it = parser->getPathNames().cbegin(); + ASSERT_EQUALS("file1.c", *it++); + ASSERT_EQUALS("file2.cpp", *it++); + ASSERT_EQUALS("file.cpp", *it); + } + + void fileListNoFile() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--file-list=files.txt", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: couldn't open the file: \"files.txt\".\n", logger->str()); + } + + void fileListStdin() { + REDIRECT; + RedirectInput input("file1.c\nfile2.cpp\n"); + const char * const argv[] = {"cppcheck", "--file-list=-", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(3, parser->getPathNames().size()); + auto it = parser->getPathNames().cbegin(); + ASSERT_EQUALS("file1.c", *it++); + ASSERT_EQUALS("file2.cpp", *it++); + ASSERT_EQUALS("file.cpp", *it); + } + + void fileListInvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--file-list", "files.txt", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: unrecognized command line option: \"--file-list\".\n", logger->str()); + } + + void inlineSuppr() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--inline-suppr", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->inlineSuppressions); + } + + void jobs() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-j", "3", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS(3, settings->jobs); + } + + void jobs2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-j3", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(3, settings->jobs); + } + + void jobsMissingCount() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-j", "file.cpp"}; + // Fails since -j is missing thread count + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '-j' is not valid - not an integer.\n", logger->str()); + } + + void jobsInvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-j", "e", "file.cpp"}; + // Fails since invalid count given for -j + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '-j' is not valid - not an integer.\n", logger->str()); + } + + void jobsNoJobs() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-j0", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument for '-j' must be greater than 0.\n", logger->str()); + } + + void jobsTooBig() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-j1025", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument for '-j' is allowed to be 1024 at max.\n", logger->str()); + } + + void maxConfigs() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-f", "--max-configs=12", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS(12, settings->maxConfigs); + ASSERT_EQUALS(false, settings->force); + } + + void maxConfigsMissingCount() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--max-configs=", "file.cpp"}; + // Fails since --max-configs= is missing limit + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--max-configs=' is not valid - not an integer.\n", logger->str()); + } + + void maxConfigsInvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--max-configs=e", "file.cpp"}; + // Fails since invalid count given for --max-configs= + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--max-configs=' is not valid - not an integer.\n", logger->str()); + } + + void maxConfigsTooSmall() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--max-configs=0", "file.cpp"}; + // Fails since limit must be greater than 0 + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--max-configs=' must be greater than 0.\n", logger->str()); + } + + void premiumOptions1() { + REDIRECT; + asPremium(); + const char * const argv[] = {"cppcheck", "--premium=autosar", "file.c"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT_EQUALS(true, settings->severity.isEnabled(Severity::warning)); + } + + void premiumOptions2() { + REDIRECT; + asPremium(); + const char * const argv[] = {"cppcheck", "--premium=misra-c-2012", "file.c"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT_EQUALS(true, settings->severity.isEnabled(Severity::warning)); + } + + void premiumOptions3() { + REDIRECT; + asPremium(); + const char * const argv[] = {"cppcheck", "--premium=misra-c++-2023", "file.c"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT_EQUALS(true, settings->severity.isEnabled(Severity::warning)); + } + + void premiumOptions4() { + REDIRECT; + asPremium(); + const char * const argv[] = {"cppcheck", "--premium=cert-c++-2016", "file.c"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT_EQUALS(true, settings->severity.isEnabled(Severity::warning)); + } + + void premiumOptions5() { + REDIRECT; + asPremium(); + const char * const argv[] = {"cppcheck", "--premium=safety", "file.c"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->severity.isEnabled(Severity::error)); + ASSERT_EQUALS(false, settings->severity.isEnabled(Severity::warning)); + } + + void premiumOptionsInvalid1() { + REDIRECT; + asPremium(); + const char * const argv[] = {"cppcheck", "--premium=misra", "file.c"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: invalid --premium option 'misra'.\n", logger->str()); + } + + void premiumOptionsInvalid2() { + REDIRECT; + asPremium(); + const char * const argv[] = {"cppcheck", "--premium=cert", "file.c"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: invalid --premium option 'cert'.\n", logger->str()); + } + + void premiumSafety() { + REDIRECT; + asPremium(); + const char * const argv[] = {"cppcheck", "--premium=safety", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->safety); + } + + void reportProgress1() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--report-progress", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(10, settings->reportProgress); + } + + void reportProgress2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--report-progress=", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--report-progress=' is not valid - not an integer.\n", logger->str()); + } + + void reportProgress3() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--report-progress=-1", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--report-progress=' needs to be a positive integer.\n", logger->str()); + } + + void reportProgress4() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--report-progress=0", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(0, settings->reportProgress); + } + + void reportProgress5() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--report-progress=1", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(1, settings->reportProgress); + } + + void stdc99() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--std=c99", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->standards.c == Standards::C99); + } + + void stdcpp11() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--std=c++11", "file.cpp"}; + settings->standards.cpp = Standards::CPP03; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->standards.cpp == Standards::CPP11); + } + + void stdunknown1() { + REDIRECT; + const char *const argv[] = {"cppcheck", "--std=d++11", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unknown --std value 'd++11'\n", logger->str()); + } + + void stdunknown2() { + REDIRECT; + const char *const argv[] = {"cppcheck", "--std=cplusplus11", "file.cpp"}; + TODO_ASSERT_EQUALS(static_cast(CmdLineParser::Result::Fail), static_cast(CmdLineParser::Result::Success), static_cast(parser->parseFromArgs(3, argv))); + TODO_ASSERT_EQUALS("cppcheck: error: unknown --std value 'cplusplus11'\n", "", logger->str()); + } + + void platformWin64() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--platform=win64", "file.cpp"}; + ASSERT(settings->platform.set(Platform::Type::Unspecified)); + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(Platform::Type::Win64, settings->platform.type); + } + + void platformWin32A() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--platform=win32A", "file.cpp"}; + ASSERT(settings->platform.set(Platform::Type::Unspecified)); + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(Platform::Type::Win32A, settings->platform.type); + } + + void platformWin32W() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--platform=win32W", "file.cpp"}; + ASSERT(settings->platform.set(Platform::Type::Unspecified)); + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(Platform::Type::Win32W, settings->platform.type); + } + + void platformUnix32() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--platform=unix32", "file.cpp"}; + ASSERT(settings->platform.set(Platform::Type::Unspecified)); + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(Platform::Type::Unix32, settings->platform.type); + } + + void platformUnix32Unsigned() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--platform=unix32-unsigned", "file.cpp"}; + ASSERT(settings->platform.set(Platform::Type::Unspecified)); + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(Platform::Type::Unix32, settings->platform.type); + } + + void platformUnix64() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--platform=unix64", "file.cpp"}; + ASSERT(settings->platform.set(Platform::Type::Unspecified)); + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(Platform::Type::Unix64, settings->platform.type); + } + + void platformUnix64Unsigned() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--platform=unix64-unsigned", "file.cpp"}; + ASSERT(settings->platform.set(Platform::Type::Unspecified)); + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(Platform::Type::Unix64, settings->platform.type); + } + + void platformNative() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--platform=native", "file.cpp"}; + ASSERT(settings->platform.set(Platform::Type::Unspecified)); + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(Platform::Type::Native, settings->platform.type); + } + + void platformUnspecified() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--platform=unspecified", "file.cpp"}; + ASSERT(settings->platform.set(Platform::Type::Native)); + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(Platform::Type::Unspecified, settings->platform.type); + } + + void platformPlatformFile() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--platform=avr8", "file.cpp"}; + ASSERT(settings->platform.set(Platform::Type::Unspecified)); + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(Platform::Type::File, settings->platform.type); + } + + void platformUnknown() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--platform=win128", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unrecognized platform: 'win128'.\n", logger->str()); + } + + void plistEmpty() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--plist-output=", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->plistOutput == "./"); + } + + void plistDoesNotExist() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--plist-output=./cppcheck_reports", "file.cpp"}; + // Fails since folder pointed by --plist-output= does not exist + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: plist folder does not exist: 'cppcheck_reports'.\n", logger->str()); + } + + void suppressionsOld() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--suppressions", "suppr.txt", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: unrecognized command line option: \"--suppressions\".\n", logger->str()); + } + + void suppressions() { + REDIRECT; + ScopedFile file("suppr.txt", + "uninitvar\n" + "unusedFunction\n"); + const char * const argv[] = {"cppcheck", "--suppressions-list=suppr.txt", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(2, settings->supprs.nomsg.getSuppressions().size()); + auto it = settings->supprs.nomsg.getSuppressions().cbegin(); + ASSERT_EQUALS("uninitvar", (it++)->errorId); + ASSERT_EQUALS("unusedFunction", it->errorId); + } + + void suppressionsNoFile1() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--suppressions-list=", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(false, logger->str().find("If you want to pass two files") != std::string::npos); + } + + void suppressionsNoFile2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--suppressions-list=a.suppr,b.suppr", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, logger->str().find("If you want to pass two files") != std::string::npos); + } + + void suppressionsNoFile3() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--suppressions-list=a.suppr b.suppr", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, logger->str().find("If you want to pass two files") != std::string::npos); + } + + static SuppressionList::ErrorMessage errorMessage(const std::string &errorId, const std::string &fileName, int lineNumber) { + SuppressionList::ErrorMessage e; + e.errorId = errorId; + e.setFileName(fileName); + e.lineNumber = lineNumber; + return e; + } + + void suppressionSingle() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--suppress=uninitvar", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->supprs.nomsg.isSuppressed(errorMessage("uninitvar", "file.cpp", 1))); + } + + void suppressionSingleFile() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--suppress=uninitvar:file.cpp", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->supprs.nomsg.isSuppressed(errorMessage("uninitvar", "file.cpp", 1U))); + } + + void suppressionTwo() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--suppress=uninitvar,noConstructor", "file.cpp"}; + TODO_ASSERT_EQUALS(static_cast(CmdLineParser::Result::Success), static_cast(CmdLineParser::Result::Fail), static_cast(parser->parseFromArgs(3, argv))); + TODO_ASSERT_EQUALS(true, false, settings->supprs.nomsg.isSuppressed(errorMessage("uninitvar", "file.cpp", 1U))); + TODO_ASSERT_EQUALS(true, false, settings->supprs.nomsg.isSuppressed(errorMessage("noConstructor", "file.cpp", 1U))); + TODO_ASSERT_EQUALS("", "cppcheck: error: Failed to add suppression. Invalid id \"uninitvar,noConstructor\"\n", logger->str()); + } + + void suppressionTwoSeparate() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--suppress=uninitvar", "--suppress=noConstructor", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS(true, settings->supprs.nomsg.isSuppressed(errorMessage("uninitvar", "file.cpp", 1U))); + ASSERT_EQUALS(true, settings->supprs.nomsg.isSuppressed(errorMessage("noConstructor", "file.cpp", 1U))); + } + + void templates() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--template={file}:{line},{severity},{id},{message}", "--template-location={file}:{line}:{column} {info}", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("{file}:{line},{severity},{id},{message}", settings->templateFormat); + ASSERT_EQUALS("{file}:{line}:{column} {info}", settings->templateLocation); + } + + void templatesGcc() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--template=gcc", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("{file}:{line}:{column}: warning: {message} [{id}]\n{code}", settings->templateFormat); + ASSERT_EQUALS("{file}:{line}:{column}: note: {info}\n{code}", settings->templateLocation); + } + + void templatesVs() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--template=vs", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("{file}({line}): {severity}: {message}", settings->templateFormat); + ASSERT_EQUALS("", settings->templateLocation); + } + + void templatesEdit() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--template=edit", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("{file} +{line}: {severity}: {message}", settings->templateFormat); + ASSERT_EQUALS("", settings->templateLocation); + } + + void templatesCppcheck1() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--template=cppcheck1", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("{callstack}: ({severity}{inconclusive:, inconclusive}) {message}", settings->templateFormat); + ASSERT_EQUALS("", settings->templateLocation); + } + + void templatesDaca2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--template=daca2", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]", settings->templateFormat); + ASSERT_EQUALS("{file}:{line}:{column}: note: {info}", settings->templateLocation); + ASSERT_EQUALS(true, settings->daca); + } + + void templatesSelfcheck() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--template=selfcheck", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]\n{code}", settings->templateFormat); + ASSERT_EQUALS("{file}:{line}:{column}: note: {info}\n{code}", settings->templateLocation); + } + + void templatesSimple() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--template=simple", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]", settings->templateFormat); + ASSERT_EQUALS("", settings->templateLocation); + } + + // TODO: we should bail out on this + void templatesNoPlaceholder() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--template=selfchek", "file.cpp"}; + TODO_ASSERT_EQUALS(static_cast(CmdLineParser::Result::Fail), static_cast(CmdLineParser::Result::Success), static_cast(parser->parseFromArgs(3, argv))); + ASSERT_EQUALS("selfchek", settings->templateFormat); + ASSERT_EQUALS("", settings->templateLocation); + } + + void templateFormatInvalid() { + REDIRECT; + const char* const argv[] = { "cppcheck", "--template", "file.cpp" }; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unrecognized command line option: \"--template\".\n", logger->str()); + } + + // will use the default + // TODO: bail out on empty? + void templateFormatEmpty() { + REDIRECT; + const char* const argv[] = { "cppcheck", "--template=", "file.cpp" }; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("{file}:{line}:{column}: {inconclusive:}{severity}:{inconclusive: inconclusive:} {message} [{id}]\n{code}", settings->templateFormat); + ASSERT_EQUALS("{file}:{line}:{column}: note: {info}\n{code}", settings->templateLocation); + } + + void templateLocationInvalid() { + REDIRECT; + const char* const argv[] = { "cppcheck", "--template-location", "--template={file}", "file.cpp" }; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: unrecognized command line option: \"--template-location\".\n", logger->str()); + } + + // will use the default + // TODO: bail out on empty? + void templateLocationEmpty() { + REDIRECT; + const char* const argv[] = { "cppcheck", "--template-location=", "file.cpp" }; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("{file}:{line}:{column}: {inconclusive:}{severity}:{inconclusive: inconclusive:} {message} [{id}]\n{code}", settings->templateFormat); + ASSERT_EQUALS("{file}:{line}:{column}: note: {info}\n{code}", settings->templateLocation); + } + + void xml() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->xml); + ASSERT_EQUALS(2, settings->xml_version); + } + + void xmlver2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--xml-version=2", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->xml); + ASSERT_EQUALS(2, settings->xml_version); + } + + void xmlver2both() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--xml", "--xml-version=2", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT(settings->xml); + ASSERT_EQUALS(2, settings->xml_version); + } + + void xmlver2both2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--xml-version=2", "--xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT(settings->xml); + ASSERT_EQUALS(2, settings->xml_version); + } + + void xmlverunknown() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--xml", "--xml-version=3", "file.cpp"}; + // FAils since unknown XML format version + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: '--xml-version' can only be 2.\n", logger->str()); + } + + void xmlverinvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--xml", "--xml-version=a", "file.cpp"}; + // FAils since unknown XML format version + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--xml-version=' is not valid - not an integer.\n", logger->str()); + } + + void doc() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--doc"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Exit, parser->parseFromArgs(2, argv)); + ASSERT(startsWith(logger->str(), "## ")); + } + + void docExclusive() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--library=missing", "--doc"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Exit, parser->parseFromArgs(3, argv)); + ASSERT(startsWith(logger->str(), "## ")); + } + + void showtimeSummary() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--showtime=summary", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->showtime == SHOWTIME_MODES::SHOWTIME_SUMMARY); + } + + void showtimeFile() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--showtime=file", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->showtime == SHOWTIME_MODES::SHOWTIME_FILE); + } + + void showtimeFileTotal() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--showtime=file-total", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->showtime == SHOWTIME_MODES::SHOWTIME_FILE_TOTAL); + } + + void showtimeTop5() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--showtime=top5", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->showtime == SHOWTIME_MODES::SHOWTIME_TOP5_FILE); + ASSERT_EQUALS("cppcheck: --showtime=top5 is deprecated and will be removed in Cppcheck 2.14. Please use --showtime=top5_file or --showtime=top5_summary instead.\n", logger->str()); + } + + void showtimeTop5File() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--showtime=top5_file", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->showtime == SHOWTIME_MODES::SHOWTIME_TOP5_FILE); + } + + void showtimeTop5Summary() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--showtime=top5_summary", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->showtime == SHOWTIME_MODES::SHOWTIME_TOP5_SUMMARY); + } + + void showtimeNone() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--showtime=none", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->showtime == SHOWTIME_MODES::SHOWTIME_NONE); + } + + void showtimeEmpty() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--showtime=", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: no mode provided for --showtime\n", logger->str()); + } + + void showtimeInvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--showtime=top10", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unrecognized --showtime mode: 'top10'. Supported modes: file, file-total, summary, top5, top5_file, top5_summary.\n", logger->str()); + } + + void errorlist() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--errorlist"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Exit, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("", logger->str()); // empty since it is logged via ErrorLogger + const std::string errout_s = GET_REDIRECT_OUTPUT; + ASSERT(startsWith(errout_s, ErrorMessage::getXMLHeader(""))); + ASSERT(endsWith(errout_s, "\n")); + } + + void errorlistWithCfg() { + REDIRECT; + ScopedFile file(Path::join(Path::getPathFromFilename(Path::getCurrentExecutablePath("")), "cppcheck.cfg"), + R"({"productName": "The Product"}\n)"); + const char * const argv[] = {"cppcheck", "--errorlist"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Exit, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("", logger->str()); // empty since it is logged via ErrorLogger + ASSERT(startsWith(GET_REDIRECT_OUTPUT, ErrorMessage::getXMLHeader("The Product"))); + } + + void errorlistExclusive() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--library=missing", "--errorlist"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Exit, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("", logger->str()); // empty since it is logged via ErrorLogger + const std::string errout_s = GET_REDIRECT_OUTPUT; + ASSERT(startsWith(errout_s, ErrorMessage::getXMLHeader(""))); + ASSERT(endsWith(errout_s, "\n")); + } + + void errorlistWithInvalidCfg() { + REDIRECT; + ScopedFile file(Path::join(Path::getPathFromFilename(Path::getCurrentExecutablePath("")), "cppcheck.cfg"), + "{\n"); + const char * const argv[] = {"cppcheck", "--errorlist"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: could not load cppcheck.cfg - not a valid JSON - syntax error at line 2 near: \n", logger->str()); + } + + void ignorepathsnopath() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-i"}; + // Fails since no ignored path given + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '-i' is missing.\n", logger->str()); + } + +#if defined(USE_WINDOWS_SEH) || defined(USE_UNIX_SIGNAL_HANDLING) + void exceptionhandling() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--exception-handling", "file.cpp"}; + CppCheckExecutor::setExceptionOutput(stderr); + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->exceptionHandling); + ASSERT_EQUALS(stderr, CppCheckExecutor::getExceptionOutput()); + } + + void exceptionhandling2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--exception-handling=stderr", "file.cpp"}; + CppCheckExecutor::setExceptionOutput(stdout); + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->exceptionHandling); + ASSERT_EQUALS(stderr, CppCheckExecutor::getExceptionOutput()); + } + + void exceptionhandling3() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--exception-handling=stdout", "file.cpp"}; + CppCheckExecutor::setExceptionOutput(stderr); + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->exceptionHandling); + ASSERT_EQUALS(stdout, CppCheckExecutor::getExceptionOutput()); + } + + void exceptionhandlingInvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--exception-handling=exfile"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: invalid '--exception-handling' argument\n", logger->str()); + } + + void exceptionhandlingInvalid2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--exception-handling-foo"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: unrecognized command line option: \"--exception-handling-foo\".\n", logger->str()); + } +#else + void exceptionhandlingNotSupported() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--exception-handling", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: Option --exception-handling is not supported since Cppcheck has not been built with any exception handling enabled.\n", logger->str()); + } + + void exceptionhandlingNotSupported2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--exception-handling=stderr", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: Option --exception-handling is not supported since Cppcheck has not been built with any exception handling enabled.\n", logger->str()); + } +#endif + + void clang() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--clang", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->clang); + ASSERT_EQUALS("clang", settings->clangExecutable); + } + + void clang2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--clang=clang-14", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT(settings->clang); + ASSERT_EQUALS("clang-14", settings->clangExecutable); + } + + void clangInvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--clang-foo"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: unrecognized command line option: \"--clang-foo\".\n", logger->str()); + } + + void valueFlowMaxIterations() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--valueflow-max-iterations=0", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(0, settings->valueFlowMaxIterations); + } + + void valueFlowMaxIterations2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--valueflow-max-iterations=11", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(11, settings->valueFlowMaxIterations); + } + + void valueFlowMaxIterationsInvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--valueflow-max-iterations"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: unrecognized command line option: \"--valueflow-max-iterations\".\n", logger->str()); + } + + void valueFlowMaxIterationsInvalid2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--valueflow-max-iterations=seven"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--valueflow-max-iterations=' is not valid - not an integer.\n", logger->str()); + } + + void valueFlowMaxIterationsInvalid3() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--valueflow-max-iterations=-1"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--valueflow-max-iterations=' is not valid - needs to be positive.\n", logger->str()); + } + + void checksMaxTime() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--checks-max-time=12", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(12, settings->checksMaxTime); + } + + void checksMaxTime2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--checks-max-time=-1", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--checks-max-time=' needs to be a positive integer.\n", logger->str()); + } + + void checksMaxTimeInvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--checks-max-time=one", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--checks-max-time=' is not valid - not an integer.\n", logger->str()); + } + +#ifdef HAS_THREADING_MODEL_FORK + void loadAverage() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-l", "12", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS(12, settings->loadAverage); + } + + void loadAverage2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-l12", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(12, settings->loadAverage); + } + + void loadAverageInvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-l", "one", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '-l' is not valid - not an integer.\n", logger->str()); + } +#else + void loadAverageNotSupported() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-l", "12", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: Option -l cannot be used as Cppcheck has not been built with fork threading model.\n", logger->str()); + } +#endif + + void maxCtuDepth() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--max-ctu-depth=12", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(12, settings->maxCtuDepth); + } + + void maxCtuDepthInvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--max-ctu-depth=one", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--max-ctu-depth=' is not valid - not an integer.\n", logger->str()); + } + + void performanceValueflowMaxTime() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--performance-valueflow-max-time=12", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(12, settings->performanceValueFlowMaxTime); + } + + void performanceValueflowMaxTimeInvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--performance-valueflow-max-time=one", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--performance-valueflow-max-time=' is not valid - not an integer.\n", logger->str()); + } + + void performanceValueFlowMaxIfCount() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--performance-valueflow-max-if-count=12", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(12, settings->performanceValueFlowMaxIfCount); + } + + void performanceValueFlowMaxIfCountInvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--performance-valueflow-max-if-count=one", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--performance-valueflow-max-if-count=' is not valid - not an integer.\n", logger->str()); + } + + void templateMaxTime() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--template-max-time=12", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(12, settings->templateMaxTime); + } + + void templateMaxTimeInvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--template-max-time=one", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--template-max-time=' is not valid - not an integer.\n", logger->str()); + } + + void templateMaxTimeInvalid2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--template-max-time=-1", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--template-max-time=' is not valid - needs to be positive.\n", logger->str()); + } + + void typedefMaxTime() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--typedef-max-time=12", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(12, settings->typedefMaxTime); + } + + void typedefMaxTimeInvalid() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--typedef-max-time=one", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--typedef-max-time=' is not valid - not an integer.\n", logger->str()); + } + + void typedefMaxTimeInvalid2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--typedef-max-time=-1", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '--typedef-max-time=' is not valid - needs to be positive.\n", logger->str()); + } + + void project() { + REDIRECT; + ScopedFile file("project.cppcheck", + "\n" + "\n" + "

\n" + "\n" + ""); + const char * const argv[] = {"cppcheck", "--project=project.cppcheck"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS(1, parser->getPathNames().size()); + auto it = parser->getPathNames().cbegin(); + ASSERT_EQUALS("dir", *it); + } + + void projectMultiple() { + REDIRECT; + ScopedFile file("project.cppcheck", ""); + const char * const argv[] = {"cppcheck", "--project=project.cppcheck", "--project=project.cppcheck", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: multiple --project options are not supported.\n", logger->str()); + } + + void projectAndSource() { + REDIRECT; + ScopedFile file("project.cppcheck", ""); + const char * const argv[] = {"cppcheck", "--project=project.cppcheck", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: --project cannot be used in conjunction with source files.\n", logger->str()); + } + + void projectEmpty() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--project=", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: failed to open project ''. The file does not exist.\n", logger->str()); + } + + void projectMissing() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--project=project.cppcheck", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: failed to open project 'project.cppcheck'. The file does not exist.\n", logger->str()); + } + + void projectNoPaths() { + ScopedFile file("project.cppcheck", ""); + const char * const argv[] = {"cppcheck", "--project=project.cppcheck"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: no C or C++ source files found.\n", logger->str()); + } + + void addon() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--addon=misra", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(1, settings->addons.size()); + ASSERT_EQUALS("misra", *settings->addons.cbegin()); + } + + void addonMissing() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--addon=misra2", "file.cpp"}; + ASSERT(!parser->fillSettingsFromArgs(3, argv)); + ASSERT_EQUALS(1, settings->addons.size()); + ASSERT_EQUALS("misra2", *settings->addons.cbegin()); + ASSERT_EQUALS("Did not find addon misra2.py\n", logger->str()); + } + + void signedChar() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--fsigned-char", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS('s', settings->platform.defaultSign); + } + + void signedChar2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--platform=avr8", "--fsigned-char", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS('s', settings->platform.defaultSign); + } + + void unsignedChar() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--funsigned-char", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS('u', settings->platform.defaultSign); + } + + void unsignedChar2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--platform=mips32", "--funsigned-char", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS('u', settings->platform.defaultSign); + } + + void signedCharUnsignedChar() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--fsigned-char", "--funsigned-char", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS('u', settings->platform.defaultSign); + } + +#ifdef HAVE_RULES + void rule() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--rule=.+", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(1, settings->rules.size()); + auto it = settings->rules.cbegin(); + ASSERT_EQUALS(".+", it->pattern); + } + + void ruleMissingPattern() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--rule=", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: no rule pattern provided.\n", logger->str()); + } +#else + void ruleNotSupported() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--rule=.+", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: Option --rule cannot be used as Cppcheck has not been built with rules support.\n", logger->str()); + } +#endif + +#ifdef HAVE_RULES + void ruleFileMulti() { + REDIRECT; + ScopedFile file("rule.xml", + "\n" + "\n" + "raw\n" + ".+\n" + "\n" + "error\n" + "ruleId1\n" + "ruleSummary1\n" + "\n" + "\n" + "\n" + "define\n" + ".*\n" + "\n" + "warning\n" + "ruleId2\n" + "ruleSummary2\n" + "\n" + "\n" + ""); + const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(2, settings->rules.size()); + auto it = settings->rules.cbegin(); + ASSERT_EQUALS("raw", it->tokenlist); + ASSERT_EQUALS(".+", it->pattern); + ASSERT_EQUALS_ENUM(Severity::error, it->severity); + ASSERT_EQUALS("ruleId1", it->id); + ASSERT_EQUALS("ruleSummary1", it->summary); + ++it; + ASSERT_EQUALS("define", it->tokenlist); + ASSERT_EQUALS(".*", it->pattern); + ASSERT_EQUALS_ENUM(Severity::warning, it->severity); + ASSERT_EQUALS("ruleId2", it->id); + ASSERT_EQUALS("ruleSummary2", it->summary); + } + + void ruleFileSingle() { + REDIRECT; + ScopedFile file("rule.xml", + "\n" + "define\n" + ".+\n" + "\n" + "error\n" + "ruleId\n" + "ruleSummary\n" + "\n" + "\n"); + const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(1, settings->rules.size()); + auto it = settings->rules.cbegin(); + ASSERT_EQUALS("define", it->tokenlist); + ASSERT_EQUALS(".+", it->pattern); + ASSERT_EQUALS_ENUM(Severity::error, it->severity); + ASSERT_EQUALS("ruleId", it->id); + ASSERT_EQUALS("ruleSummary", it->summary); + } + + void ruleFileEmpty() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--rule-file=", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unable to load rule-file '' (XML_ERROR_FILE_NOT_FOUND).\n", logger->str()); + } + + void ruleFileMissing() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unable to load rule-file 'rule.xml' (XML_ERROR_FILE_NOT_FOUND).\n", logger->str()); + } + + void ruleFileInvalid() { + REDIRECT; + ScopedFile file("rule.xml", ""); + const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unable to load rule-file 'rule.xml' (XML_ERROR_EMPTY_DOCUMENT).\n", logger->str()); + } + + void ruleFileNoRoot() { + REDIRECT; + ScopedFile file("rule.xml", ""); + const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(0, settings->rules.size()); + } + + void ruleFileEmptyElements1() { + REDIRECT; + ScopedFile file("rule.xml", + "" + "" + "" + "" + "" + ); + const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); // do not crash + ASSERT_EQUALS("cppcheck: error: unable to load rule-file 'rule.xml' - a rule is lacking a pattern.\n", logger->str()); + } + + void ruleFileEmptyElements2() { + REDIRECT; + ScopedFile file("rule.xml", + "" + "" + "" + "" + "" + "" + "" + ); + const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); // do not crash + ASSERT_EQUALS("cppcheck: error: unable to load rule-file 'rule.xml' - a rule is lacking a pattern.\n", logger->str()); + } + + void ruleFileUnknownElement1() { + REDIRECT; + ScopedFile file("rule.xml", + "" + "" + "" + ); + const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unable to load rule-file 'rule.xml' - unknown element 'messages' encountered in 'rule'.\n", logger->str()); + } + + void ruleFileUnknownElement2() { + REDIRECT; + ScopedFile file("rule.xml", + "" + "" + "" + "" + "" + ); + const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unable to load rule-file 'rule.xml' - unknown element 'pattern' encountered in 'message'.\n", logger->str()); + } + + void ruleFileMissingTokenList() { + REDIRECT; + ScopedFile file("rule.xml", + "\n" + "\n" + ".+\n" + "\n"); + const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unable to load rule-file 'rule.xml' - a rule is lacking a tokenlist.\n", logger->str()); + } + + void ruleFileUnknownTokenList() { + REDIRECT; + ScopedFile file("rule.xml", + "\n" + "simple\n" + ".+\n" + "\n"); + const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unable to load rule-file 'rule.xml' - a rule is using the unsupported tokenlist 'simple'.\n", logger->str()); + } + + void ruleFileMissingId() { + REDIRECT; + ScopedFile file("rule.xml", + "\n" + ".+\n" + "\n" + "" + "\n" + "\n"); + const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unable to load rule-file 'rule.xml' - a rule is lacking an id.\n", logger->str()); + } + + void ruleFileInvalidSeverity1() { + REDIRECT; + ScopedFile file("rule.xml", + "\n" + ".+\n" + "\n" + "" + "\n" + "\n"); + const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unable to load rule-file 'rule.xml' - a rule has an invalid severity.\n", logger->str()); + } + + void ruleFileInvalidSeverity2() { + REDIRECT; + ScopedFile file("rule.xml", + "\n" + ".+\n" + "\n" + "none" + "\n" + "\n"); + const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unable to load rule-file 'rule.xml' - a rule has an invalid severity.\n", logger->str()); + } +#else + void ruleFileNotSupported() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--rule-file=rule.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: Option --rule-file cannot be used as Cppcheck has not been built with rules support.\n", logger->str()); + } +#endif + + void library() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--library=posix", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(1, settings->libraries.size()); + ASSERT_EQUALS("posix", *settings->libraries.cbegin()); + } + + void libraryMissing() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--library=posix2", "file.cpp"}; + ASSERT_EQUALS(false, parser->fillSettingsFromArgs(3, argv)); + ASSERT_EQUALS(1, settings->libraries.size()); + ASSERT_EQUALS("posix2", *settings->libraries.cbegin()); + ASSERT_EQUALS("cppcheck: Failed to load library configuration file 'posix2'. File not found\n", logger->str()); + } + + void suppressXml() { + REDIRECT; + ScopedFile file("suppress.xml", + "\n" + "\n" + "uninitvar\n" + "\n" + ""); + const char * const argv[] = {"cppcheck", "--suppress-xml=suppress.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + const auto& supprs = settings->supprs.nomsg.getSuppressions(); + ASSERT_EQUALS(1, supprs.size()); + const auto it = supprs.cbegin(); + ASSERT_EQUALS("uninitvar", it->errorId); + } + + void suppressXmlEmpty() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--suppress-xml=", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: failed to load suppressions XML '' (XML_ERROR_FILE_NOT_FOUND).\n", logger->str()); + } + + void suppressXmlMissing() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--suppress-xml=suppress.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: failed to load suppressions XML 'suppress.xml' (XML_ERROR_FILE_NOT_FOUND).\n", logger->str()); + } + + void suppressXmlInvalid() { + REDIRECT; + ScopedFile file("suppress.xml", ""); + const char * const argv[] = {"cppcheck", "--suppress-xml=suppress.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: failed to load suppressions XML 'suppress.xml' (XML_ERROR_EMPTY_DOCUMENT).\n", logger->str()); + } + + void suppressXmlNoRoot() { + REDIRECT; + ScopedFile file("suppress.xml", ""); + const char * const argv[] = {"cppcheck", "--suppress-xml=suppress.xml", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: failed to load suppressions XML 'suppress.xml' (no root node found).\n", logger->str()); + } + + void executorDefault() { + REDIRECT; + const char * const argv[] = {"cppcheck", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(2, argv)); +#if defined(HAS_THREADING_MODEL_FORK) + ASSERT_EQUALS_ENUM(Settings::ExecutorType::Process, settings->executor); +#elif defined(HAS_THREADING_MODEL_THREAD) + ASSERT_EQUALS_ENUM(Settings::ExecutorType::Thread, settings->executor); +#endif + } + + void executorAuto() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-j2", "--executor=auto", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); +#if defined(HAS_THREADING_MODEL_FORK) + ASSERT_EQUALS_ENUM(Settings::ExecutorType::Process, settings->executor); +#elif defined(HAS_THREADING_MODEL_THREAD) + ASSERT_EQUALS_ENUM(Settings::ExecutorType::Thread, settings->executor); +#endif + } + + void executorAutoNoJobs() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--executor=auto", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); +#if defined(HAS_THREADING_MODEL_FORK) + ASSERT_EQUALS_ENUM(Settings::ExecutorType::Process, settings->executor); +#elif defined(HAS_THREADING_MODEL_THREAD) + ASSERT_EQUALS_ENUM(Settings::ExecutorType::Thread, settings->executor); +#endif + } + +#if defined(HAS_THREADING_MODEL_THREAD) + void executorThread() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-j2", "--executor=thread", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS_ENUM(Settings::ExecutorType::Thread, settings->executor); + } + + void executorThreadNoJobs() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--executor=thread", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS_ENUM(Settings::ExecutorType::Thread, settings->executor); + ASSERT_EQUALS("cppcheck: '--executor' has no effect as only a single job will be used.\n", logger->str()); + } +#else + void executorThreadNotSupported() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-j2", "--executor=thread", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: executor type 'thread' cannot be used as Cppcheck has not been built with a respective threading model.\n", logger->str()); + } +#endif + +#if defined(HAS_THREADING_MODEL_FORK) + void executorProcess() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-j2", "--executor=process", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS_ENUM(Settings::ExecutorType::Process, settings->executor); + } + + void executorProcessNoJobs() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--executor=process", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS_ENUM(Settings::ExecutorType::Process, settings->executor); + ASSERT_EQUALS("cppcheck: '--executor' has no effect as only a single job will be used.\n", logger->str()); + } +#else + void executorProcessNotSupported() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-j2", "--executor=process", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: executor type 'process' cannot be used as Cppcheck has not been built with a respective threading model.\n", logger->str()); + } +#endif + + void checkLevelDefault() { + REDIRECT; + const char * const argv[] = {"cppcheck", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS_ENUM(Settings::CheckLevel::normal, settings->checkLevel); + ASSERT_EQUALS(100, settings->performanceValueFlowMaxIfCount); + ASSERT_EQUALS(8, settings->performanceValueFlowMaxSubFunctionArgs); + } + + void checkLevelNormal() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--check-level=normal", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS_ENUM(Settings::CheckLevel::normal, settings->checkLevel); + ASSERT_EQUALS(100, settings->performanceValueFlowMaxIfCount); + ASSERT_EQUALS(8, settings->performanceValueFlowMaxSubFunctionArgs); + } + + void checkLevelExhaustive() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--check-level=exhaustive", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS_ENUM(Settings::CheckLevel::exhaustive, settings->checkLevel); + ASSERT_EQUALS(-1, settings->performanceValueFlowMaxIfCount); + ASSERT_EQUALS(256, settings->performanceValueFlowMaxSubFunctionArgs); + } + + void checkLevelUnknown() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--check-level=default", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unknown '--check-level' value 'default'.\n", logger->str()); + } + + void ignorepaths1() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-isrc", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(1, parser->getIgnoredPaths().size()); + ASSERT_EQUALS("src", parser->getIgnoredPaths()[0]); + } + + void ignorepaths2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-i", "src", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS(1, parser->getIgnoredPaths().size()); + ASSERT_EQUALS("src", parser->getIgnoredPaths()[0]); + } + + void ignorepaths3() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-isrc", "-imodule", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS(2, parser->getIgnoredPaths().size()); + ASSERT_EQUALS("src", parser->getIgnoredPaths()[0]); + ASSERT_EQUALS("module", parser->getIgnoredPaths()[1]); + } + + void ignorepaths4() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-i", "src", "-i", "module", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(6, argv)); + ASSERT_EQUALS(2, parser->getIgnoredPaths().size()); + ASSERT_EQUALS("src", parser->getIgnoredPaths()[0]); + ASSERT_EQUALS("module", parser->getIgnoredPaths()[1]); + } + + void ignorefilepaths1() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-ifoo.cpp", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(1, parser->getIgnoredPaths().size()); + ASSERT_EQUALS("foo.cpp", parser->getIgnoredPaths()[0]); + } + + void ignorefilepaths2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-isrc/foo.cpp", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(1, parser->getIgnoredPaths().size()); + ASSERT_EQUALS("src/foo.cpp", parser->getIgnoredPaths()[0]); + } + + void ignorefilepaths3() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-i", "foo.cpp", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS(1, parser->getIgnoredPaths().size()); + ASSERT_EQUALS("foo.cpp", parser->getIgnoredPaths()[0]); + } + + void checkconfig() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--check-config", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(true, settings->checkConfiguration); + } + + void unknownParam() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--foo", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS("cppcheck: error: unrecognized command line option: \"--foo\".\n", logger->str()); + } + + void undefs() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-U_WIN32", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + ASSERT_EQUALS(1, settings->userUndefs.size()); + ASSERT(settings->userUndefs.find("_WIN32") != settings->userUndefs.end()); + } + + void undefs2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-U_WIN32", "-UNODEBUG", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS(2, settings->userUndefs.size()); + ASSERT(settings->userUndefs.find("_WIN32") != settings->userUndefs.end()); + ASSERT(settings->userUndefs.find("NODEBUG") != settings->userUndefs.end()); + } + + void undefs_noarg() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-U"}; + // Fails since -U has no param + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '-U' is missing.\n", logger->str()); + } + + void undefs_noarg2() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-U", "-v", "file.cpp"}; + // Fails since -U has no param + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '-U' is missing.\n", logger->str()); + } + + void undefs_noarg3() { + REDIRECT; + const char * const argv[] = {"cppcheck", "-U", "--quiet", "file.cpp"}; + // Fails since -U has no param + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(4, argv)); + ASSERT_EQUALS("cppcheck: error: argument to '-U' is missing.\n", logger->str()); + } + + void cppcheckBuildDirExistent() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--cppcheck-build-dir=.", "file.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Success, parser->parseFromArgs(3, argv)); + } + + void cppcheckBuildDirNonExistent() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--cppcheck-build-dir=non-existent-path"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: Directory 'non-existent-path' specified by --cppcheck-build-dir argument has to be existent.\n", logger->str()); + } + + void cppcheckBuildDirEmpty() { + REDIRECT; + const char * const argv[] = {"cppcheck", "--cppcheck-build-dir="}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: Directory '' specified by --cppcheck-build-dir argument has to be existent.\n", logger->str()); + } + + void invalidCppcheckCfg() { + REDIRECT; + ScopedFile file(Path::join(Path::getPathFromFilename(Path::getCurrentExecutablePath("")), "cppcheck.cfg"), + "{\n"); + const char * const argv[] = {"cppcheck", "test.cpp"}; + ASSERT_EQUALS_ENUM(CmdLineParser::Result::Fail, parser->parseFromArgs(2, argv)); + ASSERT_EQUALS("cppcheck: error: could not load cppcheck.cfg - not a valid JSON - syntax error at line 2 near: \n", logger->str()); + } +}; + +REGISTER_TEST(TestCmdlineParser) diff --git a/cppcheck-2.14.0/test/testcolor.cpp b/cppcheck-2.14.0/test/testcolor.cpp new file mode 100644 index 00000000..7351cb2d --- /dev/null +++ b/cppcheck-2.14.0/test/testcolor.cpp @@ -0,0 +1,38 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "color.h" +#include "fixture.h" + +class TestColor : public TestFixture { +public: + TestColor() : TestFixture("TestColor") {} + +private: + + void run() override { + TEST_CASE(toString); + } + + void toString() const { + // TODO: color conversion is dependent on stdout/stderr being a TTY + ASSERT_EQUALS("", ::toString(Color::FgRed)); + } +}; + +REGISTER_TEST(TestColor) diff --git a/cppcheck-2.14.0/test/testcondition.cpp b/cppcheck-2.14.0/test/testcondition.cpp new file mode 100644 index 00000000..6f4feed4 --- /dev/null +++ b/cppcheck-2.14.0/test/testcondition.cpp @@ -0,0 +1,5990 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "checkcondition.h" +#include "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "platform.h" +#include "settings.h" +#include "tokenize.h" + +#include +#include +#include + +class TestCondition : public TestFixture { +public: + TestCondition() : TestFixture("TestCondition") {} + +private: + const Settings settings0 = settingsBuilder().library("qt.cfg").library("std.cfg").severity(Severity::style).severity(Severity::warning).build(); + /*const*/ Settings settings1 = settingsBuilder().severity(Severity::style).severity(Severity::warning).build(); + + void run() override { + const char cfg[] = "\n" + "\n" + " \n" + ""; + settings1 = settingsBuilder(settings1).libraryxml(cfg, sizeof(cfg)).build(); + + TEST_CASE(assignAndCompare); // assignment and comparison don't match + TEST_CASE(mismatchingBitAnd); // overlapping bitmasks + TEST_CASE(comparison); // CheckCondition::comparison test cases + TEST_CASE(multicompare); // mismatching comparisons + TEST_CASE(overlappingElseIfCondition); // overlapping conditions in if and else-if + TEST_CASE(oppositeElseIfCondition); // opposite conditions in if and else-if + + TEST_CASE(checkBadBitmaskCheck); + + TEST_CASE(incorrectLogicOperator1); + TEST_CASE(incorrectLogicOperator2); + TEST_CASE(incorrectLogicOperator3); + TEST_CASE(incorrectLogicOperator4); + TEST_CASE(incorrectLogicOperator5); // complex expressions + TEST_CASE(incorrectLogicOperator6); // char literals + TEST_CASE(incorrectLogicOperator7); // opposite expressions: (expr || !expr) + TEST_CASE(incorrectLogicOperator8); // ! + TEST_CASE(incorrectLogicOperator9); + TEST_CASE(incorrectLogicOperator10); // enum + TEST_CASE(incorrectLogicOperator11); + TEST_CASE(incorrectLogicOperator12); + TEST_CASE(incorrectLogicOperator13); + TEST_CASE(incorrectLogicOperator14); + TEST_CASE(incorrectLogicOperator15); + TEST_CASE(incorrectLogicOperator16); // #10070 + TEST_CASE(incorrectLogicOperator17); + TEST_CASE(secondAlwaysTrueFalseWhenFirstTrueError); + TEST_CASE(incorrectLogicOp_condSwapping); + TEST_CASE(testBug5895); + TEST_CASE(testBug5309); + + TEST_CASE(modulo); + + TEST_CASE(oppositeInnerCondition); + TEST_CASE(oppositeInnerConditionPointers); + TEST_CASE(oppositeInnerConditionClass); + TEST_CASE(oppositeInnerConditionUndeclaredVariable); + TEST_CASE(oppositeInnerConditionAlias); + TEST_CASE(oppositeInnerCondition2); + TEST_CASE(oppositeInnerCondition3); + TEST_CASE(oppositeInnerConditionAnd); + TEST_CASE(oppositeInnerConditionOr); + TEST_CASE(oppositeInnerConditionEmpty); + TEST_CASE(oppositeInnerConditionFollowVar); + + TEST_CASE(identicalInnerCondition); + + TEST_CASE(identicalConditionAfterEarlyExit); + TEST_CASE(innerConditionModified); + + TEST_CASE(clarifyCondition1); // if (a = b() < 0) + TEST_CASE(clarifyCondition2); // if (a & b == c) + TEST_CASE(clarifyCondition3); // if (! a & b) + TEST_CASE(clarifyCondition4); // ticket #3110 + TEST_CASE(clarifyCondition5); // #3609 CWinTraits.. + TEST_CASE(clarifyCondition6); // #3818 + TEST_CASE(clarifyCondition7); + TEST_CASE(clarifyCondition8); + + TEST_CASE(alwaysTrue); + TEST_CASE(alwaysTrueSymbolic); + TEST_CASE(alwaysTrueInfer); + TEST_CASE(alwaysTrueContainer); + TEST_CASE(alwaysTrueLoop); + TEST_CASE(alwaysTrueTryCatch); + TEST_CASE(multiConditionAlwaysTrue); + TEST_CASE(duplicateCondition); + + TEST_CASE(checkInvalidTestForOverflow); + TEST_CASE(checkConditionIsAlwaysTrueOrFalseInsideIfWhile); + TEST_CASE(alwaysTrueFalseInLogicalOperators); + TEST_CASE(pointerAdditionResultNotNull); + TEST_CASE(duplicateConditionalAssign); + + TEST_CASE(checkAssignmentInCondition); + TEST_CASE(compareOutOfTypeRange); + TEST_CASE(knownConditionCast); // #9976 + TEST_CASE(knownConditionIncrementLoop); // #9808 + TEST_CASE(knownConditionAfterBailout); // #12526 + } + +#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) + void check_(const char* file, int line, const char code[], const Settings &settings, const char* filename = "test.cpp") { + std::vector files(1, filename); + Tokenizer tokenizer(settings, *this); + PreprocessorHelper::preprocess(code, files, tokenizer, *this); + + // Tokenizer.. + ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); + + // Run checks.. + runChecks(tokenizer, this); + } + + void check_(const char* file, int line, const char code[], const char* filename = "test.cpp", bool inconclusive = false) { + const Settings settings = settingsBuilder(settings0).certainty(Certainty::inconclusive, inconclusive).build(); + check_(file, line, code, settings, filename); + } + + 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==0x710' is always false.\n", errout_str()); + + check("void foo(int x) {\n" + " int y = x | 0x14;\n" + " if (y == 0x71f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // various simple assignments + check("void foo(int x) {\n" + " int y = (x+1) | 1;\n" + " if (y == 2);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==2' is always false.\n", errout_str()); + + check("void foo() {\n" + " int y = 1 | x();\n" + " if (y == 2);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==2' is always false.\n", errout_str()); + + // multiple conditions + check("void foo(int x) {\n" + " int y = x & 4;\n" + " if ((y == 3) && (z == 1));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==3' is always false.\n", errout_str()); + + check("void foo(int x) {\n" + " int y = x & 4;\n" + " if ((x==123) || ((y == 3) && (z == 1)));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==3' is always false.\n", errout_str()); + + check("void f(int x) {\n" + " int y = x & 7;\n" + " if (setvalue(&y) && y != 8);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // recursive checking into scopes + check("void f(int x) {\n" + " int y = x & 7;\n" + " if (z) y=0;\n" + " else { if (y==8); }\n" // always false + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false.\n", errout_str()); + + // while + check("void f(int x) {\n" + " int y = x & 7;\n" + " while (y==8);\n" // local variable => always false + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false.\n", errout_str()); + + check("void f(int x) {\n" + " extern int y; y = x & 7;\n" + " while (y==8);\n" // non-local variable => no error + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " int a = 100;\n" + " while (x) {\n" + " int y = 16 | a;\n" + " while (y != 0) y--;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void g(int x);\n" + "void f(int x) {\n" + " int a = 100;\n" + " while (x) {\n" + " int y = 16 | a;\n" + " while (y != 0) g(y);\n" + " }\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:5] -> [test.cpp:6]: (style) Mismatching assignment and comparison, comparison 'y!=0' is always true.\n", + errout_str()); + + check("void g(int &x);\n" + "void f(int x) {\n" + " int a = 100;\n" + " while (x) {\n" + " int y = 16 | a;\n" + " while (y != 0) g(y);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // calling function + check("void f(int x) {\n" + " int y = x & 7;\n" + " do_something();\n" + " if (y==8);\n" // local variable => always false + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false.\n", errout_str()); + + check("void f(int x) {\n" + " int y = x & 7;\n" + " do_something(&y);\n" // passing variable => no error + " if (y==8);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void do_something(int);\n" + "void f(int x) {\n" + " int y = x & 7;\n" + " do_something(y);\n" + " if (y==8);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Mismatching assignment and comparison, comparison 'y==8' is always false.\n", errout_str()); + + check("void f(int x) {\n" + " extern int y; y = x & 7;\n" + " do_something();\n" + " if (y==8);\n" // non-local variable => no error + "}"); + ASSERT_EQUALS("", errout_str()); + + // #4434 : false positive: ?: + check("void f(int x) {\n" + " x = x & 1;\n" + " x = x & 1 ? 1 : -1;\n" + " if(x != -1) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #4735 + check("void f() {\n" + " int x = *(char*)&0x12345678;\n" + " if (x==18) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // bailout: no variable info + check("void foo(int x) {\n" + " y = 2 | x;\n" // y not declared => no error + " if(y == 1) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // bailout: negative number + check("void foo(int x) {\n" + " int y = -2 | x;\n" // negative number => no error + " if (y==1) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // bailout: pass variable to function + check("void foo(int x) {\n" + " int y = 2 | x;\n" + " bar(&y);\n" // pass variable to function => no error + " if (y==1) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // no crash on unary operator& (#5643) + // #11610 + check("SdrObject* ApplyGraphicToObject() {\n" + " if (&rHitObject) {}\n" + " else if (rHitObject.IsClosedObj() && !&rHitObject) { }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Condition '&rHitObject' is always true\n" + "[test.cpp:3]: (style) Condition '!&rHitObject' is always false\n", + 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" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int a) {\n" // #6662 + " int x = a & 1;\n" + " while (x <= 4) {\n" + " if (x != 5) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Mismatching assignment and comparison, comparison 'x!=5' is always true.\n", errout_str()); + + check("void f(int a) {\n" // #6662 + " int x = a & 1;\n" + " while ((x += 4) < 10) {\n" + " if (x != 5) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int x = 100;\n" + " while (x) {\n" + " g(x);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void g(int x);\n" + "void f() {\n" + " int x = 100;\n" + " while (x) {\n" + " g(x);\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'x' is always true\n", errout_str()); + + check("void g(int & x);\n" + "void f() {\n" + " int x = 100;\n" + " while (x) {\n" + " g(x);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + } + + void mismatchingBitAnd() { + check("void f(int a) {\n" + " int b = a & 0xf0;\n" + " b &= 1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching bitmasks. Result is always 0 (X = Y & 0xf0; Z = X & 0x1; => Z=0).\n", errout_str()); + + check("void f(int a) {\n" + " int b = a & 0xf0;\n" + " int c = b & 1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Mismatching bitmasks. Result is always 0 (X = Y & 0xf0; Z = X & 0x1; => Z=0).\n", errout_str()); + + check("void f(int a) {\n" + " int b = a;" + " switch (x) {\n" + " case 1: b &= 1; break;\n" + " case 2: b &= 2; break;\n" + " };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void comparison() { + // CheckCondition::comparison test cases + // '==' + check("void f(int a) {\n assert( (a & 0x07) == 8U );\n}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) == 0x8' is always false.\n",errout_str()); + check("void f(int a) {\n assert( (a & b & 4 & c ) == 3 );\n}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x4) == 0x3' is always false.\n", errout_str()); + check("void f(int a) {\n assert( (a | 0x07) == 8U );\n}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) == 0x8' is always false.\n",errout_str()); + check("void f(int a) {\n assert( (a & 0x07) == 7U );\n}"); + ASSERT_EQUALS("", errout_str()); + check("void f(int a) {\n assert( (a | 0x01) == -15 );\n}"); + ASSERT_EQUALS("", errout_str()); + // '!=' + check("void f(int a) {\n assert( (a & 0x07) != 8U );\n}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) != 0x8' is always true.\n",errout_str()); + check("void f(int a) {\n assert( (a | 0x07) != 8U );\n}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) != 0x8' is always true.\n",errout_str()); + check("void f(int a) {\n assert( (a & 0x07) != 7U );\n}"); + ASSERT_EQUALS("", errout_str()); + check("void f(int a) {\n assert( (a | 0x07) != 7U );\n}"); + ASSERT_EQUALS("", errout_str()); + // '>=' + check("void f(int a) {\n assert( (a & 0x07) >= 8U );\n}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) >= 0x8' is always false.\n",errout_str()); + check("void f(unsigned int a) {\n assert( (a | 0x7) >= 7U );\n}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) >= 0x7' is always true.\n",errout_str()); + check("void f(int a) {\n assert( (a & 0x07) >= 7U );\n}"); + ASSERT_EQUALS("",errout_str()); + check("void f(int a) {\n assert( (a | 0x07) >= 8U );\n}"); + ASSERT_EQUALS("",errout_str()); //correct for negative 'a' + // '>' + check("void f(int a) {\n assert( (a & 0x07) > 7U );\n}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) > 0x7' is always false.\n",errout_str()); + check("void f(unsigned int a) {\n assert( (a | 0x7) > 6U );\n}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) > 0x6' is always true.\n",errout_str()); + check("void f(int a) {\n assert( (a & 0x07) > 6U );\n}"); + ASSERT_EQUALS("",errout_str()); + check("void f(int a) {\n assert( (a | 0x07) > 7U );\n}"); + ASSERT_EQUALS("",errout_str()); //correct for negative 'a' + // '<=' + check("void f(int a) {\n assert( (a & 0x07) <= 7U );\n}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) <= 0x7' is always true.\n",errout_str()); + check("void f(unsigned int a) {\n assert( (a | 0x08) <= 7U );\n}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x8) <= 0x7' is always false.\n",errout_str()); + check("void f(int a) {\n assert( (a & 0x07) <= 6U );\n}"); + ASSERT_EQUALS("",errout_str()); + check("void f(int a) {\n assert( (a | 0x08) <= 7U );\n}"); + ASSERT_EQUALS("",errout_str()); //correct for negative 'a' + // '<' + check("void f(int a) {\n assert( (a & 0x07) < 8U );\n}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X & 0x7) < 0x8' is always true.\n",errout_str()); + check("void f(unsigned int a) {\n assert( (a | 0x07) < 7U );\n}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Expression '(X | 0x7) < 0x7' is always false.\n",errout_str()); + check("void f(int a) {\n assert( (a & 0x07) < 3U );\n}"); + ASSERT_EQUALS("",errout_str()); + check("void f(int a) {\n assert( (a | 0x07) < 7U );\n}"); + ASSERT_EQUALS("",errout_str()); //correct for negative 'a' + + check("void f(int i) {\n" // #11998 + " if ((i & 0x100) == 0x200) {}\n" + " if (0x200 == (i & 0x100)) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The if condition is the same as the previous if condition\n" + "[test.cpp:2]: (style) Expression '(X & 0x100) == 0x200' is always false.\n" + "[test.cpp:3]: (style) Expression '(X & 0x100) == 0x200' is always false.\n", + errout_str()); + } + +#define checkPureFunction(code) checkPureFunction_(code, __FILE__, __LINE__) + void multicompare() { + check("void foo(int x)\n" + "{\n" + " if (x & 7);\n" + " else { if (x == 1); }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Expression is always false because 'else if' condition matches previous condition at line 3.\n", errout_str()); + + check("void foo(int x)\n" + "{\n" + " if (x & 7);\n" + " else { if (x & 1); }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Expression is always false because 'else if' condition matches previous condition at line 3.\n", errout_str()); + + check("extern int bar() __attribute__((pure));\n" + "void foo(int x)\n" + "{\n" + " if ( bar() >1 && b) {}\n" + " else if (bar() >1 && b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) Expression is always false because 'else if' condition matches previous condition at line 4.\n", errout_str()); + + checkPureFunction("extern int bar();\n" + "void foo(int x)\n" + "{\n" + " if ( bar() >1 && b) {}\n" + " else if (bar() >1 && b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) Expression is always false because 'else if' condition matches previous condition at line 4.\n", errout_str()); + + // 7284 + check("void foo() {\n" + " if (a) {}\n" + " else if (!!a) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout_str()); + + // #11059 + check("int f();\n" + "void g() {\n" + " int i = f();\n" + " if (i == 3) {}\n" + " else if ((i = f()) == 5) {}\n" + " else if (i == 3) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int f();\n" + "void g() {\n" + " int i = f();\n" + " if (i == 3) {}\n" + " else if ((i = f()) == 5) {}\n" + " else if (i != 3) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void checkPureFunction_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings1, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + runChecks(tokenizer, this); + } + + void overlappingElseIfCondition() { + check("void f(int a, int &b) {\n" + " if (a) { b = 1; }\n" + " else { if (a) { b = 2; } }\n" + "}"); + ASSERT_EQUALS("[test.cpp: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(Class &c) {\n" + " if (c.dostuff() == 3) {}\n" + " else { if (c.dostuff() == 3) {} }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const Class &c) {\n" + " if (c.dostuff() == 3) {}\n" + " else { if (c.dostuff() == 3) {} }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout_str()); + } + + check("void f(int a, int &b) {\n" + " x = x / 2;\n" + " if (x < 100) { b = 1; }\n" + " else { x = x / 2; if (x < 100) { b = 2; } }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int i) {\n" + " if(i == 0x02e2000000 || i == 0xa0c6000000)\n" + " foo(i);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // ticket 3689 ( avoid false positive ) + check("int fitInt(long long int nValue){\n" + " if( nValue < 0x7fffffffLL )\n" + " {\n" + " return 32;\n" + " }\n" + " if( nValue < 0x7fffffffffffLL )\n" + " {\n" + " return 48;\n" + " }\n" + " else {\n" + " if( nValue < 0x7fffffffffffffffLL )\n" + " {\n" + " return 64;\n" + " } else\n" + " {\n" + " return -1;\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(WIDGET *widget) {\n" + " if (dynamic_cast(widget)){}\n" + " else if (dynamic_cast(widget)){}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class B { virtual void v() {} };\n" // #11037 + "class D1 : public B {};\n" + "class D2 : public B {};\n" + "void f(const std::shared_ptr&p) {\n" + " const auto d1 = dynamic_cast(p.get());\n" + " const auto d2 = dynamic_cast(p.get());\n" + " if (d1) {}\n" + " else if (d2) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" // #6482 + " if (x & 1) {}\n" + " else if (x == 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if (x & 15) {}\n" + " else if (x == 40) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout_str()); + + check("void f(int x) {\n" + " if (x == sizeof(double)) {}\n" + " else { if (x == sizeof(long double)) {} }" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if (x & 0x08) {}\n" + " else if (x & 0xF8) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if (x & 0xF8) {}\n" + " else if (x & 0x08) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout_str()); + + check("void f(bool a, bool b) {\n" + " if(a && b){}\n" + " else if( !!b && !!a){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout_str()); + + check("void f(bool a, bool b) {\n" + " if(a && b){}\n" + " else if( !!b && a){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout_str()); + + check("void f(bool a, bool b) {\n" + " if(a && b){}\n" + " else if( b && !!a){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout_str()); + + check("void f(bool a, bool b) {\n" + " if(a && b){}\n" + " else if( b && !(!a)){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout_str()); + + check("void f(bool a, bool b) {\n" + " if(a && b){}\n" + " else if( !!b && !(!a)){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Expression is always false because 'else if' condition matches previous condition at line 2.\n", errout_str()); + + check("void f(bool a, bool b) {\n" + " if(a && b){}\n" + " else if( !!(b) && !!(a+b)){}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #8168 + check("enum MaskValues\n" + "{\n" + " Value1 = 0x00000001,\n" + " Value2 = 0x00000002\n" + "};\n" + "void TestFunction(int value) {\n" + " if ( value & (int)Value1 ) {}\n" + " else if ( value & (int)Value2 ) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(size_t x) {\n" + " if (x == sizeof(int)) {}\n" + " else { if (x == sizeof(long))} {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(size_t x) {\n" + " if (x == sizeof(long)) {}\n" + " else { if (x == sizeof(long long))} {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void oppositeElseIfCondition() { + setMultiline(); + + check("void f(int x) {\n" + " if (x) {}\n" + " else if (!x) {}\n" + "}"); + ASSERT_EQUALS("test.cpp:3:style:Expression is always true because 'else if' condition is opposite to previous condition at line 2.\n" + "test.cpp:2:note:first condition\n" + "test.cpp:3:note:else if condition is opposite to first condition\n", errout_str()); + + check("void f(int x) {\n" + " int y = x;\n" + " if (x) {}\n" + " else if (!y) {}\n" + "}"); + ASSERT_EQUALS("test.cpp:4:style:Expression is always true because 'else if' condition is opposite to previous condition at line 3.\n" + "test.cpp:2:note:'y' is assigned value 'x' here.\n" + "test.cpp:3:note:first condition\n" + "test.cpp:4:note:else if condition is opposite to first condition\n", errout_str()); + } + + void checkBadBitmaskCheck() { + check("bool f(int x) {\n" + " bool b = x | 0x02;\n" + " return b;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout_str()); + + check("bool f(int x) {\n" + " bool b = 0x02 | x;\n" + " return b;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout_str()); + + check("int f(int x) {\n" + " int b = x | 0x02;\n" + " return b;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(int x) {\n" + " bool b = x & 0x02;\n" + " return b;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(int x) {\n" + " if(x | 0x02)\n" + " return b;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout_str()); + + check("bool f(int x) {\n" + " int y = 0x1;\n" + " if(b) y = 0;\n" + " if(x | y)\n" + " return b;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(int x) {\n" + " foo(a && (x | 0x02));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout_str()); + + check("int f(int x) {\n" + " return (x | 0x02) ? 0 : 5;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout_str()); + + check("int f(int x) {\n" + " return x ? (x | 0x02) : 5;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(int x) {\n" + " return x | 0x02;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout_str()); + + check("bool f(int x) {\n" + " if (x) {\n" + " return x | 0x02;\n" + " }\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout_str()); + + check("const bool f(int x) {\n" + " return x | 0x02;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout_str()); + + check("struct F {\n" + " static const bool f(int x) {\n" + " return x | 0x02;\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout_str()); + + check("struct F {\n" + " typedef bool b_t;\n" + "};\n" + "F::b_t f(int x) {\n" + " return x | 0x02;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?\n", errout_str()); + + check("int f(int x) {\n" + " return x | 0x02;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void create_rop_masks_4( rop_mask_bits *bits) {\n" + "DWORD mask_offset;\n" + "BYTE *and_bits = bits->and;\n" + "rop_mask *rop_mask;\n" + "and_bits[mask_offset] |= (rop_mask->and & 0x0f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(unsigned a, unsigned b) {\n" + " unsigned cmd1 = b & 0x0F;\n" + " if (cmd1 | a) {\n" + " if (b == 0x0C) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int i) {\n" // #11082 + " int j = 0;\n" + " if (i | j) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Operator '|' with one operand equal to zero is redundant.\n", errout_str()); + + check("#define EIGHTTOIS(x) (((x) << 8) | (x))\n" + "int f() {\n" + " return EIGHTTOIS(0);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("#define O_RDONLY 0\n" + "void f(const char* s, int* pFd) {\n" + " *pFd = open(s, O_RDONLY | O_BINARY, 0);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("const int FEATURE_BITS = x |\n" + "#if FOO_ENABLED\n" + " FEATURE_FOO |\n" + "#endif\n" + "#if BAR_ENABLED\n" + " FEATURE_BAR |\n" + "#endif\n" + " 0;"); + ASSERT_EQUALS("", errout_str()); + + check("enum precedence { PC0, UNARY };\n" + "int x = PC0 | UNARY;\n" + "int y = UNARY | PC0;\n"); + ASSERT_EQUALS("", errout_str()); + + check("#define MASK 0\n" + "#define SHIFT 1\n" + "int x = 1 | (MASK << SHIFT);\n"); + ASSERT_EQUALS("", errout_str()); + } + + + void incorrectLogicOperator1() { + check("void f(int x) {\n" + " if ((x != 1) || (x != 3))\n" + " a++;\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" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x != 1 || x != 3.\n", errout_str()); + + check("void f(int x) {\n" + " if (x<0 && !x) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 0 && !x.\n", errout_str()); + + check("void f(int x) {\n" + " if (x==0 && x) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 0 && x.\n", errout_str()); + + check("void f(int x) {\n" // ast.. + " if (y == 1 && x == 1 && x == 7) { }\n" + "}"); + 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" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x, int y) {\n" + " if ((y == 1) && (x != 1) || (x != 3))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x, int y) {\n" + " if ((x != 1) || (x != 3) && (y == 1))\n" + " a++;\n" + "}" + ); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x!=3' is always true\n", errout_str()); + + check("void f(int x) {\n" + " if ((x != 1) && (x != 3))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if ((x == 1) || (x == 3))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x, int y) {\n" + " if ((x != 1) || (y != 3))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x, int y) {\n" + " if ((x != hotdog) || (y != hotdog))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x, int y) {\n" + " if ((x != 5) || (y != 5))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + + check("void f(int x) {\n" + " if ((x != 5) || (x != 6))\n" + " a++;\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" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'c!=a' is always false\n", errout_str()); + } + + void incorrectLogicOperator2() { + check("void f(float x) {\n" + " if ((x == 1) && (x == 1.0))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if ((x == 1) && (x == 0x00000001))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'x==0x00000001' is always true\n", errout_str()); + + check("void f(int x) {\n" + " if (x == 1 && x == 3)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 1 && x == 3.\n", errout_str()); + + check("void f(int x) {\n" + " if (x == 1.0 && x == 3.0)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); // float comparisons with == and != are not checked right now - such comparison is a bad idea + + check("void f(float x) {\n" + " if (x == 1 && x == 1.0)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void bar(float f) {\n" // #5246 + " if ((f > 0) && (f < 1)) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if (x < 1 && x > 1)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 1.\n", errout_str()); + + check("void f(int x) {\n" + " if (x < 1.0 && x > 1.0)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1.0 && x > 1.0.\n", errout_str()); + + check("void f(int x) {\n" + " if (x < 1 && x > 1.0)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 1.0.\n", errout_str()); + + check("void f(int x) {\n" + " if (x >= 1.0 && x <= 1.001)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if (x < 1 && x > 3)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout_str()); + + check("void f(float x) {\n" + " if (x < 1.0 && x > 3.0)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1.0 && x > 3.0.\n", errout_str()); + + check("void f(int x) {\n" + " if (1 > x && 3 < x)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout_str()); + + check("void f(int x) {\n" + " if (x < 3 && x > 1)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if (x > 3 || x < 10)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x > 3 || x < 10.\n", errout_str()); + + check("void f(int x) {\n" + " if (x >= 3 || x <= 10)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x >= 3 || x <= 10.\n", errout_str()); + + check("void f(int x) {\n" + " if (x >= 3 || x < 10)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x >= 3 || x < 10.\n", errout_str()); + + check("void f(int x) {\n" + " if (x > 3 || x <= 10)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x > 3 || x <= 10.\n", errout_str()); + + check("void f(int x) {\n" + " if (x > 3 || x < 3)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if (x >= 3 || x <= 3)\n" + " a++;\n" + "}" + ); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x >= 3 || x <= 3.\n", errout_str()); + + check("void f(int x) {\n" + " if (x >= 3 || x < 3)\n" + " a++;\n" + "}" + ); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x >= 3 || x < 3.\n", errout_str()); + + check("void f(int x) {\n" + " if (x > 3 || x <= 3)\n" + " a++;\n" + "}" + ); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x > 3 || x <= 3.\n", errout_str()); + + check("void f(int x) {\n" + " if((x==3) && (x!=4))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: The condition 'x != 4' is redundant since 'x == 3' is sufficient.\n", errout_str()); + + check("void f(const std::string &s) {\n" // #8860 + " const std::size_t p = s.find(\"42\");\n" + " const std::size_t * const ptr = &p;\n" + " if(p != std::string::npos && p == 0 && *ptr != 1){;}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:4]: (style) Condition '*ptr!=1' is always true\n", errout_str()); + + check("void f(int x) {\n" + " if ((x!=4) && (x==3))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: The condition 'x != 4' is redundant since 'x == 3' is sufficient.\n", errout_str()); + + check("void f(int x) {\n" + " if ((x==3) || (x!=4))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: The condition 'x == 3' is redundant since 'x != 4' is sufficient.\n", errout_str()); + + check("void f(int x) {\n" + " if ((x!=4) || (x==3))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: The condition 'x == 3' is redundant since 'x != 4' is sufficient.\n", errout_str()); + + check("void f(int x) {\n" + " if ((x==3) && (x!=3))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == 3 && x != 3.\n", errout_str()); + + check("void f(int x) {\n" + " if ((x==6) || (x!=6))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: x == 6 || x != 6.\n", errout_str()); + + check("void f(int x) {\n" + " if (x > 10 || x < 3)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if (x > 5 && x == 1)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 5 && x == 1.\n", errout_str()); + + check("void f(int x) {\n" + " if (x > 5 && x == 6)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: The condition 'x > 5' is redundant since 'x == 6' is sufficient.\n", errout_str()); + + // #3419 + check("void f() {\n" + " if ( &q != &a && &q != &b ) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #3676 + check("void f(int m_x2, int w, int x) {\n" + " if (x + w - 1 > m_x2 || m_x2 < 0 )\n" + " m_x2 = x + w - 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(float x) {\n" // x+1 => x + " if (x <= 1.0e20 && x >= -1.0e20) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(float x) {\n" // x+1 => x + " if (x >= 1.0e20 && x <= 1.0e21) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(float x) {\n" // x+1 => x + " if (x <= -1.0e20 && x >= -1.0e21) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void incorrectLogicOperator3() { + check("void f(int x, bool& b) {\n" + " b = x > 5 && x == 1;\n" + " c = x < 1 && x == 3;\n" + " d = x >= 5 && x == 1;\n" + " e = x <= 1 && x == 3;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 5 && x == 1.\n" + "[test.cpp:3]: (warning) Logical conjunction always evaluates to false: x < 1 && x == 3.\n" + "[test.cpp:4]: (warning) Logical conjunction always evaluates to false: x >= 5 && x == 1.\n" + "[test.cpp:5]: (warning) Logical conjunction always evaluates to false: x <= 1 && x == 3.\n", errout_str()); + } + + void incorrectLogicOperator4() { + check("#define ZERO 0\n" + "void f(int x) {\n" + " if (x && x != ZERO) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int N) {\n" // #9789 + " T a[20] = { 0 };\n" + " for (int i = 0; i < N; ++i) {\n" + " if (0 < a[i] && a[i] < 1) {}\n" + " }\n" + "}\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: x+3 > 2 || x+3 < 10.\n", errout_str()); + } + + void incorrectLogicOperator6() { // char literals + check("void f(char x) {\n" + " if (x == '1' || x == '2') {}\n" + "}", "test.cpp", true); + ASSERT_EQUALS("", errout_str()); + + check("void f(char x) {\n" + " if (x == '1' && x == '2') {}\n" + "}", "test.cpp", true); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x == '1' && x == '2'.\n", errout_str()); + + check("int f(char c) {\n" + " return (c >= 'a' && c <= 'z');\n" + "}", "test.cpp", true); + ASSERT_EQUALS("", errout_str()); + + check("int f(char c) {\n" + " return (c <= 'a' && c >= 'z');\n" + "}", "test.cpp", true); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Logical conjunction always evaluates to false: c <= 'a' && c >= 'z'.\n", errout_str()); + + check("int f(char c) {\n" + " return (c <= 'a' && c >= 'z');\n" + "}", "test.cpp", false); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Return value 'c>='z'' is always false\n", errout_str()); + } + + void incorrectLogicOperator7() { // opposite expressions + check("void f(int i) {\n" + " if (i || !i) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: i || !(i).\n", errout_str()); + + check("void f(int a, int b) {\n" + " if (a>b || a<=b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical disjunction always evaluates to true: a > b || a <= b.\n", errout_str()); + + check("void f(int a, int b) {\n" + " if (a>b || a T icdf( const T uniform ) {\n" + " if ((0 -1.0 - 1.0e-12))\n" + " return;\n" + " else\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void incorrectLogicOperator8() { // opposite expressions + check("void f(int i) {\n" + " if (!(i!=10) && !(i!=20)) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: !(i != 10) && !(i != 20).\n", errout_str()); + } + + void incorrectLogicOperator9() { // #6069 "False positive incorrectLogicOperator due to dynamic_cast" + check("class MyType;\n" + "class OtherType;\n" + "void foo (OtherType* obj) {\n" + " assert((!obj) || dynamic_cast(obj));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void incorrectLogicOperator10() { // #7794 - enum + check("typedef enum { A, B } Type_t;\n" + "void f(Type_t t) {\n" + " if ((t == A) && (t == B))\n" + " {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Logical conjunction always evaluates to false: t == 0 && t == 1.\n", errout_str()); + } + + void incorrectLogicOperator11() { + check("void foo(int i, const int n) { if ( i < n && i == n ) {} }"); + ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i < n && i == n.\n", errout_str()); + + check("void foo(int i, const int n) { if ( i > n && i == n ) {} }"); + ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i > n && i == n.\n", errout_str()); + + check("void foo(int i, const int n) { if ( i == n && i > n ) {} }"); + ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i == n && i > n.\n", errout_str()); + + check("void foo(int i, const int n) { if ( i == n && i < n ) {} }"); + ASSERT_EQUALS("[test.cpp:1]: (warning) Logical conjunction always evaluates to false: i == n && i < n.\n", errout_str()); + } + + void incorrectLogicOperator12() { // #8696 + check("struct A {\n" + " void f() const;\n" + "};\n" + "void foo(A a, A b) {\n" + " A x = b;\n" + " A y = b;\n" + " y.f();\n" + " if (a > x && a < y)\n" + " return;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:5] -> [test.cpp:6] -> [test.cpp:8]: (warning) Logical conjunction always evaluates to false: a > x && a < y.\n", + errout_str()); + + check("struct A {\n" + " void f();\n" + "};\n" + "void foo(A a, A b) {\n" + " A x = b;\n" + " A y = b;\n" + " y.f();\n" + " if (a > x && a < y)\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(A a, A b) {\n" + " A x = b;\n" + " A y = b;\n" + " y.f();\n" + " if (a > x && a < y)\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(A a, A b) {\n" + " const A x = b;\n" + " const A y = b;\n" + " y.f();\n" + " if (a > x && a < y)\n" + " return;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:3] -> [test.cpp:5]: (warning) Logical conjunction always evaluates to false: a > x && a < y.\n", + errout_str()); + + check("struct A {\n" + " void f() const;\n" + "};\n" + "void foo(A a) {\n" + " A x = a;\n" + " A y = a;\n" + " y.f();\n" + " if (a > x && a < y)\n" + " return;\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (style) Condition 'a>x' is always false\n" + "[test.cpp:8]: (style) Condition 'a x && a < y)\n" + " return;\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (style) Condition 'a>x' is always false\n", errout_str()); + + check("void foo(A a) {\n" + " A x = a;\n" + " A y = a;\n" + " y.f();\n" + " if (a > x && a < y)\n" + " return;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'a>x' is always false\n", errout_str()); + + check("void foo(A a) {\n" + " const A x = a;\n" + " const A y = a;\n" + " y.f();\n" + " if (a > x && a < y)\n" + " return;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'a>x' is always false\n" + "[test.cpp:5]: (style) Condition 'a [test.cpp:3]: (warning) Logical conjunction always evaluates to false: v == 1 && x == 2.\n", errout_str()); + + check("void f2(const int *v) {\n" + " const int *x=v;\n" + " if ((*v == 1) && (*x == 2)) {;}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Logical conjunction always evaluates to false: *(v) == 1 && *(x) == 2.\n", errout_str()); + } + + void incorrectLogicOperator14() { + check("static const std ::string h;\n" + "class i {\n" + "public:\n" + " struct j {\n" + " std ::string k;\n" + " std ::string l;\n" + " };\n" + " struct a {\n" + " enum { m = 1 };\n" + " };\n" + "} b;\n" + "namespace n {\n" + "class c;\n" + "}\n" + "struct o {\n" + " enum { p, d, q, r };\n" + " enum { e, f };\n" + "\n" + "public:\n" + " class j {\n" + " public:\n" + " class s {\n" + " std ::string a;\n" + " };\n" + " };\n" + "};\n" + "namespace n {\n" + "class b;\n" + "}\n" + "namespace aa {\n" + "class d {\n" + "public:\n" + " char t;\n" + " enum {} u;\n" + "};\n" + "} // namespace aa\n" + "namespace aa {\n" + "struct e {};\n" + "} // namespace aa\n" + "class a;\n" + "class w {\n" + "public:\n" + " enum { x };\n" + " struct {\n" + " } y;\n" + " std ::string z;\n" + "};\n" + "class ab {\n" + " friend class c;\n" + "\n" + "public:\n" + " class ac {\n" + " void e(const ac &v) const;\n" + " };\n" + "};\n" + "class f;\n" + "class ad {\n" + " friend class e;\n" + " enum { e, ae, ag, ah, ai, aj, ak, a, b };\n" + " class c {};\n" + " class d {\n" + " enum am { f, an, ao, ap, aq, ar, b, as, at, c, au };\n" + " enum av { aw, ax, ay, az, e, ba, bb, bc, bd, a };\n" + " struct b {\n" + " am action;\n" + " av c;\n" + " };\n" + " };\n" + " class e {\n" + " public:\n" + " std ::string e;\n" + " class f {\n" + " } f;\n" + " class be {\n" + " public:\n" + " };\n" + " std ::vector bf;\n" + " enum { bg, b } c;\n" + " };\n" + " struct bh {\n" + " std ::map b;\n" + " };\n" + " std ::map bi;\n" + " struct {\n" + " int b;\n" + " char bj;\n" + " } bk;\n" + " class a {\n" + " public:\n" + " std ::set b;\n" + " };\n" + "};\n" + "class bl;\n" + "class al;\n" + "class bm;\n" + "class f;\n" + "class b;\n" + "class bn;\n" + "namespace bo {\n" + "class bp {\n" + "public:\n" + " typedef std ::pair bq;\n" + " typedef std ::list br;\n" + "};\n" + "const bo ::bp *dg(const f *a, const al *b);\n" + "} // namespace bo\n" + "const bn *dh(const f *d, bo ::bp ::br &bs);\n" + "class f {\n" + "public:\n" + " struct bt {};\n" + " std ::vector f;\n" + "};\n" + "class bu;\n" + "class a;\n" + "class c;\n" + "struct bv {};\n" + "class af {\n" + "private:\n" + "public:\n" + " enum { b, d, e, f, c, bw };\n" + " void a(int c);\n" + " af *bx() const;\n" + "};\n" + "namespace by {\n" + "class b;\n" + "}\n" + "class b {\n" + "public:\n" + " bool d, c;\n" + "};\n" + "class bz;\n" + "class f;\n" + "class ca {\n" + " friend class b;\n" + "\n" + "public:\n" + " const bm *cb() const { return cc; }\n" + " f *d(f *e, bool f) const;\n" + " int e() { return ++cd; }\n" + " bl *const c;\n" + " bm *cc;\n" + " std ::map ce;\n" + " int cd;\n" + " bz *a;\n" + "};\n" + "namespace n {\n" + "class c;\n" + "class d;\n" + "} // namespace n\n" + "class cf {\n" + "public:\n" + " explicit cf(const std ::string &aname);\n" + " cf(const std ::string &aname, const ca *cg, const al *ch, bl *ci)\n" + " : cj(cg), ck(ch), cl(ci), cn(aname) {}\n" + "\n" + "protected:\n" + " const ca *const cj;\n" + " const al *const ck;\n" + " bl *const cl;\n" + " const std ::string cn;\n" + "};\n" + "class cm : public cf {\n" + "public:\n" + " void cp();\n" + " std ::string d() const;\n" + "};\n" + "struct co {\n" + " co();\n" + " const bu *a;\n" + " enum f {};\n" + " enum {\n" + " b = (1 << 0),\n" + " c = (1 << 1),\n" + " };\n" + " void d(bool e);\n" + "};\n" + "class bu {\n" + " friend class e;\n" + "\n" + "public:\n" + " struct f {};\n" + " enum { d, cr, cq, ct, cs, e, a, b, c, dd, cu, cv, cw, cx, cy, cz, da };\n" + " const f *db;\n" + " const af *dc;\n" + "} f{};\n" + "class bm {\n" + "public:\n" + " std ::list df;\n" + " std ::vector de;\n" + " mutable std ::set f;\n" + "};\n" + "void cm ::cp() {\n" + " const bm *a = cj->cb();\n" + " for (const bu *b : a->de)\n" + " for (af *c = b->dc->bx();;) {\n" + " af *d = c;\n" + " af *e = c;\n" + " bool f(d);\n" + " bool g(e);\n" + " if (f && g)\n" + " ;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:200] -> [test.cpp:200]: (style) Condition 'g' is always true\n", errout_str()); + } + + void incorrectLogicOperator15() { + // 10022 + check("struct PipeRoute {\n" + " std::deque points;\n" + " std::deque estimates;\n" + "};\n" + "void CleanPipeRoutes(std::map& pipeRoutes) {\n" + " for (auto it = pipeRoutes.begin(); it != pipeRoutes.end(); ) {\n" + " PipeRoute* curRoute = it->second;\n" + " if (curRoute->points.empty() && curRoute->estimates.size() != 2)\n" + " {\n" + " delete curRoute;\n" + " it = pipeRoutes.erase(it);\n" + " }\n" + " else\n" + " {\n" + " ++it;\n" + " }\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void incorrectLogicOperator16() { // #10070 + check("void foo(void* p) {\n" + " if (!p || p == -1) { }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void incorrectLogicOperator17() { // #12471 + check("struct R {\n" + " void set() { i = 1; }\n" + " int get() const { return i; }\n" + " int i;\n" + "};\n" + "struct P {\n" + " void f();\n" + " R* r;\n" + "};\n" + "void P::f() {\n" + " int a = r->get();\n" + " r->set();\n" + " if (a == 0 && r->get()) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void secondAlwaysTrueFalseWhenFirstTrueError() { + check("void f(void) {\n" // #8892 + " const char c[1] = { \'x\' }; \n" + " if(c[0] == \'x\'){;}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'c[0]=='x'' is always true\n", errout_str()); + + check("void f(int x) {\n" + " if (x > 5 && x != 1)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: The condition 'x != 1' is redundant since 'x > 5' is sufficient.\n", errout_str()); + + check("void f(int x) {\n" + " if (x > 5 && x != 6)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if ((x > 5) && (x != 1))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: The condition 'x != 1' is redundant since 'x > 5' is sufficient.\n", errout_str()); + + check("void f(int x) {\n" + " if ((x > 5) && (x != 6))\n" + " a++;\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: The condition 'x == 4' is redundant since 'x > 3' is sufficient.\n" + "[test.cpp:3]: (style) Redundant condition: The condition 'x == 4' is redundant since 'x < 5' is sufficient.\n" + "[test.cpp:4]: (style) Redundant condition: The condition 'x == 4' is redundant since 'x >= 3' is sufficient.\n" + "[test.cpp:5]: (style) Redundant condition: The condition 'x == 4' is redundant since 'x <= 5' is sufficient.\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: The condition 'x > 5' is redundant since 'x != 1' is sufficient.\n" + "[test.cpp:3]: (style) Redundant condition: The condition 'x < 1' is redundant since 'x != 3' is sufficient.\n" + "[test.cpp:4]: (style) Redundant condition: The condition 'x >= 5' is redundant since 'x != 1' is sufficient.\n" + "[test.cpp:5]: (style) Redundant condition: The condition 'x <= 1' is redundant since 'x != 3' is sufficient.\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: The condition 'x > 5' is redundant since 'x > 6' is sufficient.\n" + "[test.cpp:3]: (style) Redundant condition: The condition 'x > 6' is redundant since 'x > 5' is sufficient.\n" + "[test.cpp:4]: (style) Redundant condition: The condition 'x < 6' is redundant since 'x < 5' is sufficient.\n" + "[test.cpp:5]: (style) Redundant condition: The condition 'x < 5' is redundant since 'x < 6' is sufficient.\n", + errout_str()); + + check("void f(double x, bool& b) {\n" + " b = x > 6.5 && x > 5.5;\n" + " c = x > 5.5 || x > 6.5;\n" + " d = x < 6.5 && x < 5.5;\n" + " e = x < 5.5 || x < 6.5;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: The condition 'x > 5.5' is redundant since 'x > 6.5' is sufficient.\n" + "[test.cpp:3]: (style) Redundant condition: The condition 'x > 6.5' is redundant since 'x > 5.5' is sufficient.\n" + "[test.cpp:4]: (style) Redundant condition: The condition 'x < 6.5' is redundant since 'x < 5.5' is sufficient.\n" + "[test.cpp:5]: (style) Redundant condition: The condition 'x < 5.5' is redundant since 'x < 6.5' is sufficient.\n", + errout_str()); + + check("void f(const char *p) {\n" // #10320 + " if (!p || !*p || *p != 'x') {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: The condition '!*p' is redundant since '*p != 'x'' is sufficient.\n", + errout_str()); + } + + void incorrectLogicOp_condSwapping() { + check("void f(int x) {\n" + " if (x < 1 && x > 3)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout_str()); + + check("void f(int x) {\n" + " if (1 > x && x > 3)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout_str()); + + check("void f(int x) {\n" + " if (x < 1 && 3 < x)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout_str()); + + check("void f(int x) {\n" + " if (1 > x && 3 < x)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x < 1 && x > 3.\n", errout_str()); + + check("void f(int x) {\n" + " if (x > 3 && x < 1)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1.\n", errout_str()); + + check("void f(int x) {\n" + " if (3 < x && x < 1)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1.\n", errout_str()); + + check("void f(int x) {\n" + " if (x > 3 && 1 > x)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1.\n", errout_str()); + + check("void f(int x) {\n" + " if (3 < x && 1 > x)\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Logical conjunction always evaluates to false: x > 3 && x < 1.\n", errout_str()); + } + + void modulo() { + check("bool f(bool& b1, bool& b2, bool& b3) {\n" + " b1 = a % 5 == 4;\n" + " b2 = a % c == 100000;\n" + " b3 = a % 5 == c;\n" + " return a % 5 == 5-p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(bool& b1, bool& b2, bool& b3, bool& b4, bool& b5) {\n" + " b1 = a % 5 < 5;\n" + " b2 = a % 5 <= 5;\n" + " b3 = a % 5 == 5;\n" + " b4 = a % 5 != 5;\n" + " b5 = a % 5 >= 5;\n" + " return a % 5 > 5;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" + "[test.cpp:3]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" + "[test.cpp:4]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" + "[test.cpp:5]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" + "[test.cpp:6]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" + "[test.cpp:7]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n", + errout_str()); + + check("void f(bool& b1, bool& b2) {\n" + " b1 = bar() % 5 < 889;\n" + " if(x[593] % 5 <= 5)\n" + " b2 = x.a % 5 == 5;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" + "[test.cpp:3]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n" + "[test.cpp:4]: (warning) Comparison of modulo result is predetermined, because it is always less than 5.\n", + errout_str()); + + check("void f() {\n" + " if (a % 2 + b % 2 == 2)\n" + " foo();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void oppositeInnerCondition() { + check("void foo(int a, int b) {\n" + " if(a==b)\n" + " if(a!=b)\n" + " cout << a;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("bool foo(int a, int b) {\n" + " if(a==b)\n" + " return a!=b;\n" + " return false;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'return' condition leads to a dead code block.\n", errout_str()); + + check("void foo(int a, int b) {\n" + " if(a==b)\n" + " if(b!=a)\n" + " cout << a;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void foo(int a) {\n" + " if(a >= 50) {\n" + " if(a < 50)\n" + " cout << a;\n" + " else\n" + " cout << 100;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + // #4186 + check("void foo(int a) {\n" + " if(a >= 50) {\n" + " if(a > 50)\n" + " cout << a;\n" + " else\n" + " cout << 100;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // 4170 + check("class foo {\n" + " void bar() {\n" + " if (tok == '(') {\n" + " next();\n" + " if (tok == ',') {\n" + " next();\n" + " if (tok != ',') {\n" + " op->reg2 = asm_parse_reg();\n" + " }\n" + " skip(',');\n" + " }\n" + " }\n" + " }\n" + " void next();\n" + " const char *tok;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int i)\n" + "{\n" + " if(i > 5) {\n" + " i = bar();\n" + " if(i < 5) {\n" + " cout << a;\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int& i) {\n" + " i=6;\n" + "}\n" + "void bar(int i) {\n" + " if(i>5) {\n" + " foo(i);\n" + " if(i<5) {\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int& i);\n" + "void bar() {\n" + " int i; i = func();\n" + " if(i>5) {\n" + " foo(i);\n" + " if(i<5) {\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int i);\n" + "void bar(int i) {\n" + " if(i>5) {\n" + " foo(i);\n" + " if(i<5) {\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void foo(const int &i);\n" + "void bar(int i) {\n" + " if(i>5) {\n" + " foo(i);\n" + " if(i<5) {\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void foo(int i);\n" + "void bar() {\n" + " int i; i = func();\n" + " if(i>5) {\n" + " foo(i);\n" + " if(i<5) {\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("class C { void f(int &i) const; };\n" // #7028 - variable is changed by const method + "void foo(C c, int i) {\n" + " if (i==5) {\n" + " c.f(i);\n" + " if (i != 5) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // see linux revision 1f80c0cc + check("int generic_write_sync(int,int,int);\n" + "\n" + "void cifs_writev(int i) {\n" + " int rc = __generic_file_aio_write();\n" + " if (rc > 0){\n" + " err = generic_write_sync(file, iocb->ki_pos - rc, rc);\n" + " if(rc < 0) {\n" // <- condition is always false + " err = rc;\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:7]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + + // #5874 - array + check("void testOppositeConditions2() {\n" + " int array[2] = { 0, 0 };\n" + " if (array[0] < 2) {\n" + " array[0] += 5;\n" + " if (array[0] > 2) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #6227 - FP caused by simplifications of casts and known variables + check("void foo(A *a) {\n" + " if(a) {\n" + " B *b = dynamic_cast(a);\n" + " if(!b) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int a) {\n" + " if(a) {\n" + " int b = a;\n" + " if(!b) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void foo(unsigned u) {\n" + " if (u != 0) {\n" + " for (int i=0; i<32; i++) {\n" + " if (u == 0) {}\n" // <- don't warn + " u = x;\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #8186 + check("void f() {\n" + " for (int i=0;i<4;i++) {\n" + " if (i==5) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + // #8938 + check("void Delete(SS_CELLCOORD upperleft) {\n" + " if ((upperleft.Col == -1) && (upperleft.Row == -1)) {\n" + " GetActiveCell(&(upperleft.Col), &(upperleft.Row));\n" + " if (upperleft.Row == -1) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9702 + check("struct A {\n" + " void DoTest() {\n" + " if (!IsSet()) {\n" + " m_value = true;\n" + " if (IsSet());\n" + " }\n" + " }\n" + " bool IsSet() const { return m_value; }\n" + " bool m_value = false;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void oppositeInnerConditionPointers() { + check("void f(struct ABC *abc) {\n" + " struct AB *ab = abc->ab;\n" + " if (ab->a == 123){\n" + " do_something(abc);\n" // might change ab->a + " if (ab->a != 123) {\n" + " err = rc;\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void Fred::f() {\n" // daca: ace + " if (this->next_ == map_man_->table_) {\n" + " this->next_ = n;\n" + " if (this->next_ != map_man_->table_) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void test(float *f) {\n" // #7405 + " if(*f>10) {\n" + " (*f) += 0.1f;\n" + " if(*f<10) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int * f(int * x, int * y) {\n" + " if(!x) return x;\n" + " return y;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void oppositeInnerConditionClass() { + // #6095 - calling member function that might change the state + check("void f() {\n" + " const Fred fred;\n" // <- fred is const, warn + " if (fred.isValid()) {\n" + " fred.dostuff();\n" + " if (!fred.isValid()) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("class Fred { public: bool isValid() const; void dostuff() const; };\n" + "void f() {\n" + " Fred fred;\n" + " if (fred.isValid()) {\n" + " fred.dostuff();\n" // <- dostuff() is const, warn + " if (!fred.isValid()) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void f() {\n" + " Fred fred;\n" + " if (fred.isValid()) {\n" + " fred.dostuff();\n" + " if (!fred.isValid()) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #6385 "crash in Variable::getFlag()" + check("class TranslationHandler {\n" + "QTranslator *mTranslator;\n" + "void SetLanguage() {\n" + " if (mTranslator) {\n" + " qApp->removeTranslator(mTranslator);\n" + " }\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); // just don't crash... + + check("bool f(std::ofstream &CFileStream) {\n" // #8198 + " if(!CFileStream.good()) { return; }\n" + " CFileStream << \"abc\";\n" + " if (!CFileStream.good()) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void oppositeInnerConditionUndeclaredVariable() { + // #5731 - fp when undeclared variable is used + check("void f() {\n" + " if (x == -1){\n" + " x = do_something();\n" + " if (x != -1) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #5750 - another fp when undeclared variable is used + check("void f() {\n" + " if (r < w){\n" + " r += 3;\n" + " if (r > w) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #6574 - another fp when undeclared variable is used + check("void foo() {\n" + " if(i) {\n" + " i++;\n" + " if(!i) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // undeclared array + check("void f(int x) {\n" + " if (a[x] > 0) {\n" + " a[x] -= dt;\n" + " if (a[x] < 0) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #6313 - false positive: opposite conditions in nested if blocks when condition changed + check("void Foo::Bar() {\n" + " if(var){\n" + " --var;\n" + " if(!var){}\n" + " else {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // daca hyphy + check("bool f() {\n" + " if (rec.lLength==0) {\n" + " rec.Delete(i);\n" + " if (rec.lLength!=0) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void oppositeInnerConditionAlias() { + check("void f() {\n" + " struct S s;\n" + " bool hasFailed = false;\n" + " s.status = &hasFailed;\n" + "\n" + " if (! hasFailed) {\n" + " doStuff(&s);\n" + " if (hasFailed) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (style) Condition '!hasFailed' is always true\n", errout_str()); + } + + void oppositeInnerCondition2() { + // first comparison: < + check("void f(int x) {\n" + "\n" + " if (x<4) {\n" + " if (x==5) {}\n" // <- Warning + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", + errout_str()); + check("void f(int x) {\n" + "\n" + " if (x<4) {\n" + " if (x!=5) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x!=5' is always true\n", errout_str()); + check("void f(int x) {\n" + "\n" + " if (x<4) {\n" + " if (x>5) {}\n" // <- Warning + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", + errout_str()); + check("void f(int x) {\n" + "\n" + " if (x<4) {\n" + " if (x>=5) {}\n" // <- Warning + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", + errout_str()); + check("void f(int x) {\n" + "\n" + " if (x<4) {\n" + " if (x<5) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x<5' is always true\n", errout_str()); + check("void f(int x) {\n" + "\n" + " if (x<4) {\n" + " if (x<=5) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x<=5' is always true\n", errout_str()); + + check("void f(int x) {\n" + "\n" + " if (x<5) {\n" + " if (x==4) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void f(int x) {\n" + "\n" + " if (x<5) {\n" + " if (x!=4) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void f(int x) {\n" + "\n" + " if (x<5) {\n" + " if (x!=6) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x!=6' is always true\n", errout_str()); + check("void f(int x) {\n" + "\n" + " if (x<5) {\n" + " if (x>4) {}\n" // <- Warning + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x>4' is always false\n", errout_str()); + check("void f(int x) {\n" + "\n" + " if (x<5) {\n" + " if (x>=4) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void f(int x) {\n" + "\n" + " if (x<5) {\n" + " if (x<4) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void f(int x) {\n" + "\n" + " if (x<5) {\n" + " if (x<=4) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x<=4' is always true\n", errout_str()); + + // first comparison: > + check("void f(int x) {\n" + "\n" + " if (x>4) {\n" + " if (x==5) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void f(int x) {\n" + "\n" + " if (x>4) {\n" + " if (x>5) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void f(int x) {\n" + "\n" + " if (x>4) {\n" + " if (x>=5) {}\n" // <- Warning + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x>=5' is always true\n", errout_str()); + check("void f(int x) {\n" + "\n" + " if (x>4) {\n" + " if (x<5) {}\n" // <- Warning + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x<5' is always false\n", errout_str()); + check("void f(int x) {\n" + "\n" + " if (x>4) {\n" + " if (x<=5) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + "\n" + " if (x>5) {\n" + " if (x==4) {}\n" // <- Warning + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", + errout_str()); + check("void f(int x) {\n" + "\n" + " if (x>5) {\n" + " if (x>4) {}\n" // <- Warning + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x>4' is always true\n", errout_str()); + check("void f(int x) {\n" + "\n" + " if (x>5) {\n" + " if (x>=4) {}\n" // <- Warning + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'x>=4' is always true\n", errout_str()); + check("void f(int x) {\n" + "\n" + " if (x>5) {\n" + " if (x<4) {}\n" // <- Warning + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", + errout_str()); + check("void f(int x) {\n" + "\n" + " if (x>5) {\n" + " if (x<=4) {}\n" // <- Warning + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", + errout_str()); + + check("void f(int x) {\n" + " if (x < 4) {\n" + " if (10 < x) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + } + + void oppositeInnerCondition3() { + check("void f3(char c) { if(c=='x') if(c=='y') {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void f4(char *p) { if(*p=='x') if(*p=='y') {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void f5(const char * const p) { if(*p=='x') if(*p=='y') {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void f5(const char * const p) { if('x'==*p) if('y'==*p) {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void f6(char * const p) { if(*p=='x') if(*p=='y') {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void f7(const char * p) { if(*p=='x') if(*p=='y') {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void f8(int i) { if(i==4) if(i==2) {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void f9(int *p) { if (*p==4) if(*p==2) {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void f10(int * const p) { if (*p==4) if(*p==2) {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void f11(const int *p) { if (*p==4) if(*p==2) {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void f12(const int * const p) { if (*p==4) if(*p==2) {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("struct foo {\n" + " int a;\n" + " int b;\n" + "};\n" + "void f(foo x) { if(x.a==4) if(x.b==2) {}}"); + ASSERT_EQUALS("", errout_str()); + + check("struct foo {\n" + " int a;\n" + " int b;\n" + "};\n" + "void f(foo x) { if(x.a==4) if(x.b==4) {}}"); + ASSERT_EQUALS("", errout_str()); + + check("void f3(char a, char b) { if(a==b) if(a==0) {}}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) { if (x == 1) if (x != 1) {} }"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + } + + void oppositeInnerConditionAnd() { + check("void f(int x) {\n" + " if (a>3 && x > 100) {\n" + " if (x < 10) {}\n" + " }" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void f(bool x, const int a, const int b) {\n" + " if(x && a < b)\n" + " if( x && a > b){}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + } + + void oppositeInnerConditionOr() + { + check("void f(int x) {\n" + " if (x == 1 || x == 2) {\n" + " if (x == 3) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", + errout_str()); + + check("void f(int x) {\n" + " if (x == 1 || x == 2) {\n" + " if (x == 1) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if (x == 1 || x == 2) {\n" + " if (x == 2) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::string x) {\n" + " if (x == \"1\" || x == \"2\") {\n" + " if (x == \"1\") {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if (x < 1 || x > 3) {\n" + " if (x == 3) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", + errout_str()); + } + + void oppositeInnerConditionEmpty() { + check("void f1(const std::string &s) { if(s.size() > 42) if(s.empty()) {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void f1(const std::string &s) { if(s.size() > 0) if(s.empty()) {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void f1(const std::string &s) { if(s.size() < 0) if(s.empty()) {}} "); // <- CheckOther reports: checking if unsigned expression is less than zero + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Condition 's.empty()' is always false\n", errout_str()); + + check("void f1(const std::string &s) { if(s.empty()) if(s.size() > 42) {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("template void f1(const T &s) { if(s.size() > 42) if(s.empty()) {}}"); + ASSERT_EQUALS("", errout_str()); //We don't know the type of T so we don't know the relationship between size() and empty(). e.g. s might be a 50 tonne truck with nothing in it. + + check("void f2(const std::wstring &s) { if(s.empty()) if(s.size() > 42) {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void f1(QString s) { if(s.isEmpty()) if(s.length() > 42) {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Opposite inner 'if' condition leads to a dead code block.\n", errout_str()); + + check("void f1(const std::string &s, bool b) { if(s.empty() || ((s.size() == 1) && b)) {}}"); + ASSERT_EQUALS("", errout_str()); + + check("void f1(const std::string &x, const std::string &y) { if(x.size() > 42) if(y.empty()) {}}"); + ASSERT_EQUALS("", errout_str()); + + check("void f1(const std::string &x, const std::string &y) { if(y.empty()) if(x.size() > 42) {}}"); + ASSERT_EQUALS("", errout_str()); + + check("void f1(const std::string v[10]) { if(v[0].size() > 42) if(v[1].empty()) {}}"); + ASSERT_EQUALS("", errout_str()); + + check("void f1(const std::string &s) { if(s.size() <= 1) if(s.empty()) {}}"); + ASSERT_EQUALS("", errout_str()); + + check("void f1(const std::string &s) { if(s.size() <= 2) if(s.empty()) {}}"); + ASSERT_EQUALS("", errout_str()); + + check("void f1(const std::string &s) { if(s.size() < 2) if(s.empty()) {}}"); + ASSERT_EQUALS("", errout_str()); + + check("void f1(const std::string &s) { if(s.size() >= 0) if(s.empty()) {}} "); // CheckOther says: Unsigned expression 's.size()' can't be negative so it is unnecessary to test it. [unsignedPositive] + ASSERT_EQUALS("", errout_str()); + + // TODO: These are identical condition since size cannot be negative + check("void f1(const std::string &s) { if(s.size() <= 0) if(s.empty()) {}}"); + ASSERT_EQUALS("", errout_str()); + + // TODO: These are identical condition since size cannot be negative + check("void f1(const std::string &s) { if(s.size() < 1) if(s.empty()) {}}"); + ASSERT_EQUALS("", errout_str()); + } + + void oppositeInnerConditionFollowVar() { + check("struct X {\n" + " void f() {\n" + " const int flag = get();\n" + " if (flag) {\n" + " bar();\n" + " if (!get()) {}\n" + " }\n" + " }\n" + " void bar();\n" + " int get() const;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct CD {\n" + " bool state;\n" + " void foo() {\n" + " const bool flag = this->get();\n" + " if (flag) {\n" + " this->bar();\n" + " if (!this->get()) return;\n" + " }\n" + " }\n" + " bool get() const;\n" + " void bar();\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("class C {\n" + "public:\n" + " bool f() const { return x > 0; }\n" + " void g();\n" + " int x = 0;\n" + "};\n" + "\n" + "void C::g() {\n" + " bool b = f();\n" + " x += 1;\n" + " if (!b && f()) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(double d) {\n" + " if (d != 0) {\n" + " int i = d;\n" + " if (i == 0) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void identicalInnerCondition() { + check("void f1(int a, int b) { if(a==b) if(a==b) {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Identical inner 'if' condition is always true.\n", errout_str()); + + check("void f2(int a, int b) { if(a!=b) if(a!=b) {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (warning) Identical inner 'if' condition is always true.\n", errout_str()); + + // #6645 false negative: condition is always false + check("void f(bool a, bool b) {\n" + " if(a && b) {\n" + " if(a) {}\n" + " else {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical inner 'if' condition is always true.\n", errout_str()); + + check("bool f(int a, int b) {\n" + " if(a == b) { return a == b; }\n" + " return false;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (warning) Identical inner 'return' condition is always true.\n", errout_str()); + + check("bool f(bool a) {\n" + " if(a) { return a; }\n" + " return false;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int* f(int* a, int * b) {\n" + " if(a) { return a; }\n" + " return b;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int* f(std::shared_ptr a, std::shared_ptr b) {\n" + " if(a.get()) { return a.get(); }\n" + " return b.get();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int * x; };\n" + "int* f(A a, int * b) {\n" + " if(a.x) { return a.x; }\n" + " return b;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " uint32_t value;\n" + " get_value(&value);\n" + " int opt_function_capable = (value >> 28) & 1;\n" + " if (opt_function_capable) {\n" + " value = 0;\n" + " get_value (&value);\n" + " if ((value >> 28) & 1) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void identicalConditionAfterEarlyExit() { + check("void f(int x) {\n" // #8137 + " if (x > 100) { return; }\n" + " if (x > 100) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'x>100', second condition is always false\n", errout_str()); + + check("bool f(int x) {\n" + " if (x > 100) { return false; }\n" + " return x > 100;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition and return expression 'x>100', return value is always false\n", errout_str()); + + check("void f(int x) {\n" + " if (x > 100) { return; }\n" + " if (x > 100 || y > 100) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'x>100', second condition is always false\n", errout_str()); + + check("void f(int x) {\n" + " if (x > 100) { return; }\n" + " if (x > 100 && y > 100) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'x>100', second condition is always false\n", errout_str()); + + check("void f(int x) {\n" + " if (x > 100) { return; }\n" + " if (abc) {}\n" + " if (x > 100) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Identical condition 'x>100', second condition is always false\n", errout_str()); + + check("void f(int x) {\n" + " if (x > 100) { return; }\n" + " while (abc) { y = x; }\n" + " if (x > 100) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Identical condition 'x>100', second condition is always false\n", errout_str()); + + ASSERT_THROW_INTERNAL(check("void f(int x) {\n" // #8217 - crash for incomplete code + " if (x > 100) { return; }\n" + " X(do);\n" + " if (x > 100) {}\n" + "}"), + SYNTAX); + + check("void f(const int *i) {\n" + " if (!i) return;\n" + " if (!num1tok) { *num1 = *num2; }\n" + " if (!i) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Identical condition '!i', second condition is always false\n", errout_str()); + + check("void C::f(Tree &coreTree) {\n" // daca + " if(!coreTree.build())\n" + " return;\n" + " coreTree.dostuff();\n" + " if(!coreTree.build()) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct C { void f(const Tree &coreTree); };\n" + "void C::f(const Tree &coreTree) {\n" + " if(!coreTree.build())\n" + " return;\n" + " coreTree.dostuff();\n" + " if(!coreTree.build()) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (warning) Identical condition '!coreTree.build()', second condition is always false\n", errout_str()); + + check("void f(int x) {\n" // daca: labplot + " switch(type) {\n" + " case 1:\n" + " if (x == 0) return 1;\n" + " else return 2;\n" + " case 2:\n" + " if (x == 0) return 3;\n" + " else return 4;\n" + " }\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("static int failed = 0;\n" + "void f() {\n" + " if (failed) return;\n" + " checkBuffer();\n" + " if (failed) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // daca icu + check("void f(const uint32_t *section, int32_t start) {\n" + " if(10<=section[start]) { return; }\n" + " if(++start<100 && 10<=section[start]) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // daca iqtree + check("void readNCBITree(std::istream &in) {\n" + " char ch;\n" + " in >> ch;\n" + " if (ch != '|') return;\n" + " in >> ch;\n" + " if (ch != '|') {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #8924 + check("struct A {\n" + " void f() {\n" + " if (this->FileIndex >= 0) return;\n" + " this->FileIndex = 1 ;\n" + " if (this->FileIndex < 0) return;\n" + " }\n" + " int FileIndex;\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'this->FileIndex<0' is always false\n", errout_str()); + + // #8858 - #if + check("short Do() {\n" + " short ret = bar1();\n" + " if ( ret )\n" + " return ret;\n" + "#ifdef FEATURE\n" + " ret = bar2();\n" + "#endif\n" + " return ret;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #10456 + check("int f() {\n" + " int i = 0;\n" + " auto f = [&](bool b) { if (b) ++i; };\n" + " if (i) return i;\n" + " f(true);\n" + " if (i) return i;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #11478 + check("struct S {\n" + " void run();\n" + " bool b = false;\n" + " const std::function f;\n" + "};\n" + "void S::run() {\n" + " while (true) {\n" + " if (b)\n" + " return;\n" + " f(*this);\n" + " if (b)\n" + " return;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void innerConditionModified() { + check("void f(int x, int y) {\n" + " if (x == 0) {\n" + " x += y;\n" + " if (x == 0) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if (x == 0) {\n" + " x += y;\n" + " if (x == 1) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int * x, int * y) {\n" + " if (x[*y] == 0) {\n" + " (*y)++;\n" + " if (x[*y] == 0) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + // clarify conditions with = and comparison + void clarifyCondition1() { + check("void f() {\n" + " if (x = b() < 0) {}\n" // don't simplify and verify this code + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Suspicious condition (assignment + comparison); Clarify expression with parentheses.\n", errout_str()); + + check("void f(int i) {\n" + " for (i = 0; i < 10; i++) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " x = a(); if (x) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " if (x = b < 0 ? 1 : 2) {}\n" // don't simplify and verify this code + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int y = rand(), z = rand();\n" + " if (y || (!y && z));\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition: !y. 'y || (!y && z)' is equivalent to 'y || z'\n", errout_str()); + + check("void f() {\n" + " int y = rand(), z = rand();\n" + " if (y || !y && z);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition: !y. 'y || (!y && z)' is equivalent to 'y || z'\n", errout_str()); + + check("void f() {\n" + " if (!a || a && b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: a. '!a || (a && b)' is equivalent to '!a || b'\n", errout_str()); + + + check("void f(const Token *tok) {\n" + " if (!tok->next()->function() ||\n" + " (tok->next()->function() && tok->next()->function()->isConstructor()));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: tok->next()->function(). '!A || (A && B)' is equivalent to '!A || B'\n", errout_str()); + + check("void f() {\n" + " if (!tok->next()->function() ||\n" + " (!tok->next()->function() && tok->next()->function()->isConstructor()));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " if (!tok->next()->function() ||\n" + " (!tok2->next()->function() && tok->next()->function()->isConstructor()));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const Token *tok) {\n" + " if (!tok->next(1)->function(1) ||\n" + " (tok->next(1)->function(1) && tok->next(1)->function(1)->isConstructor()));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: tok->next(1)->function(1). '!A || (A && B)' is equivalent to '!A || B'\n", errout_str()); + + check("void f() {\n" + " if (!tok->next()->function(1) ||\n" + " (tok->next()->function(2) && tok->next()->function()->isConstructor()));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int y = rand(), z = rand();\n" + " if (y==0 || y!=0 && z);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Redundant condition: y!=0. 'y==0 || (y!=0 && z)' is equivalent to 'y==0 || z'\n", errout_str()); + + check("void f() {\n" + " if (x>0 || (x<0 && y)) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Test Token::expressionString, TODO move this test + check("void f() {\n" + " if (!dead || (dead && (*it).ticks > 0)) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: dead. '!dead || (dead && (*it).ticks>0)' is equivalent to '!dead || (*it).ticks>0'\n", errout_str()); + + check("void f() {\n" + " if (!x || (x && (2>(y-1)))) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: x. '!x || (x && 2>(y-1))' is equivalent to '!x || 2>(y-1)'\n", errout_str()); + + check("void f(bool a, bool b) {\n" + " if (a || (a && b)) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: a. 'a || (a && b)' is equivalent to 'a'\n", errout_str()); + + check("void f(bool a, bool b) {\n" + " if (a && (a || b)) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant condition: a. 'a && (a || b)' is equivalent to 'a'\n", errout_str()); + } + + // clarify conditions with bitwise operator and comparison + void clarifyCondition2() { + check("void f() {\n" + " if (x & 3 == 2) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses.\n" + "[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n" + "[test.cpp:2]: (style) Condition 'x&3==2' is always false\n", errout_str()); + + check("void f() {\n" + " if (a & fred1.x == fred2.y) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Suspicious condition (bitwise operator + comparison); Clarify expression with parentheses.\n" + "[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n" + , errout_str()); + } + + // clarify condition that uses ! operator and then bitwise operator + void clarifyCondition3() { + check("void f(int w) {\n" + " if(!w & 0x8000) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n", errout_str()); + + check("void f(int w) {\n" + " if((!w) & 0x8000) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " if (x == foo() & 2) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n", errout_str()); + + check("void f() {\n" + " if (2 & x == foo()) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n", errout_str()); + + check("void f() {\n" + " if (2 & (x == foo())) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::list &ints) { }"); + ASSERT_EQUALS("", errout_str()); + + check("void f() { A a; }"); + ASSERT_EQUALS("", errout_str()); + + check("void f() { a(x there are never templates + ASSERT_EQUALS("[test.c:1]: (style) Boolean result is used in bitwise operation. Clarify expression with parentheses.\n", errout_str()); + + check("class A;", "test.cpp"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " if (result != (char *)&inline_result) { }\n" // don't simplify and verify cast + "}"); + ASSERT_EQUALS("", errout_str()); + + // #8495 + check("void f(bool a, bool b) {\n" + " C & a & b;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void clarifyCondition4() { // ticket #3110 + check("typedef double SomeType;\n" + "typedef std::pair PairType;\n" + "struct S\n" + "{\n" + " bool operator()\n" + " ( PairType const & left\n" + " , PairType const & right) const\n" + " {\n" + " return left.first < right.first;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void clarifyCondition5() { // ticket #3609 (using | in template instantiation) + check("template struct CWinTraits;\n" + "CWinTraits::GetWndStyle(0);"); + ASSERT_EQUALS("", errout_str()); + } + + void clarifyCondition6() { + check("template\n" + "SharedPtr& operator=( SharedPtr const & r ) {\n" + " px = r.px;\n" + " return *this;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void clarifyCondition7() { + // Ensure that binary and unary &, and & in declarations are distinguished properly + check("void f(bool error) {\n" + " bool & withoutSideEffects=found.first->second;\n" // Declaring a reference to a boolean; & is no operator at all + " execute(secondExpression, &programMemory, &result, &error);\n" // Unary & + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void clarifyCondition8() { + // don't warn when boolean result comes from function call, array index, etc + // the operator precedence is not unknown then + check("bool a();\n" + "bool f(bool b) {\n" + " return (a() & b);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(bool *a, bool b) {\n" + " return (a[10] & b);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { bool a; };\n" + "bool f(struct A a, bool b) {\n" + " return (a.a & b);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { bool a; };\n" + "bool f(struct A a, bool b) {\n" + " return (A::a & b);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testBug5895() { + check("void png_parse(uint64_t init, int buf_size) {\n" + " if (init == 0x89504e470d0a1a0a || init == 0x8a4d4e470d0a1a0a)\n" + " ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testBug5309() { + check("extern uint64_t value;\n" + "void foo() {\n" + " if( ( value >= 0x7ff0000000000001ULL )\n" + " && ( value <= 0x7fffffffffffffffULL ) );\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void alwaysTrue() { + + check("void f(const struct S *s) {\n" //#8196 + " int x1 = s->x;\n" + " int x2 = s->x;\n" + " if (x1 == 10 && x2 == 10) {}\n" // << + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:4]: (style) Condition 'x2==10' is always true\n", errout_str()); + + check("void f ()\n"// #8220 + "{\n" + " int a;\n" + " int b = 0;\n" + " int ret;\n" + " \n" + " a = rand();\n" + " while (((0 < a) && (a < 2)) && ((8 < a) && (a < 10))) \n" + " {\n" + " b += a;\n" + " a ++;\n" + " }\n" + " ret = b;\n" + "}"); + ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:8]: (style) Condition '8 [test.cpp:2]: (style) Return value 'x==0' is always false\n", errout_str()); + + check("void f() {\n" // #6898 (Token::expressionString) + " int x = 0;\n" + " A(x++ == 1);\n" + " A(x++ == 2);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'x++==1' is always false\n" + "[test.cpp:4]: (style) Condition 'x++==2' is always false\n", + errout_str()); + + check("bool foo(int bar) {\n" + " bool ret = false;\n" + " if (bar == 1)\n" + " return ret;\n" // <- #9326 - FP condition is always false + " if (bar == 2)\n" + " ret = true;\n" + " return ret;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f1(const std::string &s) { if(s.empty()) if(s.size() == 0) {}}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Condition 's.size()==0' is always true\n", errout_str()); + + check("void f() {\n" + " int buf[42];\n" + " if( buf != 0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'buf!=0' is always true\n", errout_str()); // #8924 + + check("void f() {\n" + " int buf[42];\n" + " if( !buf ) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition '!buf' is always false\n", errout_str()); + + check("void f() {\n" + " int buf[42];\n" + " bool b = buf;\n" + " if( b ) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'b' is always true\n", errout_str()); + + check("void f() {\n" + " int buf[42];\n" + " bool b = buf;\n" + " if( !b ) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Condition '!b' is always false\n", errout_str()); + + check("void f() {\n" + " int buf[42];\n" + " int * p = nullptr;\n" + " if( buf == p ) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'buf==p' is always false\n", errout_str()); + + check("void f(bool x) {\n" + " int buf[42];\n" + " if( buf || x ) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'buf' is always true\n", errout_str()); + + check("void f(int * p) {\n" + " int buf[42];\n" + " if( buf == p ) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int buf[42];\n" + " int p[42];\n" + " if( buf == p ) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int buf[42];\n" + " if( buf == 1) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Avoid FP when condition comes from macro + check("#define NOT !\n" + "void f() {\n" + " int x = 0;\n" + " if (a) { return; }\n" // <- this is just here to fool simplifyKnownVariabels + " if (NOT x) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("#define M x != 0\n" + "void f() {\n" + " int x = 0;\n" + " if (a) { return; }\n" // <- this is just here to fool simplifyKnownVariabels + " if (M) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("#define IF(X) if (X && x())\n" + "void f() {\n" + " IF(1) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Avoid FP for sizeof condition + check("void f() {\n" + " if (sizeof(char) != 123) {}\n" + " if (123 != sizeof(char)) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int x = 123;\n" + " if (sizeof(char) != x) {}\n" + " if (x != sizeof(char)) {}\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'sizeof(char)!=x' is always true\n" + "[test.cpp:4]: (style) Condition 'x!=sizeof(char)' is always true\n", "", errout_str()); + + // Don't warn in assertions. Condition is often 'always true' by intention. + // If platform,defines,etc cause an 'always false' assertion then that is not very dangerous neither + check("void f() {\n" + " int x = 0;\n" + " assert(x == 0);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9363 - do not warn about value passed to function + check("void f(bool b) {\n" + " if (b) {\n" + " if (bar(!b)) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + + // #7783 FP knownConditionTrueFalse on assert(0 && "message") + check("void foo(int x) {\n" + " if (x<0)\n" + " {\n" + " assert(0 && \"bla\");\n" + " ASSERT(0 && \"bla\");\n" + " assert_foo(0 && \"bla\");\n" + " ASSERT_FOO(0 && \"bla\");\n" + " assert((int)(0==0));\n" + " assert((int)(0==0) && \"bla\");\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #7750 char literals in boolean expressions + check("void f() {\n" + " if('a'){}\n" + " if(L'b'){}\n" + " if(1 && 'c'){}\n" + " int x = 'd' ? 1 : 2;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #8206 - knownCondition always false + check("void f(int i)\n" + "{\n" + " if(i > 4)\n" + " for( int x = 0; i < 3; ++x){}\n" // << + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Condition 'i<3' is always false\n", errout_str()); + + // Skip literals + check("void f() { if(true) {} }"); + ASSERT_EQUALS("", errout_str()); + + check("void f() { if(false) {} }"); + ASSERT_EQUALS("", errout_str()); + + check("void f() { if(!true) {} }"); + ASSERT_EQUALS("", errout_str()); + + check("void f() { if(!false) {} }"); + ASSERT_EQUALS("", errout_str()); + + check("void f() { if(0) {} }"); + ASSERT_EQUALS("", errout_str()); + + check("void f() { if(1) {} }"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int i) {\n" + " bool b = false;\n" + " if (i == 0) b = true;\n" + " else if (!b && i == 1) {}\n" + " if (b)\n" + " {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Condition '!b' is always true\n", errout_str()); + + check("bool f() { return nullptr; }"); + ASSERT_EQUALS("", errout_str()); + + check("enum E { A };\n" + "bool f() { return A; }"); + ASSERT_EQUALS("", errout_str()); + + check("bool f() {\n" + " const int x = 0;\n" + " return x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(void){return 1/abs(10);}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f() {\n" + " int x = 0;\n" + " return x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f() {\n" + " const int a = 50;\n" + " const int b = 52;\n" + " return a+b;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Return value 'a+b' is always true\n", errout_str()); + + check("int f() {\n" + " int a = 50;\n" + " int b = 52;\n" + " a++;\n" + " b++;\n" + " return a+b;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool& g();\n" + "bool f() {\n" + " bool & b = g();\n" + " b = false;\n" + " return b;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " bool b;\n" + " bool f() {\n" + " b = false;\n" + " return b;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(long maxtime) {\n" + " if (std::time(0) > maxtime)\n" + " return std::time(0) > maxtime;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(double param) {\n" + " while(bar()) {\n" + " if (param<0.)\n" + " return;\n" + " }\n" + " if (param<0.)\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int i) {\n" + " if (i==42)\n" + " {\n" + " bar();\n" + " }\n" + " if (cond && (42==i))\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // 8842 crash + check("class a {\n" + " int b;\n" + " c(b);\n" + " void f() {\n" + " if (b) return;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const char* x, const char* t) {\n" + " if (!(strcmp(x, y) == 0)) { return; }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const int a[]){ if (a == 0){} }"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " bool operator<(const S&);\n" + "};\n" + "int main() {\n" + " S s;\n" + " bool c = s [test.cpp:3]: (style) Condition 'handle' is always true\n", errout_str()); + + check("int f(void *handle) {\n" + " if (handle == 0) return 0;\n" + " if (handle) return 1;\n" + " else return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'handle' is always true\n", errout_str()); + + check("int f(void *handle) {\n" + " if (handle != 0) return 0;\n" + " if (handle) return 1;\n" + " else return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'handle!=0', second condition is always false\n", errout_str()); + + check("int f(void *handle) {\n" + " if (handle != nullptr) return 0;\n" + " if (handle) return 1;\n" + " else return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Identical condition 'handle!=nullptr', second condition is always false\n", errout_str()); + + check("void f(void* x, void* y) {\n" + " if (x == nullptr && y == nullptr)\n" + " return;\n" + " if (x == nullptr || y == nullptr)\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void* g();\n" + "void f(void* a, void* b) {\n" + " while (a) {\n" + " a = g();\n" + " if (a == b)\n" + " break;\n" + " }\n" + " if (a) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void* g();\n" + "void f(void* a, void* b) {\n" + " while (a) {\n" + " a = g();\n" + " }\n" + " if (a) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'a' is always false\n", errout_str()); + + check("void f(int * x, bool b) {\n" + " if (!x && b) {}\n" + " else if (x) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " const std::string x=\"xyz\";\n" + " if(!x.empty()){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition '!x.empty()' is always true\n", errout_str()); + + check("std::string g();\n" + "void f() {\n" + " const std::string msg = g();\n" + " if(!msg.empty()){}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int *array, int size ) {\n" + " for(int i = 0; i < size; ++i) {\n" + " if(array == 0)\n" + " continue;\n" + " if(array){}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'array' is always true\n", errout_str()); + + check("void f(int *array, int size ) {\n" + " for(int i = 0; i < size; ++i) {\n" + " if(array == 0)\n" + " continue;\n" + " else if(array){}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'array' is always true\n", errout_str()); + + // #9277 + check("int f() {\n" + " constexpr bool x = true;\n" + " if constexpr (x)\n" + " return 0;\n" + " else\n" + " return 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9954 + check("void f() {\n" + " const size_t a(8 * sizeof(short));\n" + " const size_t b(8 * sizeof(int));\n" + " if constexpr (a == 16 && b == 16) {}\n" + " else if constexpr (a == 16 && b == 32) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #9319 + check("struct S {\n" + " int a;\n" + " int b;\n" + "};\n" + "void g(S s, bool& x);\n" + "void f() {\n" + " bool x = false;\n" + " g({0, 1}, x);\n" + " if (x) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9318 + check("class A {};\n" + "class B : public A {};\n" + "void f(A* x) {\n" + " if (!x)\n" + " return;\n" + " auto b = dynamic_cast(x);\n" + " if (b) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int foo() {\n" + " auto x = getX();\n" + " if (x == nullptr)\n" + " return 1;\n" + " auto y = dynamic_cast(x)\n" + " if (y == nullptr)\n" + " return 2;\n" + " return 3;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // handleKnownValuesInLoop + check("bool g();\n" + "void f(bool x) {\n" + " if (x) while(x) x = g();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // isLikelyStream + check("void f(std::istringstream& iss) {\n" + " std::string x;\n" + " while (iss) {\n" + " iss >> x;\n" + " if (!iss) break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9332 + check("struct A { void* g(); };\n" + "void f() {\n" + " A a;\n" + " void* b = a.g();\n" + " if (!b) return;\n" + " void* c = a.g();\n" + " if (!c) return;\n" + " bool compare = c == b;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9361 + check("void f(char c) {\n" + " if (c == '.') {}\n" + " else if (isdigit(c) != 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9351 + check("int f(int x) {\n" + " const bool b = x < 42;\n" + " if(b) return b?0:-1;\n" + " return 42;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (style) Condition 'b' is always true\n", errout_str()); + + // #9362 + check("uint8_t g();\n" + "void f() {\n" + " const uint8_t v = g();\n" + " if((v != 0x00)) {\n" + " if( (v & 0x01) == 0x00) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9367 + check("void f(long x) {\n" + " if (x <= 0L)\n" + " return;\n" + " if (x % 360L == 0)\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int a, int b) {\n" + " static const int x = 10;\n" + " return x == 1 ? a : b;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("const bool x = false;\n" + "void f() {\n" + " if (x) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("const bool x = false;\n" + "void f() {\n" + " if (!x) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9709 + check("void f(int a) {\n" + " bool ok = false;\n" + " const char * r = nullptr;\n" + " do_something(&r);\n" + " if (r != nullptr)\n" + " ok = a != 0;\n" + " if (ok) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9816 + check("bool g();\n" + "void f() {\n" + " bool b = false;\n" + " do {\n" + " do {\n" + " if (g())\n" + " break;\n" + " b = true;\n" + " } while(false);\n" + " } while(!b);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #9865 + check("void f(const std::string &s) {\n" + " for (std::string::const_iterator it = s.begin(); it != s.end(); ++it) {\n" + " const unsigned char c = static_cast(*it);\n" + " if (c == '0') {}\n" + " else if ((c == 'a' || c == 'A')\n" + " || (c == 'b' || c == 'B')) {}\n" + " else {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #9711 + check("int main(int argc, char* argv[]) {\n" + " int foo = 0;\n" + " struct option options[] = {\n" + " {\"foo\", no_argument, &foo, \'f\'},\n" + " {NULL, 0, NULL, 0},\n" + " };\n" + " getopt_long(argc, argv, \"f\", options, NULL);\n" + " if (foo) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // TODO: if (!v) is a known condition as well + check("struct a {\n" + " int *b();\n" + "};\n" + "bool g(a c, a* d) {\n" + " a *v, *e = v = &c;\n" + " if (!v)\n" + " return true;\n" + " int *f = v->b();\n" + " if (f)\n" + " v = nullptr;\n" + " if (v == nullptr && e) {}\n" + " return d;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:11]: (style) Condition 'e' is always true\n", errout_str()); + + // #10037 + check("struct a {\n" + " int* p;\n" + "};\n" + "void g(a*);\n" + "void f() {\n" + " struct a b;\n" + " uint32_t p = (uint32_t) -1;\n" + " b.p = (void *) &p;\n" + " int r = g(&b);\n" + " if (r == 0)\n" + " if (p != (uint32_t) -1) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #9890 + check("int g(int);\n" + "bool h(int*);\n" + "int f(int *x) {\n" + " int y = g(0);\n" + " if (!y) {\n" + " if (h(x)) {\n" + " y = g(1);\n" + " if (y) {}\n" + " return 0;\n" + " }\n" + " if (!y) {}\n" + " }\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:11]: (style) Condition '!y' is always true\n", errout_str()); + + // #10134 + check("bool foo(bool b);\n" + "bool thud(const std::vector& Arr, const std::wstring& Str) {\n" + " if (Arr.empty() && Str.empty())\n" + " return false;\n" + " bool OldFormat = Arr.empty() && !Str.empty();\n" + " if (OldFormat)\n" + " return foo(OldFormat);\n" + " return false;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10208 + check("bool GetFirst(std::string &first);\n" + "bool GetNext(std::string &next);\n" + "void g(const std::string& name);\n" + "void f() {\n" + " for (std::string name; name.empty() ? GetFirst(name) : GetNext(name);)\n" + " g(name);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("bool GetFirst(std::string &first);\n" + "bool GetNext(std::string &next);\n" + "void g(const std::string& name);\n" + "void f() {\n" + " for (std::string name{}; name.empty() ? GetFirst(name) : GetNext(name);)\n" + " g(name);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("bool GetFirst(std::string &first);\n" + "bool GetNext(std::string &next);\n" + "void g(const std::string& name);\n" + "void f() {\n" + " for (std::string name{'a', 'b'}; name.empty() ? GetFirst(name) : GetNext(name);)\n" + " g(name);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("bool GetFirst(const std::string &first);\n" + "bool GetNext(const std::string &next);\n" + "void g(const std::string& name);\n" + "void f() {\n" + " for (std::string name; name.empty() ? GetFirst(name) : GetNext(name);)\n" + " g(name);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'name.empty()' is always true\n", errout_str()); + + // #10278 + check("void foo(unsigned int x) {\n" + " if ((100 - x) > 0) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10298 + check("void foo(unsigned int x) {\n" + " if (x == -1) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10121 + check("struct AB {\n" + " int a;\n" + "};\n" + "struct ABC {\n" + " AB* ab;\n" + "};\n" + "void g(ABC*);\n" + "int f(struct ABC *abc) {\n" + " int err = 0;\n" + " AB *ab = abc->ab;\n" + " if (ab->a == 123){\n" + " g(abc);\n" + " if (ab->a != 123) {\n" + " err = 1;\n" + " }\n" + " }\n" + " return err;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10323 + check("void foo(int x) {\n" + " if(x)\n" + " if(x == 1) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int x) {\n" + " if(x) {}\n" + " else\n" + " if(x == 1) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x==1' is always false\n", errout_str()); + + // do not report both unsignedLessThanZero and knownConditionTrueFalse + check("void foo(unsigned int max) {\n" + " unsigned int num = max - 1;\n" + " if (num < 0) {}\n" // <- do not report knownConditionTrueFalse + "}"); + ASSERT_EQUALS("", errout_str()); + + // #10297 + check("void foo(size_t len, int start) {\n" + " if (start < 0) {\n" + " start = len+start;\n" + " if (start < 0) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10362 + check("int tok;\n" + "void next();\n" + "void parse_attribute() {\n" + " if (tok == '(') {\n" + " int parenthesis = 0;\n" + " do {\n" + " if (tok == '(')\n" + " parenthesis++;\n" + " else if (tok == ')')\n" + " parenthesis--;\n" + " next();\n" + " } while (parenthesis && tok != -1);\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #7843 + check("void f(int i) {\n" + " if(abs(i) == -1) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'abs(i)==-1' is always false\n", errout_str()); + + // #7844 + check("void f(int i) {\n" + " if(i > 0 && abs(i) == i) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'abs(i)==i' is always true\n", errout_str()); + + check("void f(int i) {\n" + " if(i < 0 && abs(i) == i) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'abs(i)==i' is always false\n", errout_str()); + + check("void f(int i) {\n" + " if(i > -3 && abs(i) == i) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #9948 + check("bool f(bool a, bool b) {\n" + " return a || ! b || ! a;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Return value '!a' is always true\n", errout_str()); + + // #10148 + check("void f(int i) {\n" + " if (i >= 64) {}\n" + " else if (i >= 32) {\n" + " i &= 31;\n" + " if (i == 0) {}\n" + " else {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10548 + check("void f() {\n" + " int i = 0;\n" + " do {} while (i++ == 0);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10582 + check("static void fun(message_t *message) {\n" + " if (message->length >= 1) {\n" + " switch (data[0]) {}\n" + " }\n" + " uint8_t d0 = message->length > 0 ? data[0] : 0xff;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #8266 + check("void f(bool b) {\n" + " if (b)\n" + " return;\n" + " if (g(&b) || b)\n" + " return;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #9720 + check("bool bar(int &);\n" + "void f(int a, int b) {\n" + " if (a + b == 3)\n" + " return;\n" + " if (bar(a) && (a + b == 3)) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10437 + check("void f() {\n" + " Obj* PObj = nullptr;\n" + " bool b = false;\n" + " if (GetObj(PObj) && PObj != nullptr)\n" + " b = true;\n" + " if (b) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10223 + check("static volatile sig_atomic_t is_running;\n" + "static void handler(int signum) {\n" + " is_running = 0;\n" + "}\n" + "void f() {\n" + " signal(SIGINT, &handler);\n" + " is_running = 1;\n" + " while (is_running) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10659 + check("auto func(const std::tuple& t) {\n" + " auto& [foo, bar] = t;\n" + " std::cout << foo << bar << std::endl;\n" + " return foo < bar;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10484 + check("void f() {\n" + " static bool init = true;\n" + " if (init)\n" + " init = false;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style) The statement 'if (init) init=false' is logically equivalent to 'init=false'.\n", errout_str()); + + check("void f() {\n" + " static bool init(true);\n" + " if (init)\n" + " init = false;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style) The statement 'if (init) init=false' is logically equivalent to 'init=false'.\n", errout_str()); + + check("void f() {\n" + " static bool init{ true };\n" + " if (init)\n" + " init = false;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style) The statement 'if (init) init=false' is logically equivalent to 'init=false'.\n", errout_str()); + + // #10248 + check("void f() {\n" + " static int var(1);\n" + " if (var == 1) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " static int var{ 1 };\n" + " if (var == 1) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void Fun();\n" + "using Fn = void (*)();\n" + "void f() {\n" + " static Fn logger = nullptr;\n" + " if (logger == nullptr)\n" + " logger = Fun;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void Fun();\n" + "using Fn = void (*)();\n" + "void f() {\n" + " static Fn logger(nullptr);\n" + " if (logger == nullptr)\n" + " logger = Fun;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void Fun();\n" + "using Fn = void (*)();\n" + "void f() {\n" + " static Fn logger{ nullptr };\n" + " if (logger == nullptr)\n" + " logger = Fun;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void Fun();\n" + "typedef void (*Fn)();\n" + "void f() {\n" + " static Fn logger = nullptr;\n" + " if (logger == nullptr)\n" + " logger = Fun;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void Fun();\n" + "typedef void (*Fn)();\n" + "void f() {\n" + " static Fn logger(nullptr);\n" + " if (logger == nullptr)\n" + " logger = Fun;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void Fun();\n" + "typedef void (*Fn)();\n" + "void f() {\n" + " static Fn logger{ nullptr };\n" + " if (logger == nullptr)\n" + " logger = Fun;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #9256 + check("bool f() {\n" + " bool b = false;\n" + " b = true;\n" + " return b;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10702 + check("struct Object {\n" + " int _count=0;\n" + " void increment() { ++_count;}\n" + " auto get() const { return _count; }\n" + "};\n" + "struct Modifier {\n" + "Object & _object;\n" + " explicit Modifier(Object & object) : _object(object) {}\n" + " void do_something() { _object.increment(); }\n" + "};\n" + "struct Foo {\n" + " Object _object;\n" + " void foo() {\n" + " Modifier mod(_object);\n" + " if (_object.get()>0)\n" + " return;\n" + " mod.do_something();\n" + " if (_object.get()>0)\n" + " return;\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct Object {\n" + " int _count=0;\n" + " auto get() const;\n" + "};\n" + "struct Modifier {\n" + "Object & _object;\n" + " explicit Modifier(Object & object);\n" + " void do_something();\n" + "};\n" + "struct Foo {\n" + " Object _object;\n" + " void foo() {\n" + " Modifier mod(_object);\n" + " if (_object.get()>0)\n" + " return;\n" + " mod.do_something();\n" + " if (_object.get()>0)\n" + " return;\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const uint32_t u) {\n" + " const uint32_t v = u < 4;\n" + " if (v) {\n" + " const uint32_t w = v < 2;\n" + " if (w) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'v<2' is always true\n" + "[test.cpp:5]: (style) Condition 'w' is always true\n", + errout_str()); + + check("void f(double d) {\n" // #10792 + " if (d != 0) {\n" + " int i = (int)d;\n" + " if (i == 0) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(double d) {\n" + " if (0 != d) {\n" + " int i = (int)d;\n" + " if (i == 0) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { double d; }\n" + "void f(A a) {\n" + " if (a.d != 0) {\n" + " int i = a.d;\n" + " if (i == 0) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " if(strlen(\"abc\") == 3) {;}\n" + " if(strlen(\"abc\") == 1) {;}\n" + " if(wcslen(L\"abc\") == 3) {;}\n" + " if(wcslen(L\"abc\") == 1) {;}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'strlen(\"abc\")==3' is always true\n" + "[test.cpp:3]: (style) Condition 'strlen(\"abc\")==1' is always false\n" + "[test.cpp:4]: (style) Condition 'wcslen(L\"abc\")==3' is always true\n" + "[test.cpp:5]: (style) Condition 'wcslen(L\"abc\")==1' is always false\n", + errout_str()); + + check("int foo(bool a, bool b) {\n" + " if(!a && b && (!a == !b))\n" + " return 1;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition '!a==!b' is always false\n", errout_str()); + + // #10454 + check("struct S {\n" + " int f() const { return g() ? 0 : 1; }\n" + " bool g() const { return u == 18446744073709551615ULL; }\n" + " unsigned long long u{};\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + // #8358 + check("void f(double d) { if ((d * 0) != 0) {} }"); + ASSERT_EQUALS("", errout_str()); + + // #6870 + check("struct S {\n" + " int* p;\n" + " void f() const;\n" + " int g();\n" + "};\n" + "void S::f() {\n" + " if ((p == NULL) || ((p) && (g() >= *p))) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7]: (style) Condition 'p' is always true\n", errout_str()); + + // #10749 + check("struct Interface {\n" + " virtual int method() = 0;\n" + "};\n" + "struct Child : Interface {\n" + " int method() override { return 0; }\n" + " auto foo() {\n" + " if (method() == 0)\n" + " return true;\n" + " else\n" + " return false;\n" + " }\n" + "};\n" + "struct GrandChild : Child {\n" + " int method() override { return 1; }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + // #6855 + check("struct S { int i; };\n" + "void f(S& s) {\n" + " if (!(s.i > 0) && (s.i != 0))\n" + " s.i = 0;\n" + " else if (s.i < 0)\n" + " s.s = 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Condition 's.i<0' is always false\n", errout_str()); + + // #6857 + check("int bar(int i) { return i; }\n" + "void foo() {\n" + " if (bar(1) == 0 && bar(1) > 0) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'bar(1)==0' is always false\n" + "[test.cpp:3]: (style) Condition 'bar(1)>0' is always true\n", + errout_str()); + + check("struct S { int bar(int i) const; };\n" + "void foo(const S& s) {\n" + " if (s.bar(1) == 0 && s.bar(1) > 0) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Logical conjunction always evaluates to false: s.bar(1) == 0 && s.bar(1) > 0.\n", + errout_str()); + + check("struct B {\n" // #10618 + " void Modify();\n" + " static void Static();\n" + " virtual void CalledByModify();\n" + "};\n" + "struct D : B {\n" + " int i{};\n" + " void testV();\n" + " void testS();\n" + " void CalledByModify() override { i = 0; }\n" + "};\n" + "void D::testV() {\n" + " i = 1;\n" + " B::Modify();\n" + " if (i == 1) {}\n" + "}\n" + "void D::testS() {\n" + " i = 1;\n" + " B::Static();\n" + " if (i == 1) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:20]: (style) Condition 'i==1' is always true\n", errout_str()); + + check("typedef struct { bool x; } s_t;\n" // #8446 + "unsigned f(bool a, bool b) {\n" + " s_t s;\n" + " const unsigned col = a ? (s.x = false) : (b = true);\n" + " if (!s.x) {}\n" + " return col;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" // #11233 + " static std::string m;\n" + " static void f() { m = \"abc\"; }\n" + " static void g() {\n" + " m.clear();\n" + " f();\n" + " if (m.empty()) {}\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + // #11203 + check("void f() {\n" + " int i = 10;\n" + " if(i > 9.9){}\n" + " float f = 9.9f;\n" + " if(f < 10) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'i>9.9' is always true\n" + "[test.cpp:5]: (style) Condition 'f<10' is always true\n", + errout_str()); + check("constexpr int f() {\n" // #11238 + " return 1;\n" + "}\n" + "constexpr bool g() {\n" + " return f() == 1;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int g() { return -1; }\n" + "void f() {\n" + " if (g() == 1 && g() == -1) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'g()==1' is always false\n" + "[test.cpp:3]: (style) Condition 'g()==-1' is always true\n", + errout_str()); + + // #9817 + check("void f(float x) {\n" + " if (x <= 0) {}\n" + " else if (x < 1) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10426 + check("int f() {\n" + " std::string s;\n" + " for (; !s.empty();) {}\n" + " for (; s.empty();) {}\n" + " if (s.empty()) {}\n" + " if ((bool)0) {}\n" + " return s.empty();" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition '!s.empty()' is always false\n" + "[test.cpp:4]: (style) Condition 's.empty()' is always true\n" + "[test.cpp:5]: (style) Condition 's.empty()' is always true\n" + "[test.cpp:6]: (style) Condition '(bool)0' is always false\n" + "[test.cpp:7]: (style) Return value 's.empty()' is always true\n", + errout_str()); + + check("int f(bool b) {\n" + " if (b) return static_cast(1);\n" + " return (int)0;\n" + "}\n" + "bool g(bool b) {\n" + " if (b) return static_cast(1);\n" + " return (int)0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:6]: (style) Return value 'static_cast(1)' is always true\n" + "[test.cpp:7]: (style) Return value '(int)0' is always false\n", + errout_str()); + + check("int f() { return 3; }\n" + "int g() { return f(); }\n" + "int h() { if (f()) {} }\n" + "int i() { return f() == 3; }\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'f()' is always true\n" + "[test.cpp:4]: (style) Return value 'f()==3' is always true\n", + errout_str()); + + check("int f() {\n" + " const char *n;\n" + " return((n=42) &&\n" + " *n == 'A');\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::istringstream& i) {\n" // #9327 + " std::string s;\n" + " if (!(i >> s))\n" + " return;\n" + " if (!(i >> s))\n" + " return;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #11227 + check("struct S {\n" + " int get();\n" + "};\n" + "void f(const S* s) {\n" + " if (!s)\n" + " return;\n" + " g(s ? s->get() : 0);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:7]: (style) Condition 's' is always true\n", errout_str()); + + check("void f(const char* o) {\n" // #11558 + " if (!o || !o[0])\n" + " return;\n" + " if (o[0] == '-' && o[1]) {\n" + " if (o[1] == '-') {}\n" + " if (o[1] == '\\0') {}\n" + " }\n" + "}\n"); + if (std::numeric_limits::is_signed) { + ASSERT_EQUALS("[test.cpp:6]: (style) Condition 'o[1]=='\\0'' is always false\n", errout_str()); + } else { + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (style) Condition 'o[1]=='\\0'' is always false\n", errout_str()); + } + + check("void f(int x) {\n" // #11449 + " int i = x;\n" + " i = (std::min)(i, 1);\n" + " if (i == 1) {}\n" + " int j = x;\n" + " j = (::std::min)(j, 1);\n" + " if (j == 1) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void h(int);\n" // #11679 + "bool g(int a) { h(a); return false; }\n" + "bool f(int i) {\n" + " return g(i);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::string a) {\n" // #11051 + " a = \"x\";\n" + " if (a == \"x\") {}\n" + " return a;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'a==\"x\"' is always true\n", errout_str()); + + check("void g(bool);\n" + "void f() {\n" + " int i = 5;\n" + " int* p = &i;\n" + " g(i == 7);\n" + " g(p == nullptr);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'i==7' is always false\n" + "[test.cpp:6]: (style) Condition 'p==nullptr' is always false\n", + errout_str()); + + check("enum E { E0, E1 };\n" + "void f() {\n" + " static_assert(static_cast(E::E1) == 1);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct a {\n" + " bool g();\n" + " int h();\n" + "};\n" + "void f(a c, int d, int e) {\n" + " if (c.g() && c.h()) {}\n" + " else {\n" + " bool u = false;\n" + " if (d && e)\n" + " u = true;\n" + " if (u) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int i) {\n" // #11741 + " i = -i - 1;\n" + " if (i < 0 || i >= 20)\n" + " return 0;\n" + " return 1;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void alwaysTrueSymbolic() + { + check("void f(const uint32_t x) {\n" + " uint32_t y[1];\n" + " y[0]=x;\n" + " if(x > 0 || y[0] < 42){}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:4]: (style) Condition 'y[0]<42' is always true\n", errout_str()); + + check("void f(int x, int y) {\n" + " if(x < y && x < 42) {\n" + " --x;\n" + " if(x == y) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x==y' is always false\n", errout_str()); + + check("void f(bool a, bool b) { if (a == b && a && !b){} }"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Condition '!b' is always false\n", errout_str()); + + check("bool f(bool a, bool b) { if(a && b && (!a)){} }"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Condition '!a' is always false\n", errout_str()); + + check("void f(int x, int y) {\n" + " if (x < y) {\n" + " auto z = y - x;\n" + " if (z < 1) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'z<1' is always false\n", errout_str()); + + check("bool f(int &index, const int s, const double * const array, double & x) {\n" + " if (index >= s)\n" + " return false;\n" + " else {\n" + " x = array[index];\n" + " return (index++) >= s;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:6]: (style) Return value '(index++)>=s' is always false\n", errout_str()); + + check("struct a {\n" + " a *b() const;\n" + "} c;\n" + "void d() {\n" + " a *e = nullptr;\n" + " e = c.b();\n" + " if (e) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int g(int i) {\n" + " if (i < 256)\n" + " return 1;\n" + " const int N = 2 * i;\n" + " i -= 256;\n" + " if (i == 0)\n" + " return 0;\n" + " return N;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int i, int j) {\n" + " if (i < j) {\n" + " i++;\n" + " if (i >= j)\n" + " return;\n" + " i++;\n" + " if (i >= j) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int get_delta() {\n" + " clock_t now_ms = (clock() / (CLOCKS_PER_SEC / 1000));\n" + " static clock_t last_clock_ms = now_ms;\n" + " clock_t delta = now_ms - last_clock_ms;\n" + " last_clock_ms = now_ms;\n" + " if (delta > 50)\n" + " delta = 50;\n" + " return delta;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10555 + check("struct C {\n" + " int GetI() const { return i; }\n" + " int i{};\n" + "};\n" + "struct B {\n" + " C *m_PC{};\n" + " Modify();\n" + "};\n" + "struct D : B {\n" + " void test(); \n" + "};\n" + "void D::test() {\n" + " const int I = m_PC->GetI();\n" + " Modify();\n" + " if (m_PC->GetI() != I) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10624 + check("struct Data {\n" + " Base* PBase{};\n" + "};\n" + "void f(Data* BaseData) {\n" + " Base* PObj = BaseData->PBase;\n" + " if (PObj == nullptr)\n" + " return;\n" + " Derived* pD = dynamic_cast(PObj);\n" + " if (pD) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #9549 + check("void f(const uint32_t v) {\n" + " const uint32_t v16 = v >> 16;\n" + " if (v16) {\n" + " const uint32_t v8 = v16 >> 8;\n" + " if (v8) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10649 + check("void foo(struct diag_msg *msg) {\n" + " msg = msg->next;\n" + " if (msg == NULL)\n" + " return CMD_OK;\n" + " msg = msg->next;\n" + " if (msg == NULL)\n" + " return CMD_OK;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int foo(bool a, bool b) {\n" + " if((!a == !b) && !a && b)\n" + " return 1;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 'b' is always false\n", errout_str()); + + // #11124 + check("struct Basket {\n" + " std::vector getApples() const;\n" + " std::vector getBananas() const; \n" + "};\n" + "int getFruit(const Basket & b, bool preferApples)\n" + "{\n" + " std::vector apples = b.getApples();\n" + " int apple = apples.empty() ? -1 : apples.front();\n" + " std::vector bananas = b.getBananas();\n" + " int banana = bananas.empty() ? -1 : bananas.front();\n" + " int fruit = std::max(apple, banana);\n" + " if (fruit == -1)\n" + " return fruit;\n" + " if (std::min(apple, banana) != -1)\n" + " fruit = preferApples ? apple : banana;\n" + " return fruit;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const std::string & s, int i) {\n" + " const char c = s[i];\n" + " if (!std::isalnum(c)) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" // #11404 + " int f() const;\n" + " void g();\n" + "};\n" + "void h(std::vector::iterator it) {\n" + " auto i = (*it)->f();\n" + " (*it)->g();\n" + " auto j = (*it)->f();\n" + " if (i == j) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #11384 + check("bool f(const int* it, const int* end) {\n" + " return (it != end) && *it++ &&\n" + " (it != end) && *it;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #12116 + check("void f(int n) {\n" + " for (int i = 0; i < N; ++i) {\n" + " if (i < n) {}\n" + " else if (i > n) {}\n" + " else {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void alwaysTrueInfer() { + check("void f(int x) {\n" + " if (x > 5) {\n" + " x++;\n" + " if (x == 1) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x==1' is always false\n", errout_str()); + + check("void f(int x) {\n" + " if (x > 5) {\n" + " x++;\n" + " if (x != 1) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x!=1' is always true\n", errout_str()); + + // #6890 + check("void f(int i) {\n" + " int x = i;\n" + " if (x >= 1) {}\n" + " else {\n" + " x = 8 - x;\n" + " if (x == -1) {}\n" + " else {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x==-1' is always false\n", errout_str()); + + check("void f(int i) {\n" + " int x = i;\n" + " if (x >= 1) {}\n" + " else {\n" + " x = 8 - x;\n" + " if (x != -1) {}\n" + " else {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x!=-1' is always true\n", errout_str()); + + check("void f(int i) {\n" + " int x = i;\n" + " if (x >= 1) {}\n" + " else {\n" + " x = 8 - x;\n" + " if (x >= -1) {}\n" + " else {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x>=-1' is always true\n", errout_str()); + + check("void f(int i) {\n" + " int x = i;\n" + " if (x >= 1) {}\n" + " else {\n" + " x = 8 - x;\n" + " if (x > -1) {}\n" + " else {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x>-1' is always true\n", errout_str()); + + check("void f(int i) {\n" + " int x = i;\n" + " if (x >= 1) {}\n" + " else {\n" + " x = 8 - x;\n" + " if (x < -1) {}\n" + " else {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x<-1' is always false\n", errout_str()); + + check("void f(int i) {\n" + " int x = i;\n" + " if (x >= 1) {}\n" + " else {\n" + " x = 8 - x;\n" + " if (x <= -1) {}\n" + " else {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x<=-1' is always false\n", errout_str()); + + check("void f(int i) {\n" + " int x = i;\n" + " if (x >= 1) {}\n" + " else {\n" + " x = 8 - x;\n" + " if (x > 7) {}\n" + " else {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) Condition 'x>7' is always true\n", errout_str()); + + check("void f(int i) {\n" + " int x = i;\n" + " if (x >= 1) {}\n" + " else {\n" + " x = 8 - x;\n" + " if (x > 9) {}\n" + " else {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int i) {\n" + " int x = i;\n" + " if (x >= 1) {}\n" + " else {\n" + " x = 8 - x;\n" + " if (x > 10) {}\n" + " else {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #11100 + check("struct T {\n" + " bool m{};\n" + " void f(bool b);\n" + " bool get() const { return m; }\n" + " void set(bool v) { m = v; }\n" + "};\n" + "void T::f(bool b) {\n" + " bool tmp = get();\n" + " set(b);\n" + " if (tmp != get()) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #9541 + check("int f(int pos, int a) {\n" + " if (pos <= 0)\n" + " pos = 0;\n" + " else if (pos < a)\n" + " if(pos > 0)\n" + " --pos;\n" + " return pos;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (style) Condition 'pos>0' is always true\n", errout_str()); + + // #9721 + check("void f(int x) {\n" + " if (x > 127) {\n" + " if ( (x>255) || (-128>x) )\n" + " return;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Condition '-128>x' is always false\n", errout_str()); + + // #8778 + check("void f() {\n" + " for(int i = 0; i < 19; ++i)\n" + " if(i<=18) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'i<=18' is always true\n", errout_str()); + + // #8209 + check("void f() {\n" + " for(int x = 0; x < 3; ++x)\n" + " if(x == -5) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'x==-5' is always false\n", errout_str()); + + // #8407 + check("int f(void) {\n" + " for(int i = 0; i <1; ++i)\n" + " if(i == 0) return 1; \n" // << + " else return 0;\n" + " return -1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'i==0' is always true\n", errout_str()); + + check("void f(unsigned int u1, unsigned int u2) {\n" + " if (u1 <= 10 && u2 >= 20) {\n" + " if (u1 != u2) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Condition 'u1!=u2' is always true\n", errout_str()); + + // #10544 + check("void f(int N) {\n" + " if (N > 0) {\n" + " while (N)\n" + " N = test();\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #11098 + check("void f(unsigned int x) { if (x == -1u) {} }\n"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(const int *p, const int *q) {\n" + " return p != NULL && q != NULL && p == NULL;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Return value 'p==NULL' is always false\n", errout_str()); + + check("struct S {\n" // #11789 + " std::vector v;\n" + " void f(int i) const;\n" + "};\n" + "void S::f(int i) const {\n" + " int j = i - v.size();\n" + " if (j >= 0) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int i) {\n" // #12039 + " if ((128 + i < 255 ? 128 + i : 255) > 0) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void alwaysTrueContainer() { + // #9329 + check("void c1(std::vector&);\n" + "void c2(std::vector&);\n" + "void foo(int flag) {\n" + " std::vector g;\n" + " if (flag)\n" + " c1(g );\n" + " else\n" + " c2(g );\n" + " if ( !g.empty() )\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int flag) {\n" + " std::vector g;\n" + " if (flag)\n" + " c1(g );\n" + " else\n" + " c2(g );\n" + " if ( !g.empty() )\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " std::vector v;\n" + " void g();\n" + " void f(bool b) {\n" + " v.clear();\n" + " g();\n" + " return !v.empty();\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + // #10409 + check("void foo(const std::string& s) {\n" + " if( s.size() < 2 ) return;\n" + " if( s == \"ab\" ) return;\n" + " if( s.size() < 3 ) return;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(const std::string& s) {\n" + " if( s.size() < 2 ) return;\n" + " if( s != \"ab\" )\n" + " if( s.size() < 3 ) return;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10226 + check("int f(std::vector::iterator it, const std::vector& vector) {\n" + " if (!(it != vector.end() && it != vector.begin()))\n" + " throw 0;\n" + " if (it != vector.end() && *it == 0)\n" + " return -1;\n" + " return *it;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'it!=vector.end()' is always true\n", errout_str()); + + // #11303 + check("void f(int n) {\n" + " std::vector buffer(n);\n" + " if(buffer.back() == 0 ||\n" + " buffer.back() == '\\n' ||\n" + " buffer.back() == '\\0') {}\n" + "}\n"); + if (std::numeric_limits::is_signed) { + ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'buffer.back()=='\\0'' is always false\n", errout_str()); + } else { + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Condition 'buffer.back()=='\\0'' is always false\n", errout_str()); + } + + // #9353 + check("struct X { std::string s; };\n" + "void f(const std::vector&v) {\n" + " for (std::vector::const_iterator it = v.begin(); it != v.end(); ++it)\n" + " if (!it->s.empty()) {\n" + " if (!it->s.empty()) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (style) Condition '!it->s.empty()' is always true\n", errout_str()); + + check("struct X { std::string s; };\n" + "void f(const std::vector&v) {\n" + " for (std::vector::const_iterator it = v.begin(); it != v.end(); ++it)\n" + " if (!it->s.empty()) {\n" + " if (!it->s.empty()) {}\n" + " }\n" + "}\n"); + TODO_ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (style) Condition '!it->s.empty()' is always true\n", "", errout_str()); + + // #10508 + check("bool f(const std::string& a, const std::string& b) {\n" + " return a.empty() || (b.empty() && a.empty());\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Return value 'a.empty()' is always false\n", errout_str()); + + check("struct A {\n" + " struct iterator;\n" + " iterator begin() const;\n" + " iterator end() const;\n" + "};\n" + "A g();\n" + "void f(bool b) {\n" + " std::set s;\n" + " auto v = g();\n" + " s.insert(v.begin(), v.end());\n" + " if(!b && s.size() != 1)\n" + " return;\n" + " if(!s.empty()) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int f(std::string s) {\n" + " if (s.empty())\n" + " return -1;\n" + " s += '\\n';\n" + " if (s.empty())\n" + " return -1;\n" + " return -1;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (style) Condition 's.empty()' is always false\n", errout_str()); + + check("void f(std::string& p) {\n" + " const std::string d{ \"abc\" };\n" + " p += d;\n" + " if(p.empty()) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'p.empty()' is always false\n", errout_str()); + + check("bool f(int i, FILE* fp) {\n" + " std::string s = \"abc\";\n" + " s += std::to_string(i);\n" + " s += \"\\n\";\n" + " return fwrite(s.c_str(), 1, s.length(), fp) == s.length();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const std::string& s) {\n" // #9148 + " if (s.empty() || s.size() < 1) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Condition 's.size()<1' is always false\n", errout_str()); + + check("void bar(std::vector& vv) {\n" // #11464 + " class F {\n" + " public:\n" + " F(int, std::vector& lv) : mV(lv) {\n" + " mV.push_back(0);\n" + " }\n" + " private:\n" + " std::vector& mV;\n" + " } fi(1, vv);\n" + "}\n" + "void g() {\n" + " std::vector v;\n" + " bar(v);\n" + " if (v.empty()) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct F {\n" + " F(int, std::vector&lv) : mV(lv) {\n" + " mV.push_back(0);\n" + " }\n" + " std::vector& mV;\n" + "};\n" + "void g(std::vector& vv) {\n" + " F(1, vv);\n" + "}\n" + "void f() {\n" + " std::vector v;\n" + " g(v);\n" + " if (v.empty()) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void alwaysTrueLoop() + { + check("long foo() {\n" + " bool bUpdated = false;\n" + " long Ret{};\n" + " do {\n" + " Ret = bar();\n" + " if (Ret == 0) {\n" + " if (bUpdated)\n" + " return 1;\n" + " bUpdated = true;\n" + " }\n" + " else\n" + " bUpdated = false;\n" + " }\n" + " while (bUpdated);\n" + " return Ret;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("bool foo() {\n" + " bool bFirst = true;\n" + " do {\n" + " if (bFirst)\n" + " bar();\n" + " if (baz())\n" + " break; \n" + " bFirst = false;\n" + " } while (true);\n" + " return bFirst;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " void * pool = NULL;\n" + " do {\n" + " pool = malloc(40);\n" + " if (dostuff())\n" + " break;\n" + " pool = NULL;\n" + " }\n" + " while (0);\n" + " if (pool) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #8499 + check("void f(void)\n" + "{\n" + " for (int i = 0; i < 2; ++i)\n" + " {\n" + " for (int j = 0; j < 8; ++j)\n" + " {\n" + " if ( (i==0|| i==1)\n" // << always true + " && (j==0) )\n" + " {;}\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:7]: (style) Condition 'i==1' is always true\n", errout_str()); + + // #10863 + check("void f(const int A[], int Len) {\n" + " if (Len <= 0)\n" + " return;\n" + " int I = 0;\n" + " while (I < Len) {\n" + " int K = I + 1;\n" + " for (; K < Len; K++) {\n" + " if (A[I] != A[K])\n" + " break;\n" + " } \n" + " I = K; \n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #11434 + " const int N = 5;\n" + " bool a[N];\n" + " for (int i = 0; i < N; a[i++] = false);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #8192 + " for (int i = 0; i > 10; ++i) {}\n" + "}\n"); + TODO_ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'i>10' is always false\n", "", errout_str()); + + check("void f() {\n" + " for (int i = 1000; i < 20; ++i) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'i<20' is always false\n", errout_str()); + } + + void alwaysTrueTryCatch() + { + check("void g();\n" + "void f(int x)\n" + "{\n" + " if( x ) {\n" + " try {\n" + " g();\n" + " }\n" + " catch(...) {\n" + " return;\n" + " }\n" + " }\n" + " g();\n" + " if( x ) {\n" + " g();\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void g();\n" + "void h();\n" + "void f(int x) {\n" + " if( x ) {\n" + " try {\n" + " g();\n" + " return;\n" + " }\n" + " catch( ... ) {}\n" + " }\n" + " h();\n" + " if( x ) {\n" + " g();\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #10701 + " std::string s;\n" + " try {\n" + " try {\n" + " s = g();\n" + " }\n" + " catch (const Err& err) {}\n" + " }\n" + " catch (const std::exception& e) {}\n" + " if (s != \"abc\") {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void multiConditionAlwaysTrue() { + check("void f() {\n" + " int val = 0;\n" + " if (val < 0) continue;\n" + " if (val > 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int val = 0;\n" + " if (val < 0) {\n" + " if (val > 0) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int val = 0;\n" + " if (val < 0) {\n" + " if (val < 0) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int activate = 0;\n" + " int foo = 0;\n" + " if (activate) {}\n" + " else if (foo) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'activate' is always false\n" + "[test.cpp:5]: (style) Condition 'foo' is always false\n", errout_str()); + + // #6904 + check("void f() {\n" + " const int b[2] = { 1,0 };\n" + " if(b[1] == 2) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'b[1]==2' is always false\n", errout_str()); + + // #9878 + check("void f(bool a, bool b) {\n" + " if (a && b){;}\n" + " else if (!a && b){;}\n" + " else if (!a && !b){;}\n" + " else {;}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateCondition() { + check("void f(bool x) {\n" + " if(x) {}\n" + " if(x) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The if condition is the same as the previous if condition\n", + errout_str()); + + check("void f(int x) {\n" + " if(x == 1) {}\n" + " if(x == 1) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The if condition is the same as the previous if condition\n", + errout_str()); + + check("void f(int x) {\n" + " if(x == 1) {}\n" + " if(x == 2) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if(x == 1) {}\n" + " if(x != 1) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool x) {\n" + " if(x) {}\n" + " g();\n" + " if(x) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if(x == 1) { x++; }\n" + " if(x == 1) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #8996 + check("void g(int** v);\n" + "void f() {\n" + " int a = 0;\n" + " int b = 0;\n" + " int* d[] = {&a, &b};\n" + " g(d);\n" + " if (a) {}\n" + " if (b) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9311 + check("struct c {\n" + " int* p;\n" + "};\n" + "void g(struct c* v);\n" + "void f() {\n" + " int a = 0;\n" + " int b = 0;\n" + " struct c d[] = {{&a}, {&b}};\n" + " g(d);\n" + " if (a) {}\n" + " if (b) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #8993 + check("void f(const std::string& x) {\n" + " auto y = x;\n" + " if (x.empty()) y = \"1\";\n" + " if (y.empty()) return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9106 + check("struct A {int b;};\n" + "void f(A a, int c) {\n" + " if (a.b) a.b = c;\n" + " if (a.b) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " int a;\n" + " void b() const {\n" + " return a == 1;\n" + " }\n" + " void c();\n" + " void d() {\n" + " if(b()) {\n" + " c();\n" + " }\n" + " if (b()) {\n" + " a = 3;\n" + " }\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " int a;\n" + " void b() const {\n" + " return a == 1;\n" + " }\n" + " void d() {\n" + " if(b()) {\n" + " a = 2;\n" + " }\n" + " if (b()) {\n" + " a = 3;\n" + " }\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " int a;\n" + " void b() const {\n" + " return a == 1;\n" + " }\n" + " void d() {\n" + " if(b()) {\n" + " }\n" + " if (b()) {\n" + " a = 3;\n" + " }\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (style) The if condition is the same as the previous if condition\n", + errout_str()); + + check("void f(bool a, bool b) {\n" + " auto g = [&] { b = !a; };\n" + " if (b)\n" + " g();\n" + " if (b) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void g(bool& a);\n" + "void f(bool b) {\n" + " auto h = std::bind(&g, std::ref(b));\n" + " if (b)\n" + " h();\n" + " if (b) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int *i) {\n" + " if (*i == 0) {\n" + " *i = 1;\n" + " }\n" + " if (*i == 0) {\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void g(std::function);\n" + "void f(std::vector v) {\n" + " auto x = [&v] { v.push_back(1); };\n" + " if(v.empty()) {\n" + " g(x);\n" + " }\n" + " if(v.empty())\n" + " return;\n" + " return;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { int i; };\n" + "int f(const S& s) {\n" + " int a = 0, b = 0;\n" + " if (s.i == 0)\n" + " a = 1;\n" + " if (s.i == 0)\n" + " b = 1;\n" + " return a + b;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:6]: (style) The if condition is the same as the previous if condition\n", errout_str()); + + // do not crash + check("void assign(const MMA& other) {\n" + " if (mPA.cols != other.mPA.cols || mPA.rows != other.mPA.rows)\n" + " ;\n" + " if (other.mPA.cols > 0 && other.mPA.rows > 0)\n" + " ;\n" + "}"); + } + + void checkInvalidTestForOverflow() { + check("void f(char *p, unsigned int x) {\n" + " assert((p + x) < p);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow '(p+x)= p);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow '(p+x)>=p'; pointer overflow is undefined behavior. Some mainstream compilers remove such overflow tests when optimising the code and assume it's always true.\n", errout_str()); + + check("void f(char *p, unsigned int x) {\n" + " assert(p > (p + x));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow 'p>(p+x)'; pointer overflow is undefined behavior. Some mainstream compilers remove such overflow tests when optimising the code and assume it's always false.\n", errout_str()); + + check("void f(char *p, unsigned int x) {\n" + " assert(p <= (p + x));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Invalid test for overflow 'p<=(p+x)'; pointer overflow is undefined behavior. Some mainstream compilers remove such overflow tests when optimising the code and assume it's always true.\n", errout_str()); + + check("void f(signed int x) {\n" // unsigned overflow => don't warn + " assert(x + 100U < x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + + // x + c < x + +#define MSG(EXPR, RESULT) "[test.cpp:1]: (warning) Invalid test for overflow '" EXPR "'; signed integer overflow is undefined behavior. Some mainstream compilers remove such overflow tests when optimising the code and assume it's always " RESULT ".\n" + + check("int f(int x) { return x + 10 > x; }"); + ASSERT_EQUALS(MSG("x+10>x", "true"), errout_str()); + + check("int f(int x) { return x + 10 >= x; }"); + ASSERT_EQUALS(MSG("x+10>=x", "true"), errout_str()); + + check("int f(int x) { return x + 10 < x; }"); + ASSERT_EQUALS(MSG("x+10 x; }"); + ASSERT_EQUALS(MSG("x-10>x", "false"), errout_str()); + + check("int f(int x) { return x - 10 >= x; }"); + ASSERT_EQUALS(MSG("x-10>=x", "false"), errout_str()); + + check("int f(int x) { return x - 10 < x; }"); + ASSERT_EQUALS(MSG("x-10 x; }"); + ASSERT_EQUALS(MSG("x+y>x", "y>0"), errout_str()); + + check("int f(int x, int y) { return x + y >= x; }"); + ASSERT_EQUALS(MSG("x+y>=x", "y>=0"), errout_str()); + + // x - y < x + check("int f(int x, int y) { return x - y < x; }"); + ASSERT_EQUALS(MSG("x-y0"), errout_str()); + + check("int f(int x, int y) { return x - y <= x; }"); + ASSERT_EQUALS(MSG("x-y<=x", "y>=0"), errout_str()); + + check("int f(int x, int y) { return x - y > x; }"); + ASSERT_EQUALS(MSG("x-y>x", "y<0"), errout_str()); + + check("int f(int x, int y) { return x - y >= x; }"); + ASSERT_EQUALS(MSG("x-y>=x", "y<=0"), errout_str()); + } + + void checkConditionIsAlwaysTrueOrFalseInsideIfWhile() { + check("void f() {\n" + " enum states {A,B,C};\n" + " const unsigned g_flags = B|C;\n" + " if(g_flags & A) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Condition 'g_flags&A' is always false\n", errout_str()); + + check("void f() {\n" + " int a = 5;" + " if(a) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'a' is always true\n", errout_str()); + + check("void f() {\n" + " int a = 5;" + " while(a + 1) { a--; }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int a = 5;" + " while(a + 1) { return; }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'a+1' is always true\n", errout_str()); + } + + void alwaysTrueFalseInLogicalOperators() { + check("bool f();\n" + "void foo() { bool x = true; if(x||f()) {}}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always true\n", errout_str()); + + check("void foo(bool b) { bool x = true; if(x||b) {}}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Condition 'x' is always true\n", errout_str()); + + check("void foo(bool b) { if(true||b) {}}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f();\n" + "void foo() { bool x = false; if(x||f()) {}}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always false\n", errout_str()); + + check("bool f();\n" + "void foo() { bool x = false; if(x&&f()) {}}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always false\n", errout_str()); + + check("void foo(bool b) { bool x = false; if(x&&b) {}}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Condition 'x' is always false\n", errout_str()); + + check("void foo(bool b) { if(false&&b) {}}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f();\n" + "void foo() { bool x = true; if(x&&f()) {}}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'x' is always true\n", errout_str()); + + // #9578 + check("bool f(const std::string &s) {\n" + " return s.size()>2U && s[0]=='4' && s[0]=='2';\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (style) Return value 's[0]=='2'' is always false\n", errout_str()); + + check("void f(int i) { if (i == 1 || 2) {} }\n"); // #12487 + ASSERT_EQUALS("[test.cpp:1]: (style) Condition 'i==1||2' is always true\n", errout_str()); + + check("enum E { E1 = 1, E2 = 2 };\n" + "void f(int i) { if (i == E1 || E2) {} }\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'i==E1||E2' is always true\n", errout_str()); + } + + void pointerAdditionResultNotNull() { + check("void f(char *ptr) {\n" + " if (ptr + 1 != 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison is wrong. Result of 'ptr+1' can't be 0 unless there is pointer overflow, and pointer overflow is undefined behaviour.\n", errout_str()); + } + + void duplicateConditionalAssign() { + setMultiline(); + + check("void f(int& x, int y) {\n" + " if (x == y)\n" + " x = y;\n" + "}"); + ASSERT_EQUALS("test.cpp:3:style:Assignment 'x=y' is redundant with condition 'x==y'.\n" + "test.cpp:2:note:Condition 'x==y'\n" + "test.cpp:3:note:Assignment 'x=y' is redundant\n", errout_str()); + + check("void f(int& x, int y) {\n" + " if (x != y)\n" + " x = y;\n" + "}"); + ASSERT_EQUALS("test.cpp:2:style:The statement 'if (x!=y) x=y' is logically equivalent to 'x=y'.\n" + "test.cpp:3:note:Assignment 'x=y'\n" + "test.cpp:2:note:Condition 'x!=y' is redundant\n", errout_str()); + + check("void f(int& x, int y) {\n" + " if (x == y)\n" + " x = y;\n" + " else\n" + " x = 1;\n" + "}"); + ASSERT_EQUALS("test.cpp:3:style:Assignment 'x=y' is redundant with condition 'x==y'.\n" + "test.cpp:2:note:Condition 'x==y'\n" + "test.cpp:3:note:Assignment 'x=y' is redundant\n", errout_str()); + + check("void f(int& x, int y) {\n" + " if (x != y)\n" + " x = y;\n" + " else\n" + " x = 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int& x, int y) {\n" + " if (x == y)\n" + " x = y + 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void g();\n" + "void f(int& x, int y) {\n" + " if (x == y) {\n" + " x = y;\n" + " g();\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(bool b) {\n" + " if (b)\n" + " b = false;\n" + " else\n" + " g();\n" + " return b;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int& i) {\n" + " if (!i)\n" + " i = 1; \n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" // #9406 + " S() : b(false) {}\n" + " void f() {\n" + " if (b) b = true;\n" + " if (b) b = false;\n" + " if (!b) b = true;\n" + " if (!b) b = false;\n" + " }\n" + " bool b;\n" + "};\n"); + ASSERT_EQUALS("test.cpp:4:style:The statement 'if (b) b=true' is redundant.\n" + "test.cpp:4:note:Assignment 'b=true'\n" + "test.cpp:4:note:Condition 'b' is redundant\n" + "test.cpp:5:style:The statement 'if (b) b=false' is logically equivalent to 'b=false'.\n" + "test.cpp:5:note:Assignment 'b=false'\n" + "test.cpp:5:note:Condition 'b' is redundant\n" + "test.cpp:6:style:The statement 'if (!b) b=true' is logically equivalent to 'b=true'.\n" + "test.cpp:6:note:Assignment 'b=true'\n" + "test.cpp:6:note:Condition '!b' is redundant\n" + "test.cpp:7:style:The statement 'if (!b) b=false' is redundant.\n" + "test.cpp:7:note:Assignment 'b=false'\n" + "test.cpp:7:note:Condition '!b' is redundant\n", + errout_str()); + } + + void checkAssignmentInCondition() { + check("void f(std::string s) {\n" + " if (s=\"123\"){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Suspicious assignment in condition. Condition 's=\"123\"' is always true.\n", errout_str()); + + check("void f(std::string *p) {\n" + " if (p=foo()){}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(uint32_t u) {\n" // #2490 + " if ((u = 0x00000000) || (u = 0xffffffff)) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Condition 'u=0x00000000' is always false\n" + "[test.cpp:2]: (style) Condition 'u=0xffffffff' is always true\n", + errout_str()); + } + + void compareOutOfTypeRange() { + const Settings settingsUnix64 = settingsBuilder().severity(Severity::style).platform(Platform::Type::Unix64).build(); + + check("void f(unsigned char c) {\n" + " if (c == 256) {}\n" + "}", settingsUnix64); + ASSERT_EQUALS("[test.cpp:2]: (style) Comparing expression of type 'unsigned char' against value 256. Condition is always false.\n", errout_str()); + + check("void f(unsigned char* b, int i) {\n" // #6372 + " if (b[i] == 256) {}\n" + "}", settingsUnix64); + ASSERT_EQUALS("[test.cpp:2]: (style) Comparing expression of type 'unsigned char' against value 256. Condition is always false.\n", errout_str()); + + check("void f(unsigned char c) {\n" + " if (c == 255) {}\n" + "}", settingsUnix64); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool b) {\n" + " if (b == true) {}\n" + "}", settingsUnix64); + ASSERT_EQUALS("", errout_str()); + + // #10372 + check("void f(signed char x) {\n" + " if (x == 0xff) {}\n" + "}", settingsUnix64); + ASSERT_EQUALS("[test.cpp:2]: (style) Comparing expression of type 'signed char' against value 255. Condition is always false.\n", errout_str()); + + check("void f(short x) {\n" + " if (x == 0xffff) {}\n" + "}", settingsUnix64); + ASSERT_EQUALS("[test.cpp:2]: (style) Comparing expression of type 'signed short' against value 65535. Condition is always false.\n", errout_str()); + + check("void f(int x) {\n" + " if (x == 0xffffffff) {}\n" + "}", settingsUnix64); + ASSERT_EQUALS("", errout_str()); + + check("void f(long x) {\n" + " if (x == ~0L) {}\n" + "}", settingsUnix64); + ASSERT_EQUALS("", errout_str()); + + check("void f(long long x) {\n" + " if (x == ~0LL) {}\n" + "}", settingsUnix64); + ASSERT_EQUALS("", errout_str()); + + check("int f(int x) {\n" + " const int i = 0xFFFFFFFF;\n" + " if (x == i) {}\n" + "}", settingsUnix64); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " char c;\n" + " if ((c = foo()) != -1) {}\n" + "}", settingsUnix64); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " if (x < 3000000000) {}\n" + "}", settingsUnix64); + ASSERT_EQUALS("[test.cpp:2]: (style) Comparing expression of type 'signed int' against value 3000000000. Condition is always true.\n", errout_str()); + + check("void f(const signed char i) {\n" // #8545 + " if (i > -129) {}\n" // warn + " if (i >= -128) {}\n" // warn + " if (i >= -127) {}\n" + " if (i < +128) {}\n" // warn + " if (i <= +127) {}\n" // warn + " if (i <= +126) {}\n" + "}\n", settingsUnix64); + ASSERT_EQUALS("[test.cpp:2]: (style) Comparing expression of type 'const signed char' against value -129. Condition is always true.\n" + "[test.cpp:3]: (style) Comparing expression of type 'const signed char' against value -128. Condition is always true.\n" + "[test.cpp:5]: (style) Comparing expression of type 'const signed char' against value 128. Condition is always true.\n" + "[test.cpp:6]: (style) Comparing expression of type 'const signed char' against value 127. Condition is always true.\n", + errout_str()); + + check("void f(const unsigned char u) {\n" + " if (u > 0) {}\n" + " if (u < 0) {}\n" // warn + " if (u >= 0) {}\n" // warn + " if (u <= 0) {}\n" + " if (u > 255) {}\n" // warn + " if (u < 255) {}\n" + " if (u >= 255) {}\n" + " if (u <= 255) {}\n" // warn + " if (0 < u) {}\n" + " if (0 > u) {}\n" // warn + " if (0 <= u) {}\n" // warn + " if (0 >= u) {}\n" + " if (255 < u) {}\n" // warn + " if (255 > u) {}\n" + " if (255 <= u) {}\n" + " if (255 >= u) {}\n" // warn + "}\n", settingsUnix64); + ASSERT_EQUALS("[test.cpp:3]: (style) Comparing expression of type 'const unsigned char' against value 0. Condition is always false.\n" + "[test.cpp:4]: (style) Comparing expression of type 'const unsigned char' against value 0. Condition is always true.\n" + "[test.cpp:6]: (style) Comparing expression of type 'const unsigned char' against value 255. Condition is always false.\n" + "[test.cpp:9]: (style) Comparing expression of type 'const unsigned char' against value 255. Condition is always true.\n" + "[test.cpp:11]: (style) Comparing expression of type 'const unsigned char' against value 0. Condition is always false.\n" + "[test.cpp:12]: (style) Comparing expression of type 'const unsigned char' against value 0. Condition is always true.\n" + "[test.cpp:14]: (style) Comparing expression of type 'const unsigned char' against value 255. Condition is always false.\n" + "[test.cpp:17]: (style) Comparing expression of type 'const unsigned char' against value 255. Condition is always true.\n", + errout_str()); + } + + void knownConditionCast() { // #9976 + check("void f(int i) {\n" + " if (i < 0 || (unsigned)i > 5) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void knownConditionIncrementLoop() { // #9808 + check("void f() {\n" + " int a = 0;\n" + " while (++a < 5) {}\n" + " if (a == 1) {}\n" + " std::cout << a;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void knownConditionAfterBailout() { // #12526 + check( + "#include \n" + "int func()\n" + "{\n" + " return VALUE_1;" + "}\n" + "\n" + "struct S1 {\n" + " bool b{};\n" + "};\n" + "\n" + "struct S {\n" + " void f(const std::list& l) const\n" + " {\n" + " if (mS.b)\n" + " return;\n" + " for (int i : l)\n" + " {\n" + " (void)i;\n" + " if (mS.b)\n" + " continue;\n" + " }\n" + " }\n" + "\n" + " S1 mS;\n" + "};" + ); + ASSERT_EQUALS("[test.cpp:13] -> [test.cpp:18]: (style) Condition 'mS.b' is always false\n", errout_str()); + } +}; + +REGISTER_TEST(TestCondition) diff --git a/cppcheck-2.14.0/test/testconstructors.cpp b/cppcheck-2.14.0/test/testconstructors.cpp new file mode 100644 index 00000000..52ddf961 --- /dev/null +++ b/cppcheck-2.14.0/test/testconstructors.cpp @@ -0,0 +1,4502 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "standards.h" +#include "settings.h" + +class TestConstructors : public TestFixture { +public: + TestConstructors() : TestFixture("TestConstructors") {} + +private: + const Settings settings = settingsBuilder().severity(Severity::style).severity(Severity::warning).build(); + +#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) + void check_(const char* file, int line, const char code[], bool inconclusive = false) { + const Settings settings1 = settingsBuilder(settings).certainty(Certainty::inconclusive, inconclusive).build(); + + // Tokenize.. + SimpleTokenizer tokenizer(settings1, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check class constructors.. + CheckClass checkClass(&tokenizer, &settings1, this); + checkClass.constructors(); + } + + void check_(const char* file, int line, const char code[], const Settings &s) { + // Tokenize.. + SimpleTokenizer tokenizer(s, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check class constructors.. + CheckClass checkClass(&tokenizer, &s, this); + checkClass.constructors(); + } + + void run() override { + TEST_CASE(simple1); + TEST_CASE(simple2); + TEST_CASE(simple3); + TEST_CASE(simple4); + TEST_CASE(simple5); // ticket #2560 + TEST_CASE(simple6); // ticket #4085 - uninstantiated template class + TEST_CASE(simple7); // ticket #4531 + TEST_CASE(simple8); + TEST_CASE(simple9); // ticket #4574 + TEST_CASE(simple10); // ticket #4388 + TEST_CASE(simple11); // ticket #4536, #6214 + TEST_CASE(simple12); // ticket #4620 + TEST_CASE(simple13); // #5498 - no constructor, c++11 assignments + TEST_CASE(simple14); // #6253 template base + TEST_CASE(simple15); // #8942 multiple arguments, decltype + TEST_CASE(simple16); // copy members with c++11 init + TEST_CASE(simple17); // #10360 + + TEST_CASE(noConstructor1); + TEST_CASE(noConstructor2); + TEST_CASE(noConstructor3); + TEST_CASE(noConstructor4); + TEST_CASE(noConstructor5); + TEST_CASE(noConstructor6); // ticket #4386 + TEST_CASE(noConstructor7); // ticket #4391 + TEST_CASE(noConstructor8); // ticket #4404 + TEST_CASE(noConstructor9); // ticket #4419 + TEST_CASE(noConstructor10); // ticket #6614 + TEST_CASE(noConstructor11); // ticket #3552 + TEST_CASE(noConstructor12); // #8951 - member initialization + TEST_CASE(noConstructor13); // #9998 + TEST_CASE(noConstructor14); // #10770 + TEST_CASE(noConstructor15); // #5499 + + TEST_CASE(forwardDeclaration); // ticket #4290/#3190 + + TEST_CASE(initvar_with_this); // BUG 2190300 + TEST_CASE(initvar_if); // BUG 2190290 + TEST_CASE(initvar_operator_eq1); // BUG 2190376 + TEST_CASE(initvar_operator_eq2); // BUG 2190376 + TEST_CASE(initvar_operator_eq3); + TEST_CASE(initvar_operator_eq4); // ticket #2204 + TEST_CASE(initvar_operator_eq5); // ticket #4119 + TEST_CASE(initvar_operator_eq6); + TEST_CASE(initvar_operator_eq7); + TEST_CASE(initvar_operator_eq8); + 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_mutablevar); + TEST_CASE(initvar_staticvar); + TEST_CASE(initvar_brace_init); + TEST_CASE(initvar_union); + TEST_CASE(initvar_delegate); // ticket #4302 + TEST_CASE(initvar_delegate2); + TEST_CASE(initvar_derived_class); + TEST_CASE(initvar_derived_pod_struct_with_union); // #11101 + + 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_nocopy4); // ticket #9247 + TEST_CASE(initvar_with_member_function_this); // ticket #4824 + + TEST_CASE(initvar_destructor); // No variables need to be initialized in a destructor + TEST_CASE(initvar_func_ret_func_ptr); // ticket #4449 + + TEST_CASE(initvar_alias); // #6921 + + TEST_CASE(initvar_templateMember); // #7205 + TEST_CASE(initvar_smartptr); // #10237 + + TEST_CASE(operatorEqSTL); + + TEST_CASE(uninitVar1); + TEST_CASE(uninitVar2); + TEST_CASE(uninitVar3); + TEST_CASE(uninitVar4); + TEST_CASE(uninitVar5); + TEST_CASE(uninitVar6); + TEST_CASE(uninitVar7); + TEST_CASE(uninitVar8); + TEST_CASE(uninitVar9); // ticket #1730 + TEST_CASE(uninitVar10); // ticket #1993 + TEST_CASE(uninitVar11); + TEST_CASE(uninitVar12); // ticket #2078 + TEST_CASE(uninitVar13); // ticket #1195 + TEST_CASE(uninitVar14); // ticket #2149 + TEST_CASE(uninitVar15); + TEST_CASE(uninitVar16); + TEST_CASE(uninitVar17); + TEST_CASE(uninitVar18); // ticket #2465 + TEST_CASE(uninitVar19); // ticket #2792 + TEST_CASE(uninitVar20); // ticket #2867 + TEST_CASE(uninitVar21); // ticket #2947 + TEST_CASE(uninitVar22); // ticket #3043 + TEST_CASE(uninitVar23); // ticket #3702 + TEST_CASE(uninitVar24); // ticket #3190 + TEST_CASE(uninitVar25); // ticket #4789 + TEST_CASE(uninitVar26); + TEST_CASE(uninitVar27); // ticket #5170 - rtl::math::setNan(&d) + TEST_CASE(uninitVar28); // ticket #6258 + TEST_CASE(uninitVar29); + TEST_CASE(uninitVar30); // ticket #6417 + TEST_CASE(uninitVar31); // ticket #8271 + TEST_CASE(uninitVar32); // ticket #8835 + TEST_CASE(uninitVar33); // ticket #10295 + TEST_CASE(uninitVar34); // ticket #10841 + TEST_CASE(uninitVarEnum1); + TEST_CASE(uninitVarEnum2); // ticket #8146 + TEST_CASE(uninitVarStream); + TEST_CASE(uninitVarTypedef); + TEST_CASE(uninitVarMemset); + TEST_CASE(uninitVarArray1); + TEST_CASE(uninitVarArray2); + TEST_CASE(uninitVarArray3); + TEST_CASE(uninitVarArray4); + TEST_CASE(uninitVarArray5); + TEST_CASE(uninitVarArray6); + TEST_CASE(uninitVarArray7); + TEST_CASE(uninitVarArray8); + TEST_CASE(uninitVarArray9); // ticket #6957, #6959 + TEST_CASE(uninitVarArray10); + TEST_CASE(uninitVarArray11); + TEST_CASE(uninitVarArray2D); + TEST_CASE(uninitVarArray3D); + TEST_CASE(uninitVarCpp11Init1); + TEST_CASE(uninitVarCpp11Init2); + TEST_CASE(uninitVarStruct1); // ticket #2172 + TEST_CASE(uninitVarStruct2); // ticket #838 + TEST_CASE(uninitVarUnion1); // ticket #3196 + TEST_CASE(uninitVarUnion2); + TEST_CASE(uninitMissingFuncDef); // can't expand function in constructor + TEST_CASE(privateCtor1); // If constructor is private.. + TEST_CASE(privateCtor2); // If constructor is private.. + TEST_CASE(function); // Function is not variable + TEST_CASE(uninitVarPublished); // Borland C++: Variables in the published section are auto-initialized + TEST_CASE(uninitVarInheritClassInit); // Borland C++: if class inherits from TObject, all variables are initialized + TEST_CASE(uninitOperator); // No FP about uninitialized 'operator[]' + TEST_CASE(uninitFunction1); // No FP when initialized in function + TEST_CASE(uninitFunction2); // No FP when initialized in function + TEST_CASE(uninitFunction3); // No FP when initialized in function + TEST_CASE(uninitFunction4); + TEST_CASE(uninitFunction5); + TEST_CASE(uninitSameClassName); // No FP when two classes have the same name + TEST_CASE(uninitFunctionOverload); // No FP when there are overloaded functions + TEST_CASE(uninitVarOperatorEqual); // ticket #2415 + TEST_CASE(uninitVarPointer); // ticket #3801 + TEST_CASE(uninitConstVar); + TEST_CASE(constructors_crash1); // ticket #5641 + TEST_CASE(classWithOperatorInName);// ticket #2827 + TEST_CASE(templateConstructor); // ticket #7942 + TEST_CASE(typedefArray); // ticket #5766 + + TEST_CASE(uninitAssignmentWithOperator); // ticket #7429 + TEST_CASE(uninitCompoundAssignment); // ticket #7429 + TEST_CASE(uninitComparisonAssignment); // ticket #7429 + + TEST_CASE(uninitTemplate1); // ticket #7372 + + TEST_CASE(unknownTemplateType); + } + + + 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 declare a constructor although it has private member variables which likely require initialization.\n", errout_str()); + + check("struct Fred\n" + "{\n" + "private:\n" + " int i;\n" + "};"); + ASSERT_EQUALS("[test.cpp:1]: (style) The struct 'Fred' does not declare a constructor although it has private member variables which likely require initialization.\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 copy constructor.\n" + "[test.cpp:6]: (warning) Member variable 'Fred::i' is not initialized in the move 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 copy constructor.\n" + "[test.cpp:5]: (warning) Member variable 'Fred::i' is not initialized in the move 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 declare a constructor although it has private member variables which likely require initialization.\n" + "[test.cpp:3]: (style) The class 'Wilma' does not declare a constructor although it has private member variables which likely require initialization.\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() { + check("class Fred {\n" // ticket #4388 + "public:\n" + " Fred() = default;\n" + "private:\n" + " int x;\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n", errout_str()); + + check("struct S {\n" // #9391 + " S() = default;\n" + " ~S() = default;\n" + " S(const S&) = default;\n" + " S(S&&) = default;\n" + " S& operator=(const S&) = default;\n" + " S& operator=(S&&) = default;\n" + " int i;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Member variable 'S::i' is not initialized in the constructor.\n", errout_str()); + } + + void simple11() { // ticket #4536, #6214 + check("class Fred {\n" + "public:\n" + " Fred() {}\n" + "private:\n" + " int x = 0;\n" + " int y = f();\n" + " int z{0};\n" + " int (*pf[2])(){nullptr, nullptr};\n" + " int a[2][3] = {{1,2,3},{4,5,6}};\n" + " int b{1}, c{2};\n" + " int d, e{3};\n" + " int f{4}, g;\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'Fred::d' is not initialized in the constructor.\n" + "[test.cpp:3]: (warning) Member variable 'Fred::g' is not initialized in the constructor.\n", errout_str()); + } + + void simple12() { // ticket #4620 + check("class Fred {\n" + " int x;\n" + "public:\n" + " Fred() { Init(); }\n" + " void Init(int i = 0);\n" + "};\n" + "void Fred::Init(int i) { x = i; }"); + ASSERT_EQUALS("", errout_str()); + + check("class Fred {\n" + " int x;\n" + " int y;\n" + "public:\n" + " Fred() { Init(0); }\n" + " void Init(int i, int j = 0);\n" + "};\n" + "void Fred::Init(int i, int j) { x = i; y = j; }"); + ASSERT_EQUALS("", errout_str()); + } + + void simple13() { // #5498 + check("class Fred {\n" + " int x=1;\n" + " int *y=0;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void simple14() { // #6253 template base + check("class Fred : public Base {" + "public:" + " Fred()\n" + " :Base(1),\n" + " x(1)\n" + " {}\n" + "private:\n" + " int x;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class Fred : public Base {" + "public:" + " Fred()\n" + " :Base{1},\n" + " x{1}\n" + " {}\n" + "private:\n" + " int x;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void simple16() { + check("struct S {\n" + " int i{};\n" + " S() = default;\n" + " S(const S& s) {}\n" + " S& operator=(const S& s) { return *this; }\n" + "};\n", /*inconclusive*/ true); + ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) Member variable 'S::i' is not assigned in the copy constructor. Should it be copied?\n" + "[test.cpp:5]: (warning) Member variable 'S::i' is not assigned a value in 'S::operator='.\n", + errout_str()); + + check("struct S {\n" + " int i;\n" + " S() : i(0) {}\n" + " S(const S& s) {}\n" + " S& operator=(const S& s) { return *this; }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'S::i' is not initialized in the copy constructor.\n" + "[test.cpp:5]: (warning) Member variable 'S::i' is not assigned a value in 'S::operator='.\n", + errout_str()); + } + + void simple17() { // #10360 + check("class Base {\n" + "public:\n" + " virtual void Copy(const Base& Src) = 0;\n" + "};\n" + "class Derived : public Base {\n" + "public:\n" + " Derived() : i(0) {}\n" + " Derived(const Derived& Src);\n" + " void Copy(const Base& Src) override;\n" + " int i;\n" + "};\n" + "Derived::Derived(const Derived& Src) {\n" + " Copy(Src);\n" + "}\n" + "void Derived::Copy(const Base& Src) {\n" + " auto d = dynamic_cast(Src);\n" + " i = d.i;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void simple15() { // #8942 + check("class A\n" + "{\n" + "public:\n" + " int member;\n" + "};\n" + "class B\n" + "{\n" + "public:\n" + " B(const decltype(A::member)& x, const decltype(A::member)& y) : x(x), y(y) {}\n" + "private:\n" + " const decltype(A::member)& x;\n" + " const decltype(A::member)& y;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void noConstructor1() { + // There are nonstatic member variables - constructor is needed + check("class Fred\n" + "{\n" + " int i;\n" + "};"); + ASSERT_EQUALS("[test.cpp:1]: (style) The class 'Fred' does not declare a constructor although it has private member variables which likely require initialization.\n", errout_str()); + } + + void noConstructor2() { + check("class Fred\n" + "{\n" + "public:\n" + " static void foobar();\n" + "};\n" + "\n" + "void Fred::foobar()\n" + "{ }"); + ASSERT_EQUALS("", errout_str()); + } + + void noConstructor3() { + check("class Fred\n" + "{\n" + "private:\n" + " static int foobar;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void noConstructor4() { + check("class Fred\n" + "{\n" + "public:\n" + " int foobar;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void noConstructor5() { + check("namespace Foo\n" + "{\n" + " int i;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void noConstructor6() { + // ticket #4386 + check("class Ccpucycles {\n" + " friend class foo::bar;\n" + " Ccpucycles() :\n" + " m_v(0), m_b(true)\n" + " {}\n" + "private:\n" + " cpucyclesT m_v;\n" + " bool m_b;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void noConstructor7() { + // ticket #4391 + check("short bar;\n" + "class foo;"); + ASSERT_EQUALS("", errout_str()); + } + + void noConstructor8() { + // ticket #4404 + check("class LineSegment;\n" + "class PointArray { };\n" + "void* tech_ = NULL;"); + ASSERT_EQUALS("", errout_str()); + } + + void noConstructor9() { + // ticket #4419 + check("class CGreeting : public CGreetingBase {\n" + "public:\n" + " CGreeting() : CGreetingBase(), MessageSet(false) {}\n" + "private:\n" + " bool MessageSet;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void noConstructor10() { + // ticket #6614 + check("class A : public wxDialog\n" + "{\n" + "private:\n" + " DECLARE_EVENT_TABLE()\n" + "public:\n" + " A(wxWindow *parent,\n" + " wxWindowID id = 1,\n" + " const wxString &title = wxT(" "),\n" + " const wxPoint& pos = wxDefaultPosition,\n" + " const wxSize& size = wxDefaultSize,\n" + " long style = wxDIALOG_NO_PARENT | wxMINIMIZE_BOX | wxMAXIMIZE_BOX | wxCLOSE_BOX);\n" + " virtual ~A();\n" + "private:\n" + " wxTimer *WxTimer1;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + + void noConstructor11() { // #3552 + check("class Fred { int x; };\n" + "union U { int y; Fred fred; };"); + ASSERT_EQUALS("", errout_str()); + } + + void noConstructor12() { // #8951 + check("class Fred { int x{0}; };"); + ASSERT_EQUALS("", errout_str()); + + check("class Fred { int x=0; };"); + ASSERT_EQUALS("", errout_str()); + + check("class Fred { int x[1]={0}; };"); // #8850 + ASSERT_EQUALS("", errout_str()); + + check("class Fred { int x[1]{0}; };"); + ASSERT_EQUALS("", errout_str()); + } + + void noConstructor13() { // #9998 + check("struct C { int v; };\n" + "struct B { C c[5] = {}; };\n" + "struct A {\n" + " A() {}\n" + " B b;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void noConstructor14() { // #10770 + check("typedef void (*Func)();\n" + "class C {\n" + "public:\n" + " void f();\n" + "private:\n" + " Func fp = nullptr;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void noConstructor15() { // #5499 + check("class C {\n" + "private:\n" + " int i1 = 0;\n" + " int i2;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'C::i2' is not initialized.\n", errout_str()); + + check("class C {\n" + "private:\n" + " int i1;\n" + " int i2;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) The class 'C' does not declare a constructor although it has private member variables which likely require initialization.\n", errout_str()); + + check("class C {\n" + "public:\n" + " C(int i) : i1(i) {}\n" + "private:\n" + " int i1;\n" + " int i2;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'C::i2' is not initialized in the constructor.\n", errout_str()); + } + + // ticket #4290 "False Positive: style (noConstructor): The class 'foo' does not have a constructor." + // ticket #3190 "SymbolDatabase: Parse of sub class constructor fails" + void forwardDeclaration() { + check("class foo;\n" + "int bar;"); + ASSERT_EQUALS("", errout_str()); + + check("class foo;\n" + "class foo;"); + ASSERT_EQUALS("", errout_str()); + + check("class foo{};\n" + "class foo;"); + ASSERT_EQUALS("", errout_str()); + } + + void initvar_with_this() { + check("struct Fred\n" + "{\n" + " Fred()\n" + " { this->i = 0; }\n" + " int i;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void initvar_if() { + check("struct Fred\n" + "{\n" + " Fred()\n" + " {\n" + " if (true)\n" + " i = 0;\n" + " else\n" + " i = 1;\n" + " }\n" + " int i;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void initvar_operator_eq1() { + // Bug 2190376 and #3820 - False positive, Uninitialized member variable with operator= + + check("struct Fred\n" + "{\n" + " int i;\n" + "\n" + " Fred()\n" + " { i = 0; }\n" + "\n" + " Fred(const Fred &fred)\n" + " { *this = fred; }\n" + "\n" + " const Fred & operator=(const Fred &fred)\n" + " { i = fred.i; return *this; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct Fred {\n" + " int i;\n" + "\n" + " Fred(const Fred &fred)\n" + " { (*this) = fred; }\n" + "\n" + " const Fred & operator=(const Fred &fred)\n" + " { i = fred.i; return *this; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct A\n" + "{\n" + " A() : i(0), j(0) {}\n" + "\n" + " A &operator=(const int &value)\n" + " {\n" + " i = value;\n" + " return (*this);\n" + " }\n" + "\n" + " int i;\n" + " int j;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + + void initvar_operator_eq2() { + check("struct Fred\n" + "{\n" + " Fred() { i = 0; }\n" + " void operator=(const Fred &fred) { }\n" + " int i;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Fred::i' is not assigned a value in 'Fred::operator='.\n", errout_str()); + } + + void initvar_operator_eq3() { + check("struct Fred\n" + "{\n" + " Fred() { Init(); }\n" + " void operator=(const Fred &fred) { Init(); }\n" + "private:\n" + " void Init() { i = 0; }\n" + " int i;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void initvar_operator_eq4() { + check("class Fred\n" + "{\n" + " int i;\n" + "public:\n" + " Fred() : i(5) { }\n" + " Fred & operator=(const Fred &fred)\n" + " {\n" + " if (&fred != this)\n" + " {\n" + " }\n" + " return *this\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'Fred::i' is not assigned a value in 'Fred::operator='.\n", errout_str()); + + check("class Fred\n" + "{\n" + " int * i;\n" + "public:\n" + " Fred() : i(NULL) { }\n" + " Fred & operator=(const Fred &fred)\n" + " {\n" + " if (&fred != this)\n" + " {\n" + " }\n" + " return *this\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'Fred::i' is not assigned a value in 'Fred::operator='.\n", errout_str()); + + check("class Fred\n" + "{\n" + " const int * i;\n" + "public:\n" + " Fred() : i(NULL) { }\n" + " Fred & operator=(const Fred &fred)\n" + " {\n" + " if (&fred != this)\n" + " {\n" + " }\n" + " return *this\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'Fred::i' is not assigned a value in 'Fred::operator='.\n", errout_str()); + + check("class Fred\n" + "{\n" + " const int i;\n" + "public:\n" + " Fred() : i(5) { }\n" + " Fred & operator=(const Fred &fred)\n" + " {\n" + " if (&fred != this)\n" + " {\n" + " }\n" + " return *this\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void initvar_operator_eq5() { // #4119 - false positive when using swap to assign variables + check("class Fred {\n" + " int i;\n" + "public:\n" + " Fred() : i(5) { }\n" + " ~Fred() { }\n" + " Fred(const Fred &fred) : i(fred.i) { }\n" + " Fred & operator=(const Fred &rhs) {\n" + " Fred(rhs).swap(*this);\n" + " return *this;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void initvar_operator_eq6() { // std::vector + check("struct Fred {\n" + " uint8_t data;\n" + " Fred & operator=(const Fred &rhs) {\n" + " return *this;\n" + " }\n" + "};",true); + ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Member variable 'Fred::data' is not assigned a value in 'Fred::operator='.\n", errout_str()); + + check("struct Fred {\n" + " std::vector ints;\n" + " Fred & operator=(const Fred &rhs) {\n" + " return *this;\n" + " }\n" + "};",true); + ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Member variable 'Fred::ints' is not assigned a value in 'Fred::operator='.\n", errout_str()); + + check("struct Fred {\n" + " Data data;\n" + " Fred & operator=(const Fred &rhs) {\n" + " return *this;\n" + " }\n" + "};",true); + ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Member variable 'Fred::data' is not assigned a value in 'Fred::operator='.\n", errout_str()); + } + + void initvar_operator_eq7() { + check("struct B {\n" + " virtual void CopyImpl(const B& Src) = 0;\n" + " void Copy(const B& Src);\n" + "};\n" + "struct D : B {};\n" + "struct DD : D {\n" + " void CopyImpl(const B& Src) override;\n" + " DD& operator=(const DD& Src);\n" + " int i{};\n" + "};\n" + "DD& DD::operator=(const DD& Src) {\n" + " if (this != &Src)\n" + " Copy(Src);\n" + " return *this;\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + } + + void initvar_operator_eq8() { + check("struct B {\n" + " int b;\n" + "};\n" + "struct D1 : B {\n" + " D1& operator=(const D1& src);\n" + " int d1;\n" + "};\n" + "struct D2 : D1 {\n" + " D2& operator=(const D2& src);\n" + " int d2;\n" + "};\n" + "struct D3 : D2 {\n" + " D3& operator=(const D3& src) {\n" + " D1::operator=(src);\n" + " d3_1 = src.d3_1;\n" + " }\n" + " int d3_1;\n" + " int d3_2;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:13]: (warning) Member variable 'D3::d3_2' is not assigned a value in 'D3::operator='.\n" + "[test.cpp:13]: (warning) Member variable 'D3::d2' is not assigned a value in 'D3::operator='.\n", errout_str()); + } + + void initvar_same_classname() { + // Bug 2208157 - False positive: Uninitialized variable, same class name + + check("void func1()\n" + "{\n" + " class Fred\n" + " {\n" + " int a;\n" + " Fred() { a = 0; }\n" + " };\n" + "}\n" + "\n" + "void func2()\n" + "{\n" + " class Fred\n" + " {\n" + " int b;\n" + " Fred() { b = 0; }\n" + " };\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + + check("void func1()\n" + "{\n" + " struct Fred\n" + " {\n" + " int a;\n" + " Fred() { a = 0; }\n" + " };\n" + "}\n" + "\n" + "void func2()\n" + "{\n" + " class Fred\n" + " {\n" + " int b;\n" + " Fred() { b = 0; }\n" + " };\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + + check("void func1()\n" + "{\n" + " struct Fred\n" + " {\n" + " int a;\n" + " Fred() { a = 0; }\n" + " };\n" + "}\n" + "\n" + "void func2()\n" + "{\n" + " struct Fred\n" + " {\n" + " int b;\n" + " Fred() { b = 0; }\n" + " };\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + + check("class Foo {\n" + " void func1()\n" + " {\n" + " struct Fred\n" + " {\n" + " int a;\n" + " Fred() { a = 0; }\n" + " };\n" + " }\n" + "\n" + " void func2()\n" + " {\n" + " struct Fred\n" + " {\n" + " int b;\n" + " Fred() { b = 0; }\n" + " };\n" + " }\n" + "};"); + + ASSERT_EQUALS("", errout_str()); + + check("class Foo {\n" + " void func1()\n" + " {\n" + " struct Fred\n" + " {\n" + " int a;\n" + " Fred() { }\n" + " };\n" + " }\n" + "\n" + " void func2()\n" + " {\n" + " struct Fred\n" + " {\n" + " int b;\n" + " Fred() { }\n" + " };\n" + " }\n" + "};"); + + ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Fred::a' is not initialized in the constructor.\n" + "[test.cpp:16]: (warning) Member variable 'Fred::b' is not initialized in the constructor.\n", errout_str()); + } + + void initvar_chained_assign() { + // Bug 2270433 - Uninitialized variable false positive on chained assigns + + check("struct c\n" + "{\n" + " c();\n" + "\n" + " int m_iMyInt1;\n" + " int m_iMyInt2;\n" + "}\n" + "\n" + "c::c()\n" + "{\n" + " m_iMyInt1 = m_iMyInt2 = 0;\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + } + + + void initvar_2constructors() { + check("struct c\n" + "{\n" + " c();\n" + " explicit c(bool b);" + "\n" + " void InitInt();\n" + "\n" + " int m_iMyInt;\n" + " int m_bMyBool;\n" + "}\n" + "\n" + "c::c()\n" + "{\n" + " m_bMyBool = false;\n" + " InitInt();" + "}\n" + "\n" + "c::c(bool b)\n" + "{\n" + " m_bMyBool = b;\n" + " InitInt();\n" + "}\n" + "\n" + "void c::InitInt()\n" + "{\n" + " m_iMyInt = 0;\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + } + + + void initvar_constvar() { + check("struct Fred\n" + "{\n" + " const char *s;\n" + " Fred();\n" + "};\n" + "Fred::Fred() : s(NULL)\n" + "{ }"); + ASSERT_EQUALS("", errout_str()); + + check("struct Fred\n" + "{\n" + " const char *s;\n" + " Fred();\n" + "};\n" + "Fred::Fred()\n" + "{ s = NULL; }"); + ASSERT_EQUALS("", errout_str()); + + check("struct Fred\n" + "{\n" + " const char *s;\n" + " Fred();\n" + "};\n" + "Fred::Fred()\n" + "{ }"); + ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'Fred::s' is not initialized in the constructor.\n", errout_str()); + } + + + void initvar_mutablevar() { + check("class Foo {\n" + "public:\n" + " Foo() { update(); }\n" + "private:\n" + " void update() const;\n" + " mutable int x;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + + void initvar_staticvar() { + check("class Fred\n" + "{\n" + "public:\n" + " Fred() { }\n" + " static void *p;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + + void initvar_brace_init() { // #10142 + check("class C\n" + "{\n" + "public:\n" + " C() {}\n" + "\n" + "private:\n" + " std::map * values_{};\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + + void initvar_union() { + check("class Fred\n" + "{\n" + " union\n" + " {\n" + " int a;\n" + " char b[4];\n" + " } U;\n" + "public:\n" + " Fred()\n" + " {\n" + " U.a = 0;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class Fred\n" + "{\n" + " union\n" + " {\n" + " int a;\n" + " char b[4];\n" + " } U;\n" + "public:\n" + " Fred()\n" + " {\n" + " }\n" + "};"); + TODO_ASSERT_EQUALS("[test.cpp:9]: (warning) Member variable 'Fred::U' is not initialized in the constructor.\n", "", errout_str()); + } + + + void initvar_delegate() { + check("class A {\n" + " int number;\n" + "public:\n" + " A(int n) { }\n" + " A() : A(42) {}\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::number' is not initialized in the constructor.\n" + "[test.cpp:5]: (warning) Member variable 'A::number' is not initialized in the constructor.\n", errout_str()); + + check("class A {\n" + " int number;\n" + "public:\n" + " A(int n) { number = n; }\n" + " A() : A(42) {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class A {\n" + " int number;\n" + "public:\n" + " A(int n) : A() { }\n" + " A() {}\n" + "};", true); + ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::number' is not initialized in the constructor.\n" + "[test.cpp:5]: (warning, inconclusive) Member variable 'A::number' is not initialized in the constructor.\n", errout_str()); + + check("class A {\n" + " int number;\n" + "public:\n" + " A(int n) : A() { }\n" + " A() { number = 42; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class A {\n" + " int number;\n" + "public:\n" + " A(int n) { }\n" + " A() : A{42} {}\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::number' is not initialized in the constructor.\n" + "[test.cpp:5]: (warning) Member variable 'A::number' is not initialized in the constructor.\n", errout_str()); + + check("class A {\n" + " int number;\n" + "public:\n" + " A(int n) { number = n; }\n" + " A() : A{42} {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class A {\n" + " int number;\n" + "public:\n" + " A(int n) : A{} { }\n" + " A() {}\n" + "};", true); + ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'A::number' is not initialized in the constructor.\n" + "[test.cpp:5]: (warning, inconclusive) Member variable 'A::number' is not initialized in the constructor.\n", errout_str()); + + check("class A {\n" + " int number;\n" + "public:\n" + " A(int n) : A{} { }\n" + " A() { number = 42; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // Ticket #6675 + check("struct Foo {\n" + " Foo();\n" + " Foo(int foo);\n" + " int foo_;\n" + "};\n" + "Foo::Foo() : Foo(0) {}\n" + "Foo::Foo(int foo) : foo_(foo) {}"); + ASSERT_EQUALS("", errout_str()); + + // Noexcept ctors + check("class A {\n" + "private:\n" + " int _a;\n" + "public:\n" + " A(const int a) noexcept : _a{a} {}\n" + " A() noexcept;\n" + "};\n" + "\n" + "A::A() noexcept: A(0) {}"); + ASSERT_EQUALS("", errout_str()); + + // Ticket #8581 + check("class A {\n" + "private:\n" + " int _a;\n" + "public:\n" + " A(int a) : _a(a) {}\n" + " A(float a) : A(int(a)) {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // Ticket #8258 + check("struct F{};\n" + "struct Foo {\n" + " Foo(int a, F&& f, int b = 21) : _a(a), _b(b), _f(f) {}\n" + " Foo(int x, const char* value) : Foo(x, F(), 42) {}\n" + " Foo(int x, int* value) : Foo(x, F()) {}\n" + " int _a;\n" + " int _b;\n" + " F _f;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void initvar_delegate2() { + check("class Foo {\n" + "public:\n" + " explicit Foo(const Bar bar);\n" + " Foo(const std::string& id);\n" + " virtual ~RtpSession() { }\n" + "protected:\n" + " bool a;\n" + " uint16_t b;\n" + "};\n" + "\n" + "Foo::Foo(const Bar var)\n" + " : Foo(bar->getId())\n" + "{\n" + "}\n" + "\n" + "Foo::Foo(const std::string& id)\n" + " : a(true)\n" + " , b(0)\n" + "{\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void initvar_derived_class() { + check("class Base {\n" // #10161 + "public:\n" + " virtual void foo() = 0;\n" + " int x;\n" // <- uninitialized + "};\n" + "\n" + "class Derived: public Base {\n" + "public:\n" + " Derived() {}\n" + " void foo() override;\n" + "};"); + ASSERT_EQUALS("[test.cpp:9]: (warning) Member variable 'Base::x' is not initialized in the constructor. Maybe it should be initialized directly in the class Base?\n", errout_str()); + + check("struct A {\n" // #3462 + " char ca;\n" + " A& operator=(const A& a) {\n" + " ca = a.ca;\n" + " return *this;\n" + " }\n" + "};\n" + "struct B : public A {\n" + " char cb;\n" + " B& operator=(const B& b) {\n" + " A::operator=(b);\n" + " return *this;\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'B::cb' is not assigned a value in 'B::operator='.\n", errout_str()); + + check("struct A {\n" + " char ca;\n" + " A& operator=(const A& a) {\n" + " return *this;\n" + " }\n" + "};\n" + "struct B : public A {\n" + " char cb;\n" + " B& operator=(const B& b) {\n" + " A::operator=(b);\n" + " return *this;\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'A::ca' is not assigned a value in 'A::operator='.\n" + "[test.cpp:9]: (warning) Member variable 'B::cb' is not assigned a value in 'B::operator='.\n" + "[test.cpp:9]: (warning) Member variable 'B::ca' is not assigned a value in 'B::operator='.\n", + errout_str()); + + check("class C : B {\n" // don't crash + " virtual C& operator=(C& c);\n" + "};\n" + "class D : public C {\n" + " virtual C& operator=(C& c) { return C::operator=(c); };\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct B;\n" // don't crash + "struct D : B { D& operator=(const D&); };\n" + "struct E : D { E& operator=(const E& rhs); };\n" + "E& E::operator=(const E& rhs) {\n" + " if (this != &rhs)\n" + " D::operator=(rhs);\n" + " return *this;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("template \n" // #12128 + "struct B {\n" + " T x;\n" + "};\n" + "struct D : B {\n" + " D(double x) : B{ x } {}\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct B {\n" + " int x;\n" + "};\n" + "struct D : B {\n" + " D(int i) : B{ i } {}\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void initvar_derived_pod_struct_with_union() { + check("struct S {\n" + " union {\n" + " unsigned short all;\n" + " struct {\n" + " unsigned char flag1;\n" + " unsigned char flag2;\n" + " };\n" + " };\n" + "};\n" + "\n" + "class C : public S {\n" + "public:\n" + " C() { all = 0; tick = 0; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " union {\n" + " unsigned short all;\n" + " struct {\n" + " unsigned char flag1;\n" + " unsigned char flag2;\n" + " };\n" + " };\n" + "};\n" + "\n" + "class C : public S {\n" + "public:\n" + " C() { flag1 = flag2 = 0; tick = 0; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " union {\n" + " unsigned short all;\n" + " struct {\n" + " unsigned char flag1;\n" + " unsigned char flag2;\n" + " };\n" + " };\n" + "};\n" + "\n" + "class C : public S {\n" + "public:\n" + " C() {}\n" + "};"); + ASSERT_EQUALS("[test.cpp:13]: (warning) Member variable 'S::all' is not initialized in the constructor. Maybe it should be initialized directly in the class S?\n" + "[test.cpp:13]: (warning) Member variable 'S::flag1' is not initialized in the constructor. Maybe it should be initialized directly in the class S?\n" + "[test.cpp:13]: (warning) Member variable 'S::flag2' is not initialized in the constructor. Maybe it should be initialized directly in the class S?\n", errout_str()); + } + + void initvar_private_constructor() { + { + const Settings s = settingsBuilder(settings).cpp( Standards::CPP11).build(); + check("class Fred\n" + "{\n" + "private:\n" + " int var;\n" + " Fred();\n" + "};\n" + "Fred::Fred()\n" + "{ }", s); + ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Fred::var' is not initialized in the constructor.\n", errout_str()); + } + + { + const Settings s = settingsBuilder(settings).cpp(Standards::CPP03).build(); + check("class Fred\n" + "{\n" + "private:\n" + " int var;\n" + " Fred();\n" + "};\n" + "Fred::Fred()\n" + "{ }", s); + 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 assigned in the copy constructor. Should it be copied?\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 assigned in the copy constructor. Should it be copied?\n", errout_str()); + + check("class Baz {};\n" // #8496 + "class Bar {\n" + "public:\n" + " explicit Bar(Baz* pBaz = NULL) : i(0) {}\n" + " Bar(const Bar& bar) {}\n" + " int i;\n" + "};\n", true); + ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Bar::i' is not initialized in the copy 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 copy constructor.\n", errout_str()); + + check("class A {\n" + "public:\n" + " A();\n" + " struct B {\n" + " explicit B(int x);\n" + " struct C {\n" + " explicit C(int y);\n" + " struct D {\n" + " struct E { int e; };\n" + " struct E d;\n" + " explicit D(const E &);\n" + " };\n" + " int c;\n" + " };\n" + " int b;\n" + " };\n" + "private:\n" + " int a;\n" + " B b;\n" + "};\n" + "A::A(){}\n" + "A::B::B(int x){}\n" + "A::B::C::C(int y){}\n" + "A::B::C::D::D(const A::B::C::D::E & e){}"); + // Note that the example code is not compilable. The A constructor must + // explicitly initialize A::b. A warning for A::b is not necessary. + ASSERT_EQUALS("[test.cpp:21]: (warning) Member variable 'A::a' is not initialized in the constructor.\n" + "[test.cpp:22]: (warning) Member variable 'B::b' is not initialized in the constructor.\n" + "[test.cpp:23]: (warning) Member variable 'C::c' is not initialized in the constructor.\n" + "[test.cpp:24]: (warning) Member variable 'D::d' is not initialized in the constructor.\n", errout_str()); + } + + void initvar_nocopy1() { // ticket #2474 + check("class B\n" + "{\n" + " B (const B & Var);\n" + "};\n" + "class A\n" + "{\n" + " B m_SemVar;\n" + "public:\n" + " A(){}\n" + " A(const A&){}\n" + " A(A &&){}\n" + " const A& operator=(const A&){return *this;}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class B\n" + "{\n" + " B (B && Var);\n" + "};\n" + "class A\n" + "{\n" + " B m_SemVar;\n" + "public:\n" + " A(){}\n" + " A(const A&){}\n" + " A(A &&){}\n" + " const A& operator=(const A&){return *this;}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class B\n" + "{\n" + " B & operator= (const B & Var);\n" + "public:\n" + " B ();\n" + "};\n" + "class A\n" + "{\n" + " B m_SemVar;\n" + "public:\n" + " A(){}\n" + " A(const A&){}\n" + " A(A &&){}\n" + " const A& operator=(const A&){return *this;}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class B\n" + "{\n" + "public:\n" + " B (const B & Var);\n" + "};\n" + "class A\n" + "{\n" + " B m_SemVar;\n" + "public:\n" + " A(){}\n" + " A(const A&){}\n" + " A(A &&){}\n" + " const A& operator=(const A&){return *this;}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class A : public std::vector\n" + "{\n" + "public:\n" + " A(const A &a);\n" + "};\n" + "class B\n" + "{\n" + " A a;\n" + "public:\n" + " B(){}\n" + " B(const B&){}\n" + " B(B &&){}\n" + " const B& operator=(const B&){return *this;}\n" + "};", true); + ASSERT_EQUALS("[test.cpp:11]: (warning, inconclusive) Member variable 'B::a' is not assigned in the copy constructor. Should it be copied?\n" + "[test.cpp:12]: (warning, inconclusive) Member variable 'B::a' is not assigned in the move constructor. Should it be moved?\n" + "[test.cpp:13]: (warning, inconclusive) Member variable 'B::a' is not assigned a value in 'B::operator='.\n", + errout_str()); + + check("class B\n" + "{\n" + "public:\n" + " B (B && Var);\n" + " int data;\n" + "};\n" + "class A\n" + "{\n" + " B m_SemVar;\n" + "public:\n" + " A(){}\n" + " A(const A&){}\n" + " A(A &&){}\n" + " const A& operator=(const A&){return *this;}\n" + "};"); + ASSERT_EQUALS("[test.cpp:13]: (warning) Member variable 'A::m_SemVar' is not initialized in the move constructor.\n", errout_str()); + + check("class B\n" + "{\n" + "public:\n" + " B ();\n" + " B & operator= (const B & Var);\n" + " int data;\n" + "};\n" + "class A\n" + "{\n" + " B m_SemVar;\n" + "public:\n" + " A(){}\n" + " A(const A&){}\n" + " A(A &&){}\n" + " const A& operator=(const A&){return *this;}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class A\n" + "{\n" + " B m_SemVar;\n" + "public:\n" + " A(){}\n" + " A(const A&){}\n" + " const A& operator=(const A&){return *this;}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void initvar_nocopy2() { // ticket #2484 + check("class B\n" + "{\n" + " B (B & Var);\n" + " B & operator= (const B & Var);\n" + " int data;\n" + "};\n" + "class A\n" + "{\n" + " B m_SemVar;\n" + "public:\n" + " A(){}\n" + " A(const A&){}\n" + " const A& operator=(const A&){return *this;}\n" + "};", true); + ASSERT_EQUALS("", errout_str()); + + check("class B\n" + "{\n" + "public:\n" + " B (B & Var);\n" + " B & operator= (const B & Var);\n" + " int data;\n" + "};\n" + "class A\n" + "{\n" + " B m_SemVar;\n" + "public:\n" + " A(){}\n" + " A(const A&){}\n" + " const A& operator=(const A&){return *this;}\n" + "};", true); + ASSERT_EQUALS("[test.cpp:12]: (warning) Member variable 'A::m_SemVar' is not initialized in the constructor.\n" + "[test.cpp:13]: (warning) Member variable 'A::m_SemVar' is not initialized in the copy constructor.\n" + "[test.cpp:14]: (warning) Member variable 'A::m_SemVar' is not assigned a value in 'A::operator='.\n", errout_str()); + } + + void initvar_nocopy3() { // #3611 - unknown type is non-copyable + check("struct A {\n" + " B b;\n" + " A() {}\n" + " A(const A& rhs) {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " B b;\n" + " A() {}\n" + " A(const A& rhs) {}\n" + "};", true); + ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) Member variable 'A::b' is not assigned in the copy constructor. Should it be copied?\n", errout_str()); + } + + void initvar_nocopy4() { // #9247 + check("struct S {\n" + " S(const S & s);\n" + " void S::Set(const T& val);\n" + " void S::Set(const U& val);\n" + " T t;\n" + "};\n" + "S::S(const S& s) {\n" + " Set(s.t);\n" + "}\n" + "void S::Set(const T& val) {\n" + " t = val;\n" + "}", /*inconclusive*/ true); + ASSERT_EQUALS("", errout_str()); + } + + void initvar_with_member_function_this() { + check("struct Foo {\n" + " Foo(int m) { this->setMember(m); }\n" + " void setMember(int m) { member = m; }\n" + " int member;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void initvar_destructor() { + check("class Fred\n" + "{\n" + "private:\n" + " int var;\n" + "public:\n" + " Fred() : var(0) {}\n" + " ~Fred() {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void initvar_func_ret_func_ptr() { // ticket #4449 (segmentation fault) + check("class something {\n" + " int * ( something :: * process()) () { return 0; }\n" + " something() { process(); }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void initvar_alias() { // #6921 + check("struct S {\n" + " int a;\n" + " S() {\n" + " int& pa = a;\n" + " pa = 4;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " int a;\n" + " S() {\n" + " int* pa = &a;\n" + " *pa = 4;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " int a[2];\n" + " S() {\n" + " int* pa = a;\n" + " for (int i = 0; i < 2; i++)\n" + " *pa++ = i;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " int* a[2];\n" + " S() {\n" + " int* pa = a[1];\n" + " *pa = 0;\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'S::a' is not initialized in the constructor.\n", errout_str()); + + check("struct S {\n" + " int a;\n" + " S() {\n" + " int pa = a;\n" + " pa = 4;\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'S::a' is not initialized in the constructor.\n", errout_str()); + } + + void initvar_templateMember() { + check("template\n" + "struct Wrapper {\n" + " static void foo(int * x) {\n" + " for (int i(0); i <= n_; ++i)\n" + " x[i] = 5;\n" + " }\n" + "};\n" + "class A {\n" + "public:\n" + " static constexpr int dim = 5;\n" + " int x[dim + 1];\n" + " A() {\n" + " Wrapper::foo(x);\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void initvar_smartptr() { // #10237 + // TODO: test should probably not pass without library + const Settings s = settingsBuilder() /*.library("std.cfg")*/.build(); + check("struct S {\n" + " explicit S(const std::shared_ptr& sp) {\n" + " set(*sp);\n" + " }\n" + " double get() const {\n" + " return d;\n" + " }\n" + " void set(const S& rhs) {\n" + " d = rhs.get();\n" + " }\n" + " double d;\n" + "};", s); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" // #8485 + " explicit S(const T& rhs) { set(*rhs); }\n" + " void set(const S& v) {\n" + " d = v.d;\n" + " }\n" + " double d; \n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void operatorEqSTL() { + check("class Fred\n" + "{\n" + "private:\n" + " std::vector ints;\n" + "public:\n" + " Fred();\n" + " void operator=(const Fred &f);\n" + "};\n" + "\n" + "Fred::Fred()\n" + "{ }\n" + "\n" + "void Fred::operator=(const Fred &f)\n" + "{ }", true); + ASSERT_EQUALS("[test.cpp:13]: (warning, inconclusive) Member variable 'Fred::ints' is not assigned a value in 'Fred::operator='.\n", errout_str()); + + const Settings s = settingsBuilder().certainty(Certainty::inconclusive).severity(Severity::style).severity(Severity::warning).library("std.cfg").build(); + check("struct S {\n" + " S& operator=(const S& s) { return *this; }\n" + " std::mutex m;\n" + "};\n", s); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar1() { + check("enum ECODES\n" + "{\n" + " CODE_1 = 0,\n" + " CODE_2 = 1\n" + "};\n" + "\n" + "class Fred\n" + "{\n" + "public:\n" + " Fred() {}\n" + "\n" + "private:\n" + " ECODES _code;\n" + "};"); + + ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'Fred::_code' is not initialized in the constructor.\n", errout_str()); + + + check("class A{};\n" + "\n" + "class B : public A\n" + "{\n" + "public:\n" + " B() {}\n" + "private:\n" + " float f;\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'B::f' is not initialized in the constructor.\n", errout_str()); + + check("class C\n" + "{\n" + " FILE *fp;\n" + "\n" + "public:\n" + " explicit C(FILE *fp);\n" + "};\n" + "\n" + "C::C(FILE *fp) {\n" + " C::fp = fp;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar2() { + check("class John\n" + "{\n" + "public:\n" + " John() { (*this).i = 0; }\n" + "private:\n" + " int i;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar3() { + // No FP when struct has constructor + check("class Foo\n" + "{\n" + "public:\n" + " Foo() { }\n" + "private:\n" + " struct Bar {\n" + " Bar();\n" + " };\n" + " Bar bars[2];\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // Using struct that doesn't have constructor + check("class Foo\n" + "{\n" + "public:\n" + " Foo() { }\n" + "private:\n" + " struct Bar {\n" + " int x;\n" + " };\n" + " Bar bars[2];\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Foo::bars' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVar4() { + check("class Foo\n" + "{\n" + "public:\n" + " Foo() { bar.x = 0; }\n" + "private:\n" + " struct Bar {\n" + " int x;\n" + " };\n" + " struct Bar bar;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar5() { + check("class Foo\n" + "{\n" + "public:\n" + " Foo() { }\n" + " Foo &operator=(const Foo &)\n" + " { return *this; }\n" + " static int i;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar6() { + check("class Foo : public Bar\n" + "{\n" + "public:\n" + " explicit Foo(int i) : Bar(mi=i) { }\n" + "private:\n" + " int mi;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class Foo : public Bar\n" + "{\n" + "public:\n" + " explicit Foo(int i) : Bar{mi=i} { }\n" + "private:\n" + " int mi;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar7() { + check("class Foo {\n" + " int a;\n" + "public:\n" + " Foo() : a(0) {}\n" + " Foo& operator=(const Foo&);\n" + " void Swap(Foo& rhs);\n" + "};\n" + "\n" + "void Foo::Swap(Foo& rhs) {\n" + " std::swap(a,rhs.a);\n" + "}\n" + "\n" + "Foo& Foo::operator=(const Foo& rhs) {\n" + " Foo copy(rhs);\n" + " copy.Swap(*this);\n" + " return *this;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar8() { + check("class Foo {\n" + " int a;\n" + "public:\n" + " Foo() : a(0) {}\n" + " Foo& operator=(const Foo&);\n" + "};\n" + "\n" + "Foo& Foo::operator=(const Foo& rhs) {\n" + " if (&rhs != this)\n" + " {\n" + " }\n" + " return *this;\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (warning) Member variable 'Foo::a' is not assigned a value in 'Foo::operator='.\n", errout_str()); + } + + void uninitVar9() { // ticket #1730 + check("class Prefs {\n" + "private:\n" + " int xasd;\n" + "public:\n" + " explicit Prefs(wxSize size);\n" + "};\n" + "Prefs::Prefs(wxSize size)\n" + "{\n" + " SetMinSize( wxSize( 48,48 ) );\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Prefs::xasd' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVar10() { // ticket #1993 + check("class A {\n" + "public:\n" + " A();\n" + "private:\n" + " int var1;\n" + " int var2;\n" + "};\n" + "A::A() : var1(0) { }"); + ASSERT_EQUALS("[test.cpp:8]: (warning) Member variable 'A::var2' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVar11() { + check("class A {\n" + "public:\n" + " explicit A(int a = 0);\n" + "private:\n" + " int var;\n" + "};\n" + "A::A(int a) { }"); + ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'A::var' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVar12() { // ticket #2078 + check("class Point\n" + "{\n" + "public:\n" + " Point()\n" + " {\n" + " Point(0, 0);\n" + " }\n" + " Point(int x, int y)\n" + " : x(x), y(y)\n" + " {}\n" + " int x, y;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Point::x' is not initialized in the constructor.\n" + "[test.cpp:4]: (warning) Member variable 'Point::y' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVar13() { // ticket #1195 + check("class A {\n" + "private:\n" + " std::vector *ints;\n" + "public:\n" + " A()\n" + " {}\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'A::ints' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVar14() { // ticket #2149 + // no namespace + check("class Foo\n" + "{\n" + "public:\n" + " Foo();\n" + "private:\n" + " bool mMember;\n" + "};\n" + "Foo::Foo()\n" + "{\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout_str()); + + // single namespace + check("namespace Output\n" + "{\n" + " class Foo\n" + " {\n" + " public:\n" + " Foo();\n" + " private:\n" + " bool mMember;\n" + " };\n" + " Foo::Foo()\n" + " {\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout_str()); + + // constructor outside namespace + check("namespace Output\n" + "{\n" + " class Foo\n" + " {\n" + " public:\n" + " Foo();\n" + " private:\n" + " bool mMember;\n" + " };\n" + "}\n" + "Foo::Foo()\n" + "{\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // constructor outside namespace + check("namespace Output\n" + "{\n" + " class Foo\n" + " {\n" + " public:\n" + " Foo();\n" + " private:\n" + " bool mMember;\n" + " };\n" + "}\n" + "Output::Foo::Foo()\n" + "{\n" + "}"); + ASSERT_EQUALS("[test.cpp:11]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout_str()); + + // constructor outside namespace with using, #4792 + check("namespace Output\n" + "{\n" + " class Foo\n" + " {\n" + " public:\n" + " Foo();\n" + " private:\n" + " bool mMember;\n" + " };\n" + "}\n" + "using namespace Output;" + "Foo::Foo()\n" + "{\n" + "}"); + ASSERT_EQUALS("[test.cpp:11]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout_str()); + + // constructor in separate namespace + check("namespace Output\n" + "{\n" + " class Foo\n" + " {\n" + " public:\n" + " Foo();\n" + " private:\n" + " bool mMember;\n" + " };\n" + "}\n" + "namespace Output\n" + "{\n" + " Foo::Foo()\n" + " {\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:13]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout_str()); + + // constructor in different separate namespace + check("namespace Output\n" + "{\n" + " class Foo\n" + " {\n" + " public:\n" + " Foo();\n" + " private:\n" + " bool mMember;\n" + " };\n" + "}\n" + "namespace Input\n" + "{\n" + " Foo::Foo()\n" + " {\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // constructor in different separate namespace (won't compile) + check("namespace Output\n" + "{\n" + " class Foo\n" + " {\n" + " public:\n" + " Foo();\n" + " private:\n" + " bool mMember;\n" + " };\n" + "}\n" + "namespace Input\n" + "{\n" + " Output::Foo::Foo()\n" + " {\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // constructor in nested separate namespace + check("namespace A\n" + "{\n" + " namespace Output\n" + " {\n" + " class Foo\n" + " {\n" + " public:\n" + " Foo();\n" + " private:\n" + " bool mMember;\n" + " };\n" + " }\n" + " namespace Output\n" + " {\n" + " Foo::Foo()\n" + " {\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:15]: (warning) Member variable 'Foo::mMember' is not initialized in the constructor.\n", errout_str()); + + // constructor in nested different separate namespace + check("namespace A\n" + "{\n" + " namespace Output\n" + " {\n" + " class Foo\n" + " {\n" + " public:\n" + " Foo();\n" + " private:\n" + " bool mMember;\n" + " };\n" + " }\n" + " namespace Input\n" + " {\n" + " Foo::Foo()\n" + " {\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // constructor in nested different separate namespace + check("namespace A\n" + "{\n" + " namespace Output\n" + " {\n" + " class Foo\n" + " {\n" + " public:\n" + " Foo();\n" + " private:\n" + " bool mMember;\n" + " };\n" + " }\n" + " namespace Input\n" + " {\n" + " Output::Foo::Foo()\n" + " {\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar15() { + check("class Fred\n" + "{\n" + " int a;\n" + "public:\n" + " Fred();\n" + " ~Fred();\n" + "};\n" + "Fred::~Fred()\n" + "{\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar16() { + check("struct Foo\n" + "{\n" + " int a;\n" + " void set(int x) { a = x; }\n" + "};\n" + "class Bar\n" + "{\n" + " Foo foo;\n" + "public:\n" + " Bar()\n" + " {\n" + " foo.set(0);\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct Foo\n" + "{\n" + " int a;\n" + " void set(int x) { a = x; }\n" + "};\n" + "class Bar\n" + "{\n" + " Foo foo;\n" + "public:\n" + " Bar()\n" + " {\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'Bar::foo' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVar17() { + check("struct Foo\n" + "{\n" + " int a;\n" + "};\n" + "class Bar\n" + "{\n" + " Foo foo[10];\n" + "public:\n" + " Bar()\n" + " {\n" + " foo[0].a = 0;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct Foo\n" + "{\n" + " int a;\n" + "};\n" + "class Bar\n" + "{\n" + " Foo foo[10];\n" + "public:\n" + " Bar()\n" + " {\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:9]: (warning) Member variable 'Bar::foo' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVar18() { // ticket #2465 + check("struct Altren\n" + "{\n" + " explicit Altren(int _a = 0) : value(0) { }\n" + " int value;\n" + "};\n" + "class A\n" + "{\n" + "public:\n" + " A() { }\n" + "private:\n" + " Altren value;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar19() { // ticket #2792 + check("class mystring\n" + "{\n" + " char* m_str;\n" + " int m_len;\n" + "public:\n" + " explicit mystring(const char* str)\n" + " {\n" + " m_len = strlen(str);\n" + " m_str = (char*) malloc(m_len+1);\n" + " memcpy(m_str, str, m_len+1);\n" + " }\n" + " mystring& operator=(const mystring& copy)\n" + " {\n" + " return (*this = copy.m_str);\n" + " }\n" + " ~mystring()\n" + " {\n" + " free(m_str);\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar20() { // ticket #2867 + check("Object::MemFunc() {\n" + " class LocalClass {\n" + " public:\n" + " LocalClass() : dataLength_(0) {}\n" + " std::streamsize dataLength_;\n" + " double bitsInData_;\n" + " } obj;\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'LocalClass::bitsInData_' is not initialized in the constructor.\n", errout_str()); + + check("struct copy_protected;\n" + "Object::MemFunc() {\n" + " class LocalClass : public copy_protected {\n" + " public:\n" + " LocalClass() : copy_protected(1), dataLength_(0) {}\n" + " std::streamsize dataLength_;\n" + " double bitsInData_;\n" + " } obj;\n" + "};"); + ASSERT_EQUALS( + "[test.cpp:5]: (warning) Member variable 'LocalClass::bitsInData_' is not initialized in the constructor.\n", + errout_str()); + + check("struct copy_protected;\n" + "Object::MemFunc() {\n" + " class LocalClass : ::copy_protected {\n" + " public:\n" + " LocalClass() : copy_protected(1), dataLength_(0) {}\n" + " std::streamsize dataLength_;\n" + " double bitsInData_;\n" + " } obj;\n" + "};"); + ASSERT_EQUALS( + "[test.cpp:5]: (warning) Member variable 'LocalClass::bitsInData_' is not initialized in the constructor.\n", + errout_str()); + } + + void uninitVar21() { // ticket #2947 + check("class Fred {\n" + "private:\n" + " int a[23];\n" + "public:\n" + " Fred();\n" + "};\n" + "Fred::Fred() {\n" + " a[x::y] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar22() { // ticket #3043 + check("class Fred {\n" + " public:\n" + " Fred & operator=(const Fred &);\n" + " virtual Fred & clone(const Fred & other);\n" + " int x;\n" + "};\n" + "Fred & Fred::operator=(const Fred & other) {\n" + " return clone(other);\n" + "}\n" + "Fred & Fred::clone(const Fred & other) {\n" + " x = 0;\n" + " return *this;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class Fred {\n" + " public:\n" + " Fred & operator=(const Fred &);\n" + " virtual Fred & clone(const Fred & other);\n" + " int x;\n" + "};\n" + "Fred & Fred::operator=(const Fred & other) {\n" + " return clone(other);\n" + "}\n" + "Fred & Fred::clone(const Fred & other) {\n" + " return *this;\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Fred::x' is not assigned a value in 'Fred::operator='.\n", errout_str()); + } + + void uninitVar23() { // ticket #3702 + check("class Fred {\n" + " int x;\n" + "public:\n" + " Fred(struct A a, struct B b);\n" + " Fred(C c, struct D d);\n" + " Fred(struct E e, F f);\n" + " Fred(struct G, struct H);\n" + " Fred(I, J);\n" + "};\n" + "Fred::Fred(A a, B b) { }\n" + "Fred::Fred(struct C c, D d) { }\n" + "Fred::Fred(E e, struct F f) { }\n" + "Fred::Fred(G g, H h) { }\n" + "Fred::Fred(struct I i, struct J j) { }"); + ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n" + "[test.cpp:11]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n" + "[test.cpp:12]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n" + "[test.cpp:13]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n" + "[test.cpp:14]: (warning) Member variable 'Fred::x' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVar24() { // ticket #3190 + check("class Foo;\n" + "class Bar;\n" + "class Sub;\n" + "class Foo { class Sub; };\n" + "class Bar { class Sub; };\n" + "class Bar::Sub {\n" + " int b;\n" + "public:\n" + " Sub() { }\n" + " Sub(int);\n" + "};\n" + "Bar::Sub::Sub(int) { };\n" + "class ::Foo::Sub {\n" + " int f;\n" + "public:\n" + " ~Sub();\n" + " Sub();\n" + "};\n" + "::Foo::Sub::~Sub() { }\n" + "::Foo::Sub::Sub() { }\n" + "class Foo;\n" + "class Bar;\n" + "class Sub;\n", true); + + ASSERT_EQUALS("[test.cpp:9]: (warning, inconclusive) Member variable 'Sub::b' is not initialized in the constructor.\n" + "[test.cpp:12]: (warning) Member variable 'Sub::b' is not initialized in the constructor.\n" + "[test.cpp:20]: (warning) Member variable 'Sub::f' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVar25() { // ticket #4789 + check("struct A {\n" + " int a;\n" + " int b;\n" + " int c;\n" + " A(int x = 0, int y = 0, int z = 0);\n" + "};\n" + "A::A(int x = 0, int y = 0, int z = 0) { }\n" + "struct B {\n" + " int a;\n" + " int b;\n" + " int c;\n" + " B(int x = 0, int y = 0, int z = 0);\n" + "};\n" + "B::B(int x, int y, int z) { }\n" + "struct C {\n" + " int a;\n" + " int b;\n" + " int c;\n" + " C(int, int, int);\n" + "};\n" + "C::C(int x = 0, int y = 0, int z = 0) { }\n" + "struct D {\n" + " int a;\n" + " int b;\n" + " int c;\n" + " D(int, int, int);\n" + "};\n" + "D::D(int x, int y, int z) { }\n" + "struct E {\n" + " int a;\n" + " int b;\n" + " int c;\n" + " E(int x, int y, int z);\n" + "};\n" + "E::E(int, int, int) { }\n" + "struct F {\n" + " int a;\n" + " int b;\n" + " int c;\n" + " F(int x = 0, int y = 0, int z = 0);\n" + "};\n" + "F::F(int, int, int) { }\n", true); + ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'A::a' is not initialized in the constructor.\n" + "[test.cpp:7]: (warning) Member variable 'A::b' is not initialized in the constructor.\n" + "[test.cpp:7]: (warning) Member variable 'A::c' is not initialized in the constructor.\n" + "[test.cpp:14]: (warning) Member variable 'B::a' is not initialized in the constructor.\n" + "[test.cpp:14]: (warning) Member variable 'B::b' is not initialized in the constructor.\n" + "[test.cpp:14]: (warning) Member variable 'B::c' is not initialized in the constructor.\n" + "[test.cpp:21]: (warning) Member variable 'C::a' is not initialized in the constructor.\n" + "[test.cpp:21]: (warning) Member variable 'C::b' is not initialized in the constructor.\n" + "[test.cpp:21]: (warning) Member variable 'C::c' is not initialized in the constructor.\n" + "[test.cpp:28]: (warning) Member variable 'D::a' is not initialized in the constructor.\n" + "[test.cpp:28]: (warning) Member variable 'D::b' is not initialized in the constructor.\n" + "[test.cpp:28]: (warning) Member variable 'D::c' is not initialized in the constructor.\n" + "[test.cpp:35]: (warning) Member variable 'E::a' is not initialized in the constructor.\n" + "[test.cpp:35]: (warning) Member variable 'E::b' is not initialized in the constructor.\n" + "[test.cpp:35]: (warning) Member variable 'E::c' is not initialized in the constructor.\n" + "[test.cpp:42]: (warning) Member variable 'F::a' is not initialized in the constructor.\n" + "[test.cpp:42]: (warning) Member variable 'F::b' is not initialized in the constructor.\n" + "[test.cpp:42]: (warning) Member variable 'F::c' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVar26() { + check("class A {\n" + " int * v;\n" + " int sz;\n" + "public:\n" + " A(int s) {\n" + " v = new int [sz = s];\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar27() { + check("class A {\n" + " double d;\n" + "public:\n" + " A() {\n" + " rtl::math::setNan(&d);\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + check("class A {\n" + " double d;\n" + "public:\n" + " A() {\n" + " ::rtl::math::setNan(&d);\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar28() { + check("class Fred {\n" + " int i;\n" + " float f;\n" + "public:\n" + " Fred() {\n" + " foo(1);\n" + " foo(1.0f);\n" + " }\n" + " void foo(int a) { i = a; }\n" + " void foo(float a) { f = a; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar29() { + check("class A {\n" + " int i;\n" + "public:\n" + " A() { foo(); }\n" + " void foo() const { };\n" + " void foo() { i = 0; }\n" + "};\n" + "class B {\n" + " int i;\n" + "public:\n" + " B() { foo(); }\n" + " void foo() { i = 0; }\n" + " void foo() const { }\n" + "};\n" + "class C {\n" + " int i;\n" + "public:\n" + " C() { foo(); }\n" + " void foo() const { i = 0; }\n" + " void foo() { }\n" + "};\n" + "class D {\n" + " int i;\n" + "public:\n" + " D() { foo(); }\n" + " void foo() { }\n" + " void foo() const { i = 0; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:18]: (warning) Member variable 'C::i' is not initialized in the constructor.\n" + "[test.cpp:25]: (warning) Member variable 'D::i' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVar30() { // ticket #6417 + check("namespace NS {\n" + " class MyClass {\n" + " public:\n" + " MyClass();\n" + " ~MyClass();\n" + " private:\n" + " bool SomeVar;\n" + " };\n" + "}\n" + "using namespace NS;\n" + "MyClass::~MyClass() { }\n" + "MyClass::MyClass() : SomeVar(false) { }"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar31() { // ticket #8271 + check("void bar();\n" + "class MyClass {\n" + "public:\n" + " MyClass();\n" + " void Restart();\n" + "protected:\n" + " int m_retCode;\n" + "};\n" + "MyClass::MyClass() {\n" + " bar(),Restart();\n" + "}\n" + "void MyClass::Restart() {\n" + " m_retCode = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVar32() { // ticket #8835 + check("class Foo {\n" + " friend class Bar;\n" + " int member;\n" + "public:\n" + " Foo()\n" + " {\n" + " if (1) {}\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Foo::member' is not initialized in the constructor.\n", errout_str()); + check("class Foo {\n" + " friend class Bar;\n" + " int member;\n" + "public:\n" + " Foo()\n" + " {\n" + " while (1) {}\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Foo::member' is not initialized in the constructor.\n", errout_str()); + check("class Foo {\n" + " friend class Bar;\n" + " int member;\n" + "public:\n" + " Foo()\n" + " {\n" + " for (;;) {}\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Foo::member' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVar33() { // ticket #10295 + check("namespace app {\n" + " class B {\n" + " public:\n" + " B(void);\n" + " int x;\n" + " };\n" + "};\n" + "app::B::B(void){}"); + ASSERT_EQUALS("[test.cpp:8]: (warning) Member variable 'B::x' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVar34() { // ticket #10841 + check("struct A { void f() {} };\n" + "struct B {\n" + " B() { a->f(); }\n" + " A* a;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'B::a' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVarArray1() { + check("class John\n" + "{\n" + "public:\n" + " John() {}\n" + "\n" + "private:\n" + " char name[255];\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'John::name' is not initialized in the constructor.\n", errout_str()); + + check("class John\n" + "{\n" + "public:\n" + " John() {John::name[0] = '\\0';}\n" + "\n" + "private:\n" + " char name[255];\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class John\n" + "{\n" + "public:\n" + " John() { strcpy(name, \"\"); }\n" + "\n" + "private:\n" + " char name[255];\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class John\n" + "{\n" + "public:\n" + " John() { }\n" + "\n" + " double operator[](const unsigned long i);\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class A;\n" + "class John\n" + "{\n" + "public:\n" + " John() { }\n" + " A a[5];\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class A;\n" + "class John\n" + "{\n" + "public:\n" + " John() { }\n" + " A (*a)[5];\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'John::a' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVarArray2() { + check("class John\n" + "{\n" + "public:\n" + " John() { *name = 0; }\n" + "\n" + "private:\n" + " char name[255];\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // #5754 + check("class John\n" + "{\n" + "public:\n" + " John() {*this->name = '\\0';}\n" + "\n" + "private:\n" + " char name[255];\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVarArray3() { + check("class John\n" + "{\n" + "private:\n" + " int a[100];\n" + " int b[100];\n" + "\n" + "public:\n" + " John()\n" + " {\n" + " memset(a,0,sizeof(a));\n" + " memset(b,0,sizeof(b));\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVarArray4() { + check("class John\n" + "{\n" + "private:\n" + " int a[100];\n" + " int b[100];\n" + "\n" + "public:\n" + " John()\n" + " {\n" + " if (snprintf(a,10,\"a\")) { }\n" + " if (snprintf(b,10,\"b\")) { }\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVarArray5() { + check("class Foo\n" + "{\n" + "private:\n" + " Bar bars[10];\n" + "public:\n" + " Foo()\n" + " { }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVarArray6() { + check("class Foo\n" + "{\n" + "public:\n" + " Foo();\n" + " static const char STR[];\n" + "};\n" + "const char Foo::STR[] = \"abc\";\n" + "Foo::Foo() { }"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVarArray7() { + check("class Foo\n" + "{\n" + " int array[10];\n" + "public:\n" + " Foo() { }\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Foo::array' is not initialized in the constructor.\n", errout_str()); + + check("class Foo\n" + "{\n" + " int array[10];\n" + "public:\n" + " Foo() { memset(array, 0, sizeof(array)); }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class Foo\n" + "{\n" + " int array[10];\n" + "public:\n" + " Foo() { ::memset(array, 0, sizeof(array)); }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVarArray8() { + check("class Foo {\n" + " char a[10];\n" + "public:\n" + " Foo() { ::ZeroMemory(a); }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVarArray9() { // #6957 + check("class BaseGDL;\n" + "struct IxExprListT {\n" + "private:\n" + " BaseGDL* eArr[3];\n" + "public:\n" + " IxExprListT() {}\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'IxExprListT::eArr' is not initialized in the constructor.\n", errout_str()); + check("struct sRAIUnitDefBL {\n" + " sRAIUnitDefBL();\n" + " ~sRAIUnitDefBL();\n" + "};\n" + "struct sRAIUnitDef {\n" + " sRAIUnitDef() {}\n" + " sRAIUnitDefBL *List[35];\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (warning) Member variable 'sRAIUnitDef::List' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVarArray10() { // #11650 + const Settings s = settingsBuilder(settings).library("std.cfg").build(); + check("struct T { int j; };\n" + "struct U { int k{}; };\n" + "struct S {\n" + " std::array a;\n" + " std::array b;\n" + " std::array c;\n" + " std::array d;\n" + " std::array e;\n" + " std::array f;\n" + "S() {}\n" + "};\n", s); + + ASSERT_EQUALS("[test.cpp:10]: (warning) Member variable 'S::a' is not initialized in the constructor.\n" + "[test.cpp:10]: (warning) Member variable 'S::b' is not initialized in the constructor.\n" + "[test.cpp:10]: (warning) Member variable 'S::c' is not initialized in the constructor.\n" + "[test.cpp:10]: (warning) Member variable 'S::d' is not initialized in the constructor.\n", + errout_str()); + } + + void uninitVarArray11() { + check("class C {\n" // #12150 + "private:\n" + " int buf[10];\n" + "public:\n" + " C() {\n" + " for (int& i : buf)\n" + " i = 0;\n" + " }\n" + "};\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()); + + // non static data-member initialization + check("struct POINT\n" + "{\n" + " int x=0;\n" + " int y=0;\n" + "};\n" + "class Fred\n" + "{\n" + "private:\n" + " POINT p;\n" + "public:\n" + " Fred()\n" + " { }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVarUnion1() { + check("class Fred\n" // ticket #3196 + "{\n" + "private:\n" + " union { int a; int b; };\n" + "public:\n" + " Fred()\n" + " { a = 0; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class Fred {\n" + "private:\n" + " union { int a{}; int b; };\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()); + + // Unknown member functions and unknown static functions + check("class ABC {\n" + " static void static_base_func();\n" + " void const_base_func() const;\n" + "};\n" + "class Fred : private ABC {\n" + "public:\n" + " Fred() {\n" + " const_func();\n" + " static_func();\n" + " const_base_func();\n" + " ABC::static_base_func();\n" + " }\n" + " void const_func() const;\n" + " static void static_f();\n" + "private:\n" + " int i;\n" + "};"); + ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout_str()); + + // Unknown overloaded member functions + check("class Fred : private ABC {\n" + "public:\n" + " Fred() {\n" + " func();\n" + " }\n" + " void func() const;\n" + " void func();\n" + "private:\n" + " int i;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVarEnum1() { + check("class Fred\n" + "{\n" + "public:\n" + " enum abc {a,b,c};\n" + " Fred() {}\n" + "private:\n" + " unsigned int i;\n" + "};"); + + ASSERT_EQUALS("[test.cpp:5]: (warning) Member variable 'Fred::i' is not initialized in the constructor.\n", errout_str()); + } + + void uninitVarEnum2() { // ticket #8146 + check("enum E { E1 };\n" + "struct X { E e = E1; };\n" + "struct Y {\n" + " Y() {}\n" + " X x;\n" + "};"); + + ASSERT_EQUALS("", errout_str()); + } + + void uninitVarStream() { + check("class Foo\n" + "{\n" + "private:\n" + " int foo;\n" + "public:\n" + " explicit Foo(std::istream &in)\n" + " {\n" + " if(!(in >> foo))\n" + " throw 0;\n" + " }\n" + "};"); + + ASSERT_EQUALS("", errout_str()); + } + + void uninitVarTypedef() { + check("class Foo\n" + "{\n" + "public:\n" + " typedef int * pointer;\n" + " Foo() : a(0) {}\n" + " pointer a;\n" + "};"); + + ASSERT_EQUALS("", errout_str()); + } + + void uninitVarMemset() { + check("class Foo\n" + "{\n" + "public:\n" + " int * pointer;\n" + " Foo() { memset(this, 0, sizeof(*this)); }\n" + "};"); + + ASSERT_EQUALS("", errout_str()); + + check("class Foo\n" + "{\n" + "public:\n" + " int * pointer;\n" + " Foo() { ::memset(this, 0, sizeof(*this)); }\n" + "};"); + + ASSERT_EQUALS("", errout_str()); + + // Ticket #7068 + check("struct Foo {\n" + " int * p;\n" + " char c;\n" + " Foo() { memset(p, 0, sizeof(int)); }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Foo::c' is not initialized in the constructor.\n", errout_str()); + check("struct Foo {\n" + " int i;\n" + " char c;\n" + " Foo() { memset(&i, 0, sizeof(int)); }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'Foo::c' is not initialized in the constructor.\n", errout_str()); + check("struct Foo { int f; };\n" + "struct Bar { int b; };\n" + "struct FooBar {\n" + " FooBar() {\n" + " memset(&foo, 0, sizeof(foo));\n" + " }\n" + " Foo foo;\n" + " Bar bar;\n" + "};\n" + "int main() {\n" + " FooBar foobar;\n" + " return foobar.foo.f + foobar.bar.b;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'FooBar::bar' is not initialized in the constructor.\n", errout_str()); + check("struct Foo { int f; };\n" + "struct Bar { int b; };\n" + "struct FooBar {\n" + " FooBar() {\n" + " memset(&this->foo, 0, sizeof(this->foo));\n" + " }\n" + " Foo foo;\n" + " Bar bar;\n" + "};\n" + "int main() {\n" + " FooBar foobar;\n" + " return foobar.foo.f + foobar.bar.b;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Member variable 'FooBar::bar' is not initialized in the constructor.\n", errout_str()); + + // #7755 + check("struct A {\n" + " A() {\n" + " memset(this->data, 0, 42);\n" + " }\n" + " char data[42];\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void privateCtor1() { + { + const Settings s = settingsBuilder(settings).cpp(Standards::CPP03).build(); + check("class Foo {\n" + " int foo;\n" + " Foo() { }\n" + "};", s); + ASSERT_EQUALS("", errout_str()); + } + + { + const Settings s = settingsBuilder(settings).cpp(Standards::CPP11).build(); + check("class Foo {\n" + " int foo;\n" + " Foo() { }\n" + "};", s); + ASSERT_EQUALS("[test.cpp:3]: (warning) Member variable 'Foo::foo' is not initialized in the constructor.\n", errout_str()); + } + } + + void privateCtor2() { + check("class Foo\n" + "{\n" + "private:\n" + " int foo;\n" + " Foo() { }\n" + "public:\n" + " explicit Foo(int _i) { }\n" + "};"); + + ASSERT_EQUALS("[test.cpp:7]: (warning) Member variable 'Foo::foo' is not initialized in the constructor.\n", errout_str()); + } + + + void function() { + check("class A\n" + "{\n" + "public:\n" + " A();\n" + " int* f(int*);\n" + "};\n" + "\n" + "A::A()\n" + "{\n" + "}\n" + "\n" + "int* A::f(int* p)\n" + "{\n" + " return p;\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + } + + + // Borland C++: No FP for published pointers - they are automatically initialized + void uninitVarPublished() { + check("class Fred\n" + "{\n" + "__published:\n" + " int *i;\n" + "public:\n" + " Fred() { }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitVarInheritClassInit() { + // TODO: test should probably not pass without library + const Settings s = settingsBuilder() /*.library("vcl.cfg")*/.build(); + + check("class Fred: public TObject\n" + "{\n" + "public:\n" + " Fred() { }\n" + "private:\n" + " int x;\n" + "};", s); + 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()); + + check("struct Structure {\n" + " int C;\n" + "};\n" + "\n" + "class A {\n" + " Structure B;\n" + "public:\n" + " A() { Init( B ); };\n" + " void Init(const Structure& S) { }\n" + "};"); + ASSERT_EQUALS("[test.cpp:8]: (warning) Member variable 'A::B' is not initialized in the constructor.\n", 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()); + + check("class bar {\n" // #9887 + " int length;\n" + " bar() { length = 0; }\n" + "};\n" + "class foo {\n" + " int x;\n" + " foo() { Set(bar()); }\n" + " void Set(int num) { x = 1; }\n" + " void Set(bar num) { x = num.length; }\n" + "};\n"); + ASSERT_EQUALS("", 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 copy 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()); + + check("class Test {\n" // #8498 + "public:\n" + " Test() {}\n" + " std::map* pMap = nullptr;\n" + " std::string* pStr = nullptr;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("template \n" + "class C1 {}; \n" + "template \n" + "class C2 {};\n" + "namespace A {\n" + " template \n" + " class D1 {};\n" + " template \n" + " class D2 {};\n" + "}\n" + "class Test {\n" + "public:\n" + " Test() {}\n" + " C1* c1 = nullptr;\n" + " C2* c2 = nullptr;\n" + " A::D1* d1 = nullptr;\n" + " A::D2* d2 = nullptr;\n" + " std::map* pMap = nullptr;\n" + "};\n"); + ASSERT_EQUALS("", 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 copy 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 copy 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" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void classWithOperatorInName() { // ticket #2827 + check("class operatorX {\n" + " int mValue;\n" + "public:\n" + " operatorX() : mValue(0) {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void templateConstructor() { // ticket #7942 + check("template struct Container {\n" + " Container();\n" + " T* mElements;\n" + "};\n" + "template Container::Container() : mElements(nullptr) {}\n" + "Container intContainer;"); + ASSERT_EQUALS("", errout_str()); + } + + void typedefArray() { // ticket #5766 + check("typedef float rvec[3];\n" + "class SelectionPosition {\n" + "public:\n" + " SelectionPosition() {}\n" + " const rvec &x() const;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitAssignmentWithOperator() { + check("struct C {\n" + " int x;\n" + " C() {\n" + " bool b = false;\n" + " b = b && SetValue();\n" + " }\n" + " bool SetValue() {\n" + " x = 1;\n" + " return true;\n" + " }\n" + "};", true); + TODO_ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Member variable 'C::x' is not initialized in the constructor.\n", + "[test.cpp:3]: (warning) Member variable 'C::x' is not initialized in the constructor.\n", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " bool b = false;\n" + " b = true || SetValue();\n" + " }\n" + " bool SetValue() {\n" + " x = 1;\n" + " return true;\n" + " }\n" + "};", true); + TODO_ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Member variable 'C::x' is not initialized in the constructor.\n", + "[test.cpp:3]: (warning) Member variable 'C::x' is not initialized in the constructor.\n", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " bool b = true;\n" + " b = b & SetValue();\n" + " }\n" + " bool SetValue() {\n" + " x = 1;\n" + " return true;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " bool b = false;\n" + " b = true | SetValue();\n" + " }\n" + " bool SetValue() {\n" + " x = 1;\n" + " return true;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " int i = 0;\n" + " i = i * SetValue();\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " int i = 0;\n" + " i = i / SetValue();\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " int i = 0;\n" + " i = i % SetValue();\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " int i = 0;\n" + " i = i + SetValue();\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " int i = 0;\n" + " i = i - SetValue();\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " int i = 0;\n" + " i = i << SetValue();\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " int i = 0;\n" + " i = i >> SetValue();\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " int i = 0;\n" + " i = i ^ SetValue();\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitCompoundAssignment() { + check("struct C {\n" + " int x;\n" + " C() {\n" + " bool b = true;\n" + " b &= SetValue();\n" + " }\n" + " bool SetValue() {\n" + " x = 1;\n" + " return true;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " bool b = false;\n" + " b |= SetValue();\n" + " }\n" + " bool SetValue() {\n" + " x = 1;\n" + " return true;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " int i = 0;\n" + " i *= SetValue();\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " int i = 0;\n" + " i /= SetValue();\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " int i = 0;\n" + " i %= SetValue();\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " int i = 0;\n" + " i += SetValue();\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " int i = 0;\n" + " i -= SetValue();\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " int i = 0;\n" + " i <<= SetValue();\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " int i = 0;\n" + " i >>= SetValue();\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " int i = 0;\n" + " i ^= SetValue();\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitComparisonAssignment() { + check("struct C {\n" + " int x;\n" + " C() {\n" + " bool b = true;\n" + " b = (true == SetValue());\n" + " }\n" + " bool SetValue() {\n" + " x = 1;\n" + " return true;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " bool b = false;\n" + " b |= (true != SetValue());\n" + " }\n" + " bool SetValue() {\n" + " x = 1;\n" + " return true;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " bool b = (0 < SetValue());\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " bool b = (0 <= SetValue());\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " bool b = (0 > SetValue());\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" + " int x;\n" + " C() {\n" + " bool b = (0 >= SetValue());\n" + " }\n" + " int SetValue() { return x = 1; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void uninitTemplate1() { + check("template class C;\n" + "template \n" + "class C {\n" + " public:\n" + " C() : b(0) { }\n" + " C(A* a) : b(a) { }\n" + " private:\n" + " A* b;\n" + "};\n" + "template \n" + "class C {\n" + " private:\n" + " A* b;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("template class A{};\n" + "template class B{};\n" + "template\n" + "class A> {\n" + " public:\n" + " A();\n" + " bool m_value;\n" + "};\n" + "template\n" + "A>::A() : m_value(false) {}"); + ASSERT_EQUALS("", errout_str()); + + check("template struct S;\n" // #11177 + "template <> struct S final {\n" + " explicit S(int& i);\n" + " int& m;\n" + "};\n" + "S::S(int& i) : m(i) {}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void unknownTemplateType() { + check("template class A {\n" + "private:\n" + " T m;\n" + "public:\n" + " A& operator=() { return *this; }\n" + "};\n" + "A a;"); + ASSERT_EQUALS("", errout_str()); + } + +}; + +REGISTER_TEST(TestConstructors) diff --git a/cppcheck-2.14.0/test/testcppcheck.cpp b/cppcheck-2.14.0/test/testcppcheck.cpp new file mode 100644 index 00000000..4916a924 --- /dev/null +++ b/cppcheck-2.14.0/test/testcppcheck.cpp @@ -0,0 +1,230 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "color.h" +#include "cppcheck.h" +#include "errorlogger.h" +#include "filesettings.h" +#include "fixture.h" +#include "helpers.h" +#include "settings.h" + +#include "simplecpp.h" + +#include +#include +#include + + +class TestCppcheck : public TestFixture { +public: + TestCppcheck() : TestFixture("TestCppcheck") {} + +private: + + class ErrorLogger2 : public ErrorLogger { + public: + std::list ids; + std::list errmsgs; + + private: + void reportOut(const std::string & /*outmsg*/, Color /*c*/ = Color::Reset) override {} + + void reportErr(const ErrorMessage &msg) override { + ids.push_back(msg.id); + errmsgs.push_back(msg); + } + }; + + void run() override { + TEST_CASE(getErrorMessages); + TEST_CASE(checkWithFile); + TEST_CASE(checkWithFS); + TEST_CASE(suppress_error_library); + TEST_CASE(unique_errors); + TEST_CASE(isPremiumCodingStandardId); + TEST_CASE(getDumpFileContentsRawTokens); + } + + void getErrorMessages() const { + ErrorLogger2 errorLogger; + CppCheck::getErrorMessages(errorLogger); + ASSERT(!errorLogger.ids.empty()); + + // Check if there are duplicate error ids in errorLogger.id + std::string duplicate; + for (std::list::const_iterator it = errorLogger.ids.cbegin(); + it != errorLogger.ids.cend(); + ++it) { + if (std::find(errorLogger.ids.cbegin(), it, *it) != it) { + duplicate = "Duplicate ID: " + *it; + break; + } + } + ASSERT_EQUALS("", duplicate); + + // Check for error ids from this class. + bool foundPurgedConfiguration = false; + bool foundTooManyConfigs = false; + for (const std::string & it : errorLogger.ids) { + if (it == "purgedConfiguration") + foundPurgedConfiguration = true; + else if (it == "toomanyconfigs") + foundTooManyConfigs = true; + } + ASSERT(foundPurgedConfiguration); + ASSERT(foundTooManyConfigs); + } + + void checkWithFile() const + { + ScopedFile file("test.cpp", + "int main()\n" + "{\n" + " int i = *((int*)0);\n" + " return 0;\n" + "}"); + + ErrorLogger2 errorLogger; + CppCheck cppcheck(errorLogger, false, {}); + ASSERT_EQUALS(1, cppcheck.check(file.path())); + // TODO: how to properly disable these warnings? + errorLogger.ids.erase(std::remove_if(errorLogger.ids.begin(), errorLogger.ids.end(), [](const std::string& id) { + return id == "logChecker"; + }), errorLogger.ids.end()); + ASSERT_EQUALS(1, errorLogger.ids.size()); + ASSERT_EQUALS("nullPointer", *errorLogger.ids.cbegin()); + } + + void checkWithFS() const + { + ScopedFile file("test.cpp", + "int main()\n" + "{\n" + " int i = *((int*)0);\n" + " return 0;\n" + "}"); + + ErrorLogger2 errorLogger; + CppCheck cppcheck(errorLogger, false, {}); + FileSettings fs; + fs.filename = file.path(); + ASSERT_EQUALS(1, cppcheck.check(fs)); + // TODO: how to properly disable these warnings? + errorLogger.ids.erase(std::remove_if(errorLogger.ids.begin(), errorLogger.ids.end(), [](const std::string& id) { + return id == "logChecker"; + }), errorLogger.ids.end()); + ASSERT_EQUALS(1, errorLogger.ids.size()); + ASSERT_EQUALS("nullPointer", *errorLogger.ids.cbegin()); + } + + void suppress_error_library() const + { + ScopedFile file("test.cpp", + "int main()\n" + "{\n" + " int i = *((int*)0);\n" + " return 0;\n" + "}"); + + ErrorLogger2 errorLogger; + CppCheck cppcheck(errorLogger, false, {}); + const char xmldata[] = R"()"; + const Settings s = settingsBuilder().libraryxml(xmldata, sizeof(xmldata)).build(); + cppcheck.settings() = s; + ASSERT_EQUALS(0, cppcheck.check(file.path())); + // TODO: how to properly disable these warnings? + errorLogger.ids.erase(std::remove_if(errorLogger.ids.begin(), errorLogger.ids.end(), [](const std::string& id) { + return id == "logChecker"; + }), errorLogger.ids.end()); + ASSERT_EQUALS(0, errorLogger.ids.size()); + } + + // TODO: hwo to actually get duplicated findings + void unique_errors() const + { + ScopedFile file("inc.h", + "inline void f()\n" + "{\n" + " (void)*((int*)0);\n" + "}"); + ScopedFile test_file_a("a.cpp", + "#include \"inc.h\""); + ScopedFile test_file_b("b.cpp", + "#include \"inc.h\""); + + ErrorLogger2 errorLogger; + CppCheck cppcheck(errorLogger, false, {}); + ASSERT_EQUALS(1, cppcheck.check(test_file_a.path())); + ASSERT_EQUALS(1, cppcheck.check(test_file_b.path())); + // TODO: how to properly disable these warnings? + errorLogger.errmsgs.erase(std::remove_if(errorLogger.errmsgs.begin(), errorLogger.errmsgs.end(), [](const ErrorMessage& msg) { + return msg.id == "logChecker"; + }), errorLogger.errmsgs.end()); + // the internal errorlist is cleared after each check() call + ASSERT_EQUALS(2, errorLogger.errmsgs.size()); + auto it = errorLogger.errmsgs.cbegin(); + ASSERT_EQUALS("nullPointer", it->id); + ++it; + ASSERT_EQUALS("nullPointer", it->id); + } + + void isPremiumCodingStandardId() const { + ErrorLogger2 errorLogger; + CppCheck cppcheck(errorLogger, false, {}); + + cppcheck.settings().premiumArgs = ""; + ASSERT_EQUALS(false, cppcheck.isPremiumCodingStandardId("misra-c2012-0.0")); + ASSERT_EQUALS(false, cppcheck.isPremiumCodingStandardId("misra-c2023-0.0")); + ASSERT_EQUALS(false, cppcheck.isPremiumCodingStandardId("premium-misra-c2012-0.0")); + ASSERT_EQUALS(false, cppcheck.isPremiumCodingStandardId("premium-misra-c2023-0.0")); + ASSERT_EQUALS(false, cppcheck.isPremiumCodingStandardId("premium-misra-c++2008-0.0.0")); + ASSERT_EQUALS(false, cppcheck.isPremiumCodingStandardId("premium-misra-c++2023-0.0.0")); + ASSERT_EQUALS(false, cppcheck.isPremiumCodingStandardId("premium-cert-int50-cpp")); + ASSERT_EQUALS(false, cppcheck.isPremiumCodingStandardId("premium-autosar-0-0-0")); + + cppcheck.settings().premiumArgs = "--misra-c-2012 --cert-c++-2016 --autosar"; + ASSERT_EQUALS(true, cppcheck.isPremiumCodingStandardId("misra-c2012-0.0")); + ASSERT_EQUALS(true, cppcheck.isPremiumCodingStandardId("misra-c2023-0.0")); + ASSERT_EQUALS(true, cppcheck.isPremiumCodingStandardId("premium-misra-c2012-0.0")); + ASSERT_EQUALS(true, cppcheck.isPremiumCodingStandardId("premium-misra-c2023-0.0")); + ASSERT_EQUALS(true, cppcheck.isPremiumCodingStandardId("premium-misra-c++2008-0.0.0")); + ASSERT_EQUALS(true, cppcheck.isPremiumCodingStandardId("premium-misra-c++2023-0.0.0")); + ASSERT_EQUALS(true, cppcheck.isPremiumCodingStandardId("premium-cert-int50-cpp")); + ASSERT_EQUALS(true, cppcheck.isPremiumCodingStandardId("premium-autosar-0-0-0")); + } + + void getDumpFileContentsRawTokens() const { + ErrorLogger2 errorLogger; + CppCheck cppcheck(errorLogger, false, {}); + cppcheck.settings() = settingsBuilder().build(); + cppcheck.settings().relativePaths = true; + cppcheck.settings().basePaths.emplace_back("/some/path"); + std::vector files{"/some/path/test.cpp"}; + simplecpp::TokenList tokens1(files); + const std::string expected = " \n" + " \n" + " \n"; + ASSERT_EQUALS(expected, cppcheck.getDumpFileContentsRawTokens(files, tokens1)); + } + + // TODO: test suppressions + // TODO: test all with FS +}; + +REGISTER_TEST(TestCppcheck) diff --git a/cppcheck-2.14.0/test/testerrorlogger.cpp b/cppcheck-2.14.0/test/testerrorlogger.cpp new file mode 100644 index 00000000..32349d23 --- /dev/null +++ b/cppcheck-2.14.0/test/testerrorlogger.cpp @@ -0,0 +1,531 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" +#include "cppcheck.h" +#include "errorlogger.h" +#include "errortypes.h" +#include "fixture.h" + +#include +#include +#include + +#include "xml.h" + +class TestErrorLogger : public TestFixture { +public: + TestErrorLogger() : TestFixture("TestErrorLogger") {} + +private: + const ErrorMessage::FileLocation fooCpp5{"foo.cpp", 5, 1}; + const ErrorMessage::FileLocation barCpp8{"bar.cpp", 8, 1}; + const ErrorMessage::FileLocation barCpp8_i{"bar.cpp", "ä", 8, 1}; + + void run() override { + TEST_CASE(PatternSearchReplace); + TEST_CASE(FileLocationConstruct); + TEST_CASE(FileLocationSetFile); + TEST_CASE(ErrorMessageConstruct); + TEST_CASE(ErrorMessageConstructLocations); + TEST_CASE(ErrorMessageVerbose); + TEST_CASE(ErrorMessageVerboseLocations); + TEST_CASE(ErrorMessageFromInternalError); + TEST_CASE(CustomFormat); + TEST_CASE(CustomFormat2); + TEST_CASE(CustomFormatLocations); + TEST_CASE(ToXmlV2); + TEST_CASE(ToXmlV2Locations); + TEST_CASE(ToXmlV2Encoding); + TEST_CASE(FromXmlV2); + + // Inconclusive results in xml reports.. + TEST_CASE(InconclusiveXml); + + // Serialize / Deserialize inconclusive message + TEST_CASE(SerializeInconclusiveMessage); + TEST_CASE(DeserializeInvalidInput); + TEST_CASE(SerializeSanitize); + TEST_CASE(SerializeFileLocation); + + TEST_CASE(substituteTemplateFormatStatic); + TEST_CASE(substituteTemplateLocationStatic); + + TEST_CASE(isCriticalErrorId); + } + + void TestPatternSearchReplace(const std::string& idPlaceholder, const std::string& id) const { + const std::string plainText = "text"; + + ErrorMessage message; + message.id = id; + + std::string serialized = message.toString(true, idPlaceholder + plainText + idPlaceholder); + ASSERT_EQUALS(id + plainText + id, serialized); + + serialized = message.toString(true, idPlaceholder + idPlaceholder); + ASSERT_EQUALS(id + id, serialized); + + serialized = message.toString(true, plainText + idPlaceholder + plainText); + ASSERT_EQUALS(plainText + id + plainText, serialized); + } + + void PatternSearchReplace() const { + const std::string idPlaceholder = "{id}"; + + const std::string empty; + TestPatternSearchReplace(idPlaceholder, empty); + + const std::string shortIdValue = "ID"; + ASSERT_EQUALS(true, shortIdValue.length() < idPlaceholder.length()); + TestPatternSearchReplace(idPlaceholder, shortIdValue); + + const std::string mediumIdValue = "_ID_"; + ASSERT_EQUALS(mediumIdValue.length(), idPlaceholder.length()); + TestPatternSearchReplace(idPlaceholder, mediumIdValue); + + const std::string longIdValue = "longId"; + ASSERT_EQUALS(true, longIdValue.length() > idPlaceholder.length()); + TestPatternSearchReplace(idPlaceholder, longIdValue); + } + + void FileLocationConstruct() const { + const ErrorMessage::FileLocation loc("foo.cpp", 1, 2); + ASSERT_EQUALS("foo.cpp", loc.getOrigFile()); + ASSERT_EQUALS("foo.cpp", loc.getfile()); + ASSERT_EQUALS(1, loc.line); + ASSERT_EQUALS(2, loc.column); + } + + void FileLocationSetFile() const { + ErrorMessage::FileLocation loc("foo1.cpp", 0, 0); + loc.setfile("foo.cpp"); + ASSERT_EQUALS("foo1.cpp", loc.getOrigFile()); + ASSERT_EQUALS("foo.cpp", loc.getfile()); + ASSERT_EQUALS(0, loc.line); + ASSERT_EQUALS(0, loc.column); + } + + void ErrorMessageConstruct() const { + std::list locs(1, fooCpp5); + ErrorMessage msg(std::move(locs), emptyString, Severity::error, "Programming error.", "errorId", Certainty::normal); + ASSERT_EQUALS(1, msg.callStack.size()); + ASSERT_EQUALS("Programming error.", msg.shortMessage()); + ASSERT_EQUALS("Programming error.", msg.verboseMessage()); + ASSERT_EQUALS("[foo.cpp:5]: (error) Programming error.", msg.toString(false)); + ASSERT_EQUALS("[foo.cpp:5]: (error) Programming error.", msg.toString(true)); + } + + void ErrorMessageConstructLocations() const { + std::list locs = { fooCpp5, barCpp8 }; + ErrorMessage msg(std::move(locs), emptyString, Severity::error, "Programming error.", "errorId", Certainty::normal); + ASSERT_EQUALS(2, msg.callStack.size()); + ASSERT_EQUALS("Programming error.", msg.shortMessage()); + ASSERT_EQUALS("Programming error.", msg.verboseMessage()); + ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Programming error.", msg.toString(false)); + ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Programming error.", msg.toString(true)); + } + + void ErrorMessageVerbose() const { + std::list locs(1, fooCpp5); + ErrorMessage msg(std::move(locs), emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", Certainty::normal); + ASSERT_EQUALS(1, msg.callStack.size()); + ASSERT_EQUALS("Programming error.", msg.shortMessage()); + ASSERT_EQUALS("Verbose error", msg.verboseMessage()); + ASSERT_EQUALS("[foo.cpp:5]: (error) Programming error.", msg.toString(false)); + ASSERT_EQUALS("[foo.cpp:5]: (error) Verbose error", msg.toString(true)); + } + + void ErrorMessageVerboseLocations() const { + std::list locs = { fooCpp5, barCpp8 }; + ErrorMessage msg(std::move(locs), emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", Certainty::normal); + ASSERT_EQUALS(2, msg.callStack.size()); + ASSERT_EQUALS("Programming error.", msg.shortMessage()); + ASSERT_EQUALS("Verbose error", msg.verboseMessage()); + ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Programming error.", msg.toString(false)); + ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Verbose error", msg.toString(true)); + } + + void ErrorMessageFromInternalError() const { + // TODO: test with token + { + InternalError internalError(nullptr, "message", InternalError::INTERNAL); + const auto msg = ErrorMessage::fromInternalError(internalError, nullptr, "file.c"); + ASSERT_EQUALS(1, msg.callStack.size()); + const auto &loc = *msg.callStack.cbegin(); + ASSERT_EQUALS(0, loc.fileIndex); + ASSERT_EQUALS(0, loc.line); + ASSERT_EQUALS(0, loc.column); + ASSERT_EQUALS("message", msg.shortMessage()); + ASSERT_EQUALS("message", msg.verboseMessage()); + ASSERT_EQUALS("[file.c:0]: (error) message", msg.toString(false)); + ASSERT_EQUALS("[file.c:0]: (error) message", msg.toString(true)); + } + { + InternalError internalError(nullptr, "message", "details", InternalError::INTERNAL); + const auto msg = ErrorMessage::fromInternalError(internalError, nullptr, "file.cpp", "msg"); + ASSERT_EQUALS(1, msg.callStack.size()); + const auto &loc = *msg.callStack.cbegin(); + ASSERT_EQUALS(0, loc.fileIndex); + ASSERT_EQUALS(0, loc.line); + ASSERT_EQUALS(0, loc.column); + ASSERT_EQUALS("msg: message", msg.shortMessage()); + ASSERT_EQUALS("msg: message: details", msg.verboseMessage()); + ASSERT_EQUALS("[file.cpp:0]: (error) msg: message", msg.toString(false)); + ASSERT_EQUALS("[file.cpp:0]: (error) msg: message: details", msg.toString(true)); + } + } + + void CustomFormat() const { + std::list locs(1, fooCpp5); + ErrorMessage msg(std::move(locs), emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", Certainty::normal); + ASSERT_EQUALS(1, msg.callStack.size()); + ASSERT_EQUALS("Programming error.", msg.shortMessage()); + ASSERT_EQUALS("Verbose error", msg.verboseMessage()); + ASSERT_EQUALS("foo.cpp:5,error,errorId,Programming error.", msg.toString(false, "{file}:{line},{severity},{id},{message}")); + ASSERT_EQUALS("foo.cpp:5,error,errorId,Verbose error", msg.toString(true, "{file}:{line},{severity},{id},{message}")); + } + + void CustomFormat2() const { + std::list locs(1, fooCpp5); + ErrorMessage msg(std::move(locs), emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", Certainty::normal); + ASSERT_EQUALS(1, msg.callStack.size()); + ASSERT_EQUALS("Programming error.", msg.shortMessage()); + ASSERT_EQUALS("Verbose error", msg.verboseMessage()); + ASSERT_EQUALS("Programming error. - foo.cpp(5):(error,errorId)", msg.toString(false, "{message} - {file}({line}):({severity},{id})")); + ASSERT_EQUALS("Verbose error - foo.cpp(5):(error,errorId)", msg.toString(true, "{message} - {file}({line}):({severity},{id})")); + } + + void CustomFormatLocations() const { + // Check that first location from location stack is used in template + std::list locs = { fooCpp5, barCpp8 }; + ErrorMessage msg(std::move(locs), emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", Certainty::normal); + ASSERT_EQUALS(2, msg.callStack.size()); + ASSERT_EQUALS("Programming error.", msg.shortMessage()); + ASSERT_EQUALS("Verbose error", msg.verboseMessage()); + ASSERT_EQUALS("Programming error. - bar.cpp(8):(error,errorId)", msg.toString(false, "{message} - {file}({line}):({severity},{id})")); + ASSERT_EQUALS("Verbose error - bar.cpp(8):(error,errorId)", msg.toString(true, "{message} - {file}({line}):({severity},{id})")); + } + + void ToXmlV2() const { + std::list locs(1, fooCpp5); + ErrorMessage msg(std::move(locs), emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", Certainty::normal); + std::string header("\n\n"); + header += " \n "; + ASSERT_EQUALS(header, ErrorMessage::getXMLHeader("")); + ASSERT_EQUALS(" \n", ErrorMessage::getXMLFooter()); + std::string message(" \n"; + message += " \n "; + ASSERT_EQUALS(message, msg.toXML()); + } + + void ToXmlV2Locations() const { + std::list locs = { fooCpp5, barCpp8_i }; + ErrorMessage msg(std::move(locs), emptyString, Severity::error, "Programming error.\nVerbose error", "errorId", Certainty::normal); + std::string header("\n\n"); + header += " \n "; + ASSERT_EQUALS(header, ErrorMessage::getXMLHeader("")); + ASSERT_EQUALS(" \n", ErrorMessage::getXMLFooter()); + std::string message(" \n"; + message += " \n"; + message += " \n "; + ASSERT_EQUALS(message, msg.toXML()); + } + + void ToXmlV2Encoding() const { + { + std::list locs; + ErrorMessage msg(std::move(locs), emptyString, Severity::error, "Programming error.\nComparing \"\203\" with \"\003\"", "errorId", Certainty::normal); + const std::string expected(" "); + ASSERT_EQUALS(expected, msg.toXML()); + } + { + const char code1[]="äöü"; + const char code2[]="\x12\x00\x00\x01"; + ErrorMessage msg1({}, emptyString, Severity::error, std::string("Programming error.\nReading \"")+code1+"\"", "errorId", Certainty::normal); + ASSERT_EQUALS(" ", msg1.toXML()); + ErrorMessage msg2({}, emptyString, Severity::error, std::string("Programming error.\nReading \"")+code2+"\"", "errorId", Certainty::normal); + ASSERT_EQUALS(" ", msg2.toXML()); + } + } + + void FromXmlV2() const { + const char xmldata[] = "\n" + "\n" + " \n" + " \n" + ""; + tinyxml2::XMLDocument doc; + ASSERT(doc.Parse(xmldata, sizeof(xmldata)) == tinyxml2::XML_SUCCESS); + const auto * const rootnode = doc.FirstChildElement(); + ASSERT(rootnode); + ErrorMessage msg(doc.FirstChildElement()); + ASSERT_EQUALS("errorId", msg.id); + ASSERT_EQUALS_ENUM(Severity::error, msg.severity); + ASSERT_EQUALS(123u, msg.cwe.id); + ASSERT_EQUALS_ENUM(Certainty::inconclusive, msg.certainty); + ASSERT_EQUALS("Programming error.", msg.shortMessage()); + ASSERT_EQUALS("Verbose error", msg.verboseMessage()); + ASSERT_EQUALS(456u, msg.hash); + ASSERT_EQUALS(2u, msg.callStack.size()); + ASSERT_EQUALS("foo.cpp", msg.callStack.front().getfile()); + ASSERT_EQUALS(5, msg.callStack.front().line); + ASSERT_EQUALS(2u, msg.callStack.front().column); + ASSERT_EQUALS("bar.cpp", msg.callStack.back().getfile()); + ASSERT_EQUALS(8, msg.callStack.back().line); + ASSERT_EQUALS(1u, msg.callStack.back().column); + } + + void InconclusiveXml() const { + // Location + std::list locs(1, fooCpp5); + + // Inconclusive error message + ErrorMessage msg(std::move(locs), emptyString, Severity::error, "Programming error", "errorId", Certainty::inconclusive); + + // xml version 2 error message + ASSERT_EQUALS(" \n" + " \n" + " ", + msg.toXML()); + } + + void SerializeInconclusiveMessage() const { + // Inconclusive error message + std::list locs; + ErrorMessage msg(std::move(locs), emptyString, Severity::error, "Programming error", "errorId", Certainty::inconclusive); + msg.file0 = "test.cpp"; + + const std::string msg_str = msg.serialize(); + ASSERT_EQUALS("7 errorId" + "5 error" + "1 0" + "1 0" + "8 test.cpp" + "12 inconclusive" + "17 Programming error" + "17 Programming error" + "0 ", msg_str); + + ErrorMessage msg2; + ASSERT_NO_THROW(msg2.deserialize(msg_str)); + ASSERT_EQUALS("errorId", msg2.id); + ASSERT_EQUALS_ENUM(Severity::error, msg2.severity); + ASSERT_EQUALS("test.cpp", msg2.file0); + ASSERT_EQUALS_ENUM(Certainty::inconclusive, msg2.certainty); + ASSERT_EQUALS("Programming error", msg2.shortMessage()); + ASSERT_EQUALS("Programming error", msg2.verboseMessage()); + } + + void DeserializeInvalidInput() const { + { + // missing/invalid length + // missing separator + ErrorMessage msg; + ASSERT_THROW_INTERNAL_EQUALS(msg.deserialize("500foobar"), INTERNAL, "Internal Error: Deserialization of error message failed - invalid separator"); + } + { + // invalid length + ErrorMessage msg; + ASSERT_THROW_INTERNAL_EQUALS(msg.deserialize("foo foobar"), INTERNAL, "Internal Error: Deserialization of error message failed - invalid length"); + } + { + // mismatching length + ErrorMessage msg; + ASSERT_THROW_INTERNAL_EQUALS(msg.deserialize("8 errorId"), INTERNAL, "Internal Error: Deserialization of error message failed - premature end of data"); + } + { + // incomplete message + ErrorMessage msg; + ASSERT_THROW_INTERNAL_EQUALS(msg.deserialize("7 errorId"), INTERNAL, "Internal Error: Deserialization of error message failed - invalid length"); + } + { + // invalid CWE ID + const char str[] = "7 errorId" + "5 error" + "7 invalid" // cwe + "1 0" + "8 test.cpp" + "17 Programming error" + "17 Programming error" + "0 "; + ErrorMessage msg; + ASSERT_THROW_INTERNAL_EQUALS(msg.deserialize(str), INTERNAL, "Internal Error: Deserialization of error message failed - invalid CWE ID - not an integer"); + } + { + // invalid hash + const char str[] = "7 errorId" + "5 error" + "1 0" + "7 invalid" // hash + "8 test.cpp" + "17 Programming error" + "17 Programming error" + "0 "; + ErrorMessage msg; + ASSERT_THROW_INTERNAL_EQUALS(msg.deserialize(str), INTERNAL, "Internal Error: Deserialization of error message failed - invalid hash - not an integer"); + } + { + // out-of-range CWE ID + const char str[] = "7 errorId" + "5 error" + "5 65536" // max +1 + "1 0" + "8 test.cpp" + "17 Programming error" + "17 Programming error" + "0 "; + ErrorMessage msg; + ASSERT_THROW_INTERNAL_EQUALS(msg.deserialize(str), INTERNAL, "Internal Error: Deserialization of error message failed - invalid CWE ID - out of range (limits)"); + } + } + + void SerializeSanitize() const { + std::list locs; + ErrorMessage msg(std::move(locs), emptyString, Severity::error, std::string("Illegal character in \"foo\001bar\""), "errorId", Certainty::normal); + msg.file0 = "1.c"; + + const std::string msg_str = msg.serialize(); + ASSERT_EQUALS("7 errorId" + "5 error" + "1 0" + "1 0" + "3 1.c" + "33 Illegal character in \"foo\\001bar\"" + "33 Illegal character in \"foo\\001bar\"" + "0 ", msg_str); + + ErrorMessage msg2; + ASSERT_NO_THROW(msg2.deserialize(msg_str)); + ASSERT_EQUALS("errorId", msg2.id); + ASSERT_EQUALS_ENUM(Severity::error, msg2.severity); + ASSERT_EQUALS("1.c", msg2.file0); + ASSERT_EQUALS("Illegal character in \"foo\\001bar\"", msg2.shortMessage()); + ASSERT_EQUALS("Illegal character in \"foo\\001bar\"", msg2.verboseMessage()); + } + + void SerializeFileLocation() const { + ErrorMessage::FileLocation loc1(":/,;", "abcd:/,", 654, 33); + loc1.setfile("[]:;,()"); + + ErrorMessage msg({std::move(loc1)}, emptyString, Severity::error, "Programming error", "errorId", Certainty::inconclusive); + + const std::string msg_str = msg.serialize(); + ASSERT_EQUALS("7 errorId" + "5 error" + "1 0" + "1 0" + "0 " + "12 inconclusive" + "17 Programming error" + "17 Programming error" + "1 " + "27 654\t33\t[]:;,()\t:/,;\tabcd:/,", msg_str); + + ErrorMessage msg2; + ASSERT_NO_THROW(msg2.deserialize(msg_str)); + ASSERT_EQUALS("[]:;,()", msg2.callStack.front().getfile(false)); + ASSERT_EQUALS(":/,;", msg2.callStack.front().getOrigFile(false)); + ASSERT_EQUALS(654, msg2.callStack.front().line); + ASSERT_EQUALS(33, msg2.callStack.front().column); + ASSERT_EQUALS("abcd:/,", msg2.callStack.front().getinfo()); + } + + void substituteTemplateFormatStatic() const + { + { + std::string s; + ::substituteTemplateFormatStatic(s); + ASSERT_EQUALS("", s); + } + { + std::string s = "template{black}\\z"; + ::substituteTemplateFormatStatic(s); + ASSERT_EQUALS("template{black}\\z", s); + } + { + std::string s = "{reset}{bold}{dim}{red}{blue}{magenta}{default}\\b\\n\\r\\t"; + ::substituteTemplateFormatStatic(s); + ASSERT_EQUALS("\b\n\r\t", s); + } + { + std::string s = "\\\\n"; + ::substituteTemplateFormatStatic(s); + ASSERT_EQUALS("\\\n", s); + } + { + std::string s = "{{red}"; + ::substituteTemplateFormatStatic(s); + ASSERT_EQUALS("{", s); + } + } + + void substituteTemplateLocationStatic() const + { + { + std::string s; + ::substituteTemplateLocationStatic(s); + ASSERT_EQUALS("", s); + } + { + std::string s = "template"; + ::substituteTemplateLocationStatic(s); + ASSERT_EQUALS("template", s); + } + { + std::string s = "template{black}\\z"; + ::substituteTemplateFormatStatic(s); + ASSERT_EQUALS("template{black}\\z", s); + } + { + std::string s = "{reset}{bold}{dim}{red}{blue}{magenta}{default}\\b\\n\\r\\t"; + ::substituteTemplateFormatStatic(s); + ASSERT_EQUALS("\b\n\r\t", s); + } + { + std::string s = "\\\\n"; + ::substituteTemplateFormatStatic(s); + ASSERT_EQUALS("\\\n", s); + } + { + std::string s = "{{red}"; + ::substituteTemplateFormatStatic(s); + ASSERT_EQUALS("{", s); + } + } + + void isCriticalErrorId() const { + // It does not abort all the analysis of the file. Like "missingInclude" there can be false negatives. + ASSERT_EQUALS(false, ErrorLogger::isCriticalErrorId("misra-config")); + } +}; + +REGISTER_TEST(TestErrorLogger) diff --git a/cppcheck-2.14.0/test/testexceptionsafety.cpp b/cppcheck-2.14.0/test/testexceptionsafety.cpp new file mode 100644 index 00000000..b1c5a2b0 --- /dev/null +++ b/cppcheck-2.14.0/test/testexceptionsafety.cpp @@ -0,0 +1,451 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "settings.h" + +class TestExceptionSafety : public TestFixture { +public: + TestExceptionSafety() : TestFixture("TestExceptionSafety") {} + +private: + /*const*/ Settings settings; + + void run() override { + settings.severity.fill(); + + 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(unhandledExceptionSpecification3); + TEST_CASE(nothrowAttributeThrow); + TEST_CASE(nothrowAttributeThrow2); // #5703 + TEST_CASE(nothrowDeclspecThrow); + TEST_CASE(rethrowNoCurrentException1); + TEST_CASE(rethrowNoCurrentException2); + TEST_CASE(rethrowNoCurrentException3); + } + +#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) + void check_(const char* file, int line, const char code[], bool inconclusive = false, const Settings *s = nullptr) { + const Settings settings1 = settingsBuilder(s ? *s : settings).certainty(Certainty::inconclusive, inconclusive).build(); + + // Tokenize.. + SimpleTokenizer tokenizer(settings1, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check char variable usage.. + runChecks(tokenizer, this); + } + + void destructors() { + check("class x {\n" + " ~x() {\n" + " throw e;\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Class x is not safe, destructor throws exception\n", errout_str()); + + check("class x {\n" + " ~x();\n" + "};\n" + "x::~x() {\n" + " throw e;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Class x is not safe, destructor throws exception\n", errout_str()); + + // #3858 - throwing exception in try block in destructor. + check("class x {\n" + " ~x() {\n" + " try {\n" + " throw e;\n" + " } catch (...) {\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class x {\n" + " ~x() {\n" + " if(!std::uncaught_exception()) {\n" + " throw e;\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void deallocThrow1() { + check("int * p;\n" + "void f(int x) {\n" + " delete p;\n" + " if (x)\n" + " throw 123;\n" + " p = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Exception thrown in invalid state, 'p' points at deallocated memory.\n", errout_str()); + + check("void f() {\n" + " static int* p = foo;\n" + " delete p;\n" + " if (foo)\n" + " throw 1;\n" + " p = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Exception thrown in invalid state, 'p' points at deallocated memory.\n", errout_str()); + } + + void deallocThrow2() { + check("void f() {\n" + " int* p = 0;\n" + " delete p;\n" + " if (foo)\n" + " throw 1;\n" + " p = new int;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " static int* p = 0;\n" + " delete p;\n" + " reset(p);\n" + " throw 1;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void deallocThrow3() { + check("void f() {\n" + " static int* p = 0;\n" + " delete p;\n" + " throw 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " static int* p = 0;\n" + " delete p;\n" + " throw 1;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:4]: (warning) Exception thrown in invalid state, 'p' points at deallocated memory.\n", errout_str()); + } + + void rethrowCopy1() { + check("void f() {\n" + " try\n" + " {\n" + " foo();\n" + " }\n" + " catch(const exception& err)\n" + " {\n" + " throw err;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (style) Throwing a copy of the caught exception instead of rethrowing the original exception.\n", errout_str()); + } + + void rethrowCopy2() { + check("void f() {\n" + " try\n" + " {\n" + " foo();\n" + " }\n" + " catch(exception& err)\n" + " {\n" + " throw err;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (style) Throwing a copy of the caught exception instead of rethrowing the original exception.\n", errout_str()); + } + + void rethrowCopy3() { + check("void f() {\n" + " try {\n" + " foo();\n" + " }\n" + " catch(std::runtime_error& err) {\n" + " throw err;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (style) Throwing a copy of the caught exception instead of rethrowing the original exception.\n", errout_str()); + } + + void rethrowCopy4() { + check("void f() {\n" + " try\n" + " {\n" + " foo();\n" + " }\n" + " catch(const exception& err)\n" + " {\n" + " exception err2;\n" + " throw err2;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void rethrowCopy5() { + check("void f() {\n" + " try {\n" + " foo();\n" + " }\n" + " catch(const exception& outer) {\n" + " try {\n" + " foo(outer);\n" + " }\n" + " catch(const exception& inner) {\n" + " throw inner;\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:10]: (style) Throwing a copy of the caught exception instead of rethrowing the original exception.\n", errout_str()); + + check("void f() {\n" + " try {\n" + " foo();\n" + " }\n" + " catch(const exception& outer) {\n" + " try {\n" + " foo(outer);\n" + " }\n" + " catch(const exception& inner) {\n" + " throw outer;\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void catchExceptionByValue() { + check("void f() {\n" + " try {\n" + " bar();\n" + " }\n" + " catch( ::std::exception err) {\n" + " foo(err);\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) Exception should be caught by reference.\n", errout_str()); + + check("void f() {\n" + " try {\n" + " bar();\n" + " }\n" + " catch(const exception err) {\n" + " foo(err);\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) Exception should be caught by reference.\n", errout_str()); + + check("void f() {\n" + " try {\n" + " bar();\n" + " }\n" + " catch( ::std::exception& err) {\n" + " foo(err);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " try {\n" + " bar();\n" + " }\n" + " catch(exception* err) {\n" + " foo(err);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " try {\n" + " bar();\n" + " }\n" + " catch(const exception& err) {\n" + " foo(err);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " try {\n" + " bar();\n" + " }\n" + " catch(int err) {\n" + " foo(err);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " try {\n" + " bar();\n" + " }\n" + " catch(exception* const err) {\n" + " foo(err);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void noexceptThrow() { + check("void func1() noexcept(false) { try {} catch(...) {;} 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(); }"); + ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in function declared not to throw exceptions.\n" + "[test.cpp:3]: (error) Exception thrown in function declared not to throw exceptions.\n" + "[test.cpp:5]: (error) Exception thrown in function declared not to throw exceptions.\n", errout_str()); + + // avoid false positives + check("const char *func() noexcept { return 0; }\n" + "const char *func1() noexcept { try { throw 1; } catch(...) {} return 0; }"); + ASSERT_EQUALS("", errout_str()); + } + + void nothrowThrow() { + check("void func1() throw(int) { try {;} catch(...) { 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(); }"); + ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in function declared not to throw exceptions.\n" + "[test.cpp:4]: (error) Exception thrown in function declared not to throw exceptions.\n", errout_str()); + + // avoid false positives + check("const char *func() throw() { return 0; }"); + 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 unhandledExceptionSpecification3() { + const char code[] = "void f() const throw (std::runtime_error);\n" + "int _init() {\n" + " f();\n" + "}\n" + "int _fini() {\n" + " f();\n" + "}\n" + "int main()\n" + "{\n" + " f();\n" + "}\n"; + + check(code, true); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:1]: (style, inconclusive) Unhandled exception specification when calling function f().\n" + "[test.cpp:6] -> [test.cpp:1]: (style, inconclusive) Unhandled exception specification when calling function f().\n", errout_str()); + + const Settings s = settingsBuilder().library("gnu.cfg").build(); + check(code, true, &s); + 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(); }"); + ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in function declared not to throw exceptions.\n" + "[test.cpp:3]: (error) Exception thrown in function declared not to throw exceptions.\n", errout_str()); + + // avoid false positives + check("const char *func() __attribute((nothrow)); void func1() { return 0; }"); + ASSERT_EQUALS("", errout_str()); + } + + void nothrowAttributeThrow2() { + check("class foo {\n" + " void copyMemberValues() throw () {\n" + " copyMemberValues();\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(); }"); + ASSERT_EQUALS("[test.cpp:2]: (error) Exception thrown in function declared not to throw exceptions.\n" + "[test.cpp:3]: (error) Exception thrown in function declared not to throw exceptions.\n", errout_str()); + + // avoid false positives + check("const char *func() __attribute((nothrow)); void func1() { return 0; }"); + ASSERT_EQUALS("", errout_str()); + } + + void rethrowNoCurrentException1() { + check("void func1(const bool flag) { try{ if(!flag) throw; } catch (int&) { ; } }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Rethrowing current exception with 'throw;', it seems there is no current exception to rethrow." + " If there is no current exception this calls std::terminate(). More: https://isocpp.org/wiki/faq/exceptions#throw-without-an-object\n", errout_str()); + } + + void rethrowNoCurrentException2() { + check("void func1() { try{ ; } catch (...) { ; } throw; }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Rethrowing current exception with 'throw;', it seems there is no current exception to rethrow." + " If there is no current exception this calls std::terminate(). More: https://isocpp.org/wiki/faq/exceptions#throw-without-an-object\n", errout_str()); + } + + void rethrowNoCurrentException3() { + check("void on_error() { try { throw; } catch (const int &) { ; } catch (...) { ; } }\n" // exception dispatcher idiom + "void func2() { try{ ; } catch (const int&) { throw; } ; }\n" + "void func3() { throw 0; }"); + ASSERT_EQUALS("", errout_str()); + } +}; + +REGISTER_TEST(TestExceptionSafety) diff --git a/cppcheck-2.14.0/test/testfilelister.cpp b/cppcheck-2.14.0/test/testfilelister.cpp new file mode 100644 index 00000000..22590cb9 --- /dev/null +++ b/cppcheck-2.14.0/test/testfilelister.cpp @@ -0,0 +1,126 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "filelister.h" +#include "path.h" +#include "pathmatch.h" +#include "fixture.h" + +#include +#include +#include +#include +#include +#include +#include + +class TestFileLister : public TestFixture { +public: + TestFileLister() : TestFixture("TestFileLister") {} + +private: + void run() override { + TEST_CASE(recursiveAddFiles); + TEST_CASE(recursiveAddFilesEmptyPath); + TEST_CASE(excludeFile1); + TEST_CASE(excludeFile2); + } + + // TODO: generate file list instead + static std::string findBaseDir() { + std::string basedir; + while (!Path::isDirectory(Path::join(basedir, ".github"))) { + const std::string abspath = Path::getAbsoluteFilePath(basedir); + basedir += "../"; + // no more going up + if (Path::getAbsoluteFilePath(basedir) == abspath) + throw std::runtime_error("could not find repository root directory"); + } + return basedir; + } + + void recursiveAddFiles() const { + const std::string adddir = findBaseDir() + "."; + + // Recursively add add files.. + std::list> files; + std::vector masks; + PathMatch matcher(std::move(masks)); + std::string err = FileLister::recursiveAddFiles(files, adddir, matcher); + ASSERT_EQUALS("", err); + + ASSERT(!files.empty()); + +#ifdef _WIN32 + std::string dirprefix; + if (adddir != ".") + dirprefix = adddir + "/"; +#else + const std::string dirprefix = adddir + "/"; +#endif + + const auto find_file = [&](const std::string& name) { + return std::find_if(files.cbegin(), files.cend(), [&name](const std::pair& entry) { + return entry.first == name; + }); + }; + + // Make sure source files are added.. + ASSERT(find_file(dirprefix + "cli/main.cpp") != files.end()); + ASSERT(find_file(dirprefix + "lib/token.cpp") != files.end()); + ASSERT(find_file(dirprefix + "lib/tokenize.cpp") != files.end()); + ASSERT(find_file(dirprefix + "gui/main.cpp") != files.end()); + ASSERT(find_file(dirprefix + "test/testfilelister.cpp") != files.end()); + + // Make sure headers are not added.. + ASSERT(find_file(dirprefix + "lib/tokenize.h") == files.end()); + } + + void recursiveAddFilesEmptyPath() const { + std::list> files; + const std::string err = FileLister::recursiveAddFiles(files, "", PathMatch({})); + ASSERT_EQUALS("no path specified", err); + } + + void excludeFile1() const { + const std::string basedir = findBaseDir(); + + std::list> files; + std::vector ignored{"lib/token.cpp"}; + PathMatch matcher(ignored); + std::string err = FileLister::recursiveAddFiles(files, basedir + "lib/token.cpp", matcher); + ASSERT_EQUALS("", err); + ASSERT(files.empty()); + } + + void excludeFile2() const { + const std::string basedir = findBaseDir(); + + std::list> files; + std::vector ignored; + PathMatch matcher(ignored); + std::string err = FileLister::recursiveAddFiles(files, basedir + "lib/token.cpp", matcher); + ASSERT_EQUALS("", err); + ASSERT_EQUALS(1, files.size()); + ASSERT_EQUALS(basedir + "lib/token.cpp", files.begin()->first); + } + + // TODO: test errors +}; + +REGISTER_TEST(TestFileLister) diff --git a/cppcheck-2.14.0/test/testfunctions.cpp b/cppcheck-2.14.0/test/testfunctions.cpp new file mode 100644 index 00000000..487eefc2 --- /dev/null +++ b/cppcheck-2.14.0/test/testfunctions.cpp @@ -0,0 +1,2164 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "checkfunctions.h" +#include "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "settings.h" +#include "standards.h" + +#include + +class TestFunctions : public TestFixture { +public: + TestFunctions() : TestFixture("TestFunctions") {} + +private: + const Settings settings = settingsBuilder().severity(Severity::style).severity(Severity::warning).severity(Severity::performance).severity(Severity::portability). + certainty(Certainty::inconclusive).c(Standards::C11).cpp(Standards::CPP11).library("std.cfg").library("posix.cfg").build(); + + void run() override { + // Prohibited functions + TEST_CASE(prohibitedFunctions_posix); + TEST_CASE(prohibitedFunctions_index); + TEST_CASE(prohibitedFunctions_qt_index); // FP when using the Qt function 'index'? + TEST_CASE(prohibitedFunctions_rindex); + TEST_CASE(prohibitedFunctions_var); // no false positives for variables + TEST_CASE(prohibitedFunctions_gets); // dangerous function + TEST_CASE(prohibitedFunctions_alloca); + TEST_CASE(prohibitedFunctions_declaredFunction); // declared function ticket #3121 + TEST_CASE(prohibitedFunctions_std_gets); // test std::gets + TEST_CASE(prohibitedFunctions_multiple); // multiple use of obsolete functions + TEST_CASE(prohibitedFunctions_c_declaration); // c declared function + TEST_CASE(prohibitedFunctions_functionWithBody); // function with body + TEST_CASE(prohibitedFunctions_crypt); // Non-reentrant function + TEST_CASE(prohibitedFunctions_namespaceHandling); + + // Invalid function usage + TEST_CASE(invalidFunctionUsage1); + TEST_CASE(invalidFunctionUsageStrings); + + // Math function usage + TEST_CASE(mathfunctionCall_fmod); + TEST_CASE(mathfunctionCall_sqrt); + TEST_CASE(mathfunctionCall_log); + TEST_CASE(mathfunctionCall_acos); + TEST_CASE(mathfunctionCall_asin); + TEST_CASE(mathfunctionCall_pow); + TEST_CASE(mathfunctionCall_atan2); + TEST_CASE(mathfunctionCall_precision); + + // Ignored return value + TEST_CASE(checkIgnoredReturnValue); + TEST_CASE(checkIgnoredErrorCode); + + // memset.. + TEST_CASE(memsetZeroBytes); + TEST_CASE(memsetInvalid2ndParam); + + // missing "return" + TEST_CASE(checkMissingReturn); + + // std::move for locar variable + TEST_CASE(returnLocalStdMove1); + TEST_CASE(returnLocalStdMove2); + TEST_CASE(returnLocalStdMove3); + TEST_CASE(returnLocalStdMove4); + + TEST_CASE(returnLocalStdMove5); + + TEST_CASE(negativeMemoryAllocationSizeError); // #389 + + TEST_CASE(checkLibraryMatchFunctions); + + TEST_CASE(checkUseStandardLibrary1); + TEST_CASE(checkUseStandardLibrary2); + TEST_CASE(checkUseStandardLibrary3); + TEST_CASE(checkUseStandardLibrary4); + TEST_CASE(checkUseStandardLibrary5); + TEST_CASE(checkUseStandardLibrary6); + TEST_CASE(checkUseStandardLibrary7); + TEST_CASE(checkUseStandardLibrary8); + TEST_CASE(checkUseStandardLibrary9); + TEST_CASE(checkUseStandardLibrary10); + TEST_CASE(checkUseStandardLibrary11); + TEST_CASE(checkUseStandardLibrary12); + TEST_CASE(checkUseStandardLibrary13); + TEST_CASE(checkUseStandardLibrary14); + } + +#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) + void check_(const char* file, int line, const char code[], bool cpp = true, const Settings* settings_ = nullptr) { + if (!settings_) + settings_ = &settings; + + // Tokenize.. + SimpleTokenizer tokenizer(*settings_, *this); + ASSERT_LOC(tokenizer.tokenize(code, cpp), file, line); + + runChecks(tokenizer, this); + } + + void prohibitedFunctions_posix() { + check("void f()\n" + "{\n" + " bsd_signal(SIGABRT, SIG_IGN);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Obsolescent function 'bsd_signal' called. It is recommended to use 'sigaction' instead.\n", errout_str()); + + check("int f()\n" + "{\n" + " int bsd_signal(0);\n" + " return bsd_signal;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " struct hostent *hp;\n" + " if(!hp = gethostbyname(\"127.0.0.1\")) {\n" + " exit(1);\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Obsolescent function 'gethostbyname' called. It is recommended to use 'getaddrinfo' instead.\n", errout_str()); + + check("void f()\n" + "{\n" + " long addr;\n" + " addr = inet_addr(\"127.0.0.1\");\n" + " if(!hp = gethostbyaddr((char *) &addr, sizeof(addr), AF_INET)) {\n" + " exit(1);\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) Obsolescent function 'gethostbyaddr' called. It is recommended to use 'getnameinfo' instead.\n", errout_str()); + + check("void f()\n" + "{\n" + " usleep( 1000 );\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Obsolescent function 'usleep' called. It is recommended to use 'nanosleep' or 'setitimer' instead.\n", errout_str()); + } + + void prohibitedFunctions_index() { + check("namespace n1 {\n" + " int index(){ return 1; };\n" + "}\n" + "int main()\n" + "{\n" + " n1::index();\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::size_t f()\n" + "{\n" + " std::size_t index(0);\n" + " index++;\n" + " return index;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f()\n" + "{\n" + " return this->index();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " int index( 0 );\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("const char f()\n" + "{\n" + " const char var[6] = \"index\";\n" + " const char i = index(var, 0);\n" + " return i;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Obsolescent function 'index' called. It is recommended to use 'strchr' instead.\n", + errout_str()); + } + + void prohibitedFunctions_qt_index() { + check("void TDataModel::forceRowRefresh(int row) {\n" + " emit dataChanged(index(row, 0), index(row, columnCount() - 1));\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (style) Obsolescent function 'index' called. It is recommended to use 'strchr' instead.\n" + "[test.cpp:2]: (style) Obsolescent function 'index' called. It is recommended to use 'strchr' instead.\n", // duplicate + errout_str()); + } + + void prohibitedFunctions_rindex() { + check("void f()\n" + "{\n" + " int rindex( 0 );\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " const char var[7] = \"rindex\";\n" + " print(rindex(var, 0));\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Obsolescent function 'rindex' called. It is recommended to use 'strrchr' instead.\n", errout_str()); + } + + + void prohibitedFunctions_var() { + check("class Fred {\n" + "public:\n" + " Fred() : index(0) { }\n" + " int index;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void prohibitedFunctions_gets() { + check("void f()\n" + "{\n" + " char *x = gets(a);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n", errout_str()); + + check("void f()\n" + "{\n" + " foo(x, gets(a));\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n", errout_str()); + } + + void prohibitedFunctions_alloca() { + check("void f()\n" + "{\n" + " char *x = alloca(10);\n" + "}"); // #4382 - there are no VLAs in C++ + ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'alloca' called.\n", errout_str()); + + check("void f()\n" + "{\n" + " char *x = alloca(10);\n" + "}", false); + ASSERT_EQUALS("[test.c:3]: (warning) Obsolete function 'alloca' called. In C99 and later it is recommended to use a variable length array instead.\n", errout_str()); + + const Settings s = settingsBuilder(settings).c(Standards::C89).cpp(Standards::CPP03).build(); + check("void f()\n" + "{\n" + " char *x = alloca(10);\n" + "}", true, &s); // #4382 - there are no VLAs in C++ + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " char *x = alloca(10);\n" + "}", false, &s); // #7558 - no alternative to alloca in C89 + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " char *x = alloca(10);\n" + "}", false, &s); + ASSERT_EQUALS("", errout_str()); + } + + // ticket #3121 + void prohibitedFunctions_declaredFunction() { + check("int ftime ( int a )\n" + "{\n" + " return a;\n" + "}\n" + "int main ()\n" + "{\n" + " int b ; b = ftime ( 1 ) ;\n" + " return 0 ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + // test std::gets + void prohibitedFunctions_std_gets() { + check("void f(char * str)\n" + "{\n" + " char *x = std::gets(str);\n" + " char *y = gets(str);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n" + "[test.cpp:4]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n", errout_str()); + } + + // multiple use + void prohibitedFunctions_multiple() { + check("void f(char * str)\n" + "{\n" + " char *x = std::gets(str);\n" + " usleep( 1000 );\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n" + "[test.cpp:4]: (style) Obsolescent function 'usleep' called. It is recommended to use 'nanosleep' or 'setitimer' instead.\n", errout_str()); + } + + void prohibitedFunctions_c_declaration() { + check("char * gets ( char * c ) ;\n" + "int main ()\n" + "{\n" + " char s [ 10 ] ;\n" + " gets ( s ) ;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Obsolete function 'gets' called. It is recommended to use 'fgets' or 'gets_s' instead.\n", errout_str()); + + check("int getcontext(ucontext_t *ucp);\n" + "void f (ucontext_t *ucp)\n" + "{\n" + " getcontext ( ucp ) ;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (portability) Obsolescent function 'getcontext' called. Applications are recommended to be rewritten to use POSIX threads.\n", errout_str()); + } + + void prohibitedFunctions_functionWithBody() { + check("char * gets ( char * c ) { return c; }\n" + "int main ()\n" + "{\n" + " char s [ 10 ] ;\n" + " gets ( s ) ;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void prohibitedFunctions_crypt() { + check("void f(char *pwd)\n" + "{\n" + " char *cpwd;" + " crypt(pwd, cpwd);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function crypt() is not used.\n" + "[test.cpp:3]: (portability) Non reentrant function 'crypt' called. For threadsafe applications it is recommended to use the reentrant replacement function 'crypt_r'.\n", errout_str()); + + check("void f()\n" + "{\n" + " char *pwd = getpass(\"Password:\");" + " char *cpwd;" + " crypt(pwd, cpwd);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function crypt() is not used.\n" + "[test.cpp:3]: (portability) Non reentrant function 'crypt' called. For threadsafe applications it is recommended to use the reentrant replacement function 'crypt_r'.\n", errout_str()); + + check("int f()\n" + "{\n" + " int crypt = 0;" + " return crypt;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void prohibitedFunctions_namespaceHandling() { + check("void f()\n" + "{\n" + " time_t t = 0;" + " auto lt = 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("void f()\n" + "{\n" + " printf(\"Magic guess: %d\", getpwent());\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (portability) Non reentrant function 'getpwent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwent_r'.\n", errout_str()); + + // Pass return value + check("void 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("void f()\n" + "{\n" + " ::getpwent();\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function ::getpwent() is not used.\n" + "[test.cpp:3]: (portability) Non reentrant function 'getpwent' called. For threadsafe applications it is recommended to use the reentrant replacement function 'getpwent_r'.\n", errout_str()); + + // Be quiet on function definitions + check("int getpwent()\n" + "{\n" + " return 123;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Be quiet on other namespaces + check("void f()\n" + "{\n" + " foobar::getpwent();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Be quiet on class member functions + check("void f()\n" + "{\n" + " foobar.getpwent();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void invalidFunctionUsage1() { + check("void f() { memset(a,b,sizeof(a)!=12); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout_str()); + + check("void f() { memset(a,b,sizeof(a)!=0); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout_str()); + + check("void f() { memset(a,b,!c); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout_str()); + + // Ticket #6990 + check("void f(bool c) { memset(a,b,c); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout_str()); + check("void f() { memset(a,b,true); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout_str()); + + // Ticket #6588 (c mode) + check("void record(char* buf, int n) {\n" + " memset(buf, 0, n < 255);\n" /* KO */ + " memset(buf, 0, n < 255 ? n : 255);\n" /* OK */ + "}", false); + ASSERT_EQUALS("[test.c:2]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout_str()); + + // Ticket #6588 (c++ mode) + check("void record(char* buf, int n) {\n" + " memset(buf, 0, n < 255);\n" /* KO */ + " memset(buf, 0, n < 255 ? n : 255);\n" /* OK */ + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Invalid memset() argument nr 3. A non-boolean value is required.\n", errout_str()); + + check("int boolArgZeroIsInvalidButOneIsValid(int a, int param) {\n" + " return div(a, param > 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Invalid div() argument nr 2. The value is 0 or 1 (boolean) but the valid values are ':-1,1:'.\n", errout_str()); + + check("void boolArgZeroIsValidButOneIsInvalid(int param) {\n" + " strtol(a, b, param > 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Invalid strtol() argument nr 3. The value is 0 or 1 (boolean) but the valid values are '0,2:36'.\n", errout_str()); + + check("void f() { strtol(a,b,1); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strtol() argument nr 3. The value is 1 but the valid values are '0,2:36'.\n", errout_str()); + + check("void f() { strtol(a,b,10); }"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::vector& v) {\n" // #10754 + " int N = -1;\n" + " for (long i = 0; i < g(); i++)\n" + " N = h(N);\n" + " v.resize(N);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Invalid v.resize() argument nr 1. The value is -1 but the valid values are '0:'.\n", errout_str()); + + check("void f(std::vector& v, int N) {\n" + " if (N < -1)\n" + " return;\n" + " v.resize(N);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition 'N<-1' is redundant or v.resize() argument nr 1 can have invalid value. The value is -1 but the valid values are '0:'.\n", + errout_str()); + + check("void f(std::vector& v, int N) {\n" + " if (N == -1) {}\n" + " v.resize(N);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'N==-1' is redundant or v.resize() argument nr 1 can have invalid value. The value is -1 but the valid values are '0:'.\n", + errout_str()); + + check("void f(std::vector& v, int N, bool b) {\n" + " if (b)\n" + " N = -1;\n" + " v.resize(N);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Invalid v.resize() argument nr 1. The value is -1 but the valid values are '0:'.\n", + errout_str()); + + check("void f(std::vector& v) {\n" + " int N = -1;\n" + " v.resize(N);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid v.resize() argument nr 1. The value is -1 but the valid values are '0:'.\n", + errout_str()); + } + + void invalidFunctionUsageStrings() { + check("size_t f() { char x = 'x'; return strlen(&x); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout_str()); + + check("size_t f() { return strlen(&x); }"); + ASSERT_EQUALS("", errout_str()); + + check("size_t f(char x) { return strlen(&x); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout_str()); + + check("size_t f() { char x = '\\0'; return strlen(&x); }"); + ASSERT_EQUALS("", errout_str()); + + check("size_t f() {\n" + " char x;\n" + " if (y)\n" + " x = '\\0';\n" + " else\n" + " x = 'a';\n" + " return strlen(&x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout_str()); + + check("int f() { char x = '\\0'; return strcmp(\"Hello world\", &x); }"); + ASSERT_EQUALS("", errout_str()); + + check("int f() { char x = 'x'; return strcmp(\"Hello world\", &x); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strcmp() argument nr 2. A nul-terminated string is required.\n", errout_str()); + + check("size_t f(char x) { char * y = &x; return strlen(y); }"); + TODO_ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", "", errout_str()); + + check("size_t f(char x) { char * y = &x; char *z = y; return strlen(z); }"); + TODO_ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", "", errout_str()); + + check("size_t f() { char x = 'x'; char * y = &x; char *z = y; return strlen(z); }"); + TODO_ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", "", errout_str()); + + check("size_t f() { char x = '\\0'; char * y = &x; char *z = y; return strlen(z); }"); + ASSERT_EQUALS("", errout_str()); + + check("size_t f() { char x[] = \"Hello world\"; return strlen(x); }"); + ASSERT_EQUALS("", errout_str()); + + check("size_t f(char x[]) { return strlen(x); }"); + ASSERT_EQUALS("", errout_str()); + + check("int f(char x, char y) { return strcmp(&x, &y); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strcmp() argument nr 1. A nul-terminated string is required.\n" + "[test.cpp:1]: (error) Invalid strcmp() argument nr 2. A nul-terminated string is required.\n", errout_str()); + + check("size_t f() { char x[] = \"Hello world\"; return strlen(&x[0]); }"); + ASSERT_EQUALS("", errout_str()); + + check("size_t f() { char* x = \"Hello world\"; return strlen(&x[0]); }"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " char x;\n" + "};\n" + "size_t f() {\n" + " S s1 = {0};\n" + " S s2;\n;" + " s2.x = 'x';\n" + " size_t l1 = strlen(&s1.x);\n" + " size_t l2 = strlen(&s2.x);\n" + " return l1 + l2;\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n" + "[test.cpp:9]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout_str()); + + check("const char x = 'x'; size_t f() { return strlen(&x); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout_str()); + + check("struct someStruct {\n" + " union {\n" + " struct {\n" + " uint16_t nr;\n" + " uint8_t d[40];\n" + " } data;\n" + " char buf[42];\n" + " } x;\n" + "};\n" + "int f(struct someStruct * const tp, const int k)\n" + "{\n" + " return strcmp(&tp->x.buf[k], \"needle\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct someStruct {\n" + " char buf[42];\n" + "};\n" + "int f(struct someStruct * const tp, const int k)\n" + "{\n" + " return strcmp(&tp->buf[k], \"needle\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("const char x = 'x'; size_t f() { char y = x; return strlen(&y); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout_str()); + + check("const char x = '\\0'; size_t f() { return strlen(&x); }"); + ASSERT_EQUALS("", errout_str()); + + check("const char x = '\\0'; size_t f() { char y = x; return strlen(&y); }"); + ASSERT_EQUALS("", errout_str()); + + check("size_t f() {\n" + " char * a = \"Hello world\";\n" + " char ** b = &a;\n" + " return strlen(&b[0][0]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("size_t f() {\n" + " char ca[] = \"asdf\";\n" + " return strlen((char*) &ca);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #5225 + check("int main(void)\n" + "{\n" + " char str[80] = \"hello worl\";\n" + " char d = 'd';\n" + " strcat(str, &d);\n" + " puts(str);\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Invalid strcat() argument nr 2. A nul-terminated string is required.\n", errout_str()); + + check("FILE* f(void) {\n" + " const char fileName[1] = { \'x\' };\n" + " return fopen(fileName, \"r\"); \n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid fopen() argument nr 1. A nul-terminated string is required.\n", errout_str()); + + check("FILE* f(void) {\n" + " const char fileName[2] = { \'x\', \'y\' };\n" + " return fopen(fileName, \"r\"); \n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid fopen() argument nr 1. A nul-terminated string is required.\n", errout_str()); + + check("FILE* f(void) {\n" + " const char fileName[3] = { \'x\', \'y\' ,\'\\0\' };\n" + " return fopen(fileName, \"r\"); \n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("FILE* f(void) {\n" + " const char fileName[3] = { \'x\', \'\\0\' ,\'y\' };\n" + " return fopen(fileName, \"r\"); \n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("FILE* f(void) {\n" + " const char fileName[3] = { \'x\', \'y\' };\n" // implicit '\0' added at the end + " return fopen(fileName, \"r\"); \n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("FILE* f(void) {\n" + " const char fileName[] = { \'\\0\' };\n" + " return fopen(fileName, \"r\"); \n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("FILE* f(void) {\n" + " const char fileName[] = { \'0\' + 42 };\n" // no size is explicitly defined, no implicit '\0' is added + " return fopen(fileName, \"r\"); \n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid fopen() argument nr 1. A nul-terminated string is required.\n", errout_str()); + + check("FILE* f(void) {\n" + " const char fileName[] = { \'0\' + 42, \'x\' };\n" // no size is explicitly defined, no implicit '\0' is added + " return fopen(fileName, \"r\"); \n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid fopen() argument nr 1. A nul-terminated string is required.\n", errout_str()); + + check("FILE* f(void) {\n" + " const char fileName[2] = { \'0\' + 42 };\n" // implicitly '\0' added at the end because size is set to 2 + " return fopen(fileName, \"r\"); \n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("FILE* f(void) {\n" + " const char fileName[] = { };\n" + " return fopen(fileName, \"r\"); \n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void scanMetaTypes()\n" // don't crash + "{\n" + " QVector metaTypes;\n" + " for (int mtId = 0; mtId <= QMetaType::User; ++mtId) {\n" + " const auto name = QMetaType::typeName(mtId);\n" + " if (strstr(name, \"GammaRay::\") != name)\n" + " metaTypes.push_back(mtId);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f() {\n" + " const char c[3] = \"abc\";\n" + " return strlen(c);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid strlen() argument nr 1. A nul-terminated string is required.\n", errout_str()); + + check("int f() {\n" + " const wchar_t c[3] = L\"abc\";\n" + " return wcslen(c);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid wcslen() argument nr 1. A nul-terminated string is required.\n", errout_str()); + + check("void f(char* dest) {\n" + " char if_name[(IF_NAMESIZE > 21 ? IF_NAMESIZE : 21) + 1] = \"%\";\n" + " strcat(dest, if_name);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int f() {\n" + " const char c[3] = \"ab\\0\";\n" + " return strlen(c);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int n) {\n" // #11179 + " char s[8] = \" \";\n" + " n = (n + 1) % 100;\n" + " sprintf(s, \"lwip%02d\", n);\n" + " s[strlen(s)] = ' ';\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("size_t f() { wchar_t x = L'x'; return wcslen(&x); }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Invalid wcslen() argument nr 1. A nul-terminated string is required.\n", errout_str()); + + check("void f() { char a[10] = \"1234567890\"; puts(a); }", false); // #1770 + ASSERT_EQUALS("[test.c:1]: (error) Invalid puts() argument nr 1. A nul-terminated string is required.\n", errout_str()); + } + + void mathfunctionCall_sqrt() { + // sqrt, sqrtf, sqrtl + check("void foo()\n" + "{\n" + " std::cout << sqrt(-1) << std::endl;\n" + " std::cout << sqrtf(-1) << std::endl;\n" + " std::cout << sqrtl(-1) << std::endl;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid sqrt() argument nr 1. The value is -1 but the valid values are '0.0:'.\n" + "[test.cpp:4]: (error) Invalid sqrtf() argument nr 1. The value is -1 but the valid values are '0.0:'.\n" + "[test.cpp:5]: (error) Invalid sqrtl() argument nr 1. The value is -1 but the valid values are '0.0:'.\n", errout_str()); + + // implementation-defined behaviour for "finite values of x<0" only: + check("void foo()\n" + "{\n" + " std::cout << sqrt(-0.) << std::endl;\n" + " std::cout << sqrtf(-0.) << std::endl;\n" + " std::cout << sqrtl(-0.) << std::endl;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " std::cout << sqrt(1) << std::endl;\n" + " std::cout << sqrtf(1) << std::endl;\n" + " std::cout << sqrtl(1) << std::endl;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void mathfunctionCall_log() { + // log,log10,logf,logl,log10f,log10l,log2,log2f,log2l,log1p,log1pf,log1pl + check("void foo()\n" + "{\n" + " std::cout << log(-2) << std::endl;\n" + " std::cout << logf(-2) << std::endl;\n" + " std::cout << logl(-2) << std::endl;\n" + " std::cout << log10(-2) << std::endl;\n" + " std::cout << log10f(-2) << std::endl;\n" + " std::cout << log10l(-2) << std::endl;\n" + " std::cout << log2(-2) << std::endl;\n" + " std::cout << log2f(-2) << std::endl;\n" + " std::cout << log2l(-2) << std::endl;\n" + " std::cout << log1p(-3) << std::endl;\n" + " std::cout << log1pf(-3) << std::endl;\n" + " std::cout << log1pl(-3) << std::endl;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid log() argument nr 1. The value is -2 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:4]: (error) Invalid logf() argument nr 1. The value is -2 but the valid values are '1.4013e-45:'.\n" + "[test.cpp:5]: (error) Invalid logl() argument nr 1. The value is -2 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:6]: (error) Invalid log10() argument nr 1. The value is -2 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:7]: (error) Invalid log10f() argument nr 1. The value is -2 but the valid values are '1.4013e-45:'.\n" + "[test.cpp:8]: (error) Invalid log10l() argument nr 1. The value is -2 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:9]: (error) Invalid log2() argument nr 1. The value is -2 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:10]: (error) Invalid log2f() argument nr 1. The value is -2 but the valid values are '1.4013e-45:'.\n" + "[test.cpp:11]: (error) Invalid log2l() argument nr 1. The value is -2 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:3]: (warning) Passing value -2 to log() leads to implementation-defined result.\n" + "[test.cpp:4]: (warning) Passing value -2 to logf() leads to implementation-defined result.\n" + "[test.cpp:5]: (warning) Passing value -2 to logl() leads to implementation-defined result.\n" + "[test.cpp:6]: (warning) Passing value -2 to log10() leads to implementation-defined result.\n" + "[test.cpp:7]: (warning) Passing value -2 to log10f() leads to implementation-defined result.\n" + "[test.cpp:8]: (warning) Passing value -2 to log10l() leads to implementation-defined result.\n" + "[test.cpp:9]: (warning) Passing value -2 to log2() leads to implementation-defined result.\n" + "[test.cpp:10]: (warning) Passing value -2 to log2f() leads to implementation-defined result.\n" + "[test.cpp:11]: (warning) Passing value -2 to log2l() leads to implementation-defined result.\n" + "[test.cpp:12]: (warning) Passing value -3 to log1p() leads to implementation-defined result.\n" + "[test.cpp:13]: (warning) Passing value -3 to log1pf() leads to implementation-defined result.\n" + "[test.cpp:14]: (warning) Passing value -3 to log1pl() leads to implementation-defined result.\n", errout_str()); + + check("void foo()\n" + "{\n" + " std::cout << log(-1) << std::endl;\n" + " std::cout << logf(-1) << std::endl;\n" + " std::cout << logl(-1) << std::endl;\n" + " std::cout << log10(-1) << std::endl;\n" + " std::cout << log10f(-1) << std::endl;\n" + " std::cout << log10l(-1) << std::endl;\n" + " std::cout << log2(-1) << std::endl;\n" + " std::cout << log2f(-1) << std::endl;\n" + " std::cout << log2l(-1) << std::endl;\n" + " std::cout << log1p(-2) << std::endl;\n" + " std::cout << log1pf(-2) << std::endl;\n" + " std::cout << log1pl(-2) << std::endl;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid log() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:4]: (error) Invalid logf() argument nr 1. The value is -1 but the valid values are '1.4013e-45:'.\n" + "[test.cpp:5]: (error) Invalid logl() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:6]: (error) Invalid log10() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:7]: (error) Invalid log10f() argument nr 1. The value is -1 but the valid values are '1.4013e-45:'.\n" + "[test.cpp:8]: (error) Invalid log10l() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:9]: (error) Invalid log2() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:10]: (error) Invalid log2f() argument nr 1. The value is -1 but the valid values are '1.4013e-45:'.\n" + "[test.cpp:11]: (error) Invalid log2l() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:3]: (warning) Passing value -1 to log() leads to implementation-defined result.\n" + "[test.cpp:4]: (warning) Passing value -1 to logf() leads to implementation-defined result.\n" + "[test.cpp:5]: (warning) Passing value -1 to logl() leads to implementation-defined result.\n" + "[test.cpp:6]: (warning) Passing value -1 to log10() leads to implementation-defined result.\n" + "[test.cpp:7]: (warning) Passing value -1 to log10f() leads to implementation-defined result.\n" + "[test.cpp:8]: (warning) Passing value -1 to log10l() leads to implementation-defined result.\n" + "[test.cpp:9]: (warning) Passing value -1 to log2() leads to implementation-defined result.\n" + "[test.cpp:10]: (warning) Passing value -1 to log2f() leads to implementation-defined result.\n" + "[test.cpp:11]: (warning) Passing value -1 to log2l() leads to implementation-defined result.\n" + "[test.cpp:12]: (warning) Passing value -2 to log1p() leads to implementation-defined result.\n" + "[test.cpp:13]: (warning) Passing value -2 to log1pf() leads to implementation-defined result.\n" + "[test.cpp:14]: (warning) Passing value -2 to log1pl() leads to implementation-defined result.\n", errout_str()); + + check("void foo()\n" + "{\n" + " std::cout << log(-1.0) << std::endl;\n" + " std::cout << logf(-1.0) << std::endl;\n" + " std::cout << logl(-1.0) << std::endl;\n" + " std::cout << log10(-1.0) << std::endl;\n" + " std::cout << log10f(-1.0) << std::endl;\n" + " std::cout << log10l(-1.0) << std::endl;\n" + " std::cout << log2(-1.0) << std::endl;\n" + " std::cout << log2f(-1.0) << std::endl;\n" + " std::cout << log2l(-1.0) << std::endl;\n" + " std::cout << log1p(-2.0) << std::endl;\n" + " std::cout << log1pf(-2.0) << std::endl;\n" + " std::cout << log1pl(-2.0) << std::endl;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid log() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:4]: (error) Invalid logf() argument nr 1. The value is -1 but the valid values are '1.4013e-45:'.\n" + "[test.cpp:5]: (error) Invalid logl() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:6]: (error) Invalid log10() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:7]: (error) Invalid log10f() argument nr 1. The value is -1 but the valid values are '1.4013e-45:'.\n" + "[test.cpp:8]: (error) Invalid log10l() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:9]: (error) Invalid log2() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:10]: (error) Invalid log2f() argument nr 1. The value is -1 but the valid values are '1.4013e-45:'.\n" + "[test.cpp:11]: (error) Invalid log2l() argument nr 1. The value is -1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:3]: (warning) Passing value -1.0 to log() leads to implementation-defined result.\n" + "[test.cpp:4]: (warning) Passing value -1.0 to logf() leads to implementation-defined result.\n" + "[test.cpp:5]: (warning) Passing value -1.0 to logl() leads to implementation-defined result.\n" + "[test.cpp:6]: (warning) Passing value -1.0 to log10() leads to implementation-defined result.\n" + "[test.cpp:7]: (warning) Passing value -1.0 to log10f() leads to implementation-defined result.\n" + "[test.cpp:8]: (warning) Passing value -1.0 to log10l() leads to implementation-defined result.\n" + "[test.cpp:9]: (warning) Passing value -1.0 to log2() leads to implementation-defined result.\n" + "[test.cpp:10]: (warning) Passing value -1.0 to log2f() leads to implementation-defined result.\n" + "[test.cpp:11]: (warning) Passing value -1.0 to log2l() leads to implementation-defined result.\n" + "[test.cpp:12]: (warning) Passing value -2.0 to log1p() leads to implementation-defined result.\n" + "[test.cpp:13]: (warning) Passing value -2.0 to log1pf() leads to implementation-defined result.\n" + "[test.cpp:14]: (warning) Passing value -2.0 to log1pl() leads to implementation-defined result.\n", errout_str()); + + check("void foo()\n" + "{\n" + " std::cout << log(-0.1) << std::endl;\n" + " std::cout << logf(-0.1) << std::endl;\n" + " std::cout << logl(-0.1) << std::endl;\n" + " std::cout << log10(-0.1) << std::endl;\n" + " std::cout << log10f(-0.1) << std::endl;\n" + " std::cout << log10l(-0.1) << std::endl;\n" + " std::cout << log2(-0.1) << std::endl;\n" + " std::cout << log2f(-0.1) << std::endl;\n" + " std::cout << log2l(-0.1) << std::endl;\n" + " std::cout << log1p(-1.1) << std::endl;\n" + " std::cout << log1pf(-1.1) << std::endl;\n" + " std::cout << log1pl(-1.1) << std::endl;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid log() argument nr 1. The value is -0.1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:4]: (error) Invalid logf() argument nr 1. The value is -0.1 but the valid values are '1.4013e-45:'.\n" + "[test.cpp:5]: (error) Invalid logl() argument nr 1. The value is -0.1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:6]: (error) Invalid log10() argument nr 1. The value is -0.1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:7]: (error) Invalid log10f() argument nr 1. The value is -0.1 but the valid values are '1.4013e-45:'.\n" + "[test.cpp:8]: (error) Invalid log10l() argument nr 1. The value is -0.1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:9]: (error) Invalid log2() argument nr 1. The value is -0.1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:10]: (error) Invalid log2f() argument nr 1. The value is -0.1 but the valid values are '1.4013e-45:'.\n" + "[test.cpp:11]: (error) Invalid log2l() argument nr 1. The value is -0.1 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:3]: (warning) Passing value -0.1 to log() leads to implementation-defined result.\n" + "[test.cpp:4]: (warning) Passing value -0.1 to logf() leads to implementation-defined result.\n" + "[test.cpp:5]: (warning) Passing value -0.1 to logl() leads to implementation-defined result.\n" + "[test.cpp:6]: (warning) Passing value -0.1 to log10() leads to implementation-defined result.\n" + "[test.cpp:7]: (warning) Passing value -0.1 to log10f() leads to implementation-defined result.\n" + "[test.cpp:8]: (warning) Passing value -0.1 to log10l() leads to implementation-defined result.\n" + "[test.cpp:9]: (warning) Passing value -0.1 to log2() leads to implementation-defined result.\n" + "[test.cpp:10]: (warning) Passing value -0.1 to log2f() leads to implementation-defined result.\n" + "[test.cpp:11]: (warning) Passing value -0.1 to log2l() leads to implementation-defined result.\n" + "[test.cpp:12]: (warning) Passing value -1.1 to log1p() leads to implementation-defined result.\n" + "[test.cpp:13]: (warning) Passing value -1.1 to log1pf() leads to implementation-defined result.\n" + "[test.cpp:14]: (warning) Passing value -1.1 to log1pl() leads to implementation-defined result.\n", errout_str()); + + check("void foo()\n" + "{\n" + " std::cout << log(0) << std::endl;\n" + " std::cout << logf(0.) << std::endl;\n" + " std::cout << logl(0.0) << std::endl;\n" + " std::cout << log10(0.0) << std::endl;\n" + " std::cout << log10f(0) << std::endl;\n" + " std::cout << log10l(0.) << std::endl;\n" + " std::cout << log2(0.) << std::endl;\n" + " std::cout << log2f(0.0) << std::endl;\n" + " std::cout << log2l(0) << std::endl;\n" + " std::cout << log1p(-1.) << std::endl;\n" + " std::cout << log1pf(-1.0) << std::endl;\n" + " std::cout << log1pl(-1) << std::endl;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid log() argument nr 1. The value is 0 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:4]: (error) Invalid logf() argument nr 1. The value is 0 but the valid values are '1.4013e-45:'.\n" + "[test.cpp:5]: (error) Invalid logl() argument nr 1. The value is 0 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:6]: (error) Invalid log10() argument nr 1. The value is 0 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:7]: (error) Invalid log10f() argument nr 1. The value is 0 but the valid values are '1.4013e-45:'.\n" + "[test.cpp:8]: (error) Invalid log10l() argument nr 1. The value is 0 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:9]: (error) Invalid log2() argument nr 1. The value is 0 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:10]: (error) Invalid log2f() argument nr 1. The value is 0 but the valid values are '1.4013e-45:'.\n" + "[test.cpp:11]: (error) Invalid log2l() argument nr 1. The value is 0 but the valid values are '4.94066e-324:'.\n" + "[test.cpp:3]: (warning) Passing value 0 to log() leads to implementation-defined result.\n" + "[test.cpp:4]: (warning) Passing value 0. to logf() leads to implementation-defined result.\n" + "[test.cpp:5]: (warning) Passing value 0.0 to logl() leads to implementation-defined result.\n" + "[test.cpp:6]: (warning) Passing value 0.0 to log10() leads to implementation-defined result.\n" + "[test.cpp:7]: (warning) Passing value 0 to log10f() leads to implementation-defined result.\n" + "[test.cpp:8]: (warning) Passing value 0. to log10l() leads to implementation-defined result.\n" + "[test.cpp:9]: (warning) Passing value 0. to log2() leads to implementation-defined result.\n" + "[test.cpp:10]: (warning) Passing value 0.0 to log2f() leads to implementation-defined result.\n" + "[test.cpp:11]: (warning) Passing value 0 to log2l() leads to implementation-defined result.\n" + "[test.cpp:12]: (warning) Passing value -1. to log1p() leads to implementation-defined result.\n" + "[test.cpp:13]: (warning) Passing value -1.0 to log1pf() leads to implementation-defined result.\n" + "[test.cpp:14]: (warning) Passing value -1 to log1pl() leads to implementation-defined result.\n", errout_str()); + + check("void foo()\n" + "{\n" + " std::cout << log(1E-3) << std::endl;\n" + " std::cout << logf(1E-3) << std::endl;\n" + " std::cout << logl(1E-3) << std::endl;\n" + " std::cout << log10(1E-3) << std::endl;\n" + " std::cout << log10f(1E-3) << std::endl;\n" + " std::cout << log10l(1E-3) << std::endl;\n" + " std::cout << log2(1E-3) << std::endl;\n" + " std::cout << log2f(1E-3) << std::endl;\n" + " std::cout << log2l(1E-3) << std::endl;\n" + " std::cout << log1p(-1+1E-3) << std::endl;\n" + " std::cout << log1pf(-1+1E-3) << std::endl;\n" + " std::cout << log1pl(-1+1E-3) << std::endl;\n" + " std::cout << log(1.0E-3) << std::endl;\n" + " std::cout << logf(1.0E-3) << std::endl;\n" + " std::cout << logl(1.0E-3) << std::endl;\n" + " std::cout << log10(1.0E-3) << std::endl;\n" + " std::cout << log10f(1.0E-3) << std::endl;\n" + " std::cout << log10l(1.0E-3) << std::endl;\n" + " std::cout << log2(1.0E-3) << std::endl;\n" + " std::cout << log2f(1.0E-3) << std::endl;\n" + " std::cout << log2l(1.0E-3) << std::endl;\n" + " std::cout << log1p(-1+1.0E-3) << std::endl;\n" + " std::cout << log1pf(-1+1.0E-3) << std::endl;\n" + " std::cout << log1pl(-1+1.0E-3) << std::endl;\n" + " std::cout << log(1.0E+3) << std::endl;\n" + " std::cout << logf(1.0E+3) << std::endl;\n" + " std::cout << logl(1.0E+3) << std::endl;\n" + " std::cout << log10(1.0E+3) << std::endl;\n" + " std::cout << log10f(1.0E+3) << std::endl;\n" + " std::cout << log10l(1.0E+3) << std::endl;\n" + " std::cout << log2(1.0E+3) << std::endl;\n" + " std::cout << log2f(1.0E+3) << std::endl;\n" + " std::cout << log2l(1.0E+3) << std::endl;\n" + " std::cout << log1p(1.0E+3) << std::endl;\n" + " std::cout << log1pf(1.0E+3) << std::endl;\n" + " std::cout << log1pl(1.0E+3) << std::endl;\n" + " std::cout << log(2.0) << std::endl;\n" + " std::cout << logf(2.0) << std::endl;\n" + " std::cout << logf(2.0f) << std::endl;\n" + " std::cout << log10(2.0) << std::endl;\n" + " std::cout << log10f(2.0) << std::endl;\n" + " std::cout << log10f(2.0f) << std::endl;\n" + " std::cout << log2(2.0) << std::endl;\n" + " std::cout << log2f(2.0) << std::endl;\n" + " std::cout << log2f(2.0f) << std::endl;\n" + " std::cout << log1p(2.0) << std::endl;\n" + " std::cout << log1pf(2.0) << std::endl;\n" + " std::cout << log1pf(2.0f) << std::endl;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " std::string *log(0);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #3473 - no warning if "log" is a variable + check("Fred::Fred() : log(0) { }"); + ASSERT_EQUALS("", errout_str()); + + // #5748 + check("void f() { foo.log(0); }"); + ASSERT_EQUALS("", errout_str()); + } + + void mathfunctionCall_acos() { + // acos, acosf, acosl + check("void foo()\n" + "{\n" + " return acos(-1) \n" + " + acos(0.1) \n" + " + acos(0.0001) \n" + " + acos(0.01) \n" + " + acos(1.0E-1) \n" + " + acos(-1.0E-1) \n" + " + acos(+1.0E-1) \n" + " + acos(0.1E-1) \n" + " + acos(+0.1E-1) \n" + " + acos(-0.1E-1) \n" + " + acosf(-1) \n" + " + acosf(0.1) \n" + " + acosf(0.0001) \n" + " + acosf(0.01) \n" + " + acosf(1.0E-1) \n" + " + acosf(-1.0E-1) \n" + " + acosf(+1.0E-1) \n" + " + acosf(0.1E-1) \n" + " + acosf(+0.1E-1) \n" + " + acosf(-0.1E-1) \n" + " + acosl(-1) \n" + " + acosl(0.1) \n" + " + acosl(0.0001) \n" + " + acosl(0.01) \n" + " + acosl(1.0E-1) \n" + " + acosl(-1.0E-1) \n" + " + acosl(+1.0E-1) \n" + " + acosl(0.1E-1) \n" + " + acosl(+0.1E-1) \n" + " + acosl(-0.1E-1);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " std::cout << acos(1.1) << std::endl;\n" + " std::cout << acosf(1.1) << std::endl;\n" + " std::cout << acosl(1.1) << std::endl;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid acos() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n" + "[test.cpp:4]: (error) Invalid acosf() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n" + "[test.cpp:5]: (error) Invalid acosl() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n", errout_str()); + + check("void foo()\n" + "{\n" + " std::cout << acos(-1.1) << std::endl;\n" + " std::cout << acosf(-1.1) << std::endl;\n" + " std::cout << acosl(-1.1) << std::endl;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid acos() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n" + "[test.cpp:4]: (error) Invalid acosf() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n" + "[test.cpp:5]: (error) Invalid acosl() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n", errout_str()); + } + + void mathfunctionCall_asin() { + // asin, asinf, asinl + check("void foo()\n" + "{\n" + " return asin(1) \n" + " + asin(-1) \n" + " + asin(0.1) \n" + " + asin(0.0001) \n" + " + asin(0.01) \n" + " + asin(1.0E-1) \n" + " + asin(-1.0E-1) \n" + " + asin(+1.0E-1) \n" + " + asin(0.1E-1) \n" + " + asin(+0.1E-1) \n" + " + asin(-0.1E-1) \n" + " + asinf(1) \n" + " + asinf(-1) \n" + " + asinf(0.1) \n" + " + asinf(0.0001) \n" + " + asinf(0.01) \n" + " + asinf(1.0E-1) \n" + " + asinf(-1.0E-1) \n" + " + asinf(+1.0E-1) \n" + " + asinf(0.1E-1) \n" + " + asinf(+0.1E-1) \n" + " + asinf(-0.1E-1) \n" + " + asinl(1) \n" + " + asinl(-1) \n" + " + asinl(0.1) \n" + " + asinl(0.0001) \n" + " + asinl(0.01) \n" + " + asinl(1.0E-1) \n" + " + asinl(-1.0E-1) \n" + " + asinl(+1.0E-1) \n" + " + asinl(0.1E-1) \n" + " + asinl(+0.1E-1) \n" + " + asinl(-0.1E-1);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " std::cout << asin(1.1) << std::endl;\n" + " std::cout << asinf(1.1) << std::endl;\n" + " std::cout << asinl(1.1) << std::endl;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid asin() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n" + "[test.cpp:4]: (error) Invalid asinf() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n" + "[test.cpp:5]: (error) Invalid asinl() argument nr 1. The value is 1.1 but the valid values are '-1.0:1.0'.\n", errout_str()); + + check("void foo()\n" + "{\n" + " std::cout << asin(-1.1) << std::endl;\n" + " std::cout << asinf(-1.1) << std::endl;\n" + " std::cout << asinl(-1.1) << std::endl;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid asin() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n" + "[test.cpp:4]: (error) Invalid asinf() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n" + "[test.cpp:5]: (error) Invalid asinl() argument nr 1. The value is -1.1 but the valid values are '-1.0:1.0'.\n", errout_str()); + } + + void mathfunctionCall_pow() { + // pow, powf, powl + check("void foo()\n" + "{\n" + " std::cout << pow(0,-10) << std::endl;\n" + " std::cout << powf(0,-10) << std::endl;\n" + " std::cout << powl(0,-10) << std::endl;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Passing values 0 and -10 to pow() leads to implementation-defined result.\n" + "[test.cpp:4]: (warning) Passing values 0 and -10 to powf() leads to implementation-defined result.\n" + "[test.cpp:5]: (warning) Passing values 0 and -10 to powl() leads to implementation-defined result.\n", errout_str()); + + check("void foo()\n" + "{\n" + " std::cout << pow(0,10) << std::endl;\n" + " std::cout << powf(0,10) << std::endl;\n" + " std::cout << powl(0,10) << std::endl;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void mathfunctionCall_atan2() { + // atan2 + check("void foo()\n" + "{\n" + " std::cout << atan2(1,1) ;\n" + " std::cout << atan2(-1,-1) ;\n" + " std::cout << atan2(0.1,1) ;\n" + " std::cout << atan2(0.0001,100) ;\n" + " std::cout << atan2(0.0,1e-1) ;\n" + " std::cout << atan2(1.0E-1,-3) ;\n" + " std::cout << atan2(-1.0E-1,+2) ;\n" + " std::cout << atan2(+1.0E-1,0) ;\n" + " std::cout << atan2(0.1E-1,3) ;\n" + " std::cout << atan2(+0.1E-1,1) ;\n" + " std::cout << atan2(-0.1E-1,8) ;\n" + " std::cout << atan2f(1,1) ;\n" + " std::cout << atan2f(-1,-1) ;\n" + " std::cout << atan2f(0.1,1) ;\n" + " std::cout << atan2f(0.0001,100) ;\n" + " std::cout << atan2f(0.0,1e-1) ;\n" + " std::cout << atan2f(1.0E-1,-3) ;\n" + " std::cout << atan2f(-1.0E-1,+2) ;\n" + " std::cout << atan2f(+1.0E-1,0) ;\n" + " std::cout << atan2f(0.1E-1,3) ;\n" + " std::cout << atan2f(+0.1E-1,1) ;\n" + " std::cout << atan2f(-0.1E-1,8) ;\n" + " std::cout << atan2l(1,1) ;\n" + " std::cout << atan2l(-1,-1) ;\n" + " std::cout << atan2l(0.1,1) ;\n" + " std::cout << atan2l(0.0001,100) ;\n" + " std::cout << atan2l(0.0,1e-1) ;\n" + " std::cout << atan2l(1.0E-1,-3) ;\n" + " std::cout << atan2l(-1.0E-1,+2) ;\n" + " std::cout << atan2l(+1.0E-1,0) ;\n" + " std::cout << atan2l(0.1E-1,3) ;\n" + " std::cout << atan2l(+0.1E-1,1) ;\n" + " std::cout << atan2l(-0.1E-1,8) ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " std::cout << atan2(0,0) << std::endl;\n" + " std::cout << atan2f(0,0) << std::endl;\n" + " std::cout << atan2l(0,0) << std::endl;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Passing values 0 and 0 to atan2() leads to implementation-defined result.\n" + "[test.cpp:4]: (warning) Passing values 0 and 0 to atan2f() leads to implementation-defined result.\n" + "[test.cpp:5]: (warning) Passing values 0 and 0 to atan2l() leads to implementation-defined result.\n", errout_str()); + } + + void mathfunctionCall_fmod() { + // fmod, fmodl, fmodf + check("void foo()\n" + "{\n" + " std::cout << fmod(1.0,0) << std::endl;\n" + " std::cout << fmodf(1.0,0) << std::endl;\n" + " std::cout << fmodl(1.0,0) << std::endl;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid fmod() argument nr 2. The value is 0 but the valid values are '!0.0'.\n" + "[test.cpp:4]: (error) Invalid fmodf() argument nr 2. The value is 0 but the valid values are '!0.0'.\n" + "[test.cpp:5]: (error) Invalid fmodl() argument nr 2. The value is 0 but the valid values are '!0.0'.\n" + "[test.cpp:3]: (warning) Passing values 1.0 and 0 to fmod() leads to implementation-defined result.\n" + "[test.cpp:4]: (warning) Passing values 1.0 and 0 to fmodf() leads to implementation-defined result.\n" + "[test.cpp:5]: (warning) Passing values 1.0 and 0 to fmodl() leads to implementation-defined result.\n", errout_str()); + + check("void foo()\n" + "{\n" + " std::cout << fmod(1.0,1) << std::endl;\n" + " std::cout << fmodf(1.0,1) << std::endl;\n" + " std::cout << fmodl(1.0,1) << std::endl;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void mathfunctionCall_precision() { + check("void foo() {\n" + " print(exp(x) - 1);\n" + " print(log(1 + x));\n" + " print(1 - erf(x));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Expression 'exp(x) - 1' can be replaced by 'expm1(x)' to avoid loss of precision.\n" + "[test.cpp:3]: (style) Expression 'log(1 + x)' can be replaced by 'log1p(x)' to avoid loss of precision.\n" + "[test.cpp:4]: (style) Expression '1 - erf(x)' can be replaced by 'erfc(x)' to avoid loss of precision.\n", errout_str()); + + check("void foo() {\n" + " print(exp(x) - 1.0);\n" + " print(log(1.0 + x));\n" + " print(1.0 - erf(x));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Expression 'exp(x) - 1' can be replaced by 'expm1(x)' to avoid loss of precision.\n" + "[test.cpp:3]: (style) Expression 'log(1 + x)' can be replaced by 'log1p(x)' to avoid loss of precision.\n" + "[test.cpp:4]: (style) Expression '1 - erf(x)' can be replaced by 'erfc(x)' to avoid loss of precision.\n", errout_str()); + + check("void foo() {\n" + " print(exp(3 + x*f(a)) - 1);\n" + " print(log(x*4 + 1));\n" + " print(1 - erf(34*x + f(x) - c));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Expression 'exp(x) - 1' can be replaced by 'expm1(x)' to avoid loss of precision.\n" + "[test.cpp:3]: (style) Expression 'log(1 + x)' can be replaced by 'log1p(x)' to avoid loss of precision.\n" + "[test.cpp:4]: (style) Expression '1 - erf(x)' can be replaced by 'erfc(x)' to avoid loss of precision.\n", errout_str()); + + check("void foo() {\n" + " print(2*exp(x) - 1);\n" + " print(1 - erf(x)/2.0);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void checkIgnoredReturnValue() { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + const Settings settings2 = settingsBuilder().severity(Severity::warning).libraryxml(xmldata, sizeof(xmldata)).build(); + + check("void foo() {\n" + " mystrcmp(a, b);\n" + "}", true, &settings2); + ASSERT_EQUALS("[test.cpp:2]: (warning) Return value of function mystrcmp() is not used.\n", errout_str()); + + check("void foo() {\n" + " foo::mystrcmp(a, b);\n" + "}", true, &settings2); + ASSERT_EQUALS("[test.cpp:2]: (warning) Return value of function foo::mystrcmp() is not used.\n", errout_str()); + + check("void f() {\n" + " foo x;\n" + " x.mystrcmp(a, b);\n" + "}", true, &settings2); + ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function x.mystrcmp() is not used.\n", errout_str()); + + check("bool mystrcmp(char* a, char* b);\n" // cppcheck sees a custom strcmp definition, but it returns a value. Assume it is the one specified in the library. + "void foo() {\n" + " mystrcmp(a, b);\n" + "}", true, &settings2); + ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function mystrcmp() is not used.\n", errout_str()); + + check("void mystrcmp(char* a, char* b);\n" // cppcheck sees a custom strcmp definition which returns void! + "void foo() {\n" + " mystrcmp(a, b);\n" + "}", true, &settings2); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " class mystrcmp { mystrcmp() {} };\n" // strcmp is a constructor definition here + "}", true, &settings2); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " return mystrcmp(a, b);\n" + "}", true, &settings2); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " return foo::mystrcmp(a, b);\n" + "}", true, &settings2); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " if(mystrcmp(a, b));\n" + "}", true, &settings2); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " bool b = mystrcmp(a, b);\n" + "}", true, &settings2); + ASSERT_EQUALS("", errout_str()); + + // #6194 + check("void foo() {\n" + " MyStrCmp mystrcmp(x, y);\n" + "}", true, &settings2); + ASSERT_EQUALS("", errout_str()); + + // #6197 + check("void foo() {\n" + " abc::def.mystrcmp(a,b);\n" + "}", true, &settings2); + ASSERT_EQUALS("", errout_str()); + + // #6233 + check("int main() {\n" + " auto lambda = [](double value) {\n" + " double rounded = floor(value + 0.5);\n" + " printf(\"Rounded value = %f\\n\", rounded);\n" + " };\n" + " lambda(13.3);\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #6669 + check("void foo(size_t size) {\n" + " void * res{malloc(size)};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #7447 + check("void foo() {\n" + " int x{mystrcmp(a,b)};\n" + "}", true, &settings2); + ASSERT_EQUALS("", errout_str()); + + // #7905 + check("void foo() {\n" + " int x({mystrcmp(a,b)});\n" + "}", true, &settings2); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" // don't crash + " DEBUG(123)(mystrcmp(a,b))(fd);\n" + "}", false, &settings2); + check("struct teststruct {\n" + " int testfunc1() __attribute__ ((warn_unused_result)) { return 1; }\n" + " [[nodiscard]] int testfunc2() { return 1; }\n" + " void foo() { testfunc1(); testfunc2(); }\n" + "};\n" + "int main() {\n" + " teststruct TestStruct1;\n" + " TestStruct1.testfunc1();\n" + " TestStruct1.testfunc2();\n" + " return 0;\n" + "}", true, &settings2); + ASSERT_EQUALS("[test.cpp:4]: (warning) Return value of function testfunc1() is not used.\n" + "[test.cpp:4]: (warning) Return value of function testfunc2() is not used.\n" + "[test.cpp:8]: (warning) Return value of function TestStruct1.testfunc1() is not used.\n" + "[test.cpp:9]: (warning) Return value of function TestStruct1.testfunc2() is not used.\n", errout_str()); + + // #9006 + check("template uint8_t b(std::tuple d) {\n" + " std::tuple c{std::move(d)};\n" + " return std::get<0>(c);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int x; };\n" + "template \n" + "A f(int x, Ts... xs) {\n" + " return {std::move(x), static_cast(xs)...};\n" + "}\n" + "A g() { return f(1); }"); + ASSERT_EQUALS("", errout_str()); + + // #8412 - unused operator result + check("void foo() {\n" + " !mystrcmp(a, b);\n" + "}", true, &settings2); + ASSERT_EQUALS("[test.cpp:2]: (warning) Return value of function mystrcmp() is not used.\n", errout_str()); + + check("void f(std::vector v) {\n" + " delete *v.begin();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void checkIgnoredErrorCode() { + const char xmldata[] = "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + const Settings settings2 = settingsBuilder().severity(Severity::style).libraryxml(xmldata, sizeof(xmldata)).build(); + + check("void foo() {\n" + " mystrcmp(a, b);\n" + "}", true, &settings2); + ASSERT_EQUALS("[test.cpp:2]: (style) Error code from the return value of function mystrcmp() is not used.\n", errout_str()); + } + + void memsetZeroBytes() { + check("void f() {\n" + " memset(p, 10, 0x0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) memset() called to fill 0 bytes.\n", errout_str()); + + check("void f() {\n" + " memset(p, sizeof(p), 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) memset() called to fill 0 bytes.\n", errout_str()); + + check("void f() {\n" + " memset(p, sizeof(p), i);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #6269 false positives in case of overloaded standard library functions + check("class c {\n" + " void memset( int i );\n" + " void f( void ) {\n" + " memset( 0 );\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // #7285 + check("void f() {\n" + " memset(&tm, sizeof(tm), 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) memset() called to fill 0 bytes.\n", errout_str()); + + } + + void memsetInvalid2ndParam() { + check("void f() {\n" + " int* is = new int[10];\n" + " memset(is, 1.0f, 40);\n" + " int* is2 = new int[10];\n" + " memset(is2, 0.1f, 40);\n" + "}"); + 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" + "}"); + 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" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // FP + " float x = 2.3f;\n" + " memset(a, (x?64:0), 40);\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" + "}"); + 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" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int is[10];\n" + " const int i = g();\n" + " memset(is, 1.0f + i, 40);\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 checkMissingReturn() { + check("int f() {}"); + ASSERT_EQUALS("[test.cpp:1]: (error) Found an exit path from function with non-void return type that has missing return statement\n", errout_str()); + + { + const char code[] = "int main(void) {}"; + { + const Settings s = settingsBuilder().c(Standards::C89).build(); + + check(code, false, &s); // c code (c89) + ASSERT_EQUALS("[test.c:1]: (error) Found an exit path from function with non-void return type that has missing return statement\n", errout_str()); + } + + { + const Settings s = settingsBuilder().c(Standards::C99).build(); + check(code, false, &s); // c code (c99) + ASSERT_EQUALS("", errout_str()); + + check(code, true, &s); // c++ code + ASSERT_EQUALS("", errout_str()); + } + } + + check("F(A,B) { x=1; }"); + ASSERT_EQUALS("", errout_str()); + + check("auto foo4() -> void {}"); + ASSERT_EQUALS("", errout_str()); + + check("void STDCALL foo() {}"); + ASSERT_EQUALS("", errout_str()); + + check("void operator=(int y) { x=y; }"); + ASSERT_EQUALS("", errout_str()); + + check("int f() {\n" + "back:\n" + " return 0;\n" + "ng:\n" + " x=y;\n" + " goto back;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // unreachable code.. + check("int foo(int x) {\n" + " return 1;\n" + " (void)x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int foo(int x) {\n" + " if (x) goto out;\n" + " return 1;\n" + "out:\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Found an exit path from function with non-void return type that has missing return statement\n", errout_str()); + + // switch + check("int f() {\n" + " switch (x) {\n" + " case 1: break;\n" // <- error + " case 2: return 1;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Found an exit path from function with non-void return type that has missing return statement\n", errout_str()); + + check("int f() {\n" + " switch (x) {\n" + " case 1: return 2; break;\n" + " default: return 1;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool test(unsigned char v1, int v2) {\n" + " switch (v1) {\n" + " case 0:\n" + " switch (v2) {\n" + " case 48000:\n" + " break;\n" + " }\n" + " return false;\n" + " default:\n" + " return true;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // if/else + check("int f(int x) {\n" + " if (x) {\n" + " return 1;\n" + " }\n" // <- error (missing else) + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Found an exit path from function with non-void return type that has missing return statement\n", errout_str()); + + check("int f(int x) {\n" + " if (x) {\n" + " ;\n" // <- error + " } else {\n" + " return 1;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Found an exit path from function with non-void return type that has missing return statement\n", errout_str()); + + check("int f() {\n" + " if (!0) {\n" + " return 1;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f() {\n" + " if (!0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Found an exit path from function with non-void return type that has missing return statement\n", errout_str()); + + // loop + check("int f(int x) {\n" + " while (1) {\n" + " dostuff();\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // return {..} + check("std::pair typeDecl(int tok) {\n" + " if (!tok)\n" + " return {};\n" + " else\n" + " return {1, 2};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // noreturn function + check("int f(int x) { exit(0); }"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int x) { assert(0); }"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int x) { if (x) return 1; else return bar({1}, {}); }"); + ASSERT_EQUALS("", errout_str()); + + check("auto f() -> void {}"); // #10342 + ASSERT_EQUALS("", errout_str()); + + check("struct S1 {\n" // #7433 + " S1& operator=(const S1& r) { if (this != &r) { i = r.i; } }\n" + " int i;\n" + "};\n" + "struct S2 {\n" + " S2& operator=(const S2& s) { if (this != &s) { j = s.j; } return *this; }\n" + " int j;\n" + "};\n" + "struct S3 {\n" + " S3& operator=(const S3& t) { if (this != &t) { k = t.k; return *this; } }\n" + " int k;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2]: (error) Found an exit path from function with non-void return type that has missing return statement\n" + "[test.cpp:10]: (error) Found an exit path from function with non-void return type that has missing return statement\n", + errout_str()); + + // #11171 + check("std::enable_if_t f() {}"); + ASSERT_EQUALS("", errout_str()); + + check("std::enable_if_t f() {}"); + ASSERT_EQUALS( + "[test.cpp:1]: (error) Found an exit path from function with non-void return type that has missing return statement\n", + errout_str()); + + check("template std::enable_if_t{}, int> f(T) {}"); + ASSERT_EQUALS( + "[test.cpp:1]: (error) Found an exit path from function with non-void return type that has missing return statement\n", + errout_str()); + + check("template std::enable_if_t{}> f(T) {}"); + ASSERT_EQUALS("", errout_str()); + + check("typename std::enable_if::type f() {}"); + ASSERT_EQUALS("", errout_str()); + + check("typename std::enable_if::type f() {}"); + ASSERT_EQUALS( + "[test.cpp:1]: (error) Found an exit path from function with non-void return type that has missing return statement\n", + errout_str()); + + check("template typename std::enable_if{}, int>::type f(T) {}"); + ASSERT_EQUALS( + "[test.cpp:1]: (error) Found an exit path from function with non-void return type that has missing return statement\n", + errout_str()); + + check("template typename std::enable_if{}>::type f(T) {}"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " [[noreturn]] void f();\n" + " int g() { this->f(); }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { [[noreturn]] void f(); };\n" + "int g(S& s) { s.f(); }\n"); + ASSERT_EQUALS("", errout_str()); + } + + // NRVO check + void returnLocalStdMove1() { + check("struct A{}; A f() { A var; return std::move(var); }"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Using std::move for returning object by-value from function will affect copy elision optimization." + " More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local\n", errout_str()); + } + + // RVO, C++03 ctor style + void returnLocalStdMove2() { + check("struct A{}; A f() { return std::move( A() ); }"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Using std::move for returning object by-value from function will affect copy elision optimization." + " More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local\n", errout_str()); + } + + // RVO, new ctor style + void returnLocalStdMove3() { + check("struct A{}; A f() { return std::move(A{}); }"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Using std::move for returning object by-value from function will affect copy elision optimization." + " More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local\n", errout_str()); + } + + // Function argument + void returnLocalStdMove4() { + check("struct A{}; A f(A a) { return std::move(A{}); }"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Using std::move for returning object by-value from function will affect copy elision optimization." + " More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local\n", errout_str()); + } + + void returnLocalStdMove5() { + check("struct A{} a; A f1() { return std::move(a); }\n" + "A f2() { volatile A var; return std::move(var); }"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { std::string msg{ \"abc\" }; };\n" + "std::unique_ptr get(std::vector>& v) {\n" + " return std::move(v.front());\n" + "}\n" + "int main() {\n" + " std::vector> v;\n" + " v.emplace_back(std::make_unique());\n" + " auto p = get(v);\n" + " std::cout << p->msg;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("std::string&& f() {\n" // #11881 + " std::string s;\n" + " return std::move(s);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void negativeMemoryAllocationSizeError() { // #389 + check("void f() {\n" + " int *a;\n" + " a = malloc( -10 );\n" + " free(a);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Invalid malloc() argument nr 1. The value is -10 but the valid values are '0:'.\n", errout_str()); + + check("void f() {\n" + " int *a;\n" + " a = alloca( -10 );\n" + " free(a);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Obsolete function 'alloca' called.\n" + "[test.cpp:3]: (error) Invalid alloca() argument nr 1. The value is -10 but the valid values are '0:'.\n", errout_str()); + } + + void checkLibraryMatchFunctions() { + /*const*/ Settings s = settingsBuilder(settings).checkLibrary().debugwarnings().build(); + s.daca = true; + + check("void f() {\n" + " lib_func();" + "}", true, &s); + ASSERT_EQUALS("[test.cpp:2]: (information) --check-library: There is no matching configuration for function lib_func()\n", errout_str()); + + check("void f(void* v) {\n" + " lib_func(v);" + "}", true, &s); + ASSERT_EQUALS("[test.cpp:2]: (information) --check-library: There is no matching configuration for function lib_func()\n", errout_str()); + + // #10105 + check("class TestFixture {\n" + "protected:\n" + " bool prepareTest(const char testname[]);\n" + "};\n" + "\n" + "class TestMemleak : private TestFixture {\n" + " void run() {\n" + " do { prepareTest(\"testFunctionReturnType\"); } while (false);\n" + " }\n" + "\n" + " void testFunctionReturnType() {\n" + " }\n" + "};", true, &s); + ASSERT_EQUALS("", errout_str()); + + // #11183 + check("#include \n" + "\n" + "extern void cb(const std::string&);\n" + "\n" + "void f() {\n" + " cb(std::string(\"\"));\n" + "}", true, &s); + TODO_ASSERT_EQUALS("", "[test.cpp:6]: (information) --check-library: There is no matching configuration for function cb()\n", errout_str()); + + // #7375 + check("void f() {\n" + " struct S { int i; char c; };\n" + " size_t s = sizeof(S);\n" + " static_assert(s == 9);\n" + "}\n", true, &s); + ASSERT_EQUALS("", errout_str()); + + check("void f(char) {}\n" + "void g() {\n" + " f(int8_t(1));\n" + "}\n", true, &s); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::uint64_t& u) {\n" + " u = std::uint32_t(u) * std::uint64_t(100);\n" + "}\n", true, &s); + ASSERT_EQUALS("", errout_str()); + + check("void f() { throw(1); }\n", true, &s); // #8958 + ASSERT_EQUALS("", errout_str()); + + check("using namespace std;\n" + "void f() { throw range_error(\"abc\"); }\n", true, &s); + ASSERT_EQUALS("", errout_str()); + + check("class C {\n" // #9002 + "public:\n" + " static int f() { return 1; }\n" + "};\n" + "void g() { C::f(); }\n", true, &s); + ASSERT_EQUALS("", errout_str()); + + check("void f(const std::vector& v) {\n" // #11223 + " for (const auto& s : v)\n" + " s.find(\"\");\n" + "}\n", true, &s); + ASSERT_EQUALS("[test.cpp:3]: (warning) Return value of function s.find() is not used.\n", errout_str()); + + check("void f() {\n" + " auto* p = new std::vector(5);\n" + " p->push_back(1);\n" + " auto* q = new std::vector{ 5, 7 };\n" + " q->push_back(1);\n" + " auto* r = new std::vector;\n" + " r->push_back(1);\n" + "}\n", true, &s); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " auto p = std::make_shared>();\n" + " p->push_back(1);\n" + "}\n", true, &s); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::vector>& v) {\n" + " auto it = v.begin();\n" + " it->push_back(1);\n" + "}\n", true, &s); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " auto v = std::vector{};\n" + " v.push_back(1);\n" + " auto w = std::vector{ 1, 2, 3 };\n" + " w.push_back(1);\n" + " auto x = std::vector(1);\n" + " x.push_back(1);\n" + "}\n", true, &s); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " auto p(std::make_shared>());\n" + " p->push_back(1);\n" + " auto q{ std::make_shared>{} };\n" + " q->push_back(1);\n" + "}\n", true, &s); + TODO_ASSERT_EQUALS("", + "[test.cpp:2]: (debug) auto token with no type.\n" + "[test.cpp:4]: (debug) auto token with no type.\n" + "[test.cpp:3]: (information) --check-library: There is no matching configuration for function auto::push_back()\n" + "[test.cpp:5]: (information) --check-library: There is no matching configuration for function auto::push_back()\n", + errout_str()); + + check("struct F { void g(int); };\n" + "void f(std::list& l) {\n" + " std::list::iterator it;\n" + " for (it = l.begin(); it != l.end(); ++it)\n" + " it->g(0);\n" + "}\n", true, &s); + ASSERT_EQUALS("", filter_valueflow(errout_str())); + + check("auto f() {\n" + " return std::runtime_error(\"abc\");\n" + "}\n", true, &s); + TODO_ASSERT_EQUALS("", + "[test.cpp:1]: (debug) auto token with no type.\n", + errout_str()); + + check("struct S {\n" // #11543 + " S() {}\n" + " std::vector> v;\n" + " void f(int i) const;\n" + "};\n" + "void S::f(int i) const {\n" + " for (const std::shared_ptr& c : v)\n" + " c->f(i);\n" + "}\n", true, &s); + ASSERT_EQUALS("", errout_str()); + + check("namespace N {\n" + " struct S { static const std::set s; };\n" + "}\n" + "void f() {\n" + " const auto& t = N::S::s;\n" + " if (t.find(\"abc\") != t.end()) {}\n" + "}\n", true, &s); + ASSERT_EQUALS("", filter_valueflow(errout_str())); + + check("void f(std::vector>>& v, int i, int j) {\n" + " auto& s = v[i][j];\n" + " s.insert(0);\n" + "}\n", true, &s); + ASSERT_EQUALS("", errout_str()); + + check("int f(const std::vector& v, int i, char c) {\n" + " const auto& s = v[i];\n" + " return s.find(c);\n" + "}\n", true, &s); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #11604 + " int (*g)() = nullptr;\n" + "}\n", true, &s); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " INT (*g)() = nullptr;\n" + "}\n", true, &s); + ASSERT_EQUALS("", errout_str()); + + check("struct T;\n" + "std::shared_ptr get();\n" + "void f(int i) {\n" + " auto p = get();\n" + " p->h(i);\n" + " p.reset(nullptr);\n" + "}\n", true, &s); + ASSERT_EQUALS("[test.cpp:5]: (information) --check-library: There is no matching configuration for function T::h()\n", + errout_str()); + + check("struct S : std::vector {\n" + " void f(int i) { push_back(i); }\n" + "};\n", true, &s); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " auto g = []() -> std::string { return \"abc\"; };\n" + " auto s = g();\n" + " if (s.at(0)) {}\n" + " auto h{ []() -> std::string { return \"xyz\"; } };\n" + " auto t = h();\n" + " if (t.at(0)) {}\n" + "};\n", true, &s); + ASSERT_EQUALS("", filter_valueflow(errout_str())); + + check("::std::string f(const char* c) {\n" // #12365 + " return ::std::string(c);\n" + "}\n", true, &s); + ASSERT_EQUALS("", errout_str()); + + check("template \n" + "struct S : public std::vector {\n" + " void resize(size_t n) { std::vector::resize(n); }\n" + "};\n", true, &s); + ASSERT_EQUALS("", errout_str()); + } + + void checkUseStandardLibrary1() { + check("void f(void* dest, void const* src, const size_t count) {\n" + " size_t i;\n" + " for (i = 0; count > i; ++i)\n" + " (reinterpret_cast(dest))[i] = (reinterpret_cast(src))[i];\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::memcpy instead of loop.\n", errout_str()); + } + + void checkUseStandardLibrary2() { + check("void f(void* dest, void const* src, const size_t count) {\n" + " for (size_t i = 0; i < count; i++) {\n" + " (reinterpret_cast(dest))[i] = (reinterpret_cast(src))[i];\n" + "}}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Consider using std::memcpy instead of loop.\n", errout_str()); + } + + void checkUseStandardLibrary3() { + check("void f(void* dst, const void* src, const size_t count) {\n" + " size_t i;\n" + " for (i = 0; count > i; i++)\n" + " ((char*)dst)[i] = ((const char*)src)[i];\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::memcpy instead of loop.\n", errout_str()); + } + + void checkUseStandardLibrary4() { + check("void f(void* dst, void* src, const size_t size) {\n" + " for (size_t i = 0; i < size; i += 1) {\n" + " ((int8_t*)dst)[i] = ((int8_t*)src)[i];\n" + "}}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Consider using std::memcpy instead of loop.\n", errout_str()); + } + + // different indexes + void checkUseStandardLibrary5() { + check("void f(void* dst, void* src, const size_t size, const size_t from_idx) {\n" + " for (size_t i = 0; i < size; ++i) {\n" + " ((int8_t*)dst)[i] = ((int8_t*)src)[from_idx];\n" + "}}\n"); + ASSERT_EQUALS("", errout_str()); + } + + // unknown count + void checkUseStandardLibrary6() { + check("void f(void* dst, void* src, const size_t size) {\n" + " for (size_t i = 0; ((int8_t*)src)[i] != 0; ++i) {\n" + " ((int8_t*)dst)[i] = ((int8_t*)src)[i];\n" + "}}\n"); + ASSERT_EQUALS("", errout_str()); + } + + // increment with 2 + void checkUseStandardLibrary7() { + check("void f(void* dst, void* src, const size_t size) {\n" + " for (size_t i = 0; i < size; i += 2) {\n" + " ((int8_t*)dst)[i] = ((int8_t*)src)[i];\n" + "}}\n"); + ASSERT_EQUALS("", errout_str()); + } + + // right argument of assignment could be static_cast, functional cast, c-style and implicit cast + // functional cast case not covered + void checkUseStandardLibrary8() { + check("void f(void* dest, const size_t count) {\n" + " size_t i;\n" + " for (i = 0; i < count; ++i)\n" + " (reinterpret_cast(dest))[i] = static_cast(0);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::memset instead of loop.\n", errout_str()); + } + + void checkUseStandardLibrary9() { + check("void f(void* dest, const size_t count) {\n" + " for (size_t i = 0; i < count; i++) {\n" + " (reinterpret_cast(dest))[i] = (static_cast(0));\n" + "}}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Consider using std::memset instead of loop.\n", errout_str()); + } + + void checkUseStandardLibrary10() { + check("void f(void* dst, const size_t size) {\n" + " size_t i;\n" + " for (i = 0; i < size; i++)\n" + " ((char*)dst)[i] = (const char)0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Consider using std::memset instead of loop.\n", errout_str()); + } + + void checkUseStandardLibrary11() { + check("void f(void* dst, const size_t size) {\n" + " for (size_t i = 0; i < size; i += 1) {\n" + " ((int8_t*)dst)[i] = ((int8_t)0);\n" + "}}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Consider using std::memset instead of loop.\n", errout_str()); + } + + void checkUseStandardLibrary12() { + check("void f(void* dst, const size_t size) {\n" + " for (size_t i = 0; i < size; i += 1) {\n" + " ((int8_t*)dst)[i] = 42;\n" + "}}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Consider using std::memset instead of loop.\n", errout_str()); + } + + void checkUseStandardLibrary13() { + check("void f(void* dest, const size_t count) {\n" + " for (size_t i = 0; i < count; i++) {\n" + " reinterpret_cast(dest)[i] = '0';\n" + "}}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Consider using std::memset instead of loop.\n", errout_str()); + } + + void checkUseStandardLibrary14() { + check("void f(void* dest) {\n" + " for (size_t i = 0; i < sizeof(int)*(15 + 42/2 - 7); i++) {\n" + " reinterpret_cast(dest)[i] = '0';\n" + "}}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Consider using std::memset instead of loop.\n", errout_str()); + } +}; + +REGISTER_TEST(TestFunctions) diff --git a/cppcheck-2.14.0/test/testgarbage.cpp b/cppcheck-2.14.0/test/testgarbage.cpp new file mode 100644 index 00000000..f0ecd574 --- /dev/null +++ b/cppcheck-2.14.0/test/testgarbage.cpp @@ -0,0 +1,1877 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "check.h" +#include "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "settings.h" +#include "token.h" + +#include +#include + +class TestGarbage : public TestFixture { +public: + TestGarbage() : TestFixture("TestGarbage") {} + +private: + /*const*/ Settings settings = settingsBuilder().debugwarnings().build(); + + void run() override { + settings.severity.fill(); + settings.certainty.fill(); + + // don't freak out when the syntax is wrong + + TEST_CASE(final_class_x); + TEST_CASE(wrong_syntax1); + TEST_CASE(wrong_syntax2); + TEST_CASE(wrong_syntax3); // #3544 + TEST_CASE(wrong_syntax4); // #3618 + TEST_CASE(wrong_syntax_if_macro); // #2518 - if MACRO() + TEST_CASE(wrong_syntax_class_x_y); // #3585 - class x y { }; + TEST_CASE(wrong_syntax_anonymous_struct); + TEST_CASE(syntax_case_default); + TEST_CASE(garbageCode1); + TEST_CASE(garbageCode2); // #4300 + TEST_CASE(garbageCode3); // #4869 + TEST_CASE(garbageCode4); // #4887 + TEST_CASE(garbageCode5); // #5168 + TEST_CASE(garbageCode6); // #5214 + TEST_CASE(garbageCode7); + TEST_CASE(garbageCode8); // #5511 + TEST_CASE(garbageCode9); // #5604 + TEST_CASE(garbageCode10); // #6127 + TEST_CASE(garbageCode12); + TEST_CASE(garbageCode13); // #2607 + TEST_CASE(garbageCode15); // #5203 + TEST_CASE(garbageCode16); + TEST_CASE(garbageCode17); + TEST_CASE(garbageCode18); + TEST_CASE(garbageCode20); + TEST_CASE(garbageCode21); + TEST_CASE(garbageCode22); + TEST_CASE(garbageCode23); + TEST_CASE(garbageCode24); // #6361 + TEST_CASE(garbageCode25); + TEST_CASE(garbageCode26); + TEST_CASE(garbageCode27); + TEST_CASE(garbageCode28); + TEST_CASE(garbageCode30); // #5867 + TEST_CASE(garbageCode31); // #6539 + TEST_CASE(garbageCode33); // #6613 + TEST_CASE(garbageCode34); // #6626 + TEST_CASE(garbageCode35); // #2604 + TEST_CASE(garbageCode36); // #6334 + TEST_CASE(garbageCode37); // #5166 + TEST_CASE(garbageCode38); // #6666 + TEST_CASE(garbageCode40); // #6620 + TEST_CASE(garbageCode41); // #6685 + TEST_CASE(garbageCode42); // #5760 + TEST_CASE(garbageCode43); // #6703 + TEST_CASE(garbageCode44); // #6704 + TEST_CASE(garbageCode45); // #6608 + TEST_CASE(garbageCode46); // #6705 + TEST_CASE(garbageCode47); // #6706 + TEST_CASE(garbageCode48); // #6712 + TEST_CASE(garbageCode49); // #6715 + TEST_CASE(garbageCode51); // #6719 + TEST_CASE(garbageCode53); // #6721 + TEST_CASE(garbageCode54); // #6722 + TEST_CASE(garbageCode55); // #6724 + TEST_CASE(garbageCode56); // #6713 + TEST_CASE(garbageCode57); // #6733 + TEST_CASE(garbageCode58); // #6732 + TEST_CASE(garbageCode59); // #6735 + TEST_CASE(garbageCode60); // #6736 + TEST_CASE(garbageCode61); + TEST_CASE(garbageCode63); + TEST_CASE(garbageCode64); + TEST_CASE(garbageCode65); + TEST_CASE(garbageCode66); + TEST_CASE(garbageCode68); + TEST_CASE(garbageCode69); + TEST_CASE(garbageCode70); + TEST_CASE(garbageCode71); + TEST_CASE(garbageCode72); + TEST_CASE(garbageCode73); + TEST_CASE(garbageCode74); + TEST_CASE(garbageCode76); + TEST_CASE(garbageCode77); + TEST_CASE(garbageCode78); + TEST_CASE(garbageCode79); + TEST_CASE(garbageCode80); + TEST_CASE(garbageCode81); + TEST_CASE(garbageCode82); + TEST_CASE(garbageCode83); + TEST_CASE(garbageCode84); + TEST_CASE(garbageCode85); + TEST_CASE(garbageCode86); + TEST_CASE(garbageCode87); + TEST_CASE(garbageCode88); + TEST_CASE(garbageCode90); + TEST_CASE(garbageCode91); + TEST_CASE(garbageCode92); + TEST_CASE(garbageCode94); + TEST_CASE(garbageCode95); + TEST_CASE(garbageCode96); + TEST_CASE(garbageCode97); + TEST_CASE(garbageCode98); + TEST_CASE(garbageCode99); + TEST_CASE(garbageCode100); + TEST_CASE(garbageCode101); // #6835 + TEST_CASE(garbageCode102); // #6846 + TEST_CASE(garbageCode103); // #6824 + TEST_CASE(garbageCode104); // #6847 + TEST_CASE(garbageCode105); // #6859 + TEST_CASE(garbageCode106); + TEST_CASE(garbageCode107); + TEST_CASE(garbageCode108); + TEST_CASE(garbageCode109); + TEST_CASE(garbageCode110); + TEST_CASE(garbageCode111); + TEST_CASE(garbageCode112); + TEST_CASE(garbageCode114); // #2118 + TEST_CASE(garbageCode115); // #5506 + TEST_CASE(garbageCode116); // #5356 + TEST_CASE(garbageCode117); // #6121 + TEST_CASE(garbageCode118); // #5600 + TEST_CASE(garbageCode119); // #5598 + TEST_CASE(garbageCode120); // #4927 + TEST_CASE(garbageCode121); // #2585 + TEST_CASE(garbageCode122); // #6303 + TEST_CASE(garbageCode123); + TEST_CASE(garbageCode125); // 6782, 6834 + TEST_CASE(garbageCode126); // #6997 + TEST_CASE(garbageCode127); // #6667 + TEST_CASE(garbageCode128); // #7018 + TEST_CASE(garbageCode129); // #7020 + TEST_CASE(garbageCode130); // #7021 + TEST_CASE(garbageCode131); // #7023 + TEST_CASE(garbageCode132); // #7022 + TEST_CASE(garbageCode133); + TEST_CASE(garbageCode134); + TEST_CASE(garbageCode135); // #4994 + TEST_CASE(garbageCode136); // #7033 + TEST_CASE(garbageCode137); // #7034 + TEST_CASE(garbageCode138); // #6660 + TEST_CASE(garbageCode139); // #6659 + TEST_CASE(garbageCode140); // #7035 + TEST_CASE(garbageCode141); // #7043 + TEST_CASE(garbageCode142); // #7050 + TEST_CASE(garbageCode143); // #6922 + TEST_CASE(garbageCode144); // #6865 + TEST_CASE(garbageCode146); // #7081 + TEST_CASE(garbageCode147); // #7082 + TEST_CASE(garbageCode148); // #7090 + TEST_CASE(garbageCode149); // #7085 + TEST_CASE(garbageCode150); // #7089 + TEST_CASE(garbageCode151); // #4911 + TEST_CASE(garbageCode152); // travis after 9c7271a5 + TEST_CASE(garbageCode153); + TEST_CASE(garbageCode154); // #7112 + TEST_CASE(garbageCode156); // #7120 + TEST_CASE(garbageCode157); // #7131 + TEST_CASE(garbageCode158); // #3238 + TEST_CASE(garbageCode159); // #7119 + TEST_CASE(garbageCode160); // #7190 + TEST_CASE(garbageCode161); // #7200 + TEST_CASE(garbageCode162); // #7208 + TEST_CASE(garbageCode163); // #7228 + TEST_CASE(garbageCode164); // #7234 + TEST_CASE(garbageCode165); // #7235 + TEST_CASE(garbageCode167); // #7237 + TEST_CASE(garbageCode168); // #7246 + TEST_CASE(garbageCode169); // #6731 + TEST_CASE(garbageCode170); + TEST_CASE(garbageCode171); + TEST_CASE(garbageCode172); + TEST_CASE(garbageCode173); // #6781 + TEST_CASE(garbageCode174); // #7356 + TEST_CASE(garbageCode175); + TEST_CASE(garbageCode176); // #7527 + TEST_CASE(garbageCode181); + TEST_CASE(garbageCode182); // #4195 + TEST_CASE(garbageCode183); // #7505 + TEST_CASE(garbageCode184); // #7699 + TEST_CASE(garbageCode185); // #6011 + TEST_CASE(garbageCode186); // #8151 + TEST_CASE(garbageCode187); + TEST_CASE(garbageCode188); + TEST_CASE(garbageCode189); // #8317 + TEST_CASE(garbageCode190); // #8307 + TEST_CASE(garbageCode191); // #8333 + TEST_CASE(garbageCode192); // #8386 (segmentation fault) + TEST_CASE(garbageCode193); // #8740 + TEST_CASE(garbageCode194); // #8384 + TEST_CASE(garbageCode195); // #8709 + TEST_CASE(garbageCode196); // #8265 + TEST_CASE(garbageCode197); // #8385 + TEST_CASE(garbageCode198); // #8383 + TEST_CASE(garbageCode199); // #8752 + TEST_CASE(garbageCode200); // #8757 + TEST_CASE(garbageCode201); // #8873 + TEST_CASE(garbageCode202); // #8907 + TEST_CASE(garbageCode203); // #8972 + TEST_CASE(garbageCode204); + TEST_CASE(garbageCode205); + TEST_CASE(garbageCode206); + TEST_CASE(garbageCode207); // #8750 + TEST_CASE(garbageCode208); // #8753 + TEST_CASE(garbageCode209); // #8756 + TEST_CASE(garbageCode210); // #8762 + TEST_CASE(garbageCode211); // #8764 + TEST_CASE(garbageCode212); // #8765 + TEST_CASE(garbageCode213); // #8758 + TEST_CASE(garbageCode214); + TEST_CASE(garbageCode215); // daca@home script with extension .c + TEST_CASE(garbageCode216); // #7884 + TEST_CASE(garbageCode217); // #10011 + TEST_CASE(garbageCode218); // #8763 + TEST_CASE(garbageCode219); // #10101 + TEST_CASE(garbageCode220); // #6832 + TEST_CASE(garbageCode221); + TEST_CASE(garbageCode222); // #10763 + TEST_CASE(garbageCode223); // #11639 + TEST_CASE(garbageCode224); + TEST_CASE(garbageCode225); + TEST_CASE(garbageCode226); + TEST_CASE(garbageCode227); + + TEST_CASE(garbageCodeFuzzerClientMode1); // test cases created with the fuzzer client, mode 1 + + TEST_CASE(garbageValueFlow); + TEST_CASE(garbageSymbolDatabase); + TEST_CASE(garbageAST); + TEST_CASE(templateSimplifierCrashes); + TEST_CASE(syntaxErrorFirstToken); // Make sure syntax errors are detected and reported + TEST_CASE(syntaxErrorLastToken); // Make sure syntax errors are detected and reported + TEST_CASE(syntaxErrorCase); + TEST_CASE(syntaxErrorFuzzerCliType1); + TEST_CASE(cliCode); + TEST_CASE(enumTrailingComma); + + TEST_CASE(nonGarbageCode1); // #8346 + } + +#define checkCodeInternal(code, filename) checkCodeInternal_(code, filename, __FILE__, __LINE__) + std::string checkCode(const char code[], bool cpp = true) { + // double the tests - run each example as C as well as C++ + + // run alternate check first. It should only ensure stability - so we catch exceptions here. + try { + checkCodeInternal(code, !cpp); + } catch (const InternalError&) {} + + return checkCodeInternal(code, cpp); + } + + std::string checkCodeInternal_(const char code[], bool cpp, const char* file, int line) { + // tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code, cpp), file, line); + + // call all "runChecks" in all registered Check classes + for (std::list::const_iterator it = Check::instances().cbegin(); it != Check::instances().cend(); ++it) { + (*it)->runChecks(tokenizer, this); + } + + return tokenizer.tokens()->stringifyList(false, false, false, true, false, nullptr, nullptr); + } + +#define getSyntaxError(code) getSyntaxError_(code, __FILE__, __LINE__) + std::string getSyntaxError_(const char code[], const char* file, int line) { + SimpleTokenizer tokenizer(settings, *this); + try { + ASSERT_LOC(tokenizer.tokenize(code), file, line); + } catch (InternalError& e) { + if (e.id != "syntaxError") + return ""; + return "[test.cpp:" + std::to_string(e.token->linenr()) + "] " + e.errorMessage; + } + return ""; + } + + + void final_class_x() { + + const char code[] = "class __declspec(dllexport) x final { };"; + SimpleTokenizer tokenizer(settings, *this); + ASSERT(tokenizer.tokenize(code)); + ASSERT_EQUALS("", errout_str()); + } + + void wrong_syntax1() { + { + const char code[] ="TR(kvmpio, PROTO(int rw), ARGS(rw), TP_(aa->rw;))"; + ASSERT_THROW_INTERNAL(checkCode(code), UNKNOWN_MACRO); + ASSERT_EQUALS("", errout_str()); + } + + { + const char code[] ="struct A { template struct { }; };"; + ASSERT_THROW_INTERNAL(checkCode(code), SYNTAX); + } + + { + const char code[] ="enum ABC { A,B, typedef enum { C } };"; + ASSERT_THROW_INTERNAL(checkCode(code), SYNTAX); + } + } + + void wrong_syntax2() { // #3504 + const char code[] = "void f() {\n" + " X x;\n" + " Y y;\n" + "}\n" + "\n" + "void G( template class (j) ) {}"; + + // don't segfault.. + ASSERT_THROW_INTERNAL(checkCode(code), SYNTAX); + ignore_errout(); // we are not interested in the output + } + + + 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" + "}"; + + SimpleTokenizer tokenizer(settings, *this); + try { + ASSERT(tokenizer.tokenize(code)); + assertThrowFail(__FILE__, __LINE__); + } catch (InternalError& e) { + ASSERT_EQUALS("syntax error", e.errorMessage); + ASSERT_EQUALS("syntaxError", e.id); + ASSERT_EQUALS(4, e.token->linenr()); + } + } + + void wrong_syntax4() { // #3618 + const char code[] = "typedef void (x) (int); return x&"; + + ASSERT_THROW_INTERNAL(checkCode(code), SYNTAX); + } + + void wrong_syntax_if_macro() { + // #2518 #4171 + ASSERT_THROW_INTERNAL(checkCode("void f() { if MACRO(); }"), SYNTAX); + + // #4668 - note there is no semicolon after MACRO() + ASSERT_THROW_INTERNAL(checkCode("void f() { if (x) MACRO() {} }"), SYNTAX); + + // #4810 - note there is no semicolon after MACRO() + ASSERT_THROW_INTERNAL(checkCode("void f() { if (x) MACRO() else ; }"), SYNTAX); + } + + void wrong_syntax_class_x_y() { + // #3585 + const char code[] = "class x y { };"; + + { + SimpleTokenizer tokenizer(settings, *this); + ASSERT(tokenizer.tokenize(code, false)); + ASSERT_EQUALS("", errout_str()); + } + { + SimpleTokenizer tokenizer(settings, *this); + ASSERT(tokenizer.tokenize(code)); + ASSERT_EQUALS("[test.cpp:1]: (information) The code 'class x y {' is not handled. You can use -I or --include to add handling of this code.\n", errout_str()); + } + } + + void wrong_syntax_anonymous_struct() { + ASSERT_THROW_INTERNAL(checkCode("struct { int x; } = {0};"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("struct { int x; } * = {0};"), SYNTAX); + } + + void syntax_case_default() { + ASSERT_THROW_INTERNAL(checkCode("void f() {switch (n) { case: z(); break;}}"), SYNTAX); + + ASSERT_THROW_INTERNAL(checkCode("void f() {switch (n) { case;: z(); break;}}"), SYNTAX); + + ASSERT_THROW_INTERNAL(checkCode("void f() {switch (n) { case {}: z(); break;}}"), SYNTAX); + + ASSERT_THROW_INTERNAL(checkCode("void f() {switch (n) { case 0?{1}:{2} : z(); break;}}"), SYNTAX); + + ASSERT_THROW_INTERNAL(checkCode("void f() {switch (n) { case 0?1;:{2} : z(); break;}}"), SYNTAX); + + ASSERT_THROW_INTERNAL(checkCode("void f() {switch (n) { case 0?(1?{3:4}):2 : z(); break;}}"), AST); + + //ticket #4234 + ASSERT_THROW_INTERNAL(checkCode("( ) { switch break ; { switch ( x ) { case } y break ; : } }"), SYNTAX); + + //ticket #4267 + ASSERT_THROW_INTERNAL(checkCode("f ( ) { switch break; { switch ( x ) { case } case break; -6: ( ) ; } }"), SYNTAX); + + // Missing semicolon + ASSERT_THROW_INTERNAL(checkCode("void foo () { switch(0) case 0 : default : }"), SYNTAX); + } + + void garbageCode1() { + checkCode("struct x foo_t; foo_t typedef y;"); + } + + void garbageCode2() { //#4300 (segmentation fault) + TODO_ASSERT_THROW(checkCode("enum { D = 1 struct { } ; } s.b = D;"), InternalError); + } + + void garbageCode3() { //#4849 (segmentation fault in Tokenizer::simplifyStructDecl (invalid code)) + TODO_ASSERT_THROW(checkCode("enum { D = 2 s ; struct y { x } ; } { s.a = C ; s.b = D ; }"), InternalError); + } + + void garbageCode4() { // #4887 + ASSERT_THROW_INTERNAL(checkCode("void f ( ) { = a ; if ( 1 ) if = ( 0 ) ; }"), SYNTAX); + } + + void garbageCode5() { // #5168 + ASSERT_THROW_INTERNAL(checkCode("( asm : ; void : );"), SYNTAX); + } + + void garbageCode6() { // #5214 + ASSERT_THROW_INTERNAL(checkCode("int b = ( 0 ? ? ) 1 : 0 ;"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("int a = int b = ( 0 ? ? ) 1 : 0 ;"), SYNTAX); + } + + void garbageCode7() { + ASSERT_THROW_INTERNAL(checkCode("1 (int j) { return return (c) * sizeof } y[1];"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("foo(Args&&...) fn void = { } auto template { { e = T::error }; };\n" + "ScopedEnum1 se1; { enum class E : T { e = 0 = e ScopedEnum2 struct UnscopedEnum3 { T{ e = 4 }; };\n" + "arr[(int) E::e]; }; UnscopedEnum3 e2 = f()\n" + "{ { e = e1; T::error } int test1 ue2; g() { enum class E { e = T::error }; return E::e; } int test2 = }\n" + "namespace UnscopedEnum { template struct UnscopedEnum1 { E{ e = T::error }; }; UnscopedEnum1 { enum E : { e = 0 }; };\n" + "UnscopedEnum2 ue3; template struct UnscopedEnum3 { enum { }; }; int arr[E::e]; };\n" + "UnscopedEnum3 namespace template int f() { enum E { e }; T::error }; return (int) E(); } int test1 int g() { enum E { e = E };\n" + "E::e; } int test2 = g(); }"), InternalError); + } + + void garbageCode9() { + TODO_ASSERT_THROW(checkCode("enum { e = { } } ( ) { { enum { } } } { e } "), InternalError); + } + + void garbageCode10() { // #6127 + ASSERT_THROW_INTERNAL(checkCode("for( rl=reslist; rl!=NULL; rl=rl->next )"), SYNTAX); + } + + void garbageCode12() { // do not crash + checkCode("{ g; S (void) { struct } { } int &g; }"); + ignore_errout(); // we do not care about the output + } + + void garbageCode13() { + checkCode("struct C {} {} x"); + } + + void garbageCode15() { // Ticket #5203 + ASSERT_THROW_INTERNAL(checkCode("int f ( int* r ) { { int s[2] ; f ( s ) ; if ( ) } }"), SYNTAX); + } + + void garbageCode16() { + checkCode("{ } A() { delete }"); // #6080 + ignore_errout(); // we do not care about the output + } + + void garbageCode17() { + ASSERT_THROW_INTERNAL(checkCode("void h(int l) {\n" + " while\n" // Don't crash (#3870) + "}"), SYNTAX); + } + + void garbageCode18() { + ASSERT_THROW_INTERNAL(checkCode("switch(){case}"), SYNTAX); + } + + void garbageCode20() { + // #3953 (valgrind errors on garbage code) + ASSERT_EQUALS("void f ( 0 * ) ;", checkCode("void f ( 0 * ) ;")); + } + + void garbageCode21() { + // Ticket #3486 - Don't crash garbage code + ASSERT_THROW_INTERNAL(checkCode("void f()\n" + "{\n" + " (\n" + " x;\n" + " int a, a2, a2*x; if () ;\n" + " )\n" + "}"), SYNTAX); + } + + void garbageCode22() { + // Ticket #3480 - Don't crash garbage code + ASSERT_THROW_INTERNAL(checkCode("int f()\n" + "{\n" + " return if\n" + "}"), SYNTAX); + } + + void garbageCode23() { + //garbage code : don't crash (#3481) + ASSERT_THROW_EQUALS(checkCode("{\n" + " if (1) = x\n" + " else abort s[2]\n" + "}"), + InternalError, + "syntax error"); + } + + void garbageCode24() { + // don't crash (example from #6361) + ASSERT_THROW_INTERNAL(checkCode("float buffer[64];\n" + "main (void)\n" + "{\n" + " char *cptr;\n" + " cptr = (char *)buffer;\n" + " cptr += (-(long int) buffer & (16 * sizeof (float) - 1));\n" + "}\n"), SYNTAX); + ignore_errout(); // we do not care about the output + } + + void garbageCode25() { + // Ticket #2386 - Segmentation fault upon strange syntax + ASSERT_THROW_INTERNAL(checkCode("void f() {\n" + " switch ( x ) {\n" + " case struct Tree : break;\n" + " }\n" + "}"), SYNTAX); + ignore_errout(); // we do not care about the output + } + + void garbageCode26() { + // See tickets #2518 #2555 #4171 + ASSERT_THROW_INTERNAL(checkCode("void f() {\n" + " switch MAKEWORD(1)\n" + " {\n" + " case 0:\n" + " return;\n" + " }\n" + "}"), SYNTAX); + } + + void garbageCode27() { + ASSERT_THROW_INTERNAL(checkCode("int f() {\n" + " return if\n" + "}"), SYNTAX); + } + + void garbageCode28() { + // 5702 + checkCode("struct R1 {\n" + " int a;\n" + " R1 () : a { }\n" + "};"); + ignore_errout(); // we do not care about the output + } + + void garbageCode30() { + // simply survive - a syntax error would be even better (#5867) + checkCode("void f(int x) {\n" + " x = 42\n" + "}"); + ignore_errout(); // we do not care about the output + } + + void garbageCode31() { + ASSERT_THROW_INTERNAL(checkCode("typedef struct{}x[([],)]typedef e y;(y,x 0){}"), SYNTAX); + } + + void garbageCode33() { // #6613 + checkCode("main(()B{});"); + } + + // Bug #6626 crash: Token::astOperand2() const ( do while ) + void garbageCode34() { + const char code[] = "void foo(void) {\n" + " do\n" + " while (0);\n" + "}"; + ASSERT_THROW_INTERNAL(checkCode(code), SYNTAX); + } + + void garbageCode35() { + // ticket #2604 segmentation fault + ASSERT_THROW_INTERNAL(checkCode("sizeof <= A"), AST); + } + + void garbageCode36() { // #6334 + ASSERT_THROW_INTERNAL(checkCode("{ } < class template < > , { = } ; class... >\n" + "struct Y { }\n" + "class Types { }\n" + "( X < int > \"uses template\" ) ( < ( ) \"uses ;" + "( int int ::primary \"uses template\" ) int double \"uses )" + "::primary , \"uses template\" ;\n"), SYNTAX); + } + + void garbageCode37() { + // #5166 segmentation fault (invalid code) in lib/checkother.cpp:329 ( void * f { } void b ( ) { * f } ) + checkCode("void * f { } void b ( ) { * f }"); + ignore_errout(); // we do not care about the output + } + + void garbageCode38() { // Ticket #6666 + checkCode("{ f2 { } } void f3 () { delete[] } { }"); + ignore_errout(); // we do not care about the output + } + + void garbageCode40() { // #6620 + checkCode("{ ( ) () { virtual } ; { } E } A { : { } ( ) } * const ( ) const { }"); + // test doesn't seem to work on any platform: ASSERT_THROW(checkCode("{ ( ) () { virtual } ; { } E } A { : { } ( ) } * const ( ) const { }", "test.c"), InternalError); + } + + void garbageCode41() { // #6685 + checkCode(" { } { return } *malloc(__SIZE_TYPE__ size); *memcpy(void n); static * const () { memcpy (*slot, 3); } { (); } { }"); + } + + void garbageCode42() { // #5760 + checkCode("{ } * const ( ) { }"); + } + + void garbageCode43() { // #6703 + checkCode("int { }; struct A a = { }"); + } + + void garbageCode44() { // #6704 + ASSERT_THROW_INTERNAL(checkCode("{ { }; }; { class A : }; public typedef b;"), SYNTAX); + } + + void garbageCode45() { // #6608 + ASSERT_THROW_INTERNAL(checkCode("struct true template < > { = } > struct Types \"s\" ; static_assert < int > ;"), SYNTAX); + } + + void garbageCode46() { // #6705 + checkCode(" { bar(char *x); void foo (int ...) { struct } va_list ap; va_start(ap, size); va_arg(ap, (d)); }"); + ignore_errout(); // we do not care about the output + } + + void garbageCode47() { // #6706 + checkCode(" { { }; }; * new private: B: B;"); + } + + void garbageCode48() { // #6712 + checkCode(" { d\" ) d ...\" } int main ( ) { ( ) catch ( A a ) { { } catch ( ) \"\" } }"); + ignore_errout(); // we do not care about the output + } + + void garbageCode49() { // #6715 + ASSERT_THROW_INTERNAL(checkCode(" ( ( ) ) { } ( { ( __builtin_va_arg_pack ( ) ) ; } ) { ( int { ( ) ( ( ) ) } ( ) { } ( ) ) += ( ) }"), AST); + } + + void garbageCode51() { // #6719 + ASSERT_THROW_INTERNAL(checkCode(" (const \"C\" ...); struct base { int f2; base (int arg1, int arg2); }; global_base(0x55, 0xff); { ((global_base.f1 0x55) (global_base.f2 0xff)) { } } base::base(int arg1, int arg2) { f2 = }"), SYNTAX); + } + + void garbageCode53() { // #6721 + ASSERT_THROW_INTERNAL(checkCode("{ { } }; void foo (struct int i) { x->b[i] = = }"), SYNTAX); + } + + void garbageCode54() { // #6722 + ASSERT_THROW_INTERNAL(checkCode("{ typedef long ((pf) p) (); }"), SYNTAX); + } + + void garbageCode55() { // #6724 + ASSERT_THROW_INTERNAL(checkCode("() __attribute__((constructor)); { } { }"), SYNTAX); + } + + void garbageCode56() { // #6713 + ASSERT_THROW_INTERNAL(checkCode("void foo() { int a = 0; int b = ???; }"), AST); + } + + void garbageCode57() { // #6731 + ASSERT_THROW_INTERNAL(checkCode("{ } if () try { } catch (...) B::~B { }"), SYNTAX); + } + + void garbageCode58() { // #6732, #6762 + ASSERT_THROW_INTERNAL(checkCode("{ }> {= ~A()^{} }P { }"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("{= ~A()^{} }P { } { }> is"), SYNTAX); + } + + void garbageCode59() { // #6735 + ASSERT_THROW_INTERNAL(checkCode("{ { } }; char font8x8[256][8]"), SYNTAX); + } + + void garbageCode60() { // #6736 + ASSERT_THROW_INTERNAL(checkCode("{ } { } typedef int int_array[]; int_array &right ="), SYNTAX); + } + + void garbageCode61() { // #6737 + ASSERT_THROW_INTERNAL(checkCode("{ (const U&) }; { }; { }; struct U : virtual public"), SYNTAX); + } + + void garbageCode63() { // #6739 + ASSERT_THROW_INTERNAL(checkCode("{ } { } typedef int u_array[]; typedef u_array &u_array_ref; (u_array_ref arg) { } u_array_ref u_array_ref_gbl_obj0"), INTERNAL); + } + + void garbageCode64() { // #6740 + ASSERT_THROW_INTERNAL(checkCode("{ } foo(void (*bar)(void))"), SYNTAX); + } + + void garbageCode65() { // #6741 + // TODO write some syntax error + checkCode("{ } { } typedef int u_array[]; typedef u_array &u_array_ref; (u_array_ref arg) { } u_array_ref"); + } + + void garbageCode66() { // #6742 + ASSERT_THROW_INTERNAL(checkCode("{ { } }; { { } }; { }; class bar : public virtual"), SYNTAX); + } + + void garbageCode68() { // #6745 + checkCode("(int a[3]); typedef void (*fp) (void); fp"); + } + + void garbageCode69() { // #6746 + ASSERT_THROW_INTERNAL(checkCode("{ (make_mess, aux); } typedef void F(void); aux(void (*x)()) { } (void (*y)()) { } F*"), SYNTAX); + } + + void garbageCode70() { // #6747 + ASSERT_THROW_INTERNAL(checkCode("{ } __attribute__((constructor)) void"), SYNTAX); + } + + void garbageCode71() { // #6748 + ASSERT_THROW_INTERNAL(checkCode("( ) { } typedef void noattr_t ( ) ; noattr_t __attribute__ ( )"), SYNTAX); + } + + void garbageCode72() { // #6749 + ASSERT_THROW_INTERNAL(checkCode("{ } { } typedef void voidfn(void); = } } unsigned *d = (b b--) --*d"), SYNTAX); + } + + void garbageCode78() { // #6756 + ASSERT_THROW_INTERNAL(checkCode("( ) { [ ] } ( ) { } const_array_of_int ( ) { } typedef int A [ ] [ ] ; A a = { { } { } }"), SYNTAX); + ignore_errout(); // we do not care about the output + } + + void garbageCode79() { // #6757 + ASSERT_THROW_INTERNAL(checkCode("{ } { } typedef void ( func_type ) ( ) ; func_type & ( )"), SYNTAX); + } + + void garbageCode80() { // #6759 + ASSERT_THROW_INTERNAL(checkCode("( ) { ; ( ) ; ( * ) [ ] ; [ ] = ( ( ) ( ) h ) ! ( ( ) ) } { ; } { } head heads [ ] = ; = & heads [ 2 ]"), SYNTAX); + } + + void garbageCode81() { // #6760 + ASSERT_THROW_INTERNAL(checkCode("{ } [ ] { ( ) } { } typedef void ( *fptr1 ) ( ) const"), SYNTAX); + } + + void garbageCode82() { // #6761 + ASSERT_THROW_INTERNAL(checkCode("p(\"Hello \" 14) _yn(const size_t) typedef bool pfunk (*pfunk)(const size_t)"), SYNTAX); + } + + void garbageCode83() { // #6771 + ASSERT_THROW_INTERNAL(checkCode("namespace A { class } class A { friend C ; } { } ;"), SYNTAX); + } + + void garbageCode84() { // #6780 + ASSERT_THROW_INTERNAL(checkCode("int main ( [ ] ) { " " [ ] ; int i = 0 ; do { } ; } ( [ ] ) { }"), SYNTAX); // do not crash + } + + void garbageCode85() { // #6784 + checkCode("{ } { } typedef void ( *VoidFunc() ) ( ) ; VoidFunc"); // do not crash + } + + void garbageCode86() { // #6785 + ASSERT_THROW_INTERNAL(checkCode("{ } typedef char ( *( X ) ( void) , char ) ;"), SYNTAX); // do not crash + } + + void garbageCode87() { // #6788 + ASSERT_THROW_INTERNAL(checkCode("((X (128))) (int a) { v[ = {} (x 42) a] += }"), SYNTAX); // do not crash + } + + void garbageCode88() { // #6786 + ASSERT_THROW_INTERNAL(checkCode("( ) { ( 0 ) { ( ) } } g ( ) { i( ( false ?) ( ) : 1 ) ; } ;"), SYNTAX); // do not crash + } + + void garbageCode90() { // #6790 + ASSERT_THROW_INTERNAL(checkCode("{ } { } typedef int u_array [[ ] ; typedef u_array & u_array_ref] ( ) { } u_array_ref_gbl_obj0"), SYNTAX); // do not crash + } + + void garbageCode91() { // #6791 + ASSERT_THROW_INTERNAL(checkCode("typedef __attribute__((vector_size (16))) { return[ (v2df){ } ;] }"), SYNTAX); // throw syntax error + } + + void garbageCode92() { // #6792 + ASSERT_THROW_INTERNAL(checkCode("template < typename _Tp ( ( ) ; _Tp ) , decltype > { } { ( ) ( ) }"), SYNTAX); // do not crash + } + + void garbageCode94() { // #6803 + //checkCode("typedef long __m256i __attribute__ ( ( ( ) ) )[ ; ( ) { } typedef __m256i __attribute__ ( ( ( ) ) ) < ] ( ) { ; }"); + ASSERT_THROW_INTERNAL(checkCode("typedef long __m256i __attribute__ ( ( ( ) ) )[ ; ( ) { } typedef __m256i __attribute__ ( ( ( ) ) ) < ] ( ) { ; }"), SYNTAX); + } + + void garbageCode95() { // #6804 + ASSERT_THROW_INTERNAL(checkCode("{ } x x ; { } h h [ ] ( ) ( ) { struct x ( x ) ; int __attribute__ ( ) f ( ) { h - > first = & x ; struct x * n = h - > first ; ( ) n > } }"), AST); // do not crash + } + + void garbageCode96() { // #6807 + ASSERT_THROW_INTERNAL(checkCode("typedef J J[ ; typedef ( ) ( ) { ; } typedef J J ;] ( ) ( J cx ) { n } ;"), SYNTAX); // throw syntax error + } + + void garbageCode97() { // #6808 + ASSERT_THROW_INTERNAL(checkCode("namespace A {> } class A{ { }} class A : T< ;"), SYNTAX); + } + + void garbageCode98() { // #6838 + ASSERT_THROW_INTERNAL(checkCode("for (cocon To::ta@Taaaaaforconst oken aaaaaaaaaaaa5Dl()\n" + "const unsigned in;\n" + "fon *tok = f);.s(Token i = d-)L;"), SYNTAX); + } + + void garbageCode99() { // #6726 + ASSERT_THROW_INTERNAL_EQUALS(checkCode("{ xs :: i(:) ! ! x/5 ! !\n" + "i, :: a :: b integer, } foo2(x) :: j(:)\n" + "b type(*), d(:), a x :: end d(..), foo end\n" + "foo4 b d(..), a a x type(*), b foo2 b"), INTERNAL, "Internal error. AST cyclic dependency."); + } + + void garbageCode100() { // #6840 + ASSERT_THROW_INTERNAL(checkCode("( ) { ( i< ) } int foo ( ) { int i ; ( for ( i => 1 ) ; ) }"), SYNTAX); + } + + void garbageCode101() { // #6835 + // Reported case + ASSERT_THROW_INTERNAL(checkCode("template < class , =( , int) X = 1 > struct A { } ( ) { = } [ { } ] ( ) { A < void > 0 }"), SYNTAX); + // Reduced case + ASSERT_THROW_INTERNAL(checkCode("template < class =( , ) X = 1> struct A {}; A a;"), SYNTAX); + } + + void garbageCode102() { // #6846 + checkCode("struct Object { ( ) ; Object & operator= ( Object ) { ( ) { } if ( this != & b ) } }"); + ignore_errout(); // we do not care about the output + } + + void garbageCode103() { // #6824 + ASSERT_THROW_INTERNAL(checkCode("a f(r) int * r; { { int s[2]; [f(s); if () ] } }"), SYNTAX); + } + + void garbageCode104() { // #6847 + ASSERT_THROW_INTERNAL(checkCode("template < Types > struct S {> ( S < ) S >} { ( ) { } } ( ) { return S < void > ( ) } { ( )> >} { ( ) { } } ( ) { ( ) }"), SYNTAX); + } + + void garbageCode105() { // #6859 + ASSERT_THROW_INTERNAL(checkCode("void foo (int i) { int a , for (a 1; a( < 4; a++) if (a) (b b++) (b);) n++; }"), SYNTAX); + } + + void garbageCode106() { // #6880 + ASSERT_THROW_INTERNAL(checkCode("[ ] typedef typedef b_array b_array_ref [ ; ] ( ) b_array_ref b_array_ref_gbl_obj0 { ; { b_array_ref b_array_ref_gbl_obj0 } }"), SYNTAX); + } + + void garbageCode107() { // #6881 + TODO_ASSERT_THROW(checkCode("enum { val = 1{ }; { const} }; { } Bar { const int A = val const } ;"), InternalError); + } + + void garbageCode108() { // #6895 "segmentation fault (invalid code) in CheckCondition::isOppositeCond" + ASSERT_THROW_INTERNAL(checkCode("A( ) { } bool f( ) { ( ) F; ( ) { ( == ) if ( !=< || ( !A( ) && r[2] ) ) ( !A( ) ) ( ) } }"), SYNTAX); + } + + void garbageCode109() { // #6900 "segmentation fault (invalid code) in CheckStl::runSimplifiedChecks" + checkCode("( *const<> (( ) ) { } ( *const ( ) ( ) ) { } ( * const<> ( size_t )) ) { } ( * const ( ) ( ) ) { }"); + ignore_errout(); // we do not care about the output + } + + void garbageCode110() { // #6902 "segmentation fault (invalid code) in CheckStl::string_c_str" + ASSERT_THROW_INTERNAL(checkCode("( *const<> ( size_t ) ; foo ) { } * ( *const ( size_t ) ( ) ;> foo )< { }"), SYNTAX); + } + + void garbageCode111() { // #6907 + TODO_ASSERT_THROW(checkCode("enum { FOO = 1( ,) } {{ FOO }} ;"), InternalError); + } + + void garbageCode112() { // #6909 + TODO_ASSERT_THROW(checkCode("enum { FOO = ( , ) } {{ }}>> enum { FOO< = ( ) } { { } } ;"), InternalError); + } + + void garbageCode114() { // #2118 + checkCode("Q_GLOBAL_STATIC_WITH_INITIALIZER(Qt4NodeStaticData, qt4NodeStaticData, {\n" + " for (unsigned i = 0 ; i < count; i++) {\n" + " }\n" + "});"); + } + + void garbageCode115() { // #5506 + ASSERT_THROW_INTERNAL(checkCode("A template < int { int = -1 ; } template < int N > struct B { int [ A < N > :: zero ] ; } ; B < 0 > b ;"), UNKNOWN_MACRO); + } + + void garbageCode116() { // #5356 + ASSERT_THROW_INTERNAL(checkCode("struct template struct B { }; B < 0 > b;"), SYNTAX); + } + + void garbageCode117() { // #6121 + TODO_ASSERT_THROW(checkCode("enum E { f = {} };\n" + "int a = f;"), InternalError); + } + + void garbageCode118() { // #5600 - missing include causes invalid enum + ASSERT_THROW_INTERNAL(checkCode("enum {\n" + " NUM_OPCODES =\n" + // #include "definition" + "};\n" + "struct bytecode {};\n" + "jv jq_next() { opcode = ((opcode) +NUM_OPCODES);\n" + "}"), SYNTAX); + } + + void garbageCode119() { // #5598 + checkCode("{ { void foo() { struct }; template struct S { Used x; void bar() } auto f = [this] { }; } };"); + ignore_errout(); // we do not care about the output + } + + void garbageCode120() { // #4927 + checkCode("int main() {\n" + " return 0\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void garbageCode121() { // #2585 + ASSERT_THROW_INTERNAL(checkCode("abcdef?" "?<" + "123456?" "?>" + "+?" "?="), SYNTAX); + } + + void garbageCode122() { // #6303 + checkCode("void foo() {\n" + "char *a = malloc(10);\n" + "a[0]\n" + "}"); + ignore_errout(); // we do not care about the output + } + + void garbageCode123() { + checkCode("namespace pr16989 {\n" + " class C {\n" + " C tpl_mem(T *) { return }\n" + " };\n" + "}"); + ignore_errout(); // we do not care about the output + } + + void garbageCode125() { + ASSERT_THROW_INTERNAL(checkCode("{ T struct B : T valueA_AA ; } T : [ T > ( ) { B } template < T > struct A < > : ] { ( ) { return valueA_AC struct { : } } b A < int > AC ( ) a_aa.M ; ( ) ( ) }"), UNKNOWN_MACRO); + ASSERT_THROW_INTERNAL(checkCode("template < Types > struct S :{ ( S < ) S >} { ( ) { } } ( ) { return S < void > ( ) }"), + SYNTAX); + } + + void garbageCode126() { + ASSERT_THROW_INTERNAL(checkCode("{ } float __ieee754_sinhf ( float x ) { float t , , do { gf_u ( jx ) { } ( 0 ) return ; ( ) { } t } ( 0x42b17180 ) { } }"), + SYNTAX); + } + + void garbageCode127() { // #6667 + checkCode("extern \"C\" int printf(const char* fmt, ...);\n" + "class A {\n" + "public:\n" + " int Var;\n" + " A(int arg) { Var = arg; }\n" + " ~A() { printf(\"A d'tor\\n\"); }\n" + "};\n" + " const A& foo(const A& arg) { return arg; }\n" + " foo(A(12)).Var"); + ignore_errout(); // we do not care about the output + } + + void garbageCode128() { + TODO_ASSERT_THROW(checkCode("enum { FOO = ( , ) } {{ }} enum {{ FOO << = } ( ) } {{ }} ;"), + InternalError); + } + + void garbageCode129() { + ASSERT_THROW_INTERNAL(checkCode("operator - ( { } typedef typename x ; ( ) ) { ( { { ( ( ) ) } ( { } ) } ) }"), + SYNTAX); + } + + void garbageCode130() { + TODO_ASSERT_THROW(checkCode("enum { FOO = ( , ){ } { { } } { { FOO} = } ( ) } { { } } enumL\" ( enumL\" { { FOO } ( ) } { { } } ;"), + InternalError); + } + + void garbageCode131() { + ASSERT_THROW_INTERNAL(checkCode("( void ) { ( ) } ( ) / { ( ) }"), SYNTAX); + // actually the invalid code should trigger an syntax error... + } + + void garbageCode132() { // #7022 + ASSERT_THROW_INTERNAL(checkCode("() () { } { () () ({}) i() } void i(void(*ptr) ()) { ptr(!) () }"), SYNTAX); + } + + void garbageCode133() { + ASSERT_THROW_INTERNAL(checkCode("void f() {{}"), SYNTAX); + + ASSERT_THROW_INTERNAL(checkCode("void f()) {}"), SYNTAX); + + ASSERT_THROW_INTERNAL(checkCode("void f()\n" + "{\n" + " foo(;\n" + "}\n"), SYNTAX); + + ASSERT_THROW_INTERNAL(checkCode("void f()\n" + "{\n" + " for(;;){ foo();\n" + "}\n"), SYNTAX); + + ASSERT_THROW_INTERNAL(checkCode("void f()\n" + "{\n" + " a[10;\n" + "}\n"), SYNTAX); + + { + const char code[] = "{\n" + " a(\n" // <- error + "}\n" + "{\n" + " b());\n" + "}\n"; + ASSERT_EQUALS("[test.cpp:2] Unmatched '('. Configuration: ''.", getSyntaxError(code)); + } + + { + const char code[] = "void f() {\n" + " int x = 3) + 0;\n" // <- error: unmatched ) + "}\n"; + ASSERT_EQUALS("[test.cpp:2] Unmatched ')'. Configuration: ''.", getSyntaxError(code)); + } + + { + const char code[] = "void f() {\n" + " int x = (3] + 0;\n" // <- error: unmatched ] + "}\n"; + ASSERT_EQUALS("[test.cpp:2] Unmatched ']'. Configuration: ''.", getSyntaxError(code)); + } + + { + const char code[] = "void f() {\n" // <- error: unmatched { + " {\n" + "}\n"; + ASSERT_EQUALS("[test.cpp:1] Unmatched '{'. Configuration: ''.", getSyntaxError(code)); + } + } + + void garbageCode134() { + // Ticket #5605, #5759, #5762, #5774, #5823, #6059 + ASSERT_THROW_INTERNAL(checkCode("foo() template struct tuple Args> tuple { } main() { foo(); }"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("( ) template < T1 = typename = unused> struct Args { } main ( ) { foo < int > ( ) ; }"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("() template < T = typename = x > struct a {} { f () }"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("template < T = typename = > struct a { f }"), SYNTAX); + checkCode("struct S { int i, j; }; " + "template struct X {}; " + "X<&S::i, int> x = X<&S::i, int>(); " + "X<&S::j, int> y = X<&S::j, int>();"); + ignore_errout(); // we are not interested in the output + checkCode("template struct A {}; " + "template <> struct A {}; " + "void foo(const void* f = 0) {}"); + checkCode("template struct A { " + " static const int s = 0; " + "}; " + "A a;"); + checkCode("template class A {}; " + "template > class B {}; " + "template > class C { " + " C() : _a(0), _b(0) {} " + " int _a, _b; " + "};"); + checkCode("template struct A { " + " static int i; " + "}; " + "void f() { A::i = 0; }"); + ignore_errout(); // we are not interested in the output + } + + void garbageCode135() { // #4994 + checkCode("long f () {\n" + " return a >> extern\n" + "}\n" + "long a = 1 ;\n" + "long b = 2 ;"); + ignore_errout(); // we are not interested in the output + } + + void garbageCode136() { // #7033 + ASSERT_THROW_INTERNAL(checkCode("{ } () { void f() { node_t * n; for (; -n) {} } } { }"), + SYNTAX); + } + + void garbageCode137() { // #7034 + ASSERT_THROW_INTERNAL(checkCode("\" \" typedef signed char f; \" \"; void a() { f * s = () &[]; (; ) (; ) }"), SYNTAX); + } + + void garbageCode138() { // #6660 + checkCode("CS_PLUGIN_NAMESPACE_BEGIN(csparser)\n" + "{\n" + " struct foo\n" + " {\n" + " union\n" + " {};\n" + " } halo;\n" + "}\n" + "CS_PLUGIN_NAMESPACE_END(csparser)"); + ignore_errout(); // we are not interested in the output + } + + void garbageCode139() { // #6659 heap user after free: kernel: sm750_accel.c + ASSERT_THROW_INTERNAL(checkCode("void hw_copyarea() {\n" + " de_ctrl = (nDirection == RIGHT_TO_LEFT) ?\n" + " ( (0 & ~(((1 << (1 - (0 ? DE_CONTROL_DIRECTION))) - 1) << (0 ? DE_CONTROL_DIRECTION))) )\n" + " : 42;\n" + "}"), SYNTAX); + } + + void garbageCode140() { // #7035 + ASSERT_THROW_INTERNAL(checkCode("int foo(int align) { int off(= 0 % align; return off) ? \\ align - off : 0; \\ }"), SYNTAX); + } + + void garbageCode141() { // #7043 + TODO_ASSERT_THROW(checkCode("enum { X = << { X } } enum { X = X } = X ;"), InternalError); + } + + void garbageCode142() { // #7050 + ASSERT_THROW_INTERNAL(checkCode("{ } ( ) { void mapGraphs ( ) { node_t * n ; for (!oid n ) { } } } { }"), SYNTAX); + } + + void garbageCode143() { // #6922 + ASSERT_THROW_INTERNAL(checkCode("void neoProgramShadowRegs() {\n" + " int i;\n" + " Bool noProgramShadowRegs;\n" + " if (noProgramShadowRegs) {\n" + " } else {\n" + " switch (nPtr->NeoPanelWidth) {\n" + " case 1280:\n" + " VGAwCR(0x64,0x?? );\n" + " }\n" + " }\n" + "}"), AST); + } + + void garbageCode144() { // #6865 + ASSERT_THROW_INTERNAL(checkCode("template < typename > struct A { } ; template < typename > struct A < INVALID > : A < int[ > { }] ;"), SYNTAX); + } + + void garbageCode146() { // #7081 + ASSERT_THROW_INTERNAL(checkCode("void foo() {\n" + " ? std::cout << pow((, 1) << std::endl;\n" + " double () ^ {\n" + " int *p don't crash + checkCode("void f() {\n" + " int a;\n" + " do { a=do_something() } while (a);\n" + "}"); + } + + void garbageCode152() { // happened in travis, originally from llvm clang code + const char* code = "template \n" + "static std::string foo(char *Bla) {\n" + " while (Bla[1] && Bla[1] != ',') }\n"; + checkCode(code); + ignore_errout(); // we are not interested in the output + } + + void garbageCode153() { + TODO_ASSERT_THROW(checkCode("enum { X = << { X } } { X X } enum { X = << { ( X ) } } { } X */"), InternalError); + } + + void garbageCode154() { + checkCode("\"abc\"[];"); + } + + void garbageCode156() { // #7120 + ASSERT_THROW_INTERNAL(checkCode("struct {}a; d f() { c ? : } {}a.p"), AST); + } + + void garbageCode157() { // #7131 + ASSERT_THROW_INTERNAL(checkCode("namespace std {\n" + " template < typename >\n" + " void swap();\n" + "}" + "template std::swap\n"), SYNTAX); + } + + void garbageCode158() { // #3238 + checkCode("__FBSDID(\"...\");"); + } + + void garbageCode159() { // #7119 + ASSERT_THROW_INTERNAL(checkCode("({}typedef typename x;typename x!){({{}()})}"), SYNTAX); + } + + void garbageCode160() { // #7190 + ASSERT_THROW_INTERNAL(checkCode("f(a,b,c,d)float [ a[],d;int ] b[],c;{} "), SYNTAX); // don't hang + } + + + void garbageCodeFuzzerClientMode1() { + ASSERT_THROW_INTERNAL(checkCode("void f() { x= name2 & name3 name2 = | 0.1 , | 0.1 , | 0.1 name4 <= >( ); }"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("void f() { x = , * [ | + 0xff | > 0xff]; }"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("void f() { x = , | 0xff , 0.1 < ; }"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("void f() { x = [ 1 || ] ; }"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("void f1() { x = name6 1 || ? name3 [ ( 1 || +) ] ; }"), SYNTAX); + } + + void garbageValueFlow() { + // #6089 + const char* code = "{} int foo(struct, x1, struct x2, x3, int, x5, x6, x7)\n" + "{\n" + " (foo(s, , 2, , , 5, , 7)) abort()\n" + "}\n"; + ASSERT_THROW_INTERNAL(checkCode(code), SYNTAX); + + // 6122 survive garbage code + code = "; { int i ; for ( i = 0 ; = 123 ; ) - ; }"; + ASSERT_THROW_INTERNAL(checkCode(code), SYNTAX); + + code = "void f1() { for (int n = 0 n < 10 n++); }"; + ASSERT_THROW_INTERNAL(checkCode(code), SYNTAX); + } + + void garbageSymbolDatabase() { + checkCode("void f( { u = 1 ; } ) { }"); + + ASSERT_THROW_INTERNAL(checkCode("{ }; void namespace A::f; { g() { int } }"), SYNTAX); + + ASSERT_THROW_INTERNAL(checkCode("class Foo {}; class Bar : public Foo"), SYNTAX); + + checkCode("YY_DECL { switch (yy_act) {\n" + " case 65: YY_BREAK\n" + " case YY_STATE_EOF(block):\n" + " yyterminate();\n" + "} }"); // #5663 + ignore_errout(); // we are not interested in the output + } + + void garbageAST() { + ASSERT_THROW_INTERNAL(checkCode("N 1024 float a[N], b[N + 3], c[N]; void N; (void) i;\n" + "int #define for (i = avx_test i < c[i]; i++)\n" + "b[i + 3] = a[i] * {}"), SYNTAX); // Don't hang (#5787) + + checkCode("START_SECTION([EXTRA](bool isValid(const String &filename)))"); // Don't crash (#5991) + + // #8352 + ASSERT_THROW_INTERNAL(checkCode("else return % name5 name2 - =name1 return enum | { - name3 1 enum != >= 1 >= ++ { { || " + "{ return return { | { - name3 1 enum != >= 1 >= ++ { name6 | ; ++}}}}}}}"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("else return % name5 name2 - =name1 return enum | { - name3 1 enum != >= 1 >= ++ { { || " + "{ return return { | { - name3 1 enum != >= 1 >= ++ { { || ; ++}}}}}}}}"), SYNTAX); + } + + void templateSimplifierCrashes() { + checkCode( // #5950 + "struct A {\n" + " template operator T*();\n" + "};\n" + "\n" + "template <> A::operator char*(){ return 0; } // specialization\n" + "\n" + "int main() {\n" + " A a;\n" + " int *ip = a.operator int*();\n" + "}\n" + "\n" + "namespace PR5742 {\n" + " template struct A { };\n" + " struct S {\n" + " template operator T();\n" + " } s;\n" + " void f() {\n" + " s.operator A >();\n" + " }\n" + "}"); + + checkCode( // #6034 + "template class T, typename... Args>\n" + "struct foo > {\n" + " const bool value = true;\n" + "};\n" + "\n" + "template\n" + "struct int_\n" + "{};\n" + "\n" + "int main() {\n" + " foo >::value;\n" + "}"); + + checkCode( // #6117 + "template struct something_like_tuple\n" + "{};\n" + "template struct is_last {\n" + " static const bool value = false;\n" + "};\n" + "template class Tuple, typename ... Head>\n" + "struct is_last>\n" + "{\n" + " static const bool value = true;\n" + "};\n" + "\n" + "#define SA(X) static_assert (X, #X)\n" + "\n" + "typedef something_like_tuple something_like_tuple_t;\n" + "SA ((is_last::value == false));\n" + "SA ((is_last::value == false));"); + ignore_errout(); // we are not interested in the output + + checkCode( // #6225 + "template \n" + "void templ_fun_with_ty_pack() {}\n" + "\n" + "namespace PR20047 {\n" + " template \n" + " struct A {};\n" + " using AliasA = A;\n" + "}"); + + // #3449 + ASSERT_EQUALS("template < typename T > struct A ;\n" + "struct B { template < typename T > struct C } ;\n" + "{ } ;", + checkCode("template struct A;\n" + "struct B { template struct C };\n" + "{};")); + } + void garbageCode161() { + //7200 + ASSERT_THROW_INTERNAL(checkCode("{ }{ if () try { } catch (...)} B : : ~B { }"), SYNTAX); + } + + void garbageCode162() { + //7208 + ASSERT_THROW_INTERNAL(checkCode("return << >> x return << >> x ", false), SYNTAX); + } + + void garbageCode163() { + //7228 + ASSERT_THROW_INTERNAL(checkCode("typedef s f[](){typedef d h(;f)}", false), SYNTAX); + } + + void garbageCode164() { + //7234 + ASSERT_THROW_INTERNAL(checkCode("class d{k p;}(){d::d():B<()}"), SYNTAX); + } + + void garbageCode165() { + //7235 + ASSERT_THROW_INTERNAL(checkCode("for(;..)", false),SYNTAX); + } + + void garbageCode167() { + //7237 + ASSERT_THROW_INTERNAL(checkCode("class D00i000{:D00i000::}i"),SYNTAX); + } + + void garbageCode168() { + // 7246 + checkCode("long foo(void) { return *bar; }", false); + ignore_errout(); // we do not care about the output + } + + void garbageCode169() { + // 6713 + ASSERT_THROW_INTERNAL(checkCode("( ) { ( ) ; { return } switch ( ) i\n" + "set case break ; default: ( ) }", false), SYNTAX); + } + + void garbageCode170() { + // 7255 + ASSERT_THROW_INTERNAL(checkCode("d i(){{f*s=typeid(()0,)}}", false), SYNTAX); + } + + void garbageCode171() { + // 7270 + ASSERT_THROW_INTERNAL(checkCode("(){case()?():}:", false), SYNTAX); + } + + void garbageCode172() { + // #7357 + ASSERT_THROW_INTERNAL(checkCode("p>,"), SYNTAX); + } + + void garbageCode173() { + // #6781 heap corruption ; TemplateSimplifier::simplifyTemplateInstantiations + ASSERT_THROW_INTERNAL(checkCode(" template < Types > struct S : >( S < ...Types... > S <) > { ( ) { } } ( ) { return S < void > ( ) }"), SYNTAX); + } + + void garbageCode174() { // #7356 + ASSERT_THROW_INTERNAL(checkCode("{r e() { w*constD = (())D = cast< }}"), SYNTAX); + } + + void garbageCode175() { // #7027 + ASSERT_THROW_INTERNAL(checkCode("int f() {\n" + " int i , j;\n" + " for ( i = t3 , i < t1 ; i++ )\n" + " for ( j = 0 ; j < = j++ )\n" + " return t1 ,\n" + "}"), SYNTAX); + } + + void garbageCode176() { // #7527 + checkCode("class t { { struct } enum class f : unsigned { q } b ; operator= ( T ) { switch ( b ) { case f::q: } } { assert ( b ) ; } } { ; & ( t ) ( f::t ) ; } ;"); + ignore_errout(); // we are not interested in the output + } + + void garbageCode181() { + ASSERT_THROW_INTERNAL(checkCode("int test() { int +; }"), SYNTAX); + } + + // #4195 - segfault for "enum { int f ( ) { return = } r = f ( ) ; }" + void garbageCode182() { + ASSERT_THROW_INTERNAL(checkCode("enum { int f ( ) { return = } r = f ( ) ; }"), SYNTAX); + } + // #7505 - segfault + void garbageCode183() { + ASSERT_THROW_INTERNAL(checkCode("= { int } enum return { r = f() f(); }"), SYNTAX); + } + + void garbageCode184() { // #7699 + ASSERT_THROW_INTERNAL(checkCode("unsigned int AquaSalSystem::GetDisplayScreenCount() {\n" + " NSArray* pScreens = [NSScreen screens];\n" + " return pScreens ? [pScreens count] : 1;\n" + "}"), SYNTAX); + } + + void garbageCode185() { // #6011 crash in libreoffice failure to create proper AST + checkCode( + "namespace binfilter\n" + "{\n" + " BOOL EnhWMFReader::ReadEnhWMF()\n" + " {\n" + " pOut->CreateObject( nIndex, GDI_BRUSH, new WinMtfFillStyle( ReadColor(), ( nStyle == BS_HOLLOW ) ? TRUE : FALSE ) );\n" + " return bStatus;\n" + " };\n" + "}"); + ignore_errout(); // we are not interested in the output + } + + // #8151 - segfault due to incorrect template syntax + void garbageCode186() { + ASSERT_THROW_INTERNAL(checkCode("A<>C"), SYNTAX); + } + + void garbageCode187() { // # 8152 - segfault in handling + const char inp[] = "0|\0|0>;\n"; + ASSERT_THROW_INTERNAL(checkCode(inp), SYNTAX); + + checkCode("template struct S : A< B || C > {};"); // No syntax error: #8390 + checkCode("static_assert(A || B, ab);"); + } + + void garbageCode188() { // #8255 + ASSERT_THROW_INTERNAL(checkCode("{z r(){(){for(;<(x);){if(0==0)}}}}"), SYNTAX); + } + + void garbageCode189() { // #8317 + checkCode("t&n(){()()[](){()}}$"); + } + + void garbageCode190() { // #8307 + ASSERT_THROW_INTERNAL(checkCode("void foo() {\n" + " int i;\n" + " i *= 0;\n" + " !i <;\n" + "}"), + AST); + } + + void garbageCode191() { // #8333 + ASSERT_THROW_INTERNAL(checkCode("struct A { int f(const); };"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("struct A { int f(int, const, char); };"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("struct A { int f(struct); };"), SYNTAX); + + // The following code is valid and should not trigger any error + checkCode("struct A { int f ( char ) ; } ;"); + } + + void garbageCode192() { // #8386 (segmentation fault) + ASSERT_THROW_INTERNAL(checkCode("{(()[((0||0xf||))]0[])}"), SYNTAX); + } + + // #8740 + void garbageCode193() { + ASSERT_THROW_INTERNAL(checkCode("d f(){!=[]&&0()!=0}"), SYNTAX); + } + + // #8384 + void garbageCode194() { + ASSERT_THROW_INTERNAL(checkCode("{((()))(return 1||);}"), SYNTAX); + } + + // #8709 - no garbage but to avoid stability regression + void garbageCode195() { + checkCode("a b;\n" + "void c() {\n" + " switch (d) { case b:; }\n" + " double e(b);\n" + " if(e <= 0) {}\n" + "}"); + ignore_errout(); // we do not care about the output + } + + // #8265 + void garbageCode196() { + ASSERT_THROW_INTERNAL(checkCode("0|,0<= void { ( ()) } 1 name3 return >= >= ( ) >= name5 (\n" + " name5 name6 :nam00 [ ()])}))})})})};\n" + "}"), SYNTAX); + } + + // #8752 + void garbageCode199() { + checkCode("d f(){e n00e0[]n00e0&" "0+f=0}"); + ignore_errout(); // we do not care about the output + } + + // #8757 + void garbageCode200() { + ASSERT_THROW_INTERNAL(checkCode("(){e break,{(case)!{e:[]}}}"), SYNTAX); + } + + // #8873 + void garbageCode201() { + ASSERT_THROW_INTERNAL(checkCode("void f() { std::string s=\"abc\"; return s + }"), SYNTAX); + } + + // #8907 + void garbageCode202() { + ASSERT_THROW_INTERNAL(checkCode("void f() { UNKNOWN_MACRO(return); }"), UNKNOWN_MACRO); + ASSERT_THROW_INTERNAL(checkCode("void f() { UNKNOWN_MACRO(throw); }"), UNKNOWN_MACRO); + ignore_errout(); + } + + void garbageCode203() { // #8972 + ASSERT_THROW_INTERNAL(checkCode("{ > () {} }"), SYNTAX); + checkCode("template <> a > ::b();"); + } + + void garbageCode204() { + ASSERT_THROW_INTERNAL(checkCode("template ()> c; template a as() {} as>();"), SYNTAX); + } + + void garbageCode205() { + ASSERT_THROW_INTERNAL(checkCode("class CodeSnippetsEvent : public wxCommandEvent {\n" + "public :\n" + " CodeSnippetsEvent ( wxEventType commandType = wxEventType , int id = 0 ) ;\n" + " CodeSnippetsEvent ( const CodeSnippetsEvent & event ) ;\n" + "virtual wxEvent * Clone ( ) const { return new CodeSnippetsEvent ( * this ) ; }\n" + "private :\n" + " int m_SnippetID ;\n" + "} ;\n" + "const wxEventType wxEVT_CODESNIPPETS_GETFILELINKS = wxNewEventType ( )\n" + "CodeSnippetsEvent :: CodeSnippetsEvent ( wxEventType commandType , int id )\n" + ": wxCommandEvent ( commandType , id ) {\n" + "}\n" + "CodeSnippetsEvent :: CodeSnippetsEvent ( const CodeSnippetsEvent & Event )\n" + ": wxCommandEvent ( Event )\n" + ", m_SnippetID ( 0 ) {\n" + "}"), + INTERNAL); + } + + void garbageCode206() { + ASSERT_EQUALS("[test.cpp:1] syntax error: operator", getSyntaxError("void foo() { for (auto operator new : int); }")); + ASSERT_EQUALS("[test.cpp:1] syntax error: operator", getSyntaxError("void foo() { for (a operator== :) }")); + } + + void garbageCode207() { // #8750 + ASSERT_THROW_INTERNAL(checkCode("d f(){(.n00e0(return%n00e0''('')));}"), SYNTAX); + } + + void garbageCode208() { // #8753 + ASSERT_THROW_INTERNAL(checkCode("d f(){(for(((((0{t b;((((((((()))))))))}))))))}"), SYNTAX); + } + + void garbageCode209() { // #8756 + ASSERT_THROW_INTERNAL(checkCode("{(- -##0xf/-1 0)[]}"), SYNTAX); + } + + void garbageCode210() { // #8762 + ASSERT_THROW_INTERNAL(checkCode("{typedef typedef c n00e0[]c000(;n00e0&c000)}"), SYNTAX); + } + + void garbageCode211() { // #8764 + ASSERT_THROW_INTERNAL(checkCode("{typedef f typedef[]({typedef e e,>;typedef(((typedef struct A {};\n" + "template struct A {}; \n" + "A a;"); + } + + void garbageCode217() { // #10011 + ASSERT_THROW_INTERNAL(checkCode("void f() {\n" + " auto p;\n" + " if (g(p)) {}\n" + " assert();\n" + "}"), AST); + } + + void garbageCode218() { // #8763 + checkCode("d f(){t n0000 const[]n0000+0!=n0000,(0)}"); // don't crash + ignore_errout(); // we are not interested in the output + } + void garbageCode219() { // #10101 + checkCode("typedef void (*func) (addr) ;\n" + "void bar(void) {\n" + " func f;\n" + " f & = (func)42;\n" + "}\n"); // don't crash + ignore_errout(); // we are not interested in the output + } + void garbageCode220() { // #6832 + ASSERT_THROW_INTERNAL(checkCode("(){(){{()}}return;{switch()0 case(){}break;l:()}}\n"), SYNTAX); // don't crash + } + void garbageCode221() { + ASSERT_THROW_INTERNAL(checkCode("struct A<0<;\n"), SYNTAX); // don't crash + } + void garbageCode222() { // #10763 + ASSERT_THROW_INTERNAL(checkCode("template\n"), SYNTAX); // don't crash + } + void garbageCode223() { // #11639 + ASSERT_THROW_INTERNAL(checkCode("struct{}*"), SYNTAX); // don't crash + } + void garbageCode224() { + ASSERT_THROW_INTERNAL(checkCode("void f(){ auto* b = dynamic_cast (size_t); foo) { } *(*const (size_t)() ; foo) { }"), SYNTAX); // #6858 + ASSERT_THROW_INTERNAL(checkCode(">{ x while (y) z int = }"), SYNTAX); // #4175 + ASSERT_THROW_INTERNAL(checkCode("&p(!{}e x){({(0?:?){({})}()})}"), SYNTAX); // #7118 + ASSERT_THROW_INTERNAL(checkCode(" { struct { typename D4:typename Base }; };"), SYNTAX); // #3533 + ASSERT_THROW_INTERNAL(checkCode(" > template < . > struct Y < T > { = } ;\n"), SYNTAX); // #6108 + } + + void syntaxErrorLastToken() { + ASSERT_THROW_INTERNAL(checkCode("int *"), SYNTAX); // #7821 + ASSERT_THROW_INTERNAL(checkCode("x[y]"), SYNTAX); // #2986 + ASSERT_THROW_INTERNAL(checkCode("( ) &"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("|| #if #define <="), SYNTAX); // #2601 + ASSERT_THROW_INTERNAL(checkCode("f::y:y : \n"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("++4++ + + E++++++++++ + ch " "tp.oed5[.]"), SYNTAX); // #7074 + ASSERT_THROW_INTERNAL(checkCode("d a(){f s=0()8[]s?():0}*()?:0", false), SYNTAX); // #7236 + ASSERT_THROW_INTERNAL(checkCode("!2 : #h2 ?:", false), SYNTAX); // #7769 + ASSERT_THROW_INTERNAL(checkCode("--"), SYNTAX); + ASSERT_THROW_INTERNAL(checkCode("volatile true , test < test < #ifdef __ppc__ true ,"), SYNTAX); // #4169 + ASSERT_THROW_INTERNAL(checkCode("a,b--\n"), SYNTAX); // #2847 + ASSERT_THROW_INTERNAL(checkCode("x a[0] ="), SYNTAX); // #2682 + ASSERT_THROW_INTERNAL(checkCode("auto_ptr\n"), SYNTAX); // #2967 + ASSERT_THROW_INTERNAL(checkCode("char a[1]\n"), SYNTAX); // #2865 + ASSERT_THROW_INTERNAL(checkCode("<><<"), SYNTAX); // #2612 + ASSERT_THROW_INTERNAL(checkCode("z"), SYNTAX); // #2831 + ASSERT_THROW_INTERNAL(checkCode("><,f typedef name5 | ( , ;){ } (); }"), SYNTAX); // #9067 + ASSERT_THROW_INTERNAL(checkCode("void f() { x= {}( ) ( 'x')[ ] (); }"), SYNTAX); // #9068 + ASSERT_THROW_INTERNAL(checkCode("void f() { x= y{ } name5 y[ ] + y ^ name5 ^ name5 for ( ( y y y && y y y && name5 ++ int )); }"), SYNTAX); // #9069 + } + + void cliCode() { + // #8913 + ASSERT_NO_THROW(checkCode( + "public ref class LibCecSharp : public CecCallbackMethods {\n" + "array ^ FindAdapters(String ^ path) {}\n" + "bool GetDeviceInformation(String ^ port, LibCECConfiguration ^configuration, uint32_t timeoutMs) {\n" + "bool bReturn(false);\n" + "}\n" + "};")); + ignore_errout(); // we are not interested in the output + } + + void enumTrailingComma() { + ASSERT_THROW_INTERNAL(checkCode("enum ssl_shutdown_t {ssl_shutdown_none = 0,ssl_shutdown_close_notify = , } ;"), SYNTAX); // #8079 + } + + void nonGarbageCode1() { + checkCode("template class List {\n" + "public:\n" + " List();\n" + " virtual ~List();\n" + " template< class Predicate > u_int DeleteIf( const Predicate &pred );\n" + "};\n" + "template< class T >\n" + "template< class Predicate > int\n" + "List::DeleteIf( const Predicate &pred )\n" + "{}"); + ignore_errout(); // we are not interested in the output + + // #8749 + checkCode( + "struct A {\n" + " void operator+=(A&) && = delete;\n" + "};"); + + // #8788 + checkCode( + "struct foo;\n" + "void f() {\n" + " auto fn = []() -> foo* { return new foo(); };\n" + "}"); + ignore_errout(); // we do not care about the output + } +}; + +REGISTER_TEST(TestGarbage) diff --git a/cppcheck-2.14.0/test/testimportproject.cpp b/cppcheck-2.14.0/test/testimportproject.cpp new file mode 100644 index 00000000..466805b5 --- /dev/null +++ b/cppcheck-2.14.0/test/testimportproject.cpp @@ -0,0 +1,415 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "importproject.h" +#include "settings.h" +#include "filesettings.h" +#include "fixture.h" +#include "redirect.h" + +#include +#include +#include +#include +#include +#include + +class TestImporter : public ImportProject { +public: + using ImportProject::importCompileCommands; + using ImportProject::importCppcheckGuiProject; + + bool sourceFileExists(const std::string & /*file*/) override { + return true; + } +}; + + +class TestImportProject : public TestFixture { +public: + TestImportProject() : TestFixture("TestImportProject") {} + +private: + + void run() override { + TEST_CASE(setDefines); + TEST_CASE(setIncludePaths1); + TEST_CASE(setIncludePaths2); + TEST_CASE(setIncludePaths3); // macro names are case insensitive + TEST_CASE(importCompileCommands1); + TEST_CASE(importCompileCommands2); // #8563, #9567 + TEST_CASE(importCompileCommands3); // check with existing trailing / in directory + TEST_CASE(importCompileCommands4); // only accept certain file types + TEST_CASE(importCompileCommands5); // Windows/CMake/Ninja generated compile_commands.json + TEST_CASE(importCompileCommands6); // Windows/CMake/Ninja generated compile_commands.json with spaces + TEST_CASE(importCompileCommands7); // linux: "/home/danielm/cppcheck 2" + TEST_CASE(importCompileCommands8); // Windows: "C:\Users\danielm\cppcheck" + TEST_CASE(importCompileCommands9); + TEST_CASE(importCompileCommands10); // #10887: include path with space + TEST_CASE(importCompileCommands11); // include path order + TEST_CASE(importCompileCommandsArgumentsSection); // Handle arguments section + TEST_CASE(importCompileCommandsNoCommandSection); // gracefully handles malformed json + TEST_CASE(importCppcheckGuiProject); + TEST_CASE(ignorePaths); + } + + void setDefines() const { + FileSettings fs; + + ImportProject::fsSetDefines(fs, "A"); + ASSERT_EQUALS("A=1", fs.defines); + + ImportProject::fsSetDefines(fs, "A;B;"); + ASSERT_EQUALS("A=1;B=1", fs.defines); + + ImportProject::fsSetDefines(fs, "A;;B;"); + ASSERT_EQUALS("A=1;B=1", fs.defines); + + ImportProject::fsSetDefines(fs, "A;;B"); + ASSERT_EQUALS("A=1;B=1", fs.defines); + } + + void setIncludePaths1() const { + FileSettings fs; + std::list in(1, "../include"); + std::map variables; + ImportProject::fsSetIncludePaths(fs, "abc/def/", in, variables); + ASSERT_EQUALS(1U, fs.includePaths.size()); + ASSERT_EQUALS("abc/include/", fs.includePaths.front()); + } + + void setIncludePaths2() const { + FileSettings fs; + std::list in(1, "$(SolutionDir)other"); + std::map variables; + variables["SolutionDir"] = "c:/abc/"; + ImportProject::fsSetIncludePaths(fs, "/home/fred", in, variables); + ASSERT_EQUALS(1U, fs.includePaths.size()); + ASSERT_EQUALS("c:/abc/other/", fs.includePaths.front()); + } + + void setIncludePaths3() const { // macro names are case insensitive + FileSettings fs; + std::list in(1, "$(SOLUTIONDIR)other"); + std::map variables; + variables["SolutionDir"] = "c:/abc/"; + ImportProject::fsSetIncludePaths(fs, "/home/fred", in, variables); + ASSERT_EQUALS(1U, fs.includePaths.size()); + ASSERT_EQUALS("c:/abc/other/", fs.includePaths.front()); + } + + void importCompileCommands1() const { + REDIRECT; + constexpr char json[] = R"([{ + "directory": "/tmp", + "command": "gcc -DTEST1 -DTEST2=2 -o /tmp/src.o -c /tmp/src.c", + "file": "/tmp/src.c" + }])"; + std::istringstream istr(json); + TestImporter importer; + ASSERT_EQUALS(true, importer.importCompileCommands(istr)); + ASSERT_EQUALS(1, importer.fileSettings.size()); + ASSERT_EQUALS("TEST1=1;TEST2=2", importer.fileSettings.cbegin()->defines); + } + + void importCompileCommands2() const { + REDIRECT; + // Absolute file path +#ifdef _WIN32 + const char json[] = R"([{ + "directory": "C:/foo", + "command": "gcc -c /bar.c", + "file": "/bar.c" + }])"; + std::istringstream istr(json); + TestImporter importer; + ASSERT_EQUALS(true, importer.importCompileCommands(istr)); + ASSERT_EQUALS(1, importer.fileSettings.size()); + ASSERT_EQUALS("C:/bar.c", importer.fileSettings.cbegin()->filename); +#else + constexpr char json[] = R"([{ + "directory": "/foo", + "command": "gcc -c bar.c", + "file": "/bar.c" + }])"; + std::istringstream istr(json); + TestImporter importer; + ASSERT_EQUALS(true, importer.importCompileCommands(istr)); + ASSERT_EQUALS(1, importer.fileSettings.size()); + ASSERT_EQUALS("/bar.c", importer.fileSettings.cbegin()->filename); +#endif + } + + void importCompileCommands3() const { + REDIRECT; + const char json[] = R"([{ + "directory": "/tmp/", + "command": "gcc -c src.c", + "file": "src.c" + }])"; + std::istringstream istr(json); + TestImporter importer; + ASSERT_EQUALS(true, importer.importCompileCommands(istr)); + ASSERT_EQUALS(1, importer.fileSettings.size()); + ASSERT_EQUALS("/tmp/src.c", importer.fileSettings.cbegin()->filename); + } + + void importCompileCommands4() const { + REDIRECT; + constexpr char json[] = R"([{ + "directory": "/tmp/", + "command": "gcc -c src.mm", + "file": "src.mm" + }])"; + std::istringstream istr(json); + TestImporter importer; + ASSERT_EQUALS(true, importer.importCompileCommands(istr)); + ASSERT_EQUALS(0, importer.fileSettings.size()); + } + + void importCompileCommands5() const { + REDIRECT; + constexpr char json[] = + R"([{ + "directory": "C:/Users/dan/git/build-test-cppcheck-Desktop_Qt_5_15_0_MSVC2019_64bit-Debug", + "command": "C:\\PROGRA~2\\MICROS~1\\2019\\COMMUN~1\\VC\\Tools\\MSVC\\1427~1.291\\bin\\HostX64\\x64\\cl.exe /nologo /TP -IC:\\Users\\dan\\git\\test-cppcheck\\mylib\\src /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd -std:c++17 /Fomylib\\CMakeFiles\\mylib.dir\\src\\foobar\\mylib.cpp.obj /FdTARGET_COMPILE_PDB /FS -c C:\\Users\\dan\\git\\test-cppcheck\\mylib\\src\\foobar\\mylib.cpp", + "file": "C:\\Users\\dan\\git\\test-cppcheck\\mylib\\src\\foobar\\mylib.cpp" + }, + { + "directory": "C:/Users/dan/git/build-test-cppcheck-Desktop_Qt_5_15_0_MSVC2019_64bit-Debug", + "command": "C:\\PROGRA~2\\MICROS~1\\2019\\COMMUN~1\\VC\\Tools\\MSVC\\1427~1.291\\bin\\HostX64\\x64\\cl.exe /nologo /TP -IC:\\Users\\dan\\git\\test-cppcheck\\myapp\\src -Imyapp -IC:\\Users\\dan\\git\\test-cppcheck\\mylib\\src /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd -std:c++17 /Fomyapp\\CMakeFiles\\myapp.dir\\src\\main.cpp.obj /FdTARGET_COMPILE_PDB /FS -c C:\\Users\\dan\\git\\test-cppcheck\\myapp\\src\\main.cpp", + "file": "C:\\Users\\dan\\git\\test-cppcheck\\myapp\\src\\main.cpp" + }])"; + std::istringstream istr(json); + TestImporter importer; + ASSERT_EQUALS(true, importer.importCompileCommands(istr)); + ASSERT_EQUALS(2, importer.fileSettings.size()); + ASSERT_EQUALS("C:/Users/dan/git/test-cppcheck/mylib/src/", importer.fileSettings.cbegin()->includePaths.front()); + } + + void importCompileCommands6() const { + REDIRECT; + constexpr char json[] = + R"([{ + "directory": "C:/Users/dan/git/build-test-cppcheck-Desktop_Qt_5_15_0_MSVC2019_64bit-Debug", + "command": "C:\\PROGRA~2\\MICROS~1\\2019\\COMMUN~1\\VC\\Tools\\MSVC\\1427~1.291\\bin\\HostX64\\x64\\cl.exe /nologo /TP -IC:\\Users\\dan\\git\\test-cppcheck\\mylib\\src -I\"C:\\Users\\dan\\git\\test-cppcheck\\mylib\\second src\" /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd -std:c++17 /Fomylib\\CMakeFiles\\mylib.dir\\src\\foobar\\mylib.cpp.obj /FdTARGET_COMPILE_PDB /FS -c C:\\Users\\dan\\git\\test-cppcheck\\mylib\\src\\foobar\\mylib.cpp", + "file": "C:\\Users\\dan\\git\\test-cppcheck\\mylib\\src\\foobar\\mylib.cpp" + }, + { + "directory": "C:/Users/dan/git/build-test-cppcheck-Desktop_Qt_5_15_0_MSVC2019_64bit-Debug", + "command": "C:\\PROGRA~2\\MICROS~1\\2019\\COMMUN~1\\VC\\Tools\\MSVC\\1427~1.291\\bin\\HostX64\\x64\\cl.exe /nologo /TP -IC:\\Users\\dan\\git\\test-cppcheck\\myapp\\src -Imyapp -IC:\\Users\\dan\\git\\test-cppcheck\\mylib\\src /DWIN32 /D_WINDOWS /GR /EHsc /Zi /Ob0 /Od /RTC1 -MDd -std:c++17 /Fomyapp\\CMakeFiles\\myapp.dir\\src\\main.cpp.obj /FdTARGET_COMPILE_PDB /FS -c C:\\Users\\dan\\git\\test-cppcheck\\myapp\\src\\main.cpp", + "file": "C:\\Users\\dan\\git\\test-cppcheck\\myapp\\src\\main.cpp" + }])"; + std::istringstream istr(json); + TestImporter importer; + ASSERT_EQUALS(true, importer.importCompileCommands(istr)); + ASSERT_EQUALS(2, importer.fileSettings.size()); + ASSERT_EQUALS("C:/Users/dan/git/test-cppcheck/mylib/src/", importer.fileSettings.cbegin()->includePaths.front()); + ASSERT_EQUALS("C:/Users/dan/git/test-cppcheck/mylib/second src/", importer.fileSettings.cbegin()->includePaths.back()); + } + + + void importCompileCommands7() const { + REDIRECT; + // cmake -DFILESDIR="/some/path" .. + constexpr char json[] = + R"([{ + "directory": "/home/danielm/cppcheck 2/b/lib", + "command": "/usr/bin/c++ -DFILESDIR=\\\"/some/path\\\" -I\"/home/danielm/cppcheck 2/b/lib\" -isystem \"/home/danielm/cppcheck 2/externals\" \"/home/danielm/cppcheck 2/lib/astutils.cpp\"", + "file": "/home/danielm/cppcheck 2/lib/astutils.cpp" + }])"; + std::istringstream istr(json); + TestImporter importer; + ASSERT_EQUALS(true, importer.importCompileCommands(istr)); + ASSERT_EQUALS(1, importer.fileSettings.size()); + ASSERT_EQUALS("FILESDIR=\"/some/path\"", importer.fileSettings.cbegin()->defines); + ASSERT_EQUALS(1, importer.fileSettings.cbegin()->includePaths.size()); + ASSERT_EQUALS("/home/danielm/cppcheck 2/b/lib/", importer.fileSettings.cbegin()->includePaths.front()); + TODO_ASSERT_EQUALS("/home/danielm/cppcheck 2/externals/", + "/home/danielm/cppcheck 2/b/lib/", + importer.fileSettings.cbegin()->includePaths.back()); + } + + void importCompileCommands8() const { + REDIRECT; + // cmake -DFILESDIR="C:\Program Files\Cppcheck" -G"NMake Makefiles" .. + constexpr char json[] = + R"([{ + "directory": "C:/Users/danielm/cppcheck/build/lib", + "command": "C:\\PROGRA~2\\MICROS~2\\2017\\COMMUN~1\\VC\\Tools\\MSVC\\1412~1.258\\bin\\Hostx64\\x64\\cl.exe /nologo /TP -DFILESDIR=\"\\\"C:\\Program Files\\Cppcheck\\\"\" -IC:\\Users\\danielm\\cppcheck\\build\\lib -IC:\\Users\\danielm\\cppcheck\\lib -c C:\\Users\\danielm\\cppcheck\\lib\\astutils.cpp", + "file": "C:/Users/danielm/cppcheck/lib/astutils.cpp" + }])"; + std::istringstream istr(json); + TestImporter importer; + ASSERT_EQUALS(true, importer.importCompileCommands(istr)); // Do not crash + } + + void importCompileCommands9() const { + REDIRECT; + // IAR output (https://sourceforge.net/p/cppcheck/discussion/general/thread/608af51e0a/) + constexpr char json[] = + R"([{ + "arguments" : [ + "powershell.exe -WindowStyle Hidden -NoProfile -ExecutionPolicy Bypass -File d:\\Projekte\\xyz\\firmware\\app\\xyz-lib\\build.ps1 -IAR -COMPILER_PATH \"c:\\Program Files (x86)\\IAR Systems\\Embedded Workbench 9.0\" -CONTROLLER CC1310F128 -LIB LIB_PERMANENT -COMPILER_DEFINES \"CC1310_HFXO_FREQ=24000000 DEBUG\"" + ], + "directory" : "d:\\Projekte\\xyz\\firmware\\app", + "type" : "PRE", + "file": "1.c" + }])"; + std::istringstream istr(json); + TestImporter importer; + ASSERT_EQUALS(true, importer.importCompileCommands(istr)); + } + + void importCompileCommands10() const { // #10887 + REDIRECT; + constexpr char json[] = + R"([{ + "file": "/home/danielm/cppcheck/1/test folder/1.c" , + "directory": "", + "arguments": [ + "iccavr.exe", + "-I", + "/home/danielm/cppcheck/test folder" + ] + }])"; + std::istringstream istr(json); + TestImporter importer; + ASSERT_EQUALS(true, importer.importCompileCommands(istr)); + ASSERT_EQUALS(1, importer.fileSettings.size()); + const FileSettings &fs = importer.fileSettings.front(); + ASSERT_EQUALS("/home/danielm/cppcheck/test folder/", fs.includePaths.front()); + } + + void importCompileCommands11() const { // include path order + REDIRECT; + constexpr char json[] = + R"([{ + "file": "1.c" , + "directory": "/x", + "arguments": [ + "cc", + "-I", + "def", + "-I", + "abc" + ] + }])"; + std::istringstream istr(json); + TestImporter importer; + ASSERT_EQUALS(true, importer.importCompileCommands(istr)); + ASSERT_EQUALS(1, importer.fileSettings.size()); + const FileSettings &fs = importer.fileSettings.front(); + ASSERT_EQUALS("/x/def/", fs.includePaths.front()); + ASSERT_EQUALS("/x/abc/", fs.includePaths.back()); + } + + void importCompileCommandsArgumentsSection() const { + REDIRECT; + constexpr char json[] = "[ { \"directory\": \"/tmp/\"," + "\"arguments\": [\"gcc\", \"-c\", \"src.c\"]," + "\"file\": \"src.c\" } ]"; + std::istringstream istr(json); + TestImporter importer; + ASSERT_EQUALS(true, importer.importCompileCommands(istr)); + ASSERT_EQUALS(1, importer.fileSettings.size()); + ASSERT_EQUALS("/tmp/src.c", importer.fileSettings.cbegin()->filename); + } + + void importCompileCommandsNoCommandSection() const { + REDIRECT; + constexpr char json[] = "[ { \"directory\": \"/tmp/\"," + "\"file\": \"src.mm\" } ]"; + std::istringstream istr(json); + TestImporter importer; + ASSERT_EQUALS(false, importer.importCompileCommands(istr)); + ASSERT_EQUALS(0, importer.fileSettings.size()); + ASSERT_EQUALS("cppcheck: error: no 'arguments' or 'command' field found in compilation database entry\n", GET_REDIRECT_OUTPUT); + } + + void importCppcheckGuiProject() const { + REDIRECT; + constexpr char xml[] = "\n" + "\n" + " \n" + " out1\n" + " true\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " test test\n" + "\n"; + std::istringstream istr(xml); + Settings s; + TestImporter project; + ASSERT_EQUALS(true, project.importCppcheckGuiProject(istr, &s)); + ASSERT_EQUALS(1, project.guiProject.pathNames.size()); + ASSERT_EQUALS("cli/", project.guiProject.pathNames[0]); + ASSERT_EQUALS(1, s.includePaths.size()); + ASSERT_EQUALS("lib/", s.includePaths.front()); + } + + void ignorePaths() const { + FileSettings fs1, fs2; + fs1.filename = "foo/bar"; + fs2.filename = "qwe/rty"; + TestImporter project; + project.fileSettings = {std::move(fs1), std::move(fs2)}; + + project.ignorePaths({"*foo", "bar*"}); + ASSERT_EQUALS(2, project.fileSettings.size()); + + project.ignorePaths({"foo/*"}); + ASSERT_EQUALS(1, project.fileSettings.size()); + ASSERT_EQUALS("qwe/rty", project.fileSettings.front().filename); + + project.ignorePaths({ "*e/r*" }); + ASSERT_EQUALS(0, project.fileSettings.size()); + } + + // TODO: test fsParseCommand() + + // TODO: test vcxproj conditions + /* + + + + + Debug + x64 + + + + + CPPCHECKLIB_IMPORT + + + + + + + */ +}; + +REGISTER_TEST(TestImportProject) diff --git a/cppcheck-2.14.0/test/testincompletestatement.cpp b/cppcheck-2.14.0/test/testincompletestatement.cpp new file mode 100644 index 00000000..c3a65ed4 --- /dev/null +++ b/cppcheck-2.14.0/test/testincompletestatement.cpp @@ -0,0 +1,785 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "errortypes.h" +#include "helpers.h" +#include "settings.h" +#include "fixture.h" +#include "tokenize.h" + +#include +#include + +class TestIncompleteStatement : public TestFixture { +public: + TestIncompleteStatement() : TestFixture("TestIncompleteStatement") {} + +private: + const Settings settings = settingsBuilder().severity(Severity::warning).build(); + +#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) + void check_(const char* file, int line, const char code[], bool inconclusive = false) { + const Settings settings1 = settingsBuilder(settings).certainty(Certainty::inconclusive, inconclusive).build(); + + std::vector files(1, "test.cpp"); + Tokenizer tokenizer(settings1, *this); + PreprocessorHelper::preprocess(code, files, tokenizer, *this); + + // Tokenize.. + ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); + + // Check for incomplete statements.. + CheckOther checkOther(&tokenizer, &settings1, this); + checkOther.checkIncompleteStatement(); + } + + void run() override { + TEST_CASE(test1); + TEST_CASE(test2); + TEST_CASE(test3); + TEST_CASE(test4); + TEST_CASE(test5); + TEST_CASE(test6); + TEST_CASE(test7); + TEST_CASE(test_numeric); + TEST_CASE(void0); // #6327: No fp for statement "(void)0;" + TEST_CASE(intarray); + TEST_CASE(structarraynull); + TEST_CASE(structarray); + TEST_CASE(conditionalcall); // ; 0==x ? X() : Y(); + TEST_CASE(structinit); // #2462 : ABC abc{1,2,3}; + TEST_CASE(returnstruct); + TEST_CASE(cast); // #3009 : (struct Foo *)123.a = 1; + TEST_CASE(increment); // #3251 : FP for increment + TEST_CASE(cpp11init); // #5493 : int i{1}; + TEST_CASE(cpp11init2); // #8449 + TEST_CASE(cpp11init3); // #8995 + TEST_CASE(block); // ({ do_something(); 0; }) + TEST_CASE(mapindex); + TEST_CASE(commaoperator1); + TEST_CASE(commaoperator2); + TEST_CASE(redundantstmts); + TEST_CASE(vardecl); + TEST_CASE(archive); // ar & x + TEST_CASE(ast); + TEST_CASE(oror); // dostuff() || x=32; + } + + void test1() { + check("void foo()\n" + "{\n" + " const char def[] =\n" + " \"abc\";\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + } + + void test2() { + check("void foo()\n" + "{\n" + " \"abc\";\n" + "}"); + + ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant code: Found a statement that begins with string constant.\n", errout_str()); + } + + void test3() { + check("void foo()\n" + "{\n" + " const char *str[] = { \"abc\" };\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + } + + void test4() { + check("void foo()\n" + "{\n" + "const char *a =\n" + "{\n" + "\"hello \"\n" + "\"more \"\n" + "\"world\"\n" + "};\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + } + + void test5() { + check("void foo()\n" + "{\n" + " 50;\n" + "}"); + + ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant code: Found a statement that begins with numeric constant.\n", errout_str()); + } + + void test6() { + // don't crash + check("void f() {\n" + " 1 == (two + three);\n" + " 2 != (two + three);\n" + " (one + two) != (two + three);\n" + "}"); + } + + void test7() { // #9335 + check("namespace { std::string S = \"\"; }\n" + "\n" + "class C {\n" + "public:\n" + " explicit C(const std::string& s);\n" + "};\n" + "\n" + "void f() {\n" + " for (C c(S); ; ) {\n" + " (void)c;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void test_numeric() { + check("struct P {\n" + " double a;\n" + " double b;\n" + "};\n" + "void f() {\n" + " const P values[2] =\n" + " {\n" + " { 346.1,114.1 }, { 347.1,111.1 }\n" + " };\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + } + + void void0() { // #6327 + check("void f() { (void*)0; }"); + ASSERT_EQUALS("", errout_str()); + + check("#define X 0\n" + "void f() { X; }"); + ASSERT_EQUALS("", errout_str()); + } + + void intarray() { + check("int arr[] = { 100/2, 1*100 };"); + ASSERT_EQUALS("", errout_str()); + } + + void structarraynull() { + check("struct st arr[] = {\n" + " { 100/2, 1*100 }\n" + " { 90, 70 }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void structarray() { + check("struct st arr[] = {\n" + " { 100/2, 1*100 }\n" + " { 90, 70 }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void conditionalcall() { + check("void f() {\n" + " 0==x ? X() : Y();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void structinit() { + // #2462 - C++11 struct initialization + check("void f() {\n" + " ABC abc{1,2,3};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #6260 - C++11 array initialization + check("void foo() {\n" + " static const char* a[][2] {\n" + " {\"b\", \"\"},\n" + " };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #2482 - false positive for empty struct + check("struct A {};"); + ASSERT_EQUALS("", errout_str()); + + // #4387 - C++11 initializer list + check("A::A() : abc{0} {}"); + ASSERT_EQUALS("", errout_str()); + + // #5042 - C++11 initializer list + check("A::A() : abc::def{0} {}"); + ASSERT_EQUALS("", errout_str()); + + // #4503 - vector init + check("void f() { vector v{1}; }"); + ASSERT_EQUALS("", errout_str()); + } + + void returnstruct() { + check("struct s foo() {\n" + " return (struct s){0,0};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #4754 + check("unordered_map foo() {\n" + " return {\n" + " {\"hi\", \"there\"},\n" + " {\"happy\", \"sad\"}\n" + " };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct s foo() {\n" + " return (struct s){0};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void cast() { + check("void f() {\n" + " ((struct foo *)(0x1234))->xy = 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(const std::exception& e) {\n" // #10918 + " try {\n" + " dynamic_cast(e);\n" + " return true;\n" + " }\n" + " catch (...) {\n" + " return false;\n" + " }\n" + "}\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()); + + check("std::vector f(int* p) {\n" + " return std::vector({ p[0] });\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void cpp11init2() { + check("x handlers{\n" + " { \"mode2\", []() { return 2; } },\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void cpp11init3() { + check("struct A { void operator()(int); };\n" + "void f() {\n" + "A{}(0);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("template struct A { void operator()(int); };\n" + "void f() {\n" + "A{}(0);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void block() { + check("void f() {\n" + " ({ do_something(); 0; });\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + "out:\n" + " ({ do_something(); 0; });\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void mapindex() { + check("void f() {\n" + " map[{\"1\",\"2\"}]=0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void commaoperator1() { + check("void foo(int,const char*,int);\n" // #8827 + "void f(int value) {\n" + " foo(42,\"test\",42),(value&42);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Found suspicious operator ',', result is not used.\n", errout_str()); + + check("int f() {\n" // #11257 + " int y;\n" + " y = (3, 4);\n" + " return y;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Found suspicious operator ',', result is not used.\n", errout_str()); + } + + void commaoperator2() { + check("void f() {\n" + " for(unsigned int a=0, b; a<10; a++ ) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void g();\n" // #10952 + "bool f() {\n" + " return (void)g(), false;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int a, int b, int c, int d) {\n" + " Eigen::Vector4d V;\n" + " V << a, b, c, d;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { Eigen::Vector4d V; };\n" + "struct T { int a, int b, int c, int d; };\n" + "void f(S& s, const T& t) {\n" + " s.V << t.a, t.b, t.c, t.d;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { Eigen::Vector4d V[2]; };\n" + "void f(int a, int b, int c, int d) {\n" + " S s[1];\n" + " s[0].V[1] << a, b, c, d;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " a.b[4][3].c()->d << x , y, z;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct V {\n" + " Eigen::Vector3d& operator[](int i) { return v[i]; }\n" + " void f(int a, int b, int c);\n" + " Eigen::Vector3d v[1];\n" + "};\n" + "void V::f(int a, int b, int c) {\n" + " (*this)[0] << a, b, c;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #11359 + " struct S {\n" + " S(int x, int y) {}\n" + " } s(1, 2);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + // #8451 + void redundantstmts() { + check("void f1(int x) {\n" + " 1;\n" + " (1);\n" + " (char)1;\n" + " ((char)1);\n" + " !x;\n" + " (!x);\n" + " (unsigned int)!x;\n" + " ~x;\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:2]: (warning) Redundant code: Found a statement that begins with numeric constant.\n" + "[test.cpp:3]: (warning) Redundant code: Found a statement that begins with numeric constant.\n" + "[test.cpp:4]: (warning) Redundant code: Found a statement that begins with numeric constant.\n" + "[test.cpp:5]: (warning) Redundant code: Found a statement that begins with numeric constant.\n" + "[test.cpp:6]: (warning, inconclusive) Found suspicious operator '!', result is not used.\n" + "[test.cpp:7]: (warning, inconclusive) Found suspicious operator '!', result is not used.\n" + "[test.cpp:8]: (warning) Redundant code: Found unused cast of expression '!x'.\n" + "[test.cpp:9]: (warning, inconclusive) Found suspicious operator '~', result is not used.\n", + errout_str()); + + check("void f1(int x) { x; }", true); + ASSERT_EQUALS("[test.cpp:1]: (warning) Unused variable value 'x'\n", errout_str()); + + check("void f() { if (Type t; g(t)) {} }"); // #9776 + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) { static_cast(x); }"); + ASSERT_EQUALS("[test.cpp:1]: (warning) Redundant code: Found unused cast of expression 'x'.\n", errout_str()); + + check("void f(int x, int* p) {\n" + " static_cast(x);\n" + " (void)x;\n" + " static_cast(p);\n" + " (void*)p;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() { false; }"); // #10856 + ASSERT_EQUALS("[test.cpp:1]: (warning) Redundant code: Found a statement that begins with bool constant.\n", errout_str()); + + check("void f(int i) {\n" + " (float)(char)i;\n" + " static_cast((char)i);\n" + " (char)static_cast(i);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Redundant code: Found unused cast of expression 'i'.\n" + "[test.cpp:3]: (warning) Redundant code: Found unused cast of expression 'i'.\n" + "[test.cpp:4]: (warning) Redundant code: Found unused cast of expression 'i'.\n", + errout_str()); + + check("namespace M {\n" + " namespace N { typedef char T; }\n" + "}\n" + "void f(int i) {\n" + " (M::N::T)i;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Redundant code: Found unused cast of expression 'i'.\n", errout_str()); + + check("void f(int (g)(int a, int b)) {\n" // #10873 + " int p = 0, q = 1;\n" + " (g)(p, q);\n" + "}\n" + "void f() {\n" + " char buf[10];\n" + " (sprintf)(buf, \"%d\", 42);\n" + " (printf)(\"abc\");\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S; struct T; struct U;\n" + "void f() {\n" + " T t;\n" + " (S)(U)t;\n" + " (S)static_cast(t);\n" + " static_cast((U)t);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool b) { b ? true : false; }\n"); // #10865 + ASSERT_EQUALS("[test.cpp:1]: (warning) Redundant code: Found unused result of ternary operator.\n", errout_str()); + + check("struct S { void (*f)() = nullptr; };\n" // #10877 + "void g(S* s) {\n" + " (s->f == nullptr) ? nullptr : (s->f(), nullptr);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool b) {\n" + " g() ? true : false;\n" + " true ? g() : false;\n" + " false ? true : g();\n" + " g(b ? true : false, 1);\n" + " C c{ b ? true : false, 1 };\n" + " b = (b ? true : false);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int i) {\n" + " for (i; ;) {}\n" + " for ((long)i; ;) {}\n" + " for (1; ;) {}\n" + " for (true; ;) {}\n" + " for ('a'; ;) {}\n" + " for (L'b'; ;) {}\n" + " for (\"x\"; ;) {}\n" + " for (L\"y\"; ;) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Unused variable value 'i'\n" + "[test.cpp:3]: (warning) Redundant code: Found unused cast of expression 'i'.\n" + "[test.cpp:4]: (warning) Redundant code: Found a statement that begins with numeric constant.\n" + "[test.cpp:5]: (warning) Redundant code: Found a statement that begins with bool constant.\n" + "[test.cpp:6]: (warning) Redundant code: Found a statement that begins with character constant.\n" + "[test.cpp:7]: (warning) Redundant code: Found a statement that begins with character constant.\n" + "[test.cpp:8]: (warning) Redundant code: Found a statement that begins with string constant.\n" + "[test.cpp:9]: (warning) Redundant code: Found a statement that begins with string constant.\n", + errout_str()); + + check("struct S { bool b{}; };\n" + "struct T {\n" + " S s[2];\n" + " void g();\n" + "};\n" + "void f(const S& r, const S* p) {\n" + " r.b;\n" + " p->b;\n" + " S s;\n" + " (s.b);\n" + " T t, u[2];\n" + " t.s[1].b;\n" + " t.g();\n" + " u[0].g();\n" + " u[1].s[0].b;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7]: (warning) Redundant code: Found unused member access.\n" + "[test.cpp:8]: (warning) Redundant code: Found unused member access.\n" + "[test.cpp:10]: (warning) Redundant code: Found unused member access.\n" + "[test.cpp:12]: (warning) Redundant code: Found unused member access.\n" + "[test.cpp:15]: (warning) Redundant code: Found unused member access.\n", + errout_str()); + + check("struct S { int a[2]{}; };\n" + "struct T { S s; };\n" + "void f() {\n" + " int i[2];\n" + " i[0] = 0;\n" + " i[0];\n" // <-- + " S s[1];\n" + " s[0].a[1];\n" // <-- + " T t;\n" + " t.s.a[1];\n" // <-- + " int j[2][2][1] = {};\n" + " j[0][0][0];\n" // <-- + "}\n"); + ASSERT_EQUALS("[test.cpp:6]: (warning) Redundant code: Found unused array access.\n" + "[test.cpp:8]: (warning) Redundant code: Found unused array access.\n" + "[test.cpp:10]: (warning) Redundant code: Found unused array access.\n" + "[test.cpp:12]: (warning) Redundant code: Found unused array access.\n", + errout_str()); + + check("void g(std::map& map) {\n" + " int j[2]{};\n" + " int k[2] = {};\n" + " int l[]{ 1, 2 };\n" + " int m[] = { 1, 2 };\n" + " h(0, j[0], 1);\n" + " C c{ 0, j[0], 1 };\n" + " c[0];\n" + " int j[2][2][2] = {};\n" + " j[h()][0][0];\n" + " j[0][h()][0];\n" + " j[0][0][h()];\n" + " std::map M;\n" + " M[\"abc\"];\n" + " map[\"abc\"];\n" // #10928 + " std::auto_ptr app[4];" // #10919 + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { void* p; };\n" // #10875 + "void f(S s) {\n" + " delete (int*)s.p;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct T {\n" // #10874 + " T* p;\n" + "};\n" + "void f(T* t) {\n" + " for (decltype(t->p) (c) = t->p; ;) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int i, std::vector v);\n" // #10880 + "void g() {\n" + " f(1, { static_cast(nullptr) });\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { int i; };\n" // #10882 + "enum E {};\n" + "void f(const S* s) {\n" + " E e = (E)!s->i;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int* p) {\n" // #10932 + " int& r(*p[0]);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { int i; };\n" // #10917 + "bool f(S s) {\n" + " return [](int i) { return i > 0; }(s.i);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("extern int (*p);\n" // #10936 + "void f() {\n" + " for (int i = 0; ;) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("class T {};\n" // #10849 + "void f() {\n" + " auto g = [](const T* t) -> int {\n" + " const T* u{}, * v{};\n" + " return 0;\n" + " };\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("namespace N {\n" // #10876 + " template \n" + " inline void f() {}\n" + " template\n" + " void g(T& c) {\n" + " for (typename T::iterator v = c.begin(); v != c.end(); ++v) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::string a, std::string b) {\n" // #7529 + " const std::string s = \" x \" + a;\n" + " +\" y = \" + b;\n" + "}\n", /*inconclusive*/ true); + ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Found suspicious operator '+', result is not used.\n", errout_str()); + + check("void f() {\n" + " *new int;\n" + "}\n", /*inconclusive*/ true); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*', result is not used.\n", errout_str()); + + check("void f(int x, int y) {\n" // #12525 + " x * y;\n" + "}\n", /*inconclusive*/ true); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*', result is not used.\n", errout_str()); + + check("void f() {\n" // #5475 + " std::string(\"a\") + \"a\";\n" + "}\n" + "void f(std::string& a) {\n" + " a.erase(3) + \"suf\";\n" + "}\n", /*inconclusive*/ true); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious operator '+', result is not used.\n" + "[test.cpp:5]: (warning, inconclusive) Found suspicious operator '+', result is not used.\n", + errout_str()); + + check("void f(XMLElement& parent) {\n" // #11234 + " auto** elem = &parent.firstChild;\n" + "}\n", /*inconclusive*/ true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #11301 + " NULL;\n" + " nullptr;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Redundant code: Found a statement that begins with NULL constant.\n" + "[test.cpp:3]: (warning) Redundant code: Found a statement that begins with NULL constant.\n", + errout_str()); + + check("struct S { int i; };\n" // #6504 + "void f(S* s) {\n" + " (*s).i;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant code: Found unused member access.\n", errout_str()); + + check("int a[2];\n" // #11370 + "void f() {\n" + " auto g = [](decltype(a[0]) i) {};\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("enum E { E0 };\n" + "void f() {\n" + " E0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Redundant code: Found a statement that begins with enumerator constant.\n", + errout_str()); + + check("void f(int* a) {\n" // #12534 + " a[a[3]];\n" + " a[a[g()]];\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Redundant code: Found unused array access.\n", + errout_str()); + } + + void vardecl() { + // #8984 + check("void f() { a::b *c = d(); }", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() { std::vector *c; }", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() { a::b &c = d(); }", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() { std::vector &c; }", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() { a::b &&c = d(); }", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() { std::vector &&c; }", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() { char * const * a, * const * b; }", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() { char * const * a = 0, * volatile restrict * b; }", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() { char * const * a = 0, * volatile const * b; }", true); + ASSERT_EQUALS("", errout_str()); + } + + void archive() { + check("void f(Archive &ar) {\n" + " ar & x;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f(int ar) {\n" + " ar & x;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious operator '&', result is not used.\n", errout_str()); + } + + void ast() { + check("struct c { void a() const { for (int x=0; x;); } };", true); + ASSERT_EQUALS("", errout_str()); + } + + void oror() { + check("void foo() {\n" + " params_given (params, \"overrides\") || (overrides = \"1\");\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::ifstream& file) {\n" // #10930 + " int a{}, b{};\n" + " (file >> a) || (file >> b);\n" + " (file >> a) && (file >> b);\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + } +}; + +REGISTER_TEST(TestIncompleteStatement) diff --git a/cppcheck-2.14.0/test/testinternal.cpp b/cppcheck-2.14.0/test/testinternal.cpp new file mode 100644 index 00000000..403cd364 --- /dev/null +++ b/cppcheck-2.14.0/test/testinternal.cpp @@ -0,0 +1,527 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "fixture.h" +#include "helpers.h" +#include "settings.h" + +class TestInternal : public TestFixture { +public: + TestInternal() : TestFixture("TestInternal") {} + +private: + /*const*/ Settings settings; + + void run() override { + settings.addEnabled("internal"); + + TEST_CASE(simplePatternInTokenMatch); + TEST_CASE(complexPatternInTokenSimpleMatch); + TEST_CASE(simplePatternSquareBrackets); + TEST_CASE(simplePatternAlternatives); + TEST_CASE(missingPercentCharacter); + TEST_CASE(unknownPattern); + TEST_CASE(redundantNextPrevious); + TEST_CASE(internalError); + TEST_CASE(orInComplexPattern); + TEST_CASE(extraWhitespace); + TEST_CASE(checkRedundantTokCheck); + } + +#define check(code) check_(code, __FILE__, __LINE__) + void check_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check.. + runChecks(tokenizer, this); + } + + void simplePatternInTokenMatch() { + check("void f() {\n" + " const Token *tok;\n" + " Token::Match(tok, \";\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Found simple pattern inside Token::Match() call: \";\"\n", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::Match(tok, \"%type%\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::Match(tok, \"%or%\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Found simple pattern inside Token::Match() call: \"%or%\"\n", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::findmatch(tok, \";\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Found simple pattern inside Token::findmatch() call: \";\"\n", errout_str()); + } + + void complexPatternInTokenSimpleMatch() { + check("void f() {\n" + " const Token *tok;\n" + " Token::simpleMatch(tok, \"%type%\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::simpleMatch() call: \"%type%\"\n", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::findsimplematch(tok, \"%type%\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::findsimplematch() call: \"%type%\"\n", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::findsimplematch(tok, \"} !!else\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::findsimplematch() call: \"} !!else\"\n", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::findsimplematch(tok, \"foobar\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::findsimplematch(tok, \"%\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void simplePatternSquareBrackets() { + check("void f() {\n" + " const Token *tok;\n" + " Token::simpleMatch(tok, \"[\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::simpleMatch(tok, \"[ ]\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::simpleMatch(tok, \"[]\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::simpleMatch() call: \"[]\"\n", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::simpleMatch(tok, \"] [\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::simpleMatch(tok, \"] [ [abc]\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::simpleMatch() call: \"] [ [abc]\"\n", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::simpleMatch(tok, \"[.,;]\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::simpleMatch() call: \"[.,;]\"\n", errout_str()); + } + + void simplePatternAlternatives() { + check("void f() {\n" + " const Token *tok;\n" + " Token::simpleMatch(tok, \"||\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::simpleMatch(tok, \"|\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::simpleMatch(tok, \"a|b\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Found complex pattern inside Token::simpleMatch() call: \"a|b\"\n", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::simpleMatch(tok, \"|= 0\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::simpleMatch(tok, \"| 0 )\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void missingPercentCharacter() { + check("void f() {\n" + " const Token *tok;\n" + " Token::Match(tok, \"%type%\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::Match(tok, \"foo %type% bar\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Missing % at the end of string + check("void f() {\n" + " const Token *tok;\n" + " Token::Match(tok, \"%type\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"%type\"\n", errout_str()); + + // Missing % in the middle of a pattern + check("void f() {\n" + " const Token *tok;\n" + " Token::Match(tok, \"foo %type bar\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo %type bar\"\n", errout_str()); + + // Bei quiet on single % + check("void f() {\n" + " const Token *tok;\n" + " Token::Match(tok, \"foo % %type% bar\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " Token::Match(tok, \"foo % %type % bar\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo % %type % bar\"\n" + "[test.cpp:3]: (error) Unknown pattern used: \"%type %\"\n", errout_str()); + + // Find missing % also in 'alternatives' pattern + check("void f() {\n" + " const Token *tok;\n" + " Token::Match(tok, \"foo|%type|bar\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Missing percent end character in Token::Match() pattern: \"foo|%type|bar\"\n" + , errout_str()); + + // Make sure we don't take %or% for a broken %oror% + check("void f() {\n" + " const Token *tok;\n" + " Token::Match(tok, \"foo|%oror%|bar\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void unknownPattern() { + check("void f() {\n" + " Token::Match(tok, \"%typ%\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Unknown pattern used: \"%typ%\"\n", errout_str()); + + // Make sure we don't take %or% for a broken %oror% + check("void f() {\n" + " Token::Match(tok, \"%type%\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void redundantNextPrevious() { + check("void f() {\n" + " return tok->next()->previous();\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::next()' followed by 'Token::previous()' can be simplified.\n", errout_str()); + + check("void f() {\n" + " return tok->tokAt(5)->previous();\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::tokAt()' followed by 'Token::previous()' can be simplified.\n", errout_str()); + + check("void f() {\n" + " return tok->previous()->linkAt(5);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::previous()' followed by 'Token::linkAt()' can be simplified.\n", errout_str()); + + check("void f() {\n" + " tok->next()->previous(foo);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " return tok->next()->next();\n" // Simplification won't make code much shorter/readable + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " return tok->previous()->previous();\n" // Simplification won't make code much shorter/readable + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " return tok->tokAt(foo+bar)->tokAt();\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::tokAt()' followed by 'Token::tokAt()' can be simplified.\n", errout_str()); + + check("void f() {\n" + " return tok->tokAt(foo+bar)->link();\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::tokAt()' followed by 'Token::link()' can be simplified.\n", errout_str()); + + check("void f() {\n" + " tok->tokAt(foo+bar)->link(foo);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " return tok->next()->next()->str();\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::next()' followed by 'Token::str()' can be simplified.\n", errout_str()); + + check("void f() {\n" + " return tok->previous()->next()->str();\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Call to 'Token::previous()' followed by 'Token::next()' can be simplified.\n", errout_str()); + + } + + void internalError() { + // Make sure cppcheck does not raise an internal error of Token::Match ( Ticket #3727 ) + check("class DELPHICLASS X;\n" + "class Y {\n" + "private:\n" + " X* x;\n" + "};\n" + "class Z {\n" + " char z[1];\n" + " Z(){\n" + " z[0] = 0;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void orInComplexPattern() { + check("void f() {\n" + " Token::Match(tok, \"||\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Token::Match() pattern \"||\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\".\n", errout_str()); + + check("void f() {\n" + " Token::Match(tok, \"|\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Token::Match() pattern \"|\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\".\n", errout_str()); + + check("void f() {\n" + " Token::Match(tok, \"[|+-]\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " Token::Match(tok, \"foo | bar\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Token::Match() pattern \"foo | bar\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\".\n", errout_str()); + + check("void f() {\n" + " Token::Match(tok, \"foo |\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Token::Match() pattern \"foo |\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\".\n", errout_str()); + + check("void f() {\n" + " Token::Match(tok, \"bar foo|\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void extraWhitespace() { + // whitespace at the end + check("void f() {\n" + " const Token *tok;\n" + " Token::Match(tok, \"%str% \");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::Match() call: \"%str% \"\n", errout_str()); + + // whitespace at the begin + check("void f() {\n" + " const Token *tok;\n" + " Token::Match(tok, \" %str%\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::Match() call: \" %str%\"\n", errout_str()); + + // two whitespaces or more + check("void f() {\n" + " const Token *tok;\n" + " Token::Match(tok, \"%str% bar\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::Match() call: \"%str% bar\"\n", errout_str()); + + // test simpleMatch + check("void f() {\n" + " const Token *tok;\n" + " Token::simpleMatch(tok, \"foobar \");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::simpleMatch() call: \"foobar \"\n", errout_str()); + + // test findmatch + check("void f() {\n" + " const Token *tok;\n" + " Token::findmatch(tok, \"%str% \");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::findmatch() call: \"%str% \"\n", errout_str()); + + // test findsimplematch + check("void f() {\n" + " const Token *tok;\n" + " Token::findsimplematch(tok, \"foobar \");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Found extra whitespace inside Token::findsimplematch() call: \"foobar \"\n", errout_str()); + } + + void checkRedundantTokCheck() { + // findsimplematch + check("void f() {\n" + " const Token *tok;\n" + " if(tok && Token::findsimplematch(tok, \"foobar\")) {};\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout_str()); + + // findmatch + check("void f() {\n" + " const Token *tok;\n" + " if(tok && Token::findmatch(tok, \"%str% foobar\")) {};\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout_str()); + + // Match + check("void f() {\n" + " const Token *tok;\n" + " if(tok && Token::Match(tok, \"5str% foobar\")) {};\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " if(a && tok && Token::Match(tok, \"5str% foobar\")) {};\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " if(a && b && tok && Token::Match(tok, \"5str% foobar\")) {};\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " if(a && b && c && tok && Token::Match(tok, \"5str% foobar\")) {};\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " if(a && b && c && tok && d && Token::Match(tok, \"5str% foobar\")) {};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // simpleMatch + check("void f() {\n" + " const Token *tok;\n" + " if(tok && Token::simpleMatch(tok, \"foobar\")) {};\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout_str()); + + // Match + check("void f() {\n" + " const Token *tok;\n" + " if(tok->previous() && Token::Match(tok->previous(), \"5str% foobar\")) {};\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok->previous()\", match-function already checks if it is null.\n", errout_str()); + + // don't report: + // tok->previous() vs tok + check("void f() {\n" + " const Token *tok;\n" + " if(tok->previous() && Token::Match(tok, \"5str% foobar\")) {};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // tok vs tok->previous()) + check("void f() {\n" + " const Token *tok;\n" + " if(tok && Token::Match(tok->previous(), \"5str% foobar\")) {};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // tok->previous() vs tok->previous()->previous()) + check("void f() {\n" + " const Token *tok;\n" + " if(tok->previous() && Token::Match(tok->previous()->previous(), \"5str% foobar\")) {};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // if a && fn(a) triggers, make sure !a || !fn(a) triggers as well! + check("void f() {\n" + " const Token *tok;\n" + " if(!tok || !Token::simpleMatch(tok, \"foobar\")) {};\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout_str()); + + check("void f() {\n" + " const Token *tok;\n" + " if(a || !tok || !Token::simpleMatch(tok, \"foobar\")) {};\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok\", match-function already checks if it is null.\n", errout_str()); + + // if tok || !Token::simpleMatch... + check("void f() {\n" + " const Token *tok;\n" + " if(tok || !Token::simpleMatch(tok, \"foobar\")) {};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // if !tok || Token::simpleMatch... + check("void f() {\n" + " const Token *tok;\n" + " if(!tok || Token::simpleMatch(tok, \"foobar\")) {};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // something more complex + check("void f() {\n" + " const Token *tok;\n" + " if(!tok->previous()->previous() || !Token::simpleMatch(tok->previous()->previous(), \"foobar\")) {};\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Unnecessary check of \"tok->previous()->previous()\", match-function already checks if it is null.\n", errout_str()); + } +}; + +REGISTER_TEST(TestInternal) + +#endif // #ifdef CHECK_INTERNAL diff --git a/cppcheck-2.14.0/test/testio.cpp b/cppcheck-2.14.0/test/testio.cpp new file mode 100644 index 00000000..41ad948e --- /dev/null +++ b/cppcheck-2.14.0/test/testio.cpp @@ -0,0 +1,4919 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "config.h" +#include "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "platform.h" +#include "settings.h" + +#include + +class TestIO : public TestFixture { +public: + TestIO() : TestFixture("TestIO") {} + +private: + const Settings settings = settingsBuilder().library("std.cfg").library("windows.cfg").library("qt.cfg").build(); + /*const*/ Settings settings1 = settingsBuilder().library("std.cfg").library("windows.cfg").library("qt.cfg").severity(Severity::warning).severity(Severity::style).build(); + + void run() override { + 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(incompatibleFileOpen); + + TEST_CASE(testScanf1); // Scanf without field limiters + TEST_CASE(testScanf2); + TEST_CASE(testScanf3); // #3494 + TEST_CASE(testScanf4); // #ticket 2553 + TEST_CASE(testScanf5); // #10632 + + TEST_CASE(testScanfArgument); + TEST_CASE(testPrintfArgument); + TEST_CASE(testPrintfArgumentVariables); + TEST_CASE(testPosixPrintfScanfParameterPosition); // #4900 + + TEST_CASE(testMicrosoftPrintfArgument); // ticket #4902 + TEST_CASE(testMicrosoftScanfArgument); + TEST_CASE(testMicrosoftCStringFormatArguments); // ticket #4920 + TEST_CASE(testMicrosoftSecurePrintfArgument); + TEST_CASE(testMicrosoftSecureScanfArgument); + + TEST_CASE(testQStringFormatArguments); + + TEST_CASE(testTernary); // ticket #6182 + TEST_CASE(testUnsignedConst); // ticket #6132 + + TEST_CASE(testAstType); // #7014 + TEST_CASE(testPrintf0WithSuffix); // ticket #7069 + TEST_CASE(testReturnValueTypeStdLib); + + TEST_CASE(testPrintfTypeAlias1); + TEST_CASE(testPrintfAuto); // #8992 + TEST_CASE(testPrintfParenthesis); // #8489 + TEST_CASE(testStdDistance); // #10304 + TEST_CASE(testParameterPack); // #11289 + } + + struct CheckOptions + { + CheckOptions() = default; + bool inconclusive = false; + bool portability = false; + Platform::Type platform = Platform::Type::Unspecified; + bool onlyFormatStr = false; + bool cpp = true; + }; + +#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) + void check_(const char* file, int line, const char* code, const CheckOptions& options = make_default_obj()) { + // TODO: using dedicated Settings (i.e. copying it) object causes major slowdown + settings1.severity.setEnabled(Severity::portability, options.portability); + settings1.certainty.setEnabled(Certainty::inconclusive, options.inconclusive); + PLATFORM(settings1.platform, options.platform); + + // Tokenize.. + SimpleTokenizer tokenizer(settings1, *this); + ASSERT_LOC(tokenizer.tokenize(code, options.cpp), file, line); + + // Check.. + if (options.onlyFormatStr) { + CheckIO checkIO(&tokenizer, &settings1, this); + checkIO.checkWrongPrintfScanfArguments(); + return; + } + runChecks(tokenizer, this); + } + + 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" + "}", dinit(CheckOptions, $.platform = Platform::Type::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" + "}", dinit(CheckOptions, $.platform = Platform::Type::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" + "}", dinit(CheckOptions, $.platform = Platform::Type::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" + "}", dinit(CheckOptions, $.platform = Platform::Type::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" + "}", dinit(CheckOptions, $.platform = Platform::Type::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" + "}", dinit(CheckOptions, $.platform = Platform::Type::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" + "}", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("", errout_str()); + + check("void foo(FILE*& f) {\n" + " f = _tfopen(name, _T(\"r+\"));\n" + " fwrite(buffer, 5, 6, f);\n" + "}", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("", errout_str()); + + check("void foo(FILE*& f) {\n" + " f = _tfopen(name, _T(\"r+\"));\n" + " fwrite(buffer, 5, 6, f);\n" + "}", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("", errout_str()); + + check("void foo(FILE*& f) {\n" + " _wfopen_s(&f, name, L\"r+\");\n" + " fwrite(buffer, 5, 6, f);\n" + "}", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("", errout_str()); + + check("void foo(FILE*& f) {\n" + " _tfopen_s(&f, name, _T(\"r+\"));\n" + " fwrite(buffer, 5, 6, f);\n" + "}", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("", errout_str()); + + check("void foo(FILE*& f) {\n" + " _tfopen_s(&f, name, _T(\"r+\"));\n" + " fwrite(buffer, 5, 6, f);\n" + "}", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("", errout_str()); + + check("void foo(FILE*& f) {\n" + " f = tmpfile();\n" + " fwrite(buffer, 5, 6, f);\n" + "}", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("", errout_str()); + + // Write mode + check("void foo(FILE*& f) {\n" + " f = fopen(name, \"w\");\n" + " fwrite(buffer, 5, 6, f);\n" + " rewind(f);\n" + " fread(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Read operation on a file that was opened only for writing.\n", errout_str()); + + check("void foo(FILE*& f) {\n" + " f = fopen(name, \"w+\");\n" + " fread(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(FILE*& f) {\n" + " f = tmpfile();\n" + " fread(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Append mode + check("void foo(FILE*& f) {\n" + " f = fopen(name, \"a\");\n" + " fwrite(buffer, 5, 6, f);\n" + " rewind(f);\n" + " fread(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Repositioning operation performed on a file opened in append mode has no effect.\n" + "[test.cpp:5]: (error) Read operation on a file that was opened only for writing.\n", errout_str()); + + check("void foo(FILE*& f) {\n" + " f = fopen(name, \"a+\");\n" + " fread(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Variable declared locally + check("void foo() {\n" + " FILE* f = fopen(name, \"r\");\n" + " fwrite(buffer, 5, 6, f);\n" + " fclose(f);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout_str()); + + // Call unknown function + check("void foo(FILE*& f) {\n" + " f = fopen(name, \"a\");\n" + " fwrite(buffer, 5, 6, f);\n" + " bar(f);\n" + " fread(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // freopen and tmpfile + check("void foo(FILE*& f) {\n" + " f = freopen(name, \"r\", f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout_str()); + + check("void foo(FILE*& f) {\n" + " f = _wfreopen(name, L\"r\", f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}", dinit(CheckOptions, $.platform = Platform::Type::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" + "}", dinit(CheckOptions, $.platform = Platform::Type::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" + "}", dinit(CheckOptions, $.platform = Platform::Type::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" + "}", dinit(CheckOptions, $.platform = Platform::Type::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" + "}", dinit(CheckOptions, $.platform = Platform::Type::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" + "}", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("[test.cpp:3]: (error) Write operation on a file that was opened only for reading.\n", errout_str()); + + // Crash tests + check("void foo(FILE*& f) {\n" + " f = fopen(name, mode);\n" // No assertion failure (#3830) + " fwrite(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void fopen(std::string const &filepath, std::string const &mode);"); // #3832 + } + + void wrongMode_complex() { + check("void foo(FILE* f) {\n" + " if(a) f = fopen(name, \"w\");\n" + " else f = fopen(name, \"r\");\n" + " if(a) fwrite(buffer, 5, 6, f);\n" + " else fread(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " FILE* f;\n" + " if(a) f = fopen(name, \"w\");\n" + " else f = fopen(name, \"r\");\n" + " if(a) fwrite(buffer, 5, 6, f);\n" + " else fread(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " FILE* f = fopen(name, \"w\");\n" + " if(a) fwrite(buffer, 5, 6, f);\n" + " else fread(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Read operation on a file that was opened only for writing.\n", errout_str()); + } + + void useClosedFile() { + check("void foo(FILE*& f) {\n" + " fclose(f);\n" + " fwrite(buffer, 5, 6, f);\n" + " clearerr(f);\n" + " fread(buffer, 5, 6, f);\n" + " ungetc('a', f);\n" + " ungetwc(L'a', f);\n" + " rewind(f);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Used file that is not opened.\n" + "[test.cpp:4]: (error) Used file that is not opened.\n" + "[test.cpp:5]: (error) Used file that is not opened.\n" + "[test.cpp:6]: (error) Used file that is not opened.\n" + "[test.cpp:7]: (error) Used file that is not opened.\n" + "[test.cpp:8]: (error) Used file that is not opened.\n", errout_str()); + + check("void foo(FILE*& f) {\n" + " if(!ferror(f)) {\n" + " fclose(f);\n" + " return;" + " }\n" + " fwrite(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(FILE*& f) {\n" + " fclose(f);\n" + " f = fopen(name, \"r\");\n" + " fread(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(FILE*& f) {\n" + " f = fopen(name, \"r\");\n" + " f = g;\n" + " fwrite(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " FILE* f;\n" + " fwrite(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Used file that is not opened.\n", errout_str()); + + check("void foo() {\n" + " FILE* f(stdout);\n" + " fwrite(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" // #3965 + " FILE* f[3];\n" + " f[0] = fopen(name, mode);\n" + " fclose(f[0]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #4368: multiple functions + check("static FILE *fp = NULL;\n" + "\n" + "void close()\n" + "{\n" + " fclose(fp);\n" + "}\n" + "\n" + "void dump()\n" + "{\n" + " if (fp == NULL) return;\n" + " fprintf(fp, \"Here's the output.\\n\");\n" + "}\n" + "\n" + "int main()\n" + "{\n" + " fp = fopen(\"test.txt\", \"w\");\n" + " dump();\n" + " close();\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("static FILE *fp = NULL;\n" + "\n" + "void close()\n" + "{\n" + " fclose(fp);\n" + "}\n" + "\n" + "void dump()\n" + "{\n" + " fclose(fp);\n" + " fprintf(fp, \"Here's the output.\\n\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:11]: (error) Used file that is not opened.\n", errout_str()); + + // #4466 + check("void chdcd_parse_nero(FILE *infile) {\n" + " switch (mode) {\n" + " case 0x0300:\n" + " fclose(infile);\n" + " return;\n" + " case 0x0500:\n" + " fclose(infile);\n" + " return;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void chdcd_parse_nero(FILE *infile) {\n" + " switch (mode) {\n" + " case 0x0300:\n" + " fclose(infile);\n" + " exit(0);\n" + " case 0x0500:\n" + " fclose(infile);\n" + " return;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #4649 + check("void foo() {\n" + " struct {FILE *f1; FILE *f2;} a;\n" + " a.f1 = fopen(name,mode);\n" + " a.f2 = fopen(name,mode);\n" + " fclose(a.f1);\n" + " fclose(a.f2);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #1473 + check("void foo() {\n" + " FILE *a = fopen(\"aa\", \"r\");\n" + " while (fclose(a)) {}\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Used file that is not opened.\n", "", errout_str()); + + // #6823 + check("void foo() {\n" + " FILE f[2];\n" + " f[0] = fopen(\"1\", \"w\");\n" + " f[1] = fopen(\"2\", \"w\");\n" + " fclose(f[0]);\n" + " fclose(f[1]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #12236 + check("void f() {\n" + " FILE* f = fopen(\"abc\", \"r\");\n" + " decltype(fclose(f)) y;\n" + " y = fclose(f);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void fileIOwithoutPositioning() { + check("void foo(FILE* f) {\n" + " fwrite(buffer, 5, 6, f);\n" + " fread(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout_str()); + + check("void foo(FILE* f) {\n" + " fread(buffer, 5, 6, f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout_str()); + + check("void foo(FILE* f, bool read) {\n" + " if(read)\n" + " fread(buffer, 5, 6, f);\n" + " else\n" + " fwrite(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(FILE* f) {\n" + " fread(buffer, 5, 6, f);\n" + " fflush(f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(FILE* f) {\n" + " fread(buffer, 5, 6, f);\n" + " rewind(f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(FILE* f) {\n" + " fread(buffer, 5, 6, f);\n" + " fsetpos(f, pos);\n" + " fwrite(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(FILE* f) {\n" + " fread(buffer, 5, 6, f);\n" + " fseek(f, 0, SEEK_SET);\n" + " fwrite(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(FILE* f) {\n" + " fread(buffer, 5, 6, f);\n" + " long pos = ftell(f);\n" + " fwrite(buffer, 5, 6, f);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout_str()); + + // #6452 - member functions + check("class FileStream {\n" + " void insert(const ByteVector &data, ulong start);\n" + " void seek(long offset, Position p);\n" + " FileStreamPrivate *d;\n" + "};\n" + "void FileStream::insert(const ByteVector &data, ulong start) {\n" + " int bytesRead = fread(aboutToOverwrite.data(), 1, bufferLength, d->file);\n" + " seek(writePosition);\n" + " fwrite(buffer.data(), sizeof(char), buffer.size(), d->file);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class FileStream {\n" + " void insert(const ByteVector &data, ulong start);\n" + " FileStreamPrivate *d;\n" + "};\n" + "void FileStream::insert(const ByteVector &data, ulong start) {\n" + " int bytesRead = fread(aboutToOverwrite.data(), 1, bufferLength, d->file);\n" + " unknown(writePosition);\n" + " fwrite(buffer.data(), sizeof(char), buffer.size(), d->file);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class FileStream {\n" + " void insert(const ByteVector &data, ulong start);\n" + " FileStreamPrivate *d;\n" + "};\n" + "void known(int);\n" + "void FileStream::insert(const ByteVector &data, ulong start) {\n" + " int bytesRead = fread(aboutToOverwrite.data(), 1, bufferLength, d->file);\n" + " known(writePosition);\n" + " fwrite(buffer.data(), sizeof(char), buffer.size(), d->file);\n" + "}"); + ASSERT_EQUALS("[test.cpp:9]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout_str()); + + check("class FileStream {\n" + " void insert(const ByteVector &data, ulong start);\n" + " FileStreamPrivate *d;\n" + "};\n" + "void known(int);\n" + "void FileStream::insert(const ByteVector &data, ulong start) {\n" + " int bytesRead = fread(X::data(), 1, bufferLength, d->file);\n" + " known(writePosition);\n" + " fwrite(X::data(), sizeof(char), buffer.size(), d->file);\n" + "}"); + ASSERT_EQUALS("[test.cpp:9]: (error) Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.\n", errout_str()); + } + + void seekOnAppendedFile() { + check("void foo() {\n" + " FILE* f = fopen(\"\", \"a+\");\n" + " fseek(f, 0, SEEK_SET);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " FILE* f = fopen(\"\", \"w\");\n" + " fseek(f, 0, SEEK_SET);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " FILE* f = fopen(\"\", \"a\");\n" + " fseek(f, 0, SEEK_SET);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Repositioning operation performed on a file opened in append mode has no effect.\n", errout_str()); + + check("void foo() {\n" + " FILE* f = fopen(\"\", \"a\");\n" + " fflush(f);\n" + "}"); + ASSERT_EQUALS("", errout_str()); // #5578 + + check("void foo() {\n" + " FILE* f = fopen(\"\", \"a\");\n" + " fclose(f);\n" + " f = fopen(\"\", \"r\");\n" + " fseek(f, 0, SEEK_SET);\n" + "}"); + ASSERT_EQUALS("", errout_str()); // #6566 + } + + void fflushOnInputStream() { + check("void foo()\n" + "{\n" + " fflush(stdin);\n" + "}", dinit(CheckOptions, $.portability = true)); + ASSERT_EQUALS("[test.cpp:3]: (portability) fflush() called on input stream 'stdin' may result in undefined behaviour on non-linux systems.\n", errout_str()); + + check("void foo()\n" + "{\n" + " fflush(stdout);\n" + "}", dinit(CheckOptions, $.portability = true)); + ASSERT_EQUALS("", errout_str()); + + check("void foo(FILE*& f) {\n" + " f = fopen(path, \"r\");\n" + " fflush(f);\n" + "}", dinit(CheckOptions, $.portability = true)); + ASSERT_EQUALS("[test.cpp:3]: (portability) fflush() called on input stream 'f' may result in undefined behaviour on non-linux systems.\n", errout_str()); + + check("void foo(FILE*& f) {\n" + " f = fopen(path, \"w\");\n" + " fflush(f);\n" + "}", dinit(CheckOptions, $.portability = true)); + ASSERT_EQUALS("", errout_str()); + + check("void foo(FILE*& f) {\n" + " fflush(f);\n" + "}", dinit(CheckOptions, $.portability = true)); + ASSERT_EQUALS("", errout_str()); + } + + void incompatibleFileOpen() { + check("void foo() {\n" + " FILE *f1 = fopen(\"tmp\", \"wt\");\n" + " FILE *f2 = fopen(\"tmp\", \"rt\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) The file '\"tmp\"' is opened for read and write access at the same time on different streams\n", errout_str()); + } + + + void testScanf1() { + check("void foo() {\n" + " int a, b;\n" + " FILE *file = fopen(\"test\", \"r\");\n" + " a = fscanf(file, \"aa %s\", bar);\n" + " b = scanf(\"aa %S\", bar);\n" + " b = scanf(\"aa %ls\", bar);\n" + " sscanf(foo, \"%[^~]\", bar);\n" + " scanf(\"%dx%s\", &b, bar);\n" + " fclose(file);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) fscanf() without field width limits can crash with huge input data.\n" + "[test.cpp:5]: (warning) scanf() without field width limits can crash with huge input data.\n" + "[test.cpp:6]: (warning) scanf() without field width limits can crash with huge input data.\n" + "[test.cpp:7]: (warning) sscanf() without field width limits can crash with huge input data.\n" + "[test.cpp:8]: (warning) scanf() without field width limits can crash with huge input data.\n", errout_str()); + } + + void testScanf2() { + check("void foo() {\n" + " scanf(\"%5s\", bar);\n" // Width specifier given + " scanf(\"%5[^~]\", bar);\n" // Width specifier given + " scanf(\"aa%%s\", bar);\n" // No %s + " scanf(\"aa%d\", &a);\n" // No %s + " scanf(\"aa%ld\", &a);\n" // No %s + " scanf(\"%*[^~]\");\n" // Ignore input + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) scanf format string requires 0 parameters but 1 is given.\n", errout_str()); + } + + void testScanf3() { // ticket #3494 + check("void f() {\n" + " char str[8];\n" + " scanf(\"%7c\", str);\n" + " scanf(\"%8c\", str);\n" + " scanf(\"%9c\", str);\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Width 9 given in format string (no. 1) is larger than destination buffer 'str[8]', use %8c to prevent overflowing it.\n", errout_str()); + } + + void testScanf4() { // ticket #2553 + check("void f()\n" + "{\n" + " char str [8];\n" + " scanf (\"%70s\",str);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Width 70 given in format string (no. 1) is larger than destination buffer 'str[8]', use %7s to prevent overflowing it.\n", errout_str()); + } + + void testScanf5() { // #10632 + check("char s1[42], s2[42];\n" + "void test() {\n" + " scanf(\"%42s%42[a-z]\", s1, s2);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Width 42 given in format string (no. 1) is larger than destination buffer 's1[42]', use %41s to prevent overflowing it.\n" + "[test.cpp:3]: (error) Width 42 given in format string (no. 2) is larger than destination buffer 's2[42]', use %41[a-z] to prevent overflowing it.\n", errout_str()); + } + + +#define TEST_SCANF_CODE(format, type) \ + "void f(){" type " x; scanf(\"" format "\", &x);}" + +#define TEST_SCANF_ERR(format, formatStr, type) \ + "[test.c:1]: (warning) " format " in format string (no. 1) requires '" formatStr " *' but the argument type is '" type " *'.\n" + +#define TEST_SCANF_ERR_AKA_(file, format, formatStr, type, akaType) \ + "[" file ":1]: (portability) " format " in format string (no. 1) requires '" formatStr " *' but the argument type is '" type " * {aka " akaType " *}'.\n" + +#define TEST_SCANF_ERR_AKA(format, formatStr, type, akaType) \ + TEST_SCANF_ERR_AKA_("test.c", format, formatStr, type, akaType) + +#define TEST_SCANF_ERR_AKA_CPP(format, formatStr, type, akaType) \ + TEST_SCANF_ERR_AKA_("test.cpp", format, formatStr, type, akaType) + +#define TEST_PRINTF_CODE(format, type) \ + "void f(" type " x){printf(\"" format "\", x);}" + +#define TEST_PRINTF_ERR(format, requiredType, actualType) \ + "[test.c:1]: (warning) " format " in format string (no. 1) requires '" requiredType "' but the argument type is '" actualType "'.\n" + +#define TEST_PRINTF_ERR_AKA_(file, format, requiredType, actualType, akaType) \ + "[" file ":1]: (portability) " format " in format string (no. 1) requires '" requiredType "' but the argument type is '" actualType " {aka " akaType "}'.\n" + +#define TEST_PRINTF_ERR_AKA(format, requiredType, actualType, akaType) \ + TEST_PRINTF_ERR_AKA_("test.c", format, requiredType, actualType, akaType) + +#define TEST_PRINTF_ERR_AKA_CPP(format, requiredType, actualType, akaType) \ + TEST_PRINTF_ERR_AKA_("test.cpp", format, requiredType, actualType, akaType) + + void testFormatStrNoWarn(const char *filename, unsigned int linenr, const char* code, + bool cpp = false) { + check(code, dinit(CheckOptions, $.inconclusive = true, $.platform = Platform::Type::Unix32, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, emptyString, errout_str()); + check(code, dinit(CheckOptions, $.inconclusive = true, $.platform = Platform::Type::Unix64, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, emptyString, errout_str()); + check(code, dinit(CheckOptions, $.inconclusive = true, $.platform = Platform::Type::Win32A, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, emptyString, errout_str()); + check(code, dinit(CheckOptions, $.inconclusive = true, $.platform = Platform::Type::Win64, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, emptyString, errout_str()); + } + + void testFormatStrWarn(const char *filename, unsigned int linenr, + const char* code, const char* testScanfErrString, + bool cpp = false) { + check(code, dinit(CheckOptions, $.inconclusive = true, $.platform = Platform::Type::Unix32, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, testScanfErrString, errout_str()); + check(code, dinit(CheckOptions, $.inconclusive = true, $.platform = Platform::Type::Unix64, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, testScanfErrString, errout_str()); + check(code, dinit(CheckOptions, $.inconclusive = true, $.platform = Platform::Type::Win32A, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, testScanfErrString, errout_str()); + check(code, dinit(CheckOptions, $.inconclusive = true, $.platform = Platform::Type::Win64, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, testScanfErrString, errout_str()); + } + + void testFormatStrWarnAka(const char *filename, unsigned int linenr, + const char* code, const char* testScanfErrAkaString, const char* testScanfErrAkaWin64String, + bool cpp = false) { + check(code, dinit(CheckOptions, $.inconclusive = true, $.portability = true, $.platform = Platform::Type::Unix32, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, testScanfErrAkaString, errout_str()); + check(code, dinit(CheckOptions, $.inconclusive = true, $.portability = true, $.platform = Platform::Type::Unix64, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, testScanfErrAkaString, errout_str()); + check(code, dinit(CheckOptions, $.inconclusive = true, $.portability = true, $.platform = Platform::Type::Win32A, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, testScanfErrAkaString, errout_str()); + check(code, dinit(CheckOptions, $.inconclusive = true, $.portability = true, $.platform = Platform::Type::Win64, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, testScanfErrAkaWin64String, errout_str()); + } + + void testFormatStrWarnAkaWin64(const char *filename, unsigned int linenr, + const char* code, const char* testScanfErrAkaWin64String, + bool cpp = false) { + check(code, dinit(CheckOptions, $.inconclusive = true, $.portability = true, $.platform = Platform::Type::Unix32, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, emptyString, errout_str()); + check(code, dinit(CheckOptions, $.inconclusive = true, $.portability = true, $.platform = Platform::Type::Unix64, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, emptyString, errout_str()); + check(code, dinit(CheckOptions, $.inconclusive = true, $.portability = true, $.platform = Platform::Type::Win32A, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, emptyString, errout_str()); + check(code, dinit(CheckOptions, $.inconclusive = true, $.portability = true, $.platform = Platform::Type::Win64, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, testScanfErrAkaWin64String, errout_str()); + } + + void testFormatStrWarnAkaWin32(const char *filename, unsigned int linenr, + const char* code, const char* testScanfErrAkaString, + bool cpp = false) { + check(code, dinit(CheckOptions, $.inconclusive = true, $.portability = true, $.platform = Platform::Type::Unix32, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, testScanfErrAkaString, errout_str()); + check(code, dinit(CheckOptions, $.inconclusive = true, $.portability = true, $.platform = Platform::Type::Unix64, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, testScanfErrAkaString, errout_str()); + check(code, dinit(CheckOptions, $.inconclusive = true, $.portability = true, $.platform = Platform::Type::Win32A, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, testScanfErrAkaString, errout_str()); + check(code, dinit(CheckOptions, $.inconclusive = true, $.portability = true, $.platform = Platform::Type::Win64, $.onlyFormatStr = true, $.cpp = cpp)); + assertEquals(filename, linenr, emptyString, errout_str()); + } + +#define TEST_SCANF_NOWARN(FORMAT, FORMATSTR, TYPE) \ + testFormatStrNoWarn(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE)) +#define TEST_SCANF_NOWARN_CPP(FORMAT, FORMATSTR, TYPE) \ + testFormatStrNoWarn(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE), true) +#define TEST_SCANF_WARN(FORMAT, FORMATSTR, TYPE) \ + testFormatStrWarn(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE), TEST_SCANF_ERR(FORMAT, FORMATSTR, TYPE)) +#define TEST_SCANF_WARN_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE, AKATYPE_WIN64) \ + testFormatStrWarnAka(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE), TEST_SCANF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE), TEST_SCANF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64)) +#define TEST_SCANF_WARN_AKA_CPP(FORMAT, FORMATSTR, TYPE, AKATYPE, AKATYPE_WIN64) \ + testFormatStrWarnAka(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE), TEST_SCANF_ERR_AKA_CPP(FORMAT, FORMATSTR, TYPE, AKATYPE), TEST_SCANF_ERR_AKA_CPP(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64), true) +#define TEST_SCANF_WARN_AKA_WIN64(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64) \ + testFormatStrWarnAkaWin64(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE), TEST_SCANF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64)) +#define TEST_SCANF_WARN_AKA_CPP_WIN64(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64) \ + testFormatStrWarnAkaWin64(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE), TEST_SCANF_ERR_AKA_CPP(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64), true) +#define TEST_SCANF_WARN_AKA_WIN32(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN32) \ + testFormatStrWarnAkaWin32(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE), TEST_SCANF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN32)) +#define TEST_SCANF_WARN_AKA_CPP_WIN32(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN32) \ + testFormatStrWarnAkaWin32(__FILE__, __LINE__, TEST_SCANF_CODE(FORMAT, TYPE), TEST_SCANF_ERR_AKA_CPP(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN32), true) + +#define TEST_PRINTF_NOWARN(FORMAT, FORMATSTR, TYPE) \ + testFormatStrNoWarn(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE)) +#define TEST_PRINTF_NOWARN_CPP(FORMAT, FORMATSTR, TYPE) \ + testFormatStrNoWarn(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE), true) +#define TEST_PRINTF_WARN(FORMAT, FORMATSTR, TYPE) \ + testFormatStrWarn(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE), TEST_PRINTF_ERR(FORMAT, FORMATSTR, TYPE)) +#define TEST_PRINTF_WARN_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE, AKATYPE_WIN64) \ + testFormatStrWarnAka(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE), TEST_PRINTF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE), TEST_PRINTF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64)) +#define TEST_PRINTF_WARN_AKA_CPP(FORMAT, FORMATSTR, TYPE, AKATYPE, AKATYPE_WIN64) \ + testFormatStrWarnAka(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE), TEST_PRINTF_ERR_AKA_CPP(FORMAT, FORMATSTR, TYPE, AKATYPE), TEST_PRINTF_ERR_AKA_CPP(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64), true) +#define TEST_PRINTF_WARN_AKA_WIN64(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64) \ + testFormatStrWarnAkaWin64(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE), TEST_PRINTF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64)) +#define TEST_PRINTF_WARN_AKA_CPP_WIN64(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64) \ + testFormatStrWarnAkaWin64(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE), TEST_PRINTF_ERR_AKA_CPP(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN64), true) +#define TEST_PRINTF_WARN_AKA_WIN32(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN32) \ + testFormatStrWarnAkaWin32(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE), TEST_PRINTF_ERR_AKA(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN32)) +#define TEST_PRINTF_WARN_AKA_CPP_WIN32(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN32) \ + testFormatStrWarnAkaWin32(__FILE__, __LINE__, TEST_PRINTF_CODE(FORMAT, TYPE), TEST_PRINTF_ERR_AKA_CPP(FORMAT, FORMATSTR, TYPE, AKATYPE_WIN32), true) + + void testScanfArgument() { + check("void foo() {\n" + " scanf(\"%1d\", &foo);\n" + " sscanf(bar, \"%1d\", &foo);\n" + " scanf(\"%1u%1u\", &foo, bar());\n" + " scanf(\"%*1x %1x %29s\", &count, KeyName);\n" // #3373 + " fscanf(f, \"%7ms\", &ref);\n" // #3461 + " sscanf(ip_port, \"%*[^:]:%4d\", &port);\n" // #3468 + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " scanf(\"\", &foo);\n" + " scanf(\"%1d\", &foo, &bar);\n" + " fscanf(bar, \"%1d\", &foo, &bar);\n" + " scanf(\"%*1x %1x %29s\", &count, KeyName, foo);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) scanf format string requires 0 parameters but 1 is given.\n" + "[test.cpp:3]: (warning) scanf format string requires 1 parameter but 2 are given.\n" + "[test.cpp:4]: (warning) fscanf format string requires 1 parameter but 2 are given.\n" + "[test.cpp:5]: (warning) scanf format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo() {\n" + " scanf(\"%1d\");\n" + " scanf(\"%1u%1u\", bar());\n" + " sscanf(bar, \"%1d%1d\", &foo);\n" + " scanf(\"%*1x %1x %29s\", &count);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) scanf format string requires 1 parameter but only 0 are given.\n" + "[test.cpp:3]: (error) scanf format string requires 2 parameters but only 1 is given.\n" + "[test.cpp:4]: (error) sscanf format string requires 2 parameters but only 1 is given.\n" + "[test.cpp:5]: (error) scanf format string requires 2 parameters but only 1 is given.\n", errout_str()); + + check("void foo() {\n" + " char input[10];\n" + " char output[5];\n" + " sscanf(input, \"%3s\", output);\n" + " sscanf(input, \"%4s\", output);\n" + " sscanf(input, \"%5s\", output);\n" + "}"); + 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" + "}", dinit(CheckOptions, $.inconclusive = true)); + ASSERT_EQUALS("[test.cpp:5]: (warning, inconclusive) Width 3 given in format string (no. 1) is smaller than destination buffer 'output[5]'.\n" + "[test.cpp:7]: (error) Width 5 given in format string (no. 1) is larger than destination buffer 'output[5]', use %4s to prevent overflowing it.\n" + "[test.cpp:4]: (warning) sscanf() without field width limits can crash with huge input data.\n", errout_str()); + + check("void foo() {\n" + " const size_t BUFLENGTH(2048);\n" + " typedef char bufT[BUFLENGTH];\n" + " bufT line= {0};\n" + " bufT projectId= {0};\n" + " const int scanrc=sscanf(line, \"Project(\\\"{%36s}\\\")\", projectId);\n" + " sscanf(input, \"%5s\", output);\n" + "}", dinit(CheckOptions, $.inconclusive = true)); + ASSERT_EQUALS("[test.cpp:6]: (warning, inconclusive) Width 36 given in format string (no. 1) is smaller than destination buffer 'projectId[2048]'.\n", errout_str()); + + check("void foo(unsigned int i) {\n" + " scanf(\"%h\", &i);\n" + " scanf(\"%hh\", &i);\n" + " scanf(\"%l\", &i);\n" + " scanf(\"%ll\", &i);\n" + " scanf(\"%j\", &i);\n" + " scanf(\"%z\", &i);\n" + " scanf(\"%t\", &i);\n" + " scanf(\"%L\", &i);\n" + " scanf(\"%I\", &i);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) 'h' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:3]: (warning) 'hh' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:4]: (warning) 'l' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:5]: (warning) 'll' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:6]: (warning) 'j' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:7]: (warning) 'z' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:8]: (warning) 't' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:9]: (warning) 'L' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:10]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout_str()); + + // Unrecognized (and non-existent in standard library) specifiers. + // Perhaps should emit warnings + TEST_SCANF_NOWARN("%jb", "intmax_t", "intmax_t"); + TEST_SCANF_NOWARN("%jw", "uintmax_t", "uintmax_t"); + TEST_SCANF_NOWARN("%zr", "size_t", "size_t"); + TEST_SCANF_NOWARN("%tm", "ptrdiff_t", "ptrdiff_t"); + TEST_SCANF_NOWARN("%La", "long double", "long double"); + TEST_SCANF_NOWARN_CPP("%zv", "std::size_t", "std::size_t"); + TEST_SCANF_NOWARN_CPP("%tp", "std::ptrdiff_t", "std::ptrdiff_t"); + + TEST_SCANF_WARN("%u", "unsigned int", "bool"); + TEST_SCANF_WARN("%u", "unsigned int", "char"); + TEST_SCANF_WARN("%u", "unsigned int", "signed char"); + TEST_SCANF_WARN("%u", "unsigned int", "unsigned char"); + TEST_SCANF_WARN("%u", "unsigned int", "signed short"); + TEST_SCANF_WARN("%u", "unsigned int", "unsigned short"); + TEST_SCANF_WARN("%u", "unsigned int", "signed int"); + TEST_SCANF_NOWARN("%u", "unsigned int", "unsigned int"); + TEST_SCANF_WARN("%u", "unsigned int", "signed long"); + TEST_SCANF_WARN("%u", "unsigned int", "unsigned long"); + TEST_SCANF_WARN("%u", "unsigned int", "signed long long"); + TEST_SCANF_WARN("%u", "unsigned int", "unsigned long long"); + TEST_SCANF_WARN("%u", "unsigned int", "float"); + TEST_SCANF_WARN("%u", "unsigned int", "double"); + TEST_SCANF_WARN("%u", "unsigned int", "long double"); + TEST_SCANF_WARN("%u", "unsigned int", "void *"); + TEST_SCANF_WARN_AKA("%u", "unsigned int", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%u", "unsigned int", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%u", "unsigned int", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%u", "unsigned int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%u", "unsigned int", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%u", "unsigned int", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%u", "unsigned int", "intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%u", "unsigned int", "uintptr_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%u", "unsigned int", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%u", "unsigned int", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%u", "unsigned int", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%u", "unsigned int", "std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%u", "unsigned int", "std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%u", "unsigned int", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%u", "unsigned int", "std::uintptr_t", "unsigned long", "unsigned long long"); + + check("void foo() {\n" + " scanf(\"%u\", \"s3\");\n" + " scanf(\"%u\", L\"s5W\");\n" + "}", dinit(CheckOptions, $.inconclusive = true)); + ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 1) requires 'unsigned int *' but the argument type is 'const char *'.\n" + "[test.cpp:3]: (warning) %u in format string (no. 1) requires 'unsigned int *' but the argument type is 'const wchar_t *'.\n", errout_str()); + + check("void foo(long l) {\n" + " scanf(\"%u\", l);\n" + "}", dinit(CheckOptions, $.inconclusive = true)); + ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 1) requires 'unsigned int *' but the argument type is 'signed long'.\n", errout_str()); + + TEST_SCANF_WARN("%lu","unsigned long","bool"); + TEST_SCANF_WARN("%lu","unsigned long","char"); + TEST_SCANF_WARN("%lu","unsigned long","signed char"); + TEST_SCANF_WARN("%lu","unsigned long","unsigned char"); + TEST_SCANF_WARN("%lu","unsigned long","signed short"); + TEST_SCANF_WARN("%lu","unsigned long","unsigned short"); + TEST_SCANF_WARN("%lu","unsigned long","signed int"); + TEST_SCANF_WARN("%lu","unsigned long","unsigned int"); + TEST_SCANF_WARN("%lu","unsigned long","signed long"); + TEST_SCANF_NOWARN("%lu","unsigned long","unsigned long"); + TEST_SCANF_WARN("%lu","unsigned long","signed long long"); + TEST_SCANF_WARN("%lu","unsigned long","unsigned long long"); + TEST_SCANF_WARN("%lu","unsigned long","float"); + TEST_SCANF_WARN("%lu","unsigned long","double"); + TEST_SCANF_WARN("%lu","unsigned long","long double"); + TEST_SCANF_WARN("%lu","unsigned long","void *"); + TEST_SCANF_WARN_AKA("%lu","unsigned long","size_t","unsigned long","unsigned long long"); + TEST_SCANF_WARN_AKA("%lu","unsigned long","ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%lu","unsigned long","ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%lu","unsigned long","unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%lu","unsigned long","intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%lu","unsigned long","uintmax_t","unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%lu","unsigned long","intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_WIN64("%lu","unsigned long","uintptr_t", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%lu","unsigned long","std::size_t","unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%lu","unsigned long","std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%lu","unsigned long","std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%lu","unsigned long","std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%lu","unsigned long","std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%lu","unsigned long","std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP_WIN64("%lu","unsigned long","std::uintptr_t", "unsigned long long"); + + TEST_SCANF_WARN("%lx","unsigned long","bool"); + TEST_SCANF_WARN("%lx","unsigned long","char"); + TEST_SCANF_WARN("%lx","unsigned long","signed char"); + TEST_SCANF_WARN("%lx","unsigned long","unsigned char"); + TEST_SCANF_WARN("%lx","unsigned long","signed short"); + TEST_SCANF_WARN("%lx","unsigned long","unsigned short"); + TEST_SCANF_WARN("%lx","unsigned long","signed int"); + TEST_SCANF_WARN("%lx","unsigned long","unsigned int"); + TEST_SCANF_WARN("%lx","unsigned long","signed long"); + TEST_SCANF_NOWARN("%lx","unsigned long","unsigned long"); + TEST_SCANF_WARN("%lx","unsigned long","signed long long"); + TEST_SCANF_WARN("%lx","unsigned long","unsigned long long"); + TEST_SCANF_WARN("%lx","unsigned long","float"); + TEST_SCANF_WARN("%lx","unsigned long","double"); + TEST_SCANF_WARN("%lx","unsigned long","long double"); + TEST_SCANF_WARN("%lx","unsigned long","void *"); + TEST_SCANF_WARN_AKA("%lx","unsigned long","size_t","unsigned long","unsigned long long"); + TEST_SCANF_WARN_AKA("%lx","unsigned long","ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%lx","unsigned long","ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%lx","unsigned long","unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%lx","unsigned long","intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%lx","unsigned long","uintmax_t","unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%lx","unsigned long","intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_WIN64("%lx","unsigned long","uintptr_t", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%lx","unsigned long","std::size_t","unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%lx","unsigned long","std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%lx","unsigned long","std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%lx","unsigned long","std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%lx","unsigned long","std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%lx","unsigned long","std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP_WIN64("%lx","unsigned long","std::uintptr_t", "unsigned long long"); + + TEST_SCANF_WARN("%ld","long","bool"); + TEST_SCANF_WARN("%ld","long","char"); + TEST_SCANF_NOWARN("%ld","long","signed long"); + TEST_SCANF_WARN("%ld","long","unsigned long"); + TEST_SCANF_WARN("%ld","long","void *"); + TEST_SCANF_WARN_AKA("%ld","long","size_t","unsigned long","unsigned long long"); + TEST_SCANF_WARN_AKA("%ld","long","intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%ld","long","std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%ld","long","std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP_WIN64("%ld","long","std::intptr_t", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%ld","long","std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%llu","unsigned long long","bool"); + TEST_SCANF_WARN("%llu","unsigned long long","char"); + TEST_SCANF_WARN("%llu","unsigned long long","signed char"); + TEST_SCANF_WARN("%llu","unsigned long long","unsigned char"); + TEST_SCANF_WARN("%llu","unsigned long long","signed short"); + TEST_SCANF_WARN("%llu","unsigned long long","unsigned short"); + TEST_SCANF_WARN("%llu","unsigned long long","signed int"); + TEST_SCANF_WARN("%llu","unsigned long long","unsigned int"); + TEST_SCANF_WARN("%llu","unsigned long long","signed long"); + TEST_SCANF_WARN("%llu","unsigned long long","unsigned long"); + TEST_SCANF_WARN("%llu","unsigned long long","signed long long"); + TEST_SCANF_NOWARN("%llu","unsigned long long","unsigned long long"); + TEST_SCANF_WARN("%llu","unsigned long long","float"); + TEST_SCANF_WARN("%llu","unsigned long long","double"); + TEST_SCANF_WARN("%llu","unsigned long long","long double"); + TEST_SCANF_WARN("%llu","unsigned long long","void *"); + TEST_SCANF_WARN_AKA("%llu","unsigned long long","size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%llu","unsigned long long","ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%llu","unsigned long long","ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%llu","unsigned long long","unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%llu","unsigned long long","intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%llu","unsigned long long","uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%llu","unsigned long long","intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_WIN32("%llu","unsigned long long","uintptr_t", "unsigned long"); + TEST_SCANF_WARN_AKA_CPP("%llu","unsigned long long","std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%llu","unsigned long long","std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%llu","unsigned long long","std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%llu","unsigned long long","std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%llu","unsigned long long","std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%llu","unsigned long long","std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP_WIN32("%llu","unsigned long long","std::uintptr_t", "unsigned long"); + + TEST_SCANF_WARN("%llx","unsigned long long","bool"); + TEST_SCANF_WARN("%llx","unsigned long long","char"); + TEST_SCANF_WARN("%llx","unsigned long long","signed char"); + TEST_SCANF_WARN("%llx","unsigned long long","unsigned char"); + TEST_SCANF_WARN("%llx","unsigned long long","signed short"); + TEST_SCANF_WARN("%llx","unsigned long long","unsigned short"); + TEST_SCANF_WARN("%llx","unsigned long long","signed int"); + TEST_SCANF_WARN("%llx","unsigned long long","unsigned int"); + TEST_SCANF_WARN("%llx","unsigned long long","signed long"); + TEST_SCANF_WARN("%llx","unsigned long long","unsigned long"); + TEST_SCANF_WARN("%llx","unsigned long long","signed long long"); + TEST_SCANF_NOWARN("%llx","unsigned long long","unsigned long long"); + TEST_SCANF_WARN("%llx","unsigned long long","float"); + TEST_SCANF_WARN("%llx","unsigned long long","double"); + TEST_SCANF_WARN("%llx","unsigned long long","long double"); + TEST_SCANF_WARN("%llx","unsigned long long","void *"); + TEST_SCANF_WARN_AKA("%llx","unsigned long long","size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%llx","unsigned long long","ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%llx","unsigned long long","ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%llx","unsigned long long","unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%llx","unsigned long long","intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%llx","unsigned long long","uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%llx","unsigned long long","intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_WIN32("%llx","unsigned long long","uintptr_t", "unsigned long"); + TEST_SCANF_WARN_AKA_CPP("%llx","unsigned long long","std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%llx","unsigned long long","std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%llx","unsigned long long","std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%llx","unsigned long long","std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%llx","unsigned long long","std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%llx","unsigned long long","std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP_WIN32("%llx","unsigned long long","std::uintptr_t", "unsigned long"); + + TEST_SCANF_WARN("%lld","long long","bool"); + TEST_SCANF_WARN("%lld","long long","char"); + TEST_SCANF_NOWARN("%lld","long long","long long"); + TEST_SCANF_WARN("%lld","long long","unsigned long long"); + TEST_SCANF_WARN("%lld","long long","void *"); + TEST_SCANF_WARN_AKA("%lld","long long","size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%lld","long long","intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%lld","long long","std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%lld","long long","std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP_WIN32("%lld","long long","std::intptr_t", "signed long"); + + TEST_SCANF_WARN("%hu", "unsigned short", "bool"); + TEST_SCANF_WARN("%hu", "unsigned short", "char"); + TEST_SCANF_WARN("%hu", "unsigned short", "signed char"); + TEST_SCANF_WARN("%hu", "unsigned short", "unsigned char"); + TEST_SCANF_WARN("%hu", "unsigned short", "signed short"); + TEST_SCANF_NOWARN("%hu", "unsigned short", "unsigned short"); + TEST_SCANF_WARN("%hu", "unsigned short", "signed int"); + TEST_SCANF_WARN("%hu", "unsigned short", "unsigned int"); + TEST_SCANF_WARN("%hu", "unsigned short", "signed long"); + TEST_SCANF_WARN("%hu", "unsigned short", "unsigned long"); + TEST_SCANF_WARN("%hu", "unsigned short", "signed long long"); + TEST_SCANF_WARN("%hu", "unsigned short", "unsigned long long"); + TEST_SCANF_WARN("%hu", "unsigned short", "float"); + TEST_SCANF_WARN("%hu", "unsigned short", "double"); + TEST_SCANF_WARN("%hu", "unsigned short", "long double"); + TEST_SCANF_WARN("%hu", "unsigned short", "void *"); + TEST_SCANF_WARN_AKA("%hu", "unsigned short", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%hu", "unsigned short", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%hu", "unsigned short", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%hu", "unsigned short", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%hu", "unsigned short", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%hu", "unsigned short", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%hu", "unsigned short", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%hu", "unsigned short", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%hu", "unsigned short", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%hu", "unsigned short", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%hu", "unsigned short", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%hx", "unsigned short", "bool"); + TEST_SCANF_WARN("%hx", "unsigned short", "char"); + TEST_SCANF_WARN("%hx", "unsigned short", "signed char"); + TEST_SCANF_WARN("%hx", "unsigned short", "unsigned char"); + TEST_SCANF_WARN("%hx", "unsigned short", "signed short"); + TEST_SCANF_NOWARN("%hx", "unsigned short", "unsigned short"); + TEST_SCANF_WARN("%hx", "unsigned short", "signed int"); + TEST_SCANF_WARN("%hx", "unsigned short", "unsigned int"); + TEST_SCANF_WARN("%hx", "unsigned short", "signed long"); + TEST_SCANF_WARN("%hx", "unsigned short", "unsigned long"); + TEST_SCANF_WARN("%hx", "unsigned short", "signed long long"); + TEST_SCANF_WARN("%hx", "unsigned short", "unsigned long long"); + TEST_SCANF_WARN("%hx", "unsigned short", "float"); + TEST_SCANF_WARN("%hx", "unsigned short", "double"); + TEST_SCANF_WARN("%hx", "unsigned short", "long double"); + TEST_SCANF_WARN("%hx", "unsigned short", "void *"); + TEST_SCANF_WARN_AKA("%hx", "unsigned short", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%hx", "unsigned short", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%hx", "unsigned short", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%hx", "unsigned short", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%hx", "unsigned short", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%hx", "unsigned short", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%hx", "unsigned short", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%hx", "unsigned short", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%hx", "unsigned short", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%hx", "unsigned short", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%hx", "unsigned short", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%hd", "short", "bool"); + TEST_SCANF_WARN("%hd", "short", "char"); + TEST_SCANF_WARN("%hd", "short", "signed char"); + TEST_SCANF_WARN("%hd", "short", "unsigned char"); + TEST_SCANF_NOWARN("%hd", "short", "signed short"); + TEST_SCANF_WARN("%hd", "short", "unsigned short"); + TEST_SCANF_WARN("%hd", "short", "signed int"); + TEST_SCANF_WARN("%hd", "short", "unsigned int"); + TEST_SCANF_WARN("%hd", "short", "signed long"); + TEST_SCANF_WARN("%hd", "short", "void *"); + TEST_SCANF_WARN_AKA("%hd", "short", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%hd", "short", "ssize_t", "signed long", "signed long long"); + + TEST_SCANF_WARN("%hhu", "unsigned char", "bool"); + TEST_SCANF_WARN("%hhu", "unsigned char", "char"); + TEST_SCANF_WARN("%hhu", "unsigned char", "signed char"); + TEST_SCANF_NOWARN("%hhu", "unsigned char", "unsigned char"); + TEST_SCANF_WARN("%hhu", "unsigned char", "signed short"); + TEST_SCANF_WARN("%hhu", "unsigned char", "unsigned short"); + TEST_SCANF_WARN("%hhu", "unsigned char", "signed int"); + TEST_SCANF_WARN("%hhu", "unsigned char", "unsigned int"); + TEST_SCANF_WARN("%hhu", "unsigned char", "signed long"); + TEST_SCANF_WARN("%hhu", "unsigned char", "unsigned long"); + TEST_SCANF_WARN("%hhu", "unsigned char", "signed long long"); + TEST_SCANF_WARN("%hhu", "unsigned char", "unsigned long long"); + TEST_SCANF_WARN("%hhu", "unsigned char", "float"); + TEST_SCANF_WARN("%hhu", "unsigned char", "double"); + TEST_SCANF_WARN("%hhu", "unsigned char", "long double"); + TEST_SCANF_WARN("%hhu", "unsigned char", "void *"); + TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%hhu", "unsigned char", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%hhu", "unsigned char", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%hhu", "unsigned char", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%hhu", "unsigned char", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%hhu", "unsigned char", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%hhu", "unsigned char", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%hhx", "unsigned char", "bool"); + TEST_SCANF_WARN("%hhx", "unsigned char", "char"); + TEST_SCANF_WARN("%hhx", "unsigned char", "signed char"); + TEST_SCANF_NOWARN("%hhx", "unsigned char", "unsigned char"); + TEST_SCANF_WARN("%hhx", "unsigned char", "signed short"); + TEST_SCANF_WARN("%hhx", "unsigned char", "unsigned short"); + TEST_SCANF_WARN("%hhx", "unsigned char", "signed int"); + TEST_SCANF_WARN("%hhx", "unsigned char", "unsigned int"); + TEST_SCANF_WARN("%hhx", "unsigned char", "signed long"); + TEST_SCANF_WARN("%hhx", "unsigned char", "unsigned long"); + TEST_SCANF_WARN("%hhx", "unsigned char", "signed long long"); + TEST_SCANF_WARN("%hhx", "unsigned char", "unsigned long long"); + TEST_SCANF_WARN("%hhx", "unsigned char", "float"); + TEST_SCANF_WARN("%hhx", "unsigned char", "double"); + TEST_SCANF_WARN("%hhx", "unsigned char", "long double"); + TEST_SCANF_WARN("%hhx", "unsigned char", "void *"); + TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%hhx", "unsigned char", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%hhx", "unsigned char", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%hhx", "unsigned char", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%hhx", "unsigned char", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%hhx", "unsigned char", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%hhx", "unsigned char", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%hhd", "char", "bool"); + TEST_SCANF_NOWARN("%hhd", "char", "char"); + TEST_SCANF_NOWARN("%hhd", "char", "signed char"); + TEST_SCANF_WARN("%hhd", "char", "unsigned char"); + TEST_SCANF_WARN("%hhd", "char", "signed short"); + TEST_SCANF_WARN("%hhd", "char", "void *"); + TEST_SCANF_WARN_AKA("%hhd", "char", "size_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%Lu", "unsigned long long", "bool"); + TEST_SCANF_WARN("%Lu", "unsigned long long", "char"); + TEST_SCANF_WARN("%Lu", "unsigned long long", "signed char"); + TEST_SCANF_WARN("%Lu", "unsigned long long", "unsigned char"); + TEST_SCANF_WARN("%Lu", "unsigned long long", "signed short"); + TEST_SCANF_WARN("%Lu", "unsigned long long", "unsigned short"); + TEST_SCANF_WARN("%Lu", "unsigned long long", "signed int"); + TEST_SCANF_WARN("%Lu", "unsigned long long", "unsigned int"); + TEST_SCANF_WARN("%Lu", "unsigned long long", "signed long"); + TEST_SCANF_WARN("%Lu", "unsigned long long", "unsigned long"); + TEST_SCANF_WARN("%Lu", "unsigned long long", "signed long long"); + TEST_SCANF_NOWARN("%Lu", "unsigned long long", "unsigned long long"); + TEST_SCANF_WARN("%Lu", "unsigned long long", "float"); + TEST_SCANF_WARN("%Lu", "unsigned long long", "double"); + TEST_SCANF_WARN("%Lu", "unsigned long long", "long double"); + TEST_SCANF_WARN("%Lu", "unsigned long long", "void *"); + TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_WIN32("%Lu", "unsigned long long", "unsigned ptrdiff_t", "unsigned long"); + TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%Lu", "unsigned long long", "intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_WIN32("%Lu", "unsigned long long", "uintptr_t", "unsigned long"); + TEST_SCANF_WARN_AKA_CPP("%Lu", "unsigned long long", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%Lu", "unsigned long long", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Lu", "unsigned long long", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Lu", "unsigned long long", "std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Lu", "unsigned long long", "std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%Lu", "unsigned long long", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP_WIN32("%Lu", "unsigned long long", "std::uintptr_t", "unsigned long"); + + TEST_SCANF_WARN("%Lx", "unsigned long long", "bool"); + TEST_SCANF_WARN("%Lx", "unsigned long long", "char"); + TEST_SCANF_WARN("%Lx", "unsigned long long", "signed char"); + TEST_SCANF_WARN("%Lx", "unsigned long long", "unsigned char"); + TEST_SCANF_WARN("%Lx", "unsigned long long", "signed short"); + TEST_SCANF_WARN("%Lx", "unsigned long long", "unsigned short"); + TEST_SCANF_WARN("%Lx", "unsigned long long", "signed int"); + TEST_SCANF_WARN("%Lx", "unsigned long long", "unsigned int"); + TEST_SCANF_WARN("%Lx", "unsigned long long", "signed long"); + TEST_SCANF_WARN("%Lx", "unsigned long long", "unsigned long"); + TEST_SCANF_WARN("%Lx", "unsigned long long", "signed long long"); + TEST_SCANF_NOWARN("%Lx", "unsigned long long", "unsigned long long"); + TEST_SCANF_WARN("%Lx", "unsigned long long", "float"); + TEST_SCANF_WARN("%Lx", "unsigned long long", "double"); + TEST_SCANF_WARN("%Lx", "unsigned long long", "long double"); + TEST_SCANF_WARN("%Lx", "unsigned long long", "void *"); + TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_WIN32("%Lx", "unsigned long long", "unsigned ptrdiff_t", "unsigned long"); + TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%Lx", "unsigned long long", "intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_WIN32("%Lx", "unsigned long long", "uintptr_t", "unsigned long"); + TEST_SCANF_WARN_AKA_CPP("%Lx", "unsigned long long", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%Lx", "unsigned long long", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Lx", "unsigned long long", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Lx", "unsigned long long", "std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Lx", "unsigned long long", "std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%Lx", "unsigned long long", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP_WIN32("%Lx", "unsigned long long", "std::uintptr_t", "unsigned long"); + + TEST_SCANF_WARN("%Ld", "long long", "bool"); + TEST_SCANF_WARN("%Ld", "long long", "char"); + TEST_SCANF_WARN("%Ld", "long long", "signed char"); + TEST_SCANF_WARN("%Ld", "long long", "unsigned char"); + TEST_SCANF_WARN("%Ld", "long long", "signed short"); + TEST_SCANF_WARN("%Ld", "long long", "unsigned short"); + TEST_SCANF_WARN("%Ld", "long long", "signed int"); + TEST_SCANF_WARN("%Ld", "long long", "unsigned int"); + TEST_SCANF_WARN("%Ld", "long long", "signed long"); + TEST_SCANF_WARN("%Ld", "long long", "unsigned long"); + TEST_SCANF_NOWARN("%Ld", "long long", "signed long long"); + TEST_SCANF_WARN("%Ld", "long long", "unsigned long long"); + TEST_SCANF_WARN("%Ld", "long long", "float"); + TEST_SCANF_WARN("%Ld", "long long", "double"); + TEST_SCANF_WARN("%Ld", "long long", "long double"); + TEST_SCANF_WARN("%Ld", "long long", "void *"); + TEST_SCANF_WARN_AKA("%Ld", "long long", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_WIN32("%Ld", "long long", "ssize_t", "signed long"); + TEST_SCANF_WARN_AKA_WIN32("%Ld", "long long", "ptrdiff_t", "signed long"); + TEST_SCANF_WARN_AKA("%Ld", "long long", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_WIN32("%Ld", "long long", "intmax_t", "signed long"); + TEST_SCANF_WARN_AKA("%Ld", "long long", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%Ld", "long long", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP_WIN32("%Ld", "long long", "std::ssize_t", "signed long"); + TEST_SCANF_WARN_AKA_CPP_WIN32("%Ld", "long long", "std::ptrdiff_t", "signed long"); + TEST_SCANF_WARN_AKA_CPP_WIN32("%Ld", "long long", "std::intptr_t", "signed long"); + TEST_SCANF_WARN_AKA_CPP("%Ld", "long long", "std::uintptr_t", "unsigned long", "unsigned long long"); + + check("void foo() {\n" + " scanf(\"%Ld\", \"s3\");\n" + " scanf(\"%Ld\", L\"s5W\");\n" + "}", dinit(CheckOptions, $.inconclusive = true)); + ASSERT_EQUALS("[test.cpp:2]: (warning) %Ld in format string (no. 1) requires 'long long *' but the argument type is 'const char *'.\n" + "[test.cpp:3]: (warning) %Ld in format string (no. 1) requires 'long long *' but the argument type is 'const wchar_t *'.\n", errout_str()); + + check("void foo(int i) {\n" + " scanf(\"%Ld\", i);\n" + "}", dinit(CheckOptions, $.inconclusive = true)); + ASSERT_EQUALS("[test.cpp:2]: (warning) %Ld in format string (no. 1) requires 'long long *' but the argument type is 'signed int'.\n", errout_str()); + + TEST_SCANF_WARN("%ju", "uintmax_t", "bool"); + TEST_SCANF_WARN("%ju", "uintmax_t", "char"); + TEST_SCANF_WARN("%ju", "uintmax_t", "signed char"); + TEST_SCANF_WARN("%ju", "uintmax_t", "unsigned char"); + TEST_SCANF_WARN("%ju", "uintmax_t", "signed short"); + TEST_SCANF_WARN("%ju", "uintmax_t", "unsigned short"); + TEST_SCANF_WARN("%ju", "uintmax_t", "signed int"); + TEST_SCANF_WARN("%ju", "uintmax_t", "unsigned int"); + TEST_SCANF_WARN("%ju", "uintmax_t", "signed long"); + TEST_SCANF_WARN("%ju", "uintmax_t", "unsigned long"); + TEST_SCANF_WARN("%ju", "uintmax_t", "signed long long"); + TEST_SCANF_WARN("%ju", "uintmax_t", "unsigned long long"); + TEST_SCANF_WARN("%ju", "uintmax_t", "float"); + TEST_SCANF_WARN("%ju", "uintmax_t", "double"); + TEST_SCANF_WARN("%ju", "uintmax_t", "long double"); + TEST_SCANF_WARN("%ju", "uintmax_t", "void *"); + TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%ju", "uintmax_t", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_NOWARN("%ju", "uintmax_t", "uintmax_t"); + TEST_SCANF_WARN_AKA_CPP("%ju", "uintmax_t", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%ju", "uintmax_t", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%ju", "uintmax_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%ju", "uintmax_t", "std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_NOWARN_CPP("%ju", "uintmax_t", "std::uintmax_t"); + TEST_SCANF_WARN_AKA_CPP("%ju", "uintmax_t", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%ju", "uintmax_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%jx", "uintmax_t", "bool"); + TEST_SCANF_WARN("%jx", "uintmax_t", "char"); + TEST_SCANF_WARN("%jx", "uintmax_t", "signed char"); + TEST_SCANF_WARN("%jx", "uintmax_t", "unsigned char"); + TEST_SCANF_WARN("%jx", "uintmax_t", "signed short"); + TEST_SCANF_WARN("%jx", "uintmax_t", "unsigned short"); + TEST_SCANF_WARN("%jx", "uintmax_t", "signed int"); + TEST_SCANF_WARN("%jx", "uintmax_t", "unsigned int"); + TEST_SCANF_WARN("%jx", "uintmax_t", "signed long"); + TEST_SCANF_WARN("%jx", "uintmax_t", "unsigned long"); + TEST_SCANF_WARN("%jx", "uintmax_t", "signed long long"); + TEST_SCANF_WARN("%jx", "uintmax_t", "unsigned long long"); + TEST_SCANF_WARN("%jx", "uintmax_t", "float"); + TEST_SCANF_WARN("%jx", "uintmax_t", "double"); + TEST_SCANF_WARN("%jx", "uintmax_t", "long double"); + TEST_SCANF_WARN("%jx", "uintmax_t", "void *"); + TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%jx", "uintmax_t", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_NOWARN("%jx", "uintmax_t", "uintmax_t"); + TEST_SCANF_WARN_AKA_CPP("%jx", "uintmax_t", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%jx", "uintmax_t", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%jx", "uintmax_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%jx", "uintmax_t", "std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_NOWARN_CPP("%jx", "uintmax_t", "std::uintmax_t"); + TEST_SCANF_WARN_AKA_CPP("%jx", "uintmax_t", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%jx", "uintmax_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%jd", "intmax_t", "long double"); + TEST_SCANF_WARN("%jd", "intmax_t", "void *"); + TEST_SCANF_WARN_AKA("%jd", "intmax_t", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%jd", "intmax_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%jd", "intmax_t", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%jd", "intmax_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_NOWARN("%jd", "intmax_t", "intmax_t"); + TEST_SCANF_WARN_AKA("%jd", "intmax_t", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_NOWARN_CPP("%jd", "intmax_t", "std::intmax_t"); + + TEST_SCANF_WARN("%zu", "size_t", "bool"); + TEST_SCANF_WARN("%zu", "size_t", "char"); + TEST_SCANF_WARN("%zu", "size_t", "signed char"); + TEST_SCANF_WARN("%zu", "size_t", "unsigned char"); + TEST_SCANF_WARN("%zu", "size_t", "signed short"); + TEST_SCANF_WARN("%zu", "size_t", "unsigned short"); + TEST_SCANF_WARN("%zu", "size_t", "signed int"); + TEST_SCANF_WARN("%zu", "size_t", "unsigned int"); + TEST_SCANF_WARN("%zu", "size_t", "signed long"); + TEST_SCANF_WARN("%zu", "size_t", "unsigned long"); + TEST_SCANF_WARN("%zu", "size_t", "signed long long"); + TEST_SCANF_WARN("%zu", "size_t", "unsigned long long"); + TEST_SCANF_WARN("%zu", "size_t", "float"); + TEST_SCANF_WARN("%zu", "size_t", "double"); + TEST_SCANF_WARN("%zu", "size_t", "long double"); + TEST_SCANF_WARN("%zu", "size_t", "void *"); + TEST_SCANF_NOWARN("%zu", "size_t", "size_t"); + TEST_SCANF_WARN_AKA("%zu", "size_t", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%zu", "size_t", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%zu", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%zu", "size_t", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%zu", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_NOWARN_CPP("%zu", "size_t", "std::size_t"); + TEST_SCANF_WARN_AKA_CPP("%zu", "size_t", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%zu", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%zu", "size_t", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%zu", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%zx", "size_t", "bool"); + TEST_SCANF_WARN("%zx", "size_t", "char"); + TEST_SCANF_WARN("%zx", "size_t", "signed char"); + TEST_SCANF_WARN("%zx", "size_t", "unsigned char"); + TEST_SCANF_WARN("%zx", "size_t", "signed short"); + TEST_SCANF_WARN("%zx", "size_t", "unsigned short"); + TEST_SCANF_WARN("%zx", "size_t", "signed int"); + TEST_SCANF_WARN("%zx", "size_t", "unsigned int"); + TEST_SCANF_WARN("%zx", "size_t", "signed long"); + TEST_SCANF_WARN("%zx", "size_t", "unsigned long"); + TEST_SCANF_WARN("%zx", "size_t", "signed long long"); + TEST_SCANF_WARN("%zx", "size_t", "unsigned long long"); + TEST_SCANF_WARN("%zx", "size_t", "float"); + TEST_SCANF_WARN("%zx", "size_t", "double"); + TEST_SCANF_WARN("%zx", "size_t", "long double"); + TEST_SCANF_WARN("%zx", "size_t", "void *"); + TEST_SCANF_NOWARN("%zx", "size_t", "size_t"); + TEST_SCANF_WARN_AKA("%zx", "size_t", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%zx", "size_t", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%zx", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%zx", "size_t", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%zx", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_NOWARN_CPP("%zx", "size_t", "std::size_t"); + TEST_SCANF_WARN_AKA_CPP("%zx", "size_t", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%zx", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%zx", "size_t", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%zx", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%zd", "ssize_t", "bool"); + TEST_SCANF_WARN("%zd", "ssize_t", "signed short"); + TEST_SCANF_WARN("%zd", "ssize_t", "void *"); + TEST_SCANF_WARN_AKA("%zd", "ssize_t", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_NOWARN("%zd", "ssize_t", "ssize_t"); + TEST_SCANF_WARN_AKA("%zd", "ssize_t", "ptrdiff_t", "signed long", "signed long long"); + + TEST_SCANF_WARN_AKA("%zi", "ssize_t", "size_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "bool"); + TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "char"); + TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "signed char"); + TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "unsigned char"); + TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "signed short"); + TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "unsigned short"); + TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "signed int"); + TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "unsigned int"); + TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "signed long"); + TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "unsigned long"); + TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "signed long long"); + TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "unsigned long long"); + TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "float"); + TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "double"); + TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "long double"); + TEST_SCANF_WARN("%tu", "unsigned ptrdiff_t", "void *"); + TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_NOWARN("%tu", "unsigned ptrdiff_t", "unsigned ptrdiff_t"); + TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%tu", "unsigned ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%tu", "unsigned ptrdiff_t", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%tu", "unsigned ptrdiff_t", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%tu", "unsigned ptrdiff_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%tu", "unsigned ptrdiff_t", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%tu", "unsigned ptrdiff_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "bool"); + TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "char"); + TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "signed char"); + TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "unsigned char"); + TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "signed short"); + TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "unsigned short"); + TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "signed int"); + TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "unsigned int"); + TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "signed long"); + TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "unsigned long"); + TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "signed long long"); + TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "unsigned long long"); + TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "float"); + TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "double"); + TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "long double"); + TEST_SCANF_WARN("%tx", "unsigned ptrdiff_t", "void *"); + TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_NOWARN("%tx", "unsigned ptrdiff_t", "unsigned ptrdiff_t"); + TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%tx", "unsigned ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%tx", "unsigned ptrdiff_t", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%tx", "unsigned ptrdiff_t", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%tx", "unsigned ptrdiff_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%tx", "unsigned ptrdiff_t", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%tx", "unsigned ptrdiff_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%td", "ptrdiff_t", "long double"); + TEST_SCANF_WARN("%td", "ptrdiff_t", "void *"); + TEST_SCANF_NOWARN("%td", "ptrdiff_t", "ptrdiff_t"); + TEST_SCANF_WARN_AKA("%td", "ptrdiff_t", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%td", "ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%td", "ptrdiff_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%Iu", "size_t", "bool"); + TEST_SCANF_WARN("%Iu", "size_t", "char"); + TEST_SCANF_WARN("%Iu", "size_t", "signed char"); + TEST_SCANF_WARN("%Iu", "size_t", "unsigned char"); + TEST_SCANF_WARN("%Iu", "size_t", "signed short"); + TEST_SCANF_WARN("%Iu", "size_t", "unsigned short"); + TEST_SCANF_WARN("%Iu", "size_t", "signed int"); + TEST_SCANF_WARN("%Iu", "size_t", "unsigned int"); + TEST_SCANF_WARN("%Iu", "size_t", "signed long"); + TEST_SCANF_WARN("%Iu", "size_t", "unsigned long"); + TEST_SCANF_WARN("%Iu", "size_t", "signed long long"); + TEST_SCANF_WARN("%Iu", "size_t", "unsigned long long"); + TEST_SCANF_WARN("%Iu", "size_t", "float"); + TEST_SCANF_WARN("%Iu", "size_t", "double"); + TEST_SCANF_WARN("%Iu", "size_t", "long double"); + TEST_SCANF_WARN("%Iu", "size_t", "void *"); + TEST_SCANF_NOWARN("%Iu", "size_t", "size_t"); + TEST_SCANF_WARN_AKA("%Iu", "size_t", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%Iu", "size_t", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%Iu", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%Iu", "size_t", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%Iu", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%Iu", "size_t", "intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%Iu", "size_t", "uintptr_t", "unsigned long", "unsigned long long"); + TEST_SCANF_NOWARN_CPP("%Iu", "size_t", "std::size_t"); + TEST_SCANF_WARN_AKA_CPP("%Iu", "size_t", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Iu", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Iu", "size_t", "std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Iu", "size_t", "std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%Iu", "size_t", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Iu", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%Ix", "size_t", "bool"); + TEST_SCANF_WARN("%Ix", "size_t", "char"); + TEST_SCANF_WARN("%Ix", "size_t", "signed char"); + TEST_SCANF_WARN("%Ix", "size_t", "unsigned char"); + TEST_SCANF_WARN("%Ix", "size_t", "signed short"); + TEST_SCANF_WARN("%Ix", "size_t", "unsigned short"); + TEST_SCANF_WARN("%Ix", "size_t", "signed int"); + TEST_SCANF_WARN("%Ix", "size_t", "unsigned int"); + TEST_SCANF_WARN("%Ix", "size_t", "signed long"); + TEST_SCANF_WARN("%Ix", "size_t", "unsigned long"); + TEST_SCANF_WARN("%Ix", "size_t", "signed long long"); + TEST_SCANF_WARN("%Ix", "size_t", "unsigned long long"); + TEST_SCANF_WARN("%Ix", "size_t", "float"); + TEST_SCANF_WARN("%Ix", "size_t", "double"); + TEST_SCANF_WARN("%Ix", "size_t", "long double"); + TEST_SCANF_WARN("%Ix", "size_t", "void *"); + TEST_SCANF_NOWARN("%Ix", "size_t", "size_t"); + TEST_SCANF_WARN_AKA("%Ix", "size_t", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%Ix", "size_t", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%Ix", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%Ix", "size_t", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%Ix", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%Ix", "size_t", "intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%Ix", "size_t", "uintptr_t", "unsigned long", "unsigned long long"); + TEST_SCANF_NOWARN_CPP("%Ix", "size_t", "std::size_t"); + TEST_SCANF_WARN_AKA_CPP("%Ix", "size_t", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Ix", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Ix", "size_t", "std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Ix", "size_t", "std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%Ix", "size_t", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Ix", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%Id", "ptrdiff_t", "bool"); + TEST_SCANF_WARN("%Id", "ptrdiff_t", "char"); + TEST_SCANF_WARN("%Id", "ptrdiff_t", "signed char"); + TEST_SCANF_WARN("%Id", "ptrdiff_t", "unsigned char"); + TEST_SCANF_WARN("%Id", "ptrdiff_t", "signed short"); + TEST_SCANF_WARN("%Id", "ptrdiff_t", "unsigned short"); + TEST_SCANF_WARN("%Id", "ptrdiff_t", "signed int"); + TEST_SCANF_WARN("%Id", "ptrdiff_t", "unsigned int"); + TEST_SCANF_WARN("%Id", "ptrdiff_t", "signed long"); + TEST_SCANF_WARN("%Id", "ptrdiff_t", "unsigned long"); + TEST_SCANF_WARN("%Id", "ptrdiff_t", "signed long long"); + TEST_SCANF_WARN("%Id", "ptrdiff_t", "unsigned long long"); + TEST_SCANF_WARN("%Id", "ptrdiff_t", "float"); + TEST_SCANF_WARN("%Id", "ptrdiff_t", "double"); + TEST_SCANF_WARN("%Id", "ptrdiff_t", "long double"); + TEST_SCANF_WARN("%Id", "ptrdiff_t", "void *"); + TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_NOWARN("%Id", "ptrdiff_t", "ptrdiff_t"); + TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%Id", "ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%Id", "ptrdiff_t", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%Id", "ptrdiff_t", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_NOWARN_CPP("%Id", "ptrdiff_t", "std::ptrdiff_t"); + TEST_SCANF_WARN_AKA_CPP("%Id", "ptrdiff_t", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Id", "ptrdiff_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%I64u", "unsigned __int64", "bool"); + TEST_SCANF_WARN("%I64u", "unsigned __int64", "char"); + TEST_SCANF_WARN("%I64u", "unsigned __int64", "signed char"); + TEST_SCANF_WARN("%I64u", "unsigned __int64", "unsigned char"); + TEST_SCANF_WARN("%I64u", "unsigned __int64", "signed short"); + TEST_SCANF_WARN("%I64u", "unsigned __int64", "unsigned short"); + TEST_SCANF_WARN("%I64u", "unsigned __int64", "signed int"); + TEST_SCANF_WARN("%I64u", "unsigned __int64", "unsigned int"); + TEST_SCANF_WARN("%I64u", "unsigned __int64", "signed long"); + TEST_SCANF_WARN("%I64u", "unsigned __int64", "unsigned long"); + TEST_SCANF_WARN("%I64u", "unsigned __int64", "signed long long"); + TEST_SCANF_NOWARN("%I64u", "unsigned __int64", "unsigned long long"); + TEST_SCANF_WARN("%I64u", "unsigned __int64", "float"); + TEST_SCANF_WARN("%I64u", "unsigned __int64", "double"); + TEST_SCANF_WARN("%I64u", "unsigned __int64", "long double"); + TEST_SCANF_WARN("%I64u", "unsigned __int64", "void *"); + TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "size_t", "unsigned long"); + TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "unsigned ptrdiff_t", "unsigned long"); + TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "uintmax_t", "unsigned long"); + TEST_SCANF_WARN_AKA("%I64u", "unsigned __int64", "intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "uintptr_t", "unsigned long"); + TEST_SCANF_WARN_AKA_CPP_WIN32("%I64u", "unsigned __int64", "std::size_t", "unsigned long"); + TEST_SCANF_WARN_AKA_CPP("%I64u", "unsigned __int64", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%I64u", "unsigned __int64", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%I64u", "unsigned __int64", "std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP_WIN32("%I64u", "unsigned __int64", "std::uintmax_t", "unsigned long"); + TEST_SCANF_WARN_AKA_CPP("%I64u", "unsigned __int64", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP_WIN32("%I64u", "unsigned __int64", "std::uintptr_t", "unsigned long"); + + TEST_SCANF_WARN("%I64x", "unsigned __int64", "bool"); + TEST_SCANF_WARN("%I64x", "unsigned __int64", "char"); + TEST_SCANF_WARN("%I64x", "unsigned __int64", "signed char"); + TEST_SCANF_WARN("%I64x", "unsigned __int64", "unsigned char"); + TEST_SCANF_WARN("%I64x", "unsigned __int64", "signed short"); + TEST_SCANF_WARN("%I64x", "unsigned __int64", "unsigned short"); + TEST_SCANF_WARN("%I64x", "unsigned __int64", "signed int"); + TEST_SCANF_WARN("%I64x", "unsigned __int64", "unsigned int"); + TEST_SCANF_WARN("%I64x", "unsigned __int64", "signed long"); + TEST_SCANF_WARN("%I64x", "unsigned __int64", "unsigned long"); + TEST_SCANF_WARN("%I64x", "unsigned __int64", "signed long long"); + TEST_SCANF_NOWARN("%I64x", "unsigned __int64", "unsigned long long"); + TEST_SCANF_WARN("%I64x", "unsigned __int64", "float"); + TEST_SCANF_WARN("%I64x", "unsigned __int64", "double"); + TEST_SCANF_WARN("%I64x", "unsigned __int64", "long double"); + TEST_SCANF_WARN("%I64x", "unsigned __int64", "void *"); + TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "size_t", "unsigned long"); + TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_NOWARN("%I64x", "unsigned __int64", "unsigned __int64"); + // TODO TEST_SCANF_WARN("%I64x", "unsigned __int64", "__int64"); + TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "unsigned ptrdiff_t", "unsigned long"); + TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "uintmax_t", "unsigned long"); + TEST_SCANF_WARN_AKA("%I64x", "unsigned __int64", "intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "uintptr_t", "unsigned long"); + TEST_SCANF_WARN_AKA_CPP_WIN32("%I64x", "unsigned __int64", "std::size_t", "unsigned long"); + TEST_SCANF_WARN_AKA_CPP("%I64x", "unsigned __int64", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%I64x", "unsigned __int64", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%I64x", "unsigned __int64", "std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP_WIN32("%I64x", "unsigned __int64", "std::uintmax_t", "unsigned long"); + TEST_SCANF_WARN_AKA_CPP("%I64x", "unsigned __int64", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP_WIN32("%I64x", "unsigned __int64", "std::uintptr_t", "unsigned long"); + + TEST_SCANF_WARN("%I64d", "__int64", "bool"); + TEST_SCANF_WARN("%I64d", "__int64", "signed char"); + TEST_SCANF_WARN("%I64d", "__int64", "unsigned char"); + TEST_SCANF_WARN("%I64d", "__int64", "void *"); + // TODO TEST_SCANF_WARN("%I64d", "__int64", "size_t"); + TEST_SCANF_WARN_AKA_WIN32("%I64d", "__int64", "intmax_t", "signed long"); + TEST_SCANF_WARN_AKA_WIN32("%I64d", "__int64", "ssize_t", "signed long"); + TEST_SCANF_WARN_AKA_WIN32("%I64d", "__int64", "ptrdiff_t", "signed long"); + TEST_SCANF_NOWARN("%I64d", "__int64", "__int64"); + + TEST_SCANF_WARN("%I32u", "unsigned __int32", "bool"); + TEST_SCANF_WARN("%I32u", "unsigned __int32", "char"); + TEST_SCANF_WARN("%I32u", "unsigned __int32", "signed char"); + TEST_SCANF_WARN("%I32u", "unsigned __int32", "unsigned char"); + TEST_SCANF_WARN("%I32u", "unsigned __int32", "signed short"); + TEST_SCANF_WARN("%I32u", "unsigned __int32", "unsigned short"); + TEST_SCANF_WARN("%I32u", "unsigned __int32", "signed int"); + TEST_SCANF_NOWARN("%I32u", "unsigned __int32", "unsigned int"); + TEST_SCANF_WARN("%I32u", "unsigned __int32", "signed long"); + TEST_SCANF_WARN("%I32u", "unsigned __int32", "unsigned long"); + TEST_SCANF_WARN("%I32u", "unsigned __int32", "signed long long"); + TEST_SCANF_WARN("%I32u", "unsigned __int32", "unsigned long long"); + TEST_SCANF_WARN("%I32u", "unsigned __int32", "float"); + TEST_SCANF_WARN("%I32u", "unsigned __int32", "double"); + TEST_SCANF_WARN("%I32u", "unsigned __int32", "long double"); + TEST_SCANF_WARN("%I32u", "unsigned __int32", "void *"); + TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%I32u", "unsigned __int32", "uintptr_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%I32u", "unsigned __int32", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%I32u", "unsigned __int32", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%I32u", "unsigned __int32", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%I32u", "unsigned __int32", "std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%I32u", "unsigned __int32", "std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%I32u", "unsigned __int32", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%I32u", "unsigned __int32", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%I32x", "unsigned __int32", "bool"); + TEST_SCANF_WARN("%I32x", "unsigned __int32", "char"); + TEST_SCANF_WARN("%I32x", "unsigned __int32", "signed char"); + TEST_SCANF_WARN("%I32x", "unsigned __int32", "unsigned char"); + TEST_SCANF_WARN("%I32x", "unsigned __int32", "signed short"); + TEST_SCANF_WARN("%I32x", "unsigned __int32", "unsigned short"); + TEST_SCANF_WARN("%I32x", "unsigned __int32", "signed int"); + TEST_SCANF_NOWARN("%I32x", "unsigned __int32", "unsigned int"); + TEST_SCANF_WARN("%I32x", "unsigned __int32", "signed long"); + TEST_SCANF_WARN("%I32x", "unsigned __int32", "unsigned long"); + TEST_SCANF_WARN("%I32x", "unsigned __int32", "signed long long"); + TEST_SCANF_WARN("%I32x", "unsigned __int32", "unsigned long long"); + TEST_SCANF_WARN("%I32x", "unsigned __int32", "float"); + TEST_SCANF_WARN("%I32x", "unsigned __int32", "double"); + TEST_SCANF_WARN("%I32x", "unsigned __int32", "long double"); + TEST_SCANF_WARN("%I32x", "unsigned __int32", "void *"); + TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%I32x", "unsigned __int32", "uintptr_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%I32x", "unsigned __int32", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%I32x", "unsigned __int32", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%I32x", "unsigned __int32", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%I32x", "unsigned __int32", "std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%I32x", "unsigned __int32", "std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%I32x", "unsigned __int32", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%I32x", "unsigned __int32", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%I32d", "__int32", "bool"); + TEST_SCANF_WARN("%I32d", "__int32", "void *"); + TEST_SCANF_WARN_AKA("%I32d", "__int32", "size_t", "unsigned long", "unsigned long long"); + //TODO TEST_SCANF_WARN_AKA_WIN32("%I32d", "__int32", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%I32d", "__int32", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_NOWARN("%I32d", "__int32", "__int32"); + + TEST_SCANF_WARN("%d", "int", "bool"); + TEST_SCANF_WARN("%d", "int", "char"); + TEST_SCANF_WARN("%d", "int", "signed char"); + TEST_SCANF_WARN("%d", "int", "unsigned char"); + TEST_SCANF_WARN("%d", "int", "signed short"); + TEST_SCANF_WARN("%d", "int", "unsigned short"); + TEST_SCANF_NOWARN("%d", "int", "signed int"); + TEST_SCANF_WARN("%d", "int", "unsigned int"); + TEST_SCANF_WARN("%d", "int", "signed long"); + TEST_SCANF_WARN("%d", "int", "unsigned long"); + TEST_SCANF_WARN("%d", "int", "signed long long"); + TEST_SCANF_WARN("%d", "int", "unsigned long long"); + TEST_SCANF_WARN("%d", "int", "float"); + TEST_SCANF_WARN("%d", "int", "double"); + TEST_SCANF_WARN("%d", "int", "long double"); + TEST_SCANF_WARN("%d", "int", "void *"); + TEST_SCANF_WARN_AKA("%d", "int", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%d", "int", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%d", "int", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%d", "int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%d", "int", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%d", "int", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%d", "int", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%d", "int", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%d", "int", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%d", "int", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%d", "int", "std::uintptr_t", "unsigned long", "unsigned long long"); + + check("void foo() {\n" + " scanf(\"%d\", \"s3\");\n" + " scanf(\"%d\", L\"s5W\");\n" + "}", dinit(CheckOptions, $.inconclusive = true)); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 1) requires 'int *' but the argument type is 'const char *'.\n" + "[test.cpp:3]: (warning) %d in format string (no. 1) requires 'int *' but the argument type is 'const wchar_t *'.\n", errout_str()); + + check("void foo(long l) {\n" + " scanf(\"%d\", l);\n" + "}", dinit(CheckOptions, $.inconclusive = true)); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 1) requires 'int *' but the argument type is 'signed long'.\n", errout_str()); + + TEST_SCANF_WARN("%x", "unsigned int", "bool"); + TEST_SCANF_WARN("%x", "unsigned int", "char"); + TEST_SCANF_WARN("%x", "unsigned int", "signed char"); + TEST_SCANF_WARN("%x", "unsigned int", "unsigned char"); + TEST_SCANF_WARN("%x", "unsigned int", "signed short"); + TEST_SCANF_WARN("%x", "unsigned int", "unsigned short"); + TEST_SCANF_WARN("%x", "unsigned int", "signed int"); + TEST_SCANF_NOWARN("%x", "unsigned int", "unsigned int"); + TEST_SCANF_WARN("%x", "unsigned int", "signed long"); + TEST_SCANF_WARN("%x", "unsigned int", "unsigned long"); + TEST_SCANF_WARN("%x", "unsigned int", "signed long long"); + TEST_SCANF_WARN("%x", "unsigned int", "unsigned long long"); + TEST_SCANF_WARN("%x", "unsigned int", "float"); + TEST_SCANF_WARN("%x", "unsigned int", "double"); + TEST_SCANF_WARN("%x", "unsigned int", "long double"); + TEST_SCANF_WARN("%x", "unsigned int", "void *"); + TEST_SCANF_WARN_AKA("%x", "unsigned int", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%x", "unsigned int", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%x", "unsigned int", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%x", "unsigned int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%x", "unsigned int", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%x", "unsigned int", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%x", "unsigned int", "intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%x", "unsigned int", "uintptr_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%x", "unsigned int", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%x", "unsigned int", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%x", "unsigned int", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%x", "unsigned int", "std::intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%x", "unsigned int", "std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%x", "unsigned int", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%x", "unsigned int", "std::uintptr_t", "unsigned long", "unsigned long long"); + + check("void foo() {\n" + " scanf(\"%x\", \"s3\");\n" + " scanf(\"%x\", L\"s5W\");\n" + "}", dinit(CheckOptions, $.inconclusive = true)); + ASSERT_EQUALS("[test.cpp:2]: (warning) %x in format string (no. 1) requires 'unsigned int *' but the argument type is 'const char *'.\n" + "[test.cpp:3]: (warning) %x in format string (no. 1) requires 'unsigned int *' but the argument type is 'const wchar_t *'.\n", errout_str()); + + check("void foo(long l) {\n" + " scanf(\"%x\", l);\n" + "}", dinit(CheckOptions, $.inconclusive = true)); + ASSERT_EQUALS("[test.cpp:2]: (warning) %x in format string (no. 1) requires 'unsigned int *' but the argument type is 'signed long'.\n", errout_str()); + + TEST_SCANF_WARN("%f", "float", "bool"); + TEST_SCANF_WARN("%f", "float", "char"); + TEST_SCANF_WARN("%f", "float", "signed char"); + TEST_SCANF_WARN("%f", "float", "unsigned char"); + TEST_SCANF_WARN("%f", "float", "signed short"); + TEST_SCANF_WARN("%f", "float", "unsigned short"); + TEST_SCANF_WARN("%f", "float", "signed int"); + TEST_SCANF_WARN("%f", "float", "unsigned int"); + TEST_SCANF_WARN("%f", "float", "signed long"); + TEST_SCANF_WARN("%f", "float", "unsigned long"); + TEST_SCANF_WARN("%f", "float", "signed long long"); + TEST_SCANF_WARN("%f", "float", "unsigned long long"); + TEST_SCANF_NOWARN("%f", "float", "float"); + TEST_SCANF_WARN("%f", "float", "double"); + TEST_SCANF_WARN("%f", "float", "long double"); + TEST_SCANF_WARN("%f", "float", "void *"); + TEST_SCANF_WARN_AKA("%f", "float", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%f", "float", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%f", "float", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%f", "float", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%f", "float", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%f", "float", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%f", "float", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%f", "float", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%f", "float", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%f", "float", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%f", "float", "std::uintptr_t", "unsigned long", "unsigned long long"); + + check("void foo() {\n" + " scanf(\"%f\", \"s3\");\n" + " scanf(\"%f\", L\"s5W\");\n" + "}", dinit(CheckOptions, $.inconclusive = true)); + ASSERT_EQUALS("[test.cpp:2]: (warning) %f in format string (no. 1) requires 'float *' but the argument type is 'const char *'.\n" + "[test.cpp:3]: (warning) %f in format string (no. 1) requires 'float *' but the argument type is 'const wchar_t *'.\n", errout_str()); + + check("void foo(float f) {\n" + " scanf(\"%f\", f);\n" + "}", dinit(CheckOptions, $.inconclusive = true)); + ASSERT_EQUALS("[test.cpp:2]: (warning) %f in format string (no. 1) requires 'float *' but the argument type is 'float'.\n", errout_str()); + + TEST_SCANF_WARN("%lf", "double", "bool"); + TEST_SCANF_WARN("%lf", "double", "char"); + TEST_SCANF_WARN("%lf", "double", "signed char"); + TEST_SCANF_WARN("%lf", "double", "unsigned char"); + TEST_SCANF_WARN("%lf", "double", "signed short"); + TEST_SCANF_WARN("%lf", "double", "unsigned short"); + TEST_SCANF_WARN("%lf", "double", "signed int"); + TEST_SCANF_WARN("%lf", "double", "unsigned int"); + TEST_SCANF_WARN("%lf", "double", "signed long"); + TEST_SCANF_WARN("%lf", "double", "unsigned long"); + TEST_SCANF_WARN("%lf", "double", "signed long long"); + TEST_SCANF_WARN("%lf", "double", "unsigned long long"); + TEST_SCANF_WARN("%lf", "double", "float"); + TEST_SCANF_NOWARN("%lf", "double", "double"); + TEST_SCANF_WARN("%lf", "double", "long double"); + TEST_SCANF_WARN("%lf", "double", "void *"); + TEST_SCANF_WARN_AKA("%lf", "double", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%lf", "double", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%lf", "double", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%lf", "double", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%lf", "double", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%lf", "double", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%lf", "double", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%lf", "double", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%lf", "double", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%lf", "double", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%lf", "double", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%Lf", "long double", "bool"); + TEST_SCANF_WARN("%Lf", "long double", "char"); + TEST_SCANF_WARN("%Lf", "long double", "signed char"); + TEST_SCANF_WARN("%Lf", "long double", "unsigned char"); + TEST_SCANF_WARN("%Lf", "long double", "signed short"); + TEST_SCANF_WARN("%Lf", "long double", "unsigned short"); + TEST_SCANF_WARN("%Lf", "long double", "signed int"); + TEST_SCANF_WARN("%Lf", "long double", "unsigned int"); + TEST_SCANF_WARN("%Lf", "long double", "signed long"); + TEST_SCANF_WARN("%Lf", "long double", "unsigned long"); + TEST_SCANF_WARN("%Lf", "long double", "signed long long"); + TEST_SCANF_WARN("%Lf", "long double", "unsigned long long"); + TEST_SCANF_WARN("%Lf", "long double", "float"); + TEST_SCANF_WARN("%Lf", "long double", "double"); + TEST_SCANF_NOWARN("%Lf", "long double", "long double"); + TEST_SCANF_WARN("%Lf", "long double", "void *"); + TEST_SCANF_WARN_AKA("%Lf", "long double", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%Lf", "long double", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%Lf", "long double", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%Lf", "long double", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%Lf", "long double", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%Lf", "long double", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%Lf", "long double", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%Lf", "long double", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Lf", "long double", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Lf", "long double", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%Lf", "long double", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_SCANF_WARN("%n", "int", "bool"); + TEST_SCANF_WARN("%n", "int", "char"); + TEST_SCANF_WARN("%n", "int", "signed char"); + TEST_SCANF_WARN("%n", "int", "unsigned char"); + TEST_SCANF_WARN("%n", "int", "signed short"); + TEST_SCANF_WARN("%n", "int", "unsigned short"); + TEST_SCANF_NOWARN("%n", "int", "signed int"); + TEST_SCANF_WARN("%n", "int", "unsigned int"); + TEST_SCANF_WARN("%n", "int", "signed long"); + TEST_SCANF_WARN("%n", "int", "unsigned long"); + TEST_SCANF_WARN("%n", "int", "signed long long"); + TEST_SCANF_WARN("%n", "int", "unsigned long long"); + TEST_SCANF_WARN("%n", "int", "float"); + TEST_SCANF_WARN("%n", "int", "double"); + TEST_SCANF_WARN("%n", "int", "long double"); + TEST_SCANF_WARN("%n", "int", "void *"); + TEST_SCANF_WARN_AKA("%n", "int", "size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%n", "int", "ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%n", "int", "ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%n", "int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA("%n", "int", "intmax_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA("%n", "int", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%n", "int", "std::size_t", "unsigned long", "unsigned long long"); + TEST_SCANF_WARN_AKA_CPP("%n", "int", "std::ssize_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%n", "int", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%n", "int", "std::intptr_t", "signed long", "signed long long"); + TEST_SCANF_WARN_AKA_CPP("%n", "int", "std::uintptr_t", "unsigned long", "unsigned long long"); + + check("void foo() {\n" + " scanf(\"%n\", \"s3\");\n" + " scanf(\"%n\", L\"s5W\");\n" + "}", dinit(CheckOptions, $.inconclusive = true)); + ASSERT_EQUALS("[test.cpp:2]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const char *'.\n" + "[test.cpp:3]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const wchar_t *'.\n", errout_str()); + + check("void foo(long l) {\n" + " scanf(\"%n\", l);\n" + "}", dinit(CheckOptions, $.inconclusive = true)); + ASSERT_EQUALS("[test.cpp:2]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'signed long'.\n", errout_str()); + + check("void g() {\n" // #5104 + " myvector v1(1);\n" + " scanf(\"%d\",&v1[0]);\n" + " myvector v2(1);\n" + " scanf(\"%u\",&v2[0]);\n" + " myvector v3(1);\n" + " scanf(\"%x\",&v3[0]);\n" + " myvector v4(1);\n" + " scanf(\"%lf\",&v4[0]);\n" + " myvector v5(1);\n" + " scanf(\"%10s\",v5[0]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + { + const char * code = "void g() {\n" // #5348 + " size_t s1;\n" + " ptrdiff_t s2;\n" + " ssize_t s3;\n" + " scanf(\"%zd\", &s1);\n" + " scanf(\"%zd\", &s2);\n" + " scanf(\"%zd\", &s3);\n" + "}\n"; + const char* result("[test.cpp:5]: (portability) %zd in format string (no. 1) requires 'ssize_t *' but the argument type is 'size_t * {aka unsigned long *}'.\n" + "[test.cpp:6]: (portability) %zd in format string (no. 1) requires 'ssize_t *' but the argument type is 'ptrdiff_t * {aka signed long *}'.\n"); + const char* result_win64("[test.cpp:5]: (portability) %zd in format string (no. 1) requires 'ssize_t *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" + "[test.cpp:6]: (portability) %zd in format string (no. 1) requires 'ssize_t *' but the argument type is 'ptrdiff_t * {aka signed long long *}'.\n"); + + check(code, dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix32)); + ASSERT_EQUALS(result, errout_str()); + check(code, dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix64)); + ASSERT_EQUALS(result, errout_str()); + check(code, dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS(result, errout_str()); + check(code, dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win64)); + ASSERT_EQUALS(result_win64, errout_str()); + } + { + check("void g() {\n" + " const char c[]=\"42\";\n" + " scanf(\"%s\", c);\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()); + } + + check("void f() {\n" // #7038 + " scanf(\"%i\", \"abc\" + 1);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %i in format string (no. 1) requires 'int *' but the argument type is 'const char *'.\n", errout_str()); + } + + void testPrintfArgument() { + check("void foo() {\n" + " printf(\"%i\");\n" + " printf(\"%i%s\", 123);\n" + " printf(\"%i%s%d\", 0, bar());\n" + " printf(\"%i%%%s%d\", 0, bar());\n" + " printf(\"%idfd%%dfa%s%d\", 0, bar());\n" + " fprintf(stderr,\"%u%s\");\n" + " snprintf(str,10,\"%u%s\");\n" + " sprintf(string1, \"%-*.*s\", 32, string2);\n" // #3364 + " snprintf(a, 9, \"%s%d\", \"11223344\");\n" // #3655 + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) printf format string requires 1 parameter but only 0 are given.\n" + "[test.cpp:3]: (error) printf format string requires 2 parameters but only 1 is given.\n" + "[test.cpp:4]: (error) printf format string requires 3 parameters but only 2 are given.\n" + "[test.cpp:5]: (error) printf format string requires 3 parameters but only 2 are given.\n" + "[test.cpp:6]: (error) printf format string requires 3 parameters but only 2 are given.\n" + "[test.cpp:7]: (error) fprintf format string requires 2 parameters but only 0 are given.\n" + "[test.cpp:8]: (error) snprintf format string requires 2 parameters but only 0 are given.\n" + "[test.cpp:9]: (error) sprintf format string requires 3 parameters but only 2 are given.\n" + "[test.cpp:10]: (error) snprintf format string requires 2 parameters but only 1 is given.\n", errout_str()); + + check("void foo(char *str) {\n" + " printf(\"\", 0);\n" + " printf(\"%i\", 123, bar());\n" + " printf(\"%i%s\", 0, bar(), 43123);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) printf format string requires 0 parameters but 1 is given.\n" + "[test.cpp:3]: (warning) printf format string requires 1 parameter but 2 are given.\n" + "[test.cpp:4]: (warning) printf format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo() {\n" // swprintf exists as MSVC extension and as standard function: #4790 + " swprintf(string1, L\"%i\", 32, string2);\n" // MSVC implementation + " swprintf(string1, L\"%s%s\", L\"a\", string2);\n" // MSVC implementation + " swprintf(string1, 6, L\"%i\", 32, string2);\n" // Standard implementation + " swprintf(string1, 6, L\"%i%s\", 32, string2);\n" // Standard implementation + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) swprintf format string requires 1 parameter but 2 are given.\n" + "[test.cpp:4]: (warning) swprintf format string requires 1 parameter but 2 are given.\n", errout_str()); + + check("void foo(char *str) {\n" + " printf(\"%i\", 0);\n" + " printf(\"%i%s\", 123, bar());\n" + " printf(\"%i%s%d\", 0, bar(), 43123);\n" + " printf(\"%i%%%s%d\", 0, bar(), 43123);\n" + " printf(\"%idfd%%dfa%s%d\", 0, bar(), 43123);\n" + " printf(\"%\"PRId64\"\", 123);\n" + " fprintf(stderr,\"%\"PRId64\"\", 123);\n" + " snprintf(str,10,\"%\"PRId64\"\", 123);\n" + " fprintf(stderr, \"error: %m\");\n" // #3339 + " printf(\"string: %.*s\", len, string);\n" // #3311 + " fprintf(stderr, \"%*cText.\", indent, ' ');\n" // #3313 + " sprintf(string1, \"%*\", 32);\n" // #3364 + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(char* s, const char* s2, std::string s3, int i) {\n" + " printf(\"%s%s\", s, s2);\n" + " printf(\"%s\", i);\n" + " printf(\"%i%s\", i, i);\n" + " printf(\"%s\", s3);\n" + " printf(\"%s\", \"s4\");\n" + " printf(\"%u\", s);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %s in format string (no. 1) requires 'char *' but the argument type is 'signed int'.\n" + "[test.cpp:4]: (warning) %s in format string (no. 2) requires 'char *' but the argument type is 'signed int'.\n" + "[test.cpp:5]: (warning) %s in format string (no. 1) requires 'char *' but the argument type is 'std::string'.\n" + "[test.cpp:7]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'char *'.\n", errout_str()); + + check("void foo(char* s, const char* s2, std::string s3, int i) {\n" + " printf(\"%jd\", s);\n" + " printf(\"%ji\", s);\n" + " printf(\"%ju\", s2);\n" + " printf(\"%jo\", s3);\n" + " printf(\"%jx\", i);\n" + " printf(\"%jX\", i);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %jd in format string (no. 1) requires 'intmax_t' but the argument type is 'char *'.\n" + "[test.cpp:3]: (warning) %ji in format string (no. 1) requires 'intmax_t' but the argument type is 'char *'.\n" + "[test.cpp:4]: (warning) %ju in format string (no. 1) requires 'uintmax_t' but the argument type is 'const char *'.\n" + "[test.cpp:5]: (warning) %jo in format string (no. 1) requires 'uintmax_t' but the argument type is 'std::string'.\n" + "[test.cpp:6]: (warning) %jx in format string (no. 1) requires 'uintmax_t' but the argument type is 'signed int'.\n" + "[test.cpp:7]: (warning) %jX in format string (no. 1) requires 'uintmax_t' but the argument type is 'signed int'.\n", errout_str()); + + check("void foo(uintmax_t uim, std::string s3, unsigned int ui, int i) {\n" + " printf(\"%ju\", uim);\n" + " printf(\"%ju\", ui);\n" + " printf(\"%jd\", ui);\n" + " printf(\"%jd\", s3);\n" + " printf(\"%jd\", i);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %ju in format string (no. 1) requires 'uintmax_t' but the argument type is 'unsigned int'.\n" + "[test.cpp:4]: (warning) %jd in format string (no. 1) requires 'intmax_t' but the argument type is 'unsigned int'.\n" + "[test.cpp:5]: (warning) %jd in format string (no. 1) requires 'intmax_t' but the argument type is 'std::string'.\n" + "[test.cpp:6]: (warning) %jd in format string (no. 1) requires 'intmax_t' but the argument type is 'signed int'.\n", errout_str()); + + check("void foo(const int* cpi, const int ci, int i, int* pi, std::string s) {\n" + " printf(\"%n\", cpi);\n" + " printf(\"%n\", ci);\n" + " printf(\"%n\", i);\n" + " printf(\"%n\", pi);\n" + " printf(\"%n\", s);\n" + " printf(\"%n\", \"s4\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'signed int'.\n" + "[test.cpp:4]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'signed int'.\n" + "[test.cpp:6]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'std::string'.\n" + "[test.cpp:7]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const char *'.\n", errout_str()); + + check("void foo() {\n" + " printf(\"%n\", L\"s5W\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %n in format string (no. 1) requires 'int *' but the argument type is 'const wchar_t *'.\n", errout_str()); + + check("class foo {};\n" + "void foo(const int* cpi, foo f, bar b, bar* bp, double d, int i, unsigned int u) {\n" + " printf(\"%X\", f);\n" + " printf(\"%c\", \"s4\");\n" + " printf(\"%o\", d);\n" + " printf(\"%x\", cpi);\n" + " printf(\"%o\", b);\n" + " printf(\"%X\", bp);\n" + " printf(\"%X\", u);\n" + " printf(\"%X\", i);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %X in format string (no. 1) requires 'unsigned int' but the argument type is 'foo'.\n" + "[test.cpp:4]: (warning) %c in format string (no. 1) requires 'unsigned int' but the argument type is 'const char *'.\n" + "[test.cpp:5]: (warning) %o in format string (no. 1) requires 'unsigned int' but the argument type is 'double'.\n" + "[test.cpp:6]: (warning) %x in format string (no. 1) requires 'unsigned int' but the argument type is 'const signed int *'.\n" + "[test.cpp:8]: (warning) %X in format string (no. 1) requires 'unsigned int' but the argument type is 'bar *'.\n", errout_str()); + + check("class foo {};\n" + "void foo(const char* cpc, char* pc) {\n" + " printf(\"%x\", cpc);\n" + " printf(\"%x\", pc);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %x in format string (no. 1) requires 'unsigned int' but the argument type is 'const char *'.\n" + "[test.cpp:4]: (warning) %x in format string (no. 1) requires 'unsigned int' but the argument type is 'char *'.\n", errout_str()); + + check("class foo {};\n" + "void foo() {\n" + " printf(\"%x\", L\"s5W\");\n" + " printf(\"%X\", L\"s5W\");\n" + " printf(\"%c\", L\"s5W\");\n" + " printf(\"%o\", L\"s5W\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %x in format string (no. 1) requires 'unsigned int' but the argument type is 'const wchar_t *'.\n" + "[test.cpp:4]: (warning) %X in format string (no. 1) requires 'unsigned int' but the argument type is 'const wchar_t *'.\n" + "[test.cpp:5]: (warning) %c in format string (no. 1) requires 'unsigned int' but the argument type is 'const wchar_t *'.\n" + "[test.cpp:6]: (warning) %o in format string (no. 1) requires 'unsigned int' but the argument type is 'const wchar_t *'.\n", errout_str()); + + check("class foo {};\n" + "void foo(const int* cpi, foo f, bar b, bar* bp, double d, unsigned int u, unsigned char uc) {\n" + " printf(\"%i\", f);\n" + " printf(\"%d\", \"s4\");\n" + " printf(\"%d\", d);\n" + " printf(\"%d\", u);\n" + " printf(\"%d\", cpi);\n" + " printf(\"%i\", b);\n" + " printf(\"%i\", bp);\n" + " printf(\"%i\", uc);\n" // char is smaller than int, so there shouldn't be a problem + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %i in format string (no. 1) requires 'int' but the argument type is 'foo'.\n" + "[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'const char *'.\n" + "[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'double'.\n" + "[test.cpp:6]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:7]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'const signed int *'.\n" + "[test.cpp:9]: (warning) %i in format string (no. 1) requires 'int' but the argument type is 'bar *'.\n", errout_str()); + + check("class foo {};\n" + "void foo() {\n" + " printf(\"%i\", L\"s5W\");\n" + " printf(\"%d\", L\"s5W\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %i in format string (no. 1) requires 'int' but the argument type is 'const wchar_t *'.\n" + "[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'const wchar_t *'.\n", errout_str()); + + check("class foo {};\n" + "void foo(const int* cpi, foo f, bar b, bar* bp, double d, int i, bool bo) {\n" + " printf(\"%u\", f);\n" + " printf(\"%u\", \"s4\");\n" + " printf(\"%u\", d);\n" + " printf(\"%u\", i);\n" + " printf(\"%u\", cpi);\n" + " printf(\"%u\", b);\n" + " printf(\"%u\", bp);\n" + " printf(\"%u\", bo);\n" // bool shouldn't have a negative sign + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'foo'.\n" + "[test.cpp:4]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'const char *'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'double'.\n" + "[test.cpp:6]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:7]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'const signed int *'.\n" + "[test.cpp:9]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'bar *'.\n", errout_str()); + + check("class foo {};\n" + "void foo(const int* cpi, foo f, bar b, bar* bp, double d, int i, bool bo) {\n" + " printf(\"%u\", L\"s5W\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'const wchar_t *'.\n", errout_str()); + + check("class foo {};\n" + "void foo(const int* cpi, foo f, bar b, bar* bp, char c) {\n" + " printf(\"%p\", f);\n" + " printf(\"%p\", c);\n" + " printf(\"%p\", bp);\n" + " printf(\"%p\", cpi);\n" + " printf(\"%p\", b);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %p in format string (no. 1) requires an address but the argument type is 'foo'.\n" + "[test.cpp:4]: (warning) %p in format string (no. 1) requires an address but the argument type is 'char'.\n", errout_str()); + + check("class foo {};\n" + "void foo(char* pc, const char* cpc, wchar_t* pwc, const wchar_t* cpwc) {\n" + " printf(\"%p\", pc);\n" + " printf(\"%p\", cpc);\n" + " printf(\"%p\", pwc);\n" + " printf(\"%p\", cpwc);\n" + " printf(\"%p\", \"s4\");\n" + " printf(\"%p\", L\"s5W\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class foo {};\n" + "void foo(const int* cpi, foo f, bar b, bar* bp, double d) {\n" + " printf(\"%e\", f);\n" + " printf(\"%E\", \"s4\");\n" + " printf(\"%f\", cpi);\n" + " printf(\"%G\", bp);\n" + " printf(\"%f\", d);\n" + " printf(\"%f\", b);\n" + " printf(\"%f\", (float)cpi);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %e in format string (no. 1) requires 'double' but the argument type is 'foo'.\n" + "[test.cpp:4]: (warning) %E in format string (no. 1) requires 'double' but the argument type is 'const char *'.\n" + "[test.cpp:5]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'const signed int *'.\n" + "[test.cpp:6]: (warning) %G in format string (no. 1) requires 'double' but the argument type is 'bar *'.\n", errout_str()); + + check("class foo {};\n" + "void foo(const char* cpc, char* pc) {\n" + " printf(\"%e\", cpc);\n" + " printf(\"%E\", pc);\n" + " printf(\"%f\", cpc);\n" + " printf(\"%G\", pc);\n" + " printf(\"%f\", pc);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %e in format string (no. 1) requires 'double' but the argument type is 'const char *'.\n" + "[test.cpp:4]: (warning) %E in format string (no. 1) requires 'double' but the argument type is 'char *'.\n" + "[test.cpp:5]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'const char *'.\n" + "[test.cpp:6]: (warning) %G in format string (no. 1) requires 'double' but the argument type is 'char *'.\n" + "[test.cpp:7]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'char *'.\n", errout_str()); + + check("class foo {};\n" + "void foo() {\n" + " printf(\"%e\", L\"s5W\");\n" + " printf(\"%E\", L\"s5W\");\n" + " printf(\"%f\", L\"s5W\");\n" + " printf(\"%G\", L\"s5W\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %e in format string (no. 1) requires 'double' but the argument type is 'const wchar_t *'.\n" + "[test.cpp:4]: (warning) %E in format string (no. 1) requires 'double' but the argument type is 'const wchar_t *'.\n" + "[test.cpp:5]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'const wchar_t *'.\n" + "[test.cpp:6]: (warning) %G in format string (no. 1) requires 'double' but the argument type is 'const wchar_t *'.\n", errout_str()); + + check("class foo;\n" + "void foo(foo f) {\n" + " printf(\"%u\", f);\n" + " printf(\"%f\", f);\n" + " printf(\"%p\", f);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'foo'.\n" + "[test.cpp:4]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'foo'.\n" + "[test.cpp:5]: (warning) %p in format string (no. 1) requires an address but the argument type is 'foo'.\n", errout_str()); + + // Ticket #4189 (Improve check (printf("%l") not detected)) tests (according to C99 7.19.6.1.7) + // False positive tests + check("void foo(signed char sc, unsigned char uc, short int si, unsigned short int usi) {\n" + " printf(\"%hhx %hhd\", sc, uc);\n" + " printf(\"%hd %hu\", si, usi);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %hhx in format string (no. 1) requires 'unsigned char' but the argument type is 'signed char'.\n" + "[test.cpp:2]: (warning) %hhd in format string (no. 2) requires 'char' but the argument type is 'unsigned char'.\n", errout_str()); + + check("void foo(long long int lli, unsigned long long int ulli, long int li, unsigned long int uli) {\n" + " printf(\"%llo %llx\", lli, ulli);\n" + " printf(\"%ld %lu\", li, uli);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(intmax_t im, uintmax_t uim, size_t s, ptrdiff_t p, long double ld, std::size_t ss, std::ptrdiff_t sp) {\n" + " printf(\"%jd %jo\", im, uim);\n" + " printf(\"%zx\", s);\n" + " printf(\"%ti\", p);\n" + " printf(\"%Lf\", ld);\n" + " printf(\"%zx\", ss);\n" + " printf(\"%ti\", sp);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Unrecognized (and non-existent in standard library) specifiers. + // Perhaps should emit warnings + check("void foo(intmax_t im, uintmax_t uim, size_t s, ptrdiff_t p, long double ld, std::size_t ss, std::ptrdiff_t sp) {\n" + " printf(\"%jb %jw\", im, uim);\n" + " printf(\"%zr\", s);\n" + " printf(\"%tm\", p);\n" + " printf(\"%La\", ld);\n" + " printf(\"%zv\", ss);\n" + " printf(\"%tp\", sp);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(long long l, ptrdiff_t p, std::ptrdiff_t sp) {\n" + " printf(\"%td\", p);\n" + " printf(\"%td\", sp);\n" + " printf(\"%td\", l);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) %td in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'signed long long'.\n", errout_str()); + + check("void foo(int i, long double ld) {\n" + " printf(\"%zx %zu\", i, ld);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %zx in format string (no. 1) requires 'size_t' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %zu in format string (no. 2) requires 'size_t' but the argument type is 'long double'.\n", errout_str()); + + check("void foo(unsigned int ui, long double ld) {\n" + " printf(\"%zu %zx\", ui, ld);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %zu in format string (no. 1) requires 'size_t' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %zx in format string (no. 2) requires 'size_t' but the argument type is 'long double'.\n", errout_str()); + + check("void foo(int i, long double ld) {\n" + " printf(\"%tx %tu\", i, ld);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %tx in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %tu in format string (no. 2) requires 'unsigned ptrdiff_t' but the argument type is 'long double'.\n", errout_str()); + + // False negative test + check("void foo(unsigned int i) {\n" + " printf(\"%h\", i);\n" + " printf(\"%hh\", i);\n" + " printf(\"%l\", i);\n" + " printf(\"%ll\", i);\n" + " printf(\"%j\", i);\n" + " printf(\"%z\", i);\n" + " printf(\"%t\", i);\n" + " printf(\"%L\", i);\n" + " printf(\"%I\", i);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) 'h' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:3]: (warning) 'hh' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:4]: (warning) 'l' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:5]: (warning) 'll' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:6]: (warning) 'j' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:7]: (warning) 'z' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:8]: (warning) 't' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:9]: (warning) 'L' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:10]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout_str()); + + check("void foo(unsigned int i) {\n" + " printf(\"%hd\", i);\n" + " printf(\"%hhd\", i);\n" + " printf(\"%ld\", i);\n" + " printf(\"%lld\", i);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %hd in format string (no. 1) requires 'short' but the argument type is 'unsigned int'.\n" + "[test.cpp:3]: (warning) %hhd in format string (no. 1) requires 'char' but the argument type is 'unsigned int'.\n" + "[test.cpp:4]: (warning) %ld in format string (no. 1) requires 'long' but the argument type is 'unsigned int'.\n" + "[test.cpp:5]: (warning) %lld in format string (no. 1) requires 'long long' but the argument type is 'unsigned int'.\n", errout_str()); + + check("void foo(size_t s, ptrdiff_t p) {\n" + " printf(\"%zd\", s);\n" + " printf(\"%tu\", p);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix32)); + ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n", errout_str()); + + check("void foo(std::size_t s, std::ptrdiff_t p) {\n" + " printf(\"%zd\", s);\n" + " printf(\"%tu\", p);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix32)); + ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'std::size_t {aka unsigned long}'.\n" + "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'std::ptrdiff_t {aka signed long}'.\n", errout_str()); + + check("void foo(size_t s, ptrdiff_t p) {\n" + " printf(\"%zd\", s);\n" + " printf(\"%tu\", p);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix64)); + ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n", errout_str()); + + check("void foo(std::size_t s, std::ptrdiff_t p) {\n" + " printf(\"%zd\", s);\n" + " printf(\"%tu\", p);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix64)); + ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'std::size_t {aka unsigned long}'.\n" + "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'std::ptrdiff_t {aka signed long}'.\n", errout_str()); + + check("void foo(size_t s, ptrdiff_t p) {\n" + " printf(\"%zd\", s);\n" + " printf(\"%tu\", p);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n", errout_str()); + + check("void foo(std::size_t s, std::ptrdiff_t p) {\n" + " printf(\"%zd\", s);\n" + " printf(\"%tu\", p);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'std::size_t {aka unsigned long}'.\n" + "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'std::ptrdiff_t {aka signed long}'.\n", errout_str()); + + check("void foo(size_t s, ptrdiff_t p) {\n" + " printf(\"%zd\", s);\n" + " printf(\"%tu\", p);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win64)); + ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'size_t {aka unsigned long long}'.\n" + "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'ptrdiff_t {aka signed long long}'.\n", errout_str()); + + check("void foo(std::size_t s, std::ptrdiff_t p) {\n" + " printf(\"%zd\", s);\n" + " printf(\"%tu\", p);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win64)); + ASSERT_EQUALS("[test.cpp:2]: (portability) %zd in format string (no. 1) requires 'ssize_t' but the argument type is 'std::size_t {aka unsigned long long}'.\n" + "[test.cpp:3]: (portability) %tu in format string (no. 1) requires 'unsigned ptrdiff_t' but the argument type is 'std::ptrdiff_t {aka signed long long}'.\n", errout_str()); + + check("void foo(size_t s, uintmax_t um) {\n" + " printf(\"%lu\", s);\n" + " printf(\"%lu\", um);\n" + " printf(\"%llu\", s);\n" + " printf(\"%llu\", um);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win64)); + ASSERT_EQUALS("[test.cpp:2]: (portability) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'size_t {aka unsigned long long}'.\n" + "[test.cpp:3]: (portability) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'uintmax_t {aka unsigned long long}'.\n" + "[test.cpp:4]: (portability) %llu in format string (no. 1) requires 'unsigned long long' but the argument type is 'size_t {aka unsigned long long}'.\n" + "[test.cpp:5]: (portability) %llu in format string (no. 1) requires 'unsigned long long' but the argument type is 'uintmax_t {aka unsigned long long}'.\n", errout_str()); + + check("void foo(unsigned int i) {\n" + " printf(\"%ld\", i);\n" + " printf(\"%lld\", i);\n" + " printf(\"%lu\", i);\n" + " printf(\"%llu\", i);\n" + " printf(\"%lx\", i);\n" + " printf(\"%llx\", i);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %ld in format string (no. 1) requires 'long' but the argument type is 'unsigned int'.\n" + "[test.cpp:3]: (warning) %lld in format string (no. 1) requires 'long long' but the argument type is 'unsigned int'.\n" + "[test.cpp:4]: (warning) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'unsigned int'.\n" + "[test.cpp:5]: (warning) %llu in format string (no. 1) requires 'unsigned long long' but the argument type is 'unsigned int'.\n" + "[test.cpp:6]: (warning) %lx in format string (no. 1) requires 'unsigned long' but the argument type is 'unsigned int'.\n" + "[test.cpp:7]: (warning) %llx in format string (no. 1) requires 'unsigned long long' but the argument type is 'unsigned int'.\n", errout_str()); + + check("void foo(int i, intmax_t im, ptrdiff_t p) {\n" + " printf(\"%lld\", i);\n" + " printf(\"%lld\", im);\n" + " printf(\"%lld\", p);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %lld in format string (no. 1) requires 'long long' but the argument type is 'signed int'.\n", errout_str()); + + check("void foo(intmax_t im, ptrdiff_t p) {\n" + " printf(\"%lld\", im);\n" + " printf(\"%lld\", p);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win64)); + ASSERT_EQUALS("[test.cpp:2]: (portability) %lld in format string (no. 1) requires 'long long' but the argument type is 'intmax_t {aka signed long long}'.\n" + "[test.cpp:3]: (portability) %lld in format string (no. 1) requires 'long long' but the argument type is 'ptrdiff_t {aka signed long long}'.\n", errout_str()); + + check("class Foo {\n" + " double d;\n" + " struct Bar {\n" + " int i;\n" + " } bar[2];\n" + " struct Baz {\n" + " int i;\n" + " } baz;\n" + "};\n" + "int a[10];\n" + "Foo f[10];\n" + "void foo(const Foo* foo) {\n" + " printf(\"%d %f %f %d %f %f\",\n" + " foo->d, foo->bar[0].i, a[0],\n" + " f[0].d, f[0].baz.i, f[0].bar[0].i);\n" + "}"); + ASSERT_EQUALS("[test.cpp:13]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'double'.\n" + "[test.cpp:13]: (warning) %f in format string (no. 2) requires 'double' but the argument type is 'signed int'.\n" + "[test.cpp:13]: (warning) %f in format string (no. 3) requires 'double' but the argument type is 'int'.\n" + "[test.cpp:13]: (warning) %d in format string (no. 4) requires 'int' but the argument type is 'double'.\n" + "[test.cpp:13]: (warning) %f in format string (no. 5) requires 'double' but the argument type is 'int'.\n" + "[test.cpp:13]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'int'.\n", errout_str()); + + check("short f() { return 0; }\n" + "void foo() { printf(\"%d %u %lu %I64u %I64d %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed short'.\n" + "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed short'.\n" + "[test.cpp:2]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64' but the argument type is 'signed short'.\n" + "[test.cpp:2]: (warning) %I64d in format string (no. 5) requires '__int64' but the argument type is 'signed short'.\n" + "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'signed short'.\n" + "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'signed short'.\n" + "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'signed short'.\n", errout_str()); + + check("unsigned short f() { return 0; }\n" + "void foo() { printf(\"%u %d %ld %I64d %I64u %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned short'.\n" + "[test.cpp:2]: (warning) %I64d in format string (no. 4) requires '__int64' but the argument type is 'unsigned short'.\n" + "[test.cpp:2]: (warning) %I64u in format string (no. 5) requires 'unsigned __int64' but the argument type is 'unsigned short'.\n" + "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'unsigned short'.\n" + "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'unsigned short'.\n" + "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'unsigned short'.\n", errout_str()); + + check("int f() { return 0; }\n" + "void foo() { printf(\"%d %u %lu %I64u %I64d %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %I64d in format string (no. 5) requires '__int64' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'signed int'.\n", errout_str()); + + check("unsigned int f() { return 0; }\n" + "void foo() { printf(\"%u %d %ld %I64d %I64u %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %I64d in format string (no. 4) requires '__int64' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %I64u in format string (no. 5) requires 'unsigned __int64' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'unsigned int'.\n", errout_str()); + + check("long f() { return 0; }\n" + "void foo() { printf(\"%ld %u %lu %I64u %I64d %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed long'.\n" + "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed long'.\n" + "[test.cpp:2]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64' but the argument type is 'signed long'.\n" + "[test.cpp:2]: (warning) %I64d in format string (no. 5) requires '__int64' but the argument type is 'signed long'.\n" + "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'signed long'.\n" + "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'signed long'.\n" + "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'signed long'.\n", errout_str()); + + check("unsigned long f() { return 0; }\n" + "void foo() { printf(\"%lu %d %ld %I64d %I64u %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned long'.\n" + "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned long'.\n" + "[test.cpp:2]: (warning) %I64d in format string (no. 4) requires '__int64' but the argument type is 'unsigned long'.\n" + "[test.cpp:2]: (warning) %I64u in format string (no. 5) requires 'unsigned __int64' but the argument type is 'unsigned long'.\n" + "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'unsigned long'.\n" + "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'unsigned long'.\n" + "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'unsigned long'.\n", errout_str()); + + check("long long f() { return 0; }\n" + "void foo() { printf(\"%lld %u %lu %I64u %I64d %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed long long'.\n" + "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed long long'.\n" + "[test.cpp:2]: (warning) %I64u in format string (no. 4) requires 'unsigned __int64' but the argument type is 'signed long long'.\n" + "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'signed long long'.\n" + "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'signed long long'.\n" + "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'signed long long'.\n", errout_str()); + + check("unsigned long long f() { return 0; }\n" + "void foo() { printf(\"%llu %d %ld %I64d %I64u %f %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned long long'.\n" + "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned long long'.\n" + "[test.cpp:2]: (warning) %I64d in format string (no. 4) requires '__int64' but the argument type is 'unsigned long long'.\n" + "[test.cpp:2]: (warning) %f in format string (no. 6) requires 'double' but the argument type is 'unsigned long long'.\n" + "[test.cpp:2]: (warning) %Lf in format string (no. 7) requires 'long double' but the argument type is 'unsigned long long'.\n" + "[test.cpp:2]: (warning) %p in format string (no. 8) requires an address but the argument type is 'unsigned long long'.\n", errout_str()); + + check("float f() { return 0; }\n" + "void foo() { printf(\"%f %d %ld %u %lu %I64d %I64u %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'float'.\n" + "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'float'.\n" + "[test.cpp:2]: (warning) %u in format string (no. 4) requires 'unsigned int' but the argument type is 'float'.\n" + "[test.cpp:2]: (warning) %lu in format string (no. 5) requires 'unsigned long' but the argument type is 'float'.\n" + "[test.cpp:2]: (warning) %I64d in format string (no. 6) requires '__int64' but the argument type is 'float'.\n" + "[test.cpp:2]: (warning) %I64u in format string (no. 7) requires 'unsigned __int64' but the argument type is 'float'.\n" + "[test.cpp:2]: (warning) %Lf in format string (no. 8) requires 'long double' but the argument type is 'float'.\n" + "[test.cpp:2]: (warning) %p in format string (no. 9) requires an address but the argument type is 'float'.\n", errout_str()); + + check("double f() { return 0; }\n" + "void foo() { printf(\"%f %d %ld %u %lu %I64d %I64u %Lf %p\", f(), f(), f(), f(), f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'double'.\n" + "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'double'.\n" + "[test.cpp:2]: (warning) %u in format string (no. 4) requires 'unsigned int' but the argument type is 'double'.\n" + "[test.cpp:2]: (warning) %lu in format string (no. 5) requires 'unsigned long' but the argument type is 'double'.\n" + "[test.cpp:2]: (warning) %I64d in format string (no. 6) requires '__int64' but the argument type is 'double'.\n" + "[test.cpp:2]: (warning) %I64u in format string (no. 7) requires 'unsigned __int64' but the argument type is 'double'.\n" + "[test.cpp:2]: (warning) %Lf in format string (no. 8) requires 'long double' but the argument type is 'double'.\n" + "[test.cpp:2]: (warning) %p in format string (no. 9) requires an address but the argument type is 'double'.\n", errout_str()); + + check("long double f() { return 0; }\n" + "void foo() { printf(\"%Lf %d %ld %u %lu %I64d %I64u %f %p\", f(), f(), f(), f(), f(), f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'long double'.\n" + "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'long double'.\n" + "[test.cpp:2]: (warning) %u in format string (no. 4) requires 'unsigned int' but the argument type is 'long double'.\n" + "[test.cpp:2]: (warning) %lu in format string (no. 5) requires 'unsigned long' but the argument type is 'long double'.\n" + "[test.cpp:2]: (warning) %I64d in format string (no. 6) requires '__int64' but the argument type is 'long double'.\n" + "[test.cpp:2]: (warning) %I64u in format string (no. 7) requires 'unsigned __int64' but the argument type is 'long double'.\n" + "[test.cpp:2]: (warning) %f in format string (no. 8) requires 'double' but the argument type is 'long double'.\n" + "[test.cpp:2]: (warning) %p in format string (no. 9) requires an address but the argument type is 'long double'.\n", errout_str()); + + check("int f() { return 0; }\n" + "void foo() { printf(\"%I64d %I64u %I64x %d\", f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %I64d in format string (no. 1) requires '__int64' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %I64u in format string (no. 2) requires 'unsigned __int64' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %I64x in format string (no. 3) requires 'unsigned __int64' but the argument type is 'signed int'.\n", errout_str()); + + check("long long f() { return 0; }\n" + "void foo() { printf(\"%I32d %I32u %I32x %lld\", f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %I32d in format string (no. 1) requires '__int32' but the argument type is 'signed long long'.\n" + "[test.cpp:2]: (warning) %I32u in format string (no. 2) requires 'unsigned __int32' but the argument type is 'signed long long'.\n" + "[test.cpp:2]: (warning) %I32x in format string (no. 3) requires 'unsigned __int32' but the argument type is 'signed long long'.\n", errout_str()); + + check("unsigned long long f() { return 0; }\n" + "void foo() { printf(\"%I32d %I32u %I32x %llx\", f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned long long'.\n" + "[test.cpp:2]: (warning) %I32u in format string (no. 2) requires 'unsigned __int32' but the argument type is 'unsigned long long'.\n" + "[test.cpp:2]: (warning) %I32x in format string (no. 3) requires 'unsigned __int32' but the argument type is 'unsigned long long'.\n", errout_str()); + + check("signed char f() { return 0; }\n" + "void foo() { printf(\"%Id %Iu %Ix %hhi\", f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %Id in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'signed char'.\n" + "[test.cpp:2]: (warning) %Iu in format string (no. 2) requires 'size_t' but the argument type is 'signed char'.\n" + "[test.cpp:2]: (warning) %Ix in format string (no. 3) requires 'size_t' but the argument type is 'signed char'.\n", errout_str()); + + check("unsigned char f() { return 0; }\n" + "void foo() { printf(\"%Id %Iu %Ix %hho\", f(), f(), f(), f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %Id in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'unsigned char'.\n" + "[test.cpp:2]: (warning) %Iu in format string (no. 2) requires 'size_t' but the argument type is 'unsigned char'.\n" + "[test.cpp:2]: (warning) %Ix in format string (no. 3) requires 'size_t' but the argument type is 'unsigned char'.\n", errout_str()); + + check("namespace bar { int f() { return 0; } }\n" + "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar::f(), bar::f(), bar::f(), bar::f(), bar::f(), bar::f()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout_str()); + + check("struct Fred { int i; } f;\n" + "void foo() { printf(\"%d %u %lu %f %Lf %p\", f.i, f.i, f.i, f.i, f.i, f.i); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout_str()); + + check("struct Fred { unsigned int u; } f;\n" + "void foo() { printf(\"%u %d %ld %f %Lf %p\", f.u, f.u, f.u, f.u, f.u, f.u); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address but the argument type is 'unsigned int'.\n", errout_str()); + + check("struct Fred { unsigned int ui() { return 0; } } f;\n" + "void foo() { printf(\"%u %d %ld %f %Lf %p\", f.ui(), f.ui(), f.ui(), f.ui(), f.ui(), f.ui()); }"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %p in format string (no. 6) requires an address but the argument type is 'unsigned int'.\n", errout_str()); + + // #4975 + check("void f(int len, int newline) {\n" + " printf(\"%s\", newline ? a : str + len);\n" + " printf(\"%s\", newline + newline);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %s in format string (no. 1) requires 'char *' but the argument type is 'signed int'.\n", errout_str()); + + check("struct Fred { int i; } f;\n" + "struct Fred & bar() { };\n" + "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar().i, bar().i, bar().i, bar().i, bar().i, bar().i); }"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout_str()); + + check("struct Fred { int i; } f;\n" + "const struct Fred & bar() { };\n" + "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar().i, bar().i, bar().i, bar().i, bar().i, bar().i); }"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout_str()); + + check("struct Fred { int i; } f;\n" + "static const struct Fred & bar() { };\n" + "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar().i, bar().i, bar().i, bar().i, bar().i, bar().i); }"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout_str()); + + check("struct Fred { int i; } f[2];\n" + "struct Fred * bar() { return f; };\n" + "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i); }"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout_str()); + + check("struct Fred { int i; } f[2];\n" + "const struct Fred * bar() { return f; };\n" + "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i); }"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout_str()); + + check("struct Fred { int i; } f[2];\n" + "static const struct Fred * bar() { return f; };\n" + "void foo() { printf(\"%d %u %lu %f %Lf %p\", bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i, bar()[0].i); }"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %lu in format string (no. 3) requires 'unsigned long' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %Lf in format string (no. 5) requires 'long double' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %p in format string (no. 6) requires an address but the argument type is 'signed int'.\n", errout_str()); + + check("struct Fred { int32_t i; } f;\n" + "struct Fred & bar() { };\n" + "void foo() { printf(\"%d %ld %u %lu %f %Lf\", bar().i, bar().i, bar().i, bar().i, bar().i, bar().i); }"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %ld in format string (no. 2) requires 'long' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %u in format string (no. 3) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %lu in format string (no. 4) requires 'unsigned long' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %f in format string (no. 5) requires 'double' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %Lf in format string (no. 6) requires 'long double' but the argument type is 'signed int'.\n", + errout_str()); + + // #4984 + check("void f(double *x) {\n" + " printf(\"%f\", x[0]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int array[10];\n" + "int * foo() { return array; }\n" + "void f() {\n" + " printf(\"%f\", foo()[0]);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n", errout_str()); + + check("struct Base { int length() { } };\n" + "struct Derived : public Base { };\n" + "void foo(Derived * d) {\n" + " printf(\"%f\", d.length());\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n", errout_str()); + + check("std::vector v;\n" + "void foo() {\n" + " printf(\"%d %u %f\", v[0], v[0], v[0]);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:3]: (warning) %f in format string (no. 3) requires 'double' but the argument type is 'signed int'.\n", errout_str()); + + // #4999 (crash) + check("int bar(int a);\n" + "void foo() {\n" + " printf(\"%d\", bar(0));\n" + "}"); + 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", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" + "[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n" + "[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" + "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n", errout_str()); + + check("std::vector v;\n" + "std::string s;\n" + "void foo() {\n" + " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" + " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" + "}\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win64)); + ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long long}'.\n" + "[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long long}'.\n" + "[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long long}'.\n" + "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long long}'.\n", errout_str()); + + check("std::vector v;\n" + "std::string s;\n" + "void foo() {\n" + " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" + " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" + "}\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix32)); + ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" + "[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n" + "[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" + "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n", errout_str()); + + check("std::vector v;\n" + "std::string s;\n" + "void foo() {\n" + " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" + " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" + "}\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix64)); + ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" + "[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n" + "[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" + "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n", errout_str()); + + check("class Fred : public std::vector {} v;\n" + "std::string s;\n" + "void foo() {\n" + " printf(\"%zu %Iu %d %f\", v.size(), v.size(), v.size(), v.size());\n" + " printf(\"%zu %Iu %d %f\", s.size(), s.size(), s.size(), s.size());\n" + "}\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix64)); + ASSERT_EQUALS("[test.cpp:4]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:4]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:5]: (portability) %d in format string (no. 3) requires 'int' but the argument type is 'std::size_t {aka unsigned long}'.\n" + "[test.cpp:5]: (portability) %f in format string (no. 4) requires 'double' but the argument type is 'std::size_t {aka unsigned long}'.\n", errout_str()); + + check("class Fred : public std::vector {} v;\n" + "void foo() {\n" + " printf(\"%d %u %f\", v[0], v[0], v[0]);\n" + "}\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::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", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::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", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix64)); + ASSERT_EQUALS("[test.cpp:8]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'char *'.\n" + "[test.cpp:8]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'char *'.\n" + "[test.cpp:8]: (warning) %u in format string (no. 3) requires 'unsigned int' but the argument type is 'char *'.\n" + "[test.cpp:9]: (portability) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'std::size_t {aka unsigned long}'.\n" + "[test.cpp:9]: (portability) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:10]: (portability) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'std::size_t {aka unsigned long}'.\n" + "[test.cpp:10]: (portability) %lu in format string (no. 2) requires 'unsigned long' but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:11]: (portability) %llu in format string (no. 1) requires 'unsigned long long' but the argument type is 'std::size_t {aka unsigned long}'.\n" + "[test.cpp:11]: (portability) %llu in format string (no. 2) requires 'unsigned long long' but the argument type is 'size_t {aka unsigned long}'.\n", errout_str()); + + check("bool b; bool bf();\n" + "char c; char cf();\n" + "signed char sc; signed char scf();\n" + "unsigned char uc; unsigned char ucf();\n" + "short s; short sf();\n" + "unsigned short us; unsigned short usf();\n" + "size_t st; size_t stf();\n" + "ptrdiff_t pt; ptrdiff_t ptf();\n" + "char * pc; char * pcf();\n" + "char cl[] = \"123\";\n" + "char ca[3];\n" + "void foo() {\n" + " printf(\"%td %zd %d %d %d %d %d %d %d %d %d %d %d\", pt, pt, b, c, sc, uc, s, us, st, pt, pc, cl, ca);\n" + "}\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix64)); + ASSERT_EQUALS("[test.cpp:13]: (portability) %zd in format string (no. 2) requires 'ssize_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n" + "[test.cpp:13]: (portability) %d in format string (no. 9) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:13]: (portability) %d in format string (no. 10) requires 'int' but the argument type is 'ptrdiff_t {aka signed long}'.\n" + "[test.cpp:13]: (warning) %d in format string (no. 11) requires 'int' but the argument type is 'char *'.\n" + "[test.cpp:13]: (warning) %d in format string (no. 12) requires 'int' but the argument type is 'char *'.\n" + "[test.cpp:13]: (warning) %d in format string (no. 13) requires 'int' but the argument type is 'char *'.\n", errout_str()); + + check("bool b; bool bf();\n" + "char c; char cf();\n" + "signed char sc; signed char scf();\n" + "unsigned char uc; unsigned char ucf();\n" + "short s; short sf();\n" + "unsigned short us; unsigned short usf();\n" + "size_t st; size_t stf();\n" + "ptrdiff_t pt; ptrdiff_t ptf();\n" + "char * pc; char * pcf();\n" + "char cl[] = \"123\";\n" + "char ca[3];\n" + "void foo() {\n" + " printf(\"%ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld\", b, c, sc, uc, s, us, st, pt, pc, cl, ca);\n" + "}\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix64)); + ASSERT_EQUALS("[test.cpp:13]: (warning) %ld in format string (no. 1) requires 'long' but the argument type is 'bool'.\n" + "[test.cpp:13]: (warning) %ld in format string (no. 2) requires 'long' but the argument type is 'char'.\n" + "[test.cpp:13]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'signed char'.\n" + "[test.cpp:13]: (warning) %ld in format string (no. 4) requires 'long' but the argument type is 'unsigned char'.\n" + "[test.cpp:13]: (warning) %ld in format string (no. 5) requires 'long' but the argument type is 'signed short'.\n" + "[test.cpp:13]: (warning) %ld in format string (no. 6) requires 'long' but the argument type is 'unsigned short'.\n" + "[test.cpp:13]: (portability) %ld in format string (no. 7) requires 'long' but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:13]: (portability) %ld in format string (no. 8) requires 'long' but the argument type is 'ptrdiff_t {aka signed long}'.\n" + "[test.cpp:13]: (warning) %ld in format string (no. 9) requires 'long' but the argument type is 'char *'.\n" + "[test.cpp:13]: (warning) %ld in format string (no. 10) requires 'long' but the argument type is 'char *'.\n" + "[test.cpp:13]: (warning) %ld in format string (no. 11) requires 'long' but the argument type is 'char *'.\n", errout_str()); + + + check("bool b; bool bf();\n" + "char c; char cf();\n" + "signed char sc; signed char scf();\n" + "unsigned char uc; unsigned char ucf();\n" + "short s; short sf();\n" + "unsigned short us; unsigned short usf();\n" + "size_t st; size_t stf();\n" + "ptrdiff_t pt; ptrdiff_t ptf();\n" + "char * pc; char * pcf();\n" + "char cl[] = \"123\";\n" + "char ca[3];\n" + "void foo() {\n" + " printf(\"%td %zd %d %d %d %d %d %d %d %d %d\", ptf(), ptf(), bf(), cf(), scf(), ucf(), sf(), usf(), stf(), ptf(), pcf());\n" + "}\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix64)); + ASSERT_EQUALS("[test.cpp:13]: (portability) %zd in format string (no. 2) requires 'ssize_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n" + "[test.cpp:13]: (portability) %d in format string (no. 9) requires 'int' but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:13]: (portability) %d in format string (no. 10) requires 'int' but the argument type is 'ptrdiff_t {aka signed long}'.\n" + "[test.cpp:13]: (warning) %d in format string (no. 11) requires 'int' but the argument type is 'char *'.\n", errout_str()); + + check("bool b; bool bf();\n" + "char c; char cf();\n" + "signed char sc; signed char scf();\n" + "unsigned char uc; unsigned char ucf();\n" + "short s; short sf();\n" + "unsigned short us; unsigned short usf();\n" + "size_t st; size_t stf();\n" + "ptrdiff_t pt; ptrdiff_t ptf();\n" + "char * pc; char * pcf();\n" + "char cl[] = \"123\";\n" + "char ca[3];\n" + "void foo() {\n" + " printf(\"%ld %ld %ld %ld %ld %ld %ld %ld %ld\", bf(), cf(), scf(), ucf(), sf(), usf(), stf(), ptf(), pcf());\n" + "}\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix64)); + ASSERT_EQUALS("[test.cpp:13]: (warning) %ld in format string (no. 1) requires 'long' but the argument type is 'bool'.\n" + "[test.cpp:13]: (warning) %ld in format string (no. 2) requires 'long' but the argument type is 'char'.\n" + "[test.cpp:13]: (warning) %ld in format string (no. 3) requires 'long' but the argument type is 'signed char'.\n" + "[test.cpp:13]: (warning) %ld in format string (no. 4) requires 'long' but the argument type is 'unsigned char'.\n" + "[test.cpp:13]: (warning) %ld in format string (no. 5) requires 'long' but the argument type is 'signed short'.\n" + "[test.cpp:13]: (warning) %ld in format string (no. 6) requires 'long' but the argument type is 'unsigned short'.\n" + "[test.cpp:13]: (portability) %ld in format string (no. 7) requires 'long' but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:13]: (portability) %ld in format string (no. 8) requires 'long' but the argument type is 'ptrdiff_t {aka signed long}'.\n" + "[test.cpp:13]: (warning) %ld in format string (no. 9) requires 'long' but the argument type is 'char *'.\n", errout_str()); + + check("struct A {};\n" + "class B : public std::vector {} b;\n" + "class C : public std::vector {} c;\n" + "std::string s;\n" + "void foo() {\n" + " printf(\"%zu %u\", b.size(), b.size());\n" + " printf(\"%p %d\", b[0], b[0]);\n" + " printf(\"%p %d\", c[0], c[0]);\n" + " printf(\"%p %d\", s.c_str(), s.c_str());\n" + "}\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix64)); + ASSERT_EQUALS("[test.cpp:6]: (portability) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:7]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const int *'.\n" + "[test.cpp:8]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const struct A *'.\n" + "[test.cpp:9]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const char *'.\n", errout_str()); + + check("class A : public std::vector {} a;\n" + "class B : public std::string {} b;\n" + "std::string s;\n" + "void foo() {\n" + " printf(\"%p %d\", a[0].c_str(), a[0].c_str());\n" + " printf(\"%c %p\", b[0], b[0]);\n" + " printf(\"%c %p\", s[0], s[0]);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::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", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix64)); + ASSERT_EQUALS("[test.cpp:7]: (portability) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'size_t {aka unsigned long}'.\n", errout_str()); + + check("DWORD a;\n" + "DWORD_PTR b;\n" + "void foo() {\n" + " printf(\"%u %u\", a, b);\n" + "}\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:4]: (portability) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'DWORD {aka unsigned long}'.\n" + "[test.cpp:4]: (portability) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'DWORD_PTR {aka unsigned long}'.\n", errout_str()); + + check("unsigned long a[] = { 1, 2 };\n" + "void foo() {\n" + " printf(\"%d %d %x \", a[0], a[0], a[0]);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::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", dinit(CheckOptions, $.platform = Platform::Type::Win64)); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " printf(\"%f %d\", static_cast(1.0f), reinterpret_cast(0));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const void *'.\n", errout_str()); + + check("void foo() {\n" + " UNKNOWN * u;\n" + " printf(\"%d %x %u %f\", u[i], u[i], u[i], u[i]);\n" + "}"); + 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" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'signed long'.\n" + "[test.cpp:3]: (warning) %x in format string (no. 2) requires 'unsigned int' but the argument type is 'signed long'.\n" + "[test.cpp:3]: (warning) %u in format string (no. 3) requires 'unsigned int' but the argument type is 'signed long'.\n" + "[test.cpp:3]: (warning) %f in format string (no. 4) requires 'double' but the argument type is 'signed long'.\n", errout_str()); + + check("void f() {\n" // #5104 + " myvector v1(1,0);\n" + " printf(\"%d\",v1[0]);\n" + " myvector v2(1,0);\n" + " printf(\"%d\",v2[0]);\n" + " myvector v3(1,0);\n" + " printf(\"%u\",v3[0]);\n" + " myvector v4(1,0);\n" + " printf(\"%x\",v4[0]);\n" + " myvector v5(1,0);\n" + " printf(\"%f\",v5[0]);\n" + " myvector v6(1,0);\n" + " printf(\"%u\",v6[0]);\n" + " myvector v7(1,0);\n" + " printf(\"%s\",v7[0]);\n" + "}"); + 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" + "}"); + 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" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #5486 + check("void foo() {\n" + " ssize_t test = 0;\n" + " printf(\"%zd\", test);\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" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) %s in format string (no. 1) requires 'char *' but the argument type is 'std::string'.\n" + "[test.cpp:4]: (warning) %s in format string (no. 2) requires 'char *' but the argument type is 'signed int'.\n", errout_str()); + + check("template \n" + "struct Array {\n" + " T data[S];\n" + " T & operator [] (size_t i) { return data[i]; }\n" + "};\n" + "void foo() {\n" + " Array array1;\n" + " Array array2;\n" + " printf(\"%u %u\", array1[0], array2[0]);\n" + "}"); + ASSERT_EQUALS("[test.cpp:9]: (warning) %u in format string (no. 1) requires 'unsigned int' but the argument type is 'int'.\n" + "[test.cpp:9]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'float'.\n", errout_str()); + + // Ticket #7445 + check("struct S { unsigned short x; } s = {0};\n" + "void foo() {\n" + " printf(\"%d\", s.x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Ticket #7601 + check("void foo(int i, unsigned int ui, long long ll, unsigned long long ull) {\n" + " printf(\"%Ld %Lu %Ld %Lu\", i, ui, ll, ull);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %Ld in format string (no. 1) requires 'long long' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %Lu in format string (no. 2) requires 'unsigned long long' but the argument type is 'unsigned int'.\n", errout_str()); + + check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" + " printf(\"%hhd %hhd %hhd %hhd %hhd %hhd %hhd %hhd\", c, uc, s, us, i, ui, l, ul);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %hhd in format string (no. 2) requires 'char' but the argument type is 'unsigned char'.\n" + "[test.cpp:2]: (warning) %hhd in format string (no. 3) requires 'char' but the argument type is 'signed short'.\n" + "[test.cpp:2]: (warning) %hhd in format string (no. 4) requires 'char' but the argument type is 'unsigned short'.\n" + "[test.cpp:2]: (warning) %hhd in format string (no. 5) requires 'char' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %hhd in format string (no. 6) requires 'char' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %hhd in format string (no. 7) requires 'char' but the argument type is 'signed long'.\n" + "[test.cpp:2]: (warning) %hhd in format string (no. 8) requires 'char' but the argument type is 'unsigned long'.\n", errout_str()); + + check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" + " printf(\"%hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu\", c, uc, s, us, i, ui, l, ul);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %hhu in format string (no. 1) requires 'unsigned char' but the argument type is 'char'.\n" + "[test.cpp:2]: (warning) %hhu in format string (no. 3) requires 'unsigned char' but the argument type is 'signed short'.\n" + "[test.cpp:2]: (warning) %hhu in format string (no. 4) requires 'unsigned char' but the argument type is 'unsigned short'.\n" + "[test.cpp:2]: (warning) %hhu in format string (no. 5) requires 'unsigned char' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %hhu in format string (no. 6) requires 'unsigned char' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %hhu in format string (no. 7) requires 'unsigned char' but the argument type is 'signed long'.\n" + "[test.cpp:2]: (warning) %hhu in format string (no. 8) requires 'unsigned char' but the argument type is 'unsigned long'.\n", errout_str()); + + check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" + " printf(\"%hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx\", c, uc, s, us, i, ui, l, ul);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %hhx in format string (no. 1) requires 'unsigned char' but the argument type is 'char'.\n" + "[test.cpp:2]: (warning) %hhx in format string (no. 3) requires 'unsigned char' but the argument type is 'signed short'.\n" + "[test.cpp:2]: (warning) %hhx in format string (no. 4) requires 'unsigned char' but the argument type is 'unsigned short'.\n" + "[test.cpp:2]: (warning) %hhx in format string (no. 5) requires 'unsigned char' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %hhx in format string (no. 6) requires 'unsigned char' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %hhx in format string (no. 7) requires 'unsigned char' but the argument type is 'signed long'.\n" + "[test.cpp:2]: (warning) %hhx in format string (no. 8) requires 'unsigned char' but the argument type is 'unsigned long'.\n", errout_str()); + + check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" + " printf(\"%hd %hd %hd %hd %hd %hd %hd %hd\", c, uc, s, us, i, ui, l, ul);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %hd in format string (no. 1) requires 'short' but the argument type is 'char'.\n" + "[test.cpp:2]: (warning) %hd in format string (no. 2) requires 'short' but the argument type is 'unsigned char'.\n" + "[test.cpp:2]: (warning) %hd in format string (no. 4) requires 'short' but the argument type is 'unsigned short'.\n" + "[test.cpp:2]: (warning) %hd in format string (no. 5) requires 'short' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %hd in format string (no. 6) requires 'short' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %hd in format string (no. 7) requires 'short' but the argument type is 'signed long'.\n" + "[test.cpp:2]: (warning) %hd in format string (no. 8) requires 'short' but the argument type is 'unsigned long'.\n", errout_str()); + + check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" + " printf(\"%hu %hu %hu %hu %hu %hu %hu %hu\", c, uc, s, us, i, ui, l, ul);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %hu in format string (no. 1) requires 'unsigned short' but the argument type is 'char'.\n" + "[test.cpp:2]: (warning) %hu in format string (no. 2) requires 'unsigned short' but the argument type is 'unsigned char'.\n" + "[test.cpp:2]: (warning) %hu in format string (no. 3) requires 'unsigned short' but the argument type is 'signed short'.\n" + "[test.cpp:2]: (warning) %hu in format string (no. 5) requires 'unsigned short' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %hu in format string (no. 6) requires 'unsigned short' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %hu in format string (no. 7) requires 'unsigned short' but the argument type is 'signed long'.\n" + "[test.cpp:2]: (warning) %hu in format string (no. 8) requires 'unsigned short' but the argument type is 'unsigned long'.\n", errout_str()); + + check("void foo(char c, unsigned char uc, short s, unsigned short us, int i, unsigned int ui, long l, unsigned long ul) {\n" + " printf(\"%hx %hx %hx %hx %hx %hx %hx %hx\", c, uc, s, us, i, ui, l, ul);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %hx in format string (no. 1) requires 'unsigned short' but the argument type is 'char'.\n" + "[test.cpp:2]: (warning) %hx in format string (no. 2) requires 'unsigned short' but the argument type is 'unsigned char'.\n" + "[test.cpp:2]: (warning) %hx in format string (no. 3) requires 'unsigned short' but the argument type is 'signed short'.\n" + "[test.cpp:2]: (warning) %hx in format string (no. 5) requires 'unsigned short' but the argument type is 'signed int'.\n" + "[test.cpp:2]: (warning) %hx in format string (no. 6) requires 'unsigned short' but the argument type is 'unsigned int'.\n" + "[test.cpp:2]: (warning) %hx in format string (no. 7) requires 'unsigned short' but the argument type is 'signed long'.\n" + "[test.cpp:2]: (warning) %hx in format string (no. 8) requires 'unsigned short' but the argument type is 'unsigned long'.\n", errout_str()); + + // #7837 - Use ValueType for function call + check("struct S {\n" + " double (* f)(double);\n" + "};\n" + "\n" + "void foo(struct S x) {\n" + " printf(\"%f\", x.f(4.0));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " printf(\"%lu\", sizeof(char));\n" + "}\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win64)); + ASSERT_EQUALS("[test.cpp:2]: (portability) %lu in format string (no. 1) requires 'unsigned long' but the argument type is 'size_t {aka unsigned long long}'.\n", + errout_str()); + } + + void testPrintfArgumentVariables() { + TEST_PRINTF_NOWARN("%u", "unsigned int", "bool"); + TEST_PRINTF_WARN("%u", "unsigned int", "char"); + TEST_PRINTF_WARN("%u", "unsigned int", "signed char"); + TEST_PRINTF_NOWARN("%u", "unsigned int", "unsigned char"); + TEST_PRINTF_WARN("%u", "unsigned int", "signed short"); + TEST_PRINTF_NOWARN("%u", "unsigned int", "unsigned short"); + TEST_PRINTF_WARN("%u", "unsigned int", "signed int"); + TEST_PRINTF_NOWARN("%u", "unsigned int", "unsigned int"); + TEST_PRINTF_WARN("%u", "unsigned int", "signed long"); + TEST_PRINTF_WARN("%u", "unsigned int", "unsigned long"); + TEST_PRINTF_WARN("%u", "unsigned int", "signed long long"); + TEST_PRINTF_WARN("%u", "unsigned int", "unsigned long long"); + TEST_PRINTF_WARN("%u", "unsigned int", "float"); + TEST_PRINTF_WARN("%u", "unsigned int", "double"); + TEST_PRINTF_WARN("%u", "unsigned int", "long double"); + TEST_PRINTF_WARN("%u", "unsigned int", "void *"); + TEST_PRINTF_WARN_AKA("%u", "unsigned int", "size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%u", "unsigned int", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%u", "unsigned int", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%u", "unsigned int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%u", "unsigned int", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%u", "unsigned int", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%u", "unsigned int", "intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%u", "unsigned int", "uintptr_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%u", "unsigned int", "std::size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%u", "unsigned int", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%u", "unsigned int", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%u", "unsigned int", "std::intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%u", "unsigned int", "std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%u", "unsigned int", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%u", "unsigned int", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_NOWARN("%x", "unsigned int", "bool"); + //TODO TEST_PRINTF_WARN("%x", "unsigned int", "char"); + //TODO TEST_PRINTF_WARN("%x", "unsigned int", "signed char"); + TEST_PRINTF_NOWARN("%x", "unsigned int", "unsigned char"); + //TODO TEST_PRINTF_WARN("%x", "unsigned int", "signed short"); + TEST_PRINTF_NOWARN("%x", "unsigned int", "unsigned short"); + //TODO TEST_PRINTF_WARN("%x", "unsigned int", "signed int"); + TEST_PRINTF_NOWARN("%x", "unsigned int", "unsigned int"); + TEST_PRINTF_WARN("%x", "unsigned int", "signed long"); + TEST_PRINTF_WARN("%x", "unsigned int", "unsigned long"); + TEST_PRINTF_WARN("%x", "unsigned int", "signed long long"); + TEST_PRINTF_WARN("%x", "unsigned int", "unsigned long long"); + TEST_PRINTF_WARN("%x", "unsigned int", "float"); + TEST_PRINTF_WARN("%x", "unsigned int", "double"); + TEST_PRINTF_WARN("%x", "unsigned int", "long double"); + TEST_PRINTF_WARN("%x", "unsigned int", "void *"); + TEST_PRINTF_WARN_AKA("%x", "unsigned int", "size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%x", "unsigned int", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%x", "unsigned int", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%x", "unsigned int", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%x", "unsigned int", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%x", "unsigned int", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%x", "unsigned int", "intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%x", "unsigned int", "uintptr_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%x", "unsigned int", "std::size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%x", "unsigned int", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%x", "unsigned int", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%x", "unsigned int", "std::intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%x", "unsigned int", "std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%x", "unsigned int", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%x", "unsigned int", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_WARN("%lu","unsigned long","bool"); + TEST_PRINTF_WARN("%lu","unsigned long","char"); + TEST_PRINTF_WARN("%lu","unsigned long","signed char"); + TEST_PRINTF_WARN("%lu","unsigned long","unsigned char"); + TEST_PRINTF_WARN("%lu","unsigned long","signed short"); + TEST_PRINTF_WARN("%lu","unsigned long","unsigned short"); + TEST_PRINTF_WARN("%lu","unsigned long","signed int"); + TEST_PRINTF_WARN("%lu","unsigned long","unsigned int"); + TEST_PRINTF_WARN("%lu","unsigned long","signed long"); + TEST_PRINTF_NOWARN("%lu","unsigned long","unsigned long"); + TEST_PRINTF_WARN("%lu","unsigned long","signed long long"); + TEST_PRINTF_WARN("%lu","unsigned long","unsigned long long"); + TEST_PRINTF_WARN("%lu","unsigned long","float"); + TEST_PRINTF_WARN("%lu","unsigned long","double"); + TEST_PRINTF_WARN("%lu","unsigned long","long double"); + TEST_PRINTF_WARN("%lu","unsigned long","void *"); + TEST_PRINTF_WARN_AKA("%lu","unsigned long","size_t","unsigned long","unsigned long long"); + TEST_PRINTF_WARN_AKA("%lu","unsigned long","ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%lu","unsigned long","ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_WIN64("%lu","unsigned long","unsigned ptrdiff_t", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%lu","unsigned long","intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%lu","unsigned long","uintmax_t","unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%lu","unsigned long","intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_WIN64("%lu","unsigned long","uintptr_t", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%lu","unsigned long","std::size_t","unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%lu","unsigned long","std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%lu","unsigned long","std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%lu","unsigned long","std::intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP_WIN64("%lu","unsigned long","std::uintmax_t", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%lu","unsigned long","std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP_WIN64("%lu","unsigned long","std::uintptr_t", "unsigned long long"); + + TEST_PRINTF_WARN("%lx","unsigned long","bool"); + TEST_PRINTF_WARN("%lx","unsigned long","char"); + TEST_PRINTF_WARN("%lx","unsigned long","signed char"); + TEST_PRINTF_WARN("%lx","unsigned long","unsigned char"); + TEST_PRINTF_WARN("%lx","unsigned long","signed short"); + TEST_PRINTF_WARN("%lx","unsigned long","unsigned short"); + TEST_PRINTF_WARN("%lx","unsigned long","signed int"); + TEST_PRINTF_WARN("%lx","unsigned long","unsigned int"); + //TODO TEST_PRINTF_WARN("%lx","unsigned long","signed long"); + TEST_PRINTF_NOWARN("%lx","unsigned long","unsigned long"); + TEST_PRINTF_WARN("%lx","unsigned long","signed long long"); + TEST_PRINTF_WARN("%lx","unsigned long","unsigned long long"); + TEST_PRINTF_WARN("%lx","unsigned long","float"); + TEST_PRINTF_WARN("%lx","unsigned long","double"); + TEST_PRINTF_WARN("%lx","unsigned long","long double"); + TEST_PRINTF_WARN("%lx","unsigned long","void *"); + TEST_PRINTF_WARN_AKA("%lx","unsigned long","size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","ssize_t", "signed long long"); + TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","ptrdiff_t", "signed long long"); + TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","unsigned ptrdiff_t", "unsigned long long"); + TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","intmax_t", "signed long long"); + TEST_PRINTF_WARN_AKA("%lx","unsigned long","uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","intptr_t", "signed long long"); + TEST_PRINTF_WARN_AKA_WIN64("%lx","unsigned long","uintptr_t", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%lx","unsigned long","std::size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP_WIN64("%lx","unsigned long","std::ssize_t", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP_WIN64("%lx","unsigned long","std::ptrdiff_t", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP_WIN64("%lx","unsigned long","std::intmax_t", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP_WIN64("%lx","unsigned long","std::uintmax_t", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP_WIN64("%lx","unsigned long","std::intptr_t", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP_WIN64("%lx","unsigned long","std::uintptr_t", "unsigned long long"); + + TEST_PRINTF_WARN("%llu","unsigned long long","bool"); + TEST_PRINTF_WARN("%llu","unsigned long long","char"); + TEST_PRINTF_WARN("%llu","unsigned long long","signed char"); + TEST_PRINTF_WARN("%llu","unsigned long long","unsigned char"); + TEST_PRINTF_WARN("%llu","unsigned long long","signed short"); + TEST_PRINTF_WARN("%llu","unsigned long long","unsigned short"); + TEST_PRINTF_WARN("%llu","unsigned long long","signed int"); + TEST_PRINTF_WARN("%llu","unsigned long long","unsigned int"); + TEST_PRINTF_WARN("%llu","unsigned long long","signed long"); + TEST_PRINTF_WARN("%llu","unsigned long long","unsigned long"); + TEST_PRINTF_WARN("%llu","unsigned long long","signed long long"); + TEST_PRINTF_NOWARN("%llu","unsigned long long","unsigned long long"); + TEST_PRINTF_WARN("%llu","unsigned long long","float"); + TEST_PRINTF_WARN("%llu","unsigned long long","double"); + TEST_PRINTF_WARN("%llu","unsigned long long","long double"); + TEST_PRINTF_WARN("%llu","unsigned long long","void *"); + TEST_PRINTF_WARN_AKA("%llu","unsigned long long","size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%llu","unsigned long long","ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%llu","unsigned long long","ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_WIN32("%llu","unsigned long long","unsigned ptrdiff_t", "unsigned long"); + TEST_PRINTF_WARN_AKA("%llu","unsigned long long","intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%llu","unsigned long long","uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%llu","unsigned long long","intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_WIN32("%llu","unsigned long long","uintptr_t", "unsigned long"); + TEST_PRINTF_WARN_AKA_CPP("%llu","unsigned long long","std::size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%llu","unsigned long long","std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%llu","unsigned long long","std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%llu","unsigned long long","std::intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%llu","unsigned long long","std::uintmax_t", "unsigned long"); + TEST_PRINTF_WARN_AKA_CPP("%llu","unsigned long long","std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%llu","unsigned long long","std::uintptr_t", "unsigned long"); + + TEST_PRINTF_WARN("%llx","unsigned long long","bool"); + TEST_PRINTF_WARN("%llx","unsigned long long","char"); + TEST_PRINTF_WARN("%llx","unsigned long long","signed char"); + TEST_PRINTF_WARN("%llx","unsigned long long","unsigned char"); + TEST_PRINTF_WARN("%llx","unsigned long long","signed short"); + TEST_PRINTF_WARN("%llx","unsigned long long","unsigned short"); + TEST_PRINTF_WARN("%llx","unsigned long long","signed int"); + TEST_PRINTF_WARN("%llx","unsigned long long","unsigned int"); + TEST_PRINTF_WARN("%llx","unsigned long long","signed long"); + TEST_PRINTF_WARN("%llx","unsigned long long","unsigned long"); + //TODO TEST_PRINTF_WARN("%llx","unsigned long long","signed long long"); + TEST_PRINTF_NOWARN("%llx","unsigned long long","unsigned long long"); + TEST_PRINTF_WARN("%llx","unsigned long long","float"); + TEST_PRINTF_WARN("%llx","unsigned long long","double"); + TEST_PRINTF_WARN("%llx","unsigned long long","long double"); + TEST_PRINTF_WARN("%llx","unsigned long long","void *"); + TEST_PRINTF_WARN_AKA("%llx","unsigned long long","size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","ssize_t", "signed long"); + TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","ptrdiff_t", "signed long"); + TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","unsigned ptrdiff_t", "unsigned long"); + TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","intmax_t", "signed long"); + TEST_PRINTF_WARN_AKA("%llx","unsigned long long","uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","intptr_t", "signed long"); + TEST_PRINTF_WARN_AKA_WIN32("%llx","unsigned long long","uintptr_t", "unsigned long"); + TEST_PRINTF_WARN_AKA_CPP("%llx","unsigned long long","std::size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%llx","unsigned long long","std::ssize_t", "signed long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%llx","unsigned long long","std::ptrdiff_t", "signed long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%llx","unsigned long long","std::intmax_t", "signed long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%llx","unsigned long long","std::uintmax_t", "unsigned long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%llx","unsigned long long","std::intptr_t", "signed long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%llx","unsigned long long","std::uintptr_t", "unsigned long"); + + TEST_PRINTF_WARN("%hu", "unsigned short", "bool"); + TEST_PRINTF_WARN("%hu", "unsigned short", "char"); + TEST_PRINTF_WARN("%hu", "unsigned short", "signed char"); + TEST_PRINTF_WARN("%hu", "unsigned short", "unsigned char"); + TEST_PRINTF_WARN("%hu", "unsigned short", "signed short"); + TEST_PRINTF_NOWARN("%hu", "unsigned short", "unsigned short"); + TEST_PRINTF_WARN("%hu", "unsigned short", "signed int"); + TEST_PRINTF_WARN("%hu", "unsigned short", "unsigned int"); + TEST_PRINTF_WARN("%hu", "unsigned short", "signed long"); + TEST_PRINTF_WARN("%hu", "unsigned short", "unsigned long"); + TEST_PRINTF_WARN("%hu", "unsigned short", "signed long long"); + TEST_PRINTF_WARN("%hu", "unsigned short", "unsigned long long"); + TEST_PRINTF_WARN("%hu", "unsigned short", "float"); + TEST_PRINTF_WARN("%hu", "unsigned short", "double"); + TEST_PRINTF_WARN("%hu", "unsigned short", "long double"); + TEST_PRINTF_WARN("%hu", "unsigned short", "void *"); + TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%hu", "unsigned short", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%hu", "unsigned short", "std::size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%hu", "unsigned short", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%hu", "unsigned short", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%hu", "unsigned short", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%hu", "unsigned short", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_WARN("%hx", "unsigned short", "bool"); + TEST_PRINTF_WARN("%hx", "unsigned short", "char"); + TEST_PRINTF_WARN("%hx", "unsigned short", "signed char"); + TEST_PRINTF_WARN("%hx", "unsigned short", "unsigned char"); + TEST_PRINTF_WARN("%hx", "unsigned short", "signed short"); + TEST_PRINTF_NOWARN("%hx", "unsigned short", "unsigned short"); + TEST_PRINTF_WARN("%hx", "unsigned short", "signed int"); + TEST_PRINTF_WARN("%hx", "unsigned short", "unsigned int"); + TEST_PRINTF_WARN("%hx", "unsigned short", "signed long"); + TEST_PRINTF_WARN("%hx", "unsigned short", "unsigned long"); + TEST_PRINTF_WARN("%hx", "unsigned short", "signed long long"); + TEST_PRINTF_WARN("%hx", "unsigned short", "unsigned long long"); + TEST_PRINTF_WARN("%hx", "unsigned short", "float"); + TEST_PRINTF_WARN("%hx", "unsigned short", "double"); + TEST_PRINTF_WARN("%hx", "unsigned short", "long double"); + TEST_PRINTF_WARN("%hx", "unsigned short", "void *"); + TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%hx", "unsigned short", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%hx", "unsigned short", "std::size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%hx", "unsigned short", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%hx", "unsigned short", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%hx", "unsigned short", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%hx", "unsigned short", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_WARN("%hhu", "unsigned char", "bool"); + TEST_PRINTF_WARN("%hhu", "unsigned char", "char"); + TEST_PRINTF_WARN("%hhu", "unsigned char", "signed char"); + TEST_PRINTF_NOWARN("%hhu", "unsigned char", "unsigned char"); + TEST_PRINTF_WARN("%hhu", "unsigned char", "signed short"); + TEST_PRINTF_WARN("%hhu", "unsigned char", "unsigned short"); + TEST_PRINTF_WARN("%hhu", "unsigned char", "signed int"); + TEST_PRINTF_WARN("%hhu", "unsigned char", "unsigned int"); + TEST_PRINTF_WARN("%hhu", "unsigned char", "signed long"); + TEST_PRINTF_WARN("%hhu", "unsigned char", "unsigned long"); + TEST_PRINTF_WARN("%hhu", "unsigned char", "signed long long"); + TEST_PRINTF_WARN("%hhu", "unsigned char", "unsigned long long"); + TEST_PRINTF_WARN("%hhu", "unsigned char", "float"); + TEST_PRINTF_WARN("%hhu", "unsigned char", "double"); + TEST_PRINTF_WARN("%hhu", "unsigned char", "long double"); + TEST_PRINTF_WARN("%hhu", "unsigned char", "void *"); + TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%hhu", "unsigned char", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%hhu", "unsigned char", "std::size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%hhu", "unsigned char", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%hhu", "unsigned char", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%hhu", "unsigned char", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%hhu", "unsigned char", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_WARN("%hhx", "unsigned char", "bool"); + TEST_PRINTF_WARN("%hhx", "unsigned char", "char"); + TEST_PRINTF_WARN("%hhx", "unsigned char", "signed char"); + TEST_PRINTF_NOWARN("%hhx", "unsigned char", "unsigned char"); + TEST_PRINTF_WARN("%hhx", "unsigned char", "signed short"); + TEST_PRINTF_WARN("%hhx", "unsigned char", "unsigned short"); + TEST_PRINTF_WARN("%hhx", "unsigned char", "signed int"); + TEST_PRINTF_WARN("%hhx", "unsigned char", "unsigned int"); + TEST_PRINTF_WARN("%hhx", "unsigned char", "signed long"); + TEST_PRINTF_WARN("%hhx", "unsigned char", "unsigned long"); + TEST_PRINTF_WARN("%hhx", "unsigned char", "signed long long"); + TEST_PRINTF_WARN("%hhx", "unsigned char", "unsigned long long"); + TEST_PRINTF_WARN("%hhx", "unsigned char", "float"); + TEST_PRINTF_WARN("%hhx", "unsigned char", "double"); + TEST_PRINTF_WARN("%hhx", "unsigned char", "long double"); + TEST_PRINTF_WARN("%hhx", "unsigned char", "void *"); + TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%hhx", "unsigned char", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%hhx", "unsigned char", "std::size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%hhx", "unsigned char", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%hhx", "unsigned char", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%hhx", "unsigned char", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%hhx", "unsigned char", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_WARN("%Lu", "unsigned long long", "bool"); + TEST_PRINTF_WARN("%Lu", "unsigned long long", "char"); + TEST_PRINTF_WARN("%Lu", "unsigned long long", "signed char"); + TEST_PRINTF_WARN("%Lu", "unsigned long long", "unsigned char"); + TEST_PRINTF_WARN("%Lu", "unsigned long long", "signed short"); + TEST_PRINTF_WARN("%Lu", "unsigned long long", "unsigned short"); + TEST_PRINTF_WARN("%Lu", "unsigned long long", "signed int"); + TEST_PRINTF_WARN("%Lu", "unsigned long long", "unsigned int"); + TEST_PRINTF_WARN("%Lu", "unsigned long long", "signed long"); + TEST_PRINTF_WARN("%Lu", "unsigned long long", "unsigned long"); + TEST_PRINTF_WARN("%Lu", "unsigned long long", "signed long long"); + TEST_PRINTF_NOWARN("%Lu", "unsigned long long", "unsigned long long"); + TEST_PRINTF_WARN("%Lu", "unsigned long long", "float"); + TEST_PRINTF_WARN("%Lu", "unsigned long long", "double"); + TEST_PRINTF_WARN("%Lu", "unsigned long long", "long double"); + TEST_PRINTF_WARN("%Lu", "unsigned long long", "void *"); + TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "size_t", "unsigned long"); + TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "unsigned ptrdiff_t", "unsigned long"); + TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "uintmax_t", "unsigned long"); + TEST_PRINTF_WARN_AKA("%Lu", "unsigned long long", "intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_WIN32("%Lu", "unsigned long long", "uintptr_t", "unsigned long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%Lu", "unsigned long long", "std::size_t", "unsigned long"); + TEST_PRINTF_WARN_AKA_CPP("%Lu", "unsigned long long", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%Lu", "unsigned long long", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%Lu", "unsigned long long", "std::intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%Lu", "unsigned long long", "std::uintmax_t", "unsigned long"); + TEST_PRINTF_WARN_AKA_CPP("%Lu", "unsigned long long", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%Lu", "unsigned long long", "std::uintptr_t", "unsigned long"); + + //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "bool"); + //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "char"); + //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "signed char"); + //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "unsigned char"); + //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "signed short"); + //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "unsigned short"); + //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "signed int"); + //TODO TEST_PRINTF_WARN("%Lx", "unsigned long long", "unsigned int"); + TEST_PRINTF_WARN("%Lx", "unsigned long long", "signed long"); + TEST_PRINTF_WARN("%Lx", "unsigned long long", "unsigned long"); + TEST_PRINTF_WARN("%Lx", "unsigned long long", "signed long long"); + //TODO TEST_PRINTF_NOWARN("%Lx", "unsigned long long", "unsigned long long"); + TEST_PRINTF_WARN("%Lx", "unsigned long long", "float"); + TEST_PRINTF_WARN("%Lx", "unsigned long long", "double"); + TEST_PRINTF_WARN("%Lx", "unsigned long long", "long double"); + TEST_PRINTF_WARN("%Lx", "unsigned long long", "void *"); + TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%Lx", "unsigned long long", "uintptr_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%Lx", "unsigned long long", "std::size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%Lx", "unsigned long long", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%Lx", "unsigned long long", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%Lx", "unsigned long long", "std::intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%Lx", "unsigned long long", "std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%Lx", "unsigned long long", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%Lx", "unsigned long long", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_WARN("%ju", "uintmax_t", "bool"); + TEST_PRINTF_WARN("%ju", "uintmax_t", "char"); + TEST_PRINTF_WARN("%ju", "uintmax_t", "signed char"); + TEST_PRINTF_WARN("%ju", "uintmax_t", "unsigned char"); + TEST_PRINTF_WARN("%ju", "uintmax_t", "signed short"); + TEST_PRINTF_WARN("%ju", "uintmax_t", "unsigned short"); + TEST_PRINTF_WARN("%ju", "uintmax_t", "signed int"); + TEST_PRINTF_WARN("%ju", "uintmax_t", "unsigned int"); + TEST_PRINTF_WARN("%ju", "uintmax_t", "signed long"); + TEST_PRINTF_WARN("%ju", "uintmax_t", "unsigned long"); + TEST_PRINTF_WARN("%ju", "uintmax_t", "signed long long"); + TEST_PRINTF_WARN("%ju", "uintmax_t", "unsigned long long"); + TEST_PRINTF_WARN("%ju", "uintmax_t", "float"); + TEST_PRINTF_WARN("%ju", "uintmax_t", "double"); + TEST_PRINTF_WARN("%ju", "uintmax_t", "long double"); + TEST_PRINTF_WARN("%ju", "uintmax_t", "void *"); + TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%ju", "uintmax_t", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_NOWARN("%ju", "uintmax_t", "uintmax_t"); + TEST_PRINTF_WARN_AKA_CPP("%ju", "uintmax_t", "std::size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%ju", "uintmax_t", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%ju", "uintmax_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%ju", "uintmax_t", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%ju", "uintmax_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_WARN("%jx", "uintmax_t", "bool"); + TEST_PRINTF_WARN("%jx", "uintmax_t", "char"); + TEST_PRINTF_WARN("%jx", "uintmax_t", "signed char"); + TEST_PRINTF_WARN("%jx", "uintmax_t", "unsigned char"); + TEST_PRINTF_WARN("%jx", "uintmax_t", "signed short"); + TEST_PRINTF_WARN("%jx", "uintmax_t", "unsigned short"); + TEST_PRINTF_WARN("%jx", "uintmax_t", "signed int"); + TEST_PRINTF_WARN("%jx", "uintmax_t", "unsigned int"); + TEST_PRINTF_WARN("%jx", "uintmax_t", "signed long"); + TEST_PRINTF_WARN("%jx", "uintmax_t", "unsigned long"); + TEST_PRINTF_WARN("%jx", "uintmax_t", "signed long long"); + TEST_PRINTF_WARN("%jx", "uintmax_t", "unsigned long long"); + TEST_PRINTF_WARN("%jx", "uintmax_t", "float"); + TEST_PRINTF_WARN("%jx", "uintmax_t", "double"); + TEST_PRINTF_WARN("%jx", "uintmax_t", "long double"); + TEST_PRINTF_WARN("%jx", "uintmax_t", "void *"); + TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%jx", "uintmax_t", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_NOWARN("%jx", "uintmax_t", "uintmax_t"); + TEST_PRINTF_WARN_AKA_CPP("%jx", "uintmax_t", "std::size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%jx", "uintmax_t", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%jx", "uintmax_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%jx", "uintmax_t", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%jx", "uintmax_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_WARN_AKA("%zd", "ssize_t", "size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%zi", "ssize_t", "size_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_WARN("%zu", "size_t", "bool"); + TEST_PRINTF_WARN("%zu", "size_t", "char"); + TEST_PRINTF_WARN("%zu", "size_t", "signed char"); + TEST_PRINTF_WARN("%zu", "size_t", "unsigned char"); + TEST_PRINTF_WARN("%zu", "size_t", "signed short"); + TEST_PRINTF_WARN("%zu", "size_t", "unsigned short"); + TEST_PRINTF_WARN("%zu", "size_t", "signed int"); + TEST_PRINTF_WARN("%zu", "size_t", "unsigned int"); + TEST_PRINTF_WARN("%zu", "size_t", "signed long"); + TEST_PRINTF_WARN("%zu", "size_t", "unsigned long"); + TEST_PRINTF_WARN("%zu", "size_t", "signed long long"); + TEST_PRINTF_WARN("%zu", "size_t", "unsigned long long"); + TEST_PRINTF_WARN("%zu", "size_t", "float"); + TEST_PRINTF_WARN("%zu", "size_t", "double"); + TEST_PRINTF_WARN("%zu", "size_t", "long double"); + TEST_PRINTF_WARN("%zu", "size_t", "void *"); + TEST_PRINTF_NOWARN("%zu", "size_t", "size_t"); + TEST_PRINTF_WARN_AKA("%zu", "size_t", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%zu", "size_t", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%zu", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%zu", "size_t", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%zu", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_NOWARN_CPP("%zu", "size_t", "std::size_t"); + TEST_PRINTF_WARN_AKA_CPP("%zu", "size_t", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%zu", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%zu", "size_t", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%zu", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_WARN("%zx", "size_t", "bool"); + TEST_PRINTF_WARN("%zx", "size_t", "char"); + TEST_PRINTF_WARN("%zx", "size_t", "signed char"); + TEST_PRINTF_WARN("%zx", "size_t", "unsigned char"); + TEST_PRINTF_WARN("%zx", "size_t", "signed short"); + TEST_PRINTF_WARN("%zx", "size_t", "unsigned short"); + TEST_PRINTF_WARN("%zx", "size_t", "signed int"); + TEST_PRINTF_WARN("%zx", "size_t", "unsigned int"); + TEST_PRINTF_WARN("%zx", "size_t", "signed long"); + TEST_PRINTF_WARN("%zx", "size_t", "unsigned long"); + TEST_PRINTF_WARN("%zx", "size_t", "signed long long"); + TEST_PRINTF_WARN("%zx", "size_t", "unsigned long long"); + TEST_PRINTF_WARN("%zx", "size_t", "float"); + TEST_PRINTF_WARN("%zx", "size_t", "double"); + TEST_PRINTF_WARN("%zx", "size_t", "long double"); + TEST_PRINTF_WARN("%zx", "size_t", "void *"); + TEST_PRINTF_NOWARN("%zx", "size_t", "size_t"); + TEST_PRINTF_WARN_AKA("%zx", "size_t", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%zx", "size_t", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%zx", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%zx", "size_t", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%zx", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_NOWARN_CPP("%zx", "size_t", "std::size_t"); + TEST_PRINTF_WARN_AKA_CPP("%zx", "size_t", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%zx", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%zx", "size_t", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%zx", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "bool"); + TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "char"); + TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "signed char"); + TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "unsigned char"); + TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "signed short"); + TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "unsigned short"); + TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "signed int"); + TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "unsigned int"); + TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "signed long"); + TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "unsigned long"); + TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "signed long long"); + TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "unsigned long long"); + TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "float"); + TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "double"); + TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "long double"); + TEST_PRINTF_WARN("%tu", "unsigned ptrdiff_t", "void *"); + TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_NOWARN("%tu", "unsigned ptrdiff_t", "unsigned ptrdiff_t"); + TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%tu", "unsigned ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%tu", "unsigned ptrdiff_t", "std::size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%tu", "unsigned ptrdiff_t", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%tu", "unsigned ptrdiff_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%tu", "unsigned ptrdiff_t", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%tu", "unsigned ptrdiff_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "bool"); + TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "char"); + TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "signed char"); + TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "unsigned char"); + TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "signed short"); + TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "unsigned short"); + TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "signed int"); + TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "unsigned int"); + TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "signed long"); + TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "unsigned long"); + TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "signed long long"); + TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "unsigned long long"); + TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "float"); + TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "double"); + TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "long double"); + TEST_PRINTF_WARN("%tx", "unsigned ptrdiff_t", "void *"); + TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "ssize_t", "signed long", "signed long long"); + //TODO TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_NOWARN("%tx", "unsigned ptrdiff_t", "unsigned ptrdiff_t"); + TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%tx", "unsigned ptrdiff_t", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%tx", "unsigned ptrdiff_t", "std::size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%tx", "unsigned ptrdiff_t", "std::ssize_t", "signed long", "signed long long"); + //TODO TEST_PRINTF_WARN_AKA_CPP("%tx", "unsigned ptrdiff_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%tx", "unsigned ptrdiff_t", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%tx", "unsigned ptrdiff_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_WARN("%Iu", "size_t", "bool"); + TEST_PRINTF_WARN("%Iu", "size_t", "char"); + TEST_PRINTF_WARN("%Iu", "size_t", "signed char"); + TEST_PRINTF_WARN("%Iu", "size_t", "unsigned char"); + TEST_PRINTF_WARN("%Iu", "size_t", "signed short"); + TEST_PRINTF_WARN("%Iu", "size_t", "unsigned short"); + TEST_PRINTF_WARN("%Iu", "size_t", "signed int"); + TEST_PRINTF_WARN("%Iu", "size_t", "unsigned int"); + TEST_PRINTF_WARN("%Iu", "size_t", "signed long"); + TEST_PRINTF_WARN("%Iu", "size_t", "unsigned long"); + TEST_PRINTF_WARN("%Iu", "size_t", "signed long long"); + TEST_PRINTF_WARN("%Iu", "size_t", "unsigned long long"); + TEST_PRINTF_WARN("%Iu", "size_t", "float"); + TEST_PRINTF_WARN("%Iu", "size_t", "double"); + TEST_PRINTF_WARN("%Iu", "size_t", "long double"); + TEST_PRINTF_WARN("%Iu", "size_t", "void *"); + TEST_PRINTF_NOWARN("%Iu", "size_t", "size_t"); + TEST_PRINTF_WARN_AKA("%Iu", "size_t", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%Iu", "size_t", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%Iu", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%Iu", "size_t", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%Iu", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%Iu", "size_t", "intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%Iu", "size_t", "uintptr_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_NOWARN_CPP("%Iu", "size_t", "std::size_t"); + TEST_PRINTF_WARN_AKA_CPP("%Iu", "size_t", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%Iu", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%Iu", "size_t", "std::intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%Iu", "size_t", "std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%Iu", "size_t", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%Iu", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_WARN("%Ix", "size_t", "bool"); + TEST_PRINTF_WARN("%Ix", "size_t", "char"); + TEST_PRINTF_WARN("%Ix", "size_t", "signed char"); + TEST_PRINTF_WARN("%Ix", "size_t", "unsigned char"); + TEST_PRINTF_WARN("%Ix", "size_t", "signed short"); + TEST_PRINTF_WARN("%Ix", "size_t", "unsigned short"); + TEST_PRINTF_WARN("%Ix", "size_t", "signed int"); + TEST_PRINTF_WARN("%Ix", "size_t", "unsigned int"); + TEST_PRINTF_WARN("%Ix", "size_t", "signed long"); + TEST_PRINTF_WARN("%Ix", "size_t", "unsigned long"); + TEST_PRINTF_WARN("%Ix", "size_t", "signed long long"); + TEST_PRINTF_WARN("%Ix", "size_t", "unsigned long long"); + TEST_PRINTF_WARN("%Ix", "size_t", "float"); + TEST_PRINTF_WARN("%Ix", "size_t", "double"); + TEST_PRINTF_WARN("%Ix", "size_t", "long double"); + TEST_PRINTF_WARN("%Ix", "size_t", "void *"); + TEST_PRINTF_NOWARN("%Ix", "size_t", "size_t"); + TEST_PRINTF_WARN_AKA("%Ix", "size_t", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%Ix", "size_t", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%Ix", "size_t", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%Ix", "size_t", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%Ix", "size_t", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%Ix", "size_t", "intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%Ix", "size_t", "uintptr_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_NOWARN_CPP("%Ix", "size_t", "std::size_t"); + TEST_PRINTF_WARN_AKA_CPP("%Ix", "size_t", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%Ix", "size_t", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%Ix", "size_t", "std::intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%Ix", "size_t", "std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%Ix", "size_t", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%Ix", "size_t", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_WARN("%I64u", "unsigned __int64", "bool"); + TEST_PRINTF_WARN("%I64u", "unsigned __int64", "char"); + TEST_PRINTF_WARN("%I64u", "unsigned __int64", "signed char"); + TEST_PRINTF_WARN("%I64u", "unsigned __int64", "unsigned char"); + TEST_PRINTF_WARN("%I64u", "unsigned __int64", "signed short"); + TEST_PRINTF_WARN("%I64u", "unsigned __int64", "unsigned short"); + TEST_PRINTF_WARN("%I64u", "unsigned __int64", "signed int"); + TEST_PRINTF_WARN("%I64u", "unsigned __int64", "unsigned int"); + TEST_PRINTF_WARN("%I64u", "unsigned __int64", "signed long"); + TEST_PRINTF_WARN("%I64u", "unsigned __int64", "unsigned long"); + TEST_PRINTF_WARN("%I64u", "unsigned __int64", "signed long long"); + TEST_PRINTF_NOWARN("%I64u", "unsigned __int64", "unsigned long long"); + TEST_PRINTF_WARN("%I64u", "unsigned __int64", "float"); + TEST_PRINTF_WARN("%I64u", "unsigned __int64", "double"); + TEST_PRINTF_WARN("%I64u", "unsigned __int64", "long double"); + TEST_PRINTF_WARN("%I64u", "unsigned __int64", "void *"); + TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "size_t", "unsigned long"); + TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "unsigned ptrdiff_t", "unsigned long"); + TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "uintmax_t", "unsigned long"); + TEST_PRINTF_WARN_AKA("%I64u", "unsigned __int64", "intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_WIN32("%I64u", "unsigned __int64", "uintptr_t", "unsigned long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%I64u", "unsigned __int64", "std::size_t", "unsigned long"); + TEST_PRINTF_WARN_AKA_CPP("%I64u", "unsigned __int64", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%I64u", "unsigned __int64", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%I64u", "unsigned __int64", "std::intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%I64u", "unsigned __int64", "std::uintmax_t", "unsigned long"); + TEST_PRINTF_WARN_AKA_CPP("%I64u", "unsigned __int64", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%I64u", "unsigned __int64", "std::uintptr_t", "unsigned long"); + + TEST_PRINTF_WARN("%I64x", "unsigned __int64", "bool"); + TEST_PRINTF_WARN("%I64x", "unsigned __int64", "char"); + TEST_PRINTF_WARN("%I64x", "unsigned __int64", "signed char"); + TEST_PRINTF_WARN("%I64x", "unsigned __int64", "unsigned char"); + TEST_PRINTF_WARN("%I64x", "unsigned __int64", "signed short"); + TEST_PRINTF_WARN("%I64x", "unsigned __int64", "unsigned short"); + TEST_PRINTF_WARN("%I64x", "unsigned __int64", "signed int"); + TEST_PRINTF_WARN("%I64x", "unsigned __int64", "unsigned int"); + TEST_PRINTF_WARN("%I64x", "unsigned __int64", "signed long"); + TEST_PRINTF_WARN("%I64x", "unsigned __int64", "unsigned long"); + //TODO TEST_PRINTF_WARN("%I64x", "unsigned __int64", "signed long long"); + TEST_PRINTF_NOWARN("%I64x", "unsigned __int64", "unsigned long long"); + TEST_PRINTF_WARN("%I64x", "unsigned __int64", "float"); + TEST_PRINTF_WARN("%I64x", "unsigned __int64", "double"); + TEST_PRINTF_WARN("%I64x", "unsigned __int64", "long double"); + TEST_PRINTF_WARN("%I64x", "unsigned __int64", "void *"); + //TODO TEST_PRINTF_WARN("%I64x", "unsigned __int64", "size_t"); + TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "ssize_t", "signed long"); + TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "ptrdiff_t", "signed long"); + //TODO TEST_PRINTF_NOWARN("%I64x", "unsigned __int64", "unsigned __int64"); + // TODO TEST_PRINTF_WARN("%I64x", "unsigned __int64", "__int64"); + TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "unsigned ptrdiff_t", "unsigned long"); + TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "intmax_t", "signed long"); + TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "uintmax_t", "unsigned long"); + TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "intptr_t", "signed long"); + TEST_PRINTF_WARN_AKA_WIN32("%I64x", "unsigned __int64", "uintptr_t", "unsigned long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%I64x", "unsigned __int64", "std::size_t", "unsigned long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%I64x", "unsigned __int64", "std::ssize_t", "signed long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%I64x", "unsigned __int64", "std::ptrdiff_t", "signed long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%I64x", "unsigned __int64", "std::intmax_t", "signed long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%I64x", "unsigned __int64", "std::uintmax_t", "unsigned long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%I64x", "unsigned __int64", "std::intptr_t", "signed long"); + TEST_PRINTF_WARN_AKA_CPP_WIN32("%I64x", "unsigned __int64", "std::uintptr_t", "unsigned long"); + + TEST_PRINTF_WARN("%I64d", "__int64", "bool"); + TEST_PRINTF_WARN("%I64d", "__int64", "signed char"); + TEST_PRINTF_WARN("%I64d", "__int64", "unsigned char"); + TEST_PRINTF_WARN("%I64d", "__int64", "void *"); + // TODO TEST_PRINTF_WARN("%I64d", "__int64", "size_t"); + TEST_PRINTF_WARN_AKA_WIN32("%I64d", "__int64", "intmax_t", "signed long"); + TEST_PRINTF_WARN_AKA_WIN32("%I64d", "__int64", "ssize_t", "signed long"); + TEST_PRINTF_WARN_AKA_WIN32("%I64d", "__int64", "ptrdiff_t", "signed long"); + TEST_PRINTF_NOWARN("%I64d", "__int64", "__int64"); + + TEST_PRINTF_WARN("%I32u", "unsigned __int32", "bool"); + TEST_PRINTF_WARN("%I32u", "unsigned __int32", "char"); + TEST_PRINTF_WARN("%I32u", "unsigned __int32", "signed char"); + TEST_PRINTF_WARN("%I32u", "unsigned __int32", "unsigned char"); + TEST_PRINTF_WARN("%I32u", "unsigned __int32", "signed short"); + TEST_PRINTF_WARN("%I32u", "unsigned __int32", "unsigned short"); + TEST_PRINTF_WARN("%I32u", "unsigned __int32", "signed int"); + TEST_PRINTF_NOWARN("%I32u", "unsigned __int32", "unsigned int"); + TEST_PRINTF_WARN("%I32u", "unsigned __int32", "signed long"); + TEST_PRINTF_WARN("%I32u", "unsigned __int32", "unsigned long"); + TEST_PRINTF_WARN("%I32u", "unsigned __int32", "signed long long"); + TEST_PRINTF_WARN("%I32u", "unsigned __int32", "unsigned long long"); + TEST_PRINTF_WARN("%I32u", "unsigned __int32", "float"); + TEST_PRINTF_WARN("%I32u", "unsigned __int32", "double"); + TEST_PRINTF_WARN("%I32u", "unsigned __int32", "long double"); + TEST_PRINTF_WARN("%I32u", "unsigned __int32", "void *"); + TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%I32u", "unsigned __int32", "uintptr_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%I32u", "unsigned __int32", "std::size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%I32u", "unsigned __int32", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%I32u", "unsigned __int32", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%I32u", "unsigned __int32", "std::intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%I32u", "unsigned __int32", "std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%I32u", "unsigned __int32", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%I32u", "unsigned __int32", "std::uintptr_t", "unsigned long", "unsigned long long"); + + TEST_PRINTF_WARN("%I32x", "unsigned __int32", "bool"); + TEST_PRINTF_WARN("%I32x", "unsigned __int32", "char"); + TEST_PRINTF_WARN("%I32x", "unsigned __int32", "signed char"); + TEST_PRINTF_WARN("%I32x", "unsigned __int32", "unsigned char"); + TEST_PRINTF_WARN("%I32x", "unsigned __int32", "signed short"); + TEST_PRINTF_WARN("%I32x", "unsigned __int32", "unsigned short"); + //TODO TEST_PRINTF_WARN("%I32x", "unsigned __int32", "signed int"); + TEST_PRINTF_NOWARN("%I32x", "unsigned __int32", "unsigned int"); + TEST_PRINTF_WARN("%I32x", "unsigned __int32", "signed long"); + TEST_PRINTF_WARN("%I32x", "unsigned __int32", "unsigned long"); + TEST_PRINTF_WARN("%I32x", "unsigned __int32", "signed long long"); + TEST_PRINTF_WARN("%I32x", "unsigned __int32", "unsigned long long"); + TEST_PRINTF_WARN("%I32x", "unsigned __int32", "float"); + TEST_PRINTF_WARN("%I32x", "unsigned __int32", "double"); + TEST_PRINTF_WARN("%I32x", "unsigned __int32", "long double"); + TEST_PRINTF_WARN("%I32x", "unsigned __int32", "void *"); + TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "unsigned ptrdiff_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA("%I32x", "unsigned __int32", "uintptr_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%I32x", "unsigned __int32", "std::size_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%I32x", "unsigned __int32", "std::ssize_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%I32x", "unsigned __int32", "std::ptrdiff_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%I32x", "unsigned __int32", "std::intmax_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%I32x", "unsigned __int32", "std::uintmax_t", "unsigned long", "unsigned long long"); + TEST_PRINTF_WARN_AKA_CPP("%I32x", "unsigned __int32", "std::intptr_t", "signed long", "signed long long"); + TEST_PRINTF_WARN_AKA_CPP("%I32x", "unsigned __int32", "std::uintptr_t", "unsigned long", "unsigned long long"); + } + + void testPosixPrintfScanfParameterPosition() { // #4900 - No support for parameters in format strings + check("void foo() {" + " int bar;" + " printf(\"%1$d\", 1);" + " printf(\"%1$d, %d, %1$d\", 1, 2);" + " scanf(\"%1$d\", &bar);" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " int bar;\n" + " printf(\"%1$d\");\n" + " printf(\"%1$d, %d, %4$d\", 1, 2, 3);\n" + " scanf(\"%2$d\", &bar);\n" + " printf(\"%0$f\", 0.0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) printf format string requires 1 parameter but only 0 are given.\n" + "[test.cpp:4]: (warning) printf: referencing parameter 4 while 3 arguments given\n" + "[test.cpp:5]: (warning) scanf: referencing parameter 2 while 1 arguments given\n" + "[test.cpp:6]: (warning) printf: parameter positions start at 1, not 0\n" + "", errout_str()); + } + + + void testMicrosoftPrintfArgument() { + check("void foo() {\n" + " size_t s;\n" + " ptrdiff_t p;\n" + " __int32 i32;\n" + " unsigned __int32 u32;\n" + " __int64 i64;\n" + " unsigned __int64 u64;\n" + " printf(\"%Id %Iu %Ix\", s, s, s);\n" + " printf(\"%Id %Iu %Ix\", p, p, p);\n" + " printf(\"%I32d %I32u %I32x\", i32, i32, i32);\n" + " printf(\"%I32d %I32u %I32x\", u32, u32, u32);\n" + " printf(\"%I64d %I64u %I64x\", i64, i64, i64);\n" + " printf(\"%I64d %I64u %I64x\", u64, u64, u64);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:8]: (portability) %Id in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'size_t {aka unsigned long}'.\n" + "[test.cpp:9]: (portability) %Iu in format string (no. 2) requires 'size_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n" + "[test.cpp:9]: (portability) %Ix in format string (no. 3) requires 'size_t' but the argument type is 'ptrdiff_t {aka signed long}'.\n" + "[test.cpp:10]: (portability) %I32u in format string (no. 2) requires 'unsigned __int32' but the argument type is '__int32 {aka signed int}'.\n" + "[test.cpp:11]: (portability) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n" + "[test.cpp:12]: (portability) %I64u in format string (no. 2) requires 'unsigned __int64' but the argument type is '__int64 {aka signed long long}'.\n" + "[test.cpp:13]: (portability) %I64d in format string (no. 1) requires '__int64' but the argument type is 'unsigned __int64 {aka unsigned long long}'.\n", errout_str()); + + check("void foo() {\n" + " size_t s;\n" + " ptrdiff_t p;\n" + " __int32 i32;\n" + " unsigned __int32 u32;\n" + " __int64 i64;\n" + " unsigned __int64 u64;\n" + " printf(\"%Id %Iu %Ix\", s, s, s);\n" + " printf(\"%Id %Iu %Ix\", p, p, p);\n" + " printf(\"%I32d %I32u %I32x\", i32, i32, i32);\n" + " printf(\"%I32d %I32u %I32x\", u32, u32, u32);\n" + " printf(\"%I64d %I64u %I64x\", i64, i64, i64);\n" + " printf(\"%I64d %I64u %I64x\", u64, u64, u64);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win64)); + ASSERT_EQUALS("[test.cpp:8]: (portability) %Id in format string (no. 1) requires 'ptrdiff_t' but the argument type is 'size_t {aka unsigned long long}'.\n" + "[test.cpp:9]: (portability) %Iu in format string (no. 2) requires 'size_t' but the argument type is 'ptrdiff_t {aka signed long long}'.\n" + "[test.cpp:9]: (portability) %Ix in format string (no. 3) requires 'size_t' but the argument type is 'ptrdiff_t {aka signed long long}'.\n" + "[test.cpp:10]: (portability) %I32u in format string (no. 2) requires 'unsigned __int32' but the argument type is '__int32 {aka signed int}'.\n" + "[test.cpp:11]: (portability) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n" + "[test.cpp:12]: (portability) %I64u in format string (no. 2) requires 'unsigned __int64' but the argument type is '__int64 {aka signed long long}'.\n" + "[test.cpp:13]: (portability) %I64d in format string (no. 1) requires '__int64' but the argument type is 'unsigned __int64 {aka unsigned long long}'.\n", errout_str()); + + check("void foo() {\n" + " size_t s;\n" + " int i;\n" + " printf(\"%I\", s);\n" + " printf(\"%I6\", s);\n" + " printf(\"%I6x\", s);\n" + " printf(\"%I16\", s);\n" + " printf(\"%I16x\", s);\n" + " printf(\"%I32\", s);\n" + " printf(\"%I64\", s);\n" + " printf(\"%I%i\", s, i);\n" + " printf(\"%I6%i\", s, i);\n" + " printf(\"%I6x%i\", s, i);\n" + " printf(\"%I16%i\", s, i);\n" + " printf(\"%I16x%i\", s, i);\n" + " printf(\"%I32%i\", s, i);\n" + " printf(\"%I64%i\", s, i);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:5]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:6]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:7]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:8]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:9]: (warning) 'I32' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:10]: (warning) 'I64' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:11]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:12]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:13]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:14]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:15]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:16]: (warning) 'I32' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:17]: (warning) 'I64' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout_str()); + + // ticket #5264 + check("void foo(LPARAM lp, WPARAM wp, LRESULT lr) {\n" + " printf(\"%Ix %Ix %Ix\", lp, wp, lr);\n" + "}\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win64)); + ASSERT_EQUALS("", errout_str()); + + check("void foo(LPARAM lp, WPARAM wp, LRESULT lr) {\n" + " printf(\"%Ix %Ix %Ix\", lp, wp, lr);\n" + "}\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("", errout_str()); + + check("void foo(UINT32 a, ::UINT32 b, Fred::UINT32 c) {\n" + " printf(\"%d %d %d\", a, b, c);\n" + "};\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:2]: (portability) %d in format string (no. 1) requires 'int' but the argument type is 'UINT32 {aka unsigned int}'.\n" + "[test.cpp:2]: (portability) %d in format string (no. 2) requires 'int' but the argument type is 'UINT32 {aka unsigned int}'.\n", errout_str()); + + check("void foo(LPCVOID a, ::LPCVOID b, Fred::LPCVOID c) {\n" + " printf(\"%d %d %d\", a, b, c);\n" + "};\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:2]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'const void *'.\n" + "[test.cpp:2]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'const void *'.\n", errout_str()); + + check("void foo() {\n" + " SSIZE_T s = -2;\n" // In MSVC, SSIZE_T is available in capital letters using #include + " int i;\n" + " printf(\"%zd\", s);\n" + " printf(\"%zd%i\", s, i);\n" + " printf(\"%zu\", s);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:6]: (portability) %zu in format string (no. 1) requires 'size_t' but the argument type is 'SSIZE_T {aka signed long}'.\n", errout_str()); + + check("void foo() {\n" + " SSIZE_T s = -2;\n" // In MSVC, SSIZE_T is available in capital letters using #include + " int i;\n" + " printf(\"%zd\", s);\n" + " printf(\"%zd%i\", s, i);\n" + " printf(\"%zu\", s);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win64)); + ASSERT_EQUALS("[test.cpp:6]: (portability) %zu in format string (no. 1) requires 'size_t' but the argument type is 'SSIZE_T {aka signed long long}'.\n", errout_str()); + + check("void foo() {\n" + " SSIZE_T s = -2;\n" // Under Unix, ssize_t has to be written in small letters. Not Cppcheck, but the compiler will report this. + " int i;\n" + " printf(\"%zd\", s);\n" + " printf(\"%zd%i\", s, i);\n" + " printf(\"%zu\", s);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix64)); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " typedef SSIZE_T ssize_t;\n" // Test using typedef + " ssize_t s = -2;\n" + " int i;\n" + " printf(\"%zd\", s);\n" + " printf(\"%zd%i\", s, i);\n" + " printf(\"%zu\", s);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win64)); + ASSERT_EQUALS("[test.cpp:7]: (portability) %zu in format string (no. 1) requires 'size_t' but the argument type is 'SSIZE_T {aka signed long long}'.\n", errout_str()); + + } + + void testMicrosoftScanfArgument() { + check("void foo() {\n" + " size_t s;\n" + " ptrdiff_t p;\n" + " __int32 i32;\n" + " unsigned __int32 u32;\n" + " __int64 i64;\n" + " unsigned __int64 u64;\n" + " scanf(\"%Id %Iu %Ix\", &s, &s, &s);\n" + " scanf(\"%Id %Iu %Ix\", &p, &p, &p);\n" + " scanf(\"%I32d %I32u %I32x\", &i32, &i32, &i32);\n" + " scanf(\"%I32d %I32u %I32x\", &u32, &u32, &u32);\n" + " scanf(\"%I64d %I64u %I64x\", &i64, &i64, &i64);\n" + " scanf(\"%I64d %I64u %I64x\", &u64, &u64, &u64);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:8]: (portability) %Id in format string (no. 1) requires 'ptrdiff_t *' but the argument type is 'size_t * {aka unsigned long *}'.\n" + "[test.cpp:9]: (portability) %Iu in format string (no. 2) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka signed long *}'.\n" + "[test.cpp:9]: (portability) %Ix in format string (no. 3) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka signed long *}'.\n" + "[test.cpp:10]: (portability) %I32u in format string (no. 2) requires 'unsigned __int32 *' but the argument type is '__int32 * {aka signed int *}'.\n" + "[test.cpp:10]: (portability) %I32x in format string (no. 3) requires 'unsigned __int32 *' but the argument type is '__int32 * {aka signed int *}'.\n" + "[test.cpp:11]: (portability) %I32d in format string (no. 1) requires '__int32 *' but the argument type is 'unsigned __int32 * {aka unsigned int *}'.\n" + "[test.cpp:12]: (portability) %I64u in format string (no. 2) requires 'unsigned __int64 *' but the argument type is '__int64 * {aka signed long long *}'.\n" + "[test.cpp:12]: (portability) %I64x in format string (no. 3) requires 'unsigned __int64 *' but the argument type is '__int64 * {aka signed long long *}'.\n" + "[test.cpp:13]: (portability) %I64d in format string (no. 1) requires '__int64 *' but the argument type is 'unsigned __int64 * {aka unsigned long long *}'.\n", errout_str()); + + check("void foo() {\n" + " size_t s;\n" + " ptrdiff_t p;\n" + " __int32 i32;\n" + " unsigned __int32 u32;\n" + " __int64 i64;\n" + " unsigned __int64 u64;\n" + " scanf(\"%Id %Iu %Ix\", &s, &s, &s);\n" + " scanf(\"%Id %Iu %Ix\", &p, &p, &p);\n" + " scanf(\"%I32d %I32u %I32x\", &i32, &i32, &i32);\n" + " scanf(\"%I32d %I32u %I32x\", &u32, &u32, &u32);\n" + " scanf(\"%I64d %I64u %I64x\", &i64, &i64, &i64);\n" + " scanf(\"%I64d %I64u %I64x\", &u64, &u64, &u64);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win64)); + ASSERT_EQUALS("[test.cpp:8]: (portability) %Id in format string (no. 1) requires 'ptrdiff_t *' but the argument type is 'size_t * {aka unsigned long long *}'.\n" + "[test.cpp:9]: (portability) %Iu in format string (no. 2) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka signed long long *}'.\n" + "[test.cpp:9]: (portability) %Ix in format string (no. 3) requires 'size_t *' but the argument type is 'ptrdiff_t * {aka signed long long *}'.\n" + "[test.cpp:10]: (portability) %I32u in format string (no. 2) requires 'unsigned __int32 *' but the argument type is '__int32 * {aka signed int *}'.\n" + "[test.cpp:10]: (portability) %I32x in format string (no. 3) requires 'unsigned __int32 *' but the argument type is '__int32 * {aka signed int *}'.\n" + "[test.cpp:11]: (portability) %I32d in format string (no. 1) requires '__int32 *' but the argument type is 'unsigned __int32 * {aka unsigned int *}'.\n" + "[test.cpp:12]: (portability) %I64u in format string (no. 2) requires 'unsigned __int64 *' but the argument type is '__int64 * {aka signed long long *}'.\n" + "[test.cpp:12]: (portability) %I64x in format string (no. 3) requires 'unsigned __int64 *' but the argument type is '__int64 * {aka signed long long *}'.\n" + "[test.cpp:13]: (portability) %I64d in format string (no. 1) requires '__int64 *' but the argument type is 'unsigned __int64 * {aka unsigned long long *}'.\n", errout_str()); + + check("void foo() {\n" + " size_t s;\n" + " int i;\n" + " scanf(\"%I\", &s);\n" + " scanf(\"%I6\", &s);\n" + " scanf(\"%I6x\", &s);\n" + " scanf(\"%I16\", &s);\n" + " scanf(\"%I16x\", &s);\n" + " scanf(\"%I32\", &s);\n" + " scanf(\"%I64\", &s);\n" + " scanf(\"%I%i\", &s, &i);\n" + " scanf(\"%I6%i\", &s, &i);\n" + " scanf(\"%I6x%i\", &s, &i);\n" + " scanf(\"%I16%i\", &s, &i);\n" + " scanf(\"%I16x%i\", &s, &i);\n" + " scanf(\"%I32%i\", &s, &i);\n" + " scanf(\"%I64%i\", &s, &i);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:5]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:6]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:7]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:8]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:9]: (warning) 'I32' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:10]: (warning) 'I64' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:11]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:12]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:13]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:14]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:15]: (warning) 'I' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:16]: (warning) 'I32' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n" + "[test.cpp:17]: (warning) 'I64' in format string (no. 1) is a length modifier and cannot be used without a conversion specifier.\n", errout_str()); + + check("void foo() {\n" + " SSIZE_T s;\n" // In MSVC, SSIZE_T is available in capital letters using #include + " int i;\n" + " scanf(\"%zd\", &s);\n" + " scanf(\"%zd%i\", &s, &i);\n" + " scanf(\"%zu\", &s);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:6]: (portability) %zu in format string (no. 1) requires 'size_t *' but the argument type is 'SSIZE_T * {aka signed long *}'.\n", errout_str()); + + check("void foo() {\n" + " SSIZE_T s;\n" // In MSVC, SSIZE_T is available in capital letters using #include + " int i;\n" + " scanf(\"%zd\", &s);\n" + " scanf(\"%zd%i\", &s, &i);\n" + " scanf(\"%zu\", &s);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win64)); + ASSERT_EQUALS("[test.cpp:6]: (portability) %zu in format string (no. 1) requires 'size_t *' but the argument type is 'SSIZE_T * {aka signed long long *}'.\n", errout_str()); + + check("void foo() {\n" + " SSIZE_T s;\n" // Under Unix, ssize_t has to be written in small letters. Not Cppcheck, but the compiler will report this. + " int i;\n" + " scanf(\"%zd\", &s);\n" + " scanf(\"%zd%i\", &s, &i);\n" + " scanf(\"%zu\", &s);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix64)); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " typedef SSIZE_T ssize_t;\n" // Test using typedef + " ssize_t s;\n" + " int i;\n" + " scanf(\"%zd\", &s);\n" + " scanf(\"%zd%i\", &s, &i);\n" + " scanf(\"%zu\", &s);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win64)); + ASSERT_EQUALS("[test.cpp:7]: (portability) %zu in format string (no. 1) requires 'size_t *' but the argument type is 'SSIZE_T * {aka signed long long *}'.\n", errout_str()); + + } + + void testMicrosoftCStringFormatArguments() { // ticket #4920 + check("void foo() {\n" + " unsigned __int32 u32;\n" + " String string;\n" + " string.Format(\"%I32d\", u32);\n" + " string.AppendFormat(\"%I32d\", u32);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::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" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix32)); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " unsigned __int32 u32;\n" + " CString string;\n" + " string.Format(\"%I32d\", u32);\n" + " string.AppendFormat(\"%I32d\", u32);\n" + " CString::Format(\"%I32d\", u32);\n" + "}", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:4]: (portability) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n" + "[test.cpp:5]: (portability) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n" + "[test.cpp:6]: (portability) %I32d in format string (no. 1) requires '__int32' but the argument type is 'unsigned __int32 {aka unsigned int}'.\n", errout_str()); + } + + void testMicrosoftSecurePrintfArgument() { + check("void foo() {\n" + " int i;\n" + " unsigned int u;\n" + " _tprintf_s(_T(\"%d %u\"), u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:4]: (warning) _tprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo() {\n" + " int i;\n" + " unsigned int u;\n" + " _tprintf_s(_T(\"%d %u\"), u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:4]: (warning) _tprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo() {\n" + " int i;\n" + " unsigned int u;\n" + " printf_s(\"%d %u\", u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:4]: (warning) printf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo() {\n" + " int i;\n" + " unsigned int u;\n" + " wprintf_s(L\"%d %u\", u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:4]: (warning) wprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo() {\n" + " TCHAR str[10];\n" + " int i;\n" + " unsigned int u;\n" + " _stprintf_s(str, sizeof(str) / sizeof(TCHAR), _T(\"%d %u\"), u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:5]: (warning) _stprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo() {\n" + " TCHAR str[10];\n" + " int i;\n" + " unsigned int u;\n" + " _stprintf_s(str, sizeof(str) / sizeof(TCHAR), _T(\"%d %u\"), u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:5]: (warning) _stprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo() {\n" + " char str[10];\n" + " int i;\n" + " unsigned int u;\n" + " sprintf_s(str, sizeof(str), \"%d %u\", u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:5]: (warning) sprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo() {\n" + " char str[10];\n" + " int i;\n" + " unsigned int u;\n" + " sprintf_s(str, \"%d %u\", u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:5]: (warning) sprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo() {\n" + " wchar_t str[10];\n" + " int i;\n" + " unsigned int u;\n" + " swprintf_s(str, sizeof(str) / sizeof(wchar_t), L\"%d %u\", u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:5]: (warning) swprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo() {\n" + " wchar_t str[10];\n" + " int i;\n" + " unsigned int u;\n" + " swprintf_s(str, L\"%d %u\", u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:5]: (warning) swprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo() {\n" + " TCHAR str[10];\n" + " int i;\n" + " unsigned int u;\n" + " _sntprintf_s(str, sizeof(str) / sizeof(TCHAR), _TRUNCATE, _T(\"%d %u\"), u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:5]: (warning) _sntprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo() {\n" + " TCHAR str[10];\n" + " int i;\n" + " unsigned int u;\n" + " _sntprintf_s(str, sizeof(str) / sizeof(TCHAR), _TRUNCATE, _T(\"%d %u\"), u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:5]: (warning) _sntprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo() {\n" + " char str[10];\n" + " int i;\n" + " unsigned int u;\n" + " _snprintf_s(str, sizeof(str), _TRUNCATE, \"%d %u\", u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:5]: (warning) _snprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo() {\n" + " wchar_t str[10];\n" + " int i;\n" + " unsigned int u;\n" + " _snwprintf_s(str, sizeof(str) / sizeof(wchar_t), _TRUNCATE, L\"%d %u\", u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:5]: (warning) _snwprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo(FILE * fp) {\n" + " int i;\n" + " unsigned int u;\n" + " _ftprintf_s(fp, _T(\"%d %u\"), u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:4]: (warning) _ftprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo(FILE * fp) {\n" + " int i;\n" + " unsigned int u;\n" + " _ftprintf_s(fp, _T(\"%d %u\"), u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:4]: (warning) _ftprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo(FILE * fp) {\n" + " int i;\n" + " unsigned int u;\n" + " fprintf_s(fp, \"%d %u\", u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:4]: (warning) fprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo(FILE * fp) {\n" + " int i;\n" + " unsigned int u;\n" + " fwprintf_s(fp, L\"%d %u\", u, i, 0);\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("[test.cpp:4]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'unsigned int'.\n" + "[test.cpp:4]: (warning) %u in format string (no. 2) requires 'unsigned int' but the argument type is 'signed int'.\n" + "[test.cpp:4]: (warning) fwprintf_s format string requires 2 parameters but 3 are given.\n", errout_str()); + + check("void foo() {\n" + " char lineBuffer [600];\n" + " const char * const format = \"%15s%17s%17s%17s%17s\";\n" + " sprintf_s(lineBuffer, 600, format, \"type\", \"sum\", \"avg\", \"min\", \"max\");\n" + " sprintf_s(lineBuffer, format, \"type\", \"sum\", \"avg\", \"min\", \"max\");\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " const char * const format1 = \"%15s%17s%17s%17s%17s\";\n" + " const char format2[] = \"%15s%17s%17s%17s%17s\";\n" + " const char * const format3 = format1;\n" + " int i = 0;\n" + " sprintf_s(lineBuffer, format1, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" + " sprintf_s(lineBuffer, format2, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" + " sprintf_s(lineBuffer, format3, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" + " sprintf(lineBuffer, format1, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" + " sprintf(lineBuffer, format2, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" + " sprintf(lineBuffer, format3, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" + " printf(format1, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" + " printf(format2, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" + " printf(format3, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" + " sprintf_s(lineBuffer, 100, format1, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" + " sprintf_s(lineBuffer, 100, format2, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" + " sprintf_s(lineBuffer, 100, format3, \"type\", \"sum\", \"avg\", \"min\", i, 0);\n" + "}\n", dinit(CheckOptions, $.inconclusive = true, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:6]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" + "[test.cpp:6]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n" + "[test.cpp:7]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" + "[test.cpp:7]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n" + "[test.cpp:8]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" + "[test.cpp:8]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n" + "[test.cpp:9]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" + "[test.cpp:9]: (warning) sprintf format string requires 5 parameters but 6 are given.\n" + "[test.cpp:10]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" + "[test.cpp:10]: (warning) sprintf format string requires 5 parameters but 6 are given.\n" + "[test.cpp:11]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" + "[test.cpp:11]: (warning) sprintf format string requires 5 parameters but 6 are given.\n" + "[test.cpp:12]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" + "[test.cpp:12]: (warning) printf format string requires 5 parameters but 6 are given.\n" + "[test.cpp:13]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" + "[test.cpp:13]: (warning) printf format string requires 5 parameters but 6 are given.\n" + "[test.cpp:14]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" + "[test.cpp:14]: (warning) printf format string requires 5 parameters but 6 are given.\n" + "[test.cpp:15]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" + "[test.cpp:15]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n" + "[test.cpp:16]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" + "[test.cpp:16]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n" + "[test.cpp:17]: (warning) %s in format string (no. 5) requires 'char *' but the argument type is 'signed int'.\n" + "[test.cpp:17]: (warning) sprintf_s format string requires 5 parameters but 6 are given.\n", errout_str()); + + } + + void testMicrosoftSecureScanfArgument() { + check("void foo() {\n" + " int i;\n" + " unsigned int u;\n" + " TCHAR str[10];\n" + " _tscanf_s(_T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" + "[test.cpp:5]: (warning) _tscanf_s format string requires 6 parameters but 7 are given.\n", errout_str()); + + check("void foo() {\n" + " int i;\n" + " unsigned int u;\n" + " TCHAR str[10];\n" + " _tscanf_s(_T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" + "[test.cpp:5]: (warning) _tscanf_s format string requires 6 parameters but 7 are given.\n", errout_str()); + + check("void foo() {\n" + " int i;\n" + " unsigned int u;\n" + " char str[10];\n" + " scanf_s(\"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" + "[test.cpp:5]: (warning) scanf_s format string requires 6 parameters but 7 are given.\n", errout_str()); + + check("void foo() {\n" + " int i;\n" + " unsigned int u;\n" + " wchar_t str[10];\n" + " wscanf_s(L\"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" + "[test.cpp:5]: (warning) wscanf_s format string requires 6 parameters but 7 are given.\n", errout_str()); + + check("void f() {\n" + " char str[8];\n" + " scanf_s(\"%8c\", str, sizeof(str));\n" + " scanf_s(\"%9c\", str, sizeof(str));\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:4]: (error) Width 9 given in format string (no. 1) is larger than destination buffer 'str[8]', use %8c to prevent overflowing it.\n", errout_str()); + + check("void foo() {\n" + " TCHAR txt[100];\n" + " int i;\n" + " unsigned int u;\n" + " TCHAR str[10];\n" + " _stscanf_s(txt, _T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:6]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" + "[test.cpp:6]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" + "[test.cpp:6]: (warning) _stscanf_s format string requires 6 parameters but 7 are given.\n", errout_str()); + + check("void foo() {\n" + " TCHAR txt[100];\n" + " int i;\n" + " unsigned int u;\n" + " TCHAR str[10];\n" + " _stscanf_s(txt, _T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("[test.cpp:6]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" + "[test.cpp:6]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" + "[test.cpp:6]: (warning) _stscanf_s format string requires 6 parameters but 7 are given.\n", errout_str()); + + check("void foo() {\n" + " char txt[100];\n" + " int i;\n" + " unsigned int u;\n" + " char str[10];\n" + " sscanf_s(txt, \"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:6]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" + "[test.cpp:6]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" + "[test.cpp:6]: (warning) sscanf_s format string requires 6 parameters but 7 are given.\n", errout_str()); + + check("void foo() {\n" + " wchar_t txt[100];\n" + " int i;\n" + " unsigned int u;\n" + " wchar_t str[10];\n" + " swscanf_s(txt, L\"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("[test.cpp:6]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" + "[test.cpp:6]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" + "[test.cpp:6]: (warning) swscanf_s format string requires 6 parameters but 7 are given.\n", errout_str()); + + check("void foo(FILE * fp) {\n" + " int i;\n" + " unsigned int u;\n" + " TCHAR str[10];\n" + " _ftscanf_s(fp, _T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" + "[test.cpp:5]: (warning) _ftscanf_s format string requires 6 parameters but 7 are given.\n", errout_str()); + + check("void foo(FILE * fp) {\n" + " int i;\n" + " unsigned int u;\n" + " TCHAR str[10];\n" + " _ftscanf_s(fp, _T(\"%s %d %u %[a-z]\"), str, 10, &u, &i, str, 10, 0)\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" + "[test.cpp:5]: (warning) _ftscanf_s format string requires 6 parameters but 7 are given.\n", errout_str()); + + check("void foo(FILE * fp) {\n" + " int i;\n" + " unsigned int u;\n" + " char str[10];\n" + " fscanf_s(fp, \"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" + "[test.cpp:5]: (warning) fscanf_s format string requires 6 parameters but 7 are given.\n", errout_str()); + + check("void foo(FILE * fp) {\n" + " int i;\n" + " unsigned int u;\n" + " wchar_t str[10];\n" + " fwscanf_s(fp, L\"%s %d %u %[a-z]\", str, 10, &u, &i, str, 10, 0)\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("[test.cpp:5]: (warning) %d in format string (no. 2) requires 'int *' but the argument type is 'unsigned int *'.\n" + "[test.cpp:5]: (warning) %u in format string (no. 3) requires 'unsigned int *' but the argument type is 'signed int *'.\n" + "[test.cpp:5]: (warning) fwscanf_s format string requires 6 parameters but 7 are given.\n", errout_str()); + + check("void foo() {\n" + " WCHAR msStr1[5] = {0};\n" + " wscanf_s(L\"%4[^-]\", msStr1, _countof(msStr1));\n" + "}\n", dinit(CheckOptions, $.platform = Platform::Type::Win32W)); + ASSERT_EQUALS("", errout_str()); + } + + void testQStringFormatArguments() { + check("void foo(float f) {\n" + " QString string;\n" + " string.sprintf(\"%d\", f);\n" + "}", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:3]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'float'.\n", errout_str()); + + check("void foo(float f) {\n" + " QString string;\n" + " string = QString::asprintf(\"%d\", f);\n" + "}", dinit(CheckOptions, $.platform = Platform::Type::Win32A)); + ASSERT_EQUALS("[test.cpp:3]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'float'.\n", errout_str()); + } + + void testTernary() { // ticket #6182 + check("void test(const std::string &val) {\n" + " printf(\"%s\", val.empty() ? \"I like to eat bananas\" : val.c_str());\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testUnsignedConst() { // ticket #6321 + check("void test() {\n" + " unsigned const x = 5;\n" + " printf(\"%u\", x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testAstType() { // ticket #7014 + check("void test() {\n" + " printf(\"%c\", \"hello\"[0]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void test() {\n" + " printf(\"%lld\", (long long)1);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void test() {\n" + " printf(\"%i\", (short *)x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %i in format string (no. 1) requires 'int' but the argument type is 'signed short *'.\n", errout_str()); + + check("int (*fp)();\n" // #7178 - function pointer call + "void test() {\n" + " printf(\"%i\", fp());\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testPrintf0WithSuffix() { // ticket #7069 + check("void foo() {\n" + " printf(\"%u %lu %llu\", 0U, 0UL, 0ULL);\n" + " printf(\"%u %lu %llu\", 0u, 0ul, 0ull);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testReturnValueTypeStdLib() { + check("void f() {\n" + " const char *s = \"0\";\n" + " printf(\"%ld%lld\", atol(s), atoll(s));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // 8141 + check("void f(int i) {\n" + " printf(\"%f\", imaxabs(i));\n" + "}\n", dinit(CheckOptions, $.portability = true, $.platform = Platform::Type::Unix64)); + ASSERT_EQUALS("[test.cpp:2]: (portability) %f in format string (no. 1) requires 'double' but the argument type is 'intmax_t {aka signed long}'.\n", errout_str()); + } + + void testPrintfTypeAlias1() { + check("using INT = int;\n\n" + "using PINT = INT *;\n" + "using PCINT = const PINT;\n" + "INT i;\n" + "PINT pi;\n" + "PCINT pci;" + "void foo() {\n" + " printf(\"%d %p %p\", i, pi, pci);\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("using INT = int;\n\n" + "using PINT = INT *;\n" + "using PCINT = const PINT;\n" + "INT i;\n" + "PINT pi;\n" + "PCINT pci;" + "void foo() {\n" + " printf(\"%f %f %f\", i, pi, pci);\n" + "};"); + ASSERT_EQUALS("[test.cpp:8]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n" + "[test.cpp:8]: (warning) %f in format string (no. 2) requires 'double' but the argument type is 'signed int *'.\n" + "[test.cpp:8]: (warning) %f in format string (no. 3) requires 'double' but the argument type is 'const signed int *'.\n", errout_str()); + } + + void testPrintfAuto() { // #8992 + check("void f() {\n" + " auto s = sizeof(int);\n" + " printf(\"%zu\", s);\n" + " printf(\"%f\", s);\n" + "}\n", dinit(CheckOptions, $.portability = true)); + ASSERT_EQUALS("[test.cpp:4]: (portability) %f in format string (no. 1) requires 'double' but the argument type is 'size_t {aka unsigned long}'.\n", errout_str()); + } + + void testPrintfParenthesis() { // #8489 + check("void f(int a) {\n" + " printf(\"%f\", (a >> 24) & 0xff);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n", errout_str()); + + check("void f(int a) {\n" + " printf(\"%f\", 0xff & (a >> 24));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n", errout_str()); + + check("void f(int a) {\n" + " printf(\"%f\", ((a >> 24) + 1) & 0xff);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) %f in format string (no. 1) requires 'double' but the argument type is 'signed int'.\n", errout_str()); + } + + void testStdDistance() { // #10304 + check("void foo(const std::vector& IO, const int* pio) {\n" + "const auto Idx = std::distance(&IO.front(), pio);\n" + "printf(\"Idx = %td\", Idx);\n" + "}", dinit(CheckOptions, $.portability = true)); + ASSERT_EQUALS("", errout_str()); + } + + void testParameterPack() { // #11289 + check("template auto f(const char* format, const Args&... args) {\n" + " return snprintf(nullptr, 0, format, args...);\n" + "}\n" + "void g() {\n" + " f(\"%d%d\", 1, 2);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } +}; + +REGISTER_TEST(TestIO) diff --git a/cppcheck-2.14.0/test/testleakautovar.cpp b/cppcheck-2.14.0/test/testleakautovar.cpp new file mode 100644 index 00000000..676e2e51 --- /dev/null +++ b/cppcheck-2.14.0/test/testleakautovar.cpp @@ -0,0 +1,3316 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "checkleakautovar.h" +#include "fixture.h" +#include "helpers.h" +#include "settings.h" +#include "tokenize.h" + +#include +#include + +class TestLeakAutoVar : public TestFixture { +public: + TestLeakAutoVar() : TestFixture("TestLeakAutoVar") {} + +private: + Settings settings; + + void run() override { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " \n" + " malloc\n" + " realloc\n" + " free\n" + " \n" + " \n" + " socket\n" + " close\n" + " \n" + " \n" + " fopen\n" + " freopen\n" + " fclose\n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + settings = settingsBuilder(settings).libraryxml(xmldata, sizeof(xmldata)).build(); + + // Assign + TEST_CASE(assign1); + TEST_CASE(assign2); + TEST_CASE(assign3); + TEST_CASE(assign4); + TEST_CASE(assign5); + TEST_CASE(assign6); + TEST_CASE(assign7); + TEST_CASE(assign8); + TEST_CASE(assign9); + TEST_CASE(assign10); + TEST_CASE(assign11); // #3942: x = a(b(p)); + TEST_CASE(assign12); // #4236: FP. bar(&x); + TEST_CASE(assign13); // #4237: FP. char*&ref=p; p=malloc(10); free(ref); + TEST_CASE(assign14); + TEST_CASE(assign15); + TEST_CASE(assign16); + TEST_CASE(assign17); // #9047 + TEST_CASE(assign18); + TEST_CASE(assign19); + TEST_CASE(assign20); // #9187 + TEST_CASE(assign21); // #10186 + TEST_CASE(assign22); // #9139 + TEST_CASE(assign23); + TEST_CASE(assign24); // #7440 + TEST_CASE(assign25); + + TEST_CASE(isAutoDealloc); + + TEST_CASE(realloc1); + TEST_CASE(realloc2); + TEST_CASE(realloc3); + TEST_CASE(realloc4); + TEST_CASE(realloc5); // #9292, #9990 + TEST_CASE(freopen1); + TEST_CASE(freopen2); + + TEST_CASE(deallocuse1); + TEST_CASE(deallocuse3); + TEST_CASE(deallocuse4); + TEST_CASE(deallocuse5); // #4018: FP. free(p), p = 0; + TEST_CASE(deallocuse6); // #4034: FP. x = p = f(); + TEST_CASE(deallocuse7); // #6467, #6469, #6473 + TEST_CASE(deallocuse8); // #1765 + TEST_CASE(deallocuse9); // #9781 + TEST_CASE(deallocuse10); + TEST_CASE(deallocuse11); // #8302 + TEST_CASE(deallocuse12); + TEST_CASE(deallocuse13); + TEST_CASE(deallocuse14); + TEST_CASE(deallocuse15); + + TEST_CASE(doublefree1); + TEST_CASE(doublefree2); + TEST_CASE(doublefree3); // #4914 + TEST_CASE(doublefree4); // #5451 - FP when exit is called + TEST_CASE(doublefree5); // #5522 + TEST_CASE(doublefree6); // #7685 + TEST_CASE(doublefree7); + TEST_CASE(doublefree8); + TEST_CASE(doublefree9); + TEST_CASE(doublefree10); // #8706 + TEST_CASE(doublefree11); + TEST_CASE(doublefree12); // #10502 + TEST_CASE(doublefree13); // #11008 + TEST_CASE(doublefree14); // #9708 + TEST_CASE(doublefree15); + TEST_CASE(doublefree16); + + // exit + TEST_CASE(exit1); + TEST_CASE(exit2); + TEST_CASE(exit3); + + // handling function calls + TEST_CASE(functioncall1); + + // goto + TEST_CASE(goto1); + TEST_CASE(goto2); + TEST_CASE(goto3); // #11431 + + // if/else + TEST_CASE(ifelse1); + TEST_CASE(ifelse2); + TEST_CASE(ifelse3); + TEST_CASE(ifelse4); + TEST_CASE(ifelse5); + TEST_CASE(ifelse6); // #3370 + TEST_CASE(ifelse7); // #5576 - if (fd < 0) + TEST_CASE(ifelse8); // #5747 - if (fd == -1) + TEST_CASE(ifelse9); // #5273 - if (X(p==NULL, 0)) + TEST_CASE(ifelse10); // #8794 - if (!(x!=NULL)) + TEST_CASE(ifelse11); // #8365 - if (NULL == (p = malloc(4))) + TEST_CASE(ifelse12); // #8340 - if ((*p = malloc(4)) == NULL) + TEST_CASE(ifelse13); // #8392 + TEST_CASE(ifelse14); // #9130 - if (x == (char*)NULL) + TEST_CASE(ifelse15); // #9206 - if (global_ptr = malloc(1)) + TEST_CASE(ifelse16); // #9635 - if (p = malloc(4), p == NULL) + TEST_CASE(ifelse17); // if (!!(!p)) + TEST_CASE(ifelse18); + TEST_CASE(ifelse19); + TEST_CASE(ifelse20); // #10182 + TEST_CASE(ifelse21); + TEST_CASE(ifelse22); // #10187 + TEST_CASE(ifelse23); // #5473 + TEST_CASE(ifelse24); // #1733 + TEST_CASE(ifelse25); // #9966 + TEST_CASE(ifelse26); + TEST_CASE(ifelse27); + TEST_CASE(ifelse28); // #11038 + + // switch + TEST_CASE(switch1); + + // loops + TEST_CASE(loop1); + TEST_CASE(loop2); + + // mismatching allocation/deallocation + TEST_CASE(mismatchAllocDealloc); + + TEST_CASE(smartPointerDeleter); + TEST_CASE(smartPointerRelease); + + // Execution reaches a 'return' + TEST_CASE(return1); + TEST_CASE(return2); + TEST_CASE(return3); + TEST_CASE(return4); + TEST_CASE(return5); + TEST_CASE(return6); // #8282 return {p, p} + TEST_CASE(return7); // #9343 return (uint8_t*)x + TEST_CASE(return8); + TEST_CASE(return9); + TEST_CASE(return10); + + // General tests: variable type, allocation type, etc + TEST_CASE(test1); + TEST_CASE(test2); + TEST_CASE(test3); // #3954 - reference pointer + TEST_CASE(test4); // #5923 - static pointer + TEST_CASE(test5); // unknown type + + // Execution reaches a 'throw' + TEST_CASE(throw1); + TEST_CASE(throw2); + + // Possible leak => Further configuration is needed for complete analysis + TEST_CASE(configuration1); + TEST_CASE(configuration2); + TEST_CASE(configuration3); + TEST_CASE(configuration4); + TEST_CASE(configuration5); + TEST_CASE(configuration6); + + TEST_CASE(ptrptr); + + TEST_CASE(nestedAllocation); + TEST_CASE(testKeywords); // #6767 + + TEST_CASE(inlineFunction); // #3989 + + TEST_CASE(smartPtrInContainer); // #8262 + + TEST_CASE(functionCallCastConfig); // #9652 + TEST_CASE(functionCallLeakIgnoreConfig); // #7923 + } + +#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) + void check_(const char* file, int line, const char code[], bool cpp = false, const Settings *s = nullptr) { + const Settings settings1 = settingsBuilder(s ? *s : settings).checkLibrary().build(); + + // Tokenize.. + SimpleTokenizer tokenizer(settings1, *this); + ASSERT_LOC(tokenizer.tokenize(code, cpp), file, line); + + // Check for leaks.. + runChecks(tokenizer, this); + } + + void check_(const char* file, int line, const char code[], const Settings & s) { + const Settings settings0 = settingsBuilder(s).checkLibrary().build(); + + // Tokenize.. + SimpleTokenizer tokenizer(settings0, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check for leaks.. + runChecks(tokenizer, 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" + "}", /*cpp*/ true); + TODO_ASSERT_EQUALS("", "[test.cpp:6]: (error) Memory leak: p\n", errout_str()); + } + + void assign14() { + check("void f(int x) {\n" + " char *p;\n" + " if (x && (p = malloc(10))) { }" + "}"); + ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout_str()); + + check("void f(int x) {\n" + " char *p;\n" + " if (x && (p = new char[10])) { }" + "}", true); + ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout_str()); + } + + void assign15() { + // #8120 + check("void f() {\n" + " baz *p;\n" + " p = malloc(sizeof *p);\n" + " free(p);\n" + " p = malloc(sizeof *p);\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void assign16() { + check("void f() {\n" + " char *p = malloc(10);\n" + " free(p);\n" + " if (p=dostuff()) *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void assign17() { // #9047 + check("void f() {\n" + " char *p = (char*)malloc(10);\n" + "}"); + ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout_str()); + + check("void f() {\n" + " char *p = (char*)(int*)malloc(10);\n" + "}"); + ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout_str()); + } + + void assign18() { + check("void f(int x) {\n" + " char *p;\n" + " if (x && (p = (char*)malloc(10))) { }" + "}"); + ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout_str()); + + check("void f(int x) {\n" + " char *p;\n" + " if (x && (p = (char*)(int*)malloc(10))) { }" + "}"); + ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout_str()); + } + + void assign19() { + check("void f() {\n" + " char *p = malloc(10);\n" + " free((void*)p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void assign20() { // #9187 + check("void f() {\n" + " char *p = static_cast(malloc(10));\n" + "}", true); + ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout_str()); + } + + void assign21() { + check("void f(int **x) {\n" // #10186 + " void *p = malloc(10);\n" + " *x = (int*)p;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("struct S { int i; };\n" // #10996 + "void f() {\n" + " S* s = new S();\n" + " (void)s;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: s\n", errout_str()); + } + + void assign22() { // #9139 + check("void f(char tempFileName[256]) {\n" + " const int fd = socket(AF_INET, SOCK_PACKET, 0 );\n" + "}", true); + ASSERT_EQUALS("[test.cpp:3]: (error) Resource leak: fd\n", errout_str()); + + check("void f() {\n" + " const void * const p = malloc(10);\n" + "}", true); + ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout_str()); + } + + void assign23() { + const Settings s = settingsBuilder().library("posix.cfg").build(); + check("void f() {\n" + " int n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14;\n" + " *&n1 = open(\"xx.log\", O_RDONLY);\n" + " *&(n2) = open(\"xx.log\", O_RDONLY);\n" + " *(&n3) = open(\"xx.log\", O_RDONLY);\n" + " *&*&n4 = open(\"xx.log\", O_RDONLY);\n" + " *&*&*&(n5) = open(\"xx.log\", O_RDONLY);\n" + " *&*&(*&n6) = open(\"xx.log\", O_RDONLY);\n" + " *&*(&*&n7) = open(\"xx.log\", O_RDONLY);\n" + " *(&*&n8) = open(\"xx.log\", O_RDONLY);\n" + " *&(*&*&(*&n9)) = open(\"xx.log\", O_RDONLY);\n" + " (n10) = open(\"xx.log\", O_RDONLY);\n" + " ((n11)) = open(\"xx.log\", O_RDONLY);\n" + " ((*&n12)) = open(\"xx.log\", O_RDONLY);\n" + " *(&(*&n13)) = open(\"xx.log\", O_RDONLY);\n" + " ((*&(*&n14))) = open(\"xx.log\", O_RDONLY);\n" + "}\n", true, &s); + ASSERT_EQUALS("[test.cpp:17]: (error) Resource leak: n1\n" + "[test.cpp:17]: (error) Resource leak: n2\n" + "[test.cpp:17]: (error) Resource leak: n3\n" + "[test.cpp:17]: (error) Resource leak: n4\n" + "[test.cpp:17]: (error) Resource leak: n5\n" + "[test.cpp:17]: (error) Resource leak: n6\n" + "[test.cpp:17]: (error) Resource leak: n7\n" + "[test.cpp:17]: (error) Resource leak: n8\n" + "[test.cpp:17]: (error) Resource leak: n9\n" + "[test.cpp:17]: (error) Resource leak: n10\n" + "[test.cpp:17]: (error) Resource leak: n11\n" + "[test.cpp:17]: (error) Resource leak: n12\n" + "[test.cpp:17]: (error) Resource leak: n13\n" + "[test.cpp:17]: (error) Resource leak: n14\n", + errout_str()); + } + + void assign24() { + check("void f() {\n" // #7440 + " char* data = new char[100];\n" + " char** dataPtr = &data;\n" + " delete[] *dataPtr;\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " char* data = new char[100];\n" + " char** dataPtr = &data;\n" + " printf(\"test\");\n" + " delete[] *dataPtr;\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #9279 + " int* p = new int;\n" + " *p = 42;\n" + " g();\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function g() should have configuration\n", + errout_str()); + + check("void g();\n" + "void f() {\n" + " int* p = new int;\n" + " *p = 42;\n" + " g();\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: p\n", errout_str()); + + check("void g() {}\n" + "void f() {\n" + " int* p = new int;\n" + " *p = 42;\n" + " g();\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: p\n", errout_str()); + + check("[[noreturn]] void g();\n" + "void f() {\n" + " int* p = new int;\n" + " *p = 42;\n" + " g();\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("", errout_str()); + + check("void g() { exit(1); }\n" + "void f() {\n" + " int* p = new int;\n" + " *p = 42;\n" + " g();\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("", errout_str()); + + check("void g() {}\n" // #10517 + "void f() {\n" + " char* p = malloc(10);\n" + " g();\n" + "}\n"); + ASSERT_EQUALS("[test.c:5]: (error) Memory leak: p\n", errout_str()); + } + + void assign25() { + check("void f() {\n" // #11796 + " int* p{ new int };\n" + " int* q(new int);\n" + "}", true); + ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: p\n" + "[test.cpp:4]: (error) Memory leak: q\n", + errout_str()); + + check("struct S : B {\n" // #12239 + " void f();\n" + " void g();\n" + "};\n" + "void S::f() {\n" + " FD* fd(new FD(this));\n" + " fd->exec();\n" + "}\n" + "void S::g() {\n" + " FD* fd{ new FD(this) };\n" + " fd->exec();\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" // #12327 + " char* m_p;\n" + " C(char* p) : m_p(p) {}\n" + "};\n" + "std::list gli;\n" + "void f() {\n" + " std::list li;\n" + " char* p = new char[1];\n" + " C c(p);\n" + " li.push_back(c);\n" + " C c2(li.front());\n" + " delete[] c2.m_p;\n" + "}\n" + "void g() {\n" + " char* p = new char[1];\n" + " C c(p);\n" + " gli.push_back(c);\n" + "}\n" + "void h() {\n" + " std::list li;\n" + " char* p = new char[1];\n" + " C c(p);\n" + " li.push_back(c);\n" + " delete[] li.front().m_p;\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + } + + void isAutoDealloc() { + check("void f() {\n" + " char *p = new char[100];" + "}", true); + ASSERT_EQUALS("[test.cpp:2]: (error) Memory leak: p\n", errout_str()); + + check("void f() {\n" + " Fred *fred = new Fred;" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " std::string *str = new std::string;" + "}", true); + TODO_ASSERT_EQUALS("[test.cpp:2]: (error) Memory leak: str\n", "", errout_str()); + + check("class TestType {\n" // #9028 + "public:\n" + " char ca[12];\n" + "};\n" + "void f() {\n" + " TestType *tt = new TestType();\n" + "}", true); + ASSERT_EQUALS("[test.cpp:7]: (error) Memory leak: tt\n", errout_str()); + + check("void f(Bar& b) {\n" // #7622 + " char* data = new char[10];\n" + " b = Bar(*new Foo(data));\n" + "}", /*cpp*/ true); + ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function Foo() should have / configuration\n", errout_str()); + + check("class B {};\n" + " class D : public B {};\n" + " void g() {\n" + " auto d = new D();\n" + " if (d) {}\n" + "}", /*cpp*/ true); + ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: d\n", errout_str()); + + check("struct S {\n" // #12354 + " int i{};\n" + " void f();\n" + "};\n" + "void f(S* p, bool b) {\n" + " if (b)\n" + " p = new S();\n" + " p->f();\n" + "}", /*cpp*/ true); + ASSERT_EQUALS("[test.cpp:9]: (error) Memory leak: p\n", errout_str()); + } + + void realloc1() { + check("void f() {\n" + " void *p = malloc(10);\n" + " void *q = realloc(p, 20);\n" + " free(q)\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void realloc2() { + check("void f() {\n" + " void *p = malloc(10);\n" + " void *q = realloc(p, 20);\n" + "}"); + ASSERT_EQUALS("[test.c:4]: (error) Memory leak: q\n", errout_str()); + } + + void realloc3() { + check("void f() {\n" + " char *p = malloc(10);\n" + " char *q = (char*) realloc(p, 20);\n" + "}"); + ASSERT_EQUALS("[test.c:4]: (error) Memory leak: q\n", errout_str()); + } + + void realloc4() { + check("void f(void *p) {\n" + " void * q = realloc(p, 10);\n" + " if (q == NULL)\n" + " return;\n" + "}"); + ASSERT_EQUALS("[test.c:5]: (error) Memory leak: q\n", errout_str()); + } + + void realloc5() { + // #9292 + check("void * f(void * ptr, size_t size) {\n" + " void *datap = realloc(ptr, size);\n" + " if (size && !datap)\n" + " free(ptr);\n" + " return datap;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9990 + check("void f() {\n" + " void * p1 = malloc(10);\n" + " if (!p1)\n" + " return;\n" + " void * p2 = realloc(p1, 42);\n" + " if (!p2) {\n" + " free(p1);\n" + " return;\n" + " }\n" + " free(p2);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void freopen1() { + check("void f() {\n" + " void *p = fopen(name,a);\n" + " void *q = freopen(name, b, p);\n" + " fclose(q)\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void freopen2() { + check("void f() {\n" + " void *p = fopen(name,a);\n" + " void *q = freopen(name, b, p);\n" + "}"); + ASSERT_EQUALS("[test.c:4]: (error) Resource leak: q\n", errout_str()); + } + + void deallocuse1() { + check("void f(char *p) {\n" + " free(p);\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("[test.c:3]: (error) Dereferencing 'p' after it is deallocated / released\n", errout_str()); + + check("void f(char *p) {\n" + " free(p);\n" + " char c = *p;\n" + "}"); + ASSERT_EQUALS("[test.c:3]: (error) Dereferencing 'p' after it is deallocated / released\n", errout_str()); + } + + void deallocuse3() { + check("void f(struct str *p) {\n" + " free(p);\n" + " p = p->next;\n" + "}"); + ASSERT_EQUALS("[test.c:3]: (error) Dereferencing 'p' after it is deallocated / released\n", errout_str()); + } + + void deallocuse4() { + check("void f(char *p) {\n" + " free(p);\n" + " return p;\n" + "}"); + ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Returning/dereferencing 'p' after it is deallocated / released\n", errout_str()); + + check("void f(char *p) {\n" + " if (!p) free(p);\n" + " return p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(char *p) {\n" + " if (!p) delete p;\n" + " return p;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f(char *p) {\n" + " if (!p) delete [] p;\n" + " return p;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f(void* p) {\n" + " if (a) {\n" + " free(p);\n" + " return;\n" + " }\n" + " g(p);\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void deallocuse5() { // #4018 + check("void f(char *p) {\n" + " free(p), p = 0;\n" + " *p = 0;\n" // <- Make sure pointer info is reset. It is NOT a freed pointer dereference + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void deallocuse6() { // #4034 + check("void f(char *p) {\n" + " free(p);\n" + " x = p = foo();\n" // <- p is not dereferenced + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void deallocuse7() { // #6467, #6469, #6473, #6648 + check("struct Foo { int* ptr; };\n" + "void f(Foo* foo) {\n" + " delete foo->ptr;\n" + " foo->ptr = new Foo;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("struct Foo { int* ptr; };\n" + "void f(Foo* foo) {\n" + " delete foo->ptr;\n" + " x = *foo->ptr;\n" + "}", true); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Dereferencing 'ptr' after it is deallocated / released\n", "", errout_str()); + + check("void parse() {\n" + " struct Buf {\n" + " Buf(uint32_t len) : m_buf(new uint8_t[len]) {}\n" + " ~Buf() { delete[]m_buf; }\n" + " uint8_t *m_buf;\n" + " };\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("struct Foo {\n" + " Foo();\n" + " Foo* ptr;\n" + " void func();\n" + "};\n" + "void bar(Foo* foo) {\n" + " delete foo->ptr;\n" + " foo->ptr = new Foo;\n" + " foo->ptr->func();\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void foo(void (*conv)(char**)) {\n" + " char * ptr=(char*)malloc(42);\n" + " free(ptr);\n" + " (*conv)(&ptr);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void deallocuse8() { // #1765 + check("void f() {\n" + " int *ptr = new int;\n" + " delete(ptr);\n" + " *ptr = 0;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:4]: (error) Dereferencing 'ptr' after it is deallocated / released\n", errout_str()); + } + + void deallocuse9() { + check("void f(Type* p) {\n" // #9781 + " std::shared_ptr sp(p);\n" + " bool b = p->foo();\n" + " return b;\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" // #8635 + " std::vector> array_;\n" + " A* foo() {\n" + " A* a = new A();\n" + " array_.push_back(std::unique_ptr(a));\n" + " return a;\n" + " }\n" + "};\n", /*cpp*/ true); + ASSERT_EQUALS("", errout_str()); + + check("int g(int *p) {\n" // #9838 + " std::unique_ptr temp(p);\n" + " return DoSomething(p);\n" + "}\n" + "int f() {\n" + " return g(new int(3));\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("", errout_str()); + } + + void deallocuse10() { + check("void f(char* p) {\n" + " free(p);\n" + " p[0] = 10;\n" + "}\n"); + ASSERT_EQUALS("[test.c:3]: (error) Dereferencing 'p' after it is deallocated / released\n", errout_str()); + + check("int f(int* p) {\n" + " free(p);\n" + " return p[1];\n" + "}\n"); + ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Returning/dereferencing 'p' after it is deallocated / released\n", errout_str()); + } + + void deallocuse11() { // #8302 + check("int f() {\n" + " int *array = new int[42];\n" + " delete [] array;\n" + " return array[1];" // << + "}", true); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Returning/dereferencing 'array' after it is deallocated / released\n", errout_str()); + + check("int f() {\n" + " int *array = (int*)malloc(40);\n" + " free(array);\n" + " return array[1];" // << + "}"); + ASSERT_EQUALS("[test.c:3] -> [test.c:4]: (error) Returning/dereferencing 'array' after it is deallocated / released\n", errout_str()); + } + + void deallocuse12() { + check("struct foo { int x; }\n" + "void f1(struct foo *f) {\n" + " free(f);\n" + "}\n" + "void f2(struct foo *f, int *out) {\n" + " *out = f->x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void deallocuse13() { + check("void f() {\n" // #9695 + " auto* a = new int[2];\n" + " delete[] a;\n" + " a[1] = 0;\n" + " auto* b = static_cast(malloc(8));\n" + " free(b);\n" + " b[1] = 0;\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:4]: (error) Dereferencing 'a' after it is deallocated / released\n" + "[test.cpp:7]: (error) Dereferencing 'b' after it is deallocated / released\n", + errout_str()); + } + + void deallocuse14() { + check("struct S { void f(); };\n" // #10905 + "void g() {\n" + " S* s = new S;\n" + " delete s;\n" + " s->f();\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:5]: (error) Dereferencing 's' after it is deallocated / released\n", + errout_str()); + + check("void f() {\n" + " int *p = (int*)malloc(4);\n" + " free(p);\n" + " if (*p == 5) {}\n" + "}\n"); + ASSERT_EQUALS("[test.c:4]: (error) Dereferencing 'p' after it is deallocated / released\n", + errout_str()); + + check("int g(int);\n" + "void f(int* p) {\n" + " free(p);\n" + " g(*p);\n" + "}\n" + "int h(int* p) {\n" + " free(p);\n" + " return g(*p);\n" + "}\n"); + ASSERT_EQUALS("[test.c:4]: (error) Dereferencing 'p' after it is deallocated / released\n" + "[test.c:7] -> [test.c:8]: (error) Returning/dereferencing 'p' after it is deallocated / released\n", + errout_str()); + + check("int g(int);\n" + "void f(int* p) {\n" + " free(p);\n" + " g(1 + *p);\n" + "}\n" + "int h(int* p) {\n" + " free(p);\n" + " return g(1 + *p);\n" + "}\n"); + ASSERT_EQUALS("[test.c:4]: (error) Dereferencing 'p' after it is deallocated / released\n" + "[test.c:7] -> [test.c:8]: (error) Returning/dereferencing 'p' after it is deallocated / released\n", + errout_str()); + + check("int g(int, int);\n" + "void f(int* p) {\n" + " free(p);\n" + " g(0, 1 + *p);\n" + "}\n" + "int h(int* p) {\n" + " free(p);\n" + " return g(0, 1 + *p);\n" + "}\n"); + ASSERT_EQUALS("[test.c:4]: (error) Dereferencing 'p' after it is deallocated / released\n" + "[test.c:7] -> [test.c:8]: (error) Returning/dereferencing 'p' after it is deallocated / released\n", + errout_str()); + + check("void f() {\n" + " FOREACH(callables, ());\n" + "}\n"); + ASSERT_EQUALS("[test.c:2]: (information) --check-library: Function FOREACH() should have configuration\n", errout_str()); // don't crash + + check("int f() {\n" // #12321 + " std::invoke([](int i) {\n" + " int* p = (int*)malloc(4);\n" + " *p = 0;\n" + " if (i) {\n" + " free(p);\n" + " return;\n" + " }\n" + " free(p);\n" + " }, 1);\n" + " return 0;\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("", errout_str()); + } + + void deallocuse15() { + check("bool FileExists(const char* filename) {\n" // #12490 + " FILE* f = fopen(filename, \"rb\");\n" + " if (f) fclose(f);\n" + " return f;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("bool f() {\n" // #12590 + " FILE* fd = fopen(\"/foo/bar\", \"w\");\n" + " if (fd == nullptr)\n" + " return false;\n" + " return fclose(fd) == 0;\n" + "}\n", /*cpp*/ true); + 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:3] -> [test.c:6]: (error) Memory pointed to by 'p' is freed twice.\n", errout_str()); + + check( + "void foo(char *p) {\n" + " free(p);\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout_str()); + + check( + "void foo(char *p, char *r) {\n" + " free(p);\n" + " free(r);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo() {\n" + " free(p);\n" + " free(r);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo(char *p) {\n" + " if (x < 3) free(p);\n" + " else { if (x > 9) free(p); }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo(char *p) {\n" + " free(p);\n" + " getNext(&p);\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo(char *p) {\n" + " free(p);\n" + " bar();\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout_str()); + + check( + "void foo(char *p) {\n" + " free(p);\n" + " printf(\"Freed memory at location %x\", p);\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout_str()); + + check( + "void foo(FILE *p) {\n" + " fclose(p);\n" + " fclose(p);\n" + "}"); + ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Resource handle 'p' freed twice.\n", errout_str()); + + check( + "void foo(FILE *p, FILE *r) {\n" + " fclose(p);\n" + " fclose(r);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo(FILE *p) {\n" + " if (x < 3) fclose(p);\n" + " else { if (x > 9) fclose(p); }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo(FILE *p) {\n" + " fclose(p);\n" + " gethandle(&p);\n" + " fclose(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo(FILE *p) {\n" + " fclose(p);\n" + " gethandle();\n" + " fclose(p);\n" + "}"); + ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Resource handle 'p' freed twice.\n", errout_str()); + + check( + "void foo(Data* p) {\n" + " free(p->a);\n" + " free(p->b);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check( + "void f() {\n" + " char *p; p = malloc(100);\n" + " if (x) {\n" + " free(p);\n" + " exit();\n" + " }\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check( + "void f() {\n" + " char *p; p = malloc(100);\n" + " if (x) {\n" + " free(p);\n" + " x = 0;\n" + " }\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("[test.c:4] -> [test.c:7]: (error) Memory pointed to by 'p' is freed twice.\n", errout_str()); + + check( + "void f() {\n" + " char *p; p = do_something();\n" + " free(p);\n" + " p = do_something();\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo(char *p) {\n" + " delete p;\n" + " delete p;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout_str()); + + check( + "void foo(char *p, char *r) {\n" + " delete p;\n" + " delete r;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo(P p) {\n" + " delete p.x;\n" + " delete p;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo(char **p) {\n" + " delete p[0];\n" + " delete p[1];\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo(char *p) {\n" + " delete p;\n" + " getNext(&p);\n" + " delete p;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo(char *p) {\n" + " delete p;\n" + " bar();\n" + " delete p;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout_str()); + + check( + "void foo(char *p) {\n" + " delete[] p;\n" + " delete[] p;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Memory pointed to by 'p' is freed twice.\n", errout_str()); + + check( + "void foo(char *p, char *r) {\n" + " delete[] p;\n" + " delete[] r;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo(char *p) {\n" + " delete[] p;\n" + " getNext(&p);\n" + " delete[] p;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo(char *p) {\n" + " delete[] p;\n" + " bar();\n" + " delete[] p;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout_str()); + + check( + "LineMarker::~LineMarker() {\n" + " delete pxpm;\n" + "}\n" + "LineMarker &LineMarker::operator=(const LineMarker &) {\n" + " delete pxpm;\n" + " pxpm = NULL;\n" + " return *this;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo()\n" + "{\n" + " int* ptr; ptr = NULL;\n" + " try\n" + " {\n" + " ptr = new int(4);\n" + " }\n" + " catch(...)\n" + " {\n" + " delete ptr;\n" + " throw;\n" + " }\n" + " delete ptr;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check( + "int foo()\n" + "{\n" + " int* a; a = new int;\n" + " bool doDelete; doDelete = true;\n" + " if (a != 0)\n" + " {\n" + " doDelete = false;\n" + " delete a;\n" + " }\n" + " if(doDelete)\n" + " delete a;\n" + " return 0;\n" + "}", true); + TODO_ASSERT_EQUALS("", "[test.cpp:8] -> [test.cpp:11]: (error) Memory pointed to by 'a' is freed twice.\n", errout_str()); + + check( + "void foo(int y)\n" + "{\n" + " char * x; x = NULL;\n" + " while(true) {\n" + " x = new char[100];\n" + " if (y++ > 100)\n" + " break;\n" + " delete[] x;\n" + " }\n" + " delete[] x;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo(int y)\n" + "{\n" + " char * x; x = NULL;\n" + " for (int i = 0; i < 10000; i++) {\n" + " x = new char[100];\n" + " delete[] x;\n" + " }\n" + " delete[] x;\n" + "}", true); + TODO_ASSERT_EQUALS("[test.cpp:8]: (error) Memory pointed to by 'x' is freed twice.\n", "", errout_str()); + + check( + "void foo(int y)\n" + "{\n" + " char * x; x = NULL;\n" + " while (isRunning()) {\n" + " x = new char[100];\n" + " delete[] x;\n" + " }\n" + " delete[] x;\n" + "}", true); + TODO_ASSERT_EQUALS("[test.cpp:8]: (error) Memory pointed to by 'x' is freed twice.\n", "", errout_str()); + + check( + "void foo(int y)\n" + "{\n" + " char * x; x = NULL;\n" + " while (isRunning()) {\n" + " x = malloc(100);\n" + " free(x);\n" + " }\n" + " free(x);\n" + "}"); + TODO_ASSERT_EQUALS("[test.c:8]: (error) Memory pointed to by 'x' is freed twice.\n", "", errout_str()); + + check( + "void foo(int y)\n" + "{\n" + " char * x; x = NULL;\n" + " for (;;) {\n" + " x = new char[100];\n" + " if (y++ > 100)\n" + " break;\n" + " delete[] x;\n" + " }\n" + " delete[] x;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check( + "void foo(int y)\n" + "{\n" + " char * x; x = NULL;\n" + " do {\n" + " x = new char[100];\n" + " if (y++ > 100)\n" + " break;\n" + " delete[] x;\n" + " } while (true);\n" + " delete[] x;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check( + "void f()\n" + "{\n" + " char *p; p = 0;\n" + " if (x < 100) {\n" + " p = malloc(10);\n" + " free(p);\n" + " }\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("[test.c:6] -> [test.c:8]: (error) Memory pointed to by 'p' is freed twice.\n", errout_str()); + + check( + "void MyFunction()\n" + "{\n" + " char* data; data = new char[100];\n" + " try\n" + " {\n" + " }\n" + " catch(err)\n" + " {\n" + " delete[] data;\n" + " MyThrow(err);\n" + " }\n" + " delete[] data;\n" + "}\n" + + "void MyThrow(err)\n" + "{\n" + " throw(err);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check( + "void MyFunction()\n" + "{\n" + " char* data; data = new char[100];\n" + " try\n" + " {\n" + " }\n" + " catch(err)\n" + " {\n" + " delete[] data;\n" + " MyExit(err);\n" + " }\n" + " delete[] data;\n" + "}\n" + + "void MyExit(err)\n" + "{\n" + " exit(err);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check( // #6252 + "struct Wrapper {\n" + " Thing* m_thing;\n" + " Wrapper() : m_thing(0) {\n" + " }\n" + " ~Wrapper() {\n" + " delete m_thing;\n" + " }\n" + " void changeThing() {\n" + " delete m_thing;\n" + " m_thing = new Thing;\n" + " }\n" + "};", true); + ASSERT_EQUALS("", errout_str()); + + // #7401 + check("void pCodeLabelDestruct(pCode *pc) {\n" + " free(PCL(pc)->label);\n" + " free(pc);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void doublefree2() { // #3891 + check("void *f(int a) {\n" + " char *p = malloc(10);\n" + " if (a == 2) { free(p); return ((void*)1); }\n" + " free(p);\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void doublefree3() { // #4914 + check("void foo() {\n" + " bool done = false;\n" + " do {\n" + " char *bar = malloc(10)\n" + " if(condition()) {\n" + " free(bar);\n" + " continue;\n" + " }\n" + " done = true;\n" + " free(bar)\n" + " } while(!done);\n" + " return;" + "}" + ); + ASSERT_EQUALS("", errout_str()); + } + + void doublefree4() { + check("void f(char *p) {\n" // #5451 - exit + " if (x) {\n" + " free(p);\n" + " exit(1);\n" + " }\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(void* p, int i) {\n" // #11391 + " if (i)\n" + " goto cleanup;\n" + " free(p);\n" + " exit(0);\n" + "cleanup:\n" + " free(p);\n" + " exit(1);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void doublefree5() { // #5522 + check("void f(char *p) {\n" + " free(p);\n" + " x = (q == p);\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("[test.c:2] -> [test.c:4]: (error) Memory pointed to by 'p' is freed twice.\n", errout_str()); + } + + void doublefree6() { // #7685 + check("void do_wordexp(FILE *f) {\n" + " free(getword(f));\n" + " fclose(f);\n" + "}", /*cpp=*/ false); + ASSERT_EQUALS("", errout_str()); + } + + void doublefree7() { + check("void f(char *p, int x) {\n" + " free(p);\n" + " if (x && (p = malloc(10)))\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(char *p, int x) {\n" + " delete[] p;\n" + " if (x && (p = new char[10]))\n" + " delete[] p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void doublefree8() { + check("void f() {\n" + " int * i = new int;\n" + " std::unique_ptr x(i);\n" + " delete i;\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout_str()); + + check("void f() {\n" + " int * i = new int;\n" + " delete i;\n" + " std::unique_ptr x(i);\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout_str()); + + check("void f() {\n" + " int * i = new int;\n" + " std::unique_ptr x{i};\n" + " delete i;\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout_str()); + + check("void f() {\n" + " int * i = new int;\n" + " std::shared_ptr x(i);\n" + " delete i;\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout_str()); + + check("void f() {\n" + " int * i = new int;\n" + " std::shared_ptr x{i};\n" + " delete i;\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout_str()); + + // Check for use-after-free FP + check("void f() {\n" + " int * i = new int;\n" + " std::shared_ptr x{i};\n" + " *i = 123;\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int * i = new int[1];\n" + " std::unique_ptr x(i);\n" + " delete i;\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Memory pointed to by 'i' is freed twice.\n", errout_str()); + + check("using namespace std;\n" // #9708 + "void f() {\n" + " int* i = new int;\n" + " unique_ptr x(i);\n" + " delete i;\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (error) Memory pointed to by 'i' is freed twice.\n", errout_str()); + } + + void doublefree9() { + check("struct foo {\n" + " int* get(int) { return new int(); }\n" + "};\n" + "void f(foo* b) {\n" + " std::unique_ptr x(b->get(0));\n" + " std::unique_ptr y(b->get(1));\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + } + + void doublefree10() { + check("void f(char* s) {\n" + " char *p = malloc(strlen(s));\n" + " if (p != NULL) {\n" + " strcat(p, s);\n" + " if (strlen(s) != 10)\n" + " free(p); p = NULL;\n" + " }\n" + " if (p != NULL)\n" + " free(p);\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + check("void f(char* s) {\n" + " char *p = malloc(strlen(s));\n" + " if (p != NULL) {\n" + " strcat(p, s);\n" + " if (strlen(s) != 10)\n" + " free(p), p = NULL;\n" + " }\n" + " if (p != NULL)\n" + " free(p);\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + } + + void doublefree11() { + check("void f() {\n" + " void * p = malloc(5);\n" + " void * q = realloc(p, 10);\n" + " if (q == NULL) {\n" + " free(p);\n" + " return;\n" + " }\n" + " free(p);\n" + " if (q == NULL)\n" + " return;\n" + " free(q)\n" + "}"); + ASSERT_EQUALS("[test.c:3] -> [test.c:8]: (error) Memory pointed to by 'p' is freed twice.\n", errout_str()); + } + + void doublefree12() { // #10502 + check("int f(FILE *fp, const bool b) {\n" + " if (b)\n" + " return fclose(fp);\n" + " fclose(fp);\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void doublefree13() { // #11008 + check("struct buf_t { void* ptr; };\n" + "void f() {\n" + " struct buf_t buf;\n" + " if ((buf.ptr = malloc(10)) == NULL)\n" + " return;\n" + " free(buf.ptr);\n" + " if ((buf.ptr = malloc(10)) == NULL)\n" + " return;\n" + " free(buf.ptr);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void doublefree14() { // #9708 + check("using namespace std;\n" + " \n" + "int main()\n" + "{\n" + " int *i = new int;\n" + " unique_ptr x(i);\n" + " delete i;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:7]: (error) Memory pointed to by 'i' is freed twice.\n", errout_str()); + + check("using namespace std;\n" + " \n" + "int main()\n" + "{\n" + " int *i = new int;\n" + " unique_ptr x(i);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void doublefree15() { // #11966 + check("void f(FILE* fp) {\n" + " static_cast(fclose(fp));\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void doublefree16() { // #12236 + check("void f() {\n" + " FILE* f = fopen(\"abc\", \"r\");\n" + " decltype(fclose(f)) y;\n" + " y = fclose(f);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " FILE *fp = fopen(\"abc\", \"r\");\n" + " if (({\n" + " __typeof__(fclose(fp)) r;\n" + " r = (fclose(fp));\n" + " r;\n" + " })) {}\n" + "}\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", + errout_str()); + } + + void exit3() { + check("void f() {\n" + " char *p = malloc(100);\n" + " if (x) {\n" + " free(p);\n" + " ::exit(0);\n" + " }" + " free(p);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " char *p = malloc(100);\n" + " if (x) {\n" + " free(p);\n" + " std::exit(0);\n" + " }" + " free(p);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void functioncall1() { + check("void f(struct S *p) {\n" + " p->x = malloc(10);\n" + " free(p->x);\n" + " p->x = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(s_t s) {\n" // #11061 + " s->p = (char*)malloc(10);\n" + " free((void*)s->p);\n" + " s->p = NULL;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + const Settings s = settingsBuilder().library("std.cfg").build(); + check("struct S {};\n" + "void f(int i, std::vector> &v) {\n" + " if (i < 1) {\n" + " auto s = new S;\n" + " v.push_back(std::unique_ptr(s));\n" + " }\n" + "}\n", /*cpp*/ true, &s); + ASSERT_EQUALS("", errout_str()); // don't crash + + check("void g(size_t len) {\n" // #12365 + " char* b = new char[len + 1]{};\n" + " std::string str = std::string(b);\n" + "}", /*cpp*/ true, &s); + ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: b\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 goto3() { // #11431 + check("void f() {\n" + " int* p = (int*)malloc(2);\n" + " if (!p) {\n" + " p = (int*)malloc(1);\n" + " if (!p)\n" + " goto err;\n" + " }\n" + " free(p);\n" + "err:\n" + " (void)0;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse1() { + check("int f() {\n" + " char *p = NULL;\n" + " if (x) { p = malloc(10); }\n" + " else { return 0; }\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse2() { + check("int f() {\n" + " char *p = NULL;\n" + " if (x) { p = malloc(10); }\n" + " else { return 0; }\n" + "}"); + ASSERT_EQUALS("[test.c:5]: (error) Memory leak: p\n", errout_str()); + } + + void ifelse3() { + check("void f() {\n" + " char *p = malloc(10);\n" + " if (!p) { return; }\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("char * f(size_t size) {" + " void *p = malloc(1);" + " if (!p && size != 0)" + " return NULL;" + " return p;" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " char *p = malloc(10);\n" + " if (p) { } else { return; }\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #3866 - UNLIKELY + check("void f() {\n" + " char *p = malloc(10);\n" + " if (UNLIKELY(!p)) { return; }\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse4() { + check("void f(int x) {\n" + " char *p;\n" + " if (x) { p = malloc(10); }\n" + " if (x) { free(p); }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " char *p;\n" + " if (x) { p = malloc(10); }\n" + " if (!x) { return; }\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse5() { + check("void f() {\n" + " char *p = malloc(10);\n" + " if (!p && x) { p = malloc(10); }\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse6() { // #3370 + check("void f(int x) {\n" + " int *a = malloc(20);\n" + " if (x)\n" + " free(a);\n" + " else\n" + " a = 0;\n" + "}"); + ASSERT_EQUALS("[test.c:6]: (error) Memory leak: a\n", errout_str()); + } + + void ifelse7() { // #5576 + check("void f() {\n" + " int x = malloc(20);\n" + " if (x < 0)\n" // assume negative value indicates its unallocated + " return;\n" + " free(x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse8() { // #5747 + check("int f() {\n" + " int fd = socket(AF_INET, SOCK_PACKET, 0 );\n" + " if (fd == -1)\n" + " return -1;\n" + " return fd;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f() {\n" + " int fd = socket(AF_INET, SOCK_PACKET, 0 );\n" + " if (fd != -1)\n" + " return fd;\n" + " return -1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse9() { // #5273 + check("void f() {\n" + " char *p = malloc(100);\n" + " if (dostuff(p==NULL,0))\n" + " return;\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse10() { // #8794 + check("void f() {\n" + " void *x = malloc(1U);\n" + " if (!(x != NULL))\n" + " return;\n" + " free(x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse11() { // #8365 + check("void f() {\n" + " void *p;\n" + " if (NULL == (p = malloc(4)))\n" + " return;\n" + " free(p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse12() { // #8340 + check("void f(char **p) {\n" + " if ((*p = malloc(4)) == NULL)\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse13() { // #8392 + check("int f(int fd, const char *mode) {\n" + " char *path;\n" + " if (fd == -1 || (path = (char *)malloc(10)) == NULL)\n" + " return 1;\n" + " free(path);\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int fd, const char *mode) {\n" + " char *path;\n" + " if ((path = (char *)malloc(10)) == NULL || fd == -1)\n" + " return 1;\n" // <- memory leak + " free(path);\n" + " return 0;\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4] memory leak", "", errout_str()); + } + + void ifelse14() { // #9130 + check("char* f() {\n" + " char* buf = malloc(10);\n" + " if (buf == (char*)NULL)\n" + " return NULL;\n" + " return buf;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse15() { // #9206 + check("struct SSS { int a; };\n" + "SSS* global_ptr;\n" + "void test_alloc() {\n" + " if ( global_ptr = new SSS()) {}\n" + " return;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("FILE* hFile;\n" + "int openFile( void ) {\n" + " if ((hFile = fopen(\"1.txt\", \"wb\" )) == NULL) return 0;\n" + " return 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse16() { // #9635 + check("void f(void) {\n" + " char *p;\n" + " if(p = malloc(4), p == NULL)\n" + " return;\n" + " free(p);\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(void) {\n" + " char *p, q;\n" + " if(p = malloc(4), q = 1, p == NULL)\n" + " return;\n" + " free(p);\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse17() { + check("int *f() {\n" + " int *p = realloc(nullptr, 10);\n" + " if (!p)\n" + " return NULL;\n" + " return p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int *f() {\n" + " int *p = realloc(nullptr, 10);\n" + " if (!!(!p))\n" + " return NULL;\n" + " return p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse18() { + check("void f() {\n" + " void * p = malloc(10);\n" + " void * q = realloc(p, 20);\n" + " if (q == 0)\n" + " return;\n" + " free(q);\n" + "}"); + ASSERT_EQUALS("[test.c:5]: (error) Memory leak: p\n", errout_str()); + + check("void f() {\n" + " void * p = malloc(10);\n" + " void * q = realloc(p, 20);\n" + " if (q != 0) {\n" + " free(q);\n" + " return;\n" + " } else\n" + " return;\n" + "}"); + ASSERT_EQUALS("[test.c:8]: (error) Memory leak: p\n", errout_str()); + } + + void ifelse19() { + check("void f() {\n" + " static char * a;\n" + " char * b = realloc(a, 10);\n" + " if (!b)\n" + " return;\n" + " a = b;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse20() { + check("void f() {\n" + " if (x > 0)\n" + " void * p1 = malloc(5);\n" + " else\n" + " void * p2 = malloc(2);\n" + " return;\n" + "}"); + ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p1\n" + "[test.c:5]: (error) Memory leak: p2\n", errout_str()); + + check("void f() {\n" + " if (x > 0)\n" + " void * p1 = malloc(5);\n" + " else\n" + " void * p2 = malloc(2);\n" + "}"); + ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p1\n" + "[test.c:5]: (error) Memory leak: p2\n", errout_str()); + } + + void ifelse21() { + check("void f() {\n" + " if (y) {\n" + " void * p;\n" + " if (x > 0)\n" + " p = malloc(5);\n" + " }\n" + " return;\n" + "}"); + ASSERT_EQUALS("[test.c:6]: (error) Memory leak: p\n", errout_str()); + } + + void ifelse22() { // #10187 + check("int f(const char * pathname, int flags) {\n" + " int fd = socket(pathname, flags);\n" + " if (fd >= 0)\n" + " return fd;\n" + " return -1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(const char * pathname, int flags) {\n" + " int fd = socket(pathname, flags);\n" + " if (fd <= -1)\n" + " return -1;\n" + " return fd;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse23() { // #5473 + check("void f() {\n" + " if (FILE* fp = fopen(\"x\", \"r\")) {}\n" + "}\n"); + ASSERT_EQUALS("[test.c:2]: (error) Resource leak: fp\n", errout_str()); + } + + void ifelse24() { // #1733 + const Settings s = settingsBuilder().library("std.cfg").library("posix.cfg").build(); + + check("void f() {\n" + " char* temp = strdup(\"temp.txt\");\n" + " FILE* fp;\n" + " if (NULL == x || NULL == (fp = fopen(temp, \"rt\")))\n" + " return;\n" + "}\n", s); + ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: temp\n" + "[test.cpp:6]: (error) Memory leak: temp\n" + "[test.cpp:6]: (error) Resource leak: fp\n", + errout_str()); + + check("FILE* f() {\n" + " char* temp = strdup(\"temp.txt\");\n" + " FILE* fp = fopen(temp, \"rt\");\n" + " return fp;\n" + "}\n", s); + ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: temp\n", errout_str()); + + check("FILE* f() {\n" + " char* temp = strdup(\"temp.txt\");\n" + " FILE* fp = NULL;\n" + " fopen_s(&fp, temp, \"rt\");\n" + " return fp;\n" + "}\n", s); + ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: temp\n", errout_str()); + + check("void f() {\n" + " char* temp = strdup(\"temp.txt\");\n" + " FILE* fp = fopen(\"a.txt\", \"rb\");\n" + " if (fp)\n" + " freopen(temp, \"rt\", fp);\n" + "}\n", s); + ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: temp\n" + "[test.cpp:6]: (error) Resource leak: fp\n", + errout_str()); + + check("FILE* f() {\n" + " char* temp = strdup(\"temp.txt\");\n" + " return fopen(temp, \"rt\");\n" + "}\n", s); + TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: temp\n", "", errout_str()); + } + + void ifelse25() { // #9966 + check("void f() {\n" + " void *p, *p2;\n" + " if((p2 = p = malloc(10)) == NULL)\n" + " return;\n" + " (void)p;\n" + " free(p2);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse26() { // don't crash + check("union tidi {\n" + " long long ti;\n" + " unsigned int di[2];\n" + "};\n" + "void f(long long val) {\n" + " if (val == ({ union tidi d = {.di = {0x0, 0x80000000}}; d.ti; })) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse27() { + check("struct key { void* p; };\n" + "int f(struct key** handle) {\n" + " struct key* key;\n" + " if (!(key = calloc(1, sizeof(*key))))\n" + " return 0;\n" + " if (!(key->p = malloc(4))) {\n" + " free(key);\n" + " return 0;\n" + " }\n" + " *handle = key;\n" + " return 1;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void ifelse28() { // #11038 + check("char * f(void) {\n" + " char *buf = (char*)malloc(42*sizeof(char));\n" + " char *temp;\n" + " if ((temp = (char*)realloc(buf, 16)) != NULL)\n" + " { buf = temp; }\n" + " return buf;\n" + "}\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 loop2() { + check("void f() {\n" // #11786 + " int* p = (int*)malloc(sizeof(int));\n" + " if (1) {\n" + " while (0) {}\n" + " free(p);\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(node **p) {\n" + " node* n = *p;\n" + " if (n->left == NULL) {\n" + " *p = n->right;\n" + " free(n);\n" + " }\n" + " else if (n->right == NULL) {\n" + " *p = n->left;\n" + " free(n);\n" + " }\n" + " else {\n" + " for (int i = 0; i < 4; ++i) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void mismatchAllocDealloc() { + check("void f() {\n" + " FILE*f=fopen(fname,a);\n" + " free(f);\n" + "}"); + ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Mismatching allocation and deallocation: f\n", errout_str()); + + check("void f() {\n" + " FILE*f=fopen(fname,a);\n" + " free((void*)f);\n" + "}"); + ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Mismatching allocation and deallocation: f\n", errout_str()); + + check("void f() {\n" + " char *cPtr = new char[100];\n" + " delete[] cPtr;\n" + " cPtr = new char[100]('x');\n" + " delete[] cPtr;\n" + " cPtr = new char[100];\n" + " delete cPtr;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:7]: (error) Mismatching allocation and deallocation: cPtr\n", errout_str()); + + check("void f() {\n" + " char *cPtr = new char[100];\n" + " free(cPtr);\n" + "}", true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: cPtr\n", errout_str()); + + check("void f() {\n" + " char *cPtr = new (buf) char[100];\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int * i = new int[1];\n" + " std::unique_ptr x(i);\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: i\n", errout_str()); + + check("void f() {\n" + " int * i = new int;\n" + " std::unique_ptr x(i);\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: i\n", errout_str()); + + check("void f() {\n" + " void* a = malloc(1);\n" + " void* b = freopen(f, p, a);\n" + " free(b);\n" + "}"); + ASSERT_EQUALS("[test.c:2] -> [test.c:3]: (error) Mismatching allocation and deallocation: a\n" + "[test.c:3] -> [test.c:4]: (error) Mismatching allocation and deallocation: b\n", errout_str()); + + check("void f() {\n" + " void* a;\n" + " void* b = realloc(a, 10);\n" + " free(b);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int * i = new int;\n" + " int * j = realloc(i, 2 * sizeof(int));\n" + " delete[] j;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: i\n" + "[test.cpp:3] -> [test.cpp:4]: (error) Mismatching allocation and deallocation: j\n", errout_str()); + + check("static void deleter(int* p) { free(p); }\n" // #11392 + "void f() {\n" + " if (int* p = static_cast(malloc(4))) {\n" + " std::unique_ptr guard(p, &deleter);\n" + " }\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " if (int* p = static_cast(malloc(4))) {\n" + " std::unique_ptr guard(p, &deleter);\n" + " }\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + check("void f(int i) {\n" + " int* a = new int[i] {};\n" + " delete[] a;\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("", errout_str()); + } + + void smartPointerDeleter() { + check("void f() {\n" + " FILE*f=fopen(fname,a);\n" + " std::unique_ptr fp{f};\n" + "}", true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout_str()); + + check("void f() {\n" + " FILE*f=fopen(fname,a);\n" + " std::unique_ptr fp{f, &fclose};\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " FILE*f=fopen(fname,a);\n" + " std::shared_ptr fp{f, &fclose};\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("struct deleter { void operator()(FILE* f) { fclose(f); }};\n" + "void f() {\n" + " FILE*f=fopen(fname,a);\n" + " std::unique_ptr fp{f};\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("int * create();\n" + "void destroy(int * x);\n" + "void f() {\n" + " int x * = create()\n" + " std::unique_ptr xp{x, &destroy()};\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + check("int * create();\n" + "void destroy(int * x);\n" + "void f() {\n" + " int x * = create()\n" + " std::unique_ptr xp(x, &destroy());\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " FILE*f=fopen(fname,a);\n" + " std::shared_ptr fp{f, [](FILE* x) { fclose(x); }};\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " FILE*f=fopen(fname,a);\n" + " std::shared_ptr fp{f, +[](FILE* x) { fclose(x); }};\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " FILE*f=fopen(fname,a);\n" + " std::shared_ptr fp{f, [](FILE* x) { free(f); }};\n" + "}", true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout_str()); + + check("void f() {\n" + " FILE*f=fopen(fname,a);\n" + " std::shared_ptr fp{f, [](FILE* x) {}};\n" + "}", true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (error) Mismatching allocation and deallocation: f\n", errout_str()); + + check("class C;\n" + "void f() {\n" + " C* c = new C{};\n" + " std::shared_ptr a{c, [](C*) {}};\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("class C;\n" + "void f() {\n" + " C* c = new C{};\n" + " std::shared_ptr a{c, [](C* x) { delete x; }};\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("struct DirDeleter {\n" // #12544 + " void operator()(DIR* d) { ::closedir(d); }\n" + "};\n" + "void openDir(DIR* dir) {\n" + " std::shared_ptr dp(dir, DirDeleter());\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:2]: (information) --check-library: Function closedir() should have configuration\n", errout_str()); // don't crash + } + void smartPointerRelease() { + check("void f() {\n" + " int * i = new int;\n" + " std::unique_ptr x(i);\n" + " x.release();\n" + " delete i;\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int * i = new int;\n" + " std::unique_ptr x(i);\n" + " x.release();\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: i\n", errout_str()); + } + + void return1() { + check("int f() {\n" + " char *p = malloc(100);\n" + " return 123;\n" + "}"); + ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout_str()); + } + + void return2() { + check("char *f() {\n" + " char *p = malloc(100);\n" + " return p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void return3() { + check("struct dev * f() {\n" + " struct ABC *abc = malloc(100);\n" + " return &abc->dev;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void return4() { // ticket #3862 + // avoid false positives + check("void f(char *p, int x) {\n" + " if (x==12) {\n" + " free(p);\n" + " throw 1;\n" + " }\n" + " free(p);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f(char *p, int x) {\n" + " if (x==12) {\n" + " delete p;\n" + " throw 1;\n" + " }\n" + " delete p;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f(char *p, int x) {\n" + " if (x==12) {\n" + " delete [] p;\n" + " throw 1;\n" + " }\n" + " delete [] p;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void return5() { // ticket #6397 - conditional allocation/deallocation and conditional return + // avoid false positives + check("void f(int *p, int x) {\n" + " if (x != 0) {\n" + " free(p);\n" + " }\n" + " if (x != 0) {\n" + " return;\n" + " }\n" + " *p = 0;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void return6() { // #8282 + check("std::pair f(size_t n) {\n" + " char* p = (char* )malloc(n);\n" + " return {p, p};\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void return7() { // #9343 + check("uint8_t *f() {\n" + " void *x = malloc(1);\n" + " return (uint8_t *)x;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("uint8_t f() {\n" + " void *x = malloc(1);\n" + " return (uint8_t)x;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: x\n", errout_str()); + + check("void** f() {\n" + " void *x = malloc(1);\n" + " return (void**)x;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void* f() {\n" + " void *x = malloc(1);\n" + " return (long long)x;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void* f() {\n" + " void *x = malloc(1);\n" + " return (void*)(short)x;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: x\n", errout_str()); + + check("void* f() {\n" + " void *x = malloc(1);\n" + " return (mytype)x;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void* f() {\n" // Do not crash + " void *x = malloc(1);\n" + " return (mytype)y;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: x\n", errout_str()); + } + + void return8() { + check("void* f() {\n" + " void *x = malloc(1);\n" + " return (x);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void* f() {\n" + " void *x = malloc(1);\n" + " return ((x));\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void* f() {\n" + " void *x = malloc(1);\n" + " return ((((x))));\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("char* f() {\n" + " void *x = malloc(1);\n" + " return (char*)(x);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void return9() { + check("void* f() {\n" + " void *x = malloc (sizeof (struct alloc));\n" + " return x + sizeof (struct alloc);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void return10() { + check("char f() {\n" // #11758 + " char* p = (char*)malloc(1);\n" + " p[0] = 'x';\n" + " return p[0];\n" + "}"); + ASSERT_EQUALS("[test.c:4]: (error) Memory leak: p\n", errout_str()); + + check("struct S { int f(); };\n" // #11746 + "int g() {\n" + " S* s = new S;\n" + " delete s;\n" + " return s->f();\n" + "}", true); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (error) Returning/dereferencing 's' after it is deallocated / released\n", errout_str()); + + check("int f() {\n" + " int* p = new int(3);\n" + " delete p;\n" + " return *p;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Returning/dereferencing 'p' after it is deallocated / released\n", errout_str()); + } + + void test1() { + check("void f(double*&p) {\n" // 3809 + " p = malloc(0x100);\n" + "}", /*cpp*/ true); + ASSERT_EQUALS("", errout_str()); + + check("void f(int*& p) {\n" // #4400 + " p = (int*)malloc(4);\n" + " p = (int*)malloc(4);\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout_str()); + + check("void f() {\n" + " int* p = (int*)malloc(4);\n" + " int*& r = p;\n" + " r = (int*)malloc(4);\n" + "}\n", /*cpp*/ true); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: p\n", "", errout_str()); + + check("void f() {\n" + " int* p = (int*)malloc(4);\n" + " int*& r = p;\n" + " free(r);\n" + " p = (int*)malloc(4);\n" + "}\n", /*cpp*/ true); + TODO_ASSERT_EQUALS("", "[test.cpp:6]: (error) Memory leak: p\n", 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" + "};", /*cpp*/ true); + ASSERT_EQUALS("", errout_str()); + } + + void test4() { // 5923 - static pointer + check("void f() {\n" + " static char *p;\n" + " if (!p) p = malloc(10);\n" + " if (x) { free(p); p = 0; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void test5() { // unknown type + check("void f() { Fred *p = malloc(10); }", true); + ASSERT_EQUALS("[test.cpp:1]: (error) Memory leak: p\n", errout_str()); + + check("void f() { Fred *p = malloc(10); }", false); + ASSERT_EQUALS("[test.c:1]: (error) Memory leak: p\n", errout_str()); + + check("void f() { Fred *p = new Fred; }", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() { Fred fred = malloc(10); }", true); + ASSERT_EQUALS("", errout_str()); + } + + void throw1() { // 3987 - Execution reach a 'throw' + check("void f() {\n" + " char *p = malloc(10);\n" + " throw 123;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:3]: (error) Memory leak: p\n", errout_str()); + + check("void f() {\n" + " char *p;\n" + " try {\n" + " p = malloc(10);\n" + " throw 123;\n" + " } catch (...) { }\n" + " free(p);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void throw2() { // do not miss ::NS::Except() + check("namespace NS {\n" + " class Except {\n" + " };\n" + "}\n" + "void foo(int i)\n" + "{\n" + " int *pi = new int;\n" + " if (i == 42) {\n" + " delete pi;\n" + " throw ::NS::Except();\n" + " }\n" + " delete pi;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void configuration1() { + // Possible leak => configuration is required for complete analysis + // The user should be able to "white list" and "black list" functions. + + // possible leak. If the function 'x' deallocates the pointer or + // takes the address, there is no leak. + check("void f() {\n" + " char *p = malloc(10);\n" + " x(p);\n" + "}"); + ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function x() should have configuration\n" + "[test.c:4]: (information) --check-library: Function x() should have / configuration\n", + errout_str()); + + check("void cb();\n" // #11190, #11523 + "void f() {\n" + " cb();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void configuration2() { + // possible leak. If the function 'x' deallocates the pointer or + // takes the address, there is no leak. + check("void f() {\n" + " char *p = malloc(10);\n" + " x(&p);\n" + "}"); + ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function x() should have configuration\n" + "[test.c:4]: (information) --check-library: Function x() should have / configuration\n", + errout_str()); + } + + void configuration3() { + const char * code = "void f() {\n" + " char *p = malloc(10);\n" + " if (set_data(p)) { }\n" + "}"; + check(code); + ASSERT_EQUALS("[test.c:4]: (information) --check-library: Function set_data() should have / configuration\n", errout_str()); + check(code, true); + ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function set_data() should have / configuration\n", errout_str()); + + code = "void f() {\n" + " char *p = malloc(10);\n" + " if (set_data(p)) { return; }\n" + "}"; + check(code); + ASSERT_EQUALS("[test.c:3]: (information) --check-library: Function set_data() should have / configuration\n" + "[test.c:4]: (information) --check-library: Function set_data() should have / configuration\n" + , errout_str()); + check(code, true); + ASSERT_EQUALS("[test.cpp:3]: (information) --check-library: Function set_data() should have / configuration\n" + "[test.cpp:4]: (information) --check-library: Function set_data() should have / configuration\n" + , errout_str()); + } + + void configuration4() { + check("void f() {\n" + " char *p = malloc(10);\n" + " int ret = set_data(p);\n" + " return ret;\n" + "}"); + ASSERT_EQUALS("[test.c:4]: (information) --check-library: Function set_data() should have / configuration\n", errout_str()); + } + + void configuration5() { + check("void f() {\n" + " int(i);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " static_assert(1 == sizeof(char), \"test\");\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("", errout_str()); + + check("namespace pal {\n" // #11237 + " struct AutoTimer {};\n" + "}\n" + "int main() {\n" + " pal::AutoTimer();\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("", errout_str()); + + check("struct AutoTimer {};\n" + "int main() {\n" + " AutoTimer();\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #8666 + " asm(\"assembler code\");\n" + " asm volatile(\"assembler code\");\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #11239 + " asm goto(\"assembler code\");\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " FILE* p = fopen(\"abc.txt\", \"r\");\n" + " if (fclose(p) != 0) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S;\n" + "void f(int a, int b, S*& p) {\n" + " if (a == -1) {\n" + " FILE* file = fopen(\"abc.txt\", \"r\");\n" + " }\n" + " if (b) {\n" + " void* buf = malloc(10);\n" + " p = reinterpret_cast(buf);\n" + " }\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("[test.cpp:5]: (error) Resource leak: file\n", errout_str()); + } + + void configuration6() { + check("void f() {}\n" // #11198 + "void g() {\n" + " f();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::function cb) {\n" // #11189 + " cb();\n" + "}\n" + "void g(void (*cb)()) {\n" + " cb();\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("", errout_str()); + } + + void ptrptr() { + check("void f() {\n" + " char **p = malloc(10);\n" + "}"); + ASSERT_EQUALS("[test.c:3]: (error) Memory leak: p\n", errout_str()); + } + + void nestedAllocation() { + check("void QueueDSMCCPacket(unsigned char *data, int length) {\n" + " unsigned char *dataCopy = malloc(length * sizeof(unsigned char));\n" + " m_dsmccQueue.enqueue(new DSMCCPacket(dataCopy));\n" + "}", true); + ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function DSMCCPacket() should have / configuration\n", errout_str()); + + check("void QueueDSMCCPacket(unsigned char *data, int length) {\n" + " unsigned char *dataCopy = malloc(length * sizeof(unsigned char));\n" + " m_dsmccQueue.enqueue(new DSMCCPacket(somethingunrelated));\n" + "}", true); + ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: dataCopy\n", errout_str()); + + check("void f() {\n" + " char *buf = new char[1000];\n" + " clist.push_back(new (std::nothrow) C(buf));\n" + "}", true); + ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function C() should have / configuration\n", errout_str()); + } + + void testKeywords() { + check("int main(int argc, char **argv) {\n" + " double *new = malloc(1*sizeof(double));\n" + " free(new);\n" + " return 0;\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + } + + void inlineFunction() { + check("int test() {\n" + " char *c;\n" + " int ret() {\n" + " free(c);\n" + " return 0;\n" + " }\n" + " c = malloc(128);\n" + " return ret();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + // #8262 + void smartPtrInContainer() { + check("std::list< std::shared_ptr > mList;\n" + "void test(){\n" + " int *pt = new int(1);\n" + " mList.push_back(std::shared_ptr(pt));\n" + "}\n", + true + ); + ASSERT_EQUALS("", errout_str()); + } + + void functionCallCastConfig() { // #9652 + constexpr char xmldata[] = "\n" + "\n" + " \n" + " false\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + const Settings settingsFunctionCall = settingsBuilder(settings).libraryxml(xmldata, sizeof(xmldata)).build(); + + check("void test_func()\n" + "{\n" + " char * buf = malloc(4);\n" + " free_func((void *)(1), buf);\n" + "}", settingsFunctionCall); + ASSERT_EQUALS("[test.cpp:5]: (information) --check-library: Function free_func() should have / configuration\n", errout_str()); + + check("void g(void*);\n" + "void h(int, void*);\n" + "void f1() {\n" + " int* p = new int;\n" + " g(static_cast(p));\n" + "}\n" + "void f2() {\n" + " int* p = new int;\n" + " h(1, static_cast(p));\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("[test.cpp:6]: (information) --check-library: Function g() should have / configuration\n" + "[test.cpp:10]: (information) --check-library: Function h() should have / configuration\n", + errout_str()); + } + + void functionCallLeakIgnoreConfig() { // #7923 + constexpr char xmldata[] = "\n" + "\n" + " \n" + " \n" + " false\n" + " \n" + " \n" + "\n"; + const Settings settingsLeakIgnore = settingsBuilder().libraryxml(xmldata, sizeof(xmldata)).build(); + check("void f() {\n" + " double* a = new double[1024];\n" + " SomeClass::someMethod(a);\n" + "}\n", settingsLeakIgnore); + ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: a\n", errout_str()); + + check("void bar(int* p) {\n" + " if (p)\n" + " free(p);\n" + "}\n" + "void f() {\n" + " int* p = malloc(4);\n" + " bar(p);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int n) {\n" + " char* p = new char[n];\n" + " v.push_back(p);\n" + "}\n", /*cpp*/ true); + ASSERT_EQUALS("[test.cpp:4]: (information) --check-library: Function unknown::push_back() should have / configuration\n", errout_str()); + } +}; + +REGISTER_TEST(TestLeakAutoVar) + +class TestLeakAutoVarRecursiveCountLimit : public TestFixture { +public: + TestLeakAutoVarRecursiveCountLimit() : TestFixture("TestLeakAutoVarRecursiveCountLimit") {} + +private: + const Settings settings = settingsBuilder().library("std.cfg").checkLibrary().build(); + +#define checkP(...) checkP_(__FILE__, __LINE__, __VA_ARGS__) + void checkP_(const char* file, int line, const char code[], bool cpp = false) { + std::vector files(1, cpp?"test.cpp":"test.c"); + Tokenizer tokenizer(settings, *this); + PreprocessorHelper::preprocess(code, files, tokenizer, *this); + + // Tokenizer.. + ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); + + // Check for leaks.. + runChecks(tokenizer, this); + } + + void run() override { + TEST_CASE(recursiveCountLimit); // #5872 #6157 #9097 + } + + void recursiveCountLimit() { // #5872 #6157 #9097 + ASSERT_THROW_INTERNAL_EQUALS(checkP("#define ONE else if (0) { }\n" + "#define TEN ONE ONE ONE ONE ONE ONE ONE ONE ONE ONE\n" + "#define HUN TEN TEN TEN TEN TEN TEN TEN TEN TEN TEN\n" + "#define THOU HUN HUN HUN HUN HUN HUN HUN HUN HUN HUN\n" + "void foo() {\n" + " if (0) { }\n" + " THOU THOU\n" + "}"), LIMIT, "Internal limit: CheckLeakAutoVar::checkScope() Maximum recursive count of 1000 reached."); + ASSERT_NO_THROW(checkP("#define ONE if (0) { }\n" + "#define TEN ONE ONE ONE ONE ONE ONE ONE ONE ONE ONE\n" + "#define HUN TEN TEN TEN TEN TEN TEN TEN TEN TEN TEN\n" + "#define THOU HUN HUN HUN HUN HUN HUN HUN HUN HUN HUN\n" + "void foo() {\n" + " if (0) { }\n" + " THOU THOU\n" + "}")); + } +}; + +#if !defined(__MINGW32__) +// TODO: this crashes with a stack overflow for MinGW in the CI +REGISTER_TEST(TestLeakAutoVarRecursiveCountLimit) +#endif + +class TestLeakAutoVarStrcpy : public TestFixture { +public: + TestLeakAutoVarStrcpy() : TestFixture("TestLeakAutoVarStrcpy") {} + +private: + const Settings settings = settingsBuilder().library("std.cfg").checkLibrary().build(); + + void check_(const char* file, int line, const char code[]) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check for leaks.. + runChecks(tokenizer, this); + } + + void run() override { + TEST_CASE(returnedValue); // #9298 + TEST_CASE(deallocuse2); + TEST_CASE(fclose_false_positive); // #9575 + TEST_CASE(strcpy_false_negative); + TEST_CASE(doubleFree); + TEST_CASE(memleak_std_string); + } + + void returnedValue() { // #9298 + check("char *m;\n" + "void strcpy_returnedvalue(const char* str)\n" + "{\n" + " char* ptr = new char[strlen(str)+1];\n" + " m = strcpy(ptr, str);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void deallocuse2() { + check("void f(char *p) {\n" + " free(p);\n" + " strcpy(a, p);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Dereferencing 'p' after it is deallocated / released\n", errout_str()); + + check("void f(char *p, const char *q) {\n" // #11665 + " free(p);\n" + " strcpy(p, q);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Dereferencing 'p' after it is deallocated / released\n", + errout_str()); + + check("void f(char *p) {\n" // #3041 - assigning pointer when it's used + " free(p);\n" + " strcpy(a, p=b());\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void fclose_false_positive() { // #9575 + check("int f(FILE *fp) { return fclose(fp); }"); + ASSERT_EQUALS("", errout_str()); + } + + void strcpy_false_negative() { // #12289 + check("void f() {\n" + " char* buf = new char[12];\n" + " strcpy(buf, \"123\");\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Memory leak: buf\n", errout_str()); + } + + void doubleFree() { + check("void f(char* p) {\n" + " free(p);\n" + " printf(\"%s\", p = strdup(\"abc\"));\n" + " free(p);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void memleak_std_string() { + check("struct S {\n" // #12354 + " std::string s;\n" + " void f();\n" + "};\n" + "void f(S* p, bool b) {\n" + " if (b)\n" + " p = new S();\n" + " p->f();\n" + "}"); + ASSERT_EQUALS("[test.cpp:9]: (error) Memory leak: p\n", errout_str()); + + check("class B { std::string s; };\n" // #12062 + "class D : public B {};\n" + "void g() {\n" + " auto d = new D();\n" + " if (d) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: d\n", errout_str()); + } +}; + +REGISTER_TEST(TestLeakAutoVarStrcpy) + + +class TestLeakAutoVarWindows : public TestFixture { +public: + TestLeakAutoVarWindows() : TestFixture("TestLeakAutoVarWindows") {} + +private: + const Settings settings = settingsBuilder().library("windows.cfg").build(); + + void check_(const char* file, int line, const char code[]) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code, false), file, line); + + // Check for leaks.. + runChecks(tokenizer, this); + } + + void run() override { + TEST_CASE(heapDoubleFree); + } + + void heapDoubleFree() { + check("void f() {" + " HANDLE MyHeap = HeapCreate(0, 0, 0);" + " int *a = HeapAlloc(MyHeap, 0, sizeof(int));" + " int *b = HeapAlloc(MyHeap, 0, sizeof(int));" + " HeapFree(MyHeap, 0, a);" + " HeapFree(MyHeap, 0, b);" + " HeapDestroy(MyHeap);" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {" + " int *a = HeapAlloc(GetProcessHeap(), 0, sizeof(int));" + " int *b = HeapAlloc(GetProcessHeap(), 0, sizeof(int));" + " HeapFree(GetProcessHeap(), 0, a);" + " HeapFree(GetProcessHeap(), 0, b);" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {" + " HANDLE MyHeap = HeapCreate(0, 0, 0);" + " int *a = HeapAlloc(MyHeap, 0, sizeof(int));" + " int *b = HeapAlloc(MyHeap, 0, sizeof(int));" + " HeapFree(MyHeap, 0, a);" + " HeapDestroy(MyHeap);" + "}"); + ASSERT_EQUALS("[test.c:1]: (error) Memory leak: b\n", errout_str()); + + check("void f() {" + " HANDLE MyHeap = HeapCreate(0, 0, 0);" + " int *a = HeapAlloc(MyHeap, 0, sizeof(int));" + " int *b = HeapAlloc(MyHeap, 0, sizeof(int));" + " HeapFree(MyHeap, 0, a);" + " HeapFree(MyHeap, 0, b);" + "}"); + ASSERT_EQUALS("[test.c:1]: (error) Resource leak: MyHeap\n", errout_str()); + + check("void f() {" + " HANDLE MyHeap = HeapCreate(0, 0, 0);" + " int *a = HeapAlloc(MyHeap, 0, sizeof(int));" + " int *b = HeapAlloc(MyHeap, 0, sizeof(int));" + " HeapFree(MyHeap, 0, a);" + "}"); + ASSERT_EQUALS("[test.c:1]: (error) Resource leak: MyHeap\n" + "[test.c:1]: (error) Memory leak: b\n", + errout_str()); + } +}; + +REGISTER_TEST(TestLeakAutoVarWindows) + +class TestLeakAutoVarPosix : public TestFixture { +public: + TestLeakAutoVarPosix() : TestFixture("TestLeakAutoVarPosix") {} + +private: + const Settings settings = settingsBuilder().library("std.cfg").library("posix.cfg").build(); + + void check_(const char* file, int line, const char code[]) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check for leaks.. + runChecks(tokenizer, this); + } + + void run() override { + TEST_CASE(memleak_getline); + } + + void memleak_getline() { + check("void f(std::ifstream &is) {\n" // #12297 + " std::string str;\n" + " if (getline(is, str, 'x').good()) {};\n" + " if (!getline(is, str, 'x').good()) {};\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } +}; + +REGISTER_TEST(TestLeakAutoVarPosix) diff --git a/cppcheck-2.14.0/test/testlibrary.cpp b/cppcheck-2.14.0/test/testlibrary.cpp new file mode 100644 index 00000000..e6cf6f2c --- /dev/null +++ b/cppcheck-2.14.0/test/testlibrary.cpp @@ -0,0 +1,1077 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "library.h" +#include "settings.h" +#include "standards.h" +#include "token.h" +#include "tokenlist.h" + +#include +#include +#include +#include +#include +#include + +#include "xml.h" + +#define ASSERT_EQ(expected, actual) ASSERT(expected == actual) + +class TestLibrary : public TestFixture { +public: + TestLibrary() : TestFixture("TestLibrary") {} + +private: + const Settings settings; + + void run() override { + TEST_CASE(isCompliantValidationExpression); + TEST_CASE(empty); + TEST_CASE(function); + TEST_CASE(function_match_scope); + TEST_CASE(function_match_args); + TEST_CASE(function_match_args_default); + TEST_CASE(function_match_var); + TEST_CASE(function_arg); + TEST_CASE(function_arg_any); + TEST_CASE(function_arg_variadic); + TEST_CASE(function_arg_direction); + TEST_CASE(function_arg_valid); + TEST_CASE(function_arg_minsize); + TEST_CASE(function_namespace); + TEST_CASE(function_method); + TEST_CASE(function_baseClassMethod); // calling method in base class + TEST_CASE(function_warn); + TEST_CASE(memory); + TEST_CASE(memory2); // define extra "free" allocation functions + TEST_CASE(memory3); + TEST_CASE(resource); + TEST_CASE(podtype); + TEST_CASE(container); + TEST_CASE(version); + TEST_CASE(loadLibErrors); + TEST_CASE(loadLibCombinations); + } + + static bool loadxmldata(Library &lib, const char xmldata[], std::size_t len) + { + tinyxml2::XMLDocument doc; + return (tinyxml2::XML_SUCCESS == doc.Parse(xmldata, len)) && (lib.load(doc).errorcode == Library::ErrorCode::OK); + } + + void isCompliantValidationExpression() const { + ASSERT_EQUALS(true, Library::isCompliantValidationExpression("-1")); + ASSERT_EQUALS(true, Library::isCompliantValidationExpression("1")); + ASSERT_EQUALS(true, Library::isCompliantValidationExpression("1:")); + ASSERT_EQUALS(true, Library::isCompliantValidationExpression(":1")); + ASSERT_EQUALS(true, Library::isCompliantValidationExpression("-1,42")); + ASSERT_EQUALS(true, Library::isCompliantValidationExpression("-1,-42")); + ASSERT_EQUALS(true, Library::isCompliantValidationExpression("-1.0:42.0")); + ASSERT_EQUALS(true, Library::isCompliantValidationExpression("1.175494e-38:3.402823e+38")); + ASSERT_EQUALS(true, Library::isCompliantValidationExpression("1.175494e-38,3.402823e+38")); + ASSERT_EQUALS(true, Library::isCompliantValidationExpression("1.175494e-38:")); + ASSERT_EQUALS(true, Library::isCompliantValidationExpression(":1.175494e-38")); + ASSERT_EQUALS(true, Library::isCompliantValidationExpression(":42.0")); + ASSERT_EQUALS(true, Library::isCompliantValidationExpression("!42.0")); + + // Robustness tests + ASSERT_EQUALS(false, Library::isCompliantValidationExpression(nullptr)); + ASSERT_EQUALS(false, Library::isCompliantValidationExpression("x")); + ASSERT_EQUALS(false, Library::isCompliantValidationExpression("!")); + ASSERT_EQUALS(false, Library::isCompliantValidationExpression("")); + } + + void empty() const { + // Reading an empty library file is considered to be OK + constexpr char xmldata[] = "\n"; + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + ASSERT(library.functions.empty()); + } + + void function() const { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " false\n" + " \n" + ""; + + const char code[] = "foo();"; + SimpleTokenList tokenList(code); + tokenList.front()->next()->astOperand1(tokenList.front()); + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + ASSERT_EQUALS(library.functions.size(), 1U); + ASSERT(library.functions.at("foo").argumentChecks.empty()); + ASSERT(library.isnotnoreturn(tokenList.front())); + } + + void function_match_scope() const { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " " + " \n" + ""; + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + { + const char code[] = "fred.foo(123);"; // <- wrong scope, not library function + const SimpleTokenList tokenList(code); + + ASSERT(library.isNotLibraryFunction(tokenList.front()->tokAt(2))); + } + { + const char code[] = "Fred::foo(123);"; // <- wrong scope, not library function + const SimpleTokenList tokenList(code); + + ASSERT(library.isNotLibraryFunction(tokenList.front()->tokAt(2))); + } + } + + void function_match_args() const { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " " + " \n" + ""; + + TokenList tokenList(&settings); + std::istringstream istr("foo();"); // <- too few arguments, not library function + ASSERT(tokenList.createTokens(istr, Standards::Language::CPP)); + Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous()); + tokenList.createAst(); + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + ASSERT(library.isNotLibraryFunction(tokenList.front())); + } + + void function_match_args_default() const { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " " + " " + " \n" + ""; + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + + { + TokenList tokenList(&settings); + std::istringstream istr("foo();"); // <- too few arguments, not library function + ASSERT(tokenList.createTokens(istr, Standards::Language::CPP)); + Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous()); + tokenList.createAst(); + + ASSERT(library.isNotLibraryFunction(tokenList.front())); + } + { + TokenList tokenList(&settings); + std::istringstream istr("foo(a);"); // <- library function + ASSERT(tokenList.createTokens(istr, Standards::Language::CPP)); + Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous()); + tokenList.createAst(); + + ASSERT(!library.isNotLibraryFunction(tokenList.front())); + } + { + TokenList tokenList(&settings); + std::istringstream istr("foo(a, b);"); // <- library function + ASSERT(tokenList.createTokens(istr, Standards::Language::CPP)); + Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous()); + tokenList.createAst(); + + ASSERT(!library.isNotLibraryFunction(tokenList.front())); + } + { + TokenList tokenList(&settings); + std::istringstream istr("foo(a, b, c);"); // <- too much arguments, not library function + ASSERT(tokenList.createTokens(istr, Standards::Language::CPP)); + Token::createMutualLinks(tokenList.front()->next(), tokenList.back()->previous()); + tokenList.createAst(); + + ASSERT(library.isNotLibraryFunction(tokenList.front())); + } + } + + void function_match_var() const { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " " + " \n" + ""; + + const char code[] = "Fred foo(123);"; // <- Variable declaration, not library function + SimpleTokenList tokenList(code); + tokenList.front()->next()->astOperand1(tokenList.front()); + tokenList.front()->next()->varId(1); + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + ASSERT(library.isNotLibraryFunction(tokenList.front()->next())); + } + + void function_arg() const { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + ASSERT_EQUALS(0, library.functions["foo"].argumentChecks[1].notuninit); + ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[2].notnull); + ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[3].formatstr); + ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[4].strz); + ASSERT_EQUALS(false, library.functions["foo"].argumentChecks[4].optional); + ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[5].notbool); + ASSERT_EQUALS(true, library.functions["foo"].argumentChecks[5].optional); + } + + void function_arg_any() const { + constexpr char xmldata[] = "\n" + "\n" + "\n" + " \n" + "\n" + ""; + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + ASSERT_EQUALS(0, library.functions["foo"].argumentChecks[-1].notuninit); + } + + void function_arg_variadic() const { + constexpr char xmldata[] = "\n" + "\n" + "\n" + " \n" + " \n" + "\n" + ""; + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + ASSERT_EQUALS(0, library.functions["foo"].argumentChecks[-1].notuninit); + + const char code[] = "foo(a,b,c,d,e);"; + SimpleTokenList tokenList(code); + tokenList.front()->next()->astOperand1(tokenList.front()); + + ASSERT_EQUALS(false, library.isuninitargbad(tokenList.front(), 1)); + ASSERT_EQUALS(true, library.isuninitargbad(tokenList.front(), 2)); + ASSERT_EQUALS(true, library.isuninitargbad(tokenList.front(), 3)); + ASSERT_EQUALS(true, library.isuninitargbad(tokenList.front(), 4)); + } + + void function_arg_direction() const { + constexpr char xmldata[] = "\n" + "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + "\n" + ""; + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + + const char code[] = "foo(a,b,c,d);"; + SimpleTokenList tokenList(code); + tokenList.front()->next()->astOperand1(tokenList.front()); + + ASSERT(Library::ArgumentChecks::Direction::DIR_IN == library.getArgDirection(tokenList.front(), 1)); + ASSERT(Library::ArgumentChecks::Direction::DIR_OUT == library.getArgDirection(tokenList.front(), 2)); + ASSERT(Library::ArgumentChecks::Direction::DIR_INOUT == library.getArgDirection(tokenList.front(), 3)); + ASSERT(Library::ArgumentChecks::Direction::DIR_UNKNOWN == library.getArgDirection(tokenList.front(), 4)); + } + + void function_arg_valid() const { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " 1:\n" + " -7:0\n" + " 1:5,8\n" + " -1,5\n" + " :1,5\n" + " 1.5:\n" + " -6.7:-5.5,-3.3:-2.7\n" + " 0.0:\n" + " :2.0\n" + " 0.0\n" + " !0.0\n" + " \n" + ""; + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + + const char code[] = "foo(a,b,c,d,e,f,g,h,i,j,k);"; + SimpleTokenList tokenList(code); + tokenList.front()->next()->astOperand1(tokenList.front()); + + // 1- + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 1, -10)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 1, -10.0)); + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 1, 0)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 1, 0.0)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 1, 1)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 1, 1.0)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 1, 10)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 1, 10.0)); + + // -7-0 + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 2, -10)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, -10.0)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, -7.5)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, -7.1)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 2, -7)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 2, -7.0)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 2, -3)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 2, -3.0)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 2, -3.5)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 2, 0)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 2, 0.0)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, 0.5)); + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 2, 1)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 2, 1.0)); + + // 1-5,8 + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 3, 0)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 0.0)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 3, 1)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 3, 1.0)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 3, 3)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 3, 3.0)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 3, 5)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 3, 5.0)); + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 3, 6)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 6.0)); + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 3, 7)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 7.0)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 3, 8)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 8.0)); + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 3, 9)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 3, 9.0)); + + // -1,5 + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 4, -10)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 4, -10.0)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 4, -1)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 4, -1.0)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 4, 5.000001)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 4, 5.5)); + + // :1,5 + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 5, -10)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 5, -10.0)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 5, 1)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 5, 1.0)); + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 5, 2)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 5, 2.0)); + + // 1.5: + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 6, 0)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 6, 0.0)); + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 6, 1)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 6, 1.499999)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 6, 1.5)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 6, 2)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 6, 10)); + + // -6.7:-5.5,-3.3:-2.7 + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, -7)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -7.0)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -6.7000001)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -6.7)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 7, -6)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -6.0)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -5.5)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -5.4999999)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -3.3000001)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -3.3)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 7, -3)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -3.0)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 7, -2.7)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -2.6999999)); + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, -2)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, -2.0)); + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, 0)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, 0.0)); + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, 3)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, 3.0)); + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 7, 6)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 7, 6.0)); + + // 0.0: + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 8, -1)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 8, -1.0)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 8, -0.00000001)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 8, 0)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 8, 0.0)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 8, 0.000000001)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 8, 1)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 8, 1.0)); + + // :2.0 + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 9, -1)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 9, -1.0)); + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 9, 2)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 9, 2.0)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 9, 2.00000001)); + ASSERT_EQUALS(false, library.isIntArgValid(tokenList.front(), 9, 200)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 9, 200.0)); + + // 0.0 + ASSERT_EQUALS(true, library.isIntArgValid(tokenList.front(), 10, 0)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 10, 0.0)); + + // ! 0.0 + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 11, -0.42)); + ASSERT_EQUALS(false, library.isFloatArgValid(tokenList.front(), 11, 0.0)); + ASSERT_EQUALS(true, library.isFloatArgValid(tokenList.front(), 11, 0.42)); + } + + void function_arg_minsize() const { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + + const char code[] = "foo(a,b,c,d,e);"; + SimpleTokenList tokenList(code); + tokenList.front()->next()->astOperand1(tokenList.front()); + + // arg1: type=strlen arg2 + const std::vector *minsizes = library.argminsizes(tokenList.front(),1); + ASSERT_EQUALS(true, minsizes != nullptr); + ASSERT_EQUALS(1U, minsizes ? minsizes->size() : 1U); + if (minsizes && minsizes->size() == 1U) { + const Library::ArgumentChecks::MinSize &m = minsizes->front(); + ASSERT_EQUALS(true, Library::ArgumentChecks::MinSize::Type::STRLEN == m.type); + ASSERT_EQUALS(2, m.arg); + } + + // arg2: type=argvalue arg3 + minsizes = library.argminsizes(tokenList.front(), 2); + ASSERT_EQUALS(true, minsizes != nullptr); + ASSERT_EQUALS(1U, minsizes ? minsizes->size() : 1U); + if (minsizes && minsizes->size() == 1U) { + const Library::ArgumentChecks::MinSize &m = minsizes->front(); + ASSERT_EQUALS(true, Library::ArgumentChecks::MinSize::Type::ARGVALUE == m.type); + ASSERT_EQUALS(3, m.arg); + } + + // arg4: type=value + minsizes = library.argminsizes(tokenList.front(), 4); + ASSERT_EQUALS(true, minsizes != nullptr); + ASSERT_EQUALS(1U, minsizes ? minsizes->size() : 1U); + if (minsizes && minsizes->size() == 1U) { + const Library::ArgumentChecks::MinSize &m = minsizes->front(); + ASSERT(Library::ArgumentChecks::MinSize::Type::VALUE == m.type); + ASSERT_EQUALS(500, m.value); + ASSERT_EQUALS("", m.baseType); + } + + // arg5: type=value + minsizes = library.argminsizes(tokenList.front(), 5); + 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(Library::ArgumentChecks::MinSize::Type::VALUE == m.type); + ASSERT_EQUALS(4, m.value); + ASSERT_EQUALS("int", m.baseType); + } + } + + void function_namespace() const { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " false\n" + " \n" + ""; + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + ASSERT_EQUALS(library.functions.size(), 2U); + ASSERT(library.functions.at("Foo::foo").argumentChecks.empty()); + ASSERT(library.functions.at("bar").argumentChecks.empty()); + + { + const char code[] = "Foo::foo();"; + const SimpleTokenList tokenList(code); + ASSERT(library.isnotnoreturn(tokenList.front()->tokAt(2))); + } + + { + const char code[] = "bar();"; + const SimpleTokenList tokenList(code); + ASSERT(library.isnotnoreturn(tokenList.front())); + } + } + + void function_method() { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " false\n" + " \n" + ""; + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + ASSERT_EQUALS(library.functions.size(), 1U); + + { + SimpleTokenizer tokenizer(settings, *this); + const char code[] = "CString str; str.Format();"; + ASSERT(tokenizer.tokenize(code)); + ASSERT(library.isnotnoreturn(Token::findsimplematch(tokenizer.tokens(), "Format"))); + } + + { + SimpleTokenizer tokenizer(settings, *this); + const char code[] = "HardDrive hd; hd.Format();"; + ASSERT(tokenizer.tokenize(code)); + ASSERT(!library.isnotnoreturn(Token::findsimplematch(tokenizer.tokens(), "Format"))); + } + } + + void function_baseClassMethod() { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " \n" + " \n" + ""; + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + + { + SimpleTokenizer tokenizer(settings, *this); + const char code[] = "struct X : public Base { void dostuff() { f(0); } };"; + ASSERT(tokenizer.tokenize(code)); + ASSERT(library.isnullargbad(Token::findsimplematch(tokenizer.tokens(), "f"),1)); + } + + { + SimpleTokenizer tokenizer(settings, *this); + const char code[] = "struct X : public Base { void dostuff() { f(1,2); } };"; + ASSERT(tokenizer.tokenize(code)); + ASSERT(!library.isnullargbad(Token::findsimplematch(tokenizer.tokens(), "f"),1)); + } + } + + void function_warn() const { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " Message\n" + " \n" + " \n" + " \n" + " \n" + ""; + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + + const char code[] = "a(); b();"; + const SimpleTokenList tokenList(code); + + const Library::WarnInfo* a = library.getWarnInfo(tokenList.front()); + const Library::WarnInfo* b = library.getWarnInfo(tokenList.front()->tokAt(4)); + + ASSERT_EQUALS(2, library.functionwarn.size()); + ASSERT(a && b); + if (a && b) { + ASSERT_EQUALS("Message", a->message); + ASSERT_EQUALS_ENUM(Severity::style, a->severity); + ASSERT_EQUALS(Standards::C99, a->standards.c); + ASSERT_EQUALS(Standards::CPP03, a->standards.cpp); + + ASSERT_EQUALS("Obsolescent function 'b' called. It is recommended to use 'c', 'd' or 'e' instead.", b->message); + ASSERT_EQUALS_ENUM(Severity::performance, b->severity); + ASSERT_EQUALS(Standards::C89, b->standards.c); + ASSERT_EQUALS(Standards::CPP11, b->standards.cpp); + } + } + + void memory() const { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " CreateX\n" + " DeleteX\n" + " \n" + ""; + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + ASSERT(library.functions.empty()); + + const Library::AllocFunc* af = library.getAllocFuncInfo("CreateX"); + ASSERT(af && af->arg == -1); + ASSERT(Library::ismemory(af)); + ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX")); + const Library::AllocFunc* df = library.getDeallocFuncInfo("DeleteX"); + ASSERT(df && df->arg == 1); + } + void memory2() const { + constexpr char xmldata1[] = "\n" + "\n" + " \n" + " malloc\n" + " free\n" + " \n" + ""; + constexpr char xmldata2[] = "\n" + "\n" + " \n" + " foo\n" + " free\n" + " \n" + ""; + + Library library; + ASSERT_EQUALS(true, loadxmldata(library, xmldata1, sizeof(xmldata1))); + ASSERT_EQUALS(true, loadxmldata(library, xmldata2, sizeof(xmldata2))); + + ASSERT_EQUALS(library.deallocId("free"), library.allocId("malloc")); + ASSERT_EQUALS(library.deallocId("free"), library.allocId("foo")); + } + void memory3() const { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " CreateX\n" + " DeleteX\n" + " \n" + ""; + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + ASSERT(library.functions.empty()); + + const Library::AllocFunc* af = library.getAllocFuncInfo("CreateX"); + ASSERT(af && af->arg == 5 && !af->initData); + const Library::AllocFunc* df = library.getDeallocFuncInfo("DeleteX"); + ASSERT(df && df->arg == 2); + } + + void resource() const { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " CreateX\n" + " DeleteX\n" + " \n" + ""; + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + ASSERT(library.functions.empty()); + + ASSERT(Library::isresource(library.allocId("CreateX"))); + ASSERT_EQUALS(library.allocId("CreateX"), library.deallocId("DeleteX")); + } + + void podtype() const { + { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + ""; + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + // s8 + { + const Library::PodType * const type = library.podtype("s8"); + ASSERT_EQUALS(true, type != nullptr); + if (type) { + ASSERT_EQUALS(1U, type->size); + ASSERT_EQUALS('s', type->sign); + } + } + // u8 + { + const Library::PodType * const type = library.podtype("u8"); + ASSERT_EQUALS(true, type != nullptr); + if (type) { + ASSERT_EQUALS(1U, type->size); + ASSERT_EQUALS('u', type->sign); + } + } + // u16 + { + const Library::PodType * const type = library.podtype("u16"); + ASSERT_EQUALS(true, type != nullptr); + if (type) { + ASSERT_EQUALS(2U, type->size); + ASSERT_EQUALS('u', type->sign); + } + } + // s16 + { + const Library::PodType * const type = library.podtype("s16"); + ASSERT_EQUALS(true, type != nullptr); + if (type) { + ASSERT_EQUALS(2U, type->size); + ASSERT_EQUALS('s', type->sign); + } + } + // robustness test: provide cfg without PodType + { + const Library::PodType * const type = library.podtype("nonExistingPodType"); + ASSERT_EQUALS(true, type == nullptr); + } + } + } + + void container() { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" // Inherits all but templateParameter + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + + const Library::Container& A = library.containers["A"]; + const Library::Container& B = library.containers["B"]; + const Library::Container& C = library.containers["C"]; + + ASSERT_EQUALS(A.type_templateArgNo, 1); + ASSERT_EQUALS(A.size_templateArgNo, 4); + ASSERT_EQUALS(A.startPattern, "std :: A <"); + ASSERT_EQUALS(A.endPattern, "> !!::"); + ASSERT_EQUALS(A.itEndPattern, "> :: iterator"); + ASSERT_EQUALS(A.stdStringLike, false); + ASSERT_EQUALS(A.arrayLike_indexOp, false); + ASSERT_EQUALS(A.opLessAllowed, true); + ASSERT_EQ(Library::Container::Yield::SIZE, A.getYield("size")); + ASSERT_EQ(Library::Container::Yield::EMPTY, A.getYield("empty")); + ASSERT_EQ(Library::Container::Yield::AT_INDEX, A.getYield("at")); + ASSERT_EQ(Library::Container::Yield::START_ITERATOR, A.getYield("begin")); + ASSERT_EQ(Library::Container::Yield::END_ITERATOR, A.getYield("end")); + ASSERT_EQ(Library::Container::Yield::BUFFER, A.getYield("data")); + ASSERT_EQ(Library::Container::Yield::BUFFER_NT, A.getYield("c_str")); + ASSERT_EQ(Library::Container::Yield::ITEM, A.getYield("front")); + ASSERT_EQ(Library::Container::Yield::NO_YIELD, A.getYield("foo")); + ASSERT_EQ(Library::Container::Action::RESIZE, A.getAction("resize")); + ASSERT_EQ(Library::Container::Action::CLEAR, A.getAction("clear")); + ASSERT_EQ(Library::Container::Action::PUSH, A.getAction("push_back")); + ASSERT_EQ(Library::Container::Action::POP, A.getAction("pop_back")); + ASSERT_EQ(Library::Container::Action::FIND, A.getAction("find")); + ASSERT_EQ(Library::Container::Action::FIND_CONST, A.getAction("cfind")); + ASSERT_EQ(Library::Container::Action::NO_ACTION, A.getAction("foo")); + + ASSERT_EQUALS(B.type_templateArgNo, 1); + ASSERT_EQUALS(B.size_templateArgNo, 3); + ASSERT_EQUALS(B.startPattern, "std :: B <"); + ASSERT_EQUALS(B.endPattern, "> !!::"); + ASSERT_EQUALS(B.itEndPattern, "> :: iterator"); + ASSERT_EQUALS(B.functions.size(), A.functions.size()); + ASSERT_EQUALS(B.opLessAllowed, false); + + ASSERT(C.functions.empty()); + ASSERT_EQUALS(C.type_templateArgNo, -1); + ASSERT_EQUALS(C.size_templateArgNo, -1); + ASSERT_EQUALS(C.stdStringLike, true); + ASSERT_EQUALS(C.arrayLike_indexOp, true); + + { + const SimpleTokenizer var(*this, "std::A a;"); + ASSERT_EQUALS(&A, library.detectContainer(var.tokens())); + ASSERT(!library.detectIterator(var.tokens())); + bool isIterator; + ASSERT_EQUALS(&A, library.detectContainerOrIterator(var.tokens(), &isIterator)); + ASSERT(!isIterator); + } + + { + const SimpleTokenizer var(*this, "std::A::size_type a_s;"); + ASSERT(!library.detectContainer(var.tokens())); + ASSERT(!library.detectIterator(var.tokens())); + ASSERT(!library.detectContainerOrIterator(var.tokens())); + } + + { + const SimpleTokenizer var(*this, "std::A::iterator a_it;"); + ASSERT(!library.detectContainer(var.tokens())); + ASSERT_EQUALS(&A, library.detectIterator(var.tokens())); + bool isIterator; + ASSERT_EQUALS(&A, library.detectContainerOrIterator(var.tokens(), &isIterator)); + ASSERT(isIterator); + } + + { + const SimpleTokenizer var(*this, "std::B b;"); + ASSERT_EQUALS(&B, library.detectContainer(var.tokens())); + ASSERT(!library.detectIterator(var.tokens())); + bool isIterator; + ASSERT_EQUALS(&B, library.detectContainerOrIterator(var.tokens(), &isIterator)); + ASSERT(!isIterator); + } + + { + const SimpleTokenizer var(*this, "std::B::size_type b_s;"); + ASSERT(!library.detectContainer(var.tokens())); + ASSERT(!library.detectIterator(var.tokens())); + ASSERT(!library.detectContainerOrIterator(var.tokens())); + } + + { + const SimpleTokenizer var(*this, "std::B::iterator b_it;"); + ASSERT(!library.detectContainer(var.tokens())); + ASSERT_EQUALS(&B, library.detectIterator(var.tokens())); + bool isIterator; + ASSERT_EQUALS(&B, library.detectContainerOrIterator(var.tokens(), &isIterator)); + ASSERT(isIterator); + } + + { + const SimpleTokenizer var(*this, "C c;"); + ASSERT(!library.detectContainer(var.tokens())); + ASSERT(!library.detectIterator(var.tokens())); + ASSERT(!library.detectContainerOrIterator(var.tokens())); + } + + { + const SimpleTokenizer var(*this, "D d;"); + ASSERT(!library.detectContainer(var.tokens())); + ASSERT(!library.detectIterator(var.tokens())); + ASSERT(!library.detectContainerOrIterator(var.tokens())); + } + } + + void version() const { + { + constexpr char xmldata[] = "\n" + "\n" + ""; + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + } + { + constexpr char xmldata[] = "\n" + "\n" + ""; + Library library; + ASSERT(loadxmldata(library, xmldata, sizeof(xmldata))); + } + { + constexpr char xmldata[] = "\n" + "\n" + ""; + Library library; + const Library::Error err = readLibrary(library, xmldata); + ASSERT_EQUALS(true, err.errorcode == Library::ErrorCode::UNSUPPORTED_FORMAT); + } + } + + static Library::Error readLibrary(Library& library, const char* xmldata) { + tinyxml2::XMLDocument doc; + doc.Parse(xmldata); // TODO: check result + return library.load(doc); + } + + void loadLibError(const char xmldata[], Library::ErrorCode errorcode, const char* file, unsigned line) const { + Library library; + assertEquals(file, line, true, errorcode == readLibrary(library, xmldata).errorcode); + } + +#define LOADLIBERROR(xmldata, errorcode) loadLibError(xmldata, errorcode, __FILE__, __LINE__) +#define LOADLIB_ERROR_INVALID_RANGE(valid) LOADLIBERROR("\n" \ + "\n" \ + "\n" \ + "\n" \ + "" valid "\n" \ + "\n" \ + "\n" \ + "", \ + Library::ErrorCode::BAD_ATTRIBUTE_VALUE) + + void loadLibErrors() const { + + LOADLIBERROR("", + Library::ErrorCode::BAD_XML); + + LOADLIBERROR("\n" + "\n" + " \n" + "", + Library::ErrorCode::UNKNOWN_ELEMENT); + + // #define without attributes + LOADLIBERROR("\n" + "\n" + " \n" // no attributes provided at all + "", + Library::ErrorCode::MISSING_ATTRIBUTE); + + // #define with name but without value + LOADLIBERROR("\n" + "\n" + " \n" // no value provided + "", + Library::ErrorCode::MISSING_ATTRIBUTE); + + LOADLIBERROR("\n" + "\n" + " \n" // no name provided + "", + Library::ErrorCode::MISSING_ATTRIBUTE); + + LOADLIBERROR("\n" + "\n" + "", + Library::ErrorCode::UNSUPPORTED_FORMAT); + + // empty range + LOADLIB_ERROR_INVALID_RANGE(""); + + // letter as range + LOADLIB_ERROR_INVALID_RANGE("a"); + + // letter and number as range + LOADLIB_ERROR_INVALID_RANGE("1a"); + + // digit followed by dash + LOADLIB_ERROR_INVALID_RANGE("0:2-1"); + + // single dash + LOADLIB_ERROR_INVALID_RANGE("-"); + + // range with multiple colons + LOADLIB_ERROR_INVALID_RANGE("1:2:3"); + + // extra dot + LOADLIB_ERROR_INVALID_RANGE("1.0.0:10"); + + // consecutive dots + LOADLIB_ERROR_INVALID_RANGE("1..0:10"); + + // dot followed by dash + LOADLIB_ERROR_INVALID_RANGE("1.-0:10"); + + // dot without preceding number + LOADLIB_ERROR_INVALID_RANGE(".5:10"); + + // dash followed by dot + LOADLIB_ERROR_INVALID_RANGE("-.5:10"); + + // colon followed by dot without preceding number + LOADLIB_ERROR_INVALID_RANGE("0:.5"); + + // colon followed by dash followed by dot + LOADLIB_ERROR_INVALID_RANGE("-10:-.5"); + + // dot not followed by number + LOADLIB_ERROR_INVALID_RANGE("1:5."); + + // dot not followed by number + LOADLIB_ERROR_INVALID_RANGE("1.:5"); + + // dot followed by comma + LOADLIB_ERROR_INVALID_RANGE("1:5.,6:10"); + + // comma followed by dot + LOADLIB_ERROR_INVALID_RANGE("-10:0,.5:"); + } + + void loadLibCombinations() const { + { + const Settings s = settingsBuilder().library("std.cfg").library("gnu.cfg").library("bsd.cfg").build(); + ASSERT_EQUALS(s.library.defines.empty(), false); + } + { + const Settings s = settingsBuilder().library("std.cfg").library("microsoft_sal.cfg").build(); + ASSERT_EQUALS(s.library.defines.empty(), false); + } + { + const Settings s = settingsBuilder().library("std.cfg").library("windows.cfg").library("mfc.cfg").build(); + ASSERT_EQUALS(s.library.defines.empty(), false); + } + } +}; + +REGISTER_TEST(TestLibrary) diff --git a/cppcheck-2.14.0/test/testmathlib.cpp b/cppcheck-2.14.0/test/testmathlib.cpp new file mode 100644 index 00000000..1c7e631b --- /dev/null +++ b/cppcheck-2.14.0/test/testmathlib.cpp @@ -0,0 +1,1498 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" +#include "mathlib.h" +#include "fixture.h" + +#include +#include + +class TestMathLib : public TestFixture { +public: + TestMathLib() : TestFixture("TestMathLib") {} + +private: + + void run() override { + TEST_CASE(isint); + TEST_CASE(isbin); + TEST_CASE(isdec); + TEST_CASE(isoct); + TEST_CASE(isFloatHex); + TEST_CASE(isIntHex); + TEST_CASE(isValidIntegerSuffix); + TEST_CASE(isnegative); + TEST_CASE(ispositive); + TEST_CASE(isFloat); + TEST_CASE(isDecimalFloat); + TEST_CASE(isGreater); + TEST_CASE(isGreaterEqual); + TEST_CASE(isEqual); + TEST_CASE(isNotEqual); + TEST_CASE(isLess); + TEST_CASE(isLessEqual); + TEST_CASE(calculate); + TEST_CASE(calculate1); + TEST_CASE(typesuffix); + TEST_CASE(toBigNumber); + TEST_CASE(toBigUNumber); + TEST_CASE(toDoubleNumber); + TEST_CASE(naninf); + TEST_CASE(isNullValue); + TEST_CASE(sin); + TEST_CASE(cos); + TEST_CASE(tan); + TEST_CASE(abs); + TEST_CASE(toString); + TEST_CASE(CPP14DigitSeparators); + } + + void isGreater() const { + ASSERT_EQUALS(true, MathLib::isGreater("1.0", "0.001")); + ASSERT_EQUALS(false, MathLib::isGreater("-1.0", "0.001")); + } + + void isGreaterEqual() const { + ASSERT_EQUALS(true, MathLib::isGreaterEqual("1.00", "1.0")); + ASSERT_EQUALS(true, MathLib::isGreaterEqual("1.001", "1.0")); + ASSERT_EQUALS(true, MathLib::isGreaterEqual("1.0", "0.001")); + ASSERT_EQUALS(false, MathLib::isGreaterEqual("-1.0", "0.001")); + } + + void isEqual() const { + ASSERT_EQUALS(true, MathLib::isEqual("1.0", "1.0")); + ASSERT_EQUALS(false, MathLib::isEqual("1.", "1.01")); + ASSERT_EQUALS(true, MathLib::isEqual("0.1","1.0E-1")); + } + + void isNotEqual() const { + ASSERT_EQUALS(false, MathLib::isNotEqual("1.0", "1.0")); + ASSERT_EQUALS(true, MathLib::isNotEqual("1.", "1.01")); + } + + void isLess() const { + ASSERT_EQUALS(false, MathLib::isLess("1.0", "0.001")); + ASSERT_EQUALS(true, MathLib::isLess("-1.0", "0.001")); + } + + void isLessEqual() const { + ASSERT_EQUALS(true, MathLib::isLessEqual("1.00", "1.0")); + ASSERT_EQUALS(false, MathLib::isLessEqual("1.001", "1.0")); + ASSERT_EQUALS(false, MathLib::isLessEqual("1.0", "0.001")); + ASSERT_EQUALS(true, MathLib::isLessEqual("-1.0", "0.001")); + } + + void calculate() const { + // addition + ASSERT_EQUALS("256", MathLib::add("0xff", "1")); + ASSERT_EQUALS("249", MathLib::add("250", "-1")); + ASSERT_EQUALS("251", MathLib::add("250", "1")); + ASSERT_EQUALS("-2.0", MathLib::add("-1.", "-1")); + ASSERT_EQUALS("-1", MathLib::add("0", "-1")); + ASSERT_EQUALS("1", MathLib::add("1", "0")); + ASSERT_EQUALS("0.0", MathLib::add("0", "0.")); + ASSERT_EQUALS("1.00000001", MathLib::add("1", "0.00000001")); // #4016 + ASSERT_EQUALS("30666.22", MathLib::add("30666.22", "0.0")); // #4068 + + // subtraction + ASSERT_EQUALS("254", MathLib::subtract("0xff", "1")); + ASSERT_EQUALS("251", MathLib::subtract("250", "-1")); + ASSERT_EQUALS("249", MathLib::subtract("250", "1")); + ASSERT_EQUALS("0.0", MathLib::subtract("-1.", "-1")); + ASSERT_EQUALS("1", MathLib::subtract("0", "-1")); + ASSERT_EQUALS("1", MathLib::subtract("1", "0")); + ASSERT_EQUALS("0.0", MathLib::subtract("0", "0.")); + ASSERT_EQUALS("0.99999999", MathLib::subtract("1", "0.00000001")); // #4016 + ASSERT_EQUALS("30666.22", MathLib::subtract("30666.22", "0.0")); // #4068 + ASSERT_EQUALS("0.0", MathLib::subtract("0.0", "0.0")); + + // multiply + ASSERT_EQUALS("-0.003", MathLib::multiply("-1e-3", "3")); + ASSERT_EQUALS("-11.96", MathLib::multiply("-2.3", "5.2")); + ASSERT_EQUALS("3000.0", MathLib::multiply("1E3", "3")); + ASSERT_EQUALS("3000.0", MathLib::multiply("1E+3", "3")); + ASSERT_EQUALS("3000.0", MathLib::multiply("1.0E3", "3")); + ASSERT_EQUALS("-3000.0", MathLib::multiply("-1.0E3", "3")); + ASSERT_EQUALS("-3000.0", MathLib::multiply("-1.0E+3", "3")); + ASSERT_EQUALS("0.0", MathLib::multiply("+1.0E+3", "0")); + ASSERT_EQUALS("2147483648", MathLib::multiply("2","1073741824")); + ASSERT_EQUALS("536870912", MathLib::multiply("512","1048576")); + + // divide + ASSERT_EQUALS("1", MathLib::divide("1", "1")); + ASSERT_EQUALS("0", MathLib::divide("0", "1")); + ASSERT_EQUALS("5", MathLib::divide("-10", "-2")); + ASSERT_EQUALS("-2.5", MathLib::divide("-10.", "4")); + ASSERT_EQUALS("2.5", MathLib::divide("-10.", "-4")); + ASSERT_EQUALS("5.0", MathLib::divide("25.5", "5.1")); + ASSERT_EQUALS("7.0", MathLib::divide("21.", "3")); + ASSERT_EQUALS("1", MathLib::divide("3", "2")); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::divide("123", "0"), INTERNAL, "Internal Error: Division by zero"); // decimal zero: throw + ASSERT_THROW_INTERNAL_EQUALS(MathLib::divide("123", "00"), INTERNAL, "Internal Error: Division by zero"); // octal zero: throw + ASSERT_THROW_INTERNAL_EQUALS(MathLib::divide("123", "0x0"), INTERNAL, "Internal Error: Division by zero"); // hex zero: throw + MathLib::divide("123", "0.0f"); // float zero: don't throw + MathLib::divide("123", "0.0"); // double zero: don't throw + MathLib::divide("123", "0.0L"); // long double zero: don't throw + ASSERT_THROW_INTERNAL_EQUALS(MathLib::divide("-9223372036854775808", "-1"), INTERNAL, "Internal Error: Division overflow"); // #4520 - out of range => throw + ASSERT_EQUALS("4611686018427387904", MathLib::divide("-9223372036854775808", "-2")); // #6679 + + + // invoke for each supported action + ASSERT_EQUALS("3", MathLib::calculate("2", "1", '+')); + ASSERT_EQUALS("1", MathLib::calculate("2", "1", '-')); + ASSERT_EQUALS("2", MathLib::calculate("2", "1", '*')); + ASSERT_EQUALS("2", MathLib::calculate("2", "1", '/')); + ASSERT_EQUALS("0", MathLib::calculate("2", "1", '%')); + ASSERT_EQUALS("0", MathLib::calculate("1", "2", '&')); + ASSERT_EQUALS("1", MathLib::calculate("1", "1", '|')); + ASSERT_EQUALS("1", MathLib::calculate("0", "1", '^')); + + // Unknown action should throw exception + ASSERT_THROW_INTERNAL_EQUALS(MathLib::calculate("1","2",'j'),INTERNAL, "Unexpected action 'j' in MathLib::calculate(). Please report this to Cppcheck developers."); + } + + void calculate1() const { + ASSERT_EQUALS("0", MathLib::calculate("2", "1", '%')); + ASSERT_EQUALS("2", MathLib::calculate("12", "5", '%')); + ASSERT_EQUALS("1", MathLib::calculate("100", "3", '%')); + +#ifndef TEST_MATHLIB_VALUE + // floating point modulo is not defined in C/C++ + ASSERT_EQUALS("0.0", MathLib::calculate("2.0", "1.0", '%')); + ASSERT_EQUALS("12.0", MathLib::calculate("12.0", "13.0", '%')); + ASSERT_EQUALS("1.3", MathLib::calculate("5.3", "2.0", '%')); + ASSERT_EQUALS("1.7", MathLib::calculate("18.5", "4.2", '%')); + MathLib::calculate("123", "0.0", '%'); // don't throw +#endif + + ASSERT_THROW_INTERNAL_EQUALS(MathLib::calculate("123", "0", '%'), INTERNAL, "Internal Error: Division by zero"); // throw + + ASSERT_EQUALS("0", MathLib::calculate("1", "1", '^')); + ASSERT_EQUALS("3", MathLib::calculate("2", "1", '^')); + } + + void typesuffix() const { + ASSERT_EQUALS("2", MathLib::add("1", "1")); + ASSERT_EQUALS("2U", MathLib::add("1U", "1")); + ASSERT_EQUALS("2L", MathLib::add("1L", "1")); + ASSERT_EQUALS("2UL", MathLib::add("1UL", "1")); + ASSERT_EQUALS("2LL", MathLib::add("1LL", "1")); + ASSERT_EQUALS("2LL", MathLib::add("1i64", "1")); + ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1")); + ASSERT_EQUALS("2ULL", MathLib::add("1ui64","1")); + + ASSERT_EQUALS("2U", MathLib::add("1", "1U")); + ASSERT_EQUALS("2U", MathLib::add("1U", "1U")); + ASSERT_EQUALS("2L", MathLib::add("1L", "1U")); + ASSERT_EQUALS("2UL", MathLib::add("1UL", "1U")); + ASSERT_EQUALS("2LL", MathLib::add("1LL", "1U")); + ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1U")); + + ASSERT_EQUALS("2L", MathLib::add("1", "1L")); + ASSERT_EQUALS("2L", MathLib::add("1U", "1L")); + ASSERT_EQUALS("2L", MathLib::add("1L", "1L")); + ASSERT_EQUALS("2UL", MathLib::add("1UL", "1L")); + ASSERT_EQUALS("2LL", MathLib::add("1LL", "1L")); + ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1L")); + + ASSERT_EQUALS("2UL", MathLib::add("1", "1UL")); + ASSERT_EQUALS("2UL", MathLib::add("1U", "1UL")); + ASSERT_EQUALS("2UL", MathLib::add("1L", "1UL")); + ASSERT_EQUALS("2UL", MathLib::add("1UL", "1UL")); + ASSERT_EQUALS("2LL", MathLib::add("1LL", "1UL")); + ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1UL")); + + ASSERT_EQUALS("2UL", MathLib::add("1", "1LU")); + ASSERT_EQUALS("2UL", MathLib::add("1U", "1LU")); + ASSERT_EQUALS("2UL", MathLib::add("1L", "1LU")); + ASSERT_EQUALS("2UL", MathLib::add("1UL", "1LU")); + ASSERT_EQUALS("2LL", MathLib::add("1LL", "1LU")); + ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1LU")); + + ASSERT_EQUALS("2LL", MathLib::add("1", "1LL")); + ASSERT_EQUALS("2LL", MathLib::add("1U", "1LL")); + ASSERT_EQUALS("2LL", MathLib::add("1L", "1LL")); + ASSERT_EQUALS("2LL", MathLib::add("1UL", "1LL")); + ASSERT_EQUALS("2LL", MathLib::add("1LL", "1LL")); + ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1LL")); + + ASSERT_EQUALS("2ULL", MathLib::add("1", "1ULL")); + ASSERT_EQUALS("2ULL", MathLib::add("1U", "1ULL")); + ASSERT_EQUALS("2ULL", MathLib::add("1L", "1ULL")); + ASSERT_EQUALS("2ULL", MathLib::add("1UL", "1ULL")); + ASSERT_EQUALS("2ULL", MathLib::add("1LL", "1ULL")); + ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1ULL")); + + ASSERT_EQUALS("2ULL", MathLib::add("1", "1LLU")); + ASSERT_EQUALS("2ULL", MathLib::add("1U", "1LLU")); + ASSERT_EQUALS("2ULL", MathLib::add("1L", "1LLU")); + ASSERT_EQUALS("2ULL", MathLib::add("1UL", "1LLU")); + ASSERT_EQUALS("2ULL", MathLib::add("1LL", "1LLU")); + ASSERT_EQUALS("2ULL", MathLib::add("1ULL", "1LLU")); + } + + void toBigNumber() const { + // zero input + ASSERT_EQUALS(0, MathLib::toBigNumber("0")); + ASSERT_EQUALS(0, MathLib::toBigNumber("-0")); + ASSERT_EQUALS(0, MathLib::toBigNumber("+0")); + ASSERT_EQUALS(0, MathLib::toBigNumber("0L")); + ASSERT_EQUALS(0, MathLib::toBigNumber("0l")); + ASSERT_EQUALS(0, MathLib::toBigNumber("0LL")); + ASSERT_EQUALS(0, MathLib::toBigNumber("0ll")); + ASSERT_EQUALS(0, MathLib::toBigNumber("0U")); + ASSERT_EQUALS(0, MathLib::toBigNumber("0u")); + ASSERT_EQUALS(0, MathLib::toBigNumber("0UL")); + ASSERT_EQUALS(0, MathLib::toBigNumber("0ul")); + ASSERT_EQUALS(0, MathLib::toBigNumber("0ULL")); + ASSERT_EQUALS(0, MathLib::toBigNumber("0ull")); + ASSERT_EQUALS(0, MathLib::toBigNumber("0i64")); // Visual Studio-specific + ASSERT_EQUALS(0, MathLib::toBigNumber("0ui64")); // Visual Studio-specific + + // TODO: needs to fail + //ASSERT_EQUALS(0, MathLib::toBigNumber("0lll")); + //ASSERT_EQUALS(0, MathLib::toBigNumber("0uu")); + + ASSERT_EQUALS(1U, MathLib::toBigNumber("1U")); + ASSERT_EQUALS(10000U, MathLib::toBigNumber("1e4")); + ASSERT_EQUALS(10000U, MathLib::toBigNumber("1e4")); + ASSERT_EQUALS(0xFF00000000000000UL, MathLib::toBigNumber("0xFF00000000000000UL")); + ASSERT_EQUALS(0x0A00000000000000UL, MathLib::toBigNumber("0x0A00000000000000UL")); + + // from hex + ASSERT_EQUALS(0, MathLib::toBigNumber("0x0")); + ASSERT_EQUALS(0, MathLib::toBigNumber("-0x0")); + ASSERT_EQUALS(0, MathLib::toBigNumber("+0x0")); + ASSERT_EQUALS(10, MathLib::toBigNumber("0xa")); + ASSERT_EQUALS(10995, MathLib::toBigNumber("0x2AF3")); + ASSERT_EQUALS(-10, MathLib::toBigNumber("-0xa")); + ASSERT_EQUALS(-10995, MathLib::toBigNumber("-0x2AF3")); + ASSERT_EQUALS(10, MathLib::toBigNumber("+0xa")); + ASSERT_EQUALS(10995, MathLib::toBigNumber("+0x2AF3")); + + // from octal + ASSERT_EQUALS(8, MathLib::toBigNumber("010")); + ASSERT_EQUALS(8, MathLib::toBigNumber("+010")); + ASSERT_EQUALS(-8, MathLib::toBigNumber("-010")); + ASSERT_EQUALS(125, MathLib::toBigNumber("0175")); + ASSERT_EQUALS(125, MathLib::toBigNumber("+0175")); + ASSERT_EQUALS(-125, MathLib::toBigNumber("-0175")); + + // from binary + ASSERT_EQUALS(0, MathLib::toBigNumber("0b0")); + ASSERT_EQUALS(1, MathLib::toBigNumber("0b1")); + ASSERT_EQUALS(1, MathLib::toBigNumber("0b1U")); + ASSERT_EQUALS(1, MathLib::toBigNumber("0b1L")); + ASSERT_EQUALS(1, MathLib::toBigNumber("0b1LU")); + ASSERT_EQUALS(1, MathLib::toBigNumber("0b1LL")); + ASSERT_EQUALS(1, MathLib::toBigNumber("0b1LLU")); + ASSERT_EQUALS(1, MathLib::toBigNumber("+0b1")); + ASSERT_EQUALS(-1, MathLib::toBigNumber("-0b1")); + ASSERT_EQUALS(9U, MathLib::toBigNumber("011")); + ASSERT_EQUALS(5U, MathLib::toBigNumber("0b101")); + ASSERT_EQUALS(215, MathLib::toBigNumber("0b11010111")); + ASSERT_EQUALS(-215, MathLib::toBigNumber("-0b11010111")); + ASSERT_EQUALS(215, MathLib::toBigNumber("0B11010111")); + + // from base 10 + ASSERT_EQUALS(10, MathLib::toBigNumber("10")); + ASSERT_EQUALS(10, MathLib::toBigNumber("10.")); + ASSERT_EQUALS(10, MathLib::toBigNumber("10.0")); + ASSERT_EQUALS(100, MathLib::toBigNumber("10E+1")); + ASSERT_EQUALS(1, MathLib::toBigNumber("10E-1")); + ASSERT_EQUALS(100, MathLib::toBigNumber("+10E+1")); + ASSERT_EQUALS(-1, MathLib::toBigNumber("-10E-1")); + ASSERT_EQUALS(100, MathLib::toBigNumber("+10.E+1")); + ASSERT_EQUALS(-1, MathLib::toBigNumber("-10.E-1")); + ASSERT_EQUALS(100, MathLib::toBigNumber("+10.0E+1")); + ASSERT_EQUALS(-1, MathLib::toBigNumber("-10.0E-1")); + + // from char + ASSERT_EQUALS((int)('A'), MathLib::toBigNumber("'A'")); + ASSERT_EQUALS((int)('\x10'), MathLib::toBigNumber("'\\x10'")); + ASSERT_EQUALS((int)('\100'), MathLib::toBigNumber("'\\100'")); + ASSERT_EQUALS((int)('\200'), MathLib::toBigNumber("'\\200'")); + ASSERT_EQUALS((int)(L'A'), MathLib::toBigNumber("L'A'")); + + ASSERT_EQUALS(-8552249625308161526, MathLib::toBigNumber("0x89504e470d0a1a0a")); + ASSERT_EQUALS(-8481036456200365558, MathLib::toBigNumber("0x8a4d4e470d0a1a0a")); + + // from long long + /* + * ASSERT_EQUALS(0xFF00000000000000LL, MathLib::toBigNumber("0xFF00000000000000LL")); + * This does not work in a portable way! + * While it succeeds on 32bit Visual Studio it fails on Linux 64bit because it is greater than 0x7FFFFFFFFFFFFFFF (=LLONG_MAX) + */ + + ASSERT_EQUALS(0x0A00000000000000LL, MathLib::toBigNumber("0x0A00000000000000LL")); + + // min/max numeric limits + ASSERT_EQUALS(std::numeric_limits::min(), + MathLib::toBigNumber(std::to_string(std::numeric_limits::min()))); + ASSERT_EQUALS(std::numeric_limits::max(), + MathLib::toBigNumber(std::to_string(std::numeric_limits::max()))); + ASSERT_EQUALS(std::numeric_limits::min(), + MathLib::toBigNumber(std::to_string(std::numeric_limits::min()))); + ASSERT_EQUALS(std::numeric_limits::max(), + MathLib::toBigNumber(std::to_string(std::numeric_limits::max()))); + + // min/max and out-of-bounds - hex + { + constexpr MathLib::bigint i = 0xFFFFFFFFFFFFFFFF; + ASSERT_EQUALS(i, MathLib::toBigNumber(std::to_string(i))); + ASSERT_EQUALS(i, MathLib::toBigNumber("0xFFFFFFFFFFFFFFFF")); + } + { + constexpr MathLib::bigint i = -0xFFFFFFFFFFFFFFFF; + ASSERT_EQUALS(i, MathLib::toBigNumber(std::to_string(i))); + ASSERT_EQUALS(i, MathLib::toBigNumber("-0xFFFFFFFFFFFFFFFF")); + } + + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigNumber("0x10000000000000000"), INTERNAL, "Internal Error. MathLib::toBigNumber: out_of_range: 0x10000000000000000"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigNumber("-0x10000000000000000"), INTERNAL, "Internal Error. MathLib::toBigNumber: out_of_range: -0x10000000000000000"); + + // min/max and out-of-bounds - octal + { + constexpr MathLib::bigint i = 01777777777777777777777; + ASSERT_EQUALS(i, MathLib::toBigNumber(std::to_string(i))); + ASSERT_EQUALS(i, MathLib::toBigNumber("01777777777777777777777")); + } + { + constexpr MathLib::bigint i = -01777777777777777777777; + ASSERT_EQUALS(i, MathLib::toBigNumber(std::to_string(i))); + ASSERT_EQUALS(i, MathLib::toBigNumber("-01777777777777777777777")); + } + + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigNumber("02000000000000000000000"), INTERNAL, "Internal Error. MathLib::toBigNumber: out_of_range: 02000000000000000000000"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigNumber("-02000000000000000000000"), INTERNAL, "Internal Error. MathLib::toBigNumber: out_of_range: -02000000000000000000000"); + + // min/max and out-of-bounds - decimal + SUPPRESS_WARNING_CLANG_PUSH("-Wimplicitly-unsigned-literal") + SUPPRESS_WARNING_GCC_PUSH("-Woverflow") + { + constexpr MathLib::bigint i = 18446744073709551615; + ASSERT_EQUALS(i, MathLib::toBigNumber(std::to_string(i))); + ASSERT_EQUALS(i, MathLib::toBigNumber("18446744073709551615")); + } + { + constexpr MathLib::bigint i = -18446744073709551615; + ASSERT_EQUALS(i, MathLib::toBigNumber(std::to_string(i))); + ASSERT_EQUALS(i, MathLib::toBigNumber("-18446744073709551615")); + } + SUPPRESS_WARNING_GCC_POP + SUPPRESS_WARNING_CLANG_POP + + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigNumber("18446744073709551616"), INTERNAL, "Internal Error. MathLib::toBigNumber: out_of_range: 18446744073709551616"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigNumber("-18446744073709551616"), INTERNAL, "Internal Error. MathLib::toBigNumber: out_of_range: -18446744073709551616"); + + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigNumber("invalid"), INTERNAL, "Internal Error. MathLib::toBigNumber: invalid_argument: invalid"); + + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigNumber("1invalid"), INTERNAL, "Internal Error. MathLib::toBigNumber: input was not completely consumed: 1invalid"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigNumber("1 invalid"), INTERNAL, "Internal Error. MathLib::toBigNumber: input was not completely consumed: 1 invalid"); + + // TODO: test binary + // TODO: test floating point + + // TODO: test with 128-bit values + } + + void toBigUNumber() const { + // zero input + ASSERT_EQUALS(0, MathLib::toBigUNumber("0")); + ASSERT_EQUALS(0, MathLib::toBigUNumber("-0")); + ASSERT_EQUALS(0, MathLib::toBigUNumber("+0")); + ASSERT_EQUALS(0, MathLib::toBigUNumber("0L")); + ASSERT_EQUALS(0, MathLib::toBigUNumber("0l")); + ASSERT_EQUALS(0, MathLib::toBigUNumber("0LL")); + ASSERT_EQUALS(0, MathLib::toBigUNumber("0ll")); + ASSERT_EQUALS(0, MathLib::toBigUNumber("0U")); + ASSERT_EQUALS(0, MathLib::toBigUNumber("0u")); + ASSERT_EQUALS(0, MathLib::toBigUNumber("0UL")); + ASSERT_EQUALS(0, MathLib::toBigUNumber("0ul")); + ASSERT_EQUALS(0, MathLib::toBigUNumber("0ULL")); + ASSERT_EQUALS(0, MathLib::toBigUNumber("0ull")); + ASSERT_EQUALS(0, MathLib::toBigUNumber("0i64")); // Visual Studio-specific + ASSERT_EQUALS(0, MathLib::toBigUNumber("0ui64")); // Visual Studio-specific + + // TODO: needs to fail + //ASSERT_EQUALS(0, MathLib::toBigUNumber("0lll")); + //ASSERT_EQUALS(0, MathLib::toBigUNumber("0uu")); + + ASSERT_EQUALS(1U, MathLib::toBigUNumber("1U")); + ASSERT_EQUALS(10000U, MathLib::toBigUNumber("1e4")); + ASSERT_EQUALS(10000U, MathLib::toBigUNumber("1e4")); + ASSERT_EQUALS(0xFF00000000000000UL, MathLib::toBigUNumber("0xFF00000000000000UL")); + ASSERT_EQUALS(0x0A00000000000000UL, MathLib::toBigUNumber("0x0A00000000000000UL")); + + // from hex + ASSERT_EQUALS(0, MathLib::toBigUNumber("0x0")); + ASSERT_EQUALS(0, MathLib::toBigUNumber("-0x0")); + ASSERT_EQUALS(0, MathLib::toBigUNumber("+0x0")); + ASSERT_EQUALS(10, MathLib::toBigUNumber("0xa")); + ASSERT_EQUALS(10995, MathLib::toBigUNumber("0x2AF3")); + ASSERT_EQUALS(-10, MathLib::toBigUNumber("-0xa")); + ASSERT_EQUALS(-10995, MathLib::toBigUNumber("-0x2AF3")); + ASSERT_EQUALS(10, MathLib::toBigUNumber("+0xa")); + ASSERT_EQUALS(10995, MathLib::toBigUNumber("+0x2AF3")); + + // from octal + ASSERT_EQUALS(8, MathLib::toBigUNumber("010")); + ASSERT_EQUALS(8, MathLib::toBigUNumber("+010")); + ASSERT_EQUALS(-8, MathLib::toBigUNumber("-010")); + ASSERT_EQUALS(125, MathLib::toBigUNumber("0175")); + ASSERT_EQUALS(125, MathLib::toBigUNumber("+0175")); + ASSERT_EQUALS(-125, MathLib::toBigUNumber("-0175")); + + // from binary + ASSERT_EQUALS(0, MathLib::toBigUNumber("0b0")); + ASSERT_EQUALS(1, MathLib::toBigUNumber("0b1")); + ASSERT_EQUALS(1, MathLib::toBigUNumber("0b1U")); + ASSERT_EQUALS(1, MathLib::toBigUNumber("0b1L")); + ASSERT_EQUALS(1, MathLib::toBigUNumber("0b1LU")); + ASSERT_EQUALS(1, MathLib::toBigUNumber("0b1LL")); + ASSERT_EQUALS(1, MathLib::toBigUNumber("0b1LLU")); + ASSERT_EQUALS(1, MathLib::toBigUNumber("+0b1")); + ASSERT_EQUALS(-1, MathLib::toBigUNumber("-0b1")); + ASSERT_EQUALS(9U, MathLib::toBigUNumber("011")); + ASSERT_EQUALS(5U, MathLib::toBigUNumber("0b101")); + ASSERT_EQUALS(215, MathLib::toBigUNumber("0b11010111")); + ASSERT_EQUALS(-215, MathLib::toBigUNumber("-0b11010111")); + ASSERT_EQUALS(215, MathLib::toBigUNumber("0B11010111")); + + // from base 10 + ASSERT_EQUALS(10, MathLib::toBigUNumber("10")); + ASSERT_EQUALS(10, MathLib::toBigUNumber("10.")); + ASSERT_EQUALS(10, MathLib::toBigUNumber("10.0")); + ASSERT_EQUALS(100, MathLib::toBigUNumber("10E+1")); + ASSERT_EQUALS(1, MathLib::toBigUNumber("10E-1")); + ASSERT_EQUALS(100, MathLib::toBigUNumber("+10E+1")); + ASSERT_EQUALS(-1, MathLib::toBigUNumber("-10E-1")); + ASSERT_EQUALS(100, MathLib::toBigUNumber("+10.E+1")); + ASSERT_EQUALS(-1, MathLib::toBigUNumber("-10.E-1")); + ASSERT_EQUALS(100, MathLib::toBigUNumber("+10.0E+1")); + ASSERT_EQUALS(-1, MathLib::toBigUNumber("-10.0E-1")); + + // from char + ASSERT_EQUALS((int)('A'), MathLib::toBigUNumber("'A'")); + ASSERT_EQUALS((int)('\x10'), MathLib::toBigUNumber("'\\x10'")); + ASSERT_EQUALS((int)('\100'), MathLib::toBigUNumber("'\\100'")); + ASSERT_EQUALS((int)('\200'), MathLib::toBigUNumber("'\\200'")); + ASSERT_EQUALS((int)(L'A'), MathLib::toBigUNumber("L'A'")); + + ASSERT_EQUALS(9894494448401390090ULL, MathLib::toBigUNumber("0x89504e470d0a1a0a")); + ASSERT_EQUALS(9965707617509186058ULL, MathLib::toBigUNumber("0x8a4d4e470d0a1a0a")); + + // from long long + /* + * ASSERT_EQUALS(0xFF00000000000000LL, MathLib::toBigUNumber("0xFF00000000000000LL")); + * This does not work in a portable way! + * While it succeeds on 32bit Visual Studio it fails on Linux 64bit because it is greater than 0x7FFFFFFFFFFFFFFF (=LLONG_MAX) + */ + + ASSERT_EQUALS(0x0A00000000000000LL, MathLib::toBigUNumber("0x0A00000000000000LL")); + + // min/max numeric limits + ASSERT_EQUALS(std::numeric_limits::min(), + MathLib::toBigUNumber(std::to_string(std::numeric_limits::min()))); + ASSERT_EQUALS(std::numeric_limits::max(), + MathLib::toBigUNumber(std::to_string(std::numeric_limits::max()))); + ASSERT_EQUALS(std::numeric_limits::min(), + MathLib::toBigUNumber(std::to_string(std::numeric_limits::min()))); + ASSERT_EQUALS(std::numeric_limits::max(), + MathLib::toBigUNumber(std::to_string(std::numeric_limits::max()))); + + // min/max and out-of-bounds - hex + { + constexpr MathLib::biguint u = 0xFFFFFFFFFFFFFFFF; + ASSERT_EQUALS(u, MathLib::toBigUNumber(std::to_string(u))); + ASSERT_EQUALS(u, MathLib::toBigUNumber("0xFFFFFFFFFFFFFFFF")); + } + { + constexpr MathLib::biguint u = -0xFFFFFFFFFFFFFFFF; + ASSERT_EQUALS(u, MathLib::toBigUNumber(std::to_string(u))); + ASSERT_EQUALS(u, MathLib::toBigUNumber("-0xFFFFFFFFFFFFFFFF")); + } + + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigUNumber("0x10000000000000000"), INTERNAL, "Internal Error. MathLib::toBigUNumber: out_of_range: 0x10000000000000000"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigUNumber("-0x10000000000000000"), INTERNAL, "Internal Error. MathLib::toBigUNumber: out_of_range: -0x10000000000000000"); + + // min/max and out-of-bounds - octal + { + constexpr MathLib::biguint u = 01777777777777777777777; + ASSERT_EQUALS(u, MathLib::toBigUNumber(std::to_string(u))); + ASSERT_EQUALS(u, MathLib::toBigUNumber("01777777777777777777777")); + } + { + constexpr MathLib::biguint u = -01777777777777777777777; + ASSERT_EQUALS(u, MathLib::toBigUNumber(std::to_string(u))); + ASSERT_EQUALS(u, MathLib::toBigUNumber("-01777777777777777777777")); + } + + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigUNumber("02000000000000000000000"), INTERNAL, "Internal Error. MathLib::toBigUNumber: out_of_range: 02000000000000000000000"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigUNumber("-02000000000000000000000"), INTERNAL, "Internal Error. MathLib::toBigUNumber: out_of_range: -02000000000000000000000"); + + // min/max and out-of-bounds - decimal + SUPPRESS_WARNING_CLANG_PUSH("-Wimplicitly-unsigned-literal") + SUPPRESS_WARNING_GCC_PUSH("-Woverflow") + { + constexpr MathLib::biguint u = 18446744073709551615; + ASSERT_EQUALS(u, MathLib::toBigUNumber(std::to_string(u))); + ASSERT_EQUALS(u, MathLib::toBigUNumber("18446744073709551615")); + } + { + constexpr MathLib::biguint u = -18446744073709551615; + ASSERT_EQUALS(u, MathLib::toBigUNumber(std::to_string(u))); + ASSERT_EQUALS(u, MathLib::toBigUNumber("-18446744073709551615")); + } + SUPPRESS_WARNING_GCC_POP + SUPPRESS_WARNING_CLANG_POP + + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigUNumber("18446744073709551616"), INTERNAL, "Internal Error. MathLib::toBigUNumber: out_of_range: 18446744073709551616"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigUNumber("-18446744073709551616"), INTERNAL, "Internal Error. MathLib::toBigUNumber: out_of_range: -18446744073709551616"); + + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigUNumber("invalid"), INTERNAL, "Internal Error. MathLib::toBigUNumber: invalid_argument: invalid"); + + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigUNumber("1invalid"), INTERNAL, "Internal Error. MathLib::toBigUNumber: input was not completely consumed: 1invalid"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toBigUNumber("1 invalid"), INTERNAL, "Internal Error. MathLib::toBigUNumber: input was not completely consumed: 1 invalid"); + + // TODO: test binary + // TODO: test floating point + + // TODO: test with 128-bit values + } + + void toDoubleNumber() const { + // float values + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1."), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.f"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.F"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.l"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.L"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.0"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.0f"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.0F"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.0l"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1.0L"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("0x1"), 0.001); + ASSERT_EQUALS_DOUBLE(10.0, MathLib::toDoubleNumber("10"), 0.001); + ASSERT_EQUALS_DOUBLE(1000.0, MathLib::toDoubleNumber("10E+2"), 0.001); + ASSERT_EQUALS_DOUBLE(1000.0, MathLib::toDoubleNumber("10E+2l"), 0.001); + ASSERT_EQUALS_DOUBLE(1000.0, MathLib::toDoubleNumber("10E+2L"), 0.001); + ASSERT_EQUALS_DOUBLE(100.0, MathLib::toDoubleNumber("1.0E+2"), 0.001); + ASSERT_EQUALS_DOUBLE(-100.0, MathLib::toDoubleNumber("-1.0E+2"), 0.001); + ASSERT_EQUALS_DOUBLE(-1e+10, MathLib::toDoubleNumber("-1.0E+10"), 1); + ASSERT_EQUALS_DOUBLE(100.0, MathLib::toDoubleNumber("+1.0E+2"), 0.001); + ASSERT_EQUALS_DOUBLE(1e+10, MathLib::toDoubleNumber("+1.0E+10"), 1); + ASSERT_EQUALS_DOUBLE(100.0, MathLib::toDoubleNumber("1.0E+2"), 0.001); + ASSERT_EQUALS_DOUBLE(1e+10, MathLib::toDoubleNumber("1.0E+10"), 1); + + // valid non-float values + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1l"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1L"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1ll"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1LL"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1u"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1U"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1ul"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1UL"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1ULL"), 0.001); + // TODO: make these work with libc++ +#ifndef _LIBCPP_VERSION + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1i64"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1I64"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1ui64"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1UI64"), 0.001); + ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1I64"), 0.001); +#endif + + ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0E+0"), 0.000001); + ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0E-0"), 0.000001); + ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0E+00"), 0.000001); + ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0E-00"), 0.000001); + ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("-0E+00"), 0.000001); + ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("+0E-00"), 0.000001); + ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0"), 0.000001); + ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0."), 0.000001); + ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("0.0"), 0.000001); + ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("-0"), 0.000001); + ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("+0"), 0.000001); + ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("-0."), 0.000001); + ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("+0."), 0.000001); + ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("-0.0"), 0.000001); + ASSERT_EQUALS_DOUBLE(0.0, MathLib::toDoubleNumber("+0.0"), 0.000001); + ASSERT_EQUALS_DOUBLE('0', MathLib::toDoubleNumber("'0'"), 0.000001); + ASSERT_EQUALS_DOUBLE(L'0', MathLib::toDoubleNumber("L'0'"), 0.000001); + + ASSERT_EQUALS_DOUBLE(192, MathLib::toDoubleNumber("0x0.3p10"), 0.000001); + ASSERT_EQUALS_DOUBLE(5.42101e-20, MathLib::toDoubleNumber("0x1p-64"), 1e-20); + ASSERT_EQUALS_DOUBLE(3.14159, MathLib::toDoubleNumber("0x1.921fb5p+1"), 0.000001); + ASSERT_EQUALS_DOUBLE(2006, MathLib::toDoubleNumber("0x1.f58000p+10"), 0.000001); + ASSERT_EQUALS_DOUBLE(1e-010, MathLib::toDoubleNumber("0x1.b7cdfep-34"), 0.000001); + ASSERT_EQUALS_DOUBLE(.484375, MathLib::toDoubleNumber("0x1.fp-2"), 0.000001); + ASSERT_EQUALS_DOUBLE(9.0, MathLib::toDoubleNumber("0x1.2P3"), 0.000001); + ASSERT_EQUALS_DOUBLE(0.0625, MathLib::toDoubleNumber("0x.1P0"), 0.000001); + + // from char + ASSERT_EQUALS_DOUBLE((double)('A'), MathLib::toDoubleNumber("'A'"), 0.000001); + ASSERT_EQUALS_DOUBLE((double)('\x10'), MathLib::toDoubleNumber("'\\x10'"), 0.000001); + ASSERT_EQUALS_DOUBLE((double)('\100'), MathLib::toDoubleNumber("'\\100'"), 0.000001); + ASSERT_EQUALS_DOUBLE((double)('\200'), MathLib::toDoubleNumber("'\\200'"), 0.000001); + ASSERT_EQUALS_DOUBLE((double)(L'A'), MathLib::toDoubleNumber("L'A'"), 0.000001); + + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("invalid"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: conversion failed: invalid"); + +#ifdef _LIBCPP_VERSION + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1invalid"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: conversion failed: 1invalid"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1.1invalid"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: conversion failed: 1.1invalid"); +#else + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1invalid"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: 1invalid"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1.1invalid"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: 1.1invalid"); +#endif + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1 invalid"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: 1 invalid"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("-1e-08.0"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: -1e-08.0"); + + // invalid suffices +#ifdef _LIBCPP_VERSION + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1f"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: conversion failed: 1f"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1F"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: conversion failed: 1F"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1.ff"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: conversion failed: 1.ff"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1.FF"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: conversion failed: 1.FF"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1.0ff"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: conversion failed: 1.0ff"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1.0FF"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: conversion failed: 1.0FF"); +#else + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1f"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: 1f"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1F"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: 1F"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1.ff"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: 1.ff"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1.FF"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: 1.FF"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1.0ff"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: 1.0ff"); + ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1.0FF"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: 1.0FF"); +#endif + // TODO: needs to fail + //ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1.ll"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: 1.ll"); + //ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1.0LL"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: 1.0LL"); + //ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1.0ll"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: 1.0ll"); + //ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("1.0LL"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: input was not completely consumed: 1.0LL"); + + // verify: string --> double --> string conversion + ASSERT_EQUALS("1.0", MathLib::toString(MathLib::toDoubleNumber("1.0f"))); + ASSERT_EQUALS("1.0", MathLib::toString(MathLib::toDoubleNumber("1.0"))); + ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("0.0f"))); + ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("0.0"))); + ASSERT_EQUALS("-1.0", MathLib::toString(MathLib::toDoubleNumber("-1.0f"))); + ASSERT_EQUALS("-1.0", MathLib::toString(MathLib::toDoubleNumber("-1.0"))); + ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("-0.0f"))); + ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("-0.0"))); + ASSERT_EQUALS("1.0", MathLib::toString(MathLib::toDoubleNumber("+1.0f"))); + ASSERT_EQUALS("1.0", MathLib::toString(MathLib::toDoubleNumber("+1.0"))); + ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("+0.0f"))); + ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("+0.0"))); + ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("-0"))); + ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("-0."))); + ASSERT_EQUALS("0.0", MathLib::toString(MathLib::toDoubleNumber("-0.0"))); + } + + void isint() const { + // zero tests + ASSERT_EQUALS(true, MathLib::isInt("0")); + ASSERT_EQUALS(false, MathLib::isInt("0.")); + ASSERT_EQUALS(false, MathLib::isInt("0.0")); + ASSERT_EQUALS(false, MathLib::isInt("-0.")); + ASSERT_EQUALS(false, MathLib::isInt("+0.")); + ASSERT_EQUALS(false, MathLib::isInt("-0.0")); + ASSERT_EQUALS(false, MathLib::isInt("+0.0")); + ASSERT_EQUALS(false, MathLib::isInt("+0.0E+1")); + ASSERT_EQUALS(false, MathLib::isInt("+0.0E-1")); + ASSERT_EQUALS(false, MathLib::isInt("-0.0E+1")); + ASSERT_EQUALS(false, MathLib::isInt("-0.0E-1")); + + ASSERT_EQUALS(true, MathLib::isInt("1")); + ASSERT_EQUALS(true, MathLib::isInt("-1")); + ASSERT_EQUALS(true, MathLib::isInt("+1")); + ASSERT_EQUALS(false, MathLib::isInt("+1E+1")); + ASSERT_EQUALS(false, MathLib::isInt("+1E+10000")); + ASSERT_EQUALS(false, MathLib::isInt("-1E+1")); + ASSERT_EQUALS(false, MathLib::isInt("-1E+10000")); + ASSERT_EQUALS(false, MathLib::isInt("-1E-1")); + ASSERT_EQUALS(false, MathLib::isInt("-1E-10000")); + + ASSERT_EQUALS(true, MathLib::isInt("0xff")); + ASSERT_EQUALS(true, MathLib::isInt("0xa")); + ASSERT_EQUALS(true, MathLib::isInt("0b1000")); + ASSERT_EQUALS(true, MathLib::isInt("0B1000")); + ASSERT_EQUALS(true, MathLib::isInt("0l")); + ASSERT_EQUALS(true, MathLib::isInt("0L")); + ASSERT_EQUALS(true, MathLib::isInt("0ul")); + ASSERT_EQUALS(true, MathLib::isInt("0ull")); + ASSERT_EQUALS(true, MathLib::isInt("0llu")); + ASSERT_EQUALS(true, MathLib::isInt("333L")); + ASSERT_EQUALS(true, MathLib::isInt("330L")); + ASSERT_EQUALS(true, MathLib::isInt("330llu")); + ASSERT_EQUALS(true, MathLib::isInt("07")); + ASSERT_EQUALS(true, MathLib::isInt("0123")); + ASSERT_EQUALS(false, MathLib::isInt("0.4")); + ASSERT_EQUALS(false, MathLib::isInt("2352.3f")); + ASSERT_EQUALS(false, MathLib::isInt("0.00004")); + ASSERT_EQUALS(false, MathLib::isInt("2352.00001f")); + ASSERT_EQUALS(false, MathLib::isInt(".4")); + ASSERT_EQUALS(false, MathLib::isInt("1.0E+1")); + ASSERT_EQUALS(false, MathLib::isInt("1.0E-1")); + ASSERT_EQUALS(false, MathLib::isInt("-1.0E+1")); + ASSERT_EQUALS(false, MathLib::isInt("+1.0E-1")); + ASSERT_EQUALS(false, MathLib::isInt("-1.E+1")); + ASSERT_EQUALS(false, MathLib::isInt("+1.E-1")); + ASSERT_EQUALS(false, MathLib::isInt(" 1.0E+1")); + // with whitespace in front + ASSERT_EQUALS(false, MathLib::isInt(" 1.0E-1")); + ASSERT_EQUALS(false, MathLib::isInt(" -1.0E+1")); + ASSERT_EQUALS(false, MathLib::isInt(" +1.0E-1")); + ASSERT_EQUALS(false, MathLib::isInt(" -1.E+1")); + ASSERT_EQUALS(false, MathLib::isInt(" +1.E-1")); + // with whitespace in front and end + ASSERT_EQUALS(false, MathLib::isInt(" 1.0E-1 ")); + ASSERT_EQUALS(false, MathLib::isInt(" -1.0E+1 ")); + ASSERT_EQUALS(false, MathLib::isInt(" +1.0E-1 ")); + ASSERT_EQUALS(false, MathLib::isInt(" -1.E+1 ")); + ASSERT_EQUALS(false, MathLib::isInt(" +1.E-1 ")); + // with whitespace in front and end + ASSERT_EQUALS(false, MathLib::isInt("1.0E-1 ")); + ASSERT_EQUALS(false, MathLib::isInt("-1.0E+1 ")); + ASSERT_EQUALS(false, MathLib::isInt("+1.0E-1 ")); + ASSERT_EQUALS(false, MathLib::isInt("-1.E+1 ")); + ASSERT_EQUALS(false, MathLib::isInt("+1.E-1 ")); + // test some garbage + ASSERT_EQUALS(false, MathLib::isInt("u")); + ASSERT_EQUALS(false, MathLib::isInt("l")); + ASSERT_EQUALS(false, MathLib::isInt("ul")); + ASSERT_EQUALS(false, MathLib::isInt("ll")); + ASSERT_EQUALS(false, MathLib::isInt("U")); + ASSERT_EQUALS(false, MathLib::isInt("L")); + ASSERT_EQUALS(false, MathLib::isInt("uL")); + ASSERT_EQUALS(false, MathLib::isInt("LL")); + ASSERT_EQUALS(false, MathLib::isInt("e2")); + ASSERT_EQUALS(false, MathLib::isInt("E2")); + ASSERT_EQUALS(false, MathLib::isInt(".e2")); + ASSERT_EQUALS(false, MathLib::isInt(".E2")); + ASSERT_EQUALS(false, MathLib::isInt("0x")); + ASSERT_EQUALS(false, MathLib::isInt("0xu")); + ASSERT_EQUALS(false, MathLib::isInt("0xl")); + ASSERT_EQUALS(false, MathLib::isInt("0xul")); + // test empty string + ASSERT_EQUALS(false, MathLib::isInt("")); + } + + void isbin() const { + // positive testing + ASSERT_EQUALS(true, MathLib::isBin("0b0")); + ASSERT_EQUALS(true, MathLib::isBin("0b1")); + ASSERT_EQUALS(true, MathLib::isBin("+0b1")); + ASSERT_EQUALS(true, MathLib::isBin("-0b1")); + ASSERT_EQUALS(true, MathLib::isBin("0b11010111")); + ASSERT_EQUALS(true, MathLib::isBin("-0b11010111")); + ASSERT_EQUALS(true, MathLib::isBin("0B11010111")); + ASSERT_EQUALS(true, MathLib::isBin("0b11010111u")); + ASSERT_EQUALS(true, MathLib::isBin("0b11010111ul")); + ASSERT_EQUALS(true, MathLib::isBin("0b11010111ull")); + ASSERT_EQUALS(true, MathLib::isBin("0b11010111l")); + ASSERT_EQUALS(true, MathLib::isBin("0b11010111ll")); + ASSERT_EQUALS(true, MathLib::isBin("0b11010111llu")); + ASSERT_EQUALS(true, MathLib::isBin("0b11010111l")); + ASSERT_EQUALS(true, MathLib::isBin("0b11010111lu")); + ASSERT_EQUALS(false, MathLib::isBin("0b11010111lul")); // Suffix LUL not allowed + + // negative testing + ASSERT_EQUALS(false, MathLib::isBin("100101bx")); + ASSERT_EQUALS(false, MathLib::isBin("0")); + ASSERT_EQUALS(false, MathLib::isBin("0B")); + ASSERT_EQUALS(false, MathLib::isBin("0C")); + ASSERT_EQUALS(false, MathLib::isBin("+0B")); + ASSERT_EQUALS(false, MathLib::isBin("-0B")); + ASSERT_EQUALS(false, MathLib::isBin("-0Bx")); + ASSERT_EQUALS(false, MathLib::isBin("0b11010111x")); + ASSERT_EQUALS(false, MathLib::isBin("0b11010111ux")); + ASSERT_EQUALS(false, MathLib::isBin("0b11010111lx")); + ASSERT_EQUALS(false, MathLib::isBin("0b11010111lux")); + ASSERT_EQUALS(false, MathLib::isBin("0b11010111ulx")); + ASSERT_EQUALS(false, MathLib::isBin("0b11010111lulx")); + ASSERT_EQUALS(false, MathLib::isBin("0b11010111ullx")); + ASSERT_EQUALS(false, MathLib::isBin("0b11010111lll")); + + // test empty string + ASSERT_EQUALS(false, MathLib::isBin("")); + } + + void isnegative() const { + ASSERT_EQUALS(true, MathLib::isNegative("-1")); + ASSERT_EQUALS(true, MathLib::isNegative("-1.")); + ASSERT_EQUALS(true, MathLib::isNegative("-1.0")); + ASSERT_EQUALS(true, MathLib::isNegative("-1.0E+2")); + ASSERT_EQUALS(true, MathLib::isNegative("-1.0E-2")); + + ASSERT_EQUALS(false, MathLib::isNegative("+1")); + ASSERT_EQUALS(false, MathLib::isNegative("+1.")); + ASSERT_EQUALS(false, MathLib::isNegative("+1.0")); + ASSERT_EQUALS(false, MathLib::isNegative("+1.0E+2")); + ASSERT_EQUALS(false, MathLib::isNegative("+1.0E-2")); + // test empty string + ASSERT_EQUALS(false, MathLib::isNegative("")); + } + + void isoct() const { + // octal number format: [+|-]0[0-7][suffix] + // positive testing + ASSERT_EQUALS(true, MathLib::isOct("010")); + ASSERT_EQUALS(true, MathLib::isOct("+010")); + ASSERT_EQUALS(true, MathLib::isOct("-010")); + ASSERT_EQUALS(true, MathLib::isOct("0175")); + ASSERT_EQUALS(true, MathLib::isOct("+0175")); + ASSERT_EQUALS(true, MathLib::isOct("-0175")); + ASSERT_EQUALS(true, MathLib::isOct("00")); + ASSERT_EQUALS(true, MathLib::isOct("02")); + ASSERT_EQUALS(true, MathLib::isOct("+042")); + ASSERT_EQUALS(true, MathLib::isOct("-042")); + ASSERT_EQUALS(true, MathLib::isOct("+042U")); + ASSERT_EQUALS(true, MathLib::isOct("-042U")); + ASSERT_EQUALS(true, MathLib::isOct("+042L")); + ASSERT_EQUALS(true, MathLib::isOct("-042L")); + ASSERT_EQUALS(true, MathLib::isOct("+042LU")); + ASSERT_EQUALS(true, MathLib::isOct("-042LU")); + ASSERT_EQUALS(true, MathLib::isOct("+042UL")); + ASSERT_EQUALS(true, MathLib::isOct("-042UL")); + ASSERT_EQUALS(true, MathLib::isOct("+042ULL")); + ASSERT_EQUALS(true, MathLib::isOct("-042ULL")); + ASSERT_EQUALS(true, MathLib::isOct("+042LLU")); + ASSERT_EQUALS(true, MathLib::isOct("-042LLU")); + + // test empty string + ASSERT_EQUALS(false, MathLib::isOct("")); + + // negative testing + ASSERT_EQUALS(false, MathLib::isOct("0")); + ASSERT_EQUALS(false, MathLib::isOct("-0x175")); + ASSERT_EQUALS(false, MathLib::isOct("-0_garbage_")); + ASSERT_EQUALS(false, MathLib::isOct(" ")); + ASSERT_EQUALS(false, MathLib::isOct(" ")); + ASSERT_EQUALS(false, MathLib::isOct("02.")); + ASSERT_EQUALS(false, MathLib::isOct("02E2")); + ASSERT_EQUALS(false, MathLib::isOct("+042x")); + ASSERT_EQUALS(false, MathLib::isOct("-042x")); + ASSERT_EQUALS(false, MathLib::isOct("+042Ux")); + ASSERT_EQUALS(false, MathLib::isOct("-042Ux")); + ASSERT_EQUALS(false, MathLib::isOct("+042Lx")); + ASSERT_EQUALS(false, MathLib::isOct("-042Lx")); + ASSERT_EQUALS(false, MathLib::isOct("+042ULx")); + ASSERT_EQUALS(false, MathLib::isOct("-042ULx")); + ASSERT_EQUALS(false, MathLib::isOct("+042LLx")); + ASSERT_EQUALS(false, MathLib::isOct("-042LLx")); + ASSERT_EQUALS(false, MathLib::isOct("+042ULLx")); + ASSERT_EQUALS(false, MathLib::isOct("-042ULLx")); + ASSERT_EQUALS(false, MathLib::isOct("+042LLUx")); + ASSERT_EQUALS(false, MathLib::isOct("-042LLUx")); + ASSERT_EQUALS(false, MathLib::isOct("+042LUL")); + ASSERT_EQUALS(false, MathLib::isOct("-042LUL")); + // white space in front + ASSERT_EQUALS(false, MathLib::isOct(" -042ULL")); + // trailing white space + ASSERT_EQUALS(false, MathLib::isOct("-042ULL ")); + // front and trailing white space + ASSERT_EQUALS(false, MathLib::isOct(" -042ULL ")); + ASSERT_EQUALS(false, MathLib::isOct("+042LUL+0")); + } + + void isFloatHex() const { + // hex number syntax: [sign]0x[hexnumbers][suffix] + ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.999999999999ap-4")); + ASSERT_EQUALS(true, MathLib::isFloatHex("0x0.3p10")); + ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.fp3")); + ASSERT_EQUALS(true, MathLib::isFloatHex("0x1P-1")); + ASSERT_EQUALS(true, MathLib::isFloatHex("0xcc.ccccccccccdp-11")); + ASSERT_EQUALS(true, MathLib::isFloatHex("0x3.243F6A88p+03")); + ASSERT_EQUALS(true, MathLib::isFloatHex("0xA.Fp-10")); + ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p-10f")); + ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p+10F")); + ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p+10l")); + ASSERT_EQUALS(true, MathLib::isFloatHex("0x1.2p+10L")); + ASSERT_EQUALS(true, MathLib::isFloatHex("0X.2p-0")); + + ASSERT_EQUALS(false, MathLib::isFloatHex("")); + ASSERT_EQUALS(false, MathLib::isFloatHex("0")); + ASSERT_EQUALS(false, MathLib::isFloatHex("0x")); + ASSERT_EQUALS(false, MathLib::isFloatHex("0xa")); + ASSERT_EQUALS(false, MathLib::isFloatHex("+0x")); + ASSERT_EQUALS(false, MathLib::isFloatHex("-0x")); + ASSERT_EQUALS(false, MathLib::isFloatHex("0x")); + ASSERT_EQUALS(false, MathLib::isFloatHex("0x.")); + ASSERT_EQUALS(false, MathLib::isFloatHex("0XP")); + ASSERT_EQUALS(false, MathLib::isFloatHex("0xx")); + ASSERT_EQUALS(false, MathLib::isFloatHex("0x1P+-1")); + ASSERT_EQUALS(false, MathLib::isFloatHex("0x1p+10e")); + ASSERT_EQUALS(false, MathLib::isFloatHex("0x1p+1af")); + ASSERT_EQUALS(false, MathLib::isFloatHex("0x1p+10LL")); + } + + void isIntHex() const { + // hex number syntax: [sign]0x[hexnumbers][suffix] + + // positive testing + ASSERT_EQUALS(true, MathLib::isIntHex("0xa")); + ASSERT_EQUALS(true, MathLib::isIntHex("0x2AF3")); + ASSERT_EQUALS(true, MathLib::isIntHex("-0xa")); + ASSERT_EQUALS(true, MathLib::isIntHex("-0x2AF3")); + ASSERT_EQUALS(true, MathLib::isIntHex("+0xa")); + ASSERT_EQUALS(true, MathLib::isIntHex("+0x2AF3")); + ASSERT_EQUALS(true, MathLib::isIntHex("0x0")); + ASSERT_EQUALS(true, MathLib::isIntHex("+0x0")); + ASSERT_EQUALS(true, MathLib::isIntHex("-0x0")); + ASSERT_EQUALS(true, MathLib::isIntHex("+0x0U")); + ASSERT_EQUALS(true, MathLib::isIntHex("-0x0U")); + ASSERT_EQUALS(true, MathLib::isIntHex("+0x0L")); + ASSERT_EQUALS(true, MathLib::isIntHex("-0x0L")); + ASSERT_EQUALS(true, MathLib::isIntHex("+0x0LU")); + ASSERT_EQUALS(true, MathLib::isIntHex("-0x0LU")); + ASSERT_EQUALS(true, MathLib::isIntHex("+0x0UL")); + ASSERT_EQUALS(true, MathLib::isIntHex("-0x0UL")); + ASSERT_EQUALS(true, MathLib::isIntHex("+0x0LL")); + ASSERT_EQUALS(true, MathLib::isIntHex("-0x0LL")); + ASSERT_EQUALS(true, MathLib::isIntHex("+0x0ULL")); + ASSERT_EQUALS(true, MathLib::isIntHex("-0x0ULL")); + ASSERT_EQUALS(true, MathLib::isIntHex("+0x0LLU")); + ASSERT_EQUALS(true, MathLib::isIntHex("-0x0LLU")); + ASSERT_EQUALS(true, MathLib::isIntHex("+0x0Z")); + ASSERT_EQUALS(true, MathLib::isIntHex("-0x0Z")); + ASSERT_EQUALS(true, MathLib::isIntHex("+0x0ZU")); + ASSERT_EQUALS(true, MathLib::isIntHex("-0x0ZU")); + ASSERT_EQUALS(true, MathLib::isIntHex("+0x0UZ")); + ASSERT_EQUALS(true, MathLib::isIntHex("-0x0UZ")); + ASSERT_EQUALS(true, MathLib::isIntHex("+0x0Uz")); + ASSERT_EQUALS(true, MathLib::isIntHex("-0x0Uz")); + + // negative testing + ASSERT_EQUALS(false, MathLib::isIntHex("+0x")); + ASSERT_EQUALS(false, MathLib::isIntHex("-0x")); + ASSERT_EQUALS(false, MathLib::isIntHex("0x")); + ASSERT_EQUALS(false, MathLib::isIntHex("0xl")); + ASSERT_EQUALS(false, MathLib::isIntHex("0xx")); + ASSERT_EQUALS(false, MathLib::isIntHex("-0175")); + ASSERT_EQUALS(false, MathLib::isIntHex("-0_garbage_")); + ASSERT_EQUALS(false, MathLib::isIntHex(" ")); + ASSERT_EQUALS(false, MathLib::isIntHex(" ")); + ASSERT_EQUALS(false, MathLib::isIntHex("0")); + ASSERT_EQUALS(false, MathLib::isIntHex("+0x0Lz")); + ASSERT_EQUALS(false, MathLib::isIntHex("-0x0Lz")); + ASSERT_EQUALS(false, MathLib::isIntHex("+0x0LUz")); + ASSERT_EQUALS(false, MathLib::isIntHex("-0x0LUz")); + ASSERT_EQUALS(false, MathLib::isIntHex("+0x0ULz")); + ASSERT_EQUALS(false, MathLib::isIntHex("-0x0ULz")); + ASSERT_EQUALS(false, MathLib::isIntHex("+0x0LLz")); + ASSERT_EQUALS(false, MathLib::isIntHex("-0x0LLz")); + ASSERT_EQUALS(false, MathLib::isIntHex("+0x0ULLz")); + ASSERT_EQUALS(false, MathLib::isIntHex("-0x0ULLz")); + ASSERT_EQUALS(false, MathLib::isIntHex("+0x0LLUz")); + ASSERT_EQUALS(false, MathLib::isIntHex("-0x0LLUz")); + ASSERT_EQUALS(false, MathLib::isIntHex("0x0+0")); + ASSERT_EQUALS(false, MathLib::isIntHex("e2")); + ASSERT_EQUALS(false, MathLib::isIntHex("+E2")); + + // test empty string + ASSERT_EQUALS(false, MathLib::isIntHex("")); + } + + void isValidIntegerSuffix() const { + // negative testing + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("")); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("ux")); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("ulx")); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("uu")); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("lx")); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("lux")); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("lll")); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("garbage")); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("llu ")); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i")); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("iX")); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i6X")); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i64X")); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i64 ")); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i66")); + + // positive testing + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("u")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("ul")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("ull")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("uz")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("l")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("lu")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("ll")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("llu")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("llU")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("LLU")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("z")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("Z")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("zu")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("UZ")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("ZU")); + + // Microsoft extensions: + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("i64")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("I64")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("ui64")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("UI64")); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("i64", false)); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("I64", false)); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("ui64", false)); + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("UI64", false)); + + // User defined suffix literals + ASSERT_EQUALS(false, MathLib::isValidIntegerSuffix("_")); + ASSERT_EQUALS(true, MathLib::isValidIntegerSuffix("_MyUserDefinedLiteral")); + } + + void ispositive() const { + ASSERT_EQUALS(false, MathLib::isPositive("-1")); + ASSERT_EQUALS(false, MathLib::isPositive("-1.")); + ASSERT_EQUALS(false, MathLib::isPositive("-1.0")); + ASSERT_EQUALS(false, MathLib::isPositive("-1.0E+2")); + ASSERT_EQUALS(false, MathLib::isPositive("-1.0E-2")); + + ASSERT_EQUALS(true, MathLib::isPositive("+1")); + ASSERT_EQUALS(true, MathLib::isPositive("+1.")); + ASSERT_EQUALS(true, MathLib::isPositive("+1.0")); + ASSERT_EQUALS(true, MathLib::isPositive("+1.0E+2")); + ASSERT_EQUALS(true, MathLib::isPositive("+1.0E-2")); + + // test empty string + ASSERT_EQUALS(false, MathLib::isPositive("")); // "" is neither positive nor negative + } + + void isFloat() const { + ASSERT_EQUALS(false, MathLib::isFloat("")); + ASSERT_EQUALS(true, MathLib::isFloat("0.f")); + ASSERT_EQUALS(true, MathLib::isFloat("0.f")); + ASSERT_EQUALS(true, MathLib::isFloat("0xA.Fp-10")); + + // User defined suffix literals + ASSERT_EQUALS(false, MathLib::isFloat("0_")); + ASSERT_EQUALS(false, MathLib::isFloat("0._")); + ASSERT_EQUALS(false, MathLib::isFloat("0.1_")); + ASSERT_EQUALS(true, MathLib::isFloat("0.0_MyUserDefinedLiteral")); + ASSERT_EQUALS(true, MathLib::isFloat("0._MyUserDefinedLiteral")); + } + + void isDecimalFloat() const { + ASSERT_EQUALS(false, MathLib::isDecimalFloat("")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat(".")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("...")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat(".e")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat(".e2")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat(".E")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("+E.")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("+e.")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("-E.")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("-e.")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("-X")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("+X")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("-.")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("-.")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("-")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("+")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat(" ")); + + ASSERT_EQUALS(false, MathLib::isDecimalFloat("0")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("0 ")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat(" 0 ")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat(" 0")); + + ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.f")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.F")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.l")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.L")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("0. ")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat(" 0. ")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat(" 0.")); + + ASSERT_EQUALS(false, MathLib::isDecimalFloat("0..")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("..0..")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("..0")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.0")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.0f")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.0F")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.0l")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.0L")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0.")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0.")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0.0")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0.0")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("0E0")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0E0")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0E0")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0E+0")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0E-0")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0E+0")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0E-0")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0.0E+1")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+0.0E-1")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0.0E+1")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("-0.0E-1")); + + ASSERT_EQUALS(false, MathLib::isDecimalFloat("1")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("-1")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1e+1")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+1")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+100")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+100f")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+100F")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+100l")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+100L")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+007")); // to be sure about #5485 + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+001f")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+001F")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+001l")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+001L")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1E+10000")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("-1E+1")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("-1E+10000")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat(".1250E+04")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("-1E-1")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("-1E-10000")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1.23e+01")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("+1.23E+01")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1e+x")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+X")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+001lX")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+001LX")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+001f2")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+001F2")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1e+003x")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("+1E+003X")); + + + ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.4")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.3f")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.3F")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.3l")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.3L")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.00004")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.00001f")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.00001F")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.00001l")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("2352.00001L")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat(".4")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat(".3e2")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("1.0E+1")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("1.0E-1")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("-1.0E+1")); + + // User defined suffix literals + ASSERT_EQUALS(false, MathLib::isDecimalFloat("0_")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat(".1_")); + ASSERT_EQUALS(false, MathLib::isDecimalFloat("0.1_")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat("0.0_MyUserDefinedLiteral")); + ASSERT_EQUALS(true, MathLib::isDecimalFloat(".1_MyUserDefinedLiteral")); + } + + void naninf() const { + ASSERT_EQUALS("nan.0", MathLib::divide("0.0", "0.0")); // nan + ASSERT_EQUALS("nan.0", MathLib::divide("0.0", "0.f")); // nan (#5875) + ASSERT_EQUALS("nan.0", MathLib::divide("-0.0", "0.f")); // nan (#5875) + ASSERT_EQUALS("nan.0", MathLib::divide("-0.f", "0.f")); // nan (#5875) + ASSERT_EQUALS("nan.0", MathLib::divide("-0.0", "-0.f")); // nan (#5875) + ASSERT_EQUALS("nan.0", MathLib::divide("-.0", "-0.f")); // nan (#5875) + ASSERT_EQUALS("nan.0", MathLib::divide("0.0", "-0.f")); // nan (#5875) + ASSERT_EQUALS("nan.0", MathLib::divide("0.f", "-0.f")); // nan (#5875) + ASSERT_EQUALS("inf.0", MathLib::divide("3.0", "0.0")); // inf + ASSERT_EQUALS("inf.0", MathLib::divide("3.0", "0.f")); // inf (#5875) + ASSERT_EQUALS("-inf.0", MathLib::divide("-3.0", "0.0")); // -inf (#5142) + ASSERT_EQUALS("-inf.0", MathLib::divide("-3.0", "0.0f")); // -inf (#5142) + ASSERT_EQUALS("inf.0", MathLib::divide("-3.0", "-0.0f")); // inf (#5142) + } + + void isdec() const { + // positive testing + ASSERT_EQUALS(true, MathLib::isDec("1")); + ASSERT_EQUALS(true, MathLib::isDec("+1")); + ASSERT_EQUALS(true, MathLib::isDec("-1")); + ASSERT_EQUALS(true, MathLib::isDec("-100")); + ASSERT_EQUALS(true, MathLib::isDec("-1L")); + ASSERT_EQUALS(true, MathLib::isDec("1UL")); + + // negative testing + ASSERT_EQUALS(false, MathLib::isDec("-1.")); + ASSERT_EQUALS(false, MathLib::isDec("+1.")); + ASSERT_EQUALS(false, MathLib::isDec("-x")); + ASSERT_EQUALS(false, MathLib::isDec("+x")); + ASSERT_EQUALS(false, MathLib::isDec("x")); + ASSERT_EQUALS(false, MathLib::isDec("")); + + // User defined suffix literals + ASSERT_EQUALS(false, MathLib::isDec("0_")); + ASSERT_EQUALS(false, MathLib::isDec("+0_")); + ASSERT_EQUALS(false, MathLib::isDec("-1_")); + ASSERT_EQUALS(true, MathLib::isDec("0_MyUserDefinedLiteral")); + ASSERT_EQUALS(true, MathLib::isDec("+1_MyUserDefinedLiteral")); + ASSERT_EQUALS(true, MathLib::isDec("-1_MyUserDefinedLiteral")); + } + + 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")); + ASSERT_EQUALS(true, MathLib::isNullValue("+0X0")); + ASSERT_EQUALS(true, MathLib::isNullValue("-0X0")); + // unsigned integer zero value + ASSERT_EQUALS(true, MathLib::isNullValue("0U")); + ASSERT_EQUALS(true, MathLib::isNullValue("+0U")); + ASSERT_EQUALS(true, MathLib::isNullValue("-0U")); + // long integer zero value + ASSERT_EQUALS(true, MathLib::isNullValue("0L")); + ASSERT_EQUALS(true, MathLib::isNullValue("+0L")); + ASSERT_EQUALS(true, MathLib::isNullValue("-0L")); + // unsigned long integer zero value + ASSERT_EQUALS(true, MathLib::isNullValue("0UL")); + ASSERT_EQUALS(true, MathLib::isNullValue("+0UL")); + ASSERT_EQUALS(true, MathLib::isNullValue("-0UL")); + // unsigned long integer zero value + ASSERT_EQUALS(true, MathLib::isNullValue("0LU")); + ASSERT_EQUALS(true, MathLib::isNullValue("+0LU")); + ASSERT_EQUALS(true, MathLib::isNullValue("-0LU")); + // long long integer zero value + ASSERT_EQUALS(true, MathLib::isNullValue("0LL")); + ASSERT_EQUALS(true, MathLib::isNullValue("+0LL")); + ASSERT_EQUALS(true, MathLib::isNullValue("-0LL")); + // long long unsigned zero value + ASSERT_EQUALS(true, MathLib::isNullValue("0LLU")); + ASSERT_EQUALS(true, MathLib::isNullValue("+0LLU")); + ASSERT_EQUALS(true, MathLib::isNullValue("-0LLU")); + // unsigned long long zero value + ASSERT_EQUALS(true, MathLib::isNullValue("0ULL")); + ASSERT_EQUALS(true, MathLib::isNullValue("+0ULL")); + ASSERT_EQUALS(true, MathLib::isNullValue("-0ULL")); + // floating pointer zero value (no trailing zero after dot) + ASSERT_EQUALS(true, MathLib::isNullValue("0.")); + ASSERT_EQUALS(true, MathLib::isNullValue("+0.")); + ASSERT_EQUALS(true, MathLib::isNullValue("-0.")); + // floating pointer zero value (1 trailing zero after dot) + ASSERT_EQUALS(true, MathLib::isNullValue("0.0")); + ASSERT_EQUALS(true, MathLib::isNullValue("+0.0")); + ASSERT_EQUALS(true, MathLib::isNullValue("-0.0")); + // floating pointer zero value (3 trailing zeros after dot) + ASSERT_EQUALS(true, MathLib::isNullValue("0.000")); + ASSERT_EQUALS(true, MathLib::isNullValue("+0.000")); + ASSERT_EQUALS(true, MathLib::isNullValue("-0.000")); + // floating pointer zero value (no trailing zero after dot) + ASSERT_EQUALS(true, MathLib::isNullValue("00.")); + ASSERT_EQUALS(true, MathLib::isNullValue("+00.")); + ASSERT_EQUALS(true, MathLib::isNullValue("-00.")); + // floating pointer zero value (1 trailing zero after dot) + ASSERT_EQUALS(true, MathLib::isNullValue("00.0")); + ASSERT_EQUALS(true, MathLib::isNullValue("+00.0")); + ASSERT_EQUALS(true, MathLib::isNullValue("-00.0")); + // floating pointer zero value (3 trailing zero after dot) + ASSERT_EQUALS(true, MathLib::isNullValue("00.000")); + ASSERT_EQUALS(true, MathLib::isNullValue("+00.000")); + ASSERT_EQUALS(true, MathLib::isNullValue("-00.000")); + // floating pointer zero value (3 trailing zero after dot) + ASSERT_EQUALS(true, MathLib::isNullValue(".000")); + // integer scientific notation + ASSERT_EQUALS(true, MathLib::isNullValue("0E0")); + ASSERT_EQUALS(true, MathLib::isNullValue("+0E0")); + ASSERT_EQUALS(true, MathLib::isNullValue("-0E0")); + 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")); + 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")); + // hex float + ASSERT_EQUALS(true, MathLib::isNullValue("0x0p3")); + ASSERT_EQUALS(true, MathLib::isNullValue("0X0P3")); + ASSERT_EQUALS(true, MathLib::isNullValue("0X0p-3")); + ASSERT_EQUALS(true, MathLib::isNullValue("-0x0p3")); + ASSERT_EQUALS(true, MathLib::isNullValue("+0x0p3")); + + // 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("0xF")); + ASSERT_EQUALS(false, MathLib::isNullValue("0XFF")); + ASSERT_EQUALS(false, MathLib::isNullValue("0b01")); + ASSERT_EQUALS(false, MathLib::isNullValue("0B01")); + ASSERT_EQUALS(false, MathLib::isNullValue("0x1p0")); + ASSERT_EQUALS(false, MathLib::isNullValue("0x1P0")); + ASSERT_EQUALS(false, MathLib::isNullValue("0xap0")); + ASSERT_EQUALS(false, MathLib::isNullValue("0xbp0")); + ASSERT_EQUALS(false, MathLib::isNullValue("0xcp0")); + ASSERT_EQUALS(false, MathLib::isNullValue("0xdp0")); + ASSERT_EQUALS(false, MathLib::isNullValue("0xep0")); + ASSERT_EQUALS(false, MathLib::isNullValue("0xfp0")); + 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")); + ASSERT_EQUALS(false, MathLib::isNullValue("-ENOMEM")); + } + + void sin() const { + ASSERT_EQUALS("0.0", MathLib::sin("0")); + } + void cos() const { + ASSERT_EQUALS("1.0", MathLib::cos("0")); + } + void tan() const { + ASSERT_EQUALS("0.0", MathLib::tan("0")); + } + void abs() const { + ASSERT_EQUALS("", MathLib::abs("")); + ASSERT_EQUALS("0", MathLib::abs("0")); + ASSERT_EQUALS("+0", MathLib::abs("+0")); + ASSERT_EQUALS("0", MathLib::abs("-0")); + ASSERT_EQUALS("+1", MathLib::abs("+1")); + ASSERT_EQUALS("+1.0", MathLib::abs("+1.0")); + ASSERT_EQUALS("1", MathLib::abs("-1")); + ASSERT_EQUALS("1.0", MathLib::abs("-1.0")); + ASSERT_EQUALS("9007199254740991", MathLib::abs("9007199254740991")); + } + + void toString() const { + ASSERT_EQUALS("0.0", MathLib::toString(0.0)); + ASSERT_EQUALS("0.0", MathLib::toString(+0.0)); + ASSERT_EQUALS("0.0", MathLib::toString(-0.0)); + + ASSERT_EQUALS("1e-08", MathLib::toString(0.00000001)); + ASSERT_EQUALS("-1e-08", MathLib::toString(-0.00000001)); + + ASSERT_EQUALS("2.22507385851e-308", MathLib::toString(std::numeric_limits::min())); + ASSERT_EQUALS("1.79769313486e+308", MathLib::toString(std::numeric_limits::max())); + } + + void CPP14DigitSeparators() const { // Ticket #7137, #7565 + ASSERT(MathLib::isDigitSeparator("'", 0) == false); + ASSERT(MathLib::isDigitSeparator("123'0;", 3)); + ASSERT(MathLib::isDigitSeparator("foo(1'2);", 5)); + ASSERT(MathLib::isDigitSeparator("foo(1,1'2);", 7)); + ASSERT(MathLib::isDigitSeparator("int a=1'234-1'2-'0';", 7)); + ASSERT(MathLib::isDigitSeparator("int a=1'234-1'2-'0';", 13)); + ASSERT(MathLib::isDigitSeparator("int a=1'234-1'2-'0';", 16) == false); + ASSERT(MathLib::isDigitSeparator("int b=1+9'8;", 9)); + ASSERT(MathLib::isDigitSeparator("if (1'2) { char c = 'c'; }", 5)); + ASSERT(MathLib::isDigitSeparator("if (120%1'2) { char c = 'c'; }", 9)); + ASSERT(MathLib::isDigitSeparator("if (120&1'2) { char c = 'c'; }", 9)); + ASSERT(MathLib::isDigitSeparator("if (120|1'2) { char c = 'c'; }", 9)); + ASSERT(MathLib::isDigitSeparator("if (120%1'2) { char c = 'c'; }", 24) == false); + ASSERT(MathLib::isDigitSeparator("if (120%1'2) { char c = 'c'; }", 26) == false); + ASSERT(MathLib::isDigitSeparator("0b0000001'0010'01110", 14)); + } +}; + +REGISTER_TEST(TestMathLib) diff --git a/cppcheck-2.14.0/test/testmemleak.cpp b/cppcheck-2.14.0/test/testmemleak.cpp new file mode 100644 index 00000000..70432dba --- /dev/null +++ b/cppcheck-2.14.0/test/testmemleak.cpp @@ -0,0 +1,2875 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "settings.h" +#include "symboldatabase.h" +#include "token.h" + +#include + +class TestMemleak : private TestFixture { +public: + TestMemleak() : TestFixture("TestMemleak") {} + +private: + const Settings settings; + + void run() override { + TEST_CASE(testFunctionReturnType); + TEST_CASE(open); + } + +#define functionReturnType(code) functionReturnType_(code, __FILE__, __LINE__) + CheckMemoryLeak::AllocType functionReturnType_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + const CheckMemoryLeak c(&tokenizer, this, &settings); + + return (c.functionReturnType)(&tokenizer.getSymbolDatabase()->scopeList.front().functionList.front()); + } + + void testFunctionReturnType() { + { + const char code[] = "const char *foo()\n" + "{ return 0; }"; + ASSERT_EQUALS(CheckMemoryLeak::No, functionReturnType(code)); + } + + { + const char code[] = "Fred *newFred()\n" + "{ return new Fred; }"; + ASSERT_EQUALS(CheckMemoryLeak::New, functionReturnType(code)); + } + + { + const char code[] = "char *foo()\n" + "{ return new char[100]; }"; + ASSERT_EQUALS(CheckMemoryLeak::NewArray, functionReturnType(code)); + } + + { + const char code[] = "char *foo()\n" + "{\n" + " char *p = new char[100];\n" + " return p;\n" + "}"; + ASSERT_EQUALS(CheckMemoryLeak::NewArray, functionReturnType(code)); + } + } + + void open() { + const char code[] = "class A {\n" + " static int open() {\n" + " return 1;\n" + " }\n" + "\n" + " A() {\n" + " int ret = open();\n" + " }\n" + "};\n"; + + SimpleTokenizer tokenizer(settings, *this); + ASSERT(tokenizer.tokenize(code)); + + // there is no allocation + const Token *tok = Token::findsimplematch(tokenizer.tokens(), "ret ="); + const CheckMemoryLeak check(&tokenizer, nullptr, &settings); + ASSERT_EQUALS(CheckMemoryLeak::No, check.getAllocationType(tok->tokAt(2), 1)); + } +}; + +REGISTER_TEST(TestMemleak) + + + + + +class TestMemleakInFunction : public TestFixture { +public: + TestMemleakInFunction() : TestFixture("TestMemleakInFunction") {} + +private: + const Settings settings = settingsBuilder().library("std.cfg").library("posix.cfg").build(); + +#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) + void check_(const char* file, int line, const char code[]) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check for memory leaks.. + CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, &settings, this); + checkMemoryLeak.checkReallocUsage(); + } + + + void run() override { + TEST_CASE(realloc1); + TEST_CASE(realloc2); + TEST_CASE(realloc3); + TEST_CASE(realloc4); + TEST_CASE(realloc5); + TEST_CASE(realloc7); + TEST_CASE(realloc8); + TEST_CASE(realloc9); + TEST_CASE(realloc10); + TEST_CASE(realloc11); + TEST_CASE(realloc12); + TEST_CASE(realloc13); + TEST_CASE(realloc14); + TEST_CASE(realloc15); + TEST_CASE(realloc16); + TEST_CASE(realloc17); + TEST_CASE(realloc18); + TEST_CASE(realloc19); + TEST_CASE(realloc20); + TEST_CASE(realloc21); + TEST_CASE(realloc22); + TEST_CASE(realloc23); + TEST_CASE(realloc24); // #9228 + } + + void realloc1() { + check("void foo()\n" + "{\n" + " char *a = (char *)malloc(10);\n" + " a = realloc(a, 100);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout_str()); + } + + void realloc2() { + check("void foo()\n" + "{\n" + " char *a = (char *)malloc(10);\n" + " a = (char *)realloc(a, 100);\n" + " free(a);\n" + "}"); + + ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout_str()); + } + + void realloc3() { + check("void foo()\n" + "{\n" + " char *a = 0;\n" + " if ((a = realloc(a, 100)) == NULL)\n" + " return;\n" + " free(a);\n" + "}"); + + 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" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void realloc7() { + check("bool foo(size_t nLen, char* pData)\n" + "{\n" + " pData = (char*) realloc(pData, sizeof(char) + (nLen + 1)*sizeof(char));\n" + " if ( pData == NULL )\n" + " {\n" + " return false;\n" + " }\n" + " free(pData);\n" + " return true;\n" + "}"); + 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" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void realloc9() { + check("void foo()\n" + "{\n" + " x = realloc(x,100);\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" + "}"); + 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" + "}"); + 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" + "}"); + 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" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Common realloc mistake: \'m_options\' nulled but not freed upon failure\n", errout_str()); + } + + void realloc16() { + check("void f(char *zLine) {\n" + " zLine = realloc(zLine, 42);\n" + " if (zLine) {\n" + " free(zLine);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void realloc17() { + check("void foo()\n" + "{\n" + " void ***a = malloc(sizeof(a));\n" + " ***a = realloc(***(a), sizeof(a) * 2);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout_str()); + } + + void realloc18() { + check("void foo()\n" + "{\n" + " void *a = malloc(sizeof(a));\n" + " a = realloc((void*)a, sizeof(a) * 2);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout_str()); + } + + void realloc19() { + check("void foo()\n" + "{\n" + " void *a = malloc(sizeof(a));\n" + " a = (realloc((void*)((a)), sizeof(a) * 2));\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout_str()); + } + + void realloc20() { + check("void foo()\n" + "{\n" + " void *a = malloc(sizeof(a));\n" + " a = realloc((a) + 1, sizeof(a) * 2);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void realloc21() { + check("char *foo(char *bs0)\n" + "{\n" + " char *bs = bs0;\n" + " bs = realloc(bs, 100);\n" + " if (bs == NULL) return bs0;\n" + " return bs;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void realloc22() { + check("void foo(char **bsp)\n" + "{\n" + " char *bs = *bsp;\n" + " bs = realloc(bs, 100);\n" + " if (bs == NULL) return;\n" + " *bsp = bs;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void realloc23() { + check("void foo(struct ABC *s)\n" + "{\n" + " uint32_t *cigar = s->cigar;\n" + " if (!(cigar = realloc(cigar, 100 * sizeof(*cigar))))\n" + " return;\n" + " s->cigar = cigar;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void realloc24() { // #9228 + check("void f() {\n" + "void *a = NULL;\n" + "a = realloc(a, 20);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + "void *a = NULL;\n" + "a = malloc(10);\n" + "a = realloc(a, 20);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout_str()); + + check("void f() {\n" + "void *a = nullptr;\n" + "a = malloc(10);\n" + "a = realloc(a, 20);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Common realloc mistake: \'a\' nulled but not freed upon failure\n", errout_str()); + + check("void f(char *b) {\n" + "void *a = NULL;\n" + "a = b;\n" + "a = realloc(a, 20);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } +}; + +REGISTER_TEST(TestMemleakInFunction) + + + + + + + + +class TestMemleakInClass : public TestFixture { +public: + TestMemleakInClass() : TestFixture("TestMemleakInClass") {} + +private: + const Settings settings = settingsBuilder().severity(Severity::warning).severity(Severity::style).library("std.cfg").build(); + + /** + * Tokenize and execute leak check for given code + * @param code Source code + */ + void check_(const char* file, int line, const char code[]) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check for memory leaks.. + CheckMemoryLeakInClass checkMemoryLeak(&tokenizer, &settings, this); + (checkMemoryLeak.check)(); + } + + void run() override { + 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(class26); // ticket #10789 + TEST_CASE(class27); // ticket #8126 + + TEST_CASE(staticvar); + + TEST_CASE(free_member_in_sub_func); + + TEST_CASE(mismatch1); + TEST_CASE(mismatch2); // #5659 + + // allocating member variable in public function + TEST_CASE(func1); + TEST_CASE(func2); + } + + + void class1() { + check("class Fred\n" + "{\n" + "private:\n" + " char *str1;\n" + " char *str2;\n" + "public:\n" + " Fred();\n" + " ~Fred();\n" + "};\n" + "\n" + "Fred::Fred()\n" + "{\n" + " str1 = new char[10];\n" + " str2 = new char[10];\n" + "}\n" + "\n" + "Fred::~Fred()\n" + "{\n" + " delete [] str2;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout_str()); + + check("class Fred\n" + "{\n" + "private:\n" + " char *str1;\n" + " char *str2;\n" + "public:\n" + " Fred()\n" + " {\n" + " str1 = new char[10];\n" + " str2 = new char[10];\n" + " }\n" + " ~Fred()\n" + " {\n" + " delete [] str2;\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout_str()); + } + + void class2() { + check("class Fred\n" + "{\n" + "private:\n" + " char *str1;\n" + "public:\n" + " Fred();\n" + " ~Fred();\n" + "};\n" + "\n" + "Fred::Fred()\n" + "{\n" + " str1 = new char[10];\n" + "}\n" + "\n" + "Fred::~Fred()\n" + "{\n" + " free(str1);\n" + "}"); + ASSERT_EQUALS("[test.cpp:17]: (error) Mismatching allocation and deallocation: Fred::str1\n", errout_str()); + + check("class Fred\n" + "{\n" + "private:\n" + " char *str1;\n" + "public:\n" + " Fred()\n" + " {\n" + " str1 = new char[10];\n" + " }\n" + " ~Fred()\n" + " {\n" + " free(str1);\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:12]: (error) Mismatching allocation and deallocation: Fred::str1\n", errout_str()); + } + + void class3() { + check("class Token;\n" + "\n" + "class Tokenizer\n" + "{\n" + "private:\n" + " Token *_tokens;\n" + "\n" + "public:\n" + " Tokenizer();\n" + " ~Tokenizer();\n" + " void deleteTokens(Token *tok);\n" + "};\n" + "\n" + "Tokenizer::Tokenizer()\n" + "{\n" + " _tokens = new Token;\n" + "}\n" + "\n" + "Tokenizer::~Tokenizer()\n" + "{\n" + " deleteTokens(_tokens);\n" + "}\n" + "\n" + "void Tokenizer::deleteTokens(Token *tok)\n" + "{\n" + " while (tok)\n" + " {\n" + " Token *next = tok->next();\n" + " delete tok;\n" + " tok = next;\n" + " }\n" + "}"); + + ASSERT_EQUALS("", errout_str()); + + check("class Token;\n" + "\n" + "class Tokenizer\n" + "{\n" + "private:\n" + " Token *_tokens;\n" + "\n" + "public:\n" + " Tokenizer()\n" + " {\n" + " _tokens = new Token;\n" + " }\n" + " ~Tokenizer()\n" + " {\n" + " deleteTokens(_tokens);\n" + " }\n" + " void deleteTokens(Token *tok)\n" + " {\n" + " while (tok)\n" + " {\n" + " Token *next = tok->next();\n" + " delete tok;\n" + " tok = next;\n" + " }\n" + " }\n" + "};"); + + ASSERT_EQUALS("", errout_str()); + } + + void class4() { + check("struct ABC;\n" + "class Fred\n" + "{\n" + "private:\n" + " void addAbc(ABC *abc);\n" + "public:\n" + " void click();\n" + "};\n" + "\n" + "void Fred::addAbc(ABC* abc)\n" + "{\n" + " AbcPosts->Add(abc);\n" + "}\n" + "\n" + "void Fred::click()\n" + "{\n" + " ABC *p = new ABC;\n" + " addAbc( p );\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct ABC;\n" + "class Fred\n" + "{\n" + "private:\n" + " void addAbc(ABC* abc)\n" + " {\n" + " AbcPosts->Add(abc);\n" + " }\n" + "public:\n" + " void click()\n" + " {\n" + " ABC *p = new ABC;\n" + " addAbc( p );\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void class6() { + check("class Fred\n" + "{\n" + "public:\n" + " void foo();\n" + "};\n" + "\n" + "void Fred::foo()\n" + "{\n" + " char *str = new char[100];\n" + " delete [] str;\n" + " hello();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class Fred\n" + "{\n" + "public:\n" + " void foo()\n" + " {\n" + " char *str = new char[100];\n" + " delete [] str;\n" + " hello();\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void class7() { + check("class Fred\n" + "{\n" + "public:\n" + " int *i;\n" + " Fred();\n" + " ~Fred();\n" + "};\n" + "\n" + "Fred::Fred()\n" + "{\n" + " this->i = new int;\n" + "}\n" + "Fred::~Fred()\n" + "{\n" + " delete this->i;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class Fred\n" + "{\n" + "public:\n" + " int *i;\n" + " Fred()\n" + " {\n" + " this->i = new int;\n" + " }\n" + " ~Fred()\n" + " {\n" + " delete this->i;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void class8() { + check("class A\n" + "{\n" + "public:\n" + " void a();\n" + " void doNothing() { }\n" + "};\n" + "\n" + "void A::a()\n" + "{\n" + " int* c = new int(1);\n" + " delete c;\n" + " doNothing(c);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class A\n" + "{\n" + "public:\n" + " void a()\n" + " {\n" + " int* c = new int(1);\n" + " delete c;\n" + " doNothing(c);\n" + " }\n" + " void doNothing() { }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void class9() { + check("class A\n" + "{\n" + "public:\n" + " int * p;\n" + " A();\n" + " ~A();\n" + "};\n" + "\n" + "A::A()\n" + "{ p = new int; }\n" + "\n" + "A::~A()\n" + "{ delete (p); }"); + ASSERT_EQUALS("", errout_str()); + + check("class A\n" + "{\n" + "public:\n" + " int * p;\n" + " A()\n" + " { p = new int; }\n" + " ~A()\n" + " { delete (p); }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void class10() { + check("class A\n" + "{\n" + "public:\n" + " int * p;\n" + " A();\n" + "};\n" + "A::A()\n" + "{ p = new int; }"); + ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout_str()); + + check("class A\n" + "{\n" + "public:\n" + " int * p;\n" + " A() { p = new int; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout_str()); + } + + void class11() { + check("class A\n" + "{\n" + "public:\n" + " int * p;\n" + " A() : p(new int[10])\n" + " { }" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout_str()); + + check("class A\n" + "{\n" + "public:\n" + " int * p;\n" + " A();\n" + "};\n" + "A::A() : p(new int[10])\n" + "{ }"); + ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout_str()); + } + + void class12() { + check("class A\n" + "{\n" + "private:\n" + " int *p;\n" + "public:\n" + " A();\n" + " ~A();\n" + " void cleanup();" + "};\n" + "\n" + "A::A()\n" + "{ p = new int[10]; }\n" + "\n" + "A::~A()\n" + "{ }\n" + "\n" + "void A::cleanup()\n" + "{ delete [] p; }"); + ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout_str()); + + check("class A\n" + "{\n" + "private:\n" + " int *p;\n" + "public:\n" + " A()\n" + " { p = new int[10]; }\n" + " ~A()\n" + " { }\n" + " void cleanup()\n" + " { delete [] p; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:4]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout_str()); + } + + void class13() { + check("class A\n" + "{\n" + "private:\n" + " int *p;\n" + "public:\n" + " A();\n" + " ~A();\n" + " void foo();" + "};\n" + "\n" + "A::A()\n" + "{ }\n" + "\n" + "A::~A()\n" + "{ }\n" + "\n" + "void A::foo()\n" + "{ p = new int[10]; delete [] p; }"); + ASSERT_EQUALS("[test.cpp:17]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n", errout_str()); + + check("class A\n" + "{\n" + "private:\n" + " int *p;\n" + "public:\n" + " A()\n" + " { }\n" + " ~A()\n" + " { }\n" + " void foo()\n" + " { p = new int[10]; delete [] p; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:11]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n", errout_str()); + } + + void class14() { + check("class A\n" + "{\n" + " int *p;\n" + "public:\n" + " void init();\n" + "};\n" + "\n" + "void A::init()\n" + "{ p = new int[10]; }"); + ASSERT_EQUALS("[test.cpp:9]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" + "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout_str()); + + check("class A\n" + "{\n" + " int *p;\n" + "public:\n" + " void init()\n" + " { p = new int[10]; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" + "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout_str()); + + + check("class A\n" + "{\n" + " int *p;\n" + "public:\n" + " void init();\n" + "};\n" + "\n" + "void A::init()\n" + "{ p = new int; }"); + ASSERT_EQUALS("[test.cpp:9]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" + "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout_str()); + + check("class A\n" + "{\n" + " int *p;\n" + "public:\n" + " void init()\n" + " { p = new int; }\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" + "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout_str()); + + + check("class A\n" + "{\n" + " int *p;\n" + "public:\n" + " void init();\n" + "};\n" + "\n" + "void A::init()\n" + "{ p = malloc(sizeof(int)*10); }"); + ASSERT_EQUALS("[test.cpp:9]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" + "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout_str()); + + check("class A\n" + "{\n" + " int *p;\n" + "public:\n" + " void init()\n" + " { p = malloc(sizeof(int)*10); }\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (warning) Possible leak in public function. The pointer 'p' is not deallocated before it is allocated.\n" + "[test.cpp:3]: (style) Class 'A' is unsafe, 'A::p' can leak by wrong usage.\n", errout_str()); + } + + void class15() { + check("class A\n" + "{\n" + " int *p;\n" + "public:\n" + " A();\n" + " ~A() { delete [] p; }\n" + "};\n" + "A::A()\n" + "{ p = new int[10]; }"); + ASSERT_EQUALS("", errout_str()); + + check("class A\n" + "{\n" + " int *p;\n" + "public:\n" + " A()\n" + " { p = new int[10]; }\n" + " ~A() { delete [] p; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + + check("class A\n" + "{\n" + " int *p;\n" + "public:\n" + " A();\n" + " ~A() { delete p; }\n" + "};\n" + "A::A()\n" + "{ p = new int; }"); + ASSERT_EQUALS("", errout_str()); + + check("class A\n" + "{\n" + " int *p;\n" + "public:\n" + " A()\n" + " { p = new int; }\n" + " ~A() { delete p; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + + check("class A\n" + "{\n" + " int *p;\n" + "public:\n" + " A();\n" + " ~A() { free(p); }\n" + "};\n" + "A::A()\n" + "{ p = malloc(sizeof(int)*10); }"); + ASSERT_EQUALS("", errout_str()); + + check("class A\n" + "{\n" + " int *p;\n" + "public:\n" + " A()\n" + " { p = malloc(sizeof(int)*10); }\n" + " ~A() { free(p); }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void class16() { + // Ticket #1510 + check("class A\n" + "{\n" + " int *a;\n" + " int *b;\n" + "public:\n" + " A() { a = b = new int[10]; }\n" + " ~A() { delete [] a; }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void class17() { + // Ticket #1557 + check("class A {\n" + "private:\n" + " char *pd;\n" + "public:\n" + " void foo();\n" + "};\n" + "\n" + "void A::foo()\n" + "{\n" + " A::pd = new char[12];\n" + " delete [] A::pd;\n" + "}"); + ASSERT_EQUALS("[test.cpp:10]: (warning) Possible leak in public function. The pointer 'pd' is not deallocated before it is allocated.\n", errout_str()); + + check("class A {\n" + "private:\n" + " char *pd;\n" + "public:\n" + " void foo()\n" + " {\n" + " pd = new char[12];\n" + " delete [] pd;\n" + " }\n" + "};"); + ASSERT_EQUALS("[test.cpp:7]: (warning) Possible leak in public function. The pointer 'pd' is not deallocated before it is allocated.\n", errout_str()); + + check("class A {\n" + "private:\n" + " char *pd;\n" + "public:\n" + " void foo();\n" + "};\n" + "\n" + "void A::foo()\n" + "{\n" + " pd = new char[12];\n" + " delete [] pd;\n" + "}"); + ASSERT_EQUALS("[test.cpp:10]: (warning) Possible leak in public function. The pointer 'pd' is not deallocated before it is allocated.\n", errout_str()); + } + + void class18() { + // Ticket #853 + check("class A : public x\n" + "{\n" + "public:\n" + " A()\n" + " {\n" + " a = new char[10];\n" + " foo(a);\n" + " }\n" + "private:\n" + " char *a;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class A : public x\n" + "{\n" + "public:\n" + " A();\n" + "private:\n" + " char *a;\n" + "};\n" + "A::A()\n" + "{\n" + " a = new char[10];\n" + " foo(a);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void class19() { + // Ticket #2219 + check("class Foo\n" + "{\n" + "private:\n" + " TRadioButton* rp1;\n" + " TRadioButton* rp2;\n" + "public:\n" + " Foo();\n" + "};\n" + "Foo::Foo()\n" + "{\n" + " rp1 = new TRadioButton(this);\n" + " rp2 = new TRadioButton(this);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class TRadioButton { };\n" + "class Foo\n" + "{\n" + "private:\n" + " TRadioButton* rp1;\n" + " TRadioButton* rp2;\n" + "public:\n" + " Foo();\n" + "};\n" + "Foo::Foo()\n" + "{\n" + " rp1 = new TRadioButton;\n" + " rp2 = new TRadioButton;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) Class 'Foo' is unsafe, 'Foo::rp1' can leak by wrong usage.\n" + "[test.cpp:6]: (style) Class 'Foo' is unsafe, 'Foo::rp2' can leak by wrong usage.\n", errout_str()); + + check("class TRadioButton { };\n" + "class Foo\n" + "{\n" + "private:\n" + " TRadioButton* rp1;\n" + " TRadioButton* rp2;\n" + "public:\n" + " Foo();\n" + " ~Foo();\n" + "};\n" + "Foo::Foo()\n" + "{\n" + " rp1 = new TRadioButton;\n" + " rp2 = new TRadioButton;\n" + "}\n" + "Foo::~Foo()\n" + "{\n" + " delete rp1;\n" + " delete rp2;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void class20() { + check("namespace ns1 {\n" + " class Fred\n" + " {\n" + " private:\n" + " char *str1;\n" + " char *str2;\n" + " public:\n" + " Fred()\n" + " {\n" + " str1 = new char[10];\n" + " str2 = new char[10];\n" + " }\n" + " ~Fred()\n" + " {\n" + " delete [] str2;\n" + " }\n" + " };\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout_str()); + + check("namespace ns1 {\n" + " class Fred\n" + " {\n" + " private:\n" + " char *str1;\n" + " char *str2;\n" + " public:\n" + " Fred();\n" + " ~Fred();\n" + " };\n" + "\n" + " Fred::Fred()\n" + " {\n" + " str1 = new char[10];\n" + " str2 = new char[10];\n" + " }\n" + "\n" + " Fred::~Fred()\n" + " {\n" + " delete [] str2;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout_str()); + + check("namespace ns1 {\n" + " class Fred\n" + " {\n" + " private:\n" + " char *str1;\n" + " char *str2;\n" + " public:\n" + " Fred();\n" + " ~Fred();\n" + " };\n" + "}\n" + "ns1::Fred::Fred()\n" + "{\n" + " str1 = new char[10];\n" + " str2 = new char[10];\n" + "}\n" + "\n" + "ns1::Fred::~Fred()\n" + "{\n" + " delete [] str2;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout_str()); + + check("namespace ns1 {\n" + " namespace ns2 {\n" + " class Fred\n" + " {\n" + " private:\n" + " char *str1;\n" + " char *str2;\n" + " public:\n" + " Fred();\n" + " ~Fred();\n" + " };\n" + " }\n" + "}\n" + "ns1::ns2::Fred::Fred()\n" + "{\n" + " str1 = new char[10];\n" + " str2 = new char[10];\n" + "}\n" + "\n" + "ns1::ns2::Fred::~Fred()\n" + "{\n" + " delete [] str2;\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout_str()); + + check("namespace ns1 {\n" + " namespace ns2 {\n" + " namespace ns3 {\n" + " class Fred\n" + " {\n" + " private:\n" + " char *str1;\n" + " char *str2;\n" + " public:\n" + " Fred();\n" + " ~Fred();\n" + " };\n" + " }\n" + " }\n" + "}\n" + "ns1::ns2::ns3::Fred::Fred()\n" + "{\n" + " str1 = new char[10];\n" + " str2 = new char[10];\n" + "}\n" + "\n" + "ns1::ns2::ns3::Fred::~Fred()\n" + "{\n" + " delete [] str2;\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (style) Class 'Fred' is unsafe, 'Fred::str1' can leak by wrong usage.\n", errout_str()); + } + + void class21() { // ticket #2517 + check("struct B { };\n" + "struct C\n" + "{\n" + " B * b;\n" + " C(B * x) : b(x) { }\n" + "};\n" + "class A\n" + "{\n" + " B *b;\n" + " C *c;\n" + "public:\n" + " A() : b(new B()), c(new C(b)) { }\n" + "}"); + TODO_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", + "[test.cpp:9]: (style) Class 'A' is unsafe, 'A::b' 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" + "}"); + TODO_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", + "[test.cpp:9]: (style) Class 'A' is unsafe, 'A::b' 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 class26() { // ticket #10789 - crash + check("class C;\n" + "struct S {\n" + " S() { p = new C; }\n" + " ~S();\n" + " C* p;\n" + "};\n" + "S::~S() = default;\n"); + ASSERT_EQUALS("[test.cpp:5]: (style) Class 'S' is unsafe, 'S::p' can leak by wrong usage.\n", errout_str()); + } + + void class27() { // ticket #8126 - array of pointers + check("struct S {\n" + " S() {\n" + " for (int i = 0; i < 5; i++)\n" + " a[i] = new char[3];\n" + " }\n" + " char* a[5];\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:6]: (style) Class 'S' is unsafe, 'S::a' can leak by wrong usage.\n", 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()); + + check("struct S {\n" // 5678 + " ~S();\n" + " void f();\n" + " int* p;\n" + "};\n" + "void S::f() {\n" + " p = new char[1];\n" + " delete p;\n" + " p = 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:8]: (error) Mismatching allocation and deallocation: S::p\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" + "}"); + ASSERT_EQUALS("[test.cpp:17]: (warning) Possible leak in public function. The pointer 'data_' is not deallocated before it is allocated.\n" + "[test.cpp:18]: (error) Mismatching allocation and deallocation: Foo::data_\n", errout_str()); + + check("namespace NS\n" + "{\n" + "class Foo\n" + "{\n" + "public:\n" + " void fct(int i);\n" + "\n" + "private:\n" + " char* data_;\n" + "};\n" + "}\n" + "\n" + "using namespace NS;\n" + "\n" + "void Foo::fct(int i)\n" + "{\n" + " data_ = new char[42];\n" + " delete data_;\n" + " data_ = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:17]: (warning) Possible leak in public function. The pointer 'data_' is not deallocated before it is allocated.\n" + "[test.cpp:18]: (error) Mismatching allocation and deallocation: Foo::data_\n", errout_str()); + } + + void func1() { + check("class Fred\n" + "{\n" + "private:\n" + " char *s;\n" + "public:\n" + " Fred() { s = 0; }\n" + " ~Fred() { free(s); }\n" + " void xy()\n" + " { s = malloc(100); }\n" + "};"); + ASSERT_EQUALS("[test.cpp:9]: (warning) Possible leak in public function. The pointer 's' is not deallocated before it is allocated.\n", errout_str()); + + check("class Fred\n" + "{\n" + "public:\n" + " Fred() { s = 0; }\n" + " ~Fred() { free(s); }\n" + " void xy()\n" + " { s = malloc(100); }\n" + "private:\n" + " char *s;\n" + "};"); + ASSERT_EQUALS("[test.cpp:7]: (warning) Possible leak in public function. The pointer 's' is not deallocated before it is allocated.\n", errout_str()); + } + + void func2() { + check("class Fred\n" + "{\n" + "private:\n" + " char *s;\n" + "public:\n" + " Fred() { s = 0; }\n" + " ~Fred() { free(s); }\n" + " const Fred & operator = (const Fred &f)\n" + " { s = malloc(100); }\n" + "};"); + ASSERT_EQUALS("[test.cpp:9]: (warning) Possible leak in public function. The pointer 's' is not deallocated before it is allocated.\n", errout_str()); + } +}; + +REGISTER_TEST(TestMemleakInClass) + + + + + + + +class TestMemleakStructMember : public TestFixture { +public: + TestMemleakStructMember() : TestFixture("TestMemleakStructMember") {} + +private: + const Settings settings = settingsBuilder().library("std.cfg").library("posix.cfg").build(); + + void check_(const char* file, int line, const char code[], bool cpp = true) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code, cpp), file, line); + + // Check for memory leaks.. + CheckMemoryLeakStructMember checkMemoryLeakStructMember(&tokenizer, &settings, this); + (checkMemoryLeakStructMember.check)(); + } + + void run() override { + // 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); + TEST_CASE(assign4); // #11019 + + // 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 + TEST_CASE(function5); // #10381, #10382, #10158 + + // Handle if-else + TEST_CASE(ifelse); + + // Linked list + TEST_CASE(linkedlist); + + // struct variable is a global variable + TEST_CASE(globalvar); + + // local struct variable + TEST_CASE(localvars); + + // struct variable is a reference variable + TEST_CASE(refvar); + + // Segmentation fault in CheckMemoryLeakStructMember + TEST_CASE(trac5030); + + TEST_CASE(varid); // #5201: Analysis confused by (variable).attribute notation + TEST_CASE(varid_2); // #5315: Analysis confused by ((variable).attribute) notation + + TEST_CASE(customAllocation); + + TEST_CASE(lambdaInScope); // #9793 + } + + void err() { + check("static void foo()\n" + "{\n" + " struct ABC *abc = malloc(sizeof(struct ABC));\n" + " abc->a = malloc(10);\n" + " free(abc);\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: abc.a\n", errout_str()); + + check("static void foo()\n" + "{\n" + " struct ABC *abc = malloc(sizeof(struct ABC));\n" + " abc->a = malloc(10);\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: abc.a\n", errout_str()); + + check("static ABC * foo()\n" + "{\n" + " ABC *abc = malloc(sizeof(ABC));\n" + " abc->a = malloc(10);\n" + " abc->b = malloc(10);\n" + " if (abc->b == 0)\n" + " {\n" + " return 0;\n" + " }\n" + " return abc;\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (error) Memory leak: abc.a\n", errout_str()); + + check("static void foo(int a)\n" + "{\n" + " ABC *abc = malloc(sizeof(ABC));\n" + " abc->a = malloc(10);\n" + " if (a == 1)\n" + " {\n" + " free(abc->a);\n" + " return;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:10]: (error) Memory leak: abc.a\n", errout_str()); + } + + void goto_() { + check("static void foo()\n" + "{\n" + " struct ABC *abc = malloc(sizeof(struct ABC));\n" + " abc->a = malloc(10);\n" + " if (abc->a)\n" + " { goto out; }\n" + " free(abc);\n" + " return;\n" + "out:\n" + " free(abc->a);\n" + " free(abc);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ret1() { + check("static ABC * foo()\n" + "{\n" + " struct ABC *abc = malloc(sizeof(struct ABC));\n" + " abc->a = malloc(10);\n" + " return abc;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("static void foo(struct ABC *abc)\n" + "{\n" + " abc->a = malloc(10);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #7302 + check("void* foo() {\n" + " struct ABC abc;\n" + " abc.a = malloc(10);\n" + " return abc.a;\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + + check("void* foo() {\n" + " struct ABC abc;\n" + " abc.a = malloc(10);\n" + " return abc.b;\n" + "}", false); + ASSERT_EQUALS("[test.c:4]: (error) Memory leak: abc.a\n", errout_str()); + } + + void ret2() { + check("static ABC * foo()\n" + "{\n" + " struct ABC *abc = malloc(sizeof(struct ABC));\n" + " abc->a = malloc(10);\n" + " return &abc->self;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void assign1() { + check("static void foo()\n" + "{\n" + " struct ABC *abc = abc1;\n" + " abc->a = malloc(10);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("static void foo()\n" + "{\n" + " struct ABC *abc;\n" + " abc1 = abc = malloc(sizeof(ABC));\n" + " abc->a = malloc(10);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("static void foo()\n" + "{\n" + " struct msn_entry *ptr;\n" + " ptr = malloc(sizeof(struct msn_entry));\n" + " ptr->msn = malloc(100);\n" + " back = ptr;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + } + + void assign2() { + check("static void foo() {\n" + " struct ABC *abc = malloc(123);\n" + " abc->a = abc->b = malloc(10);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void assign3() { + check("void f(struct s *f1) {\n" + " struct s f2;\n" + " f2.a = malloc(100);\n" + " *f1 = f2;\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + } + + void assign4() { + check("struct S { int a, b, c; };\n" // #11019 + "void f() {\n" + " struct S s;\n" + " *&s.a = open(\"xx.log\", O_RDONLY);\n" + " ((s).b) = open(\"xx.log\", O_RDONLY);\n" + " (&s)->c = open(\"xx.log\", O_RDONLY);\n" + "}\n", false); + ASSERT_EQUALS("[test.c:7]: (error) Memory leak: s.a\n" + "[test.c:7]: (error) Memory leak: s.b\n" + "[test.c:7]: (error) Memory leak: s.c\n", + errout_str()); + + check("struct S { int *p, *q; };\n" // #7705 + "void f(S s) {\n" + " s.p = new int[10];\n" + " s.q = malloc(40);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Memory leak: s.p\n" + "[test.cpp:5]: (error) Memory leak: s.q\n", + errout_str()); + + check("struct S** f(struct S** s) {\n" // don't throw + " struct S** ret = malloc(sizeof(*ret));\n" + " ret[0] = malloc(sizeof(**s));\n" + " ret[0]->g = strdup(s[0]->g);\n" + " return ret;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void run_rcmd(enum rcommand rcmd, rsh_session *sess, char *cmd) {\n" + " sess->fp = popen(cmd, rcmd == RSH_PIPE_READ ? \"r\" : \"w\");\n" + "}\n", false); + ASSERT_EQUALS("", errout_str()); + + check("struct S { char* a[2]; };\n" + "enum E { E0, E1 };\n" + "void f(struct S* s, enum E e, const char* n) {\n" + " free(s->a[e]);\n" + " s->a[e] = strdup(n);\n" + "}\n", false); + ASSERT_EQUALS("", errout_str()); + + check("void f(struct S** s, const char* c) {\n" + " *s = malloc(sizeof(struct S));\n" + " (*s)->value = strdup(c);\n" + "}\n", false); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " size_t mpsz;\n" + " void* hdr;\n" + "};\n" + "void f(struct S s[static 1U], int fd, size_t size) {\n" + " s->mpsz = size;\n" + " s->hdr = mmap(NULL, s->mpsz, PROT_READ, MAP_SHARED, fd, 0);\n" + "}\n", false); + ASSERT_EQUALS("", errout_str()); + + check("void f(type_t t) {\n" + " t->p = malloc(10);\n" + " t->x.p = malloc(10);\n" + " t->y[2].p = malloc(10);\n" + "}\n", false); + ASSERT_EQUALS("", errout_str()); + } + + void failedAllocation() { + check("static struct ABC * foo()\n" + "{\n" + " struct ABC *abc = malloc(sizeof(struct ABC));\n" + " abc->a = malloc(10);\n" + " if (!abc->a)\n" + " {\n" + " free(abc);\n" + " return 0;\n" + " }\n" + " return abc;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void function1() { + // Not found function => assume that the function may deallocate + check("static void foo()\n" + "{\n" + " struct ABC *abc = malloc(sizeof(struct ABC));\n" + " abc->a = malloc(10);\n" + " func(abc);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("static void foo()\n" + "{\n" + " struct ABC *abc = malloc(sizeof(struct ABC));\n" + " abclist.push_back(abc);\n" + " abc->a = malloc(10);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + // #2848: Taking address in function 'assign' + void function2() { + check("void f() {\n" + " A a = { 0 };\n" + " a.foo = (char *) malloc(10);\n" + " assign(&a);\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + } + + // #3024: kernel list + void function3() { + check("void f() {\n" + " struct ABC *abc = malloc(100);\n" + " abc.a = (char *) malloc(10);\n" + " list_add_tail(&abc->list, head);\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + } + + // #3038: deallocating in function + void function4() { + check("void a(char *p) { char *x = p; free(x); }\n" + "void b() {\n" + " struct ABC abc;\n" + " abc.a = (char *) malloc(10);\n" + " a(abc.a);\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + } + + void function5() { + check("struct s f() {\n" // #10381 + " struct s s1;\n" + " s1->x = malloc(1);\n" + " return (s1);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct nc_rpc nc_rpc_getconfig() {\n" // #10382 + " struct nc_rpc rpc;\n" + " rpc->filter = malloc(1);\n" + " return (nc_rpc)rpc;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("T* f(const char *str) {\n" // #10158 + " S* s = malloc(sizeof(S));\n" + " s->str = strdup(str);\n" + " return NewT(s);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("typedef struct s { char* str; } attr_t;\n" // #10152 + "attr_t* f(int type) {\n" + " attr_t a;\n" + " switch (type) {\n" + " case 1:\n" + " a.str = strdup(\"?\");\n" + " break;\n" + " default:\n" + " return NULL;\n" + " }\n" + " return g(&a);\n" + "}\n"); + TODO_ASSERT_EQUALS("", "[test.cpp:9]: (error) Memory leak: a.str\n", errout_str()); + } + + void ifelse() { + check("static void foo()\n" + "{\n" + " struct ABC *abc = malloc(sizeof(struct ABC));\n" + " if (x)" + " {\n" + " abc->a = malloc(10);\n" + " }\n" + " else\n" + " {\n" + " free(abc);\n" + " return;\n" + " }\n" + " free(abc->a);\n" + " free(abc);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void linkedlist() { + // #3904 - false positive when linked list is used + check("static void foo() {\n" + " struct ABC *abc = malloc(sizeof(struct ABC));\n" + " abc->next = malloc(sizeof(struct ABC));\n" + " abc->next->next = NULL;\n" + "\n" + " while (abc) {\n" + " struct ABC *next = abc->next;\n" + " free(abc);\n" + " abc = next;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void globalvar() { + check("struct ABC *abc;\n" + "\n" + "static void foo()\n" + "{\n" + " abc = malloc(sizeof(struct ABC));\n" + " abc->a = malloc(10);\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + // Ticket #933 Leaks with struct members not detected + void localvars() { + // Test error case + const char code1[] = "struct A {\n" + " FILE* f;\n" + " char* c;\n" + " void* m;\n" + "};\n" + "\n" + "void func() {\n" + " struct A a;\n" + " a.f = fopen(\"test\", \"r\");\n" + " a.c = new char[12];\n" + " a.m = malloc(12);\n" + "}"; + + check(code1, true); + ASSERT_EQUALS("[test.cpp:12]: (error) Memory leak: a.f\n" + "[test.cpp:12]: (error) Memory leak: a.c\n" + "[test.cpp:12]: (error) Memory leak: a.m\n", errout_str()); + check(code1, false); + ASSERT_EQUALS("[test.c:12]: (error) Memory leak: a.f\n" + "[test.c:12]: (error) Memory leak: a.m\n", errout_str()); + + // Test OK case + const char code2[] = "struct A {\n" + " FILE* f;\n" + " char* c;\n" + " void* m;\n" + "};\n" + "\n" + "void func() {\n" + " struct A a;\n" + " a.f = fopen(\"test\", \"r\");\n" + " a.c = new char[12];\n" + " a.m = malloc(12);\n" + " fclose(a.f);\n" + " delete [] a.c;\n" + " free(a.m);\n" + "}"; + + check(code2, true); + ASSERT_EQUALS("", errout_str()); + check(code2, false); + ASSERT_EQUALS("", errout_str()); + + // Test unknown struct. In C++, it might have a destructor + const char code3[] = "void func() {\n" + " struct A a;\n" + " a.f = fopen(\"test\", \"r\");\n" + "}"; + + check(code3, true); + ASSERT_EQUALS("", errout_str()); + check(code3, false); + ASSERT_EQUALS("[test.c:4]: (error) Memory leak: a.f\n", errout_str()); + + // Test struct with destructor + const char code4[] = "struct A {\n" + " FILE* f;\n" + " ~A();\n" + "};\n" + "void func() {\n" + " struct A a;\n" + " a.f = fopen(\"test\", \"r\");\n" + "}"; + + check(code4, true); + ASSERT_EQUALS("", errout_str()); + } + + void refvar() { // #8116 + check("struct Test\n" + "{\n" + " int* data;\n" + "};\n" + "\n" + "void foo(Test* x)\n" + "{\n" + " Test& y = *x;\n" + " y.data = malloc(10);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + // don't crash + void trac5030() { + check("bool bob( char const **column_ptrs ) {\n" + "unique_ptrotherbuffer{new char[otherbufsize+1]};\n" + "char *const oldbuffer = otherbuffer.get();\n" + "int const oldbufsize = otherbufsize;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varid() { // #5201 + check("struct S {\n" + " void *state_check_buff;\n" + "};\n" + "void f() {\n" + " S s;\n" + " (s).state_check_buff = (void* )malloc(1);\n" + " if (s.state_check_buff == 0)\n" + " return;\n" + "}", false); + ASSERT_EQUALS("[test.c:9]: (error) Memory leak: s.state_check_buff\n", errout_str()); + } + + void varid_2() { // #5315 + check("typedef struct foo { char *realm; } foo;\n" + "void build_principal() {\n" + " foo f;\n" + " ((f)->realm) = strdup(realm);\n" + " if(f->realm == NULL) {}\n" + "}", false); + ASSERT_EQUALS("[test.c:6]: (error) Memory leak: f.realm\n", errout_str()); + } + + void customAllocation() { // #4770 + check("char *myalloc(void) {\n" + " return malloc(100);\n" + "}\n" + "void func() {\n" + " struct ABC abc;\n" + " abc.a = myalloc();\n" + "}", false); + ASSERT_EQUALS("[test.c:7]: (error) Memory leak: abc.a\n", errout_str()); + } + + void lambdaInScope() { + check( // #9793 + "struct S { int * p{nullptr}; };\n" + "int main()\n" + "{\n" + " S s;\n" + " s.p = new int[10];\n" + " for (int i = 0; i < 10; ++i) {\n" + " s.p[i] = []() { return 1; }();\n" + " }\n" + " delete[] s.p;\n" + " return 0;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check( + "struct S { int* p; };\n" + "void f() {\n" + " auto g = []() {\n" + " S s;\n" + " s.p = new int;\n" + " };\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:6]: (error) Memory leak: s.p\n", errout_str()); + + check( + "struct S { int* p; };\n" + "void f() {\n" + " S s;\n" + " s.p = new int;\n" + " auto g = [&]() {\n" + " delete s.p;\n" + " };\n" + " g();\n" + "}\n" + "void h() {\n" + " S s;\n" + " s.p = new int;\n" + " [&]() {\n" + " delete s.p;\n" + " }();\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + } +}; + +REGISTER_TEST(TestMemleakStructMember) + + + + + +class TestMemleakNoVar : public TestFixture { +public: + TestMemleakNoVar() : TestFixture("TestMemleakNoVar") {} + +private: + const Settings settings = settingsBuilder().certainty(Certainty::inconclusive).severity(Severity::warning).library("std.cfg").library("posix.cfg").build(); + + void check_(const char* file, int line, const char code[]) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check for memory leaks.. + CheckMemoryLeakNoVar checkMemoryLeakNoVar(&tokenizer, &settings, this); + (checkMemoryLeakNoVar.check)(); + } + + void run() override { + // pass allocated memory to function.. + TEST_CASE(functionParameter); + + // never use leakable resource + TEST_CASE(missingAssignment); + + // pass allocated memory to function using a smart pointer + TEST_CASE(smartPointerFunctionParam); + TEST_CASE(resourceLeak); + + // Test getAllocationType for subfunction + TEST_CASE(getAllocationType); + + TEST_CASE(crash1); // #10729 + TEST_CASE(openDevNull); // #9653 + } + + void functionParameter() { + // standard function.. + check("void x() {\n" + " strcpy(a, strdup(p));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Allocation with strdup, strcpy doesn't release it.\n", errout_str()); + + check("char *x() {\n" + " char *ret = strcpy(malloc(10), \"abc\");\n" + " return ret;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("char *x() {\n" + " return strcpy(malloc(10), \"abc\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void x() {\n" + " free(malloc(10));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // user function.. + check("void set_error(const char *msg) {\n" + "}\n" + "\n" + "void x() {\n" + " set_error(strdup(p));\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:5]: (error) Allocation with strdup, set_error doesn't release it.\n", "", errout_str()); + + check("void f()\n" + "{\n" + " int fd;\n" + " fd = mkstemp(strdup(\"/tmp/file.XXXXXXXX\"));\n" + " close(fd);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Allocation with strdup, mkstemp doesn't release it.\n", "", errout_str()); + + check("void f()\n" + "{\n" + " if(TRUE || strcmp(strdup(a), b));\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Allocation with strdup, strcmp doesn't release it.\n", errout_str()); + + check("void f()\n" + "{\n" + " if(!strcmp(strdup(a), b) == 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Allocation with strdup, strcmp doesn't release it.\n", errout_str()); + + check("void f()\n" + "{\n" + " 42, strcmp(strdup(a), b);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Allocation with strdup, strcmp doesn't release it.\n", errout_str()); + + check("void f() {\n" + " assert(freopen(\"/dev/null\", \"r\", stdin));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void x() {\n" + " strcpy(a, (void*)strdup(p));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Allocation with strdup, strcpy doesn't release it.\n", errout_str()); + + check("void* malloc1() {\n" + " return (malloc(1));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("char *x() {\n" + " char *ret = (char*)strcpy(malloc(10), \"abc\");\n" + " return ret;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " free(malloc(1));\n" + " strcpy(a, strdup(p));\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Allocation with strdup, strcpy doesn't release it.\n", errout_str()); + + check("void f() {\n" + " memcmp(calloc(10, 10), strdup(q), 100);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Allocation with calloc, memcmp doesn't release it.\n" + "[test.cpp:2]: (error) Allocation with strdup, memcmp doesn't release it.\n", errout_str()); + + check("void* f(int size) {\n" + " return (void*) malloc(size);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int* f(int size) {\n" + " return static_cast(malloc(size));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() { if (new int[42]) {} }\n" // #10857 + "void g() { if (malloc(42)) {} }\n"); + ASSERT_EQUALS("[test.cpp:1]: (error) Allocation with new, if doesn't release it.\n" + "[test.cpp:2]: (error) Allocation with malloc, if doesn't release it.\n", + errout_str()); + + check("const char* string(const char* s) {\n" + " StringSet::iterator it = strings_.find(s);\n" + " if (it != strings_.end())\n" + " return *it;\n" + " return *strings_.insert(it, std::strcpy(new char[std::strlen(s) + 1], s));\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " static void load(const QString& projPath) {\n" + " if (proj_)\n" + " return;\n" + " proj_ = new ProjectT(projPath);\n" + " proj_->open(new OpenCallback());\n" + " }\n" + "private:\n" + " static Core::ProjectBase* proj_;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const std::string& s, int n) {\n" + " std::unique_ptr u;\n" + " u.reset(strcpy(new char[n], s.c_str()));\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { char* p; };\n" + "void f(S* s, int N) {\n" + " s->p = s->p ? strcpy(new char[N], s->p) : nullptr;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {};\n" // #11866 + "void f(bool b);\n" + "void g() {\n" + " f(new int());\n" + " f(new std::vector());\n" + " f(new S());\n" + " f(new tm());\n" + " f(malloc(sizeof(S)));\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Allocation with new, f doesn't release it.\n" + "[test.cpp:5]: (error) Allocation with new, f doesn't release it.\n" + "[test.cpp:6]: (error) Allocation with new, f doesn't release it.\n" + "[test.cpp:7]: (error) Allocation with new, f doesn't release it.\n" + "[test.cpp:8]: (error) Allocation with malloc, f doesn't release it.\n", + errout_str()); + + check("void f(uintptr_t u);\n" + "void g() {\n" + " f((uintptr_t)new int());\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(uint8_t u);\n" + "void g() {\n" + " f((uint8_t)new int());\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Allocation with new, f doesn't release it.\n", + errout_str()); + + check("void f(int i, T t);\n" + "void g(int i, U* u);\n" + "void h() {\n" + " f(1, new int());\n" + " g(1, new int());\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(T t);\n" + "struct U {};\n" + "void g() {\n" + " f(new U());\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void missingAssignment() { + check("void x()\n" + "{\n" + " malloc(10);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'malloc' is not stored.\n", errout_str()); + + check("void x()\n" + "{\n" + " calloc(10, 1);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'calloc' is not stored.\n", errout_str()); + + check("void x()\n" + "{\n" + " strdup(\"Test\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'strdup' is not stored.\n", errout_str()); + + check("void x()\n" + "{\n" + " (char*) malloc(10);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'malloc' is not stored.\n", errout_str()); + + check("void x()\n" + "{\n" + " char* ptr = malloc(10);\n" + " foo(ptr);\n" + " free(ptr);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("char** x(const char* str) {\n" + " char* ptr[] = { malloc(10), malloc(5), strdup(str) };\n" + " return ptr;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void x()\n" + "{\n" + " 42,malloc(42);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'malloc' is not stored.\n", errout_str()); + + check("void *f()\n" + "{\n" + " return malloc(10);\n" + "}\n" + "void x()\n" + "{\n" + " f();\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Return value of allocation function 'f' is not stored.\n", errout_str()); + + check("void f()\n" // #8100 + "{\n" + " auto lambda = [](){return malloc(10);};\n" + "}\n" + "void x()\n" + "{\n" + " f();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void *f() {\n" // #8848 + " struct S { void *alloc() { return malloc(10); } };\n" + "}\n" + "void x()\n" + "{\n" + " f();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void x()\n" + "{\n" + " if(!malloc(5)) fail();\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'malloc' is not stored.\n", errout_str()); + + check("FOO* factory() {\n" + " FOO* foo = new (std::nothrow) FOO;\n" + " return foo;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Ticket #6536 + check("struct S { S(int) {} };\n" + "void foo(int i) {\n" + " S socket(i);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Ticket #6693 + check("struct CTest {\n" + " void Initialise();\n" + " void malloc();\n" + "};\n" + "void CTest::Initialise() {\n" + " malloc();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" // #7348 - cast + " p = (::X*)malloc(42);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #7182 "crash: CheckMemoryLeak::functionReturnType()" + check("template auto unary_right_comma (Ts... ts) { return (ts , ...); }\n" + "template auto binary_left_comma (T x, Ts... ts) { return (x , ... , ts); }\n" + "int main() {\n" + " unary_right_comma (a);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " new int[10];\n" + " new int[10][5];\n" + " new int[10]();\n" + " new int[10]{};\n" + " new int[] { 1, 2, 3 };\n" + " new std::string;\n" + " new int;\n" + " new int();\n" + " new int(1);\n" + " new int{};\n" + " new int{ 1 };\n" + " new uint8_t[4];\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:3]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:4]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:5]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:6]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:7]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:8]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:9]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:10]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:11]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:12]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:13]: (error) Return value of allocation function 'new' is not stored.\n", + errout_str()); + + check("void f(int* p) {\n" + " new auto('c');\n" + " new(p) int;\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:2]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:3]: (error) Return value of allocation function 'new' is not stored.\n", + "", + errout_str()); + + check("void g(int* p) {\n" + " new QWidget;\n" + " new QWidget();\n" + " new QWidget{ this };\n" + " h(new int[10], 1);\n" + " h(new int[10][5], 1);\n" + " h(new int[10](), 1);\n" + " h(new int[10]{}, 1);\n" + " h(new int[] { 1, 2, 3 }, 1);\n" + " h(new auto('c'), 1);\n" + " h(new std::string, 1);\n" + " h(new int, 1);\n" + " h(new int{}, 1);\n" + " h(new int(), 1);\n" + " h(new int{ 1 }, 1);\n" + " h(new int(1), 1);\n" + " h(new(p) int, 1);\n" + " h(new QWidget, 1);\n" + " C{ new int[10], 1 };\n" + " C{ new int[10](), 1 };\n" + " C{ new int[10]{}, 1 };\n" + " C{ new int[] { 1, 2, 3 }, 1 };\n" + " C{ new auto('c'), 1 };\n" + " C{ new std::string, 1 };\n" + " C{ new int, 1 };\n" + " C{ new int{}, 1 };\n" + " C{ new int(), 1 };\n" + " C{ new int{ 1 }, 1 };\n" + " C{ new int(1), 1 };\n" + " C{ new(p) int, 1 };\n" + " C{ new QWidget, 1 };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool b) { if (b && malloc(42)) {} }\n" // // #10858 + "void g(bool b) { if (b || malloc(42)) {} }\n"); + ASSERT_EQUALS("[test.cpp:1]: (error) Return value of allocation function 'malloc' is not stored.\n" + "[test.cpp:2]: (error) Return value of allocation function 'malloc' is not stored.\n", + errout_str()); + + check("void f0(const bool b) { b ? new int : nullptr; }\n" // #11155 + "void f1(const bool b) { b ? nullptr : new int; }\n" + "int* g0(const bool b) { return b ? new int : nullptr; }\n" + "void g1(const bool b) { h(b, b ? nullptr : new int); }\n"); + ASSERT_EQUALS("[test.cpp:1]: (error) Return value of allocation function 'new' is not stored.\n" + "[test.cpp:2]: (error) Return value of allocation function 'new' is not stored.\n", + errout_str()); + + check("void f() {\n" // #11157 + " switch (*new int) { case 42: break; }\n" + " switch (*malloc(42)) { case 42: break; }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (error) Allocation with new, switch doesn't release it.\n" + "[test.cpp:3]: (error) Allocation with malloc, switch doesn't release it.\n", + errout_str()); + + check("void f() {\n" + " Ref remove(new StringBuffer());\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #11039 + " delete new int;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #11327 + " int* p = (new int[3]) + 1;\n" + " delete[] &p[-1];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void smartPointerFunctionParam() { + check("void x() {\n" + " f(shared_ptr(new int(42)), g());\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_shared() instead.\n", errout_str()); + + check("void x() {\n" + " h(12, f(shared_ptr(new int(42)), g()));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_shared() instead.\n", errout_str()); + + check("void x() {\n" + " f(unique_ptr(new int(42)), g());\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_unique() instead.\n", errout_str()); + + check("void x() {\n" + " f(g(), shared_ptr(new int(42)));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_shared() instead.\n", errout_str()); + + check("void x() {\n" + " f(g(), unique_ptr(new int(42)));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_unique() instead.\n", errout_str()); + + check("void x() {\n" + " f(shared_ptr(new char), make_unique(32));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If make_unique() throws, memory could be leaked. Use make_shared() instead.\n", errout_str()); + + check("void x() {\n" + " f(g(124), h(\"test\", 234), shared_ptr(new char));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If h() throws, memory could be leaked. Use make_shared() instead.\n", errout_str()); + + check("void x() {\n" + " f(shared_ptr(new std::string(\"\")), g());\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Unsafe allocation. If g() throws, memory could be leaked. Use make_shared() instead.\n", errout_str()); + + check("void g(int x) throw() { }\n" + "void x() {\n" + " f(g(124), shared_ptr(new char));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void __declspec(nothrow) g(int x) { }\n" + "void x() {\n" + " f(g(124), shared_ptr(new char));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + void resourceLeak() { + check("void foo() {\n" + " fopen(\"file.txt\", \"r\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Return value of allocation function 'fopen' is not stored.\n", errout_str()); + + check("void foo() {\n" + " FILE f* = fopen(\"file.txt\", \"r\");\n" + " freopen(\"file.txt\", \"r\", f);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'freopen' is not stored.\n", errout_str()); + + check("void foo() {\n" + " freopen(\"file.txt\", \"r\", stdin);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct Holder {\n" + " Holder(FILE* f) : file(f) {}\n" + " ~Holder() { fclose(file); }\n" + " FILE* file;\n" + "};\n" + "void foo() {\n" + " Holder h ( fopen(\"file.txt\", \"r\"));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct Holder {\n" + " Holder(FILE* f) : file(f) {}\n" + " ~Holder() { fclose(file); }\n" + " FILE* file;\n" + "};\n" + "void foo() {\n" + " Holder ( fopen(\"file.txt\", \"r\"));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct Holder {\n" + " Holder(FILE* f) : file(f) {}\n" + " ~Holder() { fclose(file); }\n" + " FILE* file;\n" + "};\n" + "void foo() {\n" + " Holder h { fopen(\"file.txt\", \"r\")};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct Holder {\n" + " Holder(FILE* f) : file(f) {}\n" + " ~Holder() { fclose(file); }\n" + " FILE* file;\n" + "};\n" + "void foo() {\n" + " Holder h = fopen(\"file.txt\", \"r\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct Holder {\n" + " Holder(FILE* f) : file(f) {}\n" + " ~Holder() { fclose(file); }\n" + " FILE* file;\n" + "};\n" + "void foo() {\n" + " Holder { fopen(\"file.txt\", \"r\")};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct Holder {\n" + " Holder(int i, FILE* f) : file(f) {}\n" + " ~Holder() { fclose(file); }\n" + " FILE* file;\n" + "};\n" + "void foo() {\n" + " Holder { 0, fopen(\"file.txt\", \"r\")};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void getAllocationType() { + // #7845 + check("class Thing { Thing(); };\n" + "Thing * makeThing() { Thing *thing = new Thing; return thing; }\n" + "\n" + "void f() {\n" + " makeThing();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #10631 + check("struct Thing {\n" + " Thing();\n" + "};\n" + "std::vector g_things;\n" + "Thing* makeThing() {\n" + " Thing* n = new Thing();\n" + " return n;\n" + "}\n" + "Thing::Thing() {\n" + " g_things.push_back(this);\n" + "}\n" + "void f() {\n" + " makeThing();\n" + " for(Thing* t : g_things) {\n" + " delete t;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void crash1() { // #10729 + check("void foo() {\n" + " extern void *realloc (void *ptr, size_t size);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " extern void *malloc (size_t size);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void openDevNull() { + check("void f() {\n" // #9653 + " (void)open(\"/dev/null\", O_RDONLY);\n" + " open(\"/dev/null\", O_WRONLY);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } +}; +REGISTER_TEST(TestMemleakNoVar) + + diff --git a/cppcheck-2.14.0/test/testnullpointer.cpp b/cppcheck-2.14.0/test/testnullpointer.cpp new file mode 100644 index 00000000..78e39e3a --- /dev/null +++ b/cppcheck-2.14.0/test/testnullpointer.cpp @@ -0,0 +1,4595 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "check.h" +#include "checknullpointer.h" +#include "ctu.h" +#include "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "library.h" +#include "settings.h" +#include "token.h" +#include "tokenize.h" + +#include +#include +#include +#include +#include + +class TestNullPointer : public TestFixture { +public: + TestNullPointer() : TestFixture("TestNullPointer") {} + +private: + const Settings settings = settingsBuilder().library("std.cfg").severity(Severity::warning).build(); + + void run() override { + TEST_CASE(nullpointerAfterLoop); + TEST_CASE(nullpointer1); + TEST_CASE(nullpointer2); + TEST_CASE(structDerefAndCheck); // dereferencing struct and then checking if it's null + TEST_CASE(pointerDerefAndCheck); + TEST_CASE(nullpointer5); // References should not be checked + TEST_CASE(nullpointerExecutionPaths); + TEST_CASE(nullpointerExecutionPathsLoop); + TEST_CASE(nullpointer7); + TEST_CASE(nullpointer9); + TEST_CASE(nullpointer10); + TEST_CASE(nullpointer11); // ticket #2812 + TEST_CASE(nullpointer12); // ticket #2470 + TEST_CASE(nullpointer15); // #3560 (fp: return p ? f(*p) : f(0)) + TEST_CASE(nullpointer16); // #3591 + TEST_CASE(nullpointer17); // #3567 + TEST_CASE(nullpointer18); // #1927 + TEST_CASE(nullpointer19); // #3811 + TEST_CASE(nullpointer20); // #3807 (fp: return p ? (p->x() || p->y()) : z) + TEST_CASE(nullpointer21); // #4038 (fp: if (x) p=q; else return;) + TEST_CASE(nullpointer23); // #4665 (false positive) + TEST_CASE(nullpointer24); // #5082 fp: chained assignment + TEST_CASE(nullpointer25); // #5061 + TEST_CASE(nullpointer26); // #3589 + TEST_CASE(nullpointer27); // #6568 + TEST_CASE(nullpointer28); // #6491 + TEST_CASE(nullpointer30); // #6392 + TEST_CASE(nullpointer31); // #8482 + TEST_CASE(nullpointer32); // #8460 + TEST_CASE(nullpointer33); + TEST_CASE(nullpointer34); + TEST_CASE(nullpointer35); + TEST_CASE(nullpointer36); // #9264 + TEST_CASE(nullpointer37); // #9315 + TEST_CASE(nullpointer38); + TEST_CASE(nullpointer39); // #2153 + TEST_CASE(nullpointer40); + TEST_CASE(nullpointer41); + TEST_CASE(nullpointer42); + TEST_CASE(nullpointer43); // #9404 + TEST_CASE(nullpointer44); // #9395, #9423 + TEST_CASE(nullpointer45); + TEST_CASE(nullpointer46); // #9441 + TEST_CASE(nullpointer47); // #6850 + TEST_CASE(nullpointer48); // #9196 + TEST_CASE(nullpointer49); // #7804 + TEST_CASE(nullpointer50); // #6462 + TEST_CASE(nullpointer51); + TEST_CASE(nullpointer52); + TEST_CASE(nullpointer53); // #8005 + TEST_CASE(nullpointer54); // #9573 + TEST_CASE(nullpointer55); // #8144 + TEST_CASE(nullpointer56); // #9701 + TEST_CASE(nullpointer57); // #9751 + TEST_CASE(nullpointer58); // #9807 + TEST_CASE(nullpointer59); // #9897 + TEST_CASE(nullpointer60); // #9842 + TEST_CASE(nullpointer61); + TEST_CASE(nullpointer62); + TEST_CASE(nullpointer63); + TEST_CASE(nullpointer64); + TEST_CASE(nullpointer65); // #9980 + TEST_CASE(nullpointer66); // #10024 + TEST_CASE(nullpointer67); // #10062 + TEST_CASE(nullpointer68); + TEST_CASE(nullpointer69); // #8143 + TEST_CASE(nullpointer70); + TEST_CASE(nullpointer71); // #10178 + TEST_CASE(nullpointer72); // #10215 + TEST_CASE(nullpointer73); // #10321 + TEST_CASE(nullpointer74); + TEST_CASE(nullpointer75); + TEST_CASE(nullpointer76); // #10408 + TEST_CASE(nullpointer77); + TEST_CASE(nullpointer78); // #7802 + TEST_CASE(nullpointer79); // #10400 + TEST_CASE(nullpointer80); // #10410 + TEST_CASE(nullpointer81); // #8724 + TEST_CASE(nullpointer82); // #10331 + TEST_CASE(nullpointer83); // #9870 + TEST_CASE(nullpointer84); // #9873 + TEST_CASE(nullpointer85); // #10210 + TEST_CASE(nullpointer86); + TEST_CASE(nullpointer87); // #9291 + TEST_CASE(nullpointer88); // #9949 + TEST_CASE(nullpointer89); // #10640 + TEST_CASE(nullpointer90); // #6098 + TEST_CASE(nullpointer91); // #10678 + TEST_CASE(nullpointer92); + TEST_CASE(nullpointer93); // #3929 + TEST_CASE(nullpointer94); // #11040 + TEST_CASE(nullpointer95); // #11142 + TEST_CASE(nullpointer96); // #11416 + TEST_CASE(nullpointer97); // #11229 + TEST_CASE(nullpointer98); // #11458 + TEST_CASE(nullpointer99); // #10602 + TEST_CASE(nullpointer100); // #11636 + TEST_CASE(nullpointer101); // #11382 + TEST_CASE(nullpointer102); + TEST_CASE(nullpointer103); + TEST_CASE(nullpointer_addressOf); // address of + TEST_CASE(nullpointerSwitch); // #2626 + TEST_CASE(nullpointer_cast); // #4692 + TEST_CASE(nullpointer_castToVoid); // #3771 + TEST_CASE(nullpointer_subfunction); + TEST_CASE(pointerCheckAndDeRef); // check if pointer is null and then dereference it + TEST_CASE(nullConstantDereference); // Dereference NULL constant + TEST_CASE(gcc_statement_expression); // Don't crash + TEST_CASE(snprintf_with_zero_size); + TEST_CASE(snprintf_with_non_zero_size); + TEST_CASE(printf_with_invalid_va_argument); + TEST_CASE(scanf_with_invalid_va_argument); + TEST_CASE(nullpointer_in_return); + TEST_CASE(nullpointer_in_typeid); + TEST_CASE(nullpointer_in_alignof); // #11401 + TEST_CASE(nullpointer_in_for_loop); + TEST_CASE(nullpointerDelete); + TEST_CASE(nullpointerSubFunction); + TEST_CASE(nullpointerExit); + TEST_CASE(nullpointerStdString); + TEST_CASE(nullpointerStdStream); + TEST_CASE(nullpointerSmartPointer); + TEST_CASE(functioncall); + TEST_CASE(functioncalllibrary); // use Library to parse function call + TEST_CASE(functioncallDefaultArguments); + TEST_CASE(nullpointer_internal_error); // #5080 + TEST_CASE(ticket6505); + TEST_CASE(subtract); + TEST_CASE(addNull); + TEST_CASE(isPointerDeRefFunctionDecl); + + TEST_CASE(ctuTest); + } + +#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) + void check_(const char* file, int line, const char code[], bool inconclusive = false, bool cpp = true) { + const Settings settings1 = settingsBuilder(settings).certainty(Certainty::inconclusive, inconclusive).build(); + + // Tokenize.. + SimpleTokenizer tokenizer(settings1, *this); + ASSERT_LOC(tokenizer.tokenize(code, cpp), file, line); + + // Check for null pointer dereferences.. + runChecks(tokenizer, this); + } + +#define checkP(...) checkP_(__FILE__, __LINE__, __VA_ARGS__) + void checkP_(const char* file, int line, const char code[]) { + const Settings settings1 = settingsBuilder(settings).certainty(Certainty::inconclusive, false).build(); + + std::vector files(1, "test.cpp"); + Tokenizer tokenizer(settings1, *this); + PreprocessorHelper::preprocess(code, files, tokenizer, *this); + + // Tokenizer.. + ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); + + // Check for null pointer dereferences.. + runChecks(tokenizer, this); + } + + + + void nullpointerAfterLoop() { + // extracttests.start: struct Token { const Token *next() const; std::string str() const; }; + check("void foo(const Token *tok)\n" + "{\n" + " while (tok);\n" + " tok = tok->next();\n" + "}", true); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'tok' is redundant or there is possible null pointer dereference: tok.\n", errout_str()); + + // #2681 + { + const char code[] = "void foo(const Token *tok)\n" + "{\n" + " while (tok && tok->str() == \"=\")\n" + " tok = tok->next();\n" + "\n" + " if (tok->str() != \";\")\n" + " ;\n" + "}\n"; + + check(code); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (warning) Either the condition 'tok' is redundant or there is possible null pointer dereference: tok.\n", errout_str()); + } + + check("void foo()\n" + "{\n" + " for (const Token *tok = tokens; tok; tok = tok->next())\n" + " {\n" + " while (tok && tok->str() != \";\")\n" + " tok = tok->next();\n" + " }\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (warning) Either the condition 'while' is redundant or there is possible null pointer dereference: tok.\n", "", errout_str()); + + check("void foo(Token &tok)\n" + "{\n" + " for (int i = 0; i < tok.size(); i++ )\n" + " {\n" + " while (!tok)\n" + " char c = tok.read();\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " for (const Token *tok = tokens; tok; tok = tok->next())\n" + " {\n" + " while (tok && tok->str() != \";\")\n" + " tok = tok->next();\n" + " if( !tok ) break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " for (const Token *tok = tokens; tok; tok = tok ? tok->next() : NULL)\n" + " {\n" + " while (tok && tok->str() != \";\")\n" + " tok = tok->next();\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(A*a)\n" + "{\n" + " switch (a->b()) {\n" + " case 1:\n" + " while( a ){\n" + " a = a->next;\n" + " }\n" + " break;\n" + " case 2:\n" + " a->b();\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // dereference in outer scope.. + check("void foo(int x, const Token *tok) {\n" + " if (x == 123) {\n" + " while (tok) tok = tok->next();\n" + " }\n" + " tok->str();\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Either the condition 'tok' is redundant or there is possible null pointer dereference: tok.\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()); + + check("struct b {\n" + " b * c;\n" + " int i;\n" + "}\n" + "void a(b * e) {\n" + " for (b *d = e;d; d = d->c)\n" + " while (d && d->i == 0)\n" + " d = d->c;\n" + " if (!d) throw;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct b {\n" + " b * c;\n" + " int i;\n" + "};\n" + "void f(b* e1, b* e2) {\n" + " for (const b* d = e1; d != e2; d = d->c) {\n" + " if (d && d->i != 0) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:6]: (warning) Either the condition 'd' is redundant or there is possible null pointer dereference: d.\n", errout_str()); + } + + void nullpointer1() { + // ticket #1923 - no false positive when using else if + check("void f(A *a)\n" + "{\n" + " if (a->x == 1)\n" + " {\n" + " a = a->next;\n" + " }\n" + " else if (a->x == 2) { }\n" + " if (a) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // ticket #2134 - sizeof doesn't dereference + check("void f() {\n" + " int c = 1;\n" + " int *list = NULL;\n" + " sizeof(*list);\n" + " if (!list)\n" + " ;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + // ticket #2245 - sizeof doesn't dereference + check("void f(Bar *p) {\n" + " if (!p) {\n" + " int sz = sizeof(p->x);\n" + " }\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + } + + void nullpointer2() { + // Null pointer dereference can only happen with pointers + check("void foo()\n" + "{\n" + " Fred fred;\n" + " while (fred);\n" + " fred.hello();\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + // Dereferencing a struct and then checking if it is null + // This is checked by this function: + // CheckOther::nullPointerStructByDeRefAndCheck + void structDerefAndCheck() { + // extracttests.start: struct ABC { int a; int b; int x; }; + + // errors.. + check("void foo(struct ABC *abc)\n" + "{\n" + " int a = abc->a;\n" + " if (!abc)\n" + " ;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!abc' is redundant or there is possible null pointer dereference: abc.\n", errout_str()); + + check("void foo(struct ABC *abc) {\n" + " bar(abc->a);\n" + " bar(x, abc->a);\n" + " bar(x, y, abc->a);\n" + " if (!abc)\n" + " ;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:2]: (warning) Either the condition '!abc' is redundant or there is possible null pointer dereference: abc.\n" + "[test.cpp:5] -> [test.cpp:3]: (warning) Either the condition '!abc' is redundant or there is possible null pointer dereference: abc.\n" + "[test.cpp:5] -> [test.cpp:4]: (warning) Either the condition '!abc' is redundant or there is possible null pointer dereference: abc.\n", errout_str()); + + check("void foo(ABC *abc) {\n" + " if (abc->a == 3) {\n" + " return;\n" + " }\n" + " if (abc) {}\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:5] -> [test.cpp:2]: (warning) Either the condition 'abc' is redundant or there is possible null pointer dereference: abc.\n", + errout_str()); + + check("void f(ABC *abc) {\n" + " if (abc->x == 0) {\n" + " return;\n" + " }\n" + " if (!abc);\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:2]: (warning) Either the condition '!abc' is redundant or there is possible null pointer dereference: abc.\n", errout_str()); + + // TODO: False negative if member of member is dereferenced + check("void foo(ABC *abc) {\n" + " abc->next->a = 0;\n" + " if (abc->next)\n" + " ;\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Possible null pointer dereference: abc - otherwise it is redundant to check it against null.\n", "", errout_str()); + + check("void foo(ABC *abc) {\n" + " abc->a = 0;\n" + " if (abc && abc->b == 0)\n" + " ;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'abc' is redundant or there is possible null pointer dereference: abc.\n", + errout_str()); + + // ok dereferencing in a condition + check("void foo(struct ABC *abc)\n" + "{\n" + " if (abc && abc->a);\n" + " if (!abc)\n" + " ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(struct ABC *abc) {\n" + " int x = abc && a(abc->x);\n" + " if (abc) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // ok to use a linked list.. + check("void foo(struct ABC *abc)\n" + "{\n" + " abc = abc->next;\n" + " if (!abc)\n" + " ;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f(struct ABC *abc) {\n" + " abc = (ABC *)(abc->_next);\n" + " if (abc) { }" + "}", true); + ASSERT_EQUALS("", errout_str()); + + // reassign struct.. + check("void foo(struct ABC *abc)\n" + "{\n" + " int a = abc->a;\n" + " abc = abc->next;\n" + " if (!abc)\n" + " ;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void foo(struct ABC *abc)\n" + "{\n" + " int a = abc->a;\n" + " f(&abc);\n" + " if (!abc)\n" + " ;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + // goto.. + check("void foo(struct ABC *abc)\n" + "{\n" + " int a;\n" + " if (!abc)\n" + " goto out;" + " a = abc->a;\n" + " return;\n" + "out:\n" + " if (!abc)\n" + " ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // loops.. + check("void foo(struct ABC *abc)\n" + "{\n" + " int a = abc->a;" + " do\n" + " {\n" + " if (abc)\n" + " abc = abc->next;\n" + " --a;\n" + " }\n" + " while (a > 0);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + " for (const Token *tok = _tokenizer->tokens(); tok; tok = tok->next())\n" + " {\n" + " while (tok && tok->str() != \"{\")\n" + " tok = tok->next();\n" + " if (!tok)\n" + " return;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // dynamic_cast.. + check("void foo(ABC *abc)\n" + "{\n" + " int a = abc->a;\n" + " if (!dynamic_cast(abc))\n" + " ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #2641 - global pointer, function call + check("ABC *abc;\n" + "void f() {\n" + " abc->a = 0;\n" + " do_stuff();\n" + " if (abc) { }\n" + "}"); + ASSERT_EQUALS("",errout_str()); + + check("Fred *fred;\n" + "void f() {\n" + " fred->foo();\n" + " if (fred) { }\n" + "}"); + ASSERT_EQUALS("",errout_str()); + + // #2641 - local pointer, function call + check("void f() {\n" + " ABC *abc = abc1;\n" + " abc->a = 0;\n" + " do_stuff();\n" + " if (abc) { }\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:5] -> [test.cpp:3]: (warning) Either the condition 'abc' is redundant or there is possible null pointer dereference: abc.\n", + errout_str()); + + // #2641 - local pointer, function call + check("void f(ABC *abc) {\n" + " abc->a = 0;\n" + " do_stuff();\n" + " if (abc) { }\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:2]: (warning) Either the condition 'abc' is redundant or there is possible null pointer dereference: abc.\n", + errout_str()); + + // #2691 - switch/break + check("void f(ABC *abc) {\n" + " switch ( x ) {\n" + " case 14:\n" + " sprintf(buf, \"%d\", abc->a);\n" + " break;\n" + " case 15:\n" + " if ( abc ) {}\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #3128 + check("void f(ABC *abc) {\n" + " x(!abc || y(abc->a));\n" + " if (abc) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(ABC *abc) {\n" + " x(def || !abc || y(def, abc->a));\n" + " if (abc) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(ABC *abc) {\n" + " x(abc && y(def, abc->a));\n" + " if (abc) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(ABC *abc) {\n" + " x(def && abc && y(def, abc->a));\n" + " if (abc) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #3228 - calling function with null object + { + const char code[] = "void f(Fred *fred) {\n" + " fred->x();\n" + " if (fred) { }\n" + "}"; + check(code); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'fred' is redundant or there is possible null pointer dereference: fred.\n", + errout_str()); + } + + // #3425 - false positives when there are macros + checkP("#define IF if\n" + "void f(struct FRED *fred) {\n" + " fred->x = 0;\n" + " IF(!fred){}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " BUFFER *buffer = get_buffer();\n" + " if (!buffer)\n" + " uv_fatal_error();\n" + " buffer->x = 11;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + // Dereferencing a pointer and then checking if it is null + void pointerDerefAndCheck() { + // extracttests.start: void bar(int); + + // errors.. + check("void foo(int *p)\n" + "{\n" + " *p = 0;\n" + " if (!p)\n" + " ;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + check("void foo(int *p)\n" + "{\n" + " *p = 0;\n" + " if (p) { }\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", + errout_str()); + + check("void foo(int *p)\n" + "{\n" + " *p = 0;\n" + " if (p || q) { }\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", + errout_str()); + + check("void foo(int *p)\n" + "{\n" + " bar(*p);\n" + " if (!p)\n" + " ;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + check("void foo(char *p)\n" + "{\n" + " strcpy(p, \"abc\");\n" + " if (!p)\n" + " ;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + check("void foo(char *p)\n" + "{\n" + " if (*p == 0) { }\n" + " if (!p) { }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + // no error + check("void foo()\n" + "{\n" + " int *p;\n" + " f(&p);\n" + " if (!p)\n" + " ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " int **p = f();\n" + " if (!p)\n" + " ;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int *p)\n" + "{\n" + " if (x)\n" + " p = 0;\n" + " else\n" + " *p = 0;\n" + " if (!p)\n" + " ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int x)\n" + "{\n" + " int a = 2 * x;" + " if (x == 0)\n" + " ;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int *p)\n" + "{\n" + " int var1 = p ? *p : 0;\n" + " if (!p)\n" + " ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int *p, bool x)\n" + "{\n" + " int var1 = x ? *p : 5;\n" + " if (!p)\n" + " ;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", + errout_str()); + + // while + check("void f(int *p) {\n" + " *p = 0;\n" + " while (p) { p = 0; }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int *p) {\n" + " *p = 0;\n" + " while (p) { }\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", + errout_str()); + + // Ticket #3125 + check("void foo(ABC *p)\n" + "{\n" + " int var1 = p ? (p->a) : 0;\n" + " if (!p)\n" + " ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(ABC *p)\n" + "{\n" + " int var1 = p ? (1 + p->a) : 0;\n" + " if (!p)\n" + " ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int * a=0;\n" + " if (!a) {};\n" + " int c = a ? 0 : 1;\n" + "}\n",true); + ASSERT_EQUALS("", errout_str()); + + // #3686 + check("void f() {\n" + " int * a=0;\n" + " if (!a) {};\n" + " int c = a ? b : b+1;\n" + "}\n",true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int * a=0;\n" + " if (!a) {};\n" + " int c = (a) ? b : b+1;\n" + "}\n",true); + ASSERT_EQUALS("", errout_str()); + + check("void foo(P *p)\n" + "{\n" + " while (p)\n" + " if (p->check())\n" + " break;\n" + " else\n" + " p = p->next();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(Document *doc) {\n" + " int x = doc && doc->x;\n" + " if (!doc) {\n" + " return;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #3128 - false positive + check("void f(int *p) {\n" + " assert(!p || (*p<=6));\n" + " if (p) { *p = 0; }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int *p) {\n" + " assert(p && (*p<=6));\n" + " if (p) { *p = 0; }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int *p) {\n" + " *p = 12;\n" + " assert(p && (*p<=6));\n" + " if (p) { *p = 0; }\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", + errout_str()); + + check("void foo(x *p)\n" + "{\n" + " p = p->next;\n" + " if (!p)\n" + " ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(x *p)\n" + "{\n" + " p = bar(p->next);\n" + " if (!p)\n" + " ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(x *p)\n" + "{\n" + " p = aa->bar(p->next);\n" + " if (!p)\n" + " ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(x *p)\n" + "{\n" + " p = *p2 = p->next;\n" + " if (!p)\n" + " ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(struct ABC *abc)\n" + "{\n" + " abc = abc ? abc->next : 0;\n" + " if (!abc)\n" + " ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(struct ABC *abc) {\n" // #4523 + " abc = (*abc).next;\n" + " if (abc) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(struct ABC *abc) {\n" // #4523 + " abc = (*abc->ptr);\n" + " if (abc) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(Item *item) {\n" + " x = item ? ab(item->x) : 0;\n" + " if (item) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(Item *item) {\n" + " item->x = 0;\n" + " a = b ? c : d;\n" + " if (item) { }\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:2]: (warning) Either the condition 'item' is redundant or there is possible null pointer dereference: item.\n", + errout_str()); + + check("BOOL GotoFlyAnchor()\n" // #2243 + "{\n" + " const SwFrm* pFrm = GetCurrFrm();\n" + " do {\n" + " pFrm = pFrm->GetUpper();\n" + " } while( pFrm && !pFrm->IsFlyFrm() );\n" + "\n" + " if( !pFrm )\n" + " return FALSE;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Ticket #2463 + check("struct A\n" + "{\n" + " B* W;\n" + "\n" + " void f() {\n" + " switch (InData) {\n" + " case 2:\n" + " if (!W) return;\n" + " W->foo();\n" + " break;\n" + " case 3:\n" + " f();\n" + " if (!W) return;\n" + " break;\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #2525 - sizeof + check("void f() {\n" + " int *test = NULL;\n" + " int c = sizeof(test[0]);\n" + " if (!test)\n" + " ;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f(type* p) {\n" // #4983 + " x(sizeof p[0]);\n" + " if (!p)\n" + " ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #3023 - checked deref + check("void f(struct ABC *abc) {\n" + " WARN_ON(!abc || abc->x == 0);\n" + " if (!abc) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void f(struct ABC *abc) {\n" + " WARN_ON(!abc || abc->x == 7);\n" + " if (!abc) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #3425 - false positives when there are macros + checkP("#define IF if\n" + "void f(int *p) {\n" + " *p = 0;\n" + " IF(!p){}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #3914 - false positive + " int *p;\n" + " ((p=ret()) && (x=*p));\n" + " if (p);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { struct T { char c; } *p; };\n" // #6541 + "char f(S* s) { return s->p ? 'a' : s->p->c; }\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (warning) Either the condition 's->p' is redundant or there is possible null pointer dereference: s->p.\n", + 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 = *(int*)0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference: (int*)0\n", errout_str()); + + check("static void foo(int x) {\n" + " int y = 5 + *(int*)0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference: (int*)0\n", errout_str()); + + { + const char code[] = "static void foo() {\n" + " Foo *abc = 0;\n" + " abc->a();\n" + "}\n"; + + check(code); + ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: abc\n", errout_str()); + } + + check("static void foo() {\n" + " std::cout << *(int*)0;" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference: (int*)0\n", errout_str()); + + check("void f()\n" + "{\n" + " char *c = 0;\n" + " {\n" + " delete c;\n" + " }\n" + " c[0] = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Null pointer dereference: c\n", errout_str()); + + check("static void foo() {\n" + " if (3 > *(int*)0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference: (int*)0\n", errout_str()); + + // no false positive.. + check("static void foo()\n" + "{\n" + " Foo *p = 0;\n" + " p = new Foo;\n" + " p->abcd();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " int sz = sizeof((*(struct dummy *)0).x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void get_offset(long &offset)\n" + "{\n" + " mystruct * temp; temp = 0;\n" + " offset = (long)(&(temp->z));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Ticket #1893 - try/catch inside else + check("int *test(int *Z)\n" + "{\n" + " int *Q=NULL;\n" + " if (Z) {\n" + " Q = Z;\n" + " }\n" + " else {\n" + " Z = new int;\n" + " try {\n" + " } catch(...) {\n" + " }\n" + " Q = Z;\n" + " }\n" + " *Q=1;\n" + " return Q;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int *test(int *Z)\n" + "{\n" + " int *Q=NULL;\n" + " if (Z) {\n" + " Q = Z;\n" + " }\n" + " else {\n" + " try {\n" + " } catch(...) {\n" + " }\n" + " }\n" + " *Q=1;\n" + " return Q;\n" + "}"); + ASSERT_EQUALS("[test.cpp:12]: (warning) Possible null pointer dereference: Q\n", errout_str()); + + // Ticket #2052 (false positive for 'else continue;') + check("void f() {\n" + " for (int x = 0; x < 5; ++x) {" + " int *p = 0;\n" + " if (a(x)) p=b(x);\n" + " else continue;\n" + " *p = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // function pointer.. + check("void foo()\n" + "{\n" + " void (*f)();\n" + " f = 0;\n" + " f();\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: f\n", errout_str()); + + check("int* g();\n" // #11007 + "int* f() {\n" + " static int* (*fun)() = 0;\n" + " if (!fun)\n" + " fun = g;\n" + " return fun();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // loops.. + check("void f() {\n" + " int *p = 0;\n" + " for (int i = 0; i < 10; ++i) {\n" + " int x = *p + 1;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout_str()); + + check("void f(int a) {\n" + " const char *p = 0;\n" + " if (a) {\n" + " p = \"abcd\";\n" + " }\n" + " for (int i = 0; i < 3; i++) {\n" + " if (a && (p[i] == '1'));\n" + " }\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + // ticket #2251: taking the address of member + check("void f() {\n" + " Fred *fred = 0;\n" + " int x = &fred->x;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + // ticket #3220: dereferencing a null pointer is UB + check("void f() {\n" + " Fred *fred = NULL;\n" + " fred->do_something();\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: fred\n", 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]: (warning) Possible null pointer dereference: p\n", errout_str()); + } + + // ticket #8831 - FP triggered by if/return/else sequence + { + check("void f(int *p, int *q) {\n" + " if (p == NULL)\n" + " return;\n" + " else if (q == NULL)\n" + " return;\n" + " *q = 0;\n" + "}\n" + "\n" + "void g() {\n" + " f(NULL, NULL);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + check("void f() {\n" // #5979 + " int* const crash = 0;\n" + " *crash = 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: crash\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" + "}"); + ASSERT_EQUALS("[test.cpp:10]: (warning) Possible null pointer dereference: argv32\n", errout_str()); + + // #2231 - error if assignment in loop is not used + // extracttests.start: int y[20]; + check("void f() {\n" + " char *p = 0;\n" + "\n" + " for (int x = 0; x < 3; ++x) {\n" + " if (y[x] == 0) {\n" + " p = (char *)malloc(10);\n" + " break;\n" + " }\n" + " }\n" + "\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:11]: (warning) Possible null pointer dereference: p\n", errout_str()); + } + + void nullpointer7() { + check("void foo()\n" + "{\n" + " wxLongLong x = 0;\n" + " int y = x.GetValue();\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer9() { //#ticket 1778 + check("void foo()\n" + "{\n" + " std::string * x = 0;\n" + " *x = \"test\";\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: x\n", errout_str()); + } + + void nullpointer10() { + // extracttests.start: struct my_type { int x; }; + check("void foo()\n" + "{\n" + " struct my_type* p = 0;\n" + " p->x = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout_str()); + } + + void nullpointer11() { // ticket #2812 + // extracttests.start: struct my_type { int x; }; + + check("int foo()\n" + "{\n" + " struct my_type* p;\n" + " p = 0;\n" + " return p->x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: p\n", errout_str()); + } + + void nullpointer12() { // ticket #2470, #4035 + const char code[] = "int foo()\n" + "{\n" + " int* i = nullptr;\n" + " return *i;\n" + "}\n"; + + check(code, false, true); // C++ file => nullptr means NULL + ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: i\n", errout_str()); + + check(code, false, false); // C file => nullptr does not mean NULL + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer15() { // #3560 + check("void f() {\n" + " char *p = 0;\n" + " if (x) p = \"abcd\";\n" + " return p ? f(*p) : f(0);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer16() { // #3591 + check("void foo() {\n" + " int *p = 0;\n" + " bar(&p);\n" + " *p = 0;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer17() { // #3567 + check("int foo() {\n" + " int *p = 0;\n" + " if (x) { return 0; }\n" + " return !p || *p;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("int foo() {\n" + " int *p = 0;\n" + " if (x) { return 0; }\n" + " return p && *p;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer18() { // #1927 + check("void f ()\n" + "{\n" + " int i=0;\n" + " char *str=NULL;\n" + " while (str[i])\n" + " {\n" + " i++;\n" + " };\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: str\n", errout_str()); + } + + void nullpointer19() { // #3811 + check("int foo() {\n" + " perror(0);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer20() { // #3807 + check("void f(int x) {\n" + " struct xy *p = 0;\n" + " if (x) p = q;\n" + " if (p ? p->x || p->y : 0) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" // false negative + " struct xy *p = 0;\n" + " if (x) p = q;\n" + " if (y ? p->x : p->y) { }\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (warning) Possible null pointer dereference: p\n", "", errout_str()); + } + + void nullpointer21() { // #4038 - fp: if (x) p=q; else return; + check("void f(int x) {\n" + " int *p = 0;\n" + " if (x) p = q;\n" + " else return;\n" + " *p = 0;\n" // <- p is not NULL + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer23() { // #4665 + check("void f(){\n" + " char *c = NULL;\n" + " char cBuf[10];\n" + " sprintf(cBuf, \"%s\", c ? c : \"0\" );\n" + "}"); + ASSERT_EQUALS("",errout_str()); + } + + void nullpointer24() { // #5083 - fp: chained assignment + check("void f(){\n" + " char *c = NULL;\n" + " x = c = new char[10];\n" + " *c = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer25() { // #5061 + check("void f(int *data, int i)\n" + "{\n" + " int *array = NULL;\n" + " if (data == 1 && array[i] == 0)\n" + " std::cout << \"test\";\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: array\n", errout_str()); + } + + void nullpointer26() { // #3589 + check("double foo() {\n" + " sk *t1 = foo();\n" + " sk *t2 = foo();\n" + " if ((!t1) && (!t2))\n" + " return 0.0;\n" + " if (t1 && (!t2))\n" + " return t1->Inter();\n" + " if (t2->GetT() == t)\n" + " return t2->Inter();\n" + " if (t2 && (!t1))\n" + " return 0.0;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer27() { // #6568 + check("template\n" + "class Foo {\n" + " Foo& operator = ( Type* );\n" + "};\n" + "template\n" + "Foo& Foo::operator = ( Type* pointer_ ) {\n" + " pointer_=NULL;\n" + " *pointer_=0;\n" + " return *this;\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (error) Null pointer dereference: pointer_\n", errout_str()); + } + + void nullpointer28() { // #6491 + check("typedef struct { int value; } S;\n" + "int f(const S *s) {\n" + " int i = s ? s->value + 1\n" + " : s->value - 1; // <-- null ptr dereference\n" + " return i;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 's' is redundant or there is possible null pointer dereference: s.\n", + errout_str()); + } + + void nullpointer30() { // #6392 + check("void f(std::vector *values)\n" + "{\n" + " values->clear();\n" + " if (values)\n" + " {\n" + " for (int i = 0; i < values->size(); ++i)\n" + " {\n" + " values->push_back(\"test\");\n" + " }\n" + " }\n" + "}\n", true); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:3]: (warning) Either the condition 'values' is redundant or there is possible null pointer dereference: values.\n", + errout_str()); + } + + void nullpointer31() { // #8482 + check("struct F\n" + "{\n" + " int x;\n" + "};\n" + "\n" + "static void foo(F* f)\n" + "{\n" + " if( f ) {}\n" + " else { return; }\n" + " (void)f->x;\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + check("typedef struct\n" + "{\n" + " int x;\n" + "} F;\n" + "\n" + "static void foo(F* f)\n" + "{\n" + " if( !f || f->x == 0 )\n" + " {\n" + " if( !f )\n" + " return;\n" + " }\n" + "\n" + " (void)f->x;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer32() { // #8460 + check("int f(int * ptr) {\n" + " if(ptr)\n" + " { return 0;}\n" + " else{\n" + " int *p1 = ptr;\n" + " return *p1;\n" + " }\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:6]: (warning) Either the condition 'ptr' is redundant or there is possible null pointer dereference: p1.\n", errout_str()); + } + + void nullpointer33() { + check("void f(int * x) {\n" + " if (x != nullptr)\n" + " *x = 2;\n" + " else\n" + " *x = 3;\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (warning) Either the condition 'x!=nullptr' is redundant or there is possible null pointer dereference: x.\n", errout_str()); + } + + void nullpointer34() { + check("void g() {\n" + " throw " ";\n" + "}\n" + "bool f(int * x) {\n" + " if (x) *x += 1;\n" + " if (!x) g();\n" + " return *x;\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer35() { + check("bool f(int*);\n" + "void g(int* x) {\n" + " if (f(x)) {\n" + " *x = 1;\n" + " }\n" + "}\n" + "void h() {\n" + " g(0);\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + check("bool f(int*);\n" + "void g(int* x) {\n" + " bool b = f(x);\n" + " if (b) {\n" + " *x = 1;\n" + " }\n" + "}\n" + "void h() {\n" + " g(0);\n" + "}\n", + true); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer36() { + check("char* f(char* s) {\n" + " char* start = s;\n" + " if (!s)\n" + " return (s);\n" + " while (isspace(*start))\n" + " start++;\n" + " return (start);\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer37() { + check("void f(int value, char *string) {\n" + " char *ptr1 = NULL, *ptr2 = NULL;\n" + " unsigned long count = 0;\n" + " if(!string)\n" + " return;\n" + " ptr1 = string;\n" + " ptr2 = strrchr(string, 'a');\n" + " if(ptr2 == NULL)\n" + " return;\n" + " while(ptr1 < ptr2) {\n" + " count++;\n" + " ptr1++;\n" + " }\n" + "}\n", + true); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer38() { + check("void f(int * x) {\n" + " std::vector v;\n" + " if (x) {\n" + " v.push_back(x);\n" + " *x;\n" + " }\n" + "}\n", + true); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer39() { + check("struct A { int * x; };\n" + "void f(struct A *a) {\n" + " if (a->x == NULL) {}\n" + " *(a->x);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'a->x==NULL' is redundant or there is possible null pointer dereference: a->x.\n", + errout_str()); + } + + void nullpointer40() { + check("struct A { std::unique_ptr x; };\n" + "void f(struct A *a) {\n" + " if (a->x == nullptr) {}\n" + " *(a->x);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'a->x==nullptr' is redundant or there is possible null pointer dereference: a->x.\n", + errout_str()); + } + + void nullpointer41() { + check("struct A { int * g() const; };\n" + "void f(struct A *a) {\n" + " if (a->g() == nullptr) {}\n" + " *(a->g());\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'a->g()==nullptr' is redundant or there is possible null pointer dereference: a->g().\n", + errout_str()); + + check("struct A { int * g(); };\n" + "void f(struct A *a) {\n" + " if (a->g() == nullptr) {}\n" + " *(a->g());\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer42() { + check("struct A { std::unique_ptr g() const; };\n" + "void f(struct A *a) {\n" + " if (a->g() == nullptr) {}\n" + " *(a->g());\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'a->g()==nullptr' is redundant or there is possible null pointer dereference: a->g().\n", + errout_str()); + } + + void nullpointer43() { + check("struct A { int* x; };\n" + "void f(A* a) {\n" + " int * x = a->x;\n" + " if (x) {\n" + " (void)*a->x;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer44() { + // #9395 + check("int foo( ) {\n" + " const B* b = getB();\n" + " const double w = ( nullptr != b) ? 42. : 0.0;\n" + " if ( w == 0.0 )\n" + " return 0;\n" + " return b->get();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + // #9423 + check("extern F* GetF();\n" + "extern L* GetL();\n" + "void Foo() {\n" + " const F* const fPtr = GetF();\n" + " const bool fPtrOk = fPtr != NULL;\n" + " assert(fPtrOk);\n" + " if (!fPtrOk)\n" + " return;\n" + " L* const lPtr = fPtr->l;\n" + " const bool lPtrOk = lPtr != NULL;\n" + " assert(lPtrOk);\n" + " if (!lPtrOk)\n" + " return;\n" + " lPtr->Clear();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer45() { + check("struct a {\n" + " a *b() const;\n" + "};\n" + "void g() { throw 0; }\n" + "a h(a * c) {\n" + " if (c && c->b()) {}\n" + " if (!c)\n" + " g();\n" + " if (!c->b())\n" + " g();\n" + " a d = *c->b();\n" + " return d;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct a {\n" + " a *b() const;\n" + "};\n" + "void e() { throw 0; }\n" + "a f() {\n" + " a *c = 0;\n" + " if (0 && c->b()) {}\n" + " if (!c)\n" + " e();\n" + " if (!c->b())\n" + " e();\n" + " a d = *c->b();\n" + " return d;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer46() { + check("void f() {\n" + " char* p = new(std::nothrow) char[1];\n" + " if( p ) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer47() { + check("void f(int *p) {\n" + " if(!p[0]) {}\n" + " const int *const a = p;\n" + " if(!a){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (warning) Either the condition '!a' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + } + + void nullpointer48() { + check("template\n" + "auto f(T& x) -> decltype(x);\n" + "int& g(int* x) {\n" + " return f(*x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer49() { + check("void f(int *p, int n) {\n" + " int *q = 0;\n" + " if(n > 10) q = p;\n" + " *p +=2;\n" + " if(n < 120) *q+=12;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Possible null pointer dereference: q\n", errout_str()); + + check("void f(int *p, int n) {\n" + " int *q = 0;\n" + " if(n > 10) q = p;\n" + " *p +=2;\n" + " if(n > 10) *q+=12;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer50() { + check("void f(int *p, int a) {\n" + " if(!p) {\n" + " if(a > 0) {\n" + " if(a > 10){}\n" + " else {\n" + " *p = 0;\n" + " }\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:6]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", + errout_str()); + } + + void nullpointer51() { + check("struct a {\n" + " a *b();\n" + "};\n" + "bool c(a *, const char *);\n" + "a *d(a *e) {\n" + " if (e) {}\n" + " if (c(e, \"\"))\n" + " return nullptr;\n" + " return e->b();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer52() { + check("int f(int a, int* b) {\n" + " int* c = nullptr;\n" + " if(b) c = b;\n" + " if (!c) c = &a;\n" + " return *c;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int a, int* b) {\n" + " int* c = nullptr;\n" + " if(b) c = b;\n" + " bool d = !c;\n" + " if (d) c = &a;\n" + " return *c;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int* x; };\n" + "int f(int a, int* b) {\n" + " A c;\n" + " c.x = nullptr;\n" + " if(b) c.x = b;\n" + " if (!c.x) c.x = &a;\n" + " return *c.x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int* x; };\n" + "int f(int a, int* b) {\n" + " A c;\n" + " c.x = nullptr;\n" + " if(b) c.x = b;\n" + " bool d = !c.x;\n" + " if (d) c.x = &a;\n" + " return *c.x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int* x; };\n" + "int f(int a, int* b) {\n" + " A c;\n" + " c.x = nullptr;\n" + " if(b) c.x = b;\n" + " bool d = !c.x;\n" + " if (!d) c.x = &a;\n" + " return *c.x;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:8]: (warning) Possible null pointer dereference: c.x\n", errout_str()); + } + + void nullpointer53() { + check("void f(int nParams, int* params) {\n" + " for (int n=1; nastParent() && tok3->str() == \",\")\n" + " tok3 = tok3->astParent();\n" + " if (tok3 && tok3->str() == \"(\") {}\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:5] -> [test.cpp:3]: (warning) Either the condition 'tok3' is redundant or there is possible null pointer dereference: tok3.\n", + errout_str()); + + check("void f(int* t1, int* t2) {\n" + " while (t1 && t2 &&\n" + " *t1 == *t2) {\n" + " t1 = nullptr;\n" + " t2 = nullptr;\n" + " }\n" + " if (!t1 || !t2)\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(int* i);\n" + "void g(int* i) {\n" + " while(f(i) && *i == 0)\n" + " i++;\n" + " if (!i) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer56() { + check("struct ListEntry {\n" + " struct ListEntry *next;\n" + "};\n" + "static void dostuff(ListEntry * listHead) {\n" + " ListEntry *prev = NULL;\n" + " for (ListEntry *cursor = listHead; cursor != NULL; prev = cursor, cursor = cursor->next) {}\n" + " if (prev) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer57() { + check("void f() {\n" + " FILE* fptr = fopen(\"test\", \"r\");\n" + " if (fptr != nullptr) {\n" + " std::function fn([&] {\n" + " fclose(fptr);\n" + " fptr = NULL;\n" + " });\n" + " fgetc(fptr);\n" + " fn();\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer58() { + check("struct myStruct { char entry[0]; };\n" + "void f() {\n" + " struct myStruct* sPtr = NULL;\n" + " int sz = (!*(&sPtr) || ((*(&sPtr))->entry[0] > 15)) ?\n" + " sizeof((*(&sPtr))->entry[0]) : 123456789;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer59() { + check("struct Box {\n" + " struct Box* prev;\n" + " struct Box* next;\n" + "};\n" + "void foo(Box** pfreeboxes) {\n" + " Box *b = *pfreeboxes;\n" + " *pfreeboxes = b->next;\n" + " if( *pfreeboxes )\n" + " (*pfreeboxes)->prev = nullptr;\n" + " b->next = nullptr;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer60() { + check("void f(){\n" + " char uuid[128];\n" + " char *s1;\n" + " memset(uuid, 0, sizeof(uuid));\n" + " s1 = strchr(uuid, '=');\n" + " s1 = s1 ? s1 + 1 : &uuid[5];\n" + " if (!strcmp(\"00000000000000000000000000000000\", s1) )\n" + " return;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer61() { + check("struct a {\n" + " int *e;\n" + "};\n" + "struct f {\n" + " a *g() const;\n" + "};\n" + "void h() {\n" + " for (f b;;) {\n" + " a *c = b.g();\n" + " int *d = c->e;\n" + " if (d)\n" + " ;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " A* g() const;\n" + " A* h() const;\n" + "};\n" + "void f(A* a) {\n" + " if (!a->h())\n" + " return;\n" + " const A *b = a;\n" + " while (b && !b->h())\n" + " b = b->g();\n" + " if (!b || b == b->g()->h())\n" + " return;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer62() { + check("struct A {\n" + " bool f()() const;\n" + "};\n" + "void a(A *x) {\n" + " std::string b = x && x->f() ? \"\" : \"\";\n" + " if (x) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " bool f()() const;\n" + "};\n" + "void a(A *x) {\n" + " std::string b = (!x || x->f()) ? \"\" : \"\";\n" + " if (x) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " A * aa;\n" + "};\n" + "void b(A*);\n" + "void a(A *x) {\n" + " b(x ? x->aa : nullptr);\n" + " if (!x) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer63() { + check("struct A {\n" + " A* a() const;\n" + " A* b() const;\n" + "};\n" + "A* f(A*);\n" + "void g(const A* x) {\n" + " A *d = x->a();\n" + " d = f(d->b()) ? d->a() : nullptr;\n" + " if (d && f(d->b())) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer64() { + check("struct A {\n" + " A* f() const;\n" + " int g() const;\n" + "};\n" + "bool a;\n" + "bool b(A* c) {\n" + " if (c->g() == 0)\n" + " ;\n" + " A *aq = c;\n" + " if (c->g() == 0)\n" + " c = c->f();\n" + " if (c)\n" + " for (A *d = c; d != aq; d = d->f()) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " A* g() const;\n" + " A* h() const;\n" + "};\n" + "bool i(A*);\n" + "void f(A* x) {\n" + " if (i(x->g())) {\n" + " A *y = x->g();\n" + " x = x->g()->h();\n" + " if (x && x->g()) {\n" + " y = x->g()->h();\n" + " }\n" + " if (!y) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer65() { + check("struct A {\n" + " double get();\n" + "};\n" + "double x;\n" + "double run(A** begin, A** end) {\n" + " A* a = nullptr;\n" + " while (begin != end) {\n" + " a = *begin;\n" + " x = a->get();\n" + " ++begin;\n" + " }\n" + " x = 0;\n" + " if (a)\n" + " return a->get();\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer66() { + check("int f() {\n" + " int ret = 0;\n" + " int *v = nullptr;\n" + " if (!MyAlloc(&v)) {\n" + " ret = -1;\n" + " goto done;\n" + " }\n" + " DoSomething(*v);\n" + "done:\n" + " if (v)\n" + " MyFree(&v);\n" + " return ret;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer67() { + check("int result;\n" + "\n" + "int test_b(void) {\n" + " char **string = NULL;\n" + "\n" + " /* The bug disappears if \"result =\" is omitted. */\n" + " result = some_other_call(&string);\n" + " if (string && string[0])\n" + " return 0;\n" + " return -1;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int result;\n" + "\n" + "int test_b(void) {\n" + " char **string = NULL;\n" + "\n" + " some_other_call(&string);\n" + " if (string && string[0])\n" + " return 0;\n" + " return -1;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer68() { + check("struct A {\n" + " A* b;\n" + "};\n" + "void f(A* c) {\n" + " c = c->b;\n" + " if (c->b) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " A* b;\n" + "};\n" + "void f(A* c) {\n" + " A* d = c->b;\n" + " A *e = c;\n" + " while (nullptr != (e = e->b)) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer69() { + check("void f(const Scope *scope) {\n" + " if (scope->definedType) {}\n" + " while (scope) {\n" + " scope = scope->nestedIn;\n" + " enumerator = scope->findEnumerator();\n" + " }\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:5]: (warning) Either the condition 'scope' is redundant or there is possible null pointer dereference: scope.\n", + errout_str()); + + check("void f(const Scope *scope) {\n" + " if (scope->definedType) {}\n" + " while (scope && scope->nestedIn) {\n" + " if (scope->type == Scope::eFunction && scope->functionOf)\n" + " scope = scope->functionOf;\n" + " else\n" + " scope = scope->nestedIn;\n" + " enumerator = scope->findEnumerator();\n" + " }\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:8]: (warning) Either the condition 'scope' is redundant or there is possible null pointer dereference: scope.\n", + errout_str()); + + check("struct a {\n" + " a *b() const;\n" + " void c();\n" + "};\n" + "void d() {\n" + " for (a *e;;) {\n" + " e->b()->c();\n" + " while (e)\n" + " e = e->b();\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer70() { + check("struct Token {\n" + " const Token* nextArgument() const;\n" + " const Token* next() const;\n" + " int varId() const;\n" + "};\n" + "int f(const Token *first, const Token* second) {\n" + " first = first->nextArgument();\n" + " if (first)\n" + " first = first->next();\n" + " if (second->next()->varId() == 0) {\n" + " second = second->nextArgument();\n" + " if (!first || !second)\n" + " return 0;\n" + " } else if (!first) {\n" + " return 0;\n" + " }\n" + " return first->varId();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct Token {\n" + " const Token* nextArgument() const;\n" + " const Token* next() const;\n" + " int varId() const;\n" + " void str() const;" + "};\n" + "void f(const Token *first) {\n" + " first = first->nextArgument();\n" + " if (first)\n" + " first = first->next();\n" + " first->str();\n" + "}\n"); + TODO_ASSERT_EQUALS( + "[test.cpp:8] -> [test.cpp:10]: (warning) Either the condition 'first' is redundant or there is possible null pointer dereference: first.\n", + "", + errout_str()); + } + + void nullpointer71() { + check("void f() {\n" + " Device* dev = Get();\n" + " SetCount(dev == nullptr ? 0 : dev->size());\n" + " if (dev)\n" + " DoSomething(dev);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " Device* dev = Get();\n" + " SetCount(dev != nullptr ? dev->size() : 0);\n" + " if (dev)\n" + " DoSomething(dev);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer72() { // #10215 + check("int test() {\n" + " int* p0 = nullptr, *p1 = nullptr;\n" + " getFoo(p0);\n" + " getBar(p1);\n" + " if (!(p0 != nullptr && p1 != nullptr))\n" + " return {};\n" + " return *p0 + *p1;\n" + "}\n", true /*inconclusive*/); + ASSERT_EQUALS("", errout_str()); + + check("int test2() {\n" + " int* p0 = nullptr;\n" + " if (!(getBaz(p0) && p0 != nullptr))\n" + " return 0;\n" + " return *p0;\n" + "}\n", true /*inconclusive*/); + ASSERT_EQUALS("", errout_str()); + + check("int test3() {\n" + " Obj* PObj = nullptr;\n" + " if (!(GetObj(PObj) && PObj != nullptr))\n" + " return 1;\n" + " if (!PObj->foo())\n" + " test();\n" + " PObj->bar();\n" + "}\n", true /*inconclusive*/); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer73() { + check("void f(bool flag2, int* ptr) {\n" + " bool flag1 = true;\n" + " if (flag2) {\n" + " if (ptr != nullptr)\n" + " (*ptr)++;\n" + " else\n" + " flag1 = false;\n" + " }\n" + " if (flag1 && flag2)\n" + " (*ptr)++;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool flag2, int* ptr) {\n" + " bool flag1 = true;\n" + " if (flag2) {\n" + " if (ptr != nullptr)\n" + " (*ptr)++;\n" + " else\n" + " flag1 = false;\n" + " }\n" + " if (!flag1 && flag2)\n" + " (*ptr)++;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:10]: (warning) Either the condition 'ptr!=nullptr' is redundant or there is possible null pointer dereference: ptr.\n", errout_str()); + } + + void nullpointer74() { + check("struct d {\n" + " d* e();\n" + "};\n" + "void g(d* f) {\n" + " do {\n" + " f = f->e();\n" + " if (f) {}\n" + " } while (0);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct d {\n" + " d* e();\n" + "};\n" + "void g(d* f, int i) {\n" + " do {\n" + " i--;\n" + " f = f->e();\n" + " if (f) {}\n" + " } while (i > 0);\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:8] -> [test.cpp:7]: (warning) Either the condition 'f' is redundant or there is possible null pointer dereference: f.\n", + errout_str()); + + check("struct d {\n" + " d* e();\n" + "};\n" + "void g(d* f, int i) {\n" + " do {\n" + " i--;\n" + " f = f->e();\n" + " if (f) {}\n" + " } while (f && i > 0);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer75() { + check("struct a {\n" + " a *b() const;\n" + " void c();\n" + " int d() const;\n" + "};\n" + "void e(a *x) {\n" + " while (x->b()->d() == 0)\n" + " x->c();\n" + " x->c();\n" + " if (x->b()) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer76() + { + check("int* foo(int y) {\n" + " std::unique_ptr x = std::make_unique(0);\n" + " if( y == 0 )\n" + " return x.release();\n" + " (*x) ++;\n" + " return x.release();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer77() + { + check("bool h(int*);\n" + "void f(int* i) {\n" + " int* i = nullptr;\n" + " if (h(i) && *i == 1) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("bool h(int*);\n" + "void f(int* i) {\n" + " int* i = nullptr;\n" + " if (h(i))\n" + " if (*i == 1) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("bool h(int*);\n" + "void f(int* x) {\n" + " int* i = x;\n" + " if (h(i))\n" + " i = nullptr;\n" + " if (h(i) && *i == 1) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer78() // #7802 + { + check("void f()\n" + "{\n" + " int **pp;\n" + " int *p = 0;\n" + " pp = &p;\n" + " **pp = 1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Null pointer dereference: *pp\n", errout_str()); + } + + void nullpointer79() // #10400 + { + check("void resize(size_t nF, size_t nT) {\n" + " double* pValues = nullptr;\n" + " if (nF > 0 && nT > 0)\n" + " pValues = new double[nF * nT];\n" + " for (size_t cc = 0; cc < nF * nT; ++cc)\n" + " pValues[cc] = 42;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer80() // #10410 + { + check("int f(int* a, int* b) {\n" + " if( a || b ) {\n" + " int n = a ? *a : *b;\n" + " if( b )\n" + " n++;\n" + " return n;\n" + " }\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer81() // #8724 + { + check("void f(A **list) {\n" + " A *tmp_List = NULL;\n" + " *list = NULL;\n" + " while (1) {\n" + " if (*list == NULL) {\n" + " tmp_List = malloc (sizeof (ArchiveList_struct));\n" + " *list = tmp_List;\n" + " } else {\n" + " tmp_List->next = malloc (sizeof (ArchiveList_struct));\n" + " }\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer82() // #10331 + { + check("bool g();\n" + "int* h();\n" + "void f(int* ptr) {\n" + " if (!ptr) {\n" + " if (g())\n" + " goto done;\n" + " ptr = h();\n" + " if (!ptr)\n" + " return;\n" + " }\n" + " if (*ptr == 1)\n" + " return;\n" + "\n" + "done:\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer83() // #9870 + { + check("int* qux();\n" + "int* f7c2(int *x) {\n" + " int* p = 0;\n" + " if (nullptr == x)\n" + " p = qux();\n" + " if (nullptr == x)\n" + " return x;\n" + " *p = 1;\n" + " return x;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:8]: (warning) Possible null pointer dereference: p\n", errout_str()); + } + + void nullpointer84() // #9873 + { + check("void f(std::unique_ptr P) {\n" + " A *RP = P.get();\n" + " if (!RP) {\n" + " P->foo();\n" + " }\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition '!RP' is redundant or there is possible null pointer dereference: P.\n", + errout_str()); + } + + void nullpointer85() // #10210 + { + check("struct MyStruct {\n" + " int GetId() const {\n" + " int id = 0;\n" + " int page = m_notebook->GetSelection();\n" + " if (m_notebook && (m_notebook->GetPageCount() > 0))\n" + " id = page;\n" + " return id;\n" + " }\n" + " wxNoteBook *m_notebook = nullptr;\n" + "};\n" + "int f() {\n" + " const MyStruct &s = Get();\n" + " return s.GetId();\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:5] -> [test.cpp:4]: (warning) Either the condition 'm_notebook' is redundant or there is possible null pointer dereference: m_notebook.\n", + errout_str()); + } + + void nullpointer86() + { + check("struct A {\n" + " A* a() const;\n" + " int b() const;\n" + "};\n" + "A* f(A* t) {\n" + " if (t->b() == 0) {\n" + " return t;\n" + " }\n" + " return t->a();\n" + "}\n" + "void g(A* t) {\n" + " t = f(t->a());\n" + " if (!t->a()) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer87() // #9291 + { + check("int f(bool b, int* x) {\n" + " if (b && x == nullptr)\n" + " return 0;\n" + " else if (!b && x == nullptr)\n" + " return 1;\n" + " else if (!b && x != nullptr)\n" + " return *x;\n" + " else\n" + " return *x + 1;\n" + "}\n"); + TODO_ASSERT_EQUALS("", "[test.cpp:6] -> [test.cpp:9]: (warning) Either the condition 'x!=nullptr' is redundant or there is possible null pointer dereference: x.\n", errout_str()); + + check("void f(int n, int* p) {\n" + " int* r = nullptr;\n" + " if (n < 0)\n" + " return;\n" + " if (n == 0)\n" + " r = p;\n" + " else if (n > 0)\n" + " r = p + 1;\n" + " *r;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer88() // #9949 + { + check("struct S { char **ppc; };\n" + "int alloc(struct S* s) {\n" + " char** ppc = malloc(4096);\n" + " if (ppc != NULL) {\n" + " s->ppc = ppc;\n" + " return 1;\n" + " }\n" + " return 0;\n" + "}\n" + "void f() {\n" + " struct S* s = malloc(sizeof(struct S));\n" + " s->ppc = NULL;\n" + " if (alloc(s))\n" + " s->ppc[0] = \"\";\n" + "}\n", /*inconclusive*/ false, false); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer89() // #10640 + { + check("typedef struct {\n" + " int x;\n" + "} foo_t;\n" + "typedef struct {\n" + " foo_t *y;\n" + "} bar_t;\n" + "void f(bar_t *ptr) {\n" + " if(ptr->y->x)\n" + " if(ptr->y != nullptr) {}\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:9] -> [test.cpp:8]: (warning) Either the condition 'ptr->y!=nullptr' is redundant or there is possible null pointer dereference: ptr->y.\n", + errout_str()); + + check("bool argsMatch(const Token *first, const Token *second) {\n" // #6145 + " if (first->str() == \")\")\n" + " return true;\n" + " else if (first->next()->str() == \"=\")\n" + " first = first->nextArgument();\n" + " else if (second->next()->str() == \"=\") {\n" + " second = second->nextArgument();\n" + " if (second)\n" + " second = second->tokAt(-2);\n" + " if (!first || !second) {\n" + " return !first && !second;\n" + " }\n" + " }\n" + " return false;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:10] -> [test.cpp:2]: (warning) Either the condition '!first' is redundant or there is possible null pointer dereference: first.\n" + "[test.cpp:10] -> [test.cpp:4]: (warning) Either the condition '!first' is redundant or there is possible null pointer dereference: first.\n", + errout_str()); + } + + void nullpointer90() // #6098 + { + check("std::string definitionToName(Definition *ctx)\n" + "{\n" + " if (ctx->definitionType()==Definition::TypeMember)\n" // possible null pointer dereference + " {\n" + " return \"y\";\n" + " }\n" + " else if (ctx)\n" // ctx is checked against null + " {\n" + " if(ctx->definitionType()!=Definition::TypeMember)\n" + " {\n" + " return \"x\";\n" + " }\n" + " }\n" + " return \"unknown\";\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:7] -> [test.cpp:3]: (warning) Either the condition 'ctx' is redundant or there is possible null pointer dereference: ctx.\n", + errout_str()); + } + + void nullpointer91() // #10678 + { + check("void f(const char* PBeg, const char* PEnd) {\n" + " while (PEnd != nullptr) {\n" + " const int N = h(PEnd);\n" + " PEnd = g();\n" + " const int Length = PEnd == nullptr ? 0 : PEnd - PBeg;\n" + " };\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer92() + { + check("bool g(bool);\n" + "int f(int* i) {\n" + " if (!g(!!i)) return 0;\n" + " return *i;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("bool g(bool);\n" + "int f(int* i) {\n" + " if (!g(!i)) return 0;\n" + " return *i;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer93() // #3929 + { + check("int* GetThing( ) { return 0; }\n" + "int main() {\n" + " int* myNull = GetThing();\n" + " *myNull=42;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: myNull\n", errout_str()); + + check("struct foo {\n" + " int* GetThing(void) { return 0; }\n" + "};\n" + "int main(void) {\n" + " foo myFoo;\n" + " int* myNull = myFoo.GetThing();\n" + " *myNull=42;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Null pointer dereference: myNull\n", errout_str()); + + check("struct T { bool g() const; };\n" + "void f(T* p) {\n" + " if (!p)\n" + " return;\n" + " while (p->g())\n" + " p = nullptr;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Possible null pointer dereference: p\n", errout_str()); + } + + void nullpointer94() // #11040 + { + check("struct entry { struct entry* next; size_t len; };\n" + "void f(struct entry **kep, size_t slen) {\n" + " while (*kep)\n" + " kep = &(*kep)->next;\n" + " *kep = (struct entry*)malloc(sizeof(**kep));\n" + " (*kep)->next = 0;\n" + " (*kep)->len = slen;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer95() // #11142 + { + check("void f(std::vector& v) {\n" + " for (auto& p : v)\n" + " if (*p < 2)\n" + " p = nullptr;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer96() + { + check("struct S {\n" + " int x;\n" + "};\n" + "S *create_s();\n" + "void test() {\n" + " S *s = create_s();\n" + " for (int i = 0; i < s->x; i++) {\n" + " if (s->x == 17) {\n" + " s = nullptr;\n" + " break;\n" + " }\n" + " }\n" + " if (s) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer97() // #11229 + { + check("struct B { virtual int f() = 0; };\n" + "struct D : public B { int f() override; };\n" + "int g(B* p) {\n" + " if (p) {\n" + " auto d = dynamic_cast(p);\n" + " return d ? d->f() : 0;\n" + " }\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer98() // #11458 + { + check("struct S { double* d() const; };\n" + "struct T {\n" + " virtual void g(double* b, double* d) const = 0;\n" + " void g(S* b) const { g(b->d(), nullptr); }\n" + " void g(S* b, S* d) const { g(b->d(), d->d()); }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer99() // #10602 + { + check("class A\n" + "{\n" + " int *foo(const bool b)\n" + " {\n" + " if(b)\n" + " return nullptr;\n" + " else\n" + " return new int [10];\n" + " }\n" + "public:\n" + " void bar(void)\n" + " {\n" + " int * buf = foo(true);\n" + " buf[2] = 0;" // << + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:14]: (error) Null pointer dereference: buf\n", errout_str()); + } + + void nullpointer100() // #11636 + { + check("const char* type_of(double) { return \"unknown\"; }\n" + "void f() {\n" + " double tmp = 0.0;\n" + " const char* t = type_of(tmp);\n" + " std::cout << t;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer101() // #11382 + { + check("struct Base { virtual ~Base(); };\n" + "struct Derived : Base {};\n" + "bool is_valid(const Derived&);\n" + "void f(const Base* base) {\n" + " const Derived* derived = dynamic_cast(base);\n" + " if (derived && !is_valid(*derived) || base == nullptr) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer102() + { + check("struct S { std::string str; };\n" // #11534 + "struct T { S s; };\n" + "struct U { T t[1]; };\n" + "void f(const T& t, const U& u, std::string& str) {\n" + " if (str.empty())\n" + " str = t.s.str;\n" + " else\n" + " str = u.t[0].s.str;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer103() + { + check("struct S {\n" // #10572 + " int f();\n" + " int* m_P{};\n" + "};\n" + "int S::f() {\n" + " if (!m_P) {\n" + " try {\n" + " m_P = new int(1);\n" + " }\n" + " catch (...) {\n" + " return 0;\n" + " }\n" + " }\n" + " return *m_P;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int* p, const int* q) {\n" // #11873 + " if (*q == -1)\n" + " *p = 0;\n" + "}\n" + "void g() {\n" + " int x = -2;\n" + " f(nullptr, &x);\n" + "}\n"); + TODO_ASSERT_EQUALS("", "[test.cpp:3]: (warning) Possible null pointer dereference: p\n", errout_str()); + } + + void nullpointer_addressOf() { // address of + check("void f() {\n" + " struct X *x = 0;\n" + " if (addr == &x->y) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " struct X *x = 0;\n" + " if (addr == &x->y.z[0]) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkP("typedef int Count;\n" // #10018 + "#define offsetof(TYPE, MEMBER) ((Count) & ((TYPE*)0)->MEMBER)\n" + "struct S {\n" + " int a[20];\n" + "};\n" + "int g(int i) {\n" + " return offsetof(S, a[i]);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointerSwitch() { // #2626 + // extracttests.start: char *do_something(); + check("char *f(int x) {\n" + " char *p = do_something();\n" + " switch (x) {\n" + " case 1:\n" + " p = 0;\n" + " case 2:\n" + " *p = 0;\n" + " break;\n" + " }\n" + " return p;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:7]: (warning) Possible null pointer dereference: p\n", errout_str()); + } + + void nullpointer_cast() { + check("char *nasm_skip_spaces(const char *p) {\n" // #4692 + " if (p)\n" + " while (*p && nasm_isspace(*p))\n" + " p++;\n" + " return p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(char* origin) {\n" // #11449 + " char* cp = (strchr)(origin, '\\0');\n" + " if (cp[-1] != '/')\n" + " *cp++ = '/';\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer_castToVoid() { // #3771 + check("void f () {\n" + " int *buf; buf = NULL;\n" + " buf;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer_subfunction() { + check("int f(int* x, int* y) {\n" + " if (!x)\n" + " return;\n" + " return *x + *y;\n" + "}\n" + "void g() {\n" + " f(nullptr, nullptr);\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + } + + // Check if pointer is null and the dereference it + void pointerCheckAndDeRef() { + check("void foo(char *p) {\n" + " if (!p) {\n" + " }\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + check("void foo(char *p) {\n" + " if (p && *p == 0) {\n" + " }\n" + " printf(\"%c\", *p);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + check("void foo(char *p) {\n" + " if (p && *p == 0) {\n" + " } else { *p = 0; }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + check("void foo(char *p) {\n" + " if (p) {\n" + " }\n" + " strcpy(p, \"abc\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + check("void foo(char *p) {\n" + " if (p) {\n" + " }\n" + " bar();\n" + " strcpy(p, \"abc\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + check("void foo(abc *p) {\n" + " if (!p) {\n" + " }\n" + " else { if (!p->x) {\n" + " } }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + { + static const char code[] = + "void foo(char *p) {\n" + " if (!p) {\n" + " abort();\n" + " }\n" + " *p = 0;\n" + "}"; + check(code, false); + ASSERT_EQUALS("", errout_str()); + + check(code, true); + ASSERT_EQUALS("", errout_str()); + } + + check("void foo(char *p) {\n" + " if (!p) {\n" + " (*bail)();\n" + " }\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(char *p) {\n" + " if (!p) {\n" + " throw x;\n" + " }\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(char *p) {\n" + " if (!p) {\n" + " ab.abort();\n" + " }\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(char *p) {\n" + " if (!p) {\n" + " switch (x) { }\n" + " }\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void foo(char *p) {\n" + " if (!p) {\n" + " }\n" + " return *x;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("int foo(int *p) {\n" + " if (!p) {\n" + " x = *p;\n" + " return 5+*p;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n" + "[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + // operator! + check("void f() {\n" + " A a;\n" + " if (!a) {\n" + " a.x();\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // This is why this check can't be used on the simplified token list + check("void f(Foo *foo) {\n" + " if (!dynamic_cast(foo)) {\n" + " *foo = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // ticket: #2300 - calling unknown function that may initialize the pointer + check("Fred *fred;\n" + "void a() {\n" + " if (!fred) {\n" + " initfred();\n" + " fred->x = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // ticket #1219 + check("void foo(char *p) {\n" + " if (p) {\n" + " return;\n" + " }\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + // #2467 - unknown macro may terminate the application + check("void f(Fred *fred) {\n" + " if (fred == NULL) {\n" + " MACRO;\n" + " }\n" + " fred->a();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #2493 - switch + check("void f(Fred *fred) {\n" + " if (fred == NULL) {\n" + " x = 0;\n" + " }\n" + " switch (x) {\n" + " case 1:\n" + " fred->a();\n" + " break;\n" + " };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #4118 - second if + check("void f(char *p) {\n" + " int x = 1;\n" + " if (!p) x = 0;\n" + " if (x) *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #2674 - different functions + check("class Fred {\n" + "public:\n" + " Wilma *wilma;\n" + " void a();\n" + " void b();\n" + "};\n" + "\n" + "void Fred::a() {\n" + " if ( wilma ) { }\n" + "}\n" + "\n" + "void Fred::b() {\n" + " wilma->Reload();\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void test(int *i) {\n" + " if(i == NULL) { }\n" + " else {\n" + " int b = *i;\n" + " }\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + // #2696 - false positives nr 1 + check("void f()\n" + "{\n" + " struct foo *pFoo = NULL;\n" + " size_t len;\n" + "\n" + " len = sizeof(*pFoo) - sizeof(pFoo->data);\n" + "\n" + " if (pFoo)\n" + " bar();\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + // #2696 - false positives nr 2 + check("void f()\n" + "{\n" + " struct foo *pFoo = NULL;\n" + " size_t len;\n" + "\n" + " while (pFoo)\n" + " pFoo = pFoo->next;\n" + "\n" + " len = sizeof(pFoo->data);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + // #2696 - false positives nr 3 + check("void f()\n" + "{\n" + " struct foo *pFoo = NULL;\n" + " size_t len;\n" + "\n" + " while (pFoo)\n" + " pFoo = pFoo->next;\n" + "\n" + " len = decltype(*pFoo);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("int foo(struct Fred *fred) {\n" + " if (fred) { }\n" + " return fred->a;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'fred' is redundant or there is possible null pointer dereference: fred.\n", errout_str()); + + // #2789 - assign and check pointer + check("void f() {\n" + " char *p; p = x();\n" + " if (!p) { }\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + // check, assign and use + check("void f() {\n" + " char *p;\n" + " if (p == 0 && (p = malloc(10)) != 0) {\n" + " *p = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // check, assign and use + check("void f() {\n" + " char *p;\n" + " if (p == 0 && (p = malloc(10)) != a && (*p = a)) {\n" + " *p = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // check, and use + check("void f() {\n" + " char *p;\n" + " if (p == 0 && (*p = 0)) {\n" + " return;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + // check, and use + check("void f() {\n" + " struct foo *p;\n" + " if (p == 0 && p->x == 10) {\n" + " return;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + // check, and use + check("void f() {\n" + " struct foo *p;\n" + " if (p == 0 || p->x == 10) {\n" + " return;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // check, and use + check("void f() {\n" + " char *p; p = malloc(10);\n" + " if (p == NULL && (*p = a)) {\n" + " return;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (warning) Either the condition 'p==NULL' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + // check, and use + check("void f(struct X *p, int x) {\n" + " if (!p && x==1 || p && p->x==0) {\n" + " return;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + { + const char code[] = "void f(Fred *fred) {\n" + " if (fred == NULL) { }\n" + " fred->x();\n" + "}"; + + check(code); // inconclusive + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'fred==NULL' is redundant or there is possible null pointer dereference: fred.\n", errout_str()); + } + + check("void f(char *s) {\n" // #3358 + " if (s==0);\n" + " strcpy(a, s?b:c);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // sizeof + check("void f(struct fred_t *fred) {\n" + " if (!fred)\n" + " int sz = sizeof(fred->x);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + // check in macro + check("void f(int *x) {\n" + " $if (!x) {}\n" + " *x = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // return ?: + check("int f(ABC *p) {\n" // FP : return ?: + " if (!p) {}\n" + " return p ? p->x : 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("int f(ABC *p) {\n" // no fn + " if (!p) {}\n" + " return q ? p->x : 0;\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", "", errout_str()); + + check("int f(ABC *p) {\n" // FP : return && + " if (!p) {}\n" + " return p && p->x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x, int *p) {\n" + " if (x || !p) {}\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!p' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + // sizeof + check("void f() {\n" + " int *pointer = NULL;\n" + " pointer = func(sizeof pointer[0]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + // Test CheckNullPointer::nullConstantDereference + void nullConstantDereference() { + check("int f() {\n" + " int* p = 0;\n" + " return p[4];\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: p\n", errout_str()); + + check("void f() {\n" + " typeof(*NULL) y;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("int * f() {\n" + " return NULL;\n" + "}\n" + "int main() {\n" + " return *f();\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: f()\n", errout_str()); + } + + void gcc_statement_expression() { + // Ticket #2621 + check("void f(struct ABC *abc) {\n" + " ({ if (abc) dbg(); })\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void snprintf_with_zero_size() { + // Ticket #2840 + check("void f() {\n" + " int bytes = snprintf(0, 0, \"%u\", 1);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void snprintf_with_non_zero_size() { + // Ticket #2840 + check("void f() {\n" + " int bytes = snprintf(0, 10, \"%u\", 1);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout_str()); + } + + void printf_with_invalid_va_argument() { + check("void f() {\n" + " printf(\"%s\", 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout_str()); + + check("void f(char* s) {\n" + " printf(\"%s\", s);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " char* s = 0;\n" + " printf(\"%s\", s);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3]: (error) Null pointer dereference: s\n" + "[test.cpp:3]: (error) Null pointer dereference\n", + errout_str()); + + check("void f() {\n" + " char *s = 0;\n" + " printf(\"%s\", s == 0 ? a : s);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " printf(\"%u%s\", 0, 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n", errout_str()); + + check("void f(char* s) {\n" + " printf(\"%u%s\", 0, s);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " char* s = 0;\n" + " printf(\"%u%s\", 123, s);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3]: (error) Null pointer dereference: s\n" + "[test.cpp:3]: (error) Null pointer dereference\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" + "[test.cpp:2]: (error) Null pointer dereference\n", // duplicate + errout_str()); + + check("void f() {\n" + " scanf(\"%d\", 0);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (error) Null pointer dereference\n" + "[test.cpp:2]: (error) Null pointer dereference\n", // duplicate + errout_str()); + + check("void f(char* foo) {\n" + " char location[200];\n" + " int width, height;\n" + " sscanf(imgInfo, \"%s %d %d\", location, &width, &height);\n" + "}"); + ASSERT_EQUALS("", errout_str()); // ticket #3207 + + check("void f(char *dummy) {\n" + " int iVal;\n" + " sscanf(dummy, \"%d%c\", &iVal);\n" + "}"); + ASSERT_EQUALS("", errout_str()); // ticket #3211 + + check("void f(char *dummy) {\n" + " int* iVal = 0;\n" + " sscanf(dummy, \"%d\", iVal);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3]: (error) Null pointer dereference: iVal\n" + "[test.cpp:3]: (error) Null pointer dereference\n" + "[test.cpp:3]: (error) Null pointer dereference\n", // duplicate + 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" + "[test.cpp:2]: (error) Null pointer dereference\n", // duplicate + errout_str()); + } + + void nullpointer_in_return() { + // extracttests.start: int maybe(); int *g(); + check("int foo() {\n" + " int* iVal = 0;\n" + " if(maybe()) iVal = g();\n" + " return iVal[0];\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Possible null pointer dereference: iVal\n", errout_str()); + + check("int foo(int* iVal) {\n" + " return iVal[0];\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer_in_typeid() { + // Should throw std::bad_typeid + check("struct PolymorphicA { virtual ~A() {} };\n" + "bool foo() {\n" + " PolymorphicA* a = 0;\n" + " return typeid(*a) == typeid(*a);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("struct NonPolymorphicA { ~A() {} };\n" + "bool foo() {\n" + " NonPolymorphicA* a = 0;\n" + " return typeid(*a) == typeid(*a);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("bool foo() {\n" + " char* c = 0;\n" + " return typeid(*c) == typeid(*c);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer_in_alignof() // #11401 + { + check("size_t foo() {\n" + " char* c = 0;\n" + " return alignof(*c);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("size_t foo() {\n" + " return alignof(*0);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int *p) {\n" + " f(alignof(*p));\n" + " if (p) {}\n" + " return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("size_t foo() {\n" + " char* c = 0;\n" + " return _Alignof(*c);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("size_t foo() {\n" + " return _alignof(*0);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("size_t foo() {\n" + " return __alignof(*0);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("size_t foo() {\n" + " return __alignof__(*0);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointer_in_for_loop() { + // Ticket #3278 + check("void f(int* ptr, int cnt){\n" + " if (!ptr)\n" + " cnt = 0;\n" + " for (int i = 0; i < cnt; ++i)\n" + " *ptr++ = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #11635 + check("void f(char *cons, int rlen, int pos) {\n" + " int i;\n" + " char* cp1;\n" + " for (cp1 = &cons[pos], i = 1; i < rlen; cp1--)\n" + " if (*cp1 == '*')\n" + " continue;\n" + " else\n" + " i++;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointerDelete() { + check("void f() {\n" + " K *k = getK();\n" + " if (k)\n" + " k->doStuff();\n" + " delete k;\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " K *k = getK();\n" + " if (k)\n" + " k[0] = ptr;\n" + " delete [] k;\n" + " k = new K[10];\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointerSubFunction() { + check("void g(int* x) { *x; }\n" + "void f(int* x) {\n" + " if (x)\n" + " g(x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointerExit() { + check("void f() {\n" + " K *k = getK();\n" + " if (!k)\n" + " exit(1);\n" + " k->f();\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + } + + void nullpointerStdString() { + check("void f(std::string s1) {\n" + " void* p = 0;\n" + " s1 = 0;\n" + " s1 = '\\0';\n" + " std::string s2 = 0;\n" + " std::string s2 = '\\0';\n" + " std::string s3(0);\n" + " foo(std::string(0));\n" + " s1 = p;\n" + " std::string s4 = p;\n" + " std::string s5(p);\n" + " foo(std::string(p));\n" + "}", true); + ASSERT_EQUALS("[test.cpp:9]: (error) Null pointer dereference: p\n" + "[test.cpp:10]: (error) Null pointer dereference: p\n" + "[test.cpp:11]: (error) Null pointer dereference: p\n" + "[test.cpp:12]: (error) Null pointer dereference: p\n" + "[test.cpp:3]: (error) Null pointer dereference\n" + "[test.cpp:5]: (error) Null pointer dereference\n" + "[test.cpp:7]: (error) Null pointer dereference\n" + "[test.cpp:8]: (error) Null pointer dereference\n" + , errout_str()); + + check("void f(std::string s1) {\n" + " s1 = nullptr;\n" + " std::string s2 = nullptr;\n" + " std::string s3(nullptr);\n" + " foo(std::string(nullptr));\n" + "}", true); + ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n" + "[test.cpp:3]: (error) Null pointer dereference\n" + "[test.cpp:4]: (error) Null pointer dereference\n" + "[test.cpp:5]: (error) Null pointer dereference\n" + , errout_str()); + + check("void f(std::string s1) {\n" + " s1 = NULL;\n" + " std::string s2 = NULL;\n" + " std::string s3(NULL);\n" + " foo(std::string(NULL));\n" + "}", true); + ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference\n" + "[test.cpp:3]: (error) Null pointer dereference\n" + "[test.cpp:4]: (error) Null pointer dereference\n" + "[test.cpp:5]: (error) Null pointer dereference\n" + , errout_str()); + + check("void f(std::string s1, const std::string& s2, const std::string* s3) {\n" + " void* p = 0;\n" + " if (x) { return; }\n" + " foo(s1 == p);\n" + " foo(s2 == p);\n" + " foo(s3 == p);\n" + " foo(p == s1);\n" + " foo(p == s2);\n" + " foo(p == s3);\n" + "}", true); + ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n" + "[test.cpp:5]: (error) Null pointer dereference: p\n" + "[test.cpp:7]: (error) Null pointer dereference: p\n" + "[test.cpp:8]: (error) Null pointer dereference: p\n", errout_str()); + + check("void f(std::string s1, const std::string& s2, const std::string* s3) {\n" + " void* p = 0;\n" + " if (x) { return; }\n" + " foo(0 == s1.size());\n" + " foo(0 == s2.size());\n" + " foo(0 == s3->size());\n" + " foo(s1.size() == 0);\n" + " foo(s2.size() == 0);\n" + " foo(s3->size() == 0);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::string s1, const std::string& s2) {\n" + " if (x) { return; }\n" + " foo(0 == s1[0]);\n" + " foo(0 == s2[0]);\n" + " foo(s1[0] == 0);\n" + " foo(s2[0] == 0);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::string s1, const std::string& s2) {\n" + " if (x) { return; }\n" + " foo(s1 == '\\0');\n" + " foo(s2 == '\\0');\n" + " foo('\\0' == s1);\n" + " foo('\\0' == s2);\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("class Bar {\n" + " std::string s;\n" + " Bar() : s(0) {}\n" + "};\n" + "class Foo {\n" + " std::string s;\n" + " Foo();\n" + "};\n" + "Foo::Foo() : s(0) {}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference\n" + "[test.cpp:9]: (error) Null pointer dereference\n", errout_str()); + + check("void f() {\n" + " std::string s = 0 == x ? \"a\" : \"b\";\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " const std::string s = g();\n" + " ASSERT_MESSAGE(\"Error on s\", 0 == s.compare(\"Some text\"));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int i, std::string s);\n" + "void bar() {\n" + " foo(0, \"\");\n" + " foo(0, 0);\n" + " foo(var, 0);\n" + " foo(var, NULL);\n" + " foo(var, nullptr);\n" + " foo(0, var);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference\n" + "[test.cpp:5]: (error) Null pointer dereference\n" + "[test.cpp:6]: (error) Null pointer dereference\n" + "[test.cpp:7]: (error) Null pointer dereference\n", errout_str()); + + check("std::string f() {\n" // #9827 + " char* p = NULL;\n" + " int r = g(p);\n" + " if (!r)\n" + " return \"\";\n" + " std::string s(p);\n" + " return s;\n" + "}\n", /*inconclusive*/ true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #11078 + " const char* p = nullptr;\n" + " std::string s1{ p };\n" + " std::string s2{ nullptr };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: p\n" + "[test.cpp:4]: (error) Null pointer dereference\n", + errout_str()); + + check("const char* g(long) { return nullptr; }\n" // #11561 + "void f() { std::string s = g(0L); }\n"); + ASSERT_EQUALS("[test.cpp:2]: (error) Null pointer dereference: g(0L)\n", + errout_str()); + } + + void nullpointerStdStream() { + check("void f(std::ifstream& is) {\n" + " char* p = 0;\n" + " is >> p;\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Possible null pointer dereference: p\n", "", errout_str()); + + check("void f(const std::ostringstream& oss, char* q) {\n" + " char const* p = 0;\n" // Simplification makes detection of bug difficult + " oss << p;\n" + " oss << foo << p;\n" + " if(q == 0)\n" + " oss << foo << q;\n" + "}", false); + ASSERT_EQUALS("[test.cpp:3]: (error) Null pointer dereference: p\n" + "[test.cpp:4]: (error) Null pointer dereference: p\n" + "[test.cpp:5] -> [test.cpp:6]: (warning) Either the condition 'q==0' is redundant or there is possible null pointer dereference: q.\n", errout_str()); + + check("void f(const char* p) {\n" + " if(p == 0) {\n" + " std::cout << p;\n" + " std::cerr << p;\n" + " std::cin >> p;\n" + " std::cout << abc << p;\n" + " }\n" + "}", false); + TODO_ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n" + "[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n" + "[test.cpp:2] -> [test.cpp:5]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n" + "[test.cpp:2] -> [test.cpp:6]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n", + "[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n" + "[test.cpp:2] -> [test.cpp:4]: (warning) Either the condition 'p==0' is redundant or there is possible null pointer dereference: p.\n", + errout_str()); + + check("void f() {\n" + " void* p1 = 0;\n" + " std::cout << p1;\n" // No char* + " char* p2 = 0;\n" + " std::cin >> (int)p;\n" // result casted + " std::cout << (int)p;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f(const std::string& str) {\n" + " long long ret = 0;\n" + " std::istringstream istr(str);\n" + " istr >> std::hex >> ret;\n" // Read integer + " return ret;\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + check("void f(int* i) {\n" + " if(i) return;\n" + " std::cout << i;\n" // Its no char* (#4240) + "}", true); + ASSERT_EQUALS("", errout_str()); + + // #5811 false positive: (error) Null pointer dereference + check("using namespace std;\n" + "std::string itoip(int ip) {\n" + " stringstream out;\n" + " out << ((ip >> 0) & 0xFF);\n" + " return out.str();\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + // avoid regression from first fix attempt for #5811... + check("void deserialize(const std::string &data) {\n" + "std::istringstream iss(data);\n" + "unsigned int len = 0;\n" + "if (!(iss >> len))\n" + " return;\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + } + + void nullpointerSmartPointer() { + // extracttests.start: void dostuff(int); + + check("struct Fred { int x; };\n" + "void f(std::shared_ptr p) {\n" + " if (p) {}\n" + " dostuff(p->x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + check("struct Fred { int x; };\n" + "void f(std::shared_ptr p) {\n" + " p = nullptr;\n" + " dostuff(p->x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout_str()); + + check("struct Fred { int x; };\n" + "void f(std::unique_ptr p) {\n" + " if (p) {}\n" + " dostuff(p->x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", errout_str()); + + check("struct Fred { int x; };\n" + "void f(std::unique_ptr p) {\n" + " p = nullptr;\n" + " dostuff(p->x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout_str()); + + check("struct Fred { int x; };\n" + "void f() {\n" + " std::shared_ptr p;\n" + " dostuff(p->x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout_str()); + + check("struct Fred { int x; };\n" + "void f(std::shared_ptr p) {\n" + " p.reset();\n" + " dostuff(p->x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout_str()); + + check("struct Fred { int x; };\n" + "void f(std::shared_ptr p) {\n" + " Fred * pp = nullptr;\n" + " p.reset(pp);\n" + " dostuff(p->x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: p\n", errout_str()); + + check("struct Fred { int x; };\n" + "void f(Fred& f) {\n" + " std::shared_ptr p;\n" + " p.reset(&f);\n" + " dostuff(p->x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct Fred { int x; };\n" + "void f(std::shared_ptr p) {\n" + " p.reset();\n" + " dostuff(p->x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout_str()); + + check("struct Fred { int x; };\n" + "void f() {\n" + " std::shared_ptr p(nullptr);\n" + " dostuff(p->x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Null pointer dereference: p\n", errout_str()); + + check("struct A {};\n" + "void f(int n) {\n" + " std::unique_ptr p;\n" + " p.reset(new const A*[n]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9216 + check("struct A {\n" + " void reset();\n" + " void f();\n" + "};\n" + "void g(std::unique_ptr var) {\n" + " var->reset();\n" + " var->f();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9439 + check("char* g();\n" + "char* f() {\n" + " std::unique_ptr x(g());\n" + " if( x ) {}\n" + " return x.release();\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + // #9496 + check("std::shared_ptr f() {\n" + " return std::shared_ptr(nullptr);\n" + "}\n" + "void g() {\n" + " int a = *f();\n" + "}\n", + true); + ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: f()\n", errout_str()); + } + + void functioncall() { // #3443 - function calls + // dereference pointer and then check if it's null + { + // function not seen + check("void f(int *p) {\n" + " *p = 0;\n" + " foo(p);\n" + " if (p) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // function seen (taking pointer parameter) + check("void foo(int *p) { }\n" + "\n" + "void f(int *p) {\n" + " *p = 0;\n" + " foo(p);\n" + " if (p) { }\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:6] -> [test.cpp:4]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", + errout_str()); + + // function seen (taking reference parameter) + check("void foo(int *&p) { }\n" + "\n" + "void f(int *p) {\n" + " *p = 0;\n" + " foo(p);\n" + " if (p) { }\n" + "}", true); + ASSERT_EQUALS("", errout_str()); + + // function implementation not seen + check("void foo(int *p);\n" + "\n" + "void f(int *p) {\n" + " *p = 0;\n" + " foo(p);\n" + " if (p) { }\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:6] -> [test.cpp:4]: (warning) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", + errout_str()); + + // inconclusive + check("void f(int *p) {\n" + " *p = 0;\n" + " foo(p);\n" + " if (p) { }\n" + "}", true); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:2]: (warning, inconclusive) Either the condition 'p' is redundant or there is possible null pointer dereference: p.\n", + errout_str()); + } + + // dereference struct pointer and then check if it's null + { + // function not seen + check("void f(struct ABC *abc) {\n" + " abc->a = 0;\n" + " foo(abc);\n" + " if (abc) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // function seen (taking pointer parameter) + check("void foo(struct ABC *abc) { }\n" + "\n" + "void f(struct ABC *abc) {\n" + " abc->a = 0;\n" + " foo(abc);\n" + " if (abc) { }\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:6] -> [test.cpp:4]: (warning) Either the condition 'abc' is redundant or there is possible null pointer dereference: abc.\n", + errout_str()); + + // function implementation not seen + check("void foo(struct ABC *abc);\n" + "\n" + "void f(struct ABC *abc) {\n" + " abc->a = 0;\n" + " foo(abc);\n" + " if (abc) { }\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:6] -> [test.cpp:4]: (warning) Either the condition 'abc' is redundant or there is possible null pointer dereference: abc.\n", + errout_str()); + + // inconclusive + check("void f(struct ABC *abc) {\n" + " abc->a = 0;\n" + " foo(abc);\n" + " if (abc) { }\n" + "}", true); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:2]: (warning, inconclusive) Either the condition 'abc' is redundant or there is possible null pointer dereference: abc.\n", + errout_str()); + } + } + + void functioncalllibrary() { + SimpleTokenizer tokenizer(settingsDefault,*this); + const char code[] = "void f() { int a,b,c; x(a,b,c); }"; + ASSERT_EQUALS(true, tokenizer.tokenize(code, false)); + const Token *xtok = Token::findsimplematch(tokenizer.tokens(), "x"); + + // nothing bad.. + { + Library library; + Library::ArgumentChecks arg; + library.functions["x"].argumentChecks[1] = arg; + library.functions["x"].argumentChecks[2] = arg; + library.functions["x"].argumentChecks[3] = arg; + + std::list null; + CheckNullPointer::parseFunctionCall(*xtok, null, library); + ASSERT_EQUALS(0U, null.size()); + } + + // for 1st parameter null pointer is not ok.. + { + Library library; + Library::ArgumentChecks arg; + library.functions["x"].argumentChecks[1] = arg; + library.functions["x"].argumentChecks[2] = arg; + library.functions["x"].argumentChecks[3] = arg; + library.functions["x"].argumentChecks[1].notnull = true; + + std::list null; + CheckNullPointer::parseFunctionCall(*xtok, null, library); + ASSERT_EQUALS(1U, null.size()); + ASSERT_EQUALS("a", null.front()->str()); + } + } + + void functioncallDefaultArguments() { + + check("void f(int *p = 0) {\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout_str()); + + check("void f(int *p = 0) {\n" + " if (!p)\n" + " return;\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(char a, int *p = 0) {\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout_str()); + + check("void f(int *p = 0) {\n" + " printf(\"p = %d\", *p);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout_str()); + + check("void f(int *p = 0) {\n" + " printf(\"p[1] = %d\", p[1]);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout_str()); + + check("void f(int *p = 0) {\n" + " buf[p] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int *p = 0) {\n" + " if (p != 0 && bar())\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int *p) {\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int *p = 0) {\n" + " if (p != 0)\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int *p = 0) {\n" + " int y;\n" + " if (p == 0)\n" + " p = &y;\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int a, int *p = 0) {\n" + " if (a != 0)\n" + " *p = 0;\n" + "}", true); + ASSERT_EQUALS( + "[test.cpp:3]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", + errout_str()); + + check("void f(int *p = 0) {\n" + " p = a;\n" + " *p = 0;\n" // <- don't simplify and verify + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int *p = 0) {\n" + " p += a;\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int *p = 0) {\n" + " if (p == 0) {\n" + " return 0;\n" + " }\n" + " return *p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int *p = 0) {\n" + " std::cout << p ? *p : 0;\n" // Due to operator precedence, this is equivalent to: (std::cout << p) ? *p : 0; + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout_str()); // Check the first branch of ternary + + check("void f(char *p = 0) {\n" + " std::cout << p ? *p : 0;\n" // Due to operator precedence, this is equivalent to: (std::cout << p) ? *p : 0; + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n" + "[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", // duplicate + errout_str()); + + check("void f(int *p = 0) {\n" + " std::cout << (p ? *p : 0);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int *p = 0) {\n" + " std::cout << p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int *p = 0) {\n" + " std::cout << (p && p[0] ? *p : 42);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void isEmpty(int *p = 0) {\n" + " return p && *p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void g(int *p = 0) {\n" + " return !p || *p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + + // bar may initialize p but be can't know for sure without knowing + // if p is passed in by reference and is modified by bar() + check("void f(int *p = 0) {\n" + " bar(p);\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int *p = 0) {\n" + " printf(\"%p\", p);\n" + " *p = 0;\n" + "}", true); + ASSERT_EQUALS("[test.cpp:3]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout_str()); + + // The init() function may or may not initialize p, but since the address + // of p is passed in, it's a good bet that p may be modified and + // so we should not report an error. + check("void f(int *p = 0) {\n" + " init(&p);\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void init(int* &g);\n" + "void f(int *p = 0) {\n" + " init(p);\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int *p = 0) {\n" + " if (p == 0) {\n" + " init(&p);\n" + " }\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int *p = 0) {\n" + " if (p == 0) {\n" + " throw SomeException;\n" + " }\n" + " *p = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int x, int *p = 0) {\n" + " int var1 = x ? *p : 5;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Possible null pointer dereference if the default parameter value is used: p\n", errout_str()); + } + + void nullpointer_internal_error() { // ticket #5080 + check("struct A { unsigned int size; };\n" + "struct B { struct A *a; };\n" + "void f(struct B *b) {\n" + " unsigned int j;\n" + " for (j = 0; j < b[0].a->size; ++j) {\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void ticket6505() { + check("void foo(MythSocket *socket) {\n" + " bool do_write=0;\n" + " if (socket) {\n" + " do_write=something();\n" + " }\n" + " if (do_write) {\n" + " socket->func();\n" + " }\n" + "}\n" + "void bar() {\n" + " foo(0);\n" + "}\n", true, false); + ASSERT_EQUALS("", errout_str()); + } + + void subtract() { + check("void foo(char *s) {\n" + " char *p = s - 20;\n" + "}\n" + "void bar() { foo(0); }"); + ASSERT_EQUALS("[test.cpp:2]: (error) Overflow in pointer arithmetic, NULL pointer is subtracted.\n", + errout_str()); + + check("void foo(char *s) {\n" + " if (!s) {}\n" + " char *p = s - 20;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!s' is redundant or there is overflow in pointer subtraction.\n", errout_str()); + + check("void foo(char *s) {\n" + " s -= 20;\n" + "}\n" + "void bar() { foo(0); }"); + ASSERT_EQUALS("[test.cpp:2]: (error) Overflow in pointer arithmetic, NULL pointer is subtracted.\n", + errout_str()); + + check("void foo(char *s) {\n" + " if (!s) {}\n" + " s -= 20;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!s' is redundant or there is overflow in pointer subtraction.\n", errout_str()); + + check("int* f8() { int *x = NULL; return --x; }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Overflow in pointer arithmetic, NULL pointer is subtracted.\n", errout_str()); + + check("int* f9() { int *x = NULL; return x--; }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Overflow in pointer arithmetic, NULL pointer is subtracted.\n", errout_str()); + } + + void addNull() { + check("void foo(char *s) {\n" + " char * p = s + 20;\n" + "}\n" + "void bar() { foo(0); }"); + ASSERT_EQUALS("[test.cpp:2]: (error) Pointer addition with NULL pointer.\n", errout_str()); + + check("void foo(char *s) {\n" + " if (!s) {}\n" + " char * p = s + 20;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!s' is redundant or there is pointer arithmetic with NULL pointer.\n", errout_str()); + + check("void foo(char *s) {\n" + " char * p = 20 + s;\n" + "}\n" + "void bar() { foo(0); }"); + ASSERT_EQUALS("[test.cpp:2]: (error) Pointer addition with NULL pointer.\n", errout_str()); + + check("void foo(char *s) {\n" + " if (!s) {}\n" + " char * p = 20 + s;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!s' is redundant or there is pointer arithmetic with NULL pointer.\n", errout_str()); + + check("void foo(char *s) {\n" + " s += 20;\n" + "}\n" + "void bar() { foo(0); }"); + ASSERT_EQUALS("[test.cpp:2]: (error) Pointer addition with NULL pointer.\n", errout_str()); + + check("void foo(char *s) {\n" + " if (!s) {}\n" + " s += 20;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition '!s' is redundant or there is pointer arithmetic with NULL pointer.\n", errout_str()); + + check("int* f7() { int *x = NULL; return ++x; }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Pointer addition with NULL pointer.\n", errout_str()); + + check("int* f10() { int *x = NULL; return x++; }"); + ASSERT_EQUALS("[test.cpp:1]: (error) Pointer addition with NULL pointer.\n", errout_str()); + + check("class foo {};\n" + "const char* get() const { return 0; }\n" + "void f(foo x) { if (get()) x += get(); }"); + ASSERT_EQUALS("", errout_str()); + + check("typedef struct { uint8_t* buf, *buf_end; } S;\n" // #11117 + "void f(S* s, uint8_t* buffer, int buffer_size) {\n" + " if (buffer_size < 0) {\n" + " buffer_size = 0;\n" + " buffer = NULL;\n" + " }\n" + " s->buf = buffer;\n" + " s->buf_end = s->buf + buffer_size;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void isPointerDeRefFunctionDecl() { + check("const char** get() { return 0; }"); + ASSERT_EQUALS("", errout_str()); + } + +#define ctu(code) ctu_(code, __FILE__, __LINE__) + void ctu_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + CTU::FileInfo *ctu = CTU::getFileInfo(tokenizer); + + // Check code.. + std::list fileInfo; + Check& c = getCheck(); + fileInfo.push_back(c.getFileInfo(tokenizer, settings)); + c.analyseWholeProgram(ctu, fileInfo, settings, *this); + while (!fileInfo.empty()) { + delete fileInfo.back(); + fileInfo.pop_back(); + } + delete ctu; + } + + void ctuTest() { + setMultiline(); + + ctu("void f(int *fp) {\n" + " a = *fp;\n" + "}\n" + "int main() {\n" + " int *p = 0;\n" + " f(p);\n" + "}"); + ASSERT_EQUALS("test.cpp:2:error:Null pointer dereference: fp\n" + "test.cpp:5:note:Assignment 'p=0', assigned value is 0\n" + "test.cpp:6:note:Calling function f, 1st argument is null\n" + "test.cpp:2:note:Dereferencing argument fp that is null\n", errout_str()); + + ctu("void use(int *p) { a = *p + 3; }\n" + "void call(int x, int *p) { x++; use(p); }\n" + "int main() {\n" + " call(4,0);\n" + "}"); + ASSERT_EQUALS("test.cpp:1:error:Null pointer dereference: p\n" + "test.cpp:4:note:Calling function call, 2nd argument is null\n" + "test.cpp:2:note:Calling function use, 1st argument is null\n" + "test.cpp:1:note:Dereferencing argument p that is null\n", errout_str()); + + ctu("void dostuff(int *x, int *y) {\n" + " if (!var)\n" + " return -1;\n" // <- early return + " *x = *y;\n" + "}\n" + "\n" + "void f() {\n" + " dostuff(a, 0);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + ctu("void dostuff(int *x, int *y) {\n" + " if (cond)\n" + " *y = -1;\n" // <- conditionally written + " *x = *y;\n" + "}\n" + "\n" + "void f() {\n" + " dostuff(a, 0);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // else + ctu("void dostuff(int mask, int *p) {\n" + " if (mask == 13) ;\n" + " else *p = 45;\n" + "}\n" + "\n" + "void f() {\n" + " dostuff(0, 0);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // ?, &&, || + ctu("void dostuff(int mask, int *p) {\n" + " x = (mask & 1) ? *p : 0;\n" + "}\n" + "\n" + "void f() {\n" + " dostuff(0, 0);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + ctu("void g(int* x) { *x; }\n" + "void f(int* x) {\n" + " if (x)\n" + " g(x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + ctu("size_t f(int* p) {\n" + " size_t len = sizeof(*p);\n" + " return len;\n" + "}\n" + "void g() {\n" + " f(NULL);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + ctu("size_t f(int* p) {\n" + " size_t len = alignof(*p);\n" + " return len;\n" + "}\n" + "void g() {\n" + " f(NULL);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } +}; + +REGISTER_TEST(TestNullPointer) diff --git a/cppcheck-2.14.0/test/testoptions.cpp b/cppcheck-2.14.0/test/testoptions.cpp new file mode 100644 index 00000000..f6baeb87 --- /dev/null +++ b/cppcheck-2.14.0/test/testoptions.cpp @@ -0,0 +1,140 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "fixture.h" + +#include +#include +#include + + +class TestOptions : public TestFixture { +public: + TestOptions() + : TestFixture("TestOptions") {} + + +private: + void run() override { + TEST_CASE(which_test); + TEST_CASE(which_test_method); + TEST_CASE(no_test_method); + TEST_CASE(not_quiet); + TEST_CASE(quiet); + TEST_CASE(not_help); + TEST_CASE(help); + TEST_CASE(help_long); + TEST_CASE(multiple_testcases); + TEST_CASE(multiple_testcases_ignore_duplicates); + TEST_CASE(invalid_switches); + TEST_CASE(summary); + TEST_CASE(dry_run); + } + + + void which_test() const { + const char* argv[] = {"./test_runner", "TestClass"}; + options args(sizeof argv / sizeof argv[0], argv); + ASSERT(std::set {"TestClass"} == args.which_test()); + } + + + void which_test_method() const { + const char* argv[] = {"./test_runner", "TestClass::TestMethod"}; + options args(sizeof argv / sizeof argv[0], argv); + ASSERT(std::set {"TestClass::TestMethod"} == args.which_test()); + } + + + void no_test_method() const { + const char* argv[] = {"./test_runner"}; + options args(sizeof argv / sizeof argv[0], argv); + ASSERT(std::set {""} == args.which_test()); + } + + + void not_quiet() const { + const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-v"}; + options args(sizeof argv / sizeof argv[0], argv); + ASSERT_EQUALS(false, args.quiet()); + } + + + void quiet() const { + const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-q"}; + options args(sizeof argv / sizeof argv[0], argv); + ASSERT_EQUALS(true, args.quiet()); + } + + void not_help() const { + const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-v"}; + options args(sizeof argv / sizeof argv[0], argv); + ASSERT_EQUALS(false, args.help()); + } + + + void help() const { + const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-h"}; + options args(sizeof argv / sizeof argv[0], argv); + ASSERT_EQUALS(true, args.help()); + } + + + void help_long() const { + const char* argv[] = {"./test_runner", "TestClass::TestMethod", "--help"}; + options args(sizeof argv / sizeof argv[0], argv); + ASSERT_EQUALS(true, args.help()); + } + + void multiple_testcases() const { + const char* argv[] = {"./test_runner", "TestClass::TestMethod", "TestClass::AnotherTestMethod"}; + options args(sizeof argv / sizeof argv[0], argv); + std::set expected {"TestClass::TestMethod", "TestClass::AnotherTestMethod"}; + ASSERT(expected == args.which_test()); + } + + void multiple_testcases_ignore_duplicates() const { + const char* argv[] = {"./test_runner", "TestClass::TestMethod", "TestClass"}; + options args(sizeof argv / sizeof argv[0], argv); + std::set expected {"TestClass"}; + ASSERT(expected == args.which_test()); + } + + void invalid_switches() const { + const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-a", "-v", "-q"}; + options args(sizeof argv / sizeof argv[0], argv); + std::set expected {"TestClass::TestMethod"}; + ASSERT(expected == args.which_test()); + ASSERT_EQUALS(true, args.quiet()); + } + + void summary() const { + const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-n"}; + options args(sizeof argv / sizeof argv[0], argv); + ASSERT_EQUALS(false, args.summary()); + } + + void dry_run() const { + const char* argv[] = {"./test_runner", "TestClass::TestMethod", "-d"}; + options args(sizeof argv / sizeof argv[0], argv); + ASSERT_EQUALS(true, args.dry_run()); + } +}; + +REGISTER_TEST(TestOptions) diff --git a/cppcheck-2.14.0/test/testother.cpp b/cppcheck-2.14.0/test/testother.cpp new file mode 100644 index 00000000..5faec17d --- /dev/null +++ b/cppcheck-2.14.0/test/testother.cpp @@ -0,0 +1,12064 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "platform.h" +#include "settings.h" +#include "standards.h" +#include "tokenize.h" + +#include +#include + +class TestOther : public TestFixture { +public: + TestOther() : TestFixture("TestOther") {} + +private: + /*const*/ Settings _settings = settingsBuilder().library("std.cfg").build(); + + void run() override { + TEST_CASE(emptyBrackets); + + TEST_CASE(zeroDiv1); + TEST_CASE(zeroDiv2); + TEST_CASE(zeroDiv3); + TEST_CASE(zeroDiv4); + TEST_CASE(zeroDiv5); + TEST_CASE(zeroDiv6); + TEST_CASE(zeroDiv7); // #4930 + TEST_CASE(zeroDiv8); + TEST_CASE(zeroDiv9); + TEST_CASE(zeroDiv10); + TEST_CASE(zeroDiv11); + TEST_CASE(zeroDiv12); + TEST_CASE(zeroDiv13); + TEST_CASE(zeroDiv14); // #1169 + TEST_CASE(zeroDiv15); // #8319 + TEST_CASE(zeroDiv16); // #11158 + TEST_CASE(zeroDiv17); // #9931 + TEST_CASE(zeroDiv18); + TEST_CASE(zeroDiv19); + + TEST_CASE(zeroDivCond); // division by zero / useless condition + + TEST_CASE(nanInArithmeticExpression); + + TEST_CASE(varScope1); + TEST_CASE(varScope2); + TEST_CASE(varScope3); + TEST_CASE(varScope4); + TEST_CASE(varScope5); + TEST_CASE(varScope6); + TEST_CASE(varScope7); + TEST_CASE(varScope8); + TEST_CASE(varScope9); // classes may have extra side-effects + TEST_CASE(varScope10); // Undefined macro FOR + TEST_CASE(varScope11); // #2475 - struct initialization is not inner scope + TEST_CASE(varScope12); + TEST_CASE(varScope13); // variable usage in inner loop + TEST_CASE(varScope14); + TEST_CASE(varScope15); // #4573 if-else-if + TEST_CASE(varScope16); + TEST_CASE(varScope17); + TEST_CASE(varScope18); + TEST_CASE(varScope20); // Ticket #5103 + TEST_CASE(varScope21); // Ticket #5382 + TEST_CASE(varScope22); // Ticket #5684 + TEST_CASE(varScope23); // Ticket #6154 + TEST_CASE(varScope24); // pointer / reference + TEST_CASE(varScope25); // time_t + TEST_CASE(varScope26); // range for loop, map + TEST_CASE(varScope27); // #7733 - #if + TEST_CASE(varScope28); // #10527 + TEST_CASE(varScope29); // #10888 + TEST_CASE(varScope30); // #8541 + TEST_CASE(varScope31); // #11099 + TEST_CASE(varScope32); // #11441 + TEST_CASE(varScope33); + TEST_CASE(varScope34); + TEST_CASE(varScope35); + TEST_CASE(varScope36); // #12158 + TEST_CASE(varScope37); // #12158 + TEST_CASE(varScope38); + TEST_CASE(varScope39); + TEST_CASE(varScope40); + + TEST_CASE(oldStylePointerCast); + TEST_CASE(invalidPointerCast); + + TEST_CASE(passedByValue); + TEST_CASE(passedByValue_nonConst); + TEST_CASE(passedByValue_externC); + + TEST_CASE(constVariable); + TEST_CASE(constParameterCallback); + TEST_CASE(constPointer); + + TEST_CASE(switchRedundantAssignmentTest); + TEST_CASE(switchRedundantOperationTest); + TEST_CASE(switchRedundantBitwiseOperationTest); + TEST_CASE(unreachableCode); + TEST_CASE(redundantContinue); + + TEST_CASE(suspiciousCase); + TEST_CASE(suspiciousEqualityComparison); + TEST_CASE(suspiciousUnaryPlusMinus); // #8004 + + 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(testMisusedScopeObjectStandardType); + TEST_CASE(testMisusedScopeObjectNamespace); + TEST_CASE(testMisusedScopeObjectAssignment); // #11371 + TEST_CASE(trac2071); + TEST_CASE(trac2084); + TEST_CASE(trac3693); + + TEST_CASE(clarifyCalculation); + TEST_CASE(clarifyStatement); + + TEST_CASE(duplicateBranch); + TEST_CASE(duplicateBranch1); // tests extracted by http://www.viva64.com/en/b/0149/ ( Comparison between PVS-Studio and cppcheck ): Errors detected in Quake 3: Arena by PVS-Studio: Fragment 2 + TEST_CASE(duplicateBranch2); // empty macro + TEST_CASE(duplicateBranch3); + TEST_CASE(duplicateBranch4); + TEST_CASE(duplicateBranch5); // make sure the Token attributes are compared + TEST_CASE(duplicateBranch6); + TEST_CASE(duplicateExpression1); + TEST_CASE(duplicateExpression2); // ticket #2730 + TEST_CASE(duplicateExpression3); // ticket #3317 + TEST_CASE(duplicateExpression4); // ticket #3354 (++) + TEST_CASE(duplicateExpression5); // ticket #3749 (macros with same values) + TEST_CASE(duplicateExpression6); // ticket #4639 + TEST_CASE(duplicateExpression7); + TEST_CASE(duplicateExpression8); + TEST_CASE(duplicateExpression9); // #9320 + TEST_CASE(duplicateExpression10); // #9485 + TEST_CASE(duplicateExpression11); // #8916 (function call) + TEST_CASE(duplicateExpression12); // #10026 + TEST_CASE(duplicateExpression13); // #7899 + TEST_CASE(duplicateExpression14); // #9871 + TEST_CASE(duplicateExpression15); // #10650 + TEST_CASE(duplicateExpression16); // #10569 + TEST_CASE(duplicateExpression17); // #12036 + TEST_CASE(duplicateExpressionLoop); + TEST_CASE(duplicateValueTernary); + TEST_CASE(duplicateExpressionTernary); // #6391 + TEST_CASE(duplicateExpressionTemplate); // #6930 + TEST_CASE(duplicateExpressionCompareWithZero); + TEST_CASE(oppositeExpression); + TEST_CASE(duplicateVarExpression); + TEST_CASE(duplicateVarExpressionUnique); + TEST_CASE(duplicateVarExpressionAssign); + TEST_CASE(duplicateVarExpressionCrash); + TEST_CASE(multiConditionSameExpression); + + TEST_CASE(checkSignOfUnsignedVariable); + TEST_CASE(checkSignOfPointer); + + TEST_CASE(checkSuspiciousSemicolon1); + TEST_CASE(checkSuspiciousSemicolon2); + TEST_CASE(checkSuspiciousSemicolon3); + TEST_CASE(checkSuspiciousComparison); + + TEST_CASE(checkInvalidFree); + + TEST_CASE(checkRedundantCopy); + + TEST_CASE(checkNegativeShift); + + TEST_CASE(incompleteArrayFill); + + TEST_CASE(redundantVarAssignment); + TEST_CASE(redundantVarAssignment_trivial); + TEST_CASE(redundantVarAssignment_struct); + TEST_CASE(redundantVarAssignment_7133); + TEST_CASE(redundantVarAssignment_stackoverflow); + TEST_CASE(redundantVarAssignment_lambda); + TEST_CASE(redundantVarAssignment_loop); + TEST_CASE(redundantVarAssignment_after_switch); + TEST_CASE(redundantVarAssignment_pointer); + TEST_CASE(redundantVarAssignment_pointer_parameter); + TEST_CASE(redundantVarAssignment_array); + TEST_CASE(redundantVarAssignment_switch_break); + TEST_CASE(redundantInitialization); + TEST_CASE(redundantMemWrite); + + TEST_CASE(varFuncNullUB); + + TEST_CASE(checkCastIntToCharAndBack); // ticket #160 + + TEST_CASE(checkCommaSeparatedReturn); + TEST_CASE(checkPassByReference); + + TEST_CASE(checkComparisonFunctionIsAlwaysTrueOrFalse); + + TEST_CASE(integerOverflow); // #5895 + + TEST_CASE(redundantPointerOp); + TEST_CASE(test_isSameExpression); + TEST_CASE(raceAfterInterlockedDecrement); + + TEST_CASE(testUnusedLabel); + + TEST_CASE(testEvaluationOrder); + TEST_CASE(testEvaluationOrderSelfAssignment); + TEST_CASE(testEvaluationOrderMacro); + TEST_CASE(testEvaluationOrderSequencePointsFunctionCall); + TEST_CASE(testEvaluationOrderSequencePointsComma); + TEST_CASE(testEvaluationOrderSizeof); + + TEST_CASE(testUnsignedLessThanZero); + + TEST_CASE(doubleMove1); + TEST_CASE(doubleMoveMemberInitialization1); + TEST_CASE(doubleMoveMemberInitialization2); + TEST_CASE(doubleMoveMemberInitialization3); // #9974 + TEST_CASE(doubleMoveMemberInitialization4); + TEST_CASE(moveAndAssign1); + TEST_CASE(moveAndAssign2); + TEST_CASE(moveAssignMoveAssign); + TEST_CASE(moveAndReset1); + TEST_CASE(moveAndReset2); + TEST_CASE(moveResetMoveReset); + TEST_CASE(moveAndFunctionParameter); + TEST_CASE(moveAndFunctionParameterReference); + TEST_CASE(moveAndFunctionParameterConstReference); + TEST_CASE(moveAndFunctionParameterUnknown); + TEST_CASE(moveAndReturn); + TEST_CASE(moveAndClear); + TEST_CASE(movedPointer); + TEST_CASE(moveAndAddressOf); + TEST_CASE(partiallyMoved); + TEST_CASE(moveAndLambda); + TEST_CASE(moveInLoop); + TEST_CASE(moveCallback); + TEST_CASE(moveClassVariable); + TEST_CASE(forwardAndUsed); + TEST_CASE(moveAndReference); + TEST_CASE(moveForRange); + TEST_CASE(moveTernary); + + TEST_CASE(funcArgNamesDifferent); + TEST_CASE(funcArgOrderDifferent); + TEST_CASE(cpp11FunctionArgInit); // #7846 - "void foo(int declaration = {}) {" + + TEST_CASE(shadowVariables); + TEST_CASE(knownArgument); + TEST_CASE(knownArgumentHiddenVariableExpression); + TEST_CASE(knownArgumentTernaryOperator); + TEST_CASE(checkComparePointers); + + TEST_CASE(unusedVariableValueTemplate); // #8994 + + TEST_CASE(moduloOfOne); + + TEST_CASE(sameExpressionPointers); + + TEST_CASE(checkOverlappingWrite); + + TEST_CASE(constVariableArrayMember); // #10371 + + TEST_CASE(knownPointerToBool); + TEST_CASE(iterateByValue); + } + +#define check(...) check_(__FILE__, __LINE__, __VA_ARGS__) + void check_(const char* file, int line, const char code[], bool cpp = true, bool inconclusive = true, bool runSimpleChecks=true, bool verbose=false, Settings* settings = nullptr) { + if (!settings) { + settings = &_settings; + } + settings->severity.enable(Severity::style); + settings->severity.enable(Severity::warning); + settings->severity.enable(Severity::portability); + settings->severity.enable(Severity::performance); + settings->standards.c = Standards::CLatest; + settings->standards.cpp = Standards::CPPLatest; + settings->certainty.setEnabled(Certainty::inconclusive, inconclusive); + settings->verbose = verbose; + + // Tokenize.. + SimpleTokenizer tokenizer(*settings, *this); + ASSERT_LOC(tokenizer.tokenize(code, cpp), file, line); + + // Check.. + runChecks(tokenizer, this); + + (void)runSimpleChecks; // TODO Remove this + } + + void check_(const char* file, int line, const char code[], Settings *s) { + check_(file, line, code, true, true, true, false, s); + } + +#define checkP(...) checkP_(__FILE__, __LINE__, __VA_ARGS__) + void checkP_(const char* file, int line, const char code[], const char *filename = "test.cpp") { + Settings* settings = &_settings; + settings->severity.enable(Severity::style); + settings->severity.enable(Severity::warning); + settings->severity.enable(Severity::portability); + settings->severity.enable(Severity::performance); + settings->standards.c = Standards::CLatest; + settings->standards.cpp = Standards::CPPLatest; + settings->certainty.enable(Certainty::inconclusive); + + std::vector files(1, filename); + Tokenizer tokenizer(*settings, *this); + PreprocessorHelper::preprocess(code, files, tokenizer, *this); + + // Tokenizer.. + ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); + + // Check.. + runChecks(tokenizer, this); + } + + void checkInterlockedDecrement(const char code[]) { + /*const*/ Settings settings = settingsBuilder().platform(Platform::Type::Win32A).build(); + + check(code, true, false, true, false, &settings); + } + + void emptyBrackets() { + check("{\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + + void zeroDiv1() { // floating point division by zero => no error + check("void foo() {\n" + " cout << 1. / 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " cout << 42 / (double)0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " cout << 42 / (float)0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " cout << 42 / (int)0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Division by zero.\n", errout_str()); + } + + void zeroDiv2() { + check("void foo()\n" + "{\n" + " int sum = 0;\n" + " for(int i = 0; i < n; i ++)\n" + " {\n" + " sum += i;\n" + " }\n" + " cout< do not warn + check("void f() {\n" + " int a = x/2*3/0;\n" + " int b = y/2*3%0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void f(int x, int y) {\n" + " int a = x/2*3/0;\n" + " int b = y/2*3%0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Division by zero.\n" + "[test.cpp:3]: (error) Division by zero.\n", errout_str()); + } + + void zeroDiv8() { + // #5584 - FP when function is unknown + check("void f() {\n" + " int a = 0;\n" + " do_something(a);\n" + " return 4 / a;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error, inconclusive) Division by zero.\n", errout_str()); + } + + void zeroDiv9() { + // #6403 FP zerodiv - inside protecting if-clause + check("void foo() {\n" + " double fStepHelp = 0;\n" + " if( (rOuterValue >>= fStepHelp) ) {\n" + " if( fStepHelp != 0.0) {\n" + " double fStepMain = 1;\n" + " sal_Int32 nIntervalCount = static_cast< sal_Int32 >(fStepMain / fStepHelp);\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void zeroDiv10() { + // #5402 false positive: (error) Division by zero -- with boost::format + check("int main() {\n" + " std::cout\n" + " << boost::format(\" %d :: %s <> %s\") % 0 % \"a\" % \"b\"\n" + " << std::endl;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void zeroDiv11() { + check("void f(int a) {\n" + " int res = (a+2)/0;\n" + " int res = (a*2)/0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Division by zero.\n" + "[test.cpp:3]: (error) Division by zero.\n", errout_str()); + check("void f() {\n" + " int res = (a+2)/0;\n" + " int res = (a*2)/0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void zeroDiv12() { + // #8141 + check("intmax_t f() {\n" + " return 1 / imaxabs(0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Division by zero.\n", errout_str()); + } + void zeroDiv13() { + // #7324 + check("int f () {\n" + " int dividend = 10;\n" + " int divisor = 1;\n" + " dividend = dividend / (--divisor);\n" + " return dividend;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Division by zero.\n", errout_str()); + } + + void zeroDiv14() { + check("void f() {\n" // #1169 + " double dx = 1.;\n" + " int ix = 1;\n" + " int i = 1;\n" + " std::cout << ix / (i >> 1) << std::endl;\n" + " std::cout << dx / (i >> 1) << std::endl;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (error) Division by zero.\n", errout_str()); + } + + void zeroDiv15() { // #8319 + check("int f(int i) { return i - 1; }\n" + "int f() {\n" + " const int d = 1;\n" + " const int r = 1 / f(d);\n" + " return r;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (error) Division by zero.\n", errout_str()); + } + + // #11158 + void zeroDiv16() + { + check("int f(int i) {\n" + " int number = 10, a = 0;\n" + " for (int count = 0; count < 2; count++) {\n" + " a += (i / number) % 10;\n" + " number = number / 10;\n" + " }\n" + " return a;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int i) {\n" + " int number = 10, a = 0;\n" + " for (int count = 0; count < 2; count++) {\n" + " int x = number / 10;\n" + " a += (i / number) % 10;\n" + " number = x;\n" + " }\n" + " return a;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void zeroDiv17() { // #9931 + check("int f(int len) {\n" + " int sz = sizeof(void*[255]) / 255;\n" + " int x = len % sz;\n" + " return x;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void zeroDiv18() + { + check("int f(int x, int y) {\n" + " if (x == y) {}\n" + " return 1 / (x-y);\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:3]: (warning) Either the condition 'x==y' is redundant or there is division by zero at line 3.\n", + errout_str()); + } + + void zeroDiv19() + { + check("void f() {\n" // #2456 + " for (int i = 0;;)\n" + " int j = 10 / i;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (error) Division by zero.\n", errout_str()); + } + + void zeroDivCond() { + check("void f(unsigned int x) {\n" + " int y = 17 / x;\n" + " if (x > 0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x>0' is redundant or there is division by zero at line 2.\n", errout_str()); + + check("void f(unsigned int x) {\n" + " int y = 17 / x;\n" + " if (x >= 1) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x>=1' is redundant or there is division by zero at line 2.\n", errout_str()); + + check("void f(int x) {\n" + " int y = 17 / x;\n" + " if (x == 0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x==0' is redundant or there is division by zero at line 2.\n", errout_str()); + + check("void f(unsigned int x) {\n" + " int y = 17 / x;\n" + " if (x != 0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (warning) Either the condition 'x!=0' is redundant or there is division by zero at line 2.\n", errout_str()); + + // function call + check("void f1(int x, int y) { c=x/y; }\n" + "void f2(unsigned int y) {\n" + " f1(123,y);\n" + " if (y>0){}\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:1]: (warning) Either the condition 'y>0' is redundant or there is division by zero at line 1.\n", + errout_str()); + + // avoid false positives when variable is changed after division + check("void f() {\n" + " unsigned int x = do_something();\n" + " int y = 17 / x;\n" + " x = some+calculation;\n" + " if (x != 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + { + // function is called that might modify global variable + check("void do_something();\n" + "int x;\n" + "void f() {\n" + " int y = 17 / x;\n" + " do_something();\n" + " if (x != 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // function is called. but don't care, variable is local + check("void do_something();\n" + "void f() {\n" + " int x = some + calculation;\n" + " int y = 17 / x;\n" + " do_something();\n" + " if (x != 0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:4]: (warning) Either the condition 'x!=0' is redundant or there is division by zero at line 4.\n", errout_str()); + } + + check("void do_something(int value);\n" + "void f(int x) {\n" + " int y = 17 / x;\n" + " do_something(x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int x;\n" + "void f() {\n" + " int y = 17 / x;\n" + " while (y || x == 0) { x--; }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // ticket 5033 segmentation fault (valid code) in CheckOther::checkZeroDivisionOrUselessCondition + check("void f() {\n" + "double* p1= new double[1];\n" + "double* p2= new double[1];\n" + "double* p3= new double[1];\n" + "double* pp[3] = {p1,p2,p3};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #5105 - FP + check("int f(int a, int b) {\n" + " int r = a / b;\n" + " if (func(b)) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Unknown types for b and c --> do not warn + check("int f(int d) {\n" + " int r = (a?b:c) / d;\n" + " if (d == 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int a) {\n" + " int r = a ? 1 / a : 0;\n" + " if (a == 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int a) {\n" + " int r = (a == 0) ? 0 : 1 / a;\n" + " if (a == 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int g();\n" + "void f(int b) {\n" + " int x = g();\n" + " if (x == 0) {}\n" + " else if (x > 0) {}\n" + " else\n" + " a = b / -x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " int x;\n" + "};\n" + "int f(A* a) {\n" + " if (a->x == 0) \n" + " a->x = 1;\n" + " return 1/a->x;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10049 + check("int f(int argc) {\n" + " int quotient, remainder;\n" + " remainder = argc % 2;\n" + " argc = 2;\n" + " quotient = argc;\n" + " if (quotient != 0) \n" + " return quotient;\n" + " return remainder;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #11315 + checkP("#define STATIC_ASSERT(c) \\\n" + "do { enum { sa = 1/(int)(!!(c)) }; } while (0)\n" + "void f() {\n" + " STATIC_ASSERT(sizeof(int) == sizeof(FOO));\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #11505 + check("void f(uint16_t num, uint8_t radix) {\n" + " int c = num % radix;\n" + " num /= radix;\n" + " if (!num) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void nanInArithmeticExpression() { + check("void f()\n" + "{\n" + " double x = 3.0 / 0.0 + 1.0;\n" + " printf(\"%f\", x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Using NaN/Inf in a computation.\n", errout_str()); + + check("void f()\n" + "{\n" + " double x = 3.0 / 0.0 - 1.0;\n" + " printf(\"%f\", x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Using NaN/Inf in a computation.\n", errout_str()); + + check("void f()\n" + "{\n" + " double x = 1.0 + 3.0 / 0.0;\n" + " printf(\"%f\", x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Using NaN/Inf in a computation.\n", errout_str()); + + check("void f()\n" + "{\n" + " double x = 1.0 - 3.0 / 0.0;\n" + " printf(\"%f\", x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Using NaN/Inf in a computation.\n", errout_str()); + + check("void f()\n" + "{\n" + " double x = 3.0 / 0.0;\n" + " printf(\"%f\", x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + } + + void varScope1() { + check("unsigned short foo()\n" + "{\n" + " test_client CClient;\n" + " try\n" + " {\n" + " if (CClient.Open())\n" + " {\n" + " return 0;\n" + " }\n" + " }\n" + " catch (...)\n" + " {\n" + " return 2;\n" + " }\n" + "\n" + " try\n" + " {\n" + " CClient.Close();\n" + " }\n" + " catch (...)\n" + " {\n" + " return 2;\n" + " }\n" + "\n" + " return 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope2() { + check("int foo()\n" + "{\n" + " Error e;\n" + " e.SetValue(12);\n" + " throw e;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope3() { + check("void foo()\n" + "{\n" + " int i;\n" + " int *p = 0;\n" + " if (abc)\n" + " {\n" + " p = &i;\n" + " }\n" + " *p = 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope4() { + check("void foo()\n" + "{\n" + " int i;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope5() { + check("void f(int x)\n" + "{\n" + " int i = 0;\n" + " if (x) {\n" + " for ( ; i < 10; ++i) ;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) The scope of the variable 'i' can be reduced.\n", errout_str()); + + check("void f(int x) {\n" + " const unsigned char i = 0;\n" + " if (x) {\n" + " for ( ; i < 10; ++i) ;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x)\n" + "{\n" + " int i = 0;\n" + " if (x) {b()}\n" + " else {\n" + " for ( ; i < 10; ++i) ;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) The scope of the variable 'i' can be reduced.\n", errout_str()); + } + + void varScope6() { + check("void f(int x)\n" + "{\n" + " int i = x;\n" + " if (a) {\n" + " x++;\n" + " }\n" + " if (b) {\n" + " c(i);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #5398 + " bool success = false;\n" + " int notReducable(someClass.getX(&success));\n" + " if (success) {\n" + " foo(notReducable);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(Test &test) {\n" + " int& x = test.getData();\n" + " if (test.process())\n" + " x = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f()\n" + "{\n" + "int foo = 0;\n" + "std::vector vec(10);\n" + "BOOST_FOREACH(int& i, vec)\n" + "{\n" + " foo += 1;\n" + " if(foo == 10)\n" + " {\n" + " return 0;\n" + " }\n" + "}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int &x)\n" + "{\n" + " int n = 1;\n" + " do\n" + " {\n" + " ++n;\n" + " ++x;\n" + " } while (x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope7() { + check("void f(int x)\n" + "{\n" + " int y = 0;\n" + " b(y);\n" + " if (x) {\n" + " y++;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope8() { + check("void test() {\n" + " float edgeResistance=1;\n" + " std::vector edges;\n" + " BOOST_FOREACH(int edge, edges) {\n" + " edgeResistance = (edge+1) / 2.0;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'edgeResistance' can be reduced.\n", errout_str()); + } + + void varScope9() { + // classes may have extra side effects + check("class fred {\n" + "public:\n" + " void x();\n" + "};\n" + "void test(int a) {\n" + " fred f;\n" + " if (a == 2) {\n" + " f.x();\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope10() { + check("int f()\n" + "{\n" + " int x = 0;\n" + " FOR {\n" + " foo(x++);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope11() { + check("int f() {\n" + " int x = 0;\n" + " AB ab = { x, 0 };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f() {\n" + " int x = 0;\n" + " if (a == 0) { ++x; }\n" + " AB ab = { x, 0 };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f() {\n" + " int x = 0;\n" + " if (a == 0) { ++x; }\n" + " if (a == 1) { AB ab = { x, 0 }; }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope12() { + check("void f(int x) {\n" + " int i[5];\n" + " int* j = y;\n" + " if (x)\n" + " foo(i);\n" + " foo(j);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'i' can be reduced.\n", errout_str()); + + check("void f(int x) {\n" + " int i[5];\n" + " int* j;\n" + " if (x)\n" + " j = i;\n" + " foo(j);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " const bool b = true;\n" + " x++;\n" + " if (x == 5)\n" + " foo(b);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) {\n" + " const bool b = x;\n" + " x++;\n" + " if (x == 5)\n" + " foo(b);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope13() { + // #2770 + check("void f() {\n" + " int i = 0;\n" + " forever {\n" + " if (i++ == 42) { break; }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope14() { + // #3941 + check("void f() {\n" + " const int i( foo());\n" + " if(a) {\n" + " for ( ; i < 10; ++i) ;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope15() { + // #4573 + check("void f() {\n" + " int a,b,c;\n" + " if (a);\n" + " else if(b);\n" + " else if(c);\n" + " else;\n" + "}", true, false); + ASSERT_EQUALS("", errout_str()); + } + + void varScope16() { + check("void f() {\n" + " int a = 0;\n" + " while((++a) < 56) {\n" + " foo();\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int a = 0;\n" + " do {\n" + " foo();\n" + " } while((++a) < 56);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int a = 0;\n" + " do {\n" + " a = 64;\n" + " foo(a);\n" + " } while((++a) < 56);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int a = 0;\n" + " do {\n" + " a = 64;\n" + " foo(a);\n" + " } while(z());\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'a' can be reduced.\n", errout_str()); + } + + void varScope17() { + check("void f() {\n" + " int x;\n" + " if (a) {\n" + " x = stuff(x);\n" + " morestuff(x);\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'x' can be reduced.\n", errout_str()); + + check("void f() {\n" + " int x;\n" + " if (a) {\n" + " x = stuff(x);\n" + " morestuff(x);\n" + " }\n" + " if (b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'x' can be reduced.\n", errout_str()); + } + + void varScope18() { + check("void f() {\n" + " short x;\n" + "\n" + " switch (ab) {\n" + " case A:\n" + " break;\n" + " case B:\n" + " default:\n" + " break;\n" + " }\n" + "\n" + " if (c) {\n" + " x = foo();\n" + " do_something(x);\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'x' can be reduced.\n", errout_str()); + + check("void f() {\n" + " short x;\n" + "\n" + " switch (ab) {\n" + " case A:\n" + " x = 10;\n" + " break;\n" + " case B:\n" + " default:\n" + " break;\n" + " }\n" + "\n" + " if (c) {\n" + " x = foo();\n" + " do_something(x);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " short x;\n" + "\n" + " switch (ab) {\n" + " case A:\n" + " if(c)\n" + " do_something(x);\n" + " break;\n" + " case B:\n" + " default:\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'x' can be reduced.\n", errout_str()); + + check("void f() {\n" + " short x;\n" + "\n" + " switch (ab) {\n" + " case A:\n" + " if(c)\n" + " do_something(x);\n" + " break;\n" + " case B:\n" + " default:\n" + " if(d)\n" + " do_something(x);\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope20() { // Ticket #5103 - constant variable only used in inner scope + check("int f(int a) {\n" + " const int x = 234;\n" + " int b = a;\n" + " if (b > 32) b = x;\n" + " return b;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope21() { // Ticket #5382 - initializing two-dimensional array + check("int test() {\n" + " int test_value = 3;\n" + " int test_array[1][1] = { { test_value } };\n" + " return sizeof(test_array);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope22() { // Ticket #5684 - "The scope of the variable 'p' can be reduced" - But it can not. + check("void foo() {\n" + " int* p( 42 );\n" + " int i = 0;\n" + " while ( i != 100 ) {\n" + " *p = i;\n" + " ++p;\n" + " ++i;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + // try to avoid an obvious false negative after applying the fix for the example above: + check("void foo() {\n" + " int* p( 42 );\n" + " int i = 0;\n" + " int dummy = 0;\n" + " while ( i != 100 ) {\n" + " p = & dummy;\n" + " *p = i;\n" + " ++p;\n" + " ++i;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'p' can be reduced.\n", errout_str()); + } + + void varScope23() { // #6154: Don't suggest to reduce scope if inner scope is a lambda + check("int main() {\n" + " size_t myCounter = 0;\n" + " Test myTest([&](size_t aX){\n" + " std::cout << myCounter += aX << std::endl;\n" + " });\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope24() { + check("void f(Foo x) {\n" + " Foo &r = x;\n" + " if (cond) {\n" + " r.dostuff();\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'r' can be reduced.\n", errout_str()); + + check("void f(Foo x) {\n" + " Foo foo = x;\n" + " if (cond) {\n" + " foo.dostuff();\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope25() { + check("void f() {\n" + " time_t currtime;\n" + " if (a) {\n" + " currtime = time(&dummy);\n" + " if (currtime > t) {}\n" + " }\n" + "}", false); + ASSERT_EQUALS("[test.c:2]: (style) The scope of the variable 'currtime' can be reduced.\n", errout_str()); + } + + void varScope26() { + check("void f(const std::map &m) {\n" + " for (auto it : m) {\n" + " if (cond1) {\n" + " int& key = it.first;\n" + " if (cond2) { dostuff(key); }\n" + " }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope27() { + checkP("void f() {\n" + " int x = 0;\n" + "#ifdef X\n" + "#endif\n" + " if (id == ABC) { return x; }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkP("void f() {\n" + "#ifdef X\n" + "#endif\n" + " int x = 0;\n" + " if (id == ABC) { return x; }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) The scope of the variable 'x' can be reduced.\n", errout_str()); + } + + void varScope28() { + check("void f() {\n" // #10527 + " int i{};\n" + " if (double d = g(i); d == 1.0) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope29() { // #10888 + check("enum E { E0 };\n" + "struct S { int i; };\n" + "void f(int b) {\n" + " enum E e;\n" + " struct S s;\n" + " if (b) {\n" + " e = E0;\n" + " s.i = 0;\n" + " g(e, s);\n" + " }\n" + "}\n", false); + ASSERT_EQUALS("[test.c:4]: (style) The scope of the variable 'e' can be reduced.\n" + "[test.c:5]: (style) The scope of the variable 's' can be reduced.\n", + errout_str()); + + check("void f(bool b) {\n" + " std::string s;\n" + " if (b) {\n" + " s = \"abc\";\n" + " g(s);\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 's' can be reduced.\n", errout_str()); + + check("auto foo(std::vector& vec, bool flag) {\n" + " std::vector dummy;\n" + " std::vector::iterator iter;\n" + " if (flag)\n" + " iter = vec.begin();\n" + " else {\n" + " dummy.push_back(42);\n" + " iter = dummy.begin();\n" + " }\n" + " return *iter;\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'vec' can be declared as reference to const\n", errout_str()); + + check("auto& foo(std::vector& vec, bool flag) {\n" + " std::vector dummy;\n" + " std::vector::iterator iter;\n" + " if (flag)\n" + " iter = vec.begin();\n" + " else {\n" + " dummy.push_back(42);\n" + " iter = dummy.begin();\n" + " }\n" + " return *iter;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope30() { // #8541 + check("bool f(std::vector& v, int i) {\n" + " int n = 0;\n" + " bool b = false;\n" + " std::for_each(v.begin(), v.end(), [&](int j) {\n" + " if (j == i) {\n" + " ++n;\n" + " if (n > 5)\n" + " b = true;\n" + " }\n" + " });\n" + " return b;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope31() { // #11099 + check("bool g(std::vector&);\n" + "void h(std::vector);\n" + "void f0(std::vector v) {\n" + " std::vector w{ v };\n" + " bool b = g(v);\n" + " if (b)\n" + " h(w);\n" + " h(v);\n" + "}\n" + "void f1(std::vector v) {\n" + " std::vector w{ v.begin(), v.end() };\n" + " bool b = g(v);\n" + " if (b)\n" + " h(w);\n" + " h(v);\n" + "}\n" + "void f2(std::vector v) {\n" + " std::vector w{ 10, 0, std::allocator() };\n" // FN + " bool b = g(v);\n" + " if (b)\n" + " h(w);\n" + " h(v);\n" + "}\n" + "void f3(std::vector v) {\n" + " std::vector w{ 10, 0 };\n" // warn + " bool b = g(v);\n" + " if (b)\n" + " h(w);\n" + " h(v);\n" + "}\n" + "void f4(std::vector v) {\n" + " std::vector w{ 10 };\n" // warn + " bool b = g(v);\n" + " if (b)\n" + " h(w);\n" + " h(v);\n" + "}\n" + "void f5(std::vector v) {\n" + " std::vector w(v);\n" + " bool b = g(v);\n" + " if (b)\n" + " h(w);\n" + " h(v);\n" + "}\n" + "void f6(std::vector v) {\n" + " std::vector w(v.begin(), v.end());\n" + " bool b = g(v);\n" + " if (b)\n" + " h(w);\n" + " h(v);\n" + "}\n" + "void f7(std::vector v) {\n" + " std::vector w(10, 0, std::allocator);\n" // FN + " bool b = g(v);\n" + " if (b)\n" + " h(w);\n" + " h(v);\n" + "}\n" + "void f8(std::vector v) {\n" + " std::vector w(10, 0);\n" // warn + " bool b = g(v);\n" + " if (b)\n" + " h(w);\n" + " h(v);\n" + "}\n" + "void f9(std::vector v) {\n" + " std::vector w(10);\n" // warn + " bool b = g(v);\n" + " if (b)\n" + " h(w);\n" + " h(v);\n" + "}\n" + "void f10(std::vector v) {\n" + " std::vector w{};\n" // warn + " bool b = g(v);\n" + " if (b)\n" + " h(w);\n" + " h(v);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:25]: (style) The scope of the variable 'w' can be reduced.\n" + "[test.cpp:32]: (style) The scope of the variable 'w' can be reduced.\n" + "[test.cpp:60]: (style) The scope of the variable 'w' can be reduced.\n" + "[test.cpp:67]: (style) The scope of the variable 'w' can be reduced.\n" + "[test.cpp:74]: (style) The scope of the variable 'w' can be reduced.\n", + errout_str()); + } + + void varScope32() { // #11441 + check("template \n" + "std::vector g(F, const std::vector&);\n" + "void f(const std::vector&v) {\n" + " std::vector w;\n" + " for (auto x : v)\n" + " w = g([&]() { x; }, w);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:6]: (warning) Unused variable value 'x'\n", errout_str()); + } + + void varScope33() { // #11131 + check("struct S {\n" + " const std::string& getStr() const;\n" + " void mutate();\n" + " bool getB() const;\n" + "};\n" + "void g(S& s) {\n" + " std::string str = s.getStr();\n" + " s.mutate();\n" + " if (s.getB()) {\n" + " if (str == \"abc\") {}\n" + " }\n" + "}\n" + "void g(char* s, bool b) {\n" + " int i = strlen(s);\n" + " s[0] = '\\0';\n" + " if (b) {\n" + " if (i == 5) {}\n" + " }\n" + "}\n" + "void f(const S& s) {\n" + " std::string str = s.getStr();\n" + " std::string str2{ s.getStr() };\n" + " std::string str3(s.getStr());\n" + " if (s.getB()) {\n" + " if (str == \"abc\") {}\n" + " if (str2 == \"abc\") {}\n" + " if (str3 == \"abc\") {}\n" + " }\n" + "}\n" + "void f(const char* s, bool b) {\n" + " int i = strlen(s);\n" + " if (b) {\n" + " if (i == 5) {}\n" + " }\n" + "}\n" + "void f(int j, bool b) {\n" + " int k = j;\n" + " if (b) {\n" + " if (k == 5) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:21]: (style) The scope of the variable 'str' can be reduced.\n" + "[test.cpp:22]: (style) The scope of the variable 'str2' can be reduced.\n" + "[test.cpp:23]: (style) The scope of the variable 'str3' can be reduced.\n" + "[test.cpp:31]: (style) The scope of the variable 'i' can be reduced.\n" + "[test.cpp:37]: (style) The scope of the variable 'k' can be reduced.\n", + errout_str()); + } + + void varScope34() { // #11742 + check("void f() {\n" + " bool b = false;\n" + " int i = 1;\n" + " for (int k = 0; k < 20; ++k) {\n" + " b = !b;\n" + " if (b)\n" + " i++;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope35() { // #11845 + check("void f(int err, const char* src) {\n" + " const char* msg = \"Success\";\n" + " char buf[42];\n" + " if (err != 0)\n" + " msg = strcpy(buf, src);\n" + " printf(\"%d: %s\\n\", err, msg);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("char* g(char* dst, const char* src);\n" + "void f(int err, const char* src) {\n" + " const char* msg = \"Success\";\n" + " char buf[42];\n" + " if (err != 0)\n" + " msg = g(buf, src);\n" + " printf(\"%d: %s\\n\", err, msg);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("char* g(char* dst, const char* src);\n" + "void f(int err, const char* src) {\n" + " const char* msg = \"Success\";\n" + " char buf[42];\n" + " if (err != 0)\n" + " g(buf, src);\n" + " printf(\"%d: %s\\n\", err, msg);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) The scope of the variable 'buf' can be reduced.\n", errout_str()); + } + + void varScope36() { + // #12158 + check("void f( uint32_t value ) {\n" + " uint32_t i = 0U;\n" + " if ( value > 100U ) { }\n" + " else if( value > 50U ) { }\n" + " else{\n" + " for( i = 0U; i < 5U; i++ ) {}\n" + " }\n" + "}\n", true, false); + ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'i' can be reduced.\n", errout_str()); + } + + void varScope37() { + // #12158 + check("void f( uint32_t value ) {\n" + " uint32_t i = 0U;\n" + " if ( value > 100U ) { }\n" + " else {\n" + " if( value > 50U ) { }\n" + " else{\n" + " for( i = 0U; i < 5U; i++ ) {}\n" + " }\n" + " }\n" + "}\n", true, false); + ASSERT_EQUALS("[test.cpp:2]: (style) The scope of the variable 'i' can be reduced.\n", errout_str()); + } + + void varScope38() { + checkP("bool dostuff();\n" // #12519 + "#define DOSTUFF(c) if (c < 5) { if (c) b = dostuff(); }\n" + "#define DOSTUFFEX(c) { bool b = false; DOSTUFF(c); }\n" + "void f(int a) {\n" + " DOSTUFFEX(a);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope39() { + check("struct S {\n" // #12405 + " void f(const std::string&) const;\n" + " const int* g(std::string&) const;\n" + "};\n" + "void h(int);\n" + "void S::f(const std::string& s) const {\n" + " std::string n = s;\n" + " const int* a = g(n);\n" + " if (n == \"abc\") {\n" + " h(a[0]);\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void varScope40() { + checkP("#define NUM (-999.9)\n" // #8862 + "double f(int i) {\n" + " double a = NUM;\n" + " double b = -NUM;\n" + " double c = -1.0 * NUM;\n" + " if (i == 1) {\n" + " return a;\n" + " }\n" + " if (i == 2) {\n" + " return b;\n" + " }\n" + " if (i == 3) {\n" + " return c;\n" + " }\n" + " return 0.0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) The scope of the variable 'a' can be reduced.\n" + "[test.cpp:4]: (style) The scope of the variable 'b' can be reduced.\n" + "[test.cpp:5]: (style) The scope of the variable 'c' can be reduced.\n", + errout_str()); + + check("struct S { int a; };\n" // #12618 + "int f(const S* s, int i) {\n" + " int x = s->a;\n" + " const int b[] = { 1, 2, 3 };\n" + " int y = b[1];\n" + " if (i)\n" + " return x + y;\n" + " return 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) The scope of the variable 'x' can be reduced.\n" + "[test.cpp:5]: (style) The scope of the variable 'y' can be reduced.\n", + errout_str()); + } + +#define checkOldStylePointerCast(code) checkOldStylePointerCast_(code, __FILE__, __LINE__) + void checkOldStylePointerCast_(const char code[], const char* file, int line) { + // #5560 - set c++03 + const Settings settings = settingsBuilder().severity(Severity::style).cpp(Standards::CPP03).build(); + + // Tokenize.. + SimpleTokenizer tokenizerCpp(settings, *this); + ASSERT_LOC(tokenizerCpp.tokenize(code), file, line); + + CheckOther checkOtherCpp(&tokenizerCpp, &settings, this); + checkOtherCpp.warningOldStylePointerCast(); + } + + void oldStylePointerCast() { + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (Base *) derived;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout_str()); + + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (const Base *) derived;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout_str()); + + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (const Base * const) derived;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout_str()); + + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (volatile Base *) derived;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout_str()); + + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (volatile Base * const) derived;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout_str()); + + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (const volatile Base *) derived;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout_str()); + + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (const volatile Base * const) derived;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout_str()); + + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (const Base *) ( new Derived() );\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout_str()); + + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (const Base *) new Derived();\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout_str()); + + checkOldStylePointerCast("class Base;\n" + "void foo()\n" + "{\n" + " Base * b = (const Base *) new short[10];\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout_str()); + + checkOldStylePointerCast("class B;\n" + "class A\n" + "{\n" + " virtual void abc(B *) const = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkOldStylePointerCast("class B;\n" + "class A\n" + "{\n" + " virtual void abc(const B *) const = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #3630 + checkOldStylePointerCast("class SomeType;\n" + "class X : public Base {\n" + " X() : Base((SomeType*)7) {}\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style) C-style pointer casting\n", errout_str()); + + checkOldStylePointerCast("class SomeType;\n" + "class X : public Base {\n" + " X() : Base((SomeType*)var) {}\n" + "};"); + ASSERT_EQUALS("[test.cpp:3]: (style) C-style pointer casting\n", errout_str()); + + checkOldStylePointerCast("class SomeType;\n" + "class X : public Base {\n" + " X() : Base((SomeType*)0) {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + // #5560 + checkOldStylePointerCast("class C;\n" + "\n" + "class B\n" + "{ virtual G* createGui(S*, C*) const = 0; };\n" + "\n" + "class MS : public M\n" + "{ virtual void addController(C*) override {} };"); + ASSERT_EQUALS("", errout_str()); + + // #6164 + checkOldStylePointerCast("class Base {};\n" + "class Derived: public Base {};\n" + "void testCC() {\n" + " std::vector v;\n" + " v.push_back((Base*)new Derived);\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) C-style pointer casting\n", errout_str()); + + // #7709 + checkOldStylePointerCast("typedef struct S S;\n" + "typedef struct S SS;\n" + "typedef class C C;\n" + "typedef long LONG;\n" + "typedef long* LONGP;\n" + "struct T {};\n" + "typedef struct T TT;\n" + "typedef struct T2 {} TT2;\n" + "void f(int* i) {\n" + " S* s = (S*)i;\n" + " SS* ss = (SS*)i;\n" + " struct S2* s2 = (struct S2*)i;\n" + " C* c = (C*)i;\n" + " class C2* c2 = (class C2*)i;\n" + " long* l = (long*)i;\n" + " LONG* l2 = (LONG*)i;\n" + " LONGP l3 = (LONGP)i;\n" + " TT* tt = (TT*)i;\n" + " TT2* tt2 = (TT2*)i;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:10]: (style) C-style pointer casting\n" + "[test.cpp:11]: (style) C-style pointer casting\n" + "[test.cpp:12]: (style) C-style pointer casting\n" + "[test.cpp:13]: (style) C-style pointer casting\n" + "[test.cpp:14]: (style) C-style pointer casting\n" + "[test.cpp:15]: (style) C-style pointer casting\n" + "[test.cpp:16]: (style) C-style pointer casting\n" + "[test.cpp:17]: (style) C-style pointer casting\n" + "[test.cpp:18]: (style) C-style pointer casting\n" + "[test.cpp:19]: (style) C-style pointer casting\n", + errout_str()); + + // #8649 + checkOldStylePointerCast("struct S {};\n" + "void g(S*& s);\n" + "void f(int i) {\n" + " g((S*&)i);\n" + " S*& r = (S*&)i;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n" + "[test.cpp:5]: (style) C-style pointer casting\n", + errout_str()); + + // #10823 + checkOldStylePointerCast("void f(void* p) {\n" + " auto h = reinterpret_cast(p);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #5210 + checkOldStylePointerCast("void f(void* v1, void* v2) {\n" + " T** p1 = (T**)v1;\n" + " T*** p2 = (T***)v2;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) C-style pointer casting\n" + "[test.cpp:3]: (style) C-style pointer casting\n", + errout_str()); + + // #12446 + checkOldStylePointerCast("namespace N { struct S {}; }\n" + "union U {\n" + " int i;\n" + " char c[4];\n" + "};\n" + "void f(void* p) {\n" + " auto ps = (N::S*)p;\n" + " auto pu = (union U*)p;\n" + " auto pv = (std::vector*)(p);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7]: (style) C-style pointer casting\n" + "[test.cpp:8]: (style) C-style pointer casting\n" + "[test.cpp:9]: (style) C-style pointer casting\n", + errout_str()); + + // #12447 + checkOldStylePointerCast("void f(const int& i) {\n" + " int& r = (int&)i;\n" + " r = 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) C-style reference casting\n", errout_str()); + } + +#define checkInvalidPointerCast(...) checkInvalidPointerCast_(__FILE__, __LINE__, __VA_ARGS__) + void checkInvalidPointerCast_(const char* file, int line, const char code[], bool portability = true, bool inconclusive = false) { + /*const*/ Settings settings = settingsBuilder().severity(Severity::warning).severity(Severity::portability, portability).certainty(Certainty::inconclusive, inconclusive).build(); + settings.platform.defaultSign = 's'; + + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + CheckOther checkOtherCpp(&tokenizer, &settings, this); + checkOtherCpp.invalidPointerCast(); + } + + + void invalidPointerCast() { + checkInvalidPointerCast("void test() {\n" + " float *f = new float[10];\n" + " delete [] (double*)f;\n" + " delete [] (long double const*)(new float[10]);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (portability) Casting between float * and double * which have an incompatible binary data representation.\n" + "[test.cpp:4]: (portability) Casting between float * and const long double * which have an incompatible binary data representation.\n", errout_str()); + + checkInvalidPointerCast("void test(const float* f) {\n" + " double *d = (double*)f;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (portability) Casting between const float * and double * which have an incompatible binary data representation.\n", errout_str()); + + checkInvalidPointerCast("void test(double* d1) {\n" + " long double *ld = (long double*)d1;\n" + " double *d2 = (double*)ld;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (portability) Casting between double * and long double * which have an incompatible binary data representation.\n" + "[test.cpp:3]: (portability) Casting between long double * and double * which have an incompatible binary data representation.\n", errout_str()); + + checkInvalidPointerCast("char* test(int* i) {\n" + " long double *d = (long double*)(i);\n" + " double *d = (double*)(i);\n" + " float *f = reinterpret_cast(i);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (portability) Casting between signed int * and long double * which have an incompatible binary data representation.\n" + "[test.cpp:3]: (portability) Casting between signed int * and double * which have an incompatible binary data representation.\n" + "[test.cpp:4]: (portability) Casting between signed int * and float * which have an incompatible binary data representation.\n", errout_str()); + + checkInvalidPointerCast("float* test(unsigned int* i) {\n" + " return (float*)i;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (portability) Casting between unsigned int * and float * which have an incompatible binary data representation.\n", errout_str()); + + checkInvalidPointerCast("float* test(unsigned int* i) {\n" + " return (float*)i[0];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkInvalidPointerCast("float* test(double& d) {\n" + " return (float*)&d;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (portability) Casting between double * and float * which have an incompatible binary data representation.\n", errout_str()); + + checkInvalidPointerCast("void test(float* data) {\n" + " f.write((char*)data,sizeof(float));\n" + "}", true, false); + ASSERT_EQUALS("", errout_str()); + + checkInvalidPointerCast("void test(float* data) {\n" + " f.write((char*)data,sizeof(float));\n" + "}", true, true); // #3639 + ASSERT_EQUALS("[test.cpp:2]: (portability, inconclusive) Casting from float * to signed char * is not portable due to different binary data representations on different platforms.\n", errout_str()); + + + checkInvalidPointerCast("long long* test(float* f) {\n" + " return (long long*)f;\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + + checkInvalidPointerCast("long long* test(float* f, char* c) {\n" + " foo((long long*)f);\n" + " return reinterpret_cast(c);\n" + "}", true); + ASSERT_EQUALS("[test.cpp:2]: (portability) Casting from float * to signed long long * is not portable due to different binary data representations on different platforms.\n", errout_str()); + + checkInvalidPointerCast("Q_DECLARE_METATYPE(int*)"); // #4135 - don't crash + } + + + void passedByValue() { + check("void f(const std::string str) {}"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout_str()); + + check("void f(std::unique_ptr ptr) {}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const std::shared_ptr ptr) {}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const std::function ptr) {}"); + ASSERT_EQUALS("", errout_str()); + + { + check("void f(const std::pair x) {}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const std::pair x) {}"); + TODO_ASSERT_EQUALS("error", "", errout_str()); + } + + check("void f(const std::string::size_type x) {}"); + ASSERT_EQUALS("", errout_str()); + + check("class Foo;\nvoid f(const Foo foo) {}"); // Unknown class + ASSERT_EQUALS("[test.cpp:2]: (performance, inconclusive) Function parameter 'foo' should be passed by const reference.\n", errout_str()); + + check("class Foo { std::vector v; };\nvoid f(const Foo foo) {}"); // Large class (STL member) + ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'foo' should be passed by const reference.\n", errout_str()); + + check("class Foo { int i; };\nvoid f(const Foo foo) {}"); // Small class + ASSERT_EQUALS("", errout_str()); + + check("class Foo { int i[6]; };\nvoid f(const Foo foo) {}"); // Large class (array) + ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'foo' should be passed by const reference.\n", errout_str()); + + check("class Foo { std::string* s; };\nvoid f(const Foo foo) {}"); // Small class (pointer) + ASSERT_EQUALS("", errout_str()); + + check("class Foo { static std::string s; };\nvoid f(const Foo foo) {}"); // Small class (static member) + ASSERT_EQUALS("", errout_str()); + + check("class X { std::string s; }; class Foo : X { };\nvoid f(const Foo foo) {}"); // Large class (inherited) + ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'foo' should be passed by const reference.\n", errout_str()); + + check("class X { std::string s; }; class Foo { X x; };\nvoid f(const Foo foo) {}"); // Large class (inherited) + ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'foo' should be passed by const reference.\n", errout_str()); + + check("void f(const std::string &str) {}"); + ASSERT_EQUALS("", errout_str()); + + // The idiomatic way of passing a std::string_view is by value + check("void f(const std::string_view str) {}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::string_view str) {}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const std::string_view &str) {}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const std::vector v) {}"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout_str()); + + check("void f(const std::vector v) {}"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout_str()); + + check("void f(const std::vector::size_type s) {}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const std::vector &v) {}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const std::map &v) {}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const std::map v) {}"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout_str()); + + check("void f(const std::map v) {}"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout_str()); + + check("void f(const std::map v) {}"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout_str()); + + check("void f(const std::map v) {}"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout_str()); + + check("void f(const std::streamoff pos) {}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::initializer_list i) {}"); + ASSERT_EQUALS("", errout_str()); + + // #5824 + check("void log(const std::string& file, int line, const std::string& function, const std::string str, ...) {}"); + ASSERT_EQUALS("", errout_str()); + + // #5534 + check("struct float3 { };\n" + "typedef float3 vec;\n" + "class Plane {\n" + " vec Refract(vec &vec) const;\n" + " bool IntersectLinePlane(const vec &planeNormal);\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class X {\n" + " virtual void func(const std::string str) {}\n" + "};"); + ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'str' should be passed by const reference.\n", errout_str()); + + check("enum X;\n" + "void foo(X x1){}\n"); + ASSERT_EQUALS("", errout_str()); + + check("enum X { a, b, c };\n" + "void foo(X x2){}\n"); + ASSERT_EQUALS("", errout_str()); + + check("enum X { a, b, c };\n" + "enum X;" + "void foo(X x3){}\n"); + ASSERT_EQUALS("", errout_str()); + + check("enum X;\n" + "enum X { a, b, c };" + "void foo(X x4){}\n"); + ASSERT_EQUALS("", errout_str()); + + check("union U {\n" + " char* pc;\n" + " short* ps;\n" + " int* pi;\n" + "};\n" + "void f(U u) {}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { char A[8][8]; };\n" + "void f(S s) {}\n"); + ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 's' should be passed by const reference.\n", errout_str()); + + check("union U {\n" // don't crash + " int a;\n" + " decltype(nullptr) b;\n" + "};\n" + "int* f(U u) { return u.b; }\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct B { virtual int f(std::string s) = 0; };\n" // #11432 + "struct D1 : B {\n" + " int f(std::string s) override { s += 'a'; return s.size(); }\n" + "}\n" + "struct D2 : B {\n" + " int f(std::string s) override { return s.size(); }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int x(int);\n" + "void f(std::vector v, int& j) {\n" + " for (int i : v)\n" + " j = i;\n" + "}\n" + "void fn(std::vector v) {\n" + " for (int& i : v)\n" + " i = x(i);\n" + "}\n" + "void g(std::vector v, int& j) {\n" + " for (int i = 0; i < v.size(); ++i)\n" + " j = v[i];\n" + "}\n" + "void gn(std::vector v) {\n" + " for (int i = 0; i < v.size(); ++i)\n" + " v[i] = x(i);\n" + "}\n" + "void h(std::vector> v, int& j) {\n" + " for (int i = 0; i < v.size(); ++i)\n" + " j = v[i][0];\n" + "}\n" + "void hn(std::vector> v) {\n" + " for (int i = 0; i < v.size(); ++i)\n" + " v[i][0] = x(i);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'v' should be passed by const reference.\n" + "[test.cpp:10]: (performance) Function parameter 'v' should be passed by const reference.\n" + "[test.cpp:18]: (performance) Function parameter 'v' should be passed by const reference.\n", + errout_str()); + + check("struct S {\n" // #11995 + " explicit S(std::string s) noexcept;\n" + " std::string m;\n" + "};\n" + "S::S(std::string s) noexcept : m(std::move(s)) {}\n" + "struct T {\n" + " explicit S(std::string s) noexcept(true);\n" + " std::string m;\n" + "};\n" + "T::T(std::string s) noexcept(true) : m(std::move(s)) {}\n"); + ASSERT_EQUALS("", errout_str()); + + check("namespace N {\n" // #12086 + " void g(int);\n" + "}\n" + "void f(std::vector v) {\n" + " N::g(v[0]);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (performance) Function parameter 'v' should be passed by const reference.\n", errout_str()); + + check("void f(const std::string& s, std::string t) {\n" // #12083 + " const std::string& v = !s.empty() ? s : t;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 't' should be passed by const reference.\n", errout_str()); + + /*const*/ Settings settings0 = settingsBuilder(_settings).platform(Platform::Type::Unix64).build(); + check("struct S {\n" // #12138 + " union {\n" + " int a = 0;\n" + " int x;\n" + " };\n" + " union {\n" + " int b = 0;\n" + " int y;\n" + " };\n" + " union {\n" + " int c = 0;\n" + " int z;\n" + " };\n" + "};\n" + "void f(S s) {\n" + " if (s.x > s.y) {}\n" + "}\n", /*cpp*/ true, /*inconclusive*/ true, /*runSimpleChecks*/ true, /*verbose*/ false, &settings0); + ASSERT_EQUALS("", errout_str()); + + check("struct S { std::list l; };\n" // #12147 + "class C { public: std::list l; };\n" + "bool f(S s) {\n" + " return s.l.empty();\n" + "}\n" + "bool f(C c) {\n" + " return c.l.empty();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (performance) Function parameter 's' should be passed by const reference.\n" + "[test.cpp:6]: (performance) Function parameter 'c' should be passed by const reference.\n", + errout_str()); + + check("struct S { std::list a[1][1]; };\n" + "bool f(S s) {\n" + " return s.a[0][0].empty();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 's' should be passed by const reference.\n", + errout_str()); + + check("struct S {\n" + " enum class E : std::uint8_t { E0 };\n" + " static void f(S::E e) {\n" + " if (e == S::E::E0) {}\n" + " }\n" + " char a[20];\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + /*const*/ Settings settings1 = settingsBuilder().platform(Platform::Type::Win64).build(); + check("using ui64 = unsigned __int64;\n" + "ui64 Test(ui64 one, ui64 two) { return one + two; }\n", + /*cpp*/ true, /*inconclusive*/ true, /*runSimpleChecks*/ true, /*verbose*/ false, &settings1); + ASSERT_EQUALS("", errout_str()); + } + + void passedByValue_nonConst() { + check("void f(std::string str) {}"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout_str()); + + check("void f(std::string str) {\n" + " return str + x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout_str()); + + check("void f(std::string str) {\n" + " std::cout << str;\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout_str()); + + check("void f(std::string str) {\n" + " std::cin >> str;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::string str) {\n" + " std::string s2 = str;\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout_str()); + + check("void f(std::string str) {\n" + " std::string& s2 = str;\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n" + "[test.cpp:2]: (style) Variable 's2' can be declared as reference to const\n", + errout_str()); + + check("void f(std::string str) {\n" + " const std::string& s2 = str;\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'str' should be passed by const reference.\n", errout_str()); + + check("void f(std::string str) {\n" + " str = \"\";\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::string str) {\n" + " foo(str);\n" // It could be that foo takes str as non-const-reference + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(const std::string& str);\n" + "void f(std::string str) {\n" + " foo(str);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'str' should be passed by const reference.\n", errout_str()); + + check("void foo(std::string str);\n" + "void f(std::string str) {\n" + " foo(str);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'str' should be passed by const reference.\n", errout_str()); + + check("void foo(std::string& str);\n" + "void f(std::string str) {\n" + " foo(str);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(std::string* str);\n" + "void f(std::string str) {\n" + " foo(&str);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int& i1, const std::string& str, int& i2);\n" + "void f(std::string str) {\n" + " foo((a+b)*c, str, x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'str' should be passed by const reference.\n", errout_str()); + + check("std::string f(std::string str) {\n" + " str += x;\n" + " return str;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class X {\n" + " std::string s;\n" + " void func() const;\n" + "};\n" + "Y f(X x) {\n" + " x.func();\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (performance) Function parameter 'x' should be passed by const reference.\n", errout_str()); + + check("class X {\n" + " void func();\n" + "};\n" + "Y f(X x) {\n" + " x.func();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class X {\n" + " void func(std::string str) {}\n" + "};"); + ASSERT_EQUALS("[test.cpp:2]: (performance) Function parameter 'str' should be passed by const reference.\n", errout_str()); + + check("class X {\n" + " virtual void func(std::string str) {}\n" // Do not warn about virtual functions, if 'str' is not declared as const + "};"); + ASSERT_EQUALS("", errout_str()); + + check("class X {\n" + " char a[1024];\n" + "};\n" + "class Y : X {\n" + " char b;\n" + "};\n" + "void f(Y y) {\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (performance) Function parameter 'y' should be passed by const reference.\n", errout_str()); + + check("class X {\n" + " void* a;\n" + " void* b;\n" + "};\n" + "class Y {\n" + " void* a;\n" + " void* b;\n" + " char c;\n" + "};\n" + "void f(X x, Y y) {\n" + "}"); + ASSERT_EQUALS("[test.cpp:10]: (performance) Function parameter 'y' should be passed by const reference.\n", errout_str()); + + { + // 8-byte data should be passed by const reference on 32-bit platform but not on 64-bit platform + const char code[] = "class X {\n" + " uint64_t a;\n" + " uint64_t b;\n" + "};\n" + "void f(X x) {}"; + + /*const*/ Settings s32 = settingsBuilder(_settings).platform(Platform::Type::Unix32).build(); + check(code, &s32); + ASSERT_EQUALS("[test.cpp:5]: (performance) Function parameter 'x' should be passed by const reference.\n", errout_str()); + + /*const*/ Settings s64 = settingsBuilder(_settings).platform(Platform::Type::Unix64).build(); + check(code, &s64); + ASSERT_EQUALS("", errout_str()); + } + + check("Writer* getWriter();\n" + "\n" + "void foo(Buffer& buffer) {\n" + " getWriter()->operator<<(buffer);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void passedByValue_externC() { + check("struct X { int a[5]; }; void f(X v) { }"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout_str()); + + check("extern \"C\" { struct X { int a[5]; }; void f(X v) { } }"); + ASSERT_EQUALS("", errout_str()); + + check("struct X { int a[5]; }; extern \"C\" void f(X v) { }"); + ASSERT_EQUALS("", errout_str()); + + check("struct X { int a[5]; }; void f(const X v);"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'v' should be passed by const reference.\n", errout_str()); + + check("extern \"C\" { struct X { int a[5]; }; void f(const X v); }"); + ASSERT_EQUALS("", errout_str()); + + check("struct X { int a[5]; }; extern \"C\" void f(const X v) { }"); + ASSERT_EQUALS("", errout_str()); + } + + void constVariable() { + check("int f(std::vector x) {\n" + " int& i = x[0];\n" + " return i;\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'x' should be passed by const reference.\n" + "[test.cpp:2]: (style) Variable 'i' can be declared as reference to const\n", + errout_str()); + + check("int f(std::vector& x) {\n" + " return x[0];\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'x' can be declared as reference to const\n", errout_str()); + + check("int f(std::vector x) {\n" + " const int& i = x[0];\n" + " return i;\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'x' should be passed by const reference.\n", errout_str()); + + check("int f(std::vector x) {\n" + " static int& i = x[0];\n" + " return i;\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (performance) Function parameter 'x' should be passed by const reference.\n", errout_str()); + + check("int f(std::vector x) {\n" + " int& i = x[0];\n" + " i++;\n" + " return i;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int& f(std::vector& x) {\n" + " x.push_back(1);\n" + " int& i = x[0];\n" + " return i;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(const std::vector& x) {\n" + " return x[0];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int& f(std::vector& x) {\n" + " return x[0];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("const int& f(std::vector& x) {\n" + " return x[0];\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'x' can be declared as reference to const\n", errout_str()); + + check("int f(std::vector& x) {\n" + " x[0]++;\n" + " return x[0];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int a; };\n" + "A f(std::vector& x) {\n" + " x[0].a = 1;\n" + " return x[0];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int a(); };\n" + "A f(std::vector& x) {\n" + " x[0].a();\n" + " return x[0];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int g(int& x);\n" + "int f(std::vector& x) {\n" + " g(x[0]);\n" + " return x[0];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("template\n" + "T f(T& x) {\n" + " return x[0];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("template\n" + "T f(T&& x) {\n" + " return x[0];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("template\n" + "T f(T& x) {\n" + " return x[0];\n" + "}\n" + "void h() { std::vector v; h(v); }"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int& x) {\n" + " return std::move(x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::ostream& os) {\n" + " os << \"Hello\";\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void g(int*);\n" + "void f(int& x) {\n" + " g(&x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { A(int*); };\n" + "A f(int& x) {\n" + " return A(&x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { A(int*); };\n" + "A f(int& x) {\n" + " return A{&x};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int& x, int& y) {\n" + " y++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'x' can be declared as reference to const\n", errout_str()); + + check("struct A {\n" + " explicit A(int& y) : x(&y) {}\n" + " int * x = nullptr;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " std::vector v;\n" + " void swap(A& a) {\n" + " v.swap(a.v);\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " template\n" + " void f();\n" + " template\n" + " void f() const;\n" + "};\n" + "void g(A& a) {\n" + " a.f();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::vector& v) {\n" + " for(auto&& x:v)\n" + " x = 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::vector& v) {\n" + " for(auto x:v)\n" + " x = 1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared as reference to const\n", errout_str()); + + check("void f(std::vector& v) {\n" + " for(auto& x:v) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'x' can be declared as reference to const\n", + errout_str()); + + check("void f(std::vector& v) {\n" // #10980 + " for (int& i : v)\n" + " if (i == 0) {}\n" + " for (const int& i : v)\n" + " if (i == 0) {}\n" + " for (auto& i : v)\n" + " if (i == 0) {}\n" + " for (const auto& i : v)\n" + " if (i == 0) {}\n" + " v.clear();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'i' can be declared as reference to const\n" + "[test.cpp:6]: (style) Variable 'i' can be declared as reference to const\n", + errout_str()); + + check("void f(std::vector& v) {\n" + " for(const auto& x:v) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared as reference to const\n", errout_str()); + + check("void f(int& i) {\n" + " int& j = i;\n" + " j++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::vector& v) {\n" + " int& i = v[0];\n" + " i++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::map >& m, unsigned int i) {\n" + " std::map& members = m[i];\n" + " members.clear();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " int& x;\n" + " A(int& y) : x(y)\n" + " {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " A(int& x);\n" + "};\n" + "struct B : A {\n" + " B(int& x) : A(x)\n" + " {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool b, int& x, int& y) {\n" + " auto& z = x;\n" + " auto& w = b ? y : z;\n" + " w = 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " int i;\n" + "};\n" + "int& f(S& s) {\n" + " return s.i;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int* f(std::list& x, unsigned int y) {\n" + " for (int& m : x) {\n" + " if (m == y)\n" + " return &m;\n" + " }\n" + " return nullptr;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int& f(std::list& x, int& y) {\n" + " for (int& m : x) {\n" + " if (m == y)\n" + " return m;\n" + " }\n" + " return y;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool from_string(int& t, const std::string& s) {\n" + " std::istringstream iss(s);\n" + " return !(iss >> t).fail();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #9710 + check("class a {\n" + " void operator()(int& i) const {\n" + " i++;\n" + " }\n" + "};\n" + "void f(int& i) {\n" + " a()(i);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("class a {\n" + " void operator()(int& i) const {\n" + " i++;\n" + " }\n" + "};\n" + "void f(int& i) {\n" + " a x;\n" + " x(i);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("class a {\n" + " void operator()(const int& i) const;\n" + "};\n" + "void f(int& i) {\n" + " a x;\n" + " x(i);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'i' can be declared as reference to const\n", errout_str()); + + //cast or assignment to a non-const reference should prevent the warning + check("struct T { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " const T& z = x;\n" // Make sure we find all assignments + " T& y = x;\n" + " y.mutate();\n" // to avoid warnings that y can be const + "}"); + ASSERT_EQUALS("", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " const U& y = x\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " U& y = x;\n" + " y.mutate();\n" // to avoid warnings that y can be const + "}"); + ASSERT_EQUALS("", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " my::type& y = x;\n" // we don't know if y is const or not + " y.mutate();\n" // to avoid warnings that y can be const + "}"); + ASSERT_EQUALS("", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " const U& y = static_cast(x);\n" + " y.mutate();\n" // to avoid warnings that y can be const + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " U& y = static_cast(x);\n" + " y.mutate();\n" // to avoid warnings that y can be const + "}"); + ASSERT_EQUALS("", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " const U& y = dynamic_cast(x)\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " const U& y = dynamic_cast(x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " const U& y = dynamic_cast(x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " U& y = dynamic_cast(x);\n" + " y.mutate();\n" // to avoid warnings that y can be const + "}"); + ASSERT_EQUALS("", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " const U& y = dynamic_cast(x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " U& y = dynamic_cast(x);\n" + " y.mutate();\n" // to avoid warnings that y can be const + "}"); + ASSERT_EQUALS("", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " U* y = dynamic_cast(&x);\n" + " y->mutate();\n" // to avoid warnings that y can be const + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " const U * y = dynamic_cast(&x);\n" + " y->mutate();\n" // to avoid warnings that y can be const + "}"); + TODO_ASSERT_EQUALS("can be const", errout_str(), ""); //Currently taking the address is treated as a non-const operation when it should depend on what we do with it + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " U const * y = dynamic_cast(&x);\n" + " y->mutate();\n" // to avoid warnings that y can be const + "}"); + TODO_ASSERT_EQUALS("can be const", errout_str(), ""); //Currently taking the address is treated as a non-const operation when it should depend on what we do with it + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " const U const * const * const * const y = dynamic_cast(&x);\n" + " y->mutate();\n" // to avoid warnings that y can be const + "}"); + ASSERT_EQUALS("", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " const U const * const * const * const y = dynamic_cast(&x);\n" + " y->mutate();\n" // to avoid warnings that y can be const + "}"); + TODO_ASSERT_EQUALS("can be const", errout_str(), ""); //Currently taking the address is treated as a non-const operation when it should depend on what we do with it + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " const U const * const * * const y = dynamic_cast(&x);\n" + " y->mutate();\n" // to avoid warnings that y can be const + "}"); + ASSERT_EQUALS("", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " my::fancy const * const * const y = dynamic_cast const * const * const>(&x);\n" + " y->mutate();\n" // to avoid warnings that y can be const + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " const U& y = (const U&)(x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) C-style reference casting\n" + "[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", + errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " U& y = (U&)(x);\n" + " y.mutate();\n" // to avoid warnings that y can be const + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) C-style reference casting\n", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " const U& y = (typename const U&)(x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) C-style reference casting\n" + "[test.cpp:2]: (style) Parameter 'x' can be declared as reference to const\n", + errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " U& y = (typename U&)(x);\n" + " y.mutate();\n" // to avoid warnings that y can be const + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) C-style reference casting\n", errout_str()); + check("struct T : public U { void dostuff() const {}};\n" + "void a(T& x) {\n" + " x.dostuff();\n" + " U* y = (U*)(&x);\n" + " y->mutate();\n" // to avoid warnings that y can be const + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) C-style pointer casting\n", errout_str()); + + check("struct C { void f() const; };\n" // #9875 - crash + "\n" + "void foo(C& x) {\n" + " x.f();\n" + " foo( static_cast(0) );\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Parameter 'x' can be declared as reference to const\n", errout_str()); + + check("class a {\n" + " void foo(const int& i) const;\n" + " void operator()(int& i) const;\n" + "};\n" + "void f(int& i) {\n" + " a()(i);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("class a {\n" + " void operator()(const int& i) const;\n" + "};\n" + "void f(int& i) {\n" + " a()(i);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'i' can be declared as reference to const\n", errout_str()); + + // #9767 + check("void fct1(MyClass& object) {\n" + " fct2([&](void){}, object);\n" + "}\n" + "bool fct2(std::function lambdaExpression, MyClass& object) {\n" + " object.modify();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #9778 + check("struct A {};\n" + "struct B : A {};\n" + "B& f(A& x) {\n" + " return static_cast(x);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10002 + check("using A = int*;\n" + "void f(const A& x) {\n" + " ++(*x);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10086 + check("struct V {\n" + " V& get(typename std::vector::size_type i) {\n" + " std::vector& arr = v;\n" + " return arr[i];\n" + " }\n" + " std::vector v;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("void e();\n" + "void g(void);\n" + "void h(void);\n" + "void ah(void);\n" + "void ai(void);\n" + "void j(void);\n" + "void e(void);\n" + "void k(void);\n" + "void l(void);\n" + "void m(void);\n" + "void n(void);\n" + "void o(void);\n" + "void q(void);\n" + "void r(void);\n" + "void t(void);\n" + "void u(void);\n" + "void v(void);\n" + "void w(void);\n" + "void z(void);\n" + "void aj(void);\n" + "void am(void);\n" + "void g(void);\n" + "void h(void);\n" + "void ah(void);\n" + "void an(void);\n" + "void e(void);\n" + "void k(void);\n" + "void ao(wchar_t *d);\n" + "void ah(void);\n" + "void e(void);\n" + "void an(void);\n" + "void e(void);\n" + "void k(void);\n" + "void g(void);\n" + "void ah(void);\n" + "void an(void);\n" + "void e(void);\n" + "void e(void);\n" + "void e(void);\n" + "void k(void);\n" + "void g(void);\n" + "void ah(void);\n" + "void an(void);\n" + "void e(void);\n" + "void e(void);\n" + "void k(void);\n" + "void g(void);\n" + "void h(void);\n" + "void ah(void);\n" + "void an(void);\n" + "void e(void);\n" + "void k(void);\n" + "void e(void);\n" + "void g(void);\n" + "void ah(void);\n" + "void k(void);\n" + "void an(void);\n" + "void e(void);\n" + "void e(void);\n" + "void e(void);\n" + "void k(void);\n" + "void g(void);\n" + "void h(void);\n" + "void ah(void);\n" + "void k(void);\n" + "void an(void);\n" + "void k(void);\n" + "void e(void);\n" + "void g(void);\n" + "void ah(void);\n" + "void e(void);\n" + "void k(void);\n" + "void g(void);\n" + "void h(void);\n" + "void ah(void);\n" + "void an(void);\n" + "void an(void);\n" + "void k(void);\n" + "void e(void);\n" + "void e(void);\n" + "void e(void);\n" + "void g(void);\n" + "void k(void);\n" + "void g(void);\n" + "void h(void);\n" + "void ah(void);\n" + "void an(void);\n" + "void k(void);\n" + "void k(void);\n" + "void e(void);\n" + "void g(void);\n" + "void g(void);\n" + "void ah(void);\n" + "void an(void);\n" + "void e(void);\n" + "void k(void);\n" + "void e(void);\n" + "void ap(wchar_t *c, int d);\n" + "void ah(void);\n" + "void an(void);\n" + "void g(void);\n" + "void h(void);\n" + "void ah(void);\n" + "void aq(char *b, size_t d, char *c, int a);\n" + "void ar(char *b, size_t d, char *c, va_list a);\n" + "void k(void);\n" + "void g(void);\n" + "void g(void);\n" + "void h(void);\n" + "void ah(void);\n" + "void an(void);\n" + "void k(void);\n" + "void k(void);\n" + "void e(void);\n" + "void g(void);\n" + "void g(void);\n" + "void as(std::string s);\n" + "void at(std::ifstream &f);\n" + "void au(std::istream &f);\n" + "void av(std::string &aa, std::wstring &ab);\n" + "void aw(bool b, double x, double y);\n" + "void ax(int i);\n" + "void ay(std::string c, std::wstring a);\n" + "void az(const std::locale &ac);\n" + "void an();\n" + "void ba(std::ifstream &f);\n" + "void bb(std::istream &f) {\n" + "f.read(NULL, 0);\n" + "}\n" + "void h(void) {\n" + "struct tm *tm = 0;\n" + "(void)std::asctime(tm);\n" + "(void)std::asctime(0);\n" + "}\n" + "void bc(size_t ae) {\n" + "wchar_t *ad = 0, *af = 0;\n" + "struct tm *ag = 0;\n" + "(void)std::wcsftime(ad, ae, af, ag);\n" + "(void)std::wcsftime(0, ae, 0, 0);\n" + "}\n" + "void k(void) {}\n" + "void bd(void);\n" + "void be(void);\n" + "void bf(int b);\n" + "void e(void);\n" + "void e(void);\n" + "void bg(wchar_t *p);\n" + "void bh(const std::list &ak, const std::list &al);\n" + "void ah();\n" + "void an();\n" + "void h();"); + ASSERT_EQUALS("[test.cpp:131]: (style) Variable 'tm' can be declared as pointer to const\n" + "[test.cpp:136]: (style) Variable 'af' can be declared as pointer to const\n" + "[test.cpp:137]: (style) Variable 'ag' can be declared as pointer to const\n", + errout_str()); + + check("class C\n" + "{\n" + "public:\n" + " explicit C(int&);\n" + "};\n" + "\n" + "class D\n" + "{\n" + "public:\n" + " explicit D(int&);\n" + "\n" + "private:\n" + " C c;\n" + "};\n" + "\n" + "D::D(int& i)\n" + " : c(i)\n" + "{\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class C\n" + "{\n" + "public:\n" + " explicit C(int&);\n" + "};\n" + "\n" + "class D\n" + "{\n" + "public:\n" + " explicit D(int&) noexcept;\n" + "\n" + "private:\n" + " C c;\n" + "};\n" + "\n" + "D::D(int& i) noexcept\n" + " : c(i)\n" + "{}"); + ASSERT_EQUALS("", errout_str()); + + check("class C\n" + "{\n" + "public:\n" + " explicit C(const int&);\n" + "};\n" + "\n" + "class D\n" + "{\n" + "public:\n" + " explicit D(int&);\n" + "\n" + "private:\n" + " C c;\n" + "};\n" + "\n" + "D::D(int& i)\n" + " : c(i)\n" + "{\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:16]: (style) Parameter 'i' can be declared as reference to const\n", "", errout_str()); + + check("class C\n" + "{\n" + "public:\n" + " explicit C(int);\n" + "};\n" + "\n" + "class D\n" + "{\n" + "public:\n" + " explicit D(int&);\n" + "\n" + "private:\n" + " C c;\n" + "};\n" + "\n" + "D::D(int& i)\n" + " : c(i)\n" + "{\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:16]: (style) Parameter 'i' can be declared as reference to const\n", "", errout_str()); + + check("class C\n" + "{\n" + "public:\n" + " explicit C(int, int);\n" + "};\n" + "\n" + "class D\n" + "{\n" + "public:\n" + " explicit D(int&);\n" + "\n" + "private:\n" + " C c;\n" + "};\n" + "\n" + "D::D(int& i)\n" + " : c(0, i)\n" + "{\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:16]: (style) Parameter 'i' can be declared as reference to const\n", "", errout_str()); + + check("void f(std::map> &map) {\n" // #10266 + " for (auto &[slave, panels] : map)\n" + " panels.erase(it);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { void f(); int i; };\n" + "void call_f(S& s) { (s.*(&S::f))(); }\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { int a[1]; };\n" + "void f(S& s) { int* p = s.a; *p = 0; }\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct Foo {\n" // #9910 + " int* p{};\n" + " int* get() { return p; }\n" + " const int* get() const { return p; }\n" + "};\n" + "struct Bar {\n" + " int j{};\n" + " void f(Foo& foo) const { int* q = foo.get(); *q = j; }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" // #10679 + " void g(long L, const C*& PC) const;\n" + " void g(long L, C*& PC);\n" + "};\n" + "void f(S& s) {\n" + " C* PC{};\n" + " s.g(0, PC);\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + // #10785 + check("template \n" + "struct d {\n" + " T& g(C& c, T C::*f) { return c.*f; }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::map& m) {\n" + " std::cout << m[0] << std::endl;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::vector>& v) {\n" // #11607 + " for (auto& m : v)\n" + " std::cout << m[0];\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { int i; };\n" // #11473 + "void f(std::vector>&m, int*& p) {\n" + " auto& a = m[0];\n" + " for (auto& s : a) {\n" + " p = &s.i;\n" + " return;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int& g(int* p, int& r) {\n" // #11625 + " if (p)\n" + " return *p;\n" + " return r;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("template void f(std::vector& d, const std::vector& s) {\n" // #11632 + " for (const auto& e : s) {\n" + " T* newE = new T(*e);\n" + " d.push_back(newE);\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::array& a) {\n" + " if (a[0]) {}\n" + "}\n" + "void g(std::array& a) {\n" + " a.fill(0);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'a' can be declared as const array\n", errout_str()); + + // #11682 + check("struct b {\n" + " void mutate();\n" + "};\n" + "struct c {\n" + " const b& get() const;\n" + " b get();\n" + "};\n" + "struct d {\n" + " void f(c& e) const {\n" + " e.get().mutate();\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct B { virtual void f() const {} };\n" // #11528 + "struct D : B {};\n" + "void g(B* b) {\n" + " D* d = dynamic_cast(b);\n" + " if (d)\n" + " d->f();\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:4]: (style) Variable 'd' can be declared as pointer to const\n", + errout_str()); + + check("void g(const int*);\n" + "void f(const std::vector&v) {\n" + " for (int* i : v)\n" + " g(i);\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3]: (style) Variable 'i' can be declared as pointer to const\n", + errout_str()); + + check("struct A {\n" // #11225 + " A();\n" + " virtual ~A();\n" + "};\n" + "struct B : A {};\n" + "void f(A* a) {\n" + " const B* b = dynamic_cast(a);\n" + "}\n" + "void g(A* a) {\n" + " const B* b = (const B*)a;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:10]: (style) C-style pointer casting\n" + "[test.cpp:6]: (style) Parameter 'a' can be declared as pointer to const\n" + "[test.cpp:9]: (style) Parameter 'a' can be declared as pointer to const\n", + errout_str()); + + check("void g(int*);\n" + "void f(std::vector& v) {\n" + " g(v.data());\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void g(const int*);\n" + "void f(std::vector& v) {\n" + " g(v.data());\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'v' can be declared as reference to const\n", errout_str()); + + check("struct a {\n" + " template \n" + " void mutate();\n" + "};\n" + "struct b {};\n" + "template \n" + "void f(a& x) {\n" + " x.mutate();\n" + "}\n" + "template \n" + "void f(const b&)\n" + "{}\n" + "void g(a& c) { f(c); }\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " template \n" + " T* g() {\n" + " return reinterpret_cast(m);\n" + " }\n" + " template \n" + " const T* g() const {\n" + " return reinterpret_cast(m);\n" + " }\n" + " char* m;\n" + "};\n" + "void f(S& s) {\n" + " const int* p = s.g();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { int x; };\n" // #11818 + "std::istream& f(std::istream& is, S& s) {\n" + " return is >> s.x;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(std::string& s1, std::string& s2) {\n" // #12203 + " return &s1 == &s2;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 's1' can be declared as reference to const\n" + "[test.cpp:1]: (style) Parameter 's2' can be declared as reference to const\n", + errout_str()); + + check("void f(int& r) {\n" // #12214 + " (void)(true);\n" + " if (r) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'r' can be declared as reference to const\n", errout_str()); + + check("struct S { void f(int&); };\n" // #12216 + "void g(S& s, int& r, void (S::* p2m)(int&)) {\n" + " (s.*p2m)(r);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " void f(int& r) { p = &r; }\n" + " int* p;\n" + "};\n" + "void g(std::vector& v1, std::vector& v2) {\n" + " std::transform(v1.begin(), v1.end(), v2.begin(), [](auto& x) { return &x; });\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("class T;\n" // #11869 + "class E {\n" + "public:\n" + " class F {\n" + " public:\n" + " explicit F(const T* t);\n" + " };\n" + "};\n" + "void f(T& t) {\n" + " std::list c(1, E::F(&t));\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:9]: (style) Parameter 't' can be declared as reference to const\n", errout_str()); + + check("struct T;\n" + "struct U {\n" + " struct V { explicit V(const T* p); };\n" + "};\n" + "void g(U::V v);\n" + "void f(T& t) {\n" + " g(U::V(&t));\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:6]: (style) Parameter 't' can be declared as reference to const\n", errout_str()); + + check("void f1(std::vector& v) {\n" // #11207 + " auto it = v.cbegin();\n" + " while (it != v.cend()) {\n" + " if (*it > 12) {}\n" + " ++it;\n" + " }\n" + "}\n" + "void f2(std::vector& v) {\n" + " auto it = v.begin();\n" + " while (it != v.end()) {\n" + " if (*it > 12) {}\n" + " ++it;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared as reference to const\n" + "[test.cpp:8]: (style) Parameter 'v' can be declared as reference to const\n", + errout_str()); + + check("void cb(const std::string&);\n" // #12349, #12350, #12351 + "void f(std::string& s) {\n" + " const std::string& str(s);\n" + " cb(str);\n" + "}\n" + "void g(std::string& s) {\n" + " const std::string& str{ s };\n" + " cb(str);\n" + "}\n" + "void h(std::string* s) {\n" + " const std::string& str(*s);\n" + " cb(str);\n" + "}\n" + "void k(std::string* s) {\n" + " const std::string& str = *s;\n" + " cb(str);\n" + "}\n" + "void m(std::string& s) {\n" + " const std::string str(s);\n" + " cb(str);\n" + "}\n" + "void n(std::string* s) {\n" + " const std::string& str(*s);\n" + " cb(str);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 's' can be declared as reference to const\n" + "[test.cpp:6]: (style) Parameter 's' can be declared as reference to const\n" + "[test.cpp:18]: (style) Parameter 's' can be declared as reference to const\n" + "[test.cpp:10]: (style) Parameter 's' can be declared as pointer to const\n" + "[test.cpp:14]: (style) Parameter 's' can be declared as pointer to const\n" + "[test.cpp:22]: (style) Parameter 's' can be declared as pointer to const\n", + errout_str()); + + check("struct S {\n" + " S(std::string& r);\n" + "};\n" + "void f(std::string& str) {\n" + " const S& s(str);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" // #10052 + " int& operator()(int);\n" + "};\n" + "void f(std::vector& c) {\n" + " c[0](5) = 12;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int& t) {\n" // #11713 + " return 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 't' can be declared as reference to const\n", errout_str()); + + check("void f(std::list& v) {\n" // #12202 + " v.remove_if([](std::string& s) {\n" + " return true;\n" + " });\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 's' can be declared as reference to const\n", errout_str()); + } + + void constParameterCallback() { + check("int callback(std::vector& x) { return x[0]; }\n" + "void f() { dostuff(callback); }"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:1]: (style) Parameter 'x' can be declared as reference to const. However it seems that 'callback' is a callback function, if 'x' is declared with const you might also need to cast function pointer(s).\n", errout_str()); + + // #9906 + check("class EventEngine : public IEventEngine {\n" + "public:\n" + " EventEngine();\n" + "\n" + "private:\n" + " void signalEvent(ev::sig& signal, int revents);\n" + "};\n" + "\n" + "EventEngine::EventEngine() {\n" + " mSigWatcher.set(this);\n" + "}\n" + "\n" + "void EventEngine::signalEvent(ev::sig& signal, int revents) {\n" + " switch (signal.signum) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:10] -> [test.cpp:13]: (style) Parameter 'signal' can be declared as reference to const. However it seems that 'signalEvent' is a callback function, if 'signal' is declared with const you might also need to cast function pointer(s).\n", errout_str()); + } + + void constPointer() { + check("void foo(int *p) { return *p; }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout_str()); + + check("void foo(int *p) { x = *p; }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout_str()); + + check("void foo(int *p) { int &ref = *p; ref = 12; }"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int *p) { x = *p + 10; }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout_str()); + + check("void foo(int *p) { return p[10]; }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout_str()); + + check("void foo(int *p) { int &ref = p[0]; ref = 12; }"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int *p) { x[*p] = 12; }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout_str()); + + check("void foo(int *p) { if (p) {} }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout_str()); + + check("void foo(int *p) { if (p || x) {} }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout_str()); + + check("void foo(int *p) { if (p == 0) {} }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout_str()); + + check("void foo(int *p) { if (!p) {} }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout_str()); + + check("void foo(int *p) { if (*p > 123) {} }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout_str()); + + check("void foo(int *p) { return *p + 1; }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout_str()); + + check("void foo(int *p) { return *p > 1; }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout_str()); + + check("void foo(const int* c) { if (c == 0) {}; }"); + ASSERT_EQUALS("", errout_str()); + + check("struct a { void b(); };\n" + "struct c {\n" + " a* d;\n" + " a& g() { return *d; }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct a { void b(); };\n" + "struct c { a* d; };\n" + "void e(c);\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct V {\n" + " V& get(typename std::vector::size_type i, std::vector* arr) {\n" + " return arr->at(i);\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {};\n" + "struct B : A {};\n" + "B* f(A* x) {\n" + " return static_cast(x);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int f(std::vector* x) {\n" + " int& i = (*x)[0];\n" + " i++;\n" + " return i;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int a; };\n" + "A f(std::vector* x) {\n" + " x->front().a = 1;\n" + " return x->front();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::vector* v) {\n" + " for(auto&& x:*v)\n" + " x = 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " int* x;\n" + " A(int* y) : x(y)\n" + " {}\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool b, int* x, int* y) {\n" + " int* z = x;\n" + " int* w = b ? y : z;\n" + " *w = 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool b, int* x, int* y) {\n" + " int& z = *x;\n" + " int& w = b ? *y : z;\n" + " w = 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class Base { virtual void dostuff(int *p) = 0; };\n" // #10397 + "class Derived: public Base { int x; void dostuff(int *p) override { x = *p; } };"); + ASSERT_EQUALS("", errout_str()); + + check("struct Data { char buf[128]; };\n" // #10483 + "void encrypt(Data& data) {\n" + " const char a[] = \"asfasd\";\n" + " memcpy(data.buf, &a, sizeof(a));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #10547 + check("void foo(std::istream &istr) {\n" + " unsigned char x[2];\n" + " istr >> x[0];\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #10744 + check("S& f() {\n" + " static S* p = new S();\n" + " return *p;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int f() {\n" + " static int i[1] = {};\n" + " return i[0];\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'i' can be declared as const array\n", errout_str()); + + check("int f() {\n" + " static int i[] = { 0 };\n" + " int j = i[0] + 1;\n" + " return j;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'i' can be declared as const array\n", errout_str()); + + // #10471 + check("void f(std::array const& i) {\n" + " if (i[0] == 0) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #10466 + check("typedef void* HWND;\n" + "void f(const std::vector&v) {\n" + " for (const auto* h : v)\n" + " if (h) {}\n" + " for (const auto& h : v)\n" + " if (h) {}\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:5]: (style) Variable 'h' can be declared as pointer to const\n", + errout_str()); + + check("void f(const std::vector& v) {\n" + " for (const auto& p : v)\n" + " if (p == nullptr) {}\n" + " for (const auto* p : v)\n" + " if (p == nullptr) {}\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:2]: (style) Variable 'p' can be declared as pointer to const\n", + errout_str()); + + check("void f(std::vector& v) {\n" + " for (const auto& p : v)\n" + " if (p == nullptr) {}\n" + " for (const auto* p : v)\n" + " if (p == nullptr) {}\n" + " for (const int* const& p : v)\n" + " if (p == nullptr) {}\n" + " for (const int* p : v)\n" + " if (p == nullptr) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared as reference to const\n" + "[test.cpp:2]: (style) Variable 'p' can be declared as pointer to const\n", + errout_str()); + + check("void f(std::vector& v) {\n" + " for (const auto& p : v)\n" + " if (p == nullptr) {}\n" + " for (const auto* p : v)\n" + " if (p == nullptr) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'v' can be declared as reference to const\n", errout_str()); + + check("void f(const std::vector& v) {\n" + " for (const auto& p : v)\n" + " if (p == nullptr) {}\n" + " for (const auto* p : v)\n" + " if (p == nullptr) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const int* const p) {\n" + " if (p == nullptr) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void g(int*);\n" + "void f(int* const* pp) {\n" + " int* p = pp[0];\n" + " g(p);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("template \n" + "struct S {\n" + " static bool f(const T& t) { return t != nullptr; }\n" + "};\n" + "S s;\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int i) {\n" + " const char *tmp;\n" + " char* a[] = { \"a\", \"aa\" };\n" + " static char* b[] = { \"b\", \"bb\" };\n" + " tmp = a[i];\n" + " printf(\"%s\", tmp);\n" + " tmp = b[i];\n" + " printf(\"%s\", tmp);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'a' can be declared as const array\n" + "[test.cpp:4]: (style) Variable 'b' can be declared as const array\n", + errout_str()); + + check("typedef void* HWND;\n" // #11084 + "void f(const HWND h) {\n" + " if (h == nullptr) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("using HWND = void*;\n" + "void f(const HWND h) {\n" + " if (h == nullptr) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("typedef int A;\n" + "void f(A* x) {\n" + " if (x == nullptr) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as pointer to const\n", errout_str()); + + check("using A = int;\n" + "void f(A* x) {\n" + " if (x == nullptr) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'x' can be declared as pointer to const\n", errout_str()); + + check("struct S { void v(); };\n" // #11095 + "void f(S* s) {\n" + " (s - 1)->v();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(std::vector& v) {\n" // #11085 + " for (int* p : v) {\n" + " if (p) {}\n" + " }\n" + " for (auto* p : v) {\n" + " if (p) {}\n" + " }\n" + " v.clear();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'p' can be declared as pointer to const\n" + "[test.cpp:5]: (style) Variable 'p' can be declared as pointer to const\n", + errout_str()); + + check("void f() {\n" + " char a[1][1];\n" + " char* b[1];\n" + " b[0] = a[0];\n" + " **b = 0;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("ptrdiff_t f(int *p0, int *p1) {\n" // #11148 + " return p0 - p1;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p0' can be declared as pointer to const\n" + "[test.cpp:1]: (style) Parameter 'p1' can be declared as pointer to const\n", + errout_str()); + + check("void f() {\n" + " std::array a{}, b{};\n" + " const std::array& r = a;\n" + " if (r == b) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {};\n" // #11599 + "void g(S);\n" + "void h(const S&);\n" + "void h(int, int, const S&);\n" + "void i(S&);\n" + "void j(const S*);\n" + "void j(int, int, const S*);\n" + "void f1(S* s) {\n" + " g(*s);\n" + "}\n" + "void f2(S* s) {\n" + " h(*s);\n" + "}\n" + "void f3(S* s) {\n" + " h(1, 2, *s);\n" + "}\n" + "void f4(S* s) {\n" + " i(*s);\n" + "}\n" + "void f5(S& s) {\n" + " j(&s);\n" + "}\n" + "void f6(S& s) {\n" + " j(1, 2, &s);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:20]: (style) Parameter 's' can be declared as reference to const\n" + "[test.cpp:23]: (style) Parameter 's' can be declared as reference to const\n" + "[test.cpp:8]: (style) Parameter 's' can be declared as pointer to const\n" + "[test.cpp:11]: (style) Parameter 's' can be declared as pointer to const\n" + "[test.cpp:14]: (style) Parameter 's' can be declared as pointer to const\n", + errout_str()); + + check("void g(int, const int*);\n" + "void h(const int*);\n" + "void f(int* p) {\n" + " g(1, p);\n" + " h(p);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Parameter 'p' can be declared as pointer to const\n", + errout_str()); + + check("void f(int, const int*);\n" + "void f(int i, int* p) {\n" + " f(i, const_cast(p));\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { int a; };\n" + "void f(std::vector& v, int b) {\n" + " size_t n = v.size();\n" + " for (size_t i = 0; i < n; i++) {\n" + " S& s = v[i];\n" + " if (!(b & s.a))\n" + " continue;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (style) Variable 's' can be declared as reference to const\n", errout_str()); // don't crash + + check("void f(int& i) {\n" + " new (&i) int();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); // don't crash + + check("void f(int& i) {\n" + " int& r = i;\n" + " if (!&r) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'r' can be declared as reference to const\n", errout_str()); // don't crash + + check("class C;\n" // #11646 + "void g(const C* const p);\n" + "void f(C* c) {\n" + " g(c);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Parameter 'c' can be declared as pointer to const\n", errout_str()); + + check("typedef void (*cb_t)(int*);\n" // #11674 + "void cb(int* p) {\n" + " if (*p) {}\n" + "}\n" + "void g(cb_t);\n" + "void f() {\n" + " g(cb);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:2]: (style) Parameter 'p' can be declared as pointer to const. " + "However it seems that 'cb' is a callback function, if 'p' is declared with const you might also need to cast function pointer(s).\n", + errout_str()); + + check("typedef void (*cb_t)(int*);\n" + "void cb(int* p) {\n" + " if (*p) {}\n" + "}\n" + "void g(cb_t);\n" + "void f() {\n" + " g(::cb);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:2]: (style) Parameter 'p' can be declared as pointer to const. " + "However it seems that 'cb' is a callback function, if 'p' is declared with const you might also need to cast function pointer(s).\n", + errout_str()); + + check("void f1(std::vector* p) {\n" // #11681 + " if (p->empty()) {}\n" // warn + "}\n" + "void f2(std::vector* p) {\n" + " p->resize(0);\n" + "}\n" + "struct S {\n" + " void h1() const;\n" + " void h2();\n" + " int i;\n" + "};\n" + "void k(int&);\n" + "void g1(S* s) {\n" + " s->h1();\n" // warn + "}\n" + "void g1(S* s) {\n" + " s->h2();\n" + "}\n" + "void g1(S* s) {\n" + " if (s->i) {}\n" // warn + "}\n" + "void g2(S* s) {\n" + " s->i = 0;\n" + "}\n" + "void g3(S* s) {\n" + " k(s->i);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n" + "[test.cpp:13]: (style) Parameter 's' can be declared as pointer to const\n" + "[test.cpp:19]: (style) Parameter 's' can be declared as pointer to const\n", + errout_str()); + + check("struct S {\n" // #11573 + " const char* g() const {\n" + " return m;\n" + " }\n" + " const char* m;\n" + "};\n" + "struct T { std::vector v; };\n" + "void f(T* t, const char* n) {\n" + " for (const auto* p : t->v)\n" + " if (strcmp(p->g(), n) == 0) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:8]: (style) Parameter 't' can be declared as pointer to const\n", + errout_str()); + + check("void f(int*& p, int* q) {\n" + " p = q;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { int a[1]; };\n" + "void f(S* s) {\n" + " if (s->a[0]) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 's' can be declared as pointer to const\n", + errout_str()); + + check("size_t f(char* p) {\n" // #11842 + " return strlen(p);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", errout_str()); + + check("void f(int* p) {\n" // #11862 + " long long j = *(p++);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", + errout_str()); + + check("void f(void *p, size_t nmemb, size_t size, int (*cmp)(const void *, const void *)) {\n" + " qsort(p, nmemb, size, cmp);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void g(bool *r, std::size_t *b) {\n" // #12129 + " if (*r && *b >= 5) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'r' can be declared as pointer to const\n" + "[test.cpp:1]: (style) Parameter 'b' can be declared as pointer to const\n", + errout_str()); + + check("void f(int i) {\n" // #12185 + " void* p = &i;\n" + " std::cout << p << '\\n';\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'p' can be declared as pointer to const\n", + errout_str()); + + check("struct S { const T* t; };\n" // #12206 + "void f(S* s) {\n" + " if (s->t.i) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 's' can be declared as pointer to const\n", + errout_str()); + + check("void f(char *a1, char *a2) {\n" // #12252 + " char* b = new char[strlen(a1) + strlen(a2) + 2];\n" + " sprintf(b, \"%s_%s\", a1, a2);\n" + " delete[] b;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'a1' can be declared as pointer to const\n" + "[test.cpp:1]: (style) Parameter 'a2' can be declared as pointer to const\n", + errout_str()); + + check("int f(int* p) {\n" // #11713 + " return 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n", + errout_str()); + + check("void f(int *src, int* dst) {\n" // #12518 + " *dst++ = (int)*src++;\n" + " *dst++ = static_cast(*src++);\n" + " *dst = (int)*src;\n" + "}\n" + "void g(int* dst) {\n" + " (int&)*dst = 5;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'src' can be declared as pointer to const\n", + errout_str()); + + check("struct S {};\n" + "void f(T* t) {\n" + " S* s = (S*)t->p;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) C-style pointer casting\n" + "[test.cpp:3]: (style) Variable 's' can be declared as pointer to const\n", + errout_str()); // don't crash + + check("struct S { int i; };\n" // #12205 + "void f(S* s) {\n" + " (void)s->i;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 's' can be declared as pointer to const\n", + 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]: (style) 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]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout_str()); + + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " y = 2;\n" + " case 3:\n" + " if (x)\n" + " {\n" + " y = 3;\n" + " }\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " {\n" + " y = 2;\n" + " if (z)\n" + " printf(\"%d\", y);\n" + " }\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " int x = a;\n" + " int y = 1;\n" + " switch (x)\n" + " {\n" + " case 2:\n" + " x = 2;\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (x)\n" + " {\n" + " case 2:\n" + " y = 2;\n" + " break;\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " int y = 1;\n" + " while(xyz()) {\n" + " switch (x)\n" + " {\n" + " case 2:\n" + " y = 2;\n" + " continue;\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " int y = 1;\n" + " while(xyz()) {\n" + " switch (x)\n" + " {\n" + " case 2:\n" + " y = 2;\n" + " throw e;\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (x)\n" + " {\n" + " case 2:\n" + " y = 2;\n" + " printf(\"%d\", y);\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (x)\n" + " {\n" + " case 2:\n" + " y = 2;\n" + " bar();\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:10]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout_str()); + + check("void bar() {}\n" // bar isn't noreturn + "void foo()\n" + "{\n" + " int y = 1;\n" + " switch (x)\n" + " {\n" + " case 2:\n" + " y = 2;\n" + " bar();\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:11]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout_str()); + + check("void foo(int a) {\n" + " char str[10];\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " strcpy(str, \"a'\");\n" + " case 3:\n" + " strcpy(str, \"b'\");\n" + " }\n" + "}", true, false, false); + TODO_ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:8]: (style) Buffer 'str' is being written before its old content has been used. 'break;' missing?\n", + "", + errout_str()); + + check("void foo(int a) {\n" + " char str[10];\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " strncpy(str, \"a'\");\n" + " case 3:\n" + " strncpy(str, \"b'\");\n" + " }\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:8]: (style) Buffer 'str' is being written before its old content has been used. 'break;' missing?\n", + "", + errout_str()); + + check("void foo(int a) {\n" + " char str[10];\n" + " int z = 0;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " strcpy(str, \"a'\");\n" + " z++;\n" + " case 3:\n" + " strcpy(str, \"b'\");\n" + " z++;\n" + " }\n" + "}", true, false, false); + TODO_ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:10]: (style) Buffer 'str' is being written before its old content has been used. 'break;' missing?\n", + "", + errout_str()); + + check("void foo(int a) {\n" + " char str[10];\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " strcpy(str, \"a'\");\n" + " break;\n" + " case 3:\n" + " strcpy(str, \"b'\");\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int a) {\n" + " char str[10];\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " strcpy(str, \"a'\");\n" + " printf(str);\n" + " case 3:\n" + " strcpy(str, \"b'\");\n" + " }\n" + "}", true, 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", true, false, false); + ASSERT_EQUALS("", errout_str()); + + // Ticket #6132 "crash: daca: kvirc CheckOther::checkRedundantAssignment()" + check("void HttpFileTransfer :: transferTerminated ( bool bSuccess ) {\n" + "if ( m_szCompletionCallback . isNull ( ) ) {\n" + "KVS_TRIGGER_EVENT ( KviEvent_OnHTTPGetTerminated , out ? out : ( g_pApp . activeConsole ( ) ) , & vParams )\n" + "} else {\n" + "KviKvsScript :: run ( m_szCompletionCallback , out ? out : ( g_pApp . activeConsole ( ) ) , & vParams ) ;\n" + "}\n" + "}\n", true, false, true); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int x;\n" + " switch (state) {\n" + " case 1: x = 3; goto a;\n" + " case 1: x = 6; goto a;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void switchRedundantOperationTest() { + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " ++y;\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (style) 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]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout_str()); + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " (void)y;\n" + " case 3:\n" + " ++y;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " ++y;\n" + " case 3:\n" + " ++y;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " --y;\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (style) 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]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout_str()); + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " (void)y;\n" + " case 3:\n" + " --y;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " --y;\n" + " case 3:\n" + " --y;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " y++;\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:9]: (style) 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]: (style) 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" + "}", true, 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]: (style) 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]: (style) 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" + "}", true, false, false); + ASSERT_EQUALS("", errout_str()); + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " y--;\n" + " case 3:\n" + " y--;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " y++;\n" + " case 3:\n" + " if (x)\n" + " {\n" + " y = 3;\n" + " }\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " {\n" + " y++;\n" + " if (y)\n" + " printf(\"%d\", y);\n" + " }\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void foo()\n" + "{\n" + " int x = a;\n" + " int y = 1;\n" + " switch (x)\n" + " {\n" + " case 2:\n" + " x++;\n" + " case 3:\n" + " y++;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (x)\n" + " {\n" + " case 2:\n" + " y++;\n" + " break;\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void foo()\n" + "{\n" + " int y = 1;\n" + " while(xyz()) {\n" + " switch (x)\n" + " {\n" + " case 2:\n" + " y++;\n" + " continue;\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void foo()\n" + "{\n" + " int y = 1;\n" + " while(xyz()) {\n" + " switch (x)\n" + " {\n" + " case 2:\n" + " y++;\n" + " throw e;\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (x)\n" + " {\n" + " case 2:\n" + " y++;\n" + " printf(\"%d\", y);\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void foo()\n" + "{\n" + " int y = 1;\n" + " switch (x)\n" + " {\n" + " case 2:\n" + " y++;\n" + " bar();\n" + " case 3:\n" + " y = 3;\n" + " }\n" + " bar(y);\n" + "}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:10]: (style) Variable 'y' is reassigned a value before the old one has been used. 'break;' missing?\n", errout_str()); + + check("bool f() {\n" + " bool ret = false;\n" + " switch (switchCond) {\n" + " case 1:\n" + " ret = true;\n" + " break;\n" + " case 31:\n" + " ret = true;\n" + " break;\n" + " case 54:\n" + " ret = true;\n" + " break;\n" + " };\n" + " ret = true;\n" + " return ret;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:14]: (style) Variable 'ret' is reassigned a value before the old one has been used.\n" + "[test.cpp:8] -> [test.cpp:14]: (style) Variable 'ret' is reassigned a value before the old one has been used.\n" + "[test.cpp:11] -> [test.cpp:14]: (style) Variable 'ret' is reassigned a value before the old one has been used.\n", + errout_str()); + } + + void switchRedundantBitwiseOperationTest() { + check("void foo(int a)\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " y |= 3;\n" + " case 3:\n" + " y |= 3;\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (style) Redundant bitwise operation on 'y' in 'switch' statement. 'break;' missing?\n", errout_str()); + + check("void foo(int a)\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " y = y | 3;\n" + " case 3:\n" + " y = y | 3;\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (style) 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]: (style) Redundant bitwise operation on 'y' in 'switch' statement. 'break;' missing?\n", errout_str()); + + check("void foo(int a)\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " y |= 3;\n" + " default:\n" + " if (z)\n" + " y |= 3;\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int a)\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " y |= z;\n" + " z++\n" + " default:\n" + " y |= z;\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int a)\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " y |= 3;\n" + " bar(y);\n" + " case 3:\n" + " y |= 3;\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int a)\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " y |= 3;\n" + " y = 4;\n" + " case 3:\n" + " y |= 3;\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:8]: (style) Variable 'y' is reassigned a value before the old one has been used.\n", errout_str()); + + check("void foo(int a)\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " y &= 3;\n" + " case 3:\n" + " y &= 3;\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (style) Redundant bitwise operation on 'y' in 'switch' statement. 'break;' missing?\n", errout_str()); + + check("void foo(int a)\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " y |= 3;\n" + " break;\n" + " case 3:\n" + " y |= 3;\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int a)\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " y ^= 3;\n" + " case 3:\n" + " y ^= 3;\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int a)\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " y |= 2;\n" + " case 3:\n" + " y |= 3;\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int a)\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " y &= 2;\n" + " case 3:\n" + " y &= 3;\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int a)\n" + "{\n" + " int y = 1;\n" + " switch (a)\n" + " {\n" + " case 2:\n" + " y |= 2;\n" + " case 3:\n" + " y &= 2;\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void unreachableCode() { + check("void foo(int a) {\n" + " while(1) {\n" + " if (a++ >= 100) {\n" + " break;\n" + " continue;\n" + " }\n" + " }\n" + "}", true, 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" + "}", true, 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" + "}", true, false, false); + ASSERT_EQUALS("[test.cpp:3]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout_str()); + + constexpr char xmldata[] = "\n" + "\n" + " \n" + " true\n" + " \n" + " \n" + ""; + /*const*/ Settings settings = settingsBuilder().libraryxml(xmldata, sizeof(xmldata)).build(); + + check("void foo() {\n" + " exit(0);\n" + " break;\n" + "}", true, 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" + "}", true, 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" + "}", true, false, false, false, &settings); + ASSERT_EQUALS("", errout_str()); + + check("void foo() { xResAccess->exit(); }", true, 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" + "}", true, 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" + "}", true, 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" + "}", true, 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" + "}", true, false, false); + ASSERT_EQUALS("[test.cpp:3]: (style) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout_str()); + + check("void foo() {\n" + " throw 0;\n" + " return;\n" + "}", true, 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" + " throw = 0;\n" + " return 1;\n" + "}", false, false, false); + ASSERT_EQUALS("", errout_str()); + + check("int foo() {\n" + " return 0;\n" + " return 1;\n" + "}", true, 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" + "}", true, false, false); + ASSERT_EQUALS("[test.cpp:3]: (style) Statements following 'return' will never be executed.\n", errout_str()); + + check("int foo(int unused) {\n" + " return 0;\n" + " (void)unused;\n" + "}", true, false, false); + ASSERT_EQUALS("", errout_str()); + + check("int foo(int unused1, int unused2) {\n" + " return 0;\n" + " (void)unused1;\n" + " (void)unused2;\n" + "}", true, false, false); + ASSERT_EQUALS("", errout_str()); + + check("int foo(int unused1, int unused2) {\n" + " return 0;\n" + " (void)unused1;\n" + " (void)unused2;\n" + " foo();\n" + "}", true, false, false); + ASSERT_EQUALS("[test.cpp:5]: (style) Statements following 'return' 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" + "}", true, 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" + "}", true, 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" + "}", true, false, false); + ASSERT_EQUALS("[test.cpp:7]: (style) Statements following 'return' will never be executed.\n", errout_str()); + + check("int foo() {\n" + " return 0;\n" + " label:\n" + " throw 0;\n" + "}", true, false, false); + ASSERT_EQUALS("[test.cpp:3]: (style) Label 'label' is not used.\n", errout_str()); + + check("struct A {\n" + " virtual void foo (P & Val) throw ();\n" + " virtual void foo1 (P & Val) throw ();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int foo() {\n" + " goto label;\n" + " while (true) {\n" + " bar();\n" + " label:\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); // #3457 + + check("int foo() {\n" + " goto label;\n" + " do {\n" + " bar();\n" + " label:\n" + " } while (true);\n" + "}"); + ASSERT_EQUALS("", errout_str()); // #3457 + + check("int foo() {\n" + " goto label;\n" + " for (;;) {\n" + " bar();\n" + " label:\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); // #3457 + + // #3383. TODO: Use preprocessor + check("int foo() {\n" + "\n" // #ifdef A + " return 0;\n" + "\n" // #endif + " return 1;\n" + "}", true, false, false); + ASSERT_EQUALS("", errout_str()); + check("int foo() {\n" + "\n" // #ifdef A + " return 0;\n" + "\n" // #endif + " return 1;\n" + "}", true, true, false); + ASSERT_EQUALS("[test.cpp:5]: (style, inconclusive) Consecutive return, break, continue, goto or throw statements are unnecessary.\n", errout_str()); + + // #4711 lambda functions + check("int f() {\n" + " return g([](int x){(void)x+1; return x;});\n" + "}", + true, + false, + false); + ASSERT_EQUALS("", errout_str()); + + // #4756 + check("template <>\n" + "inline uint16_t htobe(uint16_t value) {\n" + " return ( __extension__ ({\n" + " register unsigned short int __v, __x = (unsigned short int) (value);\n" + " if (__builtin_constant_p (__x))\n" + " __v = ((unsigned short int) ((((__x) >> 8) & 0xff) | (((__x) & 0xff) << 8)));\n" + " else\n" + " __asm__ (\"rorw $8, %w0\" : \"=r\" (__v) : \"0\" (__x) : \"cc\");\n" + " (void)__v;\n" + " }));\n" + "}", true, false, false); + ASSERT_EQUALS("", errout_str()); + + // #6008 + check("static std::function< int ( int, int ) > GetFunctor() {\n" + " return [](int a_, int b_) -> int {\n" + " int sum = a_ + b_;\n" + " return sum;\n" + " };\n" + "}", true, false, false); + ASSERT_EQUALS("", errout_str()); + + // #5789 + check("struct per_state_info {\n" + " uint64_t enter, exit;\n" + " uint64_t events;\n" + " per_state_info() : enter(0), exit(0), events(0) {}\n" + "};", true, false, false); + ASSERT_EQUALS("", errout_str()); + + // #6664 + check("void foo() {\n" + " (beat < 100) ? (void)0 : exit(0);\n" + " bar();\n" + "}", true, false, false, false, &settings); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " (beat < 100) ? exit(0) : (void)0;\n" + " bar();\n" + "}", true, false, false, false, &settings); + ASSERT_EQUALS("", errout_str()); + + // #8261 + // TODO Do not throw AST validation exception + TODO_ASSERT_THROW(check("void foo() {\n" + " (beat < 100) ? (void)0 : throw(0);\n" + " bar();\n" + "}", true, false, false, false, &settings), InternalError); + //ASSERT_EQUALS("", errout_str()); + + check("int foo() {\n" + " exit(0);\n" + " return 1;\n" // <- clarify for tools that function does not continue.. + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " enum : uint8_t { A, B } var = A;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + checkP("#define INB(x) __extension__ ({ u_int tmp = (x); inb(tmp); })\n" // #4739 + "static unsigned char cmos_hal_read(unsigned index) {\n" + " unsigned short port_0, port_1;\n" + " assert(!verify_cmos_byte_index(index));\n" + " if (index < 128) {\n" + " port_0 = 0x70;\n" + " port_1 = 0x71;\n" + " }\n" + " else {\n" + " port_0 = 0x72;\n" + " port_1 = 0x73;\n" + " }\n" + " OUTB(index, port_0);\n" + " return INB(port_1);\n" + "}\n", "test.c"); + ASSERT_EQUALS("", errout_str()); + + check("[[noreturn]] void n();\n" + "void f() {\n" + " n();\n" + " g();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Statements following noreturn function 'n()' will never be executed.\n", errout_str()); + + check("void f() {\n" + " exit(1);\n" + " g();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Statements following noreturn function 'exit()' will never be executed.\n", errout_str()); + + check("void f() {\n" + " do {\n" + " break;\n" + " g();\n" + " } while (0);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Statements following 'break' will never be executed.\n", errout_str()); + } + + void redundantContinue() { + check("void f() {\n" // #11195 + " for (int i = 0; i < 10; ++i) {\n" + " printf(\"i = %d\\n\", i);\n" + " continue;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) 'continue' is redundant since it is the last statement in a loop.\n", errout_str()); + + check("void f() {\n" + " int i = 0;" + " do {\n" + " ++i;\n" + " printf(\"i = %d\\n\", i);\n" + " continue;\n" + " } while (i < 10);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (style) 'continue' is redundant since it is the last statement in a loop.\n", 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()); + + // TODO Do not throw AST validation exception + TODO_ASSERT_THROW(check("void foo() {\n" + " switch(a) {\n" + " case A&&B?B:A:\n" + " foo();\n" + " }\n" + "}"), InternalError); + //ASSERT_EQUALS("", errout_str()); + } + + void suspiciousEqualityComparison() { + check("void foo(int c) {\n" + " if (x) c == 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout_str()); + + check("void foo(const int* c) {\n" + " if (x) *c == 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout_str()); + + + check("void foo(int c) {\n" + " if (c == 1) {\n" + " c = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int c) {\n" + " c == 1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout_str()); + + check("void foo(int c) {\n" + " for (int i = 0; i == 10; i ++) {\n" + " a ++;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int c) {\n" + " for (i == 0; i < 10; i ++) {\n" + " c ++;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout_str()); + + check("void foo(int c) {\n" + " for (i == 1; i < 10; i ++) {\n" + " c ++;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout_str()); + + check("void foo(int c) {\n" + " for (i == 2; i < 10; i ++) {\n" + " c ++;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout_str()); + + check("void foo(int c) {\n" + " for (int i = 0; i < 10; i == c) {\n" + " c ++;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout_str()); + + check("void foo(int c) {\n" + " for (; running == 1;) {\n" + " c ++;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int c) {\n" + " printf(\"%i\", ({x==0;}));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int arg) {\n" + " printf(\"%i\", ({int x = do_something(); x == 0;}));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int x) {\n" + " printf(\"%i\", ({x == 0; x > 0 ? 10 : 20}));\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious equality comparison. Did you intend to assign a value instead?\n", errout_str()); + + check("void foo(int x) {\n" + " for (const Token* end = tok->link(); tok != end; tok = (tok == end) ? end : tok->next()) {\n" + " x++;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int x) {\n" + " for (int i = (x == 0) ? 0 : 5; i < 10; i ++) {\n" + " x++;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int x) {\n" + " for (int i = 0; i < 10; i += (x == 5) ? 1 : 2) {\n" + " x++;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void suspiciousUnaryPlusMinus() { // #8004 + check("int g() { return 1; }\n" + "void f() {\n" + " +g();\n" + " -g();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Found suspicious operator '+', result is not used.\n" + "[test.cpp:4]: (warning, inconclusive) Found suspicious operator '-', result is not used.\n", + errout_str()); + + check("void f(int i) {\n" + " +i;\n" + " -i;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious operator '+', result is not used.\n" + "[test.cpp:3]: (warning, inconclusive) Found suspicious operator '-', result is not used.\n", + errout_str()); + } + + void selfAssignment() { + check("void foo()\n" + "{\n" + " int x = 1;\n" + " x = x;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Redundant assignment of 'x' to itself.\n", errout_str()); + + check("void foo()\n" + "{\n" + " int x = x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Redundant assignment of 'x' to itself.\n", errout_str()); + + check("struct A { int b; };\n" + "void foo(A* a1, A* a2) {\n" + " a1->b = a1->b;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Redundant assignment of 'a1->b' to itself.\n", errout_str()); + + check("int x;\n" + "void f()\n" + "{\n" + " x = x = 3;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Redundant assignment of 'x' to itself.\n", errout_str()); + + // #4073 (segmentation fault) + check("void Foo::myFunc( int a )\n" + "{\n" + " if (a == 42)\n" + " a = a;\n" + "}"); + + check("void foo()\n" + "{\n" + " int x = 1;\n" + " x = x + 1;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo()\n" + "{\n" + " int *x = getx();\n" + " *x = x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " BAR *x = getx();\n" + " x = x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) 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]: (style) 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]: (style) Redundant assignment of 'this->var' to itself.\n", errout_str()); + + check("class Foo {\n" + " int var;\n" + " void func(int var);\n" + "};\n" + "void Foo::func(int var) {\n" + " this->var = var;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #6406 - designated initializer doing bogus self assignment + check("struct callbacks {\n" + " void (*s)(void);\n" + "};\n" + "void something(void) {}\n" + "void f() {\n" + " struct callbacks ops = { .s = ops.s };\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:6]: (style) Redundant assignment of 'something' to itself.\n", "", errout_str()); + + check("class V\n" + "{\n" + "public:\n" + " V()\n" + " {\n" + " x = y = z = 0.0;\n" + " }\n" + " V( double x, const double y_, const double &z_)\n" + " {\n" + " x = x; y = y; z = z;\n" + " }\n" + " double x, y, z;\n" + "};"); + ASSERT_EQUALS("[test.cpp:10]: (style) Redundant assignment of 'x' to itself.\n" + "[test.cpp:10]: (style) Redundant assignment of 'y' to itself.\n" + "[test.cpp:10]: (style) Redundant assignment of 'z' to itself.\n", errout_str()); + + check("void f(int i) { i = !!i; }"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " int x = 1;\n" + " int &ref = x;\n" + " ref = x;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Redundant assignment of 'ref' to itself.\n", errout_str()); + + check("class Foo {\n" // #9850 + " int i{};\n" + " void modify();\n" + " void method() {\n" + " Foo copy = *this;\n" + " modify();\n" + " *this = copy;\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" // #11383 + " void f() {\n" + " int x = 42;" + " auto l2 = [i = i, x, y = 0]() { return i + x + y; };\n" + " }\n" + " int i;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #10337 + " int b[2] = { 1, 2 };\n" + " int idx = 0;\n" + " int& i = b[idx];\n" + " idx++;\n" + " i = b[idx];\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("void g(int*);\n" // #12390 + "void f() {\n" + " int o = s.i;\n" + " g(&s.i);\n" + " s.i = o;\n" + "}\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" + "}"); + ASSERT_EQUALS("[test.cpp:15]: (style) Instance of 'Lock' object is destroyed immediately.\n", errout_str()); + } + + void trac3693() { + check("struct A{\n" + " enum {\n" + " b = 300\n" + " };\n" + "};\n" + "const int DFLT_TIMEOUT = A::b % 1000000 ;\n", true, false, false); + ASSERT_EQUALS("", errout_str()); + } + + void testMisusedScopeObjectDoesNotPickFunction1() { + check("int main ( )\n" + "{\n" + " CouldBeFunction ( 123 ) ;\n" + " return 0 ;\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" + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void testMisusedScopeObjectPicksClass() { + check("class NotAFunction ;\n" + "int function ( )\n" + "{\n" + " NotAFunction ( 123 );\n" + " return 0 ;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Instance of 'NotAFunction' object is destroyed immediately.\n", errout_str()); + } + + void testMisusedScopeObjectPicksStruct() { + check("struct NotAClass;\n" + "bool func ( )\n" + "{\n" + " NotAClass ( 123 ) ;\n" + " return true ;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Instance of 'NotAClass' object is destroyed immediately.\n", errout_str()); + } + + void testMisusedScopeObjectDoesNotPickIf() { + check("bool func( int a , int b , int c )\n" + "{\n" + " if ( a > b ) return c == a ;\n" + " return b == a ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testMisusedScopeObjectDoesNotPickConstructorDeclaration() { + check("class Something : public SomethingElse\n" + "{\n" + "public:\n" + "~Something ( ) ;\n" + "Something ( ) ;\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" + "}"); + 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" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (style) Instance of 'Foo' object is destroyed immediately.\n", errout_str()); + } + + void testMisusedScopeObjectDoesNotPickUsedObject() { + check("struct Foo {\n" + " void bar() {\n" + " }\n" + "};\n" + "\n" + "void fn() {\n" + " Foo().bar();\n" + "}"); + 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, true); + ASSERT_EQUALS("[test.cpp:7]: (style) Instance of 'cb_watch_bool' object is destroyed immediately.\n", errout_str()); + + check(code, false); + 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()); + + check("struct AMethodObject {\n" // #4336 + " AMethodObject(double, double, double);\n" + "};\n" + "struct S {\n" + " static void A(double, double, double);\n" + "};\n" + "void S::A(double const a1, double const a2, double const a3) {\n" + " AMethodObject(a1, a2, a3);\n" + "}\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, true); + 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, true); + ASSERT_EQUALS("[test.cpp:4]: (style) Instance of 'Foo' object is destroyed immediately.\n", errout_str()); + } + + void testMisusedScopeObjectStandardType() { + check("int g();\n" + "void f(int i) {\n" + " int();\n" + " int(0);\n" + " int( g() );\n" // don't warn + " int{};\n" + " int{ 0 };\n" + " int{ i };\n" + " int{ g() };\n" // don't warn + " g();\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:3]: (style) Instance of 'int' object is destroyed immediately.\n" + "[test.cpp:4]: (style) Instance of 'int' object is destroyed immediately.\n" + "[test.cpp:6]: (style) Instance of 'int' object is destroyed immediately.\n" + "[test.cpp:7]: (style) Instance of 'int' object is destroyed immediately.\n" + "[test.cpp:8]: (style) Instance of 'int' object is destroyed immediately.\n", + errout_str()); + + check("void f(int j) {\n" + " for (; bool(j); ) {}\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + check("void g() {\n" + " float (f);\n" + " float (*p);\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + + check("int f(int i) {\n" + " void();\n" + " return i;\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + } + + void testMisusedScopeObjectNamespace() { + check("namespace M {\n" // #4779 + " namespace N {\n" + " struct S {};\n" + " }\n" + "}\n" + "int f() {\n" + " M::N::S();\n" + " return 0;\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:7]: (style) Instance of 'M::N::S' object is destroyed immediately.\n", errout_str()); + + check("void f() {\n" // #10057 + " std::string(\"abc\");\n" + " std::string{ \"abc\" };\n" + " std::pair(1, 2);\n" + " (void)0;\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:2]: (style) Instance of 'std::string' object is destroyed immediately.\n" + "[test.cpp:3]: (style) Instance of 'std::string' object is destroyed immediately.\n" + "[test.cpp:4]: (style) Instance of 'std::pair' object is destroyed immediately.\n", + errout_str()); + + check("struct S {\n" // #10083 + " void f() {\n" + " std::lock_guard(m);\n" + " }\n" + " void g() {\n" + " std::scoped_lock(m);\n" + " }\n" + " void h() {\n" + " std::scoped_lock(m);\n" + " }\n" + " std::mutex m;\n" + "}\n", true); + ASSERT_EQUALS("[test.cpp:3]: (style) Instance of 'std::lock_guard' object is destroyed immediately.\n" + "[test.cpp:6]: (style) Instance of 'std::scoped_lock' object is destroyed immediately.\n" + "[test.cpp:9]: (style) Instance of 'std::scoped_lock' object is destroyed immediately.\n", + errout_str()); + + check("struct S { int i; };\n" + "namespace {\n" + " S s() { return ::S{42}; }\n" + "}\n", true); + ASSERT_EQUALS("", errout_str()); + } + + void testMisusedScopeObjectAssignment() { // #11371 + check("struct S;\n" + "S f();\n" + "S& g();\n" + "S&& h();\n" + "S* i();\n" + "void t0() { f() = {}; }\n" + "void t1() { g() = {}; }\n" + "void t2() { h() = {}; }\n" + "void t3() { *i() = {}; }\n", true); + ASSERT_EQUALS("[test.cpp:6]: (style) Instance of 'S' object is destroyed immediately, assignment has no effect.\n", errout_str()); + } + + void trac2084() { + check("void f()\n" + "{\n" + " struct sigaction sa;\n" + "\n" + " { sigaction(SIGHUP, &sa, 0); };\n" + " { sigaction(SIGINT, &sa, 0); };\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" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void clarifyCalculation() { + check("int f(char c) {\n" + " return 10 * (c == 0) ? 1 : 2;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '*' and '?'.\n", errout_str()); + + check("void f(char c) {\n" + " printf(\"%i\", 10 * (c == 0) ? 1 : 2);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '*' and '?'.\n", errout_str()); + + check("void f() {\n" + " return (2*a)?b:c;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(char c) {\n" + " printf(\"%i\", a + b ? 1 : 2);\n" + "}",true,false,false); + ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '+' and '?'.\n", errout_str()); + + check("void f() {\n" + " std::cout << x << y ? 2 : 3;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '<<' and '?'.\n", errout_str()); + + check("void f() {\n" + " int ab = a - b ? 2 : 3;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '-' and '?'.\n", errout_str()); + + check("void f() {\n" + " int ab = a | b ? 2 : 3;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '|' and '?'.\n", errout_str()); + + // ticket #195 + check("int f(int x, int y) {\n" + " return x >> ! y ? 8 : 2;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Clarify calculation precedence for '>>' and '?'.\n", errout_str()); + + check("int f() {\n" + " return shift < sizeof(int64_t)*8 ? 1 : 2;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() { a = *p ? 1 : 2; }"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) { const char *p = x & 1 ? \"1\" : \"0\"; }"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() { x = a % b ? \"1\" : \"0\"; }"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) { return x & 1 ? '1' : '0'; }"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) { return x & 16 ? 1 : 0; }"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) { return x % 16 ? 1 : 0; }"); + ASSERT_EQUALS("", errout_str()); + + check("enum {X,Y}; void f(int x) { return x & Y ? 1 : 0; }"); + ASSERT_EQUALS("", errout_str()); + } + + void clarifyStatement() { + check("char* f(char* c) {\n" + " *c++;\n" + " return c;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*', result is not used.\n" + "[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", + errout_str()); + + check("char* f(char** c) {\n" + " *c[5]--;\n" + " return *c;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*', result is not used.\n" + "[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", + errout_str()); + + check("void f(Foo f) {\n" + " *f.a++;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*', result is not used.\n" + "[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", + errout_str()); + + check("void f(Foo f) {\n" + " *f.a[5].v[3]++;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*', result is not used.\n" + "[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", + errout_str()); + + check("void f(Foo f) {\n" + " *f.a(1, 5).v[x + y]++;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*', result is not used.\n" + "[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", + errout_str()); + + check("char* f(char* c) {\n" + " (*c)++;\n" + " return c;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(char* c) {\n" + " bar(*c++);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("char*** f(char*** c) {\n" + " ***c++;\n" + " return c;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*', result is not used.\n" + "[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", + errout_str()); + + check("char** f(char*** c) {\n" + " **c[5]--;\n" + " return **c;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*', result is not used.\n" + "[test.cpp:2]: (warning) In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n", + errout_str()); + + check("char*** f(char*** c) {\n" + " (***c)++;\n" + " return c;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const int*** p) {\n" // #10923 + " delete[] **p;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void *f(char** c) {\n" + " bar(**c++);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void *f(char* p) {\n" + " for (p = path; *p++;) ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " std::array,3> array;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const std::vector& v) {\n" // #12088 + " for (auto it = v.begin(); it != v.end(); delete *it++);\n" + "}\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" + "}", true, false, false); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " if (b)\n" + " __asm__(\"mov ax, bx\");\n" + " else\n" + " __asm__(\"mov bx, bx\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); // #3407 + + check("void f() {\n" + " if (b)\n" + " __asm__(\"mov ax, bx\");\n" + " else\n" + " __asm__(\"mov ax, bx\");\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:2]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout_str()); + } + + void duplicateBranch1() { + + // tests inspired by http://www.viva64.com/en/b/0149/ ( Comparison between PVS-Studio and cppcheck ) + // Errors detected in Quake 3: Arena by PVS-Studio: Fragment 2 + check("void f()\n" + "{\n" + " if (front < 0)\n" + " frac = front/(front-back);\n" + " else\n" + " frac = front/(front-back);\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout_str()); + + check("void f()\n" + "{\n" + " if (front < 0)\n" + " { frac = front/(front-back);}\n" + " else\n" + " frac = front/((front-back));\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout_str()); + + // No message about empty branches (#5354) + check("void f()\n" + "{\n" + " if (front < 0)\n" + " {}\n" + " else\n" + " {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateBranch2() { + checkP("#define DOSTUFF1 ;\n" + "#define DOSTUFF2 ;\n" + "void f(int x) {\n" // #4329 + " if (x)\n" + " DOSTUFF1\n" + " else\n" + " DOSTUFF2\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateBranch3() { + check("void f(bool b, int i) {\n" + " int j = i;\n" + " if (b) {\n" + " x = i;\n" + " } else {\n" + " x = j;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5] -> [test.cpp:3]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n" + "[test.cpp:2]: (style) The scope of the variable 'j' can be reduced.\n", + errout_str()); + + check("void f(bool b, int i) {\n" + " int j = i;\n" + " i++;\n" + " if (b) {\n" + " x = i;\n" + " } else {\n" + " x = j;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateBranch4() { + check("void* f(bool b) {\n" + " if (b) {\n" + " return new A::Y(true);\n" + " } else {\n" + " return new A::Z(true);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateBranch5() { + check("void f(bool b) {\n" + " int j;\n" + " if (b) {\n" + " unsigned int i = 0;\n" + " j = i;\n" + " } else {\n" + " unsigned int i = 0;\n" + " j = i;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:3]: (style, inconclusive) Found duplicate branches for 'if' and 'else'.\n", errout_str()); + + check("void f(bool b) {\n" + " int j;\n" + " if (b) {\n" + " unsigned int i = 0;\n" + " j = i;\n" + " } else {\n" + " unsigned int i = 0;\n" + " j = 1;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool b) {\n" + " int j;\n" + " if (b) {\n" + " unsigned int i = 0;\n" + " } else {\n" + " int i = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool b) {\n" + " int j;\n" + " if (b) {\n" + " unsigned int i = 0;\n" + " j = i;\n" + " } else {\n" + " int i = 0;\n" + " j = i;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateBranch6() { + check("void f(bool b) {\n" + " if (b) {\n" + " } else {\n" + " int i = 0;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool b) {\n" + " if (b) {\n" + " int i = 0;\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]: (style) Same expression on both sides of '=='.\n", errout_str()); + + check("void fun(int b) {\n" + " return a && a ||\n" + " b == b &&\n" + " d > d &&\n" + " e < e &&\n" + " f ;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&'.\n" + "[test.cpp:3]: (style) Same expression on both sides of '=='.\n" + "[test.cpp:4]: (style) Same expression on both sides of '>'.\n" + "[test.cpp:5]: (style) Same expression on both sides of '<'.\n", errout_str()); + + check("void foo() {\n" + " return a && a;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&'.\n", errout_str()); + + check("void foo() {\n" + " a = b && b;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&'.\n", errout_str()); + + check("void foo(int b) {\n" + " f(a,b == b);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout_str()); + + check("void foo(int b) {\n" + " f(b == b, a);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout_str()); + + check("void foo() {\n" + " if (x!=2 || x!=2) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout_str()); + + check("void foo(int a, int b) {\n" + " if ((a < b) && (b > a)) { }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&' because 'aa' represent the same value.\n", errout_str()); + + check("void foo(int a, int b) {\n" + " if ((a <= b) && (b >= a)) { }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&' because 'a<=b' and 'b>=a' represent the same value.\n", errout_str()); + + check("void foo() {\n" + " if (x!=2 || y!=3 || x!=2) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression 'x!=2' found multiple times in chain of '||' operators.\n", errout_str()); + + check("void foo() {\n" + " if (x!=2 && (x=y) && x!=2) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " if (a && b || a && b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout_str()); + + check("void foo() {\n" + " if (a && b || b && c) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " if (a && b | b && c) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '|'.\n", errout_str()); + + check("void foo() {\n" + " if ((a + b) | (a + b)) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '|'.\n", errout_str()); + + check("void foo() {\n" + " if ((a | b) & (a | b)) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&'.\n", errout_str()); + + check("void foo(int a, int b) {\n" + " if ((a | b) == (a | b)) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout_str()); + + check("void foo() {\n" + " if (a1[a2[c & 0xff] & 0xff]) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void d(const char f, int o, int v)\n" + "{\n" + " if (((f=='R') && (o == 1) && ((v < 2) || (v > 99))) ||\n" + " ((f=='R') && (o == 2) && ((v < 2) || (v > 99))) ||\n" + " ((f=='T') && (o == 2) && ((v < 200) || (v > 9999)))) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int x) { return x+x; }"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) { while (x+=x) ; }"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " if (a && b && b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&'.\n", errout_str()); + + check("void foo() {\n" + " if (a || b || b) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout_str()); + + check("void foo() {\n" + " if (a / 1000 / 1000) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int foo(int i) {\n" + " return i/i;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '/'.\n", errout_str()); + + check("void foo() {\n" + " if (a << 1 << 1) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f() { return !!y; }"); // No FP + ASSERT_EQUALS("", errout_str()); + + // make sure there are not "same expression" fp when there are different casts + check("void f(long x) { if ((int32_t)x == (int64_t)x) {} }", + true, // filename + false, // inconclusive + false, // runSimpleChecks + false, // verbose + nullptr // settings + ); + ASSERT_EQUALS("", errout_str()); + + // make sure there are not "same expression" fp when there are different ({}) expressions + check("void f(long x) { if (({ 1+2; }) == ({3+4;})) {} }"); + ASSERT_EQUALS("", errout_str()); + + // #5535: Reference named like its type + check("void foo() { UMSConfig& UMSConfig = GetUMSConfiguration(); }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Variable 'UMSConfig' can be declared as reference to const\n", errout_str()); + + // #3868 - false positive (same expression on both sides of |) + check("void f(int x) {\n" + " a = x ? A | B | C\n" + " : A | B;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const Bar &bar) {\n" + " bool a = bar.isSet() && bar->isSet();\n" + " bool b = bar.isSet() && bar.isSet();\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Same expression on both sides of '&&'.\n", errout_str()); + + + check("void foo(int a, int b) {\n" + " if ((b + a) | (a + b)) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '|' because 'b+a' and 'a+b' represent the same value.\n", errout_str()); + + check("void foo(const std::string& a, const std::string& b) {\n" + " return a.find(b+\"&\") || a.find(\"&\"+b);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int a, int b) {\n" + " if ((b > a) | (a > b)) {}\n" // > is not commutative + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(double a, double b) {\n" + " if ((b + a) > (a + b)) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) The comparison 'b+a > a+b' is always false because 'b+a' and 'a+b' represent the same value.\n", errout_str()); + + check("void f(int x) {\n" + " if ((x == 1) && (x == 0x00000001))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '&&' because 'x==1' and 'x==0x00000001' represent the same value.\n", errout_str()); + + check("void f() {\n" + " enum { Four = 4 };\n" + " if (Four == 4) {}" + "}", true, true, false); + ASSERT_EQUALS("[test.cpp:3]: (style) The comparison 'Four == 4' is always true.\n", + errout_str()); + + check("void f() {\n" + " enum { Four = 4 };\n" + " static_assert(Four == 4, \"\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " enum { Four = 4 };\n" + " static_assert(4 == Four, \"\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " enum { FourInEnumOne = 4 };\n" + " enum { FourInEnumTwo = 4 };\n" + " if (FourInEnumOne == FourInEnumTwo) {}\n" + "}", true, true, false); + ASSERT_EQUALS("[test.cpp:4]: (style) The comparison 'FourInEnumOne == FourInEnumTwo' is always true because 'FourInEnumOne' and 'FourInEnumTwo' represent the same value.\n", + errout_str()); + + check("void f() {\n" + " enum { FourInEnumOne = 4 };\n" + " enum { FourInEnumTwo = 4 };\n" + " static_assert(FourInEnumOne == FourInEnumTwo, \"\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int a, int b) {\n" + " if (sizeof(a) == sizeof(a)) { }\n" + " if (sizeof(a) == sizeof(b)) { }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout_str()); + + check("float bar(int) __attribute__((pure));\n" + "char foo(int) __attribute__((pure));\n" + "int test(int a, int b) {\n" + " if (bar(a) == bar(a)) { }\n" + " if (unknown(a) == unknown(a)) { }\n" + " if (foo(a) == foo(a)) { }\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (style) Same expression on both sides of '=='.\n", errout_str()); + } + + void duplicateExpression2() { // check if float is NaN or Inf + check("int f(long double ldbl, double dbl, float flt) {\n" // ticket #2730 + " if (ldbl != ldbl) have_nan = 1;\n" + " if (!(dbl == dbl)) have_nan = 1;\n" + " if (flt != flt) have_nan = 1;\n" + " return have_nan;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("float f(float x) { return x-x; }"); // ticket #4485 (Inf) + ASSERT_EQUALS("", errout_str()); + + check("float f(float x) { return (X double)x == (X double)x; }", true, false, false); + ASSERT_EQUALS("", errout_str()); + + check("struct X { float f; };\n" + "float f(struct X x) { return x.f == x.f; }"); + ASSERT_EQUALS("", errout_str()); + + check("struct X { int i; };\n" + "int f(struct X x) { return x.i == x.i; }"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '=='.\n", errout_str()); + + // #5284 - when type is unknown, assume it's float + check("int f() { return x==x; }"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateExpression3() { + constexpr char xmldata[] = "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + ""; + /*const*/ Settings settings = settingsBuilder().libraryxml(xmldata, sizeof(xmldata)).build(); + + check("void foo() {\n" + " if (x() || x()) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {\n" + " void foo() const;\n" + " bool bar() const;\n" + "};\n" + "void A::foo() const {\n" + " if (bar() && bar()) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (style) Same expression on both sides of '&&'.\n", errout_str()); + + check("struct A {\n" + " void foo();\n" + " bool bar();\n" + " bool bar() const;\n" + "};\n" + "void A::foo() {\n" + " if (bar() && bar()) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class B {\n" + " void bar(int i);\n" + "};\n" + "class A {\n" + " void bar(int i) const;\n" + "};\n" + "void foo() {\n" + " B b;\n" + " A a;\n" + " if (b.bar(1) && b.bar(1)) {}\n" + " if (a.bar(1) && a.bar(1)) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:11]: (style) Same expression on both sides of '&&'.\n", errout_str()); + + check("class D { void strcmp(); };\n" + "void foo() {\n" + " D d;\n" + " if (d.strcmp() && d.strcmp()) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " if ((mystrcmp(a, b) == 0) || (mystrcmp(a, b) == 0)) {}\n" + "}", true, false, true, false, &settings); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout_str()); + + check("void GetValue() { return rand(); }\n" + "void foo() {\n" + " if ((GetValue() == 0) || (GetValue() == 0)) { dostuff(); }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void __attribute__((const)) GetValue() { return X; }\n" + "void foo() {\n" + " if ((GetValue() == 0) || (GetValue() == 0)) { dostuff(); }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Same expression on both sides of '||'.\n", errout_str()); + + check("void GetValue() __attribute__((const));\n" + "void GetValue() { return X; }\n" + "void foo() {\n" + " if ((GetValue() == 0) || (GetValue() == 0)) { dostuff(); }\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Same expression on both sides of '||'.\n", errout_str()); + + check("void foo() {\n" + " if (str == \"(\" || str == \"(\") {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout_str()); + + check("void foo() {\n" + " if (bar(a) && !strcmp(a, b) && bar(a) && !strcmp(a, b)) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #5334 + check("void f(C *src) {\n" + " if (x(src) || x(src))\n" + " a++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(A *src) {\n" + " if (dynamic_cast(src) || dynamic_cast(src)) {}\n" + "}\n", true, false, false); // don't run simplifications + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||'.\n", errout_str()); + + // #5819 + check("Vector func(Vector vec1) {\n" + " return fabs(vec1 & vec1 & vec1);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("Vector func(int vec1) {\n" + " return fabs(vec1 & vec1 & vec1);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (style) Same expression on both sides of '&'.\n" + "[test.cpp:2]: (style) Same expression on both sides of '&'.\n", // duplicate + errout_str()); + + } + + void duplicateExpression4() { + check("void foo() {\n" + " if (*a++ != b || *a++ != b) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " if (*a-- != b || *a-- != b) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // assignment + check("void f() {\n" + " while (*(a+=2)==*(b+=2) && *(a+=2)==*(b+=2)) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateExpression5() { // #3749 - macros with same values + check("void f() {\n" + " if ($a == $a) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateExpression6() { // #4639 + check("float IsNan(float value) { return !(value == value); }\n" + "double IsNan(double value) { return !(value == value); }\n" + "long double IsNan(long double value) { return !(value == value); }"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateExpression7() { + check("void f() {\n" + " const int i = sizeof(int);\n" + " if ( i != sizeof (int)){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'i != sizeof(int)' is always false because 'i' and 'sizeof(int)' represent the same value.\n", errout_str()); + + check("void f() {\n" + " const int i = sizeof(int);\n" + " if ( sizeof (int) != i){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'sizeof(int) != i' is always false because 'sizeof(int)' and 'i' represent the same value.\n", errout_str()); + + check("void f(int a = 1) { if ( a != 1){}}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int a = 1;\n" + " if ( a != 1){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'a != 1' is always false.\n", errout_str()); + + check("void f() {\n" + " int a = 1;\n" + " int b = 1;\n" + " if ( a != b){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:4]: (style) The comparison 'a != b' is always false because 'a' and 'b' represent the same value.\n", errout_str()); + + check("void f() {\n" + " int a = 1;\n" + " int b = a;\n" + " if ( a != b){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) The comparison 'a != b' is always false because 'a' and 'b' represent the same value.\n", errout_str()); + + check("void use(int);\n" + "void f() {\n" + " int a = 1;\n" + " int b = 1;\n" + " use(b);\n" + " if ( a != 1){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (style) The comparison 'a != 1' is always false.\n", errout_str()); + + check("void use(int);\n" + "void f() {\n" + " int a = 1;\n" + " use(a);\n" + " a = 2;\n" + " if ( a != 1){}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void use(int);\n" + "void f() {\n" + " int a = 2;\n" + " use(a);\n" + " a = 1;\n" + " if ( a != 1){}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("const int a = 1;\n" + "void f() {\n" + " if ( a != 1){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:3]: (style) The comparison 'a != 1' is always false.\n", errout_str()); + + check("int a = 1;\n" + " void f() {\n" + " if ( a != 1){}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " static const int a = 1;\n" + " if ( a != 1){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'a != 1' is always false.\n", errout_str()); + + check("void f() {\n" + " static int a = 1;\n" + " if ( a != 1){}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int a = 1;\n" + " if ( a != 1){\n" + " a++;\n" + " }}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'a != 1' is always false.\n", errout_str()); + + check("void f(int b) {\n" + " int a = 1;\n" + " while (b) {\n" + " if ( a != 1){}\n" + " a++;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(bool a, bool b) {\n" + " const bool c = a;\n" + " return a && b && c;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression 'a' found multiple times in chain of '&&' operators because 'a' and 'c' represent the same value.\n", + errout_str()); + + // 6906 + check("void f(const bool b) {\n" + " const bool b1 = !b;\n" + " if(!b && b1){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression on both sides of '&&' because '!b' and 'b1' represent the same value.\n", errout_str()); + + // 7284 + check("void f(void) {\n" + " if (a || !!a) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||' because 'a' and '!!a' represent the same value.\n", errout_str()); + + // 8205 + check("void f(int x) {\n" + " int Diag = 0;\n" + " switch (x) {\n" + " case 12:\n" + " if (Diag==0) {}\n" + " break;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:5]: (style) The comparison 'Diag == 0' is always true.\n", errout_str()); + + // #9744 + check("void f(const std::vector& ints) {\n" + " int i = 0;\n" + " for (int p = 0; i < ints.size(); ++i) {\n" + " if (p == 0) {}\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) The comparison 'p == 0' is always true.\n", errout_str()); + + // #11820 + check("unsigned f(unsigned x) {\n" + " return x - !!x;\n" + "}\n" + "unsigned g(unsigned x) {\n" + " return !!x - x;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateExpression8() { + check("void f() {\n" + " int a = 1;\n" + " int b = a;\n" + " a = 2;\n" + " if ( b != a){}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int * a, int i) { int b = a[i]; a[i] = 2; if ( b != a[i]){}}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int * a, int i) { int b = *a; *a = 2; if ( b != *a){}}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int f() const; };\n" + "A g();\n" + "void foo() {\n" + " for (A x = A();;) {\n" + " const int a = x.f();\n" + " x = g();\n" + " if (x.f() == a) break;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int i);\n" + "struct A {\n" + " enum E { B, C };\n" + " bool f(E);\n" + "};\n" + "void foo() {\n" + " A a;\n" + " const bool x = a.f(A::B);\n" + " const bool y = a.f(A::C);\n" + " if(!x && !y) return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " const bool x = a.f(A::B);\n" + " const bool y = a.f(A::C);\n" + " if (!x && !y) return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool * const b);\n" + "void foo() {\n" + " bool x = true;\n" + " bool y = true;\n" + " f(&x);\n" + " if (!x && !y) return;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " const int a = {};\n" + " if(a == 1) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("volatile const int var = 42;\n" + "void f() { if(var == 42) {} }"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int a = 0;\n" + " struct b c;\n" + " c.a = &a;\n" + " g(&c);\n" + " if (a == 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateExpression9() { + // #9320 + check("void f() {\n" + " uint16_t x = 1000;\n" + " uint8_t y = x;\n" + " if (x != y) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateExpression10() { + // #9485 + check("int f() {\n" + " const int a = 1;\n" + " const int b = a-1;\n" + " const int c = a+1;\n" + " return c;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateExpression11() { + check("class Fred {\n" + "public:\n" + " double getScale() const { return m_range * m_zoom; }\n" + " void setZoom(double z) { m_zoom = z; }\n" + " void dostuff(int);\n" + "private:\n" + " double m_zoom;\n" + " double m_range;\n" + "};\n" + "\n" + "void Fred::dostuff(int x) {\n" + " if (x == 43) {\n" + " double old_scale = getScale();\n" + " setZoom(m_zoom + 1);\n" + " double scale_ratio = getScale() / old_scale;\n" // <- FP + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateExpression12() { //#10026 + check("int f(const std::vector &buffer, const uint8_t index)\n" + "{\n" + " int var = buffer[index - 1];\n" + " return buffer[index - 1] - var;\n" // << + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) Same expression on both sides of '-' because 'buffer[index-1]' and 'var' represent the same value.\n", errout_str()); + } + + void duplicateExpression13() { //#7899 + check("void f() {\n" + " if (sizeof(long) == sizeof(long long)) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateExpression14() { //#9871 + check("int f() {\n" + " int k = 7;\n" + " int* f = &k;\n" + " int* g = &k;\n" + " return (f + 4 != g + 4);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4] -> [test.cpp:5]: (style) The comparison 'f+4 != g+4' is always false because 'f+4' and 'g+4' represent the same value.\n", errout_str()); + } + + void duplicateExpression15() { //#10650 + check("bool f() {\n" + " const int i = int(0);\n" + " return i == 0;\n" + "}\n" + "bool g() {\n" + " const int i = int{ 0 };\n" + " return i == 0;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'i == 0' is always true.\n" + "[test.cpp:6] -> [test.cpp:7]: (style) The comparison 'i == 0' is always true.\n", + errout_str()); + } + + void duplicateExpression16() { + check("void f(const std::string& a) {\n" //#10569 + " if ((a == \"x\") ||\n" + " (a == \"42\") ||\n" + " (a == \"y\") ||\n" + " (a == \"42\")) {}\n" + "}\n" + "void g(const std::string& a) {\n" + " if ((a == \"42\") ||\n" + " (a == \"x\") ||\n" + " (a == \"42\") ||\n" + " (a == \"y\")) {}\n" + "}\n" + "void h(const std::string& a) {\n" + " if ((a == \"42\") ||\n" + " (a == \"x\") ||\n" + " (a == \"y\") ||\n" + " (a == \"42\")) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:4]: (style) Same expression 'a==\"42\"' found multiple times in chain of '||' operators.\n" + "[test.cpp:7] -> [test.cpp:9]: (style) Same expression 'a==\"42\"' found multiple times in chain of '||' operators.\n" + "[test.cpp:13] -> [test.cpp:16]: (style) Same expression 'a==\"42\"' found multiple times in chain of '||' operators.\n", + errout_str()); + + check("void f(const char* s) {\n" // #6371 + " if (*s == '\x0F') {\n" + " if (!s[1] || !s[2] || !s[1])\n" + " break;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Same expression '!s[1]' found multiple times in chain of '||' operators.\n", errout_str()); + } + + void duplicateExpression17() { + check("enum { E0 };\n" // #12036 + "void f() {\n" + " if (0 > E0) {}\n" + " if (E0 > 0) {}\n" + " if (E0 == 0) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) The comparison '0 > E0' is always false.\n" + "[test.cpp:4]: (style) The comparison 'E0 > 0' is always false.\n" + "[test.cpp:5]: (style) The comparison 'E0 == 0' is always true.\n", + errout_str()); + + check("struct S {\n" // #12040, #12044 + " static const int I = 0;\n" + " enum { E0 };\n" + " enum F { F0 };\n" + " void f() {\n" + " if (0 > I) {}\n" + " if (0 > S::I) {}\n" + " if (0 > E0) {}\n" + " if (0 > S::E0) {}\n" + " }\n" + "};\n" + "void g() {\n" + " if (0 > S::I) {}\n" + " if (0 > S::E0) {}\n" + " if (0 > S::F::F0) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:6]: (style) The comparison '0 > I' is always false.\n" + "[test.cpp:2] -> [test.cpp:7]: (style) The comparison '0 > S::I' is always false.\n" + "[test.cpp:8]: (style) The comparison '0 > E0' is always false.\n" + "[test.cpp:9]: (style) The comparison '0 > S::E0' is always false.\n" + "[test.cpp:2] -> [test.cpp:13]: (style) The comparison '0 > S::I' is always false.\n" + "[test.cpp:14]: (style) The comparison '0 > S::E0' is always false.\n" + "[test.cpp:15]: (style) The comparison '0 > S::F::F0' is always false.\n", + errout_str()); + + check("template\n" // #12122 + "void f() {\n" + " static_assert(std::is_same::value || std::is_integral::value);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateExpressionLoop() { + check("void f() {\n" + " int a = 1;\n" + " while ( a != 1){}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'a != 1' is always false.\n", errout_str()); + + check("void f() { int a = 1; while ( a != 1){ a++; }}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() { int a = 1; for ( int i=0; i < 3 && a != 1; i++){ a++; }}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int b) { int a = 1; while (b) { if ( a != 1){} b++; } a++; }"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " for(int i = 0; i < 10;) {\n" + " if( i != 0 ) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'i != 0' is always false.\n", errout_str()); + + check("void f() {\n" + " for(int i = 0; i < 10;) {\n" + " if( i != 0 ) {}\n" + " i++;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " for(int i = 0; i < 10;) {\n" + " if( i != 0 ) { i++; }\n" + " i++;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " for(int i = 0; i < 10;) {\n" + " if( i != 0 ) { i++; }\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int i = 0;\n" + " while(i < 10) {\n" + " if( i != 0 ) {}\n" + " i++;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int b) {\n" + " while (b) {\n" + " int a = 1;\n" + " if ( a != 1){}\n" + " b++;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style) The comparison 'a != 1' is always false.\n", errout_str()); + + check("struct T {\n" // #11083 + " std::string m;\n" + " const std::string & str() const { return m; }\n" + " T* next();\n" + "};\n" + "void f(T* t) {\n" + " const std::string& s = t->str();\n" + " while (t && t->str() == s)\n" + " t = t->next();\n" + " do {\n" + " t = t->next();\n" + " } while (t && t->str() == s);\n" + " for (; t && t->str() == s; t = t->next());\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateExpressionTernary() { // #6391 + check("void f() {\n" + " return A ? x : x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression in both branches of ternary operator.\n", errout_str()); + + check("int f(bool b, int a) {\n" + " const int c = a;\n" + " return b ? a : c;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Same expression in both branches of ternary operator.\n", errout_str()); + + check("void f() {\n" + " return A ? x : z;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(unsigned char c) {\n" + " x = y ? (signed char)c : (unsigned char)c;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("std::string stringMerge(std::string const& x, std::string const& y) {\n" // #7938 + " return ((x > y) ? (y + x) : (x + y));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #6426 + { + const char code[] = "void foo(bool flag) {\n" + " bar( (flag) ? ~0u : ~0ul);\n" + "}"; + /*const*/ Settings settings = _settings; + settings.platform.sizeof_int = 4; + settings.platform.int_bit = 32; + + settings.platform.sizeof_long = 4; + settings.platform.long_bit = 32; + check(code, &settings); + ASSERT_EQUALS("[test.cpp:2]: (style) Same value in both branches of ternary operator.\n", errout_str()); + + settings.platform.sizeof_long = 8; + settings.platform.long_bit = 64; + check(code, &settings); + ASSERT_EQUALS("", errout_str()); + } + } + + void duplicateValueTernary() { + check("void f() {\n" + " if( a ? (b ? false:false): false ) ;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same value in both branches of ternary operator.\n", errout_str()); + + check("int f1(int a) {return (a == 1) ? (int)1 : 1; }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout_str()); + + check("int f2(int a) {return (a == 1) ? (int)1 : (int)1; }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout_str()); + + check("int f3(int a) {return (a == 1) ? 1 : (int)1; }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout_str()); + + check("int f4(int a) {return (a == 1) ? 1 : 1; }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout_str()); + + check("int f5(int a) {return (a == (int)1) ? (int)1 : 1; }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout_str()); + + check("int f6(int a) {return (a == (int)1) ? (int)1 : (int)1; }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout_str()); + + check("int f7(int a) {return (a == (int)1) ? 1 : (int)1; }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout_str()); + + check("int f8(int a) {return (a == (int)1) ? 1 : 1; }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Same value in both branches of ternary operator.\n", errout_str()); + + check("struct Foo {\n" + " std::vector bar{1,2,3};\n" + " std::vector baz{4,5,6};\n" + "};\n" + "void f() {\n" + " Foo foo;\n" + " it = true ? foo.bar.begin() : foo.baz.begin();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool b) {\n" + " std::vector bar{1,2,3};\n" + " std::vector baz{4,5,6};\n" + " std::vector v = b ? bar : baz;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool q) {\n" // #9570 + " static int a = 0;\n" + " static int b = 0;\n" + " int& x = q ? a : b;\n" + " ++x;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { int a, b; };\n" // #10107 + "S f(bool x, S s) {\n" + " (x) ? f.a = 42 : f.b = 42;\n" + " return f;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("float f(float x) {\n" // # 11368 + " return (x >= 0.0) ? 0.0 : -0.0;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateExpressionTemplate() { + check("template void f() {\n" // #6930 + " if (I >= 0 && I < 3) {}\n" + "}\n" + "\n" + "static auto a = f<0>();"); + ASSERT_EQUALS("", errout_str()); + + check("template\n" // #7754 + "void f() {\n" + " if (std::is_same_v || std::is_same_v) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("typedef long long int64_t;" + "template\n" + "void f() {\n" + " if (std::is_same_v || std::is_same_v) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + checkP("#define int32_t int" + "template\n" + "void f() {\n" + " if (std::is_same_v || std::is_same_v) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + checkP("#define F(v) (v) != 0\n" // #12392 + "template\n" + "void f() {\n" + " if (F(0)) {}\n" + "}\n" + "void g() {\n" + " f();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateExpressionCompareWithZero() { + check("void f(const int* x, bool b) {\n" + " if ((x && b) || (x != 0 && b)) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||' because 'x&&b' and 'x!=0&&b' represent the same value.\n", errout_str()); + + check("void f(const int* x, bool b) {\n" + " if ((x != 0 && b) || (x && b)) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||' because 'x!=0&&b' and 'x&&b' represent the same value.\n", errout_str()); + + check("void f(const int* x, bool b) {\n" + " if ((x && b) || (b && x != 0)) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||' because 'x&&b' and 'b&&x!=0' represent the same value.\n", errout_str()); + + check("void f(const int* x, bool b) {\n" + " if ((!x && b) || (x == 0 && b)) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||' because '!x&&b' and 'x==0&&b' represent the same value.\n", errout_str()); + + check("void f(const int* x, bool b) {\n" + " if ((x == 0 && b) || (!x && b)) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||' because 'x==0&&b' and '!x&&b' represent the same value.\n", errout_str()); + + check("void f(const int* x, bool b) {\n" + " if ((!x && b) || (b && x == 0)) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2]: (style) Same expression on both sides of '||' because '!x&&b' and 'b&&x==0' represent the same value.\n", errout_str()); + + check("struct A {\n" + " int* getX() const;\n" + " bool getB() const;\n" + " void f() {\n" + " if ((getX() && getB()) || (getX() != 0 && getB())) {}\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:5]: (style) Same expression on both sides of '||' because 'getX()&&getB()' and 'getX()!=0&&getB()' represent the same value.\n", errout_str()); + + check("void f(const int* x, bool b) {\n" + " if ((x && b) || (x == 0 && b)) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const int* x, bool b) {\n" + " if ((!x && b) || (x != 0 && b)) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void oppositeExpression() { + check("void f(bool a) { if(a && !a) {} }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '&&'.\n", errout_str()); + + check("void f(bool a) { if(a != !a) {} }"); + ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '!='.\n", errout_str()); + + check("void f(bool a) { if( a == !(a) ) {}}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '=='.\n", errout_str()); + + check("void f(bool a) { if( a != !(a) ) {}}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '!='.\n", errout_str()); + + check("void f(bool a) { if( !(a) == a ) {}}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '=='.\n", errout_str()); + + check("void f(bool a) { if( !(a) != a ) {}}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '!='.\n", errout_str()); + + check("void f(bool a) { if( !(!a) == !(a) ) {}}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '=='.\n", errout_str()); + + check("void f(bool a) { if( !(!a) != !(a) ) {}}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '!='.\n", errout_str()); + + check("void f1(bool a) {\n" + " const bool b = a;\n" + " if( a == !(b) ) {}\n" + " if( b == !(a) ) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Opposite expression on both sides of '=='.\n" + "[test.cpp:2] -> [test.cpp:4]: (style) Opposite expression on both sides of '=='.\n", errout_str()); + + check("void f2(const bool *a) {\n" + " const bool b = *a;\n" + " if( *a == !(b) ) {}\n" + " if( b == !(*a) ) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Opposite expression on both sides of '=='.\n" + "[test.cpp:2] -> [test.cpp:4]: (style) Opposite expression on both sides of '=='.\n", errout_str()); + + check("void f(bool a) { a = !a; }"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int a) { if( a < -a ) {}}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Opposite expression on both sides of '<'.\n", errout_str()); + + check("void f(int a) { a -= -a; }"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int a) { a = a / (-a); }"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(int i){ return !((i - 1) & i); }"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(unsigned i){ return (x > 0) && (x & (x-1)) == 0; }"); + ASSERT_EQUALS("", errout_str()); + + check("void A::f(bool a, bool c)\n" + "{\n" + " const bool b = a;\n" + " if(c) { a = false; }\n" + " if(b && !a) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(bool c) {\n" + " const bool b = a;\n" + " if(c) { a = false; }\n" + " if(b && !a) { }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " bool x = a;\n" + " dostuff();\n" + " if (x && a) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " const bool b = g();\n" + " if (!b && g()) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const bool *a) {\n" + " const bool b = a[42];\n" + " if( b == !(a[42]) ) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Opposite expression on both sides of '=='.\n", errout_str()); + + check("void f(const bool *a) {\n" + " const bool b = a[42];\n" + " if( a[42] == !(b) ) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Opposite expression on both sides of '=='.\n", errout_str()); + + check("void f(const bool *a) {\n" + " const bool b = *a;\n" + " if( b == !(*a) ) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Opposite expression on both sides of '=='.\n", errout_str()); + + check("void f(const bool *a) {\n" + " const bool b = *a;\n" + " if( *a == !(b) ) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Opposite expression on both sides of '=='.\n", errout_str()); + + check("void f(uint16_t u) {\n" // #9342 + " if (u != (u & -u))\n" + " return false;\n" + " if (u != (-u & u))\n" + " return false;\n" + " return true;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateVarExpression() { + check("int f() __attribute__((pure));\n" + "int g() __attribute__((pure));\n" + "void test() {\n" + " int i = f();\n" + " int j = f();\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout_str()); + + check("struct Foo { int f() const; int g() const; };\n" + "void test() {\n" + " Foo f = Foo{};\n" + " int i = f.f();\n" + " int j = f.f();\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout_str()); + + check("struct Foo { int f() const; int g() const; };\n" + "void test() {\n" + " Foo f = Foo{};\n" + " Foo f2 = Foo{};\n" + " int i = f.f();\n" + " int j = f.f();\n" + "}"); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:5]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout_str()); + + check("int f() __attribute__((pure));\n" + "int g() __attribute__((pure));\n" + "void test() {\n" + " int i = 1 + f();\n" + " int j = 1 + f();\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout_str()); + + check("int f() __attribute__((pure));\n" + "int g() __attribute__((pure));\n" + "void test() {\n" + " int i = f() + 1;\n" + " int j = 1 + f();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f() __attribute__((pure));\n" + "int g() __attribute__((pure));\n" + "void test() {\n" + " int x = f();\n" + " int i = x + 1;\n" + " int j = f() + 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f() __attribute__((pure));\n" + "int g() __attribute__((pure));\n" + "void test() {\n" + " int i = f() + f();\n" + " int j = f() + f();\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout_str()); + + check("int f(int) __attribute__((pure));\n" + "int g(int) __attribute__((pure));\n" + "void test() {\n" + " int i = f(0);\n" + " int j = f(0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout_str()); + + check("int f(int) __attribute__((pure));\n" + "int g(int) __attribute__((pure));\n" + "void test() {\n" + " const int x = 0;\n" + " int i = f(0);\n" + " int j = f(x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void test(const int * p, const int * q) {\n" + " int i = *p;\n" + " int j = *p;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout_str()); + + check("struct A { int x; int y; };" + "void test(A a) {\n" + " int i = a.x;\n" + " int j = a.x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (style) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout_str()); + + check("void test() {\n" + " int i = 0;\n" + " int j = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void test() {\n" + " int i = -1;\n" + " int j = -1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f(int);\n" + "void test() {\n" + " int i = f(0);\n" + " int j = f(1);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int f();\n" + "int g();\n" + "void test() {\n" + " int i = f() || f();\n" + " int j = f() && f();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct Foo {};\n" + "void test() {\n" + " Foo i = Foo();\n" + " Foo j = Foo();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct Foo {};\n" + "void test() {\n" + " Foo i = Foo{};\n" + " Foo j = Foo{};\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct Foo { int f() const; float g() const; };\n" + "void test() {\n" + " Foo f = Foo{};\n" + " int i = f.f();\n" + " int j = f.f();\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout_str()); + + check("struct Foo { int f(); int g(); };\n" + "void test() {\n" + " Foo f = Foo{};\n" + " int i = f.f();\n" + " int j = f.f();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void test() {\n" + " int i = f();\n" + " int j = f();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void test(int x) {\n" + " int i = ++x;\n" + " int j = ++x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void test(int x) {\n" + " int i = x++;\n" + " int j = x++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void test(int x) {\n" + " int i = --x;\n" + " int j = --x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void test(int x) {\n" + " int i = x--;\n" + " int j = x--;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void test(int x) {\n" + " int i = x + 1;\n" + " int j = 1 + x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void duplicateVarExpressionUnique() { + check("struct SW { int first; };\n" + "void foo(SW* x) {\n" + " int start = x->first;\n" + " int end = x->first;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'start' and 'end'.\n" + "[test.cpp:2]: (style) Parameter 'x' can be declared as pointer to const\n", + errout_str()); + + check("struct SW { int first; };\n" + "void foo(SW* x, int i, int j) {\n" + " int start = x->first;\n" + " int end = x->first;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'start' and 'end'.\n" + "[test.cpp:2]: (style) Parameter 'x' can be declared as pointer to const\n", + errout_str()); + + check("struct Foo { int f() const; };\n" + "void test() {\n" + " Foo f = Foo{};\n" + " int i = f.f();\n" + " int j = f.f();\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout_str()); + + check("void test(const int * p) {\n" + " int i = *p;\n" + " int j = *p;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout_str()); + + check("struct Foo { int f() const; int g(int) const; };\n" + "void test() {\n" + " Foo f = Foo{};\n" + " int i = f.f();\n" + " int j = f.f();\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout_str()); + + check("struct Foo { int f() const; };\n" + "void test() {\n" + " Foo f = Foo{};\n" + " int i = f.f();\n" + " int j = f.f();\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:4]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout_str()); + } + + void duplicateVarExpressionAssign() { + check("struct A { int x; int y; };" + "void use(int);\n" + "void test(A a) {\n" + " int i = a.x;\n" + " int j = a.x;\n" + " use(i);\n" + " i = j;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout_str()); + + check("struct A { int x; int y; };" + "void use(int);\n" + "void test(A a) {\n" + " int i = a.x;\n" + " int j = a.x;\n" + " use(j);\n" + " j = i;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n", errout_str()); + + check("struct A { int x; int y; };" + "void use(int);\n" + "void test(A a) {\n" + " int i = a.x;\n" + " int j = a.x;\n" + " use(j);\n" + " if (i == j) {}\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n" + "[test.cpp:3] -> [test.cpp:4] -> [test.cpp:6]: (style) The comparison 'i == j' is always true because 'i' and 'j' represent the same value.\n", + errout_str()); + + check("struct A { int x; int y; };" + "void use(int);\n" + "void test(A a) {\n" + " int i = a.x;\n" + " int j = a.x;\n" + " use(j);\n" + " if (i == a.x) {}\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n" + "[test.cpp:3] -> [test.cpp:6]: (style) The comparison 'i == a.x' is always true because 'i' and 'a.x' represent the same value.\n", + errout_str()); + + check("struct A { int x; int y; };" + "void use(int);\n" + "void test(A a) {\n" + " int i = a.x;\n" + " int j = a.x;\n" + " use(i);\n" + " if (j == a.x) {}\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:4] -> [test.cpp:3]: (style, inconclusive) Same expression used in consecutive assignments of 'i' and 'j'.\n" + "[test.cpp:4] -> [test.cpp:6]: (style) The comparison 'j == a.x' is always true because 'j' and 'a.x' represent the same value.\n", + errout_str()); + + // Issue #8612 + check("struct P\n" + "{\n" + " void func();\n" + " bool operator==(const P&) const;\n" + "};\n" + "struct X\n" + "{\n" + " P first;\n" + " P second;\n" + "};\n" + "bool bar();\n" + "void baz(const P&);\n" + "void foo(const X& x)\n" + "{\n" + " P current = x.first;\n" + " P previous = x.first;\n" + " while (true)\n" + " {\n" + " baz(current);\n" + " if (bar() && previous == current)\n" + " {\n" + " current.func();\n" + " }\n" + " previous = current;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:16] -> [test.cpp:15]: (style, inconclusive) Same expression used in consecutive assignments of 'current' and 'previous'.\n", errout_str()); + } + + void duplicateVarExpressionCrash() { + // Issue #8624 + check("struct X {\n" + " X();\n" + " int f() const;\n" + "};\n" + "void run() {\n" + " X x;\n" + " int a = x.f();\n" + " int b = x.f();\n" + " (void)a;\n" + " (void)b;\n" + "}"); + ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:7]: (style, inconclusive) Same expression used in consecutive assignments of 'a' and 'b'.\n", errout_str()); + + // Issue #8712 + check("void f() {\n" + " unsigned char d;\n" + " d = d % 5;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("template \n" + "T f() {\n" + " T x = T();\n" + "}\n" + "int &a = f();"); + ASSERT_EQUALS("", errout_str()); + + // Issue #8713 + check("class A {\n" + " int64_t B = 32768;\n" + " P m = MakeP(B);\n" + "};\n" + "void f() {\n" + " uint32_t a = 42;\n" + " uint32_t b = uint32_t(A ::B / 1024);\n" + " int32_t c = int32_t(a / b);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Issue #8709 + check("a b;\n" + "void c() {\n" + " switch (d) { case b:; }\n" + " double e(b);\n" + " if(e <= 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #10718 + // Should probably not be inconclusive + check("struct a {\n" + " int b() const;\n" + " auto c() -> decltype(0) {\n" + " a d;\n" + " int e = d.b(), f = d.b();\n" + " return e + f;\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5]: (style, inconclusive) Same expression used in consecutive assignments of 'e' and 'f'.\n", errout_str()); + } + + void multiConditionSameExpression() { + check("void f() {\n" + " int val = 0;\n" + " if (val < 0) continue;\n" + " if ((val > 0)) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'val < 0' is always false.\n" + "[test.cpp:2] -> [test.cpp:4]: (style) The comparison 'val > 0' is always false.\n", errout_str()); + + check("void f() {\n" + " int val = 0;\n" + " int *p = &val;n" + " val = 1;\n" + " if (*p < 0) continue;\n" + " if ((*p > 0)) {}\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:3]: (style) Variable 'p' can be declared as pointer to const\n", + errout_str()); + + check("void f() {\n" + " int val = 0;\n" + " int *p = &val;\n" + " if (*p < 0) continue;\n" + " if ((*p > 0)) {}\n" + "}\n"); + TODO_ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison '*p < 0' is always false.\n" + "[test.cpp:2] -> [test.cpp:4]: (style) The comparison '*p > 0' is always false.\n", + "[test.cpp:3]: (style) Variable 'p' can be declared as pointer to const\n", + errout_str()); + + check("void f() {\n" + " int val = 0;\n" + " if (val < 0) {\n" + " if ((val > 0)) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'val < 0' is always false.\n" + "[test.cpp:2] -> [test.cpp:4]: (style) The comparison 'val > 0' is always false.\n", errout_str()); + + check("void f() {\n" + " int val = 0;\n" + " if (val < 0) {\n" + " if ((val < 0)) {}\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) The comparison 'val < 0' is always false.\n" + "[test.cpp:2] -> [test.cpp:4]: (style) The comparison 'val < 0' is always false.\n", errout_str()); + + check("void f() {\n" + " int activate = 0;\n" + " int foo = 0;\n" + " if (activate) {}\n" + " else if (foo) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void checkSignOfUnsignedVariable() { + check("void foo() {\n" + " for(unsigned char i = 10; i >= 0; i--) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'i' can't be negative so it is unnecessary to test it.\n", errout_str()); + + check("void foo(bool b) {\n" + " for(unsigned int i = 10; b || i >= 0; i--) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'i' can't be negative so it is unnecessary to test it.\n", errout_str()); + + { + const char code[] = "void foo(unsigned int x) {\n" + " if (x < 0) {}\n" + "}"; + check(code, true, false, true, false); + ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout_str()); + check(code, true, false, true, true); + ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout_str()); + } + + check("void foo(unsigned int x) {\n" + " if (x < 0u) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout_str()); + + check("void foo(int x) {\n" + " if (x < 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + { + const char code[] = "void foo(unsigned x) {\n" + " int y = 0;\n" + " if (x < y) {}\n" + "}"; + check(code, true, false, true, false); + ASSERT_EQUALS("[test.cpp:3]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout_str()); + check(code, true, false, true, true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout_str()); + } + check("void foo(unsigned x) {\n" + " int y = 0;\n" + " if (b)\n" + " y = 1;\n" + " if (x < y) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(unsigned int x) {\n" + " if (0 > x) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout_str()); + + check("void foo(unsigned int x) {\n" + " if (0UL > x) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout_str()); + + check("void foo(int x) {\n" + " if (0 > x) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(unsigned int x) {\n" + " if (x >= 0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout_str()); + + check("void foo(unsigned int x, unsigned y) {\n" + " if (x - y >= 0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x-y' can't be negative so it is unnecessary to test it.\n", errout_str()); + + check("void foo(unsigned int x) {\n" + " if (x >= 0ull) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout_str()); + + check("void foo(int x) {\n" + " if (x >= 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(unsigned int x) {\n" + " if (0 <= x) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout_str()); + + check("void foo(unsigned int x) {\n" + " if (0ll <= x) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout_str()); + + check("void foo(int x) {\n" + " if (0 <= x) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(unsigned int x, bool y) {\n" + " if (x < 0 && y) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout_str()); + + check("void foo(int x, bool y) {\n" + " if (x < 0 && y) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(unsigned int x, bool y) {\n" + " if (0 > x && y) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout_str()); + + check("void foo(int x, bool y) {\n" + " if (0 > x && y) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(unsigned int x, bool y) {\n" + " if (x >= 0 && y) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout_str()); + + check("void foo(int x, bool y) {\n" + " if (x >= 0 && y) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + + check("void foo(unsigned int x, bool y) {\n" + " if (y && x < 0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout_str()); + + check("void foo(int x, bool y) {\n" + " if (y && x < 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(unsigned int x, bool y) {\n" + " if (y && 0 > x) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout_str()); + + check("void foo(int x, bool y) {\n" + " if (y && 0 > x) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(unsigned int x, bool y) {\n" + " if (y && x >= 0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout_str()); + + check("void foo(int x, bool y) {\n" + " if (y && x >= 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + + check("void foo(unsigned int x, bool y) {\n" + " if (x < 0 || y) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout_str()); + + check("void foo(int x, bool y) {\n" + " if (x < 0 || y) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(unsigned int x, bool y) {\n" + " if (0 > x || y) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout_str()); + + check("void foo(int x, bool y) {\n" + " if (0 > x || y) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(unsigned int x, bool y) {\n" + " if (x >= 0 || y) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Unsigned expression 'x' can't be negative so it is unnecessary to test it.\n", errout_str()); + + check("void foo(int x, bool y) {\n" + " if (x >= 0 || y) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #3233 - FP when template is used (template parameter is numeric constant) + { + const char code[] = "template void foo(unsigned int x) {\n" + " if (x <= n);\n" + "}\n" + "foo<0>();"; + check(code, true, false); + ASSERT_EQUALS("", errout_str()); + check(code, true, true); + ASSERT_EQUALS("", errout_str()); + } + + { + check("template void foo(unsigned int x) {\n" + "if (x <= 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Checking if unsigned expression 'x' is less than zero.\n", errout_str()); + } + + // #8836 + check("uint32_t value = 0xFUL;\n" + "void f() {\n" + " if (value < 0u)\n" + " {\n" + " value = 0u;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Checking if unsigned expression 'value' is less than zero.\n", errout_str()); + + // #9040 + /*const*/ Settings settings1 = settingsBuilder().platform(Platform::Type::Win64).build(); + check("using BOOL = unsigned;\n" + "int i;\n" + "bool f() {\n" + " return i >= 0;\n" + "}\n", &settings1); + ASSERT_EQUALS("", errout_str()); + + // #10612 + check("void f(void) {\n" + " const uint32_t x = 0;\n" + " constexpr const auto y = 0xFFFFU;\n" + " if (y < x) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Checking if unsigned expression 'y' is less than zero.\n", errout_str()); + + // #12387 + check("template\n" + "void f(T t) {\n" + " if constexpr (std::numeric_limits::is_signed) {\n" + " if (t < 0) {}\n" + " }\n" + "}\n" + "void g() {\n" + " f(0);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void checkSignOfPointer() { + check("void foo(const int* x) {\n" + " if (x >= 0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n", errout_str()); + + { + const char code[] = "void foo(const int* x) {\n" + " int y = 0;\n" + " if (x >= y) {}\n" + "}"; + check(code, true, false, true, false); + ASSERT_EQUALS("[test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n", errout_str()); + check(code, true, false, true, true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n", errout_str()); + } + check("void foo(const int* x) {\n" + " if (*x >= 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(const int* x) {\n" + " if (x < 0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n", errout_str()); + + { + const char code[] = "void foo(const int* x) {\n" + " unsigned y = 0u;\n" + " if (x < y) {}\n" + "}"; + + check(code, true, false, true, false); + ASSERT_EQUALS("[test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n", errout_str()); + check(code, true, false, true, true); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n", errout_str()); + } + + check("void foo(const int* x) {\n" + " if (*x < 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(const int* x, const int* y) {\n" + " if (x - y < 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(const int* x, const int* y) {\n" + " if (x - y <= 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(const int* x, const int* y) {\n" + " if (x - y > 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(const int* x, const int* y) {\n" + " if (x - y >= 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(const Bar* x) {\n" + " if (0 <= x) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n", errout_str()); + + check("struct S {\n" + " int* ptr;\n" + "};\n" + "void foo(S* first) {\n" + " if (first.ptr >= 0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n" + "[test.cpp:4]: (style) Parameter 'first' can be declared as pointer to const\n", + errout_str()); + + check("struct S {\n" + " int* ptr;\n" + "};\n" + "void foo(S* first, S* second) {\n" + " if((first.ptr - second.ptr) >= 0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'first' can be declared as pointer to const\n" + "[test.cpp:4]: (style) Parameter 'second' can be declared as pointer to const\n", + errout_str()); + + check("struct S {\n" + " int* ptr;\n" + "};\n" + "void foo(S* first) {\n" + " if((first.ptr) >= 0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) A pointer can not be negative so it is either pointless or an error to check if it is not.\n" + "[test.cpp:4]: (style) Parameter 'first' can be declared as pointer to const\n", + errout_str()); + + check("struct S {\n" + " int* ptr;\n" + "};\n" + "void foo(S* first, S* second) {\n" + " if(0 <= first.ptr - second.ptr) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'first' can be declared as pointer to const\n" + "[test.cpp:4]: (style) Parameter 'second' can be declared as pointer to const\n", + errout_str()); + + check("struct S {\n" + " int* ptr;\n" + "};\n" + "void foo(S* first, S* second) {\n" + " if(0 <= (first.ptr - second.ptr)) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'first' can be declared as pointer to const\n" + "[test.cpp:4]: (style) Parameter 'second' can be declared as pointer to const\n", + errout_str()); + + check("struct S {\n" + " int* ptr;\n" + "};\n" + "void foo(S* first, S* second) {\n" + " if(first.ptr - second.ptr < 0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'first' can be declared as pointer to const\n" + "[test.cpp:4]: (style) Parameter 'second' can be declared as pointer to const\n", + errout_str()); + + check("struct S {\n" + " int* ptr;\n" + "};\n" + "void foo(S* first, S* second) {\n" + " if((first.ptr - second.ptr) < 0) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'first' can be declared as pointer to const\n" + "[test.cpp:4]: (style) Parameter 'second' can be declared as pointer to const\n", + errout_str()); + + check("struct S {\n" + " int* ptr;\n" + "};\n" + "void foo(S* first, S* second) {\n" + " if(0 > first.ptr - second.ptr) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'first' can be declared as pointer to const\n" + "[test.cpp:4]: (style) Parameter 'second' can be declared as pointer to const\n", + errout_str()); + + check("struct S {\n" + " int* ptr;\n" + "};\n" + "void foo(S* first, S* second) {\n" + " if(0 > (first.ptr - second.ptr)) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (style) Parameter 'first' can be declared as pointer to const\n" + "[test.cpp:4]: (style) Parameter 'second' can be declared as pointer to const\n", + errout_str()); + + check("void foo(const int* x) {\n" + " if (0 <= x[0]) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(Bar* x) {\n" + " if (0 <= x.y) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'x' can be declared as pointer to const\n", errout_str()); + + check("void foo(Bar* x) {\n" + " if (0 <= x->y) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'x' can be declared as pointer to const\n", errout_str()); + + check("void foo(Bar* x, Bar* y) {\n" + " if (0 <= x->y - y->y ) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'x' can be declared as pointer to const\n" + "[test.cpp:1]: (style) Parameter 'y' can be declared as pointer to const\n", + errout_str()); + + check("void foo(const Bar* x) {\n" + " if (0 > x) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n", errout_str()); + + check("void foo(const int* x) {\n" + " if (0 > x[0]) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(Bar* x) {\n" + " if (0 > x.y) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'x' can be declared as pointer to const\n", errout_str()); + + check("void foo(Bar* x) {\n" + " if (0 > x->y) {}\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'x' can be declared as pointer to const\n", errout_str()); + + check("void foo() {\n" + " int (*t)(void *a, void *b);\n" + " if (t(a, b) < 0) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " int (*t)(void *a, void *b);\n" + " if (0 > t(a, b)) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct object_info { int *typep; };\n" + "void packed_object_info(struct object_info *oi) {\n" + " if (oi->typep < 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n" + "[test.cpp:2]: (style) Parameter 'oi' can be declared as pointer to const\n", + errout_str()); + + check("struct object_info { int typep[10]; };\n" + "void packed_object_info(struct object_info *oi) {\n" + " if (oi->typep < 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) A pointer can not be negative so it is either pointless or an error to check if it is.\n" + "[test.cpp:2]: (style) Parameter 'oi' can be declared as pointer to const\n", + errout_str()); + + check("struct object_info { int *typep; };\n" + "void packed_object_info(struct object_info *oi) {\n" + " if (*oi->typep < 0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Parameter 'oi' can be declared as pointer to const\n", errout_str()); + } + + void checkSuspiciousSemicolon1() { + 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) 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) Suspicious use of ; at the end of 'while' statement.\n", errout_str()); + } + + void checkSuspiciousSemicolon2() { + check("void foo() {\n" + " if (i == 1); {\n" + " do_something();\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) 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 checkSuspiciousSemicolon3() { + checkP("#define REQUIRE(code) {code}\n" + "void foo() {\n" + " if (x == 123);\n" + " REQUIRE(y=z);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void checkSuspiciousComparison() { + checkP("void f(int a, int b) {\n" + " a > b;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious operator '>', result is not used.\n", errout_str()); + + checkP("void f() {\n" // #10607 + " for (auto p : m)\n" + " std::vector> k;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void checkInvalidFree() { + check("void foo(char *p) {\n" + " char *a; a = malloc(1024);\n" + " free(a + 10);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching address is freed. The address you get from malloc() must be freed without offset.\n", errout_str()); + + check("void foo(char *p) {\n" + " char *a; a = malloc(1024);\n" + " free(a - 10);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching address is freed. The address you get from malloc() must be freed without offset.\n", errout_str()); + + check("void foo(char *p) {\n" + " char *a; a = malloc(1024);\n" + " free(10 + a);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Mismatching address is freed. The address you get from malloc() must be freed without offset.\n", errout_str()); + + check("void foo(char *p) {\n" + " char *a; a = new char[1024];\n" + " delete[] (a + 10);\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n" + "[test.cpp:3]: (error) Mismatching address is deleted. The address you get from new must be deleted without offset.\n", + errout_str()); + + check("void foo(char *p) {\n" + " char *a; a = new char;\n" + " delete a + 10;\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n" + "[test.cpp:3]: (error) Mismatching address is deleted. The address you get from new must be deleted without offset.\n", + errout_str()); + + check("void foo(char *p) {\n" + " char *a; a = new char;\n" + " bar(a);\n" + " delete a + 10;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(char *p) {\n" + " char *a; a = new char;\n" + " char *b; b = new char;\n" + " bar(a);\n" + " delete a + 10;\n" + " delete b + 10;\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (error) Mismatching address is deleted. The address you get from new must be deleted without offset.\n", errout_str()); + + check("void foo(char *p) {\n" + " char *a; a = new char;\n" + " char *b; b = new char;\n" + " bar(a, b);\n" + " delete a + 10;\n" + " delete b + 10;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(char *p) {\n" + " char *a; a = new char;\n" + " bar()\n" + " delete a + 10;\n" + "}"); + ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p' can be declared as pointer to const\n" + "[test.cpp:4]: (error) Mismatching address is deleted. The address you get from new must be deleted without offset.\n", + errout_str()); + + check("void foo(size_t xx) {\n" + " char *ptr; ptr = malloc(42);\n" + " ptr += xx;\n" + " free(ptr + 1 - xx);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Mismatching address is freed. The address you get from malloc() must be freed without offset.\n", errout_str()); + + check("void foo(size_t xx) {\n" + " char *ptr; ptr = malloc(42);\n" + " std::cout << ptr;\n" + " ptr = otherPtr;\n" + " free(otherPtr - xx - 1);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (style) Variable 'ptr' can be declared as pointer to const\n", + errout_str()); + } + + void checkRedundantCopy() { + check("const std::string& getA(){static std::string a;return a;}\n" + "void foo() {\n" + " const std::string a = getA();\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (performance, inconclusive) Use const reference for 'a' to avoid unnecessary data copying.\n", errout_str()); + + check("class A { public: A() {} char x[100]; };\n" + "const A& getA(){static A a;return a;}\n" + "int main()\n" + "{\n" + " const A a = getA();\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (performance, inconclusive) Use const reference for 'a' to avoid unnecessary data copying.\n", errout_str()); + + check("const int& getA(){static int a;return a;}\n" + "int main()\n" + "{\n" + " const int a = getA();\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("const int& getA(){static int a;return a;}\n" + "int main()\n" + "{\n" + " int getA = 0;\n" + " const int a = getA + 3;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:4]: (style) Local variable \'getA\' shadows outer function\n", errout_str()); + + check("class A { public: A() {} char x[100]; };\n" + "const A& getA(){static A a;return a;}\n" + "int main()\n" + "{\n" + " const A a(getA());\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (performance, inconclusive) Use const reference for 'a' to avoid unnecessary data copying.\n", errout_str()); + + check("const int& getA(){static int a;return a;}\n" + "int main()\n" + "{\n" + " const int a(getA());\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class A{\n" + "public:A(int a=0){_a = a;}\n" + "A operator+(const A & a){return A(_a+a._a);}\n" + "private:int _a;};\n" + "const A& getA(){static A a;return a;}\n" + "int main()\n" + "{\n" + " const A a = getA() + 1;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class A{\n" + "public:A(int a=0){_a = a;}\n" + "A operator+(const A & a){return A(_a+a._a);}\n" + "private:int _a;};\n" + "const A& getA(){static A a;return a;}\n" + "int main()\n" + "{\n" + " const A a(getA()+1);\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #5190 - FP when creating object with constructor that takes a reference + check("class A {};\n" + "class B { B(const A &a); };\n" + "const A &getA();\n" + "void f() {\n" + " const B b(getA());\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class A {};\n" + "class B { B(const A& a); };\n" + "const A& getA();\n" + "void f() {\n" + " const B b{ getA() };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #5618 + const char* code5618 = "class Token {\n" + "public:\n" + " const std::string& str();\n" + "};\n" + "void simplifyArrayAccessSyntax() {\n" + " for (Token *tok = list.front(); tok; tok = tok->next()) {\n" + " const std::string temp = tok->str();\n" + " tok->str(tok->strAt(2));\n" + " }\n" + "}"; + check(code5618, true, true); + ASSERT_EQUALS("", errout_str()); + check(code5618, true, false); + ASSERT_EQUALS("", errout_str()); + + // #5890 - crash: wesnoth desktop_util.cpp / unicode.hpp + check("typedef std::vector X;\n" + "X f(const X &in) {\n" + " const X s = f(in);\n" + " return f(s);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #7981 - False positive redundantCopyLocalConst - const ref argument to ctor + check("class CD {\n" + " public:\n" + " CD(const CD&);\n" + " static const CD& getOne();\n" + "};\n" + " \n" + "void foo() {\n" + " const CD cd(CD::getOne());\n" + "}", true, true); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" // #10545 + " int modify();\n" + " const std::string& get() const;\n" + "};\n" + "std::string f(S& s) {\n" + " const std::string old = s.get();\n" + " int i = s.modify();\n" + " if (i != 0)\n" + " return old;\n" + " return {};\n" + "}", true, /*inconclusive*/ true); + ASSERT_EQUALS("", errout_str()); + + check("struct X { int x; };\n" // #10191 + "struct S {\n" + " X _x;\n" + " X& get() { return _x; }\n" + " void modify() { _x.x += 42; }\n" + " int copy() {\n" + " const X x = get();\n" + " modify();\n" + " return x.x;\n" + " }\n" + " int constref() {\n" + " const X& x = get();\n" + " modify();\n" + " return x.x;\n" + " }\n" + "};\n", true, /*inconclusive*/ true); + ASSERT_EQUALS("", errout_str()); + + // #10704 + check("struct C {\n" + " std::string str;\n" + " const std::string& get() const { return str; }\n" + "};\n" + "struct D {\n" + " C c;\n" + " bool f() const {\n" + " std::string s = c.get();\n" + " return s.empty();\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:8]: (performance, inconclusive) Use const reference for 's' to avoid unnecessary data copying.\n", errout_str()); + + check("struct C {\n" + " const std::string & get() const { return m; }\n" + " std::string m;\n" + "};\n" + "C getC();\n" + "void f() {\n" + " const std::string s = getC().get();\n" + "}\n" + "void g() {\n" + " std::string s = getC().get();\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" // #12139 + " int x, y;\n" + "};\n" + "struct T {\n" + " S s;\n" + " const S& get() const { return s; }\n" + "};\n" + "void f(const T& t) {\n" + " const S a = t.get();\n" + " if (a.x > a.y) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void checkNegativeShift() { + check("void foo()\n" + "{\n" + " int a; a = 123;\n" + " (void)(a << -1);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Shifting by a negative value is undefined behaviour\n", errout_str()); + check("void foo()\n" + "{\n" + " int a; a = 123;\n" + " (void)(a >> -1);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Shifting by a negative value is undefined behaviour\n", errout_str()); + check("void foo()\n" + "{\n" + " int a; a = 123;\n" + " a <<= -1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Shifting by a negative value is undefined behaviour\n", errout_str()); + check("void foo()\n" + "{\n" + " int a; a = 123;\n" + " a >>= -1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Shifting by a negative value is undefined behaviour\n", errout_str()); + check("void foo()\n" + "{\n" + " std::cout << -1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void foo()\n" + "{\n" + " std::cout << a << -1 ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void foo()\n" + "{\n" + " std::cout << 3 << -1 ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + check("void foo() {\n" + " x = (-10+2) << 3;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (portability) Shifting a negative value is technically undefined behaviour\n", errout_str()); + + check("x = y ? z << $-1 : 0;"); + ASSERT_EQUALS("", errout_str()); + + // Negative LHS + check("const int x = -1 >> 2;"); + ASSERT_EQUALS("[test.cpp:1]: (portability) Shifting a negative value is technically undefined behaviour\n", errout_str()); + + // #6383 - unsigned type + check("const int x = (unsigned int)(-1) >> 2;"); + ASSERT_EQUALS("", errout_str()); + + // #7814 - UB happening in valueflowcode when it tried to compute shifts. + check("int shift1() { return 1 >> -1 ;}\n" + "int shift2() { return 1 << -1 ;}\n" + "int shift3() { return -1 >> 1 ;}\n" + "int shift4() { return -1 << 1 ;}"); + ASSERT_EQUALS("[test.cpp:1]: (error) Shifting by a negative value is undefined behaviour\n" + "[test.cpp:2]: (error) Shifting by a negative value is undefined behaviour\n" + "[test.cpp:3]: (portability) Shifting a negative value is technically undefined behaviour\n" + "[test.cpp:4]: (portability) Shifting a negative value is technically undefined behaviour\n", errout_str()); + } + + void incompleteArrayFill() { + check("void f() {\n" + " int a[5];\n" + " memset(a, 123, 5);\n" + " memcpy(a, b, 5);\n" + " memmove(a, b, 5);\n" + "}"); + ASSERT_EQUALS(// TODO "[test.cpp:4] -> [test.cpp:5]: (performance) Buffer 'a' is being written before its old content has been used.\n" + "[test.cpp:3]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memset()' with 'sizeof(*a)'?\n" + "[test.cpp:4]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memcpy()' with 'sizeof(*a)'?\n" + "[test.cpp:5]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memmove()' with 'sizeof(*a)'?\n", errout_str()); + + check("int a[5];\n" + "namespace Z { struct B { int a[5]; } b; }\n" + "void f() {\n" + " memset(::a, 123, 5);\n" + " memset(Z::b.a, 123, 5);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) Array '::a' is filled incompletely. Did you forget to multiply the size given to 'memset()' with 'sizeof(*::a)'?\n" + "[test.cpp:5]: (warning, inconclusive) Array 'Z::b.a' is filled incompletely. Did you forget to multiply the size given to 'memset()' with 'sizeof(*Z::b.a)'?\n", + "[test.cpp:4]: (warning, inconclusive) Array '::a' is filled incompletely. Did you forget to multiply the size given to 'memset()' with 'sizeof(*::a)'?\n", errout_str()); + + check("void f() {\n" + " Foo* a[5];\n" + " 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" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) Array 'a' is filled incompletely. Did you forget to multiply the size given to 'memset()' with 'sizeof(*a)'?\n", errout_str()); + + check("void f() {\n" + " Foo a[5];\n" // Size of foo is unknown + " memset(a, 'a', 5);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " char a[5];\n" + " memset(a, 'a', 5);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int a[5];\n" + " memset(a+15, 'a', 5);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " bool a[5];\n" + " memset(a, false, 5);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (portability, inconclusive) Array 'a' might be filled incompletely. Did you forget to multiply the size given to 'memset()' with 'sizeof(*a)'?\n", errout_str()); + } + + void redundantVarAssignment() { + setMultiline(); + + // Simple tests + check("void f(int i) {\n" + " i = 1;\n" + " i = 1;\n" + "}"); + ASSERT_EQUALS("test.cpp:3:style:Variable 'i' is reassigned a value before the old one has been used.\n" + "test.cpp:2:note:i is assigned\n" + "test.cpp:3:note:i is overwritten\n", errout_str()); + + // non-local variable => only show warning when inconclusive is used + check("int i;\n" + "void f() {\n" + " i = 1;\n" + " i = 1;\n" + "}"); + ASSERT_EQUALS("test.cpp:4:style:Variable 'i' is reassigned a value before the old one has been used.\n" + "test.cpp:3:note:i is assigned\n" + "test.cpp:4:note:i is overwritten\n", errout_str()); + + check("void f() {\n" + " int i;\n" + " i = 1;\n" + " i = 1;\n" + "}"); + ASSERT_EQUALS("test.cpp:4:style:Variable 'i' is reassigned a value before the old one has been used.\n" + "test.cpp:3:note:i is assigned\n" + "test.cpp:4:note:i is overwritten\n", errout_str()); + + check("void f() {\n" + " static int i;\n" + " i = 1;\n" + " i = 1;\n" + "}"); + TODO_ASSERT_EQUALS("error", "", errout_str()); + + check("void f() {\n" + " int i[10];\n" + " i[2] = 1;\n" + " i[2] = 1;\n" + "}"); + ASSERT_EQUALS("test.cpp:4:style:Variable 'i[2]' is reassigned a value before the old one has been used.\n" + "test.cpp:3:note:i[2] is assigned\n" + "test.cpp:4:note:i[2] is overwritten\n", errout_str()); + + check("void f(int x) {\n" + " int i[10];\n" + " i[x] = 1;\n" + " x=1;\n" + " i[x] = 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(const int x) {\n" + " int i[10];\n" + " i[x] = 1;\n" + " i[x] = 1;\n" + "}"); + ASSERT_EQUALS("test.cpp:4:style:Variable 'i[x]' is reassigned a value before the old one has been used.\n" + "test.cpp:3:note:i[x] is assigned\n" + "test.cpp:4:note:i[x] is overwritten\n", errout_str()); + + // Testing different types + check("void f() {\n" + " Foo& bar = foo();\n" + " bar = x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " Foo& bar = foo();\n" + " bar = x;\n" + " bar = y;\n" + "}"); + TODO_ASSERT_EQUALS("error", "", errout_str()); + + check("void f() {\n" + " Foo& bar = foo();\n" // #4425. bar might refer to something global, etc. + " bar = y();\n" + " foo();\n" + " bar = y();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Tests with function call between assignment + check("void f(int i) {\n" + " i = 1;\n" + " bar();\n" + " i = 1;\n" + "}"); + ASSERT_EQUALS("test.cpp:4:style:Variable 'i' is reassigned a value before the old one has been used.\n" + "test.cpp:2:note:i is assigned\n" + "test.cpp:4:note:i is overwritten\n", errout_str()); + + check("int i;\n" + "void f() {\n" + " i = 1;\n" + " bar();\n" // Global variable might be accessed in bar() + " i = 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " static int i;\n" + " i = 1;\n" + " bar();\n" // bar() might call f() recursively. This could be a false positive in more complex examples (when value of i is used somewhere. See #4229) + " i = 2;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int i;\n" + " i = 1;\n" + " bar();\n" + " i = 1;\n" + "}"); + ASSERT_EQUALS("test.cpp:5:style:Variable 'i' is reassigned a value before the old one has been used.\n" + "test.cpp:3:note:i is assigned\n" + "test.cpp:5:note:i is overwritten\n", errout_str()); + + check("void bar(int i) {}\n" + "void f(int i) {\n" + " i = 1;\n" + " bar(i);\n" // Passed as argument + " i = 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " Foo bar = foo();\n" + " bar();\n" // #5568. operator() called + " bar = y();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Branch tests + check("void f(int i) {\n" + " i = 1;\n" + " if(x)\n" + " i = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int i) {\n" + " if(x)\n" + " i = 0;\n" + " i = 1;\n" + " i = 2;\n" + "}"); + ASSERT_EQUALS("test.cpp:5:style:Variable 'i' is reassigned a value before the old one has been used.\n" + "test.cpp:4:note:i is assigned\n" + "test.cpp:5:note:i is overwritten\n", errout_str()); + + // #4513 + check("int x;\n" + "int g() {\n" + " return x*x;\n" + "}\n" + "void f() {\n" + " x = 2;\n" + " x = g();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int g() {\n" + " return x*x;\n" + "}\n" + "void f(int x) {\n" + " x = 2;\n" + " x = g();\n" + "}"); + ASSERT_EQUALS("test.cpp:6:style:Variable 'x' is reassigned a value before the old one has been used.\n" + "test.cpp:5:note:x is assigned\n" + "test.cpp:6:note:x is overwritten\n", errout_str()); + + check("void f() {\n" + " Foo& bar = foo();\n" + " bar = x;\n" + " bar = y();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class C {\n" + " int x;\n" + " void g() { return x * x; }\n" + " void f();\n" + "};\n" + "\n" + "void C::f() {\n" + " x = 2;\n" + " x = g();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class C {\n" + " int x;\n" + " void g() { return x*x; }\n" + " void f(Foo z);\n" + "};\n" + "\n" + "void C::f(Foo z) {\n" + " x = 2;\n" + " x = z.g();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // ({ }) + check("void f() {\n" + " int x;\n" + " x = 321;\n" + " x = ({ asm(123); })\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // from #3103 (avoid a false negative) + check("int foo(){\n" + " int x;\n" + " x = 1;\n" + " x = 1;\n" + " return x + 1;\n" + "}"); + ASSERT_EQUALS("test.cpp:4:style:Variable 'x' is reassigned a value before the old one has been used.\n" + "test.cpp:3:note:x is assigned\n" + "test.cpp:4:note:x is overwritten\n", errout_str()); + + // from #3103 (avoid a false positive) + check("int foo(){\n" + " int x;\n" + " x = 1;\n" + " if (y)\n" // <-- cppcheck does not know anything about 'y' + " x = 2;\n" + " return x + 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // initialization, assignment with 0 + check("void f() {\n" // Ticket #4356 + " int x = 0;\n" // <- ignore initialization with 0 + " x = 3;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " state_t *x = NULL;\n" + " x = dostuff();\n" + "}"); + ASSERT_EQUALS( + "test.cpp:2:style:Variable 'x' can be declared as pointer to const\n", + errout_str()); + + check("void f() {\n" + " state_t *x;\n" + " x = NULL;\n" + " x = dostuff();\n" + "}"); + ASSERT_EQUALS( + "test.cpp:2:style:Variable 'x' can be declared as pointer to const\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:6:style:Variable 'ab.a' is reassigned a value before the old one has been used.\n" + "test.cpp:5:note:ab.a is assigned\n" + "test.cpp:6:note:ab.a is overwritten\n", errout_str()); + + check("struct AB { int a; int b; };\n" + "\n" + "int f() {\n" + " struct AB ab;\n" + " ab.a = 1;\n" + " ab = do_something();\n" + " return ab.a;\n" + "}"); + TODO_ASSERT_EQUALS("error", "", errout_str()); + + check("struct AB { int a; int b; };\n" + "\n" + "int f() {\n" + " struct AB ab;\n" + " ab.a = 1;\n" + " do_something(&ab);\n" + " ab.a = 2;\n" + " return ab.a;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct AB { int a; int b; };\n" + "\n" + "int f(DO_SOMETHING do_something) {\n" + " struct AB ab;\n" + " ab.a = 1;\n" + " do_something(&ab);\n" + " ab.a = 2;\n" + " return ab.a;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct AB { int a; int b; };\n" + "\n" + "int f(struct AB *ab) {\n" + " ab->a = 1;\n" + " ab->b = 2;\n" + " ab++;\n" + " ab->a = 1;\n" + " ab->b = 2;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct AB { int a; int b; };\n" + "\n" + "int f(struct AB *ab) {\n" + " ab->a = 1;\n" + " ab->b = 2;\n" + " ab = x;\n" + " ab->a = 1;\n" + " ab->b = 2;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(struct AB *ab) {\n" // # + " ab->data->x = 1;\n" + " ab = &ab1;\n" + " ab->data->x = 2;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #5964 + check("void func(char *buffer, const char *format, int precision, unsigned value) {\n" + " (precision < 0) ? sprintf(buffer, format, value) : sprintf(buffer, format, precision, value);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // don't crash + check("struct data {\n" + " struct { int i; } fc;\n" + "};\n" + "struct state {\n" + " struct data d[123];\n" + "};\n" + "void func(struct state *s) {\n" + " s->foo[s->x++] = 2;\n" + " s->d[1].fc.i++;\n" + "}"); + + // #6525 - inline assembly + check("void f(int i) {\n" + " i = 1;\n" + " asm(\"foo\");\n" + " i = 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #6555 + check("void foo() {\n" + " char *p = 0;\n" + " try {\n" + " p = fred();\n" + " p = wilma();\n" + " }\n" + " catch (...) {\n" + " barney(p);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo() {\n" + " char *p = 0;\n" + " try {\n" + " p = fred();\n" + " p = wilma();\n" + " }\n" + " catch (...) {\n" + " barney(x);\n" + " }\n" + "}"); + ASSERT_EQUALS("test.cpp:2:style:The scope of the variable 'p' can be reduced.\n" + "test.cpp:2:style:Variable 'p' can be declared as pointer to const\n", + errout_str()); + + check("void foo() {\n" + " char *p = 0;\n" + " try {\n" + " if(z) {\n" + " p = fred();\n" + " p = wilma();\n" + " }\n" + " }\n" + " catch (...) {\n" + " barney(p);\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Member variable pointers + check("void podMemPtrs() {\n" + " int POD::*memptr;\n" + " memptr = &POD::a;\n" + " memptr = &POD::b;\n" + " if (memptr)\n" + " memptr = 0;\n" + "}"); + ASSERT_EQUALS("test.cpp:4:style:Variable 'memptr' is reassigned a value before the old one has been used.\n" + "test.cpp:3:note:memptr is assigned\n" + "test.cpp:4:note:memptr is overwritten\n", errout_str()); + + // Pointer function argument (#3857) + check("void f(float * var)\n" + "{\n" + " var[0] = 0.2f;\n" + " var[0] = 0.2f;\n" // <-- is initialized twice + "}"); + ASSERT_EQUALS("test.cpp:4:style:Variable 'var[0]' is reassigned a value before the old one has been used.\n" + "test.cpp:3:note:var[0] is assigned\n" + "test.cpp:4:note:var[0] is overwritten\n", errout_str()); + + check("void f(float * var)\n" + "{\n" + " *var = 0.2f;\n" + " *var = 0.2f;\n" // <-- is initialized twice + "}"); + ASSERT_EQUALS("test.cpp:4:style:Variable '*var' is reassigned a value before the old one has been used.\n" + "test.cpp:3:note:*var is assigned\n" + "test.cpp:4:note:*var is overwritten\n", errout_str()); + + // Volatile variables + check("void f() {\n" + " volatile char *reg = (volatile char *)0x12345;\n" + " *reg = 12;\n" + " *reg = 34;\n" + "}"); + ASSERT_EQUALS("test.cpp:2:style:C-style pointer casting\n", errout_str()); + + check("void f(std::map& m, int key, int value) {\n" // #6379 + " m[key] = value;\n" + " m[key] = value;\n" + "}\n"); + ASSERT_EQUALS("test.cpp:3:style:Variable 'm[key]' is reassigned a value before the old one has been used.\n" + "test.cpp:2:note:m[key] is assigned\n" + "test.cpp:3:note:m[key] is overwritten\n", + errout_str()); + } + + void redundantVarAssignment_trivial() { + check("void f() {\n" + " int a = 0;\n" + " a = 4;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int a;\n" + " a = 0;\n" + " a = 4;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " unsigned a;\n" + " a = 0u;\n" + " a = 2u;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " void* a;\n" + " a = (void*)0;\n" + " a = p;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (style) Variable 'a' can be declared as pointer to const\n", + errout_str()); + + check("void f() {\n" + " void* a;\n" + " a = (void*)0U;\n" + " a = p;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2]: (style) Variable 'a' can be declared as pointer to const\n", + errout_str()); + } + + void redundantVarAssignment_struct() { + check("struct foo {\n" + " int a,b;\n" + "};\n" + "\n" + "int main() {\n" + " struct foo x;\n" + " x.a = _mm_set1_ps(1.0);\n" + " x.a = _mm_set1_ps(2.0);\n" + "}"); + ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:8]: (style) Variable 'x.a' is reassigned a value before the old one has been used.\n", errout_str()); + + check("void f() {\n" + " struct AB ab;\n" + " ab.x = 23;\n" + " ab.y = 41;\n" + " ab.x = 1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Variable 'ab.x' is reassigned a value before the old one has been used.\n", errout_str()); + + check("void f() {\n" + " struct AB ab = {0};\n" + " ab = foo();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void redundantVarAssignment_7133() { + // #7133 + check("sal_Int32 impl_Export() {\n" + " try {\n" + " try {\n" + " uno::Sequence< uno::Any > aArgs(2);\n" + " beans::NamedValue aValue;\n" + " aValue.Name = \"DocumentHandler\";\n" + " aValue.Value <<= xDocHandler;\n" + " aArgs[0] <<= aValue;\n" + " aValue.Name = \"Model\";\n" + " aValue.Value <<= xDocumentComp;\n" + " aArgs[1] <<= aValue;\n" + " }\n" + " catch (const uno::Exception&) {\n" + " }\n" + " }\n" + " catch (const uno::Exception&) {\n" + " }\n" + "}", true, true); + ASSERT_EQUALS("", errout_str()); + + check("void ConvertBitmapData(sal_uInt16 nDestBits) {\n" + " BitmapBuffer aSrcBuf;\n" + " aSrcBuf.mnBitCount = nSrcBits;\n" + " BitmapBuffer aDstBuf;\n" + " aSrcBuf.mnBitCount = nDestBits;\n" + " bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects );\n" + "}", false); + ASSERT_EQUALS("[test.c:3] -> [test.c:5]: (style) Variable 'aSrcBuf.mnBitCount' is reassigned a value before the old one has been used.\n", errout_str()); + check("void ConvertBitmapData(sal_uInt16 nDestBits) {\n" + " BitmapBuffer aSrcBuf;\n" + " aSrcBuf.mnBitCount = nSrcBits;\n" + " BitmapBuffer aDstBuf;\n" + " aSrcBuf.mnBitCount = nDestBits;\n" + " bConverted = ::ImplFastBitmapConversion( aDstBuf, aSrcBuf, aTwoRects );\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (style) Variable 'aSrcBuf.mnBitCount' is reassigned a value before the old one has been used.\n", + errout_str()); + + check("class C { void operator=(int x); };\n" // #8368 - assignment operator might have side effects => inconclusive + "void f() {\n" + " C c;\n" + " c = x;\n" + " c = x;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (style, inconclusive) Variable 'c' is reassigned a value before the old one has been used if variable is no semaphore variable.\n", errout_str()); + } + + void redundantVarAssignment_stackoverflow() { + check("typedef struct message_node {\n" + " char code;\n" + " size_t size;\n" + " struct message_node *next, *prev;\n" + "} *message_list;\n" + "static message_list remove_message_from_list(message_list m) {\n" + " m->prev->next = m->next;\n" + " m->next->prev = m->prev;\n" + " return m->next;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void redundantVarAssignment_lambda() { + // #7152 + check("int foo() {\n" + " int x = 0, y = 0;\n" + " auto f = [&]() { if (x < 5) ++y; };\n" + " x = 2;\n" + " f();\n" + " x = 6;\n" + " f();\n" + " return y;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #10228 + check("std::tuple g();\n" + "void h(int);\n" + "void f() {\n" + " auto [a, b] = g();\n" + " auto l = [a = a]() { h(i); };\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void redundantVarAssignment_loop() { + check("void f() {\n" + " char buf[10];\n" + " int i;\n" + " for (i = 0; i < 4; i++)\n" + " buf[i] = 131;\n" + " buf[i] = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void bar() {\n" // #9262 do-while with break + " int x = 0;\n" + " x = 432;\n" + " do {\n" + " if (foo()) break;\n" + " x = 1;\n" + " } while (false);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int num) {\n" // #9420 FP + " int a = num;\n" + " for (int b = 0; b < num; a = b++)\n" + " dostuff(a);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(int num) {\n" // #9420 FN + " int a = num;\n" + " for (int b = 0; b < num; a = b++);\n" + "}"); + TODO_ASSERT_EQUALS("error", "", errout_str()); + } + + void redundantVarAssignment_after_switch() { + check("void f(int x) {\n" // #7907 + " int ret;\n" + " switch (x) {\n" + " case 123:\n" + " ret = 1;\n" // redundant assignment + " break;\n" + " }\n" + " ret = 3;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:8]: (style) Variable 'ret' is reassigned a value before the old one has been used.\n", errout_str()); + } + + void redundantVarAssignment_pointer() { + check("void f(int *ptr) {\n" + " int *x = ptr + 1;\n" + " *x = 23;\n" + " foo(ptr);\n" + " *x = 32;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #8997 + check("void f() {\n" + " char x[2];\n" + " char* p = x;\n" + " *p = 1;\n" + " p += 1;\n" + " *p = 1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void redundantVarAssignment_pointer_parameter() { + check("void f(int *p) {\n" + " *p = 1;\n" + " if (condition) return;\n" + " *p = 2;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void redundantVarAssignment_array() { + check("void f() {\n" + " int arr[10];\n" + " int i = 0;\n" + " arr[i] = 1;\n" + " i += 2;\n" + " arr[i] = 3;\n" + " dostuff(arr);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void redundantVarAssignment_switch_break() { + // #10058 + check("void f(int a, int b) {\n" + " int ret = 0;\n" + " switch (a) {\n" + " case 1:\n" + " ret = 543;\n" + " if (b) break;\n" + " ret = 1;\n" + " break;\n" + " }" + " return ret;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int a, int b) {\n" + " int ret = 0;\n" + " switch (a) {\n" + " case 1:\n" + " ret = 543;\n" + " if (b) break;\n" + " ret = 1;\n" + " break;\n" + " }" + "}"); + ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:7]: (style) Variable 'ret' is reassigned a value before the old one has been used.\n", errout_str()); + } + + void redundantInitialization() { + setMultiline(); + + check("void f() {\n" + " int err = -ENOMEM;\n" + " err = dostuff();\n" + "}"); + ASSERT_EQUALS("test.cpp:3:style:Redundant initialization for 'err'. The initialized value is overwritten before it is read.\n" + "test.cpp:2:note:err is initialized\n" + "test.cpp:3:note:err is overwritten\n", + errout_str()); + + check("void f() {\n" + " struct S s = {1,2,3};\n" + " s = dostuff();\n" + "}"); + ASSERT_EQUALS("test.cpp:3:style:Redundant initialization for 's'. The initialized value is overwritten before it is read.\n" + "test.cpp:2:note:s is initialized\n" + "test.cpp:3:note:s is overwritten\n", + errout_str()); + + check("void f() {\n" + " int *p = NULL;\n" + " p = dostuff();\n" + "}"); + ASSERT_EQUALS( + "test.cpp:2:style:Variable 'p' can be declared as pointer to const\n", + errout_str()); + + // "trivial" initialization => do not warn + check("void f() {\n" + " struct S s = {0};\n" + " s = dostuff();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("namespace N { enum E {e0,e1}; }\n" + "void f() {\n" + " N::E e = N::e0;\n" // #9261 + " e = dostuff();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #10143 + " std::shared_ptr i = g();\n" + " h();\n" + " i = nullptr;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int f(const std::vector& v) {\n" // #9815 + " int i = g();\n" + " i = std::distance(v.begin(), std::find_if(v.begin(), v.end(), [=](int j) { return i == j; }));\n" + " return i;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void redundantMemWrite() { + return; // FIXME: temporary hack + + // Simple tests + // cppcheck-suppress unreachableCode - remove when code is enabled again + check("void f() {\n" + " char a[10];\n" + " memcpy(a, foo, bar);\n" + " memset(a, 0, bar);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (performance) Buffer 'a' is being written before its old content has been used.\n", errout_str()); + + check("void f() {\n" + " char a[10];\n" + " strcpy(a, foo);\n" + " strncpy(a, 0, bar);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (performance) Buffer 'a' is being written before its old content has been used.\n", errout_str()); + + check("void f() {\n" + " char a[10];\n" + " sprintf(a, \"foo\");\n" + " memmove(a, 0, bar);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (performance) Buffer 'a' is being written before its old content has been used.\n", errout_str()); + + check("void f(char *filename) {\n" + " char *p = strrchr(filename,'.');\n" + " strcpy(p, \"foo\");\n" + " dostuff(filename);\n" + " strcpy(p, \"foo\");\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Writing to different parts of a buffer + check("void f(void* a) {\n" + " memcpy(a, foo, bar);\n" + " memset(a+5, 0, bar);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Use variable as second argument + check("void f(void* a, void* b) {\n" + " memset(a, 0, 5);\n" + " memcpy(b, a, 5);\n" + " memset(a, 1, 5);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // strcat is special + check("void f() {\n" + " char a[10];\n" + " strcpy(a, foo);\n" + " strcat(a, bar);\n" // Not redundant + " strcpy(a, x);\n" // Redundant + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (performance) Buffer 'a' is being written before its old content has been used.\n", errout_str()); + + // Tests with function call between copy + check("void f() {\n" + " char a[10];\n" + " snprintf(a, foo, bar);\n" + " bar();\n" + " memset(a, 0, size);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (performance) Buffer 'a' is being written before its old content has been used.\n", errout_str()); + + check("void* a;\n" + "void f() {\n" + " memset(a, 0, size);\n" + " bar();\n" // Global variable might be accessed in bar() + " memset(a, 0, size);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " char a[10];\n" + " memset(a, 0, size);\n" + " bar();\n" + " memset(a, 0, size);\n" + "}"); + TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (performance) Buffer 'a' is being written before its old content has been used.\n", "", errout_str()); + + check("void bar(void* a) {}\n" + "void f(void* a) {\n" + " memset(a, 0, size);\n" + " bar(a);\n" // Passed as argument + " memset(a, 0, size);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // Branch tests + check("void f(void* a) {\n" + " memset(a, 0, size);\n" + " if(x)\n" + " memset(a, 0, size);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #4455 - initialization of local buffer + check("void f(void) {" + " char buf[10];\n" + " memset(buf, 0, 10);\n" + " strcpy(buf, string);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(void) {\n" + " char buf[10] = {0};\n" + " memset(buf, 0, 10);\n" + " strcpy(buf, string);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (performance) Buffer 'buf' is being written before its old content has been used.\n", errout_str()); + + // #5689 - use return value of strcpy + check("int f(void* a) {\n" + " int i = atoi(strcpy(a, foo));\n" + " strncpy(a, 0, bar);\n" + " return i;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #7175 - read+write + check("void f() {\n" + " char buf[100];\n" + " strcpy(buf, x);\n" + " strcpy(buf, dostuff(buf));\n" // <- read + write + " strcpy(buf, x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " char buf[100];\n" + " strcpy(buf, x);\n" + " strcpy(buf, dostuff(buf));\n" + " strcpy(buf, x);\n" + "}"); + TODO_ASSERT_EQUALS("error", "", errout_str()); + } + + void varFuncNullUB() { // #4482 + check("void a(...);\n" + "void b() { a(NULL); }"); + ASSERT_EQUALS("[test.cpp:2]: (portability) Passing NULL after the last typed argument to a variadic function leads to undefined behaviour.\n", errout_str()); + + check("void a(char *p, ...);\n" + "void b() { a(NULL, 2); }"); + ASSERT_EQUALS("", errout_str()); + } + + void checkCastIntToCharAndBack() { // #160 + + // check getchar + check("void f() {\n" + "unsigned char c; c = getchar();\n" + " while( c != EOF)\n" + " {\n" + " bar(c);\n" + " c = getchar();\n" + " } ;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Storing getchar() return value in char variable and then comparing with EOF.\n", errout_str()); + + check("void f() {\n" + "unsigned char c = getchar();\n" + " while( EOF != c)\n" + " {\n" + " bar(c);\n" + " } ;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Storing getchar() return value in char variable and then comparing with EOF.\n", errout_str()); + + check("void f() {\n" + " unsigned char c; c = getchar();\n" + " while( EOF != c )\n" + " {\n" + " bar(c);\n" + " c = getchar();\n" + " } ;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Storing getchar() return value in char variable and then comparing with EOF.\n", errout_str()); + + check("void f() {\n" + " unsigned char c;\n" + " while( EOF != ( c = getchar() ) )\n" + " {\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Storing getchar() return value in char variable and then comparing with EOF.\n", errout_str()); + + check("void f() {\n" + " int i; i = getchar();\n" + " while( i != EOF)\n" + " {\n" + " bar(i);\n" + " i = getchar();\n" + " } ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int i; i = getchar();\n" + " while( EOF != i )\n" + " {\n" + " bar(i);\n" + " i = getchar();\n" + " } ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + + // check getc + check("void f (FILE * pFile){\n" + "unsigned char c;\n" + "do {\n" + " c = getc (pFile);\n" + "} while (c != EOF)" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Storing getc() return value in char variable and then comparing with EOF.\n", errout_str()); + + check("void f (FILE * pFile){\n" + "unsigned char c;\n" + "do {\n" + " c = getc (pFile);\n" + "} while (EOF != c)" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Storing getc() return value in char variable and then comparing with EOF.\n", errout_str()); + + check("void f (FILE * pFile){\n" + "int i;\n" + "do {\n" + " i = getc (pFile);\n" + "} while (i != EOF)" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f (FILE * pFile){\n" + "int i;\n" + "do {\n" + " i = getc (pFile);\n" + "} while (EOF != i)" + "}"); + ASSERT_EQUALS("", errout_str()); + + + // check fgetc + check("void f (FILE * pFile){\n" + "unsigned char c;\n" + "do {\n" + " c = fgetc (pFile);\n" + "} while (c != EOF)" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Storing fgetc() return value in char variable and then comparing with EOF.\n", errout_str()); + + check("void f (FILE * pFile){\n" + "char c;\n" + "do {\n" + " c = fgetc (pFile);\n" + "} while (EOF != c)" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Storing fgetc() return value in char variable and then comparing with EOF.\n", errout_str()); + + check("void f (FILE * pFile){\n" + "signed char c;\n" + "do {\n" + " c = fgetc (pFile);\n" + "} while (EOF != c)" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f (FILE * pFile){\n" + "int i;\n" + "do {\n" + " i = fgetc (pFile);\n" + "} while (i != EOF)" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f (FILE * pFile){\n" + "int i;\n" + "do {\n" + " i = fgetc (pFile);\n" + "} while (EOF != i)" + "}"); + ASSERT_EQUALS("", errout_str()); + + // cin.get() + check("void f(){\n" + " char ch; ch = std::cin.get();\n" + " while (EOF != ch) {\n" + " std::cout << ch;\n" + " ch = std::cin.get();\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Storing cin.get() return value in char variable and then comparing with EOF.\n", errout_str()); + + check("void f(){\n" + " char ch; ch = std::cin.get();\n" + " while (ch != EOF) {\n" + " std::cout << ch;\n" + " ch = std::cin.get();\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Storing cin.get() return value in char variable and then comparing with EOF.\n", errout_str()); + + check("void f(){\n" + " int i; i = std::cin.get();\n" + " while ( EOF != i ) {\n" + " std::cout << i;\n" + " i = std::cin.get();\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(){\n" + " int i; i = std::cin.get();\n" + " while ( i != EOF ) {\n" + " std::cout << i;\n" + " i = std::cin.get();\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void checkCommaSeparatedReturn() { + check("int fun(int a) {\n" + " if (a < 0)\n" + " return a++,\n" + " do_something();\n" + "}", true, false, false); + TODO_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" + "}", true, false, false); + ASSERT_EQUALS("", errout_str()); + + check("int fun(int a) {\n" + " if (a < 0)\n" + " return a+5,\n" + " do_something();\n" + "}", true, false, false); + TODO_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" + "}", true, false, false); + ASSERT_EQUALS("", errout_str()); + + check("int fun(int a) {\n" + " if (a < 0)\n" + " return c::b;\n" + "}", true, false, false); + ASSERT_EQUALS("", errout_str()); + + // #4943 take care of C++11 initializer lists + check("std::vector Bar() {\n" + " return\n" + " {\n" + " { \"1\" },\n" + " { \"2\" },\n" + " { \"3\" }\n" + " };\n" + "}", true, false, false); + ASSERT_EQUALS("", errout_str()); + } + + void checkPassByReference() { + // #8570 passByValue when std::move is used + check("struct A\n" + "{\n" + " std::vector x;\n" + "};\n" + "\n" + "struct B\n" + "{\n" + " explicit B(A a) : a(std::move(a)) {}\n" + " void Init(A _a) { a = std::move(_a); }\n" + " A a;" + "};", true, false, true); + ASSERT_EQUALS("", errout_str()); + + check("struct A\n" + "{\n" + " std::vector x;\n" + "};\n" + "\n" + "struct B\n" + "{\n" + " explicit B(A a) : a{std::move(a)} {}\n" + " void Init(A _a) { a = std::move(_a); }\n" + " A a;" + "};", true, false, true); + ASSERT_EQUALS("", errout_str()); + + check("struct A\n" + "{\n" + " std::vector x;\n" + "};\n" + "\n" + "struct B\n" + "{\n" + " B(A a, A a2) : a{std::move(a)}, a2{std::move(a2)} {}\n" + " void Init(A _a) { a = std::move(_a); }\n" + " A a;" + " A a2;" + "};", true, false, true); + ASSERT_EQUALS("", errout_str()); + + check("struct A\n" + "{\n" + " std::vector x;\n" + "};\n" + "\n" + "struct B\n" + "{\n" + " B(A a, A a2) : a{std::move(a)}, a2{a2} {}\n" + " void Init(A _a) { a = std::move(_a); }\n" + " A a;" + " A a2;" + "};", true, false, true); + ASSERT_EQUALS("[test.cpp:8]: (performance) Function parameter 'a2' should be passed by const reference.\n", errout_str()); + + check("struct A\n" + "{\n" + " std::vector x;\n" + "};\n" + "\n" + "struct B\n" + "{\n" + " B(A a, A a2) : a{std::move(a)}, a2(a2) {}\n" + " void Init(A _a) { a = std::move(_a); }\n" + " A a;" + " A a2;" + "};", true, false, true); + ASSERT_EQUALS("[test.cpp:8]: (performance) Function parameter 'a2' should be passed by const reference.\n", errout_str()); + + check("std::map m;\n" // #10817 + "void f(const decltype(m)::const_iterator i) {}"); + ASSERT_EQUALS("", errout_str()); + + check("int (*pf) (std::vector) = nullptr;\n" // #12118 + "int f(std::vector v) {\n" + " return v.size();\n" + "}\n" + "void g() {\n" + " pf = f;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (performance) Function parameter 'v' should be passed by const reference. However it seems that 'f' is a callback function.\n", + errout_str()); + + check("template struct A;\n" // #12621 + "template\n" + "struct B { A a; };\n" + "template\n" + "struct A { B b; };\n" + "template\n" + "struct C : public virtual A, public virtual B {\n" + " A x;\n" + " B y;\n" + " C(A x_, B y_) : x(x_), y(y_) {}\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); // don't crash + } + + void checkComparisonFunctionIsAlwaysTrueOrFalse() { + // positive test + check("bool f(int x){\n" + " return isless(x,x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of two identical variables with isless(x,x) always evaluates to false.\n", errout_str()); + + check("bool f(int x){\n" + " return isgreater(x,x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of two identical variables with isgreater(x,x) always evaluates to false.\n", errout_str()); + + check("bool f(int x){\n" + " return islessgreater(x,x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of two identical variables with islessgreater(x,x) always evaluates to false.\n", errout_str()); + + check("bool f(int x){\n" + " return islessequal(x,x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of two identical variables with islessequal(x,x) always evaluates to true.\n", errout_str()); + + check("bool f(int x){\n" + " return isgreaterequal(x,x);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (warning) Comparison of two identical variables with isgreaterequal(x,x) always evaluates to true.\n", errout_str()); + + // no warning should be reported for + check("bool f(int x, int y){\n" + " return isgreaterequal(x,y) && islessequal(x,y) && islessgreater(x,y) && isgreater(x,y) && isless(x,y);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void integerOverflow() { // 5895 + // no signed integer overflow should happen + check("void f(unsigned long long ull) {\n" + " if (ull == 0x89504e470d0a1a0a || ull == 0x8a4d4e470d0a1a0a) ;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void redundantPointerOp() { + check("int *f(int *x) {\n" + " return &*x;\n" + "}\n", true, true); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant pointer operation on 'x' - it's already a pointer.\n", errout_str()); + + check("int *f(int *y) {\n" + " return &(*y);\n" + "}\n", true, true); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant pointer operation on 'y' - it's already a pointer.\n", errout_str()); + + check("int f() {\n" // #10991 + " int value = 4;\n" + " int result1 = *(&value);\n" + " int result2 = *&value;\n" + " return result1 + result2;\n" + "}\n", true, true); + ASSERT_EQUALS("[test.cpp:3]: (style) Redundant pointer operation on 'value' - it's already a variable.\n" + "[test.cpp:4]: (style) Redundant pointer operation on 'value' - it's already a variable.\n", + errout_str()); + + check("void f(int& a, int b) {\n" + " *(&a) = b;\n" + "}\n", true, true); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant pointer operation on 'a' - it's already a variable.\n", + errout_str()); + + check("void f(int**& p) {}\n", true, true); + ASSERT_EQUALS("", errout_str()); + + checkP("#define RESTORE(ORIG, COPY) { *ORIG = *COPY; }\n" + "void f(int* p, int i) {\n" + " RESTORE(p, &i);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // no warning for bitwise AND + check("void f(const int *b) {\n" + " int x = 0x20 & *b;\n" + "}\n", true, true); + ASSERT_EQUALS("", errout_str()); + + // No message for double pointers to structs + check("void f(struct foo **my_struct) {\n" + " char **pass_to_func = &(*my_struct)->buf;\n" + "}\n", true, true); + ASSERT_EQUALS("", errout_str()); + + // another double pointer to struct - with an array + check("void f(struct foo **my_struct) {\n" + " char **pass_to_func = &(*my_struct)->buf[10];\n" + "}\n", true, true); + ASSERT_EQUALS("", errout_str()); + + // double pointer to array + check("void f(char **ptr) {\n" + " int *x = &(*ptr)[10];\n" + "}\n", true, true); + ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'x' can be declared as pointer to const\n", errout_str()); + + // function calls + check("void f(Mutex *mut) {\n" + " pthread_mutex_lock(&*mut);\n" + "}\n", true, false); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant pointer operation on 'mut' - it's already a pointer.\n", errout_str()); + + // make sure we got the AST match for "(" right + check("void f(char *ptr) {\n" + " if (&*ptr == NULL)\n" + " return;\n" + "}\n", true, true); + ASSERT_EQUALS("[test.cpp:2]: (style) Redundant pointer operation on 'ptr' - it's already a pointer.\n", errout_str()); + + // no warning for macros + checkP("#define MUTEX_LOCK(m) pthread_mutex_lock(&(m))\n" + "void f(struct mutex *mut) {\n" + " MUTEX_LOCK(*mut);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + checkP("#define B(op) bar(op)\n" + "#define C(orf) B(&orf)\n" + "void foo(const int * pkey) {\n" + " C(*pkey);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void test_isSameExpression() { // see #5738 + check("bool isInUnoIncludeFile(StringRef name) {" + " return name.startswith(SRCDIR \"/com/\") || name.startswith(SRCDIR \"/uno/\");\n" + "};", true, false); + ASSERT_EQUALS("", errout_str()); + } + + void raceAfterInterlockedDecrement() { + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " InterlockedDecrement(&counter);\n" + " whatever();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " InterlockedDecrement(&counter);\n" + " if (counter)\n" + " return;\n" + " destroy();\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " InterlockedDecrement(&counter);\n" + " if (!counter)\n" + " destroy();\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " InterlockedDecrement(&counter);\n" + " if (counter > 0)\n" + " return;\n" + " destroy();\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " InterlockedDecrement(&counter);\n" + " if (0 < counter)\n" + " return;\n" + " destroy();\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " InterlockedDecrement(&counter);\n" + " if (counter == 0)\n" + " destroy();\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " InterlockedDecrement(&counter);\n" + " if (0 == counter)\n" + " destroy();\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " InterlockedDecrement(&counter);\n" + " if (0 != counter)\n" + " return;\n" + " destroy()\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " InterlockedDecrement(&counter);\n" + " if (counter != 0)\n" + " return;\n" + " destroy()\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " InterlockedDecrement(&counter);\n" + " if (counter <= 0)\n" + " destroy();\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " InterlockedDecrement(&counter);\n" + " if (0 >= counter)\n" + " destroy();\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " int newCount = InterlockedDecrement(&counter);\n" + " if (newCount)\n" + " return;\n" + " destroy();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " int newCount = InterlockedDecrement(&counter);\n" + " if (!newCount)\n" + " destroy();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " int newCount = InterlockedDecrement(&counter);\n" + " if (newCount > 0)\n" + " return;\n" + " destroy();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " int newCount = InterlockedDecrement(&counter);\n" + " if (0 < newCount)\n" + " return;\n" + " destroy();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " int newCount = InterlockedDecrement(&counter);\n" + " if (newCount == 0)\n" + " destroy();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " int newCount = InterlockedDecrement(&counter);\n" + " if (0 == newCount)\n" + " destroy();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " int newCount = InterlockedDecrement(&counter);\n" + " if (0 != newCount)\n" + " return;\n" + " destroy()\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " int newCount = InterlockedDecrement(&counter);\n" + " if (newCount != 0)\n" + " return;\n" + " destroy()\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " int newCount = InterlockedDecrement(&counter);\n" + " if (newCount <= 0)\n" + " destroy();\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkInterlockedDecrement("void f() {\n" + " int counter = 0;\n" + " int newCount = InterlockedDecrement(&counter);\n" + " if (0 >= newCount)\n" + " destroy;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + checkInterlockedDecrement("int f() {\n" + " int counter = 0;\n" + " if (InterlockedDecrement(&counter) == 0) {\n" + " destroy();\n" + " return 0;\n" + " } else {\n" + " return counter;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout_str()); + + checkInterlockedDecrement("int f() {\n" + " int counter = 0;\n" + " if (::InterlockedDecrement(&counter) == 0) {\n" + " destroy();\n" + " return 0;\n" + " } else {\n" + " return counter;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout_str()); + + + checkInterlockedDecrement("int f() {\n" + " int counter = 0;\n" + " if (InterlockedDecrement(&counter) == 0) {\n" + " destroy();\n" + " return 0;\n" + " }\n" + " return counter;\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout_str()); + + checkInterlockedDecrement("int f() {\n" + " int counter = 0;\n" + " if (::InterlockedDecrement(&counter) == 0) {\n" + " destroy();\n" + " return 0;\n" + " }\n" + " return counter;\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout_str()); + + checkInterlockedDecrement("int f() {\n" + " int counter = 0;\n" + " if (InterlockedDecrement(&counter) == 0) {\n" + " destroy();\n" + " return 0;\n" + " } else\n" + " return counter;\n" + " \n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout_str()); + + checkInterlockedDecrement("int f() {\n" + " int counter = 0;\n" + " if (::InterlockedDecrement(&counter) == 0) {\n" + " destroy();\n" + " return 0;\n" + " } else\n" + " return counter;\n" + " \n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (error) Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.\n", errout_str()); + } + + void testUnusedLabel() { + check("void f() {\n" + " label:\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Label 'label' is not used.\n", errout_str()); + + check("void f() {\n" + " label:\n" + " foo();\n" + " goto label;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " label:\n" + " foo();\n" + " goto label;\n" + "}\n" + "void g() {\n" + " label:\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (style) Label 'label' is not used.\n", errout_str()); + + check("void f() {\n" + " switch(a) {\n" + " default:\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " class X {\n" + " protected:\n" + " };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " class X {\n" + " my_protected:\n" + " };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int test(char art) {\n" + " switch (art) {\n" + " caseZERO:\n" + " return 0;\n" + " case1:\n" + " return 1;\n" + " case 2:\n" + " return 2;\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (warning) Label 'caseZERO' is not used. Should this be a 'case' of the enclosing switch()?\n" + "[test.cpp:5]: (warning) Label 'case1' is not used. Should this be a 'case' of the enclosing switch()?\n", errout_str()); + + check("int test(char art) {\n" + " switch (art) {\n" + " case 2:\n" + " return 2;\n" + " }\n" + " label:\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (style) Label 'label' is not used.\n", errout_str()); + } + + void testEvaluationOrder() { + check("void f() {\n" + " int x = dostuff();\n" + " return x + x++;\n" + "}", false); + ASSERT_EQUALS("[test.c:3]: (error) Expression 'x+x++' depends on order of evaluation of side effects\n", errout_str()); + + // #7226 + check("long int f1(const char *exp) {\n" + " return strtol(++exp, (char **)&exp, 10);\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + + check("long int f1(const char *exp) {\n" + " return dostuff(++exp, exp, 10);\n" + "}", false); + ASSERT_EQUALS("[test.c:2]: (error) Expression '++exp,exp' depends on order of evaluation of side effects\n", errout_str()); + + check("void f() {\n" + " int a;\n" + " while (a=x(), a==123) {}\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + + // # 8717 + check("void f(int argc, char *const argv[]) {\n" + " char **local_argv = safe_malloc(sizeof (*local_argv));\n" + " int local_argc = 0;\n" + " local_argv[local_argc++] = argv[0];\n" + "}\n", false); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " int x = 0;\n" + " return 0 + x++;\n" + "}\n", false); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x, int y) {\n" + " int a[10];\n" + " a[x+y] = a[y+x]++;;\n" + "}\n", false); + ASSERT_EQUALS("[test.c:3]: (error) Expression 'a[x+y]=a[y+x]++' depends on order of evaluation of side effects\n", errout_str()); + } + + void testEvaluationOrderSelfAssignment() { + // self assignment + check("void f() {\n" + " int x = x = y + 1;\n" + "}", false); + ASSERT_EQUALS( + "[test.c:2]: (style) Redundant assignment of 'x' to itself.\n" + "[test.c:2]: (style) Redundant assignment of 'x' to itself.\n", // duplicate + errout_str()); + } + + void testEvaluationOrderMacro() { + // macro, don't bailout (#7233) + checkP("#define X x\n" + "void f(int x) {\n" + " return x + X++;\n" + "}", "test.c"); + ASSERT_EQUALS("[test.c:3]: (error) Expression 'x+x++' depends on order of evaluation of side effects\n", errout_str()); + } + + void testEvaluationOrderSequencePointsFunctionCall() { + // FP + check("void f(int id) {\n" + " id = dostuff(id += 42);\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + + // FN + check("void f(int id) {\n" + " id = id + dostuff(id += 42);\n" + "}", false); + TODO_ASSERT_EQUALS("error", "", errout_str()); + } + + void testEvaluationOrderSequencePointsComma() { + check("int f(void) {\n" + " int t;\n" + " return (unsigned char)(t=1,t^c);\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + + check("void f(void) {\n" + " int t;\n" + " dostuff(t=1,t^c);\n" + "}", false); + ASSERT_EQUALS("[test.c:3]: (error) Expression 't=1,t^c' depends on order of evaluation of side effects\n", errout_str()); + + check("void f(void) {\n" + " int t;\n" + " dostuff((t=1,t),2);\n" + "}", false); + ASSERT_EQUALS("", errout_str()); + + // #8230 + check("void hprf(const char* fp) {\n" + " do\n" + " ;\n" + " while (++fp, (*fp) <= 0177);\n" + "}\n", false); + ASSERT_EQUALS("", errout_str()); + + check("void hprf(const char* fp) {\n" + " do\n" + " ;\n" + " while (i++, ++fp, (*fp) <= 0177);\n" + "}\n", false); + ASSERT_EQUALS("", errout_str()); + + check("void f(const char* fp) {\n" + " do\n" + " ;\n" + " while (f(++fp, (*fp) <= 7));\n" + "}\n", false); + ASSERT_EQUALS("[test.c:4]: (error) Expression '++fp,(*fp)<=7' depends on order of evaluation of side effects\n", errout_str()); + } + + void testEvaluationOrderSizeof() { + check("void f(char *buf) {\n" + " dostuff(buf++, sizeof(*buf));" + "}", false); + ASSERT_EQUALS("", errout_str()); + } + + void testUnsignedLessThanZero() { + check("struct d {\n" + " unsigned n;\n" + "};\n" + "void f(void) {\n" + " struct d d;\n" + " d.n = 3;\n" + "\n" + " if (d.n < 0) {\n" + " return;\n" + " }\n" + "\n" + " if (0 > d.n) {\n" + " return;\n" + " }\n" + "}", false); + ASSERT_EQUALS("[test.c:8]: (style) Checking if unsigned expression 'd.n' is less than zero.\n" + "[test.c:12]: (style) Checking if unsigned expression 'd.n' is less than zero.\n", + errout_str()); + } + + void doubleMove1() { + check("void g(A a);\n" + "void f() {\n" + " A a;\n" + " g(std::move(a));\n" + " g(std::move(a));\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'a'.\n", errout_str()); + } + + void doubleMoveMemberInitialization1() { + check("class A\n" + "{\n" + " A(B && b)\n" + " :b1(std::move(b))\n" + " {\n" + " b2 = std::move(b);\n" + " }\n" + " B b1;\n" + " B b2;\n" + "};"); + ASSERT_EQUALS("[test.cpp:6]: (warning) Access of moved variable 'b'.\n", errout_str()); + } + + void doubleMoveMemberInitialization2() { + check("class A\n" + "{\n" + " A(B && b)\n" + " :b1(std::move(b)),\n" + " b2(std::move(b))\n" + " {}\n" + " B b1;\n" + " B b2;\n" + "};"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'b'.\n", errout_str()); + } + + void doubleMoveMemberInitialization3() { // #9974 + check("struct A { int i; };\n" + "struct B { A a1; A a2; };\n" + "B f() {\n" + " A a1 = { 1 };\n" + " A a2 = { 2 };\n" + " return { .a1 = std::move(a1), .a2 = std::move(a2) };\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void doubleMoveMemberInitialization4() { // #11440 + check("struct S { void f(int); };\n" + "struct T {\n" + " T(int c, S&& d) : c{ c }, d{ std::move(d) } { d.f(c); }\n" + " int c;\n" + " S d;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Access of moved variable 'd'.\n", errout_str()); + } + + void moveAndAssign1() { + check("A g(A a);\n" + "void f() {\n" + " A a;\n" + " a = g(std::move(a));\n" + " a = g(std::move(a));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void moveAndAssign2() { + check("A g(A a);\n" + "void f() {\n" + " A a;\n" + " B b = g(std::move(a));\n" + " C c = g(std::move(a));\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'a'.\n", errout_str()); + } + + void moveAssignMoveAssign() { + check("void h(A a);\n" + "void f() {" + " A a;\n" + " g(std::move(a));\n" + " h(a);\n" + " a = b;\n" + " h(a);\n" + " g(std::move(a));\n" + " h(a);\n" + " a = b;\n" + " h(a);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Access of moved variable 'a'.\n" + "[test.cpp:8]: (warning) Access of moved variable 'a'.\n", errout_str()); + } + + void moveAndReset1() { + check("A g(A a);\n" + "void f() {\n" + " A a;\n" + " a.reset(g(std::move(a)));\n" + " a.reset(g(std::move(a)));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void moveAndReset2() { + check("A g(A a);\n" + "void f() {\n" + " A a;\n" + " A b;\n" + " A c;\n" + " b.reset(g(std::move(a)));\n" + " c.reset(g(std::move(a)));\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (warning) Access of moved variable 'a'.\n", errout_str()); + } + + void moveResetMoveReset() { + check("void h(A a);\n" + "void f() {" + " A a;\n" + " g(std::move(a));\n" + " h(a);\n" + " a.reset(b);\n" + " h(a);\n" + " g(std::move(a));\n" + " h(a);\n" + " a.reset(b);\n" + " h(a);\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Access of moved variable 'a'.\n" + "[test.cpp:8]: (warning) Access of moved variable 'a'.\n", errout_str()); + } + + void moveAndFunctionParameter() { + check("void g(A a);\n" + "void f() {\n" + " A a;\n" + " A b = std::move(a);\n" + " g(a);\n" + " A c = a;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'a'.\n" + "[test.cpp:6]: (warning) Access of moved variable 'a'.\n", errout_str()); + } + + void moveAndFunctionParameterReference() { + check("void g(A & a);\n" + "void f() {\n" + " A a;\n" + " A b = std::move(a);\n" + " g(a);\n" + " A c = a;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void moveAndFunctionParameterConstReference() { + check("void g(A const & a);\n" + "void f() {\n" + " A a;\n" + " A b = std::move(a);\n" + " g(a);\n" + " A c = a;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'a'.\n" + "[test.cpp:6]: (warning) Access of moved variable 'a'.\n", errout_str()); + } + + void moveAndFunctionParameterUnknown() { + check("void f() {\n" + " A a;\n" + " A b = std::move(a);\n" + " g(a);\n" + " A c = a;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) Access of moved variable 'a'.\n" + "[test.cpp:5]: (warning, inconclusive) Access of moved variable 'a'.\n", errout_str()); + } + + void moveAndReturn() { + check("int f(int i) {\n" + " A a;\n" + " A b;\n" + " g(std::move(a));\n" + " if (i)\n" + " return g(std::move(b));\n" + " return h(std::move(a),std::move(b));\n" + "}"); + ASSERT_EQUALS("[test.cpp:7]: (warning) Access of moved variable 'a'.\n", errout_str()); + } + + void moveAndClear() { + check("void f() {\n" + " V v;\n" + " g(std::move(v));\n" + " v.clear();\n" + " if (v.empty()) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void movedPointer() { + check("void f() {\n" + " P p;\n" + " g(std::move(p));\n" + " x = p->x;\n" + " y = p->y;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Access of moved variable 'p'.\n" + "[test.cpp:5]: (warning) Access of moved variable 'p'.\n", errout_str()); + } + + void moveAndAddressOf() { + check("void f() {\n" + " std::string s1 = x;\n" + " std::string s2 = std::move(s1);\n" + " p = &s1;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void partiallyMoved() { + check("void f() {\n" + " A a;\n" + " gx(std::move(a).x());\n" + " gy(std::move(a).y());\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void moveAndLambda() { + check("void f() {\n" + " A a;\n" + " auto h = [a=std::move(a)](){return g(std::move(a));};" + " b = a;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void moveInLoop() + { + check("void g(std::string&& s);\n" + "void f() {\n" + " std::string p;\n" + " while(true)\n" + " g(std::move(p));\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (warning) Access of moved variable 'p'.\n", errout_str()); + + check("std::list g(std::list&&);\n" + "void f(std::listl) {\n" + " for(int i = 0; i < 10; ++i) {\n" + " for (auto &j : g(std::move(l))) { (void)j; }\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Variable 'j' can be declared as reference to const\n" + "[test.cpp:4]: (warning) Access of moved variable 'l'.\n", + errout_str()); + } + + void moveCallback() + { + check("bool f(std::function&& callback);\n" + "void func(std::function callback) {\n" + " if(!f(std::move(callback)))\n" + " callback();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Access of moved variable 'callback'.\n", errout_str()); + } + + void moveClassVariable() + { + check("struct B {\n" + " virtual void f();\n" + "};\n" + "struct D : B {\n" + " void f() override {\n" + " auto p = std::unique_ptr(new D(std::move(m)));\n" + " }\n" + " D(std::unique_ptr c) : m(std::move(c)) {}\n" + " std::unique_ptr m;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void forwardAndUsed() { + check("template\n" + "void f(T && t) {\n" + " g(std::forward(t));\n" + " T s = t;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (warning) Access of forwarded variable 't'.\n", errout_str()); + } + + void moveAndReference() { // #9791 + check("void g(std::string&&);\n" + "void h(const std::string&);\n" + "void f() {\n" + " std::string s;\n" + " const std::string& r = s;\n" + " g(std::move(s));\n" + " h(r);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:7]: (warning) Access of moved variable 'r'.\n", errout_str()); + } + + void moveForRange() + { + check("struct C {\n" + " void f() {\n" + " for (auto r : mCategory.find(std::move(mWhere))) {}\n" + " }\n" + " cif::category mCategory;\n" + " cif::condition mWhere;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void moveTernary() + { + check("void gA(std::string);\n" // #12174 + "void gB(std::string);\n" + "void f(bool b) {\n" + " std::string s = \"abc\";\n" + " b ? gA(std::move(s)) : gB(std::move(s));\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int gA(std::string);\n" + "int gB(std::string);\n" + "void h(int);\n" + "void f(bool b) {\n" + " std::string s = \"abc\";\n" + " h(b ? gA(std::move(s)) : gB(std::move(s)));\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("int gA(int, std::string);\n" + "int gB(int, std::string);\n" + "int h(int);\n" + "void f(bool b) {\n" + " std::string s = \"abc\";\n" + " h(b ? h(gA(5, std::move(s))) : h(gB(7, std::move(s))));\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void funcArgNamesDifferent() { + check("void func1(int a, int b, int c);\n" + "void func1(int a, int b, int c) { }\n" + "void func2(int a, int b, int c);\n" + "void func2(int A, int B, int C) { }\n" + "class Fred {\n" + " void func1(int a, int b, int c);\n" + " void func2(int a, int b, int c);\n" + " void func3(int a = 0, int b = 0, int c = 0);\n" + " void func4(int a = 0, int b = 0, int c = 0);\n" + "};\n" + "void Fred::func1(int a, int b, int c) { }\n" + "void Fred::func2(int A, int B, int C) { }\n" + "void Fred::func3(int a, int b, int c) { }\n" + "void Fred::func4(int A, int B, int C) { }"); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Function 'func2' argument 1 names different: declaration 'a' definition 'A'.\n" + "[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Function 'func2' argument 2 names different: declaration 'b' definition 'B'.\n" + "[test.cpp:3] -> [test.cpp:4]: (style, inconclusive) Function 'func2' argument 3 names different: declaration 'c' definition 'C'.\n" + "[test.cpp:7] -> [test.cpp:12]: (style, inconclusive) Function 'func2' argument 1 names different: declaration 'a' definition 'A'.\n" + "[test.cpp:7] -> [test.cpp:12]: (style, inconclusive) Function 'func2' argument 2 names different: declaration 'b' definition 'B'.\n" + "[test.cpp:7] -> [test.cpp:12]: (style, inconclusive) Function 'func2' argument 3 names different: declaration 'c' definition 'C'.\n" + "[test.cpp:9] -> [test.cpp:14]: (style, inconclusive) Function 'func4' argument 1 names different: declaration 'a' definition 'A'.\n" + "[test.cpp:9] -> [test.cpp:14]: (style, inconclusive) Function 'func4' argument 2 names different: declaration 'b' definition 'B'.\n" + "[test.cpp:9] -> [test.cpp:14]: (style, inconclusive) Function 'func4' argument 3 names different: declaration 'c' definition 'C'.\n", errout_str()); + } + + void funcArgOrderDifferent() { + check("void func1(int a, int b, int c);\n" + "void func1(int a, int b, int c) { }\n" + "void func2(int a, int b, int c);\n" + "void func2(int c, int b, int a) { }\n" + "void func3(int, int b, int c);\n" + "void func3(int c, int b, int a) { }\n" + "class Fred {\n" + " void func1(int a, int b, int c);\n" + " void func2(int a, int b, int c);\n" + " void func3(int a = 0, int b = 0, int c = 0);\n" + " void func4(int, int b = 0, int c = 0);\n" + "};\n" + "void Fred::func1(int a, int b, int c) { }\n" + "void Fred::func2(int c, int b, int a) { }\n" + "void Fred::func3(int c, int b, int a) { }\n" + "void Fred::func4(int c, int b, int a) { }\n", + true, false); + ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Function 'func2' argument order different: declaration 'a, b, c' definition 'c, b, a'\n" + "[test.cpp:5] -> [test.cpp:6]: (warning) Function 'func3' argument order different: declaration ', b, c' definition 'c, b, a'\n" + "[test.cpp:9] -> [test.cpp:14]: (warning) Function 'func2' argument order different: declaration 'a, b, c' definition 'c, b, a'\n" + "[test.cpp:10] -> [test.cpp:15]: (warning) Function 'func3' argument order different: declaration 'a, b, c' definition 'c, b, a'\n" + "[test.cpp:11] -> [test.cpp:16]: (warning) Function 'func4' argument order different: declaration ', b, c' definition 'c, b, a'\n", errout_str()); + } + + // #7846 - Syntax error when using C++11 braced-initializer in default argument + void cpp11FunctionArgInit() { + // syntax error is not expected + ASSERT_NO_THROW(check("\n void foo(int declaration = {}) {" + "\n for (int i = 0; i < 10; i++) {}\n" + "\n }" + "\n ")); + ASSERT_EQUALS("", errout_str()); + } + + void shadowVariables() { + check("int x;\n" + "void f() { int x; }"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2]: (style) Local variable \'x\' shadows outer variable\n", errout_str()); + + check("int x();\n" + "void f() { int x; }"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:2]: (style) Local variable \'x\' shadows outer function\n", errout_str()); + + check("struct C {\n" + " C(int x) : x(x) {}\n" // <- we do not want a FP here + " int x;\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " if (cond) {int x;}\n" // <- not a shadow variable + " int x;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int size() {\n" + " int size;\n" // <- not a shadow variable + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" // #8954 - lambda + " int x;\n" + " auto f = [](){ int x; }" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int x) { int x; }"); + ASSERT_EQUALS("[test.cpp:1] -> [test.cpp:1]: (style) Local variable 'x' shadows outer argument\n", errout_str()); + + check("class C { C(); void foo() { static int C = 0; } }"); // #9195 - shadow constructor + ASSERT_EQUALS("", errout_str()); + + check("struct C {\n" // #10091 - shadow destructor + " ~C();\n" + " void f() {\n" + " bool C{};\n" + " }\n" + "};\n" + "C::~C() = default;"); + ASSERT_EQUALS("", errout_str()); + + // 10752 - no + check("struct S {\n" + " int i;\n" + "\n" + " static int foo() {\n" + " int i = 0;\n" + " return i;\n" + " }\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " int i{};\n" + " void f() { int i; }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3]: (style) Local variable 'i' shadows outer variable\n", errout_str()); + + check("struct S {\n" + " int i{};\n" + " std::vector v;\n" + " void f() const { for (const int& i : v) {} }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Local variable 'i' shadows outer variable\n", errout_str()); + + check("struct S {\n" // #10405 + " F* f{};\n" + " std::list fl;\n" + " void S::f() const;\n" + "};\n" + "void S::f() const {\n" + " for (const F& f : fl) {}\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:7]: (style) Local variable 'f' shadows outer variable\n", errout_str()); + + check("extern int a;\n" + "int a;\n" + "static int f(void) {\n" + " int a;\n" + " return 0;\n" + "}\n", false); + ASSERT_EQUALS("[test.c:1] -> [test.c:4]: (style) Local variable 'a' shadows outer variable\n", errout_str()); + + check("int f() {\n" // #12591 + " int g = 0;\n" + " return g;\n" + "}\n" + "int g() { return 1; }\n"); + ASSERT_EQUALS("", errout_str()); + } + + void knownArgument() { + check("void g(int);\n" + "void f(int x) {\n" + " g((x & 0x01) >> 7);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Argument '(x&0x01)>>7' to function g is always 0. It does not matter what value 'x' has.\n", errout_str()); + + check("void g(int);\n" + "void f(int x) {\n" + " g((int)((x & 0x01) >> 7));\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Argument '(int)((x&0x01)>>7)' to function g is always 0. It does not matter what value 'x' has.\n", errout_str()); + + check("void g(int, int);\n" + "void f(int x) {\n" + " g(x, (x & 0x01) >> 7);\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3]: (style) Argument '(x&0x01)>>7' to function g is always 0. It does not matter what value 'x' has.\n", + errout_str()); + + check("void g(int);\n" + "void f(int x) {\n" + " g(0);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void g(int);\n" + "void h() { return 1; }\n" + "void f(int x) {\n" + " g(h());\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void g(int);\n" + "void f(int x) {\n" + " g(std::strlen(\"a\"));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void g(int);\n" + "void f(int x) {\n" + " g((int)0);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void g(Foo *);\n" + "void f() {\n" + " g(reinterpret_cast(0));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void g(int);\n" + "void f(int x) {\n" + " x = 0;\n" + " g(x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void g(int);\n" + "void f() {\n" + " const int x = 0;\n" + " g(x + 1);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void g(int);\n" + "void f() {\n" + " char i = 1;\n" + " g(static_cast(i));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("char *yytext;\n" + "void re_init_scanner() {\n" + " int size = 256;\n" + " yytext = xmalloc(size * sizeof *yytext);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void foo(const char *c) {\n" + " if (*c == '+' && (operand || !isalnum(*c))) {}\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #8986 + check("void f(int);\n" + "void g() {\n" + " const int x[] = { 10, 10 };\n" + " f(x[0]);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int);\n" + "void g() {\n" + " int x[] = { 10, 10 };\n" + " f(x[0]);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'x' can be declared as const array\n", errout_str()); + + check("struct A { int x; };" + "void g(int);\n" + "void f(int x) {\n" + " A y;\n" + " y.x = 1;\n" + " g(y.x);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // allow known argument value in assert call + check("void g(int);\n" + "void f(int x) {\n" + " ASSERT((int)((x & 0x01) >> 7));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9905 - expression that does not use integer calculation at all + check("void foo() {\n" + " const std::string heading = \"Interval\";\n" + " std::cout << std::setw(heading.length());\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9909 - struct member with known value + check("struct LongStack {\n" + " int maxsize;\n" + "};\n" + "\n" + "void growLongStack(LongStack* self) {\n" + " self->maxsize = 32;\n" + " dostuff(self->maxsize * sizeof(intptr_t));\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #11894 + check("struct S {\n" + " int *p, n;\n" + "};\n" + "S* g() {\n" + " S* s = static_cast(calloc(1, sizeof(S)));\n" + " s->n = 100;\n" + " s->p = static_cast(malloc(s->n * sizeof(int)));\n" + " return s;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #11679 + check("bool g(int);\n" + "void h(int);\n" + "int k(int a) { h(a); return 0; }\n" + "void f(int i) {\n" + " if (g(k(i))) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #11889 + check("struct S {\n" + " int a[5];\n" + " void f(int i);\n" + "}\n" + "void g(int);\n" + "void S::f(int i) {\n" + " if (a[i] == 1) {\n" + " a[i] = 0;\n" + " g(a[i]);\n" + " }\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + // #11927 + check("void f(func_t func, int i) {\n" + " (func)(i, 0);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { void operator()(int, int); };\n" + "void f(int i) {\n" + " S()(i, 1);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int& r) {\n" + " g(static_cast(r = 42));\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct S { int i; };\n" + "void f(int i) {\n" + " const int a[] = { i - 1 * i, 0 };\n" + " auto s = S{ i - 1 * i };\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Argument 'i-1*i' to init list { is always 0. It does not matter what value 'i' has.\n" + "[test.cpp:4]: (style) Argument 'i-1*i' to constructor S is always 0. It does not matter what value 'i' has.\n", + errout_str()); + } + + void knownArgumentHiddenVariableExpression() { + // #9914 - variable expression is explicitly hidden + check("void f(int x) {\n" + " dostuff(x && false);\n" + " dostuff(false && x);\n" + " dostuff(x || true);\n" + " dostuff(true || x);\n" + " dostuff(x * 0);\n" + " dostuff(0 * x);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Argument 'false&&x' to function dostuff is always 0. Constant literal calculation disable/hide variable expression 'x'.\n" + "[test.cpp:5]: (style) Argument 'true||x' to function dostuff is always 1. Constant literal calculation disable/hide variable expression 'x'.\n" + "[test.cpp:6]: (style) Argument 'x*0' to function dostuff is always 0. Constant literal calculation disable/hide variable expression 'x'.\n" + "[test.cpp:7]: (style) Argument '0*x' to function dostuff is always 0. Constant literal calculation disable/hide variable expression 'x'.\n", errout_str()); + } + + void knownArgumentTernaryOperator() { // #10374 + check("void f(bool a, bool b) {\n" + " const T* P = nullptr; \n" + " long N = 0; \n" + " const bool c = foo(); \n" + " bar(P, N); \n" + " if (c ? a : b)\n" + " baz(P, N); \n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void checkComparePointers() { + check("int f() {\n" + " const int foo[1] = {0};\n" + " const int bar[1] = {0};\n" + " int diff = 0;\n" + " if(foo > bar) {\n" + " diff = 1;\n" + " }\n" + " return diff;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:5] -> [test.cpp:3] -> [test.cpp:5] -> [test.cpp:5]: (error) Comparing pointers that point to different objects\n", + errout_str()); + + check("bool f() {\n" + " int x = 0;\n" + " int y = 0;\n" + " int* xp = &x;\n" + " int* yp = &y;\n" + " return xp > yp;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:4] -> [test.cpp:3] -> [test.cpp:5] -> [test.cpp:6]: (error) Comparing pointers that point to different objects\n" + "[test.cpp:4]: (style) Variable 'xp' can be declared as pointer to const\n" + "[test.cpp:5]: (style) Variable 'yp' can be declared as pointer to const\n", + errout_str()); + + check("bool f() {\n" + " int x = 0;\n" + " int y = 1;\n" + " return &x > &y;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:4] -> [test.cpp:3] -> [test.cpp:4] -> [test.cpp:4]: (error) Comparing pointers that point to different objects\n", + errout_str()); + + check("struct A {int data;};\n" + "bool f() {\n" + " A x;\n" + " A y;\n" + " int* xp = &x.data;\n" + " int* yp = &y.data;\n" + " return xp > yp;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:3] -> [test.cpp:5] -> [test.cpp:4] -> [test.cpp:6] -> [test.cpp:7]: (error) Comparing pointers that point to different objects\n" + "[test.cpp:5]: (style) Variable 'xp' can be declared as pointer to const\n" + "[test.cpp:6]: (style) Variable 'yp' can be declared as pointer to const\n", + errout_str()); + + check("struct A {int data;};\n" + "bool f(A ix, A iy) {\n" + " A* x = &ix;\n" + " A* y = &iy;\n" + " int* xp = &x->data;\n" + " int* yp = &y->data;\n" + " return xp > yp;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:3] -> [test.cpp:5] -> [test.cpp:2] -> [test.cpp:4] -> [test.cpp:6] -> [test.cpp:7]: (error) Comparing pointers that point to different objects\n" + "[test.cpp:5]: (style) Variable 'xp' can be declared as pointer to const\n" + "[test.cpp:6]: (style) Variable 'yp' can be declared as pointer to const\n", + errout_str()); + + check("bool f(int * xp, int* yp) {\n" + " return &xp > &yp;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:1] -> [test.cpp:2] -> [test.cpp:1] -> [test.cpp:2] -> [test.cpp:2]: (error) Comparing pointers that point to different objects\n", + errout_str()); + + check("int f() {\n" + " int x = 0;\n" + " int y = 1;\n" + " return &x - &y;\n" + "}"); + ASSERT_EQUALS( + "[test.cpp:2] -> [test.cpp:4] -> [test.cpp:3] -> [test.cpp:4] -> [test.cpp:4]: (error) Subtracting pointers that point to different objects\n", + errout_str()); + + check("bool f() {\n" + " int x[2] = {1, 2}m;\n" + " int* xp = &x[0];\n" + " int* yp = &x[1];\n" + " return xp > yp;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (style) Variable 'xp' can be declared as pointer to const\n" + "[test.cpp:4]: (style) Variable 'yp' can be declared as pointer to const\n", + errout_str()); + + check("bool f(const int * xp, const int* yp) {\n" + " return xp > yp;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("bool f(const int & x, const int& y) {\n" + " return &x > &y;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("int& g();\n" + "bool f() {\n" + " const int& x = g();\n" + " const int& y = g();\n" + " const int* xp = &x;\n" + " const int* yp = &y;\n" + " return xp > yp;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("struct A {int data;};\n" + "bool f(A ix) {\n" + " A* x = &ix;\n" + " A* y = x;\n" + " int* xp = &x->data;\n" + " int* yp = &y->data;\n" + " return xp > yp;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (style) Variable 'xp' can be declared as pointer to const\n" + "[test.cpp:6]: (style) Variable 'yp' can be declared as pointer to const\n", + errout_str()); + + check("struct S { int i; };\n" // #11576 + "int f(S s) {\n" + " return &s.i - (int*)&s;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) C-style pointer casting\n", errout_str()); + + check("struct S { int i; };\n" + "int f(S s1, S s2) {\n" + " return &s1.i - reinterpret_cast(&s2);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:3] -> [test.cpp:2] -> [test.cpp:3] -> [test.cpp:3]: (error) Subtracting pointers that point to different objects\n", + errout_str()); + + check("struct S { int a; int b; };\n" // #12422 + "int f() {\n" + " S s;\n" + " return &s.b - &s.a;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void unusedVariableValueTemplate() { + check("#include \n" + "class A\n" + "{\n" + "public:\n" + " class Hash\n" + " {\n" + " public:\n" + " std::size_t operator()(const A& a) const\n" + " {\n" + " (void)a;\n" + " return 0;\n" + " }\n" + " };\n" + "};\n" + "namespace std\n" + "{\n" + " template <>\n" + " struct hash\n" + " {\n" + " std::size_t operator()(const A& a) const noexcept\n" + " {\n" + " return A::Hash{}(a);\n" + " }\n" + " };\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void moduloOfOne() { + check("void f(unsigned int x) {\n" + " int y = x % 1;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (style) Modulo of one is always equal to zero\n", errout_str()); + + check("void f() {\n" + " for (int x = 1; x < 10; x++) {\n" + " int y = 100 % x;\n" + " }\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("void f(int i, int j) {\n" // #11191 + " const int c = pow(2, i);\n" + " if (j % c) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void sameExpressionPointers() { + check("int f(int *i);\n" + "void g(int *a, const int *b) {\n" + " int c = *a;\n" + " f(a);\n" + " if (b && c != *a) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void checkOverlappingWrite() { + // union + check("void foo() {\n" + " union { int i; float f; } u;\n" + " u.i = 0;\n" + " u.i = u.f;\n" // <- error + "}"); + ASSERT_EQUALS("[test.cpp:4]: (error) Overlapping read/write of union is undefined behavior\n", errout_str()); + + check("void foo() {\n" // #11013 + " union { struct { uint8_t a; uint8_t b; }; uint16_t c; } u;\n" + " u.a = u.b = 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // memcpy + check("void foo() {\n" + " char a[10];\n" + " memcpy(&a[5], &a[4], 2u);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Overlapping read/write in memcpy() is undefined behavior\n", errout_str()); + + check("void foo() {\n" + " char a[10];\n" + " memcpy(a+5, a+4, 2u);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Overlapping read/write in memcpy() is undefined behavior\n", errout_str()); + + check("void foo() {\n" + " char a[10];\n" + " memcpy(a, a+1, 2u);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Overlapping read/write in memcpy() is undefined behavior\n", errout_str()); + + check("void foo() {\n" + " char a[8];\n" + " memcpy(&a[0], &a[4], 4u);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("_Bool a[10];\n" // #10350 + "void foo() {\n" + " memcpy(&a[5], &a[4], 2u * sizeof(a[0]));\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Overlapping read/write in memcpy() is undefined behavior\n", errout_str()); + + // wmemcpy + check("void foo() {\n" + " wchar_t a[10];\n" + " wmemcpy(&a[5], &a[4], 2u);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Overlapping read/write in wmemcpy() is undefined behavior\n", errout_str()); + + check("void foo() {\n" + " wchar_t a[10];\n" + " wmemcpy(a+5, a+4, 2u);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Overlapping read/write in wmemcpy() is undefined behavior\n", errout_str()); + + check("void foo() {\n" + " wchar_t a[10];\n" + " wmemcpy(a, a+1, 2u);\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (error) Overlapping read/write in wmemcpy() is undefined behavior\n", errout_str()); + + // strcpy + check("void foo(char *ptr) {\n" + " strcpy(ptr, ptr);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Overlapping read/write in strcpy() is undefined behavior\n", errout_str()); + } + + void constVariableArrayMember() { // #10371 + check("class Foo {\n" + "public:\n" + " Foo();\n" + " int GetVal() const { return m_Arr[0]; }\n" + " int m_Arr[1];\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void knownPointerToBool() + { + check("void g(bool);\n" + "void f() {\n" + " int i = 5;\n" + " int* p = &i;\n" + " g(p);\n" + " g(&i);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5]: (style) Pointer expression 'p' converted to bool is always true.\n" + "[test.cpp:6]: (style) Pointer expression '&i' converted to bool is always true.\n", + errout_str()); + + check("void f() {\n" + " const int* x = nullptr;\n" + " std::empty(x);\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("void f() {\n" + " const int* x = nullptr;\n" + " std::empty(const_cast(x));\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { bool x; };\n" + "bool f(A* a) {\n" + " if (a) {\n" + " return a->x;\n" + " }\n" + " return false;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int* x; };\n" + "bool f(A a) {\n" + " if (a.x) {\n" + " return a.x;\n" + " }\n" + " return false;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Pointer expression 'a.x' converted to bool is always true.\n", errout_str()); + + check("void f(bool* b) { if (b) *b = true; }"); + ASSERT_EQUALS("", errout_str()); + + check("bool f() {\n" + " int* x = nullptr;\n" + " return bool(x);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Pointer expression 'x' converted to bool is always false.\n", errout_str()); + + check("bool f() {\n" + " int* x = nullptr;\n" + " return bool{x};\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (style) Pointer expression 'x' converted to bool is always false.\n", errout_str()); + + check("struct A { A(bool); };\n" + "A f() {\n" + " int* x = nullptr;\n" + " return A(x);\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Pointer expression 'x' converted to bool is always false.\n", errout_str()); + + check("struct A { A(bool); };\n" + "A f() {\n" + " int* x = nullptr;\n" + " return A{x};\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4]: (style) Pointer expression 'x' converted to bool is always false.\n", errout_str()); + + check("struct B { virtual void f() {} };\n" // #11929 + "struct D : B {};\n" + "void g(B* b) {\n" + " if (!b)\n" + " return;\n" + " if (dynamic_cast(b)) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("bool (*ptr)();\n" // #12170 + "void f() {\n" + " if (!ptr || !ptr()) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + } + + void iterateByValue() { + check("void f() {\n" // #9684 + " const std::set ss = { \"a\", \"b\", \"c\" };\n" + " for (auto s : ss)\n" + " (void)s.size();\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3]: (performance) Range variable 's' should be declared as const reference.\n", + errout_str()); + } +}; + +REGISTER_TEST(TestOther) diff --git a/cppcheck-2.14.0/test/testpath.cpp b/cppcheck-2.14.0/test/testpath.cpp new file mode 100644 index 00000000..0bd3ae2f --- /dev/null +++ b/cppcheck-2.14.0/test/testpath.cpp @@ -0,0 +1,386 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "path.h" +#include "fixture.h" +#include "helpers.h" +#include "standards.h" + +#include +#include +#include + +class TestPath : public TestFixture { +public: + TestPath() : TestFixture("TestPath") {} + +private: + + void run() override { + TEST_CASE(removeQuotationMarks); + TEST_CASE(acceptFile); + TEST_CASE(getCurrentPath); + TEST_CASE(getCurrentExecutablePath); + TEST_CASE(isAbsolute); + TEST_CASE(getRelative); + TEST_CASE(is_c); + TEST_CASE(is_cpp); + TEST_CASE(is_header); + TEST_CASE(get_path_from_filename); + TEST_CASE(join); + TEST_CASE(isDirectory); + TEST_CASE(isFile); + TEST_CASE(sameFileName); + TEST_CASE(getFilenameExtension); + TEST_CASE(identify); + TEST_CASE(is_header_2); + } + + void removeQuotationMarks() const { + // Path::removeQuotationMarks() + ASSERT_EQUALS("index.cpp", Path::removeQuotationMarks("index.cpp")); + ASSERT_EQUALS("index.cpp", Path::removeQuotationMarks("\"index.cpp")); + ASSERT_EQUALS("index.cpp", Path::removeQuotationMarks("index.cpp\"")); + ASSERT_EQUALS("index.cpp", Path::removeQuotationMarks("\"index.cpp\"")); + ASSERT_EQUALS("path to/index.cpp", Path::removeQuotationMarks("\"path to\"/index.cpp")); + ASSERT_EQUALS("path to/index.cpp", Path::removeQuotationMarks("\"path to/index.cpp\"")); + ASSERT_EQUALS("the/path to/index.cpp", Path::removeQuotationMarks("the/\"path to\"/index.cpp")); + ASSERT_EQUALS("the/path to/index.cpp", Path::removeQuotationMarks("\"the/path to/index.cpp\"")); + } + + void acceptFile() const { + ASSERT(Path::acceptFile("index.c")); + 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")); + + const std::set extra = { ".extra", ".header" }; + ASSERT(Path::acceptFile("index.c", extra)); + ASSERT(Path::acceptFile("index.cpp", extra)); + ASSERT(Path::acceptFile("index.extra", extra)); + ASSERT(Path::acceptFile("index.header", extra)); + ASSERT(Path::acceptFile("index.h", extra)==false); + ASSERT(Path::acceptFile("index.hpp", extra)==false); + } + + void getCurrentPath() const { + ASSERT_EQUALS(true, Path::isAbsolute(Path::getCurrentPath())); + } + + void getCurrentExecutablePath() const { + ASSERT_EQUALS(false, Path::getCurrentExecutablePath("").empty()); + } + + void isAbsolute() const { +#ifdef _WIN32 + ASSERT_EQUALS(true, Path::isAbsolute("C:\\foo\\bar")); + ASSERT_EQUALS(true, Path::isAbsolute("C:/foo/bar")); + ASSERT_EQUALS(true, Path::isAbsolute("\\\\foo\\bar")); + ASSERT_EQUALS(false, Path::isAbsolute("foo\\bar")); + ASSERT_EQUALS(false, Path::isAbsolute("foo/bar")); + ASSERT_EQUALS(false, Path::isAbsolute("foo.cpp")); + ASSERT_EQUALS(false, Path::isAbsolute("C:foo.cpp")); + ASSERT_EQUALS(false, Path::isAbsolute("C:foo\\bar.cpp")); + ASSERT_EQUALS(false, Path::isAbsolute("bar.cpp")); + TODO_ASSERT_EQUALS(true, false, Path::isAbsolute("\\")); +#else + ASSERT_EQUALS(true, Path::isAbsolute("/foo/bar")); + ASSERT_EQUALS(true, Path::isAbsolute("/")); + ASSERT_EQUALS(false, Path::isAbsolute("foo/bar")); + ASSERT_EQUALS(false, Path::isAbsolute("foo.cpp")); +#endif + } + + void getRelative() const { + const std::vector basePaths = { + "", // Don't crash with empty paths + "C:/foo", + "C:/bar/", + "C:/test.cpp" + }; + + ASSERT_EQUALS("x.c", Path::getRelativePath("C:/foo/x.c", basePaths)); + ASSERT_EQUALS("y.c", Path::getRelativePath("C:/bar/y.c", basePaths)); + ASSERT_EQUALS("foo/y.c", Path::getRelativePath("C:/bar/foo/y.c", basePaths)); + ASSERT_EQUALS("C:/test.cpp", Path::getRelativePath("C:/test.cpp", basePaths)); + ASSERT_EQUALS("C:/foobar/test.cpp", Path::getRelativePath("C:/foobar/test.cpp", basePaths)); + } + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" +#endif + + void is_c() const { + ASSERT(Path::isC("index.c")); + ASSERT(Path::isC("index.cl")); + ASSERT(Path::isC("C:\\foo\\index.c")); + ASSERT(Path::isC("/mnt/c/foo/index.c")); + + // In unix .C is considered C++ +#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) + ASSERT_EQUALS(true, Path::isC("C:\\foo\\index.C")); +#else + ASSERT_EQUALS(false, Path::isC("C:\\foo\\index.C")); +#endif + + ASSERT(Path::isC("index.cpp")==false); + ASSERT(Path::isC("")==false); + ASSERT(Path::isC("c")==false); + + // unlike isCPP() it does not account for headers + ASSERT(Path::isC(".h")==false); + } + + void is_cpp() const { + ASSERT(Path::isCPP("index.cpp")); + ASSERT(Path::isCPP("index.cxx")); + ASSERT(Path::isCPP("index.cc")); + ASSERT(Path::isCPP("index.c++")); + ASSERT(Path::isCPP("index.tpp")); + ASSERT(Path::isCPP("index.txx")); + ASSERT(Path::isCPP("index.ipp")); + ASSERT(Path::isCPP("index.ixx")); + ASSERT(Path::isCPP("C:\\foo\\index.cpp")); + ASSERT(Path::isCPP("C:\\foo\\index.Cpp")); + ASSERT(Path::isCPP("/mnt/c/foo/index.cpp")); + ASSERT(Path::isCPP("/mnt/c/foo/index.Cpp")); + + // In unix .C is considered C++ +#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) + ASSERT_EQUALS(false, Path::isCPP("index.C")); +#else + ASSERT_EQUALS(true, Path::isCPP("index.C")); +#endif + + ASSERT(Path::isCPP("index.c")==false); + + // C++ headers are also considered C++ + ASSERT(Path::isCPP("index.hpp")); + // .h++ is missing in the list of C++ headers + ASSERT(Path::isCPP("index.h++")==false); + } + + void is_header() const { + ASSERT(Path::isHeader("index.h")); + ASSERT(Path::isHeader("index.hpp")); + ASSERT(Path::isHeader("index.hxx")); + ASSERT(Path::isHeader("index.h++")); + ASSERT(Path::isHeader("index.hh")); + + ASSERT(Path::isHeader("index.c")==false); + ASSERT(Path::isHeader("index.cpp")==false); + + // function uses heuristic approach which causes these false positives + // no need to fix - function is deprecated and was replaced by identify() + TODO_ASSERT(Path::isHeader("index.header")==false); + TODO_ASSERT(Path::isHeader("index.htm")==false); + TODO_ASSERT(Path::isHeader("index.html")==false); + } + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + 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/")); + } + + void join() const { + ASSERT_EQUALS("a", Path::join("a", "")); + ASSERT_EQUALS("a", Path::join("", "a")); + ASSERT_EQUALS("a/b", Path::join("a", "b")); + ASSERT_EQUALS("a/b", Path::join("a/", "b")); + ASSERT_EQUALS("/b", Path::join("a", "/b")); + } + + void isDirectory() const { + ScopedFile file("testpath.txt", "", "testpath"); + ScopedFile file2("testpath2.txt", ""); + ASSERT_EQUALS(false, Path::isDirectory("testpath.txt")); + ASSERT_EQUALS(true, Path::isDirectory("testpath")); + ASSERT_EQUALS(false, Path::isDirectory("testpath/testpath.txt")); + ASSERT_EQUALS(false, Path::isDirectory("testpath2.txt")); + } + + void isFile() const { + ScopedFile file("testpath.txt", "", "testpath"); + ScopedFile file2("testpath2.txt", ""); + ASSERT_EQUALS(false, Path::isFile("testpath")); + ASSERT_EQUALS(false, Path::isFile("testpath.txt")); + ASSERT_EQUALS(true, Path::isFile("testpath/testpath.txt")); + ASSERT_EQUALS(true, Path::isFile("testpath2.txt")); + } + + void sameFileName() const { + ASSERT(Path::sameFileName("test", "test")); + + // case sensitivity cases +#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) + ASSERT(Path::sameFileName("test", "Test")); + ASSERT(Path::sameFileName("test", "TesT")); + ASSERT(Path::sameFileName("test.h", "test.H")); + ASSERT(Path::sameFileName("test.hh", "test.Hh")); + ASSERT(Path::sameFileName("test.hh", "test.hH")); +#else + ASSERT(!Path::sameFileName("test", "Test")); + ASSERT(!Path::sameFileName("test", "TesT")); + ASSERT(!Path::sameFileName("test.h", "test.H")); + ASSERT(!Path::sameFileName("test.hh", "test.Hh")); + ASSERT(!Path::sameFileName("test.hh", "test.hH")); +#endif + } + + void getFilenameExtension() const { + ASSERT_EQUALS("", Path::getFilenameExtension("test")); + ASSERT_EQUALS("", Path::getFilenameExtension("Test")); + ASSERT_EQUALS(".h", Path::getFilenameExtension("test.h")); + ASSERT_EQUALS(".h", Path::getFilenameExtension("Test.h")); + ASSERT_EQUALS("", Path::getFilenameExtension("test", true)); + ASSERT_EQUALS("", Path::getFilenameExtension("Test", true)); + ASSERT_EQUALS(".h", Path::getFilenameExtension("test.h", true)); + ASSERT_EQUALS(".h", Path::getFilenameExtension("Test.h", true)); + + // case sensitivity cases +#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) + ASSERT_EQUALS(".h", Path::getFilenameExtension("test.H")); + ASSERT_EQUALS(".hh", Path::getFilenameExtension("test.Hh")); + ASSERT_EQUALS(".hh", Path::getFilenameExtension("test.hH")); + ASSERT_EQUALS(".h", Path::getFilenameExtension("test.H", true)); + ASSERT_EQUALS(".hh", Path::getFilenameExtension("test.Hh", true)); + ASSERT_EQUALS(".hh", Path::getFilenameExtension("test.hH", true)); +#else + ASSERT_EQUALS(".H", Path::getFilenameExtension("test.H")); + ASSERT_EQUALS(".Hh", Path::getFilenameExtension("test.Hh")); + ASSERT_EQUALS(".hH", Path::getFilenameExtension("test.hH")); + ASSERT_EQUALS(".h", Path::getFilenameExtension("test.H", true)); + ASSERT_EQUALS(".hh", Path::getFilenameExtension("test.Hh", true)); + ASSERT_EQUALS(".hh", Path::getFilenameExtension("test.hH", true)); +#endif + } + + + void identify() const { + Standards::Language lang; + bool header; + + ASSERT_EQUALS(Standards::Language::None, Path::identify("")); + ASSERT_EQUALS(Standards::Language::None, Path::identify("c")); + ASSERT_EQUALS(Standards::Language::None, Path::identify("cpp")); + ASSERT_EQUALS(Standards::Language::None, Path::identify("h")); + ASSERT_EQUALS(Standards::Language::None, Path::identify("hpp")); + + // TODO: what about files starting with a "."? + //ASSERT_EQUALS(Standards::Language::None, Path::identify(".c")); + //ASSERT_EQUALS(Standards::Language::None, Path::identify(".cpp")); + //ASSERT_EQUALS(Standards::Language::None, Path::identify(".h")); + //ASSERT_EQUALS(Standards::Language::None, Path::identify(".hpp")); + + // C + ASSERT_EQUALS(Standards::Language::C, Path::identify("index.c")); + ASSERT_EQUALS(Standards::Language::C, Path::identify("index.cl")); + ASSERT_EQUALS(Standards::Language::C, Path::identify("C:\\foo\\index.c")); + ASSERT_EQUALS(Standards::Language::C, Path::identify("/mnt/c/foo/index.c")); + + // In unix .C is considered C++ +#ifdef _WIN32 + ASSERT_EQUALS(Standards::Language::C, Path::identify("C:\\foo\\index.C")); +#endif + + lang = Path::identify("index.c", &header); + ASSERT_EQUALS(Standards::Language::C, lang); + ASSERT_EQUALS(false, header); + + // C++ + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.cpp")); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.cxx")); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.cc")); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.c++")); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.tpp")); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.txx")); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.ipp")); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.ixx")); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("C:\\foo\\index.cpp")); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("C:\\foo\\index.Cpp")); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("/mnt/c/foo/index.cpp")); + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("/mnt/c/foo/index.Cpp")); + + // TODO: check for case-insenstive filesystem instead + // In unix .C is considered C++ +#if !defined(_WIN32) && !(defined(__APPLE__) && defined(__MACH__)) + ASSERT_EQUALS(Standards::Language::CPP, Path::identify("index.C")); +#else + ASSERT_EQUALS(Standards::Language::C, Path::identify("index.C")); +#endif + + lang = Path::identify("index.cpp", &header); + ASSERT_EQUALS(Standards::Language::CPP, lang); + ASSERT_EQUALS(false, header); + + // headers + lang = Path::identify("index.h", &header); + ASSERT_EQUALS(Standards::Language::C, lang); + ASSERT_EQUALS(true, header); + + lang = Path::identify("index.hpp", &header); + ASSERT_EQUALS(Standards::Language::CPP, lang); + ASSERT_EQUALS(true, header); + lang = Path::identify("index.hxx", &header); + ASSERT_EQUALS(Standards::Language::CPP, lang); + ASSERT_EQUALS(true, header); + lang = Path::identify("index.h++", &header); + ASSERT_EQUALS(Standards::Language::CPP, lang); + ASSERT_EQUALS(true, header); + lang = Path::identify("index.hh", &header); + ASSERT_EQUALS(Standards::Language::CPP, lang); + ASSERT_EQUALS(true, header); + + ASSERT_EQUALS(Standards::Language::None, Path::identify("index.header")); + ASSERT_EQUALS(Standards::Language::None, Path::identify("index.htm")); + ASSERT_EQUALS(Standards::Language::None, Path::identify("index.html")); + } + + void is_header_2() const { + ASSERT(Path::isHeader2("index.h")); + ASSERT(Path::isHeader2("index.hpp")); + ASSERT(Path::isHeader2("index.hxx")); + ASSERT(Path::isHeader2("index.h++")); + ASSERT(Path::isHeader2("index.hh")); + + ASSERT(Path::isHeader2("index.c")==false); + ASSERT(Path::isHeader2("index.cpp")==false); + ASSERT(Path::isHeader2("index.header")==false); + ASSERT(Path::isHeader2("index.htm")==false); + ASSERT(Path::isHeader2("index.html")==false); + } +}; + +REGISTER_TEST(TestPath) diff --git a/cppcheck-2.14.0/test/testpathmatch.cpp b/cppcheck-2.14.0/test/testpathmatch.cpp new file mode 100644 index 00000000..41c602d7 --- /dev/null +++ b/cppcheck-2.14.0/test/testpathmatch.cpp @@ -0,0 +1,202 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public 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 "fixture.h" + +#include +#include +#include + + +class TestPathMatch : public TestFixture { +public: + TestPathMatch() : TestFixture("TestPathMatch") {} + +private: + const PathMatch emptyMatcher{std::vector()}; + const PathMatch srcMatcher{std::vector(1, "src/")}; + const PathMatch fooCppMatcher{std::vector(1, "foo.cpp")}; + const PathMatch srcFooCppMatcher{std::vector(1, "src/foo.cpp")}; + + void run() override { + TEST_CASE(emptymaskemptyfile); + TEST_CASE(emptymaskpath1); + TEST_CASE(emptymaskpath2); + TEST_CASE(emptymaskpath3); + TEST_CASE(onemaskemptypath); + TEST_CASE(onemasksamepath); + TEST_CASE(onemasksamepathdifferentcase); + TEST_CASE(onemasksamepathwithfile); + TEST_CASE(onemaskshorterpath); + 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(std::move(masks), false); + ASSERT(match.match("srC/")); + } + + void onemasksamepathwithfile() const { + ASSERT(srcMatcher.match("src/file.txt")); + } + + void onemaskshorterpath() const { + const std::string longerExclude("longersrc/"); + const std::string shorterToMatch("src/"); + ASSERT(shorterToMatch.length() < longerExclude.length()); + PathMatch match(std::vector(1, longerExclude)); + ASSERT(match.match(longerExclude)); + ASSERT(!match.match(shorterToMatch)); + } + + void onemaskdifferentdir1() const { + ASSERT(!srcMatcher.match("srcfiles/file.txt")); + } + + void onemaskdifferentdir2() const { + ASSERT(!srcMatcher.match("proj/srcfiles/file.txt")); + } + + void onemaskdifferentdir3() const { + ASSERT(!srcMatcher.match("proj/mysrc/file.txt")); + } + + void onemaskdifferentdir4() const { + ASSERT(!srcMatcher.match("proj/mysrcfiles/file.txt")); + } + + void onemasklongerpath1() const { + ASSERT(srcMatcher.match("/tmp/src/")); + } + + void onemasklongerpath2() const { + ASSERT(srcMatcher.match("src/module/")); + } + + void onemasklongerpath3() const { + ASSERT(srcMatcher.match("project/src/module/")); + } + + void twomasklongerpath1() const { + std::vector masks = { "src/", "module/" }; + PathMatch match(std::move(masks)); + ASSERT(!match.match("project/")); + } + + void twomasklongerpath2() const { + std::vector masks = { "src/", "module/" }; + PathMatch match(std::move(masks)); + ASSERT(match.match("project/src/")); + } + + void twomasklongerpath3() const { + std::vector masks = { "src/", "module/" }; + PathMatch match(std::move(masks)); + ASSERT(match.match("project/module/")); + } + + void twomasklongerpath4() const { + std::vector masks = { "src/", "module/" }; + PathMatch match(std::move(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(std::move(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) diff --git a/cppcheck-2.14.0/test/testplatform.cpp b/cppcheck-2.14.0/test/testplatform.cpp new file mode 100644 index 00000000..917e64b5 --- /dev/null +++ b/cppcheck-2.14.0/test/testplatform.cpp @@ -0,0 +1,437 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "platform.h" +#include "fixture.h" +#include "standards.h" + +#include + +#include "xml.h" + + +class TestPlatform : public TestFixture { +public: + TestPlatform() : TestFixture("TestPlatform") {} + +private: + void run() override { + TEST_CASE(empty); + TEST_CASE(valid_config_win32a); + TEST_CASE(valid_config_unix64); + TEST_CASE(valid_config_win32w); + TEST_CASE(valid_config_unix32); + TEST_CASE(valid_config_win64); + TEST_CASE(valid_config_file_1); + TEST_CASE(valid_config_file_2); + TEST_CASE(valid_config_file_3); + TEST_CASE(valid_config_file_4); + TEST_CASE(invalid_config_file_1); + TEST_CASE(empty_elements); + TEST_CASE(default_platform); + TEST_CASE(limitsDefines); + TEST_CASE(charMinMax); + TEST_CASE(no_root_node); + TEST_CASE(wrong_root_node); + } + + static bool readPlatform(Platform& platform, const char* xmldata) { + tinyxml2::XMLDocument doc; + return (doc.Parse(xmldata) == tinyxml2::XML_SUCCESS) && platform.loadFromXmlDocument(&doc); + } + + void empty() const { + // An empty platform file does not change values, only the type. + constexpr char xmldata[] = "\n"; + Platform platform; + // TODO: this should fail - platform files need to be complete + TODO_ASSERT(!readPlatform(platform, xmldata)); + } + + void valid_config_win32a() const { + // Verify if native Win32A platform is loaded correctly + Platform platform; + PLATFORM(platform, Platform::Type::Win32A); + ASSERT_EQUALS(Platform::Type::Win32A, platform.type); + ASSERT(platform.isWindows()); + ASSERT_EQUALS(1, platform.sizeof_bool); + ASSERT_EQUALS(2, platform.sizeof_short); + ASSERT_EQUALS(4, platform.sizeof_int); + ASSERT_EQUALS(4, platform.sizeof_long); + ASSERT_EQUALS(8, platform.sizeof_long_long); + ASSERT_EQUALS(4, platform.sizeof_float); + ASSERT_EQUALS(8, platform.sizeof_double); + ASSERT_EQUALS(8, platform.sizeof_long_double); + ASSERT_EQUALS(2, platform.sizeof_wchar_t); + ASSERT_EQUALS(4, platform.sizeof_size_t); + ASSERT_EQUALS(4, platform.sizeof_pointer); + ASSERT_EQUALS('\0', platform.defaultSign); + ASSERT_EQUALS(8, platform.char_bit); + ASSERT_EQUALS(16, platform.short_bit); + ASSERT_EQUALS(32, platform.int_bit); + ASSERT_EQUALS(32, platform.long_bit); + ASSERT_EQUALS(64, platform.long_long_bit); + } + + void valid_config_unix64() const { + // Verify if native Unix64 platform is loaded correctly + Platform platform; + PLATFORM(platform, Platform::Type::Unix64); + ASSERT_EQUALS(Platform::Type::Unix64, platform.type); + ASSERT(!platform.isWindows()); + ASSERT_EQUALS(1, platform.sizeof_bool); + ASSERT_EQUALS(2, platform.sizeof_short); + ASSERT_EQUALS(4, platform.sizeof_int); + ASSERT_EQUALS(8, platform.sizeof_long); + ASSERT_EQUALS(8, platform.sizeof_long_long); + ASSERT_EQUALS(4, platform.sizeof_float); + ASSERT_EQUALS(8, platform.sizeof_double); + ASSERT_EQUALS(16, platform.sizeof_long_double); + ASSERT_EQUALS(4, platform.sizeof_wchar_t); + ASSERT_EQUALS(8, platform.sizeof_size_t); + ASSERT_EQUALS(8, platform.sizeof_pointer); + ASSERT_EQUALS('\0', platform.defaultSign); + ASSERT_EQUALS(8, platform.char_bit); + ASSERT_EQUALS(16, platform.short_bit); + ASSERT_EQUALS(32, platform.int_bit); + ASSERT_EQUALS(64, platform.long_bit); + ASSERT_EQUALS(64, platform.long_long_bit); + } + + void valid_config_win32w() const { + // Verify if native Win32W platform is loaded correctly + Platform platform; + PLATFORM(platform, Platform::Type::Win32W); + ASSERT_EQUALS(Platform::Type::Win32W, platform.type); + ASSERT(platform.isWindows()); + ASSERT_EQUALS(1, platform.sizeof_bool); + ASSERT_EQUALS(2, platform.sizeof_short); + ASSERT_EQUALS(4, platform.sizeof_int); + ASSERT_EQUALS(4, platform.sizeof_long); + ASSERT_EQUALS(8, platform.sizeof_long_long); + ASSERT_EQUALS(4, platform.sizeof_float); + ASSERT_EQUALS(8, platform.sizeof_double); + ASSERT_EQUALS(8, platform.sizeof_long_double); + ASSERT_EQUALS(2, platform.sizeof_wchar_t); + ASSERT_EQUALS(4, platform.sizeof_size_t); + ASSERT_EQUALS(4, platform.sizeof_pointer); + ASSERT_EQUALS('\0', platform.defaultSign); + ASSERT_EQUALS(8, platform.char_bit); + ASSERT_EQUALS(16, platform.short_bit); + ASSERT_EQUALS(32, platform.int_bit); + ASSERT_EQUALS(32, platform.long_bit); + ASSERT_EQUALS(64, platform.long_long_bit); + } + + void valid_config_unix32() const { + // Verify if native Unix32 platform is loaded correctly + Platform platform; + PLATFORM(platform, Platform::Type::Unix32); + ASSERT_EQUALS(Platform::Type::Unix32, platform.type); + ASSERT(!platform.isWindows()); + ASSERT_EQUALS(1, platform.sizeof_bool); + ASSERT_EQUALS(2, platform.sizeof_short); + ASSERT_EQUALS(4, platform.sizeof_int); + ASSERT_EQUALS(4, platform.sizeof_long); + ASSERT_EQUALS(8, platform.sizeof_long_long); + ASSERT_EQUALS(4, platform.sizeof_float); + ASSERT_EQUALS(8, platform.sizeof_double); + ASSERT_EQUALS(12, platform.sizeof_long_double); + ASSERT_EQUALS(4, platform.sizeof_wchar_t); + ASSERT_EQUALS(4, platform.sizeof_size_t); + ASSERT_EQUALS(4, platform.sizeof_pointer); + ASSERT_EQUALS('\0', platform.defaultSign); + ASSERT_EQUALS(8, platform.char_bit); + ASSERT_EQUALS(16, platform.short_bit); + ASSERT_EQUALS(32, platform.int_bit); + ASSERT_EQUALS(32, platform.long_bit); + ASSERT_EQUALS(64, platform.long_long_bit); + } + + void valid_config_win64() const { + // Verify if native Win64 platform is loaded correctly + Platform platform; + PLATFORM(platform, Platform::Type::Win64); + ASSERT_EQUALS(Platform::Type::Win64, platform.type); + ASSERT(platform.isWindows()); + ASSERT_EQUALS(1, platform.sizeof_bool); + ASSERT_EQUALS(2, platform.sizeof_short); + ASSERT_EQUALS(4, platform.sizeof_int); + ASSERT_EQUALS(4, platform.sizeof_long); + ASSERT_EQUALS(8, platform.sizeof_long_long); + ASSERT_EQUALS(4, platform.sizeof_float); + ASSERT_EQUALS(8, platform.sizeof_double); + ASSERT_EQUALS(8, platform.sizeof_long_double); + ASSERT_EQUALS(2, platform.sizeof_wchar_t); + ASSERT_EQUALS(8, platform.sizeof_size_t); + ASSERT_EQUALS(8, platform.sizeof_pointer); + ASSERT_EQUALS('\0', platform.defaultSign); + ASSERT_EQUALS(8, platform.char_bit); + ASSERT_EQUALS(16, platform.short_bit); + ASSERT_EQUALS(32, platform.int_bit); + ASSERT_EQUALS(32, platform.long_bit); + ASSERT_EQUALS(64, platform.long_long_bit); + } + + void valid_config_file_1() const { + // Valid platform configuration with all possible values specified. + // Similar to the avr8 platform file. + constexpr char xmldata[] = "\n" + "\n" + " 8\n" + " unsigned\n" + " \n" + " 1\n" + " 2\n" + " 2\n" + " 4\n" + " 8\n" + " 4\n" + " 4\n" + " 4\n" + " 2\n" + " 2\n" + " 2\n" + " \n" + " "; + Platform platform; + ASSERT(readPlatform(platform, xmldata)); + ASSERT_EQUALS(Platform::Type::File, platform.type); + ASSERT(!platform.isWindows()); + ASSERT_EQUALS(8, platform.char_bit); + ASSERT_EQUALS('u', platform.defaultSign); + ASSERT_EQUALS(1, platform.sizeof_bool); + ASSERT_EQUALS(2, platform.sizeof_short); + ASSERT_EQUALS(2, platform.sizeof_int); + ASSERT_EQUALS(4, platform.sizeof_long); + ASSERT_EQUALS(8, platform.sizeof_long_long); + ASSERT_EQUALS(4, platform.sizeof_float); + ASSERT_EQUALS(4, platform.sizeof_double); + ASSERT_EQUALS(4, platform.sizeof_long_double); + ASSERT_EQUALS(2, platform.sizeof_pointer); + ASSERT_EQUALS(2, platform.sizeof_size_t); + ASSERT_EQUALS(2, platform.sizeof_wchar_t); + ASSERT_EQUALS(16, platform.short_bit); + ASSERT_EQUALS(16, platform.int_bit); + ASSERT_EQUALS(32, platform.long_bit); + ASSERT_EQUALS(64, platform.long_long_bit); + } + + void valid_config_file_2() const { + // Valid platform configuration with all possible values specified and + // char_bit > 8. + constexpr char xmldata[] = "\n" + "\n" + " 20\n" + " signed\n" + " \n" + " 1\n" + " 2\n" + " 3\n" + " 4\n" + " 5\n" + " 6\n" + " 7\n" + " 8\n" + " 9\n" + " 10\n" + " 11\n" + " \n" + " "; + Platform platform; + ASSERT(readPlatform(platform, xmldata)); + ASSERT_EQUALS(Platform::Type::File, platform.type); + ASSERT(!platform.isWindows()); + ASSERT_EQUALS(20, platform.char_bit); + ASSERT_EQUALS('s', platform.defaultSign); + ASSERT_EQUALS(1, platform.sizeof_bool); + ASSERT_EQUALS(2, platform.sizeof_short); + ASSERT_EQUALS(3, platform.sizeof_int); + ASSERT_EQUALS(4, platform.sizeof_long); + ASSERT_EQUALS(5, platform.sizeof_long_long); + ASSERT_EQUALS(6, platform.sizeof_float); + ASSERT_EQUALS(7, platform.sizeof_double); + ASSERT_EQUALS(8, platform.sizeof_long_double); + ASSERT_EQUALS(9, platform.sizeof_pointer); + ASSERT_EQUALS(10, platform.sizeof_size_t); + ASSERT_EQUALS(11, platform.sizeof_wchar_t); + ASSERT_EQUALS(40, platform.short_bit); + ASSERT_EQUALS(60, platform.int_bit); + ASSERT_EQUALS(80, platform.long_bit); + ASSERT_EQUALS(100, platform.long_long_bit); + } + + void valid_config_file_3() const { + // Valid platform configuration without any usable information. + // Similar like an empty file. + constexpr char xmldata[] = "\n" + "\n" + " 8\n" + " unsigned\n" + " \n" + " 1\n" + " 2\n" + " 3\n" + " 4\n" + " 5\n" + " 6\n" + " 7\n" + " 8\n" + " 9\n" + " 10\n" + " 11\n" + " \n" + " "; + Platform platform; + // TODO: needs to fail - files need to be complete + TODO_ASSERT(!readPlatform(platform, xmldata)); + } + + void valid_config_file_4() const { + // Valid platform configuration with all possible values specified and + // set to 0. + constexpr char xmldata[] = "\n" + "\n" + " 0\n" + " z\n" + " \n" + " 0\n" + " 0\n" + " 0\n" + " 0\n" + " 0\n" + " 0\n" + " 0\n" + " 0\n" + " 0\n" + " 0\n" + " 0\n" + " \n" + " "; + Platform platform; + ASSERT(readPlatform(platform, xmldata)); + ASSERT_EQUALS(Platform::Type::File, platform.type); + ASSERT(!platform.isWindows()); + ASSERT_EQUALS(0, platform.char_bit); + ASSERT_EQUALS('z', platform.defaultSign); + ASSERT_EQUALS(0, platform.sizeof_bool); + ASSERT_EQUALS(0, platform.sizeof_short); + ASSERT_EQUALS(0, platform.sizeof_int); + ASSERT_EQUALS(0, platform.sizeof_long); + ASSERT_EQUALS(0, platform.sizeof_long_long); + ASSERT_EQUALS(0, platform.sizeof_float); + ASSERT_EQUALS(0, platform.sizeof_double); + ASSERT_EQUALS(0, platform.sizeof_long_double); + ASSERT_EQUALS(0, platform.sizeof_pointer); + ASSERT_EQUALS(0, platform.sizeof_size_t); + ASSERT_EQUALS(0, platform.sizeof_wchar_t); + ASSERT_EQUALS(0, platform.short_bit); + ASSERT_EQUALS(0, platform.int_bit); + ASSERT_EQUALS(0, platform.long_bit); + ASSERT_EQUALS(0, platform.long_long_bit); + } + + void invalid_config_file_1() const { + // Invalid XML file: mismatching elements "boolt" vs "bool". + constexpr char xmldata[] = "\n" + "\n" + " 8\n" + " unsigned\n" + " \n" + " 1\n" + " 2\n" + " 2\n" + " 4\n" + " 8\n" + " 4\n" + " 4\n" + " 4\n" + " 2\n" + " 2\n" + " 2\n" + " \n" + " "; + Platform platform; + ASSERT(!readPlatform(platform, xmldata)); + } + + void empty_elements() const { + // Valid platform configuration without any usable information. + // Similar like an empty file. + constexpr char xmldata[] = "\n" + "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " "; + Platform platform; + ASSERT(!readPlatform(platform, xmldata)); + } + + void default_platform() const { + Platform platform; + ASSERT_EQUALS(Platform::Type::Native, platform.type); + } + + void limitsDefines() const { + Platform platform; + platform.set(Platform::Unix64); + const std::string defs = "CHAR_BIT=8;SCHAR_MIN=-128;SCHAR_MAX=127;UCHAR_MAX=255;CHAR_MIN=0;CHAR_MAX=127;SHRT_MIN=-32768;SHRT_MAX=32767;USHRT_MAX=65535;INT_MIN=-2147483648;INT_MAX=2147483647;UINT_MAX=4294967295;LONG_MIN=-9223372036854775808;LONG_MAX=9223372036854775807;ULONG_MAX=9223372036854775807"; + const std::string defs_c99 = "CHAR_BIT=8;SCHAR_MIN=-128;SCHAR_MAX=127;UCHAR_MAX=255;CHAR_MIN=0;CHAR_MAX=127;SHRT_MIN=-32768;SHRT_MAX=32767;USHRT_MAX=65535;INT_MIN=-2147483648;INT_MAX=2147483647;UINT_MAX=4294967295;LONG_MIN=-9223372036854775808;LONG_MAX=9223372036854775807;ULONG_MAX=9223372036854775807;LLONG_MIN=-9223372036854775808;LLONG_MAX=9223372036854775807;ULLONG_MAX=9223372036854775807"; + ASSERT_EQUALS(defs, platform.getLimitsDefines(Standards::cstd_t::C89)); + ASSERT_EQUALS(defs_c99, platform.getLimitsDefines(Standards::cstd_t::C99)); + ASSERT_EQUALS(defs_c99, platform.getLimitsDefines(Standards::cstd_t::CLatest)); + ASSERT_EQUALS(defs, platform.getLimitsDefines(Standards::cppstd_t::CPP03)); + ASSERT_EQUALS(defs_c99, platform.getLimitsDefines(Standards::cppstd_t::CPP11)); + ASSERT_EQUALS(defs_c99, platform.getLimitsDefines(Standards::cppstd_t::CPPLatest)); + } + + void charMinMax() const { + Platform platform; + ASSERT_EQUALS(255, platform.unsignedCharMax()); + ASSERT_EQUALS(127, platform.signedCharMax()); + ASSERT_EQUALS(-128, platform.signedCharMin()); + } + + void no_root_node() const { + constexpr char xmldata[] = ""; + Platform platform; + ASSERT(!readPlatform(platform, xmldata)); + } + + void wrong_root_node() const { + constexpr char xmldata[] = "\n" + ""; + Platform platform; + ASSERT(!readPlatform(platform, xmldata)); + } +}; + +REGISTER_TEST(TestPlatform) diff --git a/cppcheck-2.14.0/test/testpostfixoperator.cpp b/cppcheck-2.14.0/test/testpostfixoperator.cpp new file mode 100644 index 00000000..dba5f931 --- /dev/null +++ b/cppcheck-2.14.0/test/testpostfixoperator.cpp @@ -0,0 +1,382 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "checkpostfixoperator.h" +#include "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "settings.h" + +class TestPostfixOperator : public TestFixture { +public: + TestPostfixOperator() : TestFixture("TestPostfixOperator") {} + +private: + const Settings settings = settingsBuilder().severity(Severity::performance).build(); + +#define check(code) check_(code, __FILE__, __LINE__) + void check_(const char code[], const char* file, int line) { + // Tokenize.. + SimpleTokenizer tokenizer(settings, *this); + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + // Check for postfix operators.. + CheckPostfixOperator checkPostfixOperator(&tokenizer, &settings, this); + checkPostfixOperator.postfixOperator(); + } + + void run() override { + TEST_CASE(testsimple); + TEST_CASE(testfor); + TEST_CASE(testvolatile); + TEST_CASE(testiterator); + TEST_CASE(test2168); + TEST_CASE(pointerSimplest); + TEST_CASE(pointer); // #2321 - postincrement of pointer is OK + TEST_CASE(testtemplate); // #4686 + TEST_CASE(testmember); + TEST_CASE(testcomma); + TEST_CASE(testauto); // #8350 + } + + void testsimple() { + check("int main()\n" + "{\n" + " unsigned int k(0);\n" + " std::cout << k << std::endl;\n" + " k++;\n" + " std::cout << k << std::endl;\n" + " if(k) {\n" + " k++;\n" + " }\n" + " std::cout << k << std::endl;\n" + " k--;\n" + " std::cout << k << std::endl;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class K {};" + "int main()\n" + "{\n" + " K k(0);\n" + " std::cout << k << std::endl;\n" + " k++;\n" + " std::cout << k << std::endl;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + + check("struct K {};" + "void foo()\n" + "{\n" + " K k(0);\n" + " k++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + + check("struct K {};\n" + "void foo(K& k)\n" + "{\n" + " k++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + + check("union K {};" + "void foo()\n" + "{\n" + " K k(0);\n" + " k++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + + check("class K {};" + "int main()\n" + "{\n" + " K k(1);\n" + " std::cout << k << std::endl;\n" + " if(k) {\n" + " k++;\n" + " }\n" + " std::cout << k << std::endl;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + + check("class K {};" + "int main()\n" + "{\n" + " K k(1);\n" + " std::cout << k << std::endl;\n" + " if(k) {\n" + " ++k;\n" + " }\n" + " k++;\n" + " std::cout << k << std::endl;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + + + check("class K {};" + "int main()\n" + "{\n" + " K k(0);\n" + " std::cout << k << std::endl;\n" + " k--;\n" + " std::cout << k << std::endl;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:5]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + + check("class K {};" + "int main()\n" + "{\n" + " K k(0);\n" + " std::cout << k << std::endl;\n" + " ++k;\n" + " std::cout << k << std::endl;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class K {};" + "int main()\n" + "{\n" + " K k(0);\n" + " std::cout << k << std::endl;\n" + " --k;\n" + " std::cout << k << std::endl;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + // #9042 + check("template \n" + "class c {\n" + " int i = 0;\n" + " c() { i--; }\n" + "};\n" + "template \n" + "class s {};\n" + "using BOOL = char;"); + ASSERT_EQUALS("", errout_str()); + } + + void testfor() { + check("int main()\n" + "{\n" + " for ( unsigned int i=0; i <= 10; i++) {\n" + " std::cout << i << std::endl;\n" + " }\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class K {};\n" + "int main()\n" + "{\n" + " for ( K i(0); i <= 10; i++) {\n" + " std::cout << i << std::endl;\n" + " }\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + + check("class K {};\n" + "int main()\n" + "{\n" + " for ( K i(0); i <= 10; ++i) {\n" + " std::cout << i << std::endl;\n" + " }\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + check("class K {};\n" + "int main()\n" + "{\n" + " for ( K i(10); i > 1; i--) {\n" + " std::cout << i << std::endl;\n" + " }\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + + check("class K {};\n" + "int main(int argc, char *argv[])\n" + "{\n" + " for ( K i=10; i > 1; --i) {\n" + " std::cout << i << std::endl;\n" + " }\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + + + } + + void testvolatile() { + check("class K {};\n" + "int main()\n" + "{\n" + " volatile K k(0);\n" + " std::cout << k << std::endl;\n" + " k++;\n" + " std::cout << k << std::endl;\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + } + + void testiterator() { + check("class Base {};\n" + "int main() {\n" + " std::vector v;\n" + " v.push_back(new Base());\n" + " v.push_back(new Base());\n" + " for (std::vector::iterator i=v.begin(); i!=v.end(); i++) {\n" + " ;;\n" + " }\n" + " v.clear();\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:6]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + + check("int main() {\n" + " std::vector v;\n" + " std::vector::iterator it;\n" + " for( int i=0; i < 10; ++i ) v.push_back(i);\n" + " unsigned int total = 0;\n" + " it = v.begin();\n" + " while( it != v.end() ) {\n" + " total += *it;\n" + " it++;\n" + " }\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:9]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + + check("int main() {\n" + " std::vector v;\n" + " std::vector::const_iterator it;\n" + " for( int i=0; i < 10; ++i ) v.push_back(i);\n" + " unsigned int total = 0;\n" + " it = v.begin();\n" + " while( it != v.end() ) {\n" + " it++;\n" + " }\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:8]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + + check("int main() {\n" + " std::vector v;\n" + " std::vector::iterator it;\n" + " for( int i=0; i < 10; ++i ) v.push_back(i);\n" + " unsigned int total = 0;\n" + " std::vector::reverse_iterator rit;\n" + " rit= v.rend();\n" + " while( rit != v.rbegin() ) {\n" + " rit--;\n" + " }\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[test.cpp:9]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + + } + + void test2168() { + check("--> declare allocator lock here\n" + "int main(){}"); + ASSERT_EQUALS("", errout_str()); + } + + void pointerSimplest() { + check("void f(int* p){\n" + " p++;\n" + " std::cout << *p;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void pointer() { + check("static struct class * ab;\n" + "int * p;\n" + "\n" + "void f() {\n" + " p++;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testtemplate() { + check("bool foo() {\n" + " std::vector::iterator aIter(aImport.begin());\n" + " aIter++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:3]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + } + + void testmember() { + check("bool foo() {\n" + " class A {}; class B {A a;};\n" + " B b;\n" + " b.a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + + check("bool foo() {\n" + " class A {}; class B {A a;};\n" + " B b;\n" + " foo(b.a++);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testcomma() { + check("bool foo(int i) {\n" + " class A {};\n" + " A a;\n" + " i++, a++;\n" + "}"); + ASSERT_EQUALS("[test.cpp:4]: (performance) Prefer prefix ++/-- operators for non-primitive types.\n", errout_str()); + + check("bool foo(int i) {\n" + " class A {};\n" + " A a;\n" + " foo(i, a++);\n" + " foo(a++, i);\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } + + void testauto() { // #8350 + check("enum class Color { Red = 0, Green = 1, };\n" + "int fun(const Color color) {\n" + " auto a = 0;\n" + " for (auto i = static_cast(color); i < 10; i++) {\n" + " a += i;\n" + " }\n" + " return a;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + } +}; + +REGISTER_TEST(TestPostfixOperator) diff --git a/cppcheck-2.14.0/test/testpreprocessor.cpp b/cppcheck-2.14.0/test/testpreprocessor.cpp new file mode 100644 index 00000000..77bdd3c8 --- /dev/null +++ b/cppcheck-2.14.0/test/testpreprocessor.cpp @@ -0,0 +1,2484 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General 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 "errortypes.h" +#include "path.h" +#include "platform.h" +#include "preprocessor.h" +#include "settings.h" +#include "suppressions.h" +#include "fixture.h" +#include "helpers.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +class ErrorLogger; + +class TestPreprocessor : public TestFixture { +public: + TestPreprocessor() : TestFixture("TestPreprocessor") {} + + class OurPreprocessor : public Preprocessor { + public: + + static std::string expandMacros(const char code[], ErrorLogger *errorLogger = nullptr) { + std::istringstream istr(code); + simplecpp::OutputList outputList; + std::vector files; + const simplecpp::TokenList tokens1 = simplecpp::TokenList(istr, files, "file.cpp", &outputList); + std::map filedata; + simplecpp::TokenList tokens2(files); + simplecpp::preprocess(tokens2, tokens1, files, filedata, simplecpp::DUI(), &outputList); + + if (errorLogger) { + const Settings settings; + Preprocessor p(settings, *errorLogger); + p.reportOutput(outputList, true); + } + + return tokens2.stringify(); + } + }; + +private: + const Settings settings0 = settingsBuilder().severity(Severity::information).build(); + + void run() override { + + // The bug that started the whole work with the new preprocessor + TEST_CASE(Bug2190219); + + TEST_CASE(error1); // #error => don't extract any code + TEST_CASE(error2); // #error if symbol is not defined + TEST_CASE(error3); + TEST_CASE(error4); // #2919 - wrong filename is reported + TEST_CASE(error5); + TEST_CASE(error6); + TEST_CASE(error7); + TEST_CASE(error8); // #10170 -> previous #if configurations + + TEST_CASE(setPlatformInfo); + + // Handling include guards (don't create extra configuration for it) + TEST_CASE(includeguard1); + TEST_CASE(includeguard2); + + TEST_CASE(if0); + TEST_CASE(if1); + + TEST_CASE(elif); + + TEST_CASE(if_cond1); + TEST_CASE(if_cond2); + TEST_CASE(if_cond3); + TEST_CASE(if_cond4); + TEST_CASE(if_cond5); + TEST_CASE(if_cond6); + TEST_CASE(if_cond8); + TEST_CASE(if_cond9); + TEST_CASE(if_cond10); + TEST_CASE(if_cond11); + TEST_CASE(if_cond12); + TEST_CASE(if_cond13); + TEST_CASE(if_cond14); + + TEST_CASE(if_or_1); + TEST_CASE(if_or_2); + + TEST_CASE(if_macro_eq_macro); // #3536 + TEST_CASE(ticket_3675); + TEST_CASE(ticket_3699); + TEST_CASE(ticket_4922); // #4922 + + // Macros.. + TEST_CASE(macro_simple1); + TEST_CASE(macro_simple2); + TEST_CASE(macro_simple3); + TEST_CASE(macro_simple4); + TEST_CASE(macro_simple5); + TEST_CASE(macro_simple6); + TEST_CASE(macro_simple7); + TEST_CASE(macro_simple8); + TEST_CASE(macro_simple9); + TEST_CASE(macro_simple10); + TEST_CASE(macro_simple11); + TEST_CASE(macro_simple12); + TEST_CASE(macro_simple13); + TEST_CASE(macro_simple14); + TEST_CASE(macro_simple15); + TEST_CASE(macro_simple16); // #4703: Macro parameters not trimmed + TEST_CASE(macro_simple17); // #5074: isExpandedMacro not set + TEST_CASE(macro_simple18); // (1e-7) + TEST_CASE(macroInMacro1); + TEST_CASE(macroInMacro2); + TEST_CASE(macro_linenumbers); + TEST_CASE(macro_nopar); + TEST_CASE(macro_incdec); // separate ++ and -- with space when expanding such macro: '#define M(X) A-X' + TEST_CASE(macro_switchCase); + TEST_CASE(macro_NULL); // skip #define NULL .. it is replaced in the tokenizer + TEST_CASE(string1); + TEST_CASE(string2); + TEST_CASE(string3); + TEST_CASE(preprocessor_undef); + TEST_CASE(defdef); // Defined multiple times + TEST_CASE(preprocessor_doublesharp); + TEST_CASE(preprocessor_include_in_str); + TEST_CASE(va_args_1); + //TEST_CASE(va_args_2); invalid code + TEST_CASE(va_args_3); + TEST_CASE(va_args_4); + TEST_CASE(va_args_5); + TEST_CASE(multi_character_character); + + TEST_CASE(stringify); + TEST_CASE(stringify2); + TEST_CASE(stringify3); + TEST_CASE(stringify4); + TEST_CASE(stringify5); + TEST_CASE(ifdefwithfile); + TEST_CASE(pragma); + TEST_CASE(pragma_asm_1); + TEST_CASE(pragma_asm_2); + TEST_CASE(endifsemicolon); + TEST_CASE(missing_doublequote); + TEST_CASE(handle_error); + TEST_CASE(dup_defines); + + TEST_CASE(define_part_of_func); + TEST_CASE(conditionalDefine); + TEST_CASE(macro_parameters); + TEST_CASE(newline_in_macro); + TEST_CASE(ifdef_ifdefined); + + // define and then ifdef + TEST_CASE(define_if1); + TEST_CASE(define_if2); + TEST_CASE(define_if3); + TEST_CASE(define_if4); // #4079 - #define X +123 + TEST_CASE(define_if5); // #4516 - #define B (A & 0x00f0) + TEST_CASE(define_if6); // #4863 - #define B (A?-1:1) + TEST_CASE(define_ifdef); + TEST_CASE(define_ifndef1); + TEST_CASE(define_ifndef2); + TEST_CASE(ifndef_define); + TEST_CASE(undef_ifdef); + TEST_CASE(endfile); + + TEST_CASE(redundant_config); + + TEST_CASE(invalid_define_1); // #2605 - hang for: '#define =' + TEST_CASE(invalid_define_2); // #4036 - hang for: '#define () {(int f(x) }' + + // inline suppression, missingInclude/missingIncludeSystem + TEST_CASE(inline_suppressions); + + // 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); // automatically define __STDC_VERSION__ + + TEST_CASE(invalidElIf); // #2942 segfault + + // Preprocessor::getConfigs + TEST_CASE(getConfigs1); + TEST_CASE(getConfigs2); + TEST_CASE(getConfigs3); + TEST_CASE(getConfigs4); + TEST_CASE(getConfigs5); + TEST_CASE(getConfigs7); + TEST_CASE(getConfigs7a); + TEST_CASE(getConfigs7b); + TEST_CASE(getConfigs7c); + TEST_CASE(getConfigs7d); + TEST_CASE(getConfigs7e); + TEST_CASE(getConfigs8); // #if A==1 => cfg: A=1 + TEST_CASE(getConfigs10); // #5139 + TEST_CASE(getConfigs11); // #9832 - include guards + TEST_CASE(getConfigsError); + + TEST_CASE(getConfigsD1); + + TEST_CASE(getConfigsU1); + TEST_CASE(getConfigsU2); + TEST_CASE(getConfigsU3); + TEST_CASE(getConfigsU4); + TEST_CASE(getConfigsU5); + TEST_CASE(getConfigsU6); + TEST_CASE(getConfigsU7); + + TEST_CASE(if_sizeof); + + TEST_CASE(invalid_ifs); // #5909 + + TEST_CASE(garbage); + + TEST_CASE(wrongPathOnErrorDirective); + + TEST_CASE(testMissingInclude); + TEST_CASE(testMissingInclude2); + TEST_CASE(testMissingInclude3); + TEST_CASE(testMissingInclude4); + TEST_CASE(testMissingInclude5); + TEST_CASE(testMissingInclude6); + TEST_CASE(testMissingSystemInclude); + TEST_CASE(testMissingSystemInclude2); + TEST_CASE(testMissingSystemInclude3); + TEST_CASE(testMissingSystemInclude4); + TEST_CASE(testMissingSystemInclude5); + TEST_CASE(testMissingIncludeMixed); + TEST_CASE(testMissingIncludeCheckConfig); + + TEST_CASE(limitsDefines); + } + + std::string getConfigsStr(const char filedata[], const char *arg = nullptr) { + Settings settings; + if (arg && std::strncmp(arg,"-D",2)==0) + settings.userDefines = arg + 2; + if (arg && std::strncmp(arg,"-U",2)==0) + settings.userUndefs.insert(arg+2); + Preprocessor preprocessor(settings, *this); + std::vector files; + std::istringstream istr(filedata); + simplecpp::TokenList tokens(istr,files); + tokens.removeComments(); + const std::set configs = preprocessor.getConfigs(tokens); + std::string ret; + for (const std::string & config : configs) + ret += config + '\n'; + return ret; + } + + void Bug2190219() { + const char filedata[] = "#ifdef __cplusplus\n" + "cpp\n" + "#else\n" + "c\n" + "#endif"; + + { + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata, "file.cpp"); + + // Compare results.. + ASSERT_EQUALS(1U, actual.size()); + ASSERT_EQUALS("\ncpp", actual.at("")); + } + + { + // Ticket #7102 - skip __cplusplus in C code + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata, "file.c"); + + // Compare results.. + ASSERT_EQUALS(1U, actual.size()); + ASSERT_EQUALS("\n\n\nc", actual.at("")); + } + } + + void error1() { + const char filedata[] = "#ifdef A\n" + ";\n" + "#else\n" + "#error abcd\n" + "#endif\n"; + ASSERT_EQUALS("\nA\n", getConfigsStr(filedata)); + } + + void error2() { + const char filedata1[] = "#ifndef A\n" + "#error\n" + "#endif\n"; + ASSERT_EQUALS("A\n", getConfigsStr(filedata1)); + + const char filedata2[] = "#if !A\n" + "#error\n" + "#endif\n"; + ASSERT_EQUALS("A\n", getConfigsStr(filedata2)); + } + + void error3() { + const auto settings = dinit(Settings, $.userDefines = "__cplusplus"); + const std::string code("#error hello world!\n"); + PreprocessorHelper::getcode(settings, *this, 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 + { + const auto settings = dinit(Settings, $.userDefines = "TEST"); + const std::string code("#file \"ab.h\"\n#error hello world!\n#endfile"); + PreprocessorHelper::getcode(settings, *this, code, "TEST", "test.c"); + ASSERT_EQUALS("[ab.h:1]: (error) #error hello world!\n", errout_str()); + } + + // After including a file + { + const auto settings = dinit(Settings, $.userDefines = "TEST"); + const std::string code("#file \"ab.h\"\n\n#endfile\n#error aaa"); + PreprocessorHelper::getcode(settings, *this, code, "TEST", "test.c"); + ASSERT_EQUALS("[test.c:2]: (error) #error aaa\n", errout_str()); + } + } + + void error5() { + // No message if --force is given + const auto settings = dinit(Settings, + $.userDefines = "TEST", + $.force = true); + const std::string code("#error hello world!\n"); + PreprocessorHelper::getcode(settings, *this, code, "X", "test.c"); + ASSERT_EQUALS("", errout_str()); + } + + void error6() { + const char filedata1[] = "#ifdef A\n" + "#else\n" + "#error 1\n" + "#endif\n" + "#ifdef B\n" + "#else\n" + "#error 2\n" + "#endif\n"; + ASSERT_EQUALS("\nA\nA;B\nB\n", getConfigsStr(filedata1)); + + const char filedata2[] = "#ifndef A\n" + "#error 1\n" + "#endif\n" + "#ifndef B\n" + "#error 2\n" + "#endif\n"; + ASSERT_EQUALS("A;B\n", getConfigsStr(filedata2)); + + const char filedata3[] = "#if !A\n" + "#error 1\n" + "#endif\n" + "#if !B\n" + "#error 2\n" + "#endif\n"; + ASSERT_EQUALS("A;B\n", getConfigsStr(filedata3)); + + } + + void error7() { // #8074 + const char filedata[] = "#define A\n" + "\n" + "#if defined(B)\n" + "#else\n" + "#error \"1\"\n" + "#endif\n" + "\n" + "#if defined(A)\n" + "#else\n" + "#error \"2\"\n" + "#endif\n"; + ASSERT_EQUALS("\nB\n", getConfigsStr(filedata)); + } + + void error8() { + const char filedata[] = "#ifdef A\n" + "#ifdef B\n" + "#endif\n" + "#else\n" + "#endif\n" + "\n" + "#ifndef C\n" + "#error aa\n" + "#endif"; + ASSERT_EQUALS("A;B;C\nA;C\nC\n", getConfigsStr(filedata)); + } + + void setPlatformInfo() { + // read code with simplecpp.. + const char filedata[] = "#if sizeof(long) == 4\n" + "1\n" + "#else\n" + "2\n" + "#endif\n"; + std::istringstream istr(filedata); + std::vector files; + simplecpp::TokenList tokens(istr, files, "test.c"); + + // preprocess code with unix32 platform.. + { + const Settings settings = settingsBuilder().platform(Platform::Type::Unix32).build(); + Preprocessor preprocessor(settings, *this); + preprocessor.setPlatformInfo(&tokens); + ASSERT_EQUALS("\n1", preprocessor.getcode(tokens, "", files, false)); + } + + // preprocess code with unix64 platform.. + { + const Settings settings = settingsBuilder().platform(Platform::Type::Unix64).build(); + Preprocessor preprocessor(settings, *this); + preprocessor.setPlatformInfo(&tokens); + ASSERT_EQUALS("\n\n\n2", preprocessor.getcode(tokens, "", files, false)); + } + } + + void includeguard1() { + // Handling include guards.. + const char filedata[] = "#file \"abc.h\"\n" + "#ifndef abcH\n" + "#define abcH\n" + "#endif\n" + "#endfile\n" + "#ifdef ABC\n" + "#endif"; + ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); + } + + void includeguard2() { + // Handling include guards.. + const char filedata[] = "#file \"abc.h\"\n" + "foo\n" + "#ifdef ABC\n" + "\n" + "#endif\n" + "#endfile\n"; + ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); + } + + + void ifdefwithfile() { + // Handling include guards.. + const char filedata[] = "#ifdef ABC\n" + "#file \"abc.h\"\n" + "class A{};/*\n\n\n\n\n\n\n*/\n" + "#endfile\n" + "#endif\n" + "int main() {}\n"; + + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Expected configurations: "" and "ABC" + ASSERT_EQUALS(2, actual.size()); + ASSERT_EQUALS("\n\n\nint main ( ) { }", actual.at("")); + ASSERT_EQUALS("\n#line 1 \"abc.h\"\nclass A { } ;\n#line 4 \"file.c\"\n int main ( ) { }", actual.at("ABC")); + } + + void if0() { + const char filedata[] = " # if /* comment */ 0 // comment\n" + "#ifdef WIN32\n" + "#endif\n" + "#endif\n"; + ASSERT_EQUALS("\n", getConfigsStr(filedata)); + } + + void if1() { + const char filedata[] = " # if /* comment */ 1 // comment\n" + "ABC\n" + " # endif \n"; + ASSERT_EQUALS("\n", getConfigsStr(filedata)); + } + + + void elif() { + { + const char filedata[] = "#if DEF1\n" + "ABC\n" + "#elif DEF2\n" + "DEF\n" + "#endif\n"; + ASSERT_EQUALS("\nDEF1\nDEF2\n", getConfigsStr(filedata)); + } + + { + const char filedata[] = "#if(defined DEF1)\n" + "ABC\n" + "#elif(defined DEF2)\n" + "DEF\n" + "#else\n" + "GHI\n" + "#endif\n"; + ASSERT_EQUALS("\nDEF1\nDEF2\n", getConfigsStr(filedata)); + } + } + + void if_cond1() { + const char filedata[] = "#if LIBVER>100\n" + " A\n" + "#else\n" + " B\n" + "#endif\n"; + TODO_ASSERT_EQUALS("\nLIBVER=101\n", "\n", getConfigsStr(filedata)); + } + + void if_cond2() { + const char filedata[] = "#ifdef A\n" + "a\n" + "#endif\n" + "#if defined(A) && defined(B)\n" + "ab\n" + "#endif\n"; + ASSERT_EQUALS("\nA\nA;B\n", getConfigsStr(filedata)); + + if_cond2b(); + if_cond2c(); + if_cond2d(); + if_cond2e(); + } + + void if_cond2b() { + const char filedata[] = "#ifndef A\n" + "!a\n" + "#ifdef B\n" + "b\n" + "#endif\n" + "#else\n" + "a\n" + "#endif\n"; + TODO_ASSERT_EQUALS("\nA;B\n", "\nA\nB\n", getConfigsStr(filedata)); + } + + void if_cond2c() { + const char filedata[] = "#ifndef A\n" + "!a\n" + "#ifdef B\n" + "b\n" + "#else\n" + "!b\n" + "#endif\n" + "#else\n" + "a\n" + "#endif\n"; + TODO_ASSERT_EQUALS("\nA\nA;B\n", "\nA\nB\n", getConfigsStr(filedata)); + } + + void if_cond2d() { + const char filedata[] = "#ifndef A\n" + "!a\n" + "#ifdef B\n" + "b\n" + "#else\n" + "!b\n" + "#endif\n" + "#else\n" + "a\n" + "#ifdef B\n" + "b\n" + "#else\n" + "!b\n" + "#endif\n" + "#endif\n"; + ASSERT_EQUALS("\nA\nA;B\nB\n", getConfigsStr(filedata)); + } + + void if_cond2e() { + const char filedata[] = "#if !defined(A)\n" + "!a\n" + "#elif !defined(B)\n" + "!b\n" + "#endif\n"; + ASSERT_EQUALS("\nA\nB\n", getConfigsStr(filedata)); + } + + void if_cond3() { + const char filedata[] = "#ifdef A\n" + "a\n" + "#if defined(B) && defined(C)\n" + "abc\n" + "#endif\n" + "#endif\n"; + ASSERT_EQUALS("\nA\nA;B;C\n", getConfigsStr(filedata)); + } + + void if_cond4() { + { + const char filedata[] = "#define A\n" + "#define B\n" + "#if defined A || defined B\n" + "ab\n" + "#endif\n"; + ASSERT_EQUALS("\n", getConfigsStr(filedata)); + } + + { + const char filedata[] = "#if A\n" + "{\n" + "#if (defined(B))\n" + "foo();\n" + "#endif\n" + "}\n" + "#endif\n"; + ASSERT_EQUALS("\nA\nA;B\n", getConfigsStr(filedata)); + } + + { + const char filedata[] = "#define A\n" + "#define B\n" + "#if (defined A) || defined (B)\n" + "ab\n" + "#endif\n"; + ASSERT_EQUALS("\n", getConfigsStr(filedata)); + } + + { + const char filedata[] = "#if (A)\n" + "foo();\n" + "#endif\n"; + ASSERT_EQUALS("\nA\n", getConfigsStr(filedata)); + } + + { + const char filedata[] = "#if! A\n" + "foo();\n" + "#endif\n"; + ASSERT_EQUALS("\nA=0\n", getConfigsStr(filedata)); + } + } + + void if_cond5() { + const char filedata[] = "#if defined(A) && defined(B)\n" + "ab\n" + "#endif\n" + "cd\n" + "#if defined(B) && defined(A)\n" + "ef\n" + "#endif\n"; + ASSERT_EQUALS("\nA;B\n", getConfigsStr(filedata)); + } + + void if_cond6() { + const char filedata[] = "\n" + "#if defined(A) && defined(B))\n" + "#endif\n"; + ASSERT_EQUALS("\nA;B\n", getConfigsStr(filedata)); + } + + void if_cond8() { + const char filedata[] = "#if defined(A) + defined(B) + defined(C) != 1\n" + "#endif\n"; + TODO_ASSERT_EQUALS("\nA\n", "\nA;B;C\n", getConfigsStr(filedata)); + } + + + void if_cond9() { + const char filedata[] = "#if !defined _A\n" + "abc\n" + "#endif\n"; + ASSERT_EQUALS("\n_A\n", getConfigsStr(filedata)); + } + + void if_cond10() { + const char filedata[] = "#if !defined(a) && !defined(b)\n" + "#if defined(and)\n" + "#endif\n" + "#endif\n"; + + // Preprocess => don't crash.. + (void)PreprocessorHelper::getcode(settings0, *this, filedata); + } + + void if_cond11() { + const char filedata[] = "#if defined(L_fixunssfdi) && LIBGCC2_HAS_SF_MODE\n" + "#if LIBGCC2_HAS_DF_MODE\n" + "#elif FLT_MANT_DIG < W_TYPE_SIZE\n" + "#endif\n" + "#endif\n"; + (void)PreprocessorHelper::getcode(settings0, *this, filedata); + ASSERT_EQUALS("", errout_str()); + } + + void if_cond12() { + const char filedata[] = "#define A (1)\n" + "#if A == 1\n" + ";\n" + "#endif\n"; + ASSERT_EQUALS("\n", getConfigsStr(filedata)); + } + + void if_cond13() { + const char filedata[] = "#if ('A' == 0x41)\n" + "123\n" + "#endif\n"; + ASSERT_EQUALS("\n", getConfigsStr(filedata)); + } + + void if_cond14() { + const char filedata[] = "#if !(A)\n" + "123\n" + "#endif\n"; + ASSERT_EQUALS("\n", getConfigsStr(filedata)); + } + + + + void if_or_1() { + const char filedata[] = "#if defined(DEF_10) || defined(DEF_11)\n" + "a1;\n" + "#endif\n"; + ASSERT_EQUALS("\nDEF_10;DEF_11\n", getConfigsStr(filedata)); + } + + void if_or_2() { + const char filedata[] = "#if X || Y\n" + "a1;\n" + "#endif\n"; + TODO_ASSERT_EQUALS("\nX;Y\n", "\n", getConfigsStr(filedata)); + } + + void if_macro_eq_macro() { + const char *code = "#define A B\n" + "#define B 1\n" + "#define C 1\n" + "#if A == C\n" + "Wilma\n" + "#else\n" + "Betty\n" + "#endif\n"; + ASSERT_EQUALS("\n", getConfigsStr(code)); + } + + void ticket_3675() { + const char* code = "#ifdef YYSTACKSIZE\n" + "#define YYMAXDEPTH YYSTACKSIZE\n" + "#else\n" + "#define YYSTACKSIZE YYMAXDEPTH\n" + "#endif\n" + "#if YYDEBUG\n" + "#endif\n"; + (void)PreprocessorHelper::getcode(settings0, *this, code); + + // There's nothing to assert. It just needs to not hang. + } + + void ticket_3699() { + const char* code = "#define INLINE __forceinline\n" + "#define inline __forceinline\n" + "#define __forceinline inline\n" + "#if !defined(_WIN32)\n" + "#endif\n" + "INLINE inline __forceinline\n"; + const std::map actual = PreprocessorHelper::getcode(settings0, *this, code); + + // First, it must not hang. Second, inline must becomes inline, and __forceinline must become __forceinline. + ASSERT_EQUALS("\n\n\n\n\n$__forceinline $inline $__forceinline", actual.at("")); + } + + void ticket_4922() { // #4922 + const char* code = "__asm__ \n" + "{ int extern __value) 0; (double return (\"\" } extern\n" + "__typeof __finite (__finite) __finite __inline \"__GI___finite\");"; + (void)PreprocessorHelper::getcode(settings0, *this, code); + } + + void macro_simple1() const { + { + const char filedata[] = "#define AAA(aa) f(aa)\n" + "AAA(5);\n"; + ASSERT_EQUALS("\nf ( 5 ) ;", OurPreprocessor::expandMacros(filedata)); + } + + { + const char filedata[] = "#define AAA(aa) f(aa)\n" + "AAA (5);\n"; + ASSERT_EQUALS("\nf ( 5 ) ;", OurPreprocessor::expandMacros(filedata)); + } + } + + void macro_simple2() const { + const char filedata[] = "#define min(x,y) x 0 ) return 1 ;", OurPreprocessor::expandMacros(filedata)); + } + + void macro_simple5() const { + const char filedata[] = "#define ABC if( temp > 0 ) return 1;\n" + "\n" + "void foo()\n" + "{\n" + " int temp = 0;\n" + " ABC\n" + "}\n"; + ASSERT_EQUALS("\n\nvoid foo ( )\n{\nint temp = 0 ;\nif ( temp > 0 ) return 1 ;\n}", OurPreprocessor::expandMacros(filedata)); + } + + void macro_simple6() const { + const char filedata[] = "#define ABC (a+b+c)\n" + "ABC\n"; + ASSERT_EQUALS("\n( a + b + c )", OurPreprocessor::expandMacros(filedata)); + } + + void macro_simple7() const { + const char filedata[] = "#define ABC(str) str\n" + "ABC(\"(\")\n"; + ASSERT_EQUALS("\n\"(\"", OurPreprocessor::expandMacros(filedata)); + } + + void macro_simple8() const { + const char filedata[] = "#define ABC 123\n" + "#define ABCD 1234\n" + "ABC ABCD\n"; + ASSERT_EQUALS("\n\n123 1234", OurPreprocessor::expandMacros(filedata)); + } + + void macro_simple9() const { + const char filedata[] = "#define ABC(a) f(a)\n" + "ABC( \"\\\"\" );\n" + "ABC( \"g\" );\n"; + ASSERT_EQUALS("\nf ( \"\\\"\" ) ;\nf ( \"g\" ) ;", OurPreprocessor::expandMacros(filedata)); + } + + void macro_simple10() const { + const char filedata[] = "#define ABC(t) t x\n" + "ABC(unsigned long);\n"; + ASSERT_EQUALS("\nunsigned long x ;", OurPreprocessor::expandMacros(filedata)); + } + + void macro_simple11() const { + const char filedata[] = "#define ABC(x) delete x\n" + "ABC(a);\n"; + ASSERT_EQUALS("\ndelete a ;", OurPreprocessor::expandMacros(filedata)); + } + + void macro_simple12() const { + const char filedata[] = "#define AB ab.AB\n" + "AB.CD\n"; + ASSERT_EQUALS("\nab . AB . CD", OurPreprocessor::expandMacros(filedata)); + } + + void macro_simple13() const { + const char filedata[] = "#define TRACE(x)\n" + "TRACE(;if(a))\n"; + ASSERT_EQUALS("", OurPreprocessor::expandMacros(filedata)); + } + + void macro_simple14() const { + const char filedata[] = "#define A \" a \"\n" + "printf(A);\n"; + ASSERT_EQUALS("\nprintf ( \" a \" ) ;", OurPreprocessor::expandMacros(filedata)); + } + + void macro_simple15() const { + const char filedata[] = "#define FOO\"foo\"\n" + "FOO\n"; + ASSERT_EQUALS("\n\"foo\"", OurPreprocessor::expandMacros(filedata)); + } + + void macro_simple16() const { // # 4703 + const char filedata[] = "#define MACRO( A, B, C ) class A##B##C##Creator {};\n" + "MACRO( B\t, U , G )"; + ASSERT_EQUALS("\nclass BUGCreator { } ;", OurPreprocessor::expandMacros(filedata)); + } + + void macro_simple17() const { // # 5074 - the Token::isExpandedMacro() doesn't always indicate properly if token comes from macro + // It would probably be OK if the generated code was + // "\n123+$123" since the first 123 comes from the source code + const char filedata[] = "#define MACRO(A) A+123\n" + "MACRO(123)"; + ASSERT_EQUALS("\n123 + 123", OurPreprocessor::expandMacros(filedata)); + } + + void macro_simple18() const { // (1e-7) + const char filedata1[] = "#define A (1e-7)\n" + "a=A;"; + ASSERT_EQUALS("\na = ( 1e-7 ) ;", OurPreprocessor::expandMacros(filedata1)); + + const char filedata2[] = "#define A (1E-7)\n" + "a=A;"; + ASSERT_EQUALS("\na = ( 1E-7 ) ;", OurPreprocessor::expandMacros(filedata2)); + + const char filedata3[] = "#define A (1e+7)\n" + "a=A;"; + ASSERT_EQUALS("\na = ( 1e+7 ) ;", OurPreprocessor::expandMacros(filedata3)); + + const char filedata4[] = "#define A (1.e+7)\n" + "a=A;"; + ASSERT_EQUALS("\na = ( 1.e+7 ) ;", OurPreprocessor::expandMacros(filedata4)); + + const char filedata5[] = "#define A (1.7f)\n" + "a=A;"; + ASSERT_EQUALS("\na = ( 1.7f ) ;", OurPreprocessor::expandMacros(filedata5)); + + const char filedata6[] = "#define A (.1)\n" + "a=A;"; + ASSERT_EQUALS("\na = ( .1 ) ;", OurPreprocessor::expandMacros(filedata6)); + + const char filedata7[] = "#define A (1.)\n" + "a=A;"; + ASSERT_EQUALS("\na = ( 1. ) ;", OurPreprocessor::expandMacros(filedata7)); + + const char filedata8[] = "#define A (8.0E+007)\n" + "a=A;"; + ASSERT_EQUALS("\na = ( 8.0E+007 ) ;", OurPreprocessor::expandMacros(filedata8)); + } + + void macroInMacro1() const { + { + const char filedata[] = "#define A(m) long n = m; n++;\n" + "#define B(n) A(n)\n" + "B(0)\n"; + ASSERT_EQUALS("\n\nlong n = 0 ; n ++ ;", OurPreprocessor::expandMacros(filedata)); + } + + { + const char filedata[] = "#define A B\n" + "#define B 3\n" + "A\n"; + ASSERT_EQUALS("\n\n3", OurPreprocessor::expandMacros(filedata)); + } + + { + const char filedata[] = "#define BC(b, c...) 0##b * 0##c\n" + "#define ABC(a, b...) a + BC(b)\n" + "\n" + "ABC(1);\n" // <- too few parameters + "ABC(2,3);\n" + "ABC(4,5,6);\n"; + + ASSERT_EQUALS("\n\n\n1 + 0 * 0 ;\n2 + 03 * 0 ;\n4 + 05 * 06 ;", OurPreprocessor::expandMacros(filedata)); + } + + { + const char filedata[] = "#define A 4\n" + "#define B(a) a,A\n" + "B(2);\n"; + ASSERT_EQUALS("\n\n2 , 4 ;", OurPreprocessor::expandMacros(filedata)); + } + + { + const char filedata[] = "#define A(x) (x)\n" + "#define B )A(\n" + "#define C )A(\n"; + ASSERT_EQUALS("", OurPreprocessor::expandMacros(filedata)); + } + + { + const char filedata[] = "#define A(x) (x*2)\n" + "#define B A(\n" + "foo B(i));\n"; + ASSERT_EQUALS("\n\nfoo ( ( i ) * 2 ) ;", OurPreprocessor::expandMacros(filedata)); + } + + { + const char filedata[] = "#define foo foo\n" + "foo\n"; + ASSERT_EQUALS("\nfoo", OurPreprocessor::expandMacros(filedata)); + } + + { + const char filedata[] = + "#define B(A1, A2) } while (0)\n" + "#define A(name) void foo##name() { do { B(1, 2); }\n" + "A(0)\n" + "A(1)\n"; + ASSERT_EQUALS("\n\nvoid foo0 ( ) { do { } while ( 0 ) ; }\nvoid foo1 ( ) { do { } while ( 0 ) ; }", OurPreprocessor::expandMacros(filedata)); + } + + { + const char filedata[] = + "#define B(x) (\n" + "#define A() B(xx)\n" + "B(1) A() ) )\n"; + ASSERT_EQUALS("\n\n( ( ) )", OurPreprocessor::expandMacros(filedata)); + } + + { + const char filedata[] = + "#define PTR1 (\n" + "#define PTR2 PTR1 PTR1\n" + "int PTR2 PTR2 foo )))) = 0;\n"; + ASSERT_EQUALS("\n\nint ( ( ( ( foo ) ) ) ) = 0 ;", OurPreprocessor::expandMacros(filedata)); + } + + { + const char filedata[] = + "#define PTR1 (\n" + "PTR1 PTR1\n"; + ASSERT_EQUALS("\n( (", OurPreprocessor::expandMacros(filedata)); + } + } + + void macroInMacro2() const { + const char filedata[] = "#define A(x) a##x\n" + "#define B 0\n" + "A(B)\n"; + ASSERT_EQUALS("\n\naB", OurPreprocessor::expandMacros(filedata)); + } + + void macro_linenumbers() const { + const char filedata[] = "#define AAA(a)\n" + "AAA(5\n" + "\n" + ")\n" + "int a;\n"; + ASSERT_EQUALS("\n" + "\n" + "\n" + "\n" + "int a ;", + OurPreprocessor::expandMacros(filedata)); + } + + void macro_nopar() const { + const char filedata[] = "#define AAA( ) { NULL }\n" + "AAA()\n"; + ASSERT_EQUALS("\n{ NULL }", OurPreprocessor::expandMacros(filedata)); + } + + void macro_incdec() const { + const char filedata[] = "#define M1(X) 1+X\n" + "#define M2(X) 2-X\n" + "M1(+1) M2(-1)\n"; + ASSERT_EQUALS("\n\n1 + + 1 2 - - 1", OurPreprocessor::expandMacros(filedata)); + } + + void macro_switchCase() const { + { + // Make sure "case 2" doesn't become "case2" + const char filedata[] = "#define A( b ) " + "switch( a ){ " + "case 2: " + " break; " + "}\n" + "A( 5 );\n"; + ASSERT_EQUALS("\nswitch ( a ) { case 2 : break ; } ;", OurPreprocessor::expandMacros(filedata)); + } + + { + // Make sure "2 BB" doesn't become "2BB" + const char filedata[] = "#define A() AA : 2 BB\n" + "A();\n"; + ASSERT_EQUALS("\nAA : 2 BB ;", OurPreprocessor::expandMacros(filedata)); + } + + { + const char filedata[] = "#define A }\n" + "#define B() A\n" + "#define C( a ) B() break;\n" + "{C( 2 );\n"; + ASSERT_EQUALS("\n\n\n{ } break ; ;", OurPreprocessor::expandMacros(filedata)); + } + + + { + const char filedata[] = "#define A }\n" + "#define B() A\n" + "#define C( a ) B() _break;\n" + "{C( 2 );\n"; + ASSERT_EQUALS("\n\n\n{ } _break ; ;", OurPreprocessor::expandMacros(filedata)); + } + + + { + const char filedata[] = "#define A }\n" + "#define B() A\n" + "#define C( a ) B() 5;\n" + "{C( 2 );\n"; + ASSERT_EQUALS("\n\n\n{ } 5 ; ;", OurPreprocessor::expandMacros(filedata)); + } + } + + void macro_NULL() const { + // See ticket #4482 - UB when passing NULL to variadic function + ASSERT_EQUALS("\n0", OurPreprocessor::expandMacros("#define null 0\nnull")); + TODO_ASSERT_EQUALS("\nNULL", "\n0", OurPreprocessor::expandMacros("#define NULL 0\nNULL")); // TODO: Let the tokenizer handle NULL? + } + + void string1() { + const char filedata[] = "int main()" + "{" + " const char *a = \"#define A\";" + "}\n"; + + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(1, actual.size()); + ASSERT_EQUALS("int main ( ) { const char * a = \"#define A\" ; }", actual.at("")); + } + + void string2() const { + const char filedata[] = "#define AAA 123\n" + "str = \"AAA\"\n"; + + // Compare results.. + ASSERT_EQUALS("\nstr = \"AAA\"", OurPreprocessor::expandMacros(filedata)); + } + + void string3() const { + const char filedata[] = "str(\";\");\n"; + + // Compare results.. + ASSERT_EQUALS("str ( \";\" ) ;", OurPreprocessor::expandMacros(filedata)); + } + + + void preprocessor_undef() { + { + const char filedata[] = "#define AAA int a;\n" + "#undef AAA\n" + "#define AAA char b=0;\n" + "AAA\n"; + + // Compare results.. + ASSERT_EQUALS("\n\n\nchar b = 0 ;", OurPreprocessor::expandMacros(filedata)); + } + + { + // ticket #403 + const char filedata[] = "#define z p[2]\n" + "#undef z\n" + "int z;\n" + "z = 0;\n"; + ASSERT_EQUALS("\n\nint z ;\nz = 0 ;", PreprocessorHelper::getcode(settings0, *this, filedata, "", "")); + } + } + + void defdef() const { + const char filedata[] = "#define AAA 123\n" + "#define AAA 456\n" + "#define AAA 789\n" + "AAA\n"; + + // Compare results.. + ASSERT_EQUALS("\n\n\n789", OurPreprocessor::expandMacros(filedata)); + } + + void preprocessor_doublesharp() const { + // simple testcase without ## + const char filedata1[] = "#define TEST(var,val) var = val\n" + "TEST(foo,20);\n"; + ASSERT_EQUALS("\nfoo = 20 ;", OurPreprocessor::expandMacros(filedata1)); + + // simple testcase with ## + const char filedata2[] = "#define TEST(var,val) var##_##val = val\n" + "TEST(foo,20);\n"; + ASSERT_EQUALS("\nfoo_20 = 20 ;", OurPreprocessor::expandMacros(filedata2)); + + // concat macroname + const char filedata3[] = "#define ABCD 123\n" + "#define A(B) A##B\n" + "A(BCD)\n"; + ASSERT_EQUALS("\n\n123", OurPreprocessor::expandMacros(filedata3)); + + // Ticket #1802 - inner ## must be expanded before outer macro + const char filedata4[] = "#define A(B) A##B\n" + "#define a(B) A(B)\n" + "a(A(B))\n"; + ASSERT_EQUALS("\n\nAAB", OurPreprocessor::expandMacros(filedata4)); + + // Ticket #1802 - inner ## must be expanded before outer macro + const char filedata5[] = "#define AB(A,B) A##B\n" + "#define ab(A,B) AB(A,B)\n" + "ab(a,AB(b,c))\n"; + ASSERT_EQUALS("\n\nabc", OurPreprocessor::expandMacros(filedata5)); + + // Ticket #1802 + const char filedata6[] = "#define AB_(A,B) A ## B\n" + "#define AB(A,B) AB_(A,B)\n" + "#define ab(suf) AB(X, AB_(_, suf))\n" + "#define X x\n" + "ab(y)\n"; + ASSERT_EQUALS("\n\n\n\nx_y", OurPreprocessor::expandMacros(filedata6)); + } + + + + void preprocessor_include_in_str() { + const char filedata[] = "int main()\n" + "{\n" + "const char *a = \"#include \";\n" + "return 0;\n" + "}\n"; + + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(1, actual.size()); + ASSERT_EQUALS("int main ( )\n{\nconst char * a = \"#include \" ;\nreturn 0 ;\n}", actual.at("")); + } + + void va_args_1() const { + const char filedata[] = "#define DBG(fmt...) printf(fmt)\n" + "DBG(\"[0x%lx-0x%lx)\", pstart, pend);\n"; + + // Preprocess.. + std::string actual = OurPreprocessor::expandMacros(filedata); + + ASSERT_EQUALS("\nprintf ( \"[0x%lx-0x%lx)\" , pstart , pend ) ;", actual); + } + /* + void va_args_2() const { + const char filedata[] = "#define DBG(fmt, args...) printf(fmt, ## args)\n" + "DBG(\"hello\");\n"; + + // Preprocess.. + std::string actual = OurPreprocessor::expandMacros(filedata); + + // invalid code ASSERT_EQUALS("\nprintf ( \"hello\" ) ;", actual); + } + */ + void va_args_3() const { + const char filedata[] = "#define FRED(...) { fred(__VA_ARGS__); }\n" + "FRED(123)\n"; + ASSERT_EQUALS("\n{ fred ( 123 ) ; }", OurPreprocessor::expandMacros(filedata)); + } + + void va_args_4() const { + const char filedata[] = "#define FRED(name, ...) name (__VA_ARGS__)\n" + "FRED(abc, 123)\n"; + ASSERT_EQUALS("\nabc ( 123 )", OurPreprocessor::expandMacros(filedata)); + } + + void va_args_5() const { + const char filedata1[] = "#define A(...) #__VA_ARGS__\n" + "A(123)\n"; + ASSERT_EQUALS("\n\"123\"", OurPreprocessor::expandMacros(filedata1)); + + const char filedata2[] = "#define A(X,...) X(#__VA_ARGS__)\n" + "A(f,123)\n"; + ASSERT_EQUALS("\nf ( \"123\" )", OurPreprocessor::expandMacros(filedata2)); + } + + + + void multi_character_character() { + const char filedata[] = "#define FOO 'ABCD'\n" + "int main()\n" + "{\n" + "if( FOO == 0 );\n" + "return 0;\n" + "}\n"; + + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(1, actual.size()); + ASSERT_EQUALS("\nint main ( )\n{\nif ( $'ABCD' == 0 ) ;\nreturn 0 ;\n}", actual.at("")); + } + + + void stringify() const { + const char filedata[] = "#define STRINGIFY(x) #x\n" + "STRINGIFY(abc)\n"; + + // expand macros.. + std::string actual = OurPreprocessor::expandMacros(filedata); + + ASSERT_EQUALS("\n\"abc\"", actual); + } + + void stringify2() const { + const char filedata[] = "#define A(x) g(#x)\n" + "A(abc);\n"; + + // expand macros.. + std::string actual = OurPreprocessor::expandMacros(filedata); + + ASSERT_EQUALS("\ng ( \"abc\" ) ;", actual); + } + + void stringify3() const { + const char filedata[] = "#define A(x) g(#x)\n" + "A( abc);\n"; + + // expand macros.. + std::string actual = OurPreprocessor::expandMacros(filedata); + + ASSERT_EQUALS("\ng ( \"abc\" ) ;", actual); + } + + void stringify4() const { + const char filedata[] = "#define A(x) #x\n" + "1 A(\n" + "abc\n" + ") 2\n"; + + // expand macros.. + std::string actual = OurPreprocessor::expandMacros(filedata); + + ASSERT_EQUALS("\n1 \"abc\"\n\n2", actual); + } + + void stringify5() const { + const char filedata[] = "#define A(x) a(#x,x)\n" + "A(foo(\"\\\"\"))\n"; + ASSERT_EQUALS("\na ( \"foo(\\\"\\\\\\\"\\\")\" , foo ( \"\\\"\" ) )", OurPreprocessor::expandMacros(filedata)); + } + + void pragma() { + const char filedata[] = "#pragma once\n" + "void f()\n" + "{\n" + "}\n"; + + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(1, actual.size()); + ASSERT_EQUALS("\nvoid f ( )\n{\n}", actual.at("")); + } + + 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.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(1, actual.size()); + ASSERT_EQUALS("asm ( )\n;\n\naaa\nasm ( ) ;\n\n\nbbb", actual.at("")); + } + + void pragma_asm_2() { + const char filedata[] = "#pragma asm\n" + " mov @w1, 11\n" + "#pragma endasm ( temp=@w1 )\n" + "bbb"; + + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(1, actual.size()); + ASSERT_EQUALS("asm ( )\n;\n\nbbb", actual.at("")); + } + + void endifsemicolon() { + const char filedata[] = "void f() {\n" + "#ifdef A\n" + "#endif;\n" + "}\n"; + + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(2, actual.size()); + const std::string expected("void f ( ) {\n\n\n}"); + ASSERT_EQUALS(expected, actual.at("")); + ASSERT_EQUALS(expected, actual.at("A")); + } + + void handle_error() { + { + const char filedata[] = "#define A \n" + "#define B don't want to \\\n" + "more text\n" + "void f()\n" + "{\n" + " char a = 'a'; // '\n" + "}\n"; + + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + ASSERT_EQUALS(0, actual.size()); + ASSERT_EQUALS("[file.c:2]: (error) No pair for character ('). Can't process file. File is either invalid or unicode, which is currently not supported.\n", errout_str()); + } + } + + void missing_doublequote() { + { + const char filedata[] = "#define a\n" + "#ifdef 1\n" + "\"\n" + "#endif\n"; + + // expand macros.. + 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.. + 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.. + 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.. + const std::string actual(OurPreprocessor::expandMacros(filedata, this)); + + ASSERT_EQUALS("", actual); + ASSERT_EQUALS("[file.cpp:2]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", errout_str()); + } + + { + const char filedata[] = "void foo()\n" + "{\n" + "\n" + "\n" + "\n" + "int a = 0;\n" + "printf(Text\");\n" + "}\n"; + + // expand macros.. + OurPreprocessor::expandMacros(filedata, this); + + ASSERT_EQUALS("[file.cpp:7]: (error) No pair for character (\"). Can't process file. File is either invalid or unicode, which is currently not supported.\n", errout_str()); + } + } + + + void define_part_of_func() { + const char filedata[] = "#define A g(\n" + "void f() {\n" + " A );\n" + " }\n"; + + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(1, actual.size()); + ASSERT_EQUALS("\nvoid f ( ) {\n$g $( ) ;\n}", actual.at("")); + 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.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(2, actual.size()); + ASSERT_EQUALS("\n\n\n\n\n$20", actual.at("")); + ASSERT_EQUALS("\n\n\n\n\n$10", actual.at("A")); + ASSERT_EQUALS("", errout_str()); + } + + void macro_parameters() { + 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.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(1, actual.size()); + ASSERT_EQUALS("", actual.at("")); + ASSERT_EQUALS("[file.c:6]: (error) failed to expand 'BC', Wrong number of parameters for macro 'BC'.\n", errout_str()); + } + + void newline_in_macro() { + const char filedata[] = "#define ABC(str) printf( str )\n" + "void f()\n" + "{\n" + " ABC(\"\\n\");\n" + "}\n"; + + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(1, actual.size()); + ASSERT_EQUALS("\nvoid f ( )\n{\n$printf $( \"\\n\" $) ;\n}", actual.at("")); + ASSERT_EQUALS("", errout_str()); + } + + void ifdef_ifdefined() { + const char filedata[] = "#ifdef ABC\n" + "A\n" + "#endif\t\n" + "#if defined ABC\n" + "A\n" + "#endif\n"; + + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(2, actual.size()); + ASSERT_EQUALS("", actual.at("")); + ASSERT_EQUALS("\nA\n\n\nA", actual.at("ABC")); + } + + void define_if1() { + { + const char filedata[] = "#define A 0\n" + "#if A\n" + "FOO\n" + "#endif"; + ASSERT_EQUALS("", PreprocessorHelper::getcode(settings0, *this, filedata,"","")); + } + { + const char filedata[] = "#define A 1\n" + "#if A==1\n" + "FOO\n" + "#endif"; + ASSERT_EQUALS("\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","")); + } + } + + void define_if2() { + const char filedata[] = "#define A 22\n" + "#define B A\n" + "#if (B==A) || (B==C)\n" + "FOO\n" + "#endif"; + ASSERT_EQUALS("\n\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","")); + } + + void define_if3() { + const char filedata[] = "#define A 0\n" + "#if (A==0)\n" + "FOO\n" + "#endif"; + ASSERT_EQUALS("\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","")); + } + + void define_if4() { + const char filedata[] = "#define X +123\n" + "#if X==123\n" + "FOO\n" + "#endif"; + ASSERT_EQUALS("\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","")); + } + + void define_if5() { // #4516 - #define B (A & 0x00f0) + { + const char filedata[] = "#define A 0x0010\n" + "#define B (A & 0x00f0)\n" + "#if B==0x0010\n" + "FOO\n" + "#endif"; + ASSERT_EQUALS("\n\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","")); + } + { + const char filedata[] = "#define A 0x00f0\n" + "#define B (16)\n" + "#define C (B & A)\n" + "#if C==0x0010\n" + "FOO\n" + "#endif"; + ASSERT_EQUALS("\n\n\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","")); + } + { + const char filedata[] = "#define A (1+A)\n" // don't hang for recursive macros + "#if A==1\n" + "FOO\n" + "#endif"; + ASSERT_EQUALS("\n\nFOO", PreprocessorHelper::getcode(settings0, *this, filedata,"","")); + } + } + + void define_if6() { // #4516 - #define B (A?1:-1) + const char filedata[] = "#ifdef A\n" + "#define B (A?1:-1)\n" + "#endif\n" + "\n" + "#if B < 0\n" + "123\n" + "#endif\n" + "\n" + "#if B >= 0\n" + "456\n" + "#endif\n"; + const std::string actualA0 = PreprocessorHelper::getcode(settings0, *this, 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 = PreprocessorHelper::getcode(settings0, *this, 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.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(1, (int)actual.size()); + ASSERT_EQUALS("\n\n\n\nB", actual.at("")); + } + + { + const char filedata[] = "#define A 1\n" + "#ifdef A\n" + "A\n" + "#endif\n"; + + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(1, (int)actual.size()); + ASSERT_EQUALS("\n\n$1", actual.at("")); + } + + { + const char filedata[] = "#define A 1\n" + "#if A==1\n" + "A\n" + "#endif\n"; + + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(1, (int)actual.size()); + ASSERT_EQUALS("\n\n$1", actual.at("")); + } + + { + const char filedata[] = "#define A 1\n" + "#if A>0\n" + "A\n" + "#endif\n"; + + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(1, (int)actual.size()); + ASSERT_EQUALS("\n\n$1", actual.at("")); + } + + { + const char filedata[] = "#define A 1\n" + "#if 0\n" + "#undef A\n" + "#endif\n" + "A\n"; + + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(1, (int)actual.size()); + ASSERT_EQUALS("\n\n\n\n$1", actual.at("")); + } + } + + void define_ifndef1() { + const char filedata[] = "#define A(x) (x)\n" + "#ifndef A\n" + ";\n" + "#endif\n"; + + // Preprocess => actual result.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(1U, actual.size()); + ASSERT_EQUALS("", actual.at("")); + } + + void define_ifndef2() { + const char filedata[] = "#ifdef A\n" + "#define B char\n" + "#endif\n" + "#ifndef B\n" + "#define B int\n" + "#endif\n" + "B me;\n"; + + // Preprocess => actual result.. + ASSERT_EQUALS("\n\n\n\n\n\n$int me ;", PreprocessorHelper::getcode(settings0, *this, filedata, "", "a.cpp")); + ASSERT_EQUALS("\n\n\n\n\n\n$char me ;", PreprocessorHelper::getcode(settings0, *this, 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.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + ASSERT_EQUALS(1U, actual.size()); + ASSERT_EQUALS("\n\n\n123 ;", actual.at("")); + } + + void undef_ifdef() { + const char filedata[] = "#undef A\n" + "#ifdef A\n" + "123\n" + "#endif\n"; + + // Preprocess => actual result.. + ASSERT_EQUALS("", PreprocessorHelper::getcode(settings0, *this, filedata, "", "a.cpp")); + ASSERT_EQUALS("", PreprocessorHelper::getcode(settings0, *this, 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.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // 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.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // Compare results.. + ASSERT_EQUALS(1, (int)actual.size()); + ASSERT_EQUALS("char a [ ] = \"#endfile\" ;\nchar b [ ] = \"#endfile\" ;", actual.at("")); + } + + 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.. + const std::map actual = PreprocessorHelper::getcode(settings0, *this, filedata); + + // B will always be defined if A is defined; the following test + // cases should be fixed whenever this other bug is fixed + ASSERT_EQUALS(2U, actual.size()); + + ASSERT_EQUALS_MSG(true, (actual.find("A") != actual.end()), "A is expected to be checked but it was not checked"); + + ASSERT_EQUALS_MSG(true, (actual.find("A;A;B") == actual.end()), "A;A;B is expected to NOT be checked but it was checked"); + } + + void invalid_define_1() { + (void)PreprocessorHelper::getcode(settings0, *this, "#define =\n"); + ASSERT_EQUALS("[file.c:1]: (error) Failed to parse #define\n", errout_str()); + } + + void invalid_define_2() { // #4036 + (void)PreprocessorHelper::getcode(settings0, *this, "#define () {(int f(x) }\n"); + ASSERT_EQUALS("[file.c:1]: (error) Failed to parse #define\n", errout_str()); + } + + void inline_suppressions() { + /*const*/ Settings settings; + settings.inlineSuppressions = true; + settings.checks.enable(Checks::missingInclude); + + const std::string code("// cppcheck-suppress missingInclude\n" + "#include \"missing.h\"\n" + "// cppcheck-suppress missingIncludeSystem\n" + "#include \n"); + SuppressionList inlineSuppr; + PreprocessorHelper::getcode(settings, *this, code, "", "test.c", &inlineSuppr); + + auto suppressions = inlineSuppr.getSuppressions(); + ASSERT_EQUALS(2, suppressions.size()); + + auto suppr = suppressions.front(); + suppressions.pop_front(); + ASSERT_EQUALS("missingInclude", suppr.errorId); + ASSERT_EQUALS("test.c", suppr.fileName); + ASSERT_EQUALS(2, suppr.lineNumber); + ASSERT_EQUALS(false, suppr.checked); + ASSERT_EQUALS(false, suppr.matched); + + suppr = suppressions.front(); + suppressions.pop_front(); + ASSERT_EQUALS("missingIncludeSystem", suppr.errorId); + ASSERT_EQUALS("test.c", suppr.fileName); + ASSERT_EQUALS(4, suppr.lineNumber); + ASSERT_EQUALS(false, suppr.checked); + ASSERT_EQUALS(false, suppr.matched); + + ignore_errout(); // we are not interested in the output + } + + void predefine1() { + const std::string src("#if defined X || Y\n" + "Fred & Wilma\n" + "#endif\n"); + std::string actual = PreprocessorHelper::getcode(settings0, *this, src, "X=1", "test.c"); + + ASSERT_EQUALS("\nFred & Wilma", actual); + } + + void predefine2() { + const std::string src("#if defined(X) && Y\n" + "Fred & Wilma\n" + "#endif\n"); + { + std::string actual = PreprocessorHelper::getcode(settings0, *this, src, "X=1", "test.c"); + ASSERT_EQUALS("", actual); + } + + { + std::string actual = PreprocessorHelper::getcode(settings0, *this, src, "X=1;Y=2", "test.c"); + ASSERT_EQUALS("\nFred & Wilma", actual); + } + } + + void predefine3() { + // #2871 - define in source is not used if -D is used + const char code[] = "#define X 1\n" + "#define Y X\n" + "#if (X == Y)\n" + "Fred & Wilma\n" + "#endif\n"; + const std::string actual = PreprocessorHelper::getcode(settings0, *this, code, "TEST", "test.c"); + ASSERT_EQUALS("\n\n\nFred & Wilma", actual); + } + + void predefine4() { + // #3577 + const char code[] = "char buf[X];\n"; + const std::string actual = PreprocessorHelper::getcode(settings0, *this, code, "X=123", "test.c"); + ASSERT_EQUALS("char buf [ $123 ] ;", actual); + } + + void predefine5() { // #3737, #5119 - automatically define __cplusplus + // #3737... + const char code[] = "#ifdef __cplusplus\n123\n#endif"; + ASSERT_EQUALS("", PreprocessorHelper::getcode(settings0, *this, code, "", "test.c")); + ASSERT_EQUALS("\n123", PreprocessorHelper::getcode(settings0, *this, code, "", "test.cpp")); + } + + void predefine6() { // automatically define __STDC_VERSION__ + const char code[] = "#ifdef __STDC_VERSION__\n123\n#endif"; + ASSERT_EQUALS("\n123", PreprocessorHelper::getcode(settings0, *this, code, "", "test.c")); + ASSERT_EQUALS("", PreprocessorHelper::getcode(settings0, *this, code, "", "test.cpp")); + } + + void invalidElIf() { + // #2942 - segfault + const char code[] = "#elif (){\n"; + const std::string actual = PreprocessorHelper::getcode(settings0, *this, code, "TEST", "test.c"); + ASSERT_EQUALS("", actual); + ASSERT_EQUALS("[test.c:1]: (error) #elif without #if\n", errout_str()); + } + + void getConfigs1() { + const char filedata[] = "#ifdef WIN32 \n" + " abcdef\n" + "#else \n" + " qwerty\n" + "#endif \n"; + + ASSERT_EQUALS("\nWIN32\n", getConfigsStr(filedata)); + } + + void getConfigs2() { + const char filedata[] = "# ifndef WIN32\n" + " \" # ifdef WIN32\" // a comment\n" + " # else \n" + " qwerty\n" + " # endif \n"; + ASSERT_EQUALS("\nWIN32\n", getConfigsStr(filedata)); + } + + void getConfigs3() { + const char filedata[] = "#ifdef ABC\n" + "a\n" + "#ifdef DEF\n" + "b\n" + "#endif\n" + "c\n" + "#endif\n"; + + ASSERT_EQUALS("\nABC\nABC;DEF\n", getConfigsStr(filedata)); + } + + void getConfigs4() { + const char filedata[] = "#ifdef ABC\n" + "A\n" + "#endif\t\n" + "#ifdef ABC\n" + "A\n" + "#endif\n"; + ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); + } + + void getConfigs5() { + const char filedata[] = "#ifdef ABC\n" + "A\n" + "#else\n" + "B\n" + "#ifdef DEF\n" + "C\n" + "#endif\n" + "#endif\n"; + ASSERT_EQUALS("\nABC\nDEF\n", getConfigsStr(filedata)); + } + + void getConfigs7() { + const char filedata[] = "#ifdef ABC\n" + "A\n" + "#ifdef ABC\n" + "B\n" + "#endif\n" + "#endif\n"; + ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); + } + + void getConfigs7a() { + const char filedata[] = "#ifndef ABC\n" + "A\n" + "#ifndef ABC\n" + "B\n" + "#endif\n" + "#endif\n"; + ASSERT_EQUALS("\n", getConfigsStr(filedata)); + } + + void getConfigs7b() { + const char filedata[] = "#ifndef ABC\n" + "A\n" + "#ifdef ABC\n" + "B\n" + "#endif\n" + "#endif\n"; + ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); + } + + void getConfigs7c() { + const char filedata[] = "#ifdef ABC\n" + "A\n" + "#ifndef ABC\n" + "B\n" + "#endif\n" + "#endif\n"; + ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); + } + + void getConfigs7d() { + const char filedata[] = "#if defined(ABC)\n" + "A\n" + "#if defined(ABC)\n" + "B\n" + "#endif\n" + "#endif\n"; + ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); + } + + void getConfigs7e() { + const char filedata[] = "#ifdef ABC\n" + "#file \"test.h\"\n" + "#ifndef test_h\n" + "#define test_h\n" + "#ifdef ABC\n" + "#endif\n" + "#endif\n" + "#endfile\n" + "#endif\n"; + ASSERT_EQUALS("\nABC\n", getConfigsStr(filedata)); + } + + void getConfigs8() { + const char filedata[] = "#if A == 1\n" + "1\n" + "#endif\n"; + ASSERT_EQUALS("\nA=1\n", getConfigsStr(filedata)); + } + + void getConfigs10() { // Ticket #5139 + const char filedata[] = "#define foo a.foo\n" + "#define bar foo\n" + "#define baz bar+0\n" + "#if 0\n" + "#endif"; + ASSERT_EQUALS("\n", getConfigsStr(filedata)); + } + + void getConfigs11() { // #9832 - include guards + const char filedata[] = "#file \"test.h\"\n" + "#if !defined(test_h)\n" + "#define test_h\n" + "123\n" + "#endif\n" + "#endfile\n"; + ASSERT_EQUALS("\n", getConfigsStr(filedata)); + } + + void getConfigsError() { + const char filedata1[] = "#ifndef X\n" + "#error \"!X\"\n" + "#endif\n"; + ASSERT_EQUALS("X\n", getConfigsStr(filedata1)); + + const char filedata2[] = "#ifdef X\n" + "#ifndef Y\n" + "#error \"!Y\"\n" + "#endif\n" + "#endif\n"; + ASSERT_EQUALS("\nX;Y\nY\n", getConfigsStr(filedata2)); + } + + void getConfigsD1() { + const char filedata[] = "#ifdef X\n" + "#else\n" + "#ifdef Y\n" + "#endif\n" + "#endif\n"; + ASSERT_EQUALS("\n", getConfigsStr(filedata, "-DX")); + ASSERT_EQUALS("\nX\nY\n", getConfigsStr(filedata)); + } + + void getConfigsU1() { + const char filedata[] = "#ifdef X\n" + "#endif\n"; + ASSERT_EQUALS("\n", getConfigsStr(filedata, "-UX")); + ASSERT_EQUALS("\nX\n", getConfigsStr(filedata)); + } + + void getConfigsU2() { + const char filedata[] = "#ifndef X\n" + "#endif\n"; + ASSERT_EQUALS("\n", getConfigsStr(filedata, "-UX")); + ASSERT_EQUALS("\n", getConfigsStr(filedata)); // no #else + } + + void getConfigsU3() { + const char filedata[] = "#ifndef X\n" + "Fred & Wilma\n" + "#else\n" + "Barney & Betty\n" + "#endif\n"; + ASSERT_EQUALS("\n", getConfigsStr(filedata, "-UX")); + ASSERT_EQUALS("\nX\n", getConfigsStr(filedata)); + } + + void getConfigsU4() { + const char filedata[] = "#if defined(X) || defined(Y) || defined(Z)\n" + "#else\n" + "#endif\n"; + ASSERT_EQUALS("\nY;Z\n", getConfigsStr(filedata, "-UX")); + ASSERT_EQUALS("\nX;Y;Z\n", getConfigsStr(filedata)); + } + + void getConfigsU5() { + const char filedata[] = "#if X==1\n" + "#endif\n"; + ASSERT_EQUALS("\n", getConfigsStr(filedata, "-UX")); + ASSERT_EQUALS("\nX=1\n", getConfigsStr(filedata)); + } + + void getConfigsU6() { + const char filedata[] = "#if X==0\n" + "#endif\n"; + ASSERT_EQUALS("\nX=0\n", getConfigsStr(filedata, "-UX")); + ASSERT_EQUALS("\nX=0\n", getConfigsStr(filedata)); + } + + void getConfigsU7() { + const char code[] = "#ifndef Y\n" + "#else\n" + "#endif\n"; + ASSERT_EQUALS("\nY\n", getConfigsStr(code, "-DX")); + } + + void 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"; + + const std::map actual = PreprocessorHelper::getcode(settings0, *this, code); + ASSERT_EQUALS("\nFred & Wilma", actual.at("")); + } + + 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.. + (void)PreprocessorHelper::getcode(settings0, *this, filedata); + ASSERT_EQUALS( + "[file.c:1]: (error) Syntax error in #ifdef\n" + "[file.c:1]: (error) Syntax error in #ifdef\n", errout_str()); + } + + void garbage() { + const char filedata[] = "V\n" + "#define X b #endif #line 0 \"x\" ;\n" + "#if ! defined ( Y ) #endif"; + + // Preprocess => don't crash.. + (void)PreprocessorHelper::getcode(settings0, *this, filedata); + } + + void wrongPathOnErrorDirective() { + const auto settings = dinit(Settings, $.userDefines = "foo"); + const std::string code("#error hello world!\n"); + PreprocessorHelper::getcode(settings, *this, code, "X", "./././test.c"); + ASSERT_EQUALS("[test.c:1]: (error) #error hello world!\n", errout_str()); + } + + // test for existing local include + void testMissingInclude() { + /*const*/ Settings settings; + settings.clearIncludeCache = true; + settings.checks.enable(Checks::missingInclude); + settings.templateFormat = "simple"; // has no effect + setTemplateFormat("simple"); + + ScopedFile header("header.h", ""); + + std::string code("#include \"header.h\""); + PreprocessorHelper::getcode(settings, *this, code, "", "test.c"); + + ASSERT_EQUALS("", errout_str()); + } + + // test for missing local include + void testMissingInclude2() { + /*const*/ Settings settings; + settings.clearIncludeCache = true; + settings.checks.enable(Checks::missingInclude); + settings.templateFormat = "simple"; // has no effect + setTemplateFormat("simple"); + + std::string code("#include \"header.h\""); + PreprocessorHelper::getcode(settings, *this, code, "", "test.c"); + + ASSERT_EQUALS("test.c:1:0: information: Include file: \"header.h\" not found. [missingInclude]\n", errout_str()); + } + + // test for missing local include - no include path given + void testMissingInclude3() { + /*const*/ Settings settings; + settings.clearIncludeCache = true; + settings.checks.enable(Checks::missingInclude); + settings.templateFormat = "simple"; // has no effect + setTemplateFormat("simple"); + + ScopedFile header("header.h", "", "inc"); + + std::string code("#include \"header.h\""); + PreprocessorHelper::getcode(settings, *this, code, "", "test.c"); + + ASSERT_EQUALS("test.c:1:0: information: Include file: \"header.h\" not found. [missingInclude]\n", errout_str()); + } + + // test for existing local include - include path provided + void testMissingInclude4() { + /*const*/ Settings settings; + settings.clearIncludeCache = true; + settings.checks.enable(Checks::missingInclude); + settings.includePaths.emplace_back("inc"); + settings.templateFormat = "simple"; // has no effect + setTemplateFormat("simple"); + + ScopedFile header("header.h", "", "inc"); + + std::string code("#include \"inc/header.h\""); + PreprocessorHelper::getcode(settings, *this, code, "", "test.c"); + + ASSERT_EQUALS("", errout_str()); + } + + // test for existing local include - absolute path + void testMissingInclude5() { + /*const*/ Settings settings; + settings.clearIncludeCache = true; + settings.checks.enable(Checks::missingInclude); + settings.includePaths.emplace_back("inc"); + settings.templateFormat = "simple"; // has no effect + setTemplateFormat("simple"); + + ScopedFile header("header.h", "", Path::getCurrentPath()); + + std::string code("#include \"" + header.path() + "\""); + PreprocessorHelper::getcode(settings, *this, code, "", "test.c"); + + ASSERT_EQUALS("", errout_str()); + } + + // test for missing local include - absolute path + void testMissingInclude6() { + /*const*/ Settings settings; + settings.clearIncludeCache = true; + settings.checks.enable(Checks::missingInclude); + settings.templateFormat = "simple"; // has no effect + setTemplateFormat("simple"); + + const std::string header = Path::join(Path::getCurrentPath(), "header.h"); + + std::string code("#include \"" + header + "\""); + PreprocessorHelper::getcode(settings, *this, code, "", "test.c"); + + ASSERT_EQUALS("test.c:1:0: information: Include file: \"" + header + "\" not found. [missingInclude]\n", errout_str()); + } + + // test for missing system include - system includes are not searched for in relative path + void testMissingSystemInclude() { + /*const*/ Settings settings; + settings.clearIncludeCache = true; + settings.checks.enable(Checks::missingInclude); + settings.templateFormat = "simple"; // has no effect + setTemplateFormat("simple"); + + ScopedFile header("header.h", ""); + + std::string code("#include "); + PreprocessorHelper::getcode(settings, *this, code, "", "test.c"); + + ASSERT_EQUALS("test.c:1:0: information: Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results. [missingIncludeSystem]\n", errout_str()); + } + + // test for missing system include + void testMissingSystemInclude2() { + /*const*/ Settings settings; + settings.clearIncludeCache = true; + settings.checks.enable(Checks::missingInclude); + settings.templateFormat = "simple"; // has no effect + setTemplateFormat("simple"); + + std::string code("#include "); + PreprocessorHelper::getcode(settings, *this, code, "", "test.c"); + + ASSERT_EQUALS("test.c:1:0: information: Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results. [missingIncludeSystem]\n", errout_str()); + } + + // test for existing system include in system include path + void testMissingSystemInclude3() { + /*const*/ Settings settings; + settings.clearIncludeCache = true; + settings.checks.enable(Checks::missingInclude); + settings.templateFormat = "simple"; // has no effect + setTemplateFormat("simple"); + settings.includePaths.emplace_back("system"); + + ScopedFile header("header.h", "", "system"); + + std::string code("#include "); + PreprocessorHelper::getcode(settings0, *this, code, "", "test.c"); + + ASSERT_EQUALS("", errout_str()); + } + + // test for existing system include - absolute path + void testMissingSystemInclude4() { + /*const*/ Settings settings; + settings.clearIncludeCache = true; + settings.checks.enable(Checks::missingInclude); + settings.includePaths.emplace_back("inc"); + settings.templateFormat = "simple"; // has no effect + setTemplateFormat("simple"); + + ScopedFile header("header.h", "", Path::getCurrentPath()); + + std::string code("#include <" + header.path() + ">"); + PreprocessorHelper::getcode(settings, *this, code, "", "test.c"); + + ASSERT_EQUALS("", errout_str()); + } + + // test for missing system include - absolute path + void testMissingSystemInclude5() { + /*const*/ Settings settings; + settings.clearIncludeCache = true; + settings.checks.enable(Checks::missingInclude); + settings.templateFormat = "simple"; // has no effect + setTemplateFormat("simple"); + + const std::string header = Path::join(Path::getCurrentPath(), "header.h"); + + std::string code("#include <" + header + ">"); + PreprocessorHelper::getcode(settings, *this, code, "", "test.c"); + + ASSERT_EQUALS("test.c:1:0: information: Include file: <" + header + "> not found. Please note: Cppcheck does not need standard library headers to get proper results. [missingIncludeSystem]\n", errout_str()); + } + + // test for missing local and system include + void testMissingIncludeMixed() { + /*const*/ Settings settings; + settings.clearIncludeCache = true; + settings.checks.enable(Checks::missingInclude); + settings.templateFormat = "simple"; // has no effect + setTemplateFormat("simple"); + + ScopedFile header("header.h", ""); + ScopedFile header2("header2.h", ""); + + std::string code("#include \"missing.h\"\n" + "#include \n" + "#include \n" + "#include \"header2.h\""); + PreprocessorHelper::getcode(settings, *this, code, "", "test.c"); + + ASSERT_EQUALS("test.c:1:0: information: Include file: \"missing.h\" not found. [missingInclude]\n" + "test.c:2:0: information: Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results. [missingIncludeSystem]\n" + "test.c:3:0: information: Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results. [missingIncludeSystem]\n", errout_str()); + } + + void testMissingIncludeCheckConfig() { + /*const*/ Settings settings; + settings.clearIncludeCache = true; + settings.checks.enable(Checks::missingInclude); + settings.includePaths.emplace_back("system"); + settings.templateFormat = "simple"; // has no effect + setTemplateFormat("simple"); + + ScopedFile header("header.h", ""); + ScopedFile header2("header2.h", ""); + ScopedFile header3("header3.h", "", "system"); + ScopedFile header4("header4.h", "", "inc"); + ScopedFile header5("header5.h", "", Path::getCurrentPath()); + ScopedFile header6("header6.h", "", Path::getCurrentPath()); + + const std::string missing3 = Path::join(Path::getCurrentPath(), "missing3.h"); + const std::string missing4 = Path::join(Path::getCurrentPath(), "missing4.h"); + + std::string code("#include \"missing.h\"\n" + "#include \n" + "#include \n" + "#include \"header2.h\"\n" + "#include \n" + "#include \"header4.h\"\n" + "#include \"inc/header4.h\"\n" + "#include \"" + header5.path() + "\"\n" + "#include \"" + missing3 + "\"\n" + "#include <" + header6.path() + ">\n" + "#include <" + missing4 + ">\n"); + PreprocessorHelper::getcode(settings, *this, code, "", "test.c"); + + ASSERT_EQUALS("test.c:1:0: information: Include file: \"missing.h\" not found. [missingInclude]\n" + "test.c:2:0: information: Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results. [missingIncludeSystem]\n" + "test.c:3:0: information: Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results. [missingIncludeSystem]\n" + "test.c:6:0: information: Include file: \"header4.h\" not found. [missingInclude]\n" + "test.c:9:0: information: Include file: \"" + missing3 + "\" not found. [missingInclude]\n" + "test.c:11:0: information: Include file: <" + missing4 + "> not found. Please note: Cppcheck does not need standard library headers to get proper results. [missingIncludeSystem]\n", errout_str()); + } + + void limitsDefines() { + // #11928 / #10045 + const char code[] = "void f(long l) {\n" + " if (l > INT_MAX) {}\n" + "}"; + const std::string actual = PreprocessorHelper::getcode(settings0, *this, code, "", "test.c"); + ASSERT_EQUALS("void f ( long l ) {\n" + "if ( l > $2147483647 ) { }\n" + "}", actual); + } +}; + +REGISTER_TEST(TestPreprocessor) diff --git a/cppcheck-2.14.0/test/testprocessexecutor.cpp b/cppcheck-2.14.0/test/testprocessexecutor.cpp new file mode 100644 index 00000000..c29bd323 --- /dev/null +++ b/cppcheck-2.14.0/test/testprocessexecutor.cpp @@ -0,0 +1,384 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "processexecutor.h" +#include "redirect.h" +#include "settings.h" +#include "filesettings.h" +#include "fixture.h" +#include "helpers.h" +#include "suppressions.h" +#include "timer.h" + +#include +#include +#include +#include +#include +#include +#include + +class TestProcessExecutorBase : public TestFixture { +public: + TestProcessExecutorBase(const char * const name, bool useFS) : TestFixture(name), useFS(useFS) {} + +private: + /*const*/ Settings settings = settingsBuilder().library("std.cfg").build(); + bool useFS; + + std::string fprefix() const + { + if (useFS) + return "processfs"; + return "process"; + } + + struct CheckOptions + { + CheckOptions() = default; + bool quiet = true; + SHOWTIME_MODES showtime = SHOWTIME_MODES::SHOWTIME_NONE; + const char* plistOutput = nullptr; + std::vector filesList; + bool clangTidy = false; + bool executeCommandCalled = false; + std::string exe; + std::vector args; + }; + + /** + * 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, const CheckOptions& opt = make_default_obj{}) { + std::list fileSettings; + + std::list> filelist; + if (opt.filesList.empty()) { + for (int i = 1; i <= files; ++i) { + std::string f_s = fprefix() + "_" + std::to_string(i) + ".cpp"; + filelist.emplace_back(f_s, data.size()); + if (useFS) { + FileSettings fs; + fs.filename = std::move(f_s); + fileSettings.emplace_back(std::move(fs)); + } + } + } + else { + for (const auto& f : opt.filesList) + { + filelist.emplace_back(f, data.size()); + if (useFS) { + FileSettings fs; + fs.filename = f; + fileSettings.emplace_back(std::move(fs)); + } + } + } + + /*const*/ Settings s = settings; + s.jobs = jobs; + s.showtime = opt.showtime; + s.quiet = opt.quiet; + if (opt.plistOutput) + s.plistOutput = opt.plistOutput; + + bool executeCommandCalled = false; + std::string exe; + std::vector args; + // NOLINTNEXTLINE(performance-unnecessary-value-param) + auto executeFn = [&executeCommandCalled, &exe, &args](std::string e,std::vector a,std::string,std::string&){ + executeCommandCalled = true; + exe = std::move(e); + args = std::move(a); + return EXIT_SUCCESS; + }; + + std::vector> scopedfiles; + scopedfiles.reserve(filelist.size()); + for (std::list>::const_iterator i = filelist.cbegin(); i != filelist.cend(); ++i) + scopedfiles.emplace_back(new ScopedFile(i->first, data)); + + // clear files list so only fileSettings are used + if (useFS) + filelist.clear(); + + ProcessExecutor executor(filelist, fileSettings, s, s.supprs.nomsg, *this, executeFn); + ASSERT_EQUALS(result, executor.check()); + ASSERT_EQUALS(opt.executeCommandCalled, executeCommandCalled); + ASSERT_EQUALS(opt.exe, exe); + ASSERT_EQUALS(opt.args.size(), args.size()); + for (int i = 0; i < args.size(); ++i) + { + ASSERT_EQUALS(opt.args[i], args[i]); + } + } + + void run() override { +#if !defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) + TEST_CASE(deadlock_with_many_errors); + TEST_CASE(many_threads); + TEST_CASE(many_threads_showtime); + TEST_CASE(many_threads_plist); + 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); + TEST_CASE(clangTidy); + TEST_CASE(showtime_top5_file); + TEST_CASE(showtime_top5_summary); + TEST_CASE(showtime_file); + TEST_CASE(showtime_summary); + TEST_CASE(showtime_file_total); + TEST_CASE(suppress_error_library); +#endif // !WIN32 + } + + void deadlock_with_many_errors() { + std::ostringstream oss; + oss << "int main()\n" + << "{\n"; + const int num_err = 1; + for (int i = 0; i < num_err; i++) { + oss << " {int i = *((int*)0);}\n"; + } + oss << " return 0;\n" + << "}\n"; + const int num_files = 3; + check(2, num_files, num_files, oss.str()); + ASSERT_EQUALS(1LL * num_err * num_files, cppcheck::count_all_of(errout_str(), "(error) Null pointer dereference: (int*)0")); + } + + void many_threads() { + const int num_files = 100; + check(16, num_files, num_files, + "int main()\n" + "{\n" + " int i = *((int*)0);\n" + " return 0;\n" + "}"); + ASSERT_EQUALS(num_files, cppcheck::count_all_of(errout_str(), "(error) Null pointer dereference: (int*)0")); + } + + // #11249 - reports TSAN errors + void many_threads_showtime() { + SUPPRESS; + check(16, 100, 100, + "int main()\n" + "{\n" + " int i = *((int*)0);\n" + " return 0;\n" + "}", dinit(CheckOptions, $.showtime = SHOWTIME_MODES::SHOWTIME_SUMMARY)); + // we are not interested in the results - so just consume them + ignore_errout(); + } + + void many_threads_plist() { + const std::string plistOutput = "plist_" + fprefix() + "/"; + ScopedFile plistFile("dummy", "", plistOutput); + + check(16, 100, 100, + "int main()\n" + "{\n" + " int i = *((int*)0);\n" + " return 0;\n" + "}", dinit(CheckOptions, $.plistOutput = plistOutput.c_str())); + // we are not interested in the results - so just consume them + ignore_errout(); + } + + void no_errors_more_files() { + check(2, 3, 0, + "int main()\n" + "{\n" + " return 0;\n" + "}"); + } + + void no_errors_less_files() { + check(2, 1, 0, + "int main()\n" + "{\n" + " return 0;\n" + "}"); + } + + void no_errors_equal_amount_files() { + check(2, 2, 0, + "int main()\n" + "{\n" + " return 0;\n" + "}"); + } + + void one_error_less_files() { + check(2, 1, 1, + "int main()\n" + "{\n" + " {int i = *((int*)0);}\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("[" + fprefix() + "_1.cpp:3]: (error) Null pointer dereference: (int*)0\n", errout_str()); + } + + void one_error_several_files() { + const int num_files = 20; + check(2, num_files, num_files, + "int main()\n" + "{\n" + " {int i = *((int*)0);}\n" + " return 0;\n" + "}"); + ASSERT_EQUALS(num_files, cppcheck::count_all_of(errout_str(), "(error) Null pointer dereference: (int*)0")); + } + + void clangTidy() { + // TODO: we currently only invoke it with ImportProject::FileSettings + if (!useFS) + return; + +#ifdef _WIN32 + constexpr char exe[] = "clang-tidy.exe"; +#else + constexpr char exe[] = "clang-tidy"; +#endif + (void)exe; + + const std::string file = fprefix() + "_1.cpp"; + // TODO: the invocation cannot be checked as the code is called in the forked process + check(2, 1, 0, + "int main()\n" + "{\n" + " return 0;\n" + "}", + dinit(CheckOptions, + $.quiet = false, + $.clangTidy = true /*, + $.executeCommandCalled = true, + $.exe = exe, + $.args = {"-quiet", "-checks=*,-clang-analyzer-*,-llvm*", file, "--"}*/)); + ASSERT_EQUALS("Checking " + file + " ...\n", output_str()); + } + + // TODO: provide data which actually shows values above 0 + + // TODO: should this be logged only once like summary? + void showtime_top5_file() { + REDIRECT; // should not cause TSAN failures as the showtime logging is synchronized + check(2, 2, 0, + "int main() {}", + dinit(CheckOptions, + $.showtime = SHOWTIME_MODES::SHOWTIME_TOP5_FILE)); + // for each file: top5 results + overall + empty line + const std::string output_s = GET_REDIRECT_OUTPUT; + // for each file: top5 results + overall + empty line + TODO_ASSERT_EQUALS(static_cast(5 + 1 + 1) * 2, 0, cppcheck::count_all_of(output_s, '\n')); + } + + void showtime_top5_summary() { + REDIRECT; + check(2, 2, 0, + "int main() {}", + dinit(CheckOptions, + $.showtime = SHOWTIME_MODES::SHOWTIME_TOP5_SUMMARY)); + const std::string output_s = GET_REDIRECT_OUTPUT; + // once: top5 results + overall + empty line + TODO_ASSERT_EQUALS(5 + 1 + 1, 2, cppcheck::count_all_of(output_s, '\n')); + // should only report the top5 once + ASSERT(output_s.find("1 result(s)") == std::string::npos); + TODO_ASSERT(output_s.find("2 result(s)") != std::string::npos); + } + + void showtime_file() { + REDIRECT; // should not cause TSAN failures as the showtime logging is synchronized + check(2, 2, 0, + "int main() {}", + dinit(CheckOptions, + $.showtime = SHOWTIME_MODES::SHOWTIME_FILE)); + const std::string output_s = GET_REDIRECT_OUTPUT; + TODO_ASSERT_EQUALS(2, 0, cppcheck::count_all_of(output_s, "Overall time:")); + } + + void showtime_summary() { + REDIRECT; // should not cause TSAN failures as the showtime logging is synchronized + check(2, 2, 0, + "int main() {}", + dinit(CheckOptions, + $.showtime = SHOWTIME_MODES::SHOWTIME_SUMMARY)); + const std::string output_s = GET_REDIRECT_OUTPUT; + // should only report the actual summary once + ASSERT(output_s.find("1 result(s)") == std::string::npos); + TODO_ASSERT(output_s.find("2 result(s)") != std::string::npos); + } + + void showtime_file_total() { + REDIRECT; // should not cause TSAN failures as the showtime logging is synchronized + check(2, 2, 0, + "int main() {}", + dinit(CheckOptions, + $.showtime = SHOWTIME_MODES::SHOWTIME_FILE_TOTAL)); + const std::string output_s = GET_REDIRECT_OUTPUT; + TODO_ASSERT(output_s.find("Check time: " + fprefix() + "_1.cpp: ") != std::string::npos); + TODO_ASSERT(output_s.find("Check time: " + fprefix() + "_2.cpp: ") != std::string::npos); + } + + void suppress_error_library() { + SUPPRESS; + const Settings settingsOld = settings; + const char xmldata[] = R"()"; + settings = settingsBuilder().libraryxml(xmldata, sizeof(xmldata)).build(); + check(2, 1, 0, + "int main()\n" + "{\n" + " int i = *((int*)0);\n" + " return 0;\n" + "}"); + ASSERT_EQUALS("", errout_str()); + settings = settingsOld; + } + + void unique_errors() { + SUPPRESS; + ScopedFile inc_h(fprefix() + ".h", + "inline void f()\n" + "{\n" + " (void)*((int*)0);\n" + "}"); + check(2, 2, 2, + "#include \"" + inc_h.name() +"\""); + // this is made unique by the executor + ASSERT_EQUALS("[" + inc_h.name() + ":3]: (error) Null pointer dereference: (int*)0\n", errout_str()); + } + + // TODO: test whole program analysis +}; + +class TestProcessExecutorFiles : public TestProcessExecutorBase { +public: + TestProcessExecutorFiles() : TestProcessExecutorBase("TestProcessExecutorFiles", false) {} +}; + +class TestProcessExecutorFS : public TestProcessExecutorBase { +public: + TestProcessExecutorFS() : TestProcessExecutorBase("TestProcessExecutorFS", true) {} +}; + +REGISTER_TEST(TestProcessExecutorFiles) +REGISTER_TEST(TestProcessExecutorFS) diff --git a/cppcheck-2.14.0/test/testrunner.vcxproj b/cppcheck-2.14.0/test/testrunner.vcxproj new file mode 100644 index 00000000..06cbbd96 --- /dev/null +++ b/cppcheck-2.14.0/test/testrunner.vcxproj @@ -0,0 +1,335 @@ + + + + + Debug-PCRE + x64 + + + Debug + x64 + + + Release-PCRE + x64 + + + Release + x64 + + + + + {c183db5b-ad6c-423d-80ca-1f9549555a1a} + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {4F7DCE5E-6CDE-38C4-9EA7-27AF3B25CEB4} + testrunner + 10.0 + + + + Application + Unicode + false + v142 + + + Application + Unicode + false + v142 + + + Application + Unicode + false + v142 + + + Application + Unicode + false + v142 + + + + + + + + + + + + + + + + + + $(SolutionDir)bin\debug\ + $(SolutionDir)bin\debug\ + temp\$(Configuration)_$(PlatformName)\ + temp\$(Configuration)_$(PlatformName)\ + testrunner + testrunner + true + true + $(SolutionDir)bin\ + $(SolutionDir)bin\ + temp\$(Configuration)_$(PlatformName)\ + temp\$(Configuration)_$(PlatformName)\ + testrunner + testrunner + true + true + true + true + + + + ..\cli;..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) + true + ProgramDatabase + Disabled + CPPCHECKLIB_IMPORT;SIMPLECPP_IMPORT;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Level4 + 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 + true + Use + precompiled.h + precompiled.h + true + stdcpp14 + /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) + + + shlwapi.lib;%(AdditionalDependencies) + ../externals;%(AdditionalLibraryDirectories) + true + Console + true + 8000000 + 8000000 + true + + + + + ..\cli;..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) + true + ProgramDatabase + Disabled + CPPCHECKLIB_IMPORT;SIMPLECPP_IMPORT;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Level4 + 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 + true + Use + precompiled.h + precompiled.h + true + stdcpp14 + /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) + + + shlwapi.lib;%(AdditionalDependencies) + ../externals;%(AdditionalLibraryDirectories) + true + Console + true + 8000000 + 8000000 + true + + + + + ..\cli;..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) + false + MaxSpeed + CPPCHECKLIB_IMPORT;SIMPLECPP_IMPORT;NDEBUG;WIN32;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) + MultiThreadedDLL + Level4 + AnySuitable + true + Speed + true + true + true + 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 + ProgramDatabase + true + Use + precompiled.h + precompiled.h + /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) + true + stdcpp14 + + + shlwapi.lib;%(AdditionalDependencies) + ../externals;%(AdditionalLibraryDirectories) + true + true + true + Console + true + true + true + true + 8000000 + 8000000 + true + + + + + ..\cli;..\lib;..\externals;..\externals\simplecpp;..\externals\tinyxml2;%(AdditionalIncludeDirectories) + false + MaxSpeed + CPPCHECKLIB_IMPORT;SIMPLECPP_IMPORT;NDEBUG;WIN32;HAVE_RULES;_CRT_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN64;%(PreprocessorDefinitions) + MultiThreadedDLL + Level4 + AnySuitable + true + Speed + true + true + true + 4018;4127;4146;4244;4251;4267;4389;4482;4512;4701;4706;4800;4805 + ProgramDatabase + true + Use + precompiled.h + precompiled.h + /Zc:throwingNew /Zc:__cplusplus %(AdditionalOptions) + true + stdcpp14 + + + shlwapi.lib;%(AdditionalDependencies) + ../externals;%(AdditionalLibraryDirectories) + true + true + true + Console + true + true + true + true + 8000000 + 8000000 + true + + + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/test/testrunner.vcxproj.filters b/cppcheck-2.14.0/test/testrunner.vcxproj.filters new file mode 100644 index 00000000..5728c84b --- /dev/null +++ b/cppcheck-2.14.0/test/testrunner.vcxproj.filters @@ -0,0 +1,310 @@ + + + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + 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 + + + \ No newline at end of file diff --git a/cppcheck-2.14.0/test/testsettings.cpp b/cppcheck-2.14.0/test/testsettings.cpp new file mode 100644 index 00000000..75c143d5 --- /dev/null +++ b/cppcheck-2.14.0/test/testsettings.cpp @@ -0,0 +1,280 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + +#include "errortypes.h" +#include "settings.h" +#include "fixture.h" +#include "helpers.h" +#include "suppressions.h" + + + +class TestSettings : public TestFixture { +public: + TestSettings() : TestFixture("TestSettings") {} + +private: + void run() override { + TEST_CASE(simpleEnableGroup); + TEST_CASE(loadCppcheckCfg); + TEST_CASE(loadCppcheckCfgSafety); + TEST_CASE(getNameAndVersion); + TEST_CASE(ruleTexts); + TEST_CASE(checkLevelDefault); + } + + void simpleEnableGroup() const { + SimpleEnableGroup group; + + ASSERT_EQUALS(0, group.intValue()); + ASSERT_EQUALS(false, group.isEnabled(Checks::unusedFunction)); + ASSERT_EQUALS(false, group.isEnabled(Checks::missingInclude)); + ASSERT_EQUALS(false, group.isEnabled(Checks::internalCheck)); + + group.fill(); + + ASSERT_EQUALS(4294967295, group.intValue()); + ASSERT_EQUALS(true, group.isEnabled(Checks::unusedFunction)); + ASSERT_EQUALS(true, group.isEnabled(Checks::missingInclude)); + ASSERT_EQUALS(true, group.isEnabled(Checks::internalCheck)); + + group.clear(); + + ASSERT_EQUALS(0, group.intValue()); + ASSERT_EQUALS(false, group.isEnabled(Checks::unusedFunction)); + ASSERT_EQUALS(false, group.isEnabled(Checks::missingInclude)); + ASSERT_EQUALS(false, group.isEnabled(Checks::internalCheck)); + + group.enable(Checks::unusedFunction); + group.setEnabled(Checks::missingInclude, true); + + ASSERT_EQUALS(3, group.intValue()); + ASSERT_EQUALS(true, group.isEnabled(Checks::unusedFunction)); + ASSERT_EQUALS(true, group.isEnabled(Checks::missingInclude)); + ASSERT_EQUALS(false, group.isEnabled(Checks::internalCheck)); + + group.disable(Checks::unusedFunction); + group.setEnabled(Checks::missingInclude, false); + + ASSERT_EQUALS(0, group.intValue()); + ASSERT_EQUALS(false, group.isEnabled(Checks::unusedFunction)); + ASSERT_EQUALS(false, group.isEnabled(Checks::missingInclude)); + ASSERT_EQUALS(false, group.isEnabled(Checks::internalCheck)); + + SimpleEnableGroup newGroup; + newGroup.enable(Checks::missingInclude); + + group.enable(newGroup); + + ASSERT_EQUALS(2, group.intValue()); + ASSERT_EQUALS(false, group.isEnabled(Checks::unusedFunction)); + ASSERT_EQUALS(true, group.isEnabled(Checks::missingInclude)); + ASSERT_EQUALS(false, group.isEnabled(Checks::internalCheck)); + + group.disable(newGroup); + + ASSERT_EQUALS(0, group.intValue()); + ASSERT_EQUALS(false, group.isEnabled(Checks::unusedFunction)); + ASSERT_EQUALS(false, group.isEnabled(Checks::missingInclude)); + ASSERT_EQUALS(false, group.isEnabled(Checks::internalCheck)); + } + + void loadCppcheckCfg() + { + { + Settings s; + ASSERT_EQUALS("", Settings::loadCppcheckCfg(s, s.supprs)); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + "{}\n"); + ASSERT_EQUALS("", Settings::loadCppcheckCfg(s, s.supprs)); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + "{\n"); + ASSERT_EQUALS("not a valid JSON - syntax error at line 2 near: ", Settings::loadCppcheckCfg(s, s.supprs)); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + R"({"productName": ""}\n)"); + ASSERT_EQUALS("", Settings::loadCppcheckCfg(s, s.supprs)); + ASSERT_EQUALS("", s.cppcheckCfgProductName); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + R"({"productName": "product"}\n)"); + ASSERT_EQUALS("", Settings::loadCppcheckCfg(s, s.supprs)); + ASSERT_EQUALS("product", s.cppcheckCfgProductName); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + R"({"productName": 1}\n)"); + ASSERT_EQUALS("'productName' is not a string", Settings::loadCppcheckCfg(s, s.supprs)); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + R"({"about": ""}\n)"); + ASSERT_EQUALS("", Settings::loadCppcheckCfg(s, s.supprs)); + ASSERT_EQUALS("", s.cppcheckCfgAbout); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + R"({"about": "about"}\n)"); + ASSERT_EQUALS("", Settings::loadCppcheckCfg(s, s.supprs)); + ASSERT_EQUALS("about", s.cppcheckCfgAbout); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + R"({"about": 1}\n)"); + ASSERT_EQUALS("'about' is not a string", Settings::loadCppcheckCfg(s, s.supprs)); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + R"({"addons": []}\n)"); + ASSERT_EQUALS("", Settings::loadCppcheckCfg(s, s.supprs)); + ASSERT_EQUALS(0, s.addons.size()); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + R"({"addons": 1}\n)"); + ASSERT_EQUALS("'addons' is not an array", Settings::loadCppcheckCfg(s, s.supprs)); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + R"({"addons": ["addon"]}\n)"); + ASSERT_EQUALS("", Settings::loadCppcheckCfg(s, s.supprs)); + ASSERT_EQUALS(1, s.addons.size()); + ASSERT_EQUALS("addon", *s.addons.cbegin()); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + R"({"addons": [1]}\n)"); + ASSERT_EQUALS("'addons' array entry is not a string", Settings::loadCppcheckCfg(s, s.supprs)); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + R"({"addons": []}\n)"); + ASSERT_EQUALS("", Settings::loadCppcheckCfg(s, s.supprs)); + ASSERT_EQUALS(0, s.addons.size()); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + R"({"suppressions": 1}\n)"); + ASSERT_EQUALS("'suppressions' is not an array", Settings::loadCppcheckCfg(s, s.supprs)); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + R"({"suppressions": ["id"]}\n)"); + ASSERT_EQUALS("", Settings::loadCppcheckCfg(s, s.supprs)); + ASSERT_EQUALS(1, s.supprs.nomsg.getSuppressions().size()); + ASSERT_EQUALS("id", s.supprs.nomsg.getSuppressions().cbegin()->errorId); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + R"({"suppressions": [""]}\n)"); + ASSERT_EQUALS("could not parse suppression '' - Failed to add suppression. No id.", Settings::loadCppcheckCfg(s, s.supprs)); + } + { + Settings s; + ScopedFile file("cppcheck.cfg", + R"({"suppressions": [1]}\n)"); + ASSERT_EQUALS("'suppressions' array entry is not a string", Settings::loadCppcheckCfg(s, s.supprs)); + } + + // TODO: test with FILESDIR + } + + void loadCppcheckCfgSafety() const + { + // Test the "safety" flag + { + Settings s; + s.safety = false; + ScopedFile file("cppcheck.cfg", "{}"); + ASSERT_EQUALS("", Settings::loadCppcheckCfg(s, s.supprs)); + ASSERT_EQUALS(false, s.safety); + } + + { + Settings s; + s.safety = true; + ScopedFile file("cppcheck.cfg", "{\"safety\": false}"); + ASSERT_EQUALS("", Settings::loadCppcheckCfg(s, s.supprs)); + ASSERT_EQUALS(true, s.safety); + } + + { + Settings s; + s.safety = false; + ScopedFile file("cppcheck.cfg", "{\"safety\": true}"); + ASSERT_EQUALS("", Settings::loadCppcheckCfg(s, s.supprs)); + ASSERT_EQUALS(true, s.safety); + } + } + + void getNameAndVersion() const + { + { + const auto nameVersion = Settings::getNameAndVersion("Cppcheck Premium 12.3.4"); + ASSERT_EQUALS("Cppcheck Premium", nameVersion.first); + ASSERT_EQUALS("12.3.4", nameVersion.second); + } + + { + const auto nameVersion = Settings::getNameAndVersion("Cppcheck Premium 12.3.4s"); + ASSERT_EQUALS("Cppcheck Premium", nameVersion.first); + ASSERT_EQUALS("12.3.4s", nameVersion.second); + } + } + + void ruleTexts() const + { + Settings s; + s.setMisraRuleTexts("1.1 text 1\n1.2 text 2\n"); + ASSERT_EQUALS("text 1", s.getMisraRuleText("misra-c2012-1.1", "---")); + ASSERT_EQUALS("text 2", s.getMisraRuleText("misra-c2012-1.2", "---")); + } + + void checkLevelDefault() const + { + Settings s; + ASSERT_EQUALS_ENUM(s.checkLevel, Settings::CheckLevel::exhaustive); + ASSERT_EQUALS(s.performanceValueFlowMaxIfCount, -1); + ASSERT_EQUALS(s.performanceValueFlowMaxSubFunctionArgs, 256); + } +}; + +REGISTER_TEST(TestSettings) diff --git a/cppcheck-2.14.0/test/testsimplifytemplate.cpp b/cppcheck-2.14.0/test/testsimplifytemplate.cpp new file mode 100644 index 00000000..8c663a56 --- /dev/null +++ b/cppcheck-2.14.0/test/testsimplifytemplate.cpp @@ -0,0 +1,6460 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "errortypes.h" +#include "fixture.h" +#include "helpers.h" +#include "platform.h" +#include "settings.h" +#include "templatesimplifier.h" +#include "token.h" +#include "tokenize.h" +#include "tokenlist.h" + +#include +#include +#include +#include + +class TestSimplifyTemplate : public TestFixture { +public: + TestSimplifyTemplate() : TestFixture("TestSimplifyTemplate") {} + +private: + // If there are unused templates, keep those + const Settings settings = settingsBuilder().severity(Severity::portability).build(); + + void run() override { + TEST_CASE(template1); + TEST_CASE(template2); + TEST_CASE(template3); + TEST_CASE(template4); + TEST_CASE(template5); + TEST_CASE(template6); + TEST_CASE(template7); + TEST_CASE(template8); + TEST_CASE(template9); + TEST_CASE(template10); + TEST_CASE(template11); + TEST_CASE(template12); + TEST_CASE(template13); + TEST_CASE(template14); + TEST_CASE(template15); // recursive templates + TEST_CASE(template16); + TEST_CASE(template17); + TEST_CASE(template18); + TEST_CASE(template19); + TEST_CASE(template20); + TEST_CASE(template21); + TEST_CASE(template22); + TEST_CASE(template23); + TEST_CASE(template24); // #2648 - using sizeof in template parameter + TEST_CASE(template25); // #2648 - another test for sizeof template parameter + TEST_CASE(template26); // #2721 - passing 'char[2]' as template parameter + TEST_CASE(template27); // #3350 - removing unused template in macro call + TEST_CASE(template28); + TEST_CASE(template30); // #3529 - template < template < .. + TEST_CASE(template31); // #4010 - reference type + TEST_CASE(template32); // #3818 - mismatching template not handled well + TEST_CASE(template33); // #3818,#4544 - inner templates in template instantiation not handled well + TEST_CASE(template34); // #3706 - namespace => hang + TEST_CASE(template35); // #4074 - A<'x'> a; + TEST_CASE(template36); // #4310 - passing unknown template instantiation as template argument + TEST_CASE(template37); // #4544 - A a; + TEST_CASE(template38); // #4832 - crash on C++11 right angle brackets + TEST_CASE(template39); // #4742 - freeze + TEST_CASE(template40); // #5055 - template specialization outside struct + TEST_CASE(template41); // #4710 - const in instantiation not handled perfectly + TEST_CASE(template42); // #4878 - variadic templates + TEST_CASE(template43); // #5097 - assert due to '>>' not treated as end of template instantiation + TEST_CASE(template44); // #5297 - TemplateSimplifier::simplifyCalculations not eager enough + TEST_CASE(template45); // #5814 - syntax error reported for valid code + TEST_CASE(template46); // #5816 - syntax error reported for valid code + TEST_CASE(template47); // #6023 - syntax error reported for valid code + TEST_CASE(template48); // #6134 - 100% CPU upon invalid code + TEST_CASE(template49); // #6237 - template instantiation + TEST_CASE(template50); // #4272 - simple partial specialization + TEST_CASE(template52); // #6437 - crash upon valid code + TEST_CASE(template53); // #4335 - bail out for valid code + TEST_CASE(template54); // #6587 - memory corruption upon valid code + TEST_CASE(template55); // #6604 - simplify "const const" to "const" in template instantiations + TEST_CASE(template56); // #7117 - const ternary operator simplification as template parameter + TEST_CASE(template57); // #7891 + TEST_CASE(template58); // #6021 - use after free (deleted tokens in simplifyCalculations) + TEST_CASE(template59); // #8051 - TemplateSimplifier::simplifyTemplateInstantiation failure + TEST_CASE(template60); // handling of methods outside template definition + TEST_CASE(template61); // daca2, kodi + TEST_CASE(template62); // #8314 - inner template instantiation + TEST_CASE(template63); // #8576 - qualified type + TEST_CASE(template64); // #8683 + TEST_CASE(template65); // #8321 + TEST_CASE(template66); // #8725 + TEST_CASE(template67); // #8122 + TEST_CASE(template68); // union + TEST_CASE(template69); // #8791 + TEST_CASE(template70); // #5289 + TEST_CASE(template71); // #8821 + TEST_CASE(template72); + TEST_CASE(template73); + TEST_CASE(template74); + TEST_CASE(template75); + TEST_CASE(template76); + TEST_CASE(template77); + TEST_CASE(template78); + TEST_CASE(template79); // #5133 + TEST_CASE(template80); + TEST_CASE(template81); + TEST_CASE(template82); // #8603 + TEST_CASE(template83); // #8867 + TEST_CASE(template84); // #8880 + TEST_CASE(template85); // #8902 crash + TEST_CASE(template86); // crash + TEST_CASE(template87); + TEST_CASE(template88); // #6183 + TEST_CASE(template89); // #8917 + TEST_CASE(template90); // crash + TEST_CASE(template91); + TEST_CASE(template92); + TEST_CASE(template93); // crash + TEST_CASE(template94); // #8927 crash + TEST_CASE(template95); // #7417 + TEST_CASE(template96); // #7854 + TEST_CASE(template97); + TEST_CASE(template98); // #8959 + TEST_CASE(template99); // #8960 + TEST_CASE(template100); // #8967 + TEST_CASE(template101); // #8968 + TEST_CASE(template102); // #9005 + TEST_CASE(template103); + TEST_CASE(template104); // #9021 + TEST_CASE(template105); // #9076 + TEST_CASE(template106); + TEST_CASE(template107); // #8663 + TEST_CASE(template108); // #9109 + TEST_CASE(template109); // #9144 + TEST_CASE(template110); + TEST_CASE(template111); // crash + TEST_CASE(template112); // #9146 syntax error + TEST_CASE(template113); + TEST_CASE(template114); // #9155 + TEST_CASE(template115); // #9153 + TEST_CASE(template116); // #9178 + TEST_CASE(template117); + TEST_CASE(template118); + TEST_CASE(template119); // #9186 + TEST_CASE(template120); + TEST_CASE(template121); // #9193 + TEST_CASE(template122); // #9147 + TEST_CASE(template123); // #9183 + TEST_CASE(template124); // #9197 + TEST_CASE(template125); + TEST_CASE(template126); // #9217 + TEST_CASE(template127); // #9225 + TEST_CASE(template128); // #9224 + TEST_CASE(template129); + TEST_CASE(template130); // #9246 + TEST_CASE(template131); // #9249 + TEST_CASE(template132); // #9250 + TEST_CASE(template133); + TEST_CASE(template134); + TEST_CASE(template135); + TEST_CASE(template136); // #9287 + TEST_CASE(template137); // #9288 + TEST_CASE(template138); + TEST_CASE(template139); + TEST_CASE(template140); + TEST_CASE(template141); // #9337 + TEST_CASE(template142); // #9338 + TEST_CASE(template143); + TEST_CASE(template144); // #9046 + TEST_CASE(template145); // syntax error + TEST_CASE(template146); // syntax error + TEST_CASE(template147); // syntax error + TEST_CASE(template148); // syntax error + TEST_CASE(template149); // unknown macro + TEST_CASE(template150); // syntax error + TEST_CASE(template151); // crash + TEST_CASE(template152); // #9467 + TEST_CASE(template153); // #9483 + TEST_CASE(template154); // #9495 + TEST_CASE(template155); // #9539 + TEST_CASE(template156); + TEST_CASE(template157); // #9854 + TEST_CASE(template158); // daca crash + TEST_CASE(template159); // #9886 + TEST_CASE(template160); + TEST_CASE(template161); + TEST_CASE(template162); + TEST_CASE(template163); // #9685 syntax error + TEST_CASE(template164); // #9394 + TEST_CASE(template165); // #10032 syntax error + TEST_CASE(template166); // #10081 hang + TEST_CASE(template167); + TEST_CASE(template168); + TEST_CASE(template169); + TEST_CASE(template170); // crash + TEST_CASE(template171); // crash + TEST_CASE(template172); // #10258 crash + TEST_CASE(template173); // #10332 crash + TEST_CASE(template174); // #10506 hang + TEST_CASE(template175); // #10908 + TEST_CASE(template176); // #11146 + TEST_CASE(template177); + TEST_CASE(template178); + TEST_CASE(template_specialization_1); // #7868 - template specialization template struct S> {..}; + TEST_CASE(template_specialization_2); // #7868 - template specialization template struct S> {..}; + TEST_CASE(template_specialization_3); + TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template) + TEST_CASE(template_unhandled); + TEST_CASE(template_default_parameter); + TEST_CASE(template_forward_declared_default_parameter); + TEST_CASE(template_default_type); + TEST_CASE(template_typename); + TEST_CASE(template_constructor); // #3152 - template constructor is removed + TEST_CASE(syntax_error_templates_1); + TEST_CASE(template_member_ptr); // Ticket #5786 - crash upon valid code + TEST_CASE(template_namespace_1); + TEST_CASE(template_namespace_2); + TEST_CASE(template_namespace_3); + TEST_CASE(template_namespace_4); + TEST_CASE(template_namespace_5); + TEST_CASE(template_namespace_6); + TEST_CASE(template_namespace_7); // #8768 + TEST_CASE(template_namespace_8); + TEST_CASE(template_namespace_9); + TEST_CASE(template_namespace_10); + TEST_CASE(template_namespace_11); // #7145 + TEST_CASE(template_pointer_type); + TEST_CASE(template_array_type); + + // Test TemplateSimplifier::templateParameters + TEST_CASE(templateParameters); + + TEST_CASE(templateNamePosition); + + TEST_CASE(findTemplateDeclarationEnd); + + TEST_CASE(getTemplateParametersInDeclaration); + + TEST_CASE(expandSpecialized1); + TEST_CASE(expandSpecialized2); + TEST_CASE(expandSpecialized3); // #8671 + TEST_CASE(expandSpecialized4); + TEST_CASE(expandSpecialized5); // #10494 + + TEST_CASE(templateAlias1); + TEST_CASE(templateAlias2); + TEST_CASE(templateAlias3); // #8315 + TEST_CASE(templateAlias4); // #9070 + TEST_CASE(templateAlias5); + + // Test TemplateSimplifier::instantiateMatch + TEST_CASE(instantiateMatchTest); + TEST_CASE(templateParameterWithoutName); // #8602 Template default parameter without name yields syntax error + + TEST_CASE(templateTypeDeduction1); // #8962 + TEST_CASE(templateTypeDeduction2); + TEST_CASE(templateTypeDeduction3); + TEST_CASE(templateTypeDeduction4); // #9983 + TEST_CASE(templateTypeDeduction5); + + TEST_CASE(simplifyTemplateArgs1); + TEST_CASE(simplifyTemplateArgs2); + TEST_CASE(simplifyTemplateArgs3); + + TEST_CASE(template_variadic_1); // #9144 + TEST_CASE(template_variadic_2); // #4349 + TEST_CASE(template_variadic_3); // #6172 + TEST_CASE(template_variadic_4); + + TEST_CASE(template_variable_1); + TEST_CASE(template_variable_2); + TEST_CASE(template_variable_3); + TEST_CASE(template_variable_4); + + TEST_CASE(simplifyDecltype); + + TEST_CASE(castInExpansion); + + TEST_CASE(fold_expression_1); + TEST_CASE(fold_expression_2); + TEST_CASE(fold_expression_3); + TEST_CASE(fold_expression_4); + + TEST_CASE(concepts1); + TEST_CASE(requires1); + TEST_CASE(requires2); + TEST_CASE(requires3); + TEST_CASE(requires4); + TEST_CASE(requires5); + + TEST_CASE(explicitBool1); + TEST_CASE(explicitBool2); + } + +#define tok(...) tok_(__FILE__, __LINE__, __VA_ARGS__) + std::string tok_(const char* file, int line, const char code[], bool debugwarnings = false, Platform::Type type = Platform::Type::Native) { + const Settings settings1 = settingsBuilder(settings).library("std.cfg").debugwarnings(debugwarnings).platform(type).build(); + SimpleTokenizer tokenizer(settings1, *this); + + ASSERT_LOC(tokenizer.tokenize(code), file, line); + + return tokenizer.tokens()->stringifyList(nullptr, true); + } + + void template1() { + const char code[] = "template T f(T val) { T a; }\n" + "f(10);"; + + const char expected[] = "int f ( int val ) ; " + "f ( 10 ) ; " + "int f ( int val ) { int a ; }"; + + ASSERT_EQUALS(expected, tok(code)); + } + + void template2() { + const char code[] = "template class Fred { T a; };\n" + "Fred fred;"; + + const char expected[] = "class Fred ; " + "Fred fred ; " + "class Fred { int a ; } ;"; + + ASSERT_EQUALS(expected, tok(code)); + } + + void template3() { + const char code[] = "template class Fred { T data[sz]; };\n" + "Fred fred;"; + + const char expected[] = "class Fred ; " + "Fred fred ; " + "class Fred { float data [ 4 ] ; } ;"; + + ASSERT_EQUALS(expected, tok(code)); + } + + void template4() { + const char code[] = "template class Fred { Fred(); };\n" + "Fred fred;"; + + const char expected[] = "class Fred ; " + "Fred fred ; " + "class Fred { Fred ( ) ; } ;"; + + ASSERT_EQUALS(expected, tok(code)); + } + + void template5() { + const char code[] = "template class Fred { };\n" + "template Fred::Fred() { }\n" + "Fred fred;"; + + const char expected[] = "class Fred ; " + "Fred fred ; " + "class Fred { } ; " + "Fred :: Fred ( ) { }"; + + ASSERT_EQUALS(expected, tok(code)); + } + + void template6() { + const char code[] = "template class Fred { };\n" + "Fred fred1;\n" + "Fred fred2;"; + + const char expected[] = "class Fred ; " + "Fred fred1 ; " + "Fred fred2 ; " + "class Fred { } ;"; + + ASSERT_EQUALS(expected, tok(code)); + } + + void template7() { + // A template class that is not used => no simplification + { + const char code[] = "template \n" + "class ABC\n" + "{\n" + "public:\n" + " typedef ABC m;\n" + "};\n"; + + const char expected[] = "template < class T > class ABC { public: } ;"; + + ASSERT_EQUALS(expected, tok(code)); + } + + { + const char code[] = "template class ABC {\n" + "public:\n" + " typedef std::vector type;\n" + "};\n" + "int main() {\n" + " ABC::type v;\n" + " v.push_back(4);\n" + " return 0;\n" + "}\n"; + + const char wanted[] = "class ABC ; " + "int main ( ) { " + "std :: vector < int > v ; " + "v . push_back ( 4 ) ; " + "return 0 ; " + "} " + "class ABC { public: } ;"; + + const char current[] = "class ABC ; " + "int main ( ) { " + "ABC :: type v ; " + "v . push_back ( 4 ) ; " + "return 0 ; " + "} " + "class ABC { public: } ;"; + + TODO_ASSERT_EQUALS(wanted, current, tok(code)); + } + + { + const char code[] = "template class ABC {\n" + "public:\n" + " typedef std::vector type;\n" + " void f()\n" + " {\n" + " ABC::type v;\n" + " v.push_back(4);\n" + " }\n" + "};\n"; + + const char expected[] = "template < typename T > class ABC { " + "public: void f ( ) { " + "ABC < int > :: type v ; " + "v . push_back ( 4 ) ; " + "} " + "} ;"; + + ASSERT_EQUALS(expected, tok(code)); + } + } + + // Template definitions but no usage => no expansion + void template8() { + const char code[] = "template class A;\n" + "template class B;\n" + "\n" + "typedef A x;\n" + "typedef B y;\n" + "\n" + "template class A {\n" + " void f() {\n" + " B a = B::g();\n" + " T b = 0;\n" + " if (b)\n" + " b = 0;\n" + " }\n" + "};\n" + "\n" + "template inline B h() { return B(); }\n"; + + ASSERT_EQUALS("template < typename T > class A ; " + "template < typename T > class B ; " + "template < typename T > class A { void f ( ) { B < T > a ; a = B < T > :: g ( ) ; T b ; b = 0 ; if ( b ) { b = 0 ; } } } ; " + "template < typename T > B < T > h ( ) { return B < T > ( ) ; }", tok(code)); + + ASSERT_EQUALS("class A { template < typename T > int foo ( T d ) ; } ;", tok("class A{ template int foo(T d);};")); + } + + void template9() { + const char code[] = "template < typename T > class A { } ;\n" + "\n" + "void f ( ) {\n" + " A < int > a ;\n" + "}\n" + "\n" + "template < typename T >\n" + "class B {\n" + " void g ( ) {\n" + " A < T > b = A < T > :: h ( ) ;\n" + " }\n" + "} ;\n"; + + // The expected result.. + const char expected[] = "class A ; " + "void f ( ) { A a ; } " + "template < typename T > class B { void g ( ) { A < T > b ; b = A < T > :: h ( ) ; } } ; " + "class A { } ;"; + + ASSERT_EQUALS(expected, tok(code)); + } + + void template10() { + const char code[] = "template T * foo()\n" + "{ return new T[ui]; }\n" + "\n" + "void f ( )\n" + "{\n" + " foo<3,int>();\n" + "}\n"; + + // The expected result.. + const char expected[] = "int * foo<3,int> ( ) ; " + "void f ( ) " + "{" + " foo<3,int> ( ) ; " + "} " + "int * foo<3,int> ( ) { return new int [ 3 ] ; }"; + ASSERT_EQUALS(expected, tok(code)); + } + + void template11() { + const char code[] = "template T * foo()\n" + "{ return new T[ui]; }\n" + "\n" + "void f ( )\n" + "{\n" + " char * p = foo<3,char>();\n" + "}\n"; + + // The expected result.. + const char expected[] = "char * foo<3,char> ( ) ; " + "void f ( ) " + "{" + " char * p ; p = foo<3,char> ( ) ; " + "} " + "char * foo<3,char> ( ) { return new char [ 3 ] ; }"; + ASSERT_EQUALS(expected, tok(code)); + } + + void template12() { + const char code[] = "template \n" + "class A : public B\n" + "{ };\n" + "\n" + "void f()\n" + "{\n" + " A<12,12,11> a;\n" + "}\n"; + const char expected[] = "class A<12,12,11> ; " + "void f ( ) " + "{" + " A<12,12,11> a ; " + "} " + "class A<12,12,11> : public B < 12 , 12 , 0 > " + "{ } ;"; + ASSERT_EQUALS(expected, tok(code)); + } + + void template13() { + const char code[] = "class BB {};\n" + "\n" + "template \n" + "class AA {\n" + "public:\n" + " static AA create(T* newObject);\n" + " static int size();\n" + "};\n" + "\n" + "class CC { public: CC(AA, int) {} };\n" + "\n" + "class XX {\n" + " AA y;\n" + "public:\n" + " XX();\n" + "};\n" + "\n" + "XX::XX():\n" + " y(AA::create(new CC(AA(), 0)))\n" + " {}\n" + "\n" + "int yy[AA::size()];"; + const char expected[] = "class BB { } ; " + "class AA ; " + "class AA ; " + "class CC { public: CC ( AA , int ) { } } ; " + "class XX { " + "AA y ; " + "public: " + "XX ( ) ; " + "} ; " + "XX :: XX ( ) : " + "y ( AA :: create ( new CC ( AA ( ) , 0 ) ) ) " + "{ } " + "int yy [ AA :: size ( ) ] ; " + "class AA { " + "public: " + "static AA create ( BB * newObject ) ; " + "static int size ( ) ; " + "} ; " + "class AA { " + "public: " + "static AA create ( CC * newObject ) ; " + "static int size ( ) ; " + "} ;"; + ASSERT_EQUALS(expected, tok(code)); + } + + void template14() { + const char code[] = "template <> void foo()\n" + "{ x(); }\n" + "\n" + "int main()\n" + "{\n" + "foo();\n" + "}\n"; + const char expected[] = "void foo ( ) ; " + "void foo ( ) " + "{ x ( ) ; } " + "int main ( ) " + "{ foo ( ) ; }"; + ASSERT_EQUALS(expected, tok(code)); + } + + void template15() { // recursive templates #3130 etc + const char code[] = "template void a()\n" + "{\n" + " a();\n" + "}\n" + "\n" + "template <> void a<0>()\n" + "{ }\n" + "\n" + "int main()\n" + "{\n" + " a<2>();\n" + " return 0;\n" + "}\n"; + + // The expected result.. + const char expected[] = "void a<0> ( ) ; " + "void a<2> ( ) ; " + "void a<1> ( ) ; " + "void a<0> ( ) { } " + "int main ( ) " + "{ a<2> ( ) ; return 0 ; } " + "void a<2> ( ) { a<1> ( ) ; } " + "void a<1> ( ) { a<0> ( ) ; }"; + + ASSERT_EQUALS(expected, tok(code)); + + // #3130 + const char code2[] = "template struct vec {\n" + " vec() {}\n" + " vec(const vec& v) {}\n" // <- never used don't instantiate + "};\n" + "\n" + "vec<4> v;"; + const char expected2[] = "struct vec<4> ; " + "vec<4> v ; " + "struct vec<4> { " + "vec<4> ( ) { } " + "vec<4> ( const vec < 4 - 1 > & v ) { } " + "} ;"; + + ASSERT_EQUALS(expected2, tok(code2)); + } + + void template16() { + const char code[] = "template void a()\n" + "{ }\n" + "\n" + "template void b()\n" + "{ a(); }\n" + "\n" + "int main()\n" + "{\n" + " b<2>();\n" + " return 0;\n" + "}\n"; + + const char expected[] = "void a<2> ( ) ; " + "void b<2> ( ) ; " + "int main ( ) { b<2> ( ) ; return 0 ; } " + "void b<2> ( ) { a<2> ( ) ; } " + "void a<2> ( ) { }"; + + ASSERT_EQUALS(expected, tok(code)); + } + + void template17() { + const char code[] = "template\n" + "class Fred\n" + "{\n" + " template\n" + " static shared_ptr< Fred > CreateFred()\n" + " {\n" + " }\n" + "};\n" + "\n" + "shared_ptr i;\n"; + const char expected[] = "template < class T > " + "class Fred " + "{ " + "template < class T > " + "static shared_ptr < Fred < T > > CreateFred ( ) " + "{ " + "} " + "} ; " + "shared_ptr < int > i ;"; + ASSERT_EQUALS(expected, tok(code)); + } + + void template18() { + const char code[] = "template class foo { T a; };\n" + "foo *f;"; + + const char expected[] = "class foo ; " + "foo * f ; " + "class foo { int a ; } ;"; + + ASSERT_EQUALS(expected, tok(code)); + } + + void template19() { + const char code[] = "template T & foo()\n" + "{ static T temp; return temp; }\n" + "\n" + "void f ( )\n" + "{\n" + " char p = foo();\n" + "}\n"; + + // The expected result.. + const char expected[] = "char & foo ( ) ; " + "void f ( ) " + "{" + " char p ; p = foo ( ) ; " + "} " + "char & foo ( ) { static char temp ; return temp ; }"; + ASSERT_EQUALS(expected, tok(code)); + } + + void template20() { + // Ticket #1788 - the destructor implementation is lost + const char code[] = "template class A { public: ~A(); };\n" + "template A::~A() {}\n" + "A a;\n"; + + // The expected result.. + const char expected[] = "class A ; " + "A a ; " + "class A { public: ~ A ( ) ; } ; " + "A :: ~ A ( ) { }"; + ASSERT_EQUALS(expected, tok(code)); + } + + void template21() { + { + const char code[] = "template struct Fred { T a; };\n" + "Fred fred;"; + + const char expected[] = "struct Fred ; " + "Fred fred ; " + "struct Fred { int a ; } ;"; + + ASSERT_EQUALS(expected, tok(code)); + } + + { + const char code[] = "template struct Fred { T data[sz]; };\n" + "Fred fred;"; + + const char expected[] = "struct Fred ; " + "Fred fred ; " + "struct Fred { float data [ 4 ] ; } ;"; + + ASSERT_EQUALS(expected, tok(code)); + } + + { + const char code[] = "template struct Fred { Fred(); };\n" + "Fred fred;"; + + const char expected[] = "struct Fred ; " + "Fred fred ; " + "struct Fred { Fred ( ) ; } ;"; + + ASSERT_EQUALS(expected, tok(code)); + } + + { + const char code[] = "template struct Fred { };\n" + "Fred fred1;\n" + "Fred fred2;"; + + const char expected[] = "struct Fred ; " + "Fred fred1 ; " + "Fred fred2 ; " + "struct Fred { } ;"; + + ASSERT_EQUALS(expected, tok(code)); + } + } + + void template22() { + const char code[] = "template struct Fred { T a; };\n" + "Fred fred;"; + + const char expected[] = "struct Fred ; " + "Fred fred ; " + "struct Fred { std :: string a ; } ;"; + + ASSERT_EQUALS(expected, tok(code)); + } + + void template23() { + const char code[] = "template void foo() { }\n" + "void bar() {\n" + " std::cout << (foo());\n" + "}"; + + const char expected[] = "void foo ( ) ; " + "void bar ( ) {" + " std :: cout << ( foo ( ) ) ; " + "} " + "void foo ( ) { }"; + + ASSERT_EQUALS(expected, tok(code)); + } + + void template24() { + // #2648 + const char code[] = "template struct B\n" + "{\n" + " int a[n];\n" + "};\n" + "\n" + "template class bitset: B\n" + "{};\n" + "\n" + "bitset<1> z;"; + const char expected[] = "struct B<4> ; " + "class bitset<1> ; " + "bitset<1> z ; " + "class bitset<1> : B<4> { } ; " + "struct B<4> { int a [ 4 ] ; } ;"; + ASSERT_EQUALS(expected, tok(code)); + } + + void template25() { + const char code[] = "template struct B\n" + "{\n" + " int a[n];\n" + "};\n" + "\n" + "template class bitset: B<((sizeof(int)) ? : 1)>\n" + "{};\n" + "\n" + "bitset<1> z;"; + const char expected[] = "struct B<4> ; " + "class bitset<1> ; " + "bitset<1> z ; " + "class bitset<1> : B<4> { } ; " + "struct B<4> { int a [ 4 ] ; } ;"; + ASSERT_EQUALS(expected, tok(code)); + } + + void template26() { + // #2721 + const char code[] = "template\n" + "class A { public: T x; };\n" + "\n" + "template\n" + "class C: public A {};\n" + "\n" + "C<2> a;\n"; + ASSERT_EQUALS("class A ; class C<2> ; C<2> a ; class C<2> : public A { } ; class A { public: char [ 2 ] x ; } ;", tok(code)); + } + + void template27() { + // #3350 - template inside macro call + const char code[] = "X(template class Fred);"; + ASSERT_THROW_INTERNAL(tok(code), SYNTAX); + } + + void template28() { + // #3226 - inner template + const char code[] = "template class Fred {};\n" + "Fred > x;\n"; + ASSERT_EQUALS("class Fred ; " + "class Fred> ; " + "Fred> x ; " + "class Fred { } ; " + "class Fred> { } ;", tok(code)); + } + + void template30() { + // #3529 - template < template < .. + const char code[] = "template class A, class B> void f(){}"; + ASSERT_EQUALS("template < template < class > class A , class B > void f ( ) { }", tok(code)); + } + + void template31() { + // #4010 - template reference type + const char code[] = "template struct A{}; A a;"; + ASSERT_EQUALS("struct A ; " + "A a ; " + "struct A { } ;", tok(code)); + + // #7409 - rvalue + const char code2[] = "template struct A{}; A a;"; + ASSERT_EQUALS("struct A ; " + "A a ; " + "struct A { } ;", tok(code2)); + } + + void template32() { + // #3818 - mismatching template not handled well + const char code[] = "template struct A { };\n" + "\n" + "template \n" + "struct B\n" + "{\n" + " public:\n" + " A < int, Pair, int > a;\n" // mismatching parameters => don't instantiate + "};\n" + "\n" + "B b;\n"; + ASSERT_EQUALS("template < class T1 , class T2 , class T3 , class T4 > struct A { } ; " + "struct B ; " + "B b ; " + "struct B { public: A < int , Pair < int , int > , int > a ; } ;", tok(code)); + } + + void template33() { + { + // #3818 - inner templates in template instantiation not handled well + const char code[] = "template struct A { };\n" + "template struct B { };\n" + "template struct C { A > > ab; };\n" + "C c;"; + ASSERT_EQUALS("struct A>> ; " + "struct B> ; " + "struct C ; " + "C c ; " + "struct C { A>> ab ; } ; " + "struct B> { } ; " // <- redundant.. but nevermind + "struct A>> { } ;", tok(code)); + } + + { + // #4544 + const char code[] = "struct A { };\n" + "template struct B { };\n" + "template struct C { };\n" + "C< B > c;"; + ASSERT_EQUALS("struct A { } ; " + "template < class T > struct B { } ; " // <- redundant.. but nevermind + "struct C> ; " + "C> c ; " + "struct C> { } ;", + tok(code)); + } + } + + void template34() { + // #3706 - namespace => hang + const char code[] = "namespace abc {\n" + "template struct X { void f(X &x) {} };\n" + "}\n" + "template <> int X::Y(0);"; + tok(code); + } + + void template35() { // #4074 - "A<'x'> a;" is not recognized as template instantiation + const char code[] = "template class A {};\n" + "A <'x'> a;"; + ASSERT_EQUALS("class A<'x'> ; " + "A<'x'> a ; " + "class A<'x'> { } ;", tok(code)); + } + + void template36() { // #4310 - Passing unknown template instantiation as template argument + const char code[] = "template struct X { T t; };\n" + "template struct Y { Foo < X< Bar > > _foo; };\n" // <- Bar is unknown + "Y bar;"; + ASSERT_EQUALS("struct X> ; " + "struct Y ; " + "Y bar ; " + "struct Y { Foo < X> > _foo ; } ; " + "struct X> { Bar < int > t ; } ;", + tok(code)); + } + + void template37() { // #4544 - A a; + { + const char code[] = "class A { };\n" + "template class B {};\n" + "B b1;\n" + "B b2;"; + ASSERT_EQUALS("class A { } ; class B ; B b1 ; B b2 ; class B { } ;", + tok(code)); + } + { + const char code[] = "struct A { };\n" + "template class B {};\n" + "B b1;\n" + "B b2;"; + ASSERT_EQUALS("struct A { } ; class B ; B b1 ; B b2 ; class B { } ;", + tok(code)); + } + { + const char code[] = "enum A { };\n" + "template class B {};\n" + "B b1;\n" + "B b2;"; + ASSERT_EQUALS("enum A { } ; class B ; B b1 ; B b2 ; class B { } ;", + tok(code)); + } + } + + void template_unhandled() { + // An unhandled template usage should not be simplified.. + ASSERT_EQUALS("x < int > ( ) ;", tok("x();")); + } + + void template38() { // #4832 - Crash on C++11 right angle brackets + const char code[] = "template class A {\n" + " T mT;\n" + "public:\n" + " void foo() {}\n" + "};\n" + "\n" + "int main() {\n" + " A> gna1;\n" + " A gna2;\n" + "}\n"; + const char expected[] = "class A ; " + "class A> ; " + "int main ( ) { " + "A> gna1 ; " + "A gna2 ; " + "} " + "class A { " + "BLA mT ; " + "public: " + "void foo ( ) { } " + "} ; " + "class A> { " + "A mT ; " + "public: " + "void foo ( ) { } " + "} ;"; + ASSERT_EQUALS(expected, tok(code)); + } + + void template39() { // #4742 - Used to freeze in 1.60 + const char code[] = "template struct vector {" + " operator T() const;" + "};" + "void f() {" + " vector> v;" + " const vector vi = static_cast>(v);" + "}"; + tok(code); + } + + void template40() { // #5055 - false negatives when there is template specialization outside struct + const char code[] = "struct A {" + " template struct X { T t; };" + "};" + "template<> struct A::X { int *t; };"; + const char expected[] = "struct A { " + "struct X ; " + "template < typename T > struct X { T t ; } ; " + "} ; " + "struct A :: X { int * t ; } ;"; + ASSERT_EQUALS(expected, tok(code)); + } + + void template41() { // #4710 - const in template instantiation not handled perfectly + const char code1[] = "template struct X { };\n" + "void f(const X x) { }"; + ASSERT_EQUALS("struct X ; " + "void f ( const X x ) { } " + "struct X { } ;", tok(code1)); + + const char code2[] = "template T f(T t) { return t; }\n" + "int x() { return f(123); }"; + ASSERT_EQUALS("int f ( int t ) ; " + "int x ( ) { return f ( 123 ) ; } " + "int f ( int t ) { return t ; }", tok(code2)); + } + + void template42() { // #4878 cppcheck aborts in ext-blocks.cpp (clang testcode) + const char code[] = "template\n" + "int f0(Args ...args) {\n" + " return ^ {\n" + " return sizeof...(Args);\n" + " }() + ^ {\n" + " return sizeof...(args);\n" + " }();\n" + "}"; + ASSERT_THROW_INTERNAL(tok(code), SYNTAX); + } + + void template43() { // #5097 - Assert due to '>>' in 'B>' not being treated as end of template instantiation + const char code[] = "template struct E { typedef int Int; };\n" + "template struct C { };\n" + "template struct D { static int f() { return C::f(); } };\n" + "template inline int f2() { return D::f(); }\n" + "template int f1 (int x, T *) { int id = f2(); return id; }\n" + "template struct B { void f3(B & other) { } };\n" + "struct A { };\n" + "template <> struct C> {\n" + " static int f() { return f1>(0, reinterpret_cast*>(E::Int(-1))); }\n" + "};\n" + "int main(void) {\n" + " C ca;\n" + " return 0;\n" + "}"; + const char expected[] = "struct E ; " + "struct C> ; " + "struct C ; " + "struct D> ; " + "int f2> ( ) ; " + "int f1> ( int x , B * ) ; " + "struct B ; " + "struct A { } ; " + "struct C> { " + "static int f ( ) { " + "return f1> ( 0 , reinterpret_cast < B * > ( E :: Int ( -1 ) ) ) ; " + "} " + "} ; " + "int main ( ) { " + "C ca ; " + "return 0 ; " + "} " + "struct B { " + "void f3 ( B & other ) { } " + "} ; " + "int f1> ( int x , B * ) { " + "int id ; id = f2> ( ) ; " + "return id ; " + "} " + "int f2> ( ) { " + "return D> :: f ( ) ; " + "} " + "struct D> { " + "static int f ( ) { " + "return C> :: f ( ) ; " + "} " + "} ; " + "struct C { } ; struct E { " + "} ;"; + ASSERT_EQUALS(expected, tok(code)); + } + + void template44() { // #5297 + const char code[] = "template struct StackContainer {" + " void foo(int i) {" + " if (0 >= 1 && i<0) {}" + " }" + "};" + "template class ZContainer : public StackContainer {};" + "struct FGSTensor {};" + "class FoldedZContainer : public ZContainer {};"; + const char expected[] = "struct StackContainer ; " + "class ZContainer ; " + "struct FGSTensor { } ; " + "class FoldedZContainer : public ZContainer { } ; " + "class ZContainer : public StackContainer { } ; " + "struct StackContainer { " + "void foo ( int i ) { " + "if ( 0 >= 1 && i < 0 ) { } " + "} " + "} ;"; + ASSERT_EQUALS(expected, tok(code)); + } + + void template45() { // #5814 + const char code[] = "namespace Constants { const int fourtytwo = 42; } " + "template struct TypeMath { " + " static const int mult = sizeof(T) * U; " + "}; " + "template struct FOO { " + " enum { value = TypeMath::mult }; " + "}; " + "FOO foo;"; + const char expected[] = "namespace Constants { const int fourtytwo = 42 ; } " + "struct TypeMath ; " + "struct FOO ; " + "FOO foo ; " + "struct FOO { " + "enum Anonymous0 { value = TypeMath :: mult } ; " + "} ; " + "struct TypeMath { " + "static const int mult = sizeof ( int ) * Constants :: fourtytwo ; " + "} ;"; + ASSERT_EQUALS(expected, tok(code, true)); + ASSERT_EQUALS("", errout_str()); + } + + void template46() { // #5816 + tok("template struct A { static const int value = 0; }; " + "template struct B { " + " enum { value = A::value }; " + "};"); + ASSERT_EQUALS("", errout_str()); + tok("template struct A {}; " + "enum { e = sizeof(A) }; " + "template struct B {};"); + ASSERT_EQUALS("", errout_str()); + tok("template struct A { static const int value = 0; }; " + "template struct B { typedef int type; }; " + "template struct C { " + " enum { value = A::type, int>::value }; " + "};"); + ASSERT_EQUALS("", errout_str()); + } + + void template47() { // #6023 + tok("template > class C1 {}; " + "class C2 : public C1 {};"); + ASSERT_EQUALS("", errout_str()); + } + + void template48() { // #6134 + tok("template int f( { } ); " + "int foo = f<1>(0);"); + ASSERT_EQUALS("", errout_str()); + } + + void template49() { // #6237 + const char code[] = "template class Fred { void f(); void g(); };\n" + "template void Fred::f() { }\n" + "template void Fred::g() { }\n" + "template void Fred::f();\n" + "template void Fred::g();\n"; + + const char expected[] = "class Fred ; " + "class Fred ; " + "class Fred { void f ( ) ; void g ( ) ; } ; " + "void Fred :: f ( ) { } " + "void Fred :: g ( ) { } " + "class Fred { void f ( ) ; void g ( ) ; } ; " + "void Fred :: f ( ) { } " + "void Fred :: g ( ) { }"; + + ASSERT_EQUALS(expected, tok(code)); + } + + void template50() { // #4272 + const char code[] = "template class Fred { void f(); };\n" + "template void Fred::f() { }\n" + "template<> void Fred::f() { }\n" + "template<> void Fred::f() { }\n"; + + const char expected[] = "class Fred ; " + "class Fred ; " + "template < > void Fred :: f ( ) { } " + "template < > void Fred :: f ( ) { } " + "class Fred { void f ( ) ; } ; " + "void Fred :: f ( ) { } " + "class Fred { void f ( ) ; } ; " + "void Fred :: f ( ) { }"; + + ASSERT_EQUALS(expected, tok(code)); + } + + void template52() { // #6437 + const char code[] = "template int sum() { " + " return value + sum(); " + "} " + "template int calculate_value() { " + " if (x != y) { " + " return sum(); " + " } else { " + " return 0; " + " } " + "} " + "int value = calculate_value<1,1>();"; + const char expected[] = "int sum<0> ( ) ; " + "int calculate_value<1,1> ( ) ; " + "int value ; value = calculate_value<1,1> ( ) ; " + "int calculate_value<1,1> ( ) { " + "if ( 1 != 1 ) { " + "return sum<0> ( ) ; " + "} else { " + "return 0 ; " + "} " + "} " + "int sum<0> ( ) { " + "return 0 + sum<0> ( ) ; " + "}"; + ASSERT_EQUALS(expected, tok(code)); + } + + void template53() { // #4335 + const char code[] = "template struct Factorial { " + " enum { value = N * Factorial::value }; " + "};" + "template <> struct Factorial<0> { " + " enum { value = 1 }; " + "};" + "const int x = Factorial<4>::value;"; + const char expected[] = "struct Factorial<0> ; " + "struct Factorial<4> ; " + "struct Factorial<3> ; " + "struct Factorial<2> ; " + "struct Factorial<1> ; " + "struct Factorial<0> { " + "enum Anonymous1 { value = 1 } ; " + "} ; " + "const int x = Factorial<4> :: value ; " + "struct Factorial<4> { " + "enum Anonymous0 { value = 4 * Factorial<3> :: value } ; " + "} ; " + "struct Factorial<3> { " + "enum Anonymous0 { value = 3 * Factorial<2> :: value } ; " + "} ; " + "struct Factorial<2> { " + "enum Anonymous0 { value = 2 * Factorial<1> :: value } ; " + "} ; " + "struct Factorial<1> { " + "enum Anonymous0 { value = 1 * Factorial<0> :: value } ; " + "} ;"; + ASSERT_EQUALS(expected, tok(code, true)); + ASSERT_EQUALS("", errout_str()); + } + + void template54() { // #6587 + tok("template _Tp* fn(); " + "template struct A { " + " template ())> " + " struct B { }; " + "}; " + "A a;"); + } + + void template55() { // #6604 + // Avoid constconstconst in macro instantiations + ASSERT_EQUALS( + "template < class T > class AtSmartPtr : public ConstCastHelper < AtSmartPtr < const T > , T > { " + "friend struct ConstCastHelper < AtSmartPtr < const T > , T > ; " + "AtSmartPtr ( const AtSmartPtr < T > & r ) ; " + "} ;", + tok("template class AtSmartPtr : public ConstCastHelper, T>\n" + "{\n" + " friend struct ConstCastHelper, T>;\n" + " AtSmartPtr(const AtSmartPtr& r);\n" + "};")); + + // Similar problem can also happen with ... + ASSERT_EQUALS( + "struct A ; " + "struct A ; " + "A a ( 0 ) ; " + "struct A { " + "A ( int * p ) { ( A * ) ( p ) ; } " + "} ; " + "struct A { " + "A ( int * p ) { " + "( A * ) ( p ) ; " + "} } ;", + tok("template struct A\n" + "{\n" + " A(T* p) {\n" + " (A*)(p);\n" + " }\n" + "};\n" + "A a(0);")); + } + + void template56() { // #7117 + const char code[] = "template struct Foo { " + " std::array mfoo; " + "}; " + "void foo() { " + " Foo myFoo; " + "}"; + const char expected[] = "struct Foo ; " + "void foo ( ) { " + "Foo myFoo ; " + "} struct Foo { " + "std :: array < int , 1 > mfoo ; " + "} ;"; + ASSERT_EQUALS(expected, tok(code, true)); + ASSERT_EQUALS("", errout_str()); + } + + void template57() { // #7891 + const char code[] = "template struct Test { Test(T); };\n" + "Test test( 0 );"; + const char exp[] = "struct Test ; " + "Test test ( 0 ) ; " + "struct Test { Test ( unsigned long ) ; } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template58() { // #6021 + const char code[] = "template \n" + "void TestArithmetic() {\n" + " x(1 * CheckedNumeric());\n" + "}\n" + "void foo() {\n" + " TestArithmetic();\n" + "}"; + const char exp[] = "void TestArithmetic ( ) ; " + "void foo ( ) {" + " TestArithmetic ( ) ; " + "} " + "void TestArithmetic ( ) {" + " x ( 1 * CheckedNumeric < int > ( ) ) ; " + "}"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template59() { // #8051 + const char code[] = "template\n" + "struct Factorial {\n" + " enum FacHelper { value = N * Factorial::value };\n" + "};\n" + "template <>\n" + "struct Factorial<0> {\n" + " enum FacHelper { value = 1 };\n" + "};\n" + "template\n" + "int diagonalGroupTest() {\n" + " return Factorial::value;\n" + "}\n" + "int main () {\n" + " return diagonalGroupTest<4>();\n" + "}"; + const char exp[] = "struct Factorial<0> ; " + "struct Factorial<4> ; " + "struct Factorial<3> ; " + "struct Factorial<2> ; " + "struct Factorial<1> ; " + "struct Factorial<0> { enum FacHelper { value = 1 } ; } ; " + "int diagonalGroupTest<4> ( ) ; " + "int main ( ) { return diagonalGroupTest<4> ( ) ; } " + "int diagonalGroupTest<4> ( ) { return Factorial<4> :: value ; } " + "struct Factorial<4> { enum FacHelper { value = 4 * Factorial<3> :: value } ; } ; " + "struct Factorial<3> { enum FacHelper { value = 3 * Factorial<2> :: value } ; } ; " + "struct Factorial<2> { enum FacHelper { value = 2 * Factorial<1> :: value } ; } ; " + "struct Factorial<1> { enum FacHelper { value = 1 * Factorial<0> :: value } ; } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template60() { // Extracted from Clang testfile + const char code[] = "template struct S { typedef int type; };\n" + "template void f() {}\n" + "template void h() { f::type(0)>(); }\n" + "\n" + "void j() { h(); }"; + const char exp[] = "struct S ; " + "void f::type(0)> ( ) ; " + "void h ( ) ; " + "void j ( ) { h ( ) ; } " + "void h ( ) { f::type(0)> ( ) ; } " + "struct S { } ; " + "void f::type(0)> ( ) { }"; + const char act[] = "template < typename T > struct S { } ; " + "void f::type(0)> ( ) ; " + "void h ( ) ; " + "void j ( ) { h ( ) ; } " + "void h ( ) { f::type(0)> ( ) ; } " + "void f::type(0)> ( ) { }"; + TODO_ASSERT_EQUALS(exp, act, tok(code)); + } + + void template61() { // hang in daca, code extracted from kodi + const char code[] = "template struct Foo {};\n" + "template struct Bar {\n" + " void f1(Bar x) {}\n" + " Foo> f2() { }\n" + "};\n" + "Bar c;"; + const char exp[] = "struct Foo> ; " + "struct Bar ; " + "Bar c ; " + "struct Bar {" + " void f1 ( Bar x ) { }" + " Foo> f2 ( ) { } " + "} ; " + "struct Foo> { } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template62() { // #8314 + const char code[] = "template struct C1 {};\n" + "template void f() { x = y ? C1::allocate(1) : 0; }\n" + "template class C3 {};\n" + "template C3::C3(const C3 &v) { C1 c1; }\n" + "C3 c3;"; + const char exp[] = "struct C1 ; " + "template < class T > void f ( ) { x = y ? ( C1 < int > :: allocate ( 1 ) ) : 0 ; } " + "class C3 ; " + "C3 c3 ; " + "class C3 { } ; " + "C3 :: C3 ( const C3 & v ) { C1 c1 ; } " + "struct C1 { } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template63() { // #8576 + const char code[] = "template struct TestClass { T m_hi; };" + "TestClass> objTest3;"; + const char exp[] = "struct TestClass> ; " + "TestClass> objTest3 ; " + "struct TestClass> { std :: auto_ptr < v > m_hi ; } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template64() { // #8683 + const char code[] = "template \n" + "bool foo(){return true;}\n" + "struct A {\n" + "template\n" + "void t_func()\n" + "{\n" + " if( n != 0 || foo());\n" + "}\n" + "void t_caller()\n" + "{\n" + " t_func<0>();\n" + " t_func<1>();\n" + "}\n" + "};"; + const char exp[] = "bool foo ( ) ; " + "struct A { " + "void t_func<0> ( ) ; " + "void t_func<1> ( ) ; " + "void t_caller ( ) " + "{ " + "t_func<0> ( ) ; " + "t_func<1> ( ) ; " + "} " + "} ; " + "void A :: t_func<0> ( ) " + "{ " + "if ( 0 != 0 || foo ( ) ) { ; } " + "} " + "void A :: t_func<1> ( ) " + "{ " + "if ( 1 != 0 || foo ( ) ) { ; } " + "} " + "bool foo ( ) { return true ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template65() { // #8321 (crash) + const char code[] = "namespace bpp\n" + "{\n" + "template\n" + "class AssociationDAGraphImplObserver :\n" + " public AssociationGraphImplObserver\n" + "{};\n" + "template\n" + "using AssociationDAGlobalGraphObserver = AssociationDAGraphImplObserver;\n" + "}\n" + "using namespace bpp;\n" + "using namespace std;\n" + "int main() {\n" + " AssociationDAGlobalGraphObserver grObs;\n" + " return 1;\n" + "}"; + const char exp[] = "namespace bpp " + "{ " + "class AssociationDAGraphImplObserver ; " + "} " + "using namespace bpp ; " + "int main ( ) { " + "bpp :: AssociationDAGraphImplObserver grObs ; " + "return 1 ; " + "} class bpp :: AssociationDAGraphImplObserver : " + "public AssociationGraphImplObserver < std :: string , unsigned int , DAGlobalGraph > " + "{ } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template66() { // #8725 + const char code[] = "template struct Fred {\n" + " const int ** foo();\n" + "};\n" + "template const int ** Fred::foo() { return nullptr; }\n" + "Fred fred;"; + const char exp[] = "struct Fred ; " + "Fred fred ; " + "struct Fred { " + "const int * * foo ( ) ; " + "} ; " + "const int * * Fred :: foo ( ) { return nullptr ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template67() { // ticket #8122 + const char code[] = "template struct Container {\n" + " Container();\n" + " Container(const Container &);\n" + " Container & operator = (const Container &);\n" + " ~Container();\n" + " T* mElements;\n" + " const Container * c;\n" + "};\n" + "template Container::Container() : mElements(nullptr), c(nullptr) {}\n" + "template Container::Container(const Container & x) { nElements = x.nElements; c = x.c; }\n" + "template Container & Container::operator = (const Container & x) { mElements = x.mElements; c = x.c; return *this; }\n" + "template Container::~Container() {}\n" + "Container intContainer;"; + + const char expected[] = "struct Container ; " + "Container intContainer ; " + "struct Container { " + "Container ( ) ; " + "Container ( const Container & ) ; " + "Container & operator= ( const Container & ) ; " + "~ Container ( ) ; " + "int * mElements ; " + "const Container * c ; " + "} ; " + "Container :: Container ( ) : mElements ( nullptr ) , c ( nullptr ) { } " + "Container :: Container ( const Container & x ) { nElements = x . nElements ; c = x . c ; } " + "Container & Container :: operator= ( const Container & x ) { mElements = x . mElements ; c = x . c ; return * this ; } " + "Container :: ~ Container ( ) { }"; + + ASSERT_EQUALS(expected, tok(code)); + } + + void template68() { + const char code[] = "template union Fred {\n" + " char dummy[sizeof(T)];\n" + " T value;\n" + "};\n" + "Fred fred;"; + const char exp[] = "union Fred ; " + "Fred fred ; " + "union Fred { " + "char dummy [ sizeof ( int ) ] ; " + "int value ; " + "} ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template69() { // #8791 + const char code[] = "class Test {\n" + " int test;\n" + " template T lookup() { return test; }\n" + " int Fun() { return lookup(); }\n" + "};"; + const char exp[] = "class Test { " + "int test ; " + "int lookup ( ) ; " + "int Fun ( ) { return lookup ( ) ; } " + "} ; " + "int Test :: lookup ( ) { return test ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template70() { // #5289 + const char code[] = "template class Bar;\n" + "template<>\n" + "class Bar {\n" + "};\n" + "template\n" + "class Bar : private Bar {\n" + " void foo() { }\n" + "};"; + const char exp[] = "template < typename T , typename V , int KeySize = 0 > class Bar ; " + "class Bar ; " + "class Bar { " + "} ; " + "template < typename K , typename V , int KeySize = 0 > " + "class Bar : private Bar { " + "void foo ( ) { } " + "} ;"; + const char act[] = "template < typename T , typename V , int KeySize = 0 > class Bar ; " + "class Bar { " + "} ; " + "class Bar ; " + "template < typename K , typename V , int KeySize = 0 > " + "class Bar : private Bar { " + "void foo ( ) { } " + "} ;"; + TODO_ASSERT_EQUALS(exp, act, tok(code)); + } + + void template71() { // #8821 + const char code[] = "int f1(int * pInterface, int x) { return 0; }\n" + "\n" + "template< class interface_type > class Reference {\n" + " template< class interface_type > int i();\n" + " int *pInterface;\n" + "};\n" + "\n" + "template< class interface_type > int Reference< interface_type >::i() {\n" + " return f1(pInterface, interface_type::static_type());\n" + "}\n" + "\n" + "Reference< class XPropertyList > dostuff();"; + const char exp[] = "int f1 ( int * pInterface , int x ) { return 0 ; } " + "class Reference ; " + "Reference dostuff ( ) ; " + "class Reference { template < class XPropertyList > int i ( ) ; int * pInterface ; } ; " + "int Reference :: i ( ) { return f1 ( pInterface , XPropertyList :: static_type ( ) ) ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template72() { + const char code[] = "template class Tokenizer;\n" + "const Tokenizer *tokenizer() const;\n" + "template \n" + "Tokenizer::Tokenizer() { }"; + const char exp[] = "template < typename N , typename P > class Tokenizer ; " + "const Tokenizer < Node , Path > * tokenizer ( ) const ; " + "template < typename N , typename P > " + "Tokenizer < N , P > :: Tokenizer ( ) { }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template73() { + const char code[] = "template\n" + "void keep_range(T& value, const T mini, const T maxi){}\n" + "template void keep_range(float& v, const float l, const float u);\n" + "template void keep_range(int& v, const int l, const int u);"; + const char exp[] = "void keep_range ( float & value , const float mini , const float maxi ) ; " + "void keep_range ( int & value , const int mini , const int maxi ) ; " + "void keep_range ( float & value , const float mini , const float maxi ) { } " + "void keep_range ( int & value , const int mini , const int maxi ) { }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template74() { + const char code[] = "template class BTlist { };\n" + "class PushBackStreamBuf {\n" + "public:\n" + " void pushBack(const BTlist &vec);\n" + "};"; + const char exp[] = "class BTlist ; " + "class PushBackStreamBuf { " + "public: " + "void pushBack ( const BTlist & vec ) ; " + "} ; " + "class BTlist { } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template75() { + const char code[] = "template\n" + "T foo(T& value){ return value; }\n" + "template std::vector> foo>>(std::vector>& v);"; + const char exp[] = "std :: vector < std :: vector < int > > foo>> ( std :: vector < std :: vector < int > > & value ) ; " + "std :: vector < std :: vector < int > > foo>> ( std :: vector < std :: vector < int > > & value ) { return value ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template76() { + const char code[] = "namespace NS {\n" + " template T foo(T& value) { return value; }\n" + " template std::vector> foo>>(std::vector>& v);\n" + "}\n" + "std::vector> v;\n" + "v = foo>>(v);\n"; + const char exp[] = "namespace NS { " + "std :: vector < std :: vector < int > > foo>> ( std :: vector < std :: vector < int > > & value ) ; " + "} " + "std :: vector < std :: vector < int > > v ; " + "v = foo>> ( v ) ; " + "std :: vector < std :: vector < int > > NS :: foo>> ( std :: vector < std :: vector < int > > & value ) { return value ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template77() { + const char code[] = "template\n" + "struct is_void : std::false_type { };\n" + "template<>\n" + "struct is_void : std::true_type { };\n" + "int main() {\n" + " std::cout << is_void::value << std::endl;\n" + " std::cout << is_void::value << std::endl;\n" + "}"; + const char exp[] = "struct is_void ; " + "struct is_void ; " + "struct is_void : std :: true_type { } ; " + "int main ( ) { " + "std :: cout << is_void :: value << std :: endl ; " + "std :: cout << is_void :: value << std :: endl ; " + "} " + "struct is_void : std :: false_type { } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template78() { + const char code[] = "template \n" + "struct Base { };\n" + "struct S : Base ::Type { };"; + const char exp[] = "struct Base ; " + "struct S : Base :: Type { } ; " + "struct Base { } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template79() { // #5133 + const char code[] = "class Foo {\n" + "public:\n" + " template void foo() { bar(); }\n" + "private:\n" + " template void bar() { bazz(); }\n" + " void bazz() { }\n" + "};\n" + "void some_func() {\n" + " Foo x;\n" + " x.foo();\n" + "}"; + const char exp[] = "class Foo { " + "public: " + "void foo ( ) ; " + "private: " + "void bar ( ) ; " + "void bazz ( ) { } " + "} ; " + "void some_func ( ) { " + "Foo x ; " + "x . foo ( ) ; " + "} " + "void Foo :: foo ( ) { bar ( ) ; } " + "void Foo :: bar ( ) { bazz ( ) ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template80() { + const char code[] = "class Fred {\n" + " template T foo(T t) const { return t; }\n" + "};\n" + "const void * p = Fred::foo(nullptr);"; + const char exp[] = "class Fred { " + "const void * foo ( const void * t ) const ; " + "} ; " + "const void * p ; p = Fred :: foo ( nullptr ) ; " + "const void * Fred :: foo ( const void * t ) const { return t ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template81() { + const char code[] = "template \n" + "struct SortWith {\n" + " SortWith(Type);\n" + "};\n" + "template \n" + "SortWith::SortWith(Type) {}\n" + "int main() {\n" + " SortWith(0);\n" + "}"; + const char exp[] = "template < typename Type > " + "struct SortWith { " + "SortWith ( Type ) ; " + "} ; " + "SortWith :: SortWith ( int ) ; " + "int main ( ) { " + "SortWith ( 0 ) ; " + "} " + "SortWith :: SortWith ( int ) { }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template82() { // 8603 + const char code[] = "typedef int comp;\n" + "const int f16=16;\n" + "template\n" + "class tvec2 {};\n" + "template\n" + "class tvec3 {};\n" + "namespace swizzle {\n" + "template void swizzle(tvec2 v) { }\n" + "template void swizzle(tvec3 v) { }\n" + "}\n" + "void foo() {\n" + " using namespace swizzle;\n" + " tvec2 tt2;\n" + " swizzle<1>(tt2);\n" + " tvec3 tt3;\n" + " swizzle<2,3>(tt3);\n" + "}"; + const char exp[] = "const int f16 = 16 ; " + "class tvec2 ; " + "class tvec3 ; " + "namespace swizzle { " + "void swizzle<1> ( tvec2 v ) ; " + "void swizzle<2,3> ( tvec3 v ) ; " + "} " + "void foo ( ) { " + "using namespace swizzle ; " + "tvec2 tt2 ; " + "swizzle :: swizzle<1> ( tt2 ) ; " + "tvec3 tt3 ; " + "swizzle :: swizzle<2,3> ( tt3 ) ; " + "} " + "void swizzle :: swizzle<2,3> ( tvec3 v ) { } " + "void swizzle :: swizzle<1> ( tvec2 v ) { } " + "class tvec3 { } ; " + "class tvec2 { } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template83() { // #8867 + const char code[] = "template\n" + "class MultiConsumer {\n" + " MultiConsumer();\n" + "};\n" + "template\n" + "MultiConsumer::MultiConsumer() : sizeBuffer(0) {}\n" + "MultiReads::MultiReads() {\n" + " mc = new MultiConsumer();\n" + "}"; + const char exp[] = "template < typename Task > " // TODO: this should be expanded + "class MultiConsumer { " + "MultiConsumer ( ) ; " + "} ; " + "MultiConsumer :: MultiConsumer ( ) ; " + "MultiReads :: MultiReads ( ) { " + "mc = new MultiConsumer ( ) ; " + "} " + "MultiConsumer :: MultiConsumer ( ) : sizeBuffer ( 0 ) { }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template84() { // #8880 + { + const char code[] = "template \n" + "auto d() -> typename a::e {\n" + " d();\n" + "}"; + const char exp[] = "template < class b , int c , class > " + "auto d ( ) . a < decltype ( b { } ) > :: e { " + "d < int , c , int > ( ) ; " + "}"; + ASSERT_EQUALS(exp, tok(code)); + } + { + const char code[] = "template \n" + "auto d() -> typename a::e {\n" + " d();\n" + "}" + "void foo() { d(); }"; + const char exp[] = "auto d ( ) . a < char > :: e ; " + "auto d ( ) . a < int > :: e ; " + "void foo ( ) { d ( ) ; } " + "auto d ( ) . a < char > :: e { " + "d ( ) ; " + "} " + "auto d ( ) . a < int > :: e { " + "d ( ) ; " + "}"; + ASSERT_EQUALS(exp, tok(code)); + } + } + + void template85() { // #8902 - crash + const char code[] = "template\n" + "struct C\n" + "{\n" + " template::value)>::type* = nullptr>\n" + " void foo();\n" + "};\n" + "extern template void C::foo();\n" + "template\n" + "template::value)>::type>\n" + "void C::foo() {}"; + // @todo the output is very wrong but we are only worried about the crash for now + tok(code); + } + + void template86() { // crash + const char code[] = "struct S {\n" + " S();\n" + "};\n" + "template \n" + "struct U {\n" + " static S u;\n" + "};\n" + "template \n" + "S U::u;\n" + "template S U::u;\n" + "S &i = U::u;"; + tok(code); + } + + void template87() { + const char code[] = "template\n" + "T f1(T t) { return t; }\n" + "template const char * f1(const char *);\n" + "template const char & f1(const char &);"; + const char exp[] = "const char * f1 ( const char * t ) ; " + "const char & f1 ( const char & t ) ; " + "const char * f1 ( const char * t ) { return t ; } " + "const char & f1 ( const char & t ) { return t ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template88() { // #6183.cpp + const char code[] = "class CTest {\n" + "public:\n" + " template \n" + " static void Greeting(T val) {\n" + " std::cout << val << std::endl;\n" + " }\n" + "private:\n" + " static void SayHello() {\n" + " std::cout << \"Hello World!\" << std::endl;\n" + " }\n" + "};\n" + "template<>\n" + "void CTest::Greeting(bool) {\n" + " CTest::SayHello();\n" + "}\n" + "int main() {\n" + " CTest::Greeting(true);\n" + " return 0;\n" + "}"; + const char exp[] = "class CTest { " + "public: " + "static void Greeting ( bool ) ; " + "template < typename T > " + "static void Greeting ( T val ) { " + "std :: cout << val << std :: endl ; " + "} " + "private: " + "static void SayHello ( ) { " + "std :: cout << \"Hello World!\" << std :: endl ; " + "} " + "} ; " + "void CTest :: Greeting ( bool ) { " + "CTest :: SayHello ( ) ; " + "} " + "int main ( ) { " + "CTest :: Greeting ( true ) ; " + "return 0 ; " + "}"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template89() { // #8917 + const char code[] = "struct Fred {\n" + " template static void foo() { }\n" + "};\n" + "template void Fred::foo();\n" + "template void Fred::foo();\n" + "template <> void Fred::foo() { }\n" + "template <> void Fred::foo() { }"; + const char exp[] = "struct Fred { " + "static void foo ( ) ; " + "static void foo ( ) ; " + "static void foo ( ) ; " + "static void foo ( ) ; " + "} ; " + "void Fred :: foo ( ) { } " + "void Fred :: foo ( ) { } " + "void Fred :: foo ( ) { } " + "void Fred :: foo ( ) { }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template90() { // crash + const char code[] = "template struct S1 {};\n" + "void f(S1) {}\n" + "template \n" + "decltype(S1().~S1()) fun1() {};"; + const char exp[] = "struct S1 ; " + "void f ( S1 ) { } " + "template < typename T > " + "decltype ( S1 < T > ( ) . ~ S1 < T > ( ) ) fun1 ( ) { } ; " + "struct S1 { } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template91() { + { + const char code[] = "template T foo(T t) { return t; }\n" + "template<> char foo(char a) { return a; }\n" + "template<> int foo(int a) { return a; }\n" + "template float foo(float);\n" + "template double foo(double);"; + const char exp[] = "int foo ( int a ) ; " + "char foo ( char a ) ; " + "float foo ( float t ) ; " + "double foo ( double t ) ; " + "char foo ( char a ) { return a ; } " + "int foo ( int a ) { return a ; } " + "float foo ( float t ) { return t ; } " + "double foo ( double t ) { return t ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + { + const char code[] = "struct Fred {\n" + " template T foo(T t) { return t; }\n" + " template<> char foo(char a) { return a; }\n" + " template<> int foo(int a) { return a; }\n" + "};\n" + "template float Fred::foo(float);\n" + "template double Fred::foo(double);"; + const char exp[] = "struct Fred { " + "int foo ( int a ) ; " + "char foo ( char a ) ; " + "float foo ( float t ) ; " + "double foo ( double t ) ; " + "char foo ( char a ) { return a ; } " + "int foo ( int a ) { return a ; } " + "} ; " + "float Fred :: foo ( float t ) { return t ; } " + "double Fred :: foo ( double t ) { return t ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + { + const char code[] = "namespace NS1 {\n" + " namespace NS2 {\n" + " template T foo(T t) { return t; }\n" + " template<> char foo(char a) { return a; }\n" + " template<> int foo(int a) { return a; }\n" + " template short NS2::foo(short);\n" + " template long NS1::NS2::foo(long);\n" + " }\n" + " template float NS2::foo(float);\n" + " template bool NS1::NS2::foo(bool);\n" + "}\n" + "template double NS1::NS2::foo(double);"; + const char exp[] = "namespace NS1 { " + "namespace NS2 { " + "int foo ( int a ) ; " + "char foo ( char a ) ; " + "short foo ( short t ) ; " + "long foo ( long t ) ; " + "float foo ( float t ) ; " + "bool foo ( bool t ) ; " + "double foo ( double t ) ; " + "char foo ( char a ) { return a ; } " + "int foo ( int a ) { return a ; } " + "} " + "} " + "short NS1 :: NS2 :: foo ( short t ) { return t ; } " + "long NS1 :: NS2 :: foo ( long t ) { return t ; } " + "float NS1 :: NS2 :: foo ( float t ) { return t ; } " + "bool NS1 :: NS2 :: foo ( bool t ) { return t ; } " + "double NS1 :: NS2 :: foo ( double t ) { return t ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + { + const char code[] = "namespace NS1 {\n" + " namespace NS {\n" + " template T foo(T t) { return t; }\n" + " template<> char foo(char a) { return a; }\n" + " template<> int foo(int a) { return a; }\n" + " template short NS::foo(short);\n" + " template long NS1::NS::foo(long);\n" + " }\n" + " template float NS::foo(float);\n" + " template bool NS1::NS::foo(bool);\n" + "}\n" + "template double NS1::NS::foo(double);"; + const char exp[] = "namespace NS1 { " + "namespace NS { " + "int foo ( int a ) ; " + "char foo ( char a ) ; " + "short foo ( short t ) ; " + "long foo ( long t ) ; " + "float foo ( float t ) ; " + "bool foo ( bool t ) ; " + "double foo ( double t ) ; " + "char foo ( char a ) { return a ; } " + "int foo ( int a ) { return a ; } " + "} " + "} " + "short NS1 :: NS :: foo ( short t ) { return t ; } " + "long NS1 :: NS :: foo ( long t ) { return t ; } " + "float NS1 :: NS :: foo ( float t ) { return t ; } " + "bool NS1 :: NS :: foo ( bool t ) { return t ; } " + "double NS1 :: NS :: foo ( double t ) { return t ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + } + + void template92() { + const char code[] = "template void foo(T const& t) { }\n" + "template<> void foo(double const& d) { }\n" + "template void foo(float const& f);\n" + "int main() {\n" + " foo(2);\n" + " foo(3.14);\n" + " foo(3.14f);\n" + "}"; + const char exp[] = "void foo ( const double & d ) ; " + "void foo ( const float & t ) ; " + "void foo ( const int & t ) ; " + "void foo ( const double & d ) { } " + "int main ( ) { " + "foo ( 2 ) ; " + "foo ( 3.14 ) ; " + "foo ( 3.14f ) ; " + "} " + "void foo ( const float & t ) { } " + "void foo ( const int & t ) { }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template93() { // crash + const char code[] = "template \n" + "void ForEach() { }\n" + "template \n" + "class Vector2 : public Vector {\n" + " template \n" + " void ForEach();\n" + "public:\n" + " void process();\n" + "};\n" + "template \n" + "void Vector2::process() {\n" + " ForEach();\n" + "}\n" + "Vector2 c;"; + const char exp[] = "void ForEach ( ) ; " + "class Vector2 ; " + "Vector2 c ; " + "class Vector2 : public Vector { " + "template < typename Iterator > " + "void ForEach ( ) ; " + "public: " + "void process ( ) ; " + "} ; " + "void Vector2 :: process ( ) { " + "ForEach ( ) ; " + "} " + "void ForEach ( ) { " + "}"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template94() { // #8927 crash + const char code[] = "template \n" + "class Array { };\n" + "template\n" + "Array foo() {};\n" + "template <> Array foo() { }\n" + "template <> Array> foo>() { }\n" + "template <> Array foo() { }\n" + "template < typename T >\n" + "Array matmul() {\n" + " return foo( );\n" + "}\n" + "template Array> matmul>();"; + const char exp[] = "class Array ; " + "class Array> ; " + "class Array ; " + "Array foo ( ) ; " + "Array> foo> ( ) ; " + "Array foo ( ) ; " + "template < typename T > " + "Array < T > foo ( ) { } ; " + "Array foo ( ) { } " + "Array> foo> ( ) { } " + "Array foo ( ) { } " + "Array> matmul> ( ) ; " + "Array> matmul> ( ) { " + "return foo> ( ) ; " + "} " + "class Array { } ; " + "class Array> { } ; " + "class Array { } ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template95() { // #7417 + const char code[] = "template \n" + "T Value = 123;\n" + "template<>\n" + "int Value = 456;\n" + "float f = Value;\n" + "int i = Value;"; + const char exp[] = "float Value ; Value = 123 ; " + "int Value ; Value = 456 ; " + "float f ; f = Value ; " + "int i ; i = Value ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template96() { // #7854 + { + const char code[] = "template\n" + " constexpr long fib = fib + fib;\n" + "template<>\n" + " constexpr long fib<0> = 0;\n" + "template<>\n" + " constexpr long fib<1> = 1;\n" + "long f0 = fib<0>;\n" + "long f1 = fib<1>;\n" + "long f2 = fib<2>;\n" + "long f3 = fib<3>;"; + const char exp[] = "constexpr long fib<2> = fib<1> + fib<0> ; " + "constexpr long fib<3> = fib<2> + fib<1> ; " + "constexpr long fib<0> = 0 ; " + "constexpr long fib<1> = 1 ; " + "long f0 ; f0 = fib<0> ; " + "long f1 ; f1 = fib<1> ; " + "long f2 ; f2 = fib<2> ; " + "long f3 ; f3 = fib<3> ;"; + ASSERT_EQUALS(exp, tok(code)); + } + { + const char code[] = "template\n" + " constexpr long fib = fib + fib;\n" + "template<>\n" + " constexpr long fib<0> = 0;\n" + "template<>\n" + " constexpr long fib<1> = 1;\n" + "long f5 = fib<5>;\n"; + const char exp[] = "constexpr long fib<5> = fib<4> + fib<3> ; " + "constexpr long fib<4> = fib<3> + fib<2> ; " + "constexpr long fib<3> = fib<2> + fib<1> ; " + "constexpr long fib<2> = fib<1> + fib<0> ; " + "constexpr long fib<0> = 0 ; " + "constexpr long fib<1> = 1 ; " + "long f5 ; f5 = fib<5> ;"; + ASSERT_EQUALS(exp, tok(code)); + } + } + + void template97() { + const char code[] ="namespace NS1 {\n" + " namespace NS2 {\n" + " namespace NS3 {\n" + " namespace NS4 {\n" + " template\n" + " class Fred {\n" + " T * t;\n" + " public:\n" + " Fred() : t(nullptr) {}\n" + " };\n" + " }\n" + " using namespace NS4;\n" + " Fred fred_bool;\n" + " NS4::Fred fred_char;\n" + " }\n" + " using namespace NS3;\n" + " NS4::Fred fred_short;\n" + " using namespace NS3::NS4;\n" + " Fred fred_int;\n" + " NS3::NS4::Fred fred_long;\n" + " NS2::NS3::NS4::Fred fred_float;\n" + " NS1::NS2::NS3::NS4::Fred fred_double;\n" + " }\n" + " using namespace NS2;\n" + " NS3::NS4::Fred fred_float1;\n" + " NS2::NS3::NS4::Fred fred_double1;\n" + "}\n" + "using namespace NS1::NS2::NS3::NS4;\n" + "Fred fred_bool1;\n" + "NS1::NS2::NS3::NS4::Fred fred_int1;"; + const char exp[] = "namespace NS1 { " + "namespace NS2 { " + "namespace NS3 { " + "namespace NS4 { " + "class Fred ; " + "class Fred ; " + "class Fred ; " + "class Fred ; " + "class Fred ; " + "class Fred ; " + "class Fred ; " + "} " + "using namespace NS4 ; " + "NS4 :: Fred fred_bool ; " + "NS4 :: Fred fred_char ; " + "} " + "using namespace NS3 ; " + "NS3 :: NS4 :: Fred fred_short ; " + "using namespace NS3 :: NS4 ; " + "NS3 :: NS4 :: Fred fred_int ; " + "NS3 :: NS4 :: Fred fred_long ; " + "NS2 :: NS3 :: NS4 :: Fred fred_float ; " + "NS1 :: NS2 :: NS3 :: NS4 :: Fred fred_double ; " + "} " + "using namespace NS2 ; " + "NS2 :: NS3 :: NS4 :: Fred fred_float1 ; " + "NS2 :: NS3 :: NS4 :: Fred fred_double1 ; " + "} " + "using namespace NS1 :: NS2 :: NS3 :: NS4 ; " + "NS1 :: NS2 :: NS3 :: NS4 :: Fred fred_bool1 ; " + "NS1 :: NS2 :: NS3 :: NS4 :: Fred fred_int1 ; " + "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " + "bool * t ; " + "public: " + "Fred ( ) : t ( nullptr ) { } " + "} ; " + "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " + "char * t ; " + "public: " + "Fred ( ) : t ( nullptr ) { } " + "} ; " + "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " + "short * t ; " + "public: " + "Fred ( ) : t ( nullptr ) { } " + "} ; " + "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " + "int * t ; " + "public: " + "Fred ( ) : t ( nullptr ) { } " + "} ; " + "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " + "long * t ; " + "public: " + "Fred ( ) : t ( nullptr ) { } " + "} ; " + "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " + "float * t ; " + "public: " + "Fred ( ) : t ( nullptr ) { } " + "} ; " + "class NS1 :: NS2 :: NS3 :: NS4 :: Fred { " + "double * t ; " + "public: " + "Fred ( ) : t ( nullptr ) { } " + "} ;"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template98() { // #8959 + const char code[] = "template \n" + "using unique_ptr_with_deleter = std::unique_ptr>;\n" + "class A {};\n" + "static void func() {\n" + " unique_ptr_with_deleter tmp(new A(), [](A* a) {\n" + " delete a;\n" + " });\n" + "}"; + const char exp[] = "class A { } ; " + "static void func ( ) { " + "std :: unique_ptr < A , std :: function < void ( A * ) > > tmp ( new A ( ) , [ ] ( A * a ) { " + "delete a ; " + "} ) ; " + "}"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template99() { // #8960 + const char code[] = "template \n" + "class Base {\n" + "public:\n" + " using ArrayType = std::vector>;\n" + "};\n" + "using A = Base;\n" + "static A::ArrayType array;\n"; + const char exp[] = "class Base ; " + "static std :: vector < Base > array ; " + "class Base { " + "public: " + "} ;"; + + ASSERT_EQUALS(exp, tok(code)); + } + + void template100() { // #8967 + const char code[] = "enum class Device { I2C0, I2C1 };\n" + "template \n" + "const char* deviceFile;\n" + "template <>\n" + "const char* deviceFile = \"/tmp/i2c-0\";\n"; + + const char exp[] = "enum class Device { I2C0 , I2C1 } ; " + "template < Device D > " + "const char * deviceFile ; " + "const char * deviceFile ; deviceFile = \"/tmp/i2c-0\" ;"; + + ASSERT_EQUALS(exp, tok(code)); + } + + void template101() { // #8968 + const char code[] = "class A {\n" + "public:\n" + " using ArrayType = std::vector;\n" + " void func(typename ArrayType::size_type i) {\n" + " }\n" + "};"; + + const char exp[] = "class A { " + "public: " + "void func ( std :: vector < int > :: size_type i ) { " + "} " + "} ;"; + + ASSERT_EQUALS(exp, tok(code)); + ASSERT_EQUALS("", errout_str()); + } + + void template102() { // #9005 + const char code[] = "namespace ns {\n" + "template \n" + "struct is_floating_point\n" + ": std::integral_constant::value || true>\n" + "{};\n" + "}\n" + "void f() {\n" + " if(std::is_floating_point::value) {}\n" + "}"; + const char exp[] = "namespace ns { " + "template < class T > " + "struct is_floating_point " + ": std :: integral_constant < bool , std :: is_floating_point < T > :: value || true > " + "{ } ; " + "} " + "void f ( ) { " + "if ( std :: is_floating_point < float > :: value ) { } " + "}"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template103() { + const char code[] = "namespace sample {\n" + " template \n" + " class Sample {\n" + " public:\n" + " T function(T t);\n" + " };\n" + " template \n" + " T Sample::function(T t) {\n" + " return t;\n" + " }\n" + "}\n" + "sample::Sample s1;"; + const char exp[] = "namespace sample { " + "class Sample ; " + "} " + "sample :: Sample s1 ; " + "class sample :: Sample { " + "public: " + "int function ( int t ) ; " + "} ; " + "int sample :: Sample :: function ( int t ) { " + "return t ; " + "}"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template104() { // #9021 + const char code[] = "template < int i >\n" + "auto key ( ) { return hana :: test :: ct_eq < i > { } ; }\n" + "template < int i >\n" + "auto val ( ) { return hana :: test :: ct_eq < - i > { } ; }\n" + "template < int i , int j >\n" + "auto p ( ) { return :: minimal_product ( key < i > ( ) , val < j > ( ) ) ; }\n" + "int main ( ) {\n" + " BOOST_HANA_CONSTANT_CHECK ( hana :: equal (\n" + " hana :: at_key ( hana :: make_map ( p < 0 , 0 > ( ) ) , key < 0 > ( ) ) ,\n" + " val < 0 > ( ) ) ) ;\n" + "}"; + const char exp[] = "auto key<0> ( ) ; " + "auto val<0> ( ) ; " + "auto p<0,0> ( ) ; " + "int main ( ) { " + "BOOST_HANA_CONSTANT_CHECK ( hana :: equal ( " + "hana :: at_key ( hana :: make_map ( p<0,0> ( ) ) , key<0> ( ) ) , " + "val<0> ( ) ) ) ; " + "} " + "auto p<0,0> ( ) { return :: minimal_product ( key<0> ( ) , val<0> ( ) ) ; } " + "auto val<0> ( ) { return hana :: test :: ct_eq < - 0 > { } ; } " + "auto key<0> ( ) { return hana :: test :: ct_eq < 0 > { } ; }"; + ASSERT_EQUALS(exp, tok(code)); + } + + void template105() { // #9076 + const char code[] = "template

If you want to get started, look at the walk through.